improvements-2.x-dev/modules/improvements_taxonomy/improvements_taxonomy.module
modules/improvements_taxonomy/improvements_taxonomy.module
<?php
use Drupal\Core\Condition\ConditionManager;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\Element\Checkboxes;
use Drupal\druhels\ArrayHelper;
use Drupal\druhels\TaxonomyHelper;
use Drupal\improvements\ImprovementsHelper;
use Drupal\pathauto\Form\PatternEditForm;
use Drupal\pathauto\PathautoPatternInterface;
use Drupal\taxonomy\TermForm;
use Drupal\taxonomy\TermInterface;
/**
* Implements hook_entity_base_field_info().
*/
function improvements_taxonomy_entity_base_field_info(EntityTypeInterface $entity_type): array {
if ($entity_type->id() == 'taxonomy_term') {
$fields = [];
$fields['stored_depth'] = BaseFieldDefinition::create('integer')
->setLabel(t('Depth'))
->setSetting('unsigned', TRUE);
$fields['has_children'] = BaseFieldDefinition::create('boolean')
->setLabel(t('Has chilren'))
->setDefaultValue(FALSE);
return $fields;
}
return [];
}
/**
* Implements hook_entity_base_field_info_alter().
*
* @param BaseFieldDefinition[] $fields
*
* @see \Drupal\taxonomy\Entity\Term::baseFieldDefinitions()
*/
function improvements_taxonomy_entity_base_field_info_alter(array &$fields, EntityTypeInterface $entity_type): void {
if ($entity_type->id() == 'taxonomy_term') {
$fields['name']->setDisplayConfigurable('view', TRUE);
}
}
/**
* Implements hook_entity_extra_field_info().
*/
function improvements_taxonomy_entity_extra_field_info(): array {
$extra_fields = [];
foreach (array_keys(\Drupal::service('entity_type.bundle.info')->getBundleInfo('taxonomy_term')) as $bundle) {
/** @see improvements_taxonomy_form_taxonomy_term_form_alter() */
$extra_fields['taxonomy_term'][$bundle]['form']['parent'] = [
'label' => t('Parents'),
'description' => t('Term parents.'),
'weight' => 50,
];
$extra_fields['taxonomy_term'][$bundle]['form']['revision_information'] = [
'label' => t('Revision'),
'description' => t('Revision information.'),
'weight' => 50,
];
}
return $extra_fields;
}
/**
* Implements hook_ENTITY_TYPE_presave(): taxonomy_term.
*/
function improvements_taxonomy_taxonomy_term_presave(TermInterface $term): void {
// Set "stored_depth" field
$term->set('stored_depth', TaxonomyHelper::getTermDepth($term));
// Set "has_chilren" field
if ($term->get('has_children')->isEmpty()) {
$term->set('has_children', TaxonomyHelper::termHasChilds($term->id()));
}
}
/**
* Implements hook_ENTITY_TYPE_insert(): taxonomy_term.
*/
function improvements_taxonomy_taxonomy_term_insert(TermInterface $term): void {
// Set parent term "has_children" field value
$parent_term = $term->get('parent')->entity; /** @var TermInterface $parent_term */
if ($parent_term && !$parent_term->get('has_children')->value) {
$parent_term->set('has_children', TRUE)->save();
}
}
/**
* Implements hook_ENTITY_TYPE_update(): taxonomy_term.
*/
function improvements_taxonomy_taxonomy_term_update(TermInterface $term): void {
/** @noinspection All */
$original_term = $term->original; /** @var TermInterface $original_term */
// Manualy save children terms to run improvements_taxonomy_term_presave() and update "stored_depth" field
$term_depth_is_changed = ($term->get('stored_depth')->value != $original_term->get('stored_depth')->value);
if ($term_depth_is_changed || $original_term->get('stored_depth')->isEmpty()) {
if ($term_direct_children = TaxonomyHelper::getDirectChildTerms($term->bundle(), $term->id(), TRUE)) {
foreach ($term_direct_children as $children_term) {
$children_term->save();
}
}
}
// Update parent terms "has_children" field
$term_parent_items = $term->get('parent');
$original_term_parent_items = $original_term->get('parent');
$term_parent_is_changed = $term_parent_items->hasAffectingChanges($original_term_parent_items, $term_parent_items->getLangcode());
if ($term_parent_is_changed) {
// Update new parent terms
if ($term_parent_items->entity) {
foreach ($term_parent_items as $term_parent_item) {
$parent_term = $term_parent_item->entity; /** @var TermInterface $parent_term */
if (!$parent_term->get('has_children')->value) {
$parent_term->set('has_children', TRUE)->save();
}
}
}
// Update old parent terms
if ($original_term_parent_items->entity) {
foreach ($original_term_parent_items as $original_term_parent_item) {
$original_parent_term = $original_term_parent_item->entity; /** @var TermInterface $original_parent_term */
$original_parent_term_has_children_new = TaxonomyHelper::termHasChilds($original_parent_term->id());
if ($original_parent_term->get('has_children')->value != $original_parent_term_has_children_new) {
$original_parent_term->set('has_children', $original_parent_term_has_children_new)->save();
}
}
}
}
}
/**
* Implements hook_ENTITY_TYPE_delete(): taxonomy_term.
*/
function improvements_taxonomy_taxonomy_term_delete(TermInterface $term): void {
// Update parent term "has_children" field value
$parent_term = $term->get('parent')->entity; /** @var TermInterface $parent_term */
if ($parent_term && !TaxonomyHelper::termHasChilds($parent_term->id(), TRUE)) {
$parent_term->set('has_children', FALSE)->save();
}
}
/**
* Implements hook_form_BASE_FORM_ID_alter(): taxonomy_term_form.
*
* @see \Drupal\taxonomy\TermForm::form()
*/
function improvements_taxonomy_form_taxonomy_term_form_alter(array &$form, FormStateInterface $form_state, $foo = NULL, $bar = NULL): void {
$form_object = $form_state->getFormObject(); /** @var TermForm $form_object */
$term = $form_object->getEntity(); /** @var TermInterface $term */
// Autofocus to term "name" field
if (isset($form['name'])) {
$form['name']['widget']['0']['value']['#attributes']['autofocus'] = TRUE;
}
// Change "relations" item type from "details" to "container"
$form['relations']['#type'] = 'container';
if (isset($form['relations']['parent'])) {
// "parent" field visibility
$entity_form_display = EntityFormDisplay::load("taxonomy_term.{$term->bundle()}.default");
if ($entity_form_display && !$entity_form_display->getComponent('parent')) {
$form['relations']['parent']['#access'] = FALSE;
}
// Increase "parent" select size
if (
$form['relations']['parent']['#type'] == 'select' &&
$form['relations']['parent']['#multiple'] &&
count($form['relations']['parent']['#options']) > 15
) {
$form['relations']['parent']['#size'] = 15;
}
}
// Change text of submit button
if ($term->isNew() && isset($form['actions']['submit'])) {
$form['actions']['submit']['#value'] = t('Save and add more');
}
// Move "delete" button to end
if (isset($form['actions']['delete'])) {
$form['actions']['delete']['#weight'] = 100;
}
}
/**
* Implements hook_form_FORM_ID_alter(): taxonomy_overview_terms.
*/
function improvements_taxonomy_form_taxonomy_overview_terms_alter(array &$form, FormStateInterface $form_state): void {
$form['actions']['reset_alphabetical']['#access'] = FALSE;
// Add "ID" column
$form['terms']['#header'] = ArrayHelper::insertAfter($form['terms']['#header'], 'term', ['tid' => t('ID')]);
foreach (Element::children($form['terms']) as $key) {
$tid = explode(':', $key)[1];
$form['terms'][$key] = ArrayHelper::insertAfter($form['terms'][$key], 'term', [
'tid' => [
'#type' => 'item',
'#markup' => $tid,
],
]);
}
}
/**
* Implements hook_entity_type_build().
*/
function improvements_taxonomy_entity_type_build(array &$entity_types): void {
// See https://www.drupal.org/node/3043840
if (isset($entity_types['taxonomy_term'])) {
$entity_types['taxonomy_term']->set('enable_base_field_custom_preprocess_skipping', TRUE);
}
}
/**
* Implements hook_entity_type_alter().
*
* @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
*/
function improvements_taxonomy_entity_type_alter(array &$entity_types): void {
if (isset($entity_types['taxonomy_term']) && !$entity_types['taxonomy_term']->hasLinkTemplate('delete-multiple-form')) {
$entity_types['taxonomy_term']->setLinkTemplate('delete-multiple-form', '/admin/structure/taxonomy/delete-terms');
}
}
/**
* Implements hook_token_info().
*/
function improvements_taxonomy_token_info(): array {
$info = [];
$info['tokens']['term']['parents_field'] = [
'name' => t('Parents field value'),
'description' => t('An array of all the term\'s parents field value, starting with the root.'),
'type' => 'array',
'dynamic' => TRUE,
];
return $info;
}
/**
* Implements hook_tokens().
*/
function improvements_taxonomy_tokens(string $type, array $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata): array {
$replacements = [];
// [term:*]
if ($type == 'term' && !empty($data['term'])) {
$term = $data['term']; /* @var TermInterface $term */
$token_service = \Drupal::token();
// [term:parents_field:*]
if ($parents_field_tokens = $token_service->findWithPrefix($tokens, 'parents_field')) {
foreach ($parents_field_tokens as $name => $original) {
[$field_name, ] = explode(':', $name);
$field_values = ImprovementsHelper::getTermAllParentsFieldValues($term->id(), $field_name);
if ($array_tokens = $token_service->findWithPrefix($parents_field_tokens, $field_name)) {
$replacements += $token_service->generate('array', $array_tokens, ['array' => $field_values], $options, $bubbleable_metadata);
}
else {
$replacements[$original] = token_render_array($field_values, $options);
}
}
}
}
return $replacements;
}
/**
* Implements hook_form_FORM_ID_alter(): pathauto_pattern_form.
*/
function improvements_taxonomy_form_pathauto_pattern_form_alter(array &$form, FormStateInterface $form_state): void {
$pathauto_patter_form_object = $form_state->getFormObject(); /** @var PatternEditForm $pathauto_patter_form_object */
$pathauto_pattern_entity = $pathauto_patter_form_object->getEntity(); /** @var PathautoPatternInterface $pathauto_pattern_entity */
if ($pathauto_pattern_entity->getType() == 'canonical_entities:taxonomy_term') {
// Find term_depth condition
$term_depth_condition = NULL;
foreach ($pathauto_pattern_entity->getSelectionConditions() as $condition) {
if ($condition->getPluginId() == 'term_depth') {
$term_depth_condition = $condition;
break;
}
}
// Create term_depth condition if not exists
if (!$term_depth_condition) {
$condition_manager = \Drupal::service('plugin.manager.condition'); /** @var ConditionManager $condition_manager */
$term_depth_condition = $condition_manager->createInstance('term_depth');
}
$form['pattern_container']['depth_condition'] = [
'#type' => 'details',
'#title' => t('Term depth condition'),
'#tree' => TRUE,
];
$form['pattern_container']['depth_condition'] = $term_depth_condition->buildConfigurationForm($form['pattern_container']['depth_condition'], $form_state);
$form['#entity_builders'][] = 'improvements_taxonomy_form_pathauto_pattern_form_entity_builders';
}
}
/**
* Pathauto "pattern" form #entity_builders callback.
*
* @see improvements_taxonomy_form_pathauto_pattern_form_alter().
*/
function improvements_taxonomy_form_pathauto_pattern_form_entity_builders(string $entity_type, PathautoPatternInterface $pathauto_pattern, array &$form, FormStateInterface $form_state): void {
$selected_depth = Checkboxes::getCheckedCheckboxes($form_state->getValue(['depth_condition', 'depth']));
// Remove old condition
foreach ($pathauto_pattern->getSelectionConditions() as $condition_id => $condition) {
if ($condition->getPluginId() == 'term_depth') {
$pathauto_pattern->removeSelectionCondition($condition_id);
}
}
// Add new condition
if ($selected_depth) {
$pathauto_pattern->addSelectionCondition([
'id' => 'term_depth',
'depth' => $selected_depth,
'negate' => FALSE,
]);
}
}
/**
* Implements hook_token_info_alter().
*/
function improvements_taxonomy_token_info_alter(array &$data): void {
if (isset($data['tokens']['term']['parents'])) {
$data['tokens']['term']['parents']['name'] = t('Parents names');
}
}
/**
* Implements hook_local_tasks_alter().
*/
function improvements_taxonomy_local_tasks_alter(array &$local_tasks): void {
if (isset($local_tasks['entity.taxonomy_vocabulary.overview_form'])) {
$local_tasks['entity.taxonomy_vocabulary.overview_form']['title'] = t('Terms');
}
if (isset($local_tasks['entity.taxonomy_vocabulary.edit_form'])) {
$local_tasks['entity.taxonomy_vocabulary.edit_form']['title'] = t('Edit vocabulary');
}
}
