
Hola, bienvenido(a), al Post 11 del Taller de Desarrollo de una Tienda en línea con Bootstrap, Laravel 5.1 y la API de Paypal.
Continuamos desarrollando el Panel de Administración de nuestra tienda en línea.
Hoy vamos a finalizar el desarrollo de nuestro panel, crearemos la parte de Pedidos y pondremos unas restricciones de acceso al Panel, para que solo puedan ingresar los usuarios que hayan iniciado sesión y que tengan el rol de administrador:
Creamos nuestro controlador, esto lo hacemos desde la línea de comandos:
1 |
php artisan make:controller Admin/OrderController --plain |
Para los pedidos no vamos a usar RESTful porque solo nos interesa ver el listado de pedidos, el detalle y poder eliminarlos.
Ahora registramos las peticiones que vamos a usar en nuestro router:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Obtenemos todos los pedidos Route::get('orders', [ 'as' => 'admin.order.index', 'uses' => 'OrderController@index' ]); // Eliminamos un pedido en base a su id Route::delete('order/{id}', [ 'as' => 'admin.order.destroy', 'uses' => 'OrderController@destroy' ]); // Petición que se hará vía ajax Route::post('order/get-items', [ 'as' => 'admin.order.getItems', 'uses' => 'OrderController@getItems' ]); |
Necesitamos establecer las relaciones que existen entre nuestros recursos para poder simplificar las consultas, estas relaciones las establecemos en los modelos.
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 |
// Relación User -> Order public function orders() { return $this->hasMany('App\Order'); } // Relación Order -> User public function user() { return $this->belongsTo('App\User'); } // Relación Order -> OrderItem public function order_items() { return $this->hasMany('App\OrderItem'); } // Relación OrderItem -> Order public function order() { return $this->belongsTo('App\Order'); } // Relación OrderItem -> Product public function product() { return $this->belongsTo('App\Product'); } // Relación Product -> OrderItem public function order_item() { return $this->hasOne('App\OrderItem'); } |
PEDIDOS
Para mostrar todos los pedidos recibidos usaremos el método index de nuestro controller, ahí obtenemos los pedidos ordenados por id de forma descendente y de 5 en 5 (para realizar la paginación), se los pasamos a la vista index:
1 2 3 4 5 |
public function index() { $orders = Order::orderBy('id', 'desc')->paginate(5); return view('admin.order.index', compact('orders')); } |
Creamos en la carpeta admin dentro de views una carpeta llamada order y dentro el archivo index.blade.php, en el que mostraremos una tabla con todos nuestros pedidos:
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 |
@extends('admin.template') @section('content') <div class="container text-center"> <div class="page-header"> <h1> <i class="fa fa-shopping-cart"></i> PEDIDOS </h1> </div> <div class="page"> <div class="table-responsive"> <table class="table table-striped table-bordered table-hover"> <thead> <tr> <th>Ver Detalle</th> <th>Eliminar</th> <th>Fecha</th> <th>Usuario</th> <th>Subtotal</th> <th>Envio</th> <th>Total</th> </tr> </thead> <tbody> @foreach($orders as $order) <tr> <td> <a href="#" class="btn btn-primary btn-detalle-pedido" data-id="{{ $order->id }}" data-path="{{ route('admin.order.getItems') }}" data-toggle="modal" data-target="#myModal" data-token="{{ csrf_token() }}" > <i class="fa fa-external-link"></i> </a> </td> <td> {!! Form::open(['route' => ['admin.order.destroy', $order->id]]) !!} <button onClick="return confirm('Eliminar registro?')" class="btn btn-danger"> <i class="fa fa-trash-o"></i> </button> {!! Form::close() !!} </td> <td>{{ $order->created_at }}</td> <td>{{ $order->user->name . " " . $order->user->last_name }}</td> <td>${{ number_format($order->subtotal,2) }}</td> <td>${{ number_format($order->shipping,2) }}</td> <td>${{ number_format($order->subtotal + $order->shipping,2) }}</td> </tr> @endforeach </tbody> </table> </div> <hr> <?php echo $orders->render(); ?> </div> </div> @include('admin.partials.modal-detalle-pedido') @stop |
Con esto nuestra vista index se vera así:
También necesitamos crear la vista parcial modal-detalle-pedido, esta vista contendrá el código de bootstrap para crear ventanas modales.
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 |
<!-- Modal --> <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title" id="myModalLabel">Detalle del Pedido: </h4> </div> <div class="modal-body"> <div class="table-responsive"> <table class="table table-stripped table-bordered table-hover" id="table-detalle-pedido"> <thead> <tr> <th>Imagen</th> <th>Producto</th> <th>Precio</th> <th>Cantidad</th> <th>Subtotal</th> </tr> </thead> <tbody> </tbody> </table> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">Cerrar</button> </div> </div> </div> </div> |
DETALLE DEL PEDIDO
Ahora para implementar el detalle de cada pedido vamos a usar javascript, mediante jquery haremos una petición al server para que nos devuelva todos los items de un pedido y los mostraremos en una ventana modal (usando bootstrap).
El código del botón “Ver detalle”, guardará los datos que necesitamos usar desde javascript: el id del pedido, el path a donde haremos la petición, el token, la clase btn-detalle-pedido (que nos permitirá ubicar cada botón) y otros datos necesarios para usar la ventana modal:
1 2 3 4 5 6 7 8 9 10 11 |
<a href="#" class="btn btn-primary btn-detalle-pedido" data-id="{{ $order->id }}" data-path="{{ route('admin.order.getItems') }}" data-toggle="modal" data-target="#myModal" data-token="{{ csrf_token() }}" > <i class="fa fa-external-link"></i> </a> |
Creamos nuestro archivo javascript llamado main.js que guardaremos en la carpeta js dentro de admin en public, enlazamos este archivo desde nuestro template y el código que contendrá es el 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 33 34 35 36 37 38 39 40 41 42 43 44 |
$(document).ready(function(){ // Ver detalle de un Pedido $(".btn-detalle-pedido").on('click', function(e){ e.preventDefault(); var id_pedido = $(this).data('id'); var path = $(this).data('path'); var token = $(this).data('token'); var modal_title = $(".modal-title"); var modal_body = $(".modal-body"); var loading = '<p><i class="fa fa-circle-o-notch fa-spin"></i> Cargando datos</p>'; var table = $("#table-detalle-pedido tbody"); var data = {'_token' : token, 'order_id' : id_pedido}; modal_title.html('Detalle del Pedido: ' + id_pedido); table.html(loading); $.post( path, data, function(data){ console.log(data); table.html(""); for(var i=0; i<data.length; i++){ var fila = "<tr>"; fila += "<td><img src='" + data[i].product.image + "' width='30'></td>"; fila += "<td>" + data[i].product.name + "</td>"; fila += "<td>$ " + parseFloat(data[i].price).toFixed(2) + "</td>"; fila += "<td>" + parseInt(data[i].quantity) + "</td>"; fila += "<td>$ " + (parseFloat(data[i].quantity) * parseFloat(data[i].price)).toFixed(2) + "</td>"; fila += "</tr>"; table.append(fila); } }, 'json' ); }); }); |
Cada vez que se de click en un botón “Ver detalle”, sucederá lo siguiente (usando javascript):
- Se obtendrán todos los datos guardados en el botón
- Se hará una petición al server vía Ajax, usando la función $.post de jquery
- El server devolverá los items del pedido correspondiente en formato json
- Crearemos el html necesario por cada item y lo agregaremos a la tabla contenida en la ventana modal
- Mostraremos la ventana modal
El método getItems de nuestro controller es el que dará respuesta a la petición:
1 2 3 4 5 |
public function getItems(Request $request) { $items = OrderItem::with('product')->where('order_id', $request->get('order_id'))->get(); return json_encode($items); } |
Así se vera el detalle de un pedido, mostrado en una ventana modal:
ACCESO AL PANEL (RESTRICCIONES)
Nuestra aplicación tiene 2 áreas a las que es necesario haber iniciado sesión para poder ingresar y son el Detalle de un pedido (front) y el Panel de administración (back), además para el panel es necesario que el usuario tenga el rol de administrador, por lo que para establecer estas restricciones modificaremos el Middleware auth, esto en el archivo Authenticate.php, quedando de la siguiente forma:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public function handle($request, Closure $next) { if ($this->auth->guest()) { if ($request->ajax()) { return response('Unauthorized.', 401); } else { return redirect()->guest('auth/login'); } } if($request->path() == 'order-detail') return $next($request); if(auth()->user()->type != "admin"){ $message = 'Permiso denegado: Solo los administradores pueden entrar a esta sección'; return redirect()->route('home')->with('message', $message); } return $next($request); } |
Vamos a aplicar el middleware auth a todas las peticiones del panel en nuestro router, además de establecer el namespace y un prefijo, para hacerlo agrupamos nuestra peticiones:
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 |
// Admin Route::group(['namespace' => 'Admin', 'middleware' => ['auth:admin'], 'prefix' => 'admin'], function() { Route::get('home', function(){ return view('admin.index'); }); Route::resource('category', 'CategoryController'); Route::resource('product', 'ProductController'); Route::resource('user', 'UserController'); Route::get('orders', [ 'as' => 'admin.order.index', 'uses' => 'OrderController@index' ]); Route::get('order/{id}', [ 'as' => 'admin.order.destroy', 'uses' => 'OrderController@destroy' ]); Route::post('order/get-items', [ 'as' => 'admin.order.getItems', 'uses' => 'OrderController@getItems' ]); }); |
De esta forma si tratamos de entrar al panel y no hemos iniciado sesión, seremos redireccionados al formulario de login y si ya iniciamos sesión pero no tenemos el rol de administrador, seremos redireccionados al home y se nos mostrara un mensaje indicándonos que no tenemos acceso a esa sección.
En el siguiente vídeo puedes ver la explicación completa:
De esta forma hemos finalizado el desarrollo de nuestra tienda, tanto el front como el back, probablemente haga otro post sobre el deploy, (espero tener el tiempo suficiente).
Eso es todo para este post, compartanlo por favor y si es el ultimo les agradezco su tiempo, espero les haya sido de utilidad este taller y nos vemos en algún otro taller, suerte 🙂
Si les gusto el taller denle un like por favor a la página de facebook de linkdesigns.com.