Toca manejar imágenes. Un usuario podrá subir opcionalmente una imagen al crear o modificar una nota, ya sea desde una url o desde un archivo de su dispositivo (entendiendo como dispositivo desde un movil/tablet/ordenador). Para ello vamos a:
- Alterar la tabla de notas para añadir un nuevo campo de imagen.
- Instalar paquete Intervention Image.
- Configurar el servidor Apache para el manejo de imágenes.
- Configurar Laravel para el guardado de imágenes.
- Crear un Trait para poder usar sus funciones dentro cualquier clase que la «use».
1 |
php artisan make:migration add_images_to_notes |
Abrimos el archivo y editamos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
use Illuminate\Database\Migrations\Migration; use Illuminate\Support\Facades\Schema; class AddImagesToNotes extends Migration { public function up() { Schema::table('notes', function ($table) { $table->string('image')->default(''); $table->string('thumbnail')->default(''); }); } public function down() { Schema::table('notes', function ($table) { $table->dropColumn('image'); $table->dropColumn('thumbnail'); }); } } |
Instalamos el paquete para el manejo de imagenes:
1 |
composer require intervention/image |
Añadimos el provider y el alias:
1 2 3 4 5 6 7 8 9 10 11 |
return [ ...... 'provides' => [ ...... Intervention\Image\ImageServiceProvider::class, ], 'aliases' => [ ..... 'Image' => Intervention\Image\Facades\Image::class, ] ] |
Para no tener error en los «mime type» hay que descomentar esta linea:
1 2 3 4 5 6 7 |
# Si usas laragon la ruta es C:\laragon\bin\php\php.ini # Si usas xampp la ruta es C:\xampp\php\php.ini # quitar el punto y coma de esta linea extension=php_fileinfo.dll # Guardar archivo y reiniciar Apache |
Vamos a editar el archivo de configuracion de guardado de archivos:
1 2 3 4 5 6 7 8 9 10 11 12 |
'disks' => [ 'thumbnails' => [ 'driver' => 'local', 'root' => public_path('thumbnails'), ], 'originals' => [ 'driver' => 'local', 'root' => public_path('images'), ], . . . . ], |
Ahora, a por el Trait. Tienes la definición aquí :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
<?php namespace App\Traits; use Illuminate\Http\Exceptions\HttpResponseException; trait Upload { public function upload($file) { if ($file === "" || $file === null) { return ['original' => null, 'thumbnail' => null]; } switch (gettype($file)) { case 'string':return $this->uploadFromUrl($file); break; case 'object':return $this->uploadFromFile($file); break; } } public function uploadFromFile($file) { $name = time() . '_' . str_random(8) . '_' . $file->getClientOriginalName(); $path = \Storage::disk('thumbnails')->getDriver()->getAdapter()->getPathPrefix(); try { \Storage::disk('originals')->put($name, \File::get($file)); \Image::make($file->getRealPath()) // ->fit(100, 100) esto es recortar ->resize(100, 100, function ($constraint) { $constraint->aspectRatio(); }) ->save($path . '\\' . $name) ->destroy(); } catch (\Intervention\Image\Exception\NotWritableException $e) { throw new HttpResponseException(response()->error('error_saving_image_from_file', 422)); } return [ 'original' => "/" . "images/" . $name, 'thumbnail' => "/" . "thumbnails/" . $name, ]; } public function uploadFromUrl($file) { $originalPath = \Storage::disk('originals')->getDriver()->getAdapter()->getPathPrefix(); $thumbnailPath = \Storage::disk('thumbnails')->getDriver()->getAdapter()->getPathPrefix(); $elems = explode("/", $file); $name = time() . '_' . str_random(8) . '_'; if (sizeof($elems) > 1) { $name = $name . array_pop($elems); } try { \Image::make($file)->save($originalPath . "\\" . $name); \Image::make($file) ->resize(100, 100, function ($constraint) { $constraint->aspectRatio(); }) ->save($thumbnailPath . "\\" . $name); } catch (\Intervention\Image\Exception\NotWritableException $e) { throw new HttpResponseException(response()->error('error_saving_image_from_url', 422)); } return [ 'original' => 'images/' . $name, 'thumbnail' => 'thumbnails/' . $name, ]; } } |
Uso del Trait. Lo actualizaremos primero los métodos store y update en el controlador de Notas y más adelante en el de Usuarios:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
<?php namespace App\Http\Controllers\Api\V1; . . . . use App\Traits\Upload; class NoteController extends Controller { use Upload; . . . . public function store(Request $request) { try { $url = $this->upload($request->image) ; $note = $this->user->notes()->create([ 'title' => $request->title, 'content' => $request->content, 'image' => $url['original'], 'thumbnail' => $url['thumbnail'], ]); return response()->success(compact('note')); } catch (Exception $e) { return response()->error('error_creating_note', $e->getStatusCode()); } } public function update(Request $request, $id) { $note = Note::findOrFail($id); if (!$this->user->isAuthor($note)) { return response()->error('unauthorized', 401); } $url = $this->upload($request->image); $note->title = $request->title; $note->content = $request->content; $note->image = $url['original']; $note->thumbnail = $url['thumbnail']; try { $note->save(); } catch (Exception $e) { return response()->error('error_updating_note', $e->getStatusCode()); } return response()->success(compact('note')); } } |
Por último pero no menos importante, hay que tener que en cuenta que la subida de imágenes no se puede hacer desde el verbo PUT, por tanto, sobreescribiremos la ruta del verbo PUT por la POST y eso es tan fácil como añadir una ruta post justo encima de la del resource, tal que:
1 2 3 4 5 6 7 8 9 10 11 12 |
Route::group(['guard' => 'api'], function () { . . . . Route::group(['middleware' => 'auth:api'], function () { Route::group(['prefix' => 'v1'], function () { Route::post('notes/{note}', 'Api\V1\NoteController@update'); Route::resource('notes', 'Api\V1\NoteController', ['except' => ['create', 'edit']]); }); }); }); |
Para aclarar, desde el Front-End las peticiones de actualización que incluyan imágenes se harán desde el verbo POST, en caso contrario, se harán desde el verbo PUT, y en ambos casos… el método en el servidor será el update. Haremos referencia a este post cuando estemos en la parte de Angular.
Fuente:
Resize Images In Laravel 5.1 using intervention
Image upload using storage function to public folder