title: Changing label on node edit form

text:
# Question
Title: Changing label on node edit form
I want to change a label/string on a node edit form for a date field.

The label is, "Show end date" - I want to change this to, "Show end date (optional)", but I can't find where this is set anywhere. My guess is that it is some sort of theming change - I took a look at the node\_form and saw nothing.

# Answer
> 2 votes
This code is compatible with Drupal 9. The label is translatable as well.

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

/**
 * Implements hook_form_alter().
 */
function new_module_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if ($form_id == 'node_new_type_form') {
    $form['field_add_date']['widget'][0]['#title'] = t(' Show end date (optional)');
  }
}

```

# Answer
> 0 votes
Simply find the adequate form by calling hook\_form\_alter and letting Devel print out every possible form id:

```
function MYMODULE_form_alter(&$form, &$form_state, $form_id) {
  dpm($form_id);
}

```

When you found your edit form id (let's say it's `'xyz_edit'`) you might inspect the form like so:

```
function MYMODULE_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id = 'xyz_edit') {
    dpm($form);
  }
}

```

Now find the label by doubleclicking through the `dpm` output and replace the label whith your desired text.

# Answer
> 0 votes
You can easily do this using hook\_form\_alter(&$form, &$form\_state, $form\_id) Alter

Hook\_form\_alter is working before your form shown Use hook\_form\_alter in Your site theme template.php file.

Like your theme name is bartik,then use this function their

```
function bartik_form_alter(&$form, &$form_state, $form_id)
{
//Firstly get form id then use below code

if($form_id=='Your_edit_form_id'){
//here print $form and chek which field label you want to change and then alter that field //like

$form['date'][#label]='Date Field(optional)';
}

}

```

may this will help you.

---
Tags: nodes, datetime
---


# Question
Title: I can't uninstall any module after I uninstalled deprecated modules
I have Drupal 9.5 installed and running. I have uninstalled 3 deprecated modules (*Aggregator*, *Color*, and *RDF*). After that, I can't uninstall any module using the user interface. It gives the following error.

> LogicException: Missing bundle entity, entity type comment\_type, entity id comment. in Drupal\Core\Entity\EntityType-\>getBundleConfigDependency() (line 885 of web/core/lib/Drupal/Core/Entity/EntityType.php).

This error is linked to the *Field Layout* core module. The error shows when I try to uninstall that module. Rather showing the confirmation, it shows the error. Then, whatever module I try to uninstall, it shows the same error.

So I re-installed the *RDF* module. After some time (I don't know what I did), I can see the uninstall confirmation page for modules. When I try to uninstall the *Field Layout* module, it shows the error, which is shown also for other modules.

The site is working okay, but uninstalling modules is not possible. I can uninstall modules using Drush. I ran `drush cr` and `drush updb`, but that error is still shown.

Could anyone help me?

# Answer
I don't think it's possible to reproduce this uninstalling and installing of unrelated modules.

The error message itself says the comment type `comment` is missing. This is part of the standard profile and could be easily recreated, either in UI */admin/structure/comment* or by importing:

*web/core/profiles/standard/config/install/comment.type.comment.yml*

```
langcode: en
status: true
dependencies: {  }
id: comment
label: 'Default comments'
target_entity_type_id: node
description: 'Allows commenting on content'

```

Goto */admin/config/development/configuration/single/import*, select the configuration type "Comment type" and paste the configuration.
> 3 votes

---
Tags: drush
---


# Question
Title: Overriding migration source URL with hook_migration_plugins_alter
In D8, using Migrate Plus (8.x-4.0-beta3), i am pulling in xml records from an external, non-Drupal API. I have an unwieldy source URL that needs to be passed dynamically to the migrate\_plus migration YML file. That doesn't seem possible, but I have read in comments that you can alter the source URL of a migration with hook\_migrations\_plugins\_alter. However when i do something like below, the URL does not change.

```
function my_module_migration_plugins_alter(array &$migrations){

  foreach ($migrations as $id => $migration) {
    if ($migration['id'] == 'my_migrate_id'){
      unset($migrations['source']['urls']);
      $migration['source']['urls'] = 'http://example_api';
    }
  }
}

```

I realize I may need to override the source plugin; if this is true what method do i use to alter the source url?

# Answer
> 4 votes
If you don't want the overridden config to be saved to the database you can override it on the fly with ConfigFactoryOverrideInterface. I'm storing the file path in State API storage via a custom file upload form.

**my\_module.services.yml**

```
services:
  my_module.overrider:
    class: \Drupal\my_module\MyModuleOverrides
    tags:
      - {name: config.factory.override, priority: 5}

```

**src/MyModuleOverrides.php**

```
<?php

namespace Drupal\my_module;

use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Config\ConfigFactoryOverrideInterface;
use Drupal\Core\Config\StorageInterface;

/**
 * Overrides migration configuration.
 */
class MyModuleOverrides implements ConfigFactoryOverrideInterface {

  /**
   * {@inheritdoc}
   */
  public function loadOverrides($names) {
    $overrides = array();
    $state = \Drupal::state();
    $overrides['migrate_plus.migration.my_migration']['source']['path'] = $state->get('my_module.my_migration_source_uri');
    return $overrides;
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheSuffix() {
    return 'MyModuleOverrider';
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheableMetadata($name) {
    return new CacheableMetadata();
  }

  /**
   * {@inheritdoc}
   */
  public function createConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION) {
    return NULL;
  }

}

```

# Answer
> 3 votes
Your approach should work, as others have said you will need to clear cache. However there is a problem with your implementation. You're overriding the url on the $migration variable but never setting that back to the $migrations array.

Try this:

```
function my_module_migration_plugins_alter(array &$migrations){
  $migrations['my_migrate_id']['source']['urls'] = 'http://example_api';
}

```

# Answer
> 1 votes
There is an issue/patch for the Migrate Plus module, which solves this common problem on a generic YAML basis. It allows to use a PHP callback function as source for the URLs.

In your migrate YAML:

```
source:
  plugin: url
  data_fetcher_plugin: http
  data_parser_plugin: json
  urls:
    callback: my_module_migrate_urls

```

In your custom module file:
(The current migration is passed as parameter to the callback)

```
use Drupal\migrate\Plugin\MigrateIdMapInterface

function my_module_migrate_urls(MigrationInterface $migration): array {
      $ids = my_module_get_all_ids();
      $urls = [];
      foreach ($ids as $id) {
        $urls[] = "https://example.com/jsonapi/node/article/{$id}";
      }
      return $urls;
}

```

For more information see the linked issue, it is documented quite well.

# Answer
> 0 votes
```
unset($migrations['source']['urls']);

```

You've got a typo here - it should be $migration, not $migrations.

Also note that plugin definitions are cached - you may need to do a cache rebuild after making changes to your alter hook for them to take effect.

# Answer
> 0 votes
I was actually running into the same issue until this morning. The source URLs are stored within the Config state (which is in the database).

I successfully altered the URL by doing the following:

```
function my_module_migration_plugins_alter(&$migrations) {
    foreach ($migrations as $id => $migration) {
        if ($migration['id'] == 'my_migrate_id'){
            $config = \Drupal::service('config.factory')->getEditable('migrate_plus.migration.'.$migration['id']);

            $config->set('source.urls', 'http://example_api');
            $config->save();
        }
    }
}

```

# Answer
> 0 votes
In order to have dynamic URLs but keep all configuration in the YML file rather that scattered in alter hooks, we can use a custom source plugin to replace placeholders in the URL, and use standard Migrate processors to provide the value for the URL.

For example to import a list of persons that have their birthday today:

```
source:
  plugin: processable_url
    data_fetcher_plugin: http
    data_parser_plugin: json
    urls:
      - 'https://mysource.com/api/persons?birthdate={{ today }}'
    url_processors:
      - key: today
        process:
          plugin: default_value
          default_value: 'today'
      - key: today
        process:
          plugin: strtotime
      - key: today
        process:
          plugin: format_date
          from_format: 'U'
          to_format: 'Y-m-d'

```

`ProcessableUrl` migrate source plugin:

```
<?php

declare(strict_types = 1);

namespace Drupal\mymodule\Plugin\migrate\source;

use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\MigrateExecutable;
use Drupal\migrate\Plugin\MigratePluginManagerInterface;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Row;
use Drupal\migrate_plus\DataParserPluginInterface;
use Drupal\migrate_plus\Plugin\migrate\source\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a migration source for a URL with processable placeholders.
 *
 * Replaces Jinja style {{ placeholders }}. Useful for REST APIs that require
 * dynamic values in the URL.
 *
 * Make sure to start with a `default_value` processor, or create your own
 * processor plugin that provides the required value.
 *
 * @MigrateSource(
 *   id = "processable_url"
 * )
 */
class ProcessableUrl extends Url implements ContainerFactoryPluginInterface {

  /**
   * The URL processors.
   *
   * @var array
   */
  protected array $urlProcessors = [];

  /**
   * Constructs a new ProcessableUrl migrate source plugin.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, protected MigratePluginManagerInterface $migratePluginManager) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $migration);

    $this->urlProcessors = $configuration['url_processors'] ?? [];
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $migration,
      $container->get('plugin.manager.migrate.process'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getDataParserPlugin(): DataParserPluginInterface {
    if (!isset($this->dataParserPlugin)) {
      $configuration = ['urls' => $this->processUrls()] + $this->configuration;
      $this->dataParserPlugin = \Drupal::service('plugin.manager.migrate_plus.data_parser')->createInstance($this->configuration['data_parser_plugin'], $configuration);
    }
    return $this->dataParserPlugin;
  }

  /**
   * Processes the source URLs.
   *
   * @return array
   *   The processed URLs.
   */
  protected function processUrls(): array {
    $placeholders = $this->processPlaceholders();
    return array_map(function (string $url) use ($placeholders): string {
      foreach ($placeholders as $placeholder => $value) {
        $url = preg_replace('/\{\{\s*' . $placeholder . '\s*\}\}/', $value, $url);
      }
      return $url;
    }, $this->sourceUrls);
  }

  /**
   * Processes the placeholders.
   *
   * @return array
   *   The processed placeholders.
   */
  protected function processPlaceholders(): array {
    // @todo Doesn't support row based processors.
    $executable = new MigrateExecutable($this->migration);
    $row = new Row();

    $placeholders = [];
    foreach ($this->urlProcessors as $definition) {
      $key = $definition['key'];
      $plugin_definition = $definition['process'];
      $plugin = $this->migratePluginManager->createInstance($plugin_definition['plugin'], $plugin_definition);
      $placeholders[$key] = $plugin->transform($placeholders[$key] ?? NULL, $executable, $row, $key);
    }

    return $placeholders;
  }

}

```

And a simple `StringToTime` processor:

```
<?php

declare(strict_types = 1);

namespace Drupal\mymodule\Plugin\migrate\process;

use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;

/**
 * Converts a string representation of a date to a Unix timestamp.
 *
 * @MigrateProcessPlugin(
 *   id = "strtotime"
 * )
 */
class StringToTime extends ProcessPluginBase {

  /**
   * {@inheritdoc}
   */
  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
    return strtotime($value);
  }

}

```

---
Tags: 8, hooks, migrations
---


# Question
Title: Converting date in field-created.html.twig
node-created.html.twig to override some html. That works fine. Although the date is being displayed as `Thu, 04/28/2022 - 11:39` which I want to be something like `May 2, 2023`. Is this possible within this piece of twig code?

```
{%
  set classes = [
    'field',
    'field--name-' ~ field_name|clean_class,
    'field--type-' ~ field_type|clean_class,
    'field--label-' ~ label_display,
    'created-label',
  ]
%}
<span{{ attributes.addClass(classes) }}>
  {%- for item in items -%}
    {{ item.content }}
  {%- endfor -%}
</span>

```

# Answer
> 0 votes
As a learning experience

I suggest you look at the twig date function https://twig.symfony.com/doc/3.x/filters/date.html

```
{{ field_crated|date("m/d/Y") }}
  or
 {{ crated|date("m/d/Y") }}

```

then also if that does not work

```
 function THEME_preprocess_field__node(&$variables) {

```

---
Tags: 9
---


# Question
Title: How do I use a custom twig template with a render element?
I am fairly new to Drupal 8 module development, so I decided to follow a tutorial at https://docs.acquia.com/articles/drupal-8-creating-elements-theming-elements-and-managing-frontend-libraries, which describes the process of setting up a module with a render element using a twig template. I followed the code exactly and have included the sections I'm working on below (I did not include the info and routing code).

I am seeing my controller return the correct elements, however I am unable to see the twig template render with additional markup. From what I understand the element 'my\_element' should be getting returned from the controller's second element in the render array ... 'type'=\>'my\_element'. Also my\_element is defined in hook\_theme in the module file but I think I am getting confused on how this links to the template called my-element.html.twig. Can anyone provide any tips or suggestions?

theme\_example.module

```
<?php

/**
* Implements hook_theme().
*/
function theme_example_theme() {
  $items = [
    'my_element' => [
      'render element' => 'element',
    ],
  ];
  return $items;
}

```

src/Controller/ThemeExampleController.php

```
<?php
/**
 * @file
 * Contains \Drupal\theme_example\Controller\ThemeExampleController.
 */

namespace Drupal\theme_example\Controller;

use Drupal\Core\Controller\ControllerBase;

/**
 * Controller routines for theme example routes.
 */
class ThemeExampleController extends ControllerBase {

  public function simple() {
    return [
      'example one' => [
        '#markup' => '<div>Markup Example</div>',
      ],
      'example two' => [
        '#type' => 'my_element',
        '#label' => $this->t('Example Label'),
        '#description' => $this->t('This is the description text.'),
      ],
    ];
  }
}

```

src/Element/MyElement.php

```
<?php
/**
 * @file
 * Contains \Drupal\theme_example\Element\MyElement.
 */

namespace Drupal\theme_example\Element;

use Drupal\Core\Render\Element\RenderElement;
use Drupal\Core\Url;

/**
 * Provides an example element.
 *
 * @RenderElement("my_element")
 */
class MyElement extends RenderElement {
  /**
   * {@inheritdoc}
   */
  public function getInfo() {
    $class = get_class($this);
    return [
      '#theme' => 'my_element',
      '#label' => 'Default Label',
      '#description' => 'Default Description',
      '#pre_render' => [
        [$class, 'preRenderMyElement'],
      ],
    ];
  }

  /**
   * Prepare the render array for the template.
   */
  public static function preRenderMyElement($element) {
    // Create a link render array using our #label.
    $element['link'] = [
      '#type' => 'link',
      '#title' => $element['#label'],
      '#url' => Url::fromUri('http://www.drupal.org'),
    ];

    // Create a description render array using #description.
    $element['description'] = [
      '#markup' => $element['#description']
    ];

    $element['pre_render_addition'] = [
      '#markup' => 'Additional text.'
    ];

    // Create a variable.
    $element['#random_number'] = rand(0,100);

    return $element;
  }
}

```

templates/my-element.html.twig

```
{#
/**
 * @file
 * Default theme implementation to my_element.
 *
 * Available variables:
 * - element: Element that will be rendered.
 * - element['link'] : a link
 * - element['description'] : a description
 * - element['pre_render_addition'] : added during the #pre_render function
 * - element['random_number'] : a random number variable, won't be printed with the element.
 *
 * @ingroup themeable
 */
#}

<div class="myElement">
  <div class="randomNumber">Random Number: {{ element['#random_number'] }}</div>
  <p>{{ element.description }}</p>
  {{ element.link }}
</div>

{# Debug output #}
<div>
  <h3>We print the element</h3>
  {{ element }}
  <h3>Link</h3>
  {{ element.link }}
  <h3>Description</h3>
  {{ element.description }}
  <h3>#pre_render addition</h3>
  {{ element.pre_render_addition }}
  <h3>Random number (not printed when we printed the whole element</h3>
  {{ element['#random_number'] }}
</div>

```

# Answer
Your code is correct. Explaining the flow below:

The key for item in the array is what you will need to call the template And the key is : my\_element

```
<?php

/**
* Implements hook_theme().
*/
function theme_example_theme() {
  $items = [
   'my_element' => [
   'render element' => 'element',
    ],
  ];
  return $items;
 }

```

Then creating twig template and replacing underscores with dashes like this : my-element.html.twig under templates folder. By render element which is 'element' ,calling each field. After creating both the file , where you are returning your render array make a call to your twig template. which is below:

```
class MyElement extends RenderElement {
/**
* {@inheritdoc}
*/
public function getInfo() {
  $class = get_class($this);
   return [
    '#theme' => 'my_element',
    '#label' => 'Default Label',
    '#description' => 'Default Description',
    '#pre_render' => [
       [$class, 'preRenderMyElement'],
     ],
   ];
 }

```

Then using the key my\_element in controller for fetching all from preRenderMyElement().

```
 public function simple() {
   return [
     'example one' => [
     '#markup' => '<div>Markup Example</div>',
     ],
     'example two' => [
     '#type' => 'my_element',
     '#label' => $this->t('Example Label'),
     '#description' => $this->t('This is the
      description
      text.'),
      ],
    ];
  }

```
> 1 votes

# Answer
All your code works.

I created your module, adding a routes and info file to make it work, enabled it, visited `/theme-example` and I could see you template was being used.

See https://github.com/dahousecat/theme-example for the whole module.
> 0 votes

---
Tags: theming
---


# Question
Title: Getting old node field value in update hook
Hi I'm using the node update hook in my custom .module file in my Drupal 9 site. I have a field called `field_amount`. And I was wondering if I can access the old value of that field within my update hook and the new value too?

```
function custom_module_node_update(\Drupal\node\NodeInterface $node) {
  //access the old value of field_amount and the new value
}

```

# Answer
> 1 votes
As the docs say:

```
Get the original entity object from $entity->original.

```

In your case that would be, for example:

```
function custom_module_node_update(\Drupal\node\NodeInterface $node) {
  $old_field_value = $node->original->field_foo->value;
}

```

---
Tags: nodes
---


# Question
Title: What is the meaning of "fields pending deletion"?
I have a Drupal 9.5.3 installation which is not customized in any way; no custom modules or themes.

I want to remove the Comment core module but I can't and the reason that is given is:

> Fields pending deletion

That's strange because I have already deleted all comments, all comment fields in any content type and all comment types and I can't think of anything else relevant to delete.

What is the meaning of this phrase "Fields pending deletion" and what should be done to remove the module?

From a bit of reading<sup>(1)(2)</sup> I understand that running `drush php-eval 'field_purge_batch(500);'` or `drush php-eval 'field_purge_batch(10000)';` but it is important for me as a someone who is not a PHP programmer to understand in simple terms what is the meaning of the phrase and why should these commands help.

# Answer
The fields are deleted by cron runs. If you have a lot of comments this can take a while.

To speed things up you can use the mentioned Drush command, but if Drush is not available there is also a UI solution. Go to the page */admin/config/development/configuration/single/import*, select "Simple configuration", enter the configuration name `field.settings` and add this config with a higher batch size:

```
purge_batch_size: 1000

```

See this screenshot:

Now each automatic cron run will delete more items and the waiting time will be much shorter. If you are in a hurry you can press the button on this page */admin/config/system/cron* until the job is finished.
> 1 votes

---
Tags: 9, comments
---


title: How can I use another PHP version?

text:
# Question
Title: How can I use another PHP version?
On the live server I use differents Drush and PHP versions for Drupal 8.4 and 7. I install Drush 9 to the ~/drush9 directory with `composer require drush/drush:9.*`.

On the development server and Drupal 8.4, I use the site.alias.yml file for Drush 9.

```
live:
  host: hostingexample.com
  user: user
  root: /home/user/example.com/public_html/
  uri: http://example.com/
  paths:
    drush-script: '/opt/php70/bin/php /home/user/drush9/vendor/bin/drush'

```

When I run `drush @site.live ev 'print PHP_VERSION'` I get this error.

> bash: /opt/php70/bin/php /home/user/drush9/vendor/bin/drush: No such file or directory

If I add these lines in the .bash\_profile file and change `drush-script: '/home/user/drush9/vendor/bin/drush'` in site.alias.yml, I get an error because the default PHP version is 5.3.

```
alias php=/opt/php70/bin/php
alias drush9=~/drush9/vendor/bin/drush

```

> PHP Parse error: syntax error, unexpected '\[' in /home/user/drush9/vendor/drush/drush/src/Config/Environment.php on line 98

How can I use a different PHP for the live server and set it for Drush 9 aliases?

# Answer
Try editing both the `.bash_rc` and `.bash_profile` files so that you include the directory where the version of PHP you want is FIRST (before existing PATH):

(add this line or adjust if existing):

`export PATH="/opt/php70/bin:$PATH"`

As a last ditch effort, you can set the env variable drush looks for as well in these files:

`DRUSH_PHP=/opt/php70/bin/php`
> 1 votes

# Answer
From **drush 10+** it is possible to set the PATH variable within self.site.yml, so the OP's situation would be resolved as follows:

```
live:
  host: hostingexample.com
  user: user
  root: /home/user/example.com/public_html/
  uri: http://example.com/
  paths:
    drush-script: '/home/user/drush9/vendor/bin/drush'
  env-vars:
    PATH: /opt/php70/bin/php:/bin:/usr/bin

```

**N.B.** I *do not* think this will work with drush 9 (but I'm happy to be proved otherwise)
> 0 votes

---
Tags: drush
---


title: How do I translate Field Group labels?

text:
# Question
Title: How do I translate Field Group labels?
I have two field sets

In the admin/config/regional/translate I have translated the "Account settings" label

But in the Form display I still have the english label:  What can I do to translate the field group labels?

# Answer
> 1 votes
https://www.drupal.org/files/issues/2020-02-05/2546212-119.patch \- Did the trick for me. It adds a "Translate form display" tab, like it is shown below.

# Answer
> 1 votes
Field groups are configurations which are not translated via the translation interface. I am quite sure that the string "Account settings" that is translated in your project does not come from your field group. Just change the label of a field group to any other string - you will not find the string in the translation interface.

You need to activate the "Configuration Translation" module.

# Answer
> 0 votes
I was able to sort of work around this with theming. In my situation I am unable to use the patch so this is what I did. I created a theme suggestion that includes my "bundle" and "group name" so I can override the fieldset.html.twig templates for the specific field groups I want to translate.

In my themes .theme file:

```
/**
 * Implements hook_theme_suggestions_HOOK_alter().
 */
function THEMENAME_theme_suggestions_fieldset_alter(array &$suggestions, array $variables, $hook) {
  $element = $variables['element'];
  $group_name = $element['#group_name'];
  $bundle = $element['#bundle'];
  if (!empty($group_name) && !empty($bundle)) {
  $suggestions[] = 'fieldset__' . strtr($bundle, '-', '_') . '__' . strtr($group_name, '-', '_');
  }
}

```

Now I can add a template for each of my content types field groups using `fieldset--CONTENT-TYPE--GROUP-NAME.html.twig`.

At this point I can copy the `fieldset.html.twig` code into my template and replace the legend with a translatable string.

```
<legend{{ legend.attributes }}>
  <span{{ legend_span.attributes.addClass(legend_span_classes) }}>{{ 'Translatable Text Using User Interface Translation'|trans }}</span>
</legend>

```

Make sure you visit your page in the language you want to translate before searching for your string in the User Interface Translation UI. It won't find your string until you do.

Bam! Easy, narrowly scoped work around until field groups gets the patch into a release.

---
Tags: 8
---


# Question
Title: How do I add Custom fields to the Order Information Checkout page in Commerce 2?
I would like to add a set of additional fields to the order summary so after a user adds a product they would need to fill out some conditional fields of information alongside their billing information.

Drupal Commerce 2 documentation is pretty lacking at the moment and I am expecting to change the YAML files to add new fields. However, there seem to be various places to add new fields but nothing is showing up as additional fields on the order summary page.

What are the steps to add new fields?

# Answer
> 3 votes
Since the release of Commerce 2, there are some new recent changes that add an additional way to add fields to an order. Which maybe better than the previous way.

To add a new field to the order:

1. Add your field on `/admin/commerce/config/order-types/default/edit/fields`
2. On the Manage form display add the new **CUSTOM DISPLAY SETTINGS** checkbox **Checkout**
3. On your new display for checkout add only your field.
4. Then navigate to the Oder Checkout flows, we are using default. /admin/commerce/config/checkout-flows add the **Order fields: Checkout** to the Order Information section. You can add a fieldset at this point in the options.
5. Save and view the field.

For this to display in the **commerce-order-receipt.html.twig** you will need to add a twig changing the field name for your machine name.

```
  {% if order_entity.field_name.value %}
    {{ order_entity.field_name.value }}
  {% endif %}

```

Then finally. To have the custom fields on order admin view page you will need to add this patch.

# Answer
> 3 votes
I found the answer to this, it's not very obvious at the moment due to the lack of documentation but it's blinding simple when you know how.

Install the new profile module. https://www.drupal.org/project/profile

Edit the page:

```
configuration > people > user profile

```

Here you can add commerce2 user pane or new fields.

For me I needed to make a new Content type form that the user could create and fill out as part of the checkout process, so I used the inline entity form.

Really you could use any field collections or paragraph's to add to the user fields as I notice there is a new patch to support field group and paragrahs.

I hope that helps someone else looking for this answer.

# Answer
> 0 votes
The original answer is outdated. In order to add forms to the checkout you need to update the "customer" user profile.

Edit the customer profile and add fields as necessary (admin/config/people/profile-types/manage/customer).

Then configure your displays as needed.

Once this is done the custom fields will be shown during checkout.

---
Tags: 8, commerce
---


# Question
Title: Getting 400 Error as A client error happened
On our multi-site platform using Drupal 8 hosted on acquia we are getting 400 error response on all the hosted websites

The error is not persistent but only occurs when user comes through a referrer like google/bing or even a link from JIRA/slack.

During this error, we see an error about 400 in Drupal Watchdog as

> drupal-watchdog.log-20181003:133708:Oct 2 13:46:06 d8: https://www.example-domain.com|1538487966|php||https://www.example-domain.com/|https://www.google.com/|0||Symfony\Component\HttpKernel\Exception\BadRequestHttpException: Untrusted Host "www.google.com". in Symfony\Component\HttpKernel\HttpKernel-\>handle() (line 71 of /mnt/www/html/d8/vendor/symfony/http-kernel/HttpKernel.php). request\_id="v-8187594e-c649-11e8-b00d-67f570bebe6a"

As an attempt to fix the issue we tried adding 'www.google.com' in `trusted_host_pattern` in settings.php

```
  $settings['trusted_host_patterns'] = array(
    '^example-domain\.com$',
    '^.+\.example-domain\.com$',
    '^google\.com$',
    '^.+\.google\.com$',
  );

```

but that did not fix the issue either, we are still getting errors with that same logs.

We are using varnish for reverse proxy and cloudflare, if that looks to be an issue, since that's the thing between Drupal and client request.

Any help would be much appreciated.

# Answer
To avoid this, make sure you have an up-to-date Drupal install, adding this code to your settings.php file (or post-settings-php hook for Site Factory customers):

unset($\_SERVER\["HTTP\_FORWARDED"\]);

Source: https://acquia.my.site.com/s/article/360044285973-Troubleshooting-the-A-client-error-happened-message-in-content
> 0 votes

---
Tags: 8
---


# Question
Title: How do I programmatically assign a new book when a new node is created?
For a given content type, if a new node is created and no book is assigned, I'd like to create a new book and assign the new node to that book.

(some background - that content type is the book cover, and for editors to create a new book I ask them to create a cover first. this makes more sense for them. The 'add child page' links will then appear automatically. I know they can select 'new book' on the create page, but that makes no sense in that context - alternatively, if I could have that selected by default, that would work )

# Answer
> 0 votes
Using @rafael 's answer:

```
/**
 * Implements hook_node_insert().
 *
 * - assigns a new book for a new MYCONTENTTYPE if none was selected
 */

function MYMODULE_node_insert($node) {
  if ($node->getType() == 'MYCONTENTTYPE'
    && property_exists($node,'book')
    && empty($node->book['bid'])) {
      $node->book['bid'] = 'new';
      $node->book['plid'] = 0;
      $node->save();
  }
}

```

# Answer
> 1 votes
you can use a hook implementation in a custom module

```
    /**
 * Implements hook_node_insert().
 */
function mymodule_node_insert($node) {
  // Check if the node is of the desired content type and no book is assigned.
  if ($node->getType() == 'my_content_type' && empty($node->book['bid'])) {
  // Get the book storage handler.
  $bookStorage = \Drupal::entityTypeManager()->getStorage('book');

  // Create a new book entity.
  $book = $bookStorage->create(array(
    'type' => 'book',
    'title' => $node->getTitle(),
    'uid' => $node->getOwnerId(),
  ));
    $book->save();

    // Assign the node to the new book.
    $node->book['bid'] = $book->id();
    $node->book['plid'] = 0;
    $node->save();
  }
}

```

---
Tags: hooks, 9, nodes
---


# Question
Title: Add specific CSS class to form elements from the address field
I try to add a custom css class to a form element inside my address field for e.g the `postal_code` field and another class to the `given_name` and `family_name` field.

I tried it with a `my_theme_theme_suggestions_form_element_alter`. But then I have to extend all the twig files like `form-element--equal-width-column.html.twig`, for just adding the css class.

```
function my_theme_theme_suggestions_form_element_alter(array &$suggestions, array $variables, $hook) {

  if (in_array($variables['element']['#attributes']['class']['0'], ['family-name', 'given-name'])) {
    $suggestions[] = $hook . '__equal_width_column';
  }
}

```

Is it possible to just add a css class to specific form-elements div's, so I don't have to extend all those twig files?

# Answer
> 1 votes
You can use the `hook_form_FORMID_alter` in order to add classes to the fields you want to.

```
/**
* Implements hook_form_FORM_ID_alter();
*/
function hook_form_FORM_ID_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  $form['my_custom_field']['#attributes']['classe'][] = "my-custom-class"
}

```

# Answer
> 0 votes
To get form id , create a custom module and in .module write a hook which is given below:

```
/**
* Implements hook_form_alter();
*/
function custom_form_form_alter(&$form,
\Drupal\Core\Form\FormStateInterface $form_state, $form_id){
 dd($form);
}

```

Then you have to go to the particular route for the form. You will find this

Then you can follow **benftwc's** answer: use the `hook_form_FORM_ID_alter()` to add css class.

---
Tags: 8, theming
---


# Question
Title: Are there JavaScript events sent by Webform Conditionals?
I am using the *iCheck* module for checkboxes, and the checkbox is initially hidden by the Webform module. Somehow, the Webform module also sets it to disabled while hidden, which is nice.

Once it gets shown, the *disabled* status is removed from the webform, but the *iCheck* module doesn't notice. Calling `iCheck.update()` fixes it, but I need to catch an event to do that.

Are there JavaScript events sent by a webform conditionals?

# Answer
> 1 votes
The Webform module for Drupal 8 is using Drupal's States API, which triggers custom events that are prefixed with 'state:\*'.

Events include...

* state:disabled
* state:required
* state:visible
* state:checked
* state:collapsed

# Answer
> 1 votes
I've spent a while looking at this in D9 it seems the events that are used don't get propagated which is probably why this is not documented or used elsewhere.

Having tweaked the JS to emit events on a reasonably complex form it's not as useful as you would think. There are a lot of events pushed and it's not entirely clear what is happening internally (it may be more clear on a simple form)

An alternative that seems to be working is to listen for change events on the elements that you are interested in, then you can check the state (for example visible) and react to that. It's not ideal and you have to check for multiple changes but it does work.

---
Tags: 7, webforms
---


# Question
Title: Scroll to view's result after applying exposed filter
when a user submit the view's exposed form the site is reloaded and the page's focus is not on the view's results but on the very top of the page. I use quite a few exposed filters above a table view. Locating the filters somewhere else is not possible (e.g. sidebar). I am trying to solve this using JS. My attempts:

```
(function ($) {

  'use strict';

  $(document).ready(function() {
    if( $('.views-table').length ){
      $("html, body").animate({ scrollTop: $(".views-table").offset().top }, "slow");
    }
  });

}(jQuery));

```

This works when I apply a filter but it also works when I used load the view page without filters applied. Unfortunately, I don't have any style classes that are only applied to the filtered view table.

```
(function ($) {

Drupal.behaviors.form_submit_processor = {
  attach: function (context, settings) {
    $("form#views-exposed-form-standortanalyse-page-1").submit(function(e) {
      $("html, body").animate({ scrollTop: $(".views-table").offset().top }, "slow");
    });
  }
}

}(jQuery));

```

When using this approach the scrolling works when hitting the submit button but after page reload the scroll setting is lost again.

Does someone has any idea how this can be achieved?

# Answer
> 0 votes
Putting this here in case anyone runs into this issue in the future — you can disable the autoscroll functionality by placing this in your code to override:

```
Drupal.AjaxCommands.prototype.viewsScrollTop = null;

```

---
Tags: views
---


title: Switch theme by content type

text:
# Question
Title: Switch theme by content type
My company has just purchased a Drupal theme to apply to some new pages they want to add to an existing site. This theme is different to the existing theme of the site.

They want to create a new content type which will work with the new theme and not affect any of the existing pages using the default theme. So basically we want to switches themes based on what the content type of the page is. They also want the freedom to create any URL they choose so using a multi-site configuration is out of the question.

I've seen a number of posts on the internet showing this is possible in Drupal 7. Is this possible in Drupal 8 and how can I make it work?

# Answer
> 4 votes
In Drupal 7, it was straight forward possible by implementing `hook_custom_theme()` in your custom module.

As for Drupal 8, you will need to create a `ThemeNegotiator`. Link to the respective change record : 'theme callback' and hook\_custom\_theme() replaced by theme negotiators.

In the `applies()` function of the class you can apply your logic and return the theme you wish to have in the `determineActiveTheme()` function.

Note that, you might have to create multiple `ThemeNegotiator`s for multiple conditions.

# Answer
> 2 votes
Thanks AjitS for the info, I've used your answer to write the following working code:

```
class ThemeNegotiator implements ThemeNegotiatorInterface {
  /**
   * {@inheritdoc}
   */
  public function applies(RouteMatchInterface $route_match) {
    // Use this theme on a certain route.
    $node = $route_match->getParameter('node');
    if (!is_null($node) && $node instanceof \Drupal\node\Entity\Node) {
      return $node->getType() == 'content_type_machine_name';
    }

    // apply default theme
    return false;
  }

  /**
   * {@inheritdoc}
   */
  public function determineActiveTheme(RouteMatchInterface $route_match) {
    // Here you return the actual theme name.
    return 'new_theme_machine_name';
  }
}

```

# Answer
> 1 votes
Building on @adrianp, this is how you would do it for the form pages (edit/add).

my\_module.services.yml

```
services:
  theme.negotiator.my_module:
    class: Drupal\my_module\Theme\MyModuleThemeNegotiator
    arguments: ['@config.factory']
    tags:
      - { name: theme_negotiator, priority: 1000  }

```

in my\_module/Routing/MyModuleThemeNegotiator.php

```
namespace Drupal\my_module\Theme;

use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Theme\ThemeNegotiatorInterface;
use Symfony\Component\Routing\Route;

/**
 * My ModuleTheme Negotiator for some content types add/edit.
 *
 * We must use a theme negotiator instead of routeSubscriberBase->alterRoutes
 * to make the route admin, as the same route works all content types
 * and we only want one.
 *
 * How determining themes work in Drupal:
 * - When is comes time for Drupal to render your page and select a theme,
 * it looks for all classes that implement ThemeNegotiatorInterface.
 * - All implementations begin to be called in priority order, from the highest
 * to the lowest.
 * - If the applies method returns false, Drupal moves on to the next
 * implementation.
 * - If the applies method returns true, Drupal calls the determineActiveTheme
 * method in the same implementation.
 * - If determineActiveTheme returns null, Drupal moves on to the next
 *  implementation.
 * - If determineActiveTheme returns a theme name, Drupal will use this theme,
 * and no further implementations are called.
 * - Drupal has its own default implementation. If no other implementations
 * selects a theme, the default will select a theme.
 *
 * @see https://jimconte.com/blog/web/dynamic-theme-switching-in-drupal-8
 */
class MyModuleThemeNegotiator implements ThemeNegotiatorInterface {

  /**
   * The config factory.
   *
   * @var \Drupal\Core\Config\ConfigFactory
   */
  protected $configFactory;

  /**
   * Service constructor.
   *
   * @param \Drupal\Core\Config\ConfigFactory $configFactory
   *   Configfactory.
   */
  public function __construct(ConfigFactory $configFactory) {
    $this->configFactory = $configFactory;
  }

  /**
   * {@inheritdoc}
   */
  public function applies(RouteMatchInterface $route_match) {
    $route = $route_match->getRouteObject();
    if (!$route instanceof Route) {
      return FALSE;
    }
    $routes_to_match = ['node.add', 'entity.node.edit_form'];
    if (in_array($route_match->getRouteName(), $routes_to_match)) {
      return TRUE;
    }
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function determineActiveTheme(RouteMatchInterface $route_match) {
    $applicable_bundles = [
      'news',
      'standard_page',
    ];
    $switch_theme = FALSE;
    switch ($route_match->getRouteName()) {
      case 'node.add':
        if ($entity_type_param = $route_match->getParameter('node_type')) {
          if (in_array($entity_type_param->id(), $applicable_bundles)) {
            $switch_theme = TRUE;
          }
        }
        break;

      case 'entity.node.edit_form':
        if ($entity_param = $route_match->getParameter('node')) {
          if (in_array($entity_param->bundle(), $applicable_bundles)) {
            $switch_theme = TRUE;
          }
        }
        break;

    }

    if ($switch_theme) {
      // Return the machine name of the front-end theme.
      return $this->configFactory->get('system.theme')->get('default');
    }
    // Not applicable.
    return NULL;
  }

}

```

# Answer
> 1 votes
In addition to programmatic ways nowadays there are plenty modules for d8+. We using this one https://www.drupal.org/project/theme\_switcher to apply a different theme on specific node types detail pages and URLs. Alternatives discussed here: https://www.drupal.org/project/theme\_switcher/issues/3348676

---
Tags: 8, theming
---


# Question
Title: How to remove the "No front page content has been created yet." message from the front page?
I only want blocks on the homepage. Because I don't have any nodes, and I want to keep the homepage as /node I am getting a "No front page content has been created yet." message on my page.

How do I get around this?

# Answer
> 13 votes
Who would have guessed? There's a module for that. Check out the Empty Front Page module. With only 10 lines of code, it must be one of the simplest modules out there.

# Answer
> 9 votes
You can do this in your theme's template file:

```
function MYTHEME_preprocess_page(&$vars) {
  if (drupal_is_front_page()) {
    unset($vars['page']['content']['system_main']['default_message']); //will remove message "no front page content is created"
    drupal_set_title(''); //removes welcome message (page title)
  }
}

```

# Answer
> 2 votes
A quick CSS workaround to hide the "no content message" is simply by adding the following line in your CSS file :

```
 #first-time {display:none;}

```

# Answer
> 1 votes
The message "No front page content has been created yet." is shown from the page callback for the page you are using as front page, node\_page\_default(), which contains the following code.

```
  $select = db_select('node', 'n')
    ->fields('n', array('nid', 'sticky', 'created'))
    ->condition('n.promote', 1)
    ->condition('n.status', 1)
    ->orderBy('n.sticky', 'DESC')
    ->orderBy('n.created', 'DESC')
    ->extend('PagerDefault')
    ->limit(variable_get('default_nodes_main', 10))
    ->addTag('node_access');

  $nids = $select->execute()->fetchCol();

  if (!empty($nids)) {
    $nodes = node_load_multiple($nids);
    $build = node_view_multiple($nodes);

    // 'rss.xml' is a path, not a file, registered in node_menu().
    drupal_add_feed('rss.xml', variable_get('site_name', 'Drupal') . ' ' . t('RSS'));
    $build['pager'] = array(
      '#theme' => 'pager',
      '#weight' => 5,
    );
    drupal_set_title('');
  }
  else {
    drupal_set_title(t('Welcome to @site-name', array('@site-name' => variable_get('site_name', 'Drupal'))), PASS_THROUGH);

    $default_message = '<p>' . t('No front page content has been created yet.') . '</p>';

    // …
  }

```

It is simply output because the site doesn't have nodes; the message is not related to the front page having (or not having) blocks.

If you want to keep that front page, but not seeing that message, you can implement the following hook in a module.

```
function mymodule_page_alter(&$page) {
  if (isset($page['default_message']['#markup']) && is_string($page['default_message']['#markup'])) {
    $page['default_message']['#markup'] = strtr($page['default_message']['#markup'], '<p>' . t('No front page content has been created yet.') . '</p>', '');
  }
}

```

The other alternative would be adding the following code at the end of the settings.php file.

```
$conf['locale_custom_strings_en'][''] = array(
  'No front page content has been created yet.' => '',
);

```

If you have other languages enabled in your website, you can add a similar entry for each language. For example, for Italian the code to enter would be the following one. (`_en` is replaced with `_it`.)

```
$conf['locale_custom_strings_it'][''] = array(
  'No front page content has been created yet.' => '',
);

```

Using this method, "No front page content has been created yet." would be replaced in every place where it is used, not just the front page. That string is normally used only in the front page, when the front page is the default one. I don't know if any other module using that string, but before replacing that string with an empty string with `$conf['locale_custom_strings_en']['']`, I would check there aren't any enabled modules that use it (except the Node module).

# Answer
> 1 votes
I have a template for all new projects that contains:

1. hook\_menu() to make a new menu callback for a generic path like "home"
2. The page callback for "home" which returns a string with one space in it ' ' (because Omega tries to be "smart" if you return an empty string)
3. A strongarm export that sets the site homepage to my "home" url

That's it, I don't have to deal with the whacky default node page ever again.

# Answer
> 1 votes
This is a cool snippet to get rid of this error without confusing with other contents placed in *Content region*. This will only hide this error and show the other contents normally.

This snippet is posted at **Remove the "No front page content has been created yet." in the default Drupal installation**.

```
/**
 * Implements template_preprocess_page().
 */

  function YOUR_THEME_preprocess_page(&$vars) {
   // Remove the "No front page content has been created yet.".
     if (isset($vars['page']['content']['system_main']['default_message'])) {
        unset($vars['page']['content']['system_main']['default_message']);
      }
  }

```

# Answer
> 1 votes
In Drupal 8, the same "No front page content has been created yet." is shown also, e.g right after you create a new site and visit its front page (homepage). But things have been simplified if you want to replace or remove it ... All that's needed is like so:

* Navigate to `/admin/structure/views/view/frontpage` (to edit the view named "Frontpage").
* Within "No results behavior", use the link "Global: Unfiltered text" to edit it. Either change the message (= "No front page content has been created yet.") to whatever you want, or just remove it entirely.
* Don't forget to *Save* the changes.

To verify your changes, browse to front page. If needed repeat the steps above to further tune the message.

# Answer
> 0 votes
hook your page.tpl.php to page--front.tpl.php and do whatever you want enjoy hooking :-)

# Answer
> 0 votes
I'm not sure if there is a way to do this from Drupal's admin interface.

One way to accomplish this, that I know of, is to add an `if` statement to your `page.tpl.php`. Find a line like:

```
<?php print render($page['content']); ?>`

```

...and change it to:

```
<?php if (!$is_front): ?>
  <?php print render($page['content']); ?>
<?php endif; ?>

```

That will basically print the main page content on all pages that are not the front page. But, this might leave you with a gaping hole in the middle of your page.

So, you might do well to make a few other adjustments to your `page.tpl.php` and perhaps encompass more of the surrounding container tags. Further altering the tamplate, by putting the heading above the first sidebar for the home page:

```
<?php if ($is_front): ?>
  <?php if ($title): ?>
    <h1 class="title" id="page-title">
      <?php print $title; ?>
    </h1>
  <?php endif; ?>
<?php endif; ?>

```

...and moving the initial `if` statement and placing it just inside the content container:

```
<div id="content" class="column">
  <?php if (!$is_front): // Moved the if statement over here.... ?>
    <div class="section">
      <?php if ($page['highlighted']): ?>
        <div id="highlighted"><?php print render($page['highlighted']); ?>
        </div>
      <?php endif; ?>
      <a id="main-content"></a>
      <?php print render($title_prefix); ?>
      <?php if ($title): ?>
        <h1 class="title" id="page-title">
          <?php print $title; ?>
        </h1>
      <?php endif; ?>
      <?php print render($title_suffix); ?>
      <?php if ($tabs): ?>
        <div class="tabs">
          <?php print render($tabs); ?>
        </div>
      <?php endif; ?>
      <?php print render($page['help']); ?>
      <?php if ($action_links): ?>
        <ul class="action-links">
          <?php print render($action_links); ?>
        </ul>
      <?php endif; ?>
        <?php print render($page['content']); ?>
      <?php print $feed_icons; ?>

    </div>
  <?php endif;  // ...and closed it here. ?>
</div> <!-- /.section, /#content -->

```

...yielded the following results:

# Answer
> 0 votes
Place this code in page.tpl.php

```
if(drupal_is_front_page()) {

unset($page['content']['system_main']['default_message']);

}

```

Or

```
unset($page['content']['system_main']['default_message']);

```

In page--front.tpl.php

# Answer
> 0 votes
I don't know why you guys complicate it so much. (well I don't know if abd's ways work, but in case it didn't work, use the solution below.

in page.tpl.php you have to find this part of code:

(well I have this is mine Adaptive Theme 7x-3.2)

```
<?php if ($title): ?>
<h1 id="page-title">
<?php print $title; ?>
</h1>
<?php endif; ?>

```

All you have to do is delete it! All lines.

This part of php simply says:

If "title" exists, print it in H1 font size. It is printed on the website in the area the code is written in. In case of Adaptive Theme it's in Header area.

If you do not have page.tpl.php in your subtheme, you have to find it in the core theme, and then copy it to the same location as core theme, to your subtheme.

In AT it's /templates/page.tpl.php

it means that in your Adaptive theme subtheme you have to create folder named "templates" and then copy there page.tpl.php and then delete the part of code mentioned above.

It's important that you do not make any changes to the Core theme!

# Answer
> 0 votes
you can use **String Overrides** Module String Overrides for replace

**"No front page content has been created yet in front page"** to **""**

# Answer
> 0 votes
Better to achieve this job is unset `$page['content']['system_main']['default_message']` in `hook_preprocess_theme` or in the `page.tpl.php` or `page--front.tpl.php`.

find

```
<?php print render($page['content']); ?>

```

and replace with below code.

```
<?php  if(drupal_is_front_page()){
            unset($page['content']['system_main']['default_message']);
            }
        print render($page['content']);
    ?>

```

**OR**

```
  function MYTHEME_preprocess_page(&$vars) {
       if (drupal_is_front_page()) {
            unset($vars['page']['content']['system_main']['default_message']); //will remove message "no front page content is    created"
     }
    }

```

Enjoy!!

# Answer
> 0 votes
Hook with unset is the best way to do this.

```
use Drupal\views\ViewExecutable;

/**
* Implements hook_views_pre_render().
*/
function drupal_practice_views_pre_render(ViewExecutable
&$view) {
  if($view->id() == 'frontpage' && $view->current_display ==
  'page_1' ){
    unset($view->feedIcons);
    unset($view->empty['area_text_custom']);
    unset($view->empty['node_listing_empty']);
  }
}

```

---
Tags: nodes
---


title: How can I collect shipping information?

text:
# Question
Title: How can I collect shipping information?
I can't figure out how to get the shipping address information to be collected on checkout in Commerce on Drupal 10.

My setup

* Created a Manual payment gateway that collects billing information.
* Installed the Commerce Shipping module (version 2.6).
* On `/admin/commerce/config/order-types/default/edit`, set **Workflow** to *Fulfillment*, checked the box **Enable shipping for this order type**, and set the **Checkout flow** to *Shipping* as instructed in the module README.
* Added four different flat rate shipping methods at `/admin/commerce/shipping-methods`. None of the shipping methods have any conditions to limit visibility; they should all be shown.

However, when I add a product to the cart and then go to checkout, shipping information is never displayed. I do see the payment information (to input a billing address) and contact information (input an email). This happens both for anonymous and logged-in users (even user 1, so it's not a permissions issue).

The Shipping checkout flow has "Shipping information" under **Order information**, which is the default configuration.

# Answer
> 1 votes
Have you enabled the "Shippable" trait on the product variation type?

---
Tags: commerce, 10
---


# Question
Title: How to delete a no longer used custom bin table?
I'm testing the cacheAPI with a custom bin. Everything is working. On the other hand, while testing, I created a custom bin with a name that does not suit me.

```
cache.lorem_bin:
  class: Drupal\Core\Cache\CacheBackendInterface
  tags:
    - { name: cache.bin }
  factory: ['@cache_factory', 'get']
  arguments: [lorem_bin]

```

A table in DB `cache_lorem_bin` was created.

So I modified it and another table was created with the name of this new custom bin. But the old table `cache_lorem_bin` was not deleted even after clearing the cache. I don't see in the documentation how to delete this table. It's not blocking but I would like to know how to do it.

# Answer
Cache bin tables are automatically removed when you uninstall the module. You should have uninstalled the module before making the change and after that install the module again.

However, as a developer you can choose to drop the table manually to avoid the reinstall process. The new cache bin table will be created the first time it is used.
> 1 votes

---
Tags: database
---


# Question
Title: How to create a service for the class with constructor arguments from a vendor package class?
I am trying to inject class from FFMpeg in my custom service of a module.

But the class has two constructor arguments. So how can I pass those arguments in my custom service.

#### my\_module.services.yml

```
services:
  # FFMPEG Factory
  my_module.php_ffmpeg:
    class: FFMpeg\FFMpeg
    autowire: true

```

I understand that as there are two parameters in the costructor, we need to pass two arguments in service definition but the class doesn't have factory method. So, what arguments should I pass?

# Answer
> 3 votes
Factory services are nothing more than classes that instantiate and return objects as an injectable (e.g. `LoggerChannelFactory` creates `LoggerChannel` instances as logger services). You can create a factory that instantiates `FFMpeg\FFMpeg`, makes it a service, and inject it to whatever needs it. This still fulfills dependency-injection duties. Your code won't be using FFMpeg directly but via the factory, and you can mock away the factory during testing.

```
// my_module/src/FFMpegFactory.php
class FFMpegFactory {
  public static function get() {
    $ffmpeg = /* instantiate driver */
    $ffprobe = /* instantiate probe */
    return new FFMpeg($ffmpeg, $ffprobe);
  }
}

// my_module/src/OtherService.php
class OtherService {
  public function __construct(FFMpeg $php_ffmpeg) {
    $this->ffmpeg = $php_ffmpeg;
  }
}

// my_module/my_module.services.yml
services:
  my_module.php_ffmpeg:
    class: 'FFMpeg\FFMpeg'
    factory: ['\Drupal\my_module\FFMpegFactory', 'get']
  my_module.other_service:
    class: '\Drupal\my_module\OtherService'
    arguments:
      - '@my_module.php_ffmpeg'

```

# Answer
> 0 votes
The constructor arguments for the class are `FFMpegDriver $ffmpeg` and `FFProbe $ffprobe`. Those are the ones to inject. You must create those object instances as services also.

Alternatively you can create a custom factory to build the object.

---
Tags: dependency-injection
---


# Question
Title: Why is cweagans/composer-patches not installing this patch?
I'm trying to apply different patches but after the command `composer install` nothing happens. Output:

```
Installing dependencies from the lock file (including require-dev)
Verifying lock file contents can be installed on current platform.
Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. It is recommended that you run `composer update` or `composer update <package name>`.
Nothing to install, update or remove
Generating autoload files
42 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
  * Homepage: https://www.drupal.org/project/drupal
  * Support:
    * docs: https://www.drupal.org/docs/user_guide/en/index.html
    * chat: https://www.drupal.org/node/314178

```

For example: I'm trying to apply a patch in my code for the module CKEditor CodeSnippet: https://www.drupal.org/project/codesnippet/issues/3269387

part of the composer.json:

```
{
    "name": "drupal/recommended-project",
    // some code
    "repositories": [
        {
            "type": "composer",
            "url": "https://packages.drupal.org/8"
        }
    ],
    "require": {
        "composer/installers": "^2.0",
        "drupal/admin_toolbar": "^3.3",
        "drupal/codesnippet": "^1.8",
        "drupal/core-composer-scaffold": "^10.0",
        "drupal/core-project-message": "^10.0",
        "drupal/core-recommended": "^10.0",
        // some code
    },
    // some code
    "config": {},
    "extra": {
        "patches": {
            "drupal/codesnippet": {
                "https://www.drupal.org/i/3269387 - Drupal 10 & CKEditor 5 support": "https://www.drupal.org/files/issues/2023-04-27/3356929-2_0.patch"
            }
        },
        "drupal-scaffold": {
            "locations": {
                "web-root": "web/"
            }
        },
    // some code

```

Also for testing, I found some patch for the admin toolbar module and tried to apply it but it also doesn't apply

# Answer
The problem was that **cweagans/composer-patches** was not installed. After I wrote the command `composer require cweagans/composer-patches` the patches are now applied after the command `composer install`
> 2 votes

# Answer
It think this is because:

> Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. It is recommended that you run `composer update` or `composer update <package name>`. Nothing to install, update or remove

That means the package has already been installed in that codebase in the desired version. Because of that you actually have to tell Composer to re-install the package so that the patch will be applied.

Do what it says: `composer update drupal/codesnippet`

An upcoming version of cweagans/composer-patches will have a composer sub-command that allows re-patching.
> 0 votes

---
Tags: composer, 10
---


# Question
Title: Why does Drupal\\Core\\Config\\UnmetDependenciesException occur on install?
I tried to Install drupal on a Ubuntu 22.04.2 VM. I route the guest via NAT to my host. Then I created a folder in my webroot and let Apache point to it. After that, I downloaded the installation file via composer to the folder. and surf to the domain in the browser and get routed to the installation site just as expected. I can progress without errors (just a warning about clean URLs, but the installation is just for testing automation scripts, so no matter about UX) until the creation of the database. There I typed in the name of the database and the SQL credentials. Then I click on "Create and Continue" but after the site is loading a while, it stays on the site and I can not progress. When I refresh the site i get a long error message.

> Error
> The website encountered an unexpected error. Please try again later.

When I typed the domain in the browser again, I get a blank site with the same error. I tried to create the database before I start the installer, tried if the installer is creating on its own, the error does not change.

Maybe it has something to do with the VM setup? How can I setup the VM to install Drupal?

There are two exceptions in the webserver:

> Drupal\Core\Config\UnmetDependenciesException: Configuration objects provided by standard have unmet dependencies: block\_content.type.basic (block\_content), comment.type.comment (comment), \[a row of unmet dependency exceptions, all from ConfigInstaller.php and paths within the folder I created\]

(It seemed like a attempt to create database tables that is going wrong.)

# Answer
This error often occurs when `max_allowed_packet` has not been set according to the Drupal database requirements.
> 1 votes

---
Tags: installation, virtual-machine
---


# Question
Title: How to write in a ckdeditor textarea with Cypress?
i'm trying to create a new node in Drupal 9. But all my attempts so far have failed when it comes to writing the body text. The body text is a Ckeditor5 field. I can't write there with Cypress.

I tried the following line with the following error: "*Cannot read properties of undefined (reading 'instances')*"

```
  cy.window().then(win => {win.CKEDITOR.instances["html_body"].setData("<p>HTML body</p>");});

```

After several attempts, i tried the jQuery way, so far this solution wont raise any error, but the textarea remains in blank without any text:

```
cy.get('.ck-editor__editable').then($editable => {$editable.focus().html('<p>HTML body</p>');});

```

how can i set text into this field with Cypress?

# Answer
> 1 votes
Drupal keeps a list of the active CKEditor 5 instances in JS, you can get them from there. For example to fill all the editors on the page:

```
cy.window().then(win => {
  win.Drupal.CKEditor5Instances.forEach(editor => editor.setData('<p>Test</p>'))
})

```

---
Tags: 9
---


# Question
Title: How can I overwrite the value of a field in a bundle class?
In Drupal 9 I would like to know if it is possible to overwrite the value of a field in a BundleClass. Looking at the Drupal API, I see that it is common to use either `hook_entity_view_alter` or `hook_views_pre_render`. The only way I have found is to define a new method:

```
public function getUpdateFieldName() {
  $value = this->get('field_name')->getValue();
  # Code here ...
  return $code
}

```

However, with this example, I have to create a pseudo-field to be able to get the value in a view. Are there any other ways?

# Answer
> 1 votes
To query field values Views operates on SQL basis, to display you can configure a Views field plugin and additionally use the Field API field formatter. These are the two classes you should consider to add PHP code, if you can't use the rewrite options Views field plugins provide out-of-the-box.

From there you can refactor the code and put the bundle specific calculations in the bundle class.

---
Tags: views, nodes
---


# Question
Title: How to inspect the form values in hook_user_update()?
In Drupal 7, one could have the following `hook_user_update()` to return TRUE if the 'roles' has been edited:

```
function example_user_update(&$edit, $account, $category) {
  isset($edit['roles']) ? TRUE : FALSE;
}

```

In Drupal 10, the signature of the hook has changed, and we no longer have direct access to the paramter `$edit` (the array of form values submitted by the user). How do one do this in Drupal 9/10?

```
function example_user_update(EntityInterface $account) {
  // What is the equivalent?
}

```

# Answer
You can follow this code:

```
function drupal_practice_user_update($account) {
 $roles = $account->original->getRoles();
    if ($roles !== $account->getRoles()) {
      \Drupal::messenger()->addMessage(t('Role has
       updated'));
    }
    else{
       \Drupal::messenger()->addWarning(t('Role has not
        updated'));
    }
}

```

As you can see `$account` parameter holds the value of the fields in a users profile.
> 0 votes

---
Tags: users, 10
---


title: Add a link field to block form

text:
# Question
Title: Add a link field to block form
I've created a module with a custom block that extends `BlockBase`. To the block form I added multiple textfields and select boxes, by simply specifying the formatter in the `'#type'` configuration.

My next step is to add a button that links to internal/external content and I've tried adding different types, such as 'link' but without any luck.

I found LinkFormatter, but I can't figure out how to use it properly with block forms.

What has to be added to the blockForm method in order to create a link field (just like the `link` field that's selectable within a custom block type):

My current code:

```
public function blockForm($form, FormStateInterface $form_state) {
  //...
  $form['link'] = [
    '#type' => 'link',
    '#title' => $this->t('Link title'),
    '#url' => 'Hmm?',
    '#options' => ['external' => TRUE]
  ];

  return $form;
}

```

# Answer
> 0 votes
You want to change `'#type' => 'link'` to `'#type' => 'url'`

```
public function blockForm($form, FormStateInterface $form_state) {
  //...
  $form['link'] = [
    '#type' => 'url',
    '#title' => $this->t('Link title'),
    '#options' => ['external' => TRUE]
  ];

  return $form;
}

```

# Answer
> 0 votes
You should try this code:

```
$form['group1'] = array(
  '#type' => 'fieldset',
  '#title' => $this->t('Link Field'),
  '#collapsible' => FALSE,
  '#collapsed' => FALSE,
);

$form['group1']['url'] = array(
  '#type' => 'url',
  '#title' => $this->t('URL'),
  '#type' => 'entity_autocomplete',
  '#target_type' => 'node',
);

$form['group1']['label'] = array(
  '#type' => 'textfield',
  '#title' => $this->t('Link Text'),
);

```

---
Tags: 8, forms, blocks
---


# Question
Title: Form API: How to add arbitrary render element after specific field?
I've got an input field in an entity form which needs quite a bit of extra documentation. This documentation is long and needs computed, contextual information, so a simple field description or `#field_suffix` just doesn't do it. I also want to keep the ability to move fields around in field UI. I'm trying to create a `form_alter` hook to inject a render element with my docs right after that field.

I already have a working prototype:

```
function MYMODULE_form_ENITY_FORMMODE_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $fieldToEnhance = 'field_foo';
  if (!isset($form[$fieldToEnhance])) {
    return;
  }
  $computedDocumentationVariables = mymodule_get_infos();
  $renderElementToAppend[$fieldToEnhance . '_append'] = [
    '#type' => 'inline_template',
    '#template' => '<div>This is a very shortened and simplified placeholder.</div>',
    '#context' => $computedDocumentationVariables,
    '#parents' => $form[$fieldToEnhance]['#parents'] ?? [],
    '#weight' => isset($form[$fieldToEnhance]['#weight']) ? $form[$fieldToEnhance]['#weight'] + 1 : NULL,
  ];
  $positionOfField = array_search($fieldToEnhance, array_keys($form));
  // inject the render element right after the field in the form array
  $form = array_merge(
    array_merge(
      array_slice($form, 0, $positionOfField+1),
      $renderElementToAppend
    ),
    $form
  );
}

```

This works as exptected for a very plain form.

But as soon as I move my field inside a container from Field group module, the positioning doesn't work as expected. When viewing it in a debugger, I can see that my custom render element is at the correct position inside the `$form` array. But on screen it is not rendered at the correct position. (My documentation render element is on root level. I want it inside the container, right after the field.)

How do I make my hook aware of potential nesting containers from Field Group module?

# Answer
> 0 votes
`#suffix` and `#description` are very different. The first one is a Render API standard property common to all render elements and is handled by the Renderer service. The second one is handled as a normal template variable with no restrictions on the number of render elements or nesting levels. You only have to ensure it is printed the way you set it. Check Twig Debug to see which template is used. The standard `form-element.html.twig` is rendering a subkey `content`. Some input elements use their own wrapper template/preprocessing and don't have this subkey. You can also define a custom wrapper template with your own set of variables.

---
Tags: forms
---


# Question
Title: How do I add a custom div with a class to CKEditor?
I have ckeditor installed via the WYSIWYG module. I need to select a group of paragraphs and give surround them with a div that has a class and end up with this

```
<div class="my_class">
<p>test text 1</p>
<p>test text 2</p>
<p>test text 3</p>
<p>test text 4</p>
</div>

```

this div could also surround other elements like links, lists, etc. I have no problem adding custom styles but can't quite add any custom formats.

# Answer
> 0 votes
This module is maybe what you're looking for

https://www.drupal.org/project/ckeditor\_div\_manager

---
Tags: 7, wysiwyg
---


# Question
Title: Get the value of taxonomy custom user field
I am trying to get a taxonomy field in `hook_user_insert()`.

The logic is that if the taxonomy field is selected as none then print warning message along with a success message.

```
function drupal_practice_user_insert($account) {
  $user = \Drupal\user\Entity\User::load(\Drupal::currentUser()->id());
  $brand = $user->field_add_brand->value;
  if ($brand == null) {
    \Drupal::messenger()->addWarning(t('My new custom message.'));
  }
}

```

The problem is that the taxonomy field value results `NULL` all the time.
What am I doing wrong? How can I get the taxonomy field value?

# Answer
Ok, I figured it out! This is the solution:

```
function drupal_practice_user_insert($account) {

    $user = \Drupal\user\Entity\User::load($account->id());
    $brand = $user->get('field_add_brand')->getValue();
    if(empty($brand)){
        \Drupal::messenger()->addWarning(t('My new custom message.'));

    }
 }

```
> 0 votes

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


# Question
Title: How to force regeneration of Twig templates?
Twig generates classes for its templates. These classes are effectively caches by being written to disc.

Generally speaking, one probably doesn't need to bother with the details of how the twig templates are handled, but there could be cases when the Twig templates must be manually re-generated. (Similar to the need for the Registry rebuild module in D7)

If it's relevant, I'm asking because I'm stuck with an error that looks like this:

> Fatal error: Class '\__TwigTemplate\_ddad913164239ac659c2bca09b3a49a5' not found in core/lib/Drupal/Core/Template/TwigEnvironment.php on line 107

# Answer
Not sure why you get that specific error, but have a look at drupal\_flush\_all\_caches(), which does

```
PhpStorageFactory::get('twig')->deleteAll();

```

That said, I'm not sure if this will solve your problem, as I've never seen that error, not sure how it could end up expecting a class that is not there.
> 6 votes

# Answer
I was seeing this Twig error in my error log, when attempting to access a Drupal 8.x site after it had been installed with drush with `drush si --db-url=mysql://root:root@localhost/mysite -y`. So to correct the issue, again with drush, simply clear the cache:

```
drush cr all

```

Additionally, after this, if your new Drupal 8.x site doesn't appear to have any CSS styles, then this is likely due to the apache/httpd user not having write permissions to sites/default/files.
> 1 votes

# Answer
Try:

```
drush twig:compile

```

> `twig:compile` (`twigc`) Compile all Twig template(s).
> 0 votes

---
Tags: 8, theming
---


# Question
Title: Override a field in a taxonomy terms view
I have a taxonomy terms view, filtered by a vocabulary. I would like to add the dashes before the term name like in the Parent terms field on the edit term page. The code in TermForm.php does that when creating $options using `str_repeat` based on the item's depth.

This is the code from TermForm.php:

```
$tree = $taxonomy_storage->loadTree($vocabulary->id());
$options = ['<' . $this->t('root') . '>'];
if (empty($parent)) {
  $parent = [0];
}

foreach ($tree as $item) {
  if (!in_array($item->tid, $exclude)) {
    $options[$item->tid] = str_repeat('-', $item->depth) . $item->name;
  }
 }
}

```

I copied `core/modules/views/templates/views-view-field.html.twig` to my module's templates directory and renamed it `views-view-field--my-view-name--name.html.twig` I added `hook_theme` to the module file to start using the template.

```
/**
 * Implements hook_theme().
 */
function my_module_theme($existing, $type, $theme, $path) {
  return [
    'views_view_field__my_view_name__name'=> [
      'render element' => 'elements',
      'template' => 'views-view-field--my-view-name--name',
    ],
  ];
}

```

If I add some markup to the template, like `<h3>hello</h3>`, `hello` is printed on the View's page but, no data is being passed to the template.

Ultimately I need to get the term object and update it with

```
str_repeat('-', $item->depth) . $item->name

```

before passing the field to the template. I'm guessing I get the term object in a preprocessor and add the updated `name` field to hook\_theme?

# Answer
It turns out I don't need the template. Using `hook_preprocess_views_view_field()` with the following code was all that was required:

```
/**
 * Implements hook_preprocess_views_view_field().
 */
function my_module_preprocess_views_view_field(&$variables) {
  $view = $variables['view'];
  $field = $variables['field'];
  $field_id = $field->field;
  if (
    $view->storage->id() === 'my_view_name' &&
    $view->current_display === 'my_display' &&
    $field_id === 'name'
  ) {
    $tid = $variables['row']->_entity->id();
    $label = $variables['row']->_entity->label();

    $parents = \Drupal::entityTypeManager()
      ->getStorage('taxonomy_term')
      ->loadAllParents($tid);
    $depth = count($parents);
    $path = \Drupal::service('path_alias.manager')
      ->getAliasByPath('/taxonomy/term/' . $tid);

    $link = '<a href="' . $path . '">' . $label . '</a>';
    $variables['output'] = Markup::create(str_repeat('-', $depth) . $link);
  }
}

```

solved the problem.

I think this line

```
    $link = '<a href="' . $path . '">' . $label . '</a>';

```

can be improved, but it will do for now.
> 0 votes

---
Tags: views, taxonomy-terms
---


# Question
Title: Kernel test throws errors: field not found, and Base table or view not found:
Here's the class I want to test:

```
class Thing {

    public function foo() {
      return \Drupal::entityQuery('node')->condition('body', 'blah')->execute();
    }

  }

```

And the test:

```
class footest extends \Drupal\KernelTests\KernelTestBase {

  public static $modules = ['node', 'user', 'field'];

 public function testFoo() {
   $thing = new Thing();
   \PHPUnit\Framework\assertEquals($thing->foo() ,4);
 }
}

```

Obviously this is not a real class or a real test, but they are pared down to the basics to demonstrate the issue.

When I run the test I get "`Drupal\Core\Entity\Query\QueryException : 'body' not found`".

If I replace "body" with "field\_baz', a field that I know exists on at least some content types, I get "`Drupal\Core\Entity\Query\QueryException : 'field_baz' not found`".

Removing the condition entirely, so that the function body becomes:

`return \Drupal::entityQuery('node')->execute();`

produces "`Drupal\Core\Database\DatabaseExceptionWrapper : SQLSTATE[42S02]: Base table or view not found: 1146 Table 'carbon.test65392739node' doesn't exist`"

What am I doing wrong? How can I write tests for code that uses EntityQuery? Thanks!

# Answer
> 2 votes
Test classes that extend `KernelTestBase` needs to create the database tables in their `setUp()` method. See `EntityDisplayTest::setUp()`, for example, which creates the database tables for the Node entity.

```
  parent::setUp();
  $this->installEntitySchema('entity_test');
  $this->installEntitySchema('node');
  $this->installEntitySchema('user');
  $this->installConfig(['field', 'node', 'user']);

```

Setting `$modules` to the required modules is not sufficient for the database tables to be created. `EntityDisplayTest` itself sets `EntityDisplayTest::$modules` to `['field_ui', 'field', 'entity_test', 'user', 'text', 'field_test', 'node', 'system']` but then calls `KernelTestBase::installEntitySchema()` for the User and Node entities.

For database tables that are not for entities, there is `KernelTestBase::installSchema()`, which creates the database tables a module defines in its `hook_schema()` hook.

---
Tags: 9, testing
---


# Question
Title: Custom form, add admin editable instructions from settings area to form
I have a custom form that is working great. I'd like to add a field "instructions" right under the title, above the first form field. This "instructions" field is editable in the module settings area. I have tried to get this data like:

```
$config = $this->config('myform.settings');
$instructions = $config->getValue('instructions');

```

however, it throws an error:

> Call to undefined method Drupal\Core\Config\ImmutableConfig::getValue()

How do I pass the data in a textfield to the form the users are filling out on render?

# Answer
The method to get a value from an immutable configuration object or an editable configuration object is `get()`.

In a form class that extends `ConfigFormBase`, the code to use for your case is the following one.

```
$instructions = $this->config('myform.settings')
  ->get('instructions');

```

`getValue()` is a `FormState` method, which is used to get a value submitted in the form. See `AccountSettingsForm::submitForm()` as example of how that method is used.

```
$this->config('user.settings')
  ->set('anonymous', $form_state
  ->getValue('anonymous'))
  ->set('register', $form_state->getValue('user_register'))
  ->set('password_strength', $form_state->getValue('user_password_strength'))
  ->set('verify_mail', $form_state->getValue('user_email_verification'))
  ->set('cancel_method', $form_state->getValue('user_cancel_method'))
  ->set('notify.status_activated', $form_state->getValue('user_mail_status_activated_notify'))
  ->set('notify.status_blocked', $form_state->getValue('user_mail_status_blocked_notify'))
  ->set('notify.status_canceled', $form_state->getValue('user_mail_status_canceled_notify'))
  ->save();

```
> 2 votes

---
Tags: configuration, 10
---


# Question
Title: How to create a group of sub tokens off an entity type?
I am trying to create sub tokens in a group off of existing Users token (sometimes referred to as "chaining"). So there are 2 parts to this. Creating the subgroup off the existing User tokens group and creating tokens within that group. I really only need the hook\_token\_info() part of this; not the actual token code.

I am trying to create a set of tokens of the form: \[user:state:{token\_id}\]. Examples:

* \[user:state:big\]
* \[user:state:small\]

I am able to create \[user:state-big\] and \[user:state-small\] but would like to know how to have these grouped in the token browser under Users -\> State.

From documentation linked to by Clive it would suggest this:

```
  $types['state'] = array(
    'name' => t('State'),
    'description' => t('Tokens related to user state.'),
    'needs-data' => 'user',
  );

  $tokens['user']['state']['big'] = array(
    'name' => t('User State (Big)'),
    'description' => t('The big state of the user.'),
    'type' => 'user',
  );

  $tokens['user']['state']['small'] = array(
    'name' => t('User State (Small)'),
    'description' => t('The small state of the user.'),
    'type' => 'user',
  );

  return array(
    'types' => $types,
    'tokens' => $tokens,
  );

```

this code gives me these in the token browser:

```
State   state   Tokens related to user state.

```

and under Users:

```
       [user:state]

```

# Answer
Your tokens/types aren't structured quite right. You want `big` and `small` to be tokens in the `state` type (not the `user`), and you want the token you're adding to the `user` type to be of type `state`.

The code explains it better:

```
$types['state'] = array(
  'name' => t('State'),
  'description' => t('Tokens related to user state.'),
  'needs-data' => 'user',
);

$tokens['state']['big'] = array(
  'name' => t('User State (Big)'),
  'description' => t('The big state of the user.'),
);

$tokens['state']['small'] = array(
  'name' => t('User State (Small)'),
  'description' => t('The small state of the user.'),
);

$tokens['user']['state'] = array(
  'name' => t('User State'),
  'description' => t('The user state.'),
  'type' => 'state',
);

return array(
  'types' => $types,
  'tokens' => $tokens,
);

```

That will enable, e.g., `[user:state:big]` for use, and show the expected hierarchy in the token browser.

As an aside, you'll also need to populate the data for the `state` type in `hook_tokens()`.

For example:

```
function module_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
  $token_service = \Drupal::token();
  $replacements = [];

  if ($type == 'user' && !empty($data['user'])) {
    if ($state_tokens = $token_service->findWithPrefix($tokens, 'state')) {
      $replacements += $token_service->generate(
        'state',
        $state_tokens,
        ['user' => $data['user']],
        $options,
        $bubbleable_metadata
      );
    }
  }

  return $replacements;
}

```

Also bear in mind that varying cache by individual user, which is happening here, can result in a lot of cache entries (depending on the number of users and how/where this token is used).

If you can find a solution which varies the content based on role, or even change the implementation so that it incorporates a render array with a lazy builder, you might see better cache performance.
> 3 votes

---
Tags: 9, tokens
---


# Question
Title: How to set default value for route parameter based on configured value?
I have a route with optional parameter that has some default value:

```
# file @hello.routing.yml
hello.sayHi:
  path: '/hello/{name}'
  defaults:
    _title: 'Say Hi'
    _content: 'Drupal\hello\Controller\SimpleCallback::sayHiAction'
    name: 'there'
  requirements:
    _permission: 'access content'

```

I'd like to make `there` value for `{name}` parameter configurable. Let's assume that this default value for `{name}` is available in simple config variable.

# Answer
You must implement a route alter. In the alter code, extract the setting and set the value on the route definition.
> 1 votes

# Answer
Implement two routes one with an argument and the second without.

## hello.routing.yml

```
hello.sayHi:
  path: '/hello/{name}'
  defaults:
    _title: 'Say Hi'
    _content: 'Drupal\hello\Controller\SimpleCallback::sayHiAction'
  requirements:
    _permission: 'access content'
hello.sayHi.default:
  path: '/hello'
  defaults:
    _title: 'Say Hi'
    _content: 'Drupal\hello\Controller\SimpleCallback::sayHiAction'
  requirements:
    _permission: 'access content'

```

## hello.config.yml

```
default_name: there

```

## hello.services.yml

```
hello.config:
  factory: ['@config.factory', get]
  class: Drupal\Core\Config\Config
  arguments: ['hello.config']

```

## Drupal\hello\Controller\SimpleCallback

```
protected $configuration;

public function __constructor(Config $configuration) {
  $this->configuration = $configuration;
}

public static function create(Container $container) {
  return new static($container->get('hello.config'));
}

public function sayHiAction($name = NULL) {
  $name = is_null($name) ? $this->configuration->get('default_name') : $name;
  return [
    '#type' => 'inline_template',
    '#template' => 'Hi {name}',
    '#context' => [
      'name' => $name
    ]
  ]
}

```
> 1 votes

# Answer
https://www.drupal.org/docs/8/api/routing-system/parameters-in-routes/using-parameters-in-routes

Optional parameters

Parameters on routes can be omitted when a default value for the parameter is supplied. Imagine you have a form controller that allows people to report different issues (e.g. bug reports, feature requests and support requests), and if the type is omitted it should default to 'support request'. Supply the default value for the optional parameter in the "defaults" section:

```
issue.report_form:
  path: '/report/{issue_type}'
  defaults:
    _controller: '\Drupal\issue\Controller\IssueController::report'
    issue_type: 'support-request'
  requirements:
    _permission: 'report issue'

```
> -2 votes

---
Tags: 8, routes
---


title: Clearing local tasks cache

text:
# Question
Title: Clearing local tasks cache
Using a deriver, I'm able to produce local tasks for each entity of my custom configuration entity type. So if I have two configuration entity labeled "Foo" and "Bar", I can have similarly labeled tabs on a report page.

However, when adding or removing one of my configuration entity, the local tasks are not updated (ie. the tab for the deleted entity is still visible). This is not a surprise as these things get cached.

I've been able to fix the issues using implementations of `hook_ENTITY_TYPE_insert()` and `hook_ENTITY_TYPE_delete()` calling `drupal_flush_all_caches()`. But clearing all caches in order to only get the tabs on a single page updated seems a bit overkill. I already tried to call both `menu_cache_clear_all()` and `Drupal::cache('discovery')->invalidateAll()` without success

The tabs are build using the following code:

`MODULE.tasks.yml`:

```
MODULE.settings.webhooks_list:
  deriver: 'Drupal\MODULE\Plugin\Menu\LocalTask\ReportLocalTasks'
  base_route: MODULE.settings.list

```

`src/Plugin/Menu/LocalTasks/ReportLocalTasks.php`:

```
namespace Drupal\MODULE\Plugin\Menu\LocalTask;

use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\MODULE\CustomEntityTypeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class ReportLocalTasks extends DeriverBase implements ContainerDeriverInterface {
  use StringTranslationTrait;

  protected $entityManager;

  /**
   * Constructor.
   *
   * @param $entityManager
   * @param $stringTranslation;
   */
  public function __construct(EntityTypeManagerInterface $entityManager, TranslationInterface $stringTranslation) {
    $this->entityManager = $entityManager;
    $this->stringTranslation = $stringTranslation;
  }

  public static function create(ContainerInterface $container, $base_plugin_id) {
    return new static(
      $container->get('entity_type.manager'),
      $container->get('string_translation')
    );
  }

  public function getDerivativeDefinitions($base_plugin_definition) {
    $this->derivatives = [];

    /** @var EntityStorageInterface $entityStorage */
    $entityStorage = $this->entityManager->getStorage('custom_type');

    /** @var CustomEntityTypeInterface $connection */
    foreach ($entityStorage->loadMultiple() as $entity) {
      $this->derivatives["MODULE.settings.list.{$entity->id()}"] = [
        'title' => $entity->label(),
        'route_name' => 'MODULE_ecom.settings.list',
        'route_parameters' => [
          'connection' => $entity->id(),
        ],
      ];
    }

    foreach ($this->derivatives as &$entry) {
      $entry += $base_plugin_definition;
    }

    return $this->derivatives;
  }
}

```

In order to have up to date tabs when adding or removing custom entity, I'm using the following code. What I'm trying to do is to have something more specific than the call the `drupal_flush_all_caches()`.

`MODULE.module`

```
/**
 * Implements hook_ENTITY_TYPE_insert();
 */
function MODULE_CUSTOM_ENTITY_ETYPE_insert(\Drupal\MODULE\CustomEntityTypeInterface $connection) {
  drupal_flush_all_caches();
}

/**
 * Implements hook_ENTITY_TYPE_delete();
 */
function lMODULE_CUSTOM_ENTITY_ETYPE_delete(\Drupal\MODULE\CustomEntityTypeInterface $connection) {
  drupal_flush_all_caches();
}

```

# Answer
> 5 votes
First of all it might be that you found a bug in core (a caching issue), so it would be great if you could report it to the Drupal core issue queue as well. There is extensive test coverage for local tasks, but it might be that there is none for adding new entities and that those in core just work due to side effects.

To solve your immediate issue I would try to invalidate just the render cache of the local task blocks.

To find out the cacheability metadata of the local tasks the easiest is to install the renderviz module:

https://www.drupal.org/project/renderviz/

and apply the patch from this comment

https://www.drupal.org/node/2640280#comment-11286381

Once you have that installed and you go to the page with your local tasks, you will see a list in the JS console of all cache tags present.

For the bartik theme you will find (e.g. tested with core search pages, which have similar code to yours):

config:block.block.bartik\_local\_tasks

If you put into the console:

```
  renderviz('tags', 'config:block.block.bartik_local_tasks');

```

Then you will see that indeed the primary local tasks will be highlighted.

So in your hook\_entity\_insert() / hook\_entity\_update(), you can just do:

```
 Drupal\Core\Cache\Cache::invalidateTags('config:block.block.bartik_local_tasks');

```

And that will invalidate just the local tasks block and solve your issue for now.

What should have happened however is that the local tasks block should show both the cache tags for the list \[entity-type\] and the entities itself. (like a normal listing of entities)

e.g. if you want to change the title of any of your entities, core should based on the deriver update the local tasks block.

And that we don't do that is likely a core bug in regards to cacheability metadata, so please file it. Unless I miss something and there is some special code that makes this work for core's derivers out-of-the box.

Edit:

We correctly add the cache tags of the config entities, however there is no 'list' cache tag, which means that whenever you add or delete a config entity the local tasks don't get invalidated.

As you have a plugin, you can likely just add your own custom cache tag to the plugin definition.

e.g.

```
$base_plugin_definition['cache_tags'][] = 'local-tasks-my-entity-type-list-cache-tag';

```

Then just invalidate that list cache tag in your insert / update.

But that definitely is also an issue in Drupal core as I can reproduce it purely with core search pages.

I missed in your question at first that caches fail to get invalidated only when adding or deleting new entities.

That however was the key clue.

Edit 2:

Core should be invalidating list cache tags automatically, so the easiest is to just use the pre-defined list cache tags.

This is the diff that fixes the bug for me in core:

```
diff --git a/core/modules/search/src/Plugin/Derivative/SearchLocalTask.php b/core/modules/search/src/Plugin/Derivative/SearchLocal
--- a/core/modules/search/src/Plugin/Derivative/SearchLocalTask.php
+++ b/core/modules/search/src/Plugin/Derivative/SearchLocalTask.php
@@ -52,7 +52,9 @@ public function getDerivativeDefinitions($base_plugin_definition) {
           'route_name' => 'search.view_' . $entity_id,
           'base_route' => 'search.plugins:' . $default,
           'weight' => $entity->getWeight(),
 -        );
 +        ) + $base_plugin_definition;
 +
 +        $this->derivatives[$entity_id] += ['cache_tags' => []];
 +        $this->derivatives[$entity_id]['cache_tags'] += $entity->getEntityType()->getListCacheTags();
       }
     }
     return $this->derivatives;

```

That fixes the bug for me for core search pages.

# Answer
> 1 votes
Based on LionsAd answer, there is no need to manually clear any cache in implementations of `hook_ENTITY_TYPE_insert` and `hook_ENTITY_TYPE_delete`. Instead, the plugin derivative should be marked with the entity type list cache tags, so Drupal can clear the appropriate caches when needed. The code for my derivative now looks like this:

```
namespace Drupal\MODULE\Plugin\Menu\LocalTask;

use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\MODULE\CustomEntityTypeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class ReportLocalTasks extends DeriverBase implements ContainerDeriverInterface {
  use StringTranslationTrait;

  protected $entityManager;

  /**
   * Constructor.
   *
   * @param $entityManager
   * @param $stringTranslation;
   */
  public function __construct(EntityTypeManagerInterface $entityManager, TranslationInterface $stringTranslation) {
    $this->entityManager = $entityManager;
    $this->stringTranslation = $stringTranslation;
  }

  public static function create(ContainerInterface $container, $base_plugin_id) {
    return new static(
      $container->get('entity_type.manager'),
      $container->get('string_translation')
    );
  }

  public function getDerivativeDefinitions($base_plugin_definition) {
    $this->derivatives = [];

    /** @var EntityStorageInterface $entityStorage */
    $entityStorage = $this->entityManager->getStorage('custom_type');

    $entityType = $this-> entityManager->getDefinition('lightspeed_ecom_connection');

    /** @var CustomEntityTypeInterface $connection */
    foreach ($entityStorage->loadMultiple() as $entity) {
      $this->derivatives["MODULE.settings.list.{$entity->id()}"] = [
        'title' => $entity->label(),
        'route_name' => 'MODULE_ecom.settings.list',
        'route_parameters' => [
          'connection' => $entity->id(),
        ],
      ];
    }

    foreach ($this->derivatives as &$entry) {
      $entry += $base_plugin_definition;
      $entry  += ['cache_tags' => []];
      $entry['cache_tags'] += $entityType->getListCacheTags();
    }

    return $this->derivatives;
  }
}

```

# Answer
> 0 votes
Knowing that `drupal_flush_all_caches()` solves your problem, all you need to do is to execute a subset of that function's code. The docs for `drupal_flush_all_caches()` is well documented so you can create a variant of that function that is specific for your needs.

For example:

```
function _MODULE_flush_partial_cache() {

  // Empty all cache bins & let modules take action on it
  // It possible that you might get by with emptying a subset of cache bins
  // (e.g. cache.menu & cache.entity?) but lets knock them all out.
  $module_handler = \Drupal::moduleHandler();
  $module_handler->invokeAll('cache_flush');
  foreach (Cache::getBins() as $service_id => $cache_backend) {
    $cache_backend->deleteAll();
  }

  // Ignore CSS, JS, static, Twig, module, & theme
  // rebuilding that's not needed in this context.

  // Obviously, your routes need to be rebuilt.
  \Drupal::service('router.builder')->rebuild();
}

/**
 * Implements hook_ENTITY_TYPE_insert();
 */
function MODULE_CUSTOM_ENTITY_ETYPE_insert(\Drupal\MODULE\CustomEntityTypeInterface $connection) {
  _MODULE_flush_partial_cache();
}

/**
 * Implements hook_ENTITY_TYPE_delete();
 */
function MODULE_CUSTOM_ENTITY_ETYPE_delete(\Drupal\MODULE\CustomEntityTypeInterface $connection) {
  _MODULE_flush_partial_cache();
}

```

# Answer
> 0 votes
I found a simple solution for this case:

1. there is a local task defined in my\_module.links.task.yml
2. Route has \_custom\_access - visibility of Tab depends on Node-Values
3. I add HOOK\_menu\_local\_tasks\_alter with this code

```
    $data['cacheability']->addCacheTags($node->getCacheTags());

```

Now the local-Tasks will be build again if node is saved.

---
Tags: 8, caching, navigation
---