entity_hierarchy-8.x-2.24/src/Form/HierarchyChildrenForm.php
src/Form/HierarchyChildrenForm.php
<?php namespace Drupal\entity_hierarchy\Form; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Entity\ContentEntityForm; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; use Drupal\Core\StringTranslation\TranslatableMarkup; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Defines a form for re-ordering children. */ class HierarchyChildrenForm extends ContentEntityForm { const CHILD_ENTITIES_STORAGE = 'child_entities'; /** * The hierarchy being displayed. * * @var \Drupal\Core\Entity\ContentEntityInterface */ protected $entity; /** * Nested set storage factory. * * @var \Drupal\entity_hierarchy\Storage\NestedSetStorageFactory */ protected $nestedSetStorageFactory; /** * Nested set node key factory. * * @var \Drupal\entity_hierarchy\Storage\NestedSetNodeKeyFactory */ protected $nodeKeyFactory; /** * Parent candidate. * * @var \Drupal\entity_hierarchy\Information\ParentCandidateInterface */ protected $parentCandidate; /** * Tree node mapper. * * @var \Drupal\entity_hierarchy\Storage\EntityTreeNodeMapperInterface */ protected $entityTreeNodeMapper; /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { /** @var self $instance */ $instance = parent::create($container); $instance->nestedSetStorageFactory = $container->get('entity_hierarchy.nested_set_storage_factory'); $instance->nodeKeyFactory = $container->get('entity_hierarchy.nested_set_node_factory'); $instance->parentCandidate = $container->get('entity_hierarchy.information.parent_candidate'); $instance->entityTreeNodeMapper = $container->get('entity_hierarchy.entity_tree_node_mapper'); return $instance; } /** * {@inheritdoc} */ public function getBaseFormId() { // Don't show a parent form here. return NULL; } /** * {@inheritdoc} */ public function form(array $form, FormStateInterface $form_state) { $cache = (new CacheableMetadata())->addCacheableDependency($this->entity); /** @var \Drupal\Core\Field\FieldDefinitionInterface[] $fields */ $fields = $this->parentCandidate->getCandidateFields($this->entity); if (!$fields) { throw new NotFoundHttpException(); } $fieldName = $form_state->getValue('fieldname') ?: reset($fields); if (count($fields) === 1) { $form['fieldname'] = [ '#type' => 'value', '#value' => $fieldName, ]; } else { $form['select_field'] = [ '#type' => 'container', '#attributes' => [ 'class' => ['container-inline'], ], ]; $form['select_field']['fieldname'] = [ '#type' => 'select', '#title' => $this->t('Field'), '#description' => $this->t('Field to reorder children in.'), '#options' => array_map(function ($field_name) { return $this->entity->getFieldDefinitions()[$field_name]->getLabel(); }, $fields), '#default_value' => $fieldName, ]; $form['select_field']['update'] = [ '#type' => 'submit', '#value' => $this->t('Update'), '#submit' => ['::updateField'], ]; } /** @var \PNX\NestedSet\Node[] $children */ /** @var \PNX\NestedSet\NestedSetInterface $storage */ $storage = $this->nestedSetStorageFactory->get($fieldName, $this->entity->getEntityTypeId()); $children = $storage->findChildren($this->nodeKeyFactory->fromEntity($this->entity)); $childEntities = $this->entityTreeNodeMapper->loadAndAccessCheckEntitysForTreeNodes($this->entity->getEntityTypeId(), $children, $cache); $form_state->setTemporaryValue(self::CHILD_ENTITIES_STORAGE, $childEntities); $form['#attached']['library'][] = 'entity_hierarchy/entity_hierarchy.nodetypeform'; $form['children'] = [ '#type' => 'table', '#header' => [ $this->t('Child'), $this->t('Type'), $this->t('Weight'), $this->t('Operations'), ], '#tabledrag' => [ [ 'action' => 'order', 'relationship' => 'sibling', 'group' => 'children-order-weight', ], ], '#empty' => $this->t('There are no children to reorder'), ]; $bundles = FALSE; foreach ($children as $weight => $node) { if (!$childEntities->contains($node)) { // Doesn't exist or is access hidden. continue; } /** @var \Drupal\Core\Entity\ContentEntityInterface $childEntity */ $childEntity = $childEntities->offsetGet($node); if (!$childEntity->isDefaultRevision()) { // We only update default revisions here. continue; } $child = $node->getId(); $form['children'][$child]['#attributes']['class'][] = 'draggable'; $form['children'][$child]['#weight'] = $weight; $form['children'][$child]['title'] = $childEntity->toLink() ->toRenderable(); if (!$bundles) { $bundles = $this->entityTypeBundleInfo->getBundleInfo($childEntity->getEntityTypeId()); } $form['children'][$child]['type'] = ['#markup' => $bundles[$childEntity->bundle()]['label']]; $form['children'][$child]['weight'] = [ '#type' => 'weight', '#delta' => 50, '#title' => $this->t('Weight for @title', ['@title' => $childEntity->label()]), '#title_display' => 'invisible', '#default_value' => $childEntity->{$fieldName}->weight, // Classify the weight element for #tabledrag. '#attributes' => ['class' => ['children-order-weight']], ]; // Operations column. $form['children'][$child]['operations'] = [ '#type' => 'operations', '#links' => [], ]; if ($childEntity->access('update') && $childEntity->hasLinkTemplate('edit-form')) { $form['children'][$child]['operations']['#links']['edit'] = [ 'title' => $this->t('Edit'), 'url' => $childEntity->toUrl('edit-form'), ]; } } $cache->applyTo($form); return $form; } /** * Submit handler for update field button. * * @param array $form * Form array. * @param \Drupal\Core\Form\FormStateInterface $formState * Form state. */ public function updateField(array $form, FormStateInterface $formState) { $formState->setRebuild(TRUE); } /** * {@inheritdoc} */ protected function actions(array $form, FormStateInterface $form_state) { $actions = parent::actions($form, $form_state); $actions['submit']['#value'] = $this->t('Update child order'); // Don't perform field validation. $actions['submit']['#limit_validation_errors'] = [['children'], ['fieldname']]; unset($actions['delete']); // Don't show the actions links if there are no children. if (empty(Element::children($form['children']))) { unset($actions['submit']); } $fields = $this->parentCandidate->getCandidateFields($this->entity); $fieldName = $form_state->getValue('fieldname') ?: reset($fields); $entityType = $this->entity->getEntityType(); if ($entityType->hasHandlerClass('entity_hierarchy') && ($childBundles = $this->parentCandidate->getCandidateBundles($this->entity)) && isset($childBundles[$fieldName])) { $handlerClass = $entityType->getHandlerClass('entity_hierarchy'); /** @var \Drupal\entity_hierarchy\Handler\EntityHierarchyHandlerInterface $handler */ $handler = new $handlerClass(); $links = []; foreach ($childBundles[$fieldName] as $id => $info) { $url = $handler->getAddChildUrl($entityType, $this->entity, $id, $fieldName); if ($url->access()) { $links[$id] = [ 'title' => $this->t('Create new @bundle', ['@bundle' => $info['label']]), 'url' => $url, ]; } } if (count($links) > 1) { $actions['add_child'] = [ '#type' => 'dropbutton', '#links' => $links, ]; } else { $link = reset($links); $actions['add_child'] = [ '#type' => 'link', '#title' => $link['title'], '#url' => $link['url'], '#attributes' => [ 'class' => ['button', 'button--primary'], ], '#weight' => -100, ]; } } return $actions; } /** * {@inheritdoc} */ public function save(array $form, FormStateInterface $form_state) { $children = $form_state->getValue('children'); $childEntities = $form_state->getTemporaryValue(self::CHILD_ENTITIES_STORAGE); $fieldName = $form_state->getValue('fieldname'); $batch = [ 'title' => new TranslatableMarkup('Reordering children ...'), 'operations' => [], 'finished' => [static::class, 'finished'], ]; foreach ($childEntities as $node) { $childEntity = $childEntities->offsetGet($node); if (!$childEntity->isDefaultRevision()) { // We don't operate on other than the default revision. continue; } $batch['operations'][] = [ [static::class, 'reorder'], [$fieldName, $childEntity, $children[$node->getId()]['weight']], ]; } batch_set($batch); return SAVED_UPDATED; } /** * Reorder batch callback. * * @param string $fieldName * Field name. * @param \Drupal\Core\Entity\ContentEntityInterface $childEntity * Child entity being updated. * @param int $weight * New weight. */ public static function reorder($fieldName, ContentEntityInterface $childEntity, $weight) { $childEntity->{$fieldName}->weight = $weight; $childEntity->save(); } /** * Batch finished callback. */ public static function finished() { \Drupal::messenger()->addMessage(new TranslatableMarkup('Updated child order.')); } }