En esta entrada, vamos a crear la página de editar, también daremos la posibilidad de eliminar posts a sus creadores y, por último, explicaremos y utilizaremos los soft deletes.
Editar
Primero, vamos a crear la página de edición de posts. Vamos a la vista posts.blade.php y añadiremos el enlace dentro del bucle que muestra los posts. Podremos la restricción de que solamente se pueda ver el enlace, si el usuario creador del post es el mismo que está logueado.
1 2 3 |
@if (Auth::check() && $post->user_id == Auth::id()) <a href="{{URL::to('/')}}/post/{{ $post->id }}/edit">Edit</a> @endif |
Ahora tenemos que crear las rutas en el archivo de rutas. Una ruta es la que muestra la vista con los inputs (GET) y la otra (PATCH) es la que actualiza los datos:
1 2 |
Route::get('/post/{id}/edit', 'PostsController@edit'); Route::patch('/post/{id}', 'PostsController@update')->name('posts.update'); |
Le estamos dando un nombre a la ruta porque después lo utilizaremos en el formulario de editar.
Una vez ya tenemos las rutas, tenemos que crear los métodos a los que hacen referencia las rutas:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public function edit(Request $request) { $post = Post::findOrFail($request->id); return view('post_edit', ['post' => $post]); } public function update(PostRequest $request) { $post = Post::findOrFail($request->id); $post->update([ 'title' => $request->title, 'body' => $request->body ]); return redirect('posts'); } |
El primer método (edit) lo que hace es según el id que se pasa, enviar los datos a la vista para mostrarlos en los inputs, si no encuentra ningún posts con ese id, muestra un error.
El segundo método actualiza el post con los datos enviados desde los inputs de la vista y redirige al listado.
Nos falta la vista de editar, vamos a crearla, se llamará post_edit.blade.php y contendrá:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@extends('layouts.app') @section('content') @include('includes.errors') {{ Form::model($post, ['route' => ['posts.update', $post->id], 'method' => 'PATCH']) }} <p>{{ Form::text('title', old('title')) }}</p> <p>{{ Form::textarea('body', old('body')) }}</p> <p>{{ Form::submit('Save', ['name' => 'submit']) }}</p> {{ Form::close() }} @endsection |
Si te fijas, estamos mostrando el formulario de otra manera. Lo estamos haciendo con las clases que proporciona el paquete HTML & Forms. Le pasamos la variable $post para que muestre en los inputs la información que hay en la base de datos y se enviará a la ruta llamada posts.update con el id del post a través del método HTTP PATCH.
Con esto ya podríamos editar los posts, pero hay problema cuando no se edita el título. En nuestra validación pusimos que el título tiene que ser único, entonces si no cambia, detecta que ya existe y nos salta un error. Para solventar esto, debemos modificar el PostRequest y dejar la línea así:
1 2 3 |
... 'title' => 'required|unique:posts,title,'.$this->id.'|max:255', ... |
Eliminar
Añadimos este código debajo del link de editar en la vista:
1 2 3 4 5 6 |
<form action="{{URL::to('/')}}/post/{{ $post->id }}" method="POST"> {{ csrf_field() }} {{ method_field('DELETE') }} <button type="submit">Delete</button> </form> |
Añadimos la ruta:
1 |
Route::delete('/post/{id}', 'PostsController@destroy'); |
Creamos el método en el controlador:
1 2 3 4 5 6 |
public function destroy(Request $request) { $post = Post::findOrFail($request->id); $post->delete(); return redirect('posts'); } |
Y así de fácil los usuarios ya podrían eliminar sus posts.
Actualizamos los middlewares para que no puedan acceder los usuarios que no queremos (los no logueados y los que no son creadores del post).
Nota: ésto se puede hacer de otras maneras que son mejores, (por ejemplo, con policies que trataremos en el siguiente post).
1 2 3 4 |
public function __construct() { $this->middleware('auth', ['only' => ['store', 'edit', 'update', 'destroy']]); $this->middleware('owner', ['only' => ['edit', 'update', 'destroy']]); } |
Soft delete
De esta manera se eliminan permanentemente de la base de datos, Laravel tiene una función que es muy sencilla de agregar que evita eso. Simplemente los marca como eliminados, pero no borra el registro. Esto se conoce como Soft delete.
Primero de todo, como siempre que tenemos que modificar la estructura de la base de datos, debemos crear una migración:
1 |
php artisan make:migration add_softdelete_to_posts |
Abrir el archivo creado y añadir lo siguiente:
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 |
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class AddSoftdeleteToPosts extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('posts', function(Blueprint $table) { $table->softDeletes(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('posts', function(Blueprint $table) { $table->dropSoftDeletes(); }); } } |
Ejecutamos la migración:
1 |
php artisan migrate |
Cuando ya tenemos el campo, simplemente, deberemos añadir estas líneas al modelo Post.php para activar los soft deletes:
1 2 3 4 5 6 7 8 |
//arriba del todo use Illuminate\Database\Eloquent\SoftDeletes; ... use SoftDeletes; protected $dates = ['deleted_at']; ... |
Hola muy buen tutorial gracias por el aporte, solo una duda en el parte donde generas el formulario de post_edit me manda un mensaje de error: la clase Input no existe,
{{ Form::text(‘title’, Input::old(‘title’)) }}
se la quite y corre bien ,
{{ Form::text(‘title’, old(‘title’)) }}
Tendré algún inconveniente a futuro.
Saludos
Hola Carlos,
Muchas gracias por tu comentario.
No te funcionaba porque te faltaba añadir la Facade Input en el array aliases del archivo config/app.php.
En realidad hacen lo mismo, lo que pasa es que he estado mirando la documentación de Laravel (sección Retrieving Old Input) y utilizan el helper old(). También he visto en StackOverflow que la Facade Input a partir de la versión 5.2 pasó a ser Request.
Por tanto, he actualizado el post para seguir lo que pone en la documentación de Laravel.
Saludos!