Montando un proyecto
Instalación del entorno necesario
1. Instalamos Laragon Full.
2. Instalamos composer
3. Instalamos globalmente el creador de proyectos de Laravel ejecutando en la terminal el siguiente comando:
composer global require laravel/installer
3. Para probar, podemos ir a localhost.
Creación de un proyecto en Laravel
Tenemos dos opciones:
Utilizando Laragon
Utilizando Laragon. Esta opción se asegura de manetener la compatibilidad entre la versión de Laravel que instalaremos y la versión del servidor que tenemos instalado, lo que se traduce en menos posibilidades de error:
Aplicación de Laragon → Menu → Quick App → Laravel
Desde la consola
1. Ejecutamos el siguiente comando dentro de c:/laragon/www para crear nuestro proyecto de Laravel:
laravel new example-app
2. Desde el panel de administración de Laragon, recargamos el apache para que detecte el proyecto que acabamos de crear.
3. Ingresamos en: example-app.test
Configuración inicial
- Debemos renombrar el fichero .env.example a .env e introducir los datos de conexión a la base de datos.
- Debemos introducir la clave de cifrado de nuestra App en el fichero .env. Esto se hará automáticamente ejecutrando los siguientes comandos:
php artisan key:generate # genera la key y la guarda automáticamente en el fichero .env
php artisan config:cache # borra la caché para que el cambio surta efecto
Tras ejecutra estos comandos habrá que reiniciar el servidor.
- Al insertar un usuario en la base de datos, nos dará este error:
Laravel Unknown Column 'updated_at'
Para evitarlo, podemos añadir esta propiedad al modelo de User:
/app/Models/User.php
public $timestamps = false;
Error en el driver
Es posible que al ejecutar el comando
composer install
o al ejecutar algunos comandos de php, puede dar el siguiente error:
could not find driver (Connection: mysql, SQL: select table_name as `name`, (data_length + index
_length) as `size`, table_comment as `comment`, engine as `engine`, table_collation as `collation`
from information_schema.tables where table_schema = '...' and table_type in ('BASE TABLE',
'SYSTEM VERSIONED') order by table_name)
Esto puede ser debido a que nos falta el driver de php_pdo_mysql. Aunque lo tengamos instalado en Laragon, puede ser que no lo tengamos instalado en el php de nuestro sistema operativo. Para que esté instalado en el PHP de nuestro sistema operativo, debemos encontrar la ubicación del fichero php.ini de nuestro sistema operativo ejecutando el comando:
php --ini
En este fichero borraremos el ; de esta línea:
;extension=php_pdo_mysql.dll
.
Ficheros y carpetas fundamentales de una aplicación Laravel
- app. Aquí estan los ficheros que nosotros estamos desarrollando. Aquí pasaremos la mayor parte del tiempo.
- Http/Controllers: Esta es la capa que procesa las solicitudes del usuario.
- Http/Middleware: Nos permiten atrapar una petición y hacer alguna validación adicional, inyectar código, etc.
- models. Se comunican con tablas de la base de datos para gestionarlas.
- routes: Aquí están las rutas correspondientes a las peticiones del cliente.
- vendor: Aquí está todo el código que necesita Laravel para poder funcionar, es decir, el propio framework y sus dependencias.
Componentes fundamentales de un proyecto con Laravel
- model → representan la estructura y la interacción con la base de datos. Proporcionan métodos para realizar operaciones de base de datos, como consultas, inserciones, actualizaciones y eliminaciones.
php artisan make:model Book
- controller → manejan la lógica de la aplicación y actúan como intermediarios entre las rutas y los modelos. Reciben solicitudes HTTP, interactúan con los modelos y devuelven una respuesta al cliente.
php artisan make:controller BookController
- seeder → permiten poblar la base de datos con datos de prueba o iniciales.
php artisan make:seeder BookSeeder
- migration → son documentos escritos en PHP que serán traducidos en tablas de la base de datos.
php artisan make:migration create_books_table
- factory → se utilizan para generar datos de prueba de manera rápida y automatizada. Son útiles para crear registros falsos en la base de datos durante las pruebas o para llenar la base de datos con datos de prueba.
La siguiente instrucción creará todos los componentes anteriores ficheros (aunque Book esta en singular, la tabla de la base de datos será creada en plural):
php artisan make:model Book -a
La siguiente instrucción creará un modelo, una migración y un seeder:
php artisan make:model Book -ms
Enrutamiento
Controller
Para crear las rutas editaremos el fichero routes/api.php:
use App\Http\Controllers\Api\BookController
...
// Es SUPER IMPORTANTE que la siguiente url no termine en barra
Route::get('/book', [BookController::class, 'index']);
Route::get('/book/{id}', [BookController::class, 'getById']);
Si con el navegador accedemos a la ruta http://laraapp.test/api/brand, deberíamos ver una página en blanco.
BookController.php
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Book;
use Illuminate\Http\Request;
class BookController extends Controller
{
public function index(Request $request)
{
// Recogemos los query params:
// i.get('/books', {params: { id}});
$id = $request->query('id');
// ... o los parámetros enviados en la url
// i.get(`/books/${id}`)
$id = $request->route('id');
if ($id!= null) {
$books = Book::where('categoryId', $categoryId)
->get();
return response()->json($books);
}
$books = Book::get();
// $books = Book::paginate(10);
return response()->json($books);
}
public function create(Request $request)
{
$title = $request->route('title');
$newBook = new Book;
$newBook->title= $title;
$newBook->save();
return response()->json(['message' => 'book created'], 200);
}
public function update(Request $request)
{
$title= $request->route('title');
$newBook = new Book();
$newBook->title= $title;
$newBook->save();
return response()->json(['message' => 'book updated'], 200);
}
public function destroy(Request $request, $digitalActionId)
{
$id = $request->route('id');
$res = Book::where('bookId','=', $id)->delete();
return response()->json(['message' => 'book deleted'], 200);
}
}
Componentes relacionados con la base de datos
Definiremos los datos de conexión el el fichero ./env
Migration
Creando la estructura de nuestras tablas
Siempre nombraremos nuestras tablas en plural. Esto es imprescindible para que todo funcione correctamente en Laravel. Las propiedades de las tablas podemos nombrarlas en singular.
Dentro de una migración, la función up() sirve para crear las columnas y la función down() para eliminar las columnas creadas. Este último paso nos permite revertir las migraciones.
Definiendo los tipos de dato
$table->string("title", 255);
$table->text("content");
$table->enum("posted", ["yes", "not"]);
$table->boolean('email_verified');
$table->unsignedBiginteger('editorial_id');
$table->unsignedInteger('price');
Relación one to many
$table->foreign("author_id")->references('id')->on('books')->onDelete("cascade");
Relación many to many
Vamos a crear una relación muchos a muchos entre la tabla students y la tabla teachers. Para ello, ambas tablas deberán tener su correspondiente fichero de modelo y migración.
Necesitaremos una tabla auxiliar o de pivote. Para crear el modelo y su correspondiente fichero de migración de la tabla de pivote, podemos usar:
php artisan make:model StudentTeacher -m
Las instrucciones para crear el modelo y el fichero de migraciones por separado serían:
php artisan make:model StudentTeacher
php artisan make:migration create_student_teachers_table
En el fichero de migración de de student_teachers:
$table->unsignedBiginteger('student_id');
$table->unsignedBiginteger('teacher_id');
$table->foreign('student_id')->references('id')->on('students')->onDelete('cascade');
$table->foreign('teacher_id')->references('id')->on('teachers')->onDelete('cascade');
Para montar la base de datos borrando los datos anteriores y cargar los nuevos…
Seeder
./database/seeders/BrandSeeder.php
class BrandSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
Book::create(
[
'title' => 'Viaje al centro de la tierra',
'description' => 'bla bla bla...'
]
);
}
}
./database/seeders/DatabaseSeeder.php
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
$this->call(BrandSeeder::class);
}
}
Para cargar los datos:
php artisan db:seed
Montar base de datos borrando datos
php artisan migrate:fresh --seed
- El modificador seed carga la base de datos con los datos especificados en los seeders.
- Es posible que en el gestor de la base de datos (heidi) no refresque los datos automáticamente y haya que cerrar la conexión y volver a abrirla para que se vean actualizados.
Consultas
Cuando necesito los datos de dos o más tablas puede pasar que:
- El where se aplica a todas las tablas que intervienen en la consulta → Usaremos join
Book::where('id', $bookId)
->join('autors', 'autors.bookId', '=', 'books.id')
- El where no se aplica a todas las tablas que intervienen en la consulta → Usaremos dos consultas
$books = $books1->concat($books2);
Añadiendo un alias.
->select('books.*', 'autors.name as autorName')
Recibiendo…
Un resultado:
$brand = Book::where('id', '=', $bookId)
->first();
Varios resultados:
$brands = Book::where('editorialId', '=', $editorialId)
->get();
Desplegar la aplicación en un servidor compartido
Navegaremos a la carpeta public de nuestro proyecto.
Logs (trazas)
Debemos añadir la siguiente variable de entorno al fichero .env:
APP_DEBUG=true
Los logs aparecerán en storage/logs/laravel.logs.
Para generar una traza podemos utilizar el siguiente código:
Log:info(print_r($obj), true); // El parámetro true hace que el objeto se muestre como un texto
Autentificación con Auth0
Configuración en Auth0
Applications → Create Application → Machine to Machine → Asignamos todos los permisos
Configuración en el código de Laravel
Instalaremos el módulo de auth0. Para ello, ejecutaremos el siguiente comando en la terminal:
composer require auth0/auth0-php
Añadiremos las siguientes variables de entorno al fichero .env:
# The URL of our Auth0 Tenant Domain.
# If we're using a Custom Domain, be sure to set this to that value instead.
AUTH0_DOMAIN='https://{yourDomain}'
# Our Auth0 application's Client ID.
AUTH0_CLIENT_ID='{yourClientId}'
# Our Auth0 application's Client Secret.
AUTH0_CLIENT_SECRET='{yourClientSecret}'
# Our Auth0 API's Identifier.
AUTH0_AUDIENCE='YOUR_API_IDENTIFIER'
Código de Laravel:
class UserController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$token = $request->post('token');
$auth0 = new Auth0([
'domain' => env('AUTH0_DOMAIN'),
'clientId' => env('AUTH0_CLIENT_ID'),
'clientSecret' => env('AUTH0_CLIENT_SECRET'),
'audience' => [env('AUTH0_AUDIENCE')],
'cookieSecret' => env('AUTH0_COOKIE_SECRET'),
]);
$userInfo = $auth0->decode($token);
var_dump($userInfo);
}
La aplicación de React:
import { useAuth0 } from "@auth0/auth0-react";
const { loginWithRedirect, isAuthenticated, getIdTokenClaims } = useAuth0();
useEffect(() => {
if (isAuthenticated) {
getIdTokenClaims().then(({ __raw: idToken }) => login(idToken));
}
}, [isAuthenticated]);
Posible error al ejecutar una petición:
cookieSecret` must be configured
Uso del Middleware para gestionar la autentificación
php artisan make:middleware EnsureTokenIsValid
/app/Http/Middleware/EnsureTokenIsValid.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Auth0\SDK\Auth0;
use App\Models\User;
use Illuminate\Support\Facades\Log;
class EnsureTokenIsValid
{
public static function getUserFromAuth0($token)
{
$auth0 = new Auth0([
'domain' => env('AUTH0_DOMAIN'),
'clientId' => env('AUTH0_CLIENT_ID'),
'clientSecret' => env('AUTH0_CLIENT_SECRET'),
'audience' => [env('AUTH0_AUDIENCE')],
'cookieSecret' => env('AUTH0_COOKIE_SECRET'),
]);
if ($token != null) {
$userInfo = $auth0->decode($token);
$userData = $userInfo->toArray();
$user = User::where('email', $userData['email'])->first();
if ($user) {
if ($user->email_verified != $userData['email_verified']) {
$user->email_verified = $userData['email_verified'];
$user->save();
}
return $user;
} else {
$user = new User();
$user->email = $userData['email'];
$user->email_verified = $userData['email_verified'];
$user->save();
return $user;
}
}
return null;
}
public function handle(Request $request, Closure $next): Response
{
$token = $request->header('Authorization');
$user = self::getUserFromAuth0($token);
if ($user == null) {
return response('Unauthenticated.', 401);
}
$request->request->add(['user' => $user]);
return $next($request, $user);
}
}
Para vincular el middleware a una petición:
/routes/api.php
Route::get('/tasks/{type}', [TaskController::class, 'index'])->middleware(EnsureTokenIsValid::class);;
Para acceder al valor seteado en el middleware desde el controlador, usaremos:
$user = $request->get('user'); // Esto coge el dato que puse en el middleware