# Question
Title: Show only used taxonomy terms in exposed filters
I have an exposed filter for tags on my view. I want this filter to only show show used tags. I found some drupal 7 options like this one and the Views Selective Filter but can't seem to find a good drupal 8 option.

Can anyone help me out with a solution for this?

# Answer
> 3 votes
As Adrian said it was possible to change the code in the article a bit to make it suitable for Drupal 8. Here is my solution:

```
function MODULENAME_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  //id of the form you want to alter
  $formId = 'views-exposed-form-agenda-page-1';
  //field tags
  $select_field = 'field_tags_target_id';

  /**
   * Apply the form_alter to a specific form #id
   * the form #id can be found through inspecting the markup
   */
  if ($form['#id'] == $formId) {
    $available_terms = _get_available_terms();
    if (isset($available_terms)) { // Unset the existing list and add new one with available terms.
      unset($form[$select_field]['#options']);

      if ($form[$select_field]['#options']['All']) {
        $form[$select_field]['#options']['All'] = '- Any Term -';
      }

      foreach ($available_terms as $available_term) {
        $tid = $available_term[0];
        $name = $available_term[1];
        $form[$select_field]['#options'][$tid] = $name;
      }
    }
    else { // Unset all options except '- Any -'
      unset($form[$select_field]['#options']);
      $form[$select_field]['#options']['All'] = '- Any Term -';
    }
  }
}

function _get_available_terms() {
  //table name of tags field
  $node_tags_table = 'node__field_tags';

  $db = \Drupal::database();
  $query = $db->select($node_tags_table, 'nft');
  $query->distinct();
  $query->join('taxonomy_term_field_data', 'tname', 'tname.tid = nft.field_tags_target_id');
  $query->fields('nft', ['field_tags_target_id']);
  $query->fields('tname', ['name']);
  $result = $query->execute();

  $term_list = [];
  while ($row = $result->fetchAssoc()) {
    array_push($term_list, [$row['field_tags_target_id'], $row['name']]);
  }

  return $term_list;
}

```

# Answer
> 2 votes
In Drupal 8, I was able to display only tags with results with the following code. Note that I'm using Better Exposed Filters contrib module, and display my filter as a Select.

```
function mymodule_form_views_exposed_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if ($form['#id'] == 'views-exposed-form-your-view-name-page') {

    if (array_key_exists('field_activities_target_id', $form)) {
      // Rewrite the default 'All' option
      if (!empty($form['field_activities_target_id']['#options']['All'])) {
        $option_default = ['All' => t('Choose a category')];
      }

      $options = $form['field_activities_target_id']['#options'];

      $connection = Database::getConnection();
      $query = $connection->select('node__field_activities', 'activities');
      $query->join('node_field_data', 'n', 'n.nid = activities.entity_id');
      $query->fields('activities', ['field_activities_target_id']);
      $query->fields('n', ['status']);
      $query->condition('activities.bundle', 'your_node_type_name');
      $query->condition('n.status', 1);
      $data = $query->distinct()->execute();

      $results = array_flip($data->fetchAll(\PDO::FETCH_COLUMN, 'field_activities_target_id'));
      $options = array_intersect_key($options, $results);

      // Rebuild the option select
      $form['field_activities_target_id']['#options'] = $option_default + $options;
    }
  }
}

```

This article was a great help : https://johndevman.com/only-show-options-in-a-views-exposed-filter-that-belong-to-result-set/

# Answer
> 2 votes
Here is the D8 module I am using to accomplish this function: https://www.drupal.org/project/selective\_better\_exposed\_filters

It depends on better\_exposed\_filters. https://www.drupal.org/project/better\_exposed\_filters

It's still in beta, so test locally in your environment first, but it worked well in my testing. It won't create the (selective) filter options when you add filter criteria like you are used to in the old D7 module, but it accomplishes the same function:

Once you have an exposed filter in filter criteria, go to **"exposed form style"** under advanced then switch to **better exposed filters (selective)**. Click apply

And then to enable it, check the box **"show only used terms"** down towards the bottom. Click apply.

**Edit:** If you have multiple exposed filters, you can set them each individually under each **"more options for..."** or you can set **all of them globally** at the very bottom of the settings options.

Pretty simple for those of us who aren't comfortable writing our own modules.

# Answer
> 0 votes
The selective better expose filters seems like an ideal solution yet after installing the module I do not get an option 'show only used terms'?

---
Tags: 8, views
---


title: Custom theme settings not saving

text:
# Question
Title: Custom theme settings not saving
I am in Drupal 9.3 and I am working to add 5 new fields to the theme settings. I am following all the instructions I can find. I have tried clearing caches a few times. I can see the fields. But everytime I save the values dissappear.

Here is the code:

```
function mytheme_form_system_theme_settings_alter(&$form, $form_state) {
  $form['price_spider'] = [
    '#type' => 'details',
    '#title' => t('Price Spider'),
    '#open' => TRUE,
    '#description' => t("Your Price Spider settings."),
  ];

  $form['price_spider']['hide'] = [
    '#type' => 'checkbox',
    '#title' => t('Hide Price Spider?'),
    '#default_value' => theme_get_setting('price_spider_hide'),
  ];

  $form['price_spider']['generic_key'] = [
    '#type' => 'textfield',
    '#title' => t('Generic Key'),
    '#default_value' => theme_get_setting('price_spider_generic_key'),
  ];

  $form['price_spider']['product_key'] = [
    '#type' => 'textfield',
    '#title' => t('Product Key'),
    '#default_value' => theme_get_setting('price_spider_product_key'),
  ];

  $form['price_spider']['country'] = [
    '#type' => 'textfield',
    '#title' => t('Country Code'),
    '#default_value' => theme_get_setting('price_spider_country'),
  ];

  $form['price_spider']['language'] = [
    '#type' => 'textfield',
    '#title' => t('Language Code'),
    '#default_value' => theme_get_setting('price_spider_language'),
  ];
}

```

---

**Update:** To be sure, I found the original form in this file: docroot/core/modules/system/src/Form/ThemeSettingsForm.php

That form calls "theme\_settings\_convert\_to\_config" in file: docroot/core/includes/theme.inc

That function only specifically saves fields provided by the default form... except the line that reads "elseif (!in\_array($key, \['theme', 'logo\_upload'\]))" where it just up and saves the setting regardless. So I dove into this using the devel module and the dpm() function (because my xdebug doesn't want to work right now).

Diving in, I see that when the form state gets to the part where the form loads all the values, what I entered is not there and each entry is empty. Maybe this is a validation thing.

I tried the idea of adding a submit function and when I do it never ends up running the submit function in the Core theme settings form. So other settings changes don't happen. Before there was no submit functions defined in the form array. By adding one it seems to break the process.

---

Any idea what I am doing wrong?

Thanks!

# Answer
> 0 votes
Ok, I figured it out. The problem was that the name for the field and the name I was getting using "theme\_get\_setting" didn't match. So it never lines up. Fixed that and the problem is solved.

**Example:**

```
  $form['price_spider']['price_spider_generic_key'] = [
    '#type' => 'textfield',
    '#title' => t('Generic Key'),
    '#default_value' => theme_get_setting('price_spider_generic_key'),
  ];

```

Notice how both parts now say "price\_spider\_generic\_key" (been playing around with the names). Matching those fixed it.

---
Tags: 9, configuration
---


title: Admin toolbar loads index.php/*

text:
# Question
Title: Admin toolbar loads index.php/*
I am using Drupal 9 all the admin tool bar menu options loads the URL with /index.php/admin/config/\* as URL

I have the clean URL programmed in .htaccess, but still the menu load only index.php/ URLs

How do I fix it

# Answer
This is an old issue still not resolved. In this topic you find three fixes. The core patch from the issue would be the most obvious, but now, 5 years later, it is the least favorable, because the module Redirect is better maintained. If you don't want to install a module then you can use the .htaccess solution.

After implementing the fix clear the cache. This removes all unclean URLs from cache and the fix prevents that new ones a generated by site visitors still using the old links, through bookmarks or autocompletes for example.
> 2 votes

# Answer
I fixed the issue by intentionally loading the URL with website.com/admin/ and apply clean URLs and clear cache

Now all the menus adjusted to /admin/ pattern

My simple xml sitemap also got fixed

it seems the PHP code is written in such a way to read the current loaded URL, if it is has the index.php then the program applies that value to the database
> 0 votes

---
Tags: 9, toolbar
---


title: Commerce pick up vs shipping

text:
# Question
Title: Commerce pick up vs shipping
I've read the documentation, but it doesn't seem to be complete just yet.

I'm using Drupal 9.4 and Commerce Core 8.x-2.31 with a few extra Commerce modules. I've got a basic question (I think?). How can you get the checkout in a way that the customer can choose between filling in an address and picking up the order at 1 of 2 locations? When they pick it up at a location, no cost should be added.

There are product variations seeing that we are using SKU numbers for stock and tracing etc.

The customer shouldn't be bothered with extra details, just choose a product and during checkout say 'I want to pay x to send, or pick it up at x or y with no extra cost'.

I have 3 shipping methods, 2 of which add 0 cost to the order, 1 adding the shipping fee. To add this to the checkout, I saw a question about how the order types and ordertype items are linked to each other.

I'm not sure if this setup needs multiple stores (1 online/ 2 offline), or just have multiple Order item types which the Product variation types link to? (The last option, it seems that you can only enter 1 Order item type per Product variation type, so that doesn't seem like the correct way to go?)

# Answer
An easy solution would be to use two flat rate shipping methods (Pick up at place x, Pick up at place Y). Flat rates is provided by Commerce Shipping module.

The drawback is that it will require the customer to enter their address. On the other hand, you probably need billing address anyway, so it may be good enough solution.
> 0 votes

---
Tags: commerce, 9
---


# Question
Title: How do I set the display suite default region for newly created fields?
**What needs to be done:**

Newly created fields in any content type should go to the hidden region of the DS Layout.

**Short background:**

In our project we use the Display Suite module, and we have the following custom layout that is used for all the content types.

```
project_layout:
  label: Project layout
  category: Display Suite
  class: '\Drupal\ds\Plugin\DsLayout'
  type: partial
  template: ds_layouts/project_layout/project_layout
  icon: ds_layouts/project_layout/project_layout.png
  regions:
    header:
      label: Header
    left:
      label: Left
    middle:
      label: Middle
    right:
      label: Right
    comments:
      label: Comments
    footer_top:
      label: Footer top
    footer_middle:
      label: Footer middle
    footer_bottom:
      label: Footer bottom

```

\`

If a new field is added it always goes to the header region.

What I have tried that does NOT work:

1. Setting

default\_region: hidden

2.Setting

```
default_region: disabled

```
3. Creating new region called test and placing it before the header region.

In that scenario, the new fields go to the "test" region. In a way this is a workaround, but there should be better solution.

# Answer
Have you tried `ds_hidden` as region\_name?

UPDATE, or `none` (still guessing).
> -2 votes

---
Tags: 9, display-suite, layout-builder
---


# Question
Title: Can't uninstall default search module because the Commerce Base module is required
I want to use Search API + Solr on my Drupal (& Drupal commerce) site. The tutorials recommend to uninstall the core Search module. However, I can't: the UI at page /admin/modules has Search module checked and grayed out and trying `drush pmu search` results in the following output:

```
In ModuleInstaller.php line 432:

The following reasons prevent the modules from being uninstalled: The Commerce Base module is required

```

I am using Drupal 9.4.5, with all modules up-to-date. I installed it with the commerce\_base installation profile as suggested at https://docs.drupalcommerce.org/commerce2/developer-guide/install-update/installation.

I didn't try it on a fresh install, but even if it works there, I would prefer to be able to modify my site as needed rather than starting from scratch -- any ideas how to start debugging this?

# Answer
> 0 votes
The *Search API* module isn't a drop-in replacement for the *Search* module. This means that modules that expect the *Search* module to be installed won't work if you replace the *Search* module with the *Search API* module.

It could also be the *Commerce Base* profile copied its dependencies from the *Standard* profile and included the Search module, even if that module isn't necessary, or the *Commerce Base* profile dependencies haven't been updated and the *Search* module is now not required. In these cases, uninstalling the *Search* module and replacing it with the *Search API* module won't cause any issue on the site.

You can verify this is the case by creating a test site where the *Composer Base* dependencies are changed to exclude the *Search* module and include the *Search API* module and checking everything works as expected, including other Commerce modules you are using on your site.
(It could be there are Commerce modules that require the *Search* module but don't add it as their dependency, thinking the *Search* module is already a dependency for the *Commerce Core* module or any other Commerce module they need.)

On the issue queue for the *Commerce Base* profile, there is Replace core search with Search API, which is stalled since July 24, 2017 (the day that issue has been created).

# Answer
> 0 votes
I think what you are asking is not possible commerce\_base requires the core search module. I mean that you are not encountering a bug but rather a consequence of using that install profile.

---
Tags: commerce, search, 9
---


# Question
Title: Deleting the original content doesn't delete translated one
When I delete original content, the translated one isn't delete and I just can't delete it ("remove" button doesn't appears).

These are the steps to reproduce the issue.

* Install the Paragraphs module
* Create a *PARA-TEST* paragraph type with a single text field and unlimited values
* Create a *CONTENT-TEST* content type with *PARA-TEST* as unique field
* Create a new *CONTENT-TEST* node and insert some random value in the paragraph field, for example *test1*, *test2*, and *test3*
* Save and reopen it; click on *Translate*
* Replace the paragraph field values with *test1 - UK*, *test2 - UK*, and *test3 - UK*
* Save the translated node
* Reopen the original *CONTENT-TEST* node and delete a value from the paragraph field, for example *test3*
* Reopen the translated node and notice the *test3 - UK* value is still present.

I would expect *test3 - UK* were deleted from the translated node. Instead, *test3 - UK* is still present in the translated node and I can't remove it.

# Answer
> 1 votes
Probably you don't have configured the paragraph field correctly.

On the field settings page of the paragraph field you should see this warning

> Paragraphs fields do not support translation. See the online documentation.

This means you should not check the box above this warning, that users may translate the field.

Instead make the paragraph type translatable and translate the field content of the paragraph.

---
Tags: i18n-l10n, paragraphs
---


# Question
Title: composer drupal-core update fail because of zetacomponents/mail patch
I try to update a Drupalmachine on 9.4.5 site via composer 2.4.1. `~/bin/composer.phar update drupal/core "drupal/core-*" --with-all-dependencies` (see below for composer.json)

first try i can run it and get into interactive question that drupal/coder has multiple files changed (439) and what i want to do:

```
y - discard changes and apply the update
n - abort the update and let you manually clean things up
v - view modified files
d - view local modifications (diff)
s - stash changes and try to reapply them after the update
? - print help

```

i try to choose 'yes', but unfortunately i ran into a problem that patch 86 from zetacomponents/mail can not be applied and the update crashed. This patch seemed to be merged and old composer try to install zetamail in version 1.9.3. I tried to apply the patch manually on the files, but this doesnt work either.

Here is the shortend version of the run through the update to the error. After gathering the patches it got this errormessage, that the patches doesnt apply.

```
Applying patches for zetacomponents/mail
    https://raw.githubusercontent.com/civicrm/civicrm-core/9d93748a36c7c5d44422911db1c98fb2f7067b34/tools/scripts/composer/patches/civicrm-custom-patches-zetacompoents-mail.patch (CiviCRM Custom Patches for ZetaCompoents mail)
    https://github.com/zetacomponents/Mail/pull/86.patch (Allow single quotes to be used in return path)
   Could not apply patch! Skipping. The error was: Cannot apply patch https://github.com/zetacomponents/Mail/pull/86.patch

In Patches.php line 326:

  Cannot apply patch Allow single quotes to be used in return path (https://github.com/zetacomponents/Mail/pull/86.patch)!

```

Like suggested I provide the composer.json:

```
{
  "name": "drupal-composer/drupal-project",
  "description": "Project template for Drupal 8 projects with composer",
  "type": "project",
  "license": "GPL-2.0-or-later",
  "authors": [
    {
      "name": "",
      "role": ""
    }
  ],
  "repositories": [
    {
      "type": "composer",
      "url": "https://packages.drupal.org/8"
    }
  ],
  "require": {
    "php": ">=7.0.8",
    "civicrm/civicrm-asset-plugin": "~1.1",
    "civicrm/civicrm-core": "5.49.4",
    "civicrm/civicrm-drupal-8": "5.49.4",
    "civicrm/civicrm-packages": "5.49.4",
    "composer/installers": "^1.2",
    "cweagans/composer-patches": "^1.6.5",
    "drupal/admin_toolbar": "^3.0",
    "drupal/civicrm_entity": "^3.0@beta",
    "drupal/console": "^1.0.2",
    "drupal/content_access": "^1.0@alpha",
    "drupal/context": "^4.1",
    "drupal/core": "^9.0.0",
    "drupal/core-composer-scaffold": "^9",
    "drupal/core-project-message": "^9",
    "drupal/core-recommended": "^9",
    "drupal/entity": "^1.2",
    "drupal/honeypot": "^2.0",
    "drupal/libraries": "^3.0@beta",
    "drupal/role_delegation": "^1.1",
    "drupal/rules": "^3.0@alpha",
    "drupal/token": "^1.10",
    "drupal/typed_data": "^1.0@beta",
    "drupal/upgrade_status": "^3.12",
    "drupal/views_aggregator": "^2.0",
    "drupal/views_data_export": "^1.1",
    "drupal/webform": "^6.1",
    "drupal/webform_civicrm": "^6.0",
    "drush/drush": "^9.7.1 | ^10.0.0",
    "howtomakeaturn/pdfinfo": "1.*",
    "vlucas/phpdotenv": "^4.0",
    "webflo/drupal-finder": "^1.0.0",
    "zaporylie/composer-drupal-optimizations": "^1.0"
  },
  "require-dev": {
    "drupal/core-dev": "^9"
  },
  "conflict": {
    "drupal/drupal": "*"
  },
  "minimum-stability": "dev",
  "prefer-stable": true,
  "config": {
    "sort-packages": true,
    "allow-plugins": {
      "composer/installers": true,
      "cweagans/composer-patches": true,
      "civicrm/composer-compile-plugin": true,
      "civicrm/composer-downloads-plugin": true,
      "civicrm/civicrm-asset-plugin": true,
      "drupal/console-extend-plugin": true,
      "drupal/core-composer-scaffold": true,
      "drupal/core-project-message": true,
      "zaporylie/composer-drupal-optimizations": true,
      "dealerdirect/phpcodesniffer-composer-installer": true
    }
  },
  "autoload": {
    "classmap": [
      "scripts/composer/ScriptHandler.php"
    ],
    "files": [
      "load.environment.php"
    ]
  },
  "scripts": {
    "pre-install-cmd": [
      "DrupalProject\\composer\\ScriptHandler::checkComposerVersion"
    ],
    "pre-update-cmd": [
      "DrupalProject\\composer\\ScriptHandler::checkComposerVersion"
    ],
    "post-install-cmd": [
      "DrupalProject\\composer\\ScriptHandler::createRequiredFiles"
    ],
    "post-update-cmd": [
      "DrupalProject\\composer\\ScriptHandler::createRequiredFiles"
    ]
  },
  "extra": {
    "composer-exit-on-patch-failure": true,
    "patchLevel": {
      "drupal/core": "-p2"
    },
    "drupal-scaffold": {
      "locations": {
        "web-root": "web/"
      }
    },
    "installer-paths": {
      "web/core": [
        "type:drupal-core"
      ],
      "web/libraries/{$name}": [
        "type:drupal-library"
      ],
      "web/modules/contrib/{$name}": [
        "type:drupal-module"
      ],
      "web/profiles/contrib/{$name}": [
        "type:drupal-profile"
      ],
      "web/themes/contrib/{$name}": [
        "type:drupal-theme"
      ],
      "drush/Commands/contrib/{$name}": [
        "type:drupal-drush"
      ]
    },
    "civicrm-asset": {
      "path": "web/libraries/civicrm",
      "url": "/libraries/civicrm",
      "assets:packages": {
        "+include": [
          "kcfinder/**"
        ]
      }
    },
    "enable-patching": "true",
    "compile-whitelist": [
      "civicrm/civicrm-core",
      "civicrm/composer-compile-lib"
    ],
    "compile-mode": "all"
  }
}

```

Is there a method to get the update done?

# Answer
The Composer Patches error is clear. This project depends on a patch that no longer applies to its base branch or release.

This particular patch was merged more than one year ago so you probably do not need it. Any further Composer support would require a look at the composer.json file.

You can set `composer-exit-on-patch-failure": false` so you may proceed but this is a CiviCRM support question.
> 1 votes

---
Tags: updating, composer
---


# Question
Title: How do I access a module configuration from a service?
I have a module configuration.

### modulename.config.yml

```
modulename:
  core_address: 107.101.xx.yy
  core_port: 153759
  core_user: superuser
  core_pass: superpass

```

I have a service.

### modulename.service.yml

```
services:
    modulename.my_service:
        class: Drupal\modulename\group\MyService

```

### MyService.php

```
/**
 * @file
 * Contains \Drupal\modulename\group\MyService.
 */

namespace Drupal\modulename\group;

/**
 * MyService.
 */
class MyService{

  public function __construct() { }

  public function getDemoValue() {
    $config =  \Drupal::config('modulename.my_service');
    $core_port = $config->get('modulename.core_port');
    return $core_port . 'asdf';
  }

}

```

`$core_port` is always empty.

How can I access a module configuration from a service it implements?

I am not sure this is relevant, but `getDemoValue()` is called from different a module.

```
/**
 * @file
 * Contains \Drupal\anothermodulename\Controller\MyForm.
 */

namespace Drupal\anothermodulename\Controller;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Component\Utility\UrlHelper;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * MyForm.
 */
class MyForm extends FormBase {

  protected $demoService;

  /**
   * Class constructor.
   */
  public function __construct($demoService) {
    $this->demoService = $demoService;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('modulename.my_service')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() { /** nothing interesting here **/ }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    /** nothing interesting here **/
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state){ /** nothing interesting here **/ }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    // Display result.
    foreach ($form_state->getValues() as $key => $value) {
      drupal_set_message($key . ': ' . $value . '  ' . $this->demoService->getDemoValue()); // i try to print here but didn't happend
    }
  }

}

```

# Answer
> 5 votes
Use `$config = \Drupal::configFactory()->getEditable('modulename.config')`.

And so on...

```
$core_port = $config->get('core_port');
$config->set('core_port', 8888)->save();

```

modulename.config.yml should be more like:

```
core_address: 107.101.xx.yy
core_port: 153759
core_user: superuser
core_pass: superpass

```

# Answer
> 3 votes
To read the variables (configs) you'd do this:

```
$config = \Drupal::config('modulename.my_service');

$core_port = $config->get('modulename.core_port');

```

To write to the variables (configs) you'd do this:

// Set and save new message value.

```
$config = \Drupal::service('config.factory')->getEditable('modulename.my_service');
$config->set('modulename.core_port', 153760)->save();

```

There's an example you can look at in the core module aggregator.

It has a settings form at /core/modules/aggregator/src/Form/SettingsForm.php

and then it adds a route to access the settings form:

```
aggregator.admin_settings:
  path: '/admin/config/services/aggregator/settings'
  defaults:
    _form: '\Drupal\aggregator\Form\SettingsForm'
    _title: 'Feed aggregator settings'
  requirements:
    _permission: 'administer news feeds'

```

You could pretty much copy the form into your module and change the variables to match your config.

Generally you'll want to add the config in your service constructor.

```
namespace Drupal\modulename;

/**
 * Example Service with config.
 */
class MyService {

  /**
   * Drupal's settings manager.
   */
  protected $settings;

  /**
   * Constructor.
   */
  public function __construct() {
    $this->settings = \Drupal::config('modulename.my_service');
  }

}

```

Then you'd access the variables like this:

```
$this->settings->get('modulename.core_port');

```

# Answer
> 1 votes
Given the code shown in the question, there are two errors in `MyService::getDemoValue()`.

* Since the configuration filename is *modulename.config.yml*, the configuration object should be created with `\Drupal::config('modulename.config')`, not `\Drupal::config('modulename.my_service')` which would instead work when the configuration filename is *modulename.my\_service.yml*
* Since none of the configuration values has a name that starts with *modulename.*, the configuration values should be read with (for example) `$config->get('core_port')`, not `$config->get('modulename.core_port')`

That is the code to read a configuration object. To alter the configuration values and save them, `\Drupal::configFactory()->getEditable('modulename.config')` should be used instead of `\Drupal::config('modulename.config')`. In this case, setting *core\_port* to 888 would be achieved using code similar to the following one.

```
$config = \Drupal::configFactory()->getEditable('modulename.config');
$config->set('core_port', 8888)
  ->save();

```

As a side note, Drupal services should use the dependency injection container instead of requiring any Drupal service to the `\Drupal` class. That is described in Services and Dependency Injection Container.

---
Tags: 8, configuration, symfony-services
---


# Question
Title: Create Vocabularies and Terms with the JSON:API module
I am using the JSON:API module.

I need to create vocabularies and terms through the endpoint, but it is only accepting `GET` requests.

Am I using the wrong route or endpoint?

# Answer
> 2 votes
JSON:API uses the entity machine names by default. (You can change the names with the JSON:API Extras module.)

If you don't know the machine name, you can install the JSON:API Explorer module, which will provide you with a nice UI at `http://www.example.com/jsonapi/explorer/app`. Just install the module and visit the URL for your site and you will be able to check everything that is available by JSON:API.

---
Tags: taxonomy-terms, 9
---


# Question
Title: How to remove a sort in hook_views_query_alter
I've got a search API search view that has two sorts on it. relevance and then published date. This means that when I do a text search the relevancy score handles the sorting, but when there is no search text, the results are ordered by date. Recently I added some boosting, which has caused that fallback to stop working.

I'm trying to figure out how to remove that first sort (relevance) when there is no search\_api\_fulltext input.

# Answer
> 1 votes
I'm no longer sure if this was a valuable question for the site, but in case my solution helps someone else:

I had recently added a small boost for search results in the current interface language. This boost was causing the issue. Before without a fulltext search value, all results would have the same relevance score, meaning that the sort would fall back to the date. With the boost, all results in the current interface would appear before all results in other languages.

I was adding the language boost with an Event Subscriber listening for `SearchApiSolrEvents::PRE_QUERY`

I added a conditional to only apply the language boost if the fulltext field search was not empty:

```
$query = $event->getSearchApiQuery();
if (!empty($query->getKeys())) {
  //apply boost here
}

```

---
Tags: views, hooks, 9
---


# Question
Title: Config sync & Field default value for entity reference fields
Given two environments dev & live, how do you properly transfer field default values for entity reference fields when deploying with Drush (cex & cim)? The default value is a UUID, which is database- / environment-specific and basically leads to an incorrect deplyoment.

E.g. I am importing categories (taxonomy terms) from a remote API, but only the name (term label) is imported. The default category for nodes is not imported, but selected on our own (dev) system. Later when running migrate import on the live system, the same term has a differnet UUID, and the deployed default value is incorrect.

What I am doing right now is awkward:

* Create a boolean field `field_is_default_value_for_other_entity` on the taxonomy terms.
* Manually edit the terms/select the custom default value on both environments.
* Create a `hook_form_alter`. Using entity query I select the fake default taxonomy terms and set the default value.

This somehow works, but IMHO this also is absolutely horrible. It is a hidden logic contradicting the settings in field UI, it is duplicating existing functionality, and the code for setting the default value is depending on the field widget too (...and breaks when switching from e.g. checkboxes to auto-complete)

Is there a better way to set environment-save default values for ER fields?

# Answer
> 1 votes
Sometimes, Drupal's separation of configuration and content gets really messy-- an entity reference default value is configuration, but it points to content.

I was very frustrated by this too so here is what I did. It is a lot of work to set up, but it is robust.

Basically, use Feeds to make all taxonomy terms content.

1. Create a feed for each vocabulary.
2. Set up the field mappings for each feed.
3. Use CSV Excel files (or whatever else Feeds supports) to store your taxonomy in code.
4. Use drush to import the feeds content as part of your build.
5. Add a drush script that uses an entity query to find the entities that are used as default values and set the uuid to a predetermined uuid.

This gives me everything in code-- the taxonomy terms are stored in a CSV file, and the uuids that need to be fixed are stored in a drush script.

As a less extreme step, you could try setting the uuids with an entity query and drush.

Here's the script I use:

```
function set_uuid(string $entity_type, int $entity_drupal_id, string $uuid): void {
  $entity = \Drupal::entityTypeManager()->getStorage($entity_type)->load($entity_drupal_id);
  if ($entity instanceof TaxonomyTermBundle) {
    $entity->set('uuid', $uuid);
    $entity->save();
  }
  else {
    throw new MyDrushScriptException("Failed to load entity of type $entity_type with uuid $uuid!");
  }
}

set_uuid('taxonomy_term', $term_id_apple, 'abcd-efgh-ijkl-mnop');
set_uuid('taxonomy_term', $term_id_orange, '1234-5678-9012-3456');

```

`$term_id_apple` and `$term_id_orange` are pulled from the db via a query.

---
Tags: configuration
---


title: Drupal 8 get cart item programmatically

text:
# Question
Title: Drupal 8 get cart item programmatically
I need to create pdf of user's cart items with custom header & footer. I decide to create custom module in which I can get cart items details. But I have no idea about how to do this? I have tried:

```
$store_id = 1;
$order_type = 'default';
$cart_manager = \Drupal::service('commerce_cart.cart_manager');
$cart_provider = \Drupal::service('commerce_cart.cart_provider');
$entity_manager = \Drupal::entityManager();
$store = $entity_manager->getStorage('commerce_store')->load($store_id);
$cart = $cart_provider->getCart($order_type, $store);
foreach ($cart-> getItems() as $order_item) {
    foreach($order_item->getPurchasedEntity() as $purchased_entity){
        print_r($purchased_entity);
    }
}

```

But it gives an array like

```
Drupal\commerce_product\Entity\ProductVariation Object
(
[values:protected] => Array
    (
        [variation_id] => Array
            (
                [x-default] => 432
            )

        [type] => Array
            (
                [x-default] => product_variations
            )

        [uuid] => Array
            (
                [x-default] => 87d27a77-4bc1-40ff-b3cf-ad3e949ce1b3
            )

        [langcode] => Array
            (
                [x-default] => en
            )

        [status] => Array
            (
                [x-default] => 1
            )

        [uid] => Array
            (
                [x-default] => 9
            )

        [product_id] => Array
            (
                [x-default] => 65
            )

        [sku] => Array
            (
                [x-default] => xx-uua
            )
        [price] => Array
            (
                [x-default] => Array
                    (
                        [number] => 22.000000
                        [currency_code] => USD
                    )

            )
        [field_order_no] => Array
            (
                [x-default] => Array
                    (
                        [0] => Array
                            (
                                [value] => PE-5070
                            )

                    )

            )

```

And many more `protected` objects. I know I can get `quantity, title & price` easily using drupal function but how can I get few custom details from above array like `field_order_no` in above array, I have some unique attribute assigned to variation that I need to display in pdf?

# Answer
> 0 votes
As the top line suggests $purchased\_entity is of of type "Drupal\commerce\_product\Entity\ProductVariation" which is basically an entity.

All entities have a `->get('field_name')` method.

So for your example to get 'field\_order\_no' you would do

```
$order_no = $purchased_entity->get('field_order_no')->getValue();

```

You can retrieve the value of any field via the `->getValue()` method which returns an array.

# Answer
> 0 votes
```
$cart_provider = \Drupal::service('commerce_cart.cart_provider');
$cart = $cart_provider->getCart('default');
$order_items = $cart->order_items->referencedEntities();
foreach ($order_items as $order_item) {
    $product_variation = $order_item->getPurchasedEntity();
    $sku = $product_variation->getSku();
    $title = $order_item->title->value;
    $quantity = $order_item->quantity->value;
}

```

---
Tags: commerce
---


title: Why do I get the development snapshot?

text:
# Question
Title: Why do I get the development snapshot?
I am following the instructions given on Updating Drupal core via Composer for users using the *drupal/core-recommended* package.

I have the following lines in the *composer.json* file.

```
    "minimum-stability": "dev",
    "prefer-stable": true,

```

```
   "require": {
     "drupal/core-composer-scaffold": "^9",
     "drupal/core-project-message": "^9",
     "drupal/core-recommended": "^9",
   },

 "require-dev": {
        "drupal/core-dev": "^9"
    },

```

Instead of Drupal 9.4.5, I get Drupal 9.5.0-dev.

Can anybody explain why?

# Answer
I needed to change

```
    "require": {
         ...
        "drupal/core-composer-scaffold": "^9",
        "drupal/core-project-message": "^9",
        "drupal/core-recommended": "^9",
         ...
        },

 "require-dev": {
        "drupal/core-dev": "^9"
    },

```

to

```
    "require": {
         ...
        "drupal/core-composer-scaffold": "^9.4",
        "drupal/core-project-message": "^9.4",
        "drupal/core-recommended": "^9.4",
         ...
        },

 "require-dev": {
        "drupal/core-dev": "^9.4"
    },

```
> 2 votes

---
Tags: composer
---


# Question
Title: Programmatically set allowed role on new taxonomy term
i'm using the `permissions_by_term` module and I now want to programmatically create a term and set the `Allowed role` field value while doing this.

I create the term using this, and I added a line to show what I want:

```
$new_term = Term::create([
  'vid' => 'term',
  'name' => 'term name',
  //and then something like
  'allowed_role' => 'admin'
]);

$new_term->enforceIsNew();
$new_term->save();

```

This is not working, but how can I do this?

# Answer
You could look into the code and search for the service that adds the role to the term. I think this could be `permissions_by_term.access_storage`. There is this function `addTermPermissionsByRoleIds`. Please take a look here.

The script would look like.

1. Create the term programatically, fetch its id.
2. Find the roles' ids that you need to have the term access granted.
3. Call the service to do its job. Pass required arguments.
> 2 votes

---
Tags: 9
---


# Question
Title: Show "Add more items" for fields of limited cardinality?
Is there a way to modify the behavior of a field that has limited input to do the 'add more items' button *instead* of show X fields on the screen?

For instance, I have added a link field, that can have up to 5 link values. When it is displayed on the form, it shows 5 empty link fields, instead of one with the option to add more. This display is cumbersome, and makes users believe they must fill all of the fields in, and conversely, become unsure how to 'remove' a link since clearing the field value is less intuitive than clicking 'Remove' that some other fields have.

For UX/training purposes, I would like for fields on forms to behave consistently for limited or unlimited. Has anyone accomplished this behavior using the Form API?

# Answer
> 3 votes
You can do this in a form alter:

```
     YOURMODULE_form_alter(&$form, &$form_state, $form_id) {

        if ($form_id == 'YOURFORM') {
           if (isset($form['YOURFILED']['und']['#max_delta']) && $form['YOURFIELD']['und']['#max_delta'] == 4) {
                $form['YOURFIELD']['und']['add_more']['#prefix'] = '<div style="display:none;">';
                $form['YOURFIELD']['und']['add_more']['#suffix'] = '</div>';
            }
}

```

The max delta should be one less than you want as the maximum. When the delta is 4, then after five addmore fields, the add more button will be hidden.

# Answer
> 1 votes
In Drupal 8 the above solution does not work. How I achieved this by setting the field configuration from a limit to "unlimited" (cardinality: -1) and with a customs module form alter to manage the field limit

Example: In your example.module

```
use Drupal\Core\Form\FormStateInterface;

/**
 * @param array $form
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 * @param $form_id
 */
function example_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  $limit = 100;

  if ($form_id === 'node_xyz_edit_form') {
    if (isset($form['field_abc']['widget']['#max_delta']) && $form['field_abc']['widget']['#max_delta'] >= $limit) {
      unset($form['field_abc']['widget']['add_more']);
    }
  }
}

```

Where 'example' will be your module name, 'node\_xyz\_edit\_form' will be your node form id and 'field\_abc' will be the content-type field.

# Answer
> 0 votes
There is an issue on drupal.org describing this problem with a patch to fix it. https://www.drupal.org/project/drupal/issues/1156338

---
Tags: 8, entities
---


title: How can I show an "Add file" button?

text:
# Question
Title: How can I show an "Add file" button?
I'm making a form that can update a node values. I managed to generate a file upload field which shows the already uploaded images, but there's no visible *Add file* button. When I add a new file, the other files disappear.

What should I change in the following code to show an *Add file* button?

```
$files = $event->field_bijlages;
$fileIds = [];

foreach($files as $file) {
  $fileIds[] = $file->entity->fid->value;
}

/* Files uploading */
$form['files'] = array(
  '#type' => 'managed_file',
  '#upload_location' => 'public://',
  '#default_value' => $fileIds,
);

```

Normally, a managed\_file type field shows the uploaded with a checkbox list which allows you to add/delete uploaded files. That's what I'm missing here.

# Answer
> 1 votes
I added this `#multiple => true` to parameters, since the original field is also a multiple files field. And that made it work!

---
Tags: 9
---


# Question
Title: How can I override the base URL as sent in one-time login links when decoupled front-end domains differ from the backend domain?
For a decoupled project I'm using a passwordless authentication flow, which means I'm using the `/user/password?_format=json` endpoint to send one-time-login links to users. In the frontend I've created a page to proxy the one-time-login link.

Since I'm using multiple domains for the frontend I want to override the base url of the absolute URL's with a value given as query parameter.

How can I override the base URL as sent in a query parameter?

For Example:

When sending a request to

```
https://api.example.com/user/password?_format=json&base_url=example.com

```

The default behavior of Drupal is to send a one-time-login link like this:

```
https://api.example.com/user/reset/<UID>/<TIMESTAMP>/<HASH>

```

But I want the one-time-login link to be this:

```
https://example.com/user/reset/<UID>/<TIMESTAMP>/<HASH>

```

# Answer
> 1 votes
Putting the comments into an answer:

The Drupal Url object has the option `base_url`:

```
$url = Url::fromRoute('user.login', [], ['base_url' => 'http://www.example.com']);

```

If you don't have a chance to add the option in the code creating the URL you can use an outbound path processor to alter the URL:

```
public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
  if (!empty($request->query->get('base_url'))) {
    $options['base_url'] = $request->query->get('base_url');
  }
  return $path;
}

```

This is a minimal code example, make it more safe by checking the query parameter for valid domains and the path where it should apply.

More info about path processing Global preprocess for all links

---
Tags: 9
---


title: Add webform field programmatically

text:
# Question
Title: Add webform field programmatically
In older versions of Webform (Drupal 7), the function `webform_component_insert()` can be used to add fields to a webform, for example inside a `hook_node_insert()` implementation, so they are added when the webform is created.

Does anyone know how to do that in Webform 6.x? I would like to add fields to webforms automatically after they are created.

# Answer
> 5 votes
You can alter a webform by modifying the YAML content of the config field `elements`:

```
use Drupal\webform\Entity\Webform;
use Drupal\Core\Serialization\Yaml;

  $webform = Webform::load('contact');
  $elements = Yaml::decode($webform->get('elements'));

  // add a field
  $elements['additional_field'] = [
    '#type' => 'textfield',
    '#title' => 'Additional field',
  ];

  $webform->set('elements', Yaml::encode($elements));
  $webform->save();

```

# Answer
> 0 votes
```
    $webform  = \Drupal::entityTypeManager()
       ->getStorage('webform')
       ->load(webform_16371); //enter webform id here
  $new_field = [
   'workflow'=>[ '#type' => 'webform_workflows_element',
   '#title' => 'Webform Workflow',
   '#required' => TRUE,
   '#workflow'=> 'webform_workflow', // Add any property
 ]];

 $new_element_arrangment = array_merge($webform->getElementsDecoded(), $new_field);
 $webform->setElements($new_element_arrangment)->save();

```

---
Tags: webforms, 9
---


# Question
Title: Honeypot hidden field in webform template "webform--contact.html.twig"
Honeypot is active on my site (Drupal 9) for all forms. When I created the template file for the contact form "webform--contact.html.twig", I can't get the honeypot default hidden field "url".

I tried :

* {{ content.url }}
* {{ element.elements.url }} ...

How can I get the honeypot hidden field "url" in my contact twig file?

```
/* templates/webform/webform--contact.html.twig */

  <form{{ attributes }}>
        {{ title_prefix }}
        <div class="row">
          <div class="col-md-6">
            {{ element.elements.email }}
            //  Honeypot hidden field "url" ?
                ...
          </div>
          <div class="col-md-6">
            {{ element.elements.message }}
            {{ element.form_build_id }}
            {{ element.form_token }}
            {{ element.form_id }}
            {{ element.elements.actions }}
          </div>
        </div>
        {{ title_suffix }}
  </form>

```

# Answer
> 2 votes
For me `{{ element.url }}` works. It seems to me that Honepot does not add the field to the elements subarray. And be sure not to be logged in with a role that has the permission to bypass honeypot protection.

Tip: with Devel module and Xdebug installed you can set a breakpoint in your twig file with `{{ devel_breakpoint() }}` which allows you to stop your script at this point and check which variables can be used in your twig file.

---
Tags: webforms
---


# Question
Title: How to filter by month in view using a date field
I have a field called `birthday` that is defined for users. In `views`, is there any way to create a filter that filters only by month? Smallest values it lets me select is year and month where I only need month. (to find users who are born in April for example). Thanks for any help.

# Answer
> 1 votes
If you are using the date module, then you can set the `granularity` to `month` in the date filter settings.

Then, you can configure the filter like so `Operator: "is equal to"` and `Date: "this month"`.

If you don't see these filter settings, you would have to enable the `date_views` module.

# Answer
> 1 votes
Here is what I ended up doing:

```
/**
 * Impliments hook_form_views_exposed_form_alter
 *
 * Used to change exposed date filter to month only, as this is not available in the views UI
 */
function MODULENAME_form_views_exposed_form_alter(&$form, $form_state) {
  if( $form['#id'] == 'form_id') {
    // Switch date formatter to month only
    $form['field_date']['value']['#date_format'] = 'm';
    // Add extra validator to handle filter submit
    $form['#validate'][] = 'MODULENAME_views_exposed_form_validate';
  }
}
/**
 * Custom validator for exposed date filter
 */
function MODULENAME_views_exposed_form_validate(&$form, &$form_state) {
  $handlers = &$form_state['view']->filter;
  foreach ($handlers as $key => $handler) {
    if (!empty($handler->options['exposed'])
     && isset($form[$key]['value']['#type'])
     && $form[$key]['value']['#type'] == 'date_select'
     && $form[$key]['value']['#date_format'] == 'm'){
      $handler->options['granularity'] = 'month';
      $handler->format = 'm';
    }
  }
}

```

# Answer
> 1 votes
If you do not want to use a hook you can do it with a more simple solution (works for D7, D8, D9).

1. Add the month field to your view.
2. Use the custom php format date that you need for exposing the month
3. On the FILTER CRITERIA add and expose Global: Combine fields filter for the month and group them
4. Hide the month field from the results using CSS (adding a class for hide)
5. Repeat the 1-4 steps for the year field

It takes a little longer to set up, this might work better for the month than the year field since month are always the same, but years might need to be updated manually (adding years) \[!\[enter image description here\]\[3\]\]\[3\]

---
Tags: views, datetime
---


title: Custom Template for Comment links

text:
# Question
Title: Custom Template for Comment links
I would like to have a custom template for links (edit, reply, deleted) for my custom Comment Type. (Drupal 9)

Twig Debugging of course enable. I have tried to use hook\_theme\_suggestions\_link\_alter and hook\_theme\_suggestions\_link, and I see my suggestion, but it doesn't apply my custom twig for links.

I spent a lot of time looking for a solution and did not find. Please, could you help me?

```
function answer_theme_suggestions_links_alter(array &$suggestions, array $variables)
    {
      if (isset($variables['links']['comment-custom-action'])) {
        array_splice($suggestions, 1, 0, 'links__comment__answer');

      }
    }

function answer_theme()
{
  return [
    'links__comment__answer' => [
      'template' => 'links--comment--answer',
      'base hook' => 'links'
    ]
  ];
}

```

# Answer
It happens because Drupal first collect all the theme suggestions following this order.

* The suggestions returned by `hook_theme_suggestions_HOOK()` implementations done by modules, the base themes for the active theme, and the active theme (in that order)
* The suggestions returned by `hook_theme_suggestions_alter()` implementations done by modules, the base themes for the active theme, and the active theme (in that order)
* The suggestions returned by `hook_theme_suggestions_HOOK_alter()` implementations done by modules, the base themes for the active theme, and the active theme (in that order)

Inside the group of modules (or base themes) that implement the same hook, the invocation order is determined by system weight, then by module (or theme) name.

Once Drupal collects all the theme suggestions in an array, it reverts the order of those suggestions, which means the last suggestion becomes the first one.

This is done by `ThemeManager::render()`, for example, which uses the following code. (Notice the code following the "// \>\>\> * \<\<\<" comment I added.)

```
  // Invoke hook_theme_suggestions_HOOK().
  $suggestions = $this->moduleHandler->invokeAll('theme_suggestions_' . $base_theme_hook, [
    $variables,
  ]);

  // If the theme implementation was invoked with a direct theme suggestion
  // like '#theme' => 'node__article', add it to the suggestions array before
  // invoking suggestion alter hooks.
  if (isset($info['base hook'])) {
    $suggestions[] = $hook;
  }

  // Invoke hook_theme_suggestions_alter() and
  // hook_theme_suggestions_HOOK_alter().
  $hooks = [
    'theme_suggestions',
    'theme_suggestions_' . $base_theme_hook,
  ];
  $this->moduleHandler->alter($hooks, $suggestions, $variables, $base_theme_hook);
  $this->alter($hooks, $suggestions, $variables, $base_theme_hook);

  // Check if each suggestion exists in the theme registry, and if so,
  // use it instead of the base hook. For example, a function may use
  // '#theme' => 'node', but a module can add 'node__article' as a suggestion
  // via hook_theme_suggestions_HOOK_alter(), enabling a theme to have
  // an alternate template file for article nodes.
  // >>> * <<<
  foreach (array_reverse($suggestions) as $suggestion) {
    if ($theme_registry->has($suggestion)) {
      $info = $theme_registry->get($suggestion);
      break;
    }
  }

```

It then search for the first existing template file that matches the suggestions and uses that template file.

In your case, the suggested template files have been checked following this order. (This is shown in the debugging output shown in the screenshot.

* links--comment.twig.html
* links--inline.twig.html
* links--comment--answer.twig.html

If *links--inline.twig.html* has been suggested by the active theme, or one of its base themes, and the code shown in the question is implemented by a module, there isn't anything you can do, as a theme suggestion always take precedence over a module suggestion.

If *links--comment.html.twig* suggestion was done by module, and the code shown in the question is implemented by a module too, you need to increment your module's weight, for example calling `module_set_weight()` inside `hook_install()` or `hook_update_N()`.

This is doesn't seem your case, but notice that if you implement `answer_theme_suggestions_links_alter()` **and** `answer_theme_suggestions_alter()` (or `answer_theme_suggestions_links_alter()` **and** `answer_theme_suggestions_links()`) the suggestions given by `answer_theme_suggestions_links_alter()` takes the precedence.
> 0 votes

---
Tags: theming
---


# Question
Title: How can I extend an existing View display style?
I'm using the views\_summarize module, which adds a custom View display style. The display style is basically a Table view but it allows you to display a summary of a column in a row at the bottom of the table in a variety of formats you can choose from.

I need to add a custom format to this list and in the Drupal 7 version of the module this could be done with hooks (see here). But since it was updated for Drupal 8/9 I can't figure out how to extend the display style plugin or if it's even possible.

I would ask the module developers but I don't think they've touched the module in a couple years, hoping someone can point me in the right direction here. Cheers!

# Answer
After extending the plugin class

*/mymodule/src/Plugin/views/style/MyTableSummarized.php*

```
<?php

namespace Drupal\mymodule\Plugin\views\style;

use Drupal\views_summarize\Plugin\views\style\TableSummarized;

class MyTableSummarized extends TableSummarized {

  protected function getHandlers() {
    $handlers = parent::getHandlers();
    // add your own handler
    $handlers['bar'] = 'Bar';
    return $handlers;
  }

}

```

you can replace the class in the plugin manager:

*/mymodule/mymodule.module*

```
function mymodule_views_plugins_style_alter(array &$info) {
  if (isset($info['tablesummarized'])) {
    $info['tablesummarized']['class'] = '\Drupal\mymodule\Plugin\views\style\MyTableSummarized';
  }
}

```

See How do I replace a plugin?
> 1 votes

---
Tags: views
---


# Question
Title: How can I use function template_preprocess_field() to pass a value from one field to an attribute of another field?
I have an **Entity Product entity** where one of the fields is:

* reference field on Media type with name **Media Product Image**

Media Product Image has fields:

* Image
* Custom color id

And in the developers tools of the browser, the `<img>` looks like this:

```
<img loading="lazy src="/sites/default/files/styles...." width="300" height="550" typeof=foaf:Image>

```

I need to add the `data-color` attribute to the `<img>` tag and pass the value from the **Custom Color id** field to it. This can be done with:
https://api.drupal.org/api/drupal/core%21includes%21theme.inc/function/template\_preprocess\_field/9.4.x

For example:

```
function my_theme_preprocess_field(&$variables, $hook) {
  $element = $variables['element'];
  if (isset($element['#field_name'])) {
    if ($element['#field_name'] == 'field_image') {
      // do something
    }
  }
}

```

But the problem is that when I write the word `media` in the **.module** file in my **entity\_product\_admin** module in the function name:

```
function entity_product_admin_preprocess_media(&$variables, $hook) {
}

```

I can't get the value of **field\_custom\_color\_id** because there is no method in the API of the **media** entity with which I can get the value of this field:

Also, I don't have access to the image attributes and I can't add the `data-color` attribute with the value of the **Custom color id** field.

If I write the word `image` in the name of the function:

```
function entity_product_admin_preprocess_image(&$variables, $hook) {
}

```

Then I already have access to the image attributes, but I do not have access to the **field\_custom\_color\_id** field:

I tried to write just **field** in the function name, but then I can get through anywhere.
How then should this function look like?

# Answer
> 0 votes
Field preprocess to preprocess a field, and it has no access generally to any other field.
A) You create a custom field type, formatter and widget. In that case, you can have the image and the colour in the same field. (if you created the field like that) but migration may be needed if you already have content.
B) Create a field formatter. In the field formatter, you can say how to render the field and you have access to the whole parent entity.

And an additional tip. Try to minimalise the usage of hook functions. They are fragile and easy to break. i.e. You write a preprocess where you read a field. You enable the layout or DS module, which will definitely break your preprocess as it changes the structure of the array in that hook as it adds the layout to it. You can extend plugins via OOP.

# Answer
> 0 votes
Try something like this:

```
/**
 * Implements hook_preprocess_field().
 */
function YOUR_THEME_preprocess_field(&$variables) {
  // Get the node object.
  $node = $variables['element']['#object'];

  // Limit the following changes to one specific node type.
  if ($node->bundle() != 'image_map') {
    return;
  }
  $element = $variables['element'];
  $field_name = $element['#field_name'] ?? NULL;

  // We only want to apply this to one specific field.
  if ($field_name != 'field_imagemap') {
    return;
  }

  // Set some variables based on the node values.
  if ($node->get('field_state_links')->getString() != '') {
    $variables['field_state_links'] = $node->get('field_state_links')->getValue();
  }
  if ($node->get('field_image_map_type')->getString() != '') {
    $variables['field_image_map_type'] = $node->get('field_image_map_type')->getValue();
  }

  // Test for a specific value of another field and modify "field_imagemap" field output as needed.
  if (strtolower($node->get('field_image_map_type')->getString()) == 'usa') {
    // Your code for mocking about with the field.
  }
}

```

---
Tags: media, hooks, 9
---


# Question
Title: English translation shows up on German page
I have Drupal 9.4.5 site. I've been hunting a problem where translations in the wrong language show up for guest users, after each release.

* Default language: English
* Interface Language Detection: Only method "Selected language" (Language based on a selected language) is checked with value: German

The intention is that this site uses only German as language. We use English as default language, a) because that seems to work better for Drupal and b) because we have some English-speaking developers on the team. Everything gets translated to German. This only concerns texts translated via t() in the templates.

The problem: After a new release is deployed to the platform.sh installation, some blocks show English translations. This only happens for guest users, everything is correctly translated for logged-in users. So it's not a problem of a missing translation. Mainly it happens with a combination of blocks and views. One is a block generated by a view, the other custom block uses a view internally.

Remedy so far: The English texts go away on cache clear. I'm not sure why this helps, as the last step of installing the release is already a "drush cr".

So far, I haven't been able to reproduce it locally and I'm not aware of the probem showing up without a release happening first. I tried to disable the platform.sh cache for a specific route, but after the next release this route showed English texts again.

So this seems to be a cache issue in some way. I'm looking for an idea why Drupal would ever render an English text on a German page in the first place!? There is no language switch available and no language part of the URL. Any idea is welcome.

**Update 29.09.: By now I have added debug output to the template and I really think this is a problem of Drupal outputting English translations when it should output the German text.**

My current test case:

* A block bundle using an entity reference field to allow the user to pick entities to be displayed.
* The block's display settings is set to "referenced entity" with the display mode "teaser".
* There is no class for the block.
* There is a template in the theme for the entities being displayed. It contains this code:

```
    {% trans %}
      {{ comment_count }} comment
    {% plural comment_count %}
      {{ comment_count }} comments
    {% endtrans %}

```

In the preprocess function for the entity, I added this:

```
  $variables['current_language_content'] = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
  $variables['current_language_interface'] = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_INTERFACE)->getId();

```

And this in the template, directly beneath the comment count output:

```
<!-- current language content: {{ current_language_content }}, current language interface: {{ current_language_interface }} -->
<!-- just as is: {{ "Join now!"|t }} -->
<!-- explicitly de: {{ "Join now!"|t({'langcode': 'de'}) }} -->

```

Now I have a page with four German teasers and one English teaser, and all of them show this in the source code: `<!-- current language content: de, current language interface: de -->`

The two extra translated Strings show the correct German translation for the first four teasers and then for the fifth:

```
<!-- current language content: de, current language interface: de -->
<!-- just as is: Join now! -->
<!-- explicitly de: Join now! -->

```

This test String is shown to be correctly translated in the UI interface and is not part of any config file.

There might be caching issues involved, but this seems to be broken on a level beneath the cache. Also, we had one instance yesterday were it needed multiple cache clears and some hours before the texts turned German again.

Sooo... Is there any other language setting that I can check? Are there known bugs of the translation method?

# Answer
> 0 votes
> So this seems to be a cache issue in some way.

That is the most likely reason. The internal page cache module, which is enabled by default, caches pages for anonymous users.

Blocks are modular components of your pages. They can be reused on multiple pages/across multiple language versions of your site. Implementing the CachableDependencyInterface, they are having their own render cache configuration. After clearing the render caches, the first occurrence of a block that meets the caching configuration will be cached and reused for each subsequent page that meets the same cache configuration.

Assuming that:

* the current URL/language aren't part of the block cache context,
* the sites' releasing process clears the render caches,
* the sites' releasing process or your releasing developer afterwards opens a page of your site in English that contains such a block,

the English version of the block will get cached and reused for any later occurrence of the same block on other pages, even though subsequently visited pages may be in German or are having a different URL. - You'll see exactly your described results. (If you clear your caches and visit the site in its German version, the block will be cached in German, and the English version of the pages will show a German block for guest visitors.)

To overcome this issue, you may want to either disable caching for those blocks alltogether (recommended only for highly dynamic block content), or - the more reasonable approach - add correct cache contexts/tags.

E.g., by adding the `'url'` or `'route'` cache context, different versions of the same block instance will be cached for each different URL/route visited. Using the `'languages:language_content'` cache context, a different version for each visit using a different content translation language will be cached.

If the blocks have been provided by Drupal core/contributed modules, you may wish to ask the maintainers of those modules to add correct caching contexts.

If those blocks have been provided by a custom module, use the following examples to extend your block classes:

(1) Override getCacheMaxAge() to disable caching of a block by setting its maximum caching time to `0` :

```
use Drupal\Core\Block\BlockBase;

class MyCustomBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  public function getCacheMaxAge() {
    return 0;
  }

}

```

(2) Override getCacheContexts() to add custom cache contexts:

```
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\Cache;

class MyCustomBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  public function getCacheContexts() {
    return Cache::mergeContexts(parent::getCacheContexts(), [
      // Your applicable cache tags.
      'url',
      'languages:language_content',
    ]);
  }

}

```

---
Tags: 9
---


# Question
Title: How to get Media image values (alt and title) in Views?
I want to edit the Media library module View to show the images Alt and Title field value, but it is not in the available fields (only "Thumbnail(alt)" which prints the image).

All available relationships also do not give alt or title.

It seems the Image settings fields are not available in views?

# Answer
> 4 votes
`alt` and `title` are stored in `field_media_image` of the media type Image. To access the values in Views add this field to FIELDS and select/configure a field formatter to display the values.

To output the raw values outside of a formatted image rewrite the field and use the replacement patterns:

```
{{ field_media_image__title }}
{{ field_media_image__alt }}

```

# Answer
> 1 votes
How to get it form media entity

```
{{ media_entity.field_media_image.title }}
{{ media_entity.field_media_image.alt }}

```

How to get it from node

```
{{ node.field_yourimagefield.entity.field_media_image.title }}
{{ node.field_yourimagefield.entity.field_media_image.alt }}

```

# Answer
> 0 votes
Not sure you'd easily get that info in a view.

Probably might need to do a hook\_preprocess\_media and add the content yourself as the view shows the rendered entity for the "media\_library" display mode

Have a look at line 110 in the core/modules/media\_library/media\_library.module file and you'll see its doing a similar thing to add stuff to that rendered display

Or if you want this title to show just specifically for that view you could do an alter on that view

# Answer
> 0 votes
As none of the other answers worked for me, i had to search again. This finally worked:

```
content.field_news_image['#items'].entity.field_media_image.alt

```

---
Tags: views, 8
---


# Question
Title: Can I delete nodes of a given content type with Drush?
Just wondering if Drush is capable of deleting nodes of a given content type.

Something like: `$ drush delete-node --type=MyContentType`

If not possible, can I create a **method** like that?

# Answer
> 6 votes
You could create a function like follows:

```
function MYMODULE_delete_all_the_things() {
  $query = new EntityFieldQuery;

  $result = $query->entityCondition('entity_type', 'node')
    ->propertyCondition('type', 'YOUR-CONTENT-TYPE')
    ->execute();

  if (isset($result['node']) && count($result['node'])) {

    $node_ids = array_keys($result['node']);

    node_delete_multiple($node_ids);
  }
}

```

Note - You could also use a simple `SELECT` query, but seeing as you're working with entities it seems more sensible/common-practice to use EntityFieldQuery.

This should also be quite easy to change or add arguments to. You can also put it in a Drush command quite easily - there's an example for how to do this in the Drush repository (check the Commands section near the bottom).

# Answer
> 40 votes
### Drush 9

`drush entity:delete node --bundle=my_content_type`

# Answer
> 25 votes
Install the devel module, and use drush to delete all the nodes,

```
$  drush genc --kill 0 0

```

You can also give a type option,

```
$  drush genc --kill --types=article 0 0

```

# Answer
> 15 votes
I guess you can by using the below command

```
drush node_delete <nid>

```

EDIT: Found a module that does something/related to the question

http://drupal.org/project/delete\_all

**Usage**

**Drush**

`drush delete-all`

Example: `drush delete-all article`

# Answer
> 5 votes
Something like this would work (untested):

```
$query = new EntityFieldQuery();

$entities = $query->entityCondition('entity_type', 'node', '=')
  ->entityCondition('bundle', 'Announcements')
  ->execute();

$nids = array_keys($entities['node']);

node_delete_multiple($nids);

```

This will find all of the nodes with the content type `Annoucements` using `EntityFieldQuery()`. It then gets all the `$nids` from the result, and deleted them using `node_delete_multiple()`.

You can take this code, put it in a separate PHP file, and then execute it with `drush scr`.

Using the API will ensure that all of the proper hooks fire. Among other things, they will also delete the node revisions and field data (and their revisions) so you don't have orphaned data in the database.

# Answer
> 5 votes
There are some good ideas on this thread. If you dont want to do any programming really and want to use Drush you can look at the Delete All contributed module:

```
Drush
drush delete-all
Example: drush delete-all article

Drush on Drupal 7 version

Delete all nodes, nodes of a specific content type or users.

Examples:
 drush delete-all article             Delect all article nodes.
 drush delete-all all                 Delete nodes of all types.
 drush delete-all --reset             Delete nodes of all types, and reset node, revision and comment counters.
 drush delete-all users               Delete users.

Options:
 --reset                              Reset counter for node, revision and comment tables.
 --roles                              pick roles

Aliases: da

```

# Answer
> 3 votes
VBO has Drush integration. Create a VBO view of nodes, execute it via Drush (using `drush vbo-execute`), pass the node type as an argument.

# Answer
> 1 votes
You can also create a drush script (let's say it's called "bulk\_delete.php" and is placed under the Drupal root folder):

```
#!/usr/bin/env drush

$res = db_delete('node')
  ->condition('type', 'mycontenttype', '=')
  ->execute();

echo "deleted:" . $res;

```

This is absolutely the fastest way to do it: a direct query to the db, using the Drupal's function db\_delete

Usage:

```
:~# cd /var/www/www.mysite.com
:~# drush --uri=www.mysite.com scr bulk_delete.php

```

Doc: https://api.drupal.org/api/drupal/includes!database!database.inc/function/db\_delete/7

Warning: this process does not delete the data of their fields. Anyway the same process can be applied to these fields (looking at the "bundle" column of each field). For example:

```
#!/usr/bin/env drush

$field_tables = array(
  'field_data_field_body',
  'field_data_field_mycoolfield',
  'field_revision_body',
  'field_revision_field_mycoolfield'
);

foreach ($field_tables as $field_table) {
  $res = db_delete($field_table)
    ->condition('bundle', 'mycontenttype', '=')
    ->execute();

  echo "deleted:" . $res . "\n\n";
}

```

Where "mycontenttype" is the one used in the first query.

# Answer
> 1 votes
**Drupal 7**

Try the following one-liner, it removes all MyContentType nodes from Drupal:

```
drush eval '$res = (new EntityFieldQuery)->entityCondition("entity_type", "node")->entityCondition('bundle', 'MyContentType')->execute(); entity_delete_multiple("node", array_keys(reset($res)));'

```

Or you can remove all the entities based on the entity type name (e.g. node):

```
drush eval '$res = (new EntityFieldQuery)->entityCondition("entity_type", "node")->execute(); entity_delete_multiple("node", array_keys(reset($res)));'

```

Note: `node` is your entity type name, you can change it if required.

---

If you'll get memory or time out errors, you may add the following sets just before the `$res`:

```
ini_set('memory_limit', -1); ini_set('max_execution_time', 0);

```

# Answer
> 1 votes
To delete all nodes of a particular content type, you may try to run the following command via drush:

```
drush eval '$res = (new EntityFieldQuery)->entityCondition("entity_type", "node")->entityCondition('bundle', 'MyContentType')->execute(); entity_delete_multiple("node", array_keys(reset($res)));'

```

Where MyContentType is your machine Content Type name (e.g. Page).

# Answer
> 1 votes
For individual nodes:

```
$ drush php-eval 'node_delete($node->nid);'

```

> drush php-eval
> Evaluate arbitrary php code after bootstrapping Drupal

# Answer
> 1 votes
Updating the answer provided by @kenorb.

In Drupal 8

```
drush eval '$nids = \Drupal::entityQuery('node')->execute(); $storage = Drupal::entityTypeManager()->getStorage('node'); $e = $storage->loadMultiple($nids); $storage->delete($e);'

```

# Answer
> 1 votes
To help those that find this thread - use the following command to delete all the nodes within a content type - **drush uses bundles now instead of types**:

```
drush genc --kill --bundles=about 0 0

```

# Answer
> 0 votes
You can also do this with the devel module as answered at Can I delete nodes of a given content type with Drush?

Install the devel module, and use drush to delete all the nodes,

$ drush genc --kill 0 0

You can also give a type option,

$ drush genc --kill --types=article 0 0

# Answer
> -1 votes
You can use

`drush node_delete NID` to delete specific node, but if you want to delete all nodes by content type I guess you can create a drush plugin using batch api.

---
Tags: drush
---


# Question
Title: How do I delete all terms from a vocabulary (bulk delete)?
I have a large vocabulary, and admin views does not yet work for taxonomy. Taxonomy Manager does not do this as well.

Are there other options for bulk-deleting taxonomy terms?

# Answer
> 15 votes
Taxonomy manager does have bulk delete functionality, just select all the terms in your vocabulary and click the 'Delete' button:

If you want to do it using code something like the following should help:

```
$vocabulary = taxonomy_vocabulary_machine_name_load('my_custom_vocabulary');
foreach (taxonomy_get_tree($vocabulary->vid) as $term) {
  taxonomy_term_delete($term->tid);
}

```

# Answer
> 28 votes
A favorite method of bulk-deleting content by content type, taxonomy vocabulary, etc utilizes the http://drupal.org/project/devel module. For deleting all terms in a vocabulary:

1. Enable the Devel and Devel Generate modules
2. Go to Development \> Generate Terms
3. Select the Vocabulary from which you wish to delete terms
4. Enter "0" for number of Terms to generate
5. Check "Delete existing terms before generating new ones"
6. Click Submit

Voila-- empty vocabulary, otherwise untouched.

# Answer
> 8 votes
Untested; use for inspiration and test:

```
<?php

$vid = 17;

$tree = taxonomy_get_tree($vid);

if (count($tree) == 0) {
  print "Nothing to delete.\n";
}
else {
  $tree = array_slice($tree, 0, 1000);

  foreach ($tree as $term) {
    print 'Deleting tid ' . $term->tid . "\n";
    taxonomy_term_delete($term->tid);
  }
}

```

Put this in its own file, and execute as

```
drush -u 1 scr nuke_vid_18.php

```

Keep running it until it doesn't show anything as deleted. The `array_slice` is there to limit things so you don't run out of memory. You can potentially save some memory by querying directly, but `taxonomy_get_tree()` isn't terribly inefficient as long as you aren't doing full term loads.

Backup your database with `drush sql-dump` as a precaution before you do anything like this.

# Answer
> 8 votes
You can use the following command.

```
drush -v eval 'foreach(taxonomy_get_tree(123) as $term) { taxonomy_term_delete($term->tid); }'

```

If it doesn't work, make sure that you clear your cache (e.g. memcached).

A faster way is using the following dirty SQL query, where *123* is the ID of the vocabulary you should change.

```
drush sqlq "DELETE FROM taxonomy_term_data WHERE vid = 123"

```

You can get the *vid* from the vocabulary name with the following command.

```
drush sqlq "SELECT name, vid FROM taxonomy_vocabulary WHERE name = 'vocabulary_name'"

```

# Answer
> 7 votes
I've these modules: Drush and Devel For exclude all terms of taxonomy, use the generate-terms command:

> drush generate-terms you\_vocabulary\_name 0 --kill

Change your\_vocabulary\_name to name of vocabulary you need delete terms

# Answer
> 3 votes
On Drupal 8 and 9 with Drush, just run :

`drush entity:delete taxonomy_term --bundle=VOCABULARY_ID`

Replace *VOCABULARY\_ID* with the vocabulary machine name.

# Answer
> 2 votes
Admin views just provides preconfigured VBO views.

VBO itself works fine with terms (or any other entity type). Install VBO, create the view that you want, then use VBO to delete the terms.

# Answer
> 2 votes
I’m using the jQuery Easy Confirm Dialog plugin for this. First download the library from here and put this in your themes js folder.

We can then add a “Delete All Terms” button with a little code in a custom module:

```
function hook_form_alter(&$form, &$form_state, $form_id) {
    switch($form_id) {
        case 'taxonomy_overview_terms':

            if($form['#total_entries']) {
                drupal_add_library('system', 'ui.dialog');
                drupal_add_js(drupal_get_path('theme', 'YOUR_THEME_NAME').'/js/jquery.easy-confirm-dialog.js');
                $js = 'jQuery(document).ready(function($){$(".confirm").easyconfirm({locale: { title: \'Delete all '.$form['#vocabulary']->name.' terms\', button: [\'No\',\'Yes\']}});});';
                drupal_add_js($js, array('type'=>'inline'));
                $form['actions']['delete_all'] = array(
                    '#markup' => '<a href="/admin/structure/taxonomy/'.$form['#vocabulary']->vid.'/delete-all" class="button confirm" title="Are you sure you want to delete all terms from the '.$form['#vocabulary']->name.' vocabulary?">Delete All Terms</a>',
                    '#weight' => 10,
                    '#attributes' => array('class' => array('button'))
                );
            }

            break;
    }
}

```

Now we need to define the path to our function to delete the terms:

```
function hook_menu() {
    $items = array();
    $items['admin/structure/taxonomy/%/delete-all'] = array(
        'title' => 'Delete all taxonomy terms',
        'type' => MENU_CALLBACK,
        'page callback' => 'delete_all_taxonomy_terms',
        'page arguments'    =>  array(3),
        'access arguments'  =>  array('administer taxonomy'),
    );
    return $items;
}

```

And finally add the functionality to actually delete the terms:

```
function delete_all_taxonomy_terms($vid) {
    $vocabulary = taxonomy_vocabulary_load($vid);
    $query = new EntityFieldQuery();
    $result = $query
        ->entityCondition('entity_type', 'taxonomy_term')
        ->propertyCondition('vid', $vid)
        ->execute();
    foreach($result['taxonomy_term'] as $term) {
        taxonomy_term_delete($term->tid);
    }
    drupal_set_message('All terms have been deleted from the '.$vocabulary->name.' vocabulary');
    drupal_goto('admin/structure/taxonomy/'.$vocabulary->machine_name);
}

```

# Answer
> 2 votes
To bulk delete all taxonomy terms within a vocabulary in Drupal 7, use `taxonomy_term_delete()` and loop through all terms.

Consider the following example.

```
// Get metadata about the vocabulary from its machine name.
$vocab = taxonomy_vocabulary_machine_name_load('TAXONOMY_MACHINE_NAME');

// Get a hierarchical representation of all terms.
$terms = taxonomy_get_tree($vocab->vid);

// Loop through all terms in the taxonomy, deleting each one.
if (!empty($terms)) {
  foreach ($terms as $term) {
    taxonomy_term_delete($term->tid);
  }
}

```

Even easier, if you have Drush and the Devel module installed, you can use the following command to bulk delete all terms in a taxonomy\* from the comfort of your shell.

```
drush generate-terms TAXONOMY_MACHINE_NAME 0 --kill

```

This assumes you have the Devel Generate module enabled, which can be installed with `drush en -y devel && drush en -y devel_generate`.

# Answer
> 2 votes
You can also use the Taxonomy Multi-delete Terms module to delete taxonomy terms.

> This module makes it possible to delete multiple terms at one time. You need to select terms and click on delete button. All selected terms will be deleted. You can also delete all terms at one time.

The module has been developed for Drupal 7 and Drupal 8. It's also covered by the security advisory policy.

# Answer
> 1 votes
I would recommend using Admin Views with VBO to get a view to replace the default display of Taxonomy terms.

What you have to do is the following:

1. Enable those modules of course
2. Enable the view called "Administration: Taxonomy terms"
3. Add a column into the view called "Bulk operations: Taxonomy term"
4. FLUSH CACHES - then go to Admin \> Structure \> Taxonomy \> Specific Vocab

# Answer
> 1 votes
In addition to @mpdonadio code, I just added the snippet to load **vid** by the vocabulary's name. I hope it might help someone to save time.

```
$voca = "forums";
$vobj=taxonomy_vocabulary_machine_name_load("forums");
$vid = $vobj->vid;

$tree = taxonomy_get_tree($vid);

if (count($tree) == 0) {
    print "Nothing to delete.\n";
}
else {
   $tree = array_slice($tree, 0, 1000);

   foreach ($tree as $term) {
    print 'Deleting tid ' . $term->tid . "\n";
    taxonomy_term_delete($term->tid);
   }
}

```

# Answer
> 1 votes
Assuming Entity API is installed we can use entity\_delete\_multiple which should be faster than deleting one every time.

```
define("MY_VOCAB", "tags");

function _custom_zap_vocab() {
  $vocab = taxonomy_vocabulary_machine_name_load(MY_VOCAB);

  $tree = taxonomy_get_tree($vocab->vid);

  $tids = array_map(function($term){
    return $term->tid;
  }, $tree);

  entity_delete_multiple('taxonomy_term', $tids);
}

```

# Answer
> 1 votes
With Drupal 8 and Drush, just run a command like the following one.

```
drush php-eval 'foreach (Drupal::entityTypeManager()->getStorage("taxonomy_term")->loadByProperties(["vid" => ["department", "region"]]) as $c) {$c->delete();}'

```

In this example, I delete all terms in two vocabularies: *department* and *region*.

# Answer
> 0 votes
Just to complete the answers, there is a module doing exactly this: the Taxonomy delete all terms module. I have used it and it works.

> For sites with very large Taxonomy vocabularies, deleting vocabularies may become impossible due to the term deletion request timing out. When that happens before the deletion transaction is complete, the transaction is rolled back, causing no terms at all to be deleted.

The module isn't covered by by the security advisory policy, and it has only a development snapshot for Drupal 7. There aren't branches/releases for Drupal 8 or 9.

# Answer
> 0 votes
My answer is tangentially-related to the original question. Building on kenorb's answer, if you wish to clean ALL vocabularies at your site, you can run the following:

```
drush -v eval '$vocabularies = taxonomy_get_vocabularies(); foreach($vocabularies as $vocabulary) { foreach(taxonomy_get_tree($vocabulary->vid) as $term) { taxonomy_term_delete($term->tid);}}'

```

# Answer
> 0 votes
You can create a drush command named `vocabulary-clean`. Create a module named `drush_taxonomy`, inside the `drush_taxonomy.drush.inc` file put this code:

```
<?php
/**
 * @file
 * Drush commands related to taxonomy.
 */

/**
* Implements hook_drush_command().
*/
function drush_taxonomy_drush_command() {
  $items['vocabulary-clean'] = array(
    'description' => dt("Delete all terms in a vocabulary."),
    'aliases' => array('vc'),
    'arguments' => array(
      'name' => dt('The vocabulary names to clean.'),
    ),
    'examples' => array(
      'drush vocabulary-clean tags' => dt('Delete all the terms in tags vocabulary'),
      'drush vocabulary-clean tags test' => dt('Delete all the terms in tags and test vocabularies'),

    ),
  );
  return $items;
}

/**
 * Callback for the vocabulary clean command.
 */
function drush_drush_taxonomy_vocabulary_clean() {
  $names = func_get_args();

  if (!empty($names)) {
    // Check for duplicate ids.
    $test_names = array_unique($names);
    if (count($test_names) != count($names)) {
      drush_set_error('DRUSH_VOCABULARY_CLEAN_ERROR', dt('You have duplicate vocabulary names.'));
      return;
    }
    //Searching the vocabularies in the site
    $vocabulary_in_db = array_column(taxonomy_get_vocabularies(), 'machine_name');
    $vocabulary_non_existent = array_diff($names, $vocabulary_in_db);
    $vocabulary_existent = array_diff($names, $vocabulary_non_existent);

    if(count($vocabulary_existent) == 0) {
      drush_set_error('DRUSH_VOCABULARY_CLEAN_ERROR', dt("The desired vocabularies to clean doesn't exists."));
      return;
    }

    if(count($vocabulary_non_existent)) {
      drush_print(dt("Non-existent vocabularies:"));
      drush_print(implode(' ', $vocabulary_non_existent));
    }

    foreach ($vocabulary_existent as $name) {
      $vid = taxonomy_vocabulary_machine_name_load($name)->vid;
      foreach(taxonomy_get_tree($vid) as $term) {
        taxonomy_term_delete($term->tid);
      }
    }

    drush_print(dt("Vocabularies cleaned:"));
    drush_print(implode(' ', $vocabulary_existent));
  }
  else {
    drush_set_error('DRUSH_VOCABULARY_CLEAN_ERROR', dt("You must enter at least one vocabulary name."));
  }
}

```

Install the module, run `drush cc drush` to clear the drush cache and use the command like this:

```
drush vc tags

```

or

```
drush vocabulary-clean tags

```

If you want add another alias to the command add elements to the aliases array like this:

```
'aliases' => array('vc', 'voc-clean', 'clean'),

```

And you can use this commands:

```
drush vc tags
drush voc-clean tags
drush clean tags

```

Always the output will be:

```
Vocabularies cleaned:
tags

```

# Answer
> 0 votes
As was said by @texas\_bronius, if you have devel generate enabled, you can use that but taking it one step further, if you also have Drush installed, I would use `drush generate-terms [vocabulary_machine_name] 0 --kill`.

Replace *\[vocabulary\_machine\_name\]* with the machine name of the vocabulary to delete. `0` is the number of taxonomy terms to add and `--kill` is to remove the existing taxonomy terms.

# Answer
> 0 votes
Today, it is also easy to do this with Drupal Console. For example, to delete all the taxonomy terms from the *Tags* vocabulary, it's sufficient to run `drupal taxonomy:term:delete tags`.

# Answer
> 0 votes
Today, within Drupal 8|9, taxonomy\_bulk\_actions contrib module can do that for you.

# Answer
> 0 votes
In Drupal 8+ you can use this:

```
 * Delete terms of chosen vocabulary.
 *
 * @param string $vocabulary
 *   Machine name of the vocabulary.
 *
 * @command module_name:delete-terms-of
 * @aliases delete-terms-of
 */
public function deleteChosenTerms(string $vocabulary) {
  try {
    $taxonomy_storage = $this->entityTypeManager->getStorage('taxonomy_term');
    $terms = $taxonomy_storage->loadByProperties(['vid' => $vocabulary]);

    if (!empty($terms)) {
      foreach ($terms as $term) {
        $term->delete();
      }
    }
  }
  catch (InvalidPluginDefinitionException | PluginNotFoundException | EntityStorageException $e) {
    $this->loggerFactory->error($e->getMessage());
  }
}

```

Then you can run `drush` command e.g: `drush delete-terms-of taxonomy_name`

---
Tags: 7, taxonomy-terms
---


# Question
Title: Entity level constraints - when and how should the validation be implemented?
I've created a custom constraint, and set it on an entity in the entity annotation:

```
* @ContentEntityType(
*   id = "student",
*   label = @Translation("Student"),
    ...
*   constraints = {
*     "StudentConstraints" = {}
*   }

```

I'm finding that the constraints are not called when using the following code:

```
$student = Student::create($array_of_student_stuff);
$student->save();

```

However, they are called if I explicitly validate the entity:

```
$student = Student::create($array_of_student_stuff);
if ($violations = $student->validate()) {
  foreach ($violations as $violation) {
    throw new InvalidStudentParameterException(strip_tags($violation->getMessage()));
  }
}
$student->save();

```

Prior to this debugging, I had expected that by default Drupal would check the constraints when the entity is saved, meaning the above behavior was not what I expected. So my questions are:

1. Is Drupal supposed to be checking entity-level constraints upon save? (This would indicate a problem or something missing in my custom entity code). If Drupal is supposed to be checking constraints upon save by default, where in core does this happen (so I can work backwards to debug)?
2. I see that content entities can have the `validationRequired` property set to `TRUE`. Is it required to set this flag somewhere that will force validation upon save? Maybe that's what I'm missing from my custom code.
3. Or, if Drupal is *not* supposed to execute entity-level constraints upon save by default, is there a reason not to perform validation in an implementation of `::preSave()` in my custom entity? This would result in the entity always being validated upon save, which would seem like a good idea to me, but seeing as the default is not to check constraints upon save, maybe there is a reason not to that I am missing.

# Answer
> 4 votes
1. No, the form or Rest API is responsible for validating the entity before saving it.
2. `validationRequired` enforces validation, not by validating the entity, but by raising an exception if you try to save an entity which was not validated.
3. I wouldn't perform validation in a preSave() method. A form or web service will probably have validated the entity already and you are running the code twice.

---
Tags: entities, 9
---


# Question
Title: Hide images for articles that are older than two weeks
I have a news website built on Drupal 7. I need to hide all images on this website's articles, when the articles are older than two weeks.

I believe a solution would a PHP line to add to the articles' template.

The line would check a article date and add `display:none` to the CSS styles of all images.

Unfortunately, I don't know PHP.

Could someone please help me with the line?

# Answer
> 1 votes
Drupal 7's \*.tpl templates still support PHP. If you have access to those templates, wrap printing the images in an according `if` condition statement. You'll wan't to compare the article creation time with the current time minus two weeks. The article creation time (assuming you use nodes), is saved as UNIX timestamp in the `created` property:

```
<?php if ($node->created < strtotime('-2 weeks')): ?>
  // Your image print statement(s) here.
<?php endif; ?>

```

This will prevent the images from being added to the output, rather than just hiding the images. It is more desirable, as it may safe traffic in the visitors' browser.

Please note, that above example is NOT taking time zones into account.

Read more about the if construct, the strtotime PHP function and possible alternatives with time zone support in the PHP documentation (as teaching PHP actually isn't the scope of this Drupal group).

---
Tags: 7, theming
---


# Question
Title: how can I add contextual links to each of the items in my view?
I am creating a view in drupal and I need that each of the items of this view (are publications) have a contextual link to access more easily to edit this publication. I have tried adding {{title\_suffix.contextual\_links }} in the twig, however the contextual links appear one on top of the other and it doesn't allow to edit well. I need help please

# Answer
> 2 votes
Rendering the links is only one half of the equation.

Contextual links are positioned absolutely. The easiest solution is to make sure that each item has the `contextual-region` class on it's wrapper.

I bet that your view has this wrapper, but none of your items do. The result is that all of the contextual links get positioned at the top right of your view rather than the top right of each item.

---
Tags: users
---