class EntityReferenceHierarchy in Entity Reference Hierarchy 3.x
Same name and namespace in other branches
- 8.2 src/Plugin/Field/FieldType/EntityReferenceHierarchy.php \Drupal\entity_hierarchy\Plugin\Field\FieldType\EntityReferenceHierarchy
Plugin implementation of the 'entity_reference_hierarchy' field type.
Plugin annotation
@FieldType(
id = "entity_reference_hierarchy",
label = @Translation("Entity reference hierarchy"),
description = @Translation("Entity parent reference with weight."),
category = @Translation("Reference"),
default_widget = "entity_reference_hierarchy_autocomplete",
default_formatter = "entity_reference_hierarchy_label",
cardinality = 1,
list_class = "\Drupal\entity_hierarchy\Plugin\Field\FieldType\EntityReferenceHierarchyFieldItemList"
)
Hierarchy
- class \Drupal\Core\TypedData\TypedData implements PluginInspectionInterface, TypedDataInterface uses DependencySerializationTrait, StringTranslationTrait, TypedDataTrait
- class \Drupal\Core\TypedData\Plugin\DataType\Map implements \Drupal\Core\TypedData\Plugin\DataType\IteratorAggregate, ComplexDataInterface
- class \Drupal\Core\Field\FieldItemBase implements FieldItemInterface
- class \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem implements PreconfiguredFieldUiOptionsInterface, OptionsProviderInterface
- class \Drupal\entity_hierarchy\Plugin\Field\FieldType\EntityReferenceHierarchy uses TreeLockTrait
- class \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem implements PreconfiguredFieldUiOptionsInterface, OptionsProviderInterface
- class \Drupal\Core\Field\FieldItemBase implements FieldItemInterface
- class \Drupal\Core\TypedData\Plugin\DataType\Map implements \Drupal\Core\TypedData\Plugin\DataType\IteratorAggregate, ComplexDataInterface
Expanded class hierarchy of EntityReferenceHierarchy
File
- src/
Plugin/ Field/ FieldType/ EntityReferenceHierarchy.php, line 30
Namespace
Drupal\entity_hierarchy\Plugin\Field\FieldTypeView source
class EntityReferenceHierarchy extends EntityReferenceItem {
use TreeLockTrait;
/**
* Defines the minimum weight of a child (but has the highest priority).
*/
const HIERARCHY_MIN_CHILD_WEIGHT = -50;
/**
* Defines the maximum weight of a child (but has the lowest priority).
*/
const HIERARCHY_MAX_CHILD_WEIGHT = 50;
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties = parent::propertyDefinitions($field_definition);
$weight_definition = DataDefinition::create('integer')
->setLabel($field_definition
->getSetting('weight_label'));
$weight_definition
->addConstraint('Range', [
'min' => self::HIERARCHY_MIN_CHILD_WEIGHT,
]);
$weight_definition
->addConstraint('Range', [
'max' => self::HIERARCHY_MAX_CHILD_WEIGHT,
]);
$properties['weight'] = $weight_definition;
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
$schema = parent::schema($field_definition);
$schema['columns']['weight'] = [
'type' => 'int',
'unsigned' => FALSE,
];
// Add weight index.
$schema['indexes']['weight'] = [
'weight',
];
return $schema;
}
/**
* {@inheritdoc}
*/
public static function defaultFieldSettings() {
return [
'weight_label' => t('Weight'),
'weight_min' => self::HIERARCHY_MIN_CHILD_WEIGHT,
'weight_max' => self::HIERARCHY_MAX_CHILD_WEIGHT,
] + parent::defaultFieldSettings();
}
/**
* {@inheritdoc}
*/
public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
$elements = parent::fieldSettingsForm($form, $form_state);
$elements['weight_min'] = [
'#type' => 'number',
'#title' => t('Minimum'),
'#default_value' => $this
->getSetting('weight_min'),
];
$elements['weight_max'] = [
'#type' => 'number',
'#title' => t('Maximum'),
'#default_value' => $this
->getSetting('weight_max'),
];
$elements['weight_label'] = [
'#type' => 'textfield',
'#title' => t('Weight Label'),
'#default_value' => $this
->getSetting('weight_label'),
'#description' => t('The weight of this child with respect to other children.'),
];
return $elements;
}
/**
* {@inheritdoc}
*/
public static function getPreconfiguredOptions() {
// In the base EntityReference class, this is used to populate the
// list of field-types with options for each destination entity type.
// Too much work, we'll just make people fill that out later.
// Also, keeps the field type dropdown from getting too cluttered.
return [];
}
/**
* {@inheritdoc}
*/
public function postSave($update) {
if (\Drupal::state()
->get('entity_hierarchy_disable_writes', FALSE)) {
return;
}
// Get the key factory and tree storage services.
$nodeKeyFactory = $this
->getNodeKeyFactory();
$storage = $this
->getTreeStorage();
// Get the field name.
$fieldDefinition = $this
->getFieldDefinition();
$fieldName = $fieldDefinition
->getName();
$entityTypeId = $fieldDefinition
->getTargetEntityTypeId();
$this
->lockTree($fieldName, $entityTypeId);
// Get the parent/child entities and their node-keys in the nested set.
$parentEntity = $this
->get('entity')
->getValue();
if (!$parentEntity) {
// Parent entity has been deleted.
// If this node was in the tree, it needs to be moved to a root node.
$stubNode = $nodeKeyFactory
->fromEntity($this
->getEntity());
if (($existingNode = $storage
->getNode($stubNode)) && $existingNode
->getDepth() > 0) {
$storage
->moveSubTreeToRoot($existingNode);
}
$this
->releaseLock($fieldName, $entityTypeId);
return;
}
$parentKey = $nodeKeyFactory
->fromEntity($parentEntity);
$childEntity = $this
->getEntity();
$childKey = $nodeKeyFactory
->fromEntity($childEntity);
// Determine if this is a new node in the tree.
$isNewNode = FALSE;
if (!($childNode = $storage
->getNode($childKey))) {
$isNewNode = TRUE;
// As we're going to be adding instead of moving, a key is all we require.
$childNode = $childKey;
}
// Does the parent already exist in the tree.
if ($existingParent = $storage
->getNode($parentKey)) {
// If there are no siblings, we simply insert/move below.
$insertPosition = new InsertPosition($existingParent, $isNewNode, InsertPosition::DIRECTION_BELOW);
// But if there are siblings, we need to ascertain the correct position in
// the order.
if ($siblingEntities = $this
->getSiblingEntityWeights($storage, $existingParent, $childNode)) {
// Group the siblings by their weight.
$weightOrderedSiblings = $this
->groupSiblingsByWeight($siblingEntities, $fieldName);
$weight = $this
->get('weight')
->getValue();
$insertPosition = $this
->getInsertPosition($weightOrderedSiblings, $weight, $isNewNode) ?: $insertPosition;
}
$insertPosition
->performInsert($storage, $childNode);
$this
->releaseLock($fieldName, $entityTypeId);
return;
}
// We need to create a node for the parent in the tree.
$parentNode = $storage
->addRootNode($parentKey);
(new InsertPosition($parentNode, $isNewNode, InsertPosition::DIRECTION_BELOW))
->performInsert($storage, $childNode);
$this
->releaseLock($fieldName, $entityTypeId);
}
/**
* Returns the storage handler for the given entity-type.
*
* @return \Drupal\Core\Entity\EntityStorageInterface
* Storage handler.
*/
protected function entityTypeStorage($entity_type_id) {
return \Drupal::entityTypeManager()
->getStorage($entity_type_id);
}
/**
* Returns the tree storage.
*
* @return \Drupal\entity_hierarchy\Storage\NestedSetStorage
* Tree storage.
*/
protected function getTreeStorage() {
$fieldDefinition = $this
->getFieldDefinition();
return \Drupal::service('entity_hierarchy.nested_set_storage_factory')
->get($fieldDefinition
->getName(), $fieldDefinition
->getTargetEntityTypeId());
}
/**
* Returns the node factory.
*
* @return \Drupal\entity_hierarchy\Storage\NestedSetNodeKeyFactory
* The factory.
*/
protected function getNodeKeyFactory() {
return \Drupal::service('entity_hierarchy.nested_set_node_factory');
}
/**
* Gets the entity type definition.
*
* @return \Drupal\Core\Entity\EntityTypeInterface
* Entity type.
*/
protected function entityTypeDefinition() {
return \Drupal::entityTypeManager()
->getDefinition($this
->getFieldDefinition()
->getTargetEntityTypeId());
}
/**
* Loads other children of the given parent.
*
* @param \PNX\NestedSet\Node[] $siblings
* Target siblings.
*
* @return \SplObjectStorage
* Map of weights keyed by node.
*/
protected function loadSiblingEntityWeights(array $siblings) {
$fieldDefinition = $this
->getFieldDefinition();
$entityType = $this
->entityTypeDefinition();
$entityTypeId = $fieldDefinition
->getTargetEntityTypeId();
$entityStorage = $this
->entityTypeStorage($entityTypeId);
$siblingEntities = new \SplObjectStorage();
$key = $entityType
->hasKey('revision') ? $entityType
->getKey('revision') : $entityType
->getKey('id');
$parentField = $fieldDefinition
->getName();
$query = $entityStorage
->getAggregateQuery();
$ids = array_map(function (Node $item) {
return $item
->getRevisionId();
}, $siblings);
$entities = $query
->groupBy($key)
->sort($key, 'ASC')
->groupBy($parentField . '.weight')
->condition($key, $ids, 'IN')
->execute();
$weightSeparator = $fieldDefinition instanceof BaseFieldDefinition ? '__' : '_';
$entities = array_combine(array_column($entities, $key), array_column($entities, $parentField . $weightSeparator . 'weight'));
foreach ($siblings as $node) {
if (!isset($entities[$node
->getRevisionId()])) {
continue;
}
$siblingEntities[$node] = (int) $entities[$node
->getRevisionId()];
}
return $siblingEntities;
}
/**
* Gets siblings.
*
* @param \Drupal\entity_hierarchy\Storage\NestedSetStorage $storage
* Storage.
* @param \PNX\NestedSet\Node $parentNode
* Existing parent node.
* @param \PNX\NestedSet\Node|\PNX\NestedSet\NodeKey $childNode
* Child node.
*
* @return \SplObjectStorage|bool
* Map of weights keyed by node or FALSE if no siblings.
*/
protected function getSiblingEntityWeights(NestedSetStorage $storage, Node $parentNode, $childNode) {
if ($siblingNodes = array_filter($storage
->findChildren($parentNode
->getNodeKey()), function (Node $node) use ($childNode) {
if ($childNode instanceof NodeKey) {
// Exclude self and all revisions.
return $childNode
->getId() !== $node
->getNodeKey()
->getId();
}
// Exclude self and all revisions.
return $childNode
->getNodeKey()
->getId() !== $node
->getNodeKey()
->getId();
})) {
return $this
->loadSiblingEntityWeights($siblingNodes);
}
return FALSE;
}
/**
* Group siblings by weight.
*
* @param \SplObjectStorage $siblingEntities
* Sibling entities keyed by nested set nodes.
* @param string $fieldName
* Field name to detect weight from.
*
* @return array
* Array of nested set nodes grouped by weight.
*/
public function groupSiblingsByWeight(\SplObjectStorage $siblingEntities, $fieldName) {
$weightMap = [];
foreach ($siblingEntities as $node) {
if (!$siblingEntities
->offsetExists($node)) {
continue;
}
$weightMap[$siblingEntities
->offsetGet($node)][] = $node;
}
ksort($weightMap);
return $weightMap;
}
/**
* Gets the insert position for the new child.
*
* @param array $weightOrderedSiblings
* Sibling nodes, grouped by weight.
* @param int $weight
* Desired weight amongst siblings of the new child.
* @param bool $isNewNode
* TRUE if the node is brand new, FALSE if it needs to be moved from
* elsewhere in the tree.
*
* @return \Drupal\entity_hierarchy\Storage\InsertPosition|bool
* Insert position, FALSE if the siblings no longer exist.
*/
public function getInsertPosition(array $weightOrderedSiblings, $weight, $isNewNode) {
if (isset($weightOrderedSiblings[$weight])) {
// There are already nodes at the same weight, insert it with them.
return new InsertPosition(end($weightOrderedSiblings[$weight]), $isNewNode, InsertPosition::DIRECTION_AFTER);
}
// There are no nodes at this weight, we need to find the right position.
$firstGroup = reset($weightOrderedSiblings);
$start = key($weightOrderedSiblings);
if ($weight < $start) {
// We're going to position before all existing nodes.
return new InsertPosition(reset($firstGroup), $isNewNode);
}
foreach (array_keys($weightOrderedSiblings) as $weightPosition) {
if ($weight < $weightPosition) {
return new InsertPosition(reset($weightOrderedSiblings[$weightPosition]), $isNewNode);
}
}
// We're inserting at the end.
$lastGroup = end($weightOrderedSiblings);
if (!$lastGroup) {
return FALSE;
}
return new InsertPosition(end($lastGroup), $isNewNode, InsertPosition::DIRECTION_AFTER);
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
DependencySerializationTrait:: |
protected | property | ||
DependencySerializationTrait:: |
protected | property | ||
DependencySerializationTrait:: |
public | function | 2 | |
DependencySerializationTrait:: |
public | function | 2 | |
EntityReferenceHierarchy:: |
public static | function |
Defines the field-level settings for this plugin. Overrides EntityReferenceItem:: |
|
EntityReferenceHierarchy:: |
protected | function | Gets the entity type definition. | |
EntityReferenceHierarchy:: |
protected | function | Returns the storage handler for the given entity-type. | |
EntityReferenceHierarchy:: |
public | function |
Returns a form for the field-level settings. Overrides EntityReferenceItem:: |
|
EntityReferenceHierarchy:: |
public | function | Gets the insert position for the new child. | |
EntityReferenceHierarchy:: |
protected | function | Returns the node factory. | |
EntityReferenceHierarchy:: |
public static | function |
Returns preconfigured field options for a field type. Overrides EntityReferenceItem:: |
|
EntityReferenceHierarchy:: |
protected | function | Gets siblings. | |
EntityReferenceHierarchy:: |
protected | function | Returns the tree storage. | |
EntityReferenceHierarchy:: |
public | function | Group siblings by weight. | |
EntityReferenceHierarchy:: |
constant | Defines the maximum weight of a child (but has the lowest priority). | ||
EntityReferenceHierarchy:: |
constant | Defines the minimum weight of a child (but has the highest priority). | ||
EntityReferenceHierarchy:: |
protected | function | Loads other children of the given parent. | |
EntityReferenceHierarchy:: |
public | function |
Defines custom post-save behavior for field values. Overrides FieldItemBase:: |
|
EntityReferenceHierarchy:: |
public static | function |
Defines field item properties. Overrides EntityReferenceItem:: |
|
EntityReferenceHierarchy:: |
public static | function |
Returns the schema for the field. Overrides EntityReferenceItem:: |
|
EntityReferenceItem:: |
public static | function |
Calculates dependencies for field items. Overrides FieldItemBase:: |
|
EntityReferenceItem:: |
public static | function |
Calculates dependencies for field items on the storage level. Overrides FieldItemBase:: |
|
EntityReferenceItem:: |
public static | function |
Defines the storage-level settings for this plugin. Overrides FieldItemBase:: |
1 |
EntityReferenceItem:: |
public static | function | Render API callback: Processes the field settings form. | |
EntityReferenceItem:: |
public static | function | Adds entity_reference specific properties to AJAX form elements from the field settings form. | |
EntityReferenceItem:: |
public static | function | Form element validation handler; Invokes selection plugin's validation. | |
EntityReferenceItem:: |
public static | function | Render API callback: Moves entity_reference specific Form API elements (i.e. 'handler_settings') up a level for easier processing by the validation and submission handlers. | |
EntityReferenceItem:: |
public static | function |
Generates placeholder field values. Overrides FieldItemBase:: |
1 |
EntityReferenceItem:: |
public | function |
Gets a list of validation constraints. Overrides TypedData:: |
|
EntityReferenceItem:: |
public | function |
Returns an array of possible values with labels for display. Overrides OptionsProviderInterface:: |
|
EntityReferenceItem:: |
public | function |
Returns an array of possible values. Overrides OptionsProviderInterface:: |
|
EntityReferenceItem:: |
protected static | function | Gets a bundle for a given entity type and selection options. | |
EntityReferenceItem:: |
public | function |
Returns an array of settable values with labels for display. Overrides OptionsProviderInterface:: |
|
EntityReferenceItem:: |
public | function |
Returns an array of settable values. Overrides OptionsProviderInterface:: |
|
EntityReferenceItem:: |
public | function |
Gets the data value. Overrides Map:: |
|
EntityReferenceItem:: |
public | function | Determines whether the item holds an unsaved entity. | |
EntityReferenceItem:: |
public | function |
Determines whether the data structure is empty. Overrides Map:: |
|
EntityReferenceItem:: |
public static | function |
Returns the name of the main property, if any. Overrides FieldItemBase:: |
|
EntityReferenceItem:: |
public | function |
React to changes to a child property or item. Overrides Map:: |
|
EntityReferenceItem:: |
public static | function |
Informs the plugin that a dependency of the field will be deleted. Overrides FieldItemBase:: |
|
EntityReferenceItem:: |
public | function |
Defines custom presave behavior for field values. Overrides FieldItemBase:: |
1 |
EntityReferenceItem:: |
public static | function | Ajax callback for the handler settings form. | |
EntityReferenceItem:: |
public static | function | Submit handler for the non-JS case. | |
EntityReferenceItem:: |
public | function |
Sets the data value. Overrides FieldItemBase:: |
|
EntityReferenceItem:: |
public | function |
Returns a form for the storage-level settings. Overrides FieldItemBase:: |
1 |
FieldItemBase:: |
public | function |
Defines custom delete behavior for field values. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public | function |
Defines custom revision delete behavior for field values. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
public static | function |
Returns a settings array in the field type's canonical representation. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public static | function |
Returns a settings array that can be stored as a configuration value. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public | function |
Gets the entity that field belongs to. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
public | function |
Gets the field definition. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
public | function |
Gets the langcode of the field values held in the object. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
protected | function | Returns the value of a field setting. | |
FieldItemBase:: |
protected | function | Returns the array of field settings. | |
FieldItemBase:: |
public static | function |
Returns a settings array in the field type's canonical representation. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public static | function |
Returns a settings array that can be stored as a configuration value. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public | function |
Returns a renderable array for a single field item. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
protected | function |
Different to the parent Map class, we avoid creating property objects as
far as possible in order to optimize performance. Thus we just update
$this->values if no property object has been created yet. Overrides Map:: |
|
FieldItemBase:: |
public | function |
Constructs a TypedData object given its definition and context. Overrides TypedData:: |
1 |
FieldItemBase:: |
public | function |
Magic method: Gets a property value. Overrides FieldItemInterface:: |
2 |
FieldItemBase:: |
public | function |
Magic method: Determines whether a property is set. Overrides FieldItemInterface:: |
|
FieldItemBase:: |
public | function |
Magic method: Sets a property value. Overrides FieldItemInterface:: |
1 |
FieldItemBase:: |
public | function |
Magic method: Unsets a property. Overrides FieldItemInterface:: |
|
Map:: |
protected | property |
The data definition. Overrides TypedData:: |
|
Map:: |
protected | property | The array of properties. | |
Map:: |
protected | property | An array of values for the contained properties. | |
Map:: |
public | function |
Applies the default value. Overrides TypedData:: |
4 |
Map:: |
public | function |
Gets a property object. Overrides ComplexDataInterface:: |
|
Map:: |
public | function | ||
Map:: |
public | function |
Gets an array of property objects. Overrides ComplexDataInterface:: |
|
Map:: |
public | function |
Returns a string representation of the data. Overrides TypedData:: |
|
Map:: |
public | function |
Sets a property value. Overrides ComplexDataInterface:: |
|
Map:: |
public | function |
Returns an array of all property values. Overrides ComplexDataInterface:: |
1 |
Map:: |
public | function | Magic method: Implements a deep clone. | |
StringTranslationTrait:: |
protected | property | The string translation service. | 4 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. | |
TreeLockTrait:: |
protected | property | Lock backend. | |
TreeLockTrait:: |
protected | function | Gets lock name. | |
TreeLockTrait:: |
protected | function | Gets lock backend. | |
TreeLockTrait:: |
protected | function | Locks tree. | |
TreeLockTrait:: |
protected | function | Releases lock. | |
TreeLockTrait:: |
public | function | Sets lock backend. | |
TypedData:: |
protected | property | The property name. | |
TypedData:: |
protected | property | The parent typed data object. | |
TypedData:: |
public static | function |
Constructs a TypedData object given its definition and context. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Gets the data definition. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Returns the name of a property or item. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Returns the parent data structure; i.e. either complex data or a list. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
|
TypedData:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
TypedData:: |
public | function |
Returns the property path of the data. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Returns the root of the typed data tree. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Sets the context of a property or item via a context aware parent. Overrides TypedDataInterface:: |
|
TypedData:: |
public | function |
Validates the currently set data value. Overrides TypedDataInterface:: |
|
TypedDataTrait:: |
protected | property | The typed data manager used for creating the data types. | |
TypedDataTrait:: |
public | function | Gets the typed data manager. | 2 |
TypedDataTrait:: |
public | function | Sets the typed data manager. | 2 |