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); });