# Question
Title: Why is composer refusing update 9.4.3 -> 9.4.5
I am trying to update drupal core from 9.4.3 to 9.4.5. Can anyone see why it is not working?

I executed `composer update -W drupal/core*` and it updated `drupal/core-composer-scaffold` and `drupal/core-project-message`, but not `drupal/core` nor `drupal/core-recommended`. So next I tried `composer update -W drupal/core drupal/core-recommended`, but no luck.

Running `composer why-not -t drupal/core 9.4.5` gives:

```
drupal/core 9.4.3 Drupal is an open source content management platform powering millions of websites and applications.
└──drupal/core-recommended 9.4.3 (requires drupal/core 9.4.3)
   └──drupal/recommended-project (requires drupal/core-recommended ^9)

```

Running `composer why-not -t drupal/core-recommended 9.4.5` gives:

```
drupal/core-recommended 9.4.3 Core and its dependencies with known-compatible minor versions. Require this project INSTEAD OF drupal/core.
├──drupal/core-recommended 9.4.5 (requires drupal/core 9.4.5) (circular dependency aborted here)
└──drupal/recommended-project (does not require drupal/core but 9.4.3 is installed) (circular dependency aborted here)

```

Running `composer update -W drupal/core-recommended:9.4.5 drupal/core:9.4.5` is give a little better error message:

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

  Problem 1
    - drupal/ckeditor_liststyle is locked to version 1.4.0 and an update of this package was not requested.
    - drupal/ckeditor_liststyle 1.4.0 requires drupal/ckeditor * -> could not be found in any version, there may be a typo in the package name.
  Problem 2
    - drupal/media[dev-1.x, 1.x-dev] require drupal/core ^8 -> found drupal/core[8.0.0-beta6, ..., 8.9.x-dev] but it conflicts with your root composer.json require (^9, 9.4.5).
    - drupal/photo_albums 1.0.2 requires drupal/media * -> satisfiable by drupal/media[dev-1.x, 1.x-dev (alias of dev-1.x)].
    - drupal/media 1.x-dev is an alias of drupal/media dev-1.x and thus requires it to be installed too.
    - drupal/photo_albums is locked to version 1.0.2 and an update of this package was not requested.
[...]

```

Both `drupal/ckeditor_liststyle` and `drupal/photo_albums` are installed, but neither required drupal core 8.x.

composer.json for drupal/ckeditor\_liststyle

```
{
  "name": "drupal/ckeditor_liststyle",
  "type": "drupal-module",
  "description": "This plugin adds numbered list and ordered list properties dialogs (available in context menu) in CKEditor.",
  "keywords": ["Drupal", "ckeditor", "list", "list-style"],
  "license": "GPL-2.0-or-later",
  "homepage": "http://drupal.org/project/ckeditor_liststyle",
  "authors": [
    {
      "name": "Osman Gormus",
      "homepage": "https://www.drupal.org/u/osman",
      "role": "Maintainer"
    }
  ],
  "support": {
    "issues": "https://www.drupal.org/project/issues/ckeditor_liststyle",
    "source": "https://git.drupalcode.org/project/ckeditor_liststyle"
  },
  "require": {
    "drupal/ckeditor": "^8||^9"
  }
}

```

composer.json for drupal/photo\_albums

```
{
  "name": "drupal/photo_albums",
  "type": "drupal-module",
  "description": "Provide photo album content.",
  "homepage": "https://www.drupal.org/project/photo_albums",
  "support": {
    "issues": "https://www.drupal.org/project/issues/photo_albums",
    "source": "https://www.drupal.org/project/photo_albums"
  },
  "authors": [
    {
      "name" : "Scot Hubbard",
      "homepage" : "https://www.drupal.org/u/scothubbard"
    }
  ]
}

```

The info.yml file for ckeditor\_liststyle do require drupal:ckeditor as a dependency, but this should be pointing to the core version and not be a problem:

```
dependencies:
  - drupal:ckeditor

```

Likewise photo\_albums requires media but does not specify a version:

```
dependencies:
  - menu_ui
  - crop
  - node
  - user
  - media
  - pathauto
  - field_group
  - media_library
  - focal_point
  - colorbox

```

No where in my project's composer.json does it require media nor ckeditor. They do show up in the composer.lock file as `"drupal/ckeditor": "*"`, and `"drupal/media": "*"`.

composer.json for project

```
{
    "name": "drupal/recommended-project",
    "description": "Project template for Drupal 8 projects with a relocated document root",
    "type": "project",
    "license": "GPL-2.0-or-later",
    "homepage": "https://www.drupal.org/project/drupal",
    "support": {
        "docs": "https://www.drupal.org/docs/user_guide/en/index.html",
        "chat": "https://www.drupal.org/node/314178"
    },
    "repositories": [
        {
            "type": "vcs",
            "url": "https://{HIDDEN}/twig_hash.git"
        },
        {
            "type": "vcs",
            "url": "https://{HIDDEN}/views_sort_expression.git"
        },
        {
            "type": "vcs",
            "url": "https://{HIDDEN}/twig-twig.git"
        },
        {
            "type": "vcs",
            "url": "https://{HIDDEN}/statistics_counter.git"
        },
        {
            "type": "vcs",
            "url": "https://{HIDDEN}/no_admin_destination_redirect.git"
        },
        {
            "type": "vcs",
            "url": "https://{HIDDEN}/menu_token.git"
        },
        {
            "type": "vcs",
            "url": "https://{HIDDEN}/ckeditor_tableselection.git"
        },
        {
            "type": "vcs",
            "url": "https://{HIDDEN}/ckeditor_find.git"
        },
        {
            "type": "composer",
            "url": "https://packages.drupal.org/8"
        },
        {
            "type": "package",
            "package": {
                "name": "ckeditor/tabletoolstoolbar",
                "version": "0.0.1",
                "type": "drupal-library",
                "dist": {
                    "url": "https://download.ckeditor.com/tabletoolstoolbar/releases/tabletoolstoolbar_0.0.1.zip",
                    "type": "zip"
                }
            }
        },
        {
            "type": "package",
            "package": {
                "name": "ckeditor/tableselection",
                "version": "4.7.2",
                "type": "drupal-library",
                "dist": {
                    "url": "https://download.ckeditor.com/tableselection/releases/tableselection_4.7.2.zip",
                    "type": "zip"
                }
            }
        },
        {
            "type": "package",
            "package": {
              "name": "ckeditor/find",
              "version": "4.14.1",
              "type": "drupal-library",
              "extra": {
                "installer-name": "ckeditor/plugins/find"
              },
              "dist": {
                "url": "https://download.ckeditor.com/find/releases/find_4.14.1.zip",
                "type": "zip"
              }
            }
        },
        {
            "type": "package",
            "package": {
              "name": "ckeditor/liststyle",
              "version": "4.8.0",
              "type": "drupal-library",
              "extra": {
                "installer-name": "ckeditor/plugins/liststyle"
              },
              "dist": {
                "url": "https://download.ckeditor.com/liststyle/releases/liststyle_4.8.0.zip",
                "type": "zip"
              }
            }
        },
        {
            "type": "package",
            "package": {
              "name": "ckeditor/resize",
              "version": "4.13.0",
              "type": "drupal-library",
              "dist": {
                "url": "https://download.ckeditor.com/resize/releases/resize_4.13.0.zip",
                "type": "zip"
              }
            }
        },
        {
            "type": "package",
            "package": {
              "name": "w8tcha/ckeditor-wordcount-plugin",
              "version": "1.17.8",
              "type": "drupal-library",
              "dist": {
                "url": "https://github.com/w8tcha/CKEditor-WordCount-Plugin/releases/download/v1.17.8/CKEditor-WordCount-Plugin.zip",
                "type": "zip"
              }
            }
        },
        {
            "type": "package",
            "package": {
              "name": "jackmoore/colorbox",
              "version": "dev-master",
              "type": "drupal-library",
              "dist": {
                "url": "https://github.com/jackmoore/colorbox/archive/refs/heads/master.zip",
                "type": "zip",
                "reference": "master"
              }
            }
        },
        {
            "type": "package",
            "package": {
              "name": "ckeditor/a11ychecker",
              "version": "1.1.1",
              "type": "drupal-library",
              "dist": {
                "url": "https://download.ckeditor.com/a11ychecker/releases/a11ychecker_1.1.1.zip",
                "type": "zip"
              }
            }
        },
        {
            "type": "package",
            "package": {
              "name": "ckeditor/balloonpanel",
              "version": "4.16.0",
              "type": "drupal-library",
              "dist": {
                "url": "https://download.ckeditor.com/balloonpanel/releases/balloonpanel_4.16.0.zip",
                "type": "zip"
              }
            }
        },
        {
            "type": "package",
            "package": {
              "name": "enyo/dropzone",
              "version": "5.9.2",
              "type": "drupal-library",
              "dist": {
                "url": "https://github.com/dropzone/dropzone/releases/download/v5.9.2/dist.zip",
                "type": "zip"
              }
            }
        },
        {
            "type": "package",
            "package": {
              "name": "moxiecode/plupload",
              "version": "3.1.3",
              "type": "drupal-library",
              "dist": {
                "url": "https://github.com/moxiecode/plupload/archive/refs/tags/v3.1.3.zip",
                "type": "zip"
              }
            }
        },
        {
            "type": "vcs",
            "url": "https://{HIDDEN}/archivesspace_tokens.git"
        },
        {
            "type": "vcs",
            "url": "https://{HIDDEN}/msul_staff_db_connector.git"
        },
        {
            "type": "vcs",
            "url": "https://{HIDDEN}/sliker_drupal_backend.git"
        },
        {
            "type": "vcs",
            "url": "https://{HIDDEN}/msul_bootstrap4.git"
        }
    ],
    "require": {
        "php": ">=7.0",
        "ckeditor/a11ychecker": "^1.1",
        "ckeditor/balloonpanel": "^4.16",
        "ckeditor/find": "^4.14.1",
        "ckeditor/liststyle": "^4.8.0",
        "ckeditor/resize": "^4.13",
        "ckeditor/tableselection": "^4.7",
        "ckeditor/tabletoolstoolbar": "^0.0.1",
        "composer/installers": "^1.9",
        "cweagans/composer-patches": "^1.7",
        "drupal/acl": "^1.0@beta",
        "drupal/address": "^1.7",
        "drupal/admin_toolbar": "^3.0",
        "drupal/advanced_help": "^1.0",
        "drupal/advanced_help_hint": "^1.0@RC",
        "drupal/anonymous_login": "^2.0",
        "drupal/book_blocks": "^1.6",
        "drupal/bootstrap4": "^2.1",
        "drupal/bootstrap_layouts": "^5.2",
        "drupal/ckeditor_a11ychecker": "^2",
        "drupal/ckeditor_balloonpanel": "^2",
        "drupal/ckeditor_blockquote_attribution": "^1.0@beta",
        "drupal/ckeditor_config": "^3.0",
        "drupal/ckeditor_find": "dev-8.x-1.x",
        "drupal/ckeditor_font": "^1.1",
        "drupal/ckeditor_liststyle": "^1.4",
        "drupal/ckeditor_resize": "^1.3",
        "drupal/ckeditor_specialchars": "^1.2",
        "drupal/ckeditor_tableselection": "dev-8.x-1.x",
        "drupal/ckeditor_tabletoolstoolbar": "^2.0",
        "drupal/ckwordcount": "^2",
        "drupal/clientside_validation": "^3",
        "drupal/colorbox": "^1.6",
        "drupal/colorbox_load": "^1.2",
        "drupal/conditional_fields": "^4.0@alpha",
        "drupal/config_single_export": "^1.3",
        "drupal/console": "^1.9",
        "drupal/content_access": "^1.0@alpha",
        "drupal/core-composer-scaffold": "^9",
        "drupal/core-project-message": "^9",
        "drupal/core-recommended": "^9",
        "drupal/create_menus_permission": "^1.0",
        "drupal/crop": "^2.1",
        "drupal/devel": "^4.0",
        "drupal/devel_entity_updates": "^3.0",
        "drupal/diff": "^1.0",
        "drupal/ds": "^3.9",
        "drupal/entity_clone": "^1.0@beta",
        "drupal/entity_embed": "^1.1",
        "drupal/entity_usage": "^2.0@beta",
        "drupal/environment_indicator": "^4.0",
        "drupal/field_group": "^3.1",
        "drupal/field_permissions": "^1.1",
        "drupal/field_validation": "^1.0@beta",
        "drupal/flood_control": "^2.1",
        "drupal/focal_point": "^1.5",
        "drupal/honeypot": "^2.0",
        "drupal/imce": "^2.2",
        "drupal/inline_responsive_images": "^2.2",
        "drupal/jquery_ui_accordion": "^1",
        "drupal/jquery_ui_datepicker": "^1.1",
        "drupal/kaltura_media": "^1.0",
        "drupal/ldap": "^4.0",
        "drupal/login_destination": "^2",
        "drupal/markup": "^1.0@beta",
        "drupal/masquerade": "^2.0@beta",
        "drupal/media_bulk_upload": "^1.0@alpha",
        "drupal/media_name": "^1.0@alpha",
        "drupal/menu_link_attributes": "^1.2",
        "drupal/menu_token": "dev-8.x-1.x",
        "drupal/module_filter": "^3.1",
        "drupal/no_admin_destination_redirect": "dev-8.x-1.x",
        "drupal/noreqnewpass": "^1.2",
        "drupal/oembed_providers": "^2.0",
        "drupal/page_manager": "^4.0@beta",
        "drupal/panels": "^4.6",
        "drupal/pathauto": "^1.6@beta",
        "drupal/photo_albums": "^1.0",
        "drupal/plupload": "^2",
        "drupal/range_slider": "^1.0",
        "drupal/realname": "^2.0@beta",
        "drupal/redirect": "^1.6",
        "drupal/require_revision_log_message": "^1.0",
        "drupal/scanner": "^1.0@RC",
        "drupal/scheduler": "^1.4",
        "drupal/search_api": "^1.19",
        "drupal/search_api_solr": "^4.1",
        "drupal/show_email": "^1.1",
        "drupal/sitemap": "^2",
        "drupal/statistics_counter": "dev-8.x-1.x",
        "drupal/taxonomy_manager": "^2.0",
        "drupal/taxonomy_unique": "^2.3",
        "drupal/term_reference_tree": "^1.1",
        "drupal/twig-twig": "dev-8.x-1.x",
        "drupal/twig_hash": "dev-8.x-1.x",
        "drupal/twig_tools": "^1.3",
        "drupal/twig_tweak": "^3.1",
        "drupal/typed_data": "^1.0@alpha",
        "drupal/ultimate_cron": "^2.0@alpha",
        "drupal/unused_modules": "^1.2",
        "drupal/user_expire": "^1.0",
        "drupal/viewfield": "^3.0@beta",
        "drupal/views_bulk_edit": "^2.5",
        "drupal/views_bulk_operations": "^4",
        "drupal/views_data_export": "^1.0",
        "drupal/views_field_view": "^1.0@beta",
        "drupal/views_sort_expression": "dev-8.x-1.x",
        "drupal/webform": "^6.1",
        "drupal/webform_access": "^6.0",
        "drupal/webform_handler_compare_fields": "^2.0",
        "drupal/webform_views": "^5.0@alpha",
        "drupal/workbench": "^1.1",
        "drupal/workbench_access": "^1.0@beta",
        "drupal/workbench_menu_access": "^1.3",
        "drush/drush": "^11",
        "enyo/dropzone": "^5",
        "jackmoore/colorbox": "dev-master",
        "moxiecode/plupload": "^3.1",
        "vlucas/phpdotenv": "^2.4",
        "w8tcha/ckeditor-wordcount-plugin": "^1.17",
        "web-services/archivesspace_tokens": "dev-master",
        "web-services/msul_bootstrap4": "dev-master",
        "web-services/msul_staff_db_connector": "dev-master",
        "web-services/sliker_drupal_backend": "dev-master",
        "webflo/drupal-finder": "^1.0.0",
        "webmozart/path-util": "^2.3",
        "wikimedia/composer-merge-plugin": "^2",
        "zaporylie/composer-drupal-optimizations": "^1.0"
    },
    "require-dev": {
        "drupal/config_delete": "^1.17",
        "drupal/core-dev": "^9",
        "kint-php/kint": "^3.3",
        "symfony/debug": "^4"
    },
    "conflict": {
        "drupal/drupal": "*"
    },
    "minimum-stability": "dev",
    "prefer-stable": true,
    "config": {
        "sort-packages": true,
        "allow-plugins": {
            "composer/installers": true,
            "cweagans/composer-patches": true,
            "drupal/core-composer-scaffold": true,
            "drupal/core-project-message": true
        }
    },
    "extra": {
        "drupal-scaffold": {
            "locations": {
                "web-root": "web/"
            }
        },
        "composer-exit-on-patch-failure": false,
        "patchLevel": {
            "drupal/core": "-p2"
        },
        "installer-paths": {
            "web/core": [
                "type:drupal-core"
            ],
            "web/libraries/{$name}/dist": [
                "enyo/dropzone"
            ],
            "web/libraries/{$name}": [
                "type:drupal-library"
            ],
            "web/modules/contrib/{$name}": [
                "type:drupal-module"
            ],
            "web/profiles/contrib/{$name}": [
                "type:drupal-profile"
            ],
            "web/themes/contrib/{$name}": [
                "type:drupal-theme"
            ],
            "drush/Commands/contrib/{$name}": [
                "type:drupal-drush"
            ],
            "web/modules/custom/{$name}": [
                "type:drupal-custom-module"
            ],
            "web/themes/custom/{$name}": [
                "type:drupal-custom-theme"
            ]
        },
        "merge-plugin": {
            "include": [
                "web/modules/contrib/webform/composer.libraries.json"
            ]
        },
        "patches": {
            "drupal/realname": {
              "Add Twig Support to Realname (https://www.drupal.org/project/realname/issues/3207285)":"https://www.drupal.org/files/issues/2022-03-09/supportTwig-3207285-10.patch"
            }
        }
    }
}

```

I have even tried removing the composer.lock file, clearing the composer cache, removing all directories (except web/sites/\*) and doing a fresh composer install, but to no avail.

update: Per suggestions from @GregW, I removed drupal/core from composer.json but it still has the exact same issue.

# Answer
> 2 votes
I finally found the reason after trying the same steps as you did!

I had installed the module "media\_embed\_extra" in version 1.0.0-beta1.

It caused the issue. Uninstalling and removing it from composer solved the issue.

If it's not exactly that module blocking you, see if

```
composer prohibits drupal/core-recommended 9.4.5

```

returns a module in the list of issues and try to uninstall and remove that. My assumption is, that there's an issue with the deprecated `core:` key in that module info file:

```
name: 'Media Embed Extra'
type: module
description: 'Provides extra override options to the embedded media.'
core: 8.x
core_version_requirement: ^8 || ^9
package: 'Media'
dependencies:
  - media

```

Edit: Indeed `drupal/photo_albums` has the same issue:

> core: 8.x

So I'm quite sure that's the reason! I created an issue for you and contacted the maintainer.

The original cause for this might be: https://www.drupal.org/project/drupal/issues/3292380

---
Tags: 9, updating, composer
---


title: Should I ignore the "vendor" directory?

text:
# Question
Title: Should I ignore the "vendor" directory?
I'm finally trying to get on board with using Drupal 8 and want to know if I should ignore the vendor directory in git with .gitignore. I would assume not, but I've seen a few example .gitignore files that do.

Thanks!

# Answer
There isn't really a right or wrong answer, it will come down to your situation. Composer themselves have pretty strong feelings that yes, you should ignore it, and in an ideal world that's definitely what you should do, but it's not always that straightforward.

It depends mainly on your hosting and deployment process. If, for example, you're on Pantheon, and don't wish to implement a complicated upstream workflow, involving a dialogue with their technical staff, you're *forced* to commit all dependencies to the repo. There's no Composer sitting at the other end waiting to run when your code is pushed, if you don't push those files up yourself, they're not there, and things will break.

Similarly if you're on shared hosting and don't have a CI workflow, you're probably going to be forced into lugging all the files around with you in one way or another.

Basically if it fits into your development/deployment workflow to be able to exclude those files, it's a good idea to (for the reasons listed in the link above).

If it doesn't, committing them to the repo is absolutely fine.
> 26 votes

# Answer
Drupal core and modules update so frequently, often with security fixes, that it has become common to use composer to manage dependencies. Composer will place dependencies in the vendor directory, and then you don't need to commit them to your codebase. You can run "composer install" on the server and it will download the dependencies on that server. This allows your code repository to be lighter. If you are using this process, you wouldn't want to commit the files, so yes, you'd want to gitignore that directory.

If you want to get up and running with using composer to manage a drupal site, you can check out the drupal scaffold project.
> 5 votes

# Answer
I would say yes. The composer.json file has version requirements and composer.lock specifies the exact versions that are installed. Having the composer.lock in version control effectively defines what is in the vendor directory and its history, therefore it is not necessary to have any history of the vendor directory itself.

Secondly, vendor directories tend to be HUGE, many times that of your own project so if you want to keep your repository lean, then don't commit the vendor directory.
> 0 votes

# Answer
Honestly, @Clive had the correct answer. It depends on the environment. I've used Patheon extensively and I've worked with other Kuberneties-based hosting that dont have a CICD. In those cases you must version control your vendor files and frankly, I've never seen a performance drop because of it. It's not what the cool-kids around the water-cooler are doing, but it works just fine.
> 0 votes

---
Tags: 8, version-control
---


# Question
Title: How do I translate a custom string in "Global: Custom text"?
In D9 website I have a view with Global: Custom text field. In that field I have placed a string like this:

```
<span class="general-info">{{ 'General Info:'|t }}</span>

```

I want to add a translation for the string 'General Info' in different languages. So I went to this translation interface (en/admin/config/regional/translate), where I searched for the string but I could not get the result. Is there any way to refresh the list of "source string" ? I have also cleared the cache and run the cron, but after clicking filter, it displayed "No strings available."

Am I missing something ?

# Answer
The straightforward way would be to translate the field in *Configuration Translation*. Create a translation of your View and then drill down to the field and translate the rewrite result text of the custom text field.

But your approach, using the *User Interface Translation* system inside of Twig, should work as well. The trick is, once you have implemented the |t filter in the Twig template, you have to visit the page in one of the installed non-English languages. Then you should be able to find the string in the user interface translation UI.
> 3 votes

---
Tags: 8, views, i18n-l10n
---


title: Custom alias for home page

text:
# Question
Title: Custom alias for home page
I have a bilingual website (French and English)

I have set "/fr/nom-de-page" as the home page in /fr/admin/config/system/site-information

But Drupal 9.4.5 insist on redirecting to /fr/

I want to keep "/fr/nom-de-page" as the URL of my home page.

Any clue? Thanks!

# Answer
No, Drupal 9.4.5 doesn't redirect when you set a node as front page. You can visit both, '/' and '/path/to/node' as front page. This is often considered as a bad SEO practice, so many people install the module Redirect which prevents that by redirecting URLs to their canonical counterpart. If you have installed this module go to the modules setting and disable *Enforce clean and canonical URLs* if you don't want that.
> 0 votes

---
Tags: path-aliases, redirection
---


# Question
Title: How do I update the sort field value dynamicaly?
I have a requirement to list events via view, but these events are categorized as past, ongoing and upcoming events. View results should show ongoing first, then upcoming and past events in the same view. I have used a computed(integer) field to set the value for each category. While creating a node based on start/end date I am setting this field value to 1(ongoing),2(upcoming), and 3(past). In the views, I am sorting the events based on this numeric field Ascending. It works fine, but issues arise when upcoming events become ongoing, then this computed field value is not updated to show as ongoing. How do I update this field value before this view is rendered?

# Answer
> 0 votes
You cannot (or at least, really should not) force nodes to be re-saved before accessing a view. Such an approach is very inefficient.

The issue here is that you have a computed field that does a calculation based on a date and the current time. If the date is in the future, the field has one value, and if the date is in the past, the field has another value. Using a computed field here is not a good approach because as time passes, you have to re-save the nodes to ensure they have correct values.

One (inefficient) way to update the computed fields would be to batch re-save your nodes in cron.

A similar approach would be to use the Job Scheduler module to set up jobs to periodically resave the nodes.

However, I don't think saving a computed value is the right approach **if it may have to be recomputed when a user accesses a node or a view**. If you need to compute a value on the fly, then don't store it in a computed field.

Instead, do the computing when you process the view. To do this, you can write a custom Views sort plugin. You can move the computed field logic to this plugin and then sort based on the date field (instead of the computed field).

---
Tags: 8, views
---


# Question
Title: Circular reference detected for service "http_kernel" with middleware having the correct number of arguments
I'm pretty confused by this.

I am experimenting with using an HTTP middleware to try to send Google Analytics page views from the server instead of the client.

However, after adding a basic middleware, I'm getting the following error:

> Circular reference detected for service "http\_kernel", path: "jsonapi.file\_upload -\> http\_kernel -\> http\_middleware.negotiation -\> http\_middleware.reverse\_proxy -\> http\_middleware.page\_cache -\> http\_middle ware.kernel\_pre\_handle -\> http\_middleware.session -\> MYMODULE.analytics\_middleware".

In `services.yml`:

```
  MYMODULE.analytics_middleware:
    class: Drupal\MYMODULE\Middleware\MyModuleAnalytics
    arguments: ['@http_kernel', '@path.current', '@logger.factory' ]
    tags:
      - { name: http_middleware, priority: 180, responder: true }

```

`MyModuleAnalytics.php`:

```
class MyModuleAnalytics implements HttpKernelInterface {
  /**
   * The wrapped HTTP kernel.
   *
   * @var \Symfony\Component\HttpKernel\HttpKernelInterface
   */
  protected $httpKernel;

  /**
   * The current path.
   *
   * @var \Drupal\Core\Path\CurrentPathStack
   */
  protected $currentPath;

  /**
   * Logger factory.
   *
   * @var \Drupal\Core\Logger\LoggerChannelInterface
   */
  protected LoggerChannelInterface $loggerFactory;

  /**
   * Creates a HTTP middleware handler.
   *
   * @param \Symfony\Component\HttpKernel\HttpKernelInterface $kernel
   *   The HTTP kernel.
   */
  public function __construct(HttpKernelInterface $kernel, CurrentPathStack $current_path, LoggerChannelFactoryInterface $logger_factory) {
    $this->httpKernel = $kernel;
    $this->currentPath = $current_path;
    $this->loggerFactory = $logger_factory->get(MyModuleLoggerChannel::Analytics->value);
  }

  /**
   * {@inheritdoc}
   */
  public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
    // Custom logic goes here.
    $current_path = $this->currentPath->getPath();
    $this->loggerFactory->info("Current path: $current_path");

    return $this->httpKernel->handle($request, $type, $catch);
  }

```

I can remove the circular error by removing the `@http_kernel` argument in `services.yml`:

```
  MYMODULE.analytics_middleware:
    class: Drupal\MYMODULE\Middleware\MyModuleAnalytics
    arguments: ['@path.current', '@logger.factory' ]
    tags:
      - { name: http_middleware, priority: 180, responder: true }

```

However, PHPStorm complains because the number of arguments are wrong. (But the site loads and the logger functions correctly, so maybe it's ok...?) The documentation states:

> The Drupal\Core\DependencyInjection\Compiler\StackedKernelPass compiler pass implicitly prepends the inner kernel as first constructor argument of the middleware. Thus, you do not need to define arguments in your modules \*.services.yml definition for the middleware unless your middleware depends on another registered service.

Does this mean I should never pass `@http_kernel` to the middleware? Because the wording suggests to me that "you do not need to include the argument if it is the only argument, but if there are multiple arguments, you should include them all."

What is the correct way to pass arguments to a middleware service?

# Answer
> 1 votes
The kernels are stacked, so you can't add the kernel as container argument, this results in a circular reference. Only the compiler pass can determine the inner kernel, which the outer middleware has to invoke if it doesn't return a response.

The IDE looks at the static container, it can't tell what compiler passes are adding to the service arguments, unless there is an IDE plugin adding a little extra intelligence.

---
Tags: 9, http-request
---


# Question
Title: How can I override a module with a custom module?
The Bee module has a `addReservationFrom` form class. I would like to override it.

I copied `addReservationFrom` to `MyaddReservationForm` and made the necessary modifications. This works as excepted, but `MyaddReservationForm` is in the original module and I would like to move the changes into a custom module.

I created a custom module, but when I move `MyaddReservationForm` to *custom/my\_reservations/src/Form*, `MyReservationsForm` is not used.

### info.yml

```
name: my reservations
type: module
description: overrides bee reservation
package: custom
core_version_requirement: ^9 || ^10
dependencies:
  - bee
  - bat:bat_booking
  - bat:bat_event_series
  - bat:bat_event_ui
  - office_hours:office_hours

```

### routing.yml

```
my_reservations.my_reservations:
  path: '/my-reservations/my-reservations'
  defaults:
    _title: 'My reservations'
    _form: 'Drupal\my_reservations\Form\MyReservationsForm'
    _title_callback: '\Drupal\bee\Controller\MyBeeController::addReservationTitle'
  requirements:
    _permission: 'access content'

```

### reservationsForm

```
namespace Drupal\my_reservations\Form;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\bat_event_series\Entity\EventSeries;
use Drupal\commerce_cart\CartManagerInterface;
use Drupal\commerce_cart\CartProviderInterface;
use Drupal\node\NodeInterface;
use Drupal\node\Entity\Node;
use Drupal\office_hours\OfficeHoursDateHelper;
use RRule\RRule;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a My reservations form.
 */
class MyReservationsForm extends FormBase {
  // ...
}

```

# Answer
In Drupal you have two options that are preferable to copying the file across as you've done. Drupal is OOP, and also contains alter hooks, which are meant to prevent code from being copied.

The common way to do what you are doing is to use hook\_form\_alter(), to massage the form render array and alter it to make the form you want. This is good for minor changes. For major changes, you can extend the form using OOP principles, then `[replace the form used at the route][1]`.
> 1 votes

---
Tags: forms, 9
---


# Question
Title: How do I set a content entity field as required?
How do I make a field in a content entity required in Drupal 8?

I tried `FieldConfigInterface::setRequired()`, but it doesn't seem to work.

# Answer
> 6 votes
You need to use the -\>setRequired(TRUE) option on the field. IE

```
$fields['source_id'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Thingy'))
      ->setDescription(t('Pick a thingy'))
      ->setSetting('target_type','thingy')
      ->setRequired(TRUE)
      ->setDisplayOptions('form', array(
        'type' => 'options_select',
        'weight' => 5,
      ))
      ->setDisplayConfigurable('form', TRUE);

```

You will also need to uninstall and then reinstall your module to have the change reflected in the database before this will work.

# Answer
> 3 votes
If you have an existing field that is optional and you want to make it required, use code similar to the following, in example.install. (Replace *example* with the machine name of the module you created.)

```
/**
 * Make the "bar" field required.
 */
function example_update_8101(&$sandbox) {
  $entityType = 'foo';
  $fieldName = 'bar';
  $manager = \Drupal::entityDefinitionUpdateManager();
  $field = $manager->getFieldStorageDefinition($fieldName, $entityType);
  $field->setRequired(TRUE);
  $manager->updateFieldStorageDefinition($field);
}

```

This allows you to update a base field definition in Drupal 8. In this example, it's for a custom content entity type **foo** and its **bar** field.

Don't forget to update your `baseFieldDefinitions()` implementation in your entity.

# Answer
> 0 votes
If you need to change it programmatically then use:

```
/**
 * Implements hook_entity_base_field_info_alter().
 */
function mymodule_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type) {
  if ($entity_type->id() === 'entitytype_id' && isset($fields['body'])) {
    $fields['body']->setRequired(TRUE);
  }
}

```

---
Tags: 8, entities
---


# Question
Title: There is a new meta tag setting to trim content from a token. How do I use it?
This and this site says there is a new Meta tag trim feature, but I have looked everywhere where I can think of and can't see an example of how to use it.

Does anyone know how to use that?

# Answer
The trimming options are at `/admin/config/search/metatag/settings` and appear as described in the documents you linked in the question. Set a value for the abstract, description, or title tag, then save the configuration.
> 3 votes

---
Tags: 9, meta-tags
---


# Question
Title: How iterate through output from "views-view-field.html.twig" file?
I have views with 6 results. However they are displayed in 2 different ways. 2 first in one way, 2 next in second way, next in first way and last in second way. I know it is quite confusing. I have found this solution: How to handle field with multiple items but I tried this without success. My output from this field is something like:

```
<a href="/dignissimos-velit" hreflang="en">Dignissimos velit</a>

```

I want to achieve something like that:

```

{% set items = output %}

{% for item in items %}
  {% if loop.index <=2 %}
<h3 class="heading3 mb-xs">
    {{ item }}
</h3>
  {% endif %}
{% endfor %}

{% for item in items %}
  {% if loop.index >= 3 and loop.index <= 4 %}
<h4 class="heading4 mb-xs">
    {{ item }}
</h4>
  {% endif %}
{% endfor %}

{% for item in items %}
  {% if loop.index >= 5 and loop.index <= 6 %}
<h3 class="heading3 mb-xs">
    {{ item }}
</h3>
  {% endif %}
{% endfor %}

```

Maybe someone could help me with this? I would be very grateful. How iterate through output field?

# Answer
> 0 votes
Ok, I found answer.

In the file views-view-fields.html.twig:

```
{% if row.index <= 1 %}
{{ fields.field_news_image.content }}
<div class="card__teaser">
  {{ fields.title.content }}
  {{ fields.field_news_category.content }}
  {{ fields.created.content }}
</div>
{% endif %}

{% if row.index >= 2 and row.index <= 3 %}
{{ fields.field_news_image.content }}
<div class="smallnews__teaser">
  {{ fields.title.content }}
  {{ fields.field_news_category.content }}
  {{ fields.created.content }}
</div>
{% endif %}

```

and in the file (one of the files) views-view-field.html.twig:

```
{% if row.index <= 1 %}
<h3 class="heading3 mb-xs">
{{ output -}}
</h3>
{% endif %}

{% if row.index >= 2 and row.index <=3 %}
<h4 class="heading4 mb-xs">
{{ output -}}
</h4>
{% endif %}

```

Overall, row.index was the solution.

---
Tags: views, theming
---


# Question
Title: 'setAttribute' causing UncaughtDOMException with jQuery Dialog?
I'm using jQuery and the jQuery UI Dialog module (here) to check for new nodes of type "call" every few seconds. Forgive me if I don't know too much about it as I just started using it. Here's what I put in my themes js file:

```
jQuery(document).ready(function(){
    setInterval(function(){
        updateModals();
    }, 3000)
});

function updateModals(){
    jQuery.get("/newcalls", function(calls){
     if(calls.length){
        if(jQuery('#dialog').length == 0){
            jQuery('.featured-top').append(jQuery("<div id='dialog'></div>"));
        }
        var call_markup = "<div><h1>New call incoming!</h1><br>Caller:" + calls[0].title + "<br>Phone Number:" + calls[0].field_phonenumber + "<br>Extension:" + calls[0].field_extension_number + "</div>";
        var client_markup = "";
        jQuery.get('/allclients/' + calls[0].field_client_name, function(clients){
            if(clients.length == 1){
                client_markup = "One client found: <br>" + "Name: " + calls[0].field_client_name + "<br><a href='/node/" + clients[0].nid + "'>" + "Click here </a> to view the client page.<br><a href='/clients'> Click here </a> to search for clients.<br>";
            }else if(clients.length > 1){
                client_markup = "List of found clients:<br>";
                clients.forEach(element => {
                    client_markup += "Client Phone Number:" + element.field_phone_number + "<br>Client Email:" + element.field_email + "<a href='/node/" + element.nid + "'>Client Page</a><br>";
                });
                client_markup = "Multiple clients found in database.<a href='/client_registration'> Click here </a> to register a new client.<br><a href='/clients'> Click here </a> to search for clients.<br>";

            }else{
                client_markup = "No clients found in database.<a href='/client_registration'> Click here </a> to register a new client.<br><a href='/clients'> Click here </a> to search for clients.<br>";
            }
            jQuery('#dialog').html(call_markup + client_markup);
            jQuery('#dialog').dialog({
                buttons : {
                    text: "Close",
                    icon: "ui-icon-close",
                    click: function(){
                        $ (this).dialog("close");
                    }
                }
            }
            );
        })
     }else{
            //jQuery("#modal").html("");
     }
    });
}

```

Running this however, gives me this error:

> jquery.min.js?v=3.6.0:2 Uncaught DOMException: Failed to execute 'setAttribute' on 'Element': '0' is not a valid attribute name. at attr (https://28c99794f6ce13.lhrtunnel.link/core/assets/vendor/jquery/jquery.min.js?v=3.6.0:2:65761) at $ (https://28c99794f6ce13.lhrtunnel.link/core/assets/vendor/jquery/jquery.min.js?v=3.6.0:2:32507) at S.fn.init.attr (https://28c99794f6ce13.lhrtunnel.link/core/assets/vendor/jquery/jquery.min.js?v=3.6.0:2:65332) at new S.fn.init (https://28c99794f6ce13.lhrtunnel.link/core/assets/vendor/jquery/jquery.min.js?v=3.6.0:2:25752) at S (https://28c99794f6ce13.lhrtunnel.link/core/assets/vendor/jquery/jquery.min.js?v=3.6.0:2:1051) at String. (https://28c99794f6ce13.lhrtunnel.link/modules/jquery\_ui\_dialog/jquery.ui/ui/widgets/dialog-min.js?v=1.12.1:4:7059) at Function.each (https://28c99794f6ce13.lhrtunnel.link/core/assets/vendor/jquery/jquery.min.js?v=3.6.0:2:3053) at t...\_createButtons (https://28c99794f6ce13.lhrtunnel.link/modules/jquery\_ui\_dialog/jquery.ui/ui/widgets/dialog-min.js?v=1.12.1:4:6736) at t...\_createButtons (https://28c99794f6ce13.lhrtunnel.link/core/assets/vendor/jquery.ui/ui/widget-min.js?v=1.13.1:9:1127) at t...\_createButtonPane (https://28c99794f6ce13.lhrtunnel.link/modules/jquery\_ui\_dialog/jquery.ui/ui/widgets/dialog-min.js?v=1.12.1:4:6488)

I figure it's being caused by me attempting to target a non-existent div ("#dialog") with jQuery.dialog(), but could someone please give me a second opinion on what the source of the issue is and how I could start fixing it?

# Answer
Clive was correct, wrapping the buttons object in an array solved the problem.
> 0 votes

---
Tags: javascript
---


title: Add the user role to the body class

text:
# Question
Title: Add the user role to the body class
How do you add the class of the user role to the body tag, say in `html.html.twig`, in much the same way as you can add the path in How can I dynamically add URL or taxonomy to body class?

# Answer
> 9 votes
Here's how you could achieve that:

```
function YOUR_THEME_preprocess_html(&$variables) {
  // The getRoles() method will return us the machine names, so there is no
  // need to process roles names additionally. However, I suggest prefixing
  // the names with "role-", so it's more obvious.
  foreach ($variables['user']->getRoles() as $role) {
    $variables['attributes']['class'][] = 'role-' . $role;
  }
}

```

Then in your `html.html.twig` file you can set the attributes this way:

```
<body{{ attributes }}>

```

Note: you might want to pass `TRUE` to the `getRoles()` method, if you want to exclude the locked roles (anonymous and authenticated). You can read the docs on this method here.

# Answer
> 0 votes
You can use Roles Classes module (works for the admin theme)

---
Tags: theming, 8
---


# Question
Title: How do I AjaxResponse on a custom AJAX call?
I have some custom Javascripts. at the part of code I want to leverage Drupal `AjaxResponse` but looks like it's only working when there Triggered by Drupal Form's ajax element. ( my ajax call verb is POST)

I mean when I can this endpoint from js, I see the response in browser network, but it does not work and not applied.

**Response:**

```
[{"command":"alert","text":"just to test trigger Drupal command"}]

```

**Route:**

```
picture_park.preset_selector:
  path: '/admin/media/picture-park/preset-selector'
  defaults:
    _title: 'Preset Selector Picture Park images as Media'
    _controller: '\Drupal\media_entity_remote_picture_park\Controller\MediaController::presetSelector'
  requirements:
    _permission: 'Remote Image Picture Park: Create new media'

```

**Controller method:**

```
  public function presetSelector(): AjaxResponse
  {
    $response = new AjaxResponse();
    $response->addCommand(new AlertCommand("just to test trigger Drupal command"));

    return $response;
  }

```

**JS:**

```
  function postData(data) {
    return jQuery.post("/admin/media/picture-park/preset-selector?_wrapper_format=drupal_ajax", {
      "data": data,
      "js": true,
      "_drupal_ajax": 1,
    })
  }

```

So, what I'm looking for is, how can I make Drupal AjaxResponse runs and get executed when the endpoint is called from a custom js?

# Answer
> 6 votes
You should be able to trigger the commands with the following:

```
ajaxObject = Drupal.ajax({
  url: "",
  base: false,
  element: false,
  // Use the 'submit' key to pass data.
  // Leave this key out if not passing any data.
  submit: {"data":"test"},
  progress: false
});

// Trigger ajax commands.
ajaxObject.success(data, "success");

```

Add the above to a success callback of your Ajax response. Note you may also need to add the following to process any Ajax inserted content:

```
Drupal.attachBehaviors();

```

---
Tags: ajax, 9
---


# Question
Title: $current_user->id() returns 0 when logged in for HTTP Middleware
I have injected `currentUser` as a dependency into my HTTP middleware, but when I check the UID while logged in, the result is always 0.

I am logged in as an admin and refreshing the watchdog page; the log messages show the UID is always 0. `$this->currentUser->isAnonymous()` also returns `1`, so my code is running at the wrong time. However, I don't know how to adjust the priority to ensure the HTTP middleware runs at a time when I have access to the uid.

(Goal: I am trying to send site analytics server-side, so I want access to the UID and current path-- I've omitted injecting the current path in the example below, but to get the current path, the middleware must have a priority of less than 100.)

```
class Analytics implements HttpKernelInterface {

  /**
   * Current request.
   *
   * @var \Symfony\Component\HttpFoundation\RequestStack
   */
  protected RequestStack $requestStack;

  /**
   * The wrapped HTTP kernel.
   *
   * @var \Symfony\Component\HttpKernel\HttpKernelInterface
   */
  protected HttpKernelInterface $httpKernel;

  /**
   * The current user.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected AccountInterface $currentUser;

  /**
   * Logger factory.
   *
   * @var \Drupal\Core\Logger\LoggerChannelInterface
   */
  protected LoggerChannelInterface $loggerFactory;

  /**
   * Creates a HTTP middleware handler.
   *
   * @param \Symfony\Component\HttpKernel\HttpKernelInterface $kernel
   *   The HTTP kernel.
   */
  public function __construct(HttpKernelInterface $kernel, RequestStack $request_stack, AccountInterface $current_user, LoggerChannelFactoryInterface $logger_factory) {
    $this->httpKernel = $kernel;
    $this->requestStack = $request_stack;
    $this->currentUser = $current_user;
    $this->loggerFactory = $logger_factory->get('analytics');
  }

  /**
   * {@inheritdoc}
   */
  public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE): Response {
    if ($type === self::MASTER_REQUEST) {
      $user_id = $this->currentUser->id();
      $this->loggerFactory->info("Found UID: $user_id");
    }
    return $this->httpKernel->handle($request, $type, $catch);
  }

}

```

**MYMODULE.services.yml**

```
  MYMODULE.analytics_middleware:
    class: Drupal\mymodule\Middleware\Analytics
    # Skip the first argument because it is injected by Drupal.
    arguments: ['@request_stack', '@current_user', '@logger.factory' ]
    tags:
      - { name: http_middleware, priority: 1000 }

```

For the priority, I tried 1000, 100, 0, -100, and -1000, all of which give the same result (UID = 0).

# Answer
> 3 votes
User authentication does not happen in a middleware, it's in the request event subscriber AuthenticationSubscriber with priority 300:

```
  public static function getSubscribedEvents() {
    // The priority for authentication must be higher than the highest event
    // subscriber accessing the current user. Especially it must be higher than
    // LanguageRequestSubscriber as LanguageManager accesses the current user if
    // the language module is enabled.
    $events[KernelEvents::REQUEST][] = ['onKernelRequestAuthenticate', 300];

    ...

    return $events;
  }

```

So you would need to set the response in your own request event subscriber with priority \<300, if you don't want to wait until the Drupal kernel has finished all request event subscribers and hands over control to the controller you have defined in a route which matches the requested path.

---
Tags: 9, http-request
---


title: AjaxCommand to scroll to top of page?

text:
# Question
Title: AjaxCommand to scroll to top of page?
I have created an AJAX form. After the form is submitted, I call `$this->createOkResponse()` which updates a section of the page with a message (it replaces the form with the message).

It all works fine except for one thing: the form is quite long and the submit button is at the bottom. When I replace the page content with my `ReplaceCommand()` the message I am showing comes at the top of the page, but the page doesn't scroll up to the top.

Then I thought I could use a `ScrollTopCommand()` to force the page to scroll up but it doesn't work. I have checked that the `body` does have the scroll bars. I have tried with `body` with `html` and with `html, body` as the selector but nothing works.

I have cleared the caches multiple times and I get no error in the error logs or the Javascript console. Just the page doesn't scroll up and my OK message remains hidden until I manually scroll up.

I see that `ScrollTopCommand` is part of Views. Does that prevent me from using it in a form?

**Edit**: I have also tried setting `views` as a dependency in my module info YAML file but that didn't change anything.

```
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\views\Ajax\ScrollTopCommand;

/**
 * Class InterventionForm.
 */
class InterventionForm extends FormBase
{

  /**
   * Function to update the page with AJAX with an OK
   * @retun $response AjaxResponse
   */
  protected function createOkResponse(): AjaxResponse
  {

    $response = new AjaxResponse();

    $render_array = [
      '#markup' => '<div class="bg-success">OK Response!</div>'
    ];

    // Replace content with our response
    $response->addCommand(new ReplaceCommand('#interventionCreate', $render_array));

    // Scroll to top of page
    $response->addCommand(new ScrollTopCommand('body'));

    return $response;
  }
}

```

# Answer
The module dependency doesn't help because Views is already installed by default, but keep it in case someone tries to uninstall the module. The next step is then to include the modules ajax library in buildForm():

```
$form['#attached']['library'][] = 'views/views.ajax';

```
> 2 votes

---
Tags: 8, forms, ajax
---


# Question
Title: Add markup before form output for user-login
I've created a template override for the user login form (user-login.tpl.php) and want to put some content before the form's elements (e.g, header text and copy). Anything you add in user-login.tpl.php become children elements of `<form>`. I want my supporting content outside the form structure.

Is there a template suggestion to get higher up in the processing chain or is this only possible using a specific preprocess hook in template.php or a module? Any examples would be greatly appreciated.

Thanks - Jeremy

# Answer
Unless someone has a more elegant solution, I used:

```
$form['#prefix'] = '<h1>Header</h1>';

```

What seems odd here is the potential for a lot of markup in your code. I'm relatively new to Drupal, so I'm not sure if this is common practice.
> 0 votes

# Answer
It appears that you can get the array with {{ element }}

https://api.drupal.org/api/drupal/core%21includes%21form.inc/function/template\_preprocess\_form/8.2.x
> 0 votes

---
Tags: forms, theming
---


# Question
Title: How to reference a custom entity using BaseFieldDefinition::create('entity_reference')
Similar to how you can reference a user or a node, I would like to reference a custom entity:

```
      $fields['department_id'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Department'))
      ->setDescription(t('Select the department for this business.'))
      ->setSettings(array(
        'target_type' => 'department_register'
      ))
      ->setSetting('handler', 'default')
      ->setDisplayOptions('view', [
        'label' => 'inline',
        'type' => 'department',
        'weight' => -10,
      ])
      ->setDisplayOptions('form', [
        'type' => 'entity_reference_autocomplete',
        'settings' => [
          'match_operator' => 'CONTAINS',
          'match_limit' => 10,
          'size' => 60,
          'placeholder' => ''
        ],
        'weight' => -10,
      ])
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

```

Where 'target\_type' =\> 'department\_register' is my custom department entity

I'm stuck in how to connect my department entity. How can I setup the right plugin configuration for my autocomplete form field which should be able to select the name of the department?

# Answer
> 2 votes
Not using bundles, entity\_reference\_autocomplete needs to know which field to use for it's autocomplete functionality, so did you add the:

"label" = "name",

to your business\_register(target entity) entity\_keys={...} ? (in the annotations) Where 'name' is the target column. (label = title when using bundles)

---
Tags: entities
---


# Question
Title: How to have some element of a custom form rendered outside <form>
I have a form based on FormBase. All the form is wrapped by form.html.twig. I want to render `$form['charts']` outside the `<form>`. I was able to do this, but {{ children }} still render all the form **with** the charts (charts it's duplicated).

Is their a better approach? I was wondering if I should use a normal controller and embed the form.

All this has to do with #theme\_wrapper (that wrap the all content with the form template, but with everything already pre-render in #childrens). I must admit I don't really understand the #theme\_wrapper logic.

```
<?php

namespace Drupal\mymodule\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a CDR Dashboard form.
 */
class SurveyQuestionsChartsForm extends FormBase {

  private AccountInterface $account;

  /**
   * @param \Drupal\Core\Session\AccountInterface $current_user
   */
  public function __construct(AccountInterface $account) {
    $this->account = $account;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    // Instantiates this form class.
    return new static(
      // Load services required to construct this class.
      $container->get('current_user')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'mymodule_myform';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state, $section = '') {

    $form['charts'] = $charts->build();
    $form['charts']['#weight'] = 50;

    $sections = $analysis->getSections();

    $form['section'] = [
      '#type' => 'select',
      '#title' => $this->t('Thématique'),
      '#options' => $sections,
      '#default_value' => $section,
    ];

    $form['actions'] = [
      '#type' => 'actions',
      '#weight' => 10,
    ];
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Afficher'),
    ];

    return $form;
  }

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

  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $section = $form_state->getValue('section');
    $form_state->setRedirect($this->getRequest()->get('_route'), [
      'section' => $section,
    ]);
  }

}

```

mymodule.module

```
function mymodule_preprocess_form(&$variables) {
  $variables['charts'] = $variables['element']['charts'];
}

```

form.html.twig

```
{{ charts }}

<form{{ attributes.addClass(classes) }}>
  {{ children }}
</form>

```

# Answer
> 2 votes
Yes, a clean solution would be a controller to render the chart. The problem with the `<form>` wrapper template, you are too late in the render pipeline. In a wrapper template the variable `{{ children }}` is already rendered. But still there should be some things you can try from within the form. You could hide the element so that it is not rendered inside the form and then unhide it to render it outside. Or use #prefix/#suffix:

```
$form['#prefix'] = $charts->build();

```

---
Tags: 8, forms
---


title: Node access based on view mode

text:
# Question
Title: Node access based on view mode
I have a content type with multiple fields. Some of the fields are accessible to everyone and there are other fields for which the access is controlled.

Example: There is a content type called Reports with fields like title, summary, body etc. Title and summary should be visible to everyone but the access to body field is controlled.

For implementing this I need to have 2 view modes. one of them will have the fields title and summary displayed with the rest of the fields hidden. The other view mode will have only the body field visible. I am planning to implement the hook hook\_node\_access to derive the access permissions for the node. So how can I get the view mode in this hook so that I can have my access check based on the view mode.

# Answer
> 5 votes
I did some searches but I couldn't find any module that does this. But you can implement the `hook_node_view()` like this:

```
function YOUR_MODULE_node_view($node, $view_mode, $langcode) {
    global $user;
    //verify that the user is not loged and the view mode is full view
    if(in_array('anonymous user', $user->roles) && $view_mode == 'full') {
        //denied the access
        drupal_access_denied();
        //avoids other hooks are invoked
        drupal_exit();
    }
}

```

I tested the above code and it works.

# Answer
> 2 votes
In Drupal 7 you can use Field Permissions module

> The Field Permissions module allows site administrators to set field-level permissions to edit, view and create fields on any entity.

# Answer
> 0 votes
In Drupal 8+

```
/**
 * Implements hook_node_view().
 * @param array $build
 * @param \Drupal\Core\Entity\EntityInterface $entity
 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
 * @param $view_mode
 */
function YOUR_MODULE_node_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
  $displayId = $display->id();
  if ($displayId === 'node.YOUR_NODE_TYPE.default') {
    $roles = \Drupal::currentUser()->getRoles();
    if (!in_array('administrator', $roles, TRUE)) {
      throw new AccessDeniedHttpException();
    }
  }
}

```
* Replace `YOUR_MODULE` with your module machine name.
* Replace `YOUR_NODE_TYPE` with the machine name of the content type.
* Add use statements for the entity and display classes.

---
Tags: 7, users, entities
---


# Question
Title: How do I translate strings in Twig templates?
I am working on a (my first) Drupal 8 project witch is a multilingual site. Drupal 8 really delivers in terms of native support for content translation. However, I have a few strings set directly in some if the theme .Twig template files printed out like ex.

```
{{ 'Read more'|trans }}

```

I am really stuck on where to handle the translation for strings like these within the Drupal 8 installation.

What steps do I need to take to be able to translate these strings into the language currently selected for on the site? Do I need any additional modules other than the ones included in Drupal 8 core?

Steps I have taken:

* Activated the Content Translation & Interface Translation modules
* Added Twig strings like {{ 'Read more'|trans }}
* Flushed cache and refreshed pages containing the strings
* Checked admin/config/regional/translate for the strings with no result

# Answer
> 47 votes
You need to visit the page in a language that is not English to have them picked up. That should be enough.

# Answer
> 38 votes
Use the `t` filter:

```
{{ 'translate me'|t }}

```

https://www.drupal.org/docs/8/theming/twig/filters-modifying-variables-in-twig-templates

# Answer
> 26 votes
You can use the `{% trans %}` Twig tag, available in Drupal 8:

```
{% trans %}
  Submitted by {{ author.username }} on {{ node.created }}
{% endtrans %}

```

See the following link for more details: http://getlevelten.com/blog/mark-carver/drupal-8-twig-templates-and-translations

# Answer
> 14 votes
This question have already been answered correctly, but if you need to translate a string with variables, here's how you do it.

```
{{ 'Subscribe to @title'|t({'@title': title}) }}

```

# Answer
> 6 votes
Here is an example I used where the language is explict provided, as well as an context. Like this I was able to translate the sting 'von' twice, as in english it meant 'of' or 'from'.

`{{ 'von'|t([],{'langcode': currentLanguageCode,'context':'Seitenzahl PDF' }) }}`

# Answer
> 2 votes
You can translate string in twig using the following functions.

You need to visit the page so the string is added in the translation database.

**With filter**

```
{{ 'Hello Earth.'|trans }}
{{ 'Hello Earth.'|t }}

```

**with trans**

```
{% trans %}
  Submitted by {{ author.username }} on {{ node.created }}
{% endtrans %}

```

**with variables**

```
<p class="submitted">{{ "Submitted by !author on @date"|t({ '!author': author, '@date': date }) }}</p>

```

**plural**

```
{% set count = comments|length %}
{% trans %}
  {{ count }} comment was deleted successfully.
{% plural count %}
  {{ count }} comments were deleted successfully.
{% endtrans %}

```

This is also documented here https://www.drupal.org/docs/theming-drupal/twig-in-drupal/filters-modifying-variables-in-twig-templates#trans

# Answer
> 1 votes
It seems that Drupal considers custom translated strings as "User interface translation". To translate those, install "User interface translation" extend and then you find "User interface translation" in the configuration. There is a long list of strings, but your strings should located in the last section.

# Answer
> 1 votes
this one helped me

```
{{ 'Read more'|trans }}

```

after this visit the page in a language that is not Original language to have them picked up by Drupal.

```
/admin/config/regional/translate

```

that s all.

# Answer
> 1 votes
What was suggested in this answer above is what fixed the bug to me. It was impossible to me to find the strings. I went to the last page at

```
/admin/config/regional/translate

```

and there it was, but the search bar couldn't find it.

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


# Question
Title: How can I set "advanced" metatags programmatically?
The Metatag documentation gives an example of setting meta tags like this:

```
$node->set('field_meta_tags', serialize([
  'title' => 'Some title',
  'description' => 'Some description.',
  'keywords' => 'Some,Keywords',
]));

```

However, I want to set the "advanced" fields, such as **Canonical URL**. (Use case: I am trying to import several hundred pages with the Feeds module, some of which are very similar, so I want to automatically set the canonical URL for the similar pages.)

I tried to get the name of the key to serialize by setting the canonical URL for a node in the UI, saving the node, and then dumping the result of `field_metatags`, but this was the result:

```
www.example.com a:4:{s:5:"@type";s:6:"Person";s:3:"@id";s:39:"https://www.example.com/bio";s:4:"name";s:12:"Person's name";s:3:"url";s:39:"https://www.example.com/my-bio";}

```

Here, the first `www.example.com` is the value I set for canonical URL, but this does not give me a hint of what to use to set the canonical URL.

How can I set the values of the "advanced" metatags programmatically?

### Similar questions

This question asks about setting the canonical URL but it is for Drupal 7 and concerns pre-processing the canonical URL before display, whereas I want to set the metatag canonical URL when saving the node in Drupal 9.

# Answer
You might also consider using tokens to fill in the meta tags at runtime instead of saving them directly on each node. This is particularly useful as it allows you adjust the settings to all such nodes at once, instead of having to deal with one-off customizations, so if in six months you decide to change them you just change the global defaults for that content type, instead of having to modify the values saved on each node.
> 2 votes

# Answer
Thanks to @Hudri, I was able to figure this out with `unserialize()`.

## How to set the advanced metatags by key

The advanced metatags are set the same way as the standard metatags, but you need to know the key to set.

In the case of the canonical URL, the key is `canonical_url`, so it can be set like this:

```
  $this->set('field_metatags', serialize([
    'canonical_url' => $canonical_link,
  ]));

```

## How to get the value of the advanced metatags key?

First, in the UI, set the value for a specific entity (for example, at `/node/1/edit`). I needed to set it for an individual entity because the values that are set globally for entities in the admin UI (at `/admin/config/search/metatag`) do not show up when you get the value of the `field_metatags` field for your entity (because Metatag module only stores the overrides for that specific entity in the field).

Then, in your code, you can use Xdebug or something like this:

```
hook_entity_presave() {
  $metatags = $this->get('field_metatags')->value;
  $unserialized = unserialize($metatags);
  print_r($unserialized);
  die();
}

```
> 1 votes

---
Tags: 9, meta-tags
---


# Question
Title: Can't add Google Webmaster metatag because "Site Verification" category is missing
I'm using Drupal 7 (I can't update) and I'm trying to add Google Webmaster verification meta tag using the following instruction: https://www.drupal.org/docs/7/modules/metatag/howto-verify-a-site-on-google-webmaster-tools-d7-d8 There is simply no "Site Verification" category available and in the "System" module has "Metatag: Verification (off)". I have no idea how to enable it.

# Answer
> 1 votes
Step 2 in the guide says to install the "Metatag Verification" submodule, that's included as part of the Metatag module suite so it should already be in your site's directory structure. Given you're new to working with Drupal, the user guide's page on installing modules will help you install the module; you should read through the entire user guide anyway, it'll help you understand how the whole system works.

---
Tags: 7, meta-tags, google-search-console
---


# Question
Title: How to set #default_value for radio button on Ajax Callback?
I've a form having a radios and 'select list with Ajax callback event'. When user changed selection from the select box, I tried to set the default\_value for radio button. but it doesn't work.

In the function `check()` I'm able to get the changed `$id` value every time I change the select list, but the default\_value not working. it's always checked to the value 1 ('Yes').

I've tried like following but not working.

```
/**
 * Form.
 */
function myform($form, &$form_state) {
    $current_items = array(1=> 'Yes',2 => 'No');
    $form['current_items'] = array(
      '#title' => t('Change value'),
      '#type' => 'select',
      '#options' => $current_items,
      '#ajax' => array(
        'callback' => 'ajax_change_callback',
        'wrapper' => 'change-div',
      ),
    );
    $form['change_fieldset'] = array(
      '#prefix' => '<div id="change-div">',
      '#suffix' => '</div>',
      '#type' => 'fieldset',
    );

    if (!empty($form_state['values']['current_items'])) {
      $form['change_fieldset']['checkin_form'] = check($form_state['values']['current_items']);
    }
    else {
      $form['change_fieldset']['checkin_form'] = check(1);
    }

  return $form;
}

/**
 * Form callback.
 */
function ajax_change_callback($form, $form_state) {
  return $form['change_fieldset'];
}

/**
 * Callback Form.
 */
function check($id) {
    $items = array(1 => 'Yes', 2 => 'No');
    $form['checkin'] = array(
      '#title' => $id,
      '#type' => 'radios',
      '#options' => $items,
      '#default_value' => $id,
    );
    return $form;
}

```

# Answer
> 1 votes
Please check if this works

```
$commands[] = ajax_command_invoke('#my-radio-button-id', 'val', array(array('checked' => TRUE)));

```

Ref: http://handsomedogstudio.com/setting-radio-button-values-drupal-ajax-call

# Answer
> 1 votes
This was so frustrating to make work. Hope this helps someone. related Ajax callback change "checked" radio button Can't update radio button fields after ajax callback

```
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\InvokeCommand;
... in the buildForm
$form['container']['steps'] = [
  '#type' => 'radios',
  '#title' => $this->t('Steps'),
  '#default_value' => '0',
  '#options' => $form_state->get('multiform_steps'),
  '#required' => TRUE,
];
...
$form['actions']['gotonext_'.$key] = [
   '#type' => 'button',
   '#value' => $this->t('Next'),
      '#data' => $nextStepKey,
      '#states' => [
        'visible' => [
          ':input[name="steps"]' => ['value' => $key],
        ],
      ],
      '#ajax' => [
        'callback' => '::nextStepCallback',
      ],
    ];
...
public function nextStepCallback(array &$form, FormStateInterface $form_state) {
$next_step = (string) $form_state->getTriggeringElement()['#data'];

$response = new AjaxResponse();
//the jquery command to run jQuery('#steps-wrapper .fieldset-wrapper input[name=steps][value=3]').attr('checked', true)
$selector='#steps-wrapper .fieldset-wrapper input[name=steps][value='.$next_step.']'; // A jQuery selector
$method='click'; // The name of a jQuery method to invoke.
$arguments=[];//['class1','class2']; // (Optional) An array of arguments to pass to the method.
$response->addCommand(new InvokeCommand($selector,$method,$arguments));
return $response;
}

```

# Answer
> -1 votes
For Drupal 8+:

```
$inputs = $form_state->getUserInput();
unset($inputs[$item]);
$form_state->setUserInput($inputs);

```

For Drupal 7:

```
unset($form['input'][$item]);

```

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


title: Migration lookup and no stubbing

text:
# Question
Title: Migration lookup and no stubbing
I have a problem with languages and no stubbing regarding migration\_lookup plugin in Drupal 8. I have a different json data, each of them is different languages for example json with de:

```
data:{ id: 12345, name: 'Product 1 de', id: 123456, name: 'Product 2 de'}

```

json with en

```
data:{ id: 12345, name: 'Product 1 en'}

```

of course this json is only example not validate.

I have a 2 migration yaml file called: migrate\_plus.migration.data.yml and migrate\_plus.migration.data\_de.yml

English version is default version in drupal.

To join de version with en I use process:

```
nid:
    -
      plugin: migration_lookup
      source: id
      migration: data
      no_stub: true
      stub_id: data

```

I use no\_stub: true because i don't want to create stub nodes for not existing en version. Everything works well but only for first time but if I want to update migration I got errors

```
[error]  Drupal\Core\Database\IntegrityConstraintViolationException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '9424706c-e536-4a59-be13-ebb91367a1a9' for key 'node_field__uuid__value': INSERT INTO {node} (vid, type, uuid, langcode) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3); Array

```

for de version item. In my case I don't want to create new stub item for english version for product 2 de so I though that no\_stub option will be ok. Even if I do migrate:rollback I noticed that this migration from de still exist.

# Answer
Make sure that is only in the translation yml, so into **migrate\_plus.migration.data\_de.yml** you should have:

```
nid:
  plugin: migration_lookup
  source: id
  migration: data
  no_stub: true

```

Also into the same translation file (and only in this one) include the langcode from migrate\_plus.migration.data.yml and specify the one for the translation into **process**:

```
langcode:
    plugin: default_value
    default_value: de
content_translation_source: en

```

And finally in the end of the **migrate\_plus.migration.data\_de.yml**:

```
destination:
  plugin: 'entity:node'
  translations: true

migration_dependencies:
  required:
    - data
  optional: { }

```
> 0 votes

---
Tags: 8, migrations
---


# Question
Title: How do I get the route name of a view page?
In my custom submit handler I want to redirect to a view page Url, let's suppose my view name is *team* and its path is `/admin/team`, I find out I should use `$from_state->setRedirect('route');` but when I tried `$from_state->setRedirect('/admin/team');` Faced with error, How can I get view route name in?

# Answer
> 54 votes
The correct routename for view is `view.VIEW_MACHINE_NAME.PAGE_MACHINENAME`(in another word `"view.$view_id.$display_id"` ) . try something like following

```
use \Drupal\Core\Url;

$url = Url::fromRoute('view.VIEW_MACHINE_NAME.PAGE_MACHINENAME');
$form_state->setRedirectUrl($url);

```

and for your case

```
$url = Url::fromRoute('view.team.page_1'); // page_1 is my guess as to your page name, please replace it with your page machine_name

```

Lean more : How to formulate the route name

after @4k4 comment, Also you can redirect form to custom router with URL

just need use `$form_state->setRedirectUrl($url)`

# Answer
> 11 votes
To use views contextual filters as parameters use arg\_X, fe.

```
$url = Url::fromRoute('view.team.page_1', ['arg_0' => 1234]);

```

# Answer
> 4 votes
The Webprofiler of the devel module let's you see the route of any page.

It also displays the number of DB queries made on the page and also the request time in addition to many other features.

It's available for Drupal 7 and 8.

# Answer
> 3 votes
Simple example: It will work :)

```
use Drupal\Core\Url;
$form_state->setRedirect('view.VIEW_MACHINE_NAME.PAGE_MACHINENAME'); // view.my_view.page_1

```

# Answer
> 2 votes
With drush you can check by path for example:

```
drush route | grep '/path/you/are/looking/for'

```

# Answer
> 1 votes
For Drupal 8 you might look into the "router" database table aswell to get your redirect parameters.

Look for the corresponding value in the "name" column and compare against the path column.

```
$form_state->setRedirect('YOUR_DB_VALUE_HERE');

```

# Answer
> 1 votes
With drupal console:

```
drupal router:debug | grep '/admin/team'

```

This returns:

```
 view.team.page_1    /admin/team   // views.team.page_1 is my route

```

Source: https://stefvanlooveren.me/blog/get-route-views-page-drupal-console

# Answer
> 0 votes
An easy trick to show the route name of every page (not just views pages) you can use `hook_menu_local_tasks_alter`:

```
function HOOK_menu_local_tasks_alter(&$data, $route_name)
{
  dump($route_name);
}

```

---
Tags: views, 8, routes
---


# Question
Title: How to catch the payload for bad POST requests with JSON:API?
When POSTing to JSON:API with invalid parameters, `UnprocessableHttpEntityException` is thrown.

However, in the logs, this exception does not contain the invalid payload that caused the exception. How can I log the bad payload with the exception?

In a custom module, I created **MymoduleExceptionSubscriber.php**::

```
namespace Drupal\mymodule\EventSubscriber;

class MymoduleExceptionSubscriber implements EventSubscriberInterface {
  public function onException(ExceptionEvent $event): void {
    $exception = $event
      ->getThrowable();
    $original_exception = $this->getOriginalException($exception);

    if ($original_exception instanceof UnprocessableHttpEntityException) {
        $this->logger->get('custom_jsonapi_exception')->error($this->getErrorMessage($original_exception));
      }
  }

  /**
   * Generate a useful error message to log an exception.
   *
   * @param \Exception $exception
   *   The exception to generate an error message for.
   *
   * @return string
   *   The generated error message.
   */
  private function getErrorMessage(\Exception $exception): string {
    $exception_class = get_class($exception);
    $violations = '';
    if(method_exists($exception, 'getViolations')) {
      $violations = '<br>Violations' . $exception->getViolations();
    }
    return "Class: $exception_class <br>Message: " .
      $exception->getMessage() .
      $violations .
      '<br>File: ' .
      $exception->getFile() .
      '<br>Line number: ' .
      $exception->getLine() .
      '<br>Stack trace:<br>' .
      $exception->getTraceAsString();
  }

  /**
   * Drupal may catch exceptions for us and rethrow them. This undoes that.
   *
   * More info: https://drupal.stackexchange.com/a/311935.
   *
   * @param \Throwable $exception
   *   The exception to drill into.
   *
   * @return \Throwable
   *   The original (first) exception.
   */
  private function getOriginalException(\Throwable $exception): \Throwable {
    $original_exception = $exception;
    while ($original_exception->getPrevious()) {
      $original_exception = $original_exception->getPrevious();
    }
    return $original_exception;
  }

}

```

This allows me to catch the exception and, by logging `getViolations()`, get information on which field(s) caused the `UnprocessableHttpEntityException`. However, knowing which fields failed to validate is only half the problem-- the other half is checking what the bad input in the payload was.

How can I log such info?

# Answer
For the payload get the request referenced in the event:

```
$request = $event->getRequest();
$body = Json::decode($request->getContent());
$data = $body['data'];

```
> 1 votes

---
Tags: 9, log-messages
---


# Question
Title: How do I configure the route to the add form for a content type created as a bundle class?
I've got a custom content entity type, created for me by `drush generate` and I'm extending that using bundle classes. I've got a MyContentType class and a MyBundle class that extends it and provides some additional fields. I'm able create MyBundle entities just fine with `drush php` or in a kernel test, so I know all that works. Now I'm trying to create MyBundle entities in the UI.

The annotation for the MyContentType class provides the path to my form handler

`"add" = "Drupal\my_content_type\Form\MyContentTypeForm"`

and a link to the form to add a new entity

`"add-form" = "/my_content_type/add/{my_content_type}"`.

But when I try going to `/my_content_type/add/my_bundle`, I get a `Page not found`.

Following what I see in node.routing.yml, I've got this in my\_module.routing.yml

```
my_content_type.add:
  path: '/my_content_type/add/{my_content_type}'
  defaults:
    _title: 'Add some content'
    _form: 'Drupal\my_content_type\Form\MyContentTypeForm'
  requirements:
    _permission: 'administer site configuration'
  options:
    parameters:
      my_content_type
        type: entity:my_content_type

```

Once I've created and saved one of my entities using drush php, the edit page loads fine. So, what's the silly little thing I'm missing here?

# Answer
I think you should be using `_entity_form` rather than `_form`. See: https://www.drupal.org/docs/drupal-apis/routing-system/structure-of-routes (search for `_entity_form`).
> 0 votes

---
Tags: entities, forms, routes
---


title: How to get page type in page.html.twig

text:
# Question
Title: How to get page type in page.html.twig
I need to set some classes in page.html.twig based on page type (node, view, etc..) and if possible in more details (node type 'article', view 'blog') etc.

some ideas how to do this? I can't figure out how to get that done in page.html.twig?!

thank you! Matthias

# Answer
> 7 votes
There is no page type in Drupal 8. But you can get the route name in a page preprocess hook to identify the type of page:

**Route name**

```
function mytheme_preprocess_page(&$variables) {
  $variables['route_name'] = \Drupal::routeMatch()->getRouteName();
}

```

and in twig:

```
{{ route_name }}

```

**Route parameters**

For route parameters:

```
 if ($node = \Drupal::routeMatch()->getParameter('node')) {
    $variables['node'] = $node;
  }

```

This code for the node is already in `theme.inc`, so you can use it right away in twig to get the content type:

```
{{ node.gettype }}

```

The node is the only route parameter that is loaded by default. You have to get parameters from other routes in preprocess, for example the view id and display id from a view route:

```
function mytheme_preprocess_page(&$variables) {
  $variables['view_id'] = \Drupal::routeMatch()->getParameter('view_id');
  $variables['display_id'] = \Drupal::routeMatch()->getParameter('display_id');
}

```

# Answer
> 1 votes
Far out

```
$node = \Drupal::routeMatch()->getParameter('node')

```

returns the nid instead of a node object on the node/\[nid\]/revisions/\[vid\]/view pages so if you do anything like

```
$node->bundle()

```

you get a WSOD

# Answer
> 0 votes
Here's what worked for me to get the node type.

In mytheme\_preprocess\_page():

```
  if ($node = \Drupal::routeMatch()->getParameter('node')) {
    $variables['content_type'] = $node->bundle();
  }

```

Then I checked if `content_type` is == to what I want in page.html.twig

# Answer
> 0 votes
Just thought I'd add a follow up since this question was asked a long time ago and Drupal evolves!

If you take a look at `core/modules/system/templates/page.html.twig` in the doc comments under **Page Content** lines ~23 - 28;

```
 * - node: Fully loaded node, if there is an automatically-loaded node
 *   associated with the page and the node ID is the second argument in the
 *   page's path (e.g. node/12345 and node/12345/revisions, but not
 *   comment/reply/12345).

```

So you can actually access the full node without any preprocessing, its right there in the twig template for you. Give it a try with;

```
{{ node.getType() }}

```

---
Tags: theming
---


# Question
Title: How to add the padding and margin options for Gutenberg?
I found the font size and color setting using your\_theme.gutenberg.yml. Is there a similar setting where I can enable the padding and margin options? If not, what's the right way to add the padding option?

# Answer
You can do it by setting `enableCustomSpacing: true` in the custom\_theme.gutenberg.yml.

Make sure to place it under `theme-support`
> 2 votes

---
Tags: 9, wysiwyg
---


title: REST edit multiple relationship field

text:
# Question
Title: REST edit multiple relationship field
I'm developing a telegram bot and I'm using a Drupal site to manage data. From my bot php code I create some nodes like this:

```
$url = "/entity/node?_format=hal_json";
    foreach($obj['pratiche'] as $nid){
        $pratiche[] = ["target_id" => $nid];
    }
    $opt = [
        'headers' => self::getHead(),
        'body' => json_encode([
                'type' => 'presentazione',
                'title' => [ [ 'value' => 'User ' . $obj['telegram_id'] ] ],
                'body' => [ [ 'value' => $obj['body'] ] ],
                'field_anno_di_nascita' => [ [ 'value' => $obj['nascita'] ] ],
                'field_pratiche' => [ [ 'value' =>$pratiche ] ],
          ])
    ];

    $response = $this->client->request('POST', $url , $opt);

```

As you can see field\_pratiche is a node relation field and can have multiple values inserted like an array of \["target\_id" =\> $tid\] arrays. The creation is fine.

When I try to edit this node I do something like:

```
 $url = "/node/".$nid."?_format=hal_json";
    $editObj = [ 'type' => 'presentazione' ];

    if(array_key_exists('nascita', $obj)){
        $editObj['field_anno_di_nascita'] = [ [ 'value' => $obj['nascita'] ] ];
    }

    if(array_key_exists('presentazione', $obj)){
        $editObj['body'] =  [ [ 'value' => $obj['presentazione'] ] ];
    }

    if(array_key_exists('pratiche', $obj)){
        foreach($obj['pratiche'] as $p){
            $pratiche[] = ["target_id" => $p];
        }
        $editObj['field_pratiche'] = [ [ 'value' => $pratiche] ];
    }

    $opt = [
        'headers' => self::getHead(),
        'body' => json_encode($editObj)
    ];
   $response = $this->client->request('PATCH', $url , $opt);

```

Using the same structure edit of 'field\_pratiche' fail. After call edit I can find the new values in the other fields but an empty one for field\_pratiche.

How can I achieve this ? How can I edit this multiple node relationship field ?

# Answer
The keys of arrays are the column names in the database. Most simple fields like integers or numbers just have one `value` column. Entity references don't have a `value`, but a `target_id` column instead.

Your (multi-value)1 `$pratiche` already is an (array of)1 arrays with `target_id` as key, so the `value` key is superfluent and incorrect `[ [ 'value' => $pratiche] ]`.

Instead simply assign `$editObj['field_pratiche'] = $pratiche;` directly
> 0 votes

---
Tags: nodes
---


# Question
Title: Error: Class 'Drupal\mysql\Driver\Database\mysql\Connection' after Drupa 9.3.14 to 9.4.1 update
I tried to update from Drupal 9.3.14 to Drupal 9.4.1.

Everything went well in our test environment.

I put the production site in maintenance mode, updated all the files for the update, successfully ran the database updates using drush, followed by cache:rebuild.

Went back to the website, refreshed it and got a white page ... checked the error log and saw this :

\[13-Jul-2022 14:30:50 America/New\_York\] Error: Class 'Drupal\mysql\Driver\Database\mysql\Connection' not found in C:\inetpub\wwwroot\enharmonie\_d9\web\core\lib\Drupal\Core\Database\Database.php on line 411 #0 C:\inetpub\wwwroot\enharmonie\_d9\web\core\lib\Drupal\Core\Database\Database.php(189): Drupal\Core\Database\Database::openConnection() #1 \[internal function\]: Drupal\Core\Database\Database::getConnection() #2 C:\inetpub\wwwroot\enharmonie\_d9\web\core\lib\Drupal\Component\DependencyInjection\PhpArrayContainer.php(79): call\_user\_func\_array() #3 C:\inetpub\wwwroot\enharmonie\_d9\web\core\lib\Drupal\Component\DependencyInjection\Container.php(176): Drupal\Component\DependencyInjection\PhpArrayContainer-\>createService() #4 C:\inetpub\wwwroot\enharmonie\_d9\web\core\lib\Drupal\Component\DependencyInjection\PhpArrayContainer.php(212): Drupal\Component\DependencyInjection\Container-\>get() #5 C:\inetpub\wwwroot\enharmonie\_d9\web\core\lib\Drupal\Component\DependencyInjection\PhpArrayContainer.php(62): Drupal\Component\DependencyInjection\PhpArrayContainer-\>resolveServicesAndParameters() #6 C:\inetpub\wwwroot\enharmonie\_d9\web\core\lib\Drupal\Component\DependencyInjection\Container.php(176): Drupal\Component\DependencyInjection\PhpArrayContainer-\>createService() #7 C:\inetpub\wwwroot\enharmonie\_d9\web\core\lib\Drupal\Core\DrupalKernel.php(550): Drupal\Component\DependencyInjection\Container-\>get() #8 C:\inetpub\wwwroot\enharmonie\_d9\web\core\lib\Drupal\Core\DrupalKernel.php(895): Drupal\Core\DrupalKernel-\>getCachedContainerDefinition() #9 C:\inetpub\wwwroot\enharmonie\_d9\web\core\lib\Drupal\Core\DrupalKernel.php(472): Drupal\Core\DrupalKernel-\>initializeContainer() #10 C:\inetpub\wwwroot\enharmonie\_d9\web\core\lib\Drupal\Core\DrupalKernel.php(708): Drupal\Core\DrupalKernel-\>boot() #11 C:\inetpub\wwwroot\enharmonie\_d9\web\index.php(19): Drupal\Core\DrupalKernel-\>handle() #12 {main}

I tried restarting the web server, the database, flushing the cache again ... nothing worked ... had to go back to the snapshot we took before I tried to run the update.

Any idea how to get rid of this error mesage? Thanks!

# Answer
We just tracked this on our Jenkins to the need to have the `$app_root` variable defined to the correct path in the `settings.php` file. This was due to that variable being added for the autoload of the driver. In my case, for Jenkins, it was different my other environments and that path wasn't account for and resulted in that exact error.

Here is the Drupal commit that made this change. Thanks to my friend John Doyle at Digital Polygon for helping me track this issue down.
> 2 votes

---
Tags: 9, updating
---


# Question
Title: Formatting options in Webform Select field
I have a select field on a webform with 20+ options for the user. The default is a long list of options to select from. Is there any way to have the options broken into columns for the user to choose from so the webform doesn't end up an excessively long form?

# Answer
> 0 votes
Take a look at Chosen module can help you.

# Answer
> 0 votes
Use CSS. Flex or grid

```
#edit-field-event-sponsors-und {
   display: flex;
   flex-wrap: wrap;
   justify-content: space-between;

```

With grid

```
.view-events.view-webinars-archive .view-content {
  display: grid;
  grid-template-columns: 1fr;
  grid-column-gap: 2rem;
  grid-row-gap: 3rem;

```

---
Tags: webforms
---


# Question
Title: Use Node title as default value in alt field of field_image migration
I've been working on a D7 to D9 migrate project, and encountered an issue around field\_image's alt value.

The D7 site does not have all the field\_image alt tag setup, so we'd like to use the imported Node Title as a replacement if the value was empty.

The migration config file works, all other fields are imported perfectly. Just the alt tag didn't make use of the replacement of '@title' instead (I saw other usage of @ syntax on other migration config files). All I saw was @title string within my alt field after importation.

How should I make it work? Thanks!

`config/sync/migrate_plus.migration.upgrade_d7_node_complete_article.yml`

```
langcode: en
status: true
dependencies: {  }
id: upgrade_d7_node_complete_article
class: Drupal\node\Plugin\migrate\D7NodeTranslation
field_plugin_method: null
cck_plugin_method: null
migration_tags:
  - 'Drupal 7'
  - Content
migration_group: migrate_drupal_7
label: 'Node complete (News)'
source:
  plugin: d7_node_complete
  node_type: article
process:
  nid:
    -
      plugin: get
      source: tnid
  vid:
    -
      plugin: get
      source: vid
  langcode:
    -
      plugin: default_value
      source: language
      default_value: und
  title:
    -
      plugin: get
      source: title
  field_image:
    -
      plugin: sub_process
      source: field_image
      process:
        target_id: fid
        alt:
          plugin: default_value
          source: alt
          default_value: '@title'
        title: title
        width: width
        height: height
destination:
  plugin: 'entity_complete:node'
  translations: true
  default_bundle: article
migration_dependencies:
  required:
    - upgrade_d7_user
    - upgrade_d7_node_type
    - upgrade_language
  optional:
    - upgrade_d7_field_instance

```

# Answer
> 1 votes
The `@` notation can only be used where property names are entered.

Since it is not possible to write property names in default\_value plugin's `default_value`, other methods must be considered.

For example, the following code should work as expected.

```
process:
  field_image:
    -
      plugin: sub_process
      source: field_image
      include_source: true
      process:
        _alt_or_title:
          # Extract non-empty values from alt and title
          - plugin: callback
            callable: array_filter
            source:
              - alt
              - source/title
          # Retrieve only the first value from the previous array
          - plugin: callback
            callable: array_shift

        target_id: fid
        alt: '@_alt_or_title'  # `@` can be used here (shorthand for `plugin: get`)
        title: title
        width: width
        height: height

```

---
Tags: 7, migrations, 9
---


# Question
Title: How do I manage select options on a multi page webform?
With a multi-page webform.

* Page 1: choose country
* Page 2: choose industry
* Page 3: choose from a list of people that support the country and industry.

#validate handler added

```
$form['#validate'][] = '_cbifasttrackcert_webform_validate_sector_country';

```

Adding the items to the list

```
$verifier_options = $verifierHandler->formatVerifierOptions($verifiers);
$form['#node']->webform['components'][$choose_verifiers_cid]['extra']['items'] =
$verifier_options;
$form_state['webform']['component_tree']['children'][$choose_verifiers_cid]['extra']['items'] = $verifier_options;

```

Which is fine throughout the form, the values don't get submitted as there is a check that values are in the node list.

The challenge is to email a response based on the values, as only *$submission* is available.

When submitting a webform, node\_load is used to load a clean version of the options in *select.inc*. These options are used to check whether any valid options have been submitted, with options not in the list being discarded.

*web/sites/all/modules/contrib/webform/components/select.inc* has \_webform\_select\_options() with options to provide a callback or filter based on a token.

I'm not sure how to access form\_state using either of these options? Similarly hook node\_load does have form\_state to allow the options to be discovered?

How to filter a select list based on a token? How to load the form data in a callback?

# Answer
> 0 votes
The secret is to load all the possible responses in hook\_node\_load. They are a valid choice, even if submitted. I made it an associative array so that the indexes don't change.

```
function module_node_load($nodes, $types) {
  $choose_verifier_no_verifier_cid = webform_get_cid($webform_node, MyModuleHelper::CHOOSE_VERIFIER_NO_VERIFIER_WEBFORM_KEY)[0];

  $verifier_options = MyModuleHelper::chooseAllVerifiers();
  $webform_node->webform['components'][$choose_verifiers_cid]['extra']['items'] = $verifier_options;
  $webform_node->webform['components'][$choose_verifier_no_verifier_cid]['extra']['items'] = $verifier_options;

```

---
Tags: 7, webforms
---


# Question
Title: Show administrative description in Views
I am needing to do a Views report for all webforms on our site (we currently have 27 forms) that can show me the total submission count per form.

The issue I am having is that we need the Views report to also show the Administrative Description of the form but I do not see that as a field option in Views.

Any ideas on how I can achieve this?

# Answer
Ok here is one way to do it does however involve creating a custom twig Extension. its not to hard and there are a few youtube videos on the subject ... for others it is good to note that custom Twig Extensions has replaced php input fields in older versions of Drupal.

in a nut shell you create a custom module, you add a MODULENAME.services.yml file.

in it you put...

```
services:
modulename.getWebformDescription:
  class: Drupal\modulename\getWebformDescriptionTwigExtension
  arguments: ['@entity_type.manager']
  tags:
    - { name: twig.extension }

```

then you need here is your class file (getWebformDescriptionTwigExtension.php)

```
<?php

namespace Drupal\modulename;

use Drupal\Core\Entity\EntityTypeManagerInterface;

/**
 * Provides twig function getWebformDescription()
 */
class getWebformDescriptionTwigExtension extends \Twig_Extension {

  /**
   * Load entity type manager.
   *
   * @var EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * {@inheritdoc}
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
    $this->entityTypeManager = $entity_type_manager;
  }

  /**
   * {@inheritdoc}
   */
  public function getName() {
    return 'getWebformDescription';
  }

  /**
   * {@inheritdoc}
   */
  public function getFunctions() {
    return [
      new \Twig_SimpleFunction('getWebformDescription',
        [$this, 'getWebformDescription'], [
          'is_safe' => ['html'],
        ]),
    ];
  }

  /**
   * Provides getWebformDescription function to Twig templates.
   *
   * Use example {{ getWebformDescription( webfrom_id) }}.
   *
   * @param string webfrom_id
   *   The webform id eg 'contact'
   */
  public function getWebformDescription($webfrom_id) : string {
    // Invoke __toString and cast to int.
    $webfrom_id = $webfrom_id->__toString();
    $webform_storage = $this->entityTypeManager->getStorage('webform');
    $webform = $webform_storage->load($webfrom_id);
    return $webform->get('description');
  }

}

```

after all that is done and the module is enabled you can then in views add the webform id field (and exclude it from display)

then add a new custom field and look at the replacement patterns you will see some thing like {{webfrom\_id}} ...

in that field you can then enter ...

```
 {{ getWebformDescription(webfrom_id) }}

```

if stuck see stuff like https://medium.com/@thihathit/extending-custom-twig-extension-to-drupal-8-twig-extension-class-d4b99c2177ae

or google "create custom twig extension for drupal 8"
> 0 votes

# Answer
Thank you taggartJ

I feel like this question hasn't helped many people over the time since it's very specific, but it helped me.

I'm trying to do something similar in a Drupal 9 custom module:

```
function MY_MODULE_views_pre_render(ViewExecutable $view) {
  $view_id = $view->id();
  $view_title = $view->display_handler->getOption('title');
  $view_description = $view->storage->get('description');
  ...
}

```
> 0 votes

---
Tags: views, 8, webforms
---


# Question
Title: How to re-save a referenced entity that is removed when saving the referencing entity?
I have added a field to the **Tags** vocabulary, **Count**.

In my **Tags** taxonomy term entity bundle, I run a database query in `preSave()` to set the value of this field (how many nodes have an entity reference to this term).

To keep the **Tags** term counts up to date, when a node with a **Tags** entity reference field is saved, in `postSave()` on my node bundle, I resave the **Tags** term:

```
  public function postSave(EntityStorageInterface $storage, $update = TRUE): int {
    $tag_id = (int) $this->field_tags->target_id;
    if ($tag_id > 0) {
      $term = $this->entityTypeManager()->getStorage('taxonomy_term')->load($tag_id);
      $term->save();
    }
    parent::postSave($storage, $update);
    return 1;
  }

```

This is fine when adding tags.

However, when removing tags, then the term for the removed tag is not resaved, so the count is incorrect.

I tried to fix this in the node entity bundle's `preSave()`:

```
if (!$this->isNew()) {
  $tag_id = (int) $this->field_tags->target_id;
  $original_node = $this->original;
  $original_tag_id = (int) $original_node->field_tags->target_id;
    // If ID of group has changed or been removed, we also need to resave the original.
    if ($original_tag_id > 0 && ($original_tag_id !== $tag_id)) {
      $term = $this->entityTypeManager()->getStorage('taxonomy_term')->load($original_tag_id);
      $term->save();
    }
  }
}

```

While this code re-saves the term that is resaved, because this is run in `preSave()`, it runs before the node is updated, so the count is off by one because the node has not been updated (to remove the tag) yet.

So I'm stuck-- to get the `original` node to detect if a tag is removed, I need to be in `preSave`, but to resave the tag, I need to do so in `postSave`.

Is there some way I can trigger a re-save of an entity after editing another entity, but only after the edited entity has been saved?

# Answer
In a `postSave` event you can no longer test `isNew()`. So check for the original entity instead, which is only present if the save operation is updating and not creating an entity:

```
if (isset($this->original)) {
  $original_node = $this->original;

```
> 1 votes

---
Tags: entities, 9
---