Please, note that this text is written in May 2008. Since then Zend Framework have evolved and now provides Zend_Application class. It is good idea to use it. The user registration and login stuff, presented here is still actual though. Also there are many other ideas presented, which I hope will be usefull for the readers.
Long title for part 4 and much new things to cover while building the registration form. In this part I will cover just the registration form, and not the actual registration process, but believe me – this is not small topic at all
EDIT: This code is written with Zend Framework version 1.5. Since then version 1.6 have been released. There we can find many new components, along with Zend_Captcha and Zend_Form_Element_Captcha. It takes different approach than mine in showing the captcha, you can still take a look at my code for an example how to build components that you need but are not shipped with Zend Framework. But I recommend in new projects to use Zend_Captcha and Zend_Form_Element_Captcha.
First we will write the outline for our User controller. We will rely for now on the default URLS provided to us by Zend_Controller_Router_Rewrite, so assuming we are running from localhost these are the actions we will have for the user controller:
http://localhost/user/login
http://localhost/user/register
http://localhost/user/lostpassword
http://localhost/user/activate – here users will activate their profiles after registration (clicking on a link in sent email)
http://localhost/user/profile – here users will be able to edit their profiles
and of course we need index action – default action, which for now we will redirect to the register action. So here is the outline of
‘application/modules/default/controllers/UserController.php‘:
< ?php require_once 'Zend/Controller/Action.php'; class UserController extends Zend_Controller_Action { public function indexAction() { $this->_forward('register'); } public function loginAction() { } public function registerAction() { } public function lostpasswordAction() { } public function activateAction() { } public function profileAction() { } }
in the index action we just redirect to register action in the same controller. And here – in registerAction() – is where we will concentrate in this part of the series. I want here to have form with the following elements: username, email address, password, password confirmation – these will be required, gender, birth date, real name, checkbox for terms and conditions or agreement – tell it what you want – also required to be checked for registration to take place, and finally – CAPTCHA field.
It is good idea to keep the code, which generates a form separated from the other logic, so several approaches are possible here – to extend Zend_Form class is one obvious way to go, but here I will write getRegistrationForm() method in the UserController class. This method (or member function, I still don’t know which term is better for this
) is quite long and have many interesting points, so I will guide you step by step in the process of writing it, instead of just pasting the code and dropping small notes on it. So first we will write small form with just one field (in ‘application/modules/default/controllers/UserController.php‘ we add this function as member function for the class)
private function getRegistrationForm() { $form = new Zend_Form(); $form->setAction('/user/register')->setMethod('post')->setAttrib('id', 'register'); $username = new Zend_Form_Element_Text('username'); $validatorAlnum = new Zend_Validate_Alnum(); $username->setRequired(true)->setLabel('Username'); $form->addElement($username); $submit = new Zend_Form_Element_Submit('register'); $submit->setLabel('Submit'); $form->addElement($submit); return $form; }
now in registerAction() we call this getRegistrationForm() function and assign the returned form as property of our View object. Then we check if the request is POST (so the form is submitted) and then call isValid() method of the form object – this method hides the big magic of Zend_Form – with just this simple call we have saved ourselves from typing many tedious lines of code validating the form input, assigning error messages and setting default values to allready filled in fields of the form. I strongly recommend to all of you, who will use Zend_Form for something more serious than just the default behaviour which it provides – to read through isValid() method of Zend_Form and follow the flow of the logic there – validating each of the fields of the form by calling their own isValid() method, which in turn iterates over the validators added to the fields and checks if the value provided is valid. Actually here – in Zend_Form_Element::isValid() – is the place, where the value of the element is stored in the element with setValue() call. So if you want to use getValue() method first you should validate the field (or the whole form). What we also do in registerAction() is to save the values submitted in a property of the View object, so we can verify how our form works (this is just debug code, we will use this in the view template)
public function registerAction() { $this->view->registerForm = $this->getRegistrationForm(); if ($this->getRequest()->isPost()) { if ($this->view->registerForm->isValid($_POST)) { $values = $this->view->registerForm->getValues(); $this->view->values = $values; } } }
and here is the view template:
application/modules/default/views/scripts/user/register.phtml
<h1>Register:</h1> < ?php echo $this->registerForm->render(); ?> < ?php var_dump($this->values); ?>
Very simple for now, so lets throw in some more complicated things – filters and validators. But before this we need to have a way to set custom error messages to the validators keeping in mind that we want to make multilangual web site. Zend_Translate will help us with this. I will use gettext translation adapter, because I really don’t like to write the files with messages for translation on my own, and Poedit has the nice feature of parsing source files and gathering strings for translation. So in our bootstrap.php we add:
//we will always use session, so this is good place to create this and save it to the registry $defSession = new Zend_Session_Namespace('Default', true); Zend_Registry::set('defSession', $defSession); // Setup translation adapter // Check if language is set in session - if not - use english as default $lang = 'en'; if (isset($defSession->lang)) { $lang = $defSession->lang; } $translate = new Zend_Translate('gettext', $siteRootDir . '/languages/'.$lang.'.mo', $lang); Zend_Registry::set('Zend_Translate', $translate); Zend_Form::setDefaultTranslator($translate);
So we add to our project new directory – languages/ – where language files will be placed. I want Poedit to parse my source files and autodiscover my strings which need to be translated. When we use validators, for example Zend_Validate_StringLength – we want to set custom error messages. Because for the choosen language to translate to is possible to be different at the time of defining the error messages to the time of showing of the form – we cannot do:
$validatorStringLength = new Zend_Validate_StringLength(3, 32); $validatorStringLength->setMessages(array( Zend_Validate_StringLength::TOO_SHORT => $translate->_('Your username have to be between 3 and 32 symbols long'), Zend_Validate_StringLength::TOO_LONG => $translate->_('Your username have to be between 3 and 32 symbols long'), ) );
If we go like this we get the translation at the time of the defining the message, and we DO NOT use the translator which we set for Zend_Form. In fact the translator will be used but the already translated strings will be used as messages to be again translated – and of course they won’t be in the messages database with the translations.
What we want is to have:
Zend_Validate_StringLength::TOO_SHORT => ‘Your username have to be between 3 and 32 symbols long’,
but this way Poedit won’t find our string. So here is my tricky solution to this. I introduce a function __($string) – these are two underscores. So I put in my bootstrap.php (for now, later all this auxiliary functions should go into some library, for now we have just 1 of these):
function __($string) { return $string; }
So the function just returns its argument.
Now some tips for Poedit. When we create the catalog in the Keywords tab we can add function names, from which to collect messages for translation in addition to the default _() function. Here I add __ – two underscores. Also I have added setLabel, but you can just use the __() function for the purposes of adding Labels to form elements also. Another thing you should do is to add .phtml file extension to the Parser setup for PHP files, so your template files are parsed actually. And finally – add ‘-L php’ to Parser command, so to become ‘xgettext –force-po -o %o %C %K %F -L php’. This prevents nasty error from showing.
Now back to our getRegistrationForm() and lets add the other fields (except the birthdate and the captcha) with desired validators and filters.
< ?php private function getRegistrationForm() { $form = new Zend_Form(); $form->setAction('/user/register')->setMethod('post')->setAttrib('id', 'register'); $filterTrim = new Zend_Filter_StringTrim(); $validatorNotEmpty = new Zend_Validate_NotEmpty(); $validatorNotEmpty->setMessage(__('This field is required, you cannot leave it empty')); $username = new Zend_Form_Element_Text('username'); $validatorAlnum = new Zend_Validate_Alnum(); $validatorAlnum->setMessage(__('You can use only latin letters and numbers')); $validatorStringLength = new Zend_Validate_StringLength(3, 32); $validatorStringLength->setMessages(array( Zend_Validate_StringLength::TOO_SHORT => __('Your username have to be between 3 and 32 symbols long'), Zend_Validate_StringLength::TOO_LONG => __('Your username have to be between 3 and 32 symbols long'), ) ); $username->addValidator($validatorNotEmpty, true)->setRequired(true)->setLabel('Username') ->addFilter($filterTrim) ->addValidator($validatorAlnum) ->addValidator($validatorStringLength); $form->addElement($username); /** * @todo Change this wired error messages to something more user friendly, or even use simple email regex matching validator */ $email = new Zend_Form_Element_Text('email'); $validatorHostname = new Zend_Validate_Hostname(); $validatorHostname->setMessages( array( Zend_Validate_Hostname::IP_ADDRESS_NOT_ALLOWED => __("'%value%' appears to be an IP address, but IP addresses are not allowed"), Zend_Validate_Hostname::UNKNOWN_TLD => __("'%value%' appears to be a DNS hostname but cannot match TLD against known list"), Zend_Validate_Hostname::INVALID_DASH => __("'%value%' appears to be a DNS hostname but contains a dash (-) in an invalid position"), Zend_Validate_Hostname::INVALID_HOSTNAME_SCHEMA => __("'%value%' appears to be a DNS hostname but cannot match against hostname schema for TLD '%tld%'"), Zend_Validate_Hostname::UNDECIPHERABLE_TLD => __("'%value%' appears to be a DNS hostname but cannot extract TLD part"), Zend_Validate_Hostname::INVALID_HOSTNAME => __("'%value%' does not match the expected structure for a DNS hostname"), Zend_Validate_Hostname::INVALID_LOCAL_NAME => __("'%value%' does not appear to be a valid local network name"), Zend_Validate_Hostname::LOCAL_NAME_NOT_ALLOWED => __("'%value%' appears to be a local network name but local network names are not allowed") ) ); $validatorEmail = new Zend_Validate_EmailAddress(Zend_Validate_Hostname::ALLOW_DNS, false, $validatorHostname); $validatorEmail->setMessages( array( Zend_Validate_EmailAddress::INVALID => __("'%value%' is not a valid email address"), Zend_Validate_EmailAddress::INVALID_HOSTNAME => __("'%hostname%' is not a valid hostname for email address '%value%'"), Zend_Validate_EmailAddress::INVALID_MX_RECORD => __("'%hostname%' does not appear to have a valid MX record for the email address '%value%'"), Zend_Validate_EmailAddress::DOT_ATOM => __("'%localPart%' not matched against dot-atom format"), Zend_Validate_EmailAddress::QUOTED_STRING => __("'%localPart%' not matched against quoted-string format"), Zend_Validate_EmailAddress::INVALID_LOCAL_PART => __("'%localPart%' is not a valid local part for email address '%value%'") ) ); $email->addValidator($validatorNotEmpty, true)->setRequired(true)->setLabel('Email Address') ->addFilter($filterTrim) ->addValidator($validatorEmail); $form->addElement($email); $password = new Zend_Form_Element_Password('password'); $password->addValidator($validatorNotEmpty, true)->setRequired(true)->setLabel('Password') ->addValidator(new Zend_Validate_StringLength(3)); $form->addElement($password); $password2 = new Zend_Form_Element_Password('password2'); $password2->setLabel('Confirm Password'); $form->addElement($password2); $gender = new Zend_Form_Element_Select('gender'); $gender->setLabel('Gender') ->addMultiOption('',' ')->addMultiOption('male',__('male'))->addMultiOption('female',__('female')); $form->addElement($gender); $realName = new Zend_Form_Element_Text('realname'); $realName->setLabel('Real Name')->addFilter($filterTrim); $form->addElement($realName); $validatorNotEmptyAgreement = new Zend_Validate_NotEmpty(); $validatorNotEmptyAgreement->setMessage(__('You have to accept our terms and conditions before you register')); $agreement = new Zend_Form_Element_Checkbox('agreement'); $agreement->setLabel('I agree to terms and conditions') ->addValidator($validatorNotEmptyAgreement, true)->setRequired(true); $form->addElement($agreement); $submit = new Zend_Form_Element_Submit('register'); $submit->setLabel('Submit'); $form->addElement($submit); return $form; }
Test the form. Everything should work, except the password confirmation field. We need a way to check if password2 is the same as password. So we will write our own validator, extending Zend_Validate_Abstract. We will name our validator My_Validate_PasswordConfirmation, and it will be located in ‘library/My/Validate/PasswordConfirmation.php‘ file.
< ?php require_once 'Zend/Validate/Abstract.php'; class My_Validate_PasswordConfirmation extends Zend_Validate_Abstract { const NOT_MATCH = 'passwordConfirmationNotMatch'; protected $_messageTemplates = array( self::NOT_MATCH => "Passwords do not match" ); protected $fieldToMatch = ''; public function __construct($fieldToMatch) { $this->fieldToMatch = (string) $fieldToMatch; } public function isValid($value, $context = null) { $valueString = (string) $value; $this->_setValue($valueString); if (!isset($context[$this->fieldToMatch]) || $context[$this->fieldToMatch] !== $valueString) { $this->_error(self::NOT_MATCH); return false; } return true; } }
The constructor of the validator has one argument – the name of the field in the form, which value our validating field should match. The isValid() method uses the $context parameter, which is passed by the Zend_Form isValid() method and contains the whole $_POST array. So we just check if the value of our validating field matches the value of the field, given in the constructor. So back in our getRegistrationForm():
$password2 = new Zend_Form_Element_Password('password2'); $validatorPassword = new My_Validate_PasswordConfirmation('password'); $validatorPassword->setMessage(__('Passwords do not match')); $password2->setLabel('Confirm Password')->addValidator($validatorPassword); $form->addElement($password2);
and now password confirmation works too.
Two intersting things remain to be done: birthday field and CAPTCHA.
First lets look at the birthday field. We can always use simple text field here, telling the user to enter a date in the format “YYYY-MM-DD”, then validate this with Zend_Validate_Date and hope that our users will understand what we mean with this “YYYY-MM-DD”
I don’t like this. Another approach is to use some fancy date picker, which fills hidden field with our desired format. This is better. But what I want here is three selects – for day, month and year. But I want this 3 selects to work as one field in the form. So I can tell something like $date = new Zend_Form_Element_Date(‘date’). Well, I use ‘My_’ naming convention for my library code, se the name will be My_Form_Element_DateSelects. In getRegistrationForm(), after our gender field, lets add this:
$date = new My_Form_Element_DateSelects('birthdate'); $validatorDate = new Zend_Validate_Date(); $validatorDate->setMessages( array( Zend_Validate_Date::NOT_YYYY_MM_DD => __("'%value%' is not of the format YYYY-MM-DD"), Zend_Validate_Date::INVALID => __("'%value%' does not appear to be a valid date"), Zend_Validate_Date::FALSEFORMAT => __("'%value%' does not fit given date format") ) ); $date->setLabel('Birthdate')->addValidator($validatorDate); $date->setShowEmptyValues(true)->setStartEndYear(1900, date("Y")-7)->setReverseYears(true); $form->addElement($date);
Our custom field will support several options
- showEmpty: if true, will add empty options at the top of the select, so allowing the user not to select any date.
- startYear and endYear – just the range of the year select
- reverseYears – if true – the years will be printed in reverse order.
Here is our My_Form_Element_DateSelects class:
‘library/My/Form/Element/DateSelects.php‘
< ?php require_once 'Zend/Form/Element.php'; class My_Form_Element_DateSelects extends Zend_Form_Element_Xhtml { /** * Use formSelect view helper by default * @var string */ public $helper = 'formDateSelects'; /** * This array will hold options: * showEmpty - bool, if true will show and allow empty date * startYear, endYear - start and end year to show * reverseYears - if true - years will be print from most recent backwards * * Zend_Form_Decorator_ViewHelper will pass this array as argument to the * view helper, responsible for rendering this element * * @var array */ public $options = array(); public function init() { $this->options['showEmpty'] = true; $this->options['startYear'] = 1900; $this->options['endYear'] = (int) date("Y"); $this->options['reverseYears'] = false; } public function setShowEmptyValues($value) { $this->options['showEmpty'] = (bool) $value; return $this; } public function setStartEndYear($start = null, $end = null) { if ($start) { $this->options['startYear'] = (int) $start; } if ($end) { $this->options['endYear'] = (int) $end; } return $this; } public function setReverseYears($value) { $this->options['reverseYears'] = (bool) $value; return $this; } /** * We want to get the date from our auxiliary fields here * * @param mixed $value * @param mixed $context * @return boolean */ public function isValid($value, $context = null) { $fieldName = $this->getName(); $auxiliaryFieldsNames = $this->getDayMonthYearFieldNames($fieldName); if (isset($context[$auxiliaryFieldsNames['day']]) && isset($context[$auxiliaryFieldsNames['month']]) && isset($context[$auxiliaryFieldsNames['year']])) { if ($context[$auxiliaryFieldsNames['year']] == '-' || $context[$auxiliaryFieldsNames['month']] == '-' || $context[$auxiliaryFieldsNames['day']] == '-') { $value = null; } else { $value = str_pad($context[$auxiliaryFieldsNames['year']], 4, '0', STR_PAD_LEFT) . '-' . str_pad($context[$auxiliaryFieldsNames['month']], 2, '0', STR_PAD_LEFT) . '-' . str_pad($context[$auxiliaryFieldsNames['day']], 2, '0', STR_PAD_LEFT); } $this->setValue($value); } return parent::isValid($value, $context); } /** * Makes day, month and year names from given element name. Special case is array notation. * * Given a value such as foo[bar][baz], the generated names will be * foo[bar][baz_day], foo[bar][baz_month] and foo[bar][baz_year] * I know it is bad design to have this function here and in the View Helper, * but I really can't think of other way * * @param string $value * @return array */ protected function getDayMonthYearFieldNames($value) { if (empty($value) || !is_string($value)) { return $value; } $ret = array( 'day' => $value . '_day', 'month' => $value . '_month', 'year' => $value . '_year' ); if (strstr($value, '[')) { $endPos = strlen($value) - 1; if (']' != $value[$endPos]) { return $ret; } $start = strrpos($value, '[') + 1; $name = substr($value, $start, $endPos - $start); $arrayName = substr($value, 0, $start-1); $ret = array( 'day' => $arrayName . '[' . $name . '_day' . ']', 'month' => $arrayName . '[' . $name . '_month' . ']', 'year' => $arrayName . '[' . $name . '_year' . ']' ); } return $ret; } }
and the corresponding view helper for rendering it:
‘library/My/View/Helper/FormDateSelect.php‘
< ?php require_once 'Zend/View/Helper/FormElement.php'; class My_View_Helper_FormDateSelects extends Zend_View_Helper_FormElement { public function formDateSelects($name, $value = null, $attribs = null, $options = null, $listsep = "\n") { $info = $this->_getInfo($name, $value, $attribs, $options, $listsep); extract($info); // name, id, value, attribs, options, listsep, disable // now start building the XHTML. $disabled = ''; if (true === $disable) { $disabled = ' disabled="disabled"'; } $elementNamesArray = $this->getDayMonthYearFieldNames($name); $valueDay = $valueMonth = $valueYear = null; if ($value !== null) { $valueExploded = explode('-', $value); if (!isset($valueExploded[2])) $value = null; else { $valueDay = (int) $valueExploded[2]; $valueMonth = (int) $valueExploded[1]; $valueYear = (int) $valueExploded[0]; } } // Build the surrounding day element first. $xhtml = ' <select id="' . $this->view->escape($id . '_day') . '" name="' . $this->view->escape($elementNamesArray['day']) . '">_htmlAttribs($attribs) . ">\n "; // build the list of options $list = array(); if ($options['showEmpty']) { $list[] = '<option value="-"> </option>'; } for ($i = 1; $i < = 31; $i++) { $list[] = '' . $i . ''; } // add the options to the xhtml and close the select $xhtml .= implode("\n ", $list) . "\n</select> "; // Build the month next $xhtml .= ' <select id="' . $this->view->escape($id . '_month') . '" name="' . $this->view->escape($elementNamesArray['month']) . '">_htmlAttribs($attribs) . ">\n "; // build the list of options $list = array(); if ($options['showEmpty']) { $list[] = '<option value="-"> </option>'; } for ($i = 1; $i < = 12; $i++) { $list[] = '' . $i . ''; } // add the options to the xhtml and close the select $xhtml .= implode("\n ", $list) . "\n</select> "; // Build the years next $xhtml .= ' <select id="' . $this->view->escape($id . '_year') . '" name="' . $this->view->escape($elementNamesArray['year']) . '">_htmlAttribs($attribs) . ">\n "; // build the list of options $list = array(); if ($options['showEmpty']) { $list[] = '<option value="-"> </option>'; } if ($options['reverseYears']) { for ($i = $options['endYear']; $i >= $options['startYear']; $i--) { $list[] = '<option selected="selected" value="' . $i . '">' . $i . '</option>'; } } else { for ($i = $options['startYear']; $i >= $options['endYear']; $i++) { $list[] = '<option selected="selected" value="' . $i . '">' . $i . '</option>'; } } // add the options to the xhtml and close the select $xhtml .= implode("\n ", $list) . "\n</select> "; return $xhtml; } /** * Makes day, month and year names from given element name. Special case is array notation. * * Given a value such as foo[bar][baz], the generated names will be * foo[bar][baz_day], foo[bar][baz_month] and foo[bar][baz_year] * * @param string $value * @return array */ protected function getDayMonthYearFieldNames($value) { if (empty($value) || !is_string($value)) { return $value; } $ret = array( 'day' => $value . '_day', 'month' => $value . '_month', 'year' => $value . '_year' ); if (strstr($value, '[')) { $endPos = strlen($value) - 1; if (']' != $value[$endPos]) { return $ret; } $start = strrpos($value, '[') + 1; $name = substr($value, $start, $endPos - $start); $arrayName = substr($value, 0, $start-1); $ret = array( 'day' => $arrayName . '[' . $name . '_day' . ']', 'month' => $arrayName . '[' . $name . '_month' . ']', 'year' => $arrayName . '[' . $name . '_year' . ']' ); } return $ret; } }
what is interesting is the getDayMonthYearFieldNames() method, which constructs the names for the three “subfields” of our date field. If the name of the data field is array based – we make sure our subfields to have according names. We duplicate the function in both the view helper and the element class itself. May be it is better decision to make this function outside these classes and make it independant, so we avoid the code duplication, but I will go with this design just for now.
In My_Form_Element_DateSelects – isValid() method – we construct the “YYYY-MM-DD” format of the date, getting the auxiliary day, month and year values from the $context (we have all submitted values here). Then we just call parent::isValid() method with our new constructed value.
One last thing remains for this to work: adding the path to our view helper to the view object. This we will do in the bootstrap, so add there:
$view->addHelperPath('My/View/Helper', 'My_View_Helper_');
just after constructing the view object.
And finally – the CAPTCHA. We will generate the challange text in the user controller and will save it to session. We can show it again if not matched and take it from the session in our captcha controller, so we render the same challange text that we generated
. The code is quite straightforward. Lets add new controller – CaptchaController.
‘application/modules/default/controllers/CaptchaController.php‘
< ?php class CaptchaController extends Zend_Controller_Action { public function init() { $this->_helper->layout->disableLayout(); $this->_helper->viewRenderer->setNoRender(); } public function getAction() { $parameters = $this->_getAllParams(); $width = 150; $height = 40; $image = ImageCreate($width, $height); if (isset($parameters['namespace']) && isset($parameters['captchaId'])) { $session = new Zend_Session_Namespace($parameters['namespace']); $text = $session->{$parameters['captchaId']}; if ($text) { $grey = ImageColorAllocate($image, 190, 190, 190); $black = ImageColorAllocate($image, 0, 0, 0); ImageString($image, 20, 50, 15, $text, $black); } } $this->_response->setHeader('Content-Type', 'image/gif'); imagegif($image); } }
In init() we disable the layout and tell the view renderer to not render.
We get as parameters the session namespace and the session key where the challange text is saved, then create new GD image, write the text in it, set content type in header to ‘image/gif’ and writing the gif image. Zend_Controller_Dispatcher_Standard which we use implicitly uses output buffering, so this gif image is captured as response body, and sent to the client from our bootstrap.php file.
To show this captcha image on our form we will use decorator, so we write
‘library/My/Form/Decorator/Captcha.php‘:
< ?php require_once 'Zend/Form/Decorator/Abstract.php'; /** * My_Form_Decorator_Captcha * * Shows CAPTCHA image * * Options that must be provided are: * - namespace: The id of the captcha to show (the key in the session namespace, where the value is saved) * - captchaId: The id of the captcha to show (the key in the session namespace, where the value is saved) * - tag: tag to use in decorator */ class My_Form_Decorator_Captcha extends Zend_Form_Decorator_Abstract { /** * Default placement: append * @var string */ protected $_placement = 'PREPEND'; /** * HTML tag with which to surround image * @var string */ protected $_tag; /** * Set HTML tag with which to surround image * * @param string $tag * @return My_Form_Decorator_Captcha */ public function setTag($tag) { $this->_tag = (string) $tag; return $this; } /** * Get HTML tag, if any, with which to surround image * * @return void */ public function getTag() { if (null === $this->_tag) { $tag = $this->getOption('tag'); if (null !== $tag) { $this->removeOption('tag'); $this->setTag($tag); } return $tag; } return $this->_tag; } /** * Render a captcha image * * @param string $content * @return string */ public function render($content) { $element = $this->getElement(); $view = $element->getView(); if (null === $view) { return $content; } $tag = $this->getTag(); $placement = $this->getPlacement(); $separator = $this->getSeparator(); $namespace = $this->getOption('namespace'); $captchaId = $this->getOption('captchaId'); if (!$namespace || !$captchaId) { require_once ('Zend/Form/Decorator/Exception.php'); $exception = new Zend_Form_Decorator_Exception('namespace or captchaId not set'); throw $exception; } $image = '<img src="/captcha/get/'.$namespace.'/'.$captchaId.'" alt="CAPTCHA challange" />'; if (null !== $tag) { require_once 'Zend/Form/Decorator/HtmlTag.php'; $decorator = new Zend_Form_Decorator_HtmlTag(); $decorator->setOptions(array('tag' => $tag)); $image = $decorator->render($image); } switch ($placement) { case self::PREPEND: return $image . $separator . $content; case self::APPEND: default: return $content . $separator . $image; } } }
As template for this decorator I have used Zend_Form_Decorator_Image, so there are snippets of code taken from there.
In order to use shorter URLs like /captcha/get/[namespace]/[captchaId], and not like /captcha/get/namespace/[namespace]/captchaId/[captchaId] we need to define custom route in our router, so in our bootstrap.php:
// define some routes (URLs) $router = $frontController->getRouter(); $captchaRoute = new Zend_Controller_Router_Route('captcha/get/:namespace/:captchaId', array('controller'=>'captcha', 'action'=>'get') ); $router->addRoute('captcha', $captchaRoute);
and now add the captcha field to the form:
in getRegistrationForm():
if (!isset($this->session->passedRegisterCaptcha) || !$this->session->passedRegisterCaptcha) { //if we have set captcha in the session for this request - use it, else generate new one if (isset($this->session->registerCaptcha)) { $captchaCode = $this->session->registerCaptcha; } else { $md5Hash = md5($_SERVER['REQUEST_TIME']); $captchaCode = substr($md5Hash, rand(0, 25), 5); $this->session->registerCaptcha = $captchaCode ; } $captcha = new Zend_Form_Element_Text('captcha'); $captcha->setLabel('Enter the text') ->addValidator(new Zend_Validate_Identical($captchaCode)) ->addValidator($validatorNotEmpty, true)->setRequired(true); $captchaDecorator = new My_Form_Decorator_Captcha(); $captchaDecorator->setOption('namespace', 'User')->setOption('captchaId', 'registerCaptcha'); $captchaDecorator->setTag('div'); $captcha->addDecorator($captchaDecorator); $form->addElement($captcha); }
so here is the whole UserContoller.php:
< ? require_once 'Zend/Controller/Action.php'; class UserController extends Zend_Controller_Action { /** * Session namespace object for user data * * @var Zend_Session_Namespace */ public $session = null; public function init() { $this->session = new Zend_Session_Namespace('User'); $this->view->translate = Zend_Registry::get('Zend_Translate'); } public function indexAction() { $this->_forward('register'); } public function loginAction() { } public function registerAction() { $this->view->registerForm = $this->getRegistrationForm(); if ($this->getRequest()->isPost()) { if ($this->view->registerForm->isValid($_POST)) { $values = $this->view->registerForm->getValues(); $this->view->values = $values; } //we want to know if we have passed the captcha, so won't show it again: if (!isset($this->session->passedRegisterCaptcha) || !$this->session->passedRegisterCaptcha) { $captchaField = $this->view->registerForm->captcha; if ($captchaField->isValid($captchaField->getValue())) { $this->session->passedRegisterCaptcha = true; $this->view->registerForm->removeElement('captcha'); } } } } public function lostpasswordAction() { } public function activateAction() { } public function profileAction() { } private function getRegistrationForm() { $form = new Zend_Form(); $form->setAction('/user/register')->setMethod('post')->setAttrib('id', 'register'); $filterTrim = new Zend_Filter_StringTrim(); $validatorNotEmpty = new Zend_Validate_NotEmpty(); $validatorNotEmpty->setMessage(__('This field is required, you cannot leave it empty')); $username = new Zend_Form_Element_Text('username'); $validatorAlnum = new Zend_Validate_Alnum(); $validatorAlnum->setMessage(__('You can use only latin letters and numbers')); $validatorStringLength = new Zend_Validate_StringLength(3, 32); $validatorStringLength->setMessages(array( Zend_Validate_StringLength::TOO_SHORT => __('Your username have to be between 3 and 32 symbols long'), Zend_Validate_StringLength::TOO_LONG => __('Your username have to be between 3 and 32 symbols long'), ) ); $username->addValidator($validatorNotEmpty, true)->setRequired(true)->setLabel('Username') ->addFilter($filterTrim) ->addValidator($validatorAlnum) ->addValidator($validatorStringLength); $form->addElement($username); /** * @todo Change this wired error messages to something more user friendly, or even use simple email regex matching validator */ $email = new Zend_Form_Element_Text('email'); $validatorHostname = new Zend_Validate_Hostname(); $validatorHostname->setMessages( array( Zend_Validate_Hostname::IP_ADDRESS_NOT_ALLOWED => __("'%value%' appears to be an IP address, but IP addresses are not allowed"), Zend_Validate_Hostname::UNKNOWN_TLD => __("'%value%' appears to be a DNS hostname but cannot match TLD against known list"), Zend_Validate_Hostname::INVALID_DASH => __("'%value%' appears to be a DNS hostname but contains a dash (-) in an invalid position"), Zend_Validate_Hostname::INVALID_HOSTNAME_SCHEMA => __("'%value%' appears to be a DNS hostname but cannot match against hostname schema for TLD '%tld%'"), Zend_Validate_Hostname::UNDECIPHERABLE_TLD => __("'%value%' appears to be a DNS hostname but cannot extract TLD part"), Zend_Validate_Hostname::INVALID_HOSTNAME => __("'%value%' does not match the expected structure for a DNS hostname"), Zend_Validate_Hostname::INVALID_LOCAL_NAME => __("'%value%' does not appear to be a valid local network name"), Zend_Validate_Hostname::LOCAL_NAME_NOT_ALLOWED => __("'%value%' appears to be a local network name but local network names are not allowed") ) ); $validatorEmail = new Zend_Validate_EmailAddress(Zend_Validate_Hostname::ALLOW_DNS, false, $validatorHostname); $validatorEmail->setMessages( array( Zend_Validate_EmailAddress::INVALID => __("'%value%' is not a valid email address"), Zend_Validate_EmailAddress::INVALID_HOSTNAME => __("'%hostname%' is not a valid hostname for email address '%value%'"), Zend_Validate_EmailAddress::INVALID_MX_RECORD => __("'%hostname%' does not appear to have a valid MX record for the email address '%value%'"), Zend_Validate_EmailAddress::DOT_ATOM => __("'%localPart%' not matched against dot-atom format"), Zend_Validate_EmailAddress::QUOTED_STRING => __("'%localPart%' not matched against quoted-string format"), Zend_Validate_EmailAddress::INVALID_LOCAL_PART => __("'%localPart%' is not a valid local part for email address '%value%'") ) ); $email->addValidator($validatorNotEmpty, true)->setRequired(true)->setLabel('Email Address') ->addFilter($filterTrim) ->addValidator($validatorEmail); $form->addElement($email); $password = new Zend_Form_Element_Password('password'); $password->addValidator($validatorNotEmpty, true)->setRequired(true)->setLabel('Password') ->addValidator(new Zend_Validate_StringLength(3)); $form->addElement($password); $password2 = new Zend_Form_Element_Password('password2'); $validatorPassword = new My_Validate_PasswordConfirmation('password'); $validatorPassword->setMessage(__('Passwords do not match')); $password2->setLabel('Confirm Password')->addValidator($validatorPassword); $form->addElement($password2); $gender = new Zend_Form_Element_Select('gender'); $gender->setLabel('Gender') ->addMultiOption('',' ')->addMultiOption('male',__('male'))->addMultiOption('female',__('female')); $form->addElement($gender); $date = new My_Form_Element_DateSelects('birthdate'); $validatorDate = new Zend_Validate_Date(); $validatorDate->setMessages( array( Zend_Validate_Date::NOT_YYYY_MM_DD => __("'%value%' is not of the format YYYY-MM-DD"), Zend_Validate_Date::INVALID => __("'%value%' does not appear to be a valid date"), Zend_Validate_Date::FALSEFORMAT => __("'%value%' does not fit given date format") ) ); $date->setLabel('Birthdate')->addValidator($validatorDate); $date->setShowEmptyValues(true)->setStartEndYear(1900, date("Y")-7)->setReverseYears(true); $form->addElement($date); $realName = new Zend_Form_Element_Text('realname'); $realName->setLabel('Real Name')->addFilter($filterTrim); $form->addElement($realName); $validatorNotEmptyAgreement = new Zend_Validate_NotEmpty(); $validatorNotEmptyAgreement->setMessage(__('You have to accept our terms and conditions before you register')); $agreement = new Zend_Form_Element_Checkbox('agreement'); $agreement->setLabel('I agree to terms and conditions') ->addValidator($validatorNotEmptyAgreement, true)->setRequired(true); $form->addElement($agreement); if (!isset($this->session->passedRegisterCaptcha) || !$this->session->passedRegisterCaptcha) { //if we have set captcha in the session for this request - use it, else generate new one if (isset($this->session->registerCaptcha)) { $captchaCode = $this->session->registerCaptcha; } else { $md5Hash = md5($_SERVER['REQUEST_TIME']); $captchaCode = substr($md5Hash, rand(0, 25), 5); $this->session->registerCaptcha = $captchaCode ; } $captcha = new Zend_Form_Element_Text('captcha'); $captcha->setLabel('Enter the text') ->addValidator(new Zend_Validate_Identical($captchaCode)) ->addValidator($validatorNotEmpty, true)->setRequired(true); $captchaDecorator = new My_Form_Decorator_Captcha(); $captchaDecorator->setOption('namespace', 'User')->setOption('captchaId', 'registerCaptcha'); $captchaDecorator->setTag('div'); $captcha->addDecorator($captchaDecorator); $form->addElement($captcha); } $submit = new Zend_Form_Element_Submit('register'); $submit->setLabel('Submit'); $form->addElement($submit); return $form; } }
and the whole bootstrap.php:
< ?php // For our dev environment we will report all errors to the screen error_reporting(E_ALL | E_STRICT); ini_set('display_startup_errors', 1); ini_set('display_errors', 1); // Set our timezone date_default_timezone_set('Europe/Sofia'); // Add /library and /application directory to our include path $siteRootDir = dirname($_SERVER['DOCUMENT_ROOT']); set_include_path( $siteRootDir . '/library' . PATH_SEPARATOR . $siteRootDir . '/application' . PATH_SEPARATOR . get_include_path() ); // Turn on autoloading, so we do not include each Zend Framework class require_once 'Zend/Loader.php'; Zend_Loader::registerAutoload(); // Create registry object and setting it as the static instance in the Zend_Registry class $registry = new Zend_Registry(); Zend_Registry::setInstance($registry); // Load configuration file and store the data in the registry $configuration = new Zend_Config_Ini($siteRootDir . '/configuration/config.ini', 'main'); Zend_Registry::set('configuration', $configuration); // Construct the database adapter class, connect to the database and store the db object in the registry $db = Zend_Db::factory($configuration->db); $db->query("SET NAMES 'utf8'"); Zend_Registry::set('db', $db); // set this adapter as default for use with Zend_Db_Table Zend_Db_Table_Abstract::setDefaultAdapter($db); // Now set session save handler to our custom class which saves the data in MySQL database $sessionManager = new My_Session_Manager(); Zend_Session::setOptions(array( 'gc_probability' => 1, 'gc_divisor' => 5000 )); Zend_Session::setSaveHandler($sessionManager); //we will always use session, so this is good place to create this and save it to the registry $defSession = new Zend_Session_Namespace('Default', true); Zend_Registry::set('defSession', $defSession); // Setup translation adapter // Check if language is set in session - if not - use english as default $lang = 'en'; if (isset($defSession->lang)) { $lang = $defSession->lang; } $translate = new Zend_Translate('gettext', $siteRootDir . '/languages/'.$lang.'.mo', $lang); Zend_Registry::set('Zend_Translate', $translate); Zend_Form::setDefaultTranslator($translate); // Setup the Front Controller, disable the error handler, set our controller directories $frontController = Zend_Controller_Front::getInstance(); $frontController->throwExceptions(true); $frontController->addModuleDirectory($siteRootDir . '/application/modules'); //we want the front controller to return the response, instead of emitting it automatically $frontController->returnResponse(true); // define some routes (URLs) $router = $frontController->getRouter(); $captchaRoute = new Zend_Controller_Router_Route('captcha/get/:namespace/:captchaId', array('controller'=>'captcha', 'action'=>'get') ); $router->addRoute('captcha', $captchaRoute); /* * We want to set the encoding to UTF-8, so we won't rely on the ViewRenderer action helper by default, * but will construct view object and deliver it to the ViewRenderer after setting some options. */ $view = new Zend_View(array('encoding'=>'UTF-8')); //$view->addHelperPath($siteRootDir . '/library/My/View/Helper', 'My_View_Helper'); $view->addHelperPath('My/View/Helper', 'My_View_Helper_'); $viewRendered = new Zend_Controller_Action_Helper_ViewRenderer($view); Zend_Controller_Action_HelperBroker::addHelper($viewRendered); // Now we initialize the Zend_Layout object with MVC support Zend_Layout::startMvc( array( 'layoutPath' => $siteRootDir . '/application/layouts', 'layout' => 'main' ) ); // run the dispatch, get the response and send it to the client $response = $frontController->dispatch(); $response->sendResponse(); function __($string) { return $string; }
A lot of new stuff we covered today, here is the part4.rar file with the current version of the project.













Brian Steele | 24-Jun-08 at 8:49 am | Permalink
I am fairly new to php and I would just like to say thank you. I am enjoying this series very much. It has greatly increased my understanding of the Zend Framework. I hope you continue to post updates with added functionality.
Peter Damianov | 03-Jul-08 at 9:01 am | Permalink
Great job on this tutorial!
I only have one suggestion regarding the password validator. We could use a more general validator – one that would compare whether strings are equal without the constraints of providing just two fields. This way we can use it in implementing other cases besides password verification.
So a sample code would look like this:
“Strings do not match”
);
public function isValid($value) {
$this->_setValue($value);
if(count(array_unique($value)) !== 1) {
$this->_error(self::NO_STRING_MATCH);
return false;
}
return true;
}
}
and the use of the validator will take advantage of the ZF FIELDS metacommand:
$validator = array(
‘password’ => array(‘StrEquals’,'fields’ => array(‘original_password_field’,'confirm_password_field’)
)
);
Keep up with the good work!
Peter Damianov | 03-Jul-08 at 9:04 am | Permalink
Hmmm, it seems that it cut out a portion of the code. I hope this time it stays:
class My_Validate_StrEquals extends Zend_Validate_Abstract {
const NO_STRING_MATCH = ‘noStringMatch’;
protected $_messageTemplates = array(
self::NO_STRING_MATCH => “Strings do not match”
);
admin | 03-Jul-08 at 3:57 pm | Permalink
Hi Peter,
When we use Zend_Filter_Input for validation it is possible to use your method – with metacommand to pass more than one field to the validator. But when we add validators to the Form Element itself and we use the “Magic” behaviour of the form object isValid method – to call all validators of the form elements – we can’t use metacommands. At least as far as I know..
Klaas van der Weij | 11-Jul-08 at 4:36 pm | Permalink
I stubbled uppon a strange effect because of the usage of:
. . public function __construct($fieldToMatch)
. . {
. . . . $this->fieldToMatch = (string) $fieldToMatch;
. . }
This causes the $fieldToMatch to call the function render(), and instead of the default “” tags arround the element, “” tags occured. Strange behavior …
We don’t want that to happen so I used the function getValue() as in:
. . public function __construct($fieldToMatch)
. . {
. . . . $this->fieldToMatch = $fieldToMatch->getValue();
. . }
Klaas van der Weij | 11-Jul-08 at 4:50 pm | Permalink
retification: (some tags don’t show when posted)
[...]
This causes the $fieldToMatch to call the function render(), and instead of the default ‘< dd >’ tags arround the element, < div > tags occured. Strange behavior …
[...]
Klaas van der Weij | 11-Jul-08 at 5:47 pm | Permalink
Ok, sorry, and this will be my final post, but the constructor of the PasswordConfirmation should (I suppose) be:
. . public function __construct($fieldToMatch)
. . {
. . . . $this->fieldToMatch = $fieldToMatch->getName();
. . }
admin | 11-Jul-08 at 8:21 pm | Permalink
Hi Klaas,
my idea was the parameter to the constructor to be the field ID itself, as it is seen from this snippet:
$validatorPassword = new My_Validate_PasswordConfirmation(‘password’);
It is my fault, that I haven’t made this clear in the text.
Alex | 15-Aug-08 at 4:11 pm | Permalink
Is there a way to place the captcha image between the label and the captcha input field?
viperx | 16-Aug-08 at 6:32 pm | Permalink
Hi Alex,
yes of course this can be done. In fact I do not mess with the design of the things, this is why I do not include example of replacing the default decorators. But anyway here is how to do it:
prevent loading of the default decorators for this element:
and then place the captcha decorator right after the ViewHelper one – the default placement is PREPEND so the rendering will be just before the text field.
mike | 22-Aug-08 at 1:57 am | Permalink
Once more thanks for this fine tutorial. I am following this tutorial with a slightly different folder structure that works fine in displaying the index page with all the styling from your css.
I however have a problem when i do the UserController class. How do i get to display the registration form from my browser (firefox)? I have done all the above code and all that i am able to view is the home page ie main.phtml. I thought$thid_forward(‘register’) would force the registration page to display! At least i need to test the registration form at this stage.
mike | 22-Aug-08 at 1:59 am | Permalink
sorry i meant to say
$this_forward(’register’) would force the registration page to display! At least i need to test the registration form at this stage.
viperx | 22-Aug-08 at 12:05 pm | Permalink
Hi mike, if you can’t view the registration (which is in UserControler, register action) – http://localhost/user/register, then the chances are that your router is not working correct (may be you haven’t told Zend Framework about your different directory structure?).
I use the default Zend_Controller_Router_Rewrite router. For this to work it is important to write some rewrite rules for the apache: see the .htaccess file
RewriteEngine on RewriteCond %{SCRIPT_FILENAME} !-f RewriteCond %{SCRIPT_FILENAME} !-d RewriteRule ^(.*)$ index.php/$1Then you should tell Zend Framework where are the files for the default module. I’m using directory structure where I have /application/modules directory and subdirectory there for each module, so I use
You should read the manual page for FrontController and find a way to describe your directory structure.
mike | 27-Aug-08 at 12:00 am | Permalink
My folder structure works fine and my router works ok as well. I have tested these with other examples.
At what stage do i get to see the forms? All i can see is the main page with it’s bluish backgound and all the styles applied. I disenabled the router and an eror message was thrown, a proof that the router is working.
I am at a loss. How and when does the indexController pass on parameters/functions to the userController? I feel there is a way/link that is misssing. Or may be with this example what is the user expected to see before registering or logging in. May be i have a wrong assumption altogether. Can we also have the code as .zip files which is freely available?
Mike
viperx | 27-Aug-08 at 4:28 pm | Permalink
Hi mike,
you can use for example 7zip to unrar files: http://www.7-zip.org/
As for why you can’t see the registration page, I really can’t tell you without seeing your code. It is obvious that something is not as it should be. If you just take the UserController.php file from my project and paste it in your folder for controllers and make the same with the view templates I think that everything should work…
“How and when does the indexController pass on parameters/functions to the userController” – this part of your comment I can’t understand.
Wave | 11-Sep-08 at 11:22 pm | Permalink
I followed this but put my reg form in the models dir as frmRegister.php. It loads correctly. However I get the following upon execution:
1) No captcha image displayed – I musta missed something here
2) Errors:
Notice: Indirect modification of overloaded property frmRegister::$session has no effect in /var/web/application/models/frmRegister.php on line 108
Strict Standards: Creating default object from empty value in /var/web/application/models/frmRegister.php on line 108
The form at 108 is:
// If we have set captcha in the session for this request – use it, else generate new one
if (isset($this->session->registerCaptcha))
{
$captchaCode = $this->session->registerCaptcha;
}
else
{
$md5Hash = md5($_SERVER['REQUEST_TIME']);
$captchaCode = substr($md5Hash, rand(0, 25), 5);
(108) $this->session->registerCaptcha = $captchaCode ;
}
viperx | 12-Sep-08 at 9:35 am | Permalink
Hi Wave,
I can’t really tell what is the problem, because when changing something many other things should be taken in mind, but I can try to guess what had happened:
you have made a class for the form in the file:
frmRegister.php
but later you use $this->session without you initialize this with the actual session variable in your constructor (or init())
What I can advise you is to have a look at version 1.6 of Zend Framework, where CAPTCHA component is shipped and is very easy to use. It is a little different from my approach, generating the image as .png file and saving it in a directory, while I serve the image on the fly.
Wave | 12-Sep-08 at 2:45 pm | Permalink
Thanx for the quick response. I was just looking at that actually. I will likely still use a portion of what you done here, but integrate the Zend_Captcha.
Wave | 12-Sep-08 at 5:48 pm | Permalink
I got it working using Zend_Captcha. Not alot of good references to it on the web yet and the documentation has two different approaches with no concrete examples. A little perserverence and its working now.
Ahmed El Talkhawy | 19-Nov-08 at 3:30 pm | Permalink
When i point the browser to
my_web_app_dir/public/user/login
i see that it almost dump the UserController.php, and give me exception
Fatal error: Uncaught exception ‘Zend_Controller_Dispatcher_Exception’ with message ‘Invalid controller class (“UserController”)’ in C:\wamp\www\library\Zend\Controller\Dispatcher\Standard.php:345 Stack trace: #0 C:\wamp\www\library\Zend\Controller\Dispatcher\Standard.php(255): Zend_Controller_Dispatcher_Standard->loadClass(‘UserController’) #1 C:\wamp\www\library\Zend\Controller\Front.php(934): Zend_Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response_Http)) #2 C:\wamp\www\test\zend\test02\application\bootstrap.php(105): Zend_Controller_Front->dispatch() #3 C:\wamp\www\test\zend\test02\public\index.php(3): require(‘C:\wamp\www\tes…’) #4 {main} thrown in C:\wamp\www\library\Zend\Controller\Dispatcher\Standard.php on line 345
why did i have this error although i have modified only the configuration and bootstrap files to a?
viperx | 19-Nov-08 at 5:40 pm | Permalink
Hi Ahmed,
by changing the bootstrap file may be you have changed something from the directory structure we use. What I have used is just an example and possible kick-start scenario, but you should understand all aspects of how zend framework front controller work, what are modules and how to use them to divide your action controller, what are the possible ways to organize your directory structure and then you will have the possibility to choose which directory structure is best for your needs and you will be able to configure zend framework correct to use your desired directory structure.
What is strange in your example is the URL:
my_web_app_dir/public/user/login
why is this /public/ there?
uylam | 22-Dec-08 at 11:07 pm | Permalink
Hi Ahmed,
Check if there is “php” in the first line of the UserController.php. The one in part4.rar does not have the “php”. It should be
<?php
boobalan | 27-Dec-08 at 10:14 am | Permalink
Hi to all,
I’m trying this code hardly for hours
but i can’t get to the registration form. how should i get the form what is the URL for getting it?
then i use same code (using rar files) but don’t know how to navigate
help me pls!
john | 30-Dec-08 at 10:16 pm | Permalink
how can I set captcha background ?
viperx | 05-Jan-09 at 5:46 pm | Permalink
Hi boobalan,
the registration is in the user controller, register action, so you access it by:
/user/register
viperx | 05-Jan-09 at 5:50 pm | Permalink
Hi john, see
http://www.php.net/manual/en/function.imagecolorallocate.php
there it says “Note: The first call to imagecolorallocate() fills the background color in palette-based images ”
so we set the background here:
$grey = ImageColorAllocate($image, 190, 190, 190);
change this to
$red = ImageColorAllocate($image, 255, 0,0);
and your background will be red
likke | 06-Feb-09 at 3:33 pm | Permalink
Hello,
I need some of your guru advices on the captcha image. I’m using the library from 1.7.4 and I followed each and every step of this guide. Thank you so much for this by the way.
All seems to work well except that when I finished this page of instructions, the site I’m setting up doesn’t show the image except for just a black box.
Strict Standards: Creating default object from empty value in /home/…/application/modules/default/controllers/UserController.php on line 163
The code that it meant was this:
$this->session->registerCaptcha = $captchaCode;
Any ideas on this?
Khaled Jouda | 06-Mar-09 at 2:18 am | Permalink
Regarding the duplicate function getDayMonthYearFieldNames
You can store these names in the options array, for example, add these lines to the init function of My_Form_Element_DateSelects
$name = $this->getName();
$this->options['names'] = array( ‘day’ => $name . ‘_day’, ‘month’ => $name . ‘_month’, ‘year’=> $name . ‘_year’);
now you have access to the day,month,date field names in the view helper through $options parameter… for example in the formDateSelect function you may use:
$xhtml = ‘_htmlAttribs($attribs)
. ‘>’;
interesting stuff!
Khaled Jouda | 06-Mar-09 at 2:20 am | Permalink
oh! part of my post was stripped
madhavi | 12-Mar-09 at 10:30 am | Permalink
I have a problem with language translator .en file please any one help mi for that
error is
Fatal error: Uncaught exception ‘Zend_Translate_Exception’ with message ‘Error opening translation file ‘C:/xampp/htdocs/languages/en.mo’.’ in C:\xampp\htdocs\zfsite\library\Zend\Translate\Adapter\Gettext.php:88 Stack trace: #0 C:\xampp\htdocs\zfsite\library\Zend\Translate\Adapter.php(445): Zend_Translate_Adapter_Gettext->_loadTranslationData(‘C:/xampp/htdocs…’, ‘en’, Array) #1 C:\xampp\htdocs\zfsite\library\Zend\Translate\Adapter.php(202): Zend_Translate_Adapter->_addTranslationData(‘C:/xampp/htdocs…’, ‘en’, Array) #2 C:\xampp\htdocs\zfsite\library\Zend\Translate\Adapter.php(109): Zend_Translate_Adapter->addTranslation(‘C:/xampp/htdocs…’, ‘en’, Array) #3 C:\xampp\htdocs\zfsite\library\Zend\Translate\Adapter\Gettext.php(50): Zend_Translate_Adapter->__construct(‘C:/xampp/htdocs…’, ‘en’, Array) #4 C:\xampp\htdocs\zfsite\library\Zend\Translate.php(93): Zend_Translate_Adapter_Gettext->__construct(‘C:/xampp/htdocs…’, ‘en’, Array) #5 C:\xampp\htdocs\zfsite\library\Zend\Translate.php(71): Zend_Translate->setAdapter(‘ge in C:\xampp\htdocs\zfsite\library\Zend\Translate\Adapter\Gettext.php on line 88
viperx | 24-Mar-09 at 2:23 pm | Permalink
Hi madhavi, is your MO file there?
C:/xampp/htdocs/languages/en.mo
If you use PoEdit the MO files should be created by the program when saving the PO file and will be located in the same directory.
Ben | 20-Jun-09 at 7:32 pm | Permalink
Hi,
I like to have my input rendered in a table format and because I have a lot of fields I use 4 columns to accept 2 fields and the rest for labels. How do I do it with ZF ?
Regards,
Ben.
Kusum | 08-Jul-09 at 12:05 am | Permalink
THis is very helping tutorial…
Everything was under control untill i got the error after adding captcha code…
i am not getting the captcha image but getting an error
Strict Standards: Creating default object from empty value in C:\wamp\www\new\application\modules\default\controllers\UserController.php on line 150
and line 150 states :-
$this->session->registerCaptcha = $captchaCode ;
Please help…
Regards
Kusum
Richard Cooke | 17-Jul-09 at 12:13 pm | Permalink
Excellent tutorial. Thanks.
There is a slight typo in this page:
and the corresponding view helper for rendering it:
‘library/My/View/Helper/FormDateSelect.php‘
should be:
‘library/My/View/Helper/FormDateSelects.php‘
The filename is correct in the source archive.
Regards,
Richard
devi | 17-Sep-09 at 5:53 pm | Permalink
How should i get the form what is the URL for getting it?
I am accessing like this:
http://localhost/part4/user/register
correct the above url to get the form.
devi | 17-Sep-09 at 6:01 pm | Permalink
I placed the “zend” directory in “application” directory
viperx | 07-Oct-09 at 1:15 pm | Permalink
place zend folder under library folder or somewhere in your php include path
and I strongly recommend to setup separate virtual host in your apache for each new site, portbased is just fine for development. So you will access it on:
http://localhost:8888/user/register
for example