模型工厂内的依赖注入

时间:2015-10-17 16:24:40

标签: dependency-injection laravel-5

这是我的第一个问题,所以我也很欣赏如何正确提问的提示。

所以,在我的Laravel应用程序中,我有一个包含用户的数据库表。首先,我想为它建立一个模型工厂。所以我从laravel doc page获取了标准代码:

setprecision

我改为:

$factory->define(App\User::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
        'password' => bcrypt(str_random(10)),
        'remember_token' => str_random(10),
    ];
});

到目前为止,一切正常。但我希望它更复杂,我决定使用更具体的Faker类来生成意大利数据。我改成了:

$factory->define(App\User::class,
                    function(Faker\Generator $faker) {

    return [
        'name' => $faker->name(),
        'email' => $faker->safeEmail(),
        'password' => bcrypt(str_random(10)),
        'phone_number' => $faker->phoneNumber(),
        'remember_token' => str_random(10),
        'account_type' => 0,
    ];

});

在播种机课上我写道:

$factory->define(App\User::class,
                    function(Faker\Generator $faker,
                             Faker\Provider\it_IT\PhoneNumber $fakerITPN,
                             Faker\Provider\it_IT\Person $fakerITPER,
                             Faker\Provider\it_IT\Internet $fakerITInt) {

    return [
        'name' => $fakerITPER->name(),
        'email' => $fakerITInt->safeEmail(),
        'password' => bcrypt(str_random(10)),
        'phone_number' => $fakerITPN->phoneNumber(),
        'remember_token' => str_random(10),
        'account_type' => 0,
    ];

});

然后,在我使用Artisan之后,命令:

factory(App\User::class)->create();

我得到以下错误(只是头部,清除):

artisan migrate:refresh --seed -vvv

显然,依赖注入有问题,但我不知道是什么。我知道,在这种情况下,我可以手动创建我需要的类的实例,但我想知道,如何正确地执行它。有人可以帮忙吗?

2 个答案:

答案 0 :(得分:2)

如果您查看faker @ https://github.com/fzaninotto/Faker#localization的文档,您会看到您可以简单地将正确的本地化作为参数进行创建。

在您的情况下,只需使用:

Faker\Factory::create('it_IT');

定义工厂时,无需在匿名函数中添加更多参数。

编辑:

添加有关依赖注入的问题。如果您跟踪源代码,它不会在下面执行任何依赖注入。

$factory->define(...)

仅设置定义数组

public function define($class, callable $attributes, $name = 'default')
{
    $this->definitions[$class][$name] = $attributes;
}

调用

Faker\Factory::create();

factory(App\User::class)->create();

$factory->of($class)

调用“of”实例化FactoryBuilder的方法 (见Illuminate \ Database \ Eloquent \ Factory.php第169-172行)

public function of($class, $name = 'default')
{
    return new FactoryBuilder($class, $name, $this->definitions, $this->faker);
}

之后,它链接了FactoryBuilder的“create”方法,该方法调用“make”方法,该方法也称为“makeInstance”

protected function makeInstance(array $attributes = [])
{
    return Model::unguarded(function () use ($attributes) {
        if (! isset($this->definitions[$this->class][$this->name])) {
            throw new InvalidArgumentException("Unable to locate factory with name [{$this->name}].");
        }

        $definition = call_user_func($this->definitions[$this->class][$this->name], $this->faker, $attributes);

        return new $this->class(array_merge($definition, $attributes));
    });
}

注意“makeInstance”中的“call_user_func”,即负责调用作为定义的第二个参数创建的匿名函数(在ModelFactory.php中)。它只向可调用函数传递了2个参数,它们是:

...$this->faker, $attributes);

第一个参数只传递了1个faker,第二个参数传递了一个属性数组(这是你之前在ErrorException上看到的那个)

这意味着您只能以这种方式定义工厂:

$factory->define(App\User::class, 
    function (Faker\Generator $faker, $attributes=array()) {

    return [
        'name' => $faker->name,
        'email' => $faker->email,
        'password' => bcrypt(str_random(10)),
        'remember_token' => str_random(10),
    ];
});

如果你真的需要其他类,你可以在“define”之外初始化它,并在这样的函数中使用它:

$sampleInstance = app(App\Sample::class);

$factory->define(App\User::class, 
    function (Faker\Generator $faker, $attributes=array()) use($sampleInstance){

    //...do something here 
    //...or process the $attributes received
    //...or call a method like
    $sampleData = $sampleInstance->doSomething();        

    return [
        'someField' => $sampleData,
        'name' => $faker->name,
        'email' => $faker->email,
        'password' => bcrypt(str_random(10)),
        'remember_token' => str_random(10),
    ];
});

答案 1 :(得分:1)

您可以将此设置放在AppServiceProvider的register()中:

$this->app->singleton(\Faker\Generator::class, function () {
        return \Faker\Factory::create('it_IT');
});