# Question
Title: webform: Pass field value of dependant field in autocomplete lookup
Using Drupal 10.1.8 and webform 6.2.2

Done a bit of searching, and the only questions I find similar, are quite old. This one seems the best match: AJAX autocomplete based on other field value

Now, my question:

I have two fields:

**Client Company** : Entity Lookup / autocomplete on taxonomy term

**Client project Manager** : Entity Lookup to user via a view entity ref.

So, as noted, I want the Client Project Manager field to pass the value (entity id) that was placed in the 'Client Company' field, so as to allow the view to filter.

I am good with all the hooks etc to apply the filter, what I cannot figure out is how to pass that value. I can do as per the noted linked question, and implement some custom js to deal with the fields, but, is that still really the only way?

I, for example, tried using a token in the dependant field's 'view arguments', which, seems the way to go logically, but, the token is just passed, as is, as a string, not the value.

Am i just maybe formatting the token wrong?

I am fairly new to Drupal.

UPDATE: I did just notice the double :: in the token, and fixed that, but the end result is still the same (got excited there for a moment!)

# Answer
> 0 votes
As per discussions on Slack, this is not an out of the box feature, and requires custom code.

I did not need to make it complicated, as I only need this in one place, on one form (for now), so I solved it with some javascript and manipulating the autocomplete path of the 2nd element.

This way I keep all the standard webform elements (so no custom elements needed) and use the drupal native autocomplete functionality.

First, I created a new javascript file with my custom code. Not going to explain what the code does, it is pretty straight forward, but, ask if something needs clarification.

```
(function ($, Drupal, once) {
  Drupal.behaviors.autocompleteHook = {
    attach: function (context) {
      if (!once('companyHook', 'html').length) {
        return;
      }
      $('body').on('autocompleteclose', '#edit-client-company', function (e) {
        setTargetPath(e.target.value);
      });
      // if loaded for an edit.
      setTargetPath($('#edit-client_company').val());
    },
  };

  function setTargetPath(elementValue) {
    $('.js-form-item-client-project-manager').hide();
    if(elementValue) {
      var regex = /\((\d+)\)/;
      var match = elementValue.match(regex);
      if (match && match[1]) {
        $('.js-form-item-client-project-manager').show();
        let url = updateUrlParameter($('#edit-client-project-manager').attr('data-autocomplete-path'), 'target_id', match[1]);
        $('#edit-client-project-manager').attr('data-autocomplete-path', url);
      }
    }
  }

  function updateUrlParameter(url, param, paramVal) {
    var newAdditionalURL = '';
    var tempArray = url.split('?');
    var baseURL = tempArray[0];
    var additionalURL = tempArray[1];
    var temp = '';

    if (additionalURL) {
      var tmpAnchor = additionalURL.split('&');
      var paramExists = false;

      for (var i = 0; i < tmpAnchor.length; i++) {
        if (tmpAnchor[i].split('=')[0] == param) {
          tmpAnchor[i] = param + '=' + paramVal;
          paramExists = true;
        }
        temp += i < (tmpAnchor.length - 1) ? tmpAnchor[i] + '&' : tmpAnchor[i];
      }

      if (!paramExists) {
        temp += '&' + param + '=' + paramVal;
      }
    } else {
      temp = param + '=' + paramVal;
    }

    newAdditionalURL = baseURL + '?' + temp;
    return newAdditionalURL;
  }
})(jQuery, Drupal, once);

```

I added a custom class 'hidden' to the 2nd element, forgoing with the built in element dependency display rules. I have that in my global theme css already as `display: none`

Then following bog standard drupal ways, I created the needed entry in \[module\].libraries.yml

```
autocompleteCompany:
  js:
    js/ihop-autocomplete-company.js: {}
  dependencies:
    - core/jquery
    - core/once

```

and then in the form's `WebformHandler::alter_form` function (Using webform handlers, as apposed to hooks in .module file), I attached that library

```
public function alterForm(
    array &$form,
    FormStateInterface $form_state,
    WebformSubmissionInterface $webform_submission
  ) {

....

$form['#attached']['library'][] = 'ihop/autocompleteCompany';

....
}

```

so, upon doing a selection of the first field's autocomplete, my js hook fires (listening to `autocompleteclose`) which then:

1. Makes the 2nd element visible / invisible, depending on some factors.
2. Injects or updates the attribute `data-autocomplete-path` of the 2nd element. Thsi then now passes an additional param, target\_id

The last(ref note below) step here is then to get the value of that param in a view hook (located in the .module file), and use it to set a filter.

```
case 'list_client_pm_entity_ref':
      $companyId =  $view->getRequest()->query->get('target_id');
      $filters['field_company_target_id']['value'] = [$companyId => $companyId];
      break;

```

Note: At this point, it works only if you use a different lookup string in the 2nd field. This is due to autocomplete.js caching the results.

So, that is my next step, and there is some details here: How to prevent Autocomplete Caching?

---
Tags: webforms
---


# Question
Title: Prevent multiple registrations from the same email using + character
Gmail and other email services allow users to extend their address using the + (plus) character.

For example, mail to

> spamme@gmail.com
>
> spamme+sideways@gmail.com
>
> spamme+tomorrow@gmail.com

will all be receivable by the owner of `spamme@gmail.com`

In its default configuration, Drupal 7 would allow the owner of `spamme@gmail.com` to register 3 separate accounts using the 3 addresses above... and more if they want!

I would like to prevent this type of multiple registration.

While I could just create a rule that blacklists email addresses containing the `+` character, there are legitimate uses of this.

I would prefer to be able to check for other equivalent email addresses, where in the above example, all 3 addresses are equivalent, because they are all collectable by `spamme@gmail.com`, and allow only one of them.

Is there some way to do that?

# Answer
Create a second field for users that stores only real email address (everything before the +).

then use a user registration preprocess hook to check if that exists already with a simple database query.

https://api.drupal.org/api/drupal/modules!user!user.api.php/function/hook\_user\_presave/7.x
> 1 votes

# Answer
Check email before register user with add your custom validator in hook\_form\_alter, then check user email without `+secondpart` :

As a Example you can Try something like this (**I didn't test this but it can give you a good clue to achieve what you want** )

```
 function YOURMODULENAME_form_alter(&$form, &$form_state, $form_id){
       if ($form_id == "user_register_form") {
            // add your custom validator
            $form['#validate'][] = 'my_custom_email_validate';
       }
     }

```

in your custom validator check it with regular expression

```
function my_custom_email_validate($form, $form_state, $form_id){
    // dpm($form_state['values']);
    $mail = $form_state['values']['mail'];
    preg_match_all('/(.*)\+.*(@.*)/',$mail,$matches);
if($matches){
    // this mean it has  xxx+xx@xxx pattern
    $clean_mail = $matches[1][0].$matches[2][0];
    $user_load = user_load_by_mail($clean_mail);
    if($user_load){
        form_set_error('mail',t('Sorry,This email used before.don\'t waste your time dear spammer'));
        return false;
 }
}

```
> 1 votes

# Answer
Or for modern Drupal versions, see this module to prevent duplicate registration:

https://www.drupal.org/project/unwanted\_email\_registration
> 1 votes

# Answer
I actually found a module that solves this problem already: Duplemail.

This doesn't actually answer my problem just yet, because I found it's incompatible with LoginToboggan, which seems to bypass the validate method that Duplemail hooks onto. Since LoginToboggan is already deployed...

So... I'm going to try Email Registration and Duplemail to see if this solves the issue
> 0 votes

---
Tags: 7, users, emails
---


# Question
Title: How do I make an activation link on registration expire in 15 minutes?
I have user registration and forgot password mails sent to the user.

I want 5 minute expiration for forgot password and user registration. Now the forgot password will expire after 5 minutes but new user registration link is not expiring after 5 minutes.

I have set the following changes in settings.php.

But it is not working for first time activation link.

`$conf['user_password_reset_timeout'] = '259200';`

How do I make an activation link on registration expire in 15 minutes?

# Answer
Drupal Core doesn’t yet have this feature. Contribute to the issue. Test and use patches there to help deliver the feature.
> 1 votes

# Answer
The password reset timeout can be set by adding to **settings.php**

`$config['user.settings']['password_reset_timeout'] = 900;//15*60`
> 1 votes

---
Tags: users, 9
---


# Question
Title: Drush command to check if Drupal is installed?
Is there any way to use Drush to determine if Drupal is installed, and have it return true or false? The boolean output would be used elsewhere in a bash script's logic.

Bonus: it needs to work for both Drupal 7 and Drupal 8.

# Answer
```
$ cd /path/to/drupal6or7or8
$ drush status bootstrap | grep -q Successful

```

Then, `$?` will be 0 if Drupal has been installed, or 1 if there is no site, or it does not have a database, or the site has not been installed.
> 8 votes

# Answer
In addition to @greg\_1\_anderson answer, I'd like to add that that is sensitive to locale problems. `Successful` will be translated in the output from Drush. To work around this problem, I'd suggest this somewhat dirty workaround:

```
drush status bootstrap | grep -q $(drush php-eval 'if (function_exists("t")) echo t("Successful");') &> /dev/null;

```

As mentioned above, `$?` will be `0` if installed, higher if not.
> 5 votes

# Answer
For anyone looking for a like for like replacement for `drush status bootstrap` in Drush 12:

```
drush status --field=bootstrap

```
> 3 votes

# Answer
As of Drush 12 `drush status bootstrap` does not work anymore.

You can check if there is something in the config table instead:

```
drush sql-query 'SELECT 1 FROM config LIMIT 0,1' > /dev/null 2>&1

```

Return value 0 means Drupal is installed.
> 0 votes

---
Tags: drush
---


# Question
Title: How to query the parent entity of a paragraph
I would like to use the entityQuery for querying paragraphs

* of type `my_paragraph_type`
* With the field `field_boolean_test` set to TRUE
* And a condition on the parent of the paragraph: `field_parent` must be set

Here is my try, but it is not working

```
Drupal::entityQuery('paragraph')
  ->condition('type', 'my_paragraph_type')
  ->exists('parent_id.entity:node.field_parent') //HERE IS THE PROBLEM
  ->condition('field_boolean_test', TRUE)
  ->execute();

```

The parent is a node and the paragraphs are attached to a field named `field_paragraphs`

# Answer
With the entity query you can get all the node ids with your desired conditions, because the entity reference fields are deposited there. For the other direction you need to do a custom database query like the following:

```
$query = \Drupal::database()->select('paragraphs_item', 'pi');
$query->fields('pi', ['id']);
// Join the field which references the paragraph item to the node.
$query->join('node__field_parent', 'fpc', 'fpc.field_parent_target_id = pi.id');
// Join the field_boolean_test field from the paragraph.
$query->join('paragraph__field_boolean_test', 'fbt', 'fbt.entity_id = pi.id');
// Join the node field data table for conditions on the status for example.
$query->join('node_field_data', 'nfd', 'nfd.nid = fpc.entity_id');
// Join the field_parent field from the node.
$query->join('node_field_parent', 'nfp', 'nfp.nid = fpc.entity_id');
// Add the required conditions.
$query->condition('pi.type', 'my_paragraph_type');
$query->condition('fbt.field_boolean_test_value', TRUE);
$query->condition('nfp.field_parent_value', TRUE); // You can also leave this condition, because the join requires a database entry.
// Get the paragraph ids.
$paragraph_ids = $query->execute()->fetchCol();

```
> 2 votes

---
Tags: paragraphs
---


title: Update database issue

text:
# Question
Title: Update database issue
In 2 of my 3 Drupal websites, `drush updb` crashes with the following error after upgrading from Drupal 9.5.11 to Drupal 10.2.2.

```
In BatchStorage.php line 143:
  [Exception]
  Serialization of 'Closure' is not allowed
Exception trace:
  at /var/www/html/drupal/web/core/lib/Drupal/Core/Batch/BatchStorage.php:143
 serialize() at /var/www/html/drupal/web/core/lib/Drupal/Core/Batch/BatchStorage.php:143
 Drupal\Core\Batch\BatchStorage->create() at /var/www/html/drupal/web/core/lib/Drupal/Core/ProxyClass/Batch/BatchStorage.php:107
 Drupal\Core\ProxyClass\Batch\BatchStorage->create() at /var/www/html/drupal/web/vendor/drush/drush/includes/batch.inc:146
 _drush_backend_batch_process() at /var/www/html/drupal/web/vendor/drush/drush/includes/batch.inc:55
 drush_backend_batch_process() at /var/www/html/drupal/web/vendor/drush/drush/src/Commands/core/UpdateDBCommands.php:410
 Drush\Commands\core\UpdateDBCommands->updateBatch() at /var/www/html/drupal/web/vendor/drush/drush/src/Commands/core/UpdateDBCommands.php:73
 Drush\Commands\core\UpdateDBCommands->updatedb() at n/a:n/a
 call_user_func_array() at /var/www/html/drupal/web/vendor/consolidation/annotated-command/src/CommandProcessor.php:276
 Consolidation\AnnotatedCommand\CommandProcessor->runCommandCallback() at /var/www/html/drupal/web/vendor/consolidation/annotated-command/src/CommandProcessor.php:212
 Consolidation\AnnotatedCommand\CommandProcessor->validateRunAndAlter() at /var/www/html/drupal/web/vendor/consolidation/annotated-command/src/CommandProcessor.php:176
 Consolidation\AnnotatedCommand\CommandProcessor->process() at /var/www/html/drupal/web/vendor/consolidation/annotated-command/src/AnnotatedCommand.php:391
 Consolidation\AnnotatedCommand\AnnotatedCommand->execute() at /var/www/html/drupal/web/vendor/symfony/console/Command/Command.php:326
 Symfony\Component\Console\Command\Command->run() at /var/www/html/drupal/web/vendor/symfony/console/Application.php:1096
 Symfony\Component\Console\Application->doRunCommand() at /var/www/html/drupal/web/vendor/symfony/console/Application.php:324
 Symfony\Component\Console\Application->doRun() at /var/www/html/drupal/web/vendor/symfony/console/Application.php:175
 Symfony\Component\Console\Application->run() at /var/www/html/drupal/web/vendor/drush/drush/src/Runtime/Runtime.php:110
 Drush\Runtime\Runtime->doRun() at /var/www/html/drupal/web/vendor/drush/drush/src/Runtime/Runtime.php:40
 Drush\Runtime\Runtime->run() at /var/www/html/drupal/web/vendor/drush/drush/drush.php:139
 require() at /var/www/html/drupal/web/vendor/drush/drush/drush:4
 include() at /var/www/html/drupal/web/vendor/bin/drush:120
``

Website is not down and I can access the back-end, but it can't stay like this.

I followed [How to upgrade from Drupal 9 to Drupal 10][1].

Do you have any idea?

I'll try to spot differences with my 3rd website which has well supported the upgrade.

  [1]: https://www.drupal.org/docs/upgrading-drupal/upgrading-from-drupal-8-or-later/how-to-upgrade-from-drupal-9-to-drupal-10

```

# Answer
As @4uk4 points out, this issue happened to me because of an old composer. Updating composer to its latest version and dumping autoload worked for me:

```
composer.phar self-update
composer.phar dump-autoload

```

After that, I could update the DB with no further issues:

```
/vendor/bin/drush updb --verbose

```

I got this from https://www.drupal.org/project/drupal/issues/3098942#comment-15378094
> 2 votes

---
Tags: database, drush, upgrading
---


# Question
Title: Feeds: Do not update existing content items not working as expected
I'm trying to use feeds + tamper to do bulk updates to an entity reference field in "Employee profile" content type but retaining existing values. I'm using a CSV file and mapping a "email" and "badge" columns as source with Email (field\_email) and Badges (field\_badges) as targets respectively, where email is set as unique. Additionally, I have configured the processor to "Do not update existing content items". When I run the import, it finishes successfully but it says "Feeds with tamper: There are no new Employee Profile items." and nothing is being added.

If I change the processor settings to "Update existing content items", the values are being added, but the existing ones are removed.

Any guidance will be appreciated.

Thanks in advance, Ruben B

# Answer
> 0 votes
There isn't a straightforward way to append values to a multi-value field without custom plugins. You might need to create a custom Feeds Tamper plugin that specifically handles appending values to your entity reference field.

---
Tags: feeds
---


# Question
Title: Can I display the add content form as accordion
In Drupal 10, can the add content form for a content type be set up to show groups of fields under expandable sections?

We have tried to make this work with the Field Group module but the removal of jQuery from Core means the accordion feature doesn't work as expected (and adding jQuery UI creates conflicts with our Bootstrap 5-based custom theme)

Either an accordion view or an expanded content view (as Drupal uses on the Extend page with the "Core", "Chaos Tools", etc. sections) would work for this case.

# Answer
> -2 votes
You can use field group module, but don't use the accordion feature, use the HTML element to add the div, classes and whatever you need to get the HTML right, then use https://getbootstrap.com/docs/5.2/components/accordion/ classes.

---
Tags: entities
---


# Question
Title: How do I programmatically update or delete menu items?
I have seen How do I create menu links programmatically? but how do I update or delete menu items in Drupal 8?

By programmatically, I mean dynamically; YAML configuration files are not going to help.

# Answer
First you will have to load the entity. Either way works:

`$menu_link = MenuLinkContent::load($menu_link_id);`

or ...

```
$menu_link = \Drupal::entityTypeManager()->getStorage('menu_link_content')->load($menu_link_id);

```

Next you can update value using `set()` method or through the magic method \__set...

`$menu_link->expanded = TRUE;`

To save, simply call the `save()` method. To delete, simply call the `delete()` method.
> 8 votes

# Answer
I had to do some thing like ....

```

 //menuLinkManager = 'plugin.manager.menu.link'
$result = $this->menuLinkManager->loadLinksByRoute('entity.node.canonical', ['node' => $node->id()]);
foreach ($result as $menu_item) {
          if (is_object($menu_item)) {
            $id = $menu_item->getPluginDefinition()['metadata']['entity_id'];
$menu_link = $this->entityTypeManager->getStorage('menu_link_content')->load($id);
$menu_link->enabled = 0;
$menu_link->save()

```
> 3 votes

# Answer
Probably too late I did like:

```
$menu_handler = \Drupal::service('menu.link_tree')
    ->load("menu-name", new \Drupal\Core\Menu\MenuTreeParameters());
foreach($menu_handler as $key=>$value) {
  if('Name' == $value->link->getTitle()) {
    $link = $value->link;
    $link->updateLink(
      [
        'enabled'=>'1',
        'title'=>'New Name'
      ],
      true
    );
  }
}

```
> 2 votes

# Answer
This is how i delete all the links in a particular menu like footer

```
$old_menu_links = \Drupal::entityTypeManager()->getStorage('menu_link_content')
  ->loadByProperties(['menu_name' => 'footer']);
foreach ($old_menu_links as $old_menu_link) {
  $old_menu_link->delete();
}

```

And to update existing menu,

```
$menu_links = \Drupal::entityTypeManager()->getStorage('menu_link_content')
  ->loadByProperties(['menu_name' => 'footer']);
foreach ($menu_links as $menu_link) {

  if ($menu_link->gettitle() == 'old-title') {
    $menu_link->set('title', 'new-title');
    $menu_link->save();
  }
}

```
> 0 votes

# Answer
This turned out to be insanely complicated. Apparently not every menu link is a `MenuLinkContent` object. Sometimes it is a `MenuLinkDefault` object. In that case, you cannot use `->set()` or `->save()`. This is the most complete answer to cover both cases. Put this inside of an `hook_update_n()` and change `$disabled_routes` and `$menu` to your needs. In this example I am disabling menu links.

```
  $disable_routes = [
    '<front>',
    'view.frontpage.page_1',
    'private_message.private_message_page',
    'view.opigno_calendar.page_month',
  ];

  $menu_name = 'main';

  $menu_link_manager = \Drupal::service('plugin.manager.menu.link');
  $menu_link_tree_elements = \Drupal::service('menu.link_tree')
    ->load($menu_name, new \Drupal\Core\Menu\MenuTreeParameters());

  foreach ($disable_routes as $disable_route) {
    foreach ($menu_link_tree_elements as $key => $menu_link_tree_element) {
      $menu_link_default = $menu_link_tree_element->link;
      $plugin_definition = $menu_link_default->getPluginDefinition();
      $link_route = $plugin_definition['route_name'];
      if ($link_route == $disable_route) {
        $uuid = $menu_link_default->getDerivativeId();
        if (!empty($uuid)) {
          $menu_link_content = \Drupal::service('entity.repository')
            ->loadEntityByUuid('menu_link_content', $uuid);
          $menu_link_content->set('enabled', 0);
          $menu_link_content->save();
        }
        else {
          $menu_link = $menu_link_manager->getDefinition($key);
          $menu_link['enabled'] = 0;
          $menu_link_manager->updateDefinition($key, $menu_link);

          // Not sure if this is necessary.
          $menu_link_default->updateLink(
            [
              'enabled' => 0,
            ],
            true
          );
        }
      }
    }
  }

```
> 0 votes

# Answer
My use case was, that I had a leftover menu link, when uninstalling the CAS module, due to this core issue https://www.drupal.org/project/drupal/issues/3324291. Since I could not load the menu link via `\Drupal::entityTypeManager()->getStorage('menu_link_content')->load($menu_link_id);`, because this entry was only in the `menu_tree` db table and not in `menu_link_content(_data)` db tables (looks like that this is because of the core issue and not standard behavior, but I didn't investigate further here), this is what I did (it was a `MenuLinkDefault` object and not a `MenuLinkContent` object for some context also):

```
  /** @var \Drupal\Core\Menu\MenuTreeStorageInterface $menu_tree_storage */
  $menu_tree_storage = \Drupal::service('menu.tree_storage');
  /** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
  $menu_link_manager = \Drupal::service('plugin.manager.menu.link');
  $cas_menu_link = $menu_link_manager->loadLinksByRoute('cas.settings');
  if (!empty($cas_menu_link)) {
    // Array keys are plugin IDs.
    $menu_tree_storage->delete(array_key_first($cas_menu_link));
  }

```

The delete function here removes the data only from the menu\_tree table, keep that in mind. But it also updates all the childred/parents of the deleted link in the tree structure. If you delete `MenuLinkContent` entities (menu\_link\_content \_data db tables), this process is also done via the preDelete function in the entity class, so the tree storage is also updated.

So to sum it up, this solution is for purging of orphaned data in the menu\_tree table, on incorrect deletion of the MenuLinkContent entities, due to a core issue.
> 0 votes

---
Tags: 8, navigation
---


# Question
Title: mkdir(): Permission Denied in core/lib/Drupal/Component/PhpStorage/FileStorage.php
On every site page, I am getting these errors:

```
User warning: mkdir(): Permission Denied in Drupal\Component\PhpStorage\FileStorage->createDirectory() (line 123 of core/lib/Drupal/Component/PhpStorage/FileStorage.php).
Drupal\Component\PhpStorage\FileStorage->createDirectory('sites/default/files/php/twig', 509) (Line: 73)

Drupal\Component\PhpStorage\FileStorage->ensureDirectory('sites/default/files/php/twig') (Line: 66)
Drupal\Component\PhpStorage\MTimeProtectedFastFileStorage->save('65cd8eafb5bac_status-messages.html.twig_GfCMeKK44m7xVsgW6vKS2K1IN', '<?php

[followed by contents of a twig php file it looks like]

```

I am also getting the same or very similar errors when I try to clear caches from the site but not if I use `drush cr` on the cli.

My sites/default/files directory has 777 permissions and is owned by the webserver (apache).

I did see this issue which seems very similar but I can't add any comments there. How do I debug Drupal\Component\PhpStorage\FileStorage::createDirectory()?

I would be happy to try using the `\Drupal::logger('my_module')->notice($message)` but can you tell me where to put that? Thanks.

# Answer
> 0 votes
It's possible to reproduce this error by changing the ownership/permission of sites/default/files/php, so that the web server no longer can access it, and clearing the content of the directory (this is only a cache and can be cleared without data loss). The next site visit will create a ton of these warnings.

Since you have full error reporting already enabled, you see all debugging details and don't need the linked topic.

What you need to do is to set up Drupal according to web/core/INSTALL.txt. If this is not a new install, this depends on the steps you did before to cause the error. If, for example, you have migrated the site to a different host, you might need to adjust the directories/files ownership and permissions. See this great tutorial https://www.singlemindconsulting.com/blog/fixing-permissions-on-the-files-directory-in-drupal/

# Answer
> 0 votes
Turns out the reason for the permissions errors in my case is SELinux not allowing the Apache httpd process to write to the filesystem. The fix is:

```
sudo chcon -R -t httpd_sys_rw_content_t /path/to/sites/default/files/

```

See https://unix.stackexchange.com/questions/50639/httpd-cant-write-to-folder-file-because-of-selinux

---
Tags: 10
---


# Question
Title: Do I still need to create() services with PHP v8 constructor promotion?
PHP v8 supports constructor property promotion, which greatly uses boilerplate code. But basically all Drupal services use a factory pattern with a `__construct()` and a `create()` function.

Is the `create()` still necessary when using constructor property promotion? I've been searching in contrib modules for MR which use the new constructor property, and I've seen both, services with and without create function.

So given this YAML file

```
services:
  my_module.helper:
    class: Drupal\my_module\Helper
    arguments: ['@request_stack', '@entity_type.manager', '@config.factory', '@current_user', '@logger.factory']

```

and this service class constructor

```
  public function __construct(
    protected RequestStack $requestStack,
    protected EntityTypeManagerInterface $entityTypeManager,
    protected ConfigFactoryInterface $configFactory,
    protected AccountInterface $currentUser,
    protected LoggerChannelFactoryInterface $loggerFactory
  ) {}

```

do I still need a `create($container)` function (with all the redundant `$container->get(...)` calls inside it) at all?

If I don't need a `create()` function, can I somehow pass arguments to the other factories used inside my own service? E.g. loggers and configs are often used in my service, and I don't want to write

```
$this->configFactory->get('my_module)->get('foo');
$this->loggerFactory->get('my_module)->error('bar');

```

all the time. Can I somehow tell the logger and config factories to use my module name to write shorter code like

```
$this->config->get('foo');
$this->logger->error('bar');

```

# Answer
> 4 votes
See Drupal\field\_ui\Access\FieldReuseAccessCheck:

*field\_ui.services.yml*

```
services:
  access_check.field_ui.field_reuse:
    class: Drupal\field_ui\Access\FieldReuseAccessCheck
    autowire: true
    tags:
      - { name: access_check, applies_to: _field_ui_field_reuse_access }

```

*/src/Access/FieldReuseAccessCheck.php*

```
<?php

namespace Drupal\field_ui\Access;

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\AccessResultInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\Core\Routing\Access\AccessInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\field\FieldStorageConfigInterface;
use Symfony\Component\Routing\Route;

/**
 * Defines an access check for the reuse existing fields form.
 */
class FieldReuseAccessCheck implements AccessInterface {

  /**
   * Creates a new FieldReuseAccessCheck.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The entity type manager.
   * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $fieldTypePluginManager
   *   The field type plugin manager.
   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entityFieldManager
   *   The entity field manager.
   */
  public function __construct(
    protected EntityTypeManagerInterface $entityTypeManager,
    protected FieldTypePluginManagerInterface $fieldTypePluginManager,
    protected EntityFieldManagerInterface $entityFieldManager,
  ) {}

  /**
   * Checks access to the reuse existing fields form.
   *
   * @param \Symfony\Component\Routing\Route $route
   *   The route to check against.
   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
   *   The parametrized route.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The currently logged in account.
   * @param string|null $bundle
   *   (optional) The bundle. Different entity types can have different names
   *   for their bundle key, so if not specified on the route via a {bundle}
   *   parameter, the access checker determines the appropriate key name, and
   *   gets the value from the corresponding request attribute. For example, for
   *   nodes, the bundle key is "node_type", so the value would be available via
   *   the {node_type} parameter rather than a {bundle} parameter.
   *
   * @return \Drupal\Core\Access\AccessResultInterface
   *   The access result.
   */
  public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account, string $bundle = NULL): AccessResultInterface {
    $access = AccessResult::neutral();
    if ($entity_type_id = $route->getDefault('entity_type_id')) {
      if (empty($bundle)) {
        $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
        $bundle = $route_match->getRawParameter($entity_type->getBundleEntityType());
      }

      $field_types = $this->fieldTypePluginManager->getDefinitions();
      // Allows access if there are any existing fields and the user
      // correct permissions.
      foreach ($this->entityFieldManager->getFieldStorageDefinitions($entity_type_id) as $field_storage) {
        // Do not include fields with
        // - non-configurable field storages,
        // - locked field storages,
        // - field storages that should not be added via user interface,
        // - field storages that already have a field in the bundle.
        $field_type = $field_storage->getType();
        $access->addCacheableDependency($field_storage);
        if ($field_storage instanceof FieldStorageConfigInterface
          && !$field_storage->isLocked()
          && empty($field_types[$field_type]['no_ui'])
          && !in_array($bundle, $field_storage->getBundles(), TRUE)) {
          $permission = $route->getRequirement('_field_ui_field_reuse_access');
          $access = $access->orIf(AccessResult::allowedIfHasPermission($account, $permission));
        }
      }
      $access->addCacheableDependency($this->entityFieldManager);
    }
    return $access;
  }

}

```

---
Tags: symfony-services
---


# Question
Title: Handle module dependency in a contrib submodule
I am maintening a contrib module in which there's a submodule. This submodule needs commerce\_product module to work, but the module doesn't.

In drupal CI pipelines I've got some warnings from phpstan

```
Parameter $commerce_product of method
Drupal\index_now\CommerceProductOperationsInterface::pingIndexNow()
has invalid type Drupal\commerce_product\Entity\Product.

```

And some errors from phpunit

```
Drupal\Tests\index_now_commerce\Unit\CommerceProductOperationsTest::testProductIsUnpublished
Prophecy\Exception\Doubler\ClassNotFoundException:
Cannot prophesize class Drupal\commerce_product\Entity\Product, because it cannot be found.

```

I added a composer.json to the submodule

```
{
    "name": "drupal/index_now_commerce",
    "description": "Index now submodule dedicated to commerce products",
    "type": "drupal-module",
    "require": {
        "drupal/core": " ^10",
        "drupal/commerce": "^2.38"
    }
}

```

and the submodule has the dependency

```
dependencies:
  - commerce:commerce_product

```

What can I do more?

# Answer
> 3 votes
There is no way for composer to check the dependency from your submodule.

Think about this. If somebody wants to install your submodule there is no way they can use a composer command to do it. They need to use the command to install the "main module" and is composer who handles the dependencies.

What happens now is your main module doesn't need the commerce module as a dependency and it will never be handled by composer while building the dependencies list to be downloaded. So your submodule will never have the dependencies it needs to work.

Two options:

* Commerce needs to be a dependency of your "main module"
* Your submodule needs to be a Drupal project in order the control the dependencies with composer.

---
Tags: 10
---


# Question
Title: Add multi-level taxonomy term selection field with multiple selection for last level
I want to add field with taxonomy term selection If suppose I have vocabulary as **Food** and it has hierarchy of terms like below:

```
Fruit
- Mango
- Apple
- Banana
Vegetables
- Spinach
- Potato
- Tomato

```

While selecting taxonomy terms in a node I want to first select Fruit or Vegetables and after that another select list should appear with provision to select multiple sub terms for respective top level terms

Example: If I select Fruit from first select list only single selection allowed and then second select list should appear with Mango, Apple, Banana so one should have chance to select multiple fruits from second select list

I tried simple hierarchical select but it doesn't give provision to select multiple terms on last level field.

# Answer
> 1 votes
Have a look at Taxonomy Term Reference Tree Widget module. This does exactly what you need as per its documentation.

Edit: Since now I bettter understand your requirement, you might want to try Hierarchical Select.

# Answer
> 0 votes
Please use https://www.drupal.org/project/facets\_taxonomy\_multilevel module which can fulfil your requirement.

---
Tags: entities, taxonomy-terms
---


# Question
Title: Create a custom $form_id for a webform integrated into a node?
I have used Webform many times before but this is the first time I am testing Webform Node :

> Provides a Webform content type which allows webforms to be integrated into a website as nodes.

Usually, I integrate webforms with blocks.

Everything works fine but I notice that the webform `$form_id` contains the node id (here `nid = 174`):

```
webform_submission_shop_contact_node_174_add_form

```

This poses a problem for me because I want to make a hook to add a library. The hook will therefore not be able to work on all environments once deployed because here the node is content and the nid will be different.

In `hook_form_alter`

```
if ($form_id === 'webform_submission_shop_contact_node_174_add_form') {
    $form['#attached']['library'][] = 'custom_theme/contact';
}

```

Do you know if I can modify the id of this webform integrated into a node?

I saw this option here `/admin/structure/webform/manage/{webform}/settings/form`

which allows you to create a custom id but this has no effect (probably because the webform is integrated into a node).

# Answer
> 1 votes
Setting a breakpoint in a form alter hook I see at least two ways to get the info you are looking for:

```
$form['#webform_id']

```

or the base form ID (without the node details) in

```
$form_state->getBuildInfo()

```

in your case `webform_submission_shop_contact_form`, which can also be used in `hook_form_BASE_FORM_ID_alter()`.

---
Tags: webforms
---


# Question
Title: Content type template takes precedence over specific node id template. How to change priority?
In my `MYCUSTOMTHEME.theme` file, I've added page template suggestion for specific content types and paths, so that I can use `page--node--CONTENT-TYPE.html.twig` to override certain pages.

In MYCUSTOMTHEME.theme, I use:

```
/**
 * Implements hook_theme_suggestions_page_alter().
 */
function MYCUSTOMTHEME_theme_suggestions_page_alter(array &$suggestions, array $variables) {
  // Add twig template name suggestions
  // For content types
  if ($node = \Drupal::routeMatch()->getParameter('node')) {
    $suggestions[] = 'page__node__' . $node->bundle();
  }
}

```

Now I've noticed that the general content type template takes priority over the specific node ID template:

```
<!-- FILE NAME SUGGESTIONS:
   x page--node--landing-page.html.twig
   * page--node--125549.html.twig
   * page--node--%.html.twig
   * page--node.html.twig
   * page.html.twig
-->

```

Is there any way to change this order?

# Answer
I think I found a solution. This comment on Drupal.org suggests using `array_splice()` to insert the template suggestion in the correct position.

The code in my .theme file is now:

```
/**
 * Implements hook_theme_suggestions_page_alter().
 */
function formule_theme_suggestions_page_alter(array &$suggestions, array $variables) {
  if ($node = \Drupal::routeMatch()->getParameter('node')) {
    array_splice($suggestions,1,0,'page__node__' . $node->bundle());
  }
}

```

This results in:

```
<!-- FILE NAME SUGGESTIONS:
   x page--node--126027.html.twig
   * page--node--%.html.twig
   * page--node--landing-page.html.twig
   * page--node.html.twig
   * page.html.twig
-->

```

Thanks again for the hints provided in the comments.
> 0 votes

---
Tags: 9
---


# Question
Title: Egulias Email validator error when upgrading from Drupal 9.5 to 10.1.8 using composer
I am trying to upgrade from Drupal 9.5 to 10.1.8 using composer. I have used upgrade status module to ensure that all modules are compatible with Drupal 10.

I have used the composer command to begin the upgrade.

```
composer require drupal/core-recommended:10.1.8 drupal/core-composer-scaffold:10.1.8 drupal/core-project-message:10.1.8 --update-with-all-dependencies

```

However I keep running into the following error.

```
  - Root composer.json requires drupal/core-recommended 10.1.8 -> satisfiable by drupal/core-recommended[10.1.8].
- drupal/core-recommended 10.1.8 requires egulias/email-validator ~4.0.1 -> found egulias/email-validator[4.0.1, 4.0.2] but these were not loaded, likely because it conflicts with another require

```

Trying to debug the error results in

```
 composer why-not egulias/email-validator 4.0.1
drupal/core             9.5.11      requires         egulias/email-validator (^2.1.22|^3.2)
drupal/core-recommended 9.5.11      requires         egulias/email-validator (~3.2.1)
swiftmailer/swiftmailer v6.3.0      requires         egulias/email-validator (^2.0|^3.1)
egulias/email-validator 4.0.1       requires         doctrine/lexer (^2.0 || ^3.0)
cti/lfhw                dev-develop does not require doctrine/lexer (but 1.2.3 is installed)
Not finding what you were looking for? Try calling `composer update "egulias/email-validator:

```

I am not able to understand what should be done to resolve the issue and what is locking the validator to this specific version.

# Answer
> 1 votes
Swift Mailer module has been deprecated, see Swift Mailer - Moderately critical - Access bypass - SA-CONTRIB-2024-006, so you should consider switching to Drupal Symfony Mailer which will probably fix this dependency.

---
Tags: composer, 10, upgrading
---


title: Theme hook not found on custom module

text:
# Question
Title: Theme hook not found on custom module
I'm trying to create a module which adds a page, the controller works but I cannot render a twig template for it.

The page layout is displayed without the page I created and this warning is reported in the logs:

"Theme hook eu\_calendar not found."

but I do have the hook defined

This is the controller:

```
class EuCalendarController extends ControllerBase {

    public function index() {
        return [
            '#title' => 'my title',
            '#theme' => 'eu_calendar',
            '#description' => 'description test',
        ];
    }
}

```

Module file:

```
function eu_calendar_theme($existing, $type, $theme, $path) {
    return [
        'eu_calendar' => [
            'template' => 'index',
        ],
    ];
}

```

I also tried adding `variables` like

```
function eu_calendar_theme($existing, $type, $theme, $path) {
    return [
        'eu_calendar' => [
            'variables' => [
                'test_var' => NULL,
            ],
            'template' => 'index',
        ],
    ];
}

```

What am I missing here?

And do I need to configure anything with the regions, I want my module to be displayed in the `content` region by default...

Any documentation or tutorial is also welcome, I read a bunch of them none of them is explaining the options and details.

# Answer
> 2 votes
I guess I couldn't be able to clear the caches or something. I changed the hook name, uninstalled the module and installed it back, cleared the caches, restarted php-fpm and memcached etc. and it started working somehow.

For reference this is how theming works, may help other newcomers like me:

First the theme and the variables that needs to be passed is defined inside the \*.module file. The trick is the `variables` key, without it Drupal ignores the definition.

The key of the array is also the name of the twig file by default, which can be overridden with `template`. So no need to pass `template` if it's the same with the key.

The theme definition to be rendered is defined inside the controller with `#theme`. Any variable that is not defined in the theme definition are not sent to the template even it's sent in the controller.

So the final working code is:

**Module:** (modules/eu\_calendar/eu\_calendar.module)

```
function eu_calendar_theme($existing, $type, $theme, $path) {
    return [
        'index' => [
            'variables' => [
               'featured' => [],
               'events' => [],
            ],
        ],
    ];
}

```

**Controller:** (modules/eu\_calendar/src/Controller/EuCalendarController.php)

```
namespace Drupal\eu_calendar\Controller;

use Drupal\Core\Controller\ControllerBase;

class EuCalendarController extends ControllerBase {
    public function index() {
        return [
            '#theme' => 'index',
            '#events' => ['event1', 'event2'],
            '#featured' => ['featured1', 'featured2'],
        ];
    }
}

```

**Template:** (modules/eu\_calendar/templates/index.html.twig)

```
<h3>Featured Events</h3>
<ul>
    {% for featured_event in featured %}
    <li>{{ featured_event }}</li>
    {% endfor %}
</ul>

<h3>Events</h3>
<ul>
    {% for event in events %}
    <li>{{ event }}</li>
    {% endfor %}
</ul>

```

# Answer
> 2 votes
**myModule.module**

```
function myModule_theme($existing, $type, $theme, $path) {
return array(
 'pagina_3' => array(
   'variables' => array(
     'titulo' => 'title',
     'descripcion' => 'description',
     'formulario'   => NULL
      ),
    'template' => 'pagina_3'
   )
  );

}

```

**mymodule/templates/pagina\_3.html.twig**

```
<div class="row">

    <div class="col-md-6">
        <h2>{{ titulo }}</h2>
        <p>{{ descripcion }}</p>
    </div>

    <div class="col-md-6">
        {{ formulario }}
    </div>

</div>

```

**myModuleController.php**

```
public function verPagina3() {

    $form = $this->formBuilder()->getForm('Drupal\myModule\Form\InsertarGastoForm');

    // Le pasamos el formulario y demás a la vista (tema configurado en el module)
        return [
            '#theme' => 'pagina_3',
            '#titulo' => $this->t('Formulario crear empresa'),
            '#descripcion' => 'Formulario para crear una nueva empresa en Drupal 8',
            '#formulario' => $form
        ];

   }

```

# Answer
> 1 votes
**This is the controller:**

```
public function index()
{
    $build['myelement'] = array(
        '#theme' => 'eu_calendar',
        '#title' => 'my title',
        '#description' => 'description test',
    );
    return $build;
}

```

**Module file:**

```
function eu_calendar_theme($existing, $type, $theme, $path)
{
    return array(
        'euCalendar' => array(
            'template' => 'index',
            'path' => $path . '/templates',
            'variables' => array(
                'title' => NULL,
                'description' => FALSE,
            ),
        ),
    );
}

```

# Answer
> 0 votes
I had a similar issue, when implementing templates for a custom block in Drupal 10.1. I propose here two important lessons I have learnt while solving it.

## Matching (some) strings

Theme name, which is the value of the array element labelled `#theme` defined in controller's class (file **MyController.php**), must exactly match the name of hook returned by *hook\_theme()* (function **my\_module\_theme()**, declared in file **my\_module.module**).

Please, let me stress about this, as in my case it was causing the error!

Indeed I was confused by that automatic substitution of dashes instead of underscores, and initially I was incorrectly putting dashes (e.g. `my-module--my-entity--card`) when declaring theme name inside controller's class (**MyController.php**), as I would do when naming template files. But hook's name returned by my *hook\_theme()* function (**my\_module\_theme()** from **my\_module.module**) had underscores in place of dashes (i.e. `my_module__my_entity__card`) - as per the common convention of using underscores for hooks names. This way I experienced the error "Theme hook *my-module--my-entity--card* not found.". Then, to solve it and finally pick up the **my-module--my-entity--card.html.twig** template file, I simply had to rewrite declaration of theme name in controller's class (**MyController.php**) to `my_module__my_entity__card`.

Please note that, as already written in another answer, programming *hook\_theme()* to also returns an array element labelled `template` is needed only if you desire to apply a template with different filename (not the same as the theme - except for underscores, which are turned to dashes).

## Rebuilding cache

As usual when working wiht Drupal's templates, **you do have** to refresh Drupal's cache (either by running the command `drush cr`, or through the web interface) after you have added any new Twig template files to your */templates* directories. Later, if you have properly set options for development, you do not really need to refresh cache after jsut saving changes to templates.

It is also worth mentioning that **you will need to update Drupal's cache as well, whenever you add/rename a variable** (to be passed to the template) into section `variables` of the array returned by function *hook\_theme()* in module's file.

If you add a variable to the *hook\_theme()*, but omit to rebuild the cache before using that variable in templates, Drupal will not raise any error; it will just treat the variable as `null`, although your controller might have assigned values to that variable.

---
Tags: 8
---


# Question
Title: Getting the "Solarium @version is too old" error after upgrade
I recently updated a Drupal 9.3 site to Drupal 9.5 and most of the contributed modules were updated to the latest version that supports both 9 and 10 in view of an upcoming Drupal 10 upgrade.

On the status report page, I am getting this error.

> Solarium @version is too old!

How do we rectify this error? The Solr search module is locking Solarium to an old version and we might need to force an upgrade on this. Is there a better way to do this?

More details of the composer.lock file with solarium set to 6.3.3

```
{
        "name": "drupal/search_api_solr",
        "version": "4.3.1",
        "source": {
            "type": "git",
            "url": "https://git.drupalcode.org/project/search_api_solr.git",
            "reference": "4.3.1"
        },
        "dist": {
            "type": "zip",
            "url": "https://ftp.drupal.org/files/projects/search_api_solr-4.3.1.zip",
            "reference": "4.3.1",
            "shasum": "002061b1160df5da5d13eea6f3a4c50f8008b157"
        },
        "require": {
            "composer-runtime-api": ">=2.0",
            "composer/semver": "^1.0|^3.0",
            "consolidation/annotated-command": "^2.12|^4.1",
            "drupal/core": "^9.3 || ^10.0",
            "drupal/search_api": "~1.30",
            "ext-dom": "*",
            "ext-json": "*",
            "ext-simplexml": "*",
            "laminas/laminas-stdlib": "^3.2",
            "maennchen/zipstream-php": "^2.2.1|^3.0.2",
            "solarium/solarium": "^6.3.3"
        },

```

# Answer
I had the same problem on a Drupal 9 with version 4.3.1 of search\_api\_solr, because version 4.3.2 is no longer compatible with D9 but only: ^10.1 || ^11.0

So I've been using version 4.2.12 while waiting to upgrade to D10.

```
composer require 'drupal/search_api_solr:4.2.12' -W

```

Do not forget the "-W" for get the Downgrading solarium/solarium works (6.x-dev b785b8b =\> 6.3.1)

And I no longer get the error in the dashboard
> 1 votes

---
Tags: search, 9, upgrading
---


# Question
Title: How get the default language of a translatable node bundle?
I'm on D10. I have a translatable content type, e.g. node, *article* bundle.

How can I get programmatically its default language code, defined in the settings form?

I've tried

```
  $entity_type_manager = \Drupal::entityTypeManager();
  $node_type_definition = $entity_type_manager->getStorage('node_type')->load('article');

```

But that contains always the site default language, no matter what I set in the form.

Thanks

# Answer
Snippet code: to get the default langcode defined in the settings form: (Drupal 9/10)

```
use Drupal\language\Entity\ContentLanguageSettings;

// Get the default langcode that selected in the entity's setting form
$lang_config = ContentLanguageSettings::loadByEntityTypeBundle('node', 'article');
$selected_langcode = $lang_config->getDefaultLangcode();

```

**Note:** node denotes entity\_type, article denotes bundle/node type
> 1 votes

---
Tags: nodes, i18n-l10n
---


title: Layout Builder & linking fields

text:
# Question
Title: Layout Builder & linking fields
I'm using Drupal 9.

I have a paragraph type which consists of 4 fields:

1. Image
2. Title
3. Text
4. Link Field

I'm using layout builder to display the paragraph.

I want the Image, title & Text to all be clickable, and click through to the Url set in the Link field (#4).

How do I do this?

# Answer
> 0 votes
To achieve this, I created a custom module for my project:

1. Create the custom module file (if it doesn't already exist)

\[PROJECT-ROOT\]/web/modules/custom/\[PROJECT-NAME\]\_paragraphs/\[PROJECT-NAME\]\_paragraphs.module

2. Create the function to check if this particular paragraph type has a field called "field\_link" (the machine name of whatever your field is called). If it has the field, store the URL in a variable called "$link\_uri"

```
     function [PROJECT_NAME]_paragraphs_preprocess_paragraph(&$variables) {
   /** @var \Drupal\paragraphs\ParagraphInterface $paragraph */
   $paragraph = $variables['paragraph'];
   if ($paragraph->hasField('field_link')
     && $uri = $paragraph->get('field_link')->uri){
     try {
       $variables['link_uri'] = $paragraph->get('field_link')->uri;
     }
     catch (\Throwable $ex) {
       // Do nothing.
     }
   }
 }

```
3. Override the default paragraph template file:

* Create a new file: web/themes/custom/\[PROJECT-NAME\]/templates/paragraphs/paragraph.html.twig
* Find the default paragraph structure & Copy the default template structure into your new file:
* + web/modules/contrib/paragraphs/templates/paragraph.html.twig

4. Customise it to add the wrap-around anchor tag and populate the href attribute with the variable we just saved ("$link\_uri")

For example, my new template is now:

```
{%
  set classes = [
    'paragraph',
    'paragraph--type--' ~ paragraph.bundle|clean_class,
    view_mode ? 'paragraph--view-mode--' ~ view_mode|clean_class,
    not paragraph.isPublished() ? 'paragraph--unpublished'
  ]
%}
{% block paragraph %}
  <div class="para-wrapper">
  {% if link_uri %}
    <a href="{{ link_uri }}">
  {% endif %}
  <div{{ attributes.addClass(classes) }}>
    {% block content %}
      {{ content }}
    {% endblock %}
  </div>
  {% if link_uri %}
    </a>
  {% endif %}
  </div>
{% endblock paragraph %}

```

---
Tags: layout-builder
---


# Question
Title: How to send POST request to https using \Drupal::httpClient()?
I want to send POST request to https using \Drupal::httpClient(). But ended up with error like

> GuzzleHttp\Exception\RequestException: cURL error 60: SSL certificate problem: self signed certificate (see http://curl.haxx.se/libcurl/c/libcurl-errors.html)

Below is my code.

```
$post_fields['name'] = 'aa';
    $fields_string = json_encode($post_fields);
    $response = \Drupal::httpClient()
           ->post("https://localhost/example/api/user-update?_format=json", [
         'auth' => ['admin', 'admin'],
         'body' => $fields_string,
    'http_errors' => FALSE,
         'headers' => [
           'Content-Type' => 'application/json',
    ],
       ]);
    dsm($response->getBody()->getContents());

```

How to send POST request with \Drupal::httpClient().

# Answer
The error message should inform you what is going on. You are using a self-signed certificate locally. This probably won't be an issue on your production sites, but this is not really useful for local development.

Inspect the `\Drupal::httpClient()` static method you may discover that it uses the `http_client_factory:fromOptions` service. Use this directly allows you to pass options into Guzzle to allow requests to sites using self-signed certificates.

```
$clientFactory = \Drupal::service('http_client_factory');
$client = $clientFactory->fromOptions(['verify' => FALSE]);

```

Obviously this is not ideal. You should probably parameterize the URL that you are using instead of hard coding the URL. Then locally you can test without a self-signed cert or HTTPS. Or parameterize the options you pass into Guzzle so that `verify => TRUE` when you're on production. It is **not** recommended to set it to FALSE and this is a security risk.
> 9 votes

# Answer
We must pass the post values in http\_client via **form\_params** . Here down is one of my code to make **OAuth2 authentication** to another drupal 8 REST api being used **simple\_oauth** module.

```
$response = \Drupal::httpClient()->post($base_url . '/oauth/token', [
      'verify' => true,
      'form_params' => ['grant_type'=> 'password',
                        'client_id' => 'CLIENT-ID',
                        'client_secret'=> 'CLIENT-SECRET',
                        'scope'=>'',
                        'username'=>'admin',
                        'password'=>'admin',],
        'headers' => [
          'Content-type' => 'application/x-www-form-urlencoded',
        ],
    ])->getBody()->getContents();

```
> 10 votes

# Answer
$client = $clientFactory-\>fromOptions(\['verify' =\> FALSE\]); make me have an error.

I tried this line above, which is working :

```
$response = $client->request($methode, $url->toString(), ['form_params' => $options, 'verify' => false]);

```
> 1 votes

# Answer
You might get some idea from this doc https://www.drupal.org/docs/8/core/modules/rest/3-post-for-creating-content-entities
> 0 votes

# Answer
I tried the various answers above, none did quite work. As it happens there was, in our case, a simpler solution. We have several environments identified via $settings\['environment'\] in settings.php. One of those, "local", needs to work with self-signed certs. To avoid Guzzle/curl from complaining we do:

```
if ($environment == 'local') $params['verify'] = FALSE; // $params of course holds other needed bits for the call to happen...

$response = json_decode(Drupal::httpClient()
      ->post($target_url, $params)
      ->getBody()
      ->getContents());

```

Hope it helps.
> 0 votes

---
Tags: 8
---


# Question
Title: How to force the browser to refresh the CSS/JS files
I've made a small change on a view (added a new class) and some related changes in the CSS files of the theme.
Since the config change (HTML output) is immediately in effect, I need the browser to be made aware that it has to re-download the theme CSS files.

However, using the "Clear all caches" button on the Performance page of the Drupal admin doesn't seem to have any effect in that regard, and the browser still shows the old (locally cached) CSS, unless I hit Ctrl+F5. (But I can't ask all my visitors to do that.)

Here's an example of the HTML output when aggregation is ON:

```
<link rel="stylesheet" media="all" href="/sites/default/files/css/css_4XTr0kQoY87Py6qdkph9UELdhubJ3OyJba-NTQiautY.css?delta=0&language=fr&theme=mytheme&include=eJxNyEEKgDAMRNELBXOmqIMUGiOd1lJPLxQXbh6fz8EK19UIqXEFdfr1bztIO0DZokDPKG45PZA7oVOni8feMl5WAyCI" />
<link rel="stylesheet" media="all" href="/sites/default/files/css/css_RRKbpxmmUbIPWnmPINgX_B4xj3dmPgTl24PPYLoDZpc.css?delta=1&language=fr&theme=mytheme&include=eJxNyEEKgDAMRNELBXOmqIMUGiOd1lJPLxQXbh6fz8EK19UIqXEFdfr1bztIO0DZokDPKG45PZA7oVOni8feMl5WAyCI" />

```

I would expect some of those parameters (or the file names) to change when clicking *Clear all caches*, but they don't.

Here is what I tried:

1. On the Performance page:

* "Clear all caches".
* Then refresh the page source in Firefox or Chrome (Ctrl+F5).

=\> Exact same output

2. On the Performance page:

* Disable aggregation, save;
* Clear all caches;
* Enable aggregation, save;
* Clear all caches;
* Then refresh the page source in Firefox or Chrome (Ctrl+F5).

=\> Exact same output

For now my "solution" was to disable CSS aggregation, so the file names are entirely different and therefore the browser will download them again, but this is hardly ideal.

Is there something I am missing?

# Answer
After changes you also have to increase the version number in your `libraries.yml` file to generate new aggregated CSS files.
> 2 votes

---
Tags: theming, 10, css-js-aggregation
---


# Question
Title: How to implement a multi step approval with webforms/entityforms based on form components?
Is it possible to invoke multi-step submission workflow using either Webform or Entityform? Here is the scenario of workflows:

> * An employee submits the form
>     + Superviser gets notification, after reviewing the form, he either approves it or denies the request on the same submission.
>     + If approved, an email notification gets sent out to Director who in turn approves or disapproves the same submission.
> * If denied by supervisor, an email gets sent to an employee.

This scenario involves submissions by multiple roles to the same request. I could hide approval fields to employee role, but how would one access webform approval field component via rules for notification purposes? How would I freeze form state?

Should I rather use entityforms since every form component is exposed? As a last resort, I might implement my own solution using forms\_api. But I would like to know first if it is possible using webform or entityforms.

# Answer
> 9 votes
Implementing this workflow is possible by using a combination of these modules (and regular content types, no need for something like "webforms"):

Refer to my answer in the question about "How can I allow anonymous visitors to submit content?" for examples (and more details) about how to use these 4 modules together (this question here seems like a variation of that question).

Below are some details to implement your workflow with a similar approach:

> An employee submits the form

Employee create a node of content type (say) "Request" (Use Content Access to finetune all sorts of permissions about "which role is allowed to create such types, if you have multiple types of requests you just extend to multipple content types).

> Supervisor gets notification, after reviewing the form, he either approves it or denies the request on the same submission.

* Use Rules to "send an eMail" (= action) to Supervisor "After content of type 'Request' gets saved" (= event). In the (custom) eMail for the Supervisor, include all relevant details about the Request (depending on the various data that are available to Rules). Such as an URL of the node to be reviewed.
* To ensure the Requestor cannot change the Request data (while the review process is outstanding), add an extra action to the same (previous) rule which updates the access to the Request (to make it read only for requestor).
* Use the Flag module to enable the Supervisor(s) to mark the submitted node with flags such as 'Approved (by Supervisor)' or 'Denied (by Supervisor)' (Rejected?). The permission to set these flags is limited to the Supervisor(s).

> If approved, an email notification gets sent out to Director who in turn approves or disapproves the same submission.

* Use Rules (again) so that "After a node gets flagged with 'Approved (by Supervisor)'" (= event), it causes a similar "send an eMail" (= action) to Director occurs. In the (custom) eMail for the director, include all relevant details about the Request (depending on the various data that are available to Rules). Such as an URL of the node to be reviewed.
* Use the Flag module (again) to enable the Director to mark the submitted node with flags such as 'Approved (by Director)' or 'Denied (by Director)'. The permission to set these flags is limited to the Director.
* Use Rules to "send an eMail" (= action) to Requestor "After content of type 'Request' gets flagged as denied (by Director)" (= event).

> If denied by supervisor, an email gets sent to an employee.

* Use Rules to "send an eMail" (= action) to Requestor "After content of type 'Request' gets flagged as denied (by Supervisor)" (= event).

So just some basic Rules/Flag stuff ...

It does not (yet) include anything related to Node Convert. This module only needs to be added to this mix if you want to further refine the above with specific "fields" that some of the workflow participants should not "see" at all (to avoid you'd have to use "field permissions", which could be an alternative to that.

# Possible enhancements

### Form Block

I've no experience with the Form Block module. On its project page it says "*Enables the presentation of user registration, site wide contact, or node creation forms in blocks. This is particularly useful for including forms on panels.*".

It seems to me that if you're using panels then there is possibly some added value when using this module. However, I'm not convinced (yet) about the real added value of this module.

There are a few things though that seem to me like red flags (= I'd think twice before using that module):

* *Where is the documentation?*

Apart from what it says on the project page, there is (a) no community documentation (at least not linked to from the project page) and (b) some readme.txt that comes with the module, but with no "real" content.
* *Where is the D7 release?*

There is an official release available for D6. But a D7 version only exists as an alfa 1, which is over a year old. And then there is a D8 version in beta status already. Not sure what is coming up in D8, but for D7 there seems to be something wrong. Is there ever going to be an official D7 version? Nevertheless, looking at the usage statistics there are a lot of D7 sites. Close to 10K reported installs for that D7-alfa version. 10K sites using the modules can't be wrong, so it must be something like "that alfa1 version is virtually an official release that should be tagged like so".

The above bullets are just a few of the criteria I often use for deciding on contributed modules. Refer to the community documentation about Maintenance Scorecards for more details. Here is the intro about it (from that page):

> ... contains a list of 23 criteria (=28-5) that might help to evaluate the maintenance and support of contributed modules. Below is an attempt to apply those criteria to each of the native charting modules ...

Obvious, these scorecards are related to "charting modules", but the same criteria are applicable, IMHO, wheneven you need to decide between multiple modules.

### Bean

Maybe consider adding another module in the area of Blocks, by looking at the Bean module. Here is a quote about its project page:

> Think of a Bean as a method to provide new types (compared to node this would be a content type) which then provides an add content interface to create as many blocks as you require (see screenshot below). The bean content can then be placed around the site just like any other block.

Combined with the options available for granting the appropriate **Bean permissions**, it should give you a lot of flexibility about how exactly you want to use this (great) module in your specific case. As a bonus, the Bean module also works great in combination with the UUID and UUID Features Integration modules. On top of that, after you become familiar with the Bean module, you might find other cases in your site where you also want to use the Bean module (which somehow compensates the fact that you need to add another module).

# Video tutorials

If you're not familiar with Rules, checkout the video tutorials Learn the Rules framework. And/or the similar set of 8 video tutorials about the Flag module.

The video tutorial Drupal Bean module tutorial - using Bean Admin UI provides a great introduction to really understand the power of the Bean module, and the kind of things you can do with it (by only using site building techniques, no custom coding involved). It also shows how the Bean module transforms Drupal blocks into fieldable entities.

# Answer
> 0 votes
You can also use the webform workflow module to design your workflows or the approval process.

https://www.drupal.org/project/webform\_workflows\_element

---
Tags: 7, webforms, entities, workflows, forms
---


title: how to create modal on load of page

text:
# Question
Title: how to create modal on load of page
The following code is not working for me.

```
$config = \Drupal::config('popup.settings');
$title = $this->t('Fnord');
$content = [
  '#type' => 'processed_text',
  '#text' => $config->get('content')['value'],
  '#format' => $config->get('content')['format'],
 ];
$options = [
  'dialogClass' => 'popup-dialog-class',
  'width' => '75%',
];

$response = new AjaxResponse();
$response->addCommand(new OpenModalDialogCommand($title, $content, $options));

return $response;

```

```
(function ($, Drupal, drupalSettings) {
  $(document).ready(function () {
    var ajaxSettings = {
     url: '/popup/modal'
    };
    var myAjaxObject = Drupal.ajax(ajaxSettings);
    myAjaxObject.execute();
  });
})(jQuery, Drupal, drupalSettings);

```

I wrote the code basing on How do I display a modal dialog on page load, with working title and dialog options, using OpenModalDialogCommand?

It is giving me this JSON response but not opening the modal. Could you please help me with this?

```
[{"command":"openDialog","selector":"#drupal-modal","settings":null,"data":"","dialogOptions":{"dialogClass":"popup-dialog-class","width":"75%","modal":true,"title":"Fnord"}}]

```

# Answer
Let's say you have a custom module

1. In the module you have controller that provides a main page page where you want to show the modal popup (on page load).
2. You also need to add a custom library (in a \<my\_module\>.libraries.yml) file. That library will point to a jasvascript file where you have javascript code mentioned above:

```
my_modal:
  js:
    js/script.js: {}
  dependencies:
    - core/drupalSettings

```
3. To load the javascript code on the main page, you need to attach the library in the controller build array:

```
$build['#attached']['library'][] = 'my_module/my_modal';

```
4. The content of the modal will provided by the ajax callback with the php code snipet showed above. Here, as I said in my previous comment, you need to attach the core dialog ajax library:

```
$attachments['library'][] = 'core/drupal.dialog.ajax';
$response->setAttachments($attachments);

```

Or, you can add it as a dependency in the custom library.

5. Last: you need to provide the routing with the paths to main page and to ajax callback.
> 0 votes

---
Tags: 10
---


# Question
Title: What does 'op' exactly mean in an submit button?
In the form\_state\['values'\]\['op'\] = t('Save');

What does 'op' exactly mean and do ?

# Answer
I'm pretty sure it's shorthand for the word `operation`.

It doesn't 'do' anything as such, it's the name assigned to all submit inputs so that the form processor can identify which button has been pressed (and perhaps fire off different submit handlers based on that).

And it looks like it's gone in Drupal 8 (in favour of `$form_state['triggering_element']`), so enjoy it while you can :)
> 4 votes

# Answer
As of 2024, the best way to access the 'operation' in a form is to use `$form_state->getFormObject()->getOperation()`.

See https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21EntityForm.php/function/EntityForm%3A%3AgetOperation/10
> 1 votes

---
Tags: 7, forms
---


# Question
Title: How to use ConfirmFormBase to confirm the action of another form?
I have a form that allows users to upload a CSV file. Upon submit, I would like to show a confirmation form ("Are you sure you want to import this file?").

However, the Drupal 8 examples for ConfirmFormBase seem to only focus on building the confirmation form without actually showing how one gets to that form from another form.

Can someone provide an example of a regular form that then works in conjunction with a confirmation form?

# Answer
> 4 votes
As you've already discovered, `ConfirmFormBase` just provides some buttons, handling for messages, and submit/cancel logic. On its own, it's not designed to be used as the sort of multistep form you're looking for.

Getting that functionality is pretty easy though; you could use the logic in `Drupal\config\Form\ConfigSingleImportForm` as a rough example. Basically you just handle the multistep part of it yourself. For example:

```
protected $step = 1;

public function buildForm(array $form, FormStateInterface $form_state) {
  // When this is the confirmation step fall through to the confirmation form.
  if ($this->step === 2) {
    return parent::buildForm($form, $form_state);
  }

  // Build and return your upload form as normal, but don't call the parent method.
}

public function validateForm(array &$form, FormStateInterface $form_state) {
  // The confirmation step needs no additional validation.
  if ($this->step === 2) {
    return;
  }

  // Perform any additional validation
}

public function submitForm(array &$form, FormStateInterface $form_state) {
  if ($this->step === 1) {
    $form_state->setRebuild();
    $this->step = 2;
    return;
  }

  // Extract rows from CSV and set batch to import here
}

```

# Answer
> 0 votes
I've come to the disappointing conclusion that it's simply not feasible to add confirmation to an existing form that does not already extend `ConfirmFormBase`. The accepted answer is only useful as an example if you are creating a completely new form. In order to add this to an existing form you would first need to override the existing form class with a new class that you control. This is possible. However, in order to avoid duplicating all of the code from the existing class, you would want to extend that class and only modify the necessary methods while calling the parent methods inside your modified methods when possible. But then of course your new class does not extend `ConfirmFormBase`, so you are right back where you started.

If you would like to see this issue resolved in core, please see this core issue: https://www.drupal.org/project/drupal/issues/2883755

---
Tags: 8, forms
---


# Question
Title: how to prevent Quoted Orders from appear under both Quoted and Completed sections for the Orders page; Completed Orders also appear under both
Orders are showing up in both COMPLETED and QUOTED..

my screenshot here shows only Quoted Order, yet they appear for BOTH:

(please note the first screenshot does not feature any fully completed orders, but does exact same thing by appear in both sections)

This screenshot shows the filter options for each (which I suspect is what needs changing, but might be wrong) with Quoted on left and Completed on the right:

Do I just need to update the filters on the Commerce Orders Dashboard? or should I be looking elsewhere?

ALSO, for the Completed tab I tried to change `Order: State (= Quoted)` to `Order: State (= Paid)`

...I though I would see the option 'Completed' but there is none:

and that made a bit of a difference, but still not as expected because now both Quoted and Completed appear under the QUOTED section

# Answer
> 0 votes
Adding a couple new filters did the trick for me!

Go to the Commerce Orders Dashboard (Order) page:

`Home > Administration > Structure > Views > Commerce orders dashboard > Edit`

Choose the **Quoted** tab under the **Displays** label:

Then scroll down to **Filter criteria** label and choose the **Add** button:

A dialog modal window will popup to be able to **Add filter criteria**. Use the **Search* field to easily find the `Order type` option, then checkmark the **Order type** and click the **Apply (this display)** button:

..in the next dialog, just select `Quoted` under **Order types** and then click the **Apply (this display)** button:

Lastly, remember to save your change with the blue Save button (at the bottom) because you will notice at the top the asterisk\* symbol after the **Quoted*** tab text, and above that you get the message "*You have unsaved changes.*"

AFTERWARDS, for repeat the same steps but under the **Completed** tab:

...and when you are filtering for the **Order types** for Completed you will want to select the `Order` checkbox, and the rest would be the same steps:

After all that, the quoted orders appeared **only** under the QUOTED section, and the completed orders appeared **only** under the COMPLETED section!

---
Tags: views, commerce, 10
---


# Question
Title: Cloning nodes between multisite instances with separate databases
I'm working on a Drupal 8.6 multisite and my client wants to be able to duplicate content between many of these sites. The codebase of the websites is the same, but each of them uses a different database. For that, I made a module with some hooks that gets the node serialized and posts it to another site. code below:

```
function a8_replicate_node_insert(Node $node) {
  $type = $node->getType();

  if($type == 'event')
  {
    $portals = $node->get('field_duplicate_to_portal')->getValue();
    $serializer = \Drupal::service('serializer');
    $data = $serializer->serialize($node, 'json', ['plugin_id' => 'entity']);
    $data = json_decode($data); // make it array again

    foreach ($portals as $url) {
      try{
        a8_replicate_post_to_other_site($url['value'], $data, 'insert');
      }
      catch (\Exception $e){
        die($e->getMessage());
      }
    }

  }
}

```

The `a8_replicate_post_to_other_site` function just uses `curl` to post the data as json to the other website. Then I made another module to receive the content, with the following function:

```
  public function receive() {
    $ip = \Drupal::request()->getClientIp();
    // to make sure it's the machine posting to itself
    if($ip == '127.0.0.1') {
      $data = json_decode(file_get_contents('php://input'), true);

      if($data['action']=='insert') {

        $node = Node::create($data['node']);
        $node->save();// <<<< error happens here
      }
    }
  }

```

It's my first Drupal development and I'm I don't know pretty well what I'm doing.

Is this the right way to create a node? Is there a better and safer way to do it?

# Answer
> 1 votes
I wouldn't use the approach you used. Here is my recommendation.

First, define your database connection to each multisite on the default setting.php with different name, check the example below:

```
#Default site

$databases['default']['default'] = array (
  'database' => 'dbname',
  'username' => 'uname',
  'password' => 'password',
  'prefix' => '',
  'host' => 'host',
  'port' => '3306',
  'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
  'driver' => 'mysql',
);

#Site 1
$databases['site1']['default'] = array (
  'database' => 'dbname',
  'username' => 'uname',
  'password' => 'password',
  'prefix' => '',
  'host' => 'host',
  'port' => '3306',
  'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
  'driver' => 'mysql',
);

#Site 2
$databases['site2']['default'] = array (
  'database' => 'dbname',
  'username' => 'uname',
  'password' => 'password',
  'prefix' => '',
  'host' => 'host',
  'port' => '3306',
  'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
  'driver' => 'mysql',
);

```

Now, on hook\_node\_insert you can save the node object to multiple databases by changing the database each time, refer the code below as an example:

```
// Switch to site 1 database
\Drupal\Core\Database\Database::setActiveConnection('site1');
//$node will have all fields you need
$site1Node = Node::create([
    'type'        => $node->getType();,
    'title'       => $node->getTitle();,
    'body_text'   => $node->get('body')->value,
]);
$site1Node->save();
// Switch to site 2 database
\Drupal\Core\Database\Database::setActiveConnection('site2');

$site2Node = Node::create([
    'type'        => $node->getType();,
    'title'       => $node->getTitle();,
    'body_text'   => $node->get('body')->value,
]);
$site2Node->save();

// finnally make sure to swich back to default datyabase
\Drupal\Core\Database\Database::setActiveConnection();

```

# Answer
> 0 votes
this module does it all! it's awesome!

https://www.drupal.org/project/single\_content\_sync

# Answer
> -1 votes
Don't reinvent the wheel. Have you had a look at either of these modules that are both feature rich and accomplish the objective?

https://www.drupal.org/project/content\_sync

https://www.drupal.org/project/entity\_pilot

---
Tags: 8, nodes
---


# Question
Title: Delete a webform file by clicking on its name
I added a file field to a webform. Everything works fine but I need to style the file submissions differently.

This is what I should get: the file name is displayed with an icon that allows you to delete it directly.

However, on webform, we have a checkbox next to the name of the file that must be checked and then click on the submit button so that the file is deleted.

I am able to modify this functionality but I still have a problem. In js, when we click on the file name, I check the checkbox and simulate a \`mousedown on the submit button. It works fine but after the 2nd click. The first does cause an ajax call but the file is not deleted.

Do you have any idea how I could handle this?

```
$('.js-form-managed-file span.file').mousedown(function (event) {
              $(this).closest('label.option').prev('input[type="checkbox"]').prop('checked', true);

              if ($(this).closest('label.option').prev('input[type="checkbox"]').prop('checked')) {
                  $('.js-form-managed-file .js-form-submit').mousedown();
              }

});

```

# Answer
> 0 votes
I managed to create this functionality where the user directly clicks on the name of the file he sent to delete it. With css, I was able to add a delete icon next to the file name.

The error comes from the sumit button selector (the one used to delete files after checking the checkboxes) which was targeted. By doing a `console.log(settings)`, I could see in

```
_triggering_element_name

```

that the trigger element was not (`$('.js-form-managed-file .js-form-submit').mousedown();` but `$("button[name='shop_files_remove_button']")`

So here is the working code. You have to adapt with the name of your button :

```
(function ($, Drupal, once) {

  Drupal.behaviors.Contact = {
    attach: function (context, settings) {
      once('contact', '.js-form-managed-file').forEach(function (element) {

        $('.js-form-managed-file span.file').mousedown(function (event) {
          $(this).closest('label.option').prev('input[type="checkbox"]').prop('checked', true);
        });

        var checkboxes = $('.js-form-managed-file .form-checkbox');
        checkboxes.change(function () {
          if (checkboxes.is(':checked')) {
            $("button[name='shop_files_remove_button']").mousedown();
          }
        });
      })
    }
  }

})(jQuery, Drupal, once);

```

---
Tags: webforms
---


# Question
Title: How can I check if the redis module is enabled in settings.php?
I've read How can I check if a module is enabled? and How can we use \Drupal class inside Settings.php and came to the conclusion that I cannot use the module\_handler service in settings.php.

How can I check if a module -- in this case the redis module -- is enabled in settings.php?

**Context**

I am using docker for a Drupal 9.4 installation. When I initialize a new container from the image, and I install Drupal (initialize a new database), the redis module isn't initially activated, although present.

This means I cannot initially specify `$settings['cache']['default'] = 'cache.backend.redis';` in settings.php (it will make the system crash).

I would have like to do

```
$moduleHandler = \Drupal::service('module_handler');
if ($moduleHandler->moduleExists('redis')) {
  $settings['cache']['default'] = 'cache.backend.redis';
}

```

Of course this doesn't work and I understand why. But I would like to avoid going into my container to edit settings.php after activating the redis module every time I start a new container.

# Answer
The answer to this precise question is now part of the Redis module:

```
use Drupal\Core\Installer\InstallerKernel;
if (!InstallerKernel::installationAttempted() && extension_loaded('redis')) {
  $settings['cache']['default'] = 'cache.backend.redis';
  //etc
}

```
> 0 votes

# Answer
For the same reasons mentioned in How can we use \Drupal class inside Settings.php, namely that the Drupal bootstrap process hasn't completed yet, this cannot be done.

see @Jaypan's comment.
> 2 votes

---
Tags: 9, installation
---


# Question
Title: Update the text format and process the body field of existing nodes programmatically
I am trying to resave a number of nodes to apply the text format changes to the body field. I’ve written a drush command to do this. The command does update the text format, but the text in the body field remains unprocessed.

```
public function saveNode() {
    $result = $this->entityTypeManager->getStorage('node');
    foreach ($result as $node) {
        $node->save();
      }
    }
  }

```

If I manually edit and save the node the text in the body field is correctly processed, but not when saved programmatically.

Notes: By "unprocessed" I mean the body field is still showing faulty HTML. If I manually edit and save the node the faulty HTML is fixed.

# Answer
I’m not sure that I understand what you code is doing. Something like this might work.

```
$html_format = 'full_html';
$type = 'some_type';

$nodes = \Drupal::entityTypeManager()
            ->getStorage('node')
            ->loadByProperties([
                'type' => $type,
            ]);

foreach ($nodes as $node) {

    $current_body_data = $node->get('body')->getValue();
    $current_body_value = $current_body_data[0]['value'];

    $new_body_data = [
            'value' => $current_body_value,
            'format' => $html_format,
        ];

    $node->set('body', $new_body_data);

    $node->save();

}

```
> 1 votes

# Answer
Above solutions give errors in drupal 9 / 10, If you want to achieve this then do the following in a update hook.

```
$node->set('body', $node->get('body')->value); // Set the new value or existing value here.
$node->body->format = 'full_html'; // Set your format here.
$node->save(); // Save the node.

```
> 0 votes

---
Tags: nodes, hooks, drush
---


# Question
Title: How to get entity object in buildform formbase?
I've the following in my routing.yml

```
project.product_elasticsearch:
  path: '/product/{commerce_product}/elasticsearch'
  defaults:
    _form: '\Drupal\mymodule\Form\ProductElasticsearchForm'
    _title: 'Elasticsearch'
  requirements:
    _permission: 'access content'
  options:
    _admin_route: TRUE

```

and in class `ProductElasticsearchForm` `buildForm()` looks as below,

```
public function buildForm(array $form, FormStateInterface $form_state, ProductInterface $commerce_product = NULL) {
}

```

`$commerce_product` is received as a string instead of an object.

I get the following PHP error.

TypeError: Drupal\mymodule\Form\ProductElasticsearchForm::buildForm(): Argument #3 ($commerce\_product) must be of type ?Drupal\commerce\_product\Entity\ProductInterface, string given in Drupal\mymodule\Form\ProductElasticsearchForm-\>buildForm() (line 25 of modules/custom/mymodule/src/Form/ProductElasticsearchForm.php).

Would like to know if I'm missing anything. Any help will be appreciated. Thanks!

# Answer
> 1 votes
You need to declare type type of object that `{commerce_product}` is in your routing definition:

```
project.product_elasticsearch:
  path: '/product/{commerce_product}/elasticsearch'
  defaults:
    _form: '\Drupal\mymodule\Form\ProductElasticsearchForm'
    _title: 'Elasticsearch'
  requirements:
    _permission: 'access content'
  options:
    _admin_route: TRUE
    # Add this section to define what type of object {commerce_prodcuct} is:
    parameters:
      commerce_product:
        type: entity:commerce_product

```

You'll need to clear your registry after this.

---
Tags: forms, commerce, 9
---


# Question
Title: How to add extra arguments/parameters to ajax callback function?
I am using drupal 7. I need to add extra arguments to my ajax callback function. I have five fieldsets in my form, and in all of them same there are same elements, just their IDs are different. For example; `group-1`, `group-2`... I need to include that `1,2,3..` part in the callback function as arguments. Otherwise, it will be too complicated to build a logic to predict which fieldset element need to change.

For example, is such a an implementation possible?

```
function mymodule_form($form, $form_state) {
    $group = 1;

    // Some form elements...

    $form['button'] = array(
        '#type' => 'button',
        '#ajax' => array(
            'callback' => 'my_callback_function',
            'wrapper' => 'my_target_area',
            'arguments' => array($group1, ...) // Is such a thing possible or another way?
        ),
    );
}

function my_callback_function($args, $form, $form_state) {
    $arguments = $args;
    return $form['my_target_area'];
}

```

This question as already been asked here. But I those answers don't fit my needs mentioned above. Thank you.

# Answer
> 9 votes
Drupal doesn't have a mechanism to do that, no. As mentioned in the post you linked to, there's no need to pass that data to Javascript and then back to PHP though - just keep it in the form:

```
function mymodule_form($form, $form_state) {
  $form['button'] = array(
    '#type' => 'button',
    '#ajax' => array(
      'callback' => 'my_callback_function',
      'wrapper' => 'my_target_area',
    ),
  );

  $form['#foo'] = array($group1, ...);

}

function my_callback_function($form, $form_state) {
  $arguments = $form['#foo'];
  return $form['my_target_area'];
}

```

# Answer
> 6 votes
For pass data to ajaxCallback, this is how i do :

```
$form['repas'][$i]['detail']['midi']['mois_midi_'.$i] = [
              '#type' => 'select',
              '#title' => $this->t('Mois'),
              '#data'=>$i,
              '#options'=>array(
                  ''=>'Tous',
                  '01'=>'Janvier',
                  '02'=>'Février',
                  '03'=>'Mars',
                  '04'=>'Avril',
                  '05'=>'Mai',
                  '06'=>'Juin',
                  '07'=>'Juillet',
                  '08'=>'Aout',
                  '09'=>'Septembre',
                  '10'=>'Octobre',
                  '11'=>'Novembre',
                  '12'=>'Décembre',

              ),
              '#ajax'   => [
                  'event' => 'change',
                  'effet'=>'fade',
                  'wrapper'=>'legumes'.$i,
                  'method'=>'replace',
                  'callback' => array($this,'changeLegumeCallback'),
              ],
              '#default_value'=>$mois,
          ];

```

> i add a custom attributes to my array : #data

Then in the callback :

```
public function changeLegumeCallback(array &$form, FormStateInterface $form_state){
    echo $form_state->getTriggeringElement()['#data'];
    exit();

}

```

You ll get your dynamic value from the trigger Element.

# Answer
> 0 votes
```
$form["action"] = [
  "#type" => "button",
  "#value" => "Retirer",
  "#arg" => $id,
  "#attributes" => ["class" => ["button button--small"]],
  "#ajax" => [
    "callback" => [$this,"remove_chiffre_item"],
    "event" => "click",
    "progress" => [],
  ],
];

public function remove_chiffre_item(array &$form, FormStateInterface $form_state) {

  $element= $form_state->getTriggeringElement()["#arg"]; // $form["action"]["#arg"]
  drupal_set_message($element);
  return $form;
}

```

# Answer
> 0 votes
an other solution (I had to loop on inputs) :

```
$form['propositions_fieldset'][$i]['add_date_de_pose'] = array(
'#type' => 'submit',
'#value' => t('Ajouter une date de pose'),
'#submit' => array('ajax_date_de_pose_add_more_add_one'),
'#name'=>$i,
  '#ajax' => array(
      'callback' => 'ajax_date_de_pose_add_more_callback',
      'wrapper' => 'date-de-pose-fieldset-wrapper-'.$i,
     ),
);

```

You just have to add your argument with #name =\> $i

And get it back into the callback function with :

```
$i = $form_state['clicked_button']['#name'];

function ajax_date_de_pose_add_more_callback($form, $form_state) {
    $i = $form_state['clicked_button']['#name'];
    return $form['propositions_fieldset'][$i]['dates_de_pose_fieldset'];
}

```

I hope it helps

# Answer
> 0 votes
Put this code into a trait

```
/**
   * Callback to trigger evolution of the number of text field.
   */
  public function addMoreCallback(array &$form, FormStateInterface $form_state) {
    $triggerElement = $form_state->getTriggeringElement();
    if (empty($targetElement = $triggerElement['#ajax']['target'])) {
      return [];
    }
    return $form[$targetElement] ?? [];
  }

  /**
   * Submit handler for the "add-one-more" button.
   *
   * Increments the max counter and causes a rebuild.
   */
  public function addOne(array &$form, FormStateInterface $form_state): void {
    $triggerElement = $form_state->getTriggeringElement();
    if (empty($counter = $triggerElement['#ajax']['counter'])) {
      return;
    }
    $counterValue = $form_state->get($counter);
    $form_state->set($counter, $counterValue + 1);
    $form_state->setRebuild();
  }

  /**
   * Submit handler for the "remove one" button.
   *
   * Decrements the max counter and causes a form rebuild.
   */
  public function removeCallback(array &$form, FormStateInterface $form_state): void {
    $triggerElement = $form_state->getTriggeringElement();
    if (empty($counter = $triggerElement['#ajax']['counter'])) {
      return;
    }
    $counterValue = $form_state->get($counter);
    if ($counterValue > 1) {
      $form_state->set($counter, $counterValue - 1);
    }
    $form_state->setRebuild();
  }

  /**
   * Add textfield with add more button.
   *
   * @param array $form
   *   Form concern.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   Form state object.
   * @param string $field
   *   Field name.
   */
  protected function addMultipleTextField(array &$form, FormStateInterface &$form_state, string $field): void {
    $counter = $field . '_count';
    $counterValue = $form_state->get($counter) ?? 1;
    $form_state->set($counter, $counterValue);

    $wrapperId = $field . '_wrapper';
    $fieldset = $field . '_fieldset';
    $form['#tree'] = TRUE;
    $form[$fieldset] = [
      '#type' => 'fieldset',
      '#title' => $this->t($field . '_label'),
      '#prefix' => '<div id="' . $wrapperId . '">',
      '#suffix' => '</div>',
    ];

    for ($i = 0; $i < $counterValue; $i++) {
      $form[$fieldset][$field][$i] = [
        '#type' => 'textfield',
      ];
    }

    $form[$fieldset]['actions'] = [
      '#type' => 'actions',
    ];
    $form[$fieldset]['actions']['add_name'] = [
      '#type' => 'submit',
      '#value' => $this->t('@add_more_' . $field),
      '#submit' => [[$this, 'addOne']],
      '#ajax' => [
        'callback' => [$this, 'addMoreCallback'],
        'wrapper' => $wrapperId,
        'target' => $fieldset,
        'counter' => $counter,
      ],
    ];
    // If there is more than one name, add the remove button.
    if ($counterValue > 1) {
      $form[$fieldset]['actions']['remove_name'] = [
        '#type' => 'submit',
        '#value' => $this->t('Remove one'),
        '#submit' => [[$this, 'removeCallback']],
        '#ajax' => [
          'callback' => [$this, 'addMoreCallback'],
          'wrapper' => $wrapperId,
          'target' => $fieldset,
          'counter' => $counter,
        ],
      ];
    }
  }

```

Then, in your form, just call that

```
    $this->addMultipleTextField($form, $form_state, 'my_field');

```

And enyoy

---
Tags: 7, forms, ajax
---


# Question
Title: How to programmatically set the front page?
I want to set the Drupal front page programmatically at run time.
How can I do this? Is this possible?

# Answer
You can use `variable_set()` for that.

```
variable_set('site_frontpage', $value);

```
> 22 votes

# Answer
There is a module (Frontpage, which is one I maintained) that allows to set a different front page for anonymous users, and authenticated users. The module allows third-party modules to alter the page to which the users are instead redirected, or to alter the structure array used to render the page. Third-party modules are allowed to change the redirection URL only when the front page for anonymous or authenticated has not been set within the Frontpage module, or when there has been an error during the node loading; the module will probably be changed in the future to allow third-party modules to redirect the users to a specific page they select.

As alternative, you can create a custom module that, using code similar to the one used by Frontpage, redirect users to a specific page, basing on specific criteria.

The module should implement hook\_menu() and associate a menu callback to, for example, http://example.com/frontpage; the page callback of that menu item should simply verify a condition is verified, and then redirect the users to a specific URL.

The code skeleton could be something similar to the following:

```
/**
 * Implements hook_menu().
 */
function mymodule_menu() {
  $items = array();

  $items['frontpage'] = array(
    'page callback' => 'mymodule_frontpage_view',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );

  return $items;
}

function mymodule_frontpage_view() {
  // These variables can be useful to redirect the users
  // to specific pages, basing on the language currently set for
  // the content, or on the fact the user is logged in.
  $langcode = $GLOBALS['language_content']->language;
  $logged_in = user_is_logged_in();

  if ($condition) {
    drupal_goto($redirect);
  }
}

```
> 16 votes

# Answer
A very flexible solution would be to use Panels. Different displays can be presented based on any criteria, and is controlled through a UI.
> 9 votes

# Answer
I fixed like this:

```
/**
 * Implements hook_page_alter.
 * @param type $page
 */
function my_module_page_alter(&$page){
  global $user;

  $is_fron_page = drupal_is_front_page();
  if($is_fron_page && $user->uid){
    $redirect = 'users/'.$user->name.'/my-argument';
    drupal_goto($redirect);
  }
}

```
> 4 votes

# Answer
The Rules module can be used also to set the frontpage "programatically".

The actually rule to be created, would be a variation of the answer at Redirect logged in (authenticated) users to a different frontpage using rules or php code.

And optionally combining it with the Flag module, might possibly further enhance the user experience. Eg for logged in users to indicate which of the possible frontpages they consider as their "favorite".
> 1 votes

# Answer
If you're looking to programmatically set this globally, you can set the variable in settings.php:

```
$conf['site_frontpage'] = 'some/custom/path';

```
> 1 votes

# Answer
I've altered the homepage node (on subdomains) by settings the variable in a hook\_url\_outbound\_alter()

```
variable_set('site_frontpage', $value);

```

And then redirecting:

```
$_GET['q'] = 'node/' . $domain_homepage_node_id;

```
> 0 votes

# Answer
From command line or in a shell's script, you can use this Drush command

```
drush variable-set site_frontpage /path/to/page

```

or, using alias *vset*

```
drush vset site_frontpage /path/to/page

```

This should comply with Drupal 7 (then Drush 8), as per question.

If you use more a modern system instead, e.g. Drupal 10/Drush 12, then try

```
drush config:set system.site page.front /path/to/page

```

or, using alias *cset*

```
drush cset system.site page.front /path/to/page

```

or alias *config-set*

```
drush config-set system.site page.front /path/to/page

```
> 0 votes

---
Tags: 7, nodes
---