Двусторонние отношения на фабриках Laravel с Faker

У меня есть два Модальности, для которых я пытаюсь написать фабрики. 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 знают, какому Счету они назначены, а Счета сохраняют общее значение назначенных им Элементов Линии.

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, потому что фабрике нужно использовать возврат, чтобы он мог фактически сохранить Invoice.

Я знаю, что это похоже на проблему X / Y. Но я рассмотрел некоторые подобные вопросы здесь, в SO, и я не фанат обходных решений, потому что они очень хрупкие, полагаясь на неочевидные порядки работы или на то, что определенные данные уже существуют до того, как вы сможете запустить завод. Я бы предпочел найти решение с использованием фабрик и просто оставить все проблемы локальными, если это возможно.

Плюс, если есть решение, это означает, что фабрики являются самодостаточными, что лучше для всех заинтересованных сторон. Но если это невозможно, это будет приемлемым ответом, поскольку он позволяет мне использовать другие (хотя и более хрупкие) способы сохранения тестовых данных.

0

Решение

Способ 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();

Теперь ваша логика внутри фабрики

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]