Inicio
Artículos
Categorias
Etiquetas
Creación del proyecto y diseño de la base de datos del blog - I
publicado el: 2021-1-28   actualizado el: 2021-1-28   incluido en: Proyecto blog laravel
palabras totales: 2094   tiempo de lectura: 10 mins  

Creación del proyecto

La pieza fundamental para construir el proyecto es tener claro la estructura de la base de datos del blog.

Diagrama EER

Una vez que tengo planificada la base de datos para crear el proyecto con laravel debo situarme en el directorio /var/www/laravel/ dentro del cual creo todos los proyectos.

1
enrique@enrique-server:$ cd /var/www/laravel/

Creo un proyecto de nombre blog_laravel con jetstream y livewire.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
enrique@enrique-server:/var/www/laravel$ laravel new blog_laravel --jet


    |     |         |
    |,---.|--- ,---.|--- ,---.,---.,---.,-.-.
    ||---'|    `---.|    |    |---',---|| | |
`---'`---'`---'`---'`---'`    `---'`---^` ' '


Which Jetstream stack do you prefer?
  [0] livewire
  [1] inertia
 > 0

 Will your application use teams? (yes/no) [no]:
 > no

Sigue el proceso de instalación hasta el fin y cuando termina me pide que ejecute los comandos npm install y npm run dev desde dentro del proyecto.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Package manifest generated successfully.
75 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
Copied Directory [/vendor/laravel/sanctum/database/migrations] To [/database/migrations]
Copied File [/vendor/laravel/sanctum/config/sanctum.php] To [/config/sanctum.php]
Publishing complete.

Livewire scaffolding installed successfully.
Please execute the "npm install && npm run dev" command to build your assets.
The [/var/www/laravel/blog_laravel/public/storage] link has been connected to [/var/www/laravel/blog_laravel/storage/app/public].
The links have been created.

Application ready! Build something amazing.

Desde la terminal me sitúo dentro del proyecto blog_laravel y ejecuto dichos comandos.

1
enrique@enrique-server:/var/www/laravel/blog_laravel$ npm install

Mientras se ejecuta el primero de ellos modifico los parámetros para la conexión con al base de datos en el archivo .env

1
2
3
4
5
6
7
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
# DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=blog_laravel
DB_USERNAME=root
DB_PASSWORD=esaborit

Vuelvo a la consola y ejecuto.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
enrique@enrique-server:/var/www/laravel/blog_laravel$ npm run dev

> @ dev /var/www/laravel/blog_laravel
> npm run development


> @ development /var/www/laravel/blog_laravel
> cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --config=node_modules/laravel-mix/setup/webpack.config.js

        Additional dependencies must be installed. This will only take a moment.
 
        Running: npm install vue-template-compiler --save-dev --production=false
 
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.13 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.1.3 (node_modules/watchpack/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})

        Okay, done. The following packages have been installed and saved to your package.json dependencies list:
 
        - vue-template-compiler
 
98% after emitting SizeLimitsPlugin

 DONE  Compiled successfully in 50548ms                                                                                                                      10:58:34 PM

       Asset      Size   Chunks             Chunk Names
/css/app.css  4.38 MiB  /js/app  [emitted]  /js/app
  /js/app.js   669 KiB  /js/app  [emitted]  /js/app
enrique@enrique-server:/var/www/laravel/blog_laravel$ 

Una vez que termina la compilación creo la tabla blog_laravelen mi gestor de bases de datos para lo que utilizaré phpmyadmin y un virtualhost en Apache.

1
2
enrique@enrique-server:/etc/apache2/sites-available$ sudo cp livewire.conf blog_laravel.conf
enrique@enrique-server:/etc/apache2/sites-available$ sudo nano blog_laravel.conf

Modifico su contenido y lo dejo como sigue;

blog_laravel.conf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<VirtualHost *:80>
    ServerName blog_laravel.test
    ServerAdmin admin@blog_laravel.test
    DocumentRoot /var/www/laravel/blog_laravel/public

    <Directory /var/www/laravel/blog_laravel/>
          Options Indexes FollowSymLinks MultiViews
          AllowOverride All
          Order Allow,Deny
          Allow from all
          Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Guardo el archivo y lo habilito.

1
2
3
4
enrique@enrique-server:/etc/apache2/sites-available$ sudo a2ensite blog_laravel.conf
Enabling site blog_laravel.
To activate the new configuration, you need to run:
  systemctl reload apache2

Compruebo que sintácticamente no hay errores.

1
2
3
enrique@enrique-server:/etc/apache2/sites-available$ sudo apachectl configtest
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this message
Syntax OK

Reinicio Apache.

1
enrique@enrique-server:/etc/apache2/sites-available$ sudo systemctl restart apache2

Incluyo blog_laravel.test dentro del hosts de mi equipo anfitrión y ya podría acceder al proyecto desde mi navegador web, pero como he creado el proyecto laravel incluyendo jetstream si intento acceder al mismo sin hacer la migración de las tablas que necesita jetstream me devolvería un mensaje de error como el siguiente.

Por lo que desde la terminal de vscode realizo la migración de las tablas.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
enrique@enrique-server:/var/www/laravel/blog_laravel$ php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (3,653.58ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (3,246.81ms)
Migrating: 2014_10_12_200000_add_two_factor_columns_to_users_table
Migrated:  2014_10_12_200000_add_two_factor_columns_to_users_table (4,505.40ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (3,372.28ms)
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated:  2019_12_14_000001_create_personal_access_tokens_table (4,506.46ms)
Migrating: 2020_12_09_223020_create_sessions_table
Migrated:  2020_12_09_223020_create_sessions_table (11,528.22ms)

Ahora ya puedo acceder al proyecto desde mi navegador web

Diseño de la base de datos

Comienzo a crear la tablas y los modelos dentro del proyecto. Para ello empiezo por aquellas que son entidades fuertes.

1
2
3
enrique@enrique-server:/var/www/laravel/blog_laravel$ php artisan make:model Category -m
Model created successfully.
Created Migration: 2020_12_10_151428_create_categories_table

Me ha creado dos archivos, App\Models\Category.php y database\migrations\2020_12_10_151428_create_categories_table.php un modelo y una migración para la tabla categories.

Comienzo por el archivo de la migración donde incluiré dentro del método up los campos que va a tener la tabla.

2020_12_10_151428_create_categories_table.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateCategoriesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('categories', function (Blueprint $table) {
            $table->id();

            $table->string('name', 45);
            $table->string('slug', 45);

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

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('categories');
    }
}

Hago lo mismo para crear la tabla posts

1
2
3
enrique@enrique-server:/var/www/laravel/blog_laravel$ php artisan make:model Post -m
Model created successfully.
Created Migration: 2020_12_10_152625_create_posts_table

Dentro del archivo de la migración incluyo los campos de la tabla posts.

2020_12_10_152625_create_posts_table.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();

            $table->string('name');
            $table->string('slug');
            $table->text('extract');
            $table->longText('body');
            $table->enum('status', [1,2])->default(1); //1 es borrador y 2 publicado
            $table->unsignedBigInteger('category_id');
            $table->unsignedBigInteger('user_id');

            $table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');

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

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}

Repito este proceder con la tabla tags.

1
2
3
enrique@enrique-server:/var/www/laravel/blog_laravel$ php artisan make:model Tag -m
Model created successfully.
Created Migration: 2020_12_10_154814_create_tags_table

2020_12_10_154814_create_tags_table.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateTagsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('tags', function (Blueprint $table) {
            $table->id();

            $table->string('name');
            $table->string('slug');
            $table->string('color');

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

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('tags');
    }
}

Como la tabla tags y la tabla posts tiene una relación muchos a muchos debo crear una tabla que llamaré post_tag .

1
2
enrique@enrique-server:/var/www/laravel/blog_laravel$ php artisan make:migration create_post_tag_table
Created Migration: 2020_12_10_155345_create_post_tag_table

Creo en la migración los campos de la tabla dentro de su método up.

2020_12_10_155345_create_post_tag_table.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePostTagTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('post_tag', function (Blueprint $table) {
            $table->id();

            $table->unsignedBigInteger('post_id');
            $table->unsignedBigInteger('tag_id');

            $table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
            $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');

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

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('post_tag');
    }
}

Por último creo la tabla images y su modelo.

1
2
3
enrique@enrique-server:/var/www/laravel/blog_laravel$ php artisan make:model Image -m
Model created successfully.
Created Migration: 2020_12_10_182248_create_images_table

Creo en la migración los campos de la tabla dentro de su método up.

2020_12_10_182248_create_images_table.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateImagesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('images', function (Blueprint $table) {
            $table->id();

            $table->string('url');
            $table->unsignedBigInteger('imageable_id');
            $table->string('imageable_type');

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

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('images');
    }
}

Realizo las migraciones de las tablas.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
enrique@enrique-server:/var/www/laravel/blog_laravel$ php artisan migrate
Migrating: 2020_12_10_151428_create_categories_table
Migrated:  2020_12_10_151428_create_categories_table (3,943.96ms)
Migrating: 2020_12_10_152625_create_posts_table
Migrated:  2020_12_10_152625_create_posts_table (14,776.35ms)
Migrating: 2020_12_10_154814_create_tags_table
Migrated:  2020_12_10_154814_create_tags_table (1,760.09ms)
Migrating: 2020_12_10_155345_create_post_tag_table
Migrated:  2020_12_10_155345_create_post_tag_table (12,294.46ms)
Migrating: 2020_12_10_182248_create_images_table
Migrated:  2020_12_10_182248_create_images_table (3,181.90ms)

Lo siguiente es realizar las relaciones a nivel de modelos. Categorias y users tienen una relación uno a muchos con posts.

En el modelo Category creo el método posts

Category.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<?php

namespace App\Models;

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

class Category extends Model
{
    use HasFactory;

    // Relación uno a muchos posts
    public function posts()
    {
        return $this->hasMany(Post::class); // Una categoría puede estar en muchos posts
    }
}

Creo el mismo método en el modelo user.

User.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Jetstream\HasProfilePhoto;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens;
    use HasFactory;
    use HasProfilePhoto;
    use Notifiable;
    use TwoFactorAuthenticatable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
        'two_factor_recovery_codes',
        'two_factor_secret',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    /**
     * The accessors to append to the model's array form.
     *
     * @var array
     */
    protected $appends = [
        'profile_photo_url',
    ];

    //relación uno a muchos posts
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

Y el modelo Post tiene la misma pero inversa, por lo que creo un método llamado user y otro category.

Post.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php

namespace App\Models;

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

class Post extends Model
{
    use HasFactory;

    // Relacion uno a muchos inversa users
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    // Relacion uno a muchos inversa categories
    public function category()
    {
        return $this->belongsTo(Category::class);
    }
}

La siguiente relación es la de posts y tags , esta es de muchos a muchos.

En el modelo Post añado el siguiente método categories.

Post.php

1
2
3
4
5
    // Relacion muchos a muchos categorias
    public function tags()
    {
        return $this->belongsToMany(Tag::class);
    }

Y en el modelo Tag creo el método posts.

Tag.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<?php

namespace App\Models;

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

class Tag extends Model
{
    use HasFactory;

    // relacion muchos a muchos con posts
    public function posts()
    {
        return $this->belongsToMany(Post::class);
    }
}

Por último queda la relación polimórfica del modelo Image y el modelo Post.

En el modelo Image.php el método imageable.

Image.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<?php

namespace App\Models;

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

class Image extends Model
{
    use HasFactory;

    // Relación uno a uno polimórfica
    public function imageable()
    {
        return $this->morphTo();
    }
}

En el modelo Post creo el método image.

Post.php

1
2
3
4
5
    // Relacion uno a uno polimórfica con image
    public function image()
    {
        return $this->morphOne(Image::class, 'imageable'); // Debo pasar el método imageable creado en Image.php
    }

Ya tengo relacionados todos los modelos.

ESC
«No se puede enseñar nada a un hombre; sólo se le puede ayudar a encontrar la respuesta dentro de sí mismo». Galileo Galilei (1564 - 1642)
Tabla de contenidos
Artículos relacionados
Creando el crud de posts - IX
Creando un crud para los post del blog Creo el controlador con los siete métodos 1 2 enrique@enrique-server:/var/www/laravel/blog_laravel$ php artisan make:controller Admin/PostController -r Controller created successfully. Lo modifico para recibir objetos post y las vistas a los métodos correspondientes. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 <?
2021-2-3
Creando el crud de etiquetas - VIII
Creando un crud para las etiquetas del blog Sigo los mismos pasos que para las categorias. Desde la consola de comandos y situado dentro del proyecto creo el controlador con sus 7 métodos para administrar las etiquetas desde el backend. 1 2 enrique@enrique-server:/var/www/laravel/blog_laravel$ php artisan make:controler Admin/TagController -r Controller created successfully.
2021-1-31
Creando el crud de categorías - VII
Creando un crud para las categorías del blog Genero en admin.php una ruta de tipo resources que me genere las rutas para el crud de las categorías. Antes creo un controlador para categorías que me genere ya los 7 métodos. 1 2 enrique@enrique-server:/var/www/laravel/blog_laravel$ php artisan make:controller Admin/CategoryController -r Controller created successfully.
2021-1-30
Creando el backend del blog -VI
Integrando AdminLTE en el proyecto Para la gestión de la parte administrativa voy a usar el paquete AdminLTE 3 Voy a generar una url llamada admin que utilizaré para desarrollar la parte de administración del blog. Esta url no la voy a declarar en el fichero web.php sino que creo uno nuevo llamado routes/admin.
2021-1-29
Mostrando y filtrando posts - V
Mostrando el detalle de un post Creo una ruta en web.php para mostrar el detalle de un post. web.php 1 Route::get('posts/{post}', [PostController::class, 'show'])->name('posts.show'); Creo en método show en el controllador PostController.php al que le paso como parámetro un objeto post. Dentro de el recupero los post que pertenecen a la misma categoría que el en la variable $similares y se los paso junto con la variable $post a la vista posts.
2021-1-28