Skip to content

Resource fields mapped to types #110

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 3.next
Choose a base branch
from
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
86 changes: 84 additions & 2 deletions src/Datasource/Marshaller.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@

use ArrayObject;
use Cake\Collection\Collection;
use Cake\Core\Configure;
use Cake\Database\TypeFactory;
use Cake\Datasource\EntityInterface;
use Cake\Datasource\InvalidPropertyInterface;
use Cake\Error\Debugger;
use Cake\Http\Exception\InternalErrorException;
use Cake\ORM\PropertyMarshalInterface;
use Cake\Utility\Inflector;
use Muffin\Webservice\Model\Endpoint;
use RuntimeException;

Expand Down Expand Up @@ -34,6 +40,33 @@ public function __construct(Endpoint $endpoint)
$this->_endpoint = $endpoint;
}

/**
* Build the map of property => marshalling callable.
*
* @param array $data The data being marshalled.
* @param array<string, mixed> $options List of options containing the 'associated' key.
* @throws \InvalidArgumentException When associations do not exist.
* @return array
*/
protected function _buildPropertyMap(array $data, array $options): array
{
$map = [];
$schema = $this->_endpoint->getSchema();

// Is a concrete column?
foreach (array_keys($data) as $prop) {
$prop = (string)$prop;
$columnType = $schema->getColumnType($prop);
if ($columnType) {
$map[$prop] = function ($value, $entity) use ($columnType) {
return TypeFactory::build($columnType)->marshal($value);
};
}
}

return $map;
}

/**
* Hydrate one entity.
*
Expand Down Expand Up @@ -65,6 +98,7 @@ public function one(array $data, array $options = []): EntityInterface
}

$errors = $this->_validate($data, $options, true);
$propertyMap = $this->_buildPropertyMap($data, $options);
$properties = [];
foreach ($data as $key => $value) {
if (!empty($errors[$key])) {
Expand All @@ -75,7 +109,11 @@ public function one(array $data, array $options = []): EntityInterface
// Skip marshalling '' for pk fields.
continue;
}
$properties[$key] = $value;
if (isset($propertyMap[$key])) {
$properties[$key] = $propertyMap[$key]($value, $entity);
} else {
$properties[$key] = $value;
}
}

if (!isset($options['fieldList'])) {
Expand All @@ -92,6 +130,7 @@ public function one(array $data, array $options = []): EntityInterface
}

$entity->setErrors($errors);
$this->dispatchAfterMarshal($entity, $data, $options);

return $entity;
}
Expand Down Expand Up @@ -216,6 +255,7 @@ public function merge(EntityInterface $entity, array $data, array $options = [])
}

$errors = $this->_validate($data + $keys, $options, $isNew);
$propertyMap = $this->_buildPropertyMap($data, $options);
$properties = [];
foreach ($data as $key => $value) {
if (!empty($errors[$key])) {
Expand All @@ -224,7 +264,33 @@ public function merge(EntityInterface $entity, array $data, array $options = [])
}
continue;
}

$original = $entity->get($key);

if (isset($propertyMap[$key])) {
$value = $propertyMap[$key]($value, $entity);

// Don't dirty scalar values and objects that didn't
// change. Arrays will always be marked as dirty because
// the original/updated list could contain references to the
// same objects, even though those objects may have changed internally.
if (
(
is_scalar($value)
&& $original === $value
)
|| (
$value === null
&& $original === $value
)
|| (
is_object($value)
&& !($value instanceof EntityInterface)
&& $original == $value
)
) {
continue;
}
}
$properties[$key] = $value;
}

Expand All @@ -242,6 +308,7 @@ public function merge(EntityInterface $entity, array $data, array $options = [])
}

$entity->setErrors($errors);
$this->dispatchAfterMarshal($entity, $data, $options);

return $entity;
}
Expand Down Expand Up @@ -313,4 +380,19 @@ public function mergeMany($entities, array $data, array $options = []): array

return $output;
}

/**
* dispatch Model.afterMarshal event.
*
* @param \Cake\Datasource\EntityInterface $entity The entity that was marshaled.
* @param array $data readOnly $data to use.
* @param array<string, mixed> $options List of options that are readOnly.
* @return void
*/
protected function dispatchAfterMarshal(EntityInterface $entity, array $data, array $options = []): void
{
$data = new ArrayObject($data);
$options = new ArrayObject($options);
$this->_endpoint->dispatchEvent('Model.afterMarshal', compact('entity', 'data', 'options'));
}
}
10 changes: 4 additions & 6 deletions src/Webservice/Webservice.php
Original file line number Diff line number Diff line change
Expand Up @@ -356,13 +356,11 @@ protected function _transformResults(Endpoint $endpoint, array $results): array
*/
protected function _transformResource(Endpoint $endpoint, array $result): Resource
{
$properties = [];
$entity = $endpoint->newEntity($result);
$entity->setNew(false);
$entity->clean();

foreach ($result as $property => $value) {
$properties[$property] = $value;
}

return $this->_createResource($endpoint->getResourceClass(), $properties);
return $entity;
}

/**
Expand Down
Loading