Issue #2364267 by yched: Clarify the logic in TypedDataManager::getPropertyInstance()
parent
b644bffdc5
commit
f503eb9c21
|
@ -8,7 +8,6 @@
|
||||||
namespace Drupal\Core\TypedData;
|
namespace Drupal\Core\TypedData;
|
||||||
|
|
||||||
use Drupal\Component\Plugin\Exception\PluginException;
|
use Drupal\Component\Plugin\Exception\PluginException;
|
||||||
use Drupal\Component\Utility\Crypt;
|
|
||||||
use Drupal\Component\Utility\String;
|
use Drupal\Component\Utility\String;
|
||||||
use Drupal\Core\Cache\CacheBackendInterface;
|
use Drupal\Core\Cache\CacheBackendInterface;
|
||||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||||
|
@ -248,26 +247,33 @@ class TypedDataManager extends DefaultPluginManager {
|
||||||
* @see \Drupal\Core\TypedData\TypedDataManager::create()
|
* @see \Drupal\Core\TypedData\TypedDataManager::create()
|
||||||
*/
|
*/
|
||||||
public function getPropertyInstance(TypedDataInterface $object, $property_name, $value = NULL) {
|
public function getPropertyInstance(TypedDataInterface $object, $property_name, $value = NULL) {
|
||||||
$definition = $object->getRoot()->getDataDefinition();
|
// For performance, try to reuse existing prototypes instead of
|
||||||
// If the definition is a list, we need to look at the data type and the
|
// constructing new objects when possible. A prototype is reused when
|
||||||
|
// creating a data object:
|
||||||
|
// - for a similar root object (same data type and settings),
|
||||||
|
// - at the same property path under that root object.
|
||||||
|
$root_definition = $object->getRoot()->getDataDefinition();
|
||||||
|
// If the root object is a list, we want to look at the data type and the
|
||||||
// settings of its item definition.
|
// settings of its item definition.
|
||||||
if ($definition instanceof ListDataDefinition) {
|
if ($root_definition instanceof ListDataDefinition) {
|
||||||
$definition = $definition->getItemDefinition();
|
$root_definition = $root_definition->getItemDefinition();
|
||||||
}
|
}
|
||||||
$key = $definition->getDataType();
|
|
||||||
if ($settings = $definition->getSettings()) {
|
|
||||||
$key .= ':' . Crypt::hashBase64(serialize($settings));
|
|
||||||
}
|
|
||||||
$key .= ':' . $object->getPropertyPath() . '.';
|
|
||||||
// If we are creating list items, we always use 0 in the key as all list
|
|
||||||
// items look the same.
|
|
||||||
$key .= is_numeric($property_name) ? 0 : $property_name;
|
|
||||||
|
|
||||||
// Make sure we have a prototype. Then, clone the prototype and set object
|
// Root data type and settings.
|
||||||
// specific values, i.e. the value and the context.
|
$parts[] = $root_definition->getDataType();
|
||||||
if (!isset($this->prototypes[$key]) || !$key) {
|
if ($settings = $root_definition->getSettings()) {
|
||||||
// Create the initial prototype. For that we need to fetch the definition
|
// Hash the settings into a string. crc32 is the the fastest way to hash
|
||||||
// of the to be created property instance from the parent.
|
// something for non-cryptographic purposes.
|
||||||
|
$parts[] = crc32(serialize($settings));
|
||||||
|
}
|
||||||
|
// Property path for the requested data object. When creating a list item,
|
||||||
|
// use 0 in the key as all items look the same.
|
||||||
|
$parts[] = $object->getPropertyPath() . '.' . (is_numeric($property_name) ? 0 : $property_name);
|
||||||
|
$key = implode(':', $parts);
|
||||||
|
|
||||||
|
// Create the prototype if needed.
|
||||||
|
if (!isset($this->prototypes[$key])) {
|
||||||
|
// Fetch the data definition for the child object from the parent.
|
||||||
if ($object instanceof ComplexDataInterface) {
|
if ($object instanceof ComplexDataInterface) {
|
||||||
$definition = $object->getDataDefinition()->getPropertyDefinition($property_name);
|
$definition = $object->getDataDefinition()->getPropertyDefinition($property_name);
|
||||||
}
|
}
|
||||||
|
@ -277,17 +283,16 @@ class TypedDataManager extends DefaultPluginManager {
|
||||||
else {
|
else {
|
||||||
throw new \InvalidArgumentException("The passed object has to either implement the ComplexDataInterface or the ListInterface.");
|
throw new \InvalidArgumentException("The passed object has to either implement the ComplexDataInterface or the ListInterface.");
|
||||||
}
|
}
|
||||||
// Make sure we have got a valid definition.
|
|
||||||
if (!$definition) {
|
if (!$definition) {
|
||||||
throw new \InvalidArgumentException('Property ' . String::checkPlain($property_name) . ' is unknown.');
|
throw new \InvalidArgumentException('Property ' . String::checkPlain($property_name) . ' is unknown.');
|
||||||
}
|
}
|
||||||
// Now create the prototype using the definition, but do not pass the
|
// Create the prototype without any value, but with initial parenting
|
||||||
// given value as it will serve as prototype for any further instance.
|
// so that constructors can set up the objects correclty.
|
||||||
$this->prototypes[$key] = $this->create($definition, NULL, $property_name, $object);
|
$this->prototypes[$key] = $this->create($definition, NULL, $property_name, $object);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone from the prototype, then update the parent relationship and set the
|
// Clone the prototype, update its parenting information, and assign the
|
||||||
// data value if necessary.
|
// value.
|
||||||
$property = clone $this->prototypes[$key];
|
$property = clone $this->prototypes[$key];
|
||||||
$property->setContext($property_name, $object);
|
$property->setContext($property_name, $object);
|
||||||
if (isset($value)) {
|
if (isset($value)) {
|
||||||
|
|
Loading…
Reference in New Issue