|
| 1 | +``html_attr_merge`` |
| 2 | +=================== |
| 3 | + |
| 4 | +.. _html_attr_merge: |
| 5 | + |
| 6 | +.. versionadded:: 3.24 |
| 7 | + |
| 8 | + The ``html_attr_merge`` filter was added in Twig 3.24. |
| 9 | + |
| 10 | +The ``html_attr_merge`` filter merges multiple mappings that represent |
| 11 | +HTML attribute values. Such mappings contain the names of the HTML attributes |
| 12 | +as keys, and the corresponding values represent the attributes' values. |
| 13 | + |
| 14 | +It is primarily designed for working with arrays that are passed to the |
| 15 | +:ref:`html_attr` function. It closely resembles the :doc:`merge <../filters/merge>` |
| 16 | +filter, but has different merge behavior for values that are iterables |
| 17 | +themselves, as it will merge such values in turn. |
| 18 | + |
| 19 | +The filter returns a new merged array: |
| 20 | + |
| 21 | +.. code-block:: twig |
| 22 | +
|
| 23 | + {% set base = {class: ['btn'], type: 'button'} %} |
| 24 | + {% set variant = {class: ['btn-primary'], disabled: true} %} |
| 25 | +
|
| 26 | + {% set merged = base|html_attr_merge(variant) %} |
| 27 | +
|
| 28 | + {# merged is now: { |
| 29 | + class: ['btn', 'btn-primary'], |
| 30 | + type: 'button', |
| 31 | + disabled: true |
| 32 | + } #} |
| 33 | +
|
| 34 | +The filter accepts multiple arrays as arguments and merges them from left to right: |
| 35 | + |
| 36 | +.. code-block:: twig |
| 37 | +
|
| 38 | + {% set merged = base|html_attr_merge(variant1, variant2, variant3) %} |
| 39 | +
|
| 40 | +A common use case is to build attribute mappings conditionally by merging multiple |
| 41 | +parts based on conditions. To make this conditional merging more convenient, filter |
| 42 | +arguments that are ``false``, ``null`` or empty arrays are ignored: |
| 43 | + |
| 44 | +.. code-block:: twig |
| 45 | +
|
| 46 | + {% set button_attrs = { |
| 47 | + type: 'button', |
| 48 | + class: ['btn'] |
| 49 | + }|html_attr_merge( |
| 50 | + variant == 'primary' ? { class: ['btn-primary'] }, |
| 51 | + variant == 'secondary' ? { class: ['btn-secondary'] }, |
| 52 | + size == 'large' ? { class: ['btn-lg'] }, |
| 53 | + size == 'small' ? { class: ['btn-sm'] }, |
| 54 | + disabled ? { disabled: true, class: ['btn-disabled'] }, |
| 55 | + loading ? { 'aria-busy': 'true', class: ['btn-loading'] }, |
| 56 | + ) %} |
| 57 | +
|
| 58 | + {# Example with variant='primary', size='large', disabled=false, loading=true: |
| 59 | +
|
| 60 | + The false values (secondary variant, small size, disabled state) are ignored. |
| 61 | +
|
| 62 | + button_attrs is: |
| 63 | + { |
| 64 | + type: 'button', |
| 65 | + class: ['btn', 'btn-primary', 'btn-lg', 'btn-loading'], |
| 66 | + 'aria-busy': 'true' |
| 67 | + } |
| 68 | + #} |
| 69 | +
|
| 70 | +Merging Rules |
| 71 | +------------- |
| 72 | + |
| 73 | +The filter follows these rules when merging attribute values: |
| 74 | + |
| 75 | +**Scalar values**: Later values override earlier ones. |
| 76 | + |
| 77 | +.. code-block:: twig |
| 78 | +
|
| 79 | + {% set result = {id: 'old'}|html_attr_merge({id: 'new'}) %} |
| 80 | + {# result: {id: 'new'} #} |
| 81 | +
|
| 82 | +**Array values**: Arrays are merged like in PHP's ``array_merge`` function - numeric keys are |
| 83 | +appended, non-numeric keys replace. |
| 84 | + |
| 85 | +.. code-block:: twig |
| 86 | +
|
| 87 | + {# Numeric keys (appended): #} |
| 88 | + {% set result = {class: ['btn']}|html_attr_merge({class: ['btn-primary']}) %} |
| 89 | + {# result: {class: ['btn', 'btn-primary']} #} |
| 90 | +
|
| 91 | + {# Non-numeric keys (replaced): #} |
| 92 | + {% set result = {class: {base: 'btn', size: 'small'}}|html_attr_merge({class: {variant: 'primary', size: 'large'}}) %} |
| 93 | + {# result: {class: {base: 'btn', size: 'large', variant: 'primary'}} #} |
| 94 | +
|
| 95 | +.. note:: |
| 96 | + |
| 97 | + Remember, attribute mappings passed to or returned from this filter are regular |
| 98 | + Twig mappings after all. If you want to completely replace an attribute value |
| 99 | + that is an iterable with another value, you can use the :doc:`merge <../filters/merge>` |
| 100 | + filter to do that. |
| 101 | + |
| 102 | +**``MergeableInterface`` implementations**: For advanced use cases, attribute values can be objects |
| 103 | +that implement the ``MergeableInterface``. These objects can define their own, custom merge |
| 104 | +behavior that takes precedence over the default rules. See the docblocks in that interface |
| 105 | +for details. |
| 106 | + |
| 107 | +.. note:: |
| 108 | + |
| 109 | + The ``html_attr_merge`` filter is part of the ``HtmlExtension`` which is not |
| 110 | + installed by default. Install it first: |
| 111 | + |
| 112 | + .. code-block:: bash |
| 113 | +
|
| 114 | + $ composer require twig/html-extra |
| 115 | +
|
| 116 | + Then, on Symfony projects, install the ``twig/extra-bundle``: |
| 117 | + |
| 118 | + .. code-block:: bash |
| 119 | +
|
| 120 | + $ composer require twig/extra-bundle |
| 121 | +
|
| 122 | + Otherwise, add the extension explicitly on the Twig environment:: |
| 123 | + |
| 124 | + use Twig\Extra\Html\HtmlExtension; |
| 125 | + |
| 126 | + $twig = new \Twig\Environment(...); |
| 127 | + $twig->addExtension(new HtmlExtension()); |
| 128 | + |
| 129 | +Arguments |
| 130 | +--------- |
| 131 | + |
| 132 | +The filter accepts a variadic list of arguments to merge. Each argument can be: |
| 133 | + |
| 134 | +* A map of attributes |
| 135 | +* ``false`` or ``null`` (ignored, useful for conditional merging) |
| 136 | +* An empty string ``''`` (ignored, to support implicit else in ternary operators) |
| 137 | + |
| 138 | +.. seealso:: |
| 139 | + |
| 140 | + :ref:`html_attr`, |
| 141 | + :doc:`html_attr_type` |
0 commit comments