Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[11.x] add ability to disable relationships in factories #53450

Draft
wants to merge 1 commit into
base: 11.x
Choose a base branch
from

Conversation

browner12
Copy link
Contributor

sometimes when makeing or createing a model, we don't actually care about the relationships. this new property and method allow us to turn them all off at once. when disabled, any attribute that is assigned a factory will instead return null.

imagine an Organization and User models. the organizations table is:

  • id
  • name

and the users table is:

  • id
  • name
  • age
  • organization_id

When we define the UserFactory we setup the relationship with a factory:

public function definition(): array
{
    return [
        'name'            => fake()->name(),
        'age'             => rand(1, 100),
        'organization_id' => Organization::factory(),
    ];
}

This is great for when we seed and when we run feature tests, because it will automatically create the Organizations for us.

However, this is not always desired, often when you're writing Unit tests. Imagine my user model has an isAdult() function:

class User
{
    public function isAdult()
    {
        return $this->age >= 18;
    }
}

To write a Unit test for this I would make a User:

public function test_can_determine_adult()
{
    $user1 = User::factory()->make(['age' => 17]);
    $user2 = User::factory()->make(['age' => 18]);
    $user3 = User::factory()->make(['age' => 19]);

    $this->assertFalse($user1->isAdult());
    $this->assertTrue($user2->isAdult());
    $this->assertTrue($user3->isAdult());
}

Unfortunately, this would try storing Organizations in a database, even though we don't care about that. It would also fail if you didn't have RefreshDatabase or something similar on your test.

With this PR, you could easily disable all relationships defined in your factories and make this test work:

public function test_can_determine_adult()
{
    $user1 = User::factory()->disableRelationships()->make(['age' => 17]);
    $user2 = User::factory()->disableRelationships()->make(['age' => 18]);
    $user3 = User::factory()->disableRelationships()->make(['age' => 19]);

    $this->assertFalse($user1->isAdult());
    $this->assertTrue($user2->isAdult());
    $this->assertTrue($user3->isAdult());
}

sometimes when `make`ing or `create`ing a model, we don't actually care about the relationship. this new property and method allow us to turn them all off at once. when disabled, any attribute that is assigned a factory will instead return `null`.
@taylorotwell
Copy link
Member

taylorotwell commented Nov 11, 2024

Would the state of this property be lost if you called a method like afterMaking which creates a new factory instance? I also wonder if it should be named withoutRelationships to have a bit more consistency.

@taylorotwell taylorotwell marked this pull request as draft November 11, 2024 20:46
@browner12
Copy link
Contributor Author

that name change seems fine to me.

do you want the property adjusted as well to withoutRelationships, and then invert the logic?

I'm not positive, but you're probably right that anything that builds a new object probably would lose this state. the whole factory is a little bit of a mess of static vs object. not sure how you would want to handle this. I believe I had a PR a long time ago that got rid of the whole static new object paradigm but it was closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants