Frontend

Let's review the work we need to do at the client side of the app. We need to create a new layout in order to remove the header menu. After that, we have to create the signup form and its view. Then, we should modify the controller to use the new layout and process the signup form. We also need to modify the controller view to show the signup form and finally adapt ApiClient to be able to send the data to the API.

The following screenshot shows the structure of the module:

Frontend

The signup.phtml file

Let's start with the layout. The file is not shown in the screenshot, but it should be placed in the Common module next to the layout.phtml file. The contents of the file are the same as that of the layout.phtml file. The only difference is that we removed the following lines of code:

<?php $this->navigation('navigation')->findBy('route', 'wall')
    ->setParams(array('username' => $this->username)); ?>
<?php $this->navigation('navigation')->findBy('route', 'feeds')
    ->setParams(array('username' => $this->username)); ?>
<?php echo $this->navigation('navigation')->menu()
    ->setUlClass('nav')->renderMenu(); ?>

The objective we want to achieve is a signup form without the navigation menu at the top. You can do this by using another layout or by creating a view helper that checks the route and based on that, shows or hides the menu.

The SignupForm.php file

The SignupForm.php file file is the file in which we are going to add all the fields for the signup form. Because the file is huge, we will look at the following relevant parts:

Field

Type

Username

Text

Email

Text

Password

Password

Repeat password

Password

Avatar

File

Name

Text

Surname

Text

Bio

Textarea

Location

Text

Gender

Radio

This is the list of fields we have on the form and their types. We need to highlight that we are also providing the label attribute and customizing the label class, as follows:

$this->add(array(
    'name' => 'location',
    'type'  => 'ZendFormElementText',
    'options' => array(
        'label' => 'Location',
        'label_attributes' => array(
            'class' => 'control-label'
        )
    )
));

This is the location field code. As you can see, the label attribute and class are specified inside the options key. Almost all the form view helpers allow us to specify their options in the following section of the field configuration:

$this->add(array(
    'name' => 'gender',
    'type'  => 'ZendFormElementRadio',
    'options' => array(
        'label' => 'Gender',
        'label_attributes' => array(
            'class' => 'radio'
        ),
        'value_options' => array(
            0 => 'Female',
            1 => 'Male'
        )
    )
));

The preceding code is the code for the Gender field. As you can see, we specified the options for the radio buttons in the value_options key. This will also configure an InArray validator based on the values provided.

For the complete code, do not hesitate to jump to the file and see it yourself.

The signup-form.phtml file

As we are trying to make a most useful and customized form, we are going to review a few things on this file.

The way of creating a view is the same as the ones we saw before; the difference is that we now also add the HTML code in between and customize some HTML tag attributes based on the data provided by the PHP code and the fields.

The following structure is followed by all the fields:

<div class="control-group <?php echo count($form->get('name')->getMessages()) > 0 ? 'error' : '' ?>">
    <?php echo $this->formLabel($form->get('name')); ?>
    <div class="controls">
        <?php echo $this->formElement($form->get('name')); ?>
        <?php echo $this->formElementErrors()
            ->setMessageOpenFormat('<div class="help-inline">')
            ->setMessageSeparatorString(
                '</div><div class="help-inline">'
            )
            ->setMessageCloseString('</div>')
            ->render($form->get('name')); ?>
    </div>
</div>

I know that is difficult to visualize, but that's what happens usually with HTML views. The code is verbose and usually doesn't fit on one line.

The first thing we do is customize the class of the div element that contains the field. We check if there are any error messages for a specific field; in case there are errors, we add the error class to the class attribute of the div element.

After that, we proceed to print the label of the field. We accomplish that by using the formLabel() helper that receives the field as a parameter. This label will contain the control-label class, because we specified it while configuring the field in the form file.

Then, we have more HTML to wrap the field itself and right after that, we print the field and the error messages.

The error messages are printed using the formElementErrors() method, which receives the field as a parameter; but in this case, we are also customizing the way the helper will render the error messages. We do that by using the provided methods, such as setMessageOpenFormat(), setMessageSeparatorString(), and setMessageCloseString(). As you can see, we customize the helper the first time we use it, and then it will remember the options we set for the next fields.

User.php

In this file, we need to define the filters and validators we are going to use to validate the data we will store. When we work with filters, we need to add the following lines below the namespace to declare the dependencies:

use ZendInputFilterInputFilter;
use ZendInputFilterFactory as InputFactory;

After that, we can jump to creating the getInputFilter() method, which will contain the filters and validators for the data. As we did with the form, we are going to show the relevant sections because the method is vast.

$inputFilter->add($factory->createInput(array(
    'name'     => 'username',
    'required' => true,
    'filters'  => array(
        array('name' => 'StripTags'),
        array('name' => 'StringTrim'),
    ),
    'validators' => array(
        array(
            'name' => 'NotEmpty',
            'break_chain_on_failure' => true
        ),
        array(
            'name' => 'StringLength',
            'options' => array(
                'min' => 1,
                'max' => 50
            ),
        ),
    ),
)));

This is the first example we are going to examine. Let's focus on the first validation. If you take a closer look, you will notice that it contains an option called break_chain_on_failure set to true. This means if the validator fails, the validation will not continue and will be stopped there for this specific field. This is useful if you want to avoid the user getting a lot of error messages, or things that don't make sense, or are evident; for example, an error message saying that the value cannot be empty, and then another error message saying that the length should be a minimum of one character.

array(
    'name' => 'EmailAddress',
    'options' => array(
        'messages' => array(
            endValidatorEmailAddress::INVALID_FORMAT => 
                'The input is not a valid email address',
        )
    )
),

This validator belongs to the Email field. What I want to highlight here is how we can customize error messages. What we have to do is define them on the options key with the name messages and using it as a key of the array, use the same constant on the validator to define the original error message. Doing this, we are overwriting the error message for that specific error.

array(
    'name' => 'InArray',
    'options' => array(
        'haystack' => array('0', '1')
    )
),

Remember we said that when you use a MultiCheck element, it adds an InArray validator by default. Well, this is how you configure the InArray validator manually for a specific element.

The contents of the IndexController.php file

We need to fill the contents of the indexAction() method, which we left empty in the previous chapters. Let's review the following lines of code step-by-step:

$this->layout('layout/signup'),

$viewData = array();
$signupForm = new SignupForm();
$signupForm->setAttribute('action', $this->url()->fromRoute(
    'users-signup'
));

The first thing we do is set a new layout. After that, we create the instance of our brand new form and set the action attribute to point to the following method:

$request = $this->getRequest();
if ($request->isPost()) {
    ...
}

$viewData['signupForm'] = $signupForm;
return $viewData;

After that, we check if the request is a POST request; if it isn't, we just pass the form to the view in order to render it if it is, we will proceed with the processing of the form using the following code:

$data = $request->getPost()->toArray();

$signupForm->setInputFilter(User::getInputFilter());
$signupForm->setData($data);

if ($signupForm->isValid()) {
    ...
}

Here, we are getting the data from the request, configuring the filters and validators on the form based on the configuration we made on the User entity, and after that checking if the data sent compiles with what we expect.

$files = $request->getFiles()->toArray();
$data = $signupForm->getData();
$data['avatar'] = $files['avatar']['name'] != '' ? 
    $files['avatar']['name'] : null;

After that, we proceed to process the data itself. By using the following lines, we will get the data filtered by the form and the possible image uploaded by the user, and merge them together:

$size = new Size(array('max' => 2048000));
$isImage = new IsImage();
$filename = $data['avatar']['name'];

$adapter = new endFileTransferAdapterHttp();
$adapter->setValidators(array($size, $isImage), $filename);

if (!$adapter->isValid($filename)){
    $errors = array();
    foreach($adapter->getMessages() as $key => $row) {
        $errors[] = $row;
    }
    $signupForm->setMessages(array('avatar' => $errors));
}

Note

Notice that you can specify the size of the file using the International System of Units. The accepted SI notation units are: KB, MB, GB, TB, PB, and EB. All these units are converted using 1024 as the base value.

In case the user has uploaded an image, we proceed to validate the size; and in case of an error, we set it on the form using the following code:

$destPath = 'data/tmp/';
$adapter->setDestination($destPath);

$fileinfo = $adapter->getFileInfo();
preg_match('/.+/(.+)/', $fileinfo['avatar']['type'], $matches);
$newFilename = sprintf('%s.%s', sha1(uniqid(time(), true)), $matches[1]);

$adapter->addFilter('FileRename',
    array(
        'target' => $destPath . $newFilename,
        'overwrite' => true,
    )
);
if ($adapter->receive($filename)) {
    $data['avatar'] = base64_encode(
        file_get_contents(
            $destPath . $newFilename
        )
    );
    
    if (file_exists($destPath . $newFilename)) {
        unlink($destPath . $newFilename);
    }
}

The following are the lines that move the sent data from the existing tmp folder to our tmp folder, then renames the file to avoid collisions and reads the contents of the file. Once the contents have been read, we remove the file and proceed to create the account using the following code:

unset($data['repeat_password']);
unset($data['csrf']);
unset($data['register']);

$response = ApiClient::registerUser($data);

if ($response['result'] == true) {
    $this->flashMessenger()->addMessage('Account created!'),
    return $this->redirect()->toRoute(
        'wall', array('username' => $data['username'])
    );
}

If you remember, we expect to have all the data as one parameter of the create() method on the API side. That is why we are getting rid of the repeat_password, csrf, and register values. Right after that, we use the ApiClient interface to call the API and then if the response is successful, we redirect the user to the new wall.

The index.phtml file

The index.phtml file is really simple, and we are just calling the partial that will render the signup form, which is accomplished using the following lines of code:

<?php echo $this->partial(
    'forms/signup-form.phtml', array('form' => $signupForm)
); ?>

Changes in the ApiClient.php file

In this file, we just need to add a new method called registerUser(), which contains the following lines:

$url = self::$endpointHost . self::$endpointUsers;
return self::doRequest($url, $postData, Request::METHOD_POST);

Also, we need to adapt the endpoints we have for the users as follows:

protected static $endpointUsers = '/api/users';
protected static $endpointGetUser = '/api/users/%s';
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.171.121