Ejemplo Lista de Peliculas
En el siguiente ejemplo se creará un sistema donde se liste y almacene información de peliculas, para esto utilizará el entorno laravel con conexión a una base de datos, en este caso MySql. El ejemplo consistirá en realizar un CRUD para peliculas, donde se trabajará y se explicara en conjunto el desarrollo de este.
El flujo normal para crear un proyecto en Laravel es :
- Creación de proyecto (con comando).
- Crear la base de datos (para este ejemplo en mysql con phpmyadmin).
- Configurar las variables de entorno en archivo .env en directorio principal del proyecto generado.
- Crear modelos y migraciones
- Autenticación
- Creación de Controladores
Enrutamiento: Definir acceso a controlador en archivo routes/web.php.
Vistas y blade
Paso 1:
composer create-project --prefer-dist laravel/laravel peliculas "5.3.*"
- Configuración inicial: actualización de llave de la aplicación.
php artisan key:generate
- Creación de base de datos: Dirigirse a gestor de base de datos y crear base de datos Ej: bd_peliculas.
- Configuración archivo .env (Vista previa)
DB_CONNECTION=mysql
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
- Ejemplo arquivo .env (modificado)
DB_CONNECTION=mysql
DB_DATABASE=db_peliculas
DB_USERNAME=root
DB_PASSWORD=12345678
Nota: Al momento de generar los modelos es necesario empezar desde el modelo mas pequeño al mas amplio, es decir, si un modelo es utilizado por una clase "mayor" este debe ser creado antes, puntualmente para nuestro ejemplo el primer modelo a crear será "Genero" ya que el modelo "Pelicula" tiene una clave foranea apuntando a un genero.
- Modelo genero
php artisan make:model Genero -m
- Dentro del modelo de Genero, definiremos la tabla a la cual pertenece, su clave primaria y finalmente especificaremos cuales son las columnas editables de la tabla, para este caso solo lo será el campo "descripcion" que actúa como nombre del género al que pertenecen las películas.
class Genero extends Model
{
protected $table = 'generos';
protected $primaryKey = 'id_genero';
public $timestamps = false;
protected $fillable = [
'descripcion',
];
}
- Dentro de las migraciones definiremos las columnas que poseerá la tabla. El siguiente código contrasta el contenido inicial de la migración creada a partir del modelo y los campos editados y/o añadidos posteriormente.
Schema::create('generos', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
});
- Migración para tabla genero post edición.
Schema::create('generos', function (Blueprint $table) {
$table->increments('id_genero');
$table->string('descripcion',50);
});
- Modelo Pelicula
php artisan make:model Pelicula -m
- Dentro de la migración correspondiente al modelo de "Pelicula" definiremos
class Pelicula extends Model
{
protected $table = 'peliculas';
protected $primaryKey = 'id_pelicula';
public $timestamps = false;
protected $fillable = [
'nombre',
'id_genero',
'anno_estreno',
'director',
'sinopsis'
];
}
- Vista previa de migración de "Pelicula".
Schema::create('peliculas', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
});
- Migración para tabla "Pelicula": Dentro de la función up() insertaremos el siguiente código que define los atributos o filas de la tabla en la base de datos.
Schema::create('peliculas', function (Blueprint $table) {
$table->increments('id_pelicula');
$table->date('anno_estreno');
$table->string('nombre');
$table->string('director',120);
$table->text('sinopsis',120);
$table->integer('id_genero')->unsigned();
$table->foreign('id_genero')->references('id_genero')->on('generos');
});
- Migraciones
php artisan migrate
- Uso de provider Auth
php artisan make:auth
- Enrutamiento
php artisan route:list
- Controladores
php artisan make:controller PeliculaController --resource
- Configuración archivo web.php
//...
Route::resource('/peliculas', 'PeliculaController');
Auth::routes();
- Dentro de PeliculaController: definir un constructor que utilice el middleware 'auth' para identificar si nos encontramos autenticados a la hora de realizar las operaciones del controlador.
public function __construct()
{
$this->middleware('auth');
}
- Método index() será requerido desde la la vista en la cual se listen todas las películas, esto se realiza trayendo todos los registros desde la base de datos, compactarlos y enciarlos a la vista "/home".
public function index()
{
$peliculas = Pelicula::all(); // se obtiene la totalidad de peliculas existentes en la BD
$datos = array ();
$contador = 0;
// se obtenendran los valores de cada pelicula y se almacenaran en un array para ser retornados hacia la vista
foreach ($peliculas as $pelicula) {
$genero = Genero::find($pelicula->id_genero); // se busca el genero especifico de la pelicula, buscando el id
// asigancion de valores
$datos[$contador]["id"] = $pelicula->id_pelicula;
$datos[$contador]["nombre"] = $pelicula->nombre;
$datos[$contador]["genero"] = $genero->descripcion;
$datos[$contador]["anno_estreno"] = $pelicula->anno_estreno;
$datos[$contador]["director"] = $pelicula->director;
$datos[$contador]["sinopsis"] = $pelicula->sinopsis;
$contador++;
}
// retorno de vista y datos que listara
return view("/home", compact('datos'));
}
- Método Create(): este método se utiliza para renderizar a la vista en donde crearemos un nuevo registro de películas, la cual se llama "createMovie", adicionalmente traeremos todos los géneros de la base de datos, los compactaremos y enviaremos a la vista en cuestión para listarlos en un select para que el usuario pueda elegir el género de película al cual corresponde su nuevo registro.
public function create()
{
$generos = Genero::all(); // se obtiene todos los generos
// retorno de vista y datos que listara
return view("/createMovie",compact('generos'));
}
- Método Store(): este método está relacionado con la vista "createMovie", se ejecuta al evento submit del formulario para crear una nueva película. Mediante el $request, traeremos los datos de la vista y crearemos un nuevo registro, por otro lado enviaremos una variable de sesión a la vista indicando si se creó o no el nuevo registro.
*Se implementa un Request propio para las validaciones de los datos el request
*Revisar App\Http\Requests\PeliculaRequest.php*/
public function store(PeliculaRequest $request)
{
// creacion y a su vez validacion si el recurso se creo correctamente
if (Pelicula::create($request->all())) {
// se envia mensaje de confirmacion, (nombre, mensaje) es recivido por alerts.blade.php ubicado en resources/views
Session::flash('message-success','La pelicula se creó exitosamente');
} else {
// se envia mensaje de confirmacion, (nombre, mensaje) es recivido por alerts.blade.php ubicado en resources/views
Session::flash('message-error','No se ha podido crear la pelicula');
}
// se retorna a la ruta
return Redirect::to('/peliculas');
}
- Método Edit(): busca la película con el id corresponiente, renderiza a la vista "editMovie" con los datos encontrados para que el usuario visualice los datos actuales.
public function edit($id)
{
$pelicula = Pelicula::find($id); // se busca la pelicula
$generos = Genero::all(); // se busca la pelicula
// se retorna la vista y los datos
return view('editMovie', compact('pelicula','generos'));
}
- Método Update(): busca la película que actualizamos y "setea" los atributos o campos que haya editado el usuario.
public function update(Request $request, $id)
{
$pelicula = Pelicula::find($id); // busqueda de la pelicula a actualizar
$pelicula->fill($request->all()); // se rellenaran los atributos del objeto con sus respectivos datos
// se guardan los cambios
if ($pelicula->save()) {
Session::flash('message-success','La pelicula se actualizó exitosamente');
} else {
Session::flash('message-error','No se ha podido actualizar la pelicula');
}
return Redirect::to('/peliculas');
}
- Método Destroy(): elimina el registro de la base de datos, envía una variable de sesión para dar a conocer el resultado de la operación y nos redirecciona a la url /peliculas.
public function destroy($id)
{
$pelicula = Pelicula::find($id); // se busca la pelicula
if ($pelicula->delete()) { // se elimina
Session::flash('message-success','La pelicula se eliminó exitosamente');
} else {
Session::flash('message-error','No se ha podido eliminar la pelicula');
}
return Redirect::to('/peliculas');
}
- Blade
Crear un archivo llamado home.blade.php el cual accederé a la función index que compacta todos los registros desde la base de datos y los envía hasta la vista home.
*Nota: dentro del código incluiremos una archivo externo llamado alerts.blade.php el cual recibe variables de sesión desde el controlador (específicamente de la función store) con las cuales verificaremos el estado de las operaciones como por ejemplo "message-success" el cual nos dice que la película se creó exitosamente o "'message-error" el cual nos dice que no se ha podido crear la película.
HTML de Blade resources/views/home.blade.php
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Listado de Peliculas</div>
<div class="col-md-10 col-md-offset-1">
<br>
{{-- se incluye las alertas --}}
@include('alerts')
</div>
<div class="panel-body">
<div class="col-md-4 col-md-offset-8">
<a class="btn btn-primary btn-block" href='/peliculas/create'">Agregar Nueva Pelicula</a>
</div>
@foreach($datos as $dato)
<div class="col-md-10 col-md-offset-1">
{{-- Se listaran los datos obtenidos como arreglo --}}
<h3>{{ $dato['nombre'] }}</h3>
<h4>Director: {{ $dato['director'] }}</h4>
<h4>Año Estreno: {{ $dato['anno_estreno'] }}</h4>
<h4>Genero: {{ $dato['genero'] }}</h4>
<h3>Sinopsis</h3>
<div style="word-wrap: break-word;">
<p>{{ $dato['sinopsis'] }}</p>
</div>
<br>
<div class="col-md-8 col-md-offset-2" style="float: bottom;">
<div class="col-md-6">
<a class="btn btn-success btn-md btn-block" href='/peliculas/{{ $dato['id'] }}/edit'>Editar</a>{{-- ruta que hace referencia al edit de peliculas --}}
</div>
<div class="col-md-6">
<form method="POST" action="{{ url('/peliculas/'.$dato['id']) }}">
<input type="hidden" name="_method" value="DELETE"> {{-- Se requiere especificar un input de este tipo para enviar una peticion de tipo DELETE --}}
{{ csrf_field() }}
<input type="submit" class="btn btn-danger btn-block" value="Eliminar">
</form>
</div>
</div>
</div>
<div class="col-md-10 col-md-offset-1">
<hr>
</div>
@endforeach
</div>
</div>
</div>
</div>
</div>
@endsection
Como se mencionó anteriormente incluiremos mensajes para "saber" el estado de las operaciones desde el controlador, estas se utilizarán al momento de crear, editar y eliminar películas.
HTML de Blade resources/views/alerts.blade.php
{{-- si se recibe algun mensaje se desplegaran --}}
@if(Session::has('message-error'))
<div class="alert alert-danger alert-dismissible" id="error-alert">
<button type="buttom" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
<strong>Error! </strong>{{ Session::get('message-error') }} {{-- se imprime el mensaje --}}
</div>
<script>
setTimeout("$('.alert').hide(1500);",5000);
</script>
@endif
@if(Session::has('message-success'))
<div class="alert alert-success alert-dismissible" id="error-alert">
<button type="buttom" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
<strong>Éxito! </strong>{{ Session::get('message-success') }}
</div>
<script>
setTimeout("$('.alert').hide(1500);",5000);
</script>
@endif
HTML de Blade resources/views/errores.blade.php
@if(count($errors)>0)
<div class="alert alert-danger alert-dismissible">
<button type="buttom" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
<ul>
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
Crearemos la vista "createMovie.blade.php" la cual es un formulario que se comunica con la función store() del controlador para enviarnos a la vista correspondiente desde el controlador con los datos de genero compactados.
HTML de Blade resources/views/createMovie.blade.php
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Crear Nueva Pelicula</div>
@include('errores')
<div class="panel-body">
<form class="form-horizontal" role="form" method="POST" action="{{ url('/peliculas') }}">
{{ csrf_field() }} {{-- token necesario para realizar la consulta --}}
<div class="form-group">
<label for="name" class="col-md-4 control-label">Name</label>
<div class="col-md-6">
<input type="text" class="form-control" name="nombre" >
</div>
</div>
<div class="form-group">
<label for="anno_estreno" class="col-md-4 control-label">Año de Estreno</label>
<div class="col-md-6">
<input type="date" class="form-control" name="anno_estreno" >
</div>
</div>
<div class="form-group">
<label for="id_genero" class="col-md-4 control-label">Genero</label>
<div class="col-md-6">
<select class="form-control" name="id_genero" >
{{-- se recorreran los generos enviados desde el servidor --}}
@foreach ($generos as $genero)
<option value="{{ $genero->id_genero }}">{{ $genero->descripcion }}</option>
@endforeach
</select>
</div>
</div>
<div class="form-group">
<label for="director" class="col-md-4 control-label">Nombre de Director</label>
<div class="col-md-6">
<input type="text" class="form-control" name="director" >
</div>
</div>
<div class="form-group">
<label for="sinopsis" class="col-md-4 control-label">Sinopsis</label>
<div class="col-md-6">
<textarea type="date" class="form-control" name="sinopsis" ></textarea>
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<a class="btn btn-default btn-md" href='/peliculas'>Volver</a>
<button type="submit" class="btn btn-primary">
Crear Pelicula
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
Crear archivo editMovie.blade.php dentro de resources/views, esta vista se "comunica" con la función edit() para traer los datos de la película específica a la vista y finalmente la función update() que guarda los cambios y renderiza a la url "/peliculas".
HTML de Blade resources/views/editMovie.blade.php
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Editar Nueva Pelicula</div>
<div class="panel-body">
<form class="form-horizontal" role="form" method="POST" action="{{ url('/peliculas/'.$pelicula->id_pelicula) }}">
<input type="hidden" name="_method" value="PUT">
{{ csrf_field() }}
<div class="form-group">
<label for="name" class="col-md-4 control-label">Name</label>
<div class="col-md-6">
<input type="text" class="form-control" name="nombre" value="{{ $pelicula->nombre }}" required>
</div>
</div>
<div class="form-group">
<label for="anno_estreno" class="col-md-4 control-label">Año de Estreno</label>
<div class="col-md-6">
<input type="date" class="form-control" name="anno_estreno" value="{{ $pelicula->anno_estreno }}" required>
</div>
</div>
<div class="form-group">
<label for="id_genero" class="col-md-4 control-label">Genero</label>
<div class="col-md-6">
<select class="form-control" name="id_genero" value="{{ $pelicula->id_genero }}" required>
@foreach ($generos as $genero)
@if($genero->id_genero == $pelicula->id_genero)
<option value="{{ $genero->id_genero }}" selected>{{ $genero->descripcion }}</option>
@else
<option value="{{ $genero->id_genero }}">{{ $genero->descripcion }}</option>
@endif
@endforeach
</select>
</div>
</div>
<div class="form-group">
<label for="director" class="col-md-4 control-label">Nombre de Director</label>
<div class="col-md-6">
<input type="text" class="form-control" name="director" value="{{ $pelicula->director }}" required>
</div>
</div>
<div class="form-group">
<label for="sinopsis" class="col-md-4 control-label">Sinopsis</label>
<div class="col-md-6">
<textarea type="date" class="form-control" name="sinopsis" required> {{ $pelicula->sinopsis }}</textarea>
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<a class="btn btn-default btn-md" href='/peliculas'>Volver</a>
<button type="submit" class="btn btn-success">
Editar Pelicula
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection