Header Ads Widget

Ticker

6/recent/ticker-posts

Creando un blog CRUD de Laravel

Las operaciones CRUD son la base de muchos sitios web y algo en lo que Laravel sobresale.

El blog que estoy creando para este ejemplo no es nada complejo, pero analizará muchos aspectos y características importantes de Laravel.

Si desea codificar junto conmigo, deberá tener un servidor PHP local en ejecución. Puede seguir mi artículo aquí para comenzar con Homestead y crear un proyecto nuevo.

De lo contrario, puede ejecutar el código terminado y ver cómo funciona todo siguiendo las instrucciones aquí:

Empezando

El primer paso es hacer un modelo para las publicaciones.

También necesitaremos una migración de base de datos para que el modelo interactúe, así como un controlador para manejar las solicitudes del modelo.

Afortunadamente, Laravel proporciona una excelente interfaz de línea de comandos llamada Artisan que nos permite generar todo esto (y mucho más), en lugar de hacerlo todo manualmente.

Para crearlos, ingrese SSH en la máquina virtual y cambie el directorio a la carpeta del código (si usa Homestead):

vagrant ssh
cd code

Ahora estamos listos para generar lo que se necesita.

El comando 'make: model' creará el modelo, sin embargo, agregar '-mcr' también creará una M igración y un C ontroller.

La 'r' indica que queremos crear un controlador de recursos . Esto elimina mucho trabajo al hacer un sistema CRUD al asignar todas las rutas deseadas automáticamente.

php artisan make:model Post -mcr

Si todo salió bien, verá esto:

Estos nuevos archivos se encuentran en:

  • Modelo: /app/Post.php
  • Migración: / database / migrations / time create posts_table.php
  • Controlador: /app/Http/Controllers/PostController.php

Se requiere un poco de configuración en los archivos de modelo y migración, pero la mayor parte del tiempo se dedicará al controlador. La generación de estos archivos ha logrado mucho en segundo plano, como verá más adelante en el artículo.

rm database/migrations/2014* resources/views/welcome.blade.php

Limpiar

Antes de continuar, deberíamos eliminar un par de archivos que no se utilizarán en este proyecto y simplemente lo desordenarán, específicamente relacionados con el registro de usuarios.

Elimine las dos migraciones de usuario predeterminadas proporcionadas por Laravel (tienen una marca de tiempo de 2014, por lo que a continuación se eliminarán ambas). También elimine la vista predeterminada de 'Bienvenida' para la página de inicio:

También hay una ruta de API de usuario que no es necesaria. Abra el archivo en '/routes/api.php' y elimine la ruta (debajo de los comentarios). Esto evitará que se muestre la ruta cuando estemos enumerando todas las rutas.

Para ver todas las rutas registradas, ejecute esto:

php artisan route:list

Como puede ver, solo tenemos una ruta definida ('/'), para navegar a la página de inicio / raíz. Cambiemos eso.

Mapeo de rutas de recursos / CRUD

Necesitamos asignar rutas que coincidan con los métodos relevantes en el controlador, que luego permitirán el manejo de solicitudes.

Laravel hace que esto sea muy fácil. Como hemos indicado anteriormente que nuestro modelo de publicación es un recurso, podemos definir todas las rutas que necesitamos con una línea en '/routes/web.php'.

Mientras estamos aquí, cambiemos la vista predeterminada ('/') para hacer referencia al controlador de publicaciones, ya que la página de inicio enumerará las últimas publicaciones.

Route::get('/', 'PostController@index');
Route::resource('posts', 'PostController');

Cambie el contenido del archivo a:

Enumere las rutas nuevamente, y verá que ahora tenemos todas las rutas necesarias para un sistema CRUD. 🎉

Eche un vistazo al Post Controller (/app/Http/Controllers/PostController.php), y verá que todas las rutas coinciden con los métodos que Laravel ya ha generado. Esto ha reducido una cantidad considerable de trabajo.

Migraciones de bases de datos

A continuación, pasaremos a la base de datos y definiremos qué campos necesitamos para una publicación de blog.

Abra el archivo de migración de publicaciones en '/ database / migrations'.

Cuando trabajo con bases de datos, encuentro útil escribir primero todas las columnas que serán necesarias y pensar en cualquier condición especial. Para una publicación, tendremos:

public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('title', 100)->unique();
$table->string('content', 2000);
$table->string('category', 30);
$table->string('slug', 200)->unique();
$table->timestamps();
});
}
  • Título: una cadena con una longitud máxima de 100 caracteres. Esto deberá ser único (cada título solo se puede usar una vez)
  • Contenido: una cadena, con una longitud máxima de 2000 caracteres
  • Categoría: una cadena, con una longitud máxima de 30 caracteres
  • Slug : una cadena, con una longitud máxima de 200 caracteres. Esto también debe ser único, ya que se utilizará en la URL para navegar a las publicaciones.

Ahora que los campos están mapeados, agréguelos al método 'up', que se usará para agregar los campos enumerados a la tabla de la base de datos de publicaciones:

Todos los tipos de columnas disponibles se pueden encontrar aquí .

Ahora, ejecute las migraciones para llamar al método 'up' e inserte las nuevas columnas en la base de datos:

php artisan migrate

Si se conecta a la base deds, verá la nueva tabla de 'publicaciones' con las columnas que especificamos, lista para aceptar datos:

Esa es la configuración inicial hecha. Ahora es el momento de pasar a crear los formularios y gestionar las solicitudes.

Archivo de diseño

Cuando se trabaja con Blade , es importante utilizar un archivo de 'diseño'. El contenido de este archivo se trasladará a todas las vistas, eliminando así el código repetido.

Cree el archivo de diseño en /resources/views/layout.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>@yield('title') - Laravel Blog</title>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.4/css/bulma.min.css"
/>
</head>
<body>
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a href="{{ route('posts.index') }}" class="navbar-item">
<img src="{{ asset('img/logo.svg') }}" width="112" height="28" />
</a>
<a
role="button"
class="navbar-burger"
aria-label="menu"
aria-expanded="false"
data-target="navMenu"
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navMenu" class="navbar-menu">
<div class="navbar-start">
<a href="{{ route('posts.index') }}" class="navbar-item">
All Posts
</a>
</div>
<div class="navbar-end">
<div class="navbar-item">
<div class="buttons">
<a href="{{ route('posts.create') }}" class="button is-info">
<strong>New Post</strong>
</a>
</div>
</div>
</div>
</div>
</nav>
<section class="section">
<div class="container">
<div class="columns is-centered">
<div class="column is-8">
@if (session('notification'))
<div class="notification is-primary">
{{ session('notification') }}
</div>
@endif @yield('content')
</div>
</div>
</div>
</section>
<footer class="footer">
<div class="content has-text-centered">
<p>
<strong>Laravel Blog</strong> |
<a href="https://stevencotterill.com">Steven Cotterill</a>
</p>
</div>
</footer>
<script src="{{ asset('js/nav.js') }}"></script>
</body>
</html>

Como de costumbre, usaré Bulma para un estilo rápido; No me centraré mucho en el diseño aquí, ya que no importa para este proyecto.

El contenido del diseño anterior es HTML básico, con algunas directivas Blade incluidas, como 'route ()' para vincular a las rutas definidas previamente, lo que significa que si las URL cambian alguna vez, los vínculos seguirán funcionando. También hay una sección para una notificación, que se manejará más adelante. La producción de 'contenido' permitirá la inserción de contenido de otras vistas en el diseño.

Una cosa rápida; Bulma no viene con JavaScript. Para que funcione la navegación móvil, cree un archivo en:

/public/js/nav.js

Agregue lo siguiente y no se preocupe por revisar este código, ya que no está en el alcance de este artículo:

document.addEventListener('DOMContentLoaded', () => {
const $navbarBurgers = Array.prototype.slice.call(
document.querySelectorAll('.navbar-burger'),
0
)
if ($navbarBurgers.length > 0) {
$navbarBurgers.forEach(el => {
el.addEventListener('click', () => {
const target = el.dataset.target
const $target = document.getElementById(target)
el.classList.toggle('is-active')
$target.classList.toggle('is-active')
})
})
}
})

CRUD - Crear

¡Es hora de pasar a lo bueno! Antes de que se puedan mostrar las publicaciones, tendremos que crear algunas, por lo que este será el primer paso.

Abra PostsController y realice este cambio en el método 'crear':

public function create()
{
// Show create post form
return view('posts.create');
}

Esto significa que cuando un usuario navega a la ruta que hemos definido previamente en / posts / create (recuerde, php artisan route: list mostrará todas las rutas definidas), el controlador devolverá una vista de creación ubicada en / resources / views / posts carpeta. Cree esta carpeta y archivo:

/resources/views/posts/create.blade.php

Agregue lo siguiente, y explicaré algunos bits después:

@section('title', 'New Post')
@extends('layout')
@section('content')
<h1 class="title">Create a new post</h1>
<form method="post" action="{{ route('posts.store') }}">
@csrf
@include('partials.errors')
<div class="field">
<label class="label">Title</label>
<div class="control">
<input type="text" name="title" value="{{ old('title') }}" class="input" placeholder="Title" minlength="5" maxlength="100" required />
</div>
</div>
<div class="field">
<label class="label">Content</label>
<div class="control">
<textarea name="content" class="textarea" placeholder="Content" minlength="5" maxlength="2000" required rows="10">{{ old('content') }}</textarea>
</div>
</div>
<div class="field">
<label class="label">Category</label>
<div class="control">
<div class="select">
<select name="category" required>
<option value="" disabled selected>Select category</option>
<option value="html" {{ old('category') === 'html' ? 'selected' : null }}>HTML</option>
<option value="css" {{ old('category') === 'css' ? 'selected' : null }}>CSS</option>
<option value="javascript" {{ old('category') === 'javascript' ? 'selected' : null }}>JavaScript</option>
<option value="php" {{ old('category') === 'php' ? 'selected' : null }}>PHP</option>
</select>
</div>
</div>
</div>
<div class="field">
<div class="control">
<button type="submit" class="button is-link is-outlined">Publish</button>
</div>
</div>
</form>
@endsection

Lo anterior crea un formulario HTML con entradas para Título, Contenido y Categoría.

Algunas notas sobre las directivas blade utilizadas:

  • @section ('title', 'New Post'): define el título de la página, que se obtiene en el diseño
  • @extends ('layout'): Extiende / usa el diseño
  • @section ('content') ... @endsection: Todo lo que hay aquí va a la sección de 'contenido' del diseño
  • @csrf: esto crea un campo oculto con un token único para cada sesión, lo que brinda seguridad contra la falsificación de solicitudes entre sitios
  • @include ('partials.errors'): Incluye un parcial de errores, que se creará a continuación. Esto mostrará cualquier error devuelto si el envío del formulario no se realizó correctamente.
  • old ('field_name'): Si el envío del formulario no fue exitoso, esto agregará los datos ingresados ​​previamente al formulario. Esto significa que el usuario no tendrá que volver a ingresar todo cada vez que envíe

Ahora es un buen momento para crear el error parcial. Este es un archivo reutilizable que se utilizará para todos los formularios. Cree una carpeta 'parciales' y agregue el archivo de errores:

/resources/views/partials/errors.blade.php

Agregue lo siguiente, que verifica si hay errores y los muestra si es así:

@if ($errors->any())
<div class="notification is-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif

Actualice la página en / posts / create y se mostrará el formulario:


Si el formulario se envía en este punto, no se mostrará nada ya que no hemos definido lo que debería suceder.

Mire la 'acción' al comienzo del formulario. Se está enviando una solicitud de 'publicación' al enviarla a 'posts.store'. Esto se relaciona con el método "almacenar" en el controlador de correos.

He agregado comentarios aquí para explicar cada parte, pero puedes ver qué poco código necesita Laravel para manejar las solicitudes:

public function store(Request $request)
{
// Validate posted form data
$validated = $request->validate([
'title' => 'required|string|unique:posts|min:5|max:100',
'content' => 'required|string|min:5|max:2000',
'category' => 'required|string|max:30'
]);
// Create slug from title
$validated['slug'] = Str::slug($validated['title'], '-');
// Create and save post with validated data
$post = Post::create($validated);
// Redirect the user to the created post with a success notification
return redirect(route('posts.show', [$post->slug]))->with('notification', 'Post created!');
}

En la parte superior del Controlador, donde están las declaraciones 'use', agregue esta para que podamos usar un Laravel Helper para crear slugs:

use Illuminate\Support\Str;

¡Todo esto se ve muy bien hasta ahora!

Estoy usando asignación masiva aquí para guardar la solicitud completa de una sola vez. Para habilitar esto, los campos 'rellenables' deben configurarse en el archivo Modelo:

/app/Post.php

class Post extends Model
{
// Set mass-assignable fields
protected $fillable = ['title', 'content', 'category', 'slug'];

También quiero vincular las publicaciones con un 'slug' único en lugar de la ID predeterminada. Agregue esto también y terminamos con este archivo:

/**
* Get the route key for the model.
* * @return string
*/
public function getRouteKeyName()
{
return 'slug';
}
}

¡El formulario de creación ya está listo para usar!

Cuando se envía el formulario, las reglas de validación establecidas se verifican con los datos recibidos. Si falla, se mostrarán los errores relevantes y la entrada del usuario se conservará en los campos del formulario. Si tiene éxito, se agregará una nueva publicación a la base de datos.

Probablemente hayas notado que cuando se creó la publicación, el usuario es redirigido a una página en blanco en '/ posts / slug', que es la URL correcta. No hay una vista definida para ver la publicación individual, lo que nos lleva a la siguiente parte del sistema CRUD.

CRUD - Leer

La parte 'Leer' es bastante fácil, ya que todo lo que implica es mostrar el contenido.

Después del envío del formulario, el usuario es redirigido a la ruta 'posts.show', que se relaciona con el método del controlador 'show'. Cambie el método a esto en el controlador de publicación:

public function show(Post $post)
{
// Pass current post to view
return view('posts.show', compact('post'));
}

Esta vista ahora debe crearse en:

/resources/views/posts/show.blade.php

Agregue lo siguiente:

@section('title', $post->title)
@extends('layout')
@section('content')
@include('partials.summary')
@endsection

He incluido un parcial aquí llamado 'resumen', ya que la visualización de la publicación individual se reutilizará en la página de inicio.

Crea el parcial en:

/resources/views/partials/summary.blade.php

Y agregue el código para mostrar los detalles de la publicación:

<div class="content">
<a href="{{ route('posts.show', [$post->slug]) }}">
<h1 class="title">{{ $post->title }}</h1>
</a>
<p><b>Posted:</b> {{ $post->created_at->diffForHumans() }}</p>
<p><b>Category:</b> {{ $post->category }}</p>
<p>{!! nl2br(e($post->content)) !!}</p>
<form method="post" action="{{ route('posts.destroy', [$post->slug]) }}">
@csrf @method('delete')
<div class="field is-grouped">
<div class="control">
<a
href="{{ route('posts.edit', [$post->slug])}}"
class="button is-info is-outlined"
>
Edit
</a>
</div>
<div class="control">
<button type="submit" class="button is-danger is-outlined">
Delete
</button>
</div>
</div>
</form>
</div>

Algunas notas sobre algunas directivas Blade y otros bits que aún no he cubierto:

  • $ post-> field_name: Esto permite el acceso a los datos de la publicación actual que se pasaron desde el método 'show' en el controlador de la publicación anteriormente
  • {{$ post-> created_at-> diffForHumans ()}}: Laravel viene con Carbon integrado para manejar fechas. 'diffForHumans' es un método que se utiliza para formatear el 'created_at' en una cadena legible
  • {!! nl2br (e ($ post-> content)) !!}: La sintaxis predeterminada en Blade para acceder a los datos ({{field}}) eliminará los espacios en blanco, que deberán mantenerse al trabajar con un área de texto. Esta solución alternativa aún aplica la validación, pero ejecuta la salida a través de nl2br para insertar saltos de línea.
  • @method ('eliminar'): la acción 'eliminar' no es compatible con los formularios. Esto crea un campo oculto para Laravel, que le dice que el formulario en realidad está enviando una solicitud de 'eliminar' en lugar de 'publicar'

¡Actualice la publicación y ahora podemos ver los detalles de la publicación!

Todo se ve como debería aquí. El formato de la fecha está funcionando, todo se muestra y la notificación que agregamos anteriormente en el archivo de diseño está funcionando.

Ahora que la visualización de una sola publicación está funcionando, es hora de mostrar una lista de las publicaciones más recientes. Esto se relaciona con el método 'index' del Post Controller, así que actualicémoslo:

public function index()
{
// Get all Posts, ordered by the newest first
$posts = Post::latest()->get();
// Pass Post Collection to view
return view('posts.index', compact('posts'));
}

Luego crea la vista relevante:

/resources/views/posts/index.blade.php

Agregue el siguiente código, que recorre cada publicación que se pasa a la vista y muestra un resumen de la publicación:

@section('title', 'Home')
@extends('layout')
@section('content')
@foreach ($posts as $post)
@include('partials.summary')
@endforeach
@endsection

CRUD - Actualización

La siguiente funcionalidad para agregar al blog es permitir la edición / actualización de publicaciones. Afortunadamente, esto es muy similar al proceso para crear una publicación, por lo que no hay mucho que explicar aquí. Cambie el método 'editar' en el controlador de publicación, que es responsable de proporcionar la vista del formulario:

public function edit(Post $post)
{
return view('posts.edit', compact('post'));
}

Cree la nueva vista en:

/resources/views/posts/edit.blade.php

Este formulario es muy similar al formulario 'crear', excepto que tiene un campo oculto para indicar que está enviando una solicitud de 'parche' para actualizar la publicación:

@section('title', 'Edit Post')
@section('action', route('posts.create'))
@extends('layout')
@section('content')
<h1 class="title">Edit: {{ $post->title }}</h1>
<form method="post" action="{{ route('posts.update', [$post->slug]) }}">
@csrf
@method('patch')
@include('partials.errors')
<div class="field">
<label class="label">Title</label>
<div class="control">
<input type="text" name="title" value="{{ $post->title }}" class="input" placeholder="Title" minlength="5" maxlength="100" required />
</div>
</div>
<div class="field">
<label class="label">Content</label>
<div class="control">
<textarea name="content" class="textarea" placeholder="Content" minlength="5" maxlength="2000" required rows="10">{{ $post->content }}</textarea>
</div>
</div>
<div class="field">
<label class="label">Category</label>
<div class="control">
<div class="select">
<select name="category" required>
<option value="" disabled selected>Select category</option>
<option value="html" {{ $post->category === 'html' ? 'selected' : null }}>HTML</option>
<option value="css" {{ $post->category === 'css' ? 'selected' : null }}>CSS</option>
<option value="javascript" {{ $post->category === 'javascript' ? 'selected' : null }}>JavaScript</option>
<option value="php" {{ $post->category === 'php' ? 'selected' : null }}>PHP</option>
</select>
</div>
</div>
</div>
<div class="field">
<div class="control">
<button type="submit" class="button is-link is-outlined">Update</button>
</div>
</div>
</form>
@endsection

Ahora para manejar la solicitud de cuándo se realiza la actualización a través del formulario. Esto se maneja mediante el método de 'actualización' en el controlador de publicación, que debería ser el siguiente:

public function update(Request $request, Post $post)
{
// Validate posted form data
$validated = $request->validate([
'title' => 'required|string|unique:posts|min:5|max:100',
'content' => 'required|string|min:5|max:2000',
'category' => 'required|string|max:30'
]);
// Create slug from title
$validated['slug'] = Str::slug($validated['title'], '-');
// Update Post with validated data
$post->update($validated);
// Redirect the user to the created post woth an updated notification
return redirect(route('posts.index', [$post->slug]))->with('notification', 'Post updated!');
}

Haga clic en el botón 'editar' en una de las publicaciones, cambie el contenido y actualícelo y los cambios se reflejarán después de que haya sido redirigido.

CRUD - Eliminar

Y ahora, la pieza final del rompecabezas, ¡y una de las más fáciles! Ahora debería ver lo rápido que es trabajar con estas solicitudes cuando se ha manejado la configuración inicial.

Actualice el método 'destruir' del controlador de correos a esto:

public function destroy(Post $post)
{
// Delete the specified Post
$post->delete();
// Redirect user with a deleted notification
return redirect(route('posts.index'))->with('notification', '"' . $post->title . '" deleted!');
}

Haga clic en el botón 'eliminar' en cualquiera de las publicaciones y se eliminará del blog, y se mostrará una notificación para confirmar esto:

Terminando

Así que ahí lo tienes; un blog CRUD básico usando Laravel y Blade. Con suerte, esto ayudará a aclarar algunas cosas o hará que desee comenzar a trabajar con Laravel.

Publicar un comentario

0 Comentarios