Laravel 6 – Image/File Upload With Validation [Step By Step]

fil-upload-in-laravel-6

File uploads is very basic functionality which require in almost all web-applications. It includes various file types like Images, PDFs,  etc. In this tutorial i will show you how to upload Image/File with validation in laravel 6. We will also see file validations as well.

Lets start file upload tutorial in laravel 6 by installing fresh laravel using below command.

composer create-project --prefer-dist laravel/laravel laravel-file-upload

Now update database credentials in .env file.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=lara-file
DB_USERNAME=root
DB_PASSWORD=

Next, start server using below command.

php artisan serve

In this tutorial, we will make simple form which upload any document such as Image, PDF, Doc etc. and list them out.

Lets create document migration using below artisan migration command.

php artisan make:migration create_documents_table

This command will generate document migration file under database/migrations directory. Open document migration file and add two fields title and file_url as shown below.

2019_09_13_150436_create_documents_table.php

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateDocumentsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('documents', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('title');
            $table->string('file_url')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('documents');
    }
}

Next, lets make a document model file using below artisan command.

php artisan make:model Document

This command will generate document model file Document.php under app/ directory. Open Document.php file and add table fields for mass assignable.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Document extends Model
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['title', 'file_url'];
}

Next create document controller using below artisan command.

php artisan make:controller DocumentController --resource

This command will create DocumentController.php file under app/Http/Controllers directory as shown below. The controller will contain a method for each of the available resource operations. Resource operations like CRUD (CREATE, READ, UPDATE and DELETE).

DocumentController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class DocumentController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        //
    }
}

This is default DocumentController with resource route methods generated by make:controller artisan command. We can easily create CRUD using this controller.

Related:  How To Add SoftDelete With Unique Validation In Laravel

Next, we will add route in web.php file. Open routes/web.php file and add document routes as shown below.

web.php

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return redirect('document');
});

Route::resource('document', 'DocumentController');
Route::post('update-document/{id}', ['as' => 'update-document', 'uses' => '[email protected]']);

Let me explain routes here.

  • First route is responsible to redirect on document route, because  we  haven’t anything on homepage ‘/’ to display users.
  • Second route is Resource route which is responsible for CREATE, READ, UPDATE and DELETE operation for document.
  • Third route is responsible for document update operation as we have to deal with file and PUT HTTP method has some bad experience to upload file.

List All Documents

To list all the documents, lest use index method of controller and display data in index.blade.php layout file.

DocumentController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Document;

class DocumentController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $docs = Document::all();
        return view('document.index', ['docs' => $docs]);
    }

    // Other Methods
   
}

index.blade.php [Layout File]

index.blade.php layout file is responsible to display all document in list view. It extends main layout file layout.blade.php

@extends('layout')

@section('content')
<div class="control-btn float-right">
  <a href="{{ route('document.create') }}" class="btn btn-primary">Add File</a>
</div>
<div class="title">
  <h1>Documents</h1>
</div>
@if (session('success'))
  <div class="alert alert-success" role="alert">
    {{ session('success') }}
  </div>
@endif
@if (session('error'))
  <div class="alert alert-danger" role="alert">
    {{ session('error') }}
  </div>
@endif
<table class="table table-bordered">
  <thead>
    <tr>
      <th>Title</th>
      <th>File Name</th>
      <th>Actions</th>
    </tr>
  </thead>
    <tbody>
      @forelse ($docs as $doc)
      <tr>
        <td>{{ $doc->title }}</td>
        <td>{{ $doc->file_url }}</td>
        <td>
          <div class="action_btn">
            <div class="action_btn">
              <a href="{{ route('document.edit', $doc->id)}}" class="btn btn-warning btn-sm">Edit</a>
            </div>
            <div class="action_btn margin-left-10">
              <form action="{{ route('document.destroy', $doc->id)}}" method="post">
                @csrf
                @method('DELETE')
                <button class="btn btn-danger btn-sm" type="submit">Delete</button>
              </form>
            </div>
          </div>
        </td>
      </tr>
      @empty
      <tr>
        <td colspan="3">
          <center>
            No data found
          </center>
        </td>
      </tr>
      @endforelse
  </tbody>
</table>
@endsection

Add New Document

To add new document [Image/File], We will use create and store method of DocumentController.php file. create method stands for rendering form and store method will store data while submitted data.

Related:  Laravel 6 - MANY TO MANY Eloquent Relationship With Example

DocumentController.php

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        return view('document.add');
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $reqData = $request->input();
        if ($request->hasFile('file')) {
            $file = $request->file('file');
            $name = time() . rand(11111, 99999) . '.' . $file->getClientOriginalExtension();
            $destinationPath = public_path('docs');
            $file->move($destinationPath, $name);
            $reqData['file_url'] = $name;
        }
        $newFile = Document::create($reqData);
        if ($newFile) {
            $request->session()->flash('success', 'Data successfully added');
        } else {
            $request->session()->flash('error', 'Data successfully added');
        }
        return redirect('document');
    }

add.blade.php

This layout file contain code for rendering add document form. This is located in resources/views/document/  directory.

@extends('layout')

@section('content')
<div class="title">
  <h1>Add Document</h1>
</div>
<form action="{{ route('document.store') }}" method="POST" enctype="multipart/form-data">
  <div class="form-group">
    <label for="title">Title:</label>
    <input type="text" class="form-control" id="title" name="title">
  </div>
  <div class="form-group">
    <label for="file">Choose File:</label>
    <input type="file" class="form-control-file" id="file" name="file">
  </div>
  @csrf
  <button type="submit" class="btn btn-primary">Submit</button>
  <a href="{{ route('document.index') }}" class="btn btn-danger">Back</a>
</form>
@endsection

Edit/Update Document

To update any document, we will use edit and update method from DocumentController.php file. Here edit method stands for render document form with data and update method will manipulate with submitted data.

DocumentController.php

/**
 * Show the form for editing the specified resource.
 *
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function edit($id)
{
    $doc = Document::find($id);
    if ($doc) {
        return view('document.edit', [ 'doc' => $doc ]);
    } else {
        return redirect('document')->with('error', 'File not found');
    }
}

/**
 * Update the specified resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function update(Request $request, $id)
{
    $document = Document::find($id);
    if (!$document) {
        return redirect('document')->with('error', 'Data not found');
    }

    if ($document->file_url == null) {
        $oldImage = '';
    } else {
        $oldImage = $document->file_url;
    }

    $updateData = $request->input();

    // Supplier Image
    if ($request->hasFile('file')) {
        $file = $request->file('file');
        $name = time() . rand(11111, 99999) . '.' . $file->getClientOriginalExtension();
        $destinationPath = public_path('docs/');
        $file->move($destinationPath, $name);
        $updateData['file_url'] = $name;            
        
        // Delete Old supplier_image
        if (!empty($oldImage)) {
            if (\File::exists(public_path('docs/' . $oldImage))) {
                \File::delete(public_path('docs/' . $oldImage));
            }
        }
    }

    $docStatus = $document->update($updateData);

    if ($docStatus) {
        return redirect('document')->with('success', 'Data successfully updated.');
    } else {
        return redirect()->route('document.edit', ['id' => 1])->with('error', 'Oops something went wrong. Please try again.');
    }
}

In update method, if user submit new file then it will automatically deleted old file and update new file. So it will not create bunch of file in the public/docs directory and require more space.

Related:  Install Laravel 5.8 On Windows XAMPP Using Composer

Here we will create edit.blade.php file for rendering document form with data. See the code below.

edit.blade.php

@extends('layout')

@section('content')
<div class="title">
  <h1>Edit Document</h1>
</div>
<form action="{{ route('update-document', ['id' => $doc->id]) }}" method="POST" enctype="multipart/form-data">
  <div class="form-group">
    <label for="title">Title:</label>
    <input type="text" class="form-control" id="title" name="title" value="{{ $doc->title }}">
  </div>
  <div class="form-group">
    <label for="file">Choose File:</label>
    <input type="file" class="form-control-file" id="file" name="file">
  </div>
  <div class="form-group">
    <img src="/docs/{{ $doc->file_url }}" width="150" height="150">
  </div>
  @csrf
  {{-- @method('PUT') --}}
  <button type="submit" class="btn btn-primary">Submit</button>
  <a href="{{ route('document.index') }}" class="btn btn-danger">Back</a>
</form>
@endsection

Delete/Destroy Document

To delete document, we will use destroy method of DocumentController.php file. It will delete database records using $document->delete() and also delete document file from public/docs directory.

DocumentController.php

/**
 * Remove the specified resource from storage.
 *
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function destroy($id)
{
    $document = Document::find($id);
    if (!$document) {
        $respStatus = 'error';
        $respMsg = 'Data not found';
    }

    // Delete file
    $fileUrl = $document->file_url;
    if (\File::exists(public_path('docs/'.$fileUrl))) {
        \File::delete(public_path('docs/'.$fileUrl));
    }

    // Delete document from database
    $isDocDeleted = $document->delete();

    if ($isDocDeleted) {
        $respStatus = 'success';
        $respMsg = 'Data deleted successfully';
    }
    return redirect('document')->with($respStatus, $respMsg);
}

After successfully deleted operation, it will redirect to document listing page with appropriate response message.

Awesome. We have completed basic CRUD in our document app.

File Validation

File validation is necessary to get rid of unwanted file extensions. Here we will do validations of image and pdf file extensions. For images we will allow only jpg,jpeg and png. We will also validate file size till 10000 kb.

Lets add validation code in store and update method.

/**
 * Store a newly created resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\Response
 */
public function store(Request $request)
{
    // Validate request data
    $validatedData = $request->validate([
        'title' => 'required|max:255',
        'file' => 'required|max:10000|mimes:png,jpeg,jpg,pdf',
    ]);

    ... // store validated document
}

/**
 * Update the specified resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function update(Request $request, $id)
{
    // Validate request data
    $validatedData = $request->validate([
        'title' => 'required|max:255',
        'file' => 'required|max:10000|mimes:png,jpeg,jpg,pdf',
    ]);

    ... // update validated document
}

We can also validate doc and docx extension by adding them in validation mimes.

'file' => 'required|max:10000|mimes:png,jpeg,jpg,pdf,doc,docx',

This tutorial is on github. You can download code this tutorial from here.

I hope that you enjoyed this tutorial on file/image upload with validation in laravel 6. Let me know if you have any issue regarding this tutorial. Please do share with your friends. Thank you guys.

About Chintan Panchal

I am web developer having 4+ year of experience of web development. I have worked on codeigniter, symfony, laravel and wordpress.

View all posts by Chintan Panchal →

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.