xero-8.x-2.x-dev/src/Plugin/DataType/XeroItemBase.php
src/Plugin/DataType/XeroItemBase.php
<?php
namespace Drupal\xero\Plugin\DataType;
use Drupal\Component\Utility\Html;
use Drupal\Core\TypedData\Plugin\DataType\Map;
use Drupal\xero\TypedData\XeroItemInterface;
/**
* Provides a base class for Xero data.
*/
abstract class XeroItemBase extends Map implements XeroItemInterface {
/**
* The Xero property name e.g. Invoice.
*
* @var string
*/
public static $xero_name;
/**
* The plural name of the Xero type.
*
* @var string
*/
public static $plural_name;
/**
* The label property for the Xero type.
*
* @var string
*/
public static $label;
/**
* Determines if the data type has been changed or not.
*
* @var bool
*/
protected $pristine = TRUE;
/**
* An array of names of properties that have been explicitly specified.
*
* @var string[]
*/
protected $specifiedPropertyNames = [];
/**
* {@inheritdoc}
*/
public static function getXeroProperty($name) {
$allowed_props = ['plural_name', 'xero_name', 'label'];
if (!in_array($name, $allowed_props)) {
throw new \InvalidArgumentException('Invalid xero property.');
}
return static::${$name};
}
/**
* {@inheritdoc}
*/
public function view(): array {
$className = substr($this->getName(), 5);
$item = [
'#type' => 'container',
'#attributes' => [
'class' => ['xero-item', 'xero-item--' . $className],
],
];
/** @var \Drupal\Core\TypedData\ComplexDataDefinitionInterface $definition */
foreach ($this->getDataDefinition()->getPropertyDefinitions() as $name => $definition) {
$item[$name] = [
'label' => [
'#type' => 'label',
'#title' => $definition->getLabel(),
],
'value' => [
'#markup' => isset($this->values[$name]) ? Html::escape($this->values[$name]) : '',
],
];
}
return $item;
}
/**
* {@inheritdoc}
*/
public function onChange($name, $notify = TRUE) {
$item = $this->get($name);
if ($item instanceof XeroItemInterface &&
$item->isPristine() &&
!$item->getDataDefinition()->isRequired()) {
// Removes the property name from the specified property names if it is
// pristine.
$this->specifiedPropertyNames = array_filter($this->specifiedPropertyNames, function ($propName) use ($name) {
return $propName !== $name;
});
if (empty($this->specifiedPropertyNames)) {
// Sets the current data type to pristine if there are no more specified
// property names.
$this->pristine = TRUE;
}
}
else {
$this->recordSpecifiedProperty($name);
}
parent::onChange($name, $notify);
}
/**
* {@inheritdoc}
*/
public function getValue() {
// Update the values and return them, but only include specified properties.
foreach ($this->getSpecifiedProperties() as $name => $property) {
$definition = $property->getDataDefinition();
if (!$definition->isComputed()) {
$value = $property->getValue();
// Only write NULL values if the whole map is not NULL.
if (isset($this->values) || isset($value)) {
$this->values[$name] = $value;
}
}
}
return $this->values;
}
/**
* {@inheritdoc}
*/
public function setValue($values, $notify = TRUE) {
parent::setValue($values, $notify);
// The parent method enforces that $values is an array if it is set.
if (isset($values)) {
foreach ($values as $name => $property) {
$this->recordSpecifiedProperty($name);
}
}
$this->pristine = FALSE;
}
/**
* Track which properties on the typed data object have been explicitly set.
*
* @param string $name
* The property name.
*/
protected function recordSpecifiedProperty($name) {
if (!in_array($name, $this->specifiedPropertyNames)) {
$this->specifiedPropertyNames[] = $name;
}
}
/**
* {@inheritdoc}
*/
public function getSpecifiedProperties() {
$properties = $this->getProperties(FALSE);
$specifiedProperties = array_intersect_key($properties, array_flip($this->specifiedPropertyNames));
$writableSpecifiedProperties = array_filter($specifiedProperties,
fn ($prop) => !$prop->getDataDefinition()->isReadOnly(),
ARRAY_FILTER_USE_BOTH
);
return $writableSpecifiedProperties;
}
/**
* {@inheritdoc}
*/
public function isPristine() {
return $this->pristine;
}
/**
* {@inheritdoc}
*/
public function markAsPristine($notify = TRUE) {
foreach ($this->getSpecifiedProperties() as $name => $property) {
if ($property->getDataDefinition()->isRequired()) {
continue;
}
if ($property instanceof XeroItemInterface) {
$property->markAsPristine(FALSE);
}
elseif (is_a($property, '\Drupal\xero\Plugin\DataType\XeroItemList')) {
foreach ($property as $index => $child) {
$child->markAsPristine(FALSE);
}
}
$this->specifiedPropertyNames = array_diff($this->specifiedPropertyNames, [$name]);
}
$this->pristine = TRUE;
if ($notify && $this->parent !== NULL) {
$this->parent->onChange($this->name);
}
}
}
