Comunidad de diseño web y desarrollo en internet online

Controladores y MVC con Laravel

Hoy quiero que aprendamos sobre los controladores con Laravel. Cuando se habla del patrón MVC (Modelo-Vista-Controlador) se dice que:

Un controlador es la capa intermediaria encargada de invocar a los modelos y pasar datos a las vistas

Laravel es compatible con el patrón MVC (por ejemplo: en la carpeta apps/ encontramos las sub-carpetas controllers/ models/ y views/. Más adelante hablaré de MVC -pros y contras- en detalle.

¿Qué son los controladores?


Imaginen que en vez de una aplicación web estamos liderando un restaurante. Los comensales (Usuarios) son recibidos por nuestro anfitrión Laravel. Laravel se encarga de llevarlos dentro del restaurante (Aplicación) donde los esperan los mesoneros (Rutas).

Cada mesonero (Ruta) se encarga de tomar nota del pedido (Solicitud o Request) de cada comensal (Usuario) y de dirigirlo a la estación adecuada. Por ejemplo: si ordeno cocteles nuestro mesonero (ruta) llevará el pedido (request) al bar, si ordeno una pasta con albóndigas dicho pedido será llevado por el mesonero (ruta) a la estación de cocina italiana.

En este ejemplo, los Controladores son los encargados de cada estación (bar, comida italiana, comida mexicana, etc.).

Por ejemplo, nuestro maestro de cocina italiana que acaba de recibir el pedido de albóndigas y pasta, es un controlador.

¿De qué se encargará? ¿De cocinar? ¡Absolutamente NO! El controlador solamente debe encargarse de dirigir la operación. Por eso, debe disponer de un grupo de chefs (Repositorios o Modelos) a su cargo y solicitar a cada uno lo que corresponda.

Ok, sé que quieren aprender a programar, sé que quieren ver código, so, here we go:

Código :

<?php
//app/routes.php

Route::get('pasta-with-meatballs/{id_table}, {type}', array('as' => 'pasta_meatballs', 'uses' => 'ItalianController@pastaWithMeatBalls'))->where(‘id_table’, ‘[0-9]+’);


Código :

<?php
//app/controllers/ItalianController.php

class ItalianController extends KitchenController {

   protected $pastaRepo;
   protected $meatRepo;
   protected $sauceRepo;

   public function __construct(PastaRepoInterface $pasta, MeatRepoInterface $meat, SauceRepoInterface $sauce)
   {
      $this->pastaRepo  = $pasta; 
      $this->meatRepo   = $meat;
      $this->sauceRepo = $sauce;
   }

   public function pastaWithMeatballs($idTable, $type = ‘long’)
   {
      // Si el usuario pidió pasta larga cocinaremos espaguetis 
      if ($type == ‘long’)
      {
         $pasta = $this->pastaRepo->cookSpaghetti();
      }
      // Si el usuario pidió pasta corta cocinaremos rigatonis
      else if ($type == ‘short’)
      {
         $pasta = $this->pastaRepo->cookRigatoni();
      }
      else
      {
         return Redirect::route(‘table’, array($idTable)->with(‘message’, ‘Por favor elija pasta corta o pasta larga’);
      }

      $meatBalls = $this->meatRepo->cookMeatBalls();
      $napoliSauce = $this->sauceRepo->cookNapoliSauce();

      $food = compact(‘pasta’, ‘meatBalls’, ‘napoliSauce’);
   
      return View::make(‘dishes/italian’, $food);
   }
}


¿Interesante, no? Lamentablemente ni Laravel ni PHP pueden preparar comida por nosotros, eso sería genial, pero si leyeron en detalle el código descrito arriba y lo entienden han dado un gran paso para ser buenos chefs, quiero decir, programadores en Laravel.

Examinemos el código:

Cómo darle nombre a una ruta y enlazarla a un controlador


Código :

Route::get('pasta-with-meatballs/{id_table}/{type}', array('as' => 'pasta_meatballs', 'uses' => 'ItalianController@pastaWithMeatBalls'))->where(‘id_table’, ‘[0-9]+’);


Fíjense que ya el segundo parámetro no es una función anónima, como en los ejemplos anteriores.

Ahora es un array:

El parámetro “as” define el nombre de la ruta, suponiendo que escribimos nuestra vista views/menu.blade.php podremos colocar el enlace a nuestra ruta así:

Código :

<!-- app/views/menu.blade.php -->
<ul>
<li><a href=”{{ route(‘pasta_meatballs’, array($idTable, ‘long’)) }}”>Pasta larga</a></li>
<li><a href=”{{ route(‘pasta_meatballs’, array($idTable, ‘short’)) }}”>Pasta corta</a></li>
</ul>


O un redirect:

Código :

Redirect::route(‘pasta_meatballs’)


El nombre de la ruta NO es la URL De hecho, más adelante podríamos cambiar la URL a “pasta-con-albondigas” y el menú y el Redirect seguirán funcionando como si nada. Bastante útil.

El parámetro ‘uses’define el controlador y la acción que se van a usar , separados por una arroba:

Código :

'ItalianController@pastaWithMeatBalls'


Composer y Laravel saben cómo cargar nuestros controladores y otras clases. Más adelante hablaremos del Autoloader de Composer


Entonces una vez que nuestro usuario haga click en los ítems “Pasta larga” o “Pasta corta” de nuestro menú Laravel armará por nosotros la Estación de comida italiana ItalianController y asignará los chefs (clases, repositorios) para la pasta, la carne y la salsa, y una vez que esté todo listo llamará a la función / acción pastaWithMeatBalls.

Cómo funciona un constructor en los controladores de Laravel


Revisemos de nuevo el constructor de nuestra clase ItalianController:

Código :

   public function __construct(PastaRepoInterface $pasta, MeatRepoInterface $meat, SauceRepoInterface $sauce)
   {
      $this->pastaRepo  = $pasta; 
      $this->meatRepo   = $meat;
      $this->sauceRepo = $sauce;
   }

Un constructor es un método especial de PHP que se llama cada vez que creamos un objeto


¿Pero de dónde salen esos parámetros $pasta, $meat, $sauce?

¡Laravel los crea y asigna por nosotros automáticamente!, siempre que las interfaces existan y haya al menos una clase que implemente dicha interfaz. Lo único que necesitamos hacer es decirle a Laravel qué clases van a ser usadas por cada interfaz:

Código :

// al final de app/start/globals.php agregaríamos lo siguiente:
App::bind(‘PastaRepoInterface’, ‘PastaRepo’);
App::bind(‘MeatRepoInterface’, ‘MeatRepo’);
App::bind(‘SauceRepoInterface’, ‘SauceRepo’);


En nuestra carpeta app/models/ podemos programar los repositorios y modelos y también las interfaces

Qué es una Interfaz


Una interfaz, es como un contrato que permite definir los métodos (funcionalidades) mínimos que una clase debe tener. En sí NO implementa código, sólo sirve para definir los requerimientos de las clases de cierto tipo. En nuestro ejemplo, los requisitos mínimos que nuestro restaurante exige para contratar a un chef de pasta, serían:

Código :

// app/models/PastaRepoInterface

interface PastaRepoInterface {

   public function cookSpaghetti();
   public function cookRigatoni();
   public function cookLasagne();

}


Debe saber cómo preparar espagueti, rigatonis, y… lasaña, why not?

Ahora si queremos crear un chef encargado de proveerle pasta a las cocinas escribiríamos lo siguiente:

Código :

// app/models/PastaRepo

class PastaRepo implements PastaRepoInterface {

   public function cookSpaguetti()
   {
      return Pasta::where(‘type’, ‘=’, ‘spaguetti’)->get();
   }

   public function cookRigatoni()
   {
      return Pasta::where(‘type’, ‘=’, ‘rigatoni’)->get();
   }

// Etc. Etc...

}


PastaRepoInterface es el contrato. PastaRepo es el chef que conoce las recetas.

Lo interesante de este ejemplo es que si luego queremos cambiar nuestro especialista en pastas, sólo tendríamos que regresar a app/start/globals.php y decirle a Laravel:

Código :

 //app/start/globals.php
App::bind(‘PastaRepoInterface’, ‘MongoPastaRepo’); 


Que ahora nuestro Chef de Pastas no será Eloquent sino Mongo.

Siempre que el nuevo chef MongoPastaRepo implemente la interfaz PastaRepoInterface nuestra cocina seguirá funcionando sin ningún tipo de cambio adicional. Genial ¿No?


Por cierto, las recetas en este caso serían la lógica de nuestra aplicación o lógica de negocios. En este caso todas las hemos puesto dentro de nuestra carpeta de modelos, como debe ser.

Vistas o Capa de Presentación


Una vez que el controlador ItalianController obtiene toda la comida necesaria para crear nuestra pasta con albóndigas:

Código :

    $food = compact(‘pasta’, ‘meatBalls’, ‘napoliSauce’);


Esta se pasa a la vista, que en nuestro ejemplo sería la estación encargada de decorar el plato para que no sólo tenga la comida (data) adecuada sino que luzca bien (diseño, layouts, HTML5).

Pero no siempre necesitaremos una capa de presentación. ¿Qué pasa si el usuario pide su comida para llevar, por ejemplo? Sólo habría que entregarla en un envase, sin platos, sin adornos, sin HTML, ¿A qué les recuerda eso? Cuando hacemos una petición AJAX, el servidor simplemente nos devuelve los datos en formato JSON:

Código :

return Response::json($food);


Cómo redirigir una acción a otra ruta



Fíjense qué pasa si el tipo de pasta ($type) solicitado no es long ni short:

Código :

return Redirect::route(‘table’, array($idTable))->with(‘message’, ‘Por favor elija pasta corta o pasta larga’);


De una manera muy sencilla le decimos a Laravel que redirija la petición del usuario de nuevo a su mesa, donde le pediremos que por favor elija pasta corta o larga.

Por supuesto necesitaríamos definir otra ruta:

Código :

//app/routes.php

// [More code here]

Route::get(‘table/{id}’, array(‘as’ => ‘table’, ‘uses’ => ‘tableController@index’))->where(‘id’, ‘[0-9]+’); //etc…


Conclusión


En este capítulo y con un ejemplo sencillo aprendimos qué son los controladores, cómo se crean y usan en Laravel, también aprendimos cómo enlazar las rutas a ellos y aprendimos sobre Repositorios e Interfaces que mantienen la lógica y responsabilidades de nuestra aplicación separadas, como debe ser. También le dimos un vistazo a cómo trabajar con vistas, redirecciones y JSON.

En los próximos capítulos iré detallando todo lo anterior.

Como última nota, quizás algunos lectores estén pensando algo como: “Pero yo no necesito un restaurante o aplicación tan elegante. Yo hago páginas web más sencillas.

Con Laravel podríamos hacer esto:

Código :

// app/routes.php
Route::get(‘pasta-with-meatballs’, function () {
   $pasta = Pasta::where(‘type’, ‘=’, ‘short’)->get();
// Etc. Etc….

return View::make(‘dish’, array($pasta));
});


¿Para qué crear un controlador y luego una intefaz y un repositorio y etc. etc. si se puede hacer todo tan rápido y fácil?

Tienen razón, eso también funciona, como uno de esos puestos de comida rápida que son atendidos por una o dos personas que te dan la bienvenida, preparan la comida, te sirven y te cobran y limpian las mesas… eso funciona. Pero si quieren ser programadores de alta categoría, lo mejor es que aprendan y usen las mejores prácticas de desarrollo en el lenguaje y framework en el cual se desempeñen... Esto es un poco de lo que quiero lograr con Laravel y estos capítulos: que aprendan Laravel pero aprendan bien.

No subestimen sus proyectos, no subestimen a sus clientes y sobretodo no se subestimen a uds. mismos.

Como siempre todas sus impresiones, dudas, preguntas, aportes, abajo en los comentarios, por favor :)

Stay tuned and happy weekend.

¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.

Publica tu comentario

o puedes...

¿Estás registrado en Cristalab y quieres
publicar tu URL y avatar?

¿No estás registrado aún pero quieres hacerlo antes de publicar tu comentario?

Registrate