15 Creare un modello personalizzato

vediamo come creare un model tramite riga di comando vdiamo l’help del comando make:model

php artisan make:model --help

Vediamo che come primo parametro bisogna passare il nome del model da creare.

Il nome deve corrispondere alla tabella in singolare con l’iniziale maiuscola.

Vediamo poi che esistono diverse opzioni,

–all genera anche: migration, seeder, factory, resource, controller

-c crea il controller

-f factory

-m migration

-s seed

Andiamo a creare il model con seed per la tabella albums (ricordate: singolare – iniziale maiuscola)

php artisan make:model Album -s

Ho fatto l’esempio separato in modo da rinfrescare le idee per creare la factory (ricordate: singolare – iniziale maiuscola – seguita da Factory)

php artisan make:factory AlbumFactory

In effetti per creare la factory sarebbe bastato aggiungere semplicemente -f al primo comando. Vediamo la base creata di app/Models/Album.php

namespace App\Models;

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

class Album extends Model
{
 use HasFactory;
}

Vediamo che in automatico viene aggiunto il trait HasFactory.

Completiamo la factory

Nella factory creata notiamo che oltre ad importare in automatico il model Album ci crea la classe con il metodo definition() dove andremo ad inserire i campi della tabella users.

Analizzando i campi troviamo album_name, album_thumb che vuole un url, description, created_at e user_id che è la foreign key che riceve l’id dello user associato.

Iniziamo a passare album_name

public function definition()
{
 return [
  'album_name' => $this->faker->text(20),
 ];
}

utilizzando faker facciamo creare un testo di 20 caratteri. Passiamo gli altri campi

'album_name' => $this->faker->text(20),
'album_thumb' => $this->faker->image(),
'description' => $this->faker->text(120),
'created_at' => $this->faker->dateTime(),
'user_id' => User::factory()

Per albumb_thumb ho usato il metodo image() di faker.

Particolare attenzione è da dare al campo user_id, la foreign key legata all’id dell’utente. Ogni utente può avere uno o più album associati, pertanto dobbiamo far generare casualmente un utente.

Nelle versioni precedenti di laravel per associare un id di un utente casuale dovevo usare alla classe User il metodo inRandomOrder() seguito da first() seguito dall’ id

'user_id' => User::inRandomOrder()->first()->id

Nelle nuove versioni posso usare semplicemente la factory della classe User ( User::factory )

'user_id' => User::factory()

Passiamo al seed

Andiamo in database/seeders/AlbumSeeder e nel metodo run() lanciamo la factory del modello Album

public function run()
{
 Album::factory(30)->create();
}

lanciamo ora solo questo seed

php artisan db:seed --class=AlbumSeeder

La tabella album è stata popolata di dati e si potrà notare che user_id sarà completato con id di utenti casuali collegati alla tabella user.

Aggiungiamo la rotta per vedere gli album sul browser, prima importiamo il model Album nella rotta

use \App\Models\Album;

ora aggiungiamo la rotta /album

Route::get('/albums', function () {
 return Album::paginate(5);
});

Eseguiamo il model per la tabella photo

Vediamo velocemente tutti i passaggi per eseguire il model, la factory, il seed pe rla tabella photo

php artisan make:model Photo -s -f

la factory

public function definition()
{
 return [
  'created_at' => $this->faker->dateTime(),
  'name' => $this->faker->text(50),
  'description' => $this->faker->text(120),
  'img_path' => $this->faker->imageUrl(),
  'album_id' => Album::factory()
 ];
}

Il seeder

public function run()
{
 Photo::factory(20)->create();
}

lanciamo il seed

php artisan db:seed --class=PhotoSeeder

Lanciare tutti i seeders creando relazioni

Ricorderete che nel seeder principale DatabaseSeeder tramite il metodo call() chiamavamo la seeder UserSeeder, ovviamente aggiungendone altri si chiameranno le altre seeders

$this->call(UserSeeder::class);
$this->call(AlbumSeeder::class);
$this->call(PhotoSeeder::class);

Lanciando il comando db:seed verranno lanciate tutte le seeders

php artisan db:seed

Otterremo degli per via della presenza di foreigne key e campi che non possono essere lasciati vuoti e mancanza di dati, quindi le tabelle non possono essere troncate.

Ovviamente ripartendo da zero con migrate:fresh e l’opzione –seed risolveremmo il problema

php artisan migrate:fresh --seed

Adesso vogliamo che per ogni utente vengano creati degli album e per ogni album vengano create delle foto. Nella documentazione di laravel su testing database troviamo l’articolo Has Many Relationships per creare relazioni tra tabelle.

Quindi nel model User inseriamo un metodo che restituisce hasMany() che conterrà la classe da collegare

public function albums() {
 return $this->hasMany(Album::class);
}

Il nome del metodo è consigliato metterlo come il nome della tabella (è solo per migliore comprensione). Il metodo restituisce a seconda se ne ha 1 si usa hasOne() o se ne ha tanti hasMany() , album.

Stessa cosa facciamo nel model Album dove ogni album avrà tante foto

public function photos()
{
 return $this->hasMany(Photo::class);
}

Richiamare le factory dei model legando con has nel seeder

Eliminiamo le precedenti call() nel nostro seeder e diciamo prendiamo la factory utenti e creamene 10

User::factory(20)->create();

gli utenti hanno 10 album usiamo per questo il metodo has()

User::factory(20)->has(
Album::factory(10) )->create();

Infine Ogni albuma avrà 10 photo, quindi il comando di creazione completo risulterà così

User::factory(20)->has(
 Album::factory(10)->has(
  Photo::factory(20)
 )
)->create();

Quindi ogni utente avrà 10 album e ogni album avrà 10 foto.

Troncare le tabelle per effettuare le migrazioni

Un buon modo per rifare la migrazione eliminando i dati preesistenti, è quello di troncare le tabelle. Troncare una tabella è simile all’eliminazione, ma è più rapido e soprattutto ogni riga non viene registrata nei log, ma il comando viene eseguito in blocco.

Utilizziamo su ogni modello il metodo truncate() nella nostra seeder

User::truncate();
Album::truncate();
Photo::truncate();

User::factory(20)->has(
 Album::factory(10)->has(
  Photo::factory(20)
 )
)->create();

a questo punto però bisogna disabilitare le foreign key che bloccherebbero il truncate.

Per disabilitare le foregn_key, utilizziamo l’alias DB della facade \Illuminate\Support\Facades\DB utilizzando il metodo statement che riceverà la query SET FOREIGN_KEY_CHECKS=0 che disabilita il controllo sulle foreign key

\DB::statement('SET FOREIGN_KEY_CHECKS=0');

la view

Passando alla nostra vista web modifichiamo la rotta /users restituendo come prima il metodo paginate() del model User solo che a questo punto avendo impostato la relazione, diremo che l’utente viene mostrato con i suoi albums User::with(‘albums’) il nome del metodo.

Route::get('/users', function () {
 return User::with('albums')->paginate(30);
});

stessa cosa faremo per la rotta /albums

Route::get('/albums', function () {
 return Album::with('photos')->paginate(5);
});