diff --git a/src/Relation/BelongsTo.php b/src/Relation/BelongsTo.php index 0f3e07ad..5342c502 100644 --- a/src/Relation/BelongsTo.php +++ b/src/Relation/BelongsTo.php @@ -140,6 +140,10 @@ private function shouldPull(Tuple $tuple, Tuple $rTuple): bool $noChanges = true; $toReference = []; foreach ($this->outerKeys as $i => $outerKey) { + if (!\array_key_exists($outerKey, $newData)) { + continue; + } + $innerKey = $this->innerKeys[$i]; if (!\array_key_exists($innerKey, $oldData) || $oldData[$innerKey] !== $newData[$outerKey]) { return true; diff --git a/tests/ORM/Functional/Driver/Common/Integration/Case428/CaseTest.php b/tests/ORM/Functional/Driver/Common/Integration/Case428/CaseTest.php new file mode 100644 index 00000000..f5688ef1 --- /dev/null +++ b/tests/ORM/Functional/Driver/Common/Integration/Case428/CaseTest.php @@ -0,0 +1,150 @@ +orm, Entity\Order::class)) + ->load('items') + ->wherePK(1) + ->fetchOne(); + + // Check result + $this->assertInstanceOf(Entity\Order::class, $order); + $this->assertCount(2, $order->items); + + // Order 2 + $order = (new Select($this->orm, Entity\Order::class)) + ->load('items') + ->wherePK(2) + ->fetchOne(); + + // Check result + $this->assertInstanceOf(Entity\Order::class, $order); + $this->assertCount(1, $order->items); + } + + public function testSave(): void + { + /** @var Order $order */ + $order = (new Select($this->orm, Entity\Order::class)) + ->wherePK(1) + ->load('items') + ->fetchOne(); + + $purchaseOrder = new PurchaseOrder('PO001'); + $orderItems = []; + foreach ($order->items as $orderItem) { + $purchaseOrderItem = new PurchaseOrderItem($orderItem->quantity); + $purchaseOrderItem->orderItem = $orderItem; + $purchaseOrder->items[] = $purchaseOrderItem; + $orderItem->purchaseOrder = $purchaseOrder; + $orderItem->status = 1; + $orderItems[] = $orderItem; + } + + $this->save($purchaseOrder, ...$orderItems); + } + + public function testCreate(): void + { + $order = new Entity\Order('O0101'); + $purchaseOrder = new PurchaseOrder('PO101'); + + $orderItems = []; + for ($i = 0; $i < 20; $i++) { + $orderItem = new Entity\OrderItem('A' . $i, 1); + $orderItem->order = $order; + // $order->items[] = $orderItem; + $orderItem->purchaseOrder = $purchaseOrder; + $orderItems[] = $orderItem; + } + + $this->save($order, $purchaseOrder, ...$orderItems); + } + + public function setUp(): void + { + // Init DB + parent::setUp(); + $this->makeTables(); + $this->fillData(); + + $this->loadSchema(__DIR__ . '/schema.php'); + } + + private function makeTables(): void + { + // Make tables + $this->makeTable('order', [ + 'id' => 'primary', // autoincrement + 'number' => 'string', + ]); + + $this->makeTable('order_item', [ + 'id' => 'primary', + 'order_id' => 'int', + 'sku' => 'string', + 'quantity' => 'int', + 'purchase_order_id' => 'int,nullable', + 'status' => 'int', + ]); + + $this->makeTable('purchase_order', [ + 'id' => 'primary', + 'number' => 'string', + ]); + + $this->makeTable('purchase_order_item', [ + 'id' => 'primary', + 'purchase_order_id' => 'int', + 'order_item_id' => 'int,nullable', + 'quantity' => 'int', + ]); + + $this->makeFK('order_item', 'order_id', 'order', 'id', 'NO ACTION', 'CASCADE'); + + $this->makeFK('order_item', 'purchase_order_id', 'purchase_order', 'id', 'NO ACTION', 'CASCADE'); + + $this->makeFK('purchase_order_item', 'purchase_order_id', 'purchase_order', 'id', 'NO ACTION', 'CASCADE'); + + $this->makeFK('purchase_order_item', 'order_item_id', 'order_item', 'id', 'NO ACTION', 'CASCADE'); + } + + private function fillData(): void + { + $this->getDatabase()->table('order')->insertMultiple( + ['number'], + [ + ['O001'], + ['O002'], + ['O003'], + ], + ); + $this->getDatabase()->table('order_item')->insertMultiple( + ['order_id', 'sku', 'quantity', 'purchase_order_id', 'status'], + [ + [1, 'A', 1, null, 0], + [1, 'B', 1, null, 0], + [2, 'A', 2, null, 0], + [3, 'B', 2, null, 0], + ], + ); + } +} diff --git a/tests/ORM/Functional/Driver/Common/Integration/Case428/Entity/Order.php b/tests/ORM/Functional/Driver/Common/Integration/Case428/Entity/Order.php new file mode 100644 index 00000000..9e841899 --- /dev/null +++ b/tests/ORM/Functional/Driver/Common/Integration/Case428/Entity/Order.php @@ -0,0 +1,19 @@ + */ + public iterable $items = []; + + public function __construct(string $number) + { + $this->number = $number; + } +} diff --git a/tests/ORM/Functional/Driver/Common/Integration/Case428/Entity/OrderItem.php b/tests/ORM/Functional/Driver/Common/Integration/Case428/Entity/OrderItem.php new file mode 100644 index 00000000..47944014 --- /dev/null +++ b/tests/ORM/Functional/Driver/Common/Integration/Case428/Entity/OrderItem.php @@ -0,0 +1,26 @@ +sku = $sku; + $this->quantity = $quantity; + } +} diff --git a/tests/ORM/Functional/Driver/Common/Integration/Case428/Entity/PurchaseOrder.php b/tests/ORM/Functional/Driver/Common/Integration/Case428/Entity/PurchaseOrder.php new file mode 100644 index 00000000..05b9e672 --- /dev/null +++ b/tests/ORM/Functional/Driver/Common/Integration/Case428/Entity/PurchaseOrder.php @@ -0,0 +1,19 @@ + */ + public iterable $items = []; + + public function __construct(string $number) + { + $this->number = $number; + } +} diff --git a/tests/ORM/Functional/Driver/Common/Integration/Case428/Entity/PurchaseOrderItem.php b/tests/ORM/Functional/Driver/Common/Integration/Case428/Entity/PurchaseOrderItem.php new file mode 100644 index 00000000..ad8c2fb0 --- /dev/null +++ b/tests/ORM/Functional/Driver/Common/Integration/Case428/Entity/PurchaseOrderItem.php @@ -0,0 +1,22 @@ +quantity = $quantity; + } +} diff --git a/tests/ORM/Functional/Driver/Common/Integration/Case428/schema.php b/tests/ORM/Functional/Driver/Common/Integration/Case428/schema.php new file mode 100644 index 00000000..11cc618b --- /dev/null +++ b/tests/ORM/Functional/Driver/Common/Integration/Case428/schema.php @@ -0,0 +1,186 @@ + [ + Schema::ENTITY => Order::class, + Schema::MAPPER => Mapper::class, + Schema::SOURCE => Source::class, + Schema::DATABASE => 'default', + Schema::TABLE => 'order', + Schema::PRIMARY_KEY => ['id'], + Schema::FIND_BY_KEYS => ['id'], + Schema::COLUMNS => [ + 'id' => 'id', + 'number' => 'number', + ], + Schema::RELATIONS => [ + 'items' => [ + Relation::TYPE => Relation::HAS_MANY, + Relation::TARGET => 'order_item', + Relation::COLLECTION_TYPE => 'array', + Relation::LOAD => Relation::LOAD_PROMISE, + Relation::SCHEMA => [ + Relation::CASCADE => true, + Relation::NULLABLE => false, + Relation::WHERE => [], + Relation::ORDER_BY => [], + Relation::INNER_KEY => ['id'], + Relation::OUTER_KEY => 'order_id', + ], + ], + ], + Schema::TYPECAST => [ + 'id' => 'int', + 'number' => 'string', + ], + Schema::SCHEMA => [], + Schema::GENERATED_FIELDS => [ + 'id' => GeneratedField::ON_INSERT, // autoincrement + ], + ], + 'order_item' => [ + Schema::ENTITY => OrderItem::class, + Schema::SOURCE => Source::class, + Schema::DATABASE => 'default', + Schema::MAPPER => Mapper::class, + Schema::TABLE => 'order_item', + Schema::PRIMARY_KEY => ['id'], + Schema::FIND_BY_KEYS => ['id'], + Schema::COLUMNS => [ + 'id' => 'id', + 'order_id' => 'order_id', + 'sku' => 'sku', + 'quantity' => 'quantity', + 'purchase_order_id' => 'purchase_order_id', + 'status' => 'status', + ], + Schema::RELATIONS => [ + 'order' => [ + Relation::TYPE => Relation::BELONGS_TO, + Relation::TARGET => 'order', + Relation::LOAD => Relation::LOAD_PROMISE, + Relation::SCHEMA => [ + Relation::CASCADE => true, + Relation::NULLABLE => false, + Relation::INNER_KEY => 'order_id', + Relation::OUTER_KEY => ['id'], + ], + ], + 'purchaseOrder' => [ + Relation::TYPE => Relation::BELONGS_TO, + Relation::TARGET => 'purchase_order', + Relation::LOAD => Relation::LOAD_PROMISE, + Relation::SCHEMA => [ + Relation::CASCADE => true, + Relation::NULLABLE => true, + Relation::INNER_KEY => 'purchase_order_id', + Relation::OUTER_KEY => ['id'], + ], + ], + ], + Schema::TYPECAST => [ + 'id' => 'int', + 'order_id' => 'int', + 'sku' => 'string', + 'quantity' => 'int', + 'purchase_order_id' => 'int', + 'status' => 'int', + ], + Schema::SCHEMA => [], + Schema::GENERATED_FIELDS => [ + 'id' => GeneratedField::ON_INSERT, // autoincrement + ], + ], + 'purchase_order' => [ + Schema::ENTITY => PurchaseOrder::class, + Schema::MAPPER => Mapper::class, + Schema::SOURCE => Source::class, + Schema::REPOSITORY => Repository::class, + Schema::DATABASE => 'default', + Schema::TABLE => 'purchase_order', + Schema::PRIMARY_KEY => ['id'], + Schema::FIND_BY_KEYS => ['id'], + Schema::COLUMNS => [ + 'id' => 'id', + 'number' => 'number', + ], + Schema::RELATIONS => [ + 'items' => [ + Relation::TYPE => Relation::HAS_MANY, + Relation::TARGET => 'purchase_order_item', + Relation::COLLECTION_TYPE => 'array', + Relation::LOAD => Relation::LOAD_PROMISE, + Relation::SCHEMA => [ + Relation::CASCADE => true, + Relation::NULLABLE => false, + Relation::WHERE => [], + Relation::ORDER_BY => [], + Relation::INNER_KEY => ['id'], + Relation::OUTER_KEY => 'purchase_order_id', + ], + ], + ], + Schema::SCOPE => null, + Schema::TYPECAST => [ + 'id' => 'int', + 'number' => 'string', + ], + Schema::SCHEMA => [], + Schema::GENERATED_FIELDS => [ + 'id' => GeneratedField::ON_INSERT, // autoincrement + ], + ], + 'purchase_order_item' => [ + Schema::ENTITY => PurchaseOrderItem::class, + Schema::MAPPER => Mapper::class, + Schema::SOURCE => Source::class, + Schema::REPOSITORY => Repository::class, + Schema::DATABASE => 'default', + Schema::TABLE => 'purchase_order_item', + Schema::PRIMARY_KEY => ['id'], + Schema::FIND_BY_KEYS => ['id'], + Schema::COLUMNS => [ + 'id' => 'id', + 'purchase_order_id' => 'purchase_order_id', + 'order_item_id' => 'order_item_id', + 'quantity' => 'quantity', + ], + Schema::RELATIONS => [ + 'orderItem' => [ + Relation::TYPE => Relation::BELONGS_TO, + Relation::TARGET => 'order_item', + Relation::LOAD => Relation::LOAD_PROMISE, + Relation::SCHEMA => [ + Relation::CASCADE => true, + Relation::NULLABLE => true, + Relation::INNER_KEY => 'order_item_id', + Relation::OUTER_KEY => ['id'], + ], + ], + ], + Schema::SCOPE => null, + Schema::TYPECAST => [ + 'id' => 'primary', + 'purchase_order_id' => 'int', + 'order_item_id' => 'int', + 'quantity' => 'int', + ], + Schema::SCHEMA => [], + Schema::GENERATED_FIELDS => [ + 'id' => GeneratedField::ON_INSERT, // autoincrement + ], + ], +]; diff --git a/tests/ORM/Functional/Driver/MySQL/Integration/Case428/CaseTest.php b/tests/ORM/Functional/Driver/MySQL/Integration/Case428/CaseTest.php new file mode 100644 index 00000000..854c70ec --- /dev/null +++ b/tests/ORM/Functional/Driver/MySQL/Integration/Case428/CaseTest.php @@ -0,0 +1,17 @@ +