Book Image

Instant Zend Framework 2.0

By : A N M Mahabubul Hasan
Book Image

Instant Zend Framework 2.0

By: A N M Mahabubul Hasan

Overview of this book

Zend Framework 2 is a modern object-oriented PHP framework. It has lots of useful components, which are easy to use and user friendly, helping you get your job done faster. This book shows you how to use the framework components to build a practical MVC application. Topics covered in this application include validation, file upload, database CRUD (create, read, update, and delete) operations, and so on.Instant Zend Framework 2.0 has been specifically created to provide you with all the information that you need to get up with Zend Framework 2 applications. You will learn the basic of Zend Framework 2.0, and get started with building your first Zend Framework MVC application. The book promises to deliver all the information you need to get started with Zend Framework 2.0. Instant Zend Framework 2.0 starts with how to download Zend Framework 2.0 and the skeleton application, and then shows how to configure the skeleton application to use the Zend Framework 2.0. Then it takes you through step-by-step instructions of how to create and configure modules, controllers, views, models, helpers and so on. By following the instructions in the book, you will be able to build a small fully functional database-driven MVC application. You will also learn how to use different framework components like the event manager, Zend log, and how to configure them with the module. This will give you an idea of how to configure any component with the module. After finishing this book you will be able to build a small database-driven web application with Zend Framework 2.
Table of Contents (7 chapters)

Top 5 features you need to know about


In the previous sections we have covered how to create a basic module with controller, model, and view and also how to inject the database connection to the model and the CRUD. Here in this section, we will dive deep into Zend Framework with some of its very common functionalities.

Input filter and validation

In the Quick start – creating your first web application section, we have created a newAction action and an editAction action. Both of these actions have one thing in common: they were dealing with the client’s data. The client can insert anything which means they can insert malicious data that could lead to malicious attacks or invalidly formatted data; for example, we have a field for e-mail and e-mail has a specific format.

Zend Framework has a rich set of filters and validator classes (Zend\Filter, Zend\Validator, Zend\InputFilter). Here we will see how to filter and validate the client’s data using Zend\InputFilter.

Let’s update our newAction method in the homeController file with the following code:

public function newAction() {
    $invalids = array();
    $filter = array(
        ‘name’ => array(
            ‘name’ => ‘name’,
            ‘required’ => true,
            ‘filters’ => array(
                array(‘name’ => ‘StripTags’),
                array(‘name’ => ‘StringTrim’)
            ),
            ‘validators’ => array(
                array(
                    ‘name’ => ‘not_empty’,
                ),
                array(
                    ‘name’ => ‘string_length’,
                    ‘options’ => array(
                        ‘min’ => 3
                    ),
                ),
            ),
        ),
        ‘email’ => array(
            ‘name’ => ‘name’,
            ‘required’ => true,
            ‘filters’ => array(
                array(‘name’ => ‘StripTags’),
                array(‘name’ => ‘StringTrim’)
            ),
            ‘validators’ => array(
                array(
                    ‘name’ => ‘not_empty’,
                ),
                array(
                    ‘name’ => ‘email_address’,
                ),
            ),
        ),
        ‘phone’ => array(
            ‘name’ => ‘name’,
            ‘required’ => true,
            ‘filters’ => array(
                array(‘name’ => ‘StripTags’),
                array(‘name’ => ‘StringTrim’)
            ),
            ‘validators’ => array(
                array(
                    ‘name’ => ‘not_empty’,
                ),
            ),
        ),
    );

    if ($_POST) {
        $factory = new \Zend\InputFilter\Factory();
        $input = $factory->createInputFilter($filter);
        $input->setData($_POST);

        if($input->isValid()){
            $contact = $this->getServiceLocator()
                        ->get(‘Contact\Model\Contact’);

            $contact->addRow($_POST);
            return $this->redirect()->toRoute(‘home’);
        }else{
            $invalids = $input->getInvalidInput();
        }

        $data = $input->getValues();
    }

    return new ViewModel(array(‘row’=>$data, ‘invalids’=>$invalids));
}

In the $filter array, we have set filter and validator rules for each field; for example, for the name field, we have set two filters, StripTags and StringTrim, and two validators, not_empty and string_length.

As the name suggests, the StripTags filter removes any unwanted HTML tags, and StringTrim removes any unwanted space or newline character from the beginning and end of the given string.

Now we will activate our input filter using the Zend\InputFilter\Factory class and pass our input data to validate with the setData method.

If the inputs are valid, we will save the data in the database, or we will show error messages on the form view page. We will get all invalid inputs with the getInvalidInput() method, and we will assign back our data and invalid inputs to the view page with the following code:

new ViewModel(array(‘row’=>$data, ‘invalids’=>$invalids));

We have three fields in our form: name, email, and phone. If one of them is invalid or missing, the other field’s value needs to stay there otherwise it will be annoying for the user to fill out the whole form every time one or more inputs are invalid.

Let’s update our form view file (new.phtml) to show the error message as follows:

<hr />
<div>
    <div>
        <?php foreach($invalids as $err):
            $msgs = $err->getMessages();
            foreach($msgs as $m){
                echo ‘<div>’.$m.’</div>’;
            }
        endforeach;?>
    </div>
    <form method=”post” action=”new”>
    Name <br />
    <input type=”text” name=”name” value=”<?=$row[‘name’]?>” />
    <br />
    Email <br />
    <input type=”text” name=”email” value=”<?=$row[‘email’]?>” />
    <br />
    Phone <br />
    <input type=”text” name=”phone” value=”<?=$row[‘phone’]?>” />
    <br />
    <br /><br />
    <input type=”submit” value=”Save” />
    </form>
</div>

Now if we browse to this page and submit the form with invalid input(s), we should see errors as follows:

  • Value is required and can’t be empty

  • The input is less than 3 characters long

  • Value is required and can’t be empty

  • The input is not a valid email address. Use the basic format local-part@hostname

  • Value is required and can’t be empty

We can use CSS to make these error messages more stylish.

Besides StripTags and StringTrim, Zend Framework has a few more standard filter classes such as NumberFormat, PregReplace, StringToLower, Digits, Alpha, and Alnum. You will find the full list of standard filter classes in the manual under the Zend\Filter package.

Just like filter classes, validator has many standard classes in Zend Framework such as CreditCard, Date, Between, Digits, Identical, ISBN, InArray, Alnum, and Alpha. You will find the full list of the standard validator classes in the manual under the Zend\Validator package.

You might be wondering why the EmailAddress class is written as email_address, or the NotEmpty class as not_empty in the validators’ array. These are just class mappings, and you will find the full list of these names in the Zend\Validator\ValidatorPluginManager.php file’s $invokableClasses array. Note that the array key doesn’t have any underscore (_) because mapping will ignore any underscore character; for example, not_empty and notempty will mean the same.

View helper

In the last code example in the view page, we have printed the error messages using the following code:

        <?php foreach($invalids as $err):
            $msgs = $err->getMessages();
            foreach($msgs as $m){
                echo ‘<div>’.$m.’</div>’;
            }
        endforeach;?>

So, it will look really bad if we have many forms and we need to print the error messages with a repetitive code like this every time. Zend Framework has a good solution for this: we can create a View Helper for this repetitive code.

Basically, a helper is a class that implements at least Zend\View\Helper\HelperInterface and has a setView and getView method. Another way to create a custom view helper is by extending Zend\View\Helper\AbstractHelper. This AbstractHelper method implements HelperInterface giving you a head start in your development.

Let’s create our very first custom view helper. We will call it ErrorMsg, and it will simply take an array and return a string. We will create our ErrorMsg.php file inside the src/Contact/View/Helper directory. So, our updated directory structure will now look as follows:

/Contact
  /config
  /src
      /Contact
             /Controller
             /Model
             /View
                 /Helper
  /view
       /contact
       /error
       /layout
  /Module.php

Code for our ErrorMsg helper class will be as follows:

<?php
namespace Contact\View\Helper;

class ErrorMsg extends \Zend\View\Helper\AbstractHelper{
    public function __invoke($value){
        $msg = ‘’;        
        if(count($value)>0){
            foreach($value as $err){                
                $msgs = $err->getMessages();
                foreach($msgs as $m){
                    $msg .= ‘<div>’.$m.’</div>’;
                }
            }
        }
        return $msg;
    }
}

Now we have our helper class but to use this class on the view file, we need to register it. One way to do this is by adding a getViewHelperConfig method in the Module class as follows:

public function getViewHelperConfig() {
    return array(
        ‘invokables’ => array(
            ‘error_msg’ => ‘Contact\View\Helper\ErrorMsg’
        ),
    );
}

Our helper is now fully ready to be used in the view file. Let’s replace the previous error message showing code with the following line of code:

<?=$this->errorMsg($invalids);?>

This will show the error messages same as the previous code but is more clean. Let’s see our final code for the new.phtml view file.

<hr />
<div>
    <div><?=$this->errorMsg($invalids)?></div>    
    
    <form method=”post” action=”new”>
    Name <br />
    <input type=”text” name=”name” value=”<?=$row[‘name’]?>” /><br />
    Email <br />
    <input type=”text” name=”email” value=”<?=$row[‘email’]?>” />
    <br />
    Phone <br />
    <input type=”text” name=”phone” value=”<?=$row[‘phone’]?>” />
    <br />
    <br /><br />
    <input type=”submit” value=”Save” />
    </form>
</div>

Besides the custom view helper, Zend Framework has several built-in view helpers. Some of them are BasePath, Cycle, Doctype, HeadLink, HeadMeta, HeadScript, HeadStyle, HeadTitle, InlineScript, JSON, Partial, and Placeholder.

If you open the view/layout/layout.phtml file, you will see that we have already used Doctype and BasePath view helper in it as <?php echo $this->doctype();?> and <?= $this->basePath()?>.

Zend Log

We often need to log in to our apps for various reasons, such as debugging or error printing. We will see how we can use Zend\Log in this small app that we are building, but before we begin, let’s see a few more points about Zend\Log as follows:

  • Most applications use the Zend\Log\Logger class’s object the most. You can have as many logger objects as you like.

  • A logger object must contain at least one writer and can optionally contain one or more filters.

  • A writer is responsible for saving the data to storage.

  • A filter blocks the log data from being saved. A filter is applied to an individual writer. Filters can be chained.

  • A formatter can format the log data before it is written by a writer. Each writer has exactly one formatter.

Let’s get started!

We will be introducing our logger in the Module class’s getServiceConfig method. We will add another Logger element in the factories array as follows:

public function getServiceConfig() {
    return array(
        ‘factories’ => array(
         ...
            ‘Logger’ => function($sm) {
                $logger = new \Zend\Log\Logger;
                $writer = new \Zend\Log\Writer\Stream(‘data.log’);

                $logger->addWriter($writer);
                return $logger;
            }                
        ),
    );
}

Here in the code, our log filename is data.log, and it will be created in the application’s root directory; you can also give the filename with the full path if you want. Our logger is ready to do any kind of logging, so let’s do a test.

Add the following lines of code in the HomeController’s indexAction method.

$log = $this->getServiceLocator()->get(‘Logger’);
$log->info(‘Hello Zend Log’);

Now run the indexAction action by going to http://localhost/address-book/public. If everything went well, you should see a data.log file in the application’s root path and in that file a line of text saying ‘Hello Zend Log’.

Zend EventManager

Zend Framework 2 introduces a new EventManager component. It allows a class to publish events that other objects can listen to and then act when the event occurs. This component is designed for the following cases:

  • Implementing simple subject/observer patterns

  • Implementing aspect-oriented designs

  • Implementing event-driven architectures

The biggest advantage of using EventManager in our application is that we can decouple classes that really shouldn’t be coupled together, which makes the code easier to write and maintain. In fact, this is so useful that Zend Framework 2’s MVC system makes heavy use of EventManager.

Let’s do some event handing in our small app. Suppose we call a function when a new contact is inserted, edited, or deleted. This means we will create an insert event, an edit event, and a delete event.

We will make our Contact model as an EventManagerAware class. To do this, we need to implement the EventManagerAwareInterface interface.

Let’s open and update the Contact module with the following code:

<?php

namespace Contact\Model;

use Zend\EventManager\EventManagerInterface;
use Zend\EventManager\EventManager;
use Zend\EventManager\EventManagerAwareInterface;

class Contact implements EventManagerAwareInterface {

    private $_db;
    protected $events;

    public function setEventManager(EventManagerInterface $events) {
        $events->setIdentifiers(array(
            __CLASS__,
            get_called_class(),
        ));
        $this->events = $events;
        return $this;
    }

    public function getEventManager() {
        if (null === $this->events) {
            $this->setEventManager(new EventManager());
        }
        return $this->events;
    }

    public function __construct($db) {
        $this->_db = $db;
    }

    public function getAllRows() {
        $sql = “select * from contact”;
        $stat = $this->_db->query($sql);        
        return $stat->fetchAll();
    }

    public function addRow($data){
        $this->getEventManager()->trigger(‘event.insert’, $this);
        
        $sql = “INSERT INTO
            contact (name,email,phone)
            VALUES (‘{$data[‘name’]}’,’{$data[‘email’]}’,’{$data[‘pho     
                    ne’]}’)”;
            return $this->_db->exec($sql);
    }
    
    public function getRow($id) {
        $sql = “select * from contact where id=?”;

        $stat = $this->_db->prepare($sql);
        $stat->execute(array($id));
        return $stat->fetch();
    }

    public function updateRow($data, $id) {
        $this->getEventManager()->trigger(‘event.edit’, $this);
        $sql = “UPDATE contact SET
            name=’{$data[‘name’]}’,
            email=’{$data[‘email’]}’,
            phone=’{$data[‘phone’]}’
            WHERE id={$id}
            “;
            
        return $this->_db->exec($sql);
    }
    
    public function delRow($id){
        $this->getEventManager()->trigger(‘event.delete’, $this);
        $sql = “delete from contact where id={$id}”;
        return $this->_db->exec($sql);
    }
}

Take a detailed look at the code. Note that we have added two new methods, setEventManager and getEventManager, and the Contact class is implementing the EventManagerAwareInterface method, which also extends EventsCapableInterface. Here in the Contact module, setEventManager is a part of EventManagerAwareInterface and getEventManager is part of the EventsCapableInterface interface.

Then, look at the addRow method. Also, in the updateRow and delRow methods, we have triggered the events event.insert, event.edit, and event.delete. Whenever these methods are executed, they will trigger those events; if we call addRow, it will trigger the event.insert event, and if we call the delRow method, it will trigger the event.delete event. Now we need to attach a listening method to each of these events to do some actions. An event has no point without its listener.

We will add our event listeners in our Module class’s getServiceConfig method where we will first instantiate the contact model. Let’s update the previous code with the following one:

public function getServiceConfig() {
    return array(
        ‘factories’ => array(
            ‘Contact\Model\Contact’ => function($sm) {

                $db_adapter = $sm->get(‘db_adapter’);
                $contact = new \Contact\Model\Contact($db_adapter);
                $log = $sm->get(‘Logger’);

                $eventManager = $contact->getEventManager();

                $eventManager->attach(‘event.insert’, 
                                      function ($e) use ($log) {
                            $event = $e->getName();
                            $log->info(“{$event} event triggered”);
                        });

                $eventManager->attach(‘event.edit’, 
                                      function ($e) use ($log) {
                            $event = $e->getName();
                            $log->info(“{$event} event triggered”);
                        });

                $eventManager->attach(‘event.delete’, 
                                     function ($e) use ($log) {
                            $event = $e->getName();
                            $log->info(“{$event} event triggered”);
                        });

                return $contact;

            },
            ‘Logger’ => function($sm) {
                $logger = new \Zend\Log\Logger;
                $writer = new \Zend\Log\Writer\Stream(‘data.log’);

                $logger->addWriter($writer);
                return $logger;
            }
        ),
    );
}

Now add a new contact, edit a contact, and delete a contact, and if everything went well, we will see the events in the log it prints as follows:

event.insert event triggered.
event.edit event triggered.
event.delete event triggered.

Uploading a file

File upload capability is very common in web applications. Zend Framework has a component (Zend\File\Transfer) to handle a file upload. Here I will show how to utilize this component for a file upload.

First, let’s create another action method in our home controller. We will be naming it as fileUploadAction, and we will need to add a file-upload.phtml file in the /view/contact/home directory. In the file-upload.phtml file, we will add code for an HTML form with enctype=multipart/form-data to handle the file upload. Let’s create the code as follows:

<hr />
<div>
  <div><?php print_r($msg)?></div>    
    
    <form method=”post” action=”file-upload” 
                       enctype=”multipart/form-data”>

    File <br />
    <input type=”file” name=”doc” />    
    <br /><br />
    <input type=”submit” value=”Upload” />
    </form>
</div>

Here, print_r($msg) will print all kinds of messages and errors it will encounter when trying to upload the file. Our form’s action is to upload the file using the home controller’s fileUploadAction method.

Now lets see the code for the fileUploadAction method:

public function fileUploadAction() {

  if($this->getRequest()->isPost()){

       $adapter = new \Zend\File\Transfer\Adapter\Http();
       $adapter->setDestination(‘public/uploads’);

       $files = $this->getRequest()->getFiles();

       if ($adapter->receive($files[‘doc’][‘name’])) {
              return new ViewModel(
              array(‘msg’=>$files[‘doc’][‘name’].’ uploaded!’)
              );
       }
  }
}

This is a very minimalistic code for a file upload. In this method, Zend\File\Transfer\Adapter\Http is the file transfer adapter class for the HTTP protocol. We have set our destination directory or the upload directory with the setDestination() method. We have set our destination path as public/uploads. So, we need to create an uploads directory inside the public directory of our application.

Here in the code, the getFiles() method returns the same value as the PHP’s $_FILES variable.

Let’s go a little deeper in the file upload. In the previous version of the code, we didn’t validate what kind of file we should be uploading or what should be the size of the file. To do this, Zend Framework has various kinds of file validator class in the Zend\Validator component; for example, Crc32, Extension, Hash, ImageSize, IsImage, IsCompressed, Size, and WordCount.

Suppose we want to allow only PDF-type files of size 10 KB to 10 MB to be uploaded. For this, we need to update our previous fileUploadAction method with this code:

public function fileUploadAction() {

  if($this->getRequest()->isPost()){

      $size = new \Zend\Validator\File\Size(
                   array(‘min’ => ‘10kB’, ‘max’ => ‘10MB’)
                   );

      $ext = new \Zend\Validator\File\Extension(‘pdf’);

      $files = $this->getRequest()->getFiles();

      $adapter = new \Zend\File\Transfer\Adapter\Http();
      $adapter->setValidators(array($size,$ext));   

      $adapter->setDestination(‘public/uploads’);

      if($adapter->isValid()){
            if ($adapter->receive($files[‘doc’][‘name’])) {
                   return new ViewModel(
                      array(‘msg’=>$files[‘doc’][‘name’].’ uploaded!’)
                      );
               }
      }else{
               return new ViewModel(
                      array(‘msg’=>$adapter->getMessages())
               );
      }
  }
}

Here in the code, we have added two validators with the setValidators method; one validator is to check the PDF extension and another one is to check whether the size of the file is between 10 KB to 10 MB.

If the file is valid, it will upload the file to the destination directory.

Like the validators, Zend Framework also provides some file filter classes; for example, Encrypt, Lowercase, Uppercase, and Rename. We can add these filter classes with the setFilters method on the adapter class instance just as we did for the setValidators method.