Monday, November 18, 2019

Laravel : How to Upload Multiple Images and Files in Laravel with Validation

File upload is an essential aspect of any project. Given this importance, it is surprising that many developers face challenges of adding file upload feature to their projects. In particular, developers are unsure about how to upload and validate files.
In this tutorial, I will discuss how to implement Laravel file upload functionality with multiple file and image uploading option. I will use the Laravel storage folder and then create database record for uploading files. I will use Laravel 5.5 and Bootstrap to power the code of this tutorial.
You might also like: PHP File Upload with jQuery AJAX
I have installed a Laravel app on a Cloudways managed Laravel server because it has everything I’ll need for this tutorial. If you do not have an account on Cloudways, sign up for free, and check out the following GIF to setup the server and application in just a few clicks.
Create Model with Migration
I will start by creating the model and the tables in which I will save the files.
Launch the SSH terminal, go to the application’s public root folder and type following commands:
  1. php artisan make:model Item -m
  2. php artisan make:model ItemDetails -m

Item Model

When the migration and the model have been created successfully, go to app/Item.php and add the following Model code to it:
  1. <?php
  2. namespace App;
  3. use Illuminate\Database\Eloquent\Model;
  4. class Item extends Model
  5. {
  6. protected $fillable = ['name'];
  7. }

Create the Migration

Go to the database/migration folder and open the migration file for item. You will see the default structure that include (in my case) id , name, timestamps.
  1. <?php
  2. use Illuminate\Support\Facades\Schema;
  3. use Illuminate\Database\Schema\Blueprint;
  4. use Illuminate\Database\Migrations\Migration;
  5. class CreateItemsTable extends Migration
  6. {
  7. public function up()
  8. {
  9. Schema::create('items', function (Blueprint $table) {
  10. $table->increments('id');
  11. $table->string('name');
  12. $table->timestamps();
  13. });
  14. }
  15. public function down()
  16. {
  17. Schema::drop('items');
  18. }
  19. }?>

Model Of ItemDetails

The model comprises of the following code:
  1. <?php
  2. namespace App;
  3. use Illuminate\Database\Eloquent\Model;
  4. class ItemDetail extends Model
  5. {
  6. protected $fillable = ['item_id', 'filename'];
  7. public function item()
  8. {
  9. return $this->belongsTo('App\Item');
  10. }
  11. }
  12. ?>
In the above code, I used belongTO because itemDetails belongs to Item table and item_id is the foreign key. This is known as inverse relation in Laravel.

Migration of ItemDetails Table

Go to the database/migration folder and open the migration file for itemdetails. You will see the default structure that include id , name . timestamps.
  1. <?php
  2. use Illuminate\Support\Facades\Schema;
  3. use Illuminate\Database\Schema\Blueprint;
  4. use Illuminate\Database\Migrations\Migration;
  5. class CreateItemDetailsTable extends Migration
  6. {
  7. /**
  8. * Run the migrations.
  9. *
  10. * @return void
  11. */
  12. public function up()
  13. {
  14. Schema::create('item_details', function (Blueprint $table) {
  15. $table->increments('id');
  16. $table->integer('item_id')->unsigned();
  17. $table->foreign('item_id')->references('id')->on('items');
  18. $table->string('filename');
  19. $table->timestamps();
  20. });
  21. }
  22. public function down()
  23. {
  24. Schema::drop('item_details');
  25. }
  26. }
  27. ?>

Next , In the app/Providers/AppServiceProvider.php file, the boot method set a default string length:
  1. use Illuminate\Support\Facades\Schema;
  2. public function boot()
  3. {
  4. Schema::defaultStringLength(191);
  5. }

Database Configuration

In a Laravel powered app, database configuration is handled by two files: env and config/database.php. In my case, I created a database with the name uploading. The Cloudways Database Manager makes the entire process very easy.
Next, run the following command in the terminal to create tables in the database:
php artisan migrate
Now, when you check the database, you will see that the tables have been created  successfully.

Set up the Route

Route sets the application URL and the controller method for this URL. Routes are located in route/web.php and contains the following code:
  1. Route::get('/multiuploads', 'UploadController@uploadForm');
  2. Route::post('/multiuploads', 'UploadController@uploadSubmit');
create the Controller by using the following command:
php artisan make:controller UploadController
Next, go to app/Http/Controller/UploadController and open the Controller file. Add the following code to it:
  1. namespace App\Http\Controllers;
  2. use Illuminate\Http\Request;
  3. class UploadController extends Controller
  4. {
  5. public function uploadForm()
  6. {
  7. return view('upload_form');
  8. }
  9. public function uploadSubmit(Request $request)
  10. {
  11. // coding ….
  12. }
  13. }

View File (Upload_form.blade.php)

In the view file, I have used Bootstrap for styling the code, link stylesheet , jQuery, JavaScript files.
  1. <!doctype html>
  2. <html lang="{{ app()->getLocale() }}">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <title>Laravel Uploading</title>
  8. <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
  9. <!-- Optional theme -->
  10. <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
  11. <!-- Fonts -->
  12. <link href="https://fonts.googleapis.com/css?family=Raleway:100,600" rel="stylesheet" type="text/css">
  13. <!-- Styles -->
  14. <style>
  15. .container {
  16. margin-top:2%;
  17. }
  18. </style>
  19. </head>
  20. <body>
  21. @if (count($errors) > 0)
  22. <div class="alert alert-danger">
  23. <ul>
  24. @foreach ($errors->all() as $error)
  25. <li>{{ $error }}</li>
  26. @endforeach
  27. </ul>
  28. </div>
  29. @endif
  30. <div class="container">
  31. <div class="row">
  32. <div class="col-md-2"> <img src="/32114.svg" width="80" /></div>
  33. <div class="col-md-8"><h2>Laravel Multiple File Uploading With Bootstrap Form</h2>
  34. </div>
  35. </div>
  36. <br>
  37. <div class="row">
  38. <div class="col-md-3"></div>
  39. <div class="col-md-6">
  40. <form action="/multiuploads" method="post" enctype="multipart/form-data">
  41. {{ csrf_field() }}
  42. <div class="form-group">
  43. <label for="Product Name">Product Name</label>
  44. <input type="text" name="name" class="form-control" placeholder="Product Name" >
  45. </div>
  46. <label for="Product Name">Product photos (can attach more than one):</label>
  47. <br />
  48. <input type="file" class="form-control" name="photos[]" multiple />
  49. <br /><br />
  50. <input type="submit" class="btn btn-primary" value="Upload" />
  51. </form>
  52. </div>
  53. </div>
  54. </div>
  55. </body>
  56. </html>

Controller with Validation

I have use Bootstrap classes for showing the alert for validation and use Laravel Validation methods to validate the type of file. Use the following code for the controller:
  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Item;
  4. use App\ItemDetail;
  5. use Illuminate\Http\Request;
  6. class UploadController extends Controller
  7. {
  8. public function uploadForm()
  9. {
  10. return view('upload_form');
  11. }
  12. public function uploadSubmit(Request $request)
  13. {
  14. $this->validate($request, [
  15. 'name' => 'required',
  16. 'photos'=>'required',
  17. ]);
  18. if($request->hasFile('photos'))
  19. {
  20. $allowedfileExtension=['pdf','jpg','png','docx'];
  21. $files = $request->file('photos');
  22. foreach($files as $file){
  23. $filename = $file->getClientOriginalName();
  24. $extension = $file->getClientOriginalExtension();
  25. $check=in_array($extension,$allowedfileExtension);
  26. //dd($check);
  27. if($check)
  28. {
  29. $items= Item::create($request->all());
  30. foreach ($request->photos as $photo) {
  31. $filename = $photo->store('photos');
  32. ItemDetail::create([
  33. 'item_id' => $items->id,
  34. 'filename' => $filename
  35. ]);
  36. }
  37. echo "Upload Successfully";
  38. }
  39. else
  40. {
  41. echo '<div class="alert alert-warning"><strong>Warning!</strong> Sorry Only Upload png , jpg , doc</div>';
  42. }
  43. }
  44. }
  45. }
  46. }?>

Storing Data and Files in Laravel

Laravel provides a storage filesystem that stores all the data including files and images.
For this, Laravel provides config/filesystems.php, located in the config folder. In this file, you can specify the locations for your file storage.
  1. return [
  2. 'default' => 'local',
  3. 'disks' => [
  4. 'local' => [
  5. 'driver' => 'local',
  6. 'root' => storage_path('app'),
  7. ],
  8. // ...

Using the above code snippet, you could save the files in app/storage folder instead of the public folder. This is a good coding practice for storing data because this location is inaccessible from the browser. For the purpose of this tutorial, I have created a folder with the name photos in storage/app/.
When the run the app in the browser, you will see the following screens:

With Validation

laravel file upload validation
laravel multiple file upload form
laravel file upload error
To see the image and file upload in Laravel in action, check out the demo.

Difference Between Local and Public Disks

You can see the disks local and public defined in config/filesystems.php. Laravel uses the local disk configuration by default. The underlying difference between local and public disk is that local disk is private and can’t be accessed from the browser, whereas the public disk can be easily accessed from the browser.
Since the public disk is in storage/app/public and Laravel’s server root is in public, you need to link storage/app/public to Laravel’s public folder. We can do by running php artisan storage:link.

Manipulating files

Laravel largely needs external help in resizing images, adding filters and other related operations. Adding these feature to the native environment of Laravel will only bloat the application since there isn’t any installs needed. For that, we need a package called intervention/image. Earlier above, we have already installed this package, but still composer requires it for reference.
We don’t need to register anything since Laravel can automatically detect packages. Read the following if you are using lesser version than Laravel 5.5.
To resize an image
  1. $image = Image::make(storage_path('app/public/profile.jpg'))->resize(300, 200);
Even Laravel’s packages are fluent.

Sending Files as Email Attachments

For send files with attachments you just paste the following code in your controller according to input fields.
  1. <?php
  2. ...
  3. ...
  4. public function uploadDocument(Request $request) {
  5. $title = $request->file('name');
  6. // Get the uploades file with name document
  7. $document = $request->file('document');
  8. // Required validation
  9. $request->validate([
  10. 'name' => 'required|max:255',
  11. 'document' => 'required'
  12. ]);
  13. // Check if uploaded file size was greater than
  14. // maximum allowed file size
  15. if ($document->getError() == 1) {
  16. $max_size = $document->getMaxFileSize() / 1024 / 1024; // Get size in Mb
  17. $error = 'The document size must be less than ' . $max_size . 'Mb.';
  18. return redirect()->back()->with('flash_danger', $error);
  19. }
  20. $data = [
  21. 'document' => $document
  22. ];
  23. // If upload was successful
  24. // send the email
  25. $to_email = test@example.com;
  26. \Mail::to($to_email)->send(new \App\Mail\Upload($data));
  27. return redirect()->back()->with('flash_success', 'Your document has been uploaded.');
  28. }

Create email sender class

To send the email in Laravel, you need to create a separate class file. This class will have the functionality to prepare email and its body. Also we will attach the uploaded file as inline attachment.
Here is my email class:
  1. <?php
  2. #App\Mail\Upload.php
  3. namespace App\Mail;
  4. use Illuminate\Bus\Queueable;
  5. use Illuminate\Mail\Mailable;
  6. use Illuminate\Queue\SerializesModels;
  7. class Upload extends Mailable
  8. {
  9. use Queueable, SerializesModels;
  10. protected $data;
  11. /**
  12. * Create a new message instance.
  13. *
  14. * @return void
  15. */
  16. public function __construct($data=[])
  17. {
  18. $this->data = $data;
  19. }
  20. /**
  21. * Build the message.
  22. *
  23. * @return $this
  24. */
  25. public function build()
  26. {
  27. return $this->view('emails/upload')
  28. ->subject('Document Upload')
  29. ->attach($this->data['document']->getRealPath(),
  30. [
  31. 'as' => $this->data['document']->getClientOriginalName(),
  32. 'mime' => $this->data['document']->getClientMimeType(),
  33. ]);
  34. }
  35. }
The important functions used here are:
  1. getRealPath(): Get the temporary upload path
  2. getClientOriginalName(): Get the name of uploaded file
  3. getClientMimeType(): Get the mime type of uploaded file

Create the email template

In the above step, our email class refers to the email template as return $this->view(’emails/upload’). So we will create the email template at resources/views/emails/upload.blade.php
  1. #resources/views/emails/upload.blade.php
  2. <p>Hi,</p>
  3. <p>Please download the attached file.</p>
  4. <p>Thanks</p>
Now when you submit the form, your uploaded file will be sent an email attachment.

No comments:

Post a Comment