# Laravel Factories: Tips for Handling Dependent Data

Something I recently came across while working on a Laravel app was an unoptimized factory. I’ve seen this many times and have even been guilty of it. Let’s say we have three models, a `User`, a `Team`, and a `Project`. A team can have a team owner (`teams.user_id`), a project can have an owner (`projects.user_id`) and a team ([`projects.team`](http://projects.team)`_id`). In the `ProjectFactory`, I want to create a user and a team, and that team should have the user as the owner. The first attempt might look something like this:

```php
class ProjectFactory extends Factory
{
    protected $model = Project::class;

    public function definition(): array
    {
        return [
            'title' => $this->faker->sentence(),
            'description' => $this->faker->paragraph(),
            'team_id' => Team::factory(),
            'user_id' => User::factory(),
        ];
    }
}
```

The problem with this is the `Team` factory is creating its own `User` and now the `User` factory created a user, and they are not related.

The next iteration could look like this:

```php
class ProjectFactory extends Factory
{
    protected $model = Project::class;

    public function definition(): array
    {
		$team = Team::factory()->create();

        return [
            'title' => $this->faker->sentence(),
            'description' => $this->faker->paragraph(),
            'team_id' => $team->id,
            'user_id' => $team->user_id,
        ];
    }
}
```

This works and gives us what we want, or does it?

It will give us a `Project` with a `User` and `Team` that are related. However, what happens if we were using the factory and we already knew the team? Something like the following:

```php
$project = Project::factory()
    ->for($previouslyCreatedTeam, 'team')
    ->for($previouslyCreatedTeam->owner, 'user')
    ->create();
```

If we look in the database, we should see a single user, team, and project record. However, looking at the `users` table, I have two users:

![Users table](https://cdn.hashnode.com/res/hashnode/image/upload/v1727017774085/2eeac220-252b-4913-b187-eb9c84e1a4c9.png align="center")

In the `teams` table, I have two teams!

![Teams table](https://cdn.hashnode.com/res/hashnode/image/upload/v1727017786070/75727b63-91f3-4327-ac72-aa2761e9236e.png align="center")

What happened?

The issue is, any time the factory runs, it calls the `definition` method, and then it looks at the fields that were passed to the factory to see which fields of the definition it needs to use. When the `definition` method is called, though, nothing is preventing it from calling the `Team` factory and creating a `Team` and `User`.

```php
class ProjectFactory extends Factory
{
    protected $model = Project::class;

    public function definition(): array
    {
		// This runs regardless of the fields provided
		// to the factory, creating extra teams and users
		// when not necessary.
		$team = Team::factory()->create();

        return [
            'title' => $this->faker->sentence(),
            'description' => $this->faker->paragraph(),
            'team_id' => $team->id,
            'user_id' => $team->user_id,
        ];
    }
}
```

How do we fix this?

It’s actually right in the [Laravel docs](https://laravel.com/docs/11.x/eloquent-factories#defining-relationships-within-factories).

> If the relationship's columns depend on the factory that defines it you may assign a closure to an attribute.

So to fix it, we can use a closure to get the `User` from the `Team` factory used in the definition:

```php
class ProjectFactory extends Factory
{
    protected $model = Project::class;

    public function definition(): array
    {
        return [
            'title' => $this->faker->sentence(),
            'description' => $this->faker->paragraph(),
            'team_id' => Team::factory(),
			// Fetch the user_id from the team that was 
			// generated in the factory above.
            'user_id' => fn (array $attributes) => Team::find($attributes['team_id'])->user_id,
        ];
    }
}
```

This is a bit of an edge case and doesn’t frequently happen, so it’s easy to miss in the docs. I hope this helps, and now you can go and improve your factories. Thanks for reading!
