<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lusso Luca</title>
    <description>The latest articles on DEV Community by Lusso Luca (@lussoluca).</description>
    <link>https://dev.to/lussoluca</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F638140%2F20ce906d-d7b4-4171-9ba3-cdab30a60de8.jpeg</url>
      <title>DEV Community: Lusso Luca</title>
      <link>https://dev.to/lussoluca</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lussoluca"/>
    <language>en</language>
    <item>
      <title>Drupal service container deep dive (Part 2): aliases, autowiring, and named arguments</title>
      <dc:creator>Lusso Luca</dc:creator>
      <pubDate>Wed, 10 Dec 2025 09:00:00 +0000</pubDate>
      <link>https://dev.to/sparkfabrik/drupal-service-container-deep-dive-part-2-aliases-autowiring-and-named-arguments-4oh8</link>
      <guid>https://dev.to/sparkfabrik/drupal-service-container-deep-dive-part-2-aliases-autowiring-and-named-arguments-4oh8</guid>
      <description>&lt;p&gt;In this second part of our deep dive into the Drupal service container, we explore the concepts of aliases, autowiring, and named arguments.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://www.sparkfabrik.com" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F48ggch57mw62gl6e063r.png" alt="SparkFabrik" width="311" height="44"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19s9nft3mfd421lbwzlt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19s9nft3mfd421lbwzlt.png" alt="Drupal Service Container deep dive, part 2" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://www.google.com/search?q=/en/blog/drupal-service-container-deep-dive-part-1" rel="noopener noreferrer"&gt;first part&lt;/a&gt; of this series, we explored the basics of the Drupal service container, including how services are defined and how dependencies are injected. We discussed concepts such as &lt;strong&gt;tags&lt;/strong&gt;, &lt;strong&gt;compiler passes&lt;/strong&gt;, &lt;strong&gt;service providers&lt;/strong&gt;, and &lt;strong&gt;autoconfiguration&lt;/strong&gt;. In this second part, we will delve deeper into more advanced concepts such as &lt;strong&gt;aliases&lt;/strong&gt;, &lt;strong&gt;autowiring&lt;/strong&gt;, and &lt;strong&gt;named arguments&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aliases
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;alias&lt;/strong&gt; in the Drupal service container is a way to create an alternative name for an existing service. This is particularly useful when you want to refer to a service by a different name without changing the original service definition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;request_stack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Symfony\Component\HttpFoundation\RequestStack&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;persist&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;span class="na"&gt;Symfony\Component\HttpFoundation\RequestStack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@request_stack'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we define a service &lt;code&gt;request_stack&lt;/code&gt; and then create an alias &lt;code&gt;Symfony\Component\HttpFoundation\RequestStack&lt;/code&gt; that points to the same service. This allows us to refer to the &lt;code&gt;RequestStack&lt;/code&gt; service using either name interchangeably. This specific example works because a service name is just a string, and we can use anything that is a valid string as a service name, including class names.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A service name is just a string: &lt;code&gt;config.factory&lt;/code&gt;, &lt;code&gt;entity_type.manager&lt;/code&gt;, &lt;code&gt;Symfony\Component\HttpFoundation\RequestStack&lt;/code&gt;, are all valid service names.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Starting from Drupal 10.1, most of the core services have been aliased with the Fully Qualified Class Name (FQCN) of the service class.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Change record: &lt;a href="https://www.drupal.org/node/3323122" rel="noopener noreferrer"&gt;https://www.drupal.org/node/3323122&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Of course, you can create your own aliases in your custom modules to improve code readability and maintainability:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;webprofiler.profiler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\webprofiler\Profiler\Profiler&lt;/span&gt;
 &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="pi"&gt;[&lt;/span&gt;
     &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@webprofiler.file_storage'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
     &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@logger.channel.webprofiler'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
     &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@config.factory'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
   &lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="s"&gt;Drupal\webprofiler\Profiler\Profiler'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@webprofiler.profiler'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a service name is equal to the service class FQCN, the service container can automatically resolve and inject dependencies simply by matching the requested FQCN.&lt;/p&gt;

&lt;p&gt;This feature is called autowiring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Autowiring
&lt;/h2&gt;

&lt;p&gt;Autowiring is a feature of the Drupal (and Symfony) service container that allows for automatic dependency injection based on type hints. When a service is defined with autowiring enabled, the container will automatically resolve and inject the required dependencies based on the type hints specified in the constructor or method signatures.&lt;/p&gt;

&lt;p&gt;The service will be instantiated in exactly the same way as before, but there is no need to explicitly specify which arguments are required; the interfaces/classes that are declared in the service constructor will be used to discover which services should be injected.&lt;/p&gt;

&lt;p&gt;Autowire can be enabled at service level, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;_defaults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;autoconfigure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="na"&gt;webprofiler.profiler_listener&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\webprofiler\EventListener\ProfilerListener&lt;/span&gt;
    &lt;span class="na"&gt;autowire&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or globally for all services, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;_defaults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;autoconfigure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;autowire&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="na"&gt;webprofiler.profiler_listener&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\webprofiler\EventListener\ProfilerListener&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;In Symfony, when a &lt;strong&gt;single&lt;/strong&gt; service exists for a given interface or class, the service container &lt;strong&gt;automatically&lt;/strong&gt; creates an alias for it using the FQCN. This means that you can type-hint against the interface or class in your constructor, and the container will automatically inject the correct service. This &lt;strong&gt;is not the case&lt;/strong&gt; in Drupal, where you need to &lt;strong&gt;explicitly&lt;/strong&gt; create the alias as shown above.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Multiple services for the same interface
&lt;/h3&gt;

&lt;p&gt;Usually, a Drupal module extends the &lt;code&gt;logger.channel_base&lt;/code&gt; service to build a new logger with the module name, like in this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;_defaults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;autowire&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;autoconfigure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="na"&gt;logger.channel.module_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;logger.channel_base&lt;/span&gt;
    &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;module_name'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="na"&gt;my_service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\module_name\MyService&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both &lt;code&gt;logger.channel_base&lt;/code&gt;, &lt;code&gt;logger.channel.module_name&lt;/code&gt;, and every logger defined by Core and modules are instances of the same interface: &lt;code&gt;Psr\Log\LoggerInterface&lt;/code&gt;. As there are many services in the service container that implement &lt;code&gt;Psr\Log\LoggerInterface&lt;/code&gt;, which specific implementation will be injected in a constructor like this?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Psr\Log\LoggerInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;LoggerInterface&lt;/span&gt; &lt;span class="nv"&gt;$logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, the service container will throw an exception because it cannot determine which specific service to inject:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3cd7hvrmj8cw494s512s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3cd7hvrmj8cw494s512s.png" alt="Exception thrown when multiple services match the type hint" width="800" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some services cannot be autowired, usually because the interface is implemented by multiple different services (like loggers or cache bins in Drupal).&lt;br&gt;
In this case, we must use the &lt;code&gt;#[Autowire]&lt;/code&gt; attribute to clearly specify which service to inject, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Psr\Log\LoggerInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\DependencyInjection\Attribute\Autowire&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="na"&gt;#[Autowire(service: 'logger.channel.module_name')]&lt;/span&gt;
      &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;LoggerInterface&lt;/span&gt; &lt;span class="nv"&gt;$logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Autowiring in Controllers and Hooks
&lt;/h3&gt;

&lt;p&gt;A controller is not a service and it's not loaded by the service container. When a route is matched, Drupal uses the controller loader service (&lt;code&gt;controller_resolver&lt;/code&gt;) to create an instance of the controller class. The controller loader calls the &lt;code&gt;create&lt;/code&gt; method on the controller class to instantiate it. The &lt;code&gt;create&lt;/code&gt; method receives the entire service container and creates a new instance of the controller class with the required services. This is an implementation of the service locator &lt;strong&gt;anti-pattern&lt;/strong&gt;, as shown in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DashboardController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ControllerBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;Profiler&lt;/span&gt; &lt;span class="nv"&gt;$profiler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;TemplateManager&lt;/span&gt; &lt;span class="nv"&gt;$templateManager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ContainerInterface&lt;/span&gt; &lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;DashboardController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'webprofiler.profiler'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'webprofiler.template_manager'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Starting from Drupal 10.2, &lt;code&gt;ControllerBase&lt;/code&gt; (the base class for controllers in Drupal) uses the &lt;code&gt;AutowireTrait&lt;/code&gt;, so if the controller constructor uses type hints and the &lt;code&gt;#[Autowire]&lt;/code&gt; attribute where necessary, we can take advantage of autowiring in controllers as well.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;AutowireTrait&lt;/code&gt; uses reflection to read the constructor parameters and their attributes, so it has a performance impact.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Drupal\Core\DependencyInjection\AutowireTrait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DashboardController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ControllerBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;AutowireTrait&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;Profiler&lt;/span&gt; &lt;span class="nv"&gt;$profiler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;TemplateManager&lt;/span&gt; &lt;span class="nv"&gt;$templateManager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see in the previous example, the &lt;code&gt;create&lt;/code&gt; method is no longer required. The &lt;code&gt;AutowireTrait&lt;/code&gt; can be used for any classes that implement &lt;code&gt;ContainerInjectionInterface&lt;/code&gt; (for some reason, &lt;code&gt;FormBase&lt;/code&gt; does not use it yet, but you can easily add it to your &lt;a href="https://www.drupal.org/docs/drupal-apis/services-and-dependency-injection/dependency-injection-for-a-form#s-service-autowiring-in-forms" rel="noopener noreferrer"&gt;custom forms&lt;/a&gt;).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With the upcoming Drupal 11.3, a similar feature will be available for plugins too: &lt;a href="https://www.drupal.org/project/drupal/issues/3452852" rel="noopener noreferrer"&gt;Add create() factory method with autowired parameters to PluginBase&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Starting from Drupal 11.1, hooks can be defined in classes (&lt;a href="https://www.drupal.org/node/3442349" rel="noopener noreferrer"&gt;https://www.drupal.org/node/3442349&lt;/a&gt;). All hook classes are automatically registered as &lt;strong&gt;autowired services&lt;/strong&gt;, so you can use autowiring in hook classes as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Drupal\file\Hook&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FileViewsHooks&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;EntityTypeManagerInterface&lt;/span&gt; &lt;span class="nv"&gt;$entityTypeManager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;EntityFieldManagerInterface&lt;/span&gt; &lt;span class="nv"&gt;$entityFieldManager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;?FieldViewsDataProvider&lt;/span&gt; &lt;span class="nv"&gt;$fieldViewsDataProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="na"&gt;#[Hook('field_views_data')]&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;fieldViewsData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FieldStorageConfigInterface&lt;/span&gt; &lt;span class="nv"&gt;$field_storage&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;fieldViewsDataProvider&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;defaultFieldImplementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$field_storage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Autowiring in Services vs Non-Services
&lt;/h3&gt;

&lt;p&gt;It's important to note that while services benefit from autowiring when properly configured, there are cases where you might want to use autowiring but cannot modify the class constructor to fix the type hint or to add the &lt;code&gt;#[Autowire]&lt;/code&gt; attribute. This is where named arguments come into play.&lt;/p&gt;

&lt;h2&gt;
  
  
  Named Arguments
&lt;/h2&gt;

&lt;p&gt;Named arguments provide an alternative way to specify dependencies in the service definition file itself, without requiring changes to the PHP code. This is particularly useful when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're working with third-party classes that you cannot modify&lt;/li&gt;
&lt;li&gt;You want to keep dependency configuration separate from the code&lt;/li&gt;
&lt;li&gt;You need to inject a specific service when multiple implementations of the same interface exist (as an alternative to using the &lt;code&gt;#[Autowire]&lt;/code&gt; attribute)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Named arguments allow us to specify which arguments to pass to a service when it is being instantiated, by name rather than by position. The syntax uses the parameter name (with a &lt;code&gt;$&lt;/code&gt; prefix) as the key in the arguments section.&lt;/p&gt;

&lt;p&gt;For example, let's say we have a service class with a constructor like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Symfony\Component\Messenger\Middleware&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SendMessageMiddleware&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="kt"&gt;SendersLocatorInterface&lt;/span&gt; &lt;span class="nv"&gt;$sendersLocator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kt"&gt;EventDispatcherInterface&lt;/span&gt; &lt;span class="nv"&gt;$eventDispatcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of passing arguments by position, we can use named arguments in the service definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;messenger.middleware.send_message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Symfony\Component\Messenger\Middleware\SendMessageMiddleware&lt;/span&gt;
  &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;$eventDispatcher&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@event_dispatcher'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we're explicitly specifying that the &lt;code&gt;$eventDispatcher&lt;/code&gt; parameter should receive the &lt;code&gt;@event_dispatcher&lt;/code&gt; service. This is much clearer than using positional arguments, especially when dealing with optional parameters or when you want to skip some arguments.&lt;/p&gt;

&lt;p&gt;Named arguments are particularly powerful when combined with autowiring. The container will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First try to autowire all type-hinted parameters based on their types&lt;/li&gt;
&lt;li&gt;Then override specific parameters with any named arguments you've defined&lt;/li&gt;
&lt;li&gt;Use default values for any remaining optional parameters&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This means you can let autowiring handle most dependencies automatically while using named arguments only for the specific cases where you need explicit control, such as when multiple services implement the same interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;_defaults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;autowire&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;autoconfigure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="na"&gt;my_custom_service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\my_module\MyCustomService&lt;/span&gt;
    &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;$logger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@logger.channel.my_module'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, most dependencies of &lt;code&gt;MyCustomService&lt;/code&gt; will be autowired, but we're explicitly specifying which logger to inject using a named argument, solving the ambiguity problem we discussed earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bringing It All Together
&lt;/h2&gt;

&lt;p&gt;Now that we understand aliases, autowiring, and named arguments, let's see how they work together to simplify service definitions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Traditional Service Definition
&lt;/h3&gt;

&lt;p&gt;In older Drupal code, a service definition might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;webprofiler.profiler_listener&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\webprofiler\EventListener\ProfilerListener&lt;/span&gt;
    &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@webprofiler.profiler'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@request_stack'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@webprofiler.matcher.exclude_path'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explicitly listing every dependency&lt;/li&gt;
&lt;li&gt;Maintaining the correct order of arguments&lt;/li&gt;
&lt;li&gt;Updating the YAML file whenever constructor parameters change&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Modern Service Definition with Aliases and Autowiring
&lt;/h3&gt;

&lt;p&gt;With the concepts we've covered, we can dramatically simplify this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;_defaults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;autoconfigure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;autowire&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="na"&gt;Drupal\webprofiler\EventListener\ProfilerListener&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Aliases&lt;/strong&gt;: The service is registered using its FQCN, which serves as both the service name and alias&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autowiring&lt;/strong&gt;: The container automatically resolves dependencies based on type hints in the constructor&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autoconfiguration&lt;/strong&gt;: The service is automatically tagged if it implements certain interfaces&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;In Yaml, &lt;code&gt;~&lt;/code&gt; symbol (tilde) is equivalent to an empty array or null, meaning no additional configuration is needed. In this case, the tilde can be omitted entirely, but in my opinion, it's clearer to explicitly show that no further configuration is necessary.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this second part of our deep dive into the Drupal service container, we have explored three powerful concepts that work together to modernize dependency injection in Drupal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Aliases&lt;/strong&gt; enable us to reference services by their class names, making code more intuitive and refactoring safer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autowiring&lt;/strong&gt; eliminates boilerplate by automatically resolving dependencies based on type hints&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Named arguments&lt;/strong&gt; provide surgical precision when we need explicit control over specific dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, these features reduce boilerplate code, improve maintainability, and make service definitions more readable. They represent a significant evolution in how Drupal developers work with the service container.&lt;/p&gt;

&lt;p&gt;Stay tuned for the next parts of this series, where we will continue to explore more advanced features of the Drupal service container:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.google.com/search?q=/en/blog/drupal-service-container-deep-dive-part-1" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt;&lt;/strong&gt; covers service tags, compiler passes, service providers, and autoconfiguration, laying the groundwork for understanding how services are defined and managed within the container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.google.com/search?q=/en/blog/drupal-service-container-deep-dive-part-3" rel="noopener noreferrer"&gt;Part 3&lt;/a&gt;&lt;/strong&gt; will cover service collectors, which aggregate multiple services into a single service for streamlined access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4&lt;/strong&gt; will explore factories, which provide a mechanism for creating services with complex initialization logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 5&lt;/strong&gt; will discuss backend overrides, enabling developers to customize service implementations for specific environments or use cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 6&lt;/strong&gt; will examine advanced features of the Drupal service container, such as service decoration and lazy loading.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;a href="https://www.sparkfabrik.com" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F48ggch57mw62gl6e063r.png" alt="SparkFabrik" width="311" height="44"&gt;&lt;/a&gt;&lt;/p&gt;




</description>
      <category>drupal</category>
      <category>php</category>
      <category>dependency</category>
      <category>autowiring</category>
    </item>
    <item>
      <title>Drupal service container deep dive part 1: tags, compiler passes, service providers and autoconfiguration</title>
      <dc:creator>Lusso Luca</dc:creator>
      <pubDate>Mon, 03 Nov 2025 09:00:00 +0000</pubDate>
      <link>https://dev.to/sparkfabrik/drupal-service-container-deep-dive-part-1-tags-compiler-passes-service-providers-and-a95</link>
      <guid>https://dev.to/sparkfabrik/drupal-service-container-deep-dive-part-1-tags-compiler-passes-service-providers-and-a95</guid>
      <description>&lt;p&gt;In this first part of our deep dive into the Drupal service container, we explore the concepts of tags, compiler passes, service providers, and autoconfiguration.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://www.sparkfabrik.com" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F48ggch57mw62gl6e063r.png" alt="SparkFabrik" width="311" height="44"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2F6897318.fs1.hubspotusercontent-na1.net%2Fhubfs%2F6897318%2FBlog%2Fdrupal_service_container-p1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2F6897318.fs1.hubspotusercontent-na1.net%2Fhubfs%2F6897318%2FBlog%2Fdrupal_service_container-p1.png" title="Drupal Service Container deep dive, part 1" alt="Drupal Service Container deep dive, part 1" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The evolution of Drupal core has increasingly embraced modern PHP standards and robust object-oriented programming (OOP) principles, driven largely by its integration of &lt;strong&gt;Symfony components&lt;/strong&gt;. Central to this modernization is the &lt;strong&gt;Service Container&lt;/strong&gt;, which acts as the core registry responsible for service instantiation, dependency resolution, and lifecycle management.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;service&lt;/strong&gt; is inherently a specialized, reusable, and stateless object designed to perform a single, well-defined task, such as ConfigFactory for configuration access or EntityTypeManager for entity access.  &lt;/p&gt;

&lt;p&gt;The philosophy underpinning service usage is the promotion of loose coupling and adherence to the Dependency Inversion Principle (DIP). A class using a service should request it from the container rather than manually instantiating it with the &lt;code&gt;new&lt;/code&gt; keyword. This arrangement enables developers to swap implementations without altering consuming code.  &lt;/p&gt;

&lt;p&gt;In this series of articles, we will explore the Drupal service container in depth, focusing on its features, capabilities, and best practices for leveraging it effectively in Drupal module development.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 1&lt;/strong&gt; will cover service tags, compiler passes, service providers, and autoconfiguration, laying the groundwork for understanding how services are defined and managed within the container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 2&lt;/strong&gt; will delve into aliases, autowiring, and name arguments, demonstrating how to resolve and inject dependencies based on type hints, reducing boilerplate code and enhancing maintainability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3&lt;/strong&gt; will cover service collectors, which aggregate multiple services into a single service for streamlined access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4&lt;/strong&gt; will explore factories, which provide a mechanism for creating services with complex initialization logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 5&lt;/strong&gt; will discuss backend overrides, enabling developers to customize service implementations for specific environments or use cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 6&lt;/strong&gt; will examine advanced features of the Drupal service container, such as service decoration and lazy loading.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Anatomy of a Drupal service
&lt;/h2&gt;

&lt;p&gt;A Drupal service is defined within a module's &lt;code&gt;MODULE.services.yml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# in modules/contrib/webprofiler/webprofiler.services.yml&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;webprofiler.matcher.exclude_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\webprofiler\RequestMatcher\WebprofilerRequestMatcher&lt;/span&gt;
        &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@path.matcher'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@config.factory'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;exclude_paths'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# in core/core.services.yml&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path.matcher&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\Core\Path\PathMatcher&lt;/span&gt;
        &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@config.factory'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@current_route_match'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;config.factory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\Core\Config\ConfigFactory&lt;/span&gt;
    &lt;span class="na"&gt;current_route_match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\Core\Routing\CurrentRouteMatch&lt;/span&gt;
        &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@request_stack'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;request_stack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Symfony\Component\HttpFoundation\RequestStack&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When code asks for the &lt;code&gt;webprofiler.matcher.exclude_path&lt;/code&gt; service, the Drupal service container first builds all the required services and finally builds an instance of the &lt;code&gt;WebprofilerRequestMatcher&lt;/code&gt; class by injecting all the dependencies in the &lt;code&gt;__construct&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Drupal\webprofiler\RequestMatcher&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Drupal\Core\Config\ConfigFactoryInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Drupal\Core\Path\PathMatcherInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\HttpFoundation\RequestMatcherInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WebprofilerRequestMatcher&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;RequestMatcherInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;PathMatcherInterface&lt;/span&gt; &lt;span class="nv"&gt;$pathMatcher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;ConfigFactoryInterface&lt;/span&gt; &lt;span class="nv"&gt;$configFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$configuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resolution graph looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftqp27lietbzk4660zhrf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftqp27lietbzk4660zhrf.png" alt="The resolution graph looks like this" width="684" height="692"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;So Drupal first builds the &lt;code&gt;request_stack&lt;/code&gt; service, then the &lt;code&gt;current_route_match&lt;/code&gt; service (which depends on &lt;code&gt;request_stack&lt;/code&gt;), then the &lt;code&gt;config.factory&lt;/code&gt; service, then the &lt;code&gt;path.matcher&lt;/code&gt; service (which depends on both &lt;code&gt;config.factory&lt;/code&gt; and &lt;code&gt;current_route_match&lt;/code&gt;), and finally the &lt;code&gt;webprofiler.matcher.exclude_path&lt;/code&gt; service (which depends on both &lt;code&gt;path.matcher&lt;/code&gt; and &lt;code&gt;config.factory&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Notice that the third argument of the &lt;code&gt;webprofiler.matcher.exclude_path&lt;/code&gt; service is a string (&lt;code&gt;exclude_paths&lt;/code&gt;). The service container supports injecting scalar values like strings, integers, and booleans as arguments to services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@&lt;/code&gt; prefix indicates a service reference.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;%&lt;/code&gt; prefix indicates a parameter reference.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@?&lt;/code&gt; indicates an optional service reference.&lt;/li&gt;
&lt;li&gt;No prefix indicates a literal value.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;param1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;some&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;string'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;some_service1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Some\Class\Name1&lt;/span&gt;

  &lt;span class="na"&gt;some_service2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Some\Class\Name2&lt;/span&gt;
    &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@some_service1'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%param1%'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;42&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;another&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;string'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@?some_service3'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the code for &lt;code&gt;some_service2&lt;/code&gt; would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Some\Class&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Name2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;Name1&lt;/span&gt; &lt;span class="nv"&gt;$someService1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$param1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$param2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nv"&gt;$param3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$param4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;?Name3&lt;/span&gt; &lt;span class="nv"&gt;$someService3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Differences between Drupal and Symfony service containers
&lt;/h2&gt;

&lt;p&gt;The Drupal service container is built on top of the Symfony service container, inheriting many of its features and capabilities. However the Drupal modular architecture and specific requirements have led to several changes. On the other hand, Drupal doesn't use the Symfony Config component for service configuration, instead relying on its own YAML-based service definition files.&lt;/p&gt;

&lt;p&gt;At the end both YAML loading and service container dumping have been heavily customized in Drupal to fit its architecture and performance needs, resulting in a service container that is optimized for Drupal's unique use cases but not fully equivalent to the standard Symfony service container.&lt;/p&gt;

&lt;p&gt;Those differences can lead to some confusion when reading Symfony documentation, as some features may not be available or behave differently in Drupal. This article series aims to clarify those differences and provide a comprehensive understanding of the Drupal service container.&lt;/p&gt;

&lt;h2&gt;
  
  
  Service Tags
&lt;/h2&gt;

&lt;p&gt;Service tags are metadata annotations that can be applied to service definitions within the Drupal service container. They provide a way to categorize and group services based on their functionality or purpose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;webprofiler.database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\webprofiler\DataCollector\DatabaseDataCollector&lt;/span&gt;
    &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@database'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@config.factory'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;
          &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;data_collector&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
          &lt;span class="nv"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@webprofiler/Collector/database.html.twig'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
          &lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;database'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
          &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Database'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
          &lt;span class="nv"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;750&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A tag has a mandatory &lt;code&gt;name&lt;/code&gt; attribute and can have any number of additional attributes. In the example above, the &lt;code&gt;webprofiler.database&lt;/code&gt; service is tagged with the &lt;code&gt;data_collector&lt;/code&gt; tag, along with several additional attributes such as &lt;code&gt;template&lt;/code&gt;, &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;label&lt;/code&gt;, and &lt;code&gt;priority&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Tags on their own don't actually alter the functionality of a service in any way. They are primarily used to identify services for special processing during the container compilation phase, often through the use of compiler passes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compiler Passes
&lt;/h2&gt;

&lt;p&gt;Compiler passes are a mechanism that allows developers to modify the service container during its compilation phase. They provide a way to programmatically alter service definitions, add or remove services, and manipulate tags before the container is fully built and dumped to the cache&lt;/p&gt;

&lt;p&gt;Compiler passes are particularly useful for implementing complex service registration logic, such as dynamically adding services based on configuration or other runtime conditions. They are executed in a specific order during the container compilation process, allowing for fine-grained control over the final service definitions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Drupal\webprofiler\Compiler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Symfony\Component\DependencyInjection\ContainerBuilder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProfilerPass&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;CompilerPassInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ContainerBuilder&lt;/span&gt; &lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;findTaggedServiceIds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'data_collector'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="mf"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A shown in the example above, the &lt;code&gt;ProfilerPass&lt;/code&gt; class implements the &lt;code&gt;CompilerPassInterface&lt;/code&gt; and defines a &lt;code&gt;process&lt;/code&gt; method that is called during the container compilation phase. Within this method, the &lt;code&gt;findTaggedServiceIds&lt;/code&gt; function is used to retrieve all services tagged with &lt;code&gt;data_collector&lt;/code&gt;, allowing for custom processing of those services. The &lt;code&gt;process&lt;/code&gt; method can modify the service definitions as needed, such as adding additional arguments, changing class names, or even removing services from the container.&lt;/p&gt;

&lt;p&gt;A compiler pass must be registered with the container in order to be executed during the compilation phase. This is typically done within a module's service provider class.&lt;/p&gt;

&lt;h2&gt;
  
  
  Service Providers
&lt;/h2&gt;

&lt;p&gt;Service providers are classes that encapsulate the logic for dynamically registering or altering services within the Drupal service container. Service providers typically extend the &lt;code&gt;ServiceProviderBase&lt;/code&gt; class and re-define the &lt;code&gt;register&lt;/code&gt; or &lt;code&gt;alter&lt;/code&gt; methods to add or modify service definitions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Drupal\webprofiler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Drupal\Core\DependencyInjection\ContainerBuilder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Drupal\Core\DependencyInjection\ServiceProviderBase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WebprofilerServiceProvider&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ServiceProviderBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ContainerBuilder&lt;/span&gt; &lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;alter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ContainerBuilder&lt;/span&gt; &lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The name and the namespace of the service provider class must follow a specific convention: it should be named &lt;code&gt;ModuleServiceProvider&lt;/code&gt; and be located in the root namespace of the module. For example, the service provider for the &lt;code&gt;webprofiler&lt;/code&gt; module is named &lt;code&gt;WebprofilerServiceProvider&lt;/code&gt; and is located in the &lt;code&gt;Drupal\webprofiler&lt;/code&gt; namespace. If the module name contains underscores, they should be removed and the following letter capitalized (e.g., &lt;code&gt;search_api&lt;/code&gt; becomes &lt;code&gt;SearchApiServiceProvider&lt;/code&gt;). Core has its own service provider named &lt;code&gt;CoreServiceProvider&lt;/code&gt; located in the &lt;code&gt;Drupal\Core&lt;/code&gt; namespace.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;register&lt;/code&gt; method can be used to add compiler passes to the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Drupal\webprofiler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Drupal\Core\DependencyInjection\ContainerBuilder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Drupal\Core\DependencyInjection\ServiceProviderBase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WebprofilerServiceProvider&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ServiceProviderBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ContainerBuilder&lt;/span&gt; &lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addCompilerPass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ProfilerPass&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the order of execution of compiler passes matters, you can specify the pass &lt;code&gt;type&lt;/code&gt; and &lt;code&gt;priority&lt;/code&gt; when adding the compiler pass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addCompilerPass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ProfilerPass&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PassConfig&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TYPE_BEFORE_OPTIMIZATION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Values for &lt;code&gt;type&lt;/code&gt; can be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PassConfig::TYPE_BEFORE_OPTIMIZATION&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PassConfig::TYPE_OPTIMIZE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PassConfig::TYPE_BEFORE_REMOVING&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PassConfig::TYPE_REMOVE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PassConfig::TYPE_AFTER_REMOVING&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This diagram that illustrates when compiler passes are executed during the Symfony service container compilation process:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftx6d3l84mm783bzckpvl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftx6d3l84mm783bzckpvl.png" alt="This diagram illustrates when compiler passes are executed during the Symfony service container compilation process" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;TYPE_BEFORE_OPTIMIZATION&lt;/strong&gt; (Blue): Where most custom compiler passes (like &lt;code&gt;ProfilerPass&lt;/code&gt;) are typically registered. This is where services are analyzed and tagged services are processed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TYPE_OPTIMIZE&lt;/strong&gt; (Purple): Internal Symfony optimization passes that inline services and optimize the container structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TYPE_BEFORE_REMOVING&lt;/strong&gt; (Orange): Validation passes that check for circular references and definition validity before cleanup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TYPE_REMOVE&lt;/strong&gt; (Red): Cleanup passes that remove unused services and private aliases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TYPE_AFTER_REMOVING&lt;/strong&gt; (Green): Final optimization passes after cleanup is complete.&lt;/p&gt;

&lt;p&gt;Within each phase, compiler passes are executed in priority order (highest to lowest), allowing for fine-grained control over the execution sequence when multiple passes are registered in the same phase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Autoconfiguration
&lt;/h2&gt;

&lt;p&gt;Autoconfiguration is a powerful feature of the Drupal service container that allows services to be automatically tagged based on their class definitions.&lt;/p&gt;

&lt;p&gt;When a service is defined in the container, the autoconfiguration process analyzes the class and applies the appropriate tags. For example, if a service implements a specific interface or extends a particular base class, the container can automatically apply the relevant tags to the service definition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Drupal\Core&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CoreServiceProvider&lt;/span&gt;
    &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ServiceProviderInterface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ServiceModifierInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ContainerBuilder&lt;/span&gt; &lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="nv"&gt;$container&lt;/span&gt;
          &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;registerForAutoconfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;EventSubscriberInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'event_subscriber'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;alter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ContainerBuilder&lt;/span&gt; &lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above, the &lt;code&gt;CoreServiceProvider&lt;/code&gt; class registers an autoconfiguration rule that automatically tags any service implementing the &lt;code&gt;EventSubscriberInterface&lt;/code&gt; with the &lt;code&gt;event_subscriber&lt;/code&gt; tag. This means that when a service is defined that implements this interface, it will automatically receive the &lt;code&gt;event_subscriber&lt;/code&gt; tag without needing to explicitly define it in the service definition.&lt;/p&gt;

&lt;p&gt;Autoconfiguration is particularly useful for module developers, as it streamlines the process of registering services and ensures that they are properly configured without requiring extensive boilerplate code.&lt;/p&gt;

&lt;p&gt;Since Drupal 10.2.0, several interfaces have been registered for autoconfiguration in &lt;code&gt;CoreServiceProvider&lt;/code&gt;. At the time of writing (Drupal 11.3.0), those interfaces, and their corresponding tags, are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MediaLibraryOpenerInterface&lt;/code&gt; (tag: media_library.opener)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EventSubscriberInterface&lt;/code&gt; (tag: event_subscriber)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LoggerAwareInterface&lt;/code&gt; (tag: logger_aware)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PreWarmableInterface&lt;/code&gt; (tag: cache_prewarmable)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ModuleUninstallValidatorInterface&lt;/code&gt; (tag: module_install.uninstall_validator)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, custom autoconfiguration rules can be added in any module's service provider class, allowing for tailored service tagging based on specific module requirements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Drupal\sm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SmServiceProvider&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ServiceProviderInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ContainerBuilder&lt;/span&gt; &lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;registerForAutoconfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TransportFactoryInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'messenger.transport_factory'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;registerForAutoconfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ServiceLocator&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'container.service_locator'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately, autoconfiguration only works for tags that doesn't have attributes.&lt;/p&gt;

&lt;p&gt;With all those concepts in place, we can remove some boilerplate from our service definition by going from a service definition like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;webprofiler.profiler_listener&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\webprofiler\EventListener\ProfilerListener&lt;/span&gt;
  &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@webprofiler.profiler'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@request_stack'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@webprofiler.matcher.exclude_path'&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;event_subscriber&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;webprofiler.profiler_listener&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\webprofiler\EventListener\ProfilerListener&lt;/span&gt;
  &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@webprofiler.profiler'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@request_stack'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@webprofiler.matcher.exclude_path'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;assuming that the &lt;code&gt;ProfilerListener&lt;/code&gt; class implements the &lt;code&gt;EventSubscriberInterface&lt;/code&gt;, which is already registered for autoconfiguration in &lt;code&gt;CoreServiceProvider&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the next part of this series, we will explore &lt;strong&gt;aliases&lt;/strong&gt;, &lt;strong&gt;autowiring&lt;/strong&gt;, and &lt;strong&gt;named arguments&lt;/strong&gt;, demonstrating how to resolve and inject dependencies based on type hints, reducing boilerplate code, enhancing maintainability, and going even further in simplifying service definitions by removing the &lt;code&gt;arguments&lt;/code&gt; section entirely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;webprofiler.profiler_listener&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\webprofiler\EventListener\ProfilerListener&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stay tuned!&lt;/p&gt;




&lt;p&gt;&lt;a href="https://www.sparkfabrik.com" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F48ggch57mw62gl6e063r.png" alt="SparkFabrik" width="311" height="44"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>drupal</category>
      <category>php</category>
      <category>dependency</category>
      <category>autowiring</category>
    </item>
    <item>
      <title>Drupal Access Policy demystified</title>
      <dc:creator>Lusso Luca</dc:creator>
      <pubDate>Mon, 24 Jun 2024 10:35:53 +0000</pubDate>
      <link>https://dev.to/sparkfabrik/drupal-access-policy-demystified-44bj</link>
      <guid>https://dev.to/sparkfabrik/drupal-access-policy-demystified-44bj</guid>
      <description>&lt;h2&gt;
  
  
  Access Policy in Core
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.drupal.org/blog/drupal-10-3-0" rel="noopener noreferrer"&gt;Drupal 10.3&lt;/a&gt; introduces a new way to assign permissions to users, going beyond the traditional roles and permissions system. This new system is called the &lt;strong&gt;Access Policy API&lt;/strong&gt;, and in this blog post, we'll try to explain how it works and how to use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The old way
&lt;/h2&gt;

&lt;p&gt;Until Drupal 10.2, the access control system was based on two main concepts: you were either in a &lt;strong&gt;role&lt;/strong&gt; that granted you a set of permissions or the &lt;strong&gt;user with UID 1&lt;/strong&gt;, and the access checks were bypassed.&lt;/p&gt;

&lt;p&gt;For example, you can have this code somewhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;AccountInterface&lt;/span&gt; &lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'access content'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code for &lt;code&gt;hasPermission&lt;/code&gt; simply checks for the two cases mentioned above: if the user is the one with UID 1 or if the user is in a role that grants that permission:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;hasPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$permission&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;AccountInterface&lt;/span&gt; &lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// User #1 has all privileges.&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;entityTypeManager&lt;/span&gt;
      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user_role'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isPermissionInRoles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$permission&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getRoles&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This implementation was quite simple and worked well when a user has a set of permissions that are valid sitewide and that don't change based on some external factors.&lt;/p&gt;

&lt;p&gt;If you need to implement use cases like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deny edit permissions on weekends&lt;/li&gt;
&lt;li&gt;allow edit permissions only if the user has 2FA enabled&lt;/li&gt;
&lt;li&gt;allow edit permissions only to contents in a group (or in a domain, or in a Commerce store, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You're probably going to need &lt;em&gt;a lot&lt;/em&gt; of custom code (otherwise it's impossible to implement them).&lt;/p&gt;

&lt;h2&gt;
  
  
  The new way
&lt;/h2&gt;

&lt;p&gt;Drupal 10.3 introduced a new &lt;a href="https://www.drupal.org/node/3385551" rel="noopener noreferrer"&gt;Access Policy API&lt;/a&gt; that allows the definition of a set of policies that can be applied based on a context.&lt;/p&gt;

&lt;p&gt;If you want to know something about the genesis of this new system, you can read this blog post: &lt;a href="https://www.thedroptimes.com/40119/kristiaan-van-den-eynde-talks-about-policy-based-access-in-core" rel="noopener noreferrer"&gt;Policy-Based Access in Core by Kristiaan Van den Eynde&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The API is quite simple; you define a policy class that extends the &lt;code&gt;\Drupal\Core\Session\AccessPolicyBase&lt;/code&gt; and provide, at least, the implementation for the methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;calculatePermissions(AccountInterface $account, string $scope): RefinableCalculatedPermissionsInterface&lt;/code&gt;: Calculates the permissions for an account within a given scope&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;getPersistentCacheContexts(): array&lt;/code&gt;: Gets the initial cache context this policy varies by&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A policy is then registered in the service container with the tag &lt;code&gt;access_policy.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Drupal 10.3 mimics the old behavior by providing two default policies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;\Drupal\Core\Session\Access\UserRolesAccessPolicy&lt;/code&gt;: Grants permissions based on a user's roles&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\Drupal\Core\Session\Access\SuperUserAccessPolicy&lt;/code&gt;: Bypass permissions checks for the user with UID equal to 1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;\Drupal\Core\Session\Access\UserRolesAccessPolicy&lt;/code&gt;, for example, is implemented as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRolesAccessPolicy&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AccessPolicyBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;EntityTypeManagerInterface&lt;/span&gt; &lt;span class="nv"&gt;$entityTypeManager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;calculatePermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;AccountInterface&lt;/span&gt; &lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$scope&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;RefinableCalculatedPermissionsInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$calculated_permissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;calculatePermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$scope&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nv"&gt;$user_roles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;entityTypeManager&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user_role'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;loadMultiple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getRoles&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

      &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_roles&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$user_role&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nv"&gt;$calculated_permissions&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CalculatedPermissionsItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_role&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPermissions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nv"&gt;$user_role&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addCacheableDependency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_role&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$calculated_permissions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getPersistentCacheContexts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user.roles'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The previous code retrieves the user's roles and adds a &lt;code&gt;CalculatedPermissionsItem&lt;/code&gt; with the permissions granted by each role. Then, it adds a cacheable dependency on the role entity so that if the role's permissions change, the cache is invalidated. Finally, the method &lt;code&gt;getPersistentCacheContexts&lt;/code&gt; returns the initial cache context that the policy varies by.&lt;/p&gt;

&lt;p&gt;We will discuss the meaning of &lt;code&gt;(Refinable)CalculatedPermissions&lt;/code&gt;, &lt;code&gt;scope&lt;/code&gt;, and &lt;code&gt;initial cache context&lt;/code&gt; shortly.&lt;/p&gt;

&lt;p&gt;The critical thing to understand here is that this new system does not aim to grant access to something, like editing a node or viewing a page. It's designed to calculate a user's permissions in a given context. The access check is still done in the old way, which checks if the user has specific permission to perform a task.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An access policy converts a context into a set of permissions&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Access policies are services, allowing us to replace or decorate the implementation provided by Core. Indeed, the Core itself allows for the policy for the super user to be disabled to increase site security (&lt;a href="https://www.drupal.org/node/2910500" rel="noopener noreferrer"&gt;https://www.drupal.org/node/2910500&lt;/a&gt;). With the super user policy disabled, we may want to define a new one that grants admin permissions based on other user characteristics. For instance, we can specify that the site admins are users with a specific email domain or who have logged in through a particular authentication system.&lt;/p&gt;

&lt;p&gt;Now, let's dive into the details of the new system using some examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 1 (alter permissions provided by Core)
&lt;/h3&gt;

&lt;p&gt;Let's say we want to add a new policy that grants permission to &lt;code&gt;access promotional banners&lt;/code&gt; only if the current language is English.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;LanguageAccessPolicy.php&lt;/code&gt; class may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LanguageAccessPolicy&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AccessPolicyBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;alterPermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="kt"&gt;AccountInterface&lt;/span&gt; &lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="kt"&gt;RefinableCalculatedPermissionsInterface&lt;/span&gt; &lt;span class="nv"&gt;$calculated_permissions&lt;/span&gt;
   &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Drupal&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;languageManager&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getCurrentLanguage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'en'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nv"&gt;$calculated_permissions&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CalculatedPermissionsItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'access promotional banners'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
               &lt;span class="n"&gt;isAdmin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;FALSE&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;overwrite&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;FALSE&lt;/span&gt;
         &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getPersistentCacheContexts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'languages'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To register the policy in the service container, you need to add the following to your &lt;code&gt;access_policy_demo.services.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;access_policy_demo.access_policy.language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\access_policy_demo\Access\LanguageAccessPolicy&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
         &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;access_policy&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now have this render array sonewhere in your code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$build&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'content'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
   &lt;span class="s1"&gt;'#markup'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'access promotional banners'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'Some promotional banner'&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="s1"&gt;'#cache'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s1"&gt;'contexts'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'languages'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The previous code will show the promotional banner only if the current language is English.&lt;/p&gt;

&lt;p&gt;Revoke permission is possible by setting the &lt;code&gt;overwrite&lt;/code&gt; parameter of the &lt;code&gt;addItem&lt;/code&gt; method to &lt;code&gt;TRUE&lt;/code&gt;, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$new_permissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="nv"&gt;$calculated_permissions&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPermissions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'view page revisions'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$calculated_permissions&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CalculatedPermissionsItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$new_permissions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;isAdmin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;FALSE&lt;/span&gt;
   &lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="n"&gt;overwrite&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;TRUE&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Altering permissions is possible because, during the access policy calculation, the object that holds the calculated permissions is an instance of the &lt;code&gt;RefinableCalculatedPermissionsInterface&lt;/code&gt; that allows the addition or removal of permissions.&lt;br&gt;
When the build and alter phases are complete, the calculated permissions are converted to an immutable object of type &lt;code&gt;CalculatedPermissionsInterface&lt;/code&gt;. Using an immutable object guarantees that the computed permissions are not altered after the access policy calculation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Drupal has moved from an RBAC (Role-Based Access Control) to a PBAC (Policy-Based Access Control) system where permissions are calculated based on a set of policies that are applied in a given context.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Access policies are tagged services, so you can define the priority with which they are applied:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;access_policy_demo.access_policy.language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\access_policy_demo\Access\LanguageAccessPolicy&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
         &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;access_policy&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;100&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The priority is a positive or negative integer that defaults to 0. The higher the number, the earlier the tagged service will be located in the service collection.&lt;/p&gt;

&lt;p&gt;Now it's time to talk about &lt;strong&gt;scopes&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 2 (split the site into sections)
&lt;/h3&gt;

&lt;p&gt;Until now, we have ignored the &lt;code&gt;scope&lt;/code&gt; parameter, but look at how the &lt;code&gt;hasPermission()&lt;/code&gt; method is implemented in Drupal 10.3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;hasPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$permission&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;AccountInterface&lt;/span&gt; &lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nv"&gt;$item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;processAccessPolicies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$permission&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;processAccessPolicies()&lt;/code&gt; has a second, non-mandatory parameter: the &lt;code&gt;scope&lt;/code&gt;.&lt;br&gt;
The &lt;code&gt;getItem()&lt;/code&gt; method has two non-mandatory parameters: the &lt;code&gt;scope&lt;/code&gt; and the &lt;code&gt;identifier&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Within Core, both &lt;code&gt;scope&lt;/code&gt; and &lt;code&gt;identifier&lt;/code&gt; default to &lt;code&gt;AccessPolicyInterface::SCOPE_DRUPAL&lt;/code&gt;, and you probably don't have to deal with them in most cases.&lt;/p&gt;

&lt;p&gt;But what are they used for?&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;scope&lt;/code&gt; is a string that identifies the context in which the policy is applied, like a group, a domain, a commerce store, etc. The &lt;code&gt;identifier&lt;/code&gt; is a string that identifies the specific value within the scope (like the group ID, the domain ID, etc).&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;AccessPolicyInterface&lt;/code&gt; defines the &lt;code&gt;applies(string $scope): bool&lt;/code&gt; method, which determines whether the policy should be applied in a given scope.&lt;/p&gt;

&lt;p&gt;Let's try to implement (a very simplified) version of modules like &lt;a href="https://www.drupal.org/project/permissions_by_term" rel="noopener noreferrer"&gt;Permissions by Term&lt;/a&gt; or &lt;a href="https://www.drupal.org/project/tac_lite" rel="noopener noreferrer"&gt;Taxonomy Access Control Lite&lt;/a&gt; using the new system.&lt;/p&gt;

&lt;p&gt;Suppose we have a vocabulary &lt;code&gt;access&lt;/code&gt;, which terms represent a group of content that can be accessed only by a specific set of users. Content types and users are tagged with terms of this vocabulary.&lt;br&gt;
The permissions a user has are calculated based on the standard roles mechanism of Drupal. But on nodes tagged with a term of the &lt;code&gt;access&lt;/code&gt; vocabulary, if the user is tagged with the same term, the user has the permissions granted by an additional role.&lt;/p&gt;

&lt;p&gt;We have the &lt;code&gt;Content editor&lt;/code&gt; role that grants standard permissions like &lt;code&gt;Article: Create new content&lt;/code&gt; or &lt;code&gt;View own unpublished content&lt;/code&gt;, and the &lt;code&gt;Content editor in term&lt;/code&gt; role that grants permissions like &lt;code&gt;Article: Edit any content&lt;/code&gt; or &lt;code&gt;Article: Delete any content&lt;/code&gt;. An editor always has the permissions granted by the &lt;code&gt;Content editor&lt;/code&gt; role. Still, on nodes tagged with a term of the &lt;code&gt;access&lt;/code&gt; vocabulary, if the user is tagged with the same term, the user has the permissions granted by the &lt;code&gt;Content editor in term&lt;/code&gt; role too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftech.sparkfabrik.com%2Fimages%2Fcontent%2Fdrupal-access-policy-demistified%2Fuser_roles.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftech.sparkfabrik.com%2Fimages%2Fcontent%2Fdrupal-access-policy-demistified%2Fuser_roles.png" alt="User roles configuration section"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Image: User roles configuration section&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The code for the &lt;code&gt;TermAccessPolicy.php&lt;/code&gt; class may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TermAccessPolicy&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AccessPolicyBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;SCOPE_TERM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'term'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;applies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$scope&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$scope&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SCOPE_TERM&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;calculatePermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;AccountInterface&lt;/span&gt; &lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$scope&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;RefinableCalculatedPermissionsInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$calculated_permissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;calculatePermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$scope&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$scope&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SCOPE_TERM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$calculated_permissions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="nv"&gt;$user_terms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'field_access'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;referencedEntities&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_terms&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$user_term&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nv"&gt;$cacheability&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CacheableMetadata&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
         &lt;span class="nv"&gt;$cacheability&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addCacheableDependency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_term&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

         &lt;span class="nv"&gt;$calculated_permissions&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CalculatedPermissionsItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                  &lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$permissions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;isAdmin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SCOPE_TERM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;identifier&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$user_term&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
               &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addCacheableDependency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cacheability&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$calculated_permissions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getPermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;AccountInterface&lt;/span&gt; &lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$extra_roles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
         &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'field_extra_role'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;referencedEntities&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$extra_roles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nv"&gt;$extra_role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$extra_roles&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$extra_role&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPermissions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getPersistentCacheContexts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user.terms'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the previous code, we've defined a new scope &lt;code&gt;term&lt;/code&gt; and we've implemented the &lt;code&gt;applies&lt;/code&gt; method to return &lt;code&gt;TRUE&lt;/code&gt; only if the scope is &lt;code&gt;term&lt;/code&gt;.&lt;br&gt;
Then, we calculate the permissions based on the terms the user is tagged with. We add a cacheable dependency on the term entity to invalidate the cache if the term changes.&lt;/p&gt;

&lt;p&gt;Note that we've passed two more arguments to the &lt;code&gt;addItem&lt;/code&gt; method: the &lt;code&gt;scope&lt;/code&gt; and the &lt;code&gt;identifier&lt;/code&gt;. The &lt;code&gt;scope&lt;/code&gt; is the string &lt;code&gt;term&lt;/code&gt;, and the &lt;code&gt;identifier&lt;/code&gt; is the term ID.&lt;/p&gt;

&lt;p&gt;We can register the policy in the service container with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;access_policy_demo.access_policy.term&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\access_policy_demo\Access\TermAccessPolicy&lt;/span&gt;
   &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;access_policy&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;getPersistentCacheContexts()&lt;/code&gt; uses a custom cache context, so we've to define it, too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserTermsCacheContext&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;CalculatedCacheContextInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;AccountInterface&lt;/span&gt; &lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getLabel&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"User's terms"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$term&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="nv"&gt;$user_terms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
         &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$loaded_term&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$loaded_term&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
         &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'field_access'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;referencedEntities&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$term&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;implode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$user_terms&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$term&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$user_terms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'true'&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'false'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getCacheableMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$term&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;CacheableMetadata&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CacheableMetadata&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setCacheTags&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'user:'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;()]);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A cache context needs to be registered in the service container, like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;cache_context.user.terms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\access_policy_demo\Access\UserTermsCacheContext&lt;/span&gt;
   &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@current_user'&lt;/span&gt;
   &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;cache.context&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we can use the new scope to check the permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;access_policy_demo_node_access&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;NodeInterface&lt;/span&gt; &lt;span class="nv"&gt;$node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$operation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;AccountInterface&lt;/span&gt; &lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;AccessResultInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nv"&gt;$access&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="c1"&gt;// This node is not under access control.&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$node&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'field_access'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;AccessResult&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="c1"&gt;// Always allow access to view the node.&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$operation&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'view'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;AccessResult&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="c1"&gt;// Check if the user has access to the node.&lt;/span&gt;
   &lt;span class="nv"&gt;$terms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$node&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'field_access'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;referencedEntities&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="nv"&gt;$type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$node&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$terms&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$term&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;\Drupal&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'access_policy_processor'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;processAccessPolicies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TermAccessPolicy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SCOPE_TERM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TermAccessPolicy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SCOPE_TERM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$term&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$operation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'update'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="nv"&gt;$access&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'edit any '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$type&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;' content'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$access&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'edit own '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$type&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;' content'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="nv"&gt;$access&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;$node&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getOwnerId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

         &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s1"&gt;'delete'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="nv"&gt;$access&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'delete any '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$type&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;' content'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$access&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'delete own '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$type&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;' content'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="nv"&gt;$access&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;$node&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getOwnerId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

         &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="nv"&gt;$access&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$access&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$access&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;AccessResult&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AccessResult&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;forbidden&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The previous code is just a rough example. Still, the critical thing to note is that we've used the &lt;code&gt;TermAccessPolicy::SCOPE_TERM&lt;/code&gt; and the term ID to retrieve a &lt;code&gt;CalculatedPermissionsItem&lt;/code&gt; that contains the permissions granted by the term to the user.&lt;/p&gt;

&lt;p&gt;What a long journey! But we're not done yet.&lt;/p&gt;

&lt;p&gt;One of the new system's most important features is that access policies are cached by context, but context can be dynamic and change during permission calculation; this is where the &lt;code&gt;initial cache context&lt;/code&gt; comes into play.&lt;/p&gt;

&lt;h3&gt;
  
  
  Variation cache
&lt;/h3&gt;

&lt;p&gt;Access policies usually vary by some context, like the user roles, the time of day, the domain, etc. Drupal has a concept of cache contexts that allows you to vary the cache based on some context, but until Drupal 10.2, this can be used only to add cache contexts to render arrays.&lt;/p&gt;

&lt;p&gt;Now, all caches can use cache contexts thanks to the Variation cache.&lt;/p&gt;

&lt;p&gt;Variation cache is not a new type of cache but a wrapper around the cache backends that already exist in Drupal. It has two interesting features.&lt;br&gt;
The first one is that it allows varying a cache by context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;cache.access_policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\Core\Cache\CacheBackendInterface&lt;/span&gt;
   &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;cache.bin&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
   &lt;span class="na"&gt;factory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@cache_factory'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
   &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;access_policy&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;variation_cache.access_policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\Core\Cache\VariationCacheInterface&lt;/span&gt;
   &lt;span class="na"&gt;factory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@variation_cache_factory'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
   &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;access_policy&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the previous example, &lt;code&gt;variation_cache.access_policy&lt;/code&gt; is a wrapper around &lt;code&gt;cache.access_policy&lt;/code&gt;. When I do something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;\Drupal&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'variation_cache.access_policy'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$cache&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'key1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'key2'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user.roles'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'languages:language_interface'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user.roles'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm saving &lt;code&gt;value&lt;/code&gt; at the &lt;code&gt;['key1', 'key2']&lt;/code&gt; of the &lt;code&gt;access_policy&lt;/code&gt; cache, and I'm telling the variation cache that the cache will vary by the &lt;code&gt;user.roles&lt;/code&gt; and &lt;code&gt;languages:language_interface&lt;/code&gt; contexts.&lt;/p&gt;

&lt;p&gt;Having not specified anything specific in the &lt;code&gt;tags&lt;/code&gt; section of the &lt;code&gt;cache.access_policy&lt;/code&gt; service, I get the default cache backend, typically the database one. I could have written:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;cache.bin.memory&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;default_backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;cache.backend.memory.memory&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To have a cache in memory.&lt;/p&gt;

&lt;p&gt;Variation cache uses cache contexts to build cache IDs. For example, the cid for the contexts &lt;code&gt;user.roles&lt;/code&gt; and &lt;code&gt;languages:language_interface&lt;/code&gt; when the user has roles 3 and 4, and the language is English could be something like: &lt;code&gt;key1:key2:[languages:language_interface]=en:[user.roles]=3,4&lt;/code&gt;. (Contexts are sorted in alphabetical order by name.)&lt;/p&gt;

&lt;p&gt;The second feature comes from the fact that when I save data in the variation cache, I can specify two sets of contexts: the actual ones to vary the cache on (the third argument of the &lt;code&gt;set&lt;/code&gt; method) and the "initial" ones (the fourth argument of the &lt;code&gt;set&lt;/code&gt; method).&lt;/p&gt;

&lt;p&gt;But what are these initial cache contexts? They are the ones that our data varies for sure, but they could not be the only ones. If, during the building of the data to cache, someone else adds one or more specific contexts, the cache system may not be aware of it.&lt;/p&gt;

&lt;p&gt;When the set of final cache contexts is more specific than the initial ones, the variation cache stores a cache with an ID built using the initial cache contexts. That cache will not store the data but a redirect that contains the final cache contexts to use to find the actual data. This chain of redirects can span more than one level.&lt;/p&gt;

&lt;p&gt;Let's add more complexity to our previous example about the &lt;code&gt;TermAccessPolicy&lt;/code&gt;. Suppose that terms in the &lt;code&gt;access&lt;/code&gt; vocabulary have a select field named &lt;code&gt;is_restricted&lt;/code&gt;, with two values: &lt;code&gt;Weekend&lt;/code&gt; and &lt;code&gt;Weekdays&lt;/code&gt;. We want to grant the permissions not only if the node is tagged with a term but also based on the day of the week.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftech.sparkfabrik.com%2Fimages%2Fcontent%2Fdrupal-access-policy-demistified%2Fterm_edit.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftech.sparkfabrik.com%2Fimages%2Fcontent%2Fdrupal-access-policy-demistified%2Fterm_edit.png" alt="User roles configuration section"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Image: Add a restriction to an access term&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If no restrictions are set, the permissions are granted as usual. If a restriction is set, the permissions are only granted if the current day matches the restriction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_terms&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$user_term&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nv"&gt;$cacheability&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CacheableMetadata&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="nv"&gt;$cacheability&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addCacheableDependency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_term&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="nv"&gt;$restricted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isRestricted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user_term&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$restricted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$cacheability&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addCacheContexts&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'is_restricted'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="nv"&gt;$permissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$permissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$account&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="nv"&gt;$calculated_permissions&lt;/span&gt;
      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
         &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CalculatedPermissionsItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$permissions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;isAdmin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SCOPE_TERM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;identifier&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$user_term&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
         &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addCacheableDependency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cacheability&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$calculated_permissions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;isRestricted&lt;/code&gt; method can be implemented as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;isRestricted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;TermInterface&lt;/span&gt; &lt;span class="nv"&gt;$user_term&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nv"&gt;$restriction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$user_term&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'field_restriction'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$restriction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$restriction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="nv"&gt;$field_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$restriction&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$field_value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'weekend'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nc"&gt;IsWeekendCacheContext&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;isWeekend&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$field_value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'weekdays'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nc"&gt;IsWeekendCacheContext&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;isWeekend&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;RestrictedCacheContext&lt;/code&gt; class can be like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RestrictedCacheContext&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;CalculatedCacheContextInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getLabel&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Is Weekend?'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$parameter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;isWeekend&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'weekend'&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'weekday'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"is_restricted.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;isWeekend&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'w'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getCacheableMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$parameter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;CacheableMetadata&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CacheableMetadata&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now suppose to have two terms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Section1&lt;/code&gt; (tid=1) with the restriction set to &lt;code&gt;Weekend&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Section2&lt;/code&gt; (tid=2) with no restrictions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And two users:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;User1&lt;/code&gt; tagged with &lt;code&gt;Section1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;User2&lt;/code&gt; tagged with &lt;code&gt;Section2&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And we're on Sunday.&lt;/p&gt;

&lt;p&gt;When permissions are calculated for &lt;code&gt;User1&lt;/code&gt;, the initial cache context will be &lt;code&gt;user.terms&lt;/code&gt;, but then we'll add the &lt;code&gt;is_restricted&lt;/code&gt; cache context because the &lt;code&gt;Section1&lt;/code&gt; term is restricted.&lt;br&gt;
When permissions are calculated for &lt;code&gt;User2&lt;/code&gt; the initial cache context will be &lt;code&gt;user.terms&lt;/code&gt;, and no other cache context will be added.&lt;/p&gt;

&lt;p&gt;The cache will be something like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;access_policies:term:[user.terms]=2&lt;/code&gt; =&amp;gt; &lt;code&gt;Drupal\Core\Session\RefinableCalculatedPermissions&lt;/code&gt;&lt;br&gt;
&lt;code&gt;access_policies:term:[user.terms]=1&lt;/code&gt; =&amp;gt; &lt;code&gt;Drupal\Core\Cache\CacheRedirect&lt;/code&gt; (&lt;code&gt;cacheContexts: ["user.terms", "is_restricted"]&lt;/code&gt;)&lt;br&gt;
&lt;code&gt;access_policies:term:[is_restricted]=is_restricted.weekend:[user.terms]=1&lt;/code&gt; =&amp;gt; &lt;code&gt;Drupal\Core\Session\RefinableCalculatedPermissions&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The Variation cache stores the data for access policies that vary only by &lt;code&gt;user.terms&lt;/code&gt; directly.&lt;br&gt;
For the access policies that also vary by &lt;code&gt;is_restricted,&lt;/code&gt; it stores a redirect (along with information about the final cache contexts to look for: &lt;code&gt;user.terms&lt;/code&gt; and &lt;code&gt;is_restricted&lt;/code&gt;).&lt;br&gt;
To access a cache with more final cache contexts than the initial ones, the variation cache will need to follow a chain of redirects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The new Access Policy API is a powerful tool that allows the implementation of complex access control systems in Drupal.&lt;br&gt;
It's a big step forward from the old system based on only roles and permissions.&lt;/p&gt;

&lt;p&gt;In the future, we'll see more and more contrib modules that will use this new system to convert custom logic to the new system. At SparkFabrik, we've already started using it in custom modules for our customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources:
&lt;/h2&gt;

&lt;p&gt;I've set up a GitHub repository with the code used in this blog post: &lt;a href="https://github.com/lussoluca/access_policy_demo" rel="noopener noreferrer"&gt;https://github.com/lussoluca/access_policy_demo&lt;/a&gt;. You can clone it and use &lt;a href="https://ddev.com/" rel="noopener noreferrer"&gt;DDEV&lt;/a&gt; to run the code.&lt;/p&gt;

&lt;p&gt;This blog post would not have been possible without the help of the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.thedroptimes.com/40119/kristiaan-van-den-eynde-talks-about-policy-based-access-in-core" rel="noopener noreferrer"&gt;https://www.thedroptimes.com/40119/kristiaan-van-den-eynde-talks-about-policy-based-access-in-core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.drupal.org/node/3365546" rel="noopener noreferrer"&gt;https://www.drupal.org/node/3365546&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.drupal.org/node/3385551" rel="noopener noreferrer"&gt;https://www.drupal.org/node/3385551&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.drupal.org/node/2910500" rel="noopener noreferrer"&gt;https://www.drupal.org/node/2910500&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.drupal.org/node/3411485" rel="noopener noreferrer"&gt;https://www.drupal.org/node/3411485&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.drupal.org/docs/develop/drupal-apis/access-policy-api" rel="noopener noreferrer"&gt;https://www.drupal.org/docs/develop/drupal-apis/access-policy-api&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bpekker.dev/access-policy-api/" rel="noopener noreferrer"&gt;https://bpekker.dev/access-policy-api/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?app=desktop&amp;amp;v=pbAUselOJy0" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?app=desktop&amp;amp;v=pbAUselOJy0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>drupal</category>
      <category>opensource</category>
      <category>security</category>
    </item>
    <item>
      <title>Drupal new contributions releases</title>
      <dc:creator>Lusso Luca</dc:creator>
      <pubDate>Tue, 21 Nov 2023 09:00:00 +0000</pubDate>
      <link>https://dev.to/sparkfabrik/drupal-new-contributions-releases-43kd</link>
      <guid>https://dev.to/sparkfabrik/drupal-new-contributions-releases-43kd</guid>
      <description>&lt;p&gt;A list of new releases and fixes on Drupal modules maintained in SparkFabrik.&lt;/p&gt;

&lt;h2&gt;
  
  
  Release day
&lt;/h2&gt;

&lt;p&gt;On November 8 2023, I updated a couple of modules that I maintain. No significant changes, just some bug fixes and improvements.&lt;/p&gt;

&lt;p&gt;The biggest change is that I've switched to GitlabCI for continuous integration. Now, all the modules are tested with phpcs, PHPStan, Stylelint, Eslint, and PHPUnit.&lt;br&gt;
The integration with the new CI system provided by the Drupal Association is pretty simple to use; just follow the documentation: &lt;a href="https://www.drupal.org/node/3356364"&gt;https://www.drupal.org/node/3356364&lt;/a&gt;.&lt;br&gt;
I've made some changes to the standard template to be sure that the pipelines fail in case of errors (take a look at the &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file in the WebProfiler module: &lt;a href="https://git.drupalcode.org/project/webprofiler/-/blob/10.1.x/.gitlab-ci.yml"&gt;https://git.drupalcode.org/project/webprofiler/-/blob/10.1.x/.gitlab-ci.yml&lt;/a&gt;).&lt;br&gt;
The provided template looks for configuration files for phpcs and PHPStan, and you can see how I've configured them here: &lt;a href="https://git.drupalcode.org/project/webprofiler/-/blob/10.1.x/phpstan.neon"&gt;https://git.drupalcode.org/project/webprofiler/-/blob/10.1.x/phpstan.neon&lt;/a&gt; and here: &lt;a href="https://git.drupalcode.org/project/webprofiler/-/blob/10.1.x/phpcs.xml.dist"&gt;https://git.drupalcode.org/project/webprofiler/-/blob/10.1.x/phpcs.xml.dist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;PHPStan is set to level 5, not the maximum level, but it's a good start. I've also added a set of rules to the phpcs configuration file that comes from the &lt;code&gt;previousnext/coding-standard&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;Now, let's see what's new in the modules.&lt;/p&gt;
&lt;h2&gt;
  
  
  WebProfiler 10.1.3
&lt;/h2&gt;

&lt;p&gt;In the new Drupal 10.1, the Database API can trigger events on the execution of statements. Previously, the module was using the logging capabilities of the Database API to collect the queries, and now it uses the new events. This new system can be used to analyze better the database layer of Drupal.&lt;/p&gt;

&lt;p&gt;The routing data collector now shows more information about the page title and the controller that will be used to render the page.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tracer 1.0.1
&lt;/h2&gt;

&lt;p&gt;A couple of bug fixes have been made to the module behind the WebProfiler module.&lt;/p&gt;

&lt;p&gt;At the moment, Tracer is not actively used by any other module, but its future is bright. I'm planning to start working again on the Observability suite module (&lt;a href="https://www.drupal.org/project/o11y"&gt;https://www.drupal.org/project/o11y&lt;/a&gt;), and Tracer is the tool that will be used to collect the data.&lt;/p&gt;
&lt;h2&gt;
  
  
  Monolog 3.0.1
&lt;/h2&gt;

&lt;p&gt;Again, there are no big changes here, but a simple new feature that can improve the analysis of the logs produced by Drupal.&lt;/p&gt;

&lt;p&gt;Due to some internal implementation, the batch operations invoked by Drush don't work if the line formatter set in Monolog produces a JSON string.&lt;br&gt;
Having the logs in JSON format is useful to analyze them with tools provided by all the major cloud providers. Until now, if you have to run some Drush commands that internally use batch operations, you have to switch to the &lt;code&gt;drush&lt;/code&gt; formatter. The latest version of the Monolog module adds a new conditional formatter that can be used to choose which formatter to use based on some condition. For example, if you want to use the JSON formatter for all the logs except for the ones produced by Drush, you can use the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;monolog.formatter.drush_or_json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Drupal\monolog\Logger\Formatter\ConditionalFormatter&lt;/span&gt;
  &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@monolog.formatter.drush'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@monolog.formatter.json'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@monolog.condition_resolver.cli'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;shared&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first two arguments are the formatters to use, and the third argument is the condition resolver. The condition resolver is a service that can be used to check if a condition is true or false. In this case, we are using the &lt;code&gt;cli&lt;/code&gt; condition resolver, which checks if the current request is a request that comes from the command line.&lt;br&gt;
You can define any condition resolver you want. Just implement the &lt;code&gt;Drupal\monolog\Logger\ConditionResolver\ConditionResolverInterface&lt;/code&gt; interface.&lt;br&gt;
The &lt;code&gt;drush_or_json&lt;/code&gt; is already provided by the module, you can use it directly (for example in a &lt;code&gt;monolog.service.yml&lt;/code&gt; file):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;monolog.level.info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;info&lt;/span&gt;
  &lt;span class="na"&gt;monolog.channel_handlers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;handlers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;website'&lt;/span&gt;
          &lt;span class="na"&gt;formatter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;drush_or_json'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;monolog.handler.website&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Monolog\Handler\StreamHandler&lt;/span&gt;
    &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;php://stdout'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%monolog.level.info%'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the Monolog module readme file for more information: &lt;a href="https://git.drupalcode.org/project/monolog/blob/HEAD/README.md"&gt;https://git.drupalcode.org/project/monolog/blob/HEAD/README.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the next month, I'll concentrate on some other interesting things, like improving the Symfony Messenger module (&lt;a href="https://www.drupal.org/project/symfony_messenger"&gt;https://www.drupal.org/project/symfony_messenger&lt;/a&gt;), and add profiling support to Drush.&lt;/p&gt;

</description>
      <category>drupal</category>
      <category>monolog</category>
      <category>tracer</category>
      <category>webprofiler</category>
    </item>
    <item>
      <title>SDC in Drupal core</title>
      <dc:creator>Lusso Luca</dc:creator>
      <pubDate>Thu, 14 Sep 2023 09:00:00 +0000</pubDate>
      <link>https://dev.to/sparkfabrik/sdc-in-drupal-core-npi</link>
      <guid>https://dev.to/sparkfabrik/sdc-in-drupal-core-npi</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;There are a lot of contributed modules to build and manage a Drupal theme using components, but none of them has ever been elected as the de facto standard.&lt;/p&gt;

&lt;p&gt;However, things have changed with the release of Drupal 10.1 in June 2023. A specific implementation was chosen for inclusion in the Drupal core. Initially developed as a contributed module named &lt;a href="https://www.drupal.org/project/cl_components" rel="noopener noreferrer"&gt;&lt;em&gt;Component Libraries: Components&lt;/em&gt;&lt;/a&gt;, this functionality has been merged into Drupal core as the new &lt;a href="https://www.drupal.org/node/3355112" rel="noopener noreferrer"&gt;&lt;em&gt;SDC experimental module&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is SDC?
&lt;/h2&gt;

&lt;p&gt;SDC stands for &lt;em&gt;Single Directory Components&lt;/em&gt;. It is a way to organize components in a single directory instead of splitting them across different folders. This approach is similar to the one used by &lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt; and &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The main advantage of this solution is that it &lt;em&gt;is easier to find the code that describes a component&lt;/em&gt;. It also simplifies the process of creating a new component, as you just need to create a new folder and add a some files to it.&lt;/p&gt;

&lt;p&gt;Finally, it is easier to &lt;em&gt;share components&lt;/em&gt; between different projects, as you can just copy the component folder from one project to another (SDC then allows you to override assets like CSS and JavaScript to adapt the component to the design system of the new project).&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use SDC?
&lt;/h2&gt;

&lt;p&gt;The simplest example we can think of is a button component, but let's try to complicate things a bit by adding some JavaScript and an image.&lt;/p&gt;

&lt;p&gt;The first step is to enable the SDC experimental module. Then you need to create a new folder named &lt;code&gt;components&lt;/code&gt; in the theme folder or in one of your custom modules (yes, you can have more than one &lt;code&gt;components&lt;/code&gt; folder). Those folders contain all the components that will be available in your theme.&lt;/p&gt;

&lt;p&gt;The second step is to define a new component. Create a new folder named &lt;code&gt;button&lt;/code&gt; in the &lt;code&gt;components&lt;/code&gt; folder. This folder will contain all the files related to the button component.&lt;/p&gt;

&lt;p&gt;Next, you'll find the code for those files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;button.component.yml&lt;/code&gt;: metadata of the component (name, description, props, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;button.twig&lt;/code&gt;: Twig code of the component&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;button.css&lt;/code&gt;: CSS code of the component&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;button.js&lt;/code&gt;: JavaScript code of the component&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;icons/cog.svg&lt;/code&gt;: cog icon&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;README.md&lt;/code&gt;: component's documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note how all the files related to the component are named after the component itself, this is mandatory to allow Drupal to automatically declare a library for the component.&lt;/p&gt;

&lt;p&gt;Let's start with &lt;code&gt;button.component.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$schema'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://git.drupalcode.org/project/drupal/-/raw/10.1.x/core/modules/sdc/src/metadata.schema.json'&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;button&lt;/span&gt;
&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stable&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A simple button&lt;/span&gt;
&lt;span class="na"&gt;libraryOverrides&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;core/once&lt;/span&gt;
&lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;title&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Button title&lt;/span&gt;
      &lt;span class="na"&gt;examples&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Click&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;me'&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Type&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Button type&lt;/span&gt;
      &lt;span class="na"&gt;examples&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;primary'&lt;/span&gt;
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Icon&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Button icon&lt;/span&gt;
      &lt;span class="na"&gt;examples&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cog.svg'&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Message&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Popup message&lt;/span&gt;
      &lt;span class="na"&gt;examples&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Lorem&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ipsum'&lt;/span&gt;
  &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;title&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;type&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;icon&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;message&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Twig file may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt; &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="err"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;attributes.addClass&lt;/span&gt;&lt;span class="err"&gt;('&lt;/span&gt;&lt;span class="na"&gt;button&lt;/span&gt;&lt;span class="err"&gt;')&lt;/span&gt; &lt;span class="err"&gt;}}&lt;/span&gt; &lt;span class="na"&gt;data-message=&lt;/span&gt;&lt;span class="s"&gt;"{{ message|default('No message') }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"{{ type }} image-button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/{{ componentMetadata.path }}/icons/{{ icon }}"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Image"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{ title }}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All the properties defined in the &lt;code&gt;button.component.yml&lt;/code&gt; file are available in the Twig file as variables. The &lt;code&gt;componentMetadata&lt;/code&gt; variable contains some helpful information, like the path of the component on the filesystem.&lt;/p&gt;

&lt;p&gt;We then need some CSS to style the button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.image-button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#124354&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.image-button&lt;/span&gt; &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To add interactivity to the button we need some JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;Drupal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;once&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Drupal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;behaviors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;at_button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;})(&lt;/span&gt;&lt;span class="nx"&gt;Drupal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;once&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the component is ready to be included in one of the Twig templates of your theme (the ones in the &lt;code&gt;templates&lt;/code&gt; folder):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;include&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'example_theme:button'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Click me'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'type'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'primary'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'icon'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'cog.svg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'message'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Lorem ipsum'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nv"&gt;with_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final result should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm3vmvi31enw9gy70gdiz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm3vmvi31enw9gy70gdiz.png" alt="The button component rendered in page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try to click on the button and see what happens.&lt;/p&gt;

&lt;p&gt;Starting from &lt;a href="https://github.com/Chi-teck/drupal-code-generator" rel="noopener noreferrer"&gt;drupal-code-generator&lt;/a&gt; version 3.2.0, a new generator has been added to easily create new components (this replaces the &lt;a href="https://www.drupal.org/project/cl_generator" rel="noopener noreferrer"&gt;Component Libraries: Generator&lt;/a&gt; module that is not compatible with Drush 12). Just run this command and follow the instructions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;drush generate sdc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  WebProfiler integration
&lt;/h2&gt;

&lt;p&gt;The latest version of the &lt;a href="https://www.drupal.org/project/webprofiler" rel="noopener noreferrer"&gt;WebProfiler&lt;/a&gt; module (10.1.1) offers full support for collecting and exploring the components used on a page.&lt;/p&gt;

&lt;p&gt;On the WebProfiler toolbar, a new counter has been added to the &lt;code&gt;theme&lt;/code&gt; widget, showing the number of components used on the page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feesbp0cf1lnee7h6fuzg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feesbp0cf1lnee7h6fuzg.png" alt="The SDC data collector in the WebProfiler toolbar"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking on the counter will open the WebProfiler dashboard, showing all the components used on the page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frixkiwmsljw32zpzjgml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frixkiwmsljw32zpzjgml.png" alt="The SDC data collector in the WebProfiler dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the time of writing, a &lt;a href="https://www.drupal.org/project/drupal/issues/3375843" rel="noopener noreferrer"&gt;core patch&lt;/a&gt; is required to enable this feature; be sure to apply it before enabling the SDC module.&lt;/p&gt;

&lt;p&gt;Another new feature is the ability to explore the &lt;em&gt;list of libraries&lt;/em&gt; used on the page (under the &lt;code&gt;Asset&lt;/code&gt; data collector). As SDC automatically declares a library for each component, you can use the new pane to find the one used by the button component:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi448t4pmf4l83owxbf3i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi448t4pmf4l83owxbf3i.png" alt="The libraries data collector in the WebProfiler dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;SDC is becoming the de facto standard for building Drupal components, with emerging modules (like &lt;a href="https://www.drupal.org/project/nomarkup" rel="noopener noreferrer"&gt;No Markup&lt;/a&gt; and &lt;a href="https://www.drupal.org/project/sdc_display" rel="noopener noreferrer"&gt;Single Directory Components: Display&lt;/a&gt;) and best practices. More will come in the future.&lt;/p&gt;

&lt;p&gt;In this article we have just scratched the surface of this new feature, to learn more we recommend you to read the just published &lt;a href="https://packt.link/CGNe7" rel="noopener noreferrer"&gt;Modernizing Drupal 10 Theme Development&lt;/a&gt; book.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlkyfrspd8pg968jaeou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlkyfrspd8pg968jaeou.png" alt="Modernizing Drupal 10 Theme Development"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the book you'll find a detailed explanation about how themes work in Drupal 10, how to use SDC, how to integrate with tools like &lt;em&gt;Browsersync&lt;/em&gt; and &lt;em&gt;Backstop.js&lt;/em&gt; and how to expose your work to frameworks like &lt;em&gt;Storybook&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>drupal</category>
      <category>components</category>
      <category>sdc</category>
      <category>webprofiler</category>
    </item>
  </channel>
</rss>
