title: How do I create and use a custom hook?

text:
# Question
Title: How do I create and use a custom hook?
I posted a question here, which had one suggestion, but no answer. Does anyone have any advise on how to implement a custom hook in Drupal 8 which will allow other modules to update a table of contents which is created by a parent module? The table of contents will be an unordered list.

# Answer
> 11 votes
This section resides in a controller in the parent module:

```
$plugin_items = [];
// Call modules that implement the hook, and let them add items.
\Drupal::moduleHandler()->alter('flot_examples_toc', $plugin_items);
if (count($plugin_items > 0)) {
  $output['plugins'] = [
    '#title' => 'Plugins',
    '#theme' => 'item_list',
    '#items' => $plugin_items,
  ];
}

```

And this resides in the child \[module\].module file.

```
use Drupal\Core\Url;

function mymodule_flot_examples_toc_alter(&$item_list) {
  $options = [
    ':one' => Url::fromRoute('flot_spider_examples.example')->toString(),
  ];
  $item_list[] = t('<a href=":one">Spider Chart</a> (with spider plugin)', $options);
}

```

The parent creates an array and passes it to the children by reference. They can alter the array by adding elements to it. The parent then adds it to the render array.

# Answer
> 14 votes
Just for simplicity sake if you want to create and use a custom hook in drupal 8 for other developers to use

first to help others define the use of your hook in mymodule.api.php file this hook can act on anything required.

example:

```
 // my hook
 function hook_mymodule_alter_something(array &$data) {
   // here others will make a module that will call this to alter "$data"
 }

```

then when needed in your module.

```
 \Drupal::moduleHandler()->invokeAll('mymodule_alter_something', [&$data]);

```

then the other developer can then make use of this by calling

```
function MYOTHERMODULE_mymodule_alter_something($data) {

```

# Answer
> -1 votes
You need to implement two hooks 1. hook\_token\_info() & 2. hook\_tokens() in your module file Below i have given code to create my custom token "query-param-all" and used that token in views- Textarea field.....

```
/**
* Implements hook_token_info().
*/
function mycustom_token_info() {
  $type = [
      'name' => ('Custom Token'),
      'description' => ('Tokens for custom things.'),
  ];
  $node['query-param-all'] = [
      'name' => ("Get all URL query string"),
      'description' => ('Get all URL query string'),
  ];
  return [
      'types' => ['customtoken' => $type],
      'tokens' => ['customtoken' => $node],
  ];
}

/**
* Implements hook_tokens().
*/
function mycustom_tokens($type, $tokens, array $data, array $options, \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata) {
  $replacements = [];
  //print '<pre>'; print_r($data);exit;
  $current_path = \Drupal::request()->query->all();
  $query_param = '';
  if( count($current_path) > 0) {
    $amper = '';
    $query_param = '?';
    foreach($current_path as $key => $value){
      $query_param .= $amper.$key.'='.$value;
      $amper = '&';
    }
  }

  if ($type == 'customtoken') {
      foreach ($tokens as $name => $original) {
          switch ($name) {
              case 'query-param-all':
                  $replacements[$original] = $query_param;
              break;
          }
      }
  }
  return $replacements;
}

```

---
Tags: 8
---


# Question
Title: Add formating to custom views's field (to remove sourounding quotes for the JSON values)
My custom views field returns JSON as a string:

```
"order_line_items": "[{"sku":"GC2","quantity":"5.00","price":"50.00"},{"sku":"SGBLEND","quantity":"1.00","price":"6.00"},{"sku":"NAV1STYR","quantity":"1.00","price":"9.50"}]",

```

I hope to get rid of the surrounding quotation (") marks.

This is how the field is created

```
class OrderLineItems extends FieldPluginBase {

  public function usesGroupBy() {
    return FALSE;
  }

  public function query() {
    // Do nothing -- to override the parent query.
  }

  public function render(ResultRow $values) {
    $order = $this->getEntity($values);

    $details = [];

    foreach ($order->getItems() as $order_item) {
      $product_sku = $order_item->getPurchasedEntity()->getSku();
      $product_quantity = $order_item->getQuantity();
      $product_price = $order_item->getUnitPrice()->getNumber();

      $order_item_array = [
        'sku' => $product_sku,
        'quantity' => $product_quantity,
        'price' => number_format((float)$product_price, 2, '.', '')
      ];
     $details[] = $order_item_array;
    }
    return json_encode($details);
    return $details;
  }
}

```

I haven't found how can I add a formatter for this custom field, to make this the JSON output valid. I did some research and I have found that there are modules that can do this for real fields, but not for this kind of custom views fields.

https://www.drupal.org/project/rest\_views

# Answer
> 0 votes
I was able to achieve this using custom views serializer. Here is the guide:

https://www.paralleldevs.com/blog/how-create-custom-views-serializer-drupal-8-or-9

---
Tags: 8, views
---


title: All pages give 404 using nginx

text:
# Question
Title: All pages give 404 using nginx
I am building a drupal site with drupal 9, and i want to publish it using azure devops.

Only the problem is that every page, except the frontpage, gives a 404 error. I've looked around the internet, and most people say they miss the .htaccess file, it is however included.

I can't edit files directly, and have to change it locally and then commit to change them, but it does somehow work locally.

The drupal files (including the htaccess file) are in a folder 'src'.

Does anybody know how to fix this?

Edit: The server is nginx

# Answer
Nginx can’t process htaccess files without a specific extension installed.

You either need to install that extension or manually configure nginx to work with Drupal.

There’s an example config here if you go down the manual route: https://www.nginx.com/resources/wiki/start/topics/recipes/drupal/
> 3 votes

---
Tags: deployment
---


# Question
Title: How to enable plain text format for body in specific content type
I have one content type, where I want to restrict the body field to plain text only. In Drupal 7, there was an option to set the plain text format explicitly in field configuration; however in Drupal 8, there is no "plain text" option in the select box. I cannot change user permissions to text formats, because I want to have plain text for the administrator as well.

In Drupal 7:

In Drupal 8:

So please, how to force the plain text in body field? Do I have to create a new text format? If so, why such a nonsense (my desired text format already exists, but cannot be used)?

Thanks!

# Answer
See the issue Allow text field to enforce a specific text format. You can use the patch from this issue or install the module Allowed Formats which provides these options in the text field configuration:
> 6 votes

# Answer
You've got a good point. I don't know why the built in plain text format is disabled unless all other formats are deleted ... "This format is shown when no other formats are available."

I couldn't find an answer for it's disabled either.

So you have to create a new plain text format.

All you have to do is go to admin/config/content/formats. Then Add text format. Then fill out the next screen and set Display any HTML as plain text.
> 1 votes

# Answer
In Drupal 8 Just add new plain text format by giving the extra space between plain and text label it will create the new one for you.
> 0 votes

# Answer
You can fix this with a hidden config option (not present in the GUI). You can set it from drush:

```
drush cset filter.settings always_show_fallback_choice TRUE

```
> 0 votes

---
Tags: 8, entities
---


# Question
Title: Form submits before File Upload completes
I have a form. It works... but the user must wait for the large file to upload first or they won't be processed by the form. The upload form will change from the Upload button to the name of the file with a Remove button.

```
    $validators = [
      'file_validate_extensions' => ['zip'],
    ];

    $form['static_archive'] = [
      '#type' => 'managed_file',
      '#name' => 'static_archive',
      '#title' => t('Static archive'),
      '#size' => 200,
      '#description' => t('ZIP format only'),
      '#upload_validators' => $validators,
      '#upload_location' => 'private://imports/uploads/',
    ];

    ...
    other fields here
    ...

    $form['actions']['#type'] = 'actions';
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Import Archive'),
      '#button_type' => 'primary',
    ];

```

This form is outputted on a route like I want, file is processed, etc. The only catch is the user having to wait.

I'm hoping there is some JS method for disabling the Submit button while the file is uploading. Bonus if there's a way to show upload progress or has a message explaining why it's disabled. The file upload is not a required field, so when the uses the form and the file doesn't upload before they hit submit, it's lost in the wind and the user doesn't even get an error.

This isn't webforms, so this doesn't solve it. https://www.drupal.org/project/webform/issues/3010084

This isn't solve via this patch: https://www.drupal.org/project/drupal/issues/2869855

I also tried clientside\_validation: https://www.drupal.org/project/clientside\_validation b/c of this: How to prevent an Ajax form from submitting

But I am having no luck solving this. Hoping someone has ideas to share.

# Answer
After spending half the day searching, I finally got a solution. I ended up using some of the code from the webform module to prevent the form from being submitted. Specifically, take a look at the webform.element.managed\_file.js and the patch from https://www.drupal.org/files/issues/2018-10-31/3010084-17.patch

I created my prevent\_submit.js file below with the following:

```
(function ($, Drupal, once) {
Drupal.behaviors.preventSubmitForm = {
  attach: function (context, drupalSettings) {

    // Add submit handler to file upload form.
    $(context).find('form')
      .once('webform-auto-file-upload')
      .on('submit', function (event) {
        var $form = $(this);
        if ($form.data('webform-auto-file-uploads') > 0 && blockSubmit($form)) {
          event.preventDefault();
          return false;
        }
        return true;
      });

    $(context).find('input[type="file"]').once('webform-auto-file-upload').on('change', function () {

      // Track file upload.
      $(this).data('msk-auto-file-upload', true);

      // Increment form file uploads.
      var $form = $(this.form);
      var fileUploads = ($form.data('webform-auto-file-uploads') || 0);
      $form.data('webform-auto-file-uploads', fileUploads + 1);
    });

    /**
     * Block form submit.
     *
     * @param {jQuery} form
     *   A form.
     *
     * @returns {boolean}
     *   TRUE if form submit should be blocked.
     */
    function blockSubmit(form) {
      if ($(form).data('webform-auto-file-uploads') < 0) {
        return false;
      }

      var message = Drupal.t('File upload in progress. Uploaded file may be lost.') +
        '\n' +
        Drupal.t('Do you want to continue?');
      return !window.confirm(message);
    }

  },
  detach: function detach(context, settings, trigger) {
    if (trigger === 'unload') {
      $(context).find('input[type="file"]').removeOnce('webform-auto-file-upload').each(function () {
        if ($(this).data('msk-auto-file-upload')) {
          // Remove file upload tracking.
          $(this).removeData('msk-auto-file-upload');

          // Decrease form file uploads.
          var $form = $(this.form);
          var fileUploads = ($form.data('webform-auto-file-uploads') || 0);
          $form.data('webform-auto-file-uploads', fileUploads - 1);
        }
      });
    }
  }
};
  }
)
(jQuery, Drupal, once);

```
> 1 votes

---
Tags: 8, forms
---


title: Entity caching and computed fields

text:
# Question
Title: Entity caching and computed fields
I have a custom entity that I've added some computed fields to, so the classes involved are:

* `class MyEntity extends ContentEntityBase`
* `class ComputedFieldA extends FieldItemList`
* `class ComputedFieldB extends FieldItemList`

My issue is that I want the computed fields to either not be cached at all, or to be cached but with a different invalidation strategy.

Is what I want possible? Or do I just need to invalidate cache for the whole entity?

Edit: The custom field itself is basically a count of the rows in another database table. I want that count to be cached separately then invalidated if new rows are inserted.

The output is being consumed via the default jsonapi endpoint for the entity.

Edit 2: I guess this is more complex than I thought so I'll describe the entities in more detail:

Entity A:

* Has a 1:1 relationship with nodes based on UUID
* Has a count of Entity B entities that are related to the node (computed)
* Has a calculation based on that count (computed)

Entity B:

* Has an N:1 relationship with nodes based on NID

So, if a new Entity B is created for node X then I want the cache invalidated on the two computed fields of Entity A for node XXXX-XXXX-XXXX-XXXX so that the jsonapi endpoint shows up to date computed data.

If I can add a cache tag of node:X to each instance of the computed field I expect that would achieve what I want.

# Answer
I doesn't matter whether the field is computed. Caching depends on how the field is rendered in the field formatter. Cache metadata added to the render array bubbles up and is merged with that of the other fields and the entity.

Fields by default are not cached on their own, but you can change this. If the field needs a different caching strategy, add cache keys, and the rendered field can be shared between fields in other entity instances rendering with the same cache keys. If the field is highly dynamic then add a lazy builder and make sure it gets placeholdered, either automatically or by setting #create\_placeholder.

Edit 2:

The entity class could add a cache list tag for the entity b:

```
class MyEntity extends ContentEntityBase {

  /**
   * {@inheritdoc}
   */
  public function getCacheTags() {
    $cache_tags = parent::getCacheTags();
    $cache_tags[] = 'entity_b_list';
    return $cache_tags;
  }

```
> 3 votes

---
Tags: caching, 9
---


# Question
Title: I'm trying to release a contrib module, but only see the dev version
I created an 8.x version of a contrib module I own (https://www.drupal.org/project/node\_usage). It has a branch (`8.x-1.x`) and tag (`8.x-1.0`) and added the release, but the only 8.x one I see is `8.x-1.x-dev`. If I click "add new release", it says no valid branches or tags found, but my branch and tag look like the examples they give. What do I have to do to make a non-dev release?

# Answer
I just cloned the repo but there is not an 8.x-1.0 tag. You have to push the tag with `git push origin 8.x-1.0`. Then you can create the 8.x-1.0 release.
> 6 votes

---
Tags: version-control, community-official-site
---


# Question
Title: theme_preprocess_menu is not seeing main menu
Background: I have an event website where I want to hide the event registration menu item when registration for the event is closed.

theme\_preprocess\_menu is not triggering on anything except 'menu\__toolbar'. The code:

```
function myTheme_preprocess_menu(&$variables, $hook) {
  dpm($hook);
}

```

merely gives the result "menu\__toolbar" on any page where my theme is active. My theme is based on Bartik.

How do I get it to trigger on the main menu?

# Answer
What you get in `$hook` doesn't say for which menu the preprocess function is called. It's simple a suggestion Drupal is picking up in `ThemeManager::render()`.

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

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

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

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

```

`olivero_theme_suggestions_menu_alter()` is one of the hooks that alter the suggestions for the menu template file. There could be a module doing a similar suggestion, in your case.

```
function olivero_theme_suggestions_menu_alter(&$suggestions, array $variables) {
  if (isset($variables['attributes']['region'])) {
    $suggestions[] = 'menu__' . $variables['attributes']['region'];
  }
}

```

What you are looking for is the menu machine name, stored in `$variables['menu_name']` as documented in menu.html.twig.

If the purpose is hiding a menu item, using that preprocess hook is not the solution. To hide a menu item is sufficient, for example, to avoid the currently logged-in user has access to the route used for that menu item. In that case, Drupal won't show the menu item.
> 0 votes

---
Tags: theming, hooks, navigation
---


# Question
Title: Change the default "Create new account" title on the User registration page
I would like the change the default title (and probably also the meta title) of the User registration page, currently "Create new account".

I tried the Static and Dynamic page title method described here, as well as a couple of other suggestions, but none of them worked...

Anyone? I added a print screen of the route (registration\_link.register), the URL is user/register.

# Answer
> 7 votes
You can add the following two hooks to your theme's `*.theme` file or your module's `*.module` file. The first to alter the title that's displayed in the page title block. The second to alter the meta title.

```
/**
 * Implements hook_preprocess_HOOK().
 */
function MYTHEME/MYMODULE_preprocess_page_title(&$variables) {

  if (\Drupal::routeMatch()->getRouteName() == 'user.register') {

    $variables['title'] = t('New Title');
  }
}

/**
 * Implements hook_preprocess_HOOK().
 */
function MYTHEME/MYMODULE_preprocess_html(&$variables) {

  if (\Drupal::routeMatch()->getRouteName() == 'user.register') {

    $variables['head_title']['title'] = t('New Title');
  }
}

```

# Answer
> 0 votes
In Drupal 9 this will be

```
/**
 * Implements hook_preprocess_HOOK().
 */
function MYTHEME/MYMODULE_preprocess_page_title(&$variables) {

  if (\Drupal::routeMatch()->getRouteName() == 'registration_link.register') {

    $variables['title'] = t('New Title');
  }
}

/**
 * Implements hook_preprocess_HOOK().
 */
function MYTHEME/MYMODULE_preprocess_html(&$variables) {

  if (\Drupal::routeMatch()->getRouteName() == 'registration_link.register') {

    $variables['head_title']['title'] = t('New Title');
  }
}

```

---
Tags: 8, theming, users
---


# Question
Title: Functional Testing - Schema Incomplete Exception missing schema
I am working on my first functional test for a custom module. Each time I run the functional test I get errors dealing with configuration schema issues. However, I'm confused by the error message.

**Command**

`phpunit modules/custom/worx_blog`

**Error Message**

```
Schema errors for editor.editor.full_html with the following errors:

editor.editor.full_html:settings.plugins.linkit missing schema

```

**My Module Conifg** \- Module Conifg File

If you look at the file you can see that the `linkit` entry is there. So I don't know what it means by it's missing.

# Answer
As Configuration API says, each module needs to define its configuration schema in files in the config/schema directory under the top-level module directory. That is used by Drupal core to validate the configuration file and verify it's not corrupted.

For example, the Book module uses the following schema, for its configuration object (core/modules/book/config/schema/book.schema.yml).

```
# Schema for the configuration files of the book module.

book.settings:
  type: config_object
  label: 'Book settings'
  mapping:
    allowed_types:
      type: sequence
      label: 'Content types allowed in book outlines'
      sequence:
        type: string
        label: 'Content type'
    block:
      type: mapping
      label: 'Block'
      mapping:
        navigation:
          type: mapping
          label: 'Navigation'
          mapping:
            mode:
              type: string
              label: 'Mode'
    child_type:
      type: string
      label: 'Content type for child pages'

block.settings.book_navigation:
  type: block_settings
  label: 'Book navigation block'
  mapping:
    block_mode:
      type: string
      label: 'Block display mode'

```

That is different from its configuration file, which is located in core/modules/book/config/installbook.settings.yml.

```
allowed_types:
  - book
block:
  navigation:
    mode: 'all pages'
child_type: book

```

The code that throws that exception is contained in the `ConfigSchemaChecker` class, used to implement an event subscriber that is invoked when a configuration object is saved.

```
  public function onConfigSave(ConfigCrudEvent $event) {
    // Only validate configuration if in the default collection. Other
    // collections may have incomplete configuration (for example language
    // overrides only). These are not valid in themselves.
    $saved_config = $event->getConfig();
    if ($saved_config->getStorage()->getCollectionName() != StorageInterface::DEFAULT_COLLECTION) {
      return;
    }
    $name = $saved_config->getName();
    $data = $saved_config->get();
    $checksum = Crypt::hashBase64(serialize($data));
    if (!in_array($name, $this->exclude) && !isset($this->checked[$name . ':' . $checksum])) {
      $this->checked[$name . ':' . $checksum] = TRUE;
      $errors = $this->checkConfigSchema($this->typedManager, $name, $data);
      if ($errors === FALSE) {
        throw new SchemaIncompleteException("No schema for {$name}");
      }
      elseif (is_array($errors)) {
        $text_errors = [];
        foreach ($errors as $key => $error) {
          $text_errors[] = new FormattableMarkup('@key @error', [
            '@key' => $key,
            '@error' => $error,
          ]);
        }
        throw new SchemaIncompleteException("Schema errors for {$name} with the following errors: " . implode(', ', $text_errors));
      }
    }
  }

```
> 1 votes

---
Tags: testing
---


# Question
Title: Entity access handler causing "The controller result claims to be providing relevant cache metadata, but leaked metadata was detected."
I'm writing a custom entity access handler for a custom entity that checks if a user has access to the underlying node. The checkAccess() code looks like this:

```
$uuid = $entity->uuid->getString();
if (isset($uuid)) {
  $nodes = $this->node->getQuery()->accessCheck(TRUE)->condition('uuid', $uuid)->execute();
  if (!empty($nodes)) {
    return AccessResult::allowed();
  }
}
return AccessResult::forbidden("Access denied.");

```

The problem is when I go to the jsonapi endpoint for the entity, I'm getting the error "`The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\\jsonapi\\CacheableResourceResponse.`"

If I return accessResult::allowed() before the $nodes query it works. What's happening here?

# Answer
It looks like using accessCheck(TRUE) causes this, so the workaround is:

```
if (isset($uuid)) {
  $node = $this->node->loadByProperties(['uuid' => $uuid]);
  $node = reset($action);
  if ($node->access('view')) {
    return AccessResult::allowed();
  }
}

```
> 2 votes

---
Tags: entities, 9
---


title: Wrong user with currentUser() on prod

text:
# Question
Title: Wrong user with currentUser() on prod
I'm getting the name of my user that I load from the profile. It's works fine on my local, but on my prod, the firstname displayed is showing the name from the previous user. How do I prevent that ? This is how I get my data.

This is where I don't understand, I'm loading data by current user, but the user isn't the good one...

I have my "new" firstname only after clearing my website cache with drush

```
/**
 * Implements hook_preprocess_HOOK().
 */
function frr_menu_link_preprocess_menu__account(&$variables)
{
    $current_user = \Drupal::currentUser();
    $variables['firstname'] = FALSE;
    if($current_user->isAuthenticated()) {
        $entity_manager = \Drupal::entityTypeManager();
        $profileStorage = $entity_manager->getStorage('profile');
        $profileCustomerFound = $profileStorage->loadByProperties([
            'uid' => $current_user->id(),
            'type' => 'customer',
            'is_default' => 1,
            'status' => 1,
        ]);
        if($profileCustomerFound > 0) {
            $customer = end($profileCustomerFound);
            $firstname = $customer->get('field_firstname')->value;
            $variables['firstname'] = !empty($firstname) ? ucfirst($firstname) : FALSE;
        }
    }
}

```

# Answer
> 3 votes
This is likely because you must add cacheability metadata so Drupal knows to cache the content separately by user.

---
Tags: users, caching, 9
---


# Question
Title: Upgrading from 8.9.20 to 9.3.9 causing errors
As the title suggests, I'm running into composer issues that I can't seem to figure out. I've found a few similar articles but none of them have seemed to fix my instance.

I've been following the steps from Upgrading from Drupal 8 to Drupal 9 (or later) and got stuck on step 3 `composer update`. I get the following errors:

```
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - drupal/core-recommended 9.2.0-alpha1 requires symfony/var-dumper v5.2.6 -> satisfiable by symfony/var-dumper[v5.2.6].
    - Conclusion: don't install symfony/var-dumper v5.2.6 (conflict analysis result)
    - drupal/core-recommended 9.3.0-alpha1 requires symfony/var-dumper v5.3.8 -> satisfiable by symfony/var-dumper[v5.3.8].
    - Conclusion: don't install symfony/var-dumper v5.3.8 (conflict analysis result)
    - drupal/core-recommended[9.2.0-beta1, ..., 9.2.0-beta2] require symfony/var-dumper v5.2.8 -> satisfiable by symfony/var-dumper[v5.2.8].
    - Conclusion: don't install symfony/var-dumper v5.2.8 (conflict analysis result)
    - drupal/core-recommended 9.2.0-beta3 requires symfony/var-dumper v5.3.0-RC1 -> satisfiable by symfony/var-dumper[v5.3.0-RC1].
    - Conclusion: don't install symfony/var-dumper v5.3.0-RC1 (conflict analysis result)
    - drupal/core-recommended 9.3.0-beta1 requires symfony/var-dumper v5.3.10 -> satisfiable by symfony/var-dumper[v5.3.10].
    - Conclusion: don't install symfony/var-dumper v5.3.10 (conflict analysis result)
    - drupal/core-recommended 9.3.0-beta2 requires symfony/var-dumper v5.4.0-BETA2 -> satisfiable by symfony/var-dumper[v5.4.0-BETA2].
    - Conclusion: don't install symfony/var-dumper v5.4.0-BETA2 (conflict analysis result)
    - drupal/core-recommended[9.0.0-beta3, ..., 9.0.0-rc1] require symfony/var-dumper v5.0.8 -> satisfiable by symfony/var-dumper[v5.0.8].
    - Conclusion: don't install symfony/var-dumper v5.0.8 (conflict analysis result)
    - drupal/core-recommended[9.3.0-beta3, ..., 9.3.0-rc1] require symfony/var-dumper v5.4.0-RC1 -> satisfiable by symfony/var-dumper[v5.4.0-RC1].
    - Conclusion: don't install symfony/var-dumper v5.4.0-RC1 (conflict analysis result)
    - drupal/core-recommended[9.0.0, ..., 9.0.12] require symfony/var-dumper v5.1.0 -> satisfiable by symfony/var-dumper[v5.1.0].
    - Conclusion: don't install symfony/var-dumper v5.1.0 (conflict analysis result)
    - drupal/core-recommended[9.1.0-beta1, ..., 9.1.7] require symfony/var-dumper v5.1.8 -> satisfiable by symfony/var-dumper[v5.1.8].
    - Conclusion: don't install symfony/var-dumper v5.1.8 (conflict analysis result)
    - drupal/core-recommended[9.0.13, ..., 9.1.x-dev] require symfony/var-dumper v5.1.11 -> satisfiable by symfony/var-dumper[v5.1.11].
    - Conclusion: don't install symfony/var-dumper v5.1.11 (conflict analysis result)
    - drupal/core-recommended[9.2.0-rc1, ..., 9.2.x-dev] require symfony/var-dumper v5.3.0 -> satisfiable by symfony/var-dumper[v5.3.0].
    - Conclusion: don't install symfony/var-dumper v5.3.0 (conflict analysis result)
    - drupal/core-recommended[9.3.0, ..., 9.4.x-dev] require symfony/var-dumper v5.4.0 -> satisfiable by symfony/var-dumper[v5.4.0].
    - Conclusion: don't install symfony/var-dumper v5.4.0 (conflict analysis result)
    - drupal/core-recommended 9.0.0-alpha1 requires composer/installers v1.7.0 -> satisfiable by composer/installers[v1.7.0].
    - drupal/core-recommended[9.0.0-alpha2, ..., 9.0.0-beta2] require composer/installers v1.8.0 -> satisfiable by composer/installers[v1.8.0].
    - composer/installers[v1.7.0, ..., v1.8.0] require composer-plugin-api ^1.0 -> found composer-plugin-api[2.1.0] but it does not match the constraint.
    - symfony/var-dumper v5.1.7 conflicts with symfony/var-dumper v5.1.7.
    - drupal/core-recommended 9.1.0-alpha1 requires symfony/var-dumper v5.1.7 -> satisfiable by symfony/var-dumper[v5.1.7].
    - Root composer.json requires drupal/core-recommended ^9 -> satisfiable by drupal/core-recommended[9.0.0-alpha1, ..., 9.4.x-dev].
    - Root composer.json requires phpunit/phpunit >=4.8.28 <5 -> satisfiable by phpunit/phpunit[4.8.28, ..., 4.8.36].

```

When I use `composer depends symfony/var-dumper`, I get the following:

```
drupal/devel         4.1.5    requires   symfony/var-dumper (^4 || ^5)
drush/drush          9.7.3    requires   symfony/var-dumper (^3.4 || ^4.0)
psy/psysh            v0.11.0  requires   symfony/var-dumper (^6.0 || ^5.0 || ^4.0 || ^3.4)
symfony/http-kernel  v3.4.44  conflicts  symfony/var-dumper (<3.3)

```

My composer.json file is below. Please let me know if there are any issues in here that are causing this.

```
{
    "name": "drupal-composer/drupal-project",
    "description": "Project template for Drupal 8 projects with composer",
    "type": "project",
    "license": "GPL-2.0+",
    "authors": [
        {
            "name": "",
            "role": ""
        }
    ],
    "repositories": [
        {
            "type": "composer",
            "url": "https://packages.drupal.org/8"
        }
    ],
    "require": {
        "composer/installers": "^1.2",
        "cweagans/composer-patches": "^1.7",
        "drupal/addtoany": "^1.9",
        "drupal/admin_toolbar": "^3.0",
        "drupal/adminimal_admin_toolbar": "^1.5",
        "drupal/adminimal_theme": "^1.3",
        "drupal/auto_entitylabel": "^3.0",
        "drupal/back_to_top": "^2.0",
        "drupal/better_exposed_filters": "^5.0",
        "drupal/blazy": "^2.5",
        "drupal/block_exclude_pages": "^2.0",
        "drupal/block_visibility_groups": "^1.2",
        "drupal/bootstrap": "^3.9",
        "drupal/classy_paragraphs": "^1.0@beta",
        "drupal/colorbox": "^1.4",
        "drupal/components": "^2.0",
        "drupal/config_split": "^1.4",
        "drupal/console": "~1.0",
        "drupal/console-launcher": "~1.0",
        "drupal/core-composer-scaffold": "^9",
        "drupal/core-project-message": "^9",
        "drupal/core-recommended": "^9",
        "drupal/crop": "^2.0",
        "drupal/date_range_formatter": "^4.0",
        "drupal/draggableviews": "^2.0",
        "drupal/dropzonejs": "^2.1",
        "drupal/ds": "^3.1",
        "drupal/easy_breadcrumb": "^2.0",
        "drupal/editor_advanced_link": "^1.4",
        "drupal/editor_file": "^1.2",
        "drupal/entity_browser": "^2.1",
        "drupal/entity_embed": "^1.0@beta",
        "drupal/facebook_pixel": "^1.0@alpha",
        "drupal/facets": "^1.0@beta",
        "drupal/fb_likebox": "^2.1",
        "drupal/field_group": "^3.0",
        "drupal/file_browser": "^1.1",
        "drupal/focal_point": "^1.0@beta",
        "drupal/fontawesome": "^2.19",
        "drupal/google_analytics": "^2.2",
        "drupal/header_and_footer_scripts": "^3.0",
        "drupal/honeypot": "^2.0",
        "drupal/iframe": "^2.0",
        "drupal/imce": "^2.0",
        "drupal/jquery_countdown_timer": "^1.2",
        "drupal/jquery_ui": "^1.4",
        "drupal/jquery_ui_datepicker": "^1.1",
        "drupal/jquery_ui_slider": "^1.1",
        "drupal/jquery_ui_touch_punch": "^1.0",
        "drupal/linkit": "^5.0@beta",
        "drupal/menu_link_attributes": "^1.0",
        "drupal/menu_trail_by_path": "^1.1",
        "drupal/metatag": "^1.4",
        "drupal/migrate_plus": "^5.1",
        "drupal/migrate_source_csv": "^3.4",
        "drupal/migrate_tools": "^5.0",
        "drupal/moderated_content_bulk_publish": "^2.0",
        "drupal/override_node_options": "^2.4",
        "drupal/paragraphs": "^1.2",
        "drupal/paragraphs_previewer": "^1.0",
        "drupal/pathauto": "^1.0",
        "drupal/rabbit_hole": "^1.0@beta",
        "drupal/redirect": "^1.2",
        "drupal/responsive_favicons": "^1.4",
        "drupal/responsive_menu": "^4.0",
        "drupal/scheduler": "^1.0",
        "drupal/schema_metatag": "^2.0",
        "drupal/search_api": "^1.6",
        "drupal/search_api_attachments": "^1.0@beta",
        "drupal/search_api_solr": "^1.2",
        "drupal/search_autocomplete": "^2.0",
        "drupal/simple_gmap": "^3.0",
        "drupal/simple_sitemap": "^4.0",
        "drupal/slick": "^2.0",
        "drupal/slick_views": "^2.0",
        "drupal/structure_sync": "^2.0",
        "drupal/superfish": "^1.3",
        "drupal/svg_image": "^1.8",
        "drupal/time_field": "^2.0",
        "drupal/tvi": "^1.0@beta",
        "drupal/twig_tweak": "^2.1",
        "drupal/video_embed_field": "^2.0",
        "drupal/views_autocomplete_filters": "^1.2",
        "drush/drush": "~8.0|^9.0.0-beta8",
        "symfony/property-access": "^4.3",
        "webflo/drupal-finder": "^1.0.0",
        "webmozart/path-util": "^2.3"
    },
    "require-dev": {
        "behat/mink": "~1.7",
        "behat/mink-goutte-driver": "~1.2",
        "drupal/devel": "^4.0",
        "drupal/masquerade": "^2.0@beta",
        "drupal/slick_extras": "^1.0@beta",
        "jcalderonzumba/gastonjs": "~1.0.2",
        "jcalderonzumba/mink-phantomjs-driver": "~0.3.1",
        "kint-php/kint": "^3.3",
        "mikey179/vfsstream": "~1.2",
        "phpunit/phpunit": ">=4.8.28 <5",
        "symfony/css-selector": "~2.8|~3.0"
    },
    "conflict": {
        "drupal/drupal": "*"
    },
    "minimum-stability": "dev",
    "prefer-stable": true,
    "config": {
        "sort-packages": true
    },
    "autoload": {
        "classmap": [
            "scripts/composer/ScriptHandler.php"
        ]
    },
    "scripts": {
        "drupal-scaffold": "DrupalComposer\\DrupalScaffold\\Plugin::scaffold",
        "pre-install-cmd": [
            "DrupalProject\\composer\\ScriptHandler::checkComposerVersion"
        ],
        "pre-update-cmd": [
            "DrupalProject\\composer\\ScriptHandler::checkComposerVersion"
        ],
        "post-install-cmd": [
            "DrupalProject\\composer\\ScriptHandler::createRequiredFiles",
            "cp web/modules/contrib/linkit/js/autocomplete.js web/modules/contrib/linkit/js/linkit.autocomplete.js"
        ],
        "post-update-cmd": [
            "DrupalProject\\composer\\ScriptHandler::createRequiredFiles",
            "cp web/modules/contrib/linkit/js/autocomplete.js web/modules/contrib/linkit/js/linkit.autocomplete.js"
        ]
    },
    "extra": {
        "installer-paths": {
            "web/core": ["type:drupal-core"],
            "web/libraries/{$name}": ["type:drupal-library"],
            "web/modules/contrib/{$name}": ["type:drupal-module"],
            "web/profiles/contrib/{$name}": ["type:drupal-profile"],
            "web/themes/contrib/{$name}": ["type:drupal-theme"],
            "drush/contrib/{$name}": ["type:drupal-drush"]
        },
        "drupal-scaffold": {
            "locations": {
                "web-root": "web/"
            }
        },
        "patches": {
          "drupal/core": {
            "CKEditor Style for <img> not working because it is an 'image' widget (drupal.org/project/drupal/issues/2642808)": "https://www.drupal.org/files/issues/2019-01-08/2642808-65.patch",
            "Indirect modification of overloaded element with Views responsive table (drupal.org/project/drupal/issues/2894449)": "https://www.drupal.org/files/issues/2018-10-05/core-indirect-modification-of-overloaded-element-2894449-18.patch",
            "Breadcrumb title doesn't reflect view contextual filter's title override (drupal.org/project/drupal/issues/2787051)": "https://www.drupal.org/files/issues/2018-12-23/drupal-view_title_with_contextual_filters-2787051-28.patch"
          }
        }
    }
}

```

Does anyone have any insights into what I need to do in order to update my site at all? Any help would be appreciated. I'm hoping it's as simple as just requiring an updated version of something.

**EDIT:** Would deleting my lock file help? Don't want to go about doing this in case it breaks something.

**EDIT 2:** I required an update to drush and then tried to `composer update` again but still get the below:

```
Problem 1
    - drupal/core-recommended 9.2.0-alpha1 requires symfony/var-dumper v5.2.6 -> satisfiable by symfony/var-dumper[v5.2.6].
    - Conclusion: don't install symfony/var-dumper v5.2.6 (conflict analysis result)
    - drupal/core-recommended 9.3.0-alpha1 requires symfony/var-dumper v5.3.8 -> satisfiable by symfony/var-dumper[v5.3.8].
    - Conclusion: don't install symfony/var-dumper v5.3.8 (conflict analysis result)
    - drupal/core-recommended[9.2.0-beta1, ..., 9.2.0-beta2] require symfony/var-dumper v5.2.8 -> satisfiable by symfony/var-dumper[v5.2.8].
    - Conclusion: don't install symfony/var-dumper v5.2.8 (conflict analysis result)
    - drupal/core-recommended 9.2.0-beta3 requires symfony/var-dumper v5.3.0-RC1 -> satisfiable by symfony/var-dumper[v5.3.0-RC1].
    - Conclusion: don't install symfony/var-dumper v5.3.0-RC1 (conflict analysis result)
    - drupal/core-recommended 9.3.0-beta1 requires symfony/var-dumper v5.3.10 -> satisfiable by symfony/var-dumper[v5.3.10].
    - Conclusion: don't install symfony/var-dumper v5.3.10 (conflict analysis result)
    - drupal/core-recommended 9.3.0-beta2 requires symfony/var-dumper v5.4.0-BETA2 -> satisfiable by symfony/var-dumper[v5.4.0-BETA2].
    - Conclusion: don't install symfony/var-dumper v5.4.0-BETA2 (conflict analysis result)
    - drupal/core-recommended[9.0.0-beta3, ..., 9.0.0-rc1] require symfony/var-dumper v5.0.8 -> satisfiable by symfony/var-dumper[v5.0.8].
    - Conclusion: don't install symfony/var-dumper v5.0.8 (conflict analysis result)
    - drupal/core-recommended[9.3.0-beta3, ..., 9.3.0-rc1] require symfony/var-dumper v5.4.0-RC1 -> satisfiable by symfony/var-dumper[v5.4.0-RC1].
    - Conclusion: don't install symfony/var-dumper v5.4.0-RC1 (conflict analysis result)
    - drupal/core-recommended[9.0.0, ..., 9.0.12] require symfony/var-dumper v5.1.0 -> satisfiable by symfony/var-dumper[v5.1.0].
    - Conclusion: don't install symfony/var-dumper v5.1.0 (conflict analysis result)
    - drupal/core-recommended[9.1.0-beta1, ..., 9.1.7] require symfony/var-dumper v5.1.8 -> satisfiable by symfony/var-dumper[v5.1.8].
    - Conclusion: don't install symfony/var-dumper v5.1.8 (conflict analysis result)
    - drupal/core-recommended[9.0.13, ..., 9.1.x-dev] require symfony/var-dumper v5.1.11 -> satisfiable by symfony/var-dumper[v5.1.11].
    - Conclusion: don't install symfony/var-dumper v5.1.11 (conflict analysis result)
    - drupal/core-recommended[9.2.0-rc1, ..., 9.2.x-dev] require symfony/var-dumper v5.3.0 -> satisfiable by symfony/var-dumper[v5.3.0].
    - Conclusion: don't install symfony/var-dumper v5.3.0 (conflict analysis result)
    - drupal/core-recommended[9.3.0, ..., 9.4.x-dev] require symfony/var-dumper v5.4.0 -> satisfiable by symfony/var-dumper[v5.4.0].
    - Conclusion: don't install symfony/var-dumper v5.4.0 (conflict analysis result)
    - drupal/core-recommended 9.0.0-alpha1 requires composer/installers v1.7.0 -> satisfiable by composer/installers[v1.7.0].
    - drupal/core-recommended[9.0.0-alpha2, ..., 9.0.0-beta2] require composer/installers v1.8.0 -> satisfiable by composer/installers[v1.8.0].
    - composer/installers[v1.7.0, ..., v1.8.0] require composer-plugin-api ^1.0 -> found composer-plugin-api[2.1.0] but it does not match the constraint.
    - symfony/var-dumper v5.1.7 conflicts with symfony/var-dumper v5.1.7.
    - drupal/core-recommended 9.1.0-alpha1 requires symfony/var-dumper v5.1.7 -> satisfiable by symfony/var-dumper[v5.1.7].
    - Root composer.json requires drupal/core-recommended ^9 -> satisfiable by drupal/core-recommended[9.0.0-alpha1, ..., 9.4.x-dev].
    - Root composer.json requires phpunit/phpunit >=4.8.28 <5 -> satisfiable by phpunit/phpunit[4.8.28, ..., 4.8.36].

```

# Answer
Just wanted to post my solution on here in case anyone else runs into the same issue.

I ended up following this blog post: https://gorannikolovski.com/blog/how-to-upgrade-drupal-8-to-9 and what seemed to do it for me was removing the following dev requirements from my composer file `composer remove behat/mink behat/mink-goutte-driver jcalderonzumba/gastonjs jcalderonzumba/mink-phantomjs-driver mikey179/vfsstream phpunit/phpunit symfony/css-selector`.

I ended up having to update a couple other smaller errors thrown at me after this but once I did that, it was smooth sailing.
> 0 votes

# Answer
`composer require drush/drush` to upgrade to Drush 10 first. Drush 9 is not compatible with Drupal 9.
> 3 votes

---
Tags: upgrading
---


# Question
Title: Alter language select on a node add/edit form
Wonder if anyone has had any experience altering the options presented to a user from the language select on a node add/edit form. I have some custom access control ont the site and basically I'm looking to remove options based on a individual editors access to approved locales. If they haven't been approved to make edits for a certain locale then they shouldn't see an option to add a translation for that locale in the dropdown.

# Answer
> 1 votes
See http://cgit.drupalcode.org/redirect/tree/src/Form/RedirectForm.php#n70

The language widget/form element is designed so that when you explicitly provide some options, then it doesn't add the default.

# Answer
> 1 votes
You can do this in an #after\_build function, the #options aren't build until after form\_alters get called.

```
<?php

/**
* Impliments hook_form_BASE_FORM_ID_alter().
**/
function hook_form_node_form_alter(&$form, $form_state, $form_id) {
  $form['#after_build'][] = '_hook_node_form_after_build';
}

/**
 * Afterbuild to alter language options for node edit forms.
 */
function _hook_node_form_after_build($form, &$form_state) {
  if(isset($form['langcode']['widget'][0]['value']['#options'])){
    // alter options here
  }
  return $form;
}

```

---
Tags: 8, forms, i18n-l10n
---


title: I can't see custom action

text:
# Question
Title: I can't see custom action
I am trying to create a custom action for bulk operations on commerce orders.

I am using the following code.

### mymodule/src/Plugin/Action/DeleteOrderAndApps.php

```
namespace Drupal\mymodule\Plugin\Action;

use Drupal\commerce_order\Entity\Order;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\node\Entity\Node;

/**
 * Delete Order and attached Apps action
 *
 * @Action(
 *   id = "delete_order_and_apps",
 *   label = @Translation("Delete Order and Apps"),
 *   type = "commerce_order"
 * )
 */
class DeleteOrderAndApps extends ActionBase {

  /**
   * {@inheritdoc}
   *
   */
  public function execute(Order $order = NULL) {
    // …
  }

  /**
   * {@inheritdoc}
   */
  public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
    $result = $object->access('delete', $account, TRUE);
    return $return_as_object ? $result : $result->isAllowed();
  }

}

```

I am not sure I need it, but I also have the *mymodule/config/install/system.action.delete\_order\_and\_apps.yml* file.

```
langcode: en
status: true
dependencies:
  module:
    - commerce_order
id: delete_order_and_apps
label: 'Delete Order and Apps'
type: commerce_order
plugin: delete_order_and_apps
configuration: {  }

```

The confusing part is there are 2 different bulk options: VBO and core's bulk actions for Order entity. Sadly these don't use the same action definitions but my new action does not show as an option for either of these when creating a view. I have done many of these the same way but all are Node actions.

Not sure if something missing in my code or possibly something missing in Commerce that is required to allow adding custom actions (something missing in entity definition perhaps).

I am using Drupal 8.9.20 and the Commerce module 8.x-2.24.

# Answer
> 3 votes
Assuming you're using the core "Bulk update" field, which the commerce order View uses out of the box, you need to create the action as a config entity.

You already have the config to do that, but it will only be used when the module is installed. If you're able to uninstall then install your module again, do that and you'll see your action in Views where it should be:

If you can't re-install the module, add an update hook in mymodule.post\_update.php to create the action manually, then run database updates:

```
function mymodule_post_update_add_my_action() {
  \Drupal::entityTypeManager()->getStorage('action')->create([
    'id' => 'delete_orders_and_apps',
    'label' => 'Delete Order and Apps',
    'type' => 'commerce_order',
    'plugin' => 'delete_order_and_apps',
  ])->save();
}

```

If you're using VBO you don't need to create the config entity, the module will pick up the action without it. If that's not working, make sure everything is named correctly, caches are cleared etc, as it's definitely there:

---
Tags: 8, commerce, actions
---


title: Deploying drupal 9 on a remote server

text:
# Question
Title: Deploying drupal 9 on a remote server
I've built my website on my local PC and I'm ready to go live and move it to the remote server.

I've bought a shared hosting package (so I can't run scripts etc, but I can copy the files over and export and import the database using phpmyadmin).

I couldn't for the life of me find a deployment guide for drupal 9, so to get things going, I tried something similar to what I've done for drupal 7 and have this structure on the remote server...

```
/public_html
    /config
    /synccomposer
    /vendor
    /web

```

So I can access the site at: `http://mysite.com/web`

But how do I set things up so that I can access the site at just `http://mysite.com` ? I've tried moving the files in the web directory into `public_html`, and moving the other directories up one level, but that didnt work. What is the best way to do this?

# Answer
> 1 votes
You can try doing this through the .htaccess file, which you should have access to via the File Browser. You may have to turn on the "Show hidden files" option tho.

Edit: Pointed out by apaderno, this is an incomplete answer. To use RewriteCond, you do in fact need to combine Cond with RewriteRule. I updated the below with a more complete answer.

```
RewriteEngine on

RewriteCond %{HTTP_HOST} ^(www.)?yourwebsite.com$
RewriteCond %{REQUEST_URI} !^/web/
RewriteCond %{HTTP_HOST} ^www.domain-name.com$
RewriteRule (.*) /web/$1 [L]

```

---
Tags: 9, deployment
---


# Question
Title: How do I programmatically add fields to Paragraph types?
I have one paragraph type of Product. and I have to add one field to paragraph product programmatically, how can I achive this.

```
function my_module_field_widget_paragraphs_form_alter(&$element, FormStateInterface &$form_state, $context){
  if ($element['#paragraph_type'] == 'products') {
    \Drupal::logger('my_module')->info('debug');

<!-- add field here -->

  }
}

```

and other answer I found is

```
use Drupal\paragraphs\Entity\Paragraph;

  $paragraph = Paragraph::load($paragraph_id);
  $paragraph->field_image[] = [
    'target_id' => $fid,
    'alt' => $alt,
    'title' => $title,
  ];
  $paragraph->save();

```

But in which function or preprocess I need to add above code?

# Answer
We cannot create a field to existing paragraph type using `hook_field_widget_paragraphs_form_alter()`, because it will not create the field. So the best way to add a custom field is using `hook_entity_base_field_info()`

In my case I need to create a text field in all my existing paragraph types and into the new paragraph which will be created in future so I used below code.

```
function hook_entity_base_field_info(EntityTypeInterface $entity_type) {
  if ($entity_type->id() === 'paragraph') {
    $fields['field_parent_section_class'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Parent section class'))
      ->setDescription(t('Class for the parent section element( Add more than one class separated by space )'))
      ->setTranslatable(TRUE)
      ->setComputed(TRUE)
      ->setDisplayConfigurable('form', array(
        'settings' => array (
        'display_label' => TRUE
      )))
      ->setDisplayConfigurable('view', TRUE)
      ->setSettings(array(
        'default_value' => '',
        'max_length' => 255,
        'persist_with_no_fields' => TRUE
      ));
    return $fields;
  }
}

```
> 0 votes

---
Tags: 8, forms, paragraphs
---


# Question
Title: How to reload a (custom) modules dependencies
In Drupal9 , if a module has dependencies, as in

*my\_master\_module.info.yml*

```
dependencies:
  - dependency1:dependency1
  - dependency2:dependency2

```

you cant disable/uninstall any of these dependencies using the UI - the checkbox to do that is disabled.

However, if a (custom) modules dependencies have updated, the dependant module does not automatically get enabled. You can also not enable it using the UI, because the checkbox is disabled.

In fact, if you use drush to enable the already enabled module, it seemingly tries to enable its dependencies, but it doesn't:

```
./drush pm:enable my_master_module
The following module(s) will be enabled: dependency1, dependency2

 Do you want to continue? (yes/no) [yes]:
 > yes

 [success] Successfully enabled: dependency1, dependency2

```

But then .. it did not.

Is there a way to update/reload a custom modules dependencies, so, that Drupal enables all the dependant modules ?

# Answer
Use hook\_update\_N. Here is an example implementation from Metatag (file `metatag.install`):

```
/**
 * Enable the new metatag_open_graph module.
 */
function metatag_update_8107() {
  \Drupal::service('module_installer')->install(['metatag_open_graph']);
  return (string) new TranslatableMarkup("The new Metatag: Open Graph module has been enabled.");
}

```
> 1 votes

---
Tags: .info
---


# Question
Title: Print the "title" variable in unformatted views twig
I am trying to do something like this in views-view-unformatted-something.html.twig:

```
<h3 class="{{ title }}">{{ title }}</h3>

```

...but something goes wrong. Nothing is injected into the class.

# Answer
> 0 votes
You can try using .addClass -\> https://www.drupal.org/docs/8/theming-drupal-8/using-attributes-in-templates

```
  <h3 {{title_attributes.addClass(title)}}>{{ title }}</h3>

```

Additionally, if you are attempting to access the title of the page, which I assume you are, you may have better luck overriding 'page-title.html.twig' instead, since that is actually what renders the title element (your results may vary).

---
Tags: views
---


title: Drush using wrong PHP binary/config

text:
# Question
Title: Drush using wrong PHP binary/config
Using D9 with Drush 10. MacOS/MAMP Pro.

Drush 10 is installed locally using composer and is starting fine and finding the database. sqlc works fine. (I have other Drupal sites that are working just fine with their composer drush, though they are Drupal 8)

The problem seems to be the php binary/config drush is using. In drush status I see:

> PHP binary: /usr/bin/php

> PHP config: /etc/php.ini

The PHP doesn't match what I see in terminal, it should use the MAMP PHP:

**which php**: php: aliased to /Applications/MAMP/bin/php/php7.4.2/bin/php -c "/Library/Application Support/appsolute/MAMP PRO/conf/php7.4.2.ini"

**php -v** PHP 7.4.2 (cli) (built: Feb 17 2020 12:56:02) ( NTS )

I also have **export DRUSH\_PHP=/Applications/MAMP/bin/php/php7.4.2/bin/php**

I can't think of what other config would be affecting which php drush is using... Any help would be appreciated.

# Answer
Try to run drush with your MAMP php, like:

```
./Applications/MAMP/bin/php/php7.4.2/bin/php vendor/drush/drush/drush

```
> 2 votes

---
Tags: drush
---


# Question
Title: How do I get the names of the tables where field data is stored?
In Drupal 7, one was able to retrieve the physical names of the database tables using some code like the following, it was pretty simple.

```
$field_definitions = field_info_fields();
foreach ($field_definitions as $field => $definition) {
  $current_storage = $definition['storage']['details']['sql'][FIELD_LOAD_CURRENT];
  $current_table = key($current_storage);
  $revision_storage = $definition['storage']['details']['sql'][FIELD_LOAD_REVISION];
  $revision_table = key($revision_storage);
}

```

In Drupal 9, the entity API changed for the better and now there are classes and services to pull this type of information. That's all well and good, I understand most of the concepts around this, but I cannot for the life of me seem to extrapolate how to get this same data.

My goal is to loop over all my entity types that implement `ContentEntityTypeInterface`, get their fields, then build an array looking something like so, I have a very specialized Drush script I'm trying to port over from Drupal 7.

```
$example = [
  'node' => [
    'field_something' => [
      'current' => 'some_table_name'
      'revision' => 'some_table_name'
    ]
  ],
  'block' => [
    'field_something' => [
      'current' => 'some_table_name'
      'revision' => 'some_table_name'
    ]
  ]
];

```

\*Taking into account whether a field is actually revisionable in the first place, of course.

On my own, I determined for the most part that table names end up being like like `$entityType . '__' . $field['name']` and `$entityType . '_revision__' . $field['name']` but hardcoding my script breaks down when unique IDs are used. For example, custom blocks have table names like `block_content_r__7fe666c7a4`. I need to be able to pull that data out of the "field storage definition" of sorts.

**Solution:** Per @Clive the solution was to get the storage class for each entity type, and then use it to retrieve the table mappings. This works great for non-base fields, but if you wanted a base field the solution would be different. Here is what I ended up with:

```
$fields = [];
foreach ($this->getContentEntityTypes() as $contentEntityType) {
  $tableMapping = $this->entityTypeManager->getStorage($contentEntityType->id())->getTableMapping();
  foreach ($this->entityFieldManager->getFieldStorageDefinitions($contentEntityType->id()) as $field) {
    // We use requiresDedicatedTableStorage() to filter out base fields
    if ($tableMapping->requiresDedicatedTableStorage($field)) {
      $fieldInfo = [
        'name' => $field->getName(),
        'type' => $field->getType(),
        'table' => $tableMapping->getDedicatedDataTableName($field)
      ];
      if ($field->isRevisionable()) {
        $fieldInfo['table_revision'] = $tableMapping->getDedicatedRevisionTableName($field);
      }
      $fields[$contentEntityType->id()][] = $fieldInfo;
    }
  }
}
return $fields;

```

I wrote a separate method to get my content entities, which the above code used.

```
function getContentEntityTypes() {
  $contentEntityTypes = [];
  $entity_type_definitions = $this->entityTypeManager->getDefinitions();
  /* @var $definition EntityTypeInterface */
  foreach ($entity_type_definitions as $entityType) {
    if ($entityType instanceof ContentEntityTypeInterface && in_array(SqlEntityStorageInterface::class, class_implements($entityType->getStorageClass()))) {
      $contentEntityTypes[] = $entityType;
    }
  }
  return $contentEntityTypes;
}

```

# Answer
> 6 votes
Assuming an SQL storage backend, the table names are available from `TableMappingInterface::getAllFieldTableNames()`. You can get an entity type's table mapping from its storage handler.

This could probably be more elegant but should be a good starting point:

```
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;

...

// These services should be injected if context allows.
$entity_type_manager = \Drupal::entityTypeManager();
/** @var \Drupal\Core\Entity\EntityFieldManagerInterface $field_manager */
$field_manager = \Drupal::service('entity_field.manager');

$tables = [];
foreach ($entity_type_manager->getDefinitions() as $entity_type) {
  // Only list content entity types using SQL storage.
  if ($entity_type instanceof ContentEntityTypeInterface && in_array(SqlEntityStorageInterface::class, class_implements($entity_type->getStorageClass()))) {
    $storage = $entity_type_manager->getStorage($entity_type->id());

    foreach ($field_manager->getFieldStorageDefinitions($entity_type->id()) as $field) {
      $tables[$entity_type->id()][$field->getName()] = $storage->getTableMapping()
        ->getAllFieldTableNames($field->getName());
    }
  }
}

```

That will get you a result like this:

You'll probably want to do some filtering and make adjustments in the inner `foreach` loop to get the exact output you need.

---
Tags: entities, database, 9
---


# Question
Title: Custom Drush command and Settings::get(), Config::get()
how can I access site configuration when in Drush command?

Global variable `$config === null` and when I use `Config::get('key')`, I get

> Error: Using $this when not in object context in Drupal\Core\Config\Config::get() (line 85 of /var/www/drupalvm/drupal/web/core/lib/Drupal/Core/Config/Config.php)\`

What is the recommended approach? I can't find it anywhere... thank you!

**Edit**

I actually intended to access Settings, not Config - that's where the confusion came from. `Settings::get()` works just fine. To get data from config, just follow the accepted answer.

# Answer
Get is not a static method, so it should not be called statically (using ::). Use Drupal's service container and get the config service using \Drupal::service('config'), then get your config from that.
> 2 votes

# Answer
## Drupal 9

Inject the config factory and the site settings and then you can get all config and settings you need.

```
# modules/custom/MY_MODULE/drush.services.yml
services:
  MY_MODULE.commands:
    class: \Drupal\MY_MODULE\Commands\MyModuleCommands
    arguments: ['@config.factory', '@settings']
    tags:
      - { name: drush.command }

```

```
// modules/custom/MY_MODULE/src/Commands/MyModuleCommands.php
<?php

namespace Drupal\MY_MODULE\Commands;

use Drupal\Core\Site\Settings;
use Drush\Commands\DrushCommands;
use Drupal\Core\Config\ConfigFactoryInterface;

/**
 * Provide Drush commands for MY MODULE.
 */
class MyModuleCommands extends DrushCommands {

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

  /**
   * The site settings.
   *
   * @var \Drupal\Core\Site\Settings
   */
  protected $settings;

  /**
   * MyModuleCommands constructor.
   */
  public function __construct(ConfigFactoryInterface $configFactory, Settings $settings) {
    parent::__construct();
    $this->configFactory = $configFactory;
    $this->settings = $settings;
  }

   /**
   * Get stuf
   *
   * @command my:stuff
   * @aliases muff
   * @usage my:stuff
   *   Get stuff from site.settings
   */
  public function getStuff() {

    $uuid = $this->configFactory->get('system.site')->get('uuid');
    $this->logger()->success($uuid);

    $batch_size = $this->settings->get('entity_update_batch_size');
    $this->logger()->success($batch_size);
  }

}

```
> 1 votes

# Answer
Try `drush cget configName`

Drush config-get Documentation

sample:

```
drush config-get system.site

```
> 0 votes

---
Tags: 8, drush
---


# Question
Title: "[message] => DB Error: no such table" error after installing civicrm
I installed CiviCRM 5.47.3 plugin using composer in the centos 8 server.

my drupal version is 9.3.5

After installing CiviCRM on the server CiviCRM plugin is enabled automatically in admin extension section

But after configuring CiviCRM no page is loading and facing the below error,

```
Initialization Error

Array
(
    [callback] => Array
        (
            [0] => CRM_Core_Error
            [1] => simpleHandler
        )

    [code] => -18
    [message] => DB Error: no such table
    [mode] => 16
    [debug_info] => SELECT id, name, value, domain_id, contact_id, is_domain, component_id, created_date, created_id
FROM civicrm_setting
WHERE (domain_id = 1) AND (is_domain = 1)
 [nativecode=1146 ** Table 'civicrm_cidd.civicrm_setting' doesn't exist]
    [type] => DB_Error
    [user_info] => SELECT id, name, value, domain_id, contact_id, is_domain, component_id, created_date, created_id
FROM civicrm_setting
WHERE (domain_id = 1) AND (is_domain = 1)
 [nativecode=1146 ** Table 'civicrm_cidd.civicrm_setting' doesn't exist]
    [to_string] => [db_error: message="DB Error: no such table" code=-18 mode=callback callback=CRM_Core_Error::simpleHandler prefix="" info="SELECT id, name, value, domain_id, contact_id, is_domain, component_id, created_date, created_id
FROM civicrm_setting
WHERE (domain_id = 1) AND (is_domain = 1)
 [nativecode=1146 ** Table 'civicrm_cidd.civicrm_setting' doesn't exist]"]
)

```

I installed CiviCRM by referring the below doc https://docs.civicrm.org/installation/en/latest/drupal8/

PHP version is v8.0.27

any guide is appreciated if need any more details please comment

# Answer
Verify that your database credentials in your CiviCRM settings file `web/sites/default/civicrm.settings.php` is correct.

If you are using a separate CiviCRM database (i.e., not using the same database as drupal), you need to specify separate SQL credentials.

By default, CiviCRM uses the installed (Drupals) MySQL credentials. Have you verified that you took the following under consideration? https://docs.civicrm.org/installation/en/latest/general/requirements/#mysql-connection

Verify the MySQL user has super privileges. You should NOT be using root in SQL, but instead should have a separate user account.

```
GRANT SUPER ON *.* TO 'civicrm_user'@'localhost';

```

Another thing to check: Make sure your DB password does **NOT** have any special characters in it. I ran into a similar error with special characters in my DB password, which prevents Civi from connecting to MySQL (Even tho Drupal could).
> 0 votes

---
Tags: database, configuration, civicrm
---


# Question
Title: Best way to handle products with hundreds of different properties
I've started building a large Drupal Commerce shop for my customer. The customer has about 30000 products: clothes, electronics, decor, candies, etc. That means that in the shop there might be hundreds of different properties along all products. The products will be imported from manufacturers product feeds.

The properties are, for example:

* Description
* Country of origin
* Materials
* Product care instructions
* Certificates
* Composition
* Paper gramage

It's quite a big job to add a new custom field for every property. Is there any better way to handle those properties?

A requirement is that most of the properties should also be filters in product category pages.

# Answer
It's already been said in a comment, but yes, if this information is going to be synchronized from a third party and not managed on site, you're free to using something a little less structured like taxonomy terms to import the data. Since one taxonomy term reference can reference terms from a variety of vocabularies, you can still use different vocabularies for the various groups of product attributes but then just add a single, unlimited value reference field on the product type called "Properties" that you use to import all of the properties.

You might just want to check to ensure you can build the search interface your client requires - e.g. that you can show facet blocks by vocabulary. I think you can, just make sure you test it out first before going whole hog.
> 1 votes

---
Tags: commerce
---


title: computed field inside field collection

text:
# Question
Title: computed field inside field collection
I have two fields (*field\_a* and *field\_b*) which are inside the *field\_available\_water\_s* field collection. I want to set the *field\_c* computed field, which is also inside the field collection, to the sum of those fields.

I tried using this code, but it doesn't work.

```
$entity_field[0]['value'] = $entity->field_a[LANGUAGE_NONE][0]['value'] + $entity->field_b[LANGUAGE_NONE][0]['value'];

```

What code should I use?

# Answer
> 0 votes
In the *Computed Code (PHP)* textarea shown in the field settings of the computed field, you need to enter the following code.

```
$field_a = field_get_items($entity_type, $entity, "field_a");
$field_b = field_get_items($entity_type, $entity, "field_b");
$entity_field[0]["value"] = $field_a[0]["value"] + $field_b[0]["value"];

```

That is the code the description for the *Computed Code (PHP)* textarea uses as example. (Emphasis is mine.)

> The variables available to your code include: `&$entity_field`, `$entity_type`, `$entity`, `$field`, `$instance`, `$langcode`, and `$items`. To set the value of the field, set `$entity_field[0]['value']`. For multi-value computed fields continue with `$entity_field[1]['value']`. Here's **a simple example which sets the computed field's value to the value of the sum of the number fields (field\_a and field\_b)** in a node entity:
>
> ```
> $field_a = field_get_items($entity_type, $entity, "field_a");
> $field_b = field_get_items($entity_type, $entity, "field_b");
> $entity_field[0]["value"] = $field_a[0]["value"] + $field_b[0]["value"];
>
> ```

>

I added the fields described in the question to a node. With this code, I get the following output.

>

---
Tags: 7
---


title: Pass a PHP array to Twig?

text:
# Question
Title: Pass a PHP array to Twig?
I want to be able to access an array in my Twig template that is built in PHP but it always appears blank, am I missing something?

mytheme.theme

```
function mytheme_preprocess_page(array &$variables) {
  $variables['myvar'] = 'foo';
}

```

page.html.twig

```
{{ myvar }}

```

works as expected but if I pass an array this doesn't work:

```
$variables['myvar'] = ['foo', 'bar'];

```

just outputs nothing

# Answer
To print all the items contained in that array without assuming how many items it contains, you can use the following code.

```
{% for key, var in myvar %}
  {{ myvar[key] }}
{% endfor %}

```

Otherwise, you can simply use the following code.

```
{{ myvar[0] }} {{ myvar[1] }}

```
> 1 votes

# Answer
There are two possible answers depending on what you are trying to achieve. You can loop over arrays in Twig like in any programming language, see the other answer.

Specific to Drupal's template engine is that if you want Twig to render an array recursively it has to be a renderable array. This means that each element which has no children has to be a render element (#markup, #plain\_text, #type or #theme):

```
$variables['myvar'] = [
  0 => ['#plain_text' => 'foo'],
  1 => ['#plain_text' => 'bar'],
];

```

Then

```
{{ myvar }}

```

renders the entire array.

See https://www.drupal.org/docs/drupal-apis/render-api/render-arrays.
> 1 votes

---
Tags: theming
---


title: Best way of entering an internal link

text:
# Question
Title: Best way of entering an internal link
How can I enter an internal link that works for different path lengths and keeps working when I upload my site to the live server?

Say I have a page here `http://localhost/site1/web/page1` and I want to create a block in the footer that has a link to this page.

If I use a relative link (e.g. `page1`) It will work when I'm on this page ...
`http://localhost/site1/web/`
But it won't if I'm on a multi segment path, e.g ...
`http://localhost/site1/web/about/terms/`

If I create a server-relative link (e.g. `/site1/web/page1`)
It will not work when the server changes ...
`http://mysite.com/`
(I develop the site locally on my PC and when the site is finished I upload it to my hosting site).
Also, it will not work if I wish to clone the site...
`http://localhost/site2/web/`

Absolute paths (e.g.`http://localhost/site1/web/page1`)
will also not work when I start running the site on the live server.

I tried the module linkit. It works for the above cases but only for some types of links (e.g. I couldn't get it to work for links to views).

Module pathologic fixed this issue in Drupal 7 but is not yet ready for Drupal 9.

I got the best results using the php module (it supplies a filter that you can add to a text-format). But I believe using this module is frowned upon.

```
<?php
use Drupal\Core\Link;
use Drupal\Core\Url;
print(Url::fromUserInput('/node/1')->toString());
?>

```

Am I missing a better alternative? (It would be handy if there was a way to make links that are relative to the drupal 'web' directory.)

# Answer
In Drupal 8+, migrating databases is not recommended, as it will overwrite any changes made on your production server in between overwrites. Instead, a configuration API has been created, allowing for site configuration - aka the settings that define what a site is, what it does, and how it works - to be migrated by between instances without having to overwrite the database. There is also a Migration API, but in my experience it's usually easier to re-create content on production after first playing with it in staging/local than it is to set up the Migration API.

Here is a tutorial I wrote on using the Configuration API: https://www.morpht.com/blog/drupal-8-configuration-part-1-configuration-api

If I were in your position, I would stop pushing databases up, and instead switch to using the Configuration API to manage your site in multiple environments. Then you can create your block on production, and pull the database down to your local installation to get that block instance on your local installation.

If you don't want to switch to migrating configuration, and would prefer to keep pushing your database up, then the two solutions I see are:

1. Use the PHP filter you mentioned in your post.
2. Create a custom block through code, rather than through the admin UI, and build your link with the URL class as you showed in your most.

While the first option is not recommended, neither is the whole process of pushing the database up, so if you are going with pushing the database up then you many as well use the PHP input filter as well.
> 1 votes

---
Tags: 9
---


# Question
Title: Is there a way to find internal links to a specific web page?
For example, for a page called `mysite.com/about` can you determine any other pages that link directly to this web page? The goal is to determine all of the webpages that exist in `mysite.com` that have a link to the `about` page.

# Answer
> 0 votes
You may want to look at the Views module (pretty sure you already have that module enabled ...), which includes a **Backlinks** view. After you activate this view, a `What links here`-tab is shown on each node.

So on your *about* page, it'll list all of the pages that have a link to the about page (as in your question).

# Answer
> 0 votes
The comment on the OP seems really dismissive.

Currently there is no way in Drupal to do this. In Drupal 7 there **was** a way to do this. To understand why this is no no longer possible you can digest this here: https://www.drupal.org/project/drupal/issues/1853536

(TLDR;) The capability relied on Core Search Code that was removed and upgraded (original was in-efficient and cause overhead problems).

We do need this capability within Drupal, as Admins, however.

Example: I have \>5k web pages in a Drupal site I've inherited. I need to delete or un-publish a node. **I need to convert all internal** (I said **internal** *not from external*... and so did the OP above...) all internal links that to point to this node, so I must first discern which internal nodes link to this node, else every one of them will break (error 403 page not found).

Example: An employee at the company has a Bio (node) on the website. Many nodes link to this Bio node (possible that many do). I've been asked to remove this Bio node from the website. The correct thing to do is re-write the text and link on all nodes linking to this node I am about to remove/unpublish.

Why is this core need so difficult to understand?

What I have done to get around this mis-understanding... I use google. (example. If I am asked to alter every page, for example, leymannx from the website) Google Search: site:drupal.stackexchange.com/questions "leymannx" then methodically open each one and edit.

What we need is a way to do this from within our Drupal Admin, however. Closest I've got is: /admin/content/files where file usage count is provided. I looked at my content type for nodes that are web pages on my site (on my site we call this content-type "basic-pages") /admin/content/basic-pages

Looking at the Drupal "View" the presents each one in admin, it seems that "Files" have access to a field type of "Aggregation", which Content Type of "nodes" do not have....

Aggregation fields have an attribute of "Aggregation Settings" which provide the functionality of "SUM(File Usage: Use Count) (Used in)" that I cannot yet work out how to provide to the Content type "nodes".

Close as I've been able to work out so far. I'll contribute more if I make progress, but it is overwhelming that this is not a common need at this point.

Hope this helps advance progress towards a solution to your question.

---
Tags: 7, nodes
---


# Question
Title: Drush site aliases not working as expected
Using drush 11 and Drupal 9 to test a local site on my Mac (using MAMP), I'm having difficulty getting site aliases to work. I used Drush Launcher to make my drush executable globally. I also created \<DRUPAL\_ROOT\>/drush/sites/self.site.yml containing:

```
local:
  host: www.en9-local.pglatz.com
  user: pglatz
  root: /Users/pglatz/dev/electronovelty/en9-www/web
  uri: https://en9-local.pglatz.com

```

If I cd to my project root and type drush wd-show I see the status command correctly. But if I try using my alias with it, i.e. `drush @local wd-show` I get this error:

```
ssh: connect to host www.en9-local.pglatz.com port 22: Connection refused

In SiteProcess.php line 214:

  The command "ssh -t -o PasswordAuthentication=no pglatz@www.en9-local.pglatz.com 'drush wd-show --uri=https://www.en9-local.pglatz.com --root=/Users/pglatz/dev/electron
  ovelty/en9-www/web'" failed.

  Exit Code: 255(Unknown error)

  Working directory:

```

This is a local host, so why is it using ssh? My goal is to have both 'local' and 'prod' aliases so can can run Drush on my production server.

# Answer
If you look at the Site aliases docs you'll see this:

> host: The fully-qualified domain name of the remote system hosting the Drupal instance. **The host option must be omitted for local sites, as this option controls various operations, such as whether or not rsync parameters are for local or remote machines, and so on.**

Emphasis mine. You can also remove `user` as it's only relevant to SSH connections.

Make sure you also read the section titled "Advanced usage", as it contains more information about using local site aliases specfically.

Also bear in mind Leymannx's comment, though - if this is a single site you're managing from that same site's docroot, just use the built in `@self`, no need to create a new alias.
> 1 votes

---
Tags: drush
---


# Question
Title: Does a database transaction rollback if a PHP/Gateway timeout / connection dropped/lost occurs?
I am using Drupal behind a reverse proxy/cache layer (e.g. Cloud Front/ Akamai) and sometimes the service goes quite slow (so I get a Gateway timeout, for reasons such as too many people are using the servers) or a something bad happens in the server farm (docker micro-architecture) and so I get a 502 Bad Gateway.

Do we know if a database transaction will roll back in such cases? This is especially relevant when doing 800+ entity updates via the Batch API.

E.g. (based off mock code: https://www.drupal.org/docs/drupal-apis/database-api/database-transactions)

```

$transaction = $connection->startTransaction();

try {
  // Do some thing that writes to the database.
  $entity = create_some_entity();
  $entity->save();

  // Pretend a 502 Bad Gateway or Gateway timeout happened here.

  // Do another database write that depends upon the first.
  $dependent_entity = update_dependent_entity($entity->id());
  $dependent_entity->save();
}
catch (\Exception $e) {
  // There was an error in writing to the database, so the database is rolled back
  // to the state when the transaction was started.
  // Not sure if catching an exception will do anything here.
  // (since no exception is expected)
  $transaction->rollBack();
}

// Commit the transaction by unsetting the $transaction variable.
unset($transaction);

```

# Answer
> 2 votes
A transaction is only rolled back when `Transaction::rollBack()` is explicitly called. In case of time outs, that doesn't happen.
Actually, in that case, the database changes aren't even committed, since that only happens when `Transaction::__destruct()` is called.

```
public function __destruct() {
  // If we rolled back then the transaction would have already been popped.
  if (!$this->rolledBack) {
    $this->connection->popTransaction($this->name);
  }
}

```

---
Tags: database
---


# Question
Title: Use CKEditor for textareas in custom form
I'm using the CKEditor module, and I would like to have the CKEditor in my custom textarea. I tried hard but it just doesn't work...

This some of the code I tried:

```
    $form['body'] = array (
        '#type' => 'textarea',
        '#title' => 'Item Snippet',
        '#default_value' => $body,
        '#cols' => 80,
        '#rows' => 7
    );
    $form['']['format'] = filter_form();

```

# Answer
Use the text\_format element type and CKEditor will catch your field:

```
  $form['body'] = array(
    '#type' => 'text_format',
    '#title' => t('Body'),
    '#format'=> 'full_html',
  );

```

If you have issues with showing CKEditor in popup use additional `#pre_render`:

```
  $form['body'] = array(
    '#type' => 'text_format',
    '#title' => t('Body'),
    '#format'=> 'full_html',
    '#pre_render' => array('ckeditor_pre_render_text_format'),
  );

```
> 14 votes

# Answer
u need to use "text\_format" instead of "textarea"

text\_format

> Description: A text-format-enabled version of a textarea.
> 5 votes

# Answer
In Drupal 8.6.x my solution was:

```
$form['Description'] = [
  '#type' => 'text_format',
  '#title' => $this->t('Description'),
  '#format' => 'rich_text',
];

```
> 3 votes

# Answer
just in case somebody only wants a **basic\_html** format one and does not want show format selection (`'#allowed_formats' => ['basic_html']` is the point),

```
$form['description'] = [
  '#type' => 'text_format',
  '#title' => $this->t('message'),
  '#format' => "basic_html",
  '#allowed_formats' => ['basic_html'],
  '#required' => TRUE,
];

```
> 2 votes

# Answer
You can try this

```
 $form['body'] = array(
   '#type' => 'textarea',
   '#title' => 'Item Snippet',
   '#default_value' => $body,
   '#cols' => 80,
   '#rows' => 7,
   '#wysiwyg' => TRUE, // just add this and you'll get the CKeditor
 );
 // …

```

I was facing the same problem; when I added `'#wysiwyg' => TRUE`, it worked.
> 0 votes

# Answer
Some of the methods discussed above do get the CKEditor in the textarea, but if it is a configuration form, the values don't get saved properly. If you are stuck with same problem you can use a second CKEditor from DNS as explained here: http://naikacademy.blogspot.com/2018/09/how-to-add-text-editor-to-custom-form.html
> 0 votes

# Answer
There is a setting page for CKEdiors. You can configure there. You need to give the path to that form and select include from dropdown and it will apply Rich Text editors to all text areas in that path.
> -1 votes

---
Tags: wysiwyg
---


# Question
Title: How to add view filter programmatically using hook_views_pre_view()
I want to add a simple filter to an existing view in Drupal 9. It appears that I can achieve this with `hook_views_pre_view()`. In every example I can find, including all the "Similar questions" links that come up when entering the title of this post, I see that `$view->add_item()` is used. See: Add views exposed filter programmatically

However, when I try to run this code:

```
function sbn_views_pre_view($view, $display_id, array $args) {
  if ($view->id() == 'draft_moderation_state') {
    // Get array of draft nids
    $state = 'draft';
    $results = sbn_get_all_nodes_in_moderation_state($state);

     // There is no draft_moderation_state filter so we have to add it
      $view->add_item(
        $view->current_display,
        'filter',
        'node',
        'nid',
        array(
          'operator' => '=',
          'value' => '59',
          'group' => 1
        )
      );
  }
}

```

I get this error:

> Call to undefined method Drupal\views\ViewExecutable::add\_item()

What is the command to add filter in `hook_views_pre_view()` in Drupal 9?

# Answer
> 0 votes
I would still like to know how to add a filter using hook\_views\_pre\_view(), but I solved my bigger problem (programmatically filtering an existing view) using

> hook\_views\_query\_alter()

```
function sbn_views_query_alter($view, $query) {
   if ($view->id() == 'draft_moderation_state') {
      $field = 'node_field_data.nid';
      // Get array of draft nids
      $state = 'draft';
      $value = sbn_get_all_nodes_in_moderation_state($state);
      $operator = 'IN';
      $query->where[1]['conditions'][] = [
        "field"    => $field,
        "value"    => $value,
        "operator" => $operator,
      ];
  }
}

```

# Answer
> 0 votes
`$view->setHandler()` worked for me. I think this should be equivalent to the code in your question:

```
$nid_filter = [
  'id' => 'nid',
  'table' => 'node_field_data',
  'field' => 'nid',
  'value' => ['value' => 59],
  'operator' => '=',
];
$view->setHandler($view->current_display, 'filter', 'nid', $nid_filter);

```

# Answer
> -1 votes
$view-\>add\_item(...) is Drupal 7

It's Drupal 7 vs Drupal 9 issue:

it would be helpful if users of this forum would put a version of Drupal when they post questions.

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


# Question
Title: How to create a module with default settings
I have a module I'm working on that links to a sound file and plays it when something happens. I have it so that there is a button in the admin config to set the path of the file to a default path which is a file that comes with the module. Furthermore, one may upload their own file and it will switch to playing that file when the thing happens. The problem is, the module on a fresh install does nothing, when I want it to have that default sound set up to play immediately after install. I tried doing that with a settings.yml file however trying to access that data returns null until I go into the admin config and either set it to default or upload my own file.

Here is a look at the files:

MYMODULE.settings.yml

```
pathToSound: '/assets/sound.mp3'

```

MYMODULE.schema.yml

```
MYMODULE.settings:
  type: config_object
  label: 'MYMODULE Settings'
  mapping:
    pathToSound:
      type: string
      label: 'Path to Sound'

```

MYMODULE.module

```
<?php

use Drupal\Core\Form\FormStateInterface;

function MYMODULE_page_attachments(array &$attachments) {
  $attachments['#attached']['library'][] = 'MYMODULE/MYMODULE';
  $attachments['#attached']['drupalSettings']['MYMODULE'] = [
  'pathToSound' => \Drupal::config('MYMODULE.settings')->get('pathToSound'),
  ];
}

```

MYMODULE.js

```
/**
 * @file
 */
(function ($, Drupal) {
  'use strict';

  Drupal.behaviors.MYMODULE = {
    /**
     * Drupal attach behavior.
     */

    attach: function (context, settings) {
      // Triggers when the cache is cleared to play a sound.
      $('body', context).once('MYMODULE').each(function () {
        console.log(settings.MYMODULE.pathToSound);
        var sound = document.createElement('audio');
        sound.setAttribute('src', settings.MYMODULE.pathToSound);
        sound.play();
      });
    },
  };
  })(jQuery, Drupal, drupalSettings, once);

```

I tried to exclude as much irrelevant code as possible. Anyway, there's also a form that edits the MYMODULE.settings "pathToSound" when the module is configured in the admin menu. Finally, there is the JS file which plays the sound. Like I said, it works after it is configured in the admin menu, but not before. I'm wondering before it is configured, the pathToSound returns null even though it is set in MYMODULE.settings. Any ideas would be greatly appreciated, thanks!

EDIT: Adjusted code according to suggestions, still not working. Also added the JS that is running the console log which is returning null.

# Answer
The configuration schema must match the configuration file. For example, the following ones match.

### myModule.settings.yml

```
pathToSound: '/assets/sound.mp3'

```

### myModule.schema.yml

```
flush.settings:
  type: config_object
  label: 'Flush Settings'
  mapping:
    pathToSound:
      type: string
      label: 'Path to Sound'

```

In this case, `\Drupal::config('myModule.settings')->get('pathToSound')` would return the *pathToSound* value.

For a configuration file containing the following lines, that schema won't work.

```
path:
  pathToSound: '/assets/sound.mp3'

```

The correct schema would be the following one.

```
flush.settings:
  type: config_object
  label: 'Flush settings'
  mapping:
    path:
      type: mapping
      label: 'Path settings'
      mapping:
        pathToSound:
          type: string
          label: 'Path to sound'

```

In this case, `\Drupal::config('myModule.settings')->get('path.pathToSound')` would return the *pathToSound* value.

As side note, changing the configuration schema or the configuration file after the module is installed doesn't help. Drupal has already loaded the default values from the configuration file and it would still use the previous configuration schema.
To let Drupal notice both the files have been changed, the module must first be uninstalled, the files changed, and the module re-installed.
> 2 votes

---
Tags: 9
---


# Question
Title: How do i force a text format and hide the format selector in a formatted text field?
I Want to force a certain text format in a user profile text format field and hide the text format selector dropdown. How do i do it? This is for Drupal 8.

# Answer
> 11 votes
Install the module Allowed Formats and configure the field to allow only one text format. Then the text format selector dropdown will no longer be displayed.

# Answer
> 10 votes
Adapted from the Allowed Formats code:

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

/**
 * Implements hook_field_widget_form_alter().
 */
function mymodule_field_widget_form_alter(&$element, FormStateInterface $form_state, $context) {
  // Maps field names to an array containing a single format.
  $map = [
    'field_myfield' => ['myformat'],
  ];

  $field_name = $context['items']->getFieldDefinition()->getName();

  if (array_key_exists($field_name, $map)) {
    $element['#allowed_formats'] = $map[$field_name];
    $element['#after_build'][] = '_remove_text_format_box';
  }
}

/**
 * #after_build callback.
 */
function _remove_text_format_box($form_element, FormStateInterface $form_state) {
  // Remove help, guidelines and wrapper.
  unset($form_element['format']['help']);
  unset($form_element['format']['guidelines']);
  unset($form_element['format']['#type']);
  unset($form_element['format']['#theme_wrappers']);

  return $form_element;
}

```

# Answer
> 1 votes
If you use the #allowed\_formats value and limit it to just 1 value; then the selector will disappear. This is code to add your own custom field; but I assume you can do a form alter to set these for an existing (UI placed) field.

```
$form['message_text'] = [
  '#type' => 'text_format',
  '#format' => 'simple',
  '#allowed_formats' => ['simple'],
  '#title' => $this->t('Message Text:'),
  '#description' => $this->t('Some help text here.'),
];

```

---
Tags: 8, entities, input-formats
---


# Question
Title: Set maximum / disable custom block cache for anonymous users
I have a little issue regarding my custom block module. The module itself fetches weather data via json-request and the data is shown as a 4-day weather forecast to the user. As the weather should be actualized every day I want to set a "maximum cache time" or even disable caching for the whole block. Right now I would have to flush the cash manually every day.

After a little bit of research to that topic I found out that using `'max-age'` in the build() function would do the trick but it does not affect the caching for anonymous users at all.

Using `\Drupal::service('page_cache_kill_switch')->trigger();` would disable the cache for a specific page or content-type but I don't want to disable the caching for a whole page at once.

So how can I trigger the caching for my custom-block? Or is there even a way to disable the caching for a certain part of the code e.g. the json-request in my case?

# Answer
This is a known issue Bubbling of elements' max-age to the page's headers and the page cache.

The Internal Page Cache is designed for database content, which is handled by cache tags. So a workaround would be to add a custom cache tag to the block

```
$build['#cache']['tags'][] = 'weather_forecast';

```

which will bubble up to the page level and so you can invalidate both cached items, block and page, in a cron job:

```
/**
 * Implements hook_cron().
 */
function mymodule_cron() {
  \Drupal\Core\Cache\Cache::invalidateTags(['weather_forecast']);
}

```

All credit goes to @Berdir: Cache max-age and varnish ESI
> 2 votes

# Answer
**Limitations of max-age**

Unfortunately, `max-age` does not work for anonymous users and the Drupal core Page Cache module. For example, see these issues:

Until these (and perhaps other) issues are resolved, beware that setting `max-age` on a render array included in a page is insufficient to ensure that anonymous users will see a new version after the max-age as elapsed. In the meanwhile, the Cache Control Override contributed module tries to mitigate the problems. You might also have more luck setting a custom cache tag on pages with time-dependent content and invalidating those cache tags manually via `hook_cron()`. Good luck!

Source: https://www.drupal.org/docs/drupal-apis/cache-api/cache-max-age#s-limitations-of-max-age
> 0 votes

---
Tags: 8, blocks, caching
---


# Question
Title: how to set Default translated texts in twig file
I developed a twig plugin (to display graphs from a webform). These plugins contain English texts; And I want the same file to keep translations for several languages by default (for example, Arabic), that is, the end user does not have to translate these texts through the Drupal interface. Currently using:

```
{%set Hotness_text ={en:'Cold',ar:'ARABIC_TEXT',fa:'PERSIAN_TEXT'}%}
{% if language == 'en' %}

Hotness Axis is: <strong>{{Hotness_text.en}}</strong>
{% elseif language == 'ar' %}
...
{% elseif language == 'fa' %}
...
{% endif %}

```

But I want a better way, according to which the translation **available in the twig file** by Default; and the translation text transferred to the Drupal translation system automatically; So that the user can add any other translation later.

# Answer
> 3 votes
Do not put translations in the twig file. Use the `|t` and `{% trans %}` tags (good overview of how to handle translation in twig here).

For Drupal modules, the translation should be provided as a PO file that is bundled with the module.

See the documentation on interface translation properties.

When you set this up correctly, the user will automatically get the correct strings installed for the translated languages when they install the module.

---
Tags: i18n-l10n, transliteration
---


title: Update menu links based on the site URL

text:
# Question
Title: Update menu links based on the site URL
I have URLs defined in links.menu.yml file as "route\_parameters: {webform: 'abc'}", but I need to update webform value based on the page URL. If URL has "/type1/" in it, keep webform: 'abc' OR If URL has "/type2/" in it, change the webform to webform: 'xyz'

please suggest.

# Answer
> 0 votes
so need to have a hook\_form\_alter
then if this is the webform id you need see https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Url.php/function/Url%3A%3AgetRouteParameters/9.2.x

```
/**
 * Implements hook_form_alter().
 */
function YOURMODUE_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  if ($form_id === 'XXX') {
    $prams = Drupal\Core\Url::getRouteParameters();
    if (!empty($prams)) {
      dump($prams);
      // set the value of the field here/
    }
  }
}

```

---
Tags: 8, navigation
---


# Question
Title: How do I instantiate a custom class with dependency injection?
I am relatively fresh to the DI party and am struggling to get my head around how exactly to use Dependency Injection. I understand that I can pass requirements as part of a service, but what about in my own class?

Say I have the below class where I want the language manager service to be injected:

```
class Foo implements ContainerInjectionInterface {

  protected $languageManager;

  public function __construct(LanguageManagerInterface $languageManager) {
    $this->languageManager = $languageManager;
  }

  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('language_manager')
    );
  }
}

```

When I call `new Foo()` I am required to pass an instance of LanguageManagerInterface. That makes sense in terms of a normal constructor method, but that would require me to instantiate the class like the below:

```
$languageManager = \Drupal::service('language_manager');
$foo = new Foo($languageManager)

```

That just doesn't feel right to me.

Is there something I am missing, or would this class always have to be a service to take advantage of dependency injection?

# Answer
A class implementing `ContainerInjectionInterface` is instantiated by calling the factory method create().

See *Drupal\Core\DependencyInjection\ClassResolver*

```
/**
 * Implements the class resolver interface supporting class names and services.
 */
class ClassResolver implements ClassResolverInterface, ContainerAwareInterface {
  use DependencySerializationTrait;
  use ContainerAwareTrait;

  /**
   * {@inheritdoc}
   */
  public function getInstanceFromDefinition($definition) {
    if ($this->container->has($definition)) {
      $instance = $this->container->get($definition);
    }
    else {
      if (!class_exists($definition)) {
        throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $definition));
      }

      if (is_subclass_of($definition, 'Drupal\Core\DependencyInjection\ContainerInjectionInterface')) {
        $instance = $definition::create($this->container);
      }
      else {
        $instance = new $definition();
      }
    }

    if ($instance instanceof ContainerAwareInterface) {
      $instance->setContainer($this->container);
    }

    return $instance;
  }

}

```
> 3 votes

# Answer
In my humble opinion, the factory design pattern is the most appropriate way to instantiate an object (of a custom class with dependency injection).

From D.O.'s documentation:

> Instantiation
>
> Creating classes directly is discouraged. Instead, use a factory function that creates the appropriate object and returns it. This provides two benefits:
>
> * It provides a layer of indirection, as the function may be written to return a different object (with the same interface) in different circumstances as appropriate.
> * PHP does not allow class constructors to be chained, but does allow the return value from a function or method to be chained.

A code example from a D.O. core issue (\[PP-1\] Add a factory method to create FileStorage instances) shows how one would go about doing that in Drupal. So, for your example that would be something like this:

> Service Definition
>
> ```
> foo_factory:
>   class: Drupal\your_module\FooFactory
>   arguments: ['@language_manager']
>
> ```
>
> and the related instantiation code:
>
> ```
> $fooFactory = \Drupal::service('foo_factory');
> $foo = $fooFactory->create();
>
> ```
> 1 votes

# Answer
The definition of the class in question is correct. All you need is correctly call the object method. To do that - drupal has special mechanism - class resolver. Here is how to initialize your object correctly:

```
use Foo;

\Drupal::classResolver(Foo::class)->yourClassMethod($some_arguments_from_hook_or_preprocess);

```

Read more about class resolver
> 0 votes

---
Tags: dependency-injection
---