Processing File Uploads with Laravel 5

  • April 08, 2015

Welcome to the incredibly popular Easy Laravel 5 companion blog. To celebrate the new edition's release (updated for Laravel 5.5!) use the discount code easteregg to receive 20% off the book or book/video package! » Buy the book

I'm currently working on a Laravel 5 application which includes a restricted administration console used to manage products sold through an online catalog. Each product includes a name, SKU, price, description, and image. The image is uploaded using the Form::file helper made available through the LaravelCollective/html package, validated alongside the other form inputs using a Laravel 5 form request (see this tutorial if you're not familiar with form requests), and if valid, stored in a special directory. In this blog post I'll show you how this was implemented.

Let's begin with a simplified version of the form used in my project. Again, this uses the LaravelCollective/html package's form helpers to generate the various form fields:

{!! Form::open(
    array(
        'route' => 'admin.products.store', 
        'class' => 'form', 
        'novalidate' => 'novalidate', 
        'files' => true)) !!}

<div class="form-group">
    {!! Form::label('Product Name') !!}
    {!! Form::text('name', null, array('placeholder'=>'Chess Board')) !!}
</div>

<div class="form-group">
    {!! Form::label('Product SKU') !!}
    {!! Form::text('sku', null, array('placeholder'=>'1234')) !!}
</div>

<div class="form-group">
    {!! Form::label('Product Image') !!}
    {!! Form::file('image', null) !!}
</div>

<div class="form-group">
    {!! Form::submit('Create Product!') !!}
</div>
{!! Form::close() !!}
</div>

Specific to the matter of file uploading there are two key characteristics of this form you'll need to keep in mind when implementing your own uploader:

  • The Form::open method sets the 'files' => true attribute. This results in the form data being encoded as "multipart/form-data", which is required when files will be included as form data.
  • The Form::file helper is used to generate the file upload control.

When rendered to the browser the form looks like this:

As you can see, the form is submitted to a route named admin.products.store. As is typical of any Laravel 5 application, the submitted form data is first routed through a form request. The validation rules are found in the form request's rules() method. Here's an example which validates the supplied image to ensure one is present and that it is specifically a PNG (image file):

public function rules()
{
    return [
      'name'        => 'required',
      'sku'         => 'required|unique:products,sku,' . $this->get('id'),
      'image'       => 'required|mimes:png'
    ];
}

You can validate uploads using plenty of other approaches such as ensuring it is a Word document or PDF. See the Laravel documentation for more information. This request is passed into the Admin/ProductController.php's store method, which looks like this:

public function store(ProductRequest $request)
{

    $product = new Product(array(
      'name' => $request->get('name'),
      'sku'  => $request->get('sku')
    ));

    $product->save();

    $imageName = $product->id . '.' . 
        $request->file('image')->getClientOriginalExtension();

    $request->file('image')->move(
        base_path() . '/public/images/catalog/', $imageName
    );

    return \Redirect::route('admin.products.edit', 
        array($product->id))->with('message', 'Product added!');    
}

In this action we first save the product, and then process the image. There are plenty of different approaches to processing the uploaded image; I'm keeping this simple and just saving the image using a name matching the product ID, so for instance if the saved product using the ID 42 then the associated uploaded image will be named 42.png. The image name is first created (and stored in $imageName) and then it is moved into the application's /public/images/catalog directory.

Believe it or not, uploading an image using Laravel 5 is really that simple!