Framework: [8.x] Faker doesn't work within model factory states

Created on 30 Aug 2020  路  6Comments  路  Source: laravel/framework


  • Laravel Version: 8.x-dev
  • PHP Version: 7.4.8
  • Database Driver & Version: N/A

Description:

Right now it appears that using Closure factory states alongside Faker does not work, this is because the Closure is created in another instance of the factory then where it is eventually invoked, so when it is invoked $this->faker still references the old instance's faker (which, depending on the situation, may or may not be null). So instead attributes created via faker this way will be null.

Steps To Reproduce:

  • Create a fresh Laravel 8.x-dev application composer create-project laravel/laravel:dev-develop
  • Paste the following snippet in the UserFactory:
    public function foo()
    {
        return $this->state(fn () => [
            'name' => $this->faker->name,
        ]);
    }
  • Open Tinker
  • Paste the following line User::factory()->foo()->make()

Potential fix:

I was able to get it working by adding $state = $state->bindTo($this); within the reduce callback in Factory::getRawAttributes(), this works because $this->faker = $this->withFaker() is called right before the states are resolved, but I don't know what further implications this has.

bug

Most helpful comment

Thanks. My brain was treating it as a regular class, but of course it's not. 馃槃

The docs don't mention it, but I've submitted a PR. https://github.com/laravel/docs/pull/6329/files

All 6 comments

Thanks! We've fixed this.

Is it just me or is this still broken?

  • laravel new testProject (Laravel 8.0.0)
  • Paste the following snippet in the UserFactory.php
public function foo()
    {
        return $this->state([
            'name' => $this->faker->name,
        ]);
    }
  • Open Tinker
  • Paste the following line: User::factory()->foo()->make()
  • returns PHP Notice: Trying to get property 'name' of non-object
  • dd($this->faker); returns null

@axlon @driesvints

(note the example method here is not returning a closure, but an array as demonstrated in the docs)

@drbyte could it be that you're using a unit test where the framework isn't booted?

@driesvints no, it's a proper FeatureTest which properly extends TestCase

Here's a blank repo with a commit showing how to reproduce it:
https://github.com/drbyte/withFaker-bug/commit/45442006ed35c394a23f2372cc5b8012f5463f6d

@drbyte its because your state is not within a closure, when you use it like this the state will be resolved as soon as you call the method, before faker is initialised

Changing it to the snippet below should fix it:

    public function male()
    {
        return $this->state(fn () => [
            'name' => $this->faker->firstNameMale,
        ]);
    }

Edit (as I didn't read your first comment initially) : I haven't checked the docs yet, so I don't know if it shows an example without a Closure but AFAIK it won't work without a closure unless you manually instantiate faker on the factory instance (which I wouldn't recommend because of the way factories work internally)

Thanks. My brain was treating it as a regular class, but of course it's not. 馃槃

The docs don't mention it, but I've submitted a PR. https://github.com/laravel/docs/pull/6329/files

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Fuzzyma picture Fuzzyma  路  3Comments

SachinAgarwal1337 picture SachinAgarwal1337  路  3Comments

PhiloNL picture PhiloNL  路  3Comments

RomainSauvaire picture RomainSauvaire  路  3Comments

ghost picture ghost  路  3Comments