> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Eloquent Factory

> Come generare dati fittizi per test e seeding in modo efficiente usando le factory dei modelli.

## Cosa sono le factory

Nei test e nel seeding del database devi inserire record.
Anziché indicare manualmente il valore di ogni colonna, con le **factory dei modelli** di Laravel puoi definire un set di attributi predefiniti per ciascun modello Eloquent.

Ogni nuova applicazione Laravel include già `database/factories/UserFactory.php`.

```php theme={null}
namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;

class UserFactory extends Factory
{
    // Manteniamo in cache la password per non re-hashare la stessa ogni volta
    protected static ?string $password;

    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),
        ];
    }
}
```

Tramite l'helper `fake()` puoi usare la libreria [Faker](https://github.com/FakerPHP/Faker), che genera vari dati casuali.

```mermaid theme={null}
flowchart LR
    F["Factory<br>database/factories/UserFactory"] -->|"make() / create()"| M["Istanza modello<br>App\\Models\\User"]
    M -->|"create()"| DB[("Database")]
    F -->|"Faker"| R["Dati casuali<br>name, email, ..."]
    R --> F
```

<Info>
  Puoi cambiare la localizzazione di Faker con l'opzione `faker_locale` in `config/app.php`.
</Info>

## Creazione di una factory

Crea una factory con il comando Artisan `make:factory`.

```shell theme={null}
php artisan make:factory PostFactory
```

La nuova classe factory viene collocata in `database/factories`.

### Rilevamento automatico modello-factory

Se usi il trait `HasFactory` sul modello, Laravel trova automaticamente la factory corrispondente.
Cerca nel namespace `Database\Factories` una classe il cui nome è quello del modello con `Factory` in coda.

Se la convenzione di naming non è rispettata, puoi indicarla esplicitamente con l'attributo `UseFactory`.

```php theme={null}
use Illuminate\Database\Eloquent\Attributes\UseFactory;
use Database\Factories\Administration\FlightFactory;

#[UseFactory(FlightFactory::class)]
class Flight extends Model
{
    // ...
}
```

## Definizione di una factory

### Metodo `definition()`

Nel cuore della factory, `definition()`, definisci gli attributi predefiniti.

```php theme={null}
namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

class PostFactory extends Factory
{
    public function definition(): array
    {
        return [
            'user_id' => \App\Models\User::factory(),
            'title' => fake()->sentence(),
            'content' => fake()->paragraphs(3, true),
            'published_at' => fake()->optional()->dateTimeBetween('-1 year', 'now'),
        ];
    }
}
```

Metodi Faker molto usati:

| Metodo                          | Esempio di dato generato |
| ------------------------------- | ------------------------ |
| `fake()->name()`                | `John Doe`               |
| `fake()->email()`               | `john@example.com`       |
| `fake()->sentence()`            | Una frase casuale        |
| `fake()->paragraph()`           | Un paragrafo casuale     |
| `fake()->numberBetween(1, 100)` | Un intero da `1` a `100` |
| `fake()->dateTime()`            | Data e ora casuali       |
| `fake()->boolean()`             | `true` / `false`         |

## Stati della factory (States)

Con i metodi di stato puoi definire varianti specifiche di una factory.

```php theme={null}
use Illuminate\Database\Eloquent\Factories\Factory;

class UserFactory extends Factory
{
    public function definition(): array
    {
        return [
            'name' => fake()->name(),
            'email' => fake()->unique()->safeEmail(),
            'account_status' => 'active',
        ];
    }

    // Stato utenti sospesi
    public function suspended(): static
    {
        return $this->state(fn (array $attributes) => [
            'account_status' => 'suspended',
        ]);
    }

    // Stato amministratori
    public function admin(): static
    {
        return $this->state(fn (array $attributes) => [
            'is_admin' => true,
        ]);
    }
}
```

Gli stati sono componibili.

```php theme={null}
$user = User::factory()->suspended()->create();
$admin = User::factory()->admin()->create();
```

### Stato soft delete

Per modelli con soft delete è disponibile lo stato integrato `trashed()`.

```php theme={null}
$user = User::factory()->trashed()->create();
```

## Callback della factory

Con `afterMaking` e `afterCreating` puoi definire logiche dopo la creazione del modello.

```php theme={null}
namespace Database\Factories;

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;

class UserFactory extends Factory
{
    public function configure(): static
    {
        return $this->afterMaking(function (User $user) {
            // Eseguito dopo make() (non salvato in DB)
        })->afterCreating(function (User $user) {
            // Eseguito dopo create() (salvato in DB)
        });
    }

    // ...
}
```

Puoi registrare callback anche dentro i metodi di stato.

```php theme={null}
public function withProfile(): static
{
    return $this->state(fn (array $attributes) => [])
        ->afterCreating(function (User $user) {
            $user->profile()->create([
                'bio' => fake()->paragraph(),
            ]);
        });
}
```

## Generazione dei modelli

### make() — senza salvare nel DB

```php theme={null}
use App\Models\User;

// Genera 1 record
$user = User::factory()->make();

// Genera 3 record (restituiti come collection)
$users = User::factory()->count(3)->make();
```

### create() — salva nel DB

```php theme={null}
// Genera 1 record e salva nel DB
$user = User::factory()->create();

// Genera 3 record e salva nel DB
$users = User::factory()->count(3)->create();
```

### Sovrascrittura degli attributi

Passando un array a `make()` o `create()` sovrascrivi attributi specifici.

```php theme={null}
$user = User::factory()->make([
    'name' => 'Mario Rossi',
]);

$user = User::factory()->create([
    'name' => 'Giulia Bianchi',
    'email' => 'giulia@example.com',
]);
```

Puoi anche fare una sovrascrittura inline tramite state.

```php theme={null}
$user = User::factory()->state([
    'name' => 'Luca Verdi',
])->make();
```

<Info>
  Quando generi modelli con le factory, la protezione da mass assignment viene disabilitata automaticamente.
</Info>

### Sequence

Se durante la generazione di più modelli vuoi alternare gli attributi, usa `Sequence`.

```php theme={null}
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Sequence;

$users = User::factory()
    ->count(10)
    ->state(new Sequence(
        ['admin' => 'Y'],
        ['admin' => 'N'],
    ))
    ->create();
// 5 vengono generati con admin='Y' e 5 con admin='N'
```

Il metodo `sequence()` è più conciso.

```php theme={null}
$users = User::factory()
    ->count(2)
    ->sequence(
        ['name' => 'Primo utente'],
        ['name' => 'Secondo utente'],
    )
    ->create();
```

Puoi generare valori dinamici casuali con una closure.

```php theme={null}
use Illuminate\Database\Eloquent\Factories\Sequence;

$users = User::factory()
    ->count(10)
    ->state(new Sequence(
        fn (Sequence $sequence) => ['name' => 'Utente ' . $sequence->index],
    ))
    ->create();
```

## Factory con relazioni

```mermaid theme={null}
flowchart TD
    UF["UserFactory"] -->|"has(PostFactory)"| PF["PostFactory"]
    PF -->|"for(UserFactory)"| UF
    UF -->|"hasAttached(RoleFactory)"| RF["RoleFactory"]

    UF -->|"create()"| U["Modello User"]
    PF -->|"create()"| P["Modello Post"]
    RF -->|"create()"| R["Modello Role"]

    U -->|"hasMany"| P
    U -->|"belongsToMany"| R
```

### Has Many (1-a-molti)

Con `has()` generi un modello che possiede una relazione "1-a-molti".

```php theme={null}
use App\Models\Post;
use App\Models\User;

// Genera uno User con 3 Post
$user = User::factory()
    ->has(Post::factory()->count(3))
    ->create();
```

Con i magic method scrivi in modo più conciso (`has` + plurale del nome della relazione).

```php theme={null}
$user = User::factory()
    ->hasPosts(3)
    ->create();

// Sovrascrivi attributi
$user = User::factory()
    ->hasPosts(3, ['published' => false])
    ->create();
```

### Belongs To (relazione inversa)

Con `for()` indichi il modello padre a cui appartiene.

```php theme={null}
use App\Models\Post;
use App\Models\User;

// Genera 3 Post che appartengono a uno specifico utente
$posts = Post::factory()
    ->count(3)
    ->for(User::factory()->state(['name' => 'Giulia Bianchi']))
    ->create();

// Usa un modello esistente come padre
$user = User::factory()->create();
$posts = Post::factory()->count(3)->for($user)->create();
```

Versione con magic method:

```php theme={null}
$posts = Post::factory()
    ->count(3)
    ->forUser(['name' => 'Giulia Bianchi'])
    ->create();
```

### Many to Many (molti-a-molti)

Con `hasAttached()` gestisci relazioni molti-a-molti. Puoi indicare anche gli attributi della tabella pivot.

```php theme={null}
use App\Models\Role;
use App\Models\User;

$user = User::factory()
    ->hasAttached(
        Role::factory()->count(3),
        ['active' => true]  // Attributi della tabella pivot
    )
    ->create();
```

Versione con magic method:

```php theme={null}
$user = User::factory()
    ->hasRoles(1, ['name' => 'Editor'])
    ->create();
```

### Relazioni polimorfiche

Come per il normale 1-a-molti, puoi usare `has()` o i magic method.

```php theme={null}
use App\Models\Post;

$post = Post::factory()->hasComments(3)->create();
```

Per la relazione `morphTo` i magic method non sono disponibili. Indica la relazione in `for()`.

```php theme={null}
$comments = Comment::factory()->count(3)->for(
    Post::factory(), 'commentable'
)->create();
```

### Relazioni definite dentro la factory

Se in `definition()` assegni un'altra factory a una chiave esterna, durante la creazione del modello viene generato automaticamente anche il modello padre.

```php theme={null}
class PostFactory extends Factory
{
    public function definition(): array
    {
        return [
            'user_id' => \App\Models\User::factory(),
            'title' => fake()->sentence(),
            'content' => fake()->paragraph(),
        ];
    }
}
```

### recycle() — riutilizzo di modelli esistenti

Se vuoi riutilizzare la stessa istanza per più relazioni, usa `recycle()`.

```php theme={null}
// Riutilizza la stessa Airline in Ticket e Flight
Ticket::factory()
    ->recycle(Airline::factory()->create())
    ->create();

// Sceglie casualmente da una collection
$airlines = Airline::factory()->count(3)->create();
Ticket::factory()->count(10)->recycle($airlines)->create();
```

## Uso nel seeding

Chiama le factory da `DatabaseSeeder` o dai tuoi seeder.

```php theme={null}
namespace Database\Seeders;

use App\Models\User;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        // Crea 10 utenti e a ciascuno associa 3 post
        User::factory()
            ->count(10)
            ->hasPosts(3)
            ->create();
    }
}
```

Esecuzione del seeder:

```shell theme={null}
php artisan db:seed
```

## Uso nei test

Nei test, combina le factory con il trait `RefreshDatabase`.

```php theme={null}
namespace Tests\Feature;

use App\Models\User;
use App\Models\Post;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class PostTest extends TestCase
{
    use RefreshDatabase;

    public function test_user_can_view_their_posts(): void
    {
        $user = User::factory()->create();
        $posts = Post::factory()->count(3)->for($user)->create();

        $response = $this->actingAs($user)
            ->get('/dashboard');

        $response->assertOk();
        $response->assertSee($posts->first()->title);
    }

    public function test_suspended_user_cannot_post(): void
    {
        $user = User::factory()->suspended()->create();

        $response = $this->actingAs($user)
            ->post('/posts', ['title' => 'Test']);

        $response->assertForbidden();
    }
}
```

`RefreshDatabase` resetta il database per ogni test, così i test non interferiscono tra loro.

<Tip>
  In Pest l'API delle factory è la stessa.
  Usa `uses(RefreshDatabase::class)` per resettare il database.
</Tip>

## Prossimi passi

<Card title="Database seeding" icon="seedling" href="/it/seeding">
  Combina seeder e factory per inserire dati iniziali.
</Card>

<Card title="Introduzione al testing" icon="flask" href="/it/testing">
  Le funzionalità di test di Laravel e il trait RefreshDatabase.
</Card>


## Related topics

- [Test del database](/it/database-testing.md)
- [Database seeding](/it/seeding.md)
- [Contracts (contratti)](/it/contracts.md)
- [Attributi PHP](/it/advanced/php-attributes.md)
- [Collezioni Eloquent](/it/eloquent-collections.md)
