/** * Get a typed data instance for a property of a given typed data object. * * This method will use prototyping for fast and efficient instantiation of * many property objects with the same property path; e.g., * when multiple comments are used comment_body.0.value needs to be * instantiated very often. * Prototyping is done by the root object's data type and the given * property path, i.e. all property instances having the same property path * and inheriting from the same data type are prototyped. * * @param \Drupal\Core\TypedData\TypedDataInterface $object * The parent typed data object, implementing the TypedDataInterface and * either the ListInterface or the ComplexDataInterface. * @param string $property_name * The name of the property to instantiate, or the delta of an list item. * @param mixed $value * (optional) The data value. If set, it has to match one of the supported * data type formats as documented by the data type classes. * * @throws \InvalidArgumentException * If the given property is not known, or the passed object does not * implement the ListInterface or the ComplexDataInterface. * * @return \Drupal\Core\TypedData\TypedDataInterface * The new property instance. * * @see \Drupal\Core\TypedData\TypedDataManager::create() */ public function getPropertyInstance(TypedDataInterface $object, $property_name, $value = NULL) { $definition = $object->getRoot()->getDataDefinition(); // If the definition is a list, we need to look at the data type and the // settings of its item definition. if ($definition instanceof ListDataDefinition) { $definition = $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 // specific values, i.e. the value and the context. if (!isset($this->prototypes[$key]) || !$key) { // Create the initial prototype. For that we need to fetch the definition // of the to be created property instance from the parent. if ($object instanceof ComplexDataInterface) { $definition = $object->getDataDefinition()->getPropertyDefinition($property_name); } elseif ($object instanceof ListInterface) { $definition = $object->getItemDefinition(); } else { 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) { throw new \InvalidArgumentException('Property ' . String::checkPlain($property_name) . ' is unknown.'); } // Now create the prototype using the definition, but do not pass the // given value as it will serve as prototype for any further instance. $this->prototypes[$key] = $this->create($definition, NULL, $property_name, $object); } // Clone from the prototype, then update the parent relationship and set the // data value if necessary. $property = clone $this->prototypes[$key]; $property->setContext($property_name, $object); if (isset($value)) { $property->setValue($value, FALSE); } return $property; }
/** * {@inheritdoc} */ public function getPropertyInstance(TypedDataInterface $object, $property_name, $value = NULL) { // For performance, try to reuse existing prototypes instead of // 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. if ($root_definition instanceof ListDataDefinition) { $root_definition = $root_definition->getItemDefinition(); } // Root data type and settings. $parts[] = $root_definition->getDataType(); if ($settings = $root_definition->getSettings()) { // Hash the settings into a string. crc32 is the fastest way to hash // something for non-cryptographic purposes. $parts[] = hash('crc32b', 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) { $definition = $object->getDataDefinition()->getPropertyDefinition($property_name); } elseif ($object instanceof ListInterface) { $definition = $object->getItemDefinition(); } else { throw new \InvalidArgumentException("The passed object has to either implement the ComplexDataInterface or the ListInterface."); } if (!$definition) { throw new \InvalidArgumentException("Property {$property_name} is unknown."); } // Create the prototype without any value, but with initial parenting // so that constructors can set up the objects correclty. $this->prototypes[$key] = $this->create($definition, NULL, $property_name, $object); } // Clone the prototype, update its parenting information, and assign the // value. $property = clone $this->prototypes[$key]; $property->setContext($property_name, $object); if (isset($value)) { $property->setValue($value, FALSE); } return $property; }