post-delete-sin-formularios-laravel-jquery

Al igual que una petición de tipo POST, las peticiones PUT y DELETE se realizan mediante el envío de la data a través de formularios, pero en ocasiones esto resulta ser muy poco práctico debido a la cantidad de código que debemos escribir para crear dicho formulario. Por ejemplo si tenemos un listado de productos en una lista que cuenta con un botón de eliminar; en este caso deberíamos crear un formulario por cada botón y es por ello que muchos acaban simplemente creando una ruta de tipo GET que reciba el id del objeto que queremos eliminar. Esto compromete un poco la seguridad o el correcto funcionamiento de nuestra aplicación, debido a que, en teoría, podríamos eliminar datos solo con ingresar una url en el navegador. Vamos a ver como solucionar este problema.

Veamos el siguiente ejemplo

Iniciemos creando un nuevo proyecto de Laravel.

$ composer create-project laravel/laravel styde

Creamos una base de datos y configuramos las credenciales en el archivo .env del proyecto

DB_HOST=localhost
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret

Editamos el archivo /database/seeds/DatabaseSeeder.php usando Model Factorya para crear algunos usuarios de prueba en la base de datos.

public function run()
{
    factory('App\User', 10)->create();
}

Ejecutamos la migración y el seed para crear la base de datos y los 10 registros de prueba.

$ php artisan migrate --seed

Creamos una vista para mostrar la lista de registros (layout.blade.php), en este caso usaremos Bootstrap.

<html lang="en">
  <head>
    <title>Styde.Net</title>
    <!-- Bootstrap -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
  </head>
  <body>
  <div class="container">
    <table class="table table-bordered table-striped table-responsive">
      <thead>
        <tr>
          <td>Name</td>
          <td>Action</td>
        </tr>
      </thead>

      <tbody>
        @foreach($users as $user)
          <tr>
            <td>{{ $user->name }}</td>
            <td><a href="#">Delete</a></td>
          </tr>
        @endforeach
      </tbody>
    </table>
  </div>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
  </body>
</html>

Finalmente una ruta para poder acceder a esta vista editando el archivo app/Http/routes.php

Route::get('users', function () {
    $users = App\User::all();
    return view('layout')
        ->with('users', $users);
});

El resultado en el navegador (url/users) seria algo similar a esto.

Capture

Aqui es donde enfrentamos el problema. Si deseamos agregar la logica para eliminar podríamos hacer los siguiente.

@foreach($users as $user)
  <tr>
    <td>{{ $user->name }}</td>
    <td>
      <form name="delete" method="DELETE" action="delete">
        {{ csrf_field() }}
        <input type="hidden" name="id" value="{{ $user->id }}">
      <button type="submit">Delete</button>
      </form>
    </td>
  </tr>
@endforeach

Para que esto funcione debemos agregar una ruta que maneje la lógica del borrado

Route::delete('user/{id}', function ($id) {
    $user = App\User::find($id)->delete();
    return Redirect::back();
});

En este ejemplo $users tiene 10 registros, estaríamos imprimiendo 10 veces el mismo código para cada formulario que contiene a cada botón «delete». Realmente esto acaba siendo algo molesto, ademas el hecho de crear un formulario para enviar un solo campo, parece no ser muy practico, por suerte, no eres la primera persona a la que le pasa esto. Esta es una solución que ofrece un programador que tal vez conozcas si trabajas con Laravel: @Jeffrey_Way.

Ingresa al siguiente link y descarga el archivo de javascript.

Enlace: Laravel.js

Una vez descargado agrégalo en la carpeta public de tu proyecto, por ejemplo: /public/js/laravel.js.

Para poder usar este script debes agregar el link al template de tu proyecto (layout.blade.php)

<script src="/js/laravel.js"></script>

Para usar este script es necesario tener jquery.js

Finalmente, podemos crear un link que envié esta petición de la siguiente manera:

@foreach($users as $user)
  <tr>
    <td>{{ $user->name }}</td>
    <td>
       <a href="{{  url('user', [$user->id])}}  " class="btn btn-danger" data-method="delete" data-confirm="Are you sure?">Delete</a>
    </td>
  </tr>
@endforeach

Si hacemos click en «Delete» veremos un mensaje en pantalla preguntando por confirmación.

Capture2

¿Cual es la ventaja de usar este script ? si ingresas desde el navegador a la url /user/2, vas a obtener un error por que dicha ruta no existe, debido a que estas realizando una peticion GET y la ruta que creamos es de tipo DELETE, por tanto, estas agregando algo de seguridad restringiendo el borrado de datos con peticiones GET.

¿Cómo funciona el script?

Se captura el evento click sobre los links que contengan el atributo «data-method»

this.methodLinks = $('a[data-method]');

Se evalúa si el método que enviamos es PUT o DELETE

 if ( $.inArray(httpMethod, ['PUT', 'DELETE']) === - 1 ) {
    return;
  }

Javascript crea el formulario por nosotros incluyendo el campo csrf_token

createForm: function(link) {
      var form = 
      $('<form>', {
        'method': 'POST',
        'action': link.attr('href')
      });

      var token = 
      $('<input>', {
        'type': 'hidden',
        'name': 'csrf_token',
          'value': '<?php echo csrf_token(); ?>' 
        });

      var hiddenInput =
      $('<input>', {
        'name': '_method',
        'type': 'hidden',
        'value': link.data('method')
      });

      return form.append(token, hiddenInput)
                 .appendTo('body');
    }

Como puedes ver, es una solución elegante a un problema común. Tienes algún otro tip que quieras compartir con la comunidad ? Forma parte de nuestra comunidad, contribuye con una publicación y ayuda a otros programadores.

Esto ha sido todo por ahora, espero que te haya gustado esta lección, si tienes alguna duda puedes dejarla en la sección de comentarios.

Material relacionado

Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.

Lección anterior Como integrar plantilla AdminLTE en Laravel Lección siguiente Cómo organizar el código en PhpStorm siguiendo el estándar PSR-2