Create a Simple File Upload Form in Zend Framework 2

simple file upload form in zend framework 2

Having trouble getting file uploads integrated into your forms in Zend Framework 2? Or are you just curious about how to do it, and you want a quick rundown? If either of these is you, come walk through today’s post with me as I show you a simple example of how it’s done – along with how to combine it with filters and validators.

Before we get started, I could have composed the code in a much shorter form than have I’ve composed it. But my assumption is that you’re likely using the full-stack framework. So that’s the approach I took. It’s composed for of three components:

  1. An Input Filter: which ensures a set of validation and filter criteria can be applied to any file which a user wants to upload. For instance, you may want to restrict files to be less than a set file size, be of a specific mime type or have a specific set of extensions. Also, you may want to perform post-processing, such as renaming the file, as I’ve done.
  2. A Form Class which extends Zend\Form: Here we see how to add a File element to a class which extends Zend\Form and that there’s not much to it.
  3. A Controller Action: Naturally, we need to process the information received in a POST request, so that we can access the file information supplied. So we’ll be working through the minimum required code you need in a controller action.

1. The Input Filter

To me, the most important part of working with forms in Zend Framework 2 is custom input filters. These, as I mentioned earlier, allow us to add a range of validation criteria and post-processing filters to our forms. This way, we can be sure that the sum of the elements meet the required criteria, so that when we access the information, it’s safe and valid to use. I’ve assumed here, that you have an existing input filter class, which extends \use Zend\InputFilter\InputFilter. This makes it almost trivial to work with them. Let’s get started.

use Zend\InputFilter\InputFilter;
use Zend\InputFilter\Input;
use Zend\Filter\File\RenameUpload;
use Zend\Validator\File\Size;
use Zend\Validator\File\Extension;
use Zend\Validator\File\MimeType;

In your InputFilter class, you need to add in all the use statements for the classes which we’ll need, which I’ve included above. You can see that we’ll be using the RenameUpload filter, along with the Size, Extension and MimeType validators.

$input = new Input('fileAttachment');
$input->setRequired(true)
      ->setAllowEmpty(false);

Next, we create a new Input object, giving it the name ‘fileAttachment’. On the new object, we specify that it’s required to be present, setRequired(true) and has to have a value supplied, setAllowEmpty(false).

$input->getValidatorChain()
      ->attach(new Size(array(
            'messageTemplates' => array(
                Size::TOO_BIG => 'The file supplied is over the allowed file size',
                Size::TOO_SMALL => 'The file supplied is too small',
                Size::NOT_FOUND => 'The file was not able to be found',
            ),
            'options' => array(
                'max' => 4000
            )
        )
    ))
    ->attach(new MimeType(array(
            'messageTemplates' => array(
                MimeType::FALSE_TYPE => 'The file is not an allowed type',
                MimeType::NOT_DETECTED => 'The file type was not detected',
                MimeType::NOT_READABLE => 'The file type was not readable',
            ),
            'options' => array(
                'enableHeaderCheck' => true,
                'mimeType' => 'text/plain'
            )
        )
    ));

Next we add two validators to the object, File\Size and File\MimeType. Size will ensure that the file is no bigger than 4mb, as in it’s options array, I’ve set max to be 4,000 bytes. The only mimetype allowed will be text/plain as that’s the only one listed in the mimeType option. I’ve set enableHeaderCheck to true so that the …

You can see for both of the validators, I’ve customised the message templates. Now this wasn’t strictly necessary, but I thought it might be nice to personalise the form errors, just a little bit.

    $input->getFilterChain()->attach(new RenameUpload(array(
        'options' => array(
            'target' => sprintf(
                "%s/data/uploads/attachment.%s.txt",
                __DIR__ . '/../../../../..',
                time()
            ),
        )
    )));

Finally, I’ve added the RenameUpload filter on to the list of filters, by passing a new RenameUpload object to the attach method, available on the Input’s InputFilterChain, which I accessed by calling getFilterChain(). The reason for this, is that I wanted to play with the file after it’s uploaded and call it something different. Specifically, I’m renaming it using the following sprintf template attachment.%s.txt, where the single placeholder will be replaced by the current UNIX timestamp, supplied by time().

$this->add($input);

Finally the new object is added to the InputFilter.

The Form Element

Once again, I’ll assume that you have an existing Form class, which extends \Zend\Form\Form and just cover the code specific to adding the File element.

$this->add(array(
    'type' => 'Zend\Form\Element\File',
    'name' => 'feedAttachment',
    'options' => array(
         'label' => 'Attachment:'
    ),
    'attributes' => array(
        'class' => 'form-control',
    )
));

In the above code, you can see that I’m adding an element, by passing a configuration array to the class’ add method. I’ve specified the type as \Zend\Form\Element\File, which will instantiate a \Zend\Form\Element\File object and pass the remaining arguments to its constructor. The name of the element is feedAttachment, it will have a label of Attachment: and a class attribute of form-control. I could have gone further, but this is enough to create a basic file upload element.

Attaching The InputFilter

To attach the InputFilter to the Form and to ensure that form has the enctype attribute set, in the class’ constructor, add the following code, replacing the name of the InputFilter class with yours, naturally.

$this->setInputFilter(new MyInputFilter())
     ->setAttribute('enctype', 'multipart/form-data');

The Controller Action

Right, now that the input filter is defined and attached to the form class, we need to access the form in our controller action. To do that, we’re going to use the FormElementManager, which makes retrieval of Forms and creation of custom form elements in Zend Framework 2 quite easy.

public function manageAction()
{
    $formManager = $this->serviceLocator->get(
        'FormElementManager'
    );

    $form = $formManager->get(
        'BabyMonitor\Forms\ManageRecordForm'
    );

Right, in our action, we first get a handle on the FormElementManager and call its `get` method, passing in the form’s path, to retrieve a copy of our form object.

    if ($this->getRequest()->isPost()) {
        $form->setData(array_merge_recursive(
            $this->getRequest()->getPost()->toArray(),
            $this->getRequest()->getFiles()->toArray()
            ));
        if ($form->isValid()) {        
            // remainder of the processing code...
        }
    }
}

With the form retrieved, we then check if the request’s method type was POST, by calling isPost() on the Request object. If so, then we call setData() passing in an array which is a combination of both the $POST and $FILES arrays. If we weren’t working with File uploads, we’d just pass in $this->getRequest()->getPost(). But we need both arrays to access the File data, which is separate from the rest of the POST data. Then, we run the validation process by calling isValid() on the form object and are then able to access the information as we normally would.

The View Template

Ok, we have one last step to go, the view template. I’m using the form ViewHelper to make rendering the form simple, whilst still retaining the ability to implement a custom layout. Here’s a concise version of the template code:

<?php
$form = $this->form;
$form->prepare();
echo $this->form()->openTag($form);
?>

<?php echo $this->formSubmit($form->get(‘submit’)); ?>  <?php echo $this->form()->closeTag(); ?>

You can see that the openTag() and closeTag() method’s have been called on the form, to render the form’s HTML tags. Inside those, I’ve rendered the file element’s individual parts (label, element and errors) by using the formLabel, formFile and FormElementErrors ViewHelpers.

With all of this in place, we have all we need to render, submit and process the form in our controller action.

Wrapping Up

That’s it for today’s tutorial. I appreciate it’s been a bit of a rush through from start to finish, but it covers the basics that you’ll need to setup a form, which incorporates a file upload element, along with suitable validators and filters in your applications. Let me know what you think in the comments and keep an eye out for more posts covering file upload in Zend forms.

Forms