Skip to content

Commit

Permalink
Add support for custom storable property fields (#8)
Browse files Browse the repository at this point in the history
Add support for custom storable property fields
  • Loading branch information
mcaskill authored Nov 4, 2019
2 parents b31cf49 + ca39455 commit 49c9f95
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 90 deletions.
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"prefer-stable": true,
"extra": {
"branch-alias": {
"dev-master": "0.5.x-dev"
"dev-master": "0.6.x-dev"
}
},
"require": {
Expand All @@ -32,7 +32,7 @@
"locomotivemtl/charcoal-cache": "~0.1",
"locomotivemtl/charcoal-config": "~0.9",
"locomotivemtl/charcoal-factory": "~0.4",
"locomotivemtl/charcoal-property": "~0.9",
"locomotivemtl/charcoal-property": "~0.10",
"locomotivemtl/charcoal-view": "~0.3"
},
"require-dev": {
Expand Down
194 changes: 122 additions & 72 deletions src/Charcoal/Model/AbstractModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,27 @@ public function __construct(array $data = null)
}
}

/**
* Retrieve the model data as a structure (serialize to array).
*
* @param array $properties Optional. List of property identifiers
* for retrieving a subset of data.
* @return array
*/
public function data(array $properties = null)
{
$data = [];
$properties = $this->properties($properties);
foreach ($properties as $propertyIdent => $property) {
// Ensure objects are properly encoded.
$val = $this->propertyValue($propertyIdent);
$val = $this->serializedValue($val);
$data[$propertyIdent] = $val;
}

return $data;
}

/**
* Sets the object data, from an associative array map (or any other Traversable).
*
Expand All @@ -122,21 +143,20 @@ public function setData(array $data)
}

/**
* Retrieve the model data as a structure (serialize to array).
* Sets the object data, from an associative array map (or any other Traversable).
*
* @param array $properties Optional. List of property identifiers
* for retrieving a subset of data.
* @return array
* @param array $data The model property data.
* @return array Returns the remaining dataset.
*/
public function data(array $properties = null)
public function setPropertyData(array $data)
{
$data = [];
$properties = $this->properties($properties);
foreach ($properties as $propertyIdent => $property) {
// Ensure objects are properly encoded.
$val = $this->propertyValue($propertyIdent);
$val = $this->serializedValue($val);
$data[$propertyIdent] = $val;
$data = $this->setIdFromData($data);

foreach ($data as $key => $value) {
if ($this->hasProperty($key)) {
$this[$key] = $value;
unset($data[$key]);
}
}

return $data;
Expand Down Expand Up @@ -196,59 +216,76 @@ public function defaultData()
/**
* Set the model data (from a flattened structure).
*
* This method takes a 1-dimensional array and fills the object with its values.
*
* @param array $flatData The model data.
* @param array $flatData The model dataset.
* @return self
*/
public function setFlatData(array $flatData)
{
$flatData = $this->setPropertyDataFromFlatData($flatData);

// Set remaining (non-property) data.
if (!empty($flatData)) {
$this->setData($flatData);
}

return $this;
}

/**
* Set the model property data (from a flattened structure).
*
* This method takes a one-dimensional dataset and, depending on the property's
* {@see \Charcoal\Property\PropertyField::fieldNames() field structure},
* returns a {@see \Charcoal\Property\PropertyField::parseFromFlatData() complex datum}.
*
* @param array $flatData The model property data.
* @return array Returns the remaining dataset.
*/
public function setPropertyDataFromFlatData(array $flatData)
{
$flatData = $this->setIdFromData($flatData);

$data = [];
$propData = [];
$properties = $this->properties();
foreach ($properties as $propertyIdent => $property) {
$fields = $property->fields(null);
foreach ($fields as $k => $f) {
if (is_string($k)) {
$fid = $f->ident();
$snake = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $propertyIdent));
$key = str_replace($snake.'_', '', $fid);
if (isset($flatData[$fid])) {
$data[$propertyIdent][$key] = $flatData[$fid];
unset($flatData[$fid]);
}
} else {
$fid = $f->ident();
if (isset($flatData[$fid])) {
$data[$propertyIdent] = $flatData[$fid];
unset($flatData[$fid]);
}
$fieldValues = [];
$fieldNames = $property->fieldNames();
foreach ($fieldNames as $fieldName) {
if (array_key_exists($fieldName, $flatData)) {
$fieldValues[$fieldName] = $flatData[$fieldName];
unset($flatData[$fieldName]);
}
}
}

$this->setData($data);

// Set remaining (non-property) data.
if (!empty($flatData)) {
$this->setData($flatData);
if ($fieldValues) {
$this[$propertyIdent] = $property->parseFromFlatData($fieldValues);
}
}

return $this;
return $flatData;
}

/**
* Retrieve the model data as a flattened structure.
*
* This method returns a 1-dimensional array of the object's values.
*
* @todo Implementation required.
* @param array $properties Optional. List of property identifiers
* for retrieving a subset of data.
* @return array
*/
public function flatData()
public function flatData(array $properties = null)
{
return [];
$flatData = [];
$properties = $this->properties($properties);
foreach ($properties as $propertyIdent => $property) {
$value = $this->propertyValue($propertyIdent);
foreach ($property->fields($value) as $field) {
$flatData[$field->ident()] = $field->val();
}
}

return $flatData;
}

/**
Expand Down Expand Up @@ -295,16 +332,29 @@ public function saveProperties(array $properties = null)
* @param string $key Key pointing a column's l10n base ident.
* @param mixed $value Value to search in all languages.
* @param array $langs List of languages (code, ex: "en") to check into.
* @throws InvalidArgumentException If a language is invalid.
* @throws PDOException If the PDO query fails.
* @return string The matching language.
*/
public function loadFromL10n($key, $value, array $langs)
{
$binds = [
'ident' => $value,
];
$switch = [];
$where = [];
foreach ($langs as $lang) {
$switch[] = 'WHEN `'.$key.'_'.$lang.'` = :ident THEN \''.$lang.'\'';
$where[] = '`'.$key.'_'.$lang.'` = :ident';
if (!is_string($lang)) {
throw new InvalidArgumentException('Language; must be a string');
}

$fieldName = $key.'_'.$lang;
$langParam = 'lang_'.$lang;
$binds[$langParam] = $lang;
$langParam = ':'.$langParam;

$switch[] = 'WHEN `'.$fieldName.'` = :ident THEN '.$langParam;
$where[] = '`'.$fieldName.'` = :ident';
}

$source = $this->source();
Expand All @@ -329,40 +379,13 @@ public function loadFromL10n($key, $value, array $langs)
return $lang;
}

/**
* Generate a model type identifier from this object's class name.
*
* Based on {@see DescribableTrait::generateMetadataIdent()}.
*
* @return string
*/
public static function objType()
{
$class = get_called_class();
$ident = preg_replace('/([a-z])([A-Z])/', '$1-$2', $class);
$ident = strtolower(str_replace('\\', '/', $ident));
return $ident;
}

/**
* Inject dependencies from a DI Container.
*
* @param Container $container A Pimple DI service container.
* @return void
*/
protected function setDependencies(Container $container)
{
// This method is a stub.
// Reimplement in children method to inject dependencies in your class from a Pimple container.
}

/**
* Set the object's ID from an associative array map (or any other Traversable).
*
* Useful for setting the object ID before the rest of the object's data.
*
* @param array $data The object data.
* @return array The object data without the pre-set ID.
* @return array Returns the remaining dataset.
*/
protected function setIdFromData(array $data)
{
Expand Down Expand Up @@ -475,4 +498,31 @@ protected function createValidator()
$validator = new ModelValidator($this);
return $validator;
}

/**
* Inject dependencies from a DI Container.
*
* @param Container $container A Pimple DI service container.
* @return void
*/
protected function setDependencies(Container $container)
{
// This method is a stub.
// Reimplement in children method to inject dependencies in your class from a Pimple container.
}

/**
* Generate a model type identifier from this object's class name.
*
* Based on {@see DescribableTrait::generateMetadataIdent()}.
*
* @return string
*/
public static function objType()
{
$class = get_called_class();
$ident = preg_replace('/([a-z])([A-Z])/', '$1-$2', $class);
$ident = strtolower(str_replace('\\', '/', $ident));
return $ident;
}
}
1 change: 1 addition & 0 deletions src/Charcoal/Source/AbstractSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ public function addFilter($param, $value = null, array $options = null)
/**
* Process a query filter with the current model.
*
* @todo If property is L10N, turn filter into group of filters for each language.
* @param FilterInterface $filter The expression object.
* @return FilterInterface The parsed expression object.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Charcoal/Source/Database/DatabaseFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ protected function byFilters()
$filter = $filter->sql();
}

if (strlen($filter) > 0) {
if ($filter && strlen($filter) > 0) {
$conditions[] = $filter;
}
}
Expand Down
Loading

0 comments on commit 49c9f95

Please sign in to comment.