【问题标题】:Two-way relationships in Laravel Factories with FakerLaravel 工厂与 Faker 的双向关系
【发布时间】:2018-08-17 02:38:24
【问题描述】:

我有两个正在尝试为其编写工厂的 Modals。 LineItems 和发票。问题是它们是基于彼此动态的。

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   

LineItems 知道它们被分配到哪个 Invoice,Invoices 保存分配给它们的 LineItems 的总值。

LineItem 看起来像这样:

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

然后要为 Invoices 建立一个工厂,我需要将 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
  ];
});

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

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

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

【问题讨论】:

  • 你使用的是什么版本的 Laravel?
  • @RossWilson Laravel 5.5
  • 我要说的是,如果您使用 >= 5.6.12,您可以使用 afterCreating() 方法。
  • 啊该死的,那会很有用。
  • 我想知道为什么total 不是计算而是存储为整数...容易出错。祝你好运!

标签: php laravel faker factories


【解决方案1】:

方法 1 - 使用种子

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

$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
    ];
});

然后你创建一个种子来填充你想要的方式

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();

现在工厂的逻辑是隔离的,你可以用自己的业务逻辑来创建。


方法 2 - 使用虚拟工厂

创建 LineItems 工厂

$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
    ];
});

创建虚拟工厂

$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
});

用法。重要提示:使用 MAKE 而不是 CREATE:

$invoiceWithItems = factory(App\Models\DummyFactory::class)->make();

现在你的逻辑在工厂里

【讨论】:

  • 除非我遗漏了什么,否则这会出现同样的问题,但反过来。我如何将节省的总金额返回到发票?您是否建议只用 eloquent 查询它并在播种器中正常更新它?
  • 我忘了。但您只需要添加自己的逻辑即可。
  • 已编辑。您可以使用“dummyfactory”在工厂而不是播种机中生成数据
猜你喜欢
  • 2021-06-29
  • 2018-05-01
  • 2015-12-09
  • 2018-05-26
  • 2016-08-18
  • 2020-03-09
  • 2020-12-28
  • 2018-06-13
  • 2014-07-28
相关资源
最近更新 更多