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.
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=falsenpm 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.
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
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.
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.
<?phpuseIlluminate\Database\Migrations\Migration;useIlluminate\Database\Schema\Blueprint;useIlluminate\Support\Facades\Schema;classCreateCategoriesTableextendsMigration{/**
* Run the migrations.
*
* @return void
*/publicfunctionup(){Schema::create('categories',function(Blueprint$table){$table->id();$table->string('name',45);$table->string('slug',45);$table->timestamps();});}/**
* Reverse the migrations.
*
* @return void
*/publicfunctiondown(){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.
<?phpuseIlluminate\Database\Migrations\Migration;useIlluminate\Database\Schema\Blueprint;useIlluminate\Support\Facades\Schema;classCreatePostsTableextendsMigration{/**
* Run the migrations.
*
* @return void
*/publicfunctionup(){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
*/publicfunctiondown(){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
<?phpuseIlluminate\Database\Migrations\Migration;useIlluminate\Database\Schema\Blueprint;useIlluminate\Support\Facades\Schema;classCreatePostTagTableextendsMigration{/**
* Run the migrations.
*
* @return void
*/publicfunctionup(){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
*/publicfunctiondown(){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.
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
<?phpnamespaceApp\Models;useIlluminate\Database\Eloquent\Factories\HasFactory;useIlluminate\Database\Eloquent\Model;classCategoryextendsModel{useHasFactory;// Relación uno a muchos posts
publicfunctionposts(){return$this->hasMany(Post::class);// Una categoría puede estar en muchos posts
}}
<?phpnamespaceApp\Models;useIlluminate\Contracts\Auth\MustVerifyEmail;useIlluminate\Database\Eloquent\Factories\HasFactory;useIlluminate\Foundation\Auth\UserasAuthenticatable;useIlluminate\Notifications\Notifiable;useLaravel\Fortify\TwoFactorAuthenticatable;useLaravel\Jetstream\HasProfilePhoto;useLaravel\Sanctum\HasApiTokens;classUserextendsAuthenticatable{useHasApiTokens;useHasFactory;useHasProfilePhoto;useNotifiable;useTwoFactorAuthenticatable;/**
* 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
publicfunctionposts(){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.
<?phpnamespaceApp\Models;useIlluminate\Database\Eloquent\Factories\HasFactory;useIlluminate\Database\Eloquent\Model;classPostextendsModel{useHasFactory;// Relacion uno a muchos inversa users
publicfunctionuser(){return$this->belongsTo(User::class);}// Relacion uno a muchos inversa categories
publicfunctioncategory(){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
publicfunctiontags(){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
<?phpnamespaceApp\Models;useIlluminate\Database\Eloquent\Factories\HasFactory;useIlluminate\Database\Eloquent\Model;classTagextendsModel{useHasFactory;// relacion muchos a muchos con posts
publicfunctionposts(){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
<?phpnamespaceApp\Models;useIlluminate\Database\Eloquent\Factories\HasFactory;useIlluminate\Database\Eloquent\Model;classImageextendsModel{useHasFactory;// Relación uno a uno polimórfica
publicfunctionimageable(){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
publicfunctionimage(){return$this->morphOne(Image::class,'imageable');// Debo pasar el método imageable creado en Image.php
}