Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions core/components/minishop3/src/Model/msProductCreateProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace MiniShop3\Model;

/**
* Resolver target for MODX Resource\Create::getInstance() when class_key is msProduct.
*
* @see \MODX\Revolution\Processors\Resource\Create::getInstance()
*/
class msProductCreateProcessor extends \MiniShop3\Processors\Product\Create
{
}
12 changes: 12 additions & 0 deletions core/components/minishop3/src/Model/msProductUpdateProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace MiniShop3\Model;

/**
* Resolver target for MODX Resource\Update::getInstance() when class_key is msProduct.
*
* @see \MODX\Revolution\Processors\Resource\Update::getInstance()
*/
class msProductUpdateProcessor extends \MiniShop3\Processors\Product\Update
{
}
8 changes: 8 additions & 0 deletions core/components/minishop3/src/Processors/Product/Create.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

class Create extends CreateProcessor
{
use ProductDataPayloadTrait;

public $classKey = msProduct::class;
public $languageTopics = ['resource', 'minishop3:default'];
public $permission = 'msproduct_save';
Expand Down Expand Up @@ -57,6 +59,8 @@ public function beforeSet()
),
]);

$this->captureProductDataPayload();

$properties = $this->getProperties();
$options = [];
$hadOptionFieldsInRequest = false;
Expand Down Expand Up @@ -91,6 +95,7 @@ public function beforeSet()
public function beforeSave()
{
$this->object->set('isfolder', false);

return parent::beforeSave();
}

Expand All @@ -114,6 +119,9 @@ public function afterSave()

$result = parent::afterSave();

// msProductData needs resource id; composite may not persist payload fields on insert (#297).
$this->persistProductDataPayload();

// Same contract as Update::afterSave (#199): only sync when the request contained options-* keys (#257).
if ($this->ms3ProductFormOptions !== null) {
/** @var \MiniShop3\Model\msProductData $productData */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<?php

namespace MiniShop3\Processors\Product;

use MiniShop3\Model\msProduct;
use MiniShop3\Model\msProductData;

/**
* Applies msProductData fields from the `Data` block and flat request keys (#297).
*
* Resource Create/Update call `$object->fromArray($properties)` without ignoreInvalid,
* so msProductData columns must be applied explicitly. On Create the msProductData row
* needs a resource id — persist in afterSave(); on Update beforeSave() is enough.
*
* @property \MODX\Revolution\modX $modx
* @property msProduct $object
*/
trait ProductDataPayloadTrait
{
private const PRODUCT_DATA_PROPERTY = 'Data';

/** @var array<string, mixed>|null */
protected ?array $ms3ProductDataPayload = null;

protected function captureProductDataPayload(): void
{
$this->ms3ProductDataPayload = null;

$payload = $this->getProperty(self::PRODUCT_DATA_PROPERTY);
if ($payload === null || $payload === '') {
return;
}

$this->unsetProperty(self::PRODUCT_DATA_PROPERTY);

if (is_string($payload)) {
$decoded = json_decode($payload, true);
if (is_array($decoded)) {
$payload = $decoded;
} else {
return;
}
}

if (is_array($payload)) {
$this->ms3ProductDataPayload = $payload;
}
}

/**
* Assign msProductData fields on the in-memory composite (Update / pre-save).
*/
protected function applyProductDataPayload(): void
{
$this->assignProductDataPayload(false);
}

/**
* Assign and save msProductData after the resource id exists (Create).
*/
protected function persistProductDataPayload(): bool
{
return $this->assignProductDataPayload(true);
}

private function assignProductDataPayload(bool $persist): bool
{
if (!$this->object instanceof msProduct) {
return false;
}

if ($persist && (int) $this->object->get('id') <= 0) {
return false;
}

$this->ensureProductDataFieldMapLoaded();

$allowedFields = $this->getAllowedProductDataFieldNames();
$flatFields = $this->collectFlatProductDataFields($allowedFields);
$nestedFields = $this->collectNestedProductDataFields($allowedFields);

if ($flatFields === [] && $nestedFields === []) {
return false;
}

$productData = $this->object->loadData();

if ($persist) {
$productData->set('id', (int) $this->object->get('id'));
}

$this->assignProductDataFields($productData, $flatFields);
$this->assignProductDataFields($productData, $nestedFields);

return $persist ? (bool) $productData->save() : true;
}

private function ensureProductDataFieldMapLoaded(): void
{
if (!$this->modx->services->has('ms3')) {
return;
}

$this->modx->services->get('ms3')->loadMap();
}

/**
* @return array<string, true>
*/
private function getAllowedProductDataFieldNames(): array
{
$fields = array_flip($this->object->getDataFieldsNames());
unset($fields['id']);

return $fields;
}

/**
* @param array<string, true> $allowedFields
* @return array<string, mixed>
*/
private function collectFlatProductDataFields(array $allowedFields): array
{
$fields = [];

foreach ($this->getProperties() as $key => $value) {
if (!isset($allowedFields[$key])) {
continue;
}
$fields[$key] = $value;
}

return $fields;
}

/**
* @param array<string, true> $allowedFields
* @return array<string, mixed>
*/
private function collectNestedProductDataFields(array $allowedFields): array
{
if ($this->ms3ProductDataPayload === null) {
return [];
}

return array_intersect_key($this->ms3ProductDataPayload, $allowedFields);
}

/**
* @param array<string, mixed> $fields
*/
private function assignProductDataFields(msProductData $productData, array $fields): void
{
if ($fields === []) {
return;
}

$productData->fromArray($fields, '', true, true);
}
}
6 changes: 6 additions & 0 deletions core/components/minishop3/src/Processors/Product/Update.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

class Update extends UpdateProcessor
{
use ProductDataPayloadTrait;

public $classKey = msProduct::class;
public $languageTopics = ['resource', 'minishop3:default'];
public $permission = 'msproduct_save';
Expand Down Expand Up @@ -48,6 +50,9 @@ public static function getInstance(modX $modx, $className, $properties = [])
public function beforeSet()
{
$this->ms3ProductFormOptions = null;

$this->captureProductDataPayload();

$properties = $this->getProperties();
$options = [];
$hadOptionFieldsInRequest = false;
Expand Down Expand Up @@ -109,6 +114,7 @@ public function checkFriendlyAlias()
public function beforeSave()
{
$this->object->set('isfolder', false);
$this->applyProductDataPayload();

return parent::beforeSave();
}
Expand Down