Laravel工厂与Faker的双向关系

时间:2018-08-16 17:54:24

标签: php laravel faker factories

我有两个要为其编写工厂的模式。订单项和发票。问题在于它们彼此之间是动态的。

Table: line_items
id | amount | invoice
1  | 15     | 1 
2  | 10     | 1 
3  | 15     | 2
4  | 5      | null

Table: invoices
id | total
1  | 25   
2  | 15   

LineItem知道将它们分配给哪个发票,并且Invoices保存分配给他们的LineItem的总价值。

LineItems看起来像这样:

$factory->define(\App\Models\LineItems::class, function (Faker $faker) {
  return [
    'amount' => $faker->numberBetween(1, 15),
    'invoice' => null
  ];
});

然后要为发票建立工厂,我需要对LineItems的金额求和,没什么大不了的,我可以在工厂内创建LineItems并总计:

$factory->define(\App\Models\Invoice::class, function (Faker $faker) {
  $total = 0;

  $line_items = $factory(\App\Models\LineItems::class, 3);

  foreach($line_items as $l) {
    $total += $l->amount;
  }

  return [
    'total' => $total
  ];
});

但是现在的问题是,如何将发票id编号保存回我刚刚创建的LineItems?显然,我没有id直到在Closure回调完成后,将保存工厂。我无法将LineItems从Closure中退回,因为工厂需要使用退货,以便实际上可以保存发票。

我知道这似乎是一个X / Y问题。但是,我在SO上浏览了一些类似的问题,我不喜欢这种解决方法,因为它们非常脆弱,依赖于非显而易见的操作顺序或某些数据在运行之前就已经存在一个工厂。我宁愿使用工厂找到解决方案,并尽可能将所有问题保持在本地。

此外,如果有解决方案,则意味着工厂可以自我维持,这对所有相关方面都更好。但是,如果这不可能,那将是一个可以接受的答案,因为它使我可以采用其他(尽管更脆弱)保存测试数据的方法。

1 个答案:

答案 0 :(得分:1)

方法1-带种子

首先,LineItems工厂设置了一些随机发票

create table dbo.CountryTable ( Countries nvarchar(80) )

insert into CountryTable ( Countries ) values ( 'Canada, Italy, France, Bélgica' )

select * from CountryTable where 
  Replace( Replace(Countries, ',', ''), 'é', 'e' ) like '%Canada Italy France Belgica%'

然后创建不依赖项的发票

$factory->define(\App\Models\LineItems::class, function (Faker $faker) {
    return [
        'amount' => $faker->numberBetween(1, 15),
        'invoice' => function () {
             return factory(\App\Models\Invoice::class)->create()->id;
        },
    ];
});

然后创建一个种子,以填充所需的方式

$factory->define(\App\Models\Invoice::class, function (Faker $faker) {
    return [
        'total' => 0
    ];
});

现在工厂的逻辑是隔离的,您可以使用自己的业务逻辑进行创建。


方法2-使用虚拟工厂

创建LineItems工厂

class InvoiceSeederClass extends Seeder
{
    public function run()
    {
        $invoice = factory(App\Models\Invoice::class)->create();

        $lineItems = factory(App\Models\LineItems::class, 3)->create([
            'invoice' => $invoice->id,
        ]);

        $total = 0;
        foreach($lineItems as $item) {
            $total += $item->amount;
        }

        $invoice->amount = $total;
        $invoice->save();
    }
}

$seeder = new InvoiceSeederClass();
$seeder->run();

创建发票工厂

$factory->define(\App\Models\LineItems::class, function (Faker $faker) {
    return [
        'amount' => $faker->numberBetween(1, 15),
        'invoice' => function () {
             return factory(\App\Models\Invoice::class)->create()->id;
        },
    ];
});

创建虚拟工厂

$factory->define(\App\Models\Invoice::class, function (Faker $faker) {
    return [
        'total' => 0
    ];
});

用法。重要提示:使用MAKE代替CREATE:

$factory->define(\App\Models\DummyFactory::class, function () use($faker) {
    $invoice = factory(App\Models\Invoice::class)->create();

    $lineItems = factory(App\Models\LineItems::class, 3)->create([
        'invoice' => $invoice->id,
    ]);

    $total = 0;
    foreach($lineItems as $item) {
        $total += $item->amount;
    }

    $invoice->amount = $total;
    $invoice->save();

    return []; // you can return any array you want, ex. $invoice
});

现在您的逻辑是在工厂内部

相关问题