logo cosasdedevs
Parte 2: Creación de modelos y migración de tablas con Laravel 8

Parte 2: Creación de modelos y migración de tablas con Laravel 8



My Profile
Dic 18, 2020

Bienvenid@s a este segundo tutorial en el que vamos a aprender como crear un blog con Laravel 8. En este segundo tutorial veremos como crear nuestros modelos para poder acceder a los datos y también aprenderemos a migrar las tablas que creemos a la base de datos que decidamos utilizar 💪.

Para este ejemplo, vamos a utilizar MySQL así que o bien podéis crear un contenedor con Docker o instalarlo directamente en vuestra máquina. Ahí lo que os resulte mejor.

Una vez tengamos instalado MySQL, debemos crear una nueva base de datos. Yo la he llamado "tutorial_laravel_blog" y como contejamiento he seleccionado "utf8mb4_unicode_ci".

Una vez hecho esto, debemos abrir el archivo .env que debería estar en el directorio principal del proyecto. Si no es así, podéis duplicar el archivo .env.example y renombrarlo como .env. Una vez hecho esto, lo abrimos y añadimos los datos para configurar nuestra conexión con MySQL. Os debe quedar algo como esto:

.
.
.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=tutorial_laravel_blog
DB_USERNAME=root
DB_PASSWORD=mypass
.
.
.

Ahora que ya tenemos esto, es hora de crear los modelos y las migraciones. Nuestro proyecto ya tiene el modelo y la migración para los usuarios, ya que se generó automáticamente en el tutorial anterior así que solo tendremos que editar la migración para añadir unos campos extras. Entonces para este caso, solo necesitaremos crear los modelos y migraciones para los posts y comentarios.

Vamos al terminal y lanzamos los siguientes comandos:

php artisan make:model Post -m
php artisan make:model Comment -m

Nota: Al añadir -m le estamos diciendo que también nos cree el archivo para las migraciones.

Ahora podremos ver que en nuestro proyecto hay 4 archivos nuevos, los modelos para posts y comentarios en la carpeta app/Models y en database/migrations los archivos en los que configuraremos que información queremos guardar en las respectivas tablas de posts y comentarios.

Usuarios

Ahora que ya tenemos las estructuras creadas para los distintos modelos y migraciones, vamos a empezar a realizar unos cambios, para ello, vamos al archivo database/migrations/2014_10_12_000000_create_users_table.php (puede que si han actualizado la librería de laravel/ui haya cambiado la fecha, lo importante es que el archivo contenga en su nombre "*_create_users_table.php"). Ahí deberíamos tener un método llamado up() con los siguientes datos:

    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

En está función se indica el nombre que tendrá la tabla en la base de datos, en este caso users, y los campos. Como veis, el objeto $table define que tipos puedes crear y configuraciones adicionales como indicar un campo único o que pueda ser nulo.

En este caso, definimos una columna de tipo id con el método id(). Esto nos crea la columna id que será la primary key y autoincrementable. También creamos una columna de tipo string para el nombre del usuario. El caso de email es igual al del nombre aunque este será único. También añade un campo de tipo fecha para la verficicación del email que podrá ser nulo. Otra columna para la contraseña. Una columna llamada remember_token que creará una columna de tipo string con un tamaño de 100 caracteres y que podrá ser nulo y el método timestamps creará dos columnas de tipo timestamp llamadas created_at y updated_at en el que se guardará la fecha de creación del usuario y su última actualización.

Aparte de estos datos, nosotros añadiremos dos columnas más de tipo booleano, una para saber si el usuario es staff y otra para saber si es administrador. El valor por defecto que le daremos será false. Una vez hecho esto, la migración nos quedará así:

    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
            $table->boolean('is_staff')->default(false);
            $table->boolean('is_admin')->default(false);
        });
    }

Si queréis ver más información acerca de las migraciones, os dejo el enlace a la documentación, en el que podréis ver los distintos tipos de datos que podemos crear y las posibles configuraciones.

Ahora que tenemos la migración configurada, vamos a añadir unas funciones extra a nuestro modelo para los usuarios, estás nos facilitarán el trabajo a la hora de verificar los permisos de un usuario.

Para ello abrimos el archivo app/Models/User.php y justo antes del cierre de la clase, añadimos las siguientes funciones:


    public function isAdmin()
    {
        return $this->is_admin;
    }

    public function isStaff()
    {
        return $this->is_staff;
    }

    public function authorizeRoles($roles)
    {
        abort_unless($this->hasAnyRole($roles), 404);
        return true;
    }

    public function hasAnyRole($roles)
    {
        if (is_array($roles)) {
            foreach ($roles as $role) {
                if ($this->$role) {
                    return true;
                }
            }
        } else {
            if ($this->$roles) {
                return true;
            }
        }
        return false;
    }

Los métodos isAdmin() e isStaff() nos devolverán si un usuario tiene permisos de administrador o de staff.

El método authorizeRoles() recibirá un array con los permisos que necesita una acción. Si el usuario los tiene, le permitirá al usuario realizarla, si no, lo enviará a una página de 404 not found. Por lo general el estado debería ser 401 unauthorized pero por seguridad y para evitar que alguna persona "malintencionada" detecte que urls pueden existir en nuestra web o no, usaremos el error 404 y no el 401.

La función abort_unless es propia de Laravel y lanza la excepción http que indiquemos si le pasamos como parámetro false.

Por último, el método hasAnyRole() recibe un string o array con una serie de permisos y comprueba si el usuario los tiene. Si los tiene devuelve true y si no es así, retornará false.

Comentarios

Ahora que ya tenemos la tabla users configurada, es hora de pasar a los comentarios, para ello vamos al archivo de migración de los comentarios que deberá llamarse algo así database/migrations/2020_12_05_100832_create_comments_table.php, pero con la fecha en la que habéis creado el modelo en vez de la que aparece ahí. Lo abrimos y sustituimos el método up() por el que os paso a continuación:

    public function up()
    {
        Schema::create('comments', function (Blueprint $table) {
            $table->id();

            $table->foreignId('user_id')->constrained();
            $table->foreignId('post_id')->constrained();

            $table->text('comment');

            $table->timestamps();
        });
    }

Para la tabla de comentarios, añadimos las claves foráneas para el usuario y post, ya que un comentario será de un usuario y estará relacionado con un post, además de un campo tipo text para guardar el comentario del usuario. Como en la tabla de usuarios, también añadimos los timestamps para conocer la fecha de creación y actualización del comentario.

Ahora vamos al archivo app/Models/Comment.php y modificamos su contenido por el siguiente:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    use HasFactory;

    protected $fillable = [
        'comment', 'user_id', 'post_id'
    ];

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function post()
    {
        return $this->belongsTo(Post::class);
    }
}

La variable fillable contendrá los campos en los que podremos guardar información de forma masiva.

Los métodos user() y post() crean la relación con esas tablas y el comentario gracias al método belongsTo(), lo que nos permitirá obtener datos del usuario y del post desde un objeto de tipo Comment, esto veremos como utilizarlo más adelante 💪.

Posts

Por último, configuraremos los posts, para ello, vamos al archivo database/migrations/2020_12_05_100832_create_posts_table.php y modificamos el método up() para que nuestra tabla contenga las siguientes columnas:

    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();

            $table->foreignId('user_id')->constrained();

            $table->string('title')->unique();
            $table->string('slug')->unique();
            $table->mediumText('body');
            $table->boolean('is_draft')->default(false);

            $table->timestamps();
        });
    }

Como veis, añadimos la relación con el usuario, ya que un post siempre pertenecerá a un usuario, también un string de tipo único para el título, otro string de tipo único para generar la url que se llamará slug y veremos más adelante como generarla de forma automática y que sea amigable para mejorar nuestro SEO. El body que contendrá todo el cuerpo de nuestro post. Un campo is_draft para saber si nuestro post es un borrador o está publicado, esto hará que se muestre o no de forma pública en nuestro proyecto y por último, los timestamps como en las tablas de usuarios y comentarios.

Posts

Ahora que ya tenemos la migración, es hora de configurar el modelo Posts pero antes, necesitaremos una dependencia extra para generar automáticamente las urls. Para instalarla lanzamos el siguiente comando:

composer require cviebrock/eloquent-sluggable

Ahora sí, abrimos el archivo app/Models/Post.php y sustituimos su código por el siguiente:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Cviebrock\EloquentSluggable\Sluggable;

class Post extends Model
{
    use HasFactory;
    use Sluggable;

    /**
     * Get the user record associated with the post.
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    /**
     * Get the comments for the blog post.
     */
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }

    /**
     * Return the sluggable configuration array for this model.
     *
     * @return array
     */
    public function sluggable()
    {
        return [
            'slug' => [
                'source' => 'title',
                'onUpdate' => true
            ]
        ];
    }

    public function getGetLimitBodyAttribute()
    {
        return substr($this->body, 0, 140) . '...';
    }
}

Aquí lo que estamos haciendo, es añadir la asociación con el usuario, también con los comentarios que puede contener un post. Al poder tener varios asociados a un mismo post, usaremos hasMany en vez de belongsTo como hacemos con el usuario.

La siguiente función que tenemos es el método sluggable() de la librería que instalamos unos pasos atrás. Esta función hará que cuando guardemos o actualicemos un post, convierta el título del post a un tipo de texto sin caracteres especiales y que podremos usarlo como url amigable al acceder a un post en concreto. Esto lo veremos en los siguientes pasos.

Por último, tenemos el método getGetLimitBodyAttribute() que realiza un substring del cuerpo del post para obtener los primeros 140 caracteres. Más adelante veremos donde daremos uso a esta función.

Ahora que ya tenemos los modelos y migraciones configuradas, es hora de crear las tablas en nuestra bbdd, para ello lanzaremos el siguiente comando:

php artisan migrate

Nota: puede que como a mí, os aparezca el siguiente error "PDOException::("SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 1000 bytes")".

Para solucionarlo, debéis ir al archivo app/Providers/AppServiceProvider.php y modificarlo para que quede de la siguiente forma:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Schema;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }


    public function boot()
    {
        Schema::defaultStringLength(191);
    }

}

Esto modificará el tamaño por defecto en las columnas de tipo string y al volver a lanzar el comando php artisan migrate, ya no nos aparecerá el error y se crearán nuestras tablas en la bbdd (aunque antes deberemos eliminar las tablas que si se han creado, si no nos aparecerá un error avisándonos de que algunas tablas ya existen).

Crear datos de prueba

Ahora que ya tenemos nuestras tablas creadas, vamos a crear unos datos fakes para poder trabajar con información en nuestro proyecto. Laravel nos provee de la librería factory que nos ayuda a automatizar ese proceso y ahorrarnos el trabajo.

Para crear datos fakes sobre la tabla de posts, lanzaremos el siguiente comando:

php artisan make:factory PostFactory -m Post

Este comando creará un archivo llamado PostFactory dentro de databases/factories. Abrimos el archivo y modificamos el método definition() para que contenga la siguiente información:

    public function definition()
    {
        return [
            'user_id'   => 1,
            'title'     => $this->faker->sentence,
            'body'      => $this->faker->text(3000),
        ];
    }

Aquí definimos que cuando queramos crear un post de prueba, siempre tendrá el user_id con valor 1, esto es así porque los queremos asociar con un usuario que crearemos más adelante. Como título y body generamos unos textos aleatorios que ya tiene predefinidos esta librería.

Ahora iremos al archivo database/seeders/DatabaseSeeder.php y modificaremos su contenido para que contenga el siguiente código:

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use App\Models\User;
use App\Models\Post;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        User::create([
            'name' => 'Alber',
            'email' => 'alber@cosasdedevs.com',
            'password' => bcrypt('admin123'),
            'is_admin' => true,
            'is_staff' => true,
        ]);

        Post::factory()->count(50)->create();
    }
}

Este código hará que ahora cuando lancemos un comando, nos cree un usuario (yo le he puesto mi mail, mi nombre y un pass sencillo para probar en local, vosotros poned lo que queráis, eso si, dejad is_admin e is_staff como true porque lo necesitaremos más adelante). Esto creará nuestro primer usuario en la bbdd con el id 1, lo que nos permitirá poder asociarle posts.

La siguiente línea utiliza la configuración que creamos en el archivo PostFactory.php, con el método count() y pasándole el valor 50 por parámetro le decimos que queremos que cree 50 posts y el método create() ejecuta la acción.

Ahora solo debemos lanzar el siguiente comando y veremos como la tabla posts será poblada con datos de pruebas y también creará nuestro usuario para poder trabajar con el proyecto.

php artisan db:seed

Con esto tendríamos configurada toda la parte de modelos y migraciones para la base de datos. Os dejo el enlace a esta parte del proyecto en mi GitHub.

En el próximo tutorial nos meteremos de lleno en la gestión de plantillas y por fin podremos ver la página principal y el detalle de los posts en funcionamiento (disponible el 24 de diciembre de 2020) 😁.

Como siempre, os recomiendo seguirme en Twitter y nos vemos en el siguiente tutorial 👋.

 
11599 vistas

🐍 Sígueme en Twitter

Si te gusta el contenido que subo y no quieres perderte nada, sígueme en Twitter y te avisaré cada vez que cree contenido nuevo 💪
Luego ¡Te sigo!

Nos tomamos en serio tu privacidad

Utilizamos cookies propias y de terceros para mejorar la experiencia del usuario a través de su navegación. Si pulsas entendido aceptas su uso. Ver política de cookies.