Problem/Motivation

Since #2572605: Make Drupal 8 optionally working with Twig 2, while keeping its dependency on Twig 1 there is a merge method on Attributes, which was the original focus of this issue.

But from #17 below this issue pivots to discussing potential improvements to the current merge method.

Proposed resolution

Remaining tasks

/

User interface changes

/

API changes

Data model changes

/

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

aspilicious created an issue. See original summary.

aspilicious’s picture

Status: Active » Needs review
FileSize
2.42 KB
star-szr’s picture

Status: Needs review » Closed (duplicate)
joelpittet’s picture

Version: 8.0.x-dev » 8.1.x-dev
Status: Closed (duplicate) » Active

Actually re-opening:) We have a proposal to fix the other in 8.0.x and this can be the 8.1 issue.

joelpittet’s picture

Title: Add a mergeAttributes function » Add a merge() method on Attribute objects
star-szr’s picture

lauriii’s picture

lauriii’s picture

Status: Active » Needs review
FileSize
2.77 KB

Posting latest patch from the other issue

aspilicious’s picture

Status: Needs review » Needs work
       '__toString',
+      'merge',
     ]);

This is not needed and dangerous :)

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.0-beta1 was released on March 2, 2016, which means new developments and disruptive changes should now be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

sun’s picture

In our project, we're trying to merge an attributes object with dynamic, case-specific attributes into a default attributes object, for which we require the merge() method, too.

We have already implemented the method in the corresponding JavaScript adapter for TwigJS some time ago and it has proven to be a big help: https://github.com/netzstrategen/twig-drupal/pull/15

However, we came to realize that it is not always possible to determine whether an Attributes object is defined or empty or populated, especially with regard to the internal classes property. Therefore I would recommend to make the argument nullable:

+++ b/core/lib/Drupal/Core/Template/Attribute.php
-  public function merge(Attribute $attribute) {
+  public function merge(?Attribute $attribute) {
+    if (!isset($attribute)) {
+      return $this;
+    }

That said, in the JavaScript port we also accept plain objects (associative arrays) as data to be merged. It would make sense to also support that here; at least I can't see a reason for not doing so.

markhalliwell’s picture

Potentially related issue.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

sun’s picture

As Twig is automatically type-casting many values, we not only removed the type-hint, but are additionally checking very explicitly for the type of the passed variable to be merged (see patch below).

Will the core components separated into standalone packages soon?

diff --git a/vendor/drupal/core/Attribute.php b/vendor/drupal/core/Attribute.php
index 5449cde..e752340 100644
--- a/vendor/drupal/core/Attribute.php
+++ b/vendor/drupal/core/Attribute.php
@@ -280,6 +280,32 @@ public function hasClass($class) {
   }
 
   /**
+   * Merges the values of another Attributes object with this one.
+   *
+   * @param \Drupal\Core\Template\Attribute|array
+   *   The other Attribute object.
+   *
+   * @return $this
+   */
+  public function merge($attribute) {
+    if (!isset($attribute)) {
+      return $this;
+    }
+    if (!is_array($attribute) && !$attribute instanceof Attribute) {
+      ob_start();
+      var_dump($attribute);
+      error_log('Attribute: Unable to merge non-array argument into attributes: ' . trim(\ob_get_clean()));
+      return $this;
+    }
+    $merged_array = array_merge_recursive($this->toArray(), $attribute instanceof Attribute ? $attribute->toArray() : $attribute);
+    foreach ($merged_array as $attribute => $value) {
+      $this->setAttribute($attribute, $value);
+    }
+
+    return $this;
+  }
+
+  /**
    * Implements the magic __toString() method.
    */
   public function __toString() {

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

sun’s picture

The merge method changes the Attributes object instead of cloning/forking into a new one, which causes unexpected behaviors and problems in loops in Twig templates, for example, when generating the output for a hierarchical navigation menu. We adjusted the method to instantiate a new class instead of modifying the existing object.

This makes the behavior of Attributes.merge consistent with the Twig |merge filter.

This only happens when data is merged, but not in the error/early-return conditions, which is an inconsistent behavior. However, I think this is a larger problem with the current Attributes objects; the same problem also exists for addClass(), setAttribute(), and other methods inside of loops. A copy() or clone() method would be helpful and perhaps more appropriate, so the user can control when a new object is necessary.

diff --git a/vendor/drupal/core/Attribute.php b/vendor/drupal/core/Attribute.php
index 5449cde..e752340 100644
--- a/vendor/drupal/core/Attribute.php
+++ b/vendor/drupal/core/Attribute.php
@@ -280,6 +280,28 @@ public function hasClass($class) {
   }
 
   /**
+   * Merges the values of another Attributes object with this one.
+   *
+   * @param \Drupal\Core\Template\Attribute|array
+   *   The other Attribute object.
+   *
+   * @return $this
+   */
+  public function merge($attribute) {
+    if (!isset($attribute)) {
+      return $this;
+    }
+    if (!is_array($attribute) && !$attribute instanceof Attribute) {
+      ob_start();
+      var_dump($attribute);
+      error_log('Attribute: Unable to merge non-array argument into attributes: ' . trim(\ob_get_clean()));
+      return $this;
+    }
+    $merged_array = array_merge_recursive($this->toArray(), $attribute instanceof Attribute ? $attribute->toArray() : $attribute);
+    return new static($merged_array);
+  }
+
+  /**
    * Implements the magic __toString() method.
    */
   public function __toString() {

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

jonathanshaw’s picture

Title: Add a merge() method on Attribute objects » Improve the merge() method on Attribute objects
Issue summary: View changes

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.