Skip to content

Commit

Permalink
add Item::$cast
Browse files Browse the repository at this point in the history
  • Loading branch information
klimov-paul committed May 26, 2023
1 parent 08a9459 commit de6983b
Show file tree
Hide file tree
Showing 3 changed files with 238 additions and 28 deletions.
129 changes: 124 additions & 5 deletions src/Item.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,23 @@ class Item extends CModel
*/
public $id;
/**
* @var string label for the {@see value} attribute.
* @var string label for the {@see $value} attribute.
*/
public $label = 'Value';
/**
* @var mixed config parameter value.
*/
protected $_value;
private $_value;
/**
* @var mixed origin (before apply persistent storage) value of this item.
*/
private $_originValue;
/**
* @var array validation rules.
* Unlike the configuration for the common model, each rule should not contain attribute name
* as it already determined as {@see value}.
*/
protected $_rules = [];
private $_rules = [];
/**
* @var string|array application config path. Path is sequence of the config array keys.
* It could be either a string, where keys are separated by '.', or an array of keys.
Expand All @@ -50,20 +54,46 @@ class Item extends CModel
* array('params', 'myparam');
* 'components.securityManager.validationKey';
* array('components', 'securityManager', 'validationKey');
* If path is not set it will point to {@see \CApplication::$params} with the key equals ot {@see id}.
* If path is not set it will point to {@see \CApplication::$params} with the key equals ot {@see $id}.
*/
public $path;
/**
* @var string brief description for the config item.
*/
public $description;
/**
* @var string|null native type for the value to be cast to.
*/
public $cast;
/**
* @var array|null additional descriptive options for this item.
* This field may contain any data, which can be consumed by other part of the program.
* For example: it may hold options for the form input composition:
*
* ```php
* [
* 'inputType' => 'text',
* 'inputCssClass' => 'config-input',
* ]
* ```
*/
public $options;
/**
* @var object|null configuration source object.
* If not set current Yii application instance will be used.
*/
public $source;

/**
* @param mixed $value
* @return static self reference.
*/
public function setValue($value): self
{
if ($this->_originValue === null) {
$this->_originValue['value'] = $this->getValue();
}

$this->_value = $value;

return $this;
Expand All @@ -81,6 +111,56 @@ public function getValue()
return $this->_value;
}

/**
* Prepares value for the saving into persistent storage, performing typecast if necessary.
*
* @return mixed value to be saved in persistent storage.
*/
public function serializeValue()
{
$value = $this->getValue();

if ($this->cast === null) {
return $value;
}

if ($value === null || is_scalar($value)) {
return $value;
}

return json_encode($value);
}

/**
* Restores value from the raw one extracted from persistent storage, performing typecast if necessary.
*
* @param mixed $value value from persistent storage.
* @return mixed actual config value.
*/
public function unserializeValue($value)
{
$value = $this->castValue($value);

$this->setValue($value);

return $value;
}

/**
* Restores original (before apply persistent storage) value of this item.
*
* @return static self reference.
*/
public function resetValue(): self
{
if ($this->_originValue !== null) {
$this->setValue($this->_originValue['value']);
$this->_originValue = null;
}

return $this;
}

/**
* @param array $rules
* @return static self reference.
Expand Down Expand Up @@ -182,7 +262,7 @@ public function extractCurrentValue()
{
$pathParts = $this->getPathParts();

return $this->findConfigPathValue(Yii::app(), $pathParts);
return $this->findConfigPathValue($this->source ? $this->source : Yii::app(), $pathParts);
}

/**
Expand Down Expand Up @@ -264,4 +344,43 @@ protected function composeConfigPathValue(array $pathParts)

return $basis;
}

/**
* Typecasts raw value from persistent storage to the actual one according to {@see $cast} value.
*
* @param string $value value from persistent storage.
* @return mixed actual value after typecast.
*/
protected function castValue($value)
{
if ($this->cast === null) {
return $value;
}

if ($value === null) {
return $value;
}

switch ($this->cast) {
case 'int':
case 'integer':
return (int) $value;
case 'real':
case 'float':
case 'double':
return (float) $value;
case 'string':
return (string) $value;
case 'bool':
case 'boolean':
return (bool) $value;
case 'object':
return json_decode($value);
case 'array':
case 'json':
return json_decode($value, true);
default:
throw new LogicException('Unsupported "' . get_class($this) . '::$cast" value: ' . print_r($this->cast, true));
}
}
}
67 changes: 50 additions & 17 deletions src/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ public function setItemValues(array $itemValues)
{
foreach ($itemValues as $id => $value) {
$item = $this->getItem($id);
$item->value = $value;
$item->setValue($value);
}

return $this;
Expand All @@ -235,7 +235,7 @@ public function getItemValues(): array
{
$itemValues = [];
foreach ($this->getItems() as $item) {
$itemValues[$item->id] = $item->value;
$itemValues[$item->id] = $item->getValue();
}

return $itemValues;
Expand Down Expand Up @@ -265,39 +265,72 @@ public function composeConfig(): array

/**
* Saves the current config item values into the persistent storage.
* @return bool success.
* @return static self reference.
*/
public function saveValues(): bool
public function save(): self
{
$result = $this->getStorage()->save($this->getItemValues());
$itemValues = [];
foreach ($this->getItems() as $item) {
$itemValues[$item->id] = $item->serializeValue();
}

$result = $this->getStorage()->save($itemValues);
if ($result) {
$this->getCacheComponent()->delete($this->cacheId);
}

return $result;
return $this;
}

/**
* Restores config item values from the persistent storage.
* @return static self reference.
*/
public function restoreValues()
public function restore(): self
{
return $this->setItemValues($this->getStorage()->get());
$storedValues = $this->getStorage()->get();

foreach ($this->getItems() as $item) {
if (!array_key_exists($item->id, $storedValues)) {
continue;
}

$item->unserializeValue($storedValues[$item->id]);
}

return $this;
}

/**
* Clears config item values saved in the persistent storage.
* @return bool success.
* Clears config item values saved in the persistent storage, restoring original values of the {@see $items}.
* @return static self reference.
*/
public function clearValues(): bool
public function reset(): self
{
$result = $this->getStorage()->clear();
if ($result) {
$this->getCacheComponent()->delete($this->cacheId);
$this->getStorage()->clear();
$this->getCacheComponent()->delete($this->cacheId);

foreach ($this->getItems() as $item) {
$item->resetValue();
}

return $result;
return $this;
}

/**
* Clear value, saved in persistent storage, for the specified item, restoring its original value.
*
* @param string $key the key of the item to be cleared.
* @return static self reference.
*/
public function resetValue($key): self
{
$this->storage->clearValue($key);
$this->getCacheComponent()->delete($this->cacheId);

$this->getItem($key)->resetValue();

return $this;
}

/**
Expand All @@ -306,12 +339,12 @@ public function clearValues(): bool
* This method caches its result for the better performance.
* @return array application configuration.
*/
public function fetchConfig()
public function fetchConfig(): array
{
$cache = $this->getCacheComponent();
$config = $cache->get($this->cacheId);
if ($config === false) {
$this->restoreValues();
$this->restore();
$config = $this->composeConfig();
$cache->set($this->cacheId, $config, $this->cacheDuration);
}
Expand Down
Loading

0 comments on commit de6983b

Please sign in to comment.