View source
<?php
namespace Drupal\entity_hierarchy\Form;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\entity_hierarchy\Information\ParentCandidateInterface;
use Drupal\entity_hierarchy\Storage\EntityTreeNodeMapperInterface;
use Drupal\entity_hierarchy\Storage\NestedSetNodeKeyFactory;
use Drupal\entity_hierarchy\Storage\NestedSetStorageFactory;
use Drupal\Core\Entity\ContentEntityForm;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class HierarchyChildrenForm extends ContentEntityForm {
const CHILD_ENTITIES_STORAGE = 'child_entities';
protected $entity;
protected $nestedSetStorageFactory;
protected $nodeKeyFactory;
protected $parentCandidate;
protected $entityTreeNodeMapper;
public static function create(ContainerInterface $container) {
$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;
}
public function getBaseFormId() {
return NULL;
}
public function form(array $form, FormStateInterface $form_state) {
$cache = (new CacheableMetadata())
->addCacheableDependency($this->entity);
$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',
],
];
}
$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' => [
t('Child'),
t('Type'),
t('Weight'),
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)) {
continue;
}
$childEntity = $childEntities
->offsetGet($node);
if (!$childEntity
->isDefaultRevision()) {
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' => t('Weight for @title', [
'@title' => $childEntity
->label(),
]),
'#title_display' => 'invisible',
'#default_value' => $childEntity->{$fieldName}->weight,
'#attributes' => [
'class' => [
'children-order-weight',
],
],
];
$form['children'][$child]['operations'] = [
'#type' => 'operations',
'#links' => [],
];
if ($childEntity
->access('update') && $childEntity
->hasLinkTemplate('edit-form')) {
$form['children'][$child]['operations']['#links']['edit'] = [
'title' => t('Edit'),
'url' => $childEntity
->toUrl('edit-form'),
];
}
if ($childEntity
->access('delete') && $childEntity
->hasLinkTemplate('delete-form')) {
$form['children'][$child]['operations']['#links']['delete'] = [
'title' => t('Delete'),
'url' => $childEntity
->toUrl('delete-form'),
];
}
}
$cache
->applyTo($form);
return $form;
}
public function updateField(array $form, FormStateInterface $formState) {
$formState
->setRebuild(TRUE);
}
protected function actions(array $form, FormStateInterface $form_state) {
$actions = parent::actions($form, $form_state);
$actions['submit']['#value'] = $this
->t('Update child order');
$actions['submit']['#limit_validation_errors'] = [
[
'children',
],
[
'fieldname',
],
];
unset($actions['delete']);
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');
$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;
}
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()) {
continue;
}
$batch['operations'][] = [
[
static::class,
'reorder',
],
[
$fieldName,
$childEntity,
$children[$node
->getId()]['weight'],
],
];
}
batch_set($batch);
}
public static function reorder($fieldName, ContentEntityInterface $childEntity, $weight) {
$childEntity->{$fieldName}->weight = $weight;
$childEntity
->save();
}
public static function finished() {
\Drupal::messenger()
->addMessage(new TranslatableMarkup('Updated child order.'));
}
}