Laravel 8 desde cero Parte V - Proyecto Blog VIP
Introducción
Primero que todo, este proyecto requiere autenticación, por lo cual se debe tener en cuenta contar con el entendimiento del código explicado en la parte I y la parte IV, para la exigencia de la verificación.
Para el Back-End o tabla para las publicaciones
Desde la linea de comandos y en la carpeta respectiva ejecutamos:
php artisan make:model Post -mf
Con el anterior comando se crean los archivos para el modelo:
App\Models\Post.php
En el modelo mencionado antes del cierre de la clase agregamos:
protected $fillable = [
'title', 'content',
];
protected $hidden = [
'id', 'user_id',
];
La migración:
Database\Migrations\aaaa_mm_dd_time_create_post_table.php
En la mencionada migración entre id y timestamp agregamos:
$table->unsignedBigInteger('user_id')->references('id')->on('users');
$table->string('title');
$table->text('content');
Y al usar -mf crea la migración y el factory 'Database\Factories\PostFactory', y cuyo uso es similar a los semilleros, pero sin el uso ni la creación del archivo Seeder, lo cual veremos en el siguiente atículo.
A continuación ejecutamos el comando:
php artisan migrate
Que se encarga de crear la tabla en nuestra base de datos.
Observaciones:
- Las variables 'title', y 'content' son los únicos datos declarados en este ejemplo, para hacerlo sencillo. Ver en Post.php protected $fillable = ['title', 'content',];
- Las variables 'id', 'user_id' son el ordinal de la publicación, y el id del usuario que la creó respectivamente. Ver en Post.php protected $hidden = ['id', 'user_id',];
- Las variables de timestamps 'created_at', y 'updated_at' corresponden a la fecha de creación y la de actualización.
Para el controlador (Back-End) y servidor de vistas (Front-End) ejecutamos
php artisan make:controller PostController --resource --model=Post
Que crea el archivo App\Http\Controllers\PostController.php, que se encarga de servir las vistas y controla las funciones store, update y destroy (almacenar, actualizar y eliminar).
En dicho archivo modificamos el servicio de vistas, con el código siguiente:
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
use Auth;
use App\Http\Requests\PostFormRequest;
class PostController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$posts = Post::paginate(10);
return view('post\index', compact('posts'));
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('post\create');
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(PostFormRequest $request)
{
$post = new Post();
$post->title = $request->input('title');
$post->content = $request->input('content');
$post->user_id = $request->user()->id;
$post->save();
return redirect()
->route('posts.show', ['post' => $post])
->with('message', 'lblcreado');
}
/**
* Display the specified resource.
*
* @param \App\Models\Post $post
* @return \Illuminate\Http\Response
*/
public function show(Post $post)
{
return view('post.show', compact("post"));
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Post $post
* @return \Illuminate\Http\Response
*/
public function edit(Post $post)
{
return view('post\edit', compact("post"));
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\Post $post
* @return \Illuminate\Http\Response
*/
public function update(PostFormRequest $request, Post $post)
{
if (Auth::user()->cant('update', $post)) {
return redirect()
->route('post/my', ['post' => $post])
->with('message', 'lblnopermisos');
}
$post->title = $request->input('title');
$post->content = $request->input('content');
$post->save();
return redirect()
->route('post/my', ['post' => $post])
->with('message', 'lblactualizado');
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Post $post
* @return \Illuminate\Http\Response
*/
public function destroy(Post $post)
{
if (Auth::user()->cant('delete', $post)) {
return redirect()
->route('post/my', ['post' => $post])
->with('message', 'lblnopermisos.');
}
$post->delete();
return redirect()
->route('post/my', ['post' => $post])
->with('message', 'lblborrado');
}
public function my(Request $request)
{
$idlcluser = $request->user()->id;
$allposts = Post::all();
$posts = $allposts->where('user_id', $idlcluser);
return view('post/my', compact("posts"));
}
}
Observaciones:
- Primero tenemos la vista index, que hace uso de paginate, es por ello que en el layout o diseño (El cual se puede ver más adelante) se declara tailwind.min.css, ya que a partir de Laravel 8, sin dicho estilo declarado no funciona la vista, con el uso de paginate.
- Create, show y edit, son vistas fijas, igual que my, que muestra las publicaciones del usuario.
- La función store, se encarga de almacenar la publicación a partir de create, tal como la función update, se encarga de actualizar la información a partir de edit, con la diferencia de que update solo está disponible para el creador de la publicación, al igual que la función destroy, y donde el archivo PostPolicy, es el encargado de evaluar para update y delete, que sea el autor de la publicación quien elimina o edita la publicación.
- Es importante notar que cuando se usa with('message',etiqueta), etiqueta es el nombre de la label multi-idioma, que mostrará la vista, y que veremos en el código de dichas vistas como lo evalua para mostrar el mensaje multi-idioma.
Para validar los datos y responder con mensajes según el error
Para validar los campos del modelo Post , incluyendo mensajes de error hacemos uso del archivo PostFormRequest, ver en el código anterior resaltado en amarillo, archivo que creamos, ejecutando el siguiente comando:
php artisan make:request PostFormRequest
Creando el archivo App\Http\Requests\PostFormRequest al cual le asignaremos el siguiente código para el ejemplo (pero que podemos modificar según requerimientos):
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class PostFormRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'title' => 'required|min:5|max:100',
'content' => 'required|min:15|max:1000',
];
}
public function messages(){
return[
'title.required' => 'El titulo es requerido - Title is required',
'title.min' => 'El titulo debe tener al menos 5 caracteres - The title must be at least 5 characters',
'title.max' => 'El titulo no debe tener mas de 100 caracteres - The title must not be more than 100 characters',
'content.required' => 'El contenido es requerido - Content is required',
'content.min' => 'El contenido debe tener al menos 15 caracteres - Content must be at least 15 characters',
'content.max' => 'El contenido no debe tener mas de 1000 caracteres - The content must not be more than 1000 characters',
];
}
}
En este caso uso dos idiomas para los errores.
Restringiendo la eliminación y modificación al creador de la publicación
Vemos en el archivo App\Http\Controllers\PostController.php, el uso de Auth, también resaltado en amarillo y que que utiliza la información del archivo PostPolicy, para verificar la autorización.
Para que solo el autor del post o publicación, pueda eliminar o actualizar la publicación.
Ejecutamos el comando:
php artisan make:policy PostPolicy --model=Post
Creando el archivo App\Policies\PostPolicy.php
Verificar en cabecera:
use App\Models\Post;
use App\Models\User;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
Si se quiere (es opcional) se elimina de cabecera:
use Illuminate\Auth\Access\HandlesAuthorization;
Y se elimina al inicio de la clase:
use HandlesAuthorization;
Y en los métodos delete y update ponemos el sgte. código:
return $user->id == $post->user_id;
Para que funcione hay que declararlo en: App\Providers\AppServiceProvider.php
El cual es una clase extends ServiceProvider, la cual vemos en el archivo PostPolicy en el use de AuthServiceProvider
Agregamos a la cabecera de App\Providers\AppServiceProvider.php
use App\Policies\PostPolicy;
use App\Post;
Con el siguiente código registramos la clase que creamos, se coloca dentro de la clase,
es el array que contiene el mapa de las políticas de la aplicación:
protected $policies = [
Post::class => PostPolicy::class,
];
Observaciones:
Para tener en cuenta la autenticación, evaluación y respuestas es importante notar la relación de los archivos:
- El ultimo archivo 'App\Providers\AppServiceProvider.php' cuya clase extiende la clase ServiceProvider conectándolo, realiza el enlace entre Post y PostPolicy.
- El archivo 'App\Policies\PostPolicy.php' que conecta el ServiceProvider, con la inclusión de:
- 'use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;'.
- Y el archivo PostController que con Auth, y la respuesta de PostPolicy, hace la validación de los métodos delete y update, y que valida los datos y sus respuestas mediante el archivo PostFormRequest .
Para el Front-End
Obsérvese el uso de @lang y el contenido entre corchetes dobles {{ contenido }}.
Creamos un archivo en Resources\Views\bienvenido.blade.php, esto con el objetivo de tener un archivo aparte personalizado.
El código de dicho archivo sería por ejemplo:
Resources\Views\bienvenido.blade.php
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@lang('main.title')</title>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet">
<!-- Styles -->
<link href="{{ asset('css/compulg.css') }}" rel="stylesheet">
<style>
html,
body {
background-color: #000;
color: #fff;
font-family: 'Nunito', sans-serif;
font-weight: 200;
height: 100vh;
margin: 0;
}
.full-height {
height: 100vh;
}
.flex-center {
align-items: center;
display: flex;
justify-content: center;
}
.position-ref {
position: relative;
}
.top-center{
position: absolute;
align-items: center;
top: 18px;
}
.content {
text-align: center;
}
.links>a {
color: #fff;
padding: 5px;
font-size: 13px;
font-weight: 600;
letter-spacing: .1rem;
text-decoration: none;
text-transform: uppercase;
}
section {
margin-top: 10vh;
min-height: 70vh;
}
</style>
</head>
<body>
<div class="flex-center position-ref full-height">
@if (Route::has('login'))
<div class="top-center links">
@auth
<a href="{{ url('/home') }}">@lang('main.lblhome')</a>
@else
<a href="{{ route('login') }}">@lang('main.lblin')</a>
@if (Route::has('register'))
<a href="{{ route('register') }}">@lang('main.lblreg')</a>
@endif
@endauth
</div>
@endif
<section class="d-flex text-center">
<div class="content">
<div><div class="conttxtimgslr">
<div class="ttltxtpimgslr">
<img src="https://i.imgur.com/6kfMQFn.jpg" alt="logo de mundo developer" class="imgmdlft">
<spam class="spmlgcenter"><b> @lang('main.lblensociedad') </b></spam>
<img src="https://i.imgur.com/Ik1TAEA.png" alt="logo de compulg" class="imgclgrgth">
</div></div></div>
<h1>@lang('main.lblpresenta')</h1>
<div class="titlelg">
@lang('main.title')
</div>
<p>@lang('main.lblpltfrmdscrp')</p>
<h1>@lang('main.subttl')</h1>
</div>
</section>
</div>
</body>
</html>
Vemos resaltado en verde, las funciones: @if (Route::has('login')) => Si ha ingresado, @auth => Si ha ingresado y esta autorizado muestra el enlace a home, @else => Si no ha ingresado muestra login o ingresar, @if (Route::has('register')) => Si tiene que registrarse (como es el caso al exigir autenticación), muestra Register o Registrarse, luego finaliza el if @endif, el auth @endauth y el if que lo anida @endif. Resumiendo si se ha logueado o ingresado muestra el enlace a inicio = /home, de lo contrario muestra ingresar y registrarse.
En aguamarina vemos las imágenes en un servidor,(es lo que suelo hacer para ahorrar espacio en servidor, y reutilizar las imagenes sin subirlas a varios servidores), sin embargo lo recomendado es descargar dichas imágenes y guardarlas en public, o en una subcarpeta dentro (esto aún no lo realizo, ya que he tenido problemas con las imagenes a nivel local, espero que al subirlas al hosting no presente este problema, de lo contrario, tendré que hacer lo recomendado).
Y en amarillo en el código previo, las etiquetas de idioma @lang, las rutas {{ url('/home') }}, {{ route('login') }}, y {{ route('register') }}. Donde home corresponde al archivo Resources\Views\home.blade.php, login a Resources\Views\Auth\login.blade.php, y register a Resources\Views\Auth\register.blade.php), y la inclusión para los estilos personalizados del archivo Public\css\compulg.css, el cual tendrá para nuestro ejemplo el siguiente código:
Public\css\compulg.css
body {
background: rgba(0, 255, 255, 0.3);
color: black;
}
/* Estilo para el encabezado */
.hdcont {
left: 0;
top: 0;
width: 100vw;
z-index: 99999;
background-color: blue;
color: aliceblue;
}
/* Estilo para contenedor del titulo y logo */
header {
position: fixed;
left: 0;
top: 0;
width: 100vw;
z-index: 99999;
background-color: blue;
color: aliceblue;
}
/* Estilos para el texto e imagen de la cabecera para pc */
.hdclgltlttl {
display: none;
}
.hdclgttl {
font-family: sans-serif;
font-size: 1.5em;
padding-left: 10%;
padding-right: 14%;
background-color: blue;
color: aliceblue;
}
.hdclg {
padding-bottom: 0.8%;
width: 15%;
height: auto;
margin-left: 20%;
}
a {
background-color: blue;
color: aliceblue;
}
a:hover {
background-color: inherit;
color: cyan;
}
.links a {
background-color: blue;
color: aliceblue;
}
.links > a {
padding: 5px;
}
.links a:hover {
background-color: blue;
color: cyan;
}
.custom-toggler.navbar-toggler {
background-color: aliceblue;
border-color: blue;
}
.custom-toggler .navbar-toggler-icon {
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0,0, 255, 1)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 8h24M4 16h24M4 24h24'/%3E%3C/svg%3E");
}
.txtpost {
height: 100vh;
padding: 1vw;
background-color: black;
color: aliceblue;
}
/* Estilo para alinear junto a card-text en post.show */
.spmlgcardin {
padding: 1.7vw;
text-align: justify-all;
}
/* Estilos para plantilla bienvenido */
.conttxtimgslr {
display: inline-block;
}
.ttltxtpimgslr {
display: flex;
align-content: space-between;
align-items: center;
}
/* Estilo para texto con imagenes a cada lado */
.imgmdlft {
height: 15vh;
width: auto;
text-align: left;
}
.imgclgrgth {
width: 25vw;
height: auto;
text-align: right;
}
/* Imagen de CompuLG a la izquierda */
.spmlgcenter {
padding: 1.7vw;
text-align: center;
font-size: 3vh;
}
.titlelg{
font-size: 5vw;
}
@media screen and (max-width: 600px) {
/* Para el texto responsivo */
.hdclg {
padding-bottom: 3%;
width: 30%;
height: auto;
}
.hdclgttl {
display: none;
}
.hdclgltlttl {
display: inline;
margin-right: 15px;
margin-left: 15px;
font-family: cursive, sans-serif;
background-color: blue;
color: aliceblue;
}
}
El archivo anterior es donde personalizamos nuestros estilos para las vistas.
Vamos al archivo Routes\web.php y cambiamos welcome, por bienvenido.
Siguiendo el patrón explicado en el artículo anterior, creamos el partial de la navbar, el diseño o layout, ya tenemos el css personalizado, por último las vistas con su respectiva declaración, y su asignación en Routes\web.php (Ver más al final).
Creamos nuestra barra de navegación, para este ejemplo
Resources\Views\_navclg.blade.php, con el contenido:
<nav class="navbar navbar-expand-lg hdcont">
<div class="container">
<a class="navbar-brand" href="{{ url('/home') }}">
<span class="hdclgttl">@lang('main.title')</span>
<span class="hdclgltlttl">@lang('main.title')</span>
</a>
<img src="https://i.imgur.com/Ik1TAEA.png" alt="logo de compulg" class="hdclg">
<button class="navbar-toggler custom-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<!-- Right Side Of Navbar -->
<ul class="navbar-nav ml-auto">
<!-- Authentication Links -->
@guest
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">@lang('main.lblin')</a>
</li>
@if (Route::has('register'))
<li class="nav-item">
<a class="nav-link" href="{{ route('register') }}">@lang('main.lblreg')</a>
</li>
@endif
@else
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
{{ Auth::user()->name }} <span class="caret"></span>
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ route('logout') }}" onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
@lang('main.lblout')
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf
</form>
<a class="dropdown-item" href="{{route('post/index')}}">
@lang('main.lblposts')
</a><br><br>
<a class="dropdown-item" href="{{route('post/create')}}">
@lang('main.lblcreatepost')
</a><br><br>
<a class="dropdown-item" href="{{route('post/my')}}">@lang('main.lblbtnmyposts')</a>
</div>
</li>
@endguest
</ul>
</div>
</div>
</nav>
Observaciones:
- En amarillo encontramos los estilos o clases declaradas en compulg.css.
- En verde vemos las rutas, y el nombre del usuario autorizado.
- Con fondo azul y texto en blanco vemos las etiquetas de idioma (@lang).
- Con fondo fucsia, tenemos la relación de la navbar colapsable.
- En aguamarina tenemos las funciones php en Laravel.
- En rojo con texto amarillo tenemos el log-out.
Nuestro diseño o layout para el ejemplo será
Resources\Views\Layouts\compulg.blade.php, con el siguiente código:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<title>@yield('title')</title>
<link href="http://www.iconj.com/icon.php?pid=sz8zcy10pc" rel="shortcut icon" />
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<meta http-equiv="content-language" content="es" />
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<meta name="name" content="{{ config('app.name', 'Laravel') }}" />
<meta itemprop="image" content="https://i.imgur.com/d7dL3sY.jpg" />
<meta itemprop="name" content="{{ config('app.name', 'Laravel') }}" />
<meta name="author" content="Luis Gabriel Hernandez Valderrama" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
<link href="{{ asset('css/compulg.css') }}" rel="stylesheet">
</head>
<body>
@include('_navclg')
<div class="container">
@section('mainttl')
<h1>@lang('main.title')</h1>
@show
<br>
@yield('content')
</div><br>
</body>
A la vista Resources\Views\home.blade.php, le asignamos el siguiente código:
home.blade.php
@extends('layouts.compulg')
@section('title')
@lang('main.titlepg')
@endsection
@section('mainttl')
@endsection
@section('content')
@if(Session::has('message'))
@if(Session::get('message') == 'lblupdateok')
<div class="container alert alert-success">
@lang('main.lblupdateok')
</div>
@endif
@endif
<div class="card">
<div class="card-header">@lang('main.lblindice') @lang('main.title')</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
<div>
<a href="{{route('post/index')}}" class="btn btn-primary">
@lang('main.lblposts')
</a><br><br>
<a href="{{route('post/create')}}" class="btn btn-primary">
@lang('main.lblcreatepost')
</a><br><br>
<a href="{{route('post/my')}}" class="btn btn-primary">@lang('main.lblbtnmyposts')</a>
</div>
</div>
</div>
@endsection
Las vistas para los posts, las creamos en Resources\Views\Post, con los siguiente nombres y códigos:
index.blade.php
@extends('layouts.compulg')
@section('title')
@lang('main.lblttlpsts')
@endsection
@section('mainttl')
@endsection
@section('content')
@if(Session::has('message'))
<div class="container alert alert-success">
{{ Session::get('message') }}
</div>
@endif
<div>
<div>
<a href="{{route('post/create')}}" class="btn btn-primary">@lang('main.lblbtncrear')</a>
<a href="{{route('post/my')}}" class="btn btn-primary">@lang('main.lblbtnmyposts')</a>
</div><br>
{{ $posts->links() }}
<div>
@foreach($posts as $post)
<div class="card">
<div class="card-header">{{ $post->title }}</div>
<textarea class="form-control" name="content" cols="30" rows="10" readonly>{{ $post->content }}</textarea>
</div><br>
@endforeach
</div>
{{ $posts->links() }}
</div>
@endsection
create.blade.php
@extends('layouts.compulg')
@section('title','Crear Publicacion')
@section('mainttl')
@endsection
@section('content')
@if(Session::has('message'))
<div class="container alert alert-success">
{{ Session::get('message') }}
</div>
@endif
@if($errors->any())
<div class="alert alert-danger">
<ul>
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route('posts.store') }}" method="post">
@csrf
<div>
<div>
<div class="form-group">
<label for="title">@lang('main.lblttlpost')</label>
<input type="text" class="form-control" name="title" id="title" placeholder="Titulo" value="{{ old('title') }}">
</div>
<div class="form-group">
<label for="contentlbl">@lang('main.lblcontpost')</label>
<textarea class="form-control" name="content" cols="30" rows="10">{{ old('content') }}</textarea>
</div>
</div>
<div>
<button class="btn btn-primary btn-block" type="submit">@lang('main.lblbtnsnd')</button>
</div>
</div>
</form>
@endsection
edit.blade.php
@extends('layouts.compulg')
@section('title')
@lang('main.lblbtnedit')
@endsection
@section('mainttl')
@endsection
@section('content')
@if(Session::has('message'))
@if(Session::get('message') == 'lblactualizado')
<div class="container alert alert-success">
@lang('main.lblactualizado')
</div>
@endif
@endif
@if($errors->any())
<div class="alert alert-danger">
<ul>
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route('posts.update',['post' => $post]) }}" method="post">
@method('PUT')
@csrf
<div>
<div>
<div class="form-group">
<label for="title">@lang('main.lblttlpost')</label>
<input type="text" class="form-control" name="title" id="title" placeholder="Titulo" value="{{ $post->title }}">
</div>
<div class="form-group">
<label for="contentlbl">@lang('main.lblcontpost')</label>
<textarea class="txtpost form-control" name="content" cols="30" rows="10">{{ $post->content }}</textarea>
</div>
</div>
<div>
<button class="btn btn-primary btn-block" type="submit">@lang('main.lblbtnsnd')</button>
</div>
</div>
</form>
@endsection
show.blade.php
@extends('layouts.compulg')
@section('title')
@lang('main.lblshwttlpst')
@endsection
@section('mainttl')
@endsection
@section('content')
@if(Session::has('message'))
@if (Session::get('message') == 'lblcreado')
<div class="container alert alert-success">
@lang('main.lblcreado')
</div>
@elseif (Session::get('message') == 'lblactualizado')
<div class="container alert alert-success">
@lang('main.lblactualizado')
</div>
@else
<div class="container alert alert-success">
{{ Session::get('message') }}
</div>
@endif
@endif
<div>
<div class="card">
<div class="card-header">{{ $post->title }}</div>
<textarea class="spmlgcardin" cols="30" rows="10" readonly>{{ $post->content }}</textarea>
</div><br>
</div>
@endsection
my.blade.php
@extends('layouts.compulg')
@section('title')
@lang('main.lblbtnmyposts')
@endsection
@section('mainttl')
@endsection
@section('content')
@if(Session::has('message'))
@if (Session::get('message') == 'lblcreado')
<div class="container alert alert-success">
@lang('main.lblcreado')
</div>
@elseif (Session::get('message') == 'lblborrado')
<div class="container alert alert-success">
@lang('main.lblborrado')
</div>
@elseif (Session::get('message') == 'lblactualizado')
<div class="container alert alert-success">
@lang('main.lblactualizado')
</div>
@elseif (Session::get('message') == 'lblnopermisos')
<div class="container alert alert-success">
@lang('main.lblnopermisos')
</div>
@else
<div class="container alert alert-success">
{{ Session::get('message') }}
</div>
@endif
@endif
<div>
<div>
<div>
@foreach($posts as $post)
<div class="card">
<div class="card-header">{{ $post->title }}</div>
<textarea class="spmlgcardin" cols="30" rows="10" readonly>{{ $post->content }}</textarea>
<div class="card-footer"><spam class="d-flex justify-content-end ml-2">
<a href="{{ route('posts.show',['post' => $post]) }}" class="btn btn-primary">@lang('main.lblbtnver')</a>
<a href="{{ route('posts.edit',['post' => $post]) }}" class="btn btn-primary ml-2">@lang('main.lblbtnedit')</a>
<form action=" {{ route('posts.destroy',['post' => $post]) }} " method="post">
@method('DELETE')
@csrf
<button class="btn btn-danger ml-2 mr-2">@lang('main.lblbtndel')</button>
</form>
</spam></div>
</div><br>
@endforeach
</div>
</div>
</div>
@endsection
Configurando las rutas, en el archivo 'Routes\web.php'
Route::get('/', function () {
return view('bienvenido');
});
Route::resource('posts', PostController::class);
Auth::routes(['verify' => true]);
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
Route::group(['middleware' => 'verified'], function(){
Route::get('/post/index', [App\Http\Controllers\PostController::class, 'index'])->name('post/index');
Route::get('/post/create', [App\Http\Controllers\PostController::class, 'create'])->name('post/create');
Route::get('/post/show', [App\Http\Controllers\PostController::class, 'show'])->name('post/show');
Route::get('/post/edit', [App\Http\Controllers\PostController::class, 'edit'])->name('post/edit');
Route::get('/post/my', [App\Http\Controllers\PostController::class, 'my'])->name('post/my');
});
Observaciones:
- Tal como está, la ruta 'http://127.0.0.1:8000/posts', nos permite ver las publicaciones, sin ingresar, ni registrarse, por medio del código resaltado en amarillo.
- Si colocamos la linea de código 'Route::resource('posts', PostController::class);', dentro de 'Route::group(['middleware' => 'verified'], function(){}' , no es visible sin ingreso, y por ende sin registro y verificación.
- Este es el medio por el cual, podemos hacer visibles las publicaciones a cualquier visitante, o restringirlas a usuarios registrados. Donde si la linea resaltada en amarillo, la colocamos fuera del código resaltado en verde, permite el aceso a 'http://127.0.0.1:8000/posts', a cualquira. Y si colocamos la linea resaltada en amarillo dentro del código resaltado en verde exige el registro y autenticación del usuario (autenticación que se realiza al verificar el correo).
- Si queremos que el acceso a las publicaciones sea libre, dejamos el conrtolador 'Route::resource('posts', PostController::class);', tal como está, de lo contrario lo incluimos al inicio del código verde.
- Para el acceso a las publicaciones de manera libre, en la vista bienvenido, y después de @endauth, colocamos el siguiente código <a href="/posts">@lang('main.lblposts')</a>, código que refiere al link de las publicaciones. Con el mismo propósito vamos a las plantillas de lenguaje y modificamos en español: 'subttl' => 'Por favor ingrese, registrese, o accede a las publicaciones según el caso.', y en inglés: 'subttl' => 'Please enter, register, or access the publications as appropriate.', con lo cual concluimos las modificaciones necesarias para dar acceso libre a las publicaciones.
En el próximo articulo veremos la creación de semilleros o seeders.
Comentarios
Publicar un comentario