Hello Stack Overflow community,
I'm working with CakePHP 4.x and facing an issue when trying to save nested associations. My application is processing orders, each containing OrderItems, and each OrderItem may have multiple OrderItemOptions. I'm using newEntity() methods to create these entities. However, when I attempt to save an Order entity that contains OrderItems with their OrderItemOptions, I encounter a ValueError related to array_combine().
The Error i'm getting
[ValueError] array_combine(): Argument #1 ($keys) and argument #2 ($values) must have the same number of elements
Simplified Api Objects: Object with 3 Options
"fields": [
{
"field_id": 2,
"field_name": "Size",
"field_type": 0,
"string_value": null,
"date_value": null,
"file_value_url": null,
"options": [
{
"option_id": 1,
"qty": 50,
"code": "S",
"name": "Small",
"sub_options": [],
"sku": "STSU822C0081S",
"vendor_sku": null,
"dn_sku_id": "246924061_392440381"
},
{
"option_id": 2,
"qty": 50,
"code": "M",
"name": "Medium",
"sub_options": [],
"sku": "STSU822C0081M",
"vendor_sku": null,
"dn_sku_id": "246924061_392439396"
},
{
"option_id": 4,
"qty": 50,
"code": "XL",
"name": "X Large",
"sub_options": [],
"sku": "STSU822C0081X",
"vendor_sku": null,
"dn_sku_id": "246924061_392439371"
}
]
},
{
"field_id": 830881,
"field_name": "Position",
"field_type": 2,
"string_value": null,
"date_value": null,
"file_value_url": null,
"options": []
},
{
"field_id": 822656,
"field_name": "Druckposition",
"field_type": 2,
"string_value": null,
"date_value": null,
"file_value_url": null,
"options": []
}
],
Object without any Options
"fields": []
Object with 1 Option
"fields": [
{
"field_id": 2,
"field_name": "Size",
"field_type": 0,
"string_value": null,
"date_value": null,
"file_value_url": null,
"options": [
{
"option_id": 6214601,
"qty": 150,
"code": "100er",
"name": "100er",
"sub_options": [],
"sku": "SBMU-C13",
"vendor_sku": null,
"dn_sku_id": "247765931_394789266"
}
]
},
{
"field_id": 830881,
"field_name": "Position",
"field_type": 2,
"string_value": null,
"date_value": null,
"file_value_url": null,
"options": []
},
{
"field_id": 822656,
"field_name": "Druckposition",
"field_type": 2,
"string_value": null,
"date_value": null,
"file_value_url": null,
"options": []
}
],
Here's a simplified version of my code: OrdersTable
public function saveOrders(array $orders): bool
{
$newOrders = [];
$defaultShippingService = $this->ShippingServices->find('all', [
'conditions' => [
'is_default' => true,
'published' => true,
],
])->first();
foreach ($orders['orders'] as $order) {
$existingOrder = $this->find()->where(['order_id' => $order['order_id']])->first();
if (!$existingOrder) {
$assignedUser = $this->Users->find()->where(['deconetwork_user_id' => $order['assigned_to']['id']])->first();
if ($assignedUser) {
$company = $order['shipping_details']['company'] ?? $order['billing_details']['company'] ?? null;
$details = $order['shipping_details'] ?? $order['billing_details'];
$address = trim($details['street']);
$firstName = trim($details['firstname']);
$lastName = trim($details['lastname']);
$street = trim($details['street']);
$postcode = trim($details['postcode']);
$city = trim($details['city']);
$countryCode = trim($details['country_code']);
$state = trim($details['state']);
$email = $details['custom_fields'];
if ($company) {
if (strlen($company) <= 35) {
$company_name1 = $company;
$company_name2 = null;
} else {
[$company_name1, $company_name2] = $this->splitCompanyName($company);
}
} else {
$company_name1 = $firstName . ' ' . $lastName;
$company_name2 = null;
}
$addressParts = $this->splitAddress($street);
$orderEntity = $this->newEntity([
'user_id' => $assignedUser->id,
'order_id' => $order['order_id'],
'customer_id' => $order['customer_id'],
'shipping_service_id' => $defaultShippingService ? $defaultShippingService->id : null,
'company' => $company,
'company_name1' => $company_name1,
'company_name2' => $company_name2,
'customer_reference_number1' => $order['order_id'],
'firstname' => $firstName,
'lastname' => $lastName,
'address' => $address,
'street' => $addressParts['street'],
'house_no' => $addressParts['house_no'],
'postcode' => $postcode,
'city' => $city,
'country_code' => $countryCode,
'state' => $state,
'email' => $this->getCustomerMail((array)$email),
'date_produced' => new FrozenTime($order['date_produced']),
'order_items' => [],
]);
foreach ($order['order_lines'] as $orderItem) {
$orderItemEntity = $this->OrderItems->newEntity([
'quantity' => $orderItem['qty'],
'order_item_options' => [],
]);
$productEntity = $this->OrderItems->Products->findOrCreate(
['product_id' => $orderItem['product_id'] ?? 2],
function ($entity) use ($orderItem) {
$entity->product_code = $orderItem['product_code'] ?? null;
$entity->product_name = $orderItem['product_name'];
$entity->product_color = $orderItem['product_color']['name'] ?? null;
}
);
$orderItemEntity->product = $productEntity;
$orderItemOptions = [];
if (!empty($orderItem['fields'])) {
$field = $orderItem['fields'][0];
if ($field['field_id'] === 2 && !empty($field['options'])) {
foreach ($field['options'] as $option) {
$orderItemOption = $this->OrderItems->OrderItemOptions->newEntity([
'option_type' => $field['field_name'],
'option_value' => $option['code'],
'quantity' => $option['qty'],
'sku' => $option['sku']
]);
$orderItemOptions[] = $orderItemOption;
}
}
}
if (!empty($orderItemOptions)) {
$orderItemEntity->order_item_options = $orderItemOptions;
}
$orderEntity->order_items[] = $orderItemEntity;
}
$newOrders[] = $orderEntity;
}
}
}
if (!empty($newOrders)) {
#Log::write('error', 'Saving ' . print_r($newOrders, true));
$savedOrders = $this->saveMany($newOrders, [
'associated' => ['OrderItems.Products', 'OrderItems.OrderItemOptions']
]);
if ($savedOrders) {
return true;
} else {
foreach ($newOrders as $order) {
Log::error('Order Error: ' . json_encode($order->getErrors()));
foreach ($order->order_items as $item) {
Log::error('OrderItem Error: ' . json_encode($item->getErrors()));
}
}
}
}
return false;
}
OrdersTable:
public function initialize(array $config): void
{
parent::initialize($config);
Syllable::setCacheDir(ROOT . DS . 'tmp' . DS . 'cache');
$this->setTable('orders');
$this->setDisplayField('firstname');
$this->setPrimaryKey('id');
$this->addBehavior('Timestamp');
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER',
]);
$this->belongsTo('ShippingServices', [
'foreignKey' => 'shipping_service_id',
'joinType' => 'INNER',
]);
$this->hasMany('OrderItems', [
'foreignKey' => 'order_id',
'dependent' => true,
]);
}
OrderItemsTable:
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('order_items');
$this->setDisplayField(['order_id', 'product_id']);
$this->setPrimaryKey(['order_id', 'product_id']);
$this->addBehavior('Timestamp');
$this->belongsTo('Orders', [
'foreignKey' => 'order_id',
'joinType' => 'INNER',
]);
$this->belongsTo('Products', [
'foreignKey' => 'product_id',
'joinType' => 'INNER',
]);
$this->hasMany('OrderItemOptions', [
'foreignKey' => 'order_item_id',
'dependent' => true,
]);
}
OrderItemOptionsTable:
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('order_item_options');
$this->setDisplayField('option_type');
$this->setPrimaryKey(['order_item_id', 'order_id', 'product_id']);
$this->addBehavior('Timestamp');
$this->belongsTo('OrderItems', [
'foreignKey' => 'order_item_id',
'joinType' => 'INNER',
]);
}
Log Stack Trace using Log::write
if (!empty($orderItemOptions)) {
$orderItemEntity->order_item_options = $orderItemOptions;
Log::write('error', 'Saving ' . print_r($orderItemEntity, true));
}
2024-01-05 06:31:47 error: Saving App\Model\Entity\OrderItem Object
(
[quantity] => 150
[order_item_options] => Array
(
[0] => App\Model\Entity\OrderItemOption Object
(
[option_type] => Size
[option_value] => S
[quantity] => 50
[sku] => STSU822C0081S
[[new]] => 1
[[accessible]] => Array
(
[order_item_id] => 1
[option_type] => 1
[option_value] => 1
[quantity] => 1
[sku] => 1
[created] => 1
[modified] => 1
[order_item] => 1
)
[[dirty]] => Array
(
[option_type] => 1
[option_value] => 1
[quantity] => 1
[sku] => 1
)
[[original]] => Array
(
)
[[virtual]] => Array
(
)
[[hasErrors]] =>
[[errors]] => Array
(
)
[[invalid]] => Array
(
)
[[repository]] => OrderItemOptions
)
[1] => App\Model\Entity\OrderItemOption Object
(
[option_type] => Size
[option_value] => M
[quantity] => 50
[sku] => STSU822C0081M
[[new]] => 1
[[accessible]] => Array
(
[order_item_id] => 1
[option_type] => 1
[option_value] => 1
[quantity] => 1
[sku] => 1
[created] => 1
[modified] => 1
[order_item] => 1
)
[[dirty]] => Array
(
[option_type] => 1
[option_value] => 1
[quantity] => 1
[sku] => 1
)
[[original]] => Array
(
)
[[virtual]] => Array
(
)
[[hasErrors]] =>
[[errors]] => Array
(
)
[[invalid]] => Array
(
)
[[repository]] => OrderItemOptions
)
[2] => App\Model\Entity\OrderItemOption Object
(
[option_type] => Size
[option_value] => XL
[quantity] => 50
[sku] => STSU822C0081X
[[new]] => 1
[[accessible]] => Array
(
[order_item_id] => 1
[option_type] => 1
[option_value] => 1
[quantity] => 1
[sku] => 1
[created] => 1
[modified] => 1
[order_item] => 1
)
[[dirty]] => Array
(
[option_type] => 1
[option_value] => 1
[quantity] => 1
[sku] => 1
)
[[original]] => Array
(
)
[[virtual]] => Array
(
)
[[hasErrors]] =>
[[errors]] => Array
(
)
[[invalid]] => Array
(
)
[[repository]] => OrderItemOptions
)
)
[product] => App\Model\Entity\Product Object
(
[id] => 27
[product_id] => 246924061
[product_code] => STSU822-B
[product_name] => STSU822 Cruiser Iconic Unisex Hoodie
[product_color] => British Khaki
[created] => Cake\I18n\FrozenTime Object
(
[date] => 2024-01-05 05:17:16.000000
[timezone_type] => 3
[timezone] => Europe/Berlin
)
[modified] => Cake\I18n\FrozenTime Object
(
[date] => 2024-01-05 05:17:16.000000
[timezone_type] => 3
[timezone] => Europe/Berlin
)
[[new]] =>
[[accessible]] => Array
(
[product_id] => 1
[product_code] => 1
[product_name] => 1
[product_color] => 1
[created] => 1
[modified] => 1
[order_items] => 1
)
[[dirty]] => Array
(
)
[[original]] => Array
(
)
[[virtual]] => Array
(
)
[[hasErrors]] =>
[[errors]] => Array
(
)
[[invalid]] => Array
(
)
[[repository]] => Products
)
[[new]] => 1
[[accessible]] => Array
(
[order_id] => 1
[product_id] => 1
[quantity] => 1
[created] => 1
[modified] => 1
[order] => 1
[product] => 1
[order_item_options] => 1
)
[[dirty]] => Array
(
[quantity] => 1
[order_item_options] => 1
[product] => 1
)
[[original]] => Array
(
[order_item_options] => Array
(
)
)
[[virtual]] => Array
(
)
[[hasErrors]] =>
[[errors]] => Array
(
)
[[invalid]] => Array
(
)
[[repository]] => OrderItems
)
2024-01-05 06:31:47 error: Saving App\Model\Entity\OrderItem Object
(
[quantity] => 150
[order_item_options] => Array
(
[0] => App\Model\Entity\OrderItemOption Object
(
[option_type] => Size
[option_value] => 100er
[quantity] => 150
[sku] => SBMU-C13
[[new]] => 1
[[accessible]] => Array
(
[order_item_id] => 1
[option_type] => 1
[option_value] => 1
[quantity] => 1
[sku] => 1
[created] => 1
[modified] => 1
[order_item] => 1
)
[[dirty]] => Array
(
[option_type] => 1
[option_value] => 1
[quantity] => 1
[sku] => 1
)
[[original]] => Array
(
)
[[virtual]] => Array
(
)
[[hasErrors]] =>
[[errors]] => Array
(
)
[[invalid]] => Array
(
)
[[repository]] => OrderItemOptions
)
)
[product] => App\Model\Entity\Product Object
(
[id] => 29
[product_id] => 247765931
[product_code] => SBMU-C
[product_name] => Siebdruck / Mit Unterdruck
[product_color] => 2–farbig
[created] => Cake\I18n\FrozenTime Object
(
[date] => 2024-01-05 05:17:16.000000
[timezone_type] => 3
[timezone] => Europe/Berlin
)
[modified] => Cake\I18n\FrozenTime Object
(
[date] => 2024-01-05 05:17:16.000000
[timezone_type] => 3
[timezone] => Europe/Berlin
)
[[new]] =>
[[accessible]] => Array
(
[product_id] => 1
[product_code] => 1
[product_name] => 1
[product_color] => 1
[created] => 1
[modified] => 1
[order_items] => 1
)
[[dirty]] => Array
(
)
[[original]] => Array
(
)
[[virtual]] => Array
(
)
[[hasErrors]] =>
[[errors]] => Array
(
)
[[invalid]] => Array
(
)
[[repository]] => Products
)
[[new]] => 1
[[accessible]] => Array
(
[order_id] => 1
[product_id] => 1
[quantity] => 1
[created] => 1
[modified] => 1
[order] => 1
[product] => 1
[order_item_options] => 1
)
[[dirty]] => Array
(
[quantity] => 1
[order_item_options] => 1
[product] => 1
)
[[original]] => Array
(
[order_item_options] => Array
(
)
)
[[virtual]] => Array
(
)
[[hasErrors]] =>
[[errors]] => Array
(
)
[[invalid]] => Array
(
)
[[repository]] => OrderItems
)
2024-01-05 06:31:47 error: [ValueError] array_combine(): Argument #1 ($keys) and argument #2 ($values) must have the same number of elements in /vendor/cakephp/cakephp/src/ORM/Association/HasMany.php on line 170
Stack Trace:
- /vendor/cakephp/cakephp/src/ORM/Association/HasMany.php:170
- /vendor/cakephp/cakephp/src/ORM/AssociationCollection.php:315
- /vendor/cakephp/cakephp/src/ORM/AssociationCollection.php:285
- /vendor/cakephp/cakephp/src/ORM/AssociationCollection.php:245
- /vendor/cakephp/cakephp/src/ORM/Table.php:2074
- /vendor/cakephp/cakephp/src/ORM/Table.php:2047
- /vendor/cakephp/cakephp/src/ORM/Table.php:1940
- /vendor/cakephp/cakephp/src/ORM/Table.php:1582
- /vendor/cakephp/cakephp/src/Database/Connection.php:896
- /vendor/cakephp/cakephp/src/ORM/Table.php:1583
- /vendor/cakephp/cakephp/src/ORM/Table.php:1941
- /vendor/cakephp/cakephp/src/ORM/Association/HasMany.php:228
- /vendor/cakephp/cakephp/src/ORM/Association/HasMany.php:185
- /vendor/cakephp/cakephp/src/ORM/AssociationCollection.php:315
- /vendor/cakephp/cakephp/src/ORM/AssociationCollection.php:285
- /vendor/cakephp/cakephp/src/ORM/AssociationCollection.php:245
- /vendor/cakephp/cakephp/src/ORM/Table.php:2074
- /vendor/cakephp/cakephp/src/ORM/Table.php:2047
- /vendor/cakephp/cakephp/src/ORM/Table.php:1940
- /vendor/cakephp/cakephp/src/ORM/Table.php:1582
- /vendor/cakephp/cakephp/src/Database/Connection.php:896
- /vendor/cakephp/cakephp/src/ORM/Table.php:1583
- /vendor/cakephp/cakephp/src/ORM/Table.php:1941
- /vendor/cakephp/cakephp/src/ORM/Table.php:2320
- /vendor/cakephp/cakephp/src/Database/Connection.php:896
- /vendor/cakephp/cakephp/src/ORM/Table.php:2326
- /vendor/cakephp/cakephp/src/ORM/Table.php:2254
- /src/Model/Table/OrdersTable.php:370
- /src/Controller/OrdersController.php:258
- /vendor/cakephp/cakephp/src/Controller/Controller.php:560
- /vendor/cakephp/cakephp/src/Controller/ControllerFactory.php:140
- /vendor/cakephp/cakephp/src/Controller/ControllerFactory.php:115
- /vendor/cakephp/cakephp/src/Http/BaseApplication.php:325
- /vendor/cakephp/cakephp/src/Http/Runner.php:86
- /vendor/cakephp/authorization/src/Middleware/RequestAuthorizationMiddleware.php:110
- /vendor/cakephp/cakephp/src/Http/Runner.php:82
- /vendor/cakephp/authorization/src/Middleware/AuthorizationMiddleware.php:129
- /vendor/cakephp/cakephp/src/Http/Runner.php:82
- /vendor/cakephp/authentication/src/Middleware/AuthenticationMiddleware.php:124
- /vendor/cakephp/cakephp/src/Http/Runner.php:82
- /vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php:176
- /vendor/cakephp/cakephp/src/Http/Runner.php:82
- /vendor/cakephp/cakephp/src/Http/Middleware/BodyParserMiddleware.php:157
- /vendor/cakephp/cakephp/src/Http/Runner.php:82
- /vendor/cakephp/cakephp/src/Routing/Middleware/RoutingMiddleware.php:189
- /vendor/cakephp/cakephp/src/Http/Runner.php:82
- /vendor/cakephp/cakephp/src/Routing/Middleware/AssetMiddleware.php:68
- /vendor/cakephp/cakephp/src/Http/Runner.php:82
- /vendor/cakephp/cakephp/src/Error/Middleware/ErrorHandlerMiddleware.php:149
- /vendor/cakephp/cakephp/src/Http/Runner.php:82
- /vendor/cakephp/debug_kit/src/Middleware/DebugKitMiddleware.php:60
- /vendor/cakephp/cakephp/src/Http/Runner.php:82
- /vendor/cakephp/cakephp/src/Http/Runner.php:67
- /vendor/cakephp/cakephp/src/Http/Server.php:99
- /webroot/index.php:40
- [main]:
Request URL: /api/get/orders
Referer URL: https://local.project.com/
Client IP: 127.0.0.1
/src/Model/Table/OrdersTable.php:370
'associated' => ['OrderItems.Products', 'OrderItems.OrderItemOptions']
Additional Context: An important observation is that if I comment out the line where order_item_options are assigned to $orderItemEntity, like so:
// $orderItemEntity->order_item_options = $orderItemOptions;
the Order and OrderItems get saved successfully without any errors. This leads me to believe that the issue specifically lies in how the OrderItemOptions are being handled or associated with OrderItem.
Any insights into why assigning order_item_options is causing the array_combine() error would be extremely helpful.