Prologo
Primi Passi
Architettura
Le Basi
- Routing
- Middleware
- Protezione da CSRF
- Controller
- Richieste
- Risposte
- Views
- Blade
- Vite
- URL
- Sessioni
- Validazione
- Errori
- Logging
Approfondimenti
- Artisan
- Broadcasting
- Cache
- Collezioni
- Concorrenza
- Contesto
- Contratti
- Eventi
- File System
- Helpers
- Client HTTP
- Localizzazione
- Notifiche
- Sviluppo di Package
- Processi
- Code
- Rate-limiting
- Stringhe
- Scheduling di Task
Sicurezza
Database
Eloquent ORM
Testing
Package
Factory
Introduzione
Quando testi la tua applicazione o popoli il tuo database, potresti aver bisogno di inserire alcuni record. Invece di specificare manualmente il valore di ogni colonna, Laravel ti permette di definire un insieme di attributi predefiniti per ciascuno dei tuoi modelli Eloquent utilizzando le factory dei modelli.
Per vedere un esempio di come scrivere una factory, dai un’occhiata al file database/factories/UserFactory.php
nella tua applicazione. Questa factory è inclusa in tutte le nuove applicazioni Laravel e contiene la seguente definizione:
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class UserFactory extends Factory
{
/**
* La password corrente utilizzata dalla factory.
*/
protected static ?string $password;
/**
* Definisce lo stato predefinito del modello.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
];
}
/**
* Indica che l'indirizzo email del modello non è verificato.
*/
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}
Come puoi vedere, nella loro forma più semplice, le factory sono classi che estendono la classe base delle factory di Laravel e definiscono un metodo definition
. Il metodo definition
restituisce l’insieme predefinito di valori degli attributi che devono essere applicati quando si crea un modello usando la factory.
Attraverso l’helper fake
, le factory hanno accesso alla libreria PHP Faker, che ti permette di generare comodamente vari tipi di dati casuali per il testing e il popolamento.
Puoi cambiare la lingua di Faker nella tua applicazione aggiornando l’opzione
faker_locale
nel file di configurazioneconfig/app.php
.
Definire le Model Factory
Generare le Factory
Per creare una factory, esegui il comando make:factory
di Artisan:
php artisan make:factory PostFactory
La nuova classe factory verrà inserita nella directory database/factories
.
Convenzioni per la Scoperta di Model e Factory
Una volta che hai definito le tue factory, puoi utilizzare il metodo statico factory
fornito ai tuoi model dal trait Illuminate\Database\Eloquent\Factories\HasFactory
per creare un’istanza della factory per quel model.
Il metodo factory
del trait HasFactory
utilizzerà le convenzioni per determinare la factory corretta per il model a cui è assegnato il trait. In particolare, il metodo cercherà una factory nel namespace Database\Factories
con un nome di classe che corrisponde al nome del model e che termina con Factory
. Se queste convenzioni non si applicano alla tua applicazione o factory, puoi sovrascrivere il metodo newFactory
nel tuo model per restituire direttamente un’istanza della factory corrispondente al model:
use Database\Factories\Administration\FlightFactory;
/**
* Crea una nuova istanza della factory per il model.
*/
protected static function newFactory()
{
return FlightFactory::new();
}
Poi, definisci una proprietà model
nella factory corrispondente:
use App\Administration\Flight;
use Illuminate\Database\Eloquent\Factories\Factory;
class FlightFactory extends Factory
{
/**
* Il nome del model corrispondente alla factory.
*
* @var class-string<\Illuminate\Database\Eloquent\Model>
*/
protected $model = Flight::class;
}
Stati delle Factory
I metodi per manipolare gli stati ti permettono di definire modifiche specifiche che possono essere applicate alle tue factory di modelli in qualsiasi combinazione. Ad esempio, la tua factory Database\Factories\UserFactory
potrebbe contenere un metodo di stato suspended
che modifica uno dei valori predefiniti degli attributi.
I metodi di trasformazione dello stato tipicamente chiamano il metodo state
fornito dalla classe base delle factory di Laravel. Il metodo state
accetta una closure che riceverà l’array degli attributi grezzi definiti per la factory e dovrebbe restituire un array di attributi da modificare:
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* Indica che l'utente è sospeso.
*/
public function suspended(): Factory
{
return $this->state(function (array $attributes) {
return [
'account_status' => 'suspended',
];
});
}
Stato "Trashed"
Se il tuo modello Eloquent può essere soft deleted, puoi utilizzare il metodo di stato integrato trashed
per indicare che il modello creato dovrebbe già essere "soft deleted". Non è necessario definire manualmente lo stato trashed
poiché è automaticamente disponibile per tutte le factory:
use App\Models\User;
$user = User::factory()->trashed()->create();
Callback delle Factory
I callback delle factory vengono registrati utilizzando i metodi afterMaking
e afterCreating
e ti permettono di eseguire operazioni aggiuntive dopo aver creato o generato un modello. Dovresti registrare questi callback definendo un metodo configure
nella tua classe factory. Questo metodo verrà chiamato automaticamente da Laravel quando la factory viene istanziata:
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class UserFactory extends Factory
{
/**
* Configura la factory del modello.
*/
public function configure(): static
{
return $this->afterMaking(function (User $user) {
// ...
})->afterCreating(function (User $user) {
// ...
});
}
// ...
}
Puoi anche registrare i callback delle factory all’interno dei metodi di stato per eseguire operazioni aggiuntive specifiche per uno stato dato:
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* Indica che l'utente è sospeso.
*/
public function suspended(): Factory
{
return $this->state(function (array $attributes) {
return [
'account_status' => 'suspended',
];
})->afterMaking(function (User $user) {
// ...
})->afterCreating(function (User $user) {
// ...
});
}
Creare Modelli usando le Factory
Istanziare Modelli
Una volta definite le tue factory, puoi usare il metodo statico factory
fornito ai tuoi modelli dal trait Illuminate\Database\Eloquent\Factories\HasFactory
per istanziare un’istanza di factory per quel modello. Vediamo alcuni esempi di creazione di modelli. Per prima cosa, useremo il metodo make
per creare modelli senza salvarli nel database:
use App\Models\User;
$user = User::factory()->make();
Puoi creare una collezione di più modelli utilizzando il metodo count
:
$users = User::factory()->count(3)->make();
Applicazione degli Stati
Puoi anche applicare uno qualsiasi dei tuoi states ai modelli. Se desideri applicare più trasformazioni di stato ai modelli, puoi semplicemente chiamare direttamente i metodi di trasformazione dello stato:
$users = User::factory()->count(5)->suspended()->make();
Sovrascrivere gli attributi
Se desideri sovrascrivere alcuni dei valori predefiniti dei tuoi modelli, puoi passare un array di valori al metodo make
. Solo gli attributi specificati verranno sostituiti mentre il resto degli attributi rimarrà impostato ai valori predefiniti specificati dalla factory:
$user = User::factory()->make([
'name' => 'Abigail Otwell',
]);
In alternativa, il metodo state
può essere chiamato direttamente sull’istanza della factory per eseguire una trasformazione dello stato in linea:
$user = User::factory()->state([
'name' => 'Abigail Otwell',
])->make();
La protezione dall’assegnazione di massa è disabilitata automaticamente quando si creano modelli utilizzando le factory.
Persistenza dei Modelli
Il metodo create
istanzia le istanze del modello e le salva nel database utilizzando il metodo save
di Eloquent:
use App\Models\User;
// Crea un'unica istanza di App\Models\User...
$user = User::factory()->create();
// Crea tre istanze di App\Models\User...
$users = User::factory()->count(3)->create();
Puoi sovrascrivere gli attributi predefiniti del modello della factory passando un array di attributi al metodo create
:
$user = User::factory()->create([
'name' => 'Abigail',
]);
Sequenze
A volte potresti voler alternare il valore di un attributo di un modello per ogni modello creato. Puoi fare questo definendo una trasformazione dello stato come una sequenza. Ad esempio, potresti voler alternare il valore di una colonna admin
tra Y
e N
per ogni utente creato:
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Sequence;
$users = User::factory()
->count(10)
->state(new Sequence(
['admin' => 'Y'],
['admin' => 'N'],
))
->create();
In questo esempio, verranno creati cinque utenti con il valore admin
impostato su Y
e cinque utenti con il valore admin
impostato su N
.
Se necessario, puoi includere una closure come valore della sequenza. La closure verrà invocata ogni volta che la sequenza necessita di un nuovo valore:
use Illuminate\Database\Eloquent\Factories\Sequence;
$users = User::factory()
->count(10)
->state(new Sequence(
fn (Sequence $sequence) => ['role' => UserRoles::all()->random()],
))
->create();
All’interno di una closure di sequenza, puoi accedere alle proprietà $index
o $count
sull’istanza della sequenza che viene iniettata nella closure. La proprietà $index
contiene il numero di iterazioni attraverso la sequenza che sono avvenute finora, mentre la proprietà $count
contiene il numero totale di volte che la sequenza verrà invocata:
$users = User::factory()
->count(10)
->sequence(fn (Sequence $sequence) => ['name' => 'Name '.$sequence->index])
->create();
Per comodità, le sequenze possono essere applicate anche usando il metodo sequence
, che internamente chiama semplicemente il metodo state
. Il metodo sequence
accetta una closure o array di attributi sequenziati:
$users = User::factory()
->count(2)
->sequence(
['name' => 'First User'],
['name' => 'Second User'],
)
->create();
Relazioni delle Factory
Relazioni Has Many
Successivamente, esploriamo la creazione delle relazioni tra modelli Eloquent utilizzando i metodi fluenti delle factory di Laravel. Per prima cosa, supponiamo che la nostra applicazione abbia un modello App\Models\User
e un modello App\Models\Post
. Inoltre, ipotizziamo che il modello User
definisca una relazione hasMany
con Post
. Possiamo creare un utente con tre post utilizzando il metodo has
fornito dalle factory di Laravel. Il metodo has
accetta un’istanza di factory:
use App\Models\Post;
use App\Models\User;
$user = User::factory()
->has(Post::factory()->count(3))
->create();
Per convenzione, quando si passa un modello Post
al metodo has
, Laravel presumerà che il modello User
debba avere un metodo posts
che definisce la relazione. Se necessario, puoi specificare esplicitamente il nome della relazione che desideri gestire:
$user = User::factory()
->has(Post::factory()->count(3), 'posts')
->create();
Naturalmente, puoi effettuare manipolazioni di stato sui modelli correlati. Inoltre, puoi passare una trasformazione di stato basata su una closure se il cambiamento di stato richiede l’accesso al modello genitore:
$user = User::factory()
->has(
Post::factory()
->count(3)
->state(function (array $attributes, User $user) {
return ['user_type' => $user->type];
})
)
->create();
Utilizzo dei Magic Methods
Per comodità, puoi usare i magic factory relationship methods di Laravel per creare le relazioni. Ad esempio, il seguente esempio utilizzerà la convenzione per determinare che i modelli correlati dovrebbero essere creati tramite un metodo di relazione posts
sul modello User
:
$user = User::factory()
->hasPosts(3)
->create();
Quando usi i magic methods per creare factory relationships, puoi passare un array di attributi per sovrascrivere sui modelli correlati:
$user = User::factory()
->hasPosts(3, [
'published' => false,
])
->create();
Puoi fornire una trasformazione di stato basata su una closure se il tuo cambiamento di stato richiede l’accesso al modello genitore:
$user = User::factory()
->hasPosts(3, function (array $attributes, User $user) {
return ['user_type' => $user->type];
})
->create();
Relazioni Belongs To
Ora che abbiamo esplorato come costruire relazioni "has many" usando le factory, vediamo l’inverso della relazione. Il metodo for
può essere utilizzato per definire il modello genitore a cui appartengono i modelli creati dalla factory. Ad esempio, possiamo creare tre istanze del modello App\Models\Post
che appartengono a un singolo utente:
use App\Models\Post;
use App\Models\User;
$posts = Post::factory()
->count(3)
->for(User::factory()->state([
'name' => 'Jessica Archer',
]))
->create();
Se hai già un’istanza del modello genitore che deve essere associata ai modelli che stai creando, puoi passare l’istanza del modello al metodo for
:
$user = User::factory()->create();
$posts = Post::factory()
->count(3)
->for($user)
->create();
Utilizzo dei Magic Methods
Per comodità, puoi utilizzare i metodi di relazione della factory magica di Laravel per definire relazioni "belongs to". Ad esempio, il seguente esempio utilizza le convenzioni per determinare che i tre post appartengono alla relazione user
sul modello Post
:
$posts = Post::factory()
->count(3)
->forUser([
'name' => 'Jessica Archer',
])
->create();
Relazioni Many-to-Many
Come le relazioni one-to-many, le relazioni "many-to-many" possono essere create utilizzando il metodo has
:
use App\Models\Role;
use App\Models\User;
$user = User::factory()
->has(Role::factory()->count(3))
->create();
Attributi della Tabella Pivot
Se hai bisogno di definire attributi che devono essere impostati sulla tabella pivot / intermedia che collega i modelli, puoi usare il metodo hasAttached
. Questo metodo accetta un array di nomi e valori degli attributi della tabella pivot come secondo argomento:
use App\Models\Role;
use App\Models\User;
$user = User::factory()
->hasAttached(
Role::factory()->count(3),
['active' => true]
)
->create();
Puoi fornire una trasformazione di stato basata su una closure se il cambiamento di stato richiede l’accesso al modello correlato:
$user = User::factory()
->hasAttached(
Role::factory()
->count(3)
->state(function (array $attributes, User $user) {
return ['name' => $user->name.' Role'];
}),
['active' => true]
)
->create();
Se hai già istanze di modelli che desideri collegare ai modelli che stai creando, puoi passare le istanze dei modelli al metodo hasAttached
. In questo esempio, gli stessi tre ruoli verranno collegati a tutti e tre gli utenti:
$roles = Role::factory()->count(3)->create();
$user = User::factory()
->count(3)
->hasAttached($roles, ['active' => true])
->create();
Uso dei Metodi Magici
Per comodità, puoi utilizzare i metodi relazionali magici di Laravel per definire relazioni molti a molti. Ad esempio, il seguente esempio utilizzerà una convenzione per determinare che i modelli correlati devono essere creati tramite un metodo di relazione roles
sul modello User
:
$user = User::factory()
->hasRoles(1, [
'name' => 'Editor'
])
->create();
Relazioni Polimorfiche
Le relazioni polimorfiche possono essere create anche usando le factory. Le relazioni polimorfiche "morph many" vengono create allo stesso modo delle tipiche relazioni "has many". Per esempio, se un modello App\Models\Post
ha una relazione morphMany
con un modello App\Models\Comment
:
use App\Models\Post;
$post = Post::factory()->hasComments(3)->create();
Relazioni Morph To
I metodi magici non possono essere usati per creare relazioni morphTo
. Invece, bisogna usare direttamente il metodo for
e fornire esplicitamente il nome della relazione. Ad esempio, immagina che il modello Comment
abbia un metodo commentable
che definisce una relazione morphTo
. In questa situazione, possiamo creare tre commenti che appartengono a un singolo post usando direttamente il metodo for
:
$comments = Comment::factory()->count(3)->for(
Post::factory(), 'commentable'
)->create();
Relazioni Many to Many Polimorfiche
Le relazioni polimorfe "molti a molti" (morphToMany
/ morphedByMany
) possono essere create proprio come le relazioni "molti a molti" non polimorfe:
use App\Models\Tag;
use App\Models\Video;
$videos = Video::factory()
->hasAttached(
Tag::factory()->count(3),
['public' => true]
)
->create();
Naturalmente, anche il metodo magico has
può essere usato per creare relazioni polimorfe "molti a molti":
$videos = Video::factory()
->hasTags(3, ['public' => true])
->create();
Definire le Relazioni all’interno delle Factory
Per definire una relazione all’interno della tua factory di modello, solitamente assegnerai una nuova istanza di factory alla chiave esterna della relazione. Questo viene fatto normalmente per le relazioni "inverse" come belongsTo
e morphTo
. Ad esempio, se vuoi creare un nuovo utente quando crei un post, puoi fare quanto segue:
use App\Models\User;
/**
* Definisce lo stato predefinito del modello.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'user_id' => User::factory(),
'title' => fake()->title(),
'content' => fake()->paragraph(),
];
}
Se le colonne della relazione dipendono dalla factory che la definisce, puoi assegnare una closure a un attributo. La closure riceverà l’array degli attributi valutati della factory:
/**
* Definisce lo stato predefinito del modello.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'user_id' => User::factory(),
'user_type' => function (array $attributes) {
return User::find($attributes['user_id'])->type;
},
'title' => fake()->title(),
'content' => fake()->paragraph(),
];
}
Riciclare un Modello Esistente per le Relazioni
Se hai modelli che condividono una relazione comune con un altro modello, puoi usare il metodo recycle
per assicurarti che una singola istanza del modello correlato venga riciclata per tutte le relazioni create dalla factory.
Ad esempio, immagina di avere i modelli Airline
, Flight
e Ticket
, dove il ticket appartiene a una compagnia aerea e a un volo, e il volo appartiene anche a una compagnia aerea. Quando crei i ticket, probabilmente vorrai la stessa compagnia aerea sia per il ticket che per il volo, quindi puoi passare un’istanza di Airline
al metodo recycle
:
Ticket::factory()
->recycle(Airline::factory()->create())
->create();
Potresti trovare il metodo recycle
particolarmente utile se hai modelli che appartengono a un utente o a un team comune.
Il metodo recycle
accetta anche una collezione di modelli esistenti. Quando una collezione viene fornita al metodo recycle
, verrà scelto un modello a caso dalla collezione quando la factory ha bisogno di un modello di quel tipo:
Ticket::factory()
->recycle($airlines)
->create();