Herramientas de usuario

Herramientas del sitio


informatica:programacion:php:frameworks:laravel:bases_de_datos

¡Esta es una revisión vieja del documento!


Trabajando con bases de datos en Laravel

Laravel

A partir de la versión 11, cada proyecto nuevo de Laravel está configurado para trabajar con una base de datos SQLite.

Conexiones soportadas

Laravel soporta por defecto los siguientes motores de bases de datos:

  • MariaDB
  • MySQL
  • PostgreSQL
  • SQLite
  • SQL Server

En el fichero config/database.php podemos ver los diferentes tipos de conexiones con bases de datos.

Conexión con bases de datos

SQLite

Fichero .env:

// ...
DB_CONNECTION=sqlite
#DB_HOST=
#DB_PORT=
#DB_DATABASE=
#DB_USERNAME=
#DB_PASSWORD=

Laravel, por defecto, asumirá que usaremos un fichero llamado database.sqlite que se almacenará en database/. En ese directorio crearemos el archivo.

MySQL

Una vez creada la base de datos MySQL, vamos a config/database.php. En cierto punto veremos que la conexión por defecto es a SQLite:

    'default' => env('DB_CONNECTION', 'sqlite'),

La variable que vamos a cambiar, DB_CONNECTION está en el archivo .env, en la raíz del proyecto. Hacemos los siguientes cambios:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=blog
DB_USERNAME=root
DB_PASSWORD=

Probaremos si la conexión es exitosa ejecutando unas migraciones:

php artisan migrate

Revisamos que se hayan creado nuevas tablas en nuestra base de datos de MySQL.

Migraciones

Son clases a partir de las cuales podemos crear o modificar tablas en la base de datos.

Si vamos a la carpeta database/migrations veremos 3 ficheros de migraciones:

  • database/migrations/xxxx_create_users_table.php
  • database/migrations/xxxx_create_cache_table.php
  • database/migrations/xxxx_create_jobs_table.php

Esas migraciones son las responsables de crear tablas. Por ejemplo, el fichero xxxx_create_users_table.php crearía las siguientes tablas:

  • users
  • password_reset_tokens
  • sesions

Ejecutar migraciones

php artisan migrate

Si ejecutamos migraciones de nuevo (sin haberlas borrado), Laravel no hará nada porque sabe que se ejecutó anteriormente porque mantiene una tabla llamada migrations con esa información.

Deshacer migraciones

php artisan migrate:rollback

Ese comando ejecutará el método down() de cada uno de los ficheros de migraciones para eliminar todas las tablas.

Crear migración

Para crear una migración que nos permita crear una tabla de productos:

php artisan make:migration create_posts_table

Esto habrá creado una migración, una clase anónima con los métodos up y down:

    // ...
    public function up(): void 
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
    }
 
    public function down(): void 
    {
        Schema::dropIfExists('posts');
    }    

La completamos a nuestro gusto:

    // ...
    public function up(): void 
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->longText('content');
            $table->timestamps();
        });
    }

$table→timestamps() creará los campos created_at y updated_at.

Para ejecutar las migraciones:

php artisan migrate:fresh

El modificador fresh elimina todo de la base de datos. Es útil mientras desarrollamos, pero no deberíamos hacer eso nunca en producción. Si usamos el modificador rollback, solo borrará las migraciones del último lote (campo batch de la tabla migrations).

Existe el modificador refresh que no eliminaría las tablas creadas sin usar php artisan

Si tuviéramos un método para insertar datos:

php artisan migrate:fresh --seed

Modificar tablas

Si hacemos cambios en las tablas gestionadas por las migraciones, lo ideal es crear una nueva migración:

php artisan make:migration add_avatar_to_users_table

Esto creará un nuevo fichero de migración:

// ...
    public function up(): void
    {
        Schema::table('users', function (Blueprint $table) {
        });
    }
 
    public function down(): void
    {
        Schema::table('users', function (Blueprint $table) {
        });
    }    

Y ahora lo modificaremos para añadir y quitar el campo:

// ...
 
    /**
    * Run the migrations
    */
 
    public function up(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('avatar')->nullable()->after('name'); 
            // Para que se añada el campo después del campo 'name', usamos el método 'after'
        });
    }
 
    /**
    * Reverse the migrations
    */
    public function up(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('avatar');
        });
    }    

Ya podríamos usar el comando:

php artisan migrate

Eloquent

Eloquent es el ORM de Laravel. Un ORM (Object-Relational Mapping) se encarga de “mapear” / asociar la base de datos con estructuras de modelos que tengamos en nuestro sistema. El propósito es poder interactura con la base de datos como si fueran objetos dentro de la Programación Orientada a Objetos.

Modelos

La carpeta app/Models se incluyó por primera vez en la versión 8

En Laravel, los modelos y las tablas están estrechamente relacionados. Los modelos son el mecanismo de conexión entre nuestro código y la estructura de tablas y base de datos.

Un modelo es una clase y contiene la funcionalidad de los campos de la tabla a la que representa.

Hay varios tipos de atributos:

  • $fillable: atributos que se pueden asignar de manera masiva.
  • $hidden: atributos que se ocultarán al recuperar información desde la base de datos.
  • $casts: atributos que deben ser transformados a cierto tipo de dato.

Por defecto, Laravel toma como nombre de la tabla el nombre del modelo, pero en plural, es decir, si tenemos un modelo User, Laravel asumirá que la tabla será users.

Si no queremos seguir esa convención, añadiríamos al modelo el atributo $table asignándole como valor el nombre de la tabla.

La clase Model.php, que es de la que heredan los modelos que creamos, está en vendor/laravel/framework/src/Illuminate/Database/Eloquent/.

Creación de un modelo

Nos ayudamos de Artisan:

php artisan make:model Post

Esto habrá creado el modelo Post.php en app/Models/:

namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Post extends Model 
{
 
}

Debemos indicarle qué tabla va a administrar:

// ...
class Post extends Model 
{
    protected $table = 'posts';
}

Si la tabla se llama igual que la clase, pero en plural, no sería necesario crear la variable $table con el nombre de la tabla. Hay que tener en cuenta que Eloquent usa el inglés, hay que tenerlo en cuenta para que funcionen estas convenciones. Por ejemplo, si tenemos una tabla llamada “voces” y el modelo Voz, Eloquent esperará encontrarse una tabla llamada vozs.

Añadir registros

Si queremos añadir un registro:

$post = new Post;
 
$post->title = "Título de prueba";
$post->content = "Contenido de prueba";
 
$post->save();

Editar registro

Si queremos editar un registro:

$post = Post::find(1); // Recuperamos el registro con ID 1
 
$post->title = "Nuevo título de prueba";
 
$post->save();

Podemos listar todos los posts atendiendo a cierto criterio:

$post = Post::orderBy('id', 'asc')
             ->select('id', 'title)
             ->take(2) // Limítalo a 2 registros
             ->get();
return $post;

Eliminar registro

Para eliminar un registro:

$post = Post::find(1); // Recuperamos el registro con ID 1
 
$post->delete();

Mutadores y accesores

Imaginemos que queremos que antes de meterse un valor en un campo, queremos tratarlo. Y también queremos tratarlo a la hora de recuperarlo. Para eso están los “mutadores” y “accesores”.

En el modelo que gestiona la tabla que tiene el campo que nos interesa, crearemos una función con el nombre del campo en cuestión:

// ...
Use Illuminate\Database\Eloquent\Casts\Attribute;
 
class Post extends Model 
{
    protected function title(): Attribute
    {
        return Attribute::make(
            set: function($value) {
                return strtolower($value);
            },
            get: function($value) {
                return ucfirst($value);
            }
        );
    }
}

Con el código anterior, cuando se vaya a introducir un valor al campo title, se transformará antes a minúscula. Cuando vayamos a coger el valor del campo, se mostrará la primera letra en mayúscula.

Casting

Atributos de fecha con Carbon para facilitar operaciones como la diferencia de fechas, etc:

namespace App\Models;
 
Use Illuminate\Database\Eloquent\Model;
 
class Post extends Model
{
    // Ahora indicamos qué campo debe ser transformado a una instancia de Carbon
    protected function casts(): array
    {
        return [
            'published_at' => 'datetime'
        ];
    }
}

Ahora podríamos usar el nuevo campo con las funcionalidades de Carbon:

Route::get('prueba', function() {
    $post = Post::find(1);
 
    return $post->published_at->format('d/m/Y');
    // return $post->published_at->diffForHumans(); // Tiempo transcurrido (relativo)
});

Podemos ver todos los tipos que podríamos transformar (casting) desde la documentación oficial.

Insertar información

Seeders

Podemos definir qué registros estén presente siempre que creemos una tabla. Para esto existen los seeders en Laravel.

En la carpeta database/seeders/ existe un fichero llamado DatabaseSeeder.php con un método run().

Imaginemos que cada vez que se cree una tabla de usuarios, se añada un registro de un usuario con ciertos campos:

namespace Database\Seeders;
 
use App\Models\User;
use Illuminate\Database\Seeder;
 
class DatabaseSeeder extends Seeder 
{
    /**
    * Seed the application's database
    */
    public function run(): void
    {
        $user = new User();
 
        $user->name = 'Pepito Grillo';
        $user->email = 'pepito@paisdelasmaravillas.com';        
        $user->password = bcrypt('contraseña');
 
        $user->save();
    }
}

Si ahora ejecutamos:

php artisan migrate:fresh

Se habrán borrado todas las tablas, pero no se añade el registro que queremos. Para ello, debemos usar otro comando:

php artisan db:seed

Esto poblará las tablas de las bases de datos con lo que hemos programado.

Podemos combinar los dos comandos anteriores de la siguiente manera:

php arisan migrate:fresh --seed

Si queremos añadir varios registros, no es muy cómodo hacerlo todo en el fichero DatabaseSeeder.php. Lo mejor es crear archivos con seeders por separado:

php artisan make:seeder UserSeeder

Esto habrá creado el fichero UserSeeder.php en database/seeders. Y ahí iremos creando el código para añadir usuarios. Lo mismo para PostSeeder.php.

Después tendremos que modificar el método run() del fichero DatabaseSeeder.php para indicar qué seeders queremos que se ejecuten:

class DatabaseSeeder extends Seeder 
{
    /**
    * Seed the application's database
    */
    public function run(): void
    {
        $this->call([
            PostSeeder::class,
            UserSeeder::class
        ]);
    }
}

Para ejecutar los seeders:

php artisan migrate:fresh --seed

Factories

Los factories son clases con el nombre de un modelo y luego una definición donde indicamos cómo queremos rellenar los datos del modelo. Funcionan como una serie de fábricas donde indicamos qué queremos que se cree en cada campo.

Al crear un proyecto en Laravel, incluye el factory User en database/factories/UserFactory.php.

// ...
    public function definition(): array
    {
        return [
            'name' => fake()->name(),
            'email' => fake()->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => static::$password ??= Hash::make('password'),
            'remember_token' => Str::random(10),
        ];
    }

Para generar estos datos, Laravel se apoya en la biblioteca Faker que permite generar información aleatoria.

Para llamar a un factory existente, primero hay que modificar el método run() de DatabaseSeeder.php:

class DatabaseSeeder extends Seeder 
{
    /**
    * Seed the application's database
    */
    public function run(): void
    {
        // ...
        User::factory(10)->create();
    }
}

En el código anterior indicamos que queremos crear 10 registros de usuarios utilizando el factory UserFactory.php.

Para ejecutar el factory:

php artisan migrate:fresh --seed

También podíamos haber colocado el código de factory en UserSeeder.php:

// ...
    public function run(): void
    {
        $user = new User();
 
        $user->name = 'Pepito Grillo';
        $user->email = 'pepito@paisdelasmaravillas.com';
        $user->password = bcrypt('alicia');
 
        $user->save();
 
        User::factory(10)->create();
    }

Para crear nuestro propio factory, usaremos Artisan:

php artisan make:factory PostFactory

Hemos indicado que el factory que queremos crear es para el modelo Post siguiendo la convención del nombre. Tendremos que rellenar la definición de la factory recién creada:

    public function definition() 
    {
        return [
            'title' => $this->faker->sentence(),
            'content' => $this->faker->text(1000),
            'category' => $this->faker->word(),
            'published_at' => $this->faker->dateTime(),
        ];
    }

Es importante indicar en nuestro modelo (app/Models/Post.php) que tiene una factory:

use Illuminate\Database\Eloquent\Factories\HasFactory;
 
class Post extends Model
{
   use HasFactory;
}

Ahora vamos a PostSeeder.php para usarlo:

// ...
    public function run(): void 
    {
        Post::factory(100)->create();
    }

Ya podremos usarlo y se generarán 100 registros en la tabla posts con información aleatoria:

php artisan migrate:fresh --seed

Database seeder

Podemos indicar por código cómo queremos crear instancias de nuestros modelos sin usar tinker.

Vamos a database/seeders/DatabaseSeeder.php:

    public function run() 
    {
        $products = Product::factory(5)->create();
    }

Para ejecutar este seeder:

php artisan migrate:fresh --seed

Se crea de nuevo la base de datos y sus tablas y finalmente el seeder que ejecutará 5 instancias de Producto y las meterá en base de datos.

Si solo queremos ejecutar el seeder:

php artisan db:seed 

Construyendo consultas a base de datos

Usando el Query Builder

Laravel dispone de un Query Builder que permite construir las consultas SQL para interactuar directamente con la base de datos.

Si queremos obtener toda la información de cierta tabla usando Query Builder:

use Illuminate\Support\Facades\DB;
 
class ProductController extends Controller
{
    public function index()
    {
        $products = DB::table("products")->get();
 
        dd($products);
    }
}

Cogiendo solo un elemento:

    // code
    public function show($product)
    {
        $product = DB::table("products")->where("id", $product)->first();
 
        dd($product);
    }

Podríamos abreviar la consulta usando el método find ya que buscará el primer elemento que coincida con el ID que indicamos:

    // code
        $product = DB::table("products")->find($product);

Usando los modelos

Cualquier cosa que se pueda hacer con el Query Builder, también se puede hacer a través del modelo, y es lo recomendable (usando Eloquent).

La mayor utilidad de usar el modelo es su mantenimiento y escalabilidad del proyecto. Por ejemplo, si en algún momento se cambia el nombre de la tabla, usando el Query Builder, tendríamos que buscar en nuestro código todas las sentencias donde usamos el Query Builder con dicha tabla. Si usamos modelos, basta con hacer el cambio en el atributo $table del modelo.

Si queremos obtener una lista completa de productos usando el modelo y Eloquent (el ORM de Laravel):

use Illuminate\Support\Facades\DB;
 
class ProductController extends Controller
{
    public function index()
    {
        $products = Product::all();
 
        dd($products);
    }
}

Cogiendo solo un elemento:

    // code
    public function show($product)
    {
        $product = Product::find($product);
 
        dd($product);
    }

Tenemos un método interesante para cuando no se encuentra lo que buscamos:

    // code
    public function show($product)
    {
        $product = Product::findOrFail($product);
 
        // code       
 
    }

Lo que hará findOrFail es lanzar una excepción que Laravel tratará como un error 404.

Si no lo usásemos y no existiera el producto, find devolvería NULL.

informatica/programacion/php/frameworks/laravel/bases_de_datos.1741257652.txt.gz · Última modificación: por tempwin