.. _user-guide-forms-and-actions: Forms and actions ================= Adding new albums ----------------- We can now code up the functionality to add new albums. There are two bits to this part: * Display a form for user to provide details * Process the form submission and store to database We use ``Zend\Form`` to do this. The ``Zend\Form`` component manages the form and for validation, we add a ``Zend\InputFilter`` to our ``Album`` entity. We start by creating a new class ``Album\Form\AlbumForm`` that extends from ``Zend\Form\Form`` to define our form. Create a file called ``AlbumForm.php`` in ``module/Album/src/Album/Form``: .. code-block:: php setAttribute('method', 'post'); $this->add(array( 'name' => 'id', 'attributes' => array( 'type' => 'hidden', ), )); $this->add(array( 'name' => 'title', 'attributes' => array( 'type' => 'text', ), 'options' => array( 'label' => 'Title', ), )); $this->add(array( 'name' => 'artist', 'attributes' => array( 'type' => 'text', ), 'options' => array( 'label' => 'Artist', ), )); $this->add(array( 'name' => 'submit', 'attributes' => array( 'type' => 'submit', 'value' => 'Go', 'id' => 'submitbutton', ), )); } } Within the constructor of ``AlbumForm``, we set the name when we call the parent’s constructor and then set the method and then create four form elements for the id, title, artist, and submit button. For each item we set various attributes and options, including the label to be displayed. We also need to set up validation for this form. In Zend Framework 2 this is done using an input filter which can either be standalone or within any class that implements ``InputFilterAwareInterface``, such as a model entity. We are going to add the input filter to our ``Album.php`` file in ``module/Album/src/Album/Model``: .. code-block:: php :emphasize-lines: 5-8,15,23-86 id = (isset($data['id'])) ? $data['id'] : null; $this->artist = (isset($data['artist'])) ? $data['artist'] : null; $this->title = (isset($data['title'])) ? $data['title'] : null; } // Add content to these methods: public function setInputFilter(InputFilterInterface $inputFilter) { throw new \Exception("Not used"); } public function getInputFilter() { if (!$this->inputFilter) { $inputFilter = new InputFilter(); $factory = new InputFactory(); $inputFilter->add($factory->createInput(array( 'name' => 'id', 'required' => true, 'filters' => array( array('name' => 'Int'), ), ))); $inputFilter->add($factory->createInput(array( 'name' => 'artist', 'required' => true, 'filters' => array( array('name' => 'StripTags'), array('name' => 'StringTrim'), ), 'validators' => array( array( 'name' => 'StringLength', 'options' => array( 'encoding' => 'UTF-8', 'min' => 1, 'max' => 100, ), ), ), ))); $inputFilter->add($factory->createInput(array( 'name' => 'title', 'required' => true, 'filters' => array( array('name' => 'StripTags'), array('name' => 'StringTrim'), ), 'validators' => array( array( 'name' => 'StringLength', 'options' => array( 'encoding' => 'UTF-8', 'min' => 1, 'max' => 100, ), ), ), ))); $this->inputFilter = $inputFilter; } return $this->inputFilter; } } The ``InputFilterAwareInterface`` defines two methods: ``setInputFilter()`` and ``getInputFilter()``. We only need to implement ``getInputFilter()`` so we simply throw an exception in ``setInputFilter()``. Within ``getInputFilter()``, we instantiate an ``InputFilter`` and then add the inputs that we require. We add one input for each property that we wish to filter or validate. For the ``id`` field we add an ``Int`` filter as we only need integers. For the text elements, we add two filters, ``StripTags`` and ``StringTrim`` to remove unwanted HTML and unnecessary white space. We also set them to be *required* and add a ``StringLength`` validator to ensure that the user doesn’t enter more characters than we can store into the database. We now need to get the form to display and then process it on submission. This is done within the ``AlbumController``’s ``addAction()``: .. code-block:: php :emphasize-lines: 6-7,10-31 // module/Album/src/Album/Controller/AlbumController.php: //... use Zend\Mvc\Controller\AbstractActionController; use Zend\View\Model\ViewModel; use Album\Model\Album; // <-- Add this import use Album\Form\AlbumForm; // <-- Add this import //... // Add content to this method: public function addAction() { $form = new AlbumForm(); $form->get('submit')->setValue('Add'); $request = $this->getRequest(); if ($request->isPost()) { $album = new Album(); $form->setInputFilter($album->getInputFilter()); $form->setData($request->getPost()); if ($form->isValid()) { $album->exchangeArray($form->getData()); $this->getAlbumTable()->saveAlbum($album); // Redirect to list of albums return $this->redirect()->toRoute('album'); } } return array('form' => $form); } //... After adding the ``AlbumForm`` to the use list, we implement ``addAction()``. Let’s look at the ``addAction()`` code in a little more detail: .. code-block:: php $form = new AlbumForm(); $form->get('submit')->setValue('Add'); We instantiate ``AlbumForm`` and set the label on the submit button to “Add”. We do this here as we’ll want to re-use the form when editing an album and will use a different label. .. code-block:: php $request = $this->getRequest(); if ($request->isPost()) { $album = new Album(); $form->setInputFilter($album->getInputFilter()); $form->setData($request->getPost()); if ($form->isValid()) { If the ``Request`` object’s ``isPost()`` method is true, then the form has been submitted and so we set the form’s input filter from an album instance. We then set the posted data to the form and check to see if it is valid using the ``isValid()`` member function of the form. .. code-block:: php $album->exchangeArray($form->getData()); $this->getAlbumTable()->saveAlbum($album); If the form is valid, then we grab the data from the form and store to the model using ``saveAlbum()``. .. code-block:: php // Redirect to list of albums return $this->redirect()->toRoute('album'); After we have saved the new album row, we redirect back to the list of albums using the ``Redirect`` controller plugin. .. code-block:: php return array('form' => $form); Finally, we return the variables that we want assigned to the view. In this case, just the form object. Note that Zend Framework 2 also allows you to simply return an array containing the variables to be assigned to the view and it will create a ``ViewModel`` behind the scenes for you. This saves a little typing. We now need to render the form in the add.phtml view script: .. code-block:: php headTitle($title); ?>
Are you sure that you want to delete 'escapeHtml($album->title); ?>' by 'escapeHtml($album->artist); ?>'?
url('album', array( 'action' => 'delete', 'id' => $this->id, )); ?> In this script, we display a confirmation message to the user and then a form with "Yes" and "No" buttons. In the action, we checked specifically for the “Yes” value when doing the deletion. Ensuring that the home page displays the list of albums ------------------------------------------------------- One final point. At the moment, the home page, http://zf2-tutorial.localhost/ doesn’t display the list of albums. This is due to a route set up in the ``Application`` module’s ``module.config.php``. To change it, open ``module/Application/config/module.config.php`` and find the home route: .. code-block:: php 'home' => array( 'type' => 'Zend\Mvc\Router\Http\Literal', 'options' => array( 'route' => '/', 'defaults' => array( 'controller' => 'Application\Controller\Index', 'action' => 'index', ), ), ), Change the ``controller`` from ``Application\Controller\Index`` to ``Album\Controller\Album``: .. code-block:: php :emphasize-lines: 6 'home' => array( 'type' => 'Zend\Mvc\Router\Http\Literal', 'options' => array( 'route' => '/', 'defaults' => array( 'controller' => 'Album\Controller\Album', // <-- change here 'action' => 'index', ), ), ), That’s it - you now have a fully working application!