Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\Core\Render;
|
Chris@0
|
4
|
Chris@17
|
5 use Drupal\Component\Render\FormattableMarkup;
|
Chris@0
|
6 use Drupal\Core\Access\AccessResultInterface;
|
Chris@0
|
7
|
Chris@0
|
8 /**
|
Chris@0
|
9 * Provides helper methods for Drupal render elements.
|
Chris@0
|
10 *
|
Chris@0
|
11 * @see \Drupal\Core\Render\Element\ElementInterface
|
Chris@0
|
12 *
|
Chris@0
|
13 * @ingroup theme_render
|
Chris@0
|
14 */
|
Chris@0
|
15 class Element {
|
Chris@0
|
16
|
Chris@0
|
17 /**
|
Chris@0
|
18 * Checks if the key is a property.
|
Chris@0
|
19 *
|
Chris@0
|
20 * @param string $key
|
Chris@0
|
21 * The key to check.
|
Chris@0
|
22 *
|
Chris@0
|
23 * @return bool
|
Chris@0
|
24 * TRUE of the key is a property, FALSE otherwise.
|
Chris@0
|
25 */
|
Chris@0
|
26 public static function property($key) {
|
Chris@0
|
27 return $key[0] == '#';
|
Chris@0
|
28 }
|
Chris@0
|
29
|
Chris@0
|
30 /**
|
Chris@0
|
31 * Gets properties of a structured array element (keys beginning with '#').
|
Chris@0
|
32 *
|
Chris@0
|
33 * @param array $element
|
Chris@0
|
34 * An element array to return properties for.
|
Chris@0
|
35 *
|
Chris@0
|
36 * @return array
|
Chris@0
|
37 * An array of property keys for the element.
|
Chris@0
|
38 */
|
Chris@0
|
39 public static function properties(array $element) {
|
Chris@0
|
40 return array_filter(array_keys($element), 'static::property');
|
Chris@0
|
41 }
|
Chris@0
|
42
|
Chris@0
|
43 /**
|
Chris@0
|
44 * Checks if the key is a child.
|
Chris@0
|
45 *
|
Chris@0
|
46 * @param string $key
|
Chris@0
|
47 * The key to check.
|
Chris@0
|
48 *
|
Chris@0
|
49 * @return bool
|
Chris@0
|
50 * TRUE if the element is a child, FALSE otherwise.
|
Chris@0
|
51 */
|
Chris@0
|
52 public static function child($key) {
|
Chris@0
|
53 return !isset($key[0]) || $key[0] != '#';
|
Chris@0
|
54 }
|
Chris@0
|
55
|
Chris@0
|
56 /**
|
Chris@0
|
57 * Identifies the children of an element array, optionally sorted by weight.
|
Chris@0
|
58 *
|
Chris@0
|
59 * The children of a element array are those key/value pairs whose key does
|
Chris@0
|
60 * not start with a '#'. See drupal_render() for details.
|
Chris@0
|
61 *
|
Chris@0
|
62 * @param array $elements
|
Chris@0
|
63 * The element array whose children are to be identified. Passed by
|
Chris@0
|
64 * reference.
|
Chris@0
|
65 * @param bool $sort
|
Chris@0
|
66 * Boolean to indicate whether the children should be sorted by weight.
|
Chris@0
|
67 *
|
Chris@0
|
68 * @return array
|
Chris@0
|
69 * The array keys of the element's children.
|
Chris@0
|
70 */
|
Chris@0
|
71 public static function children(array &$elements, $sort = FALSE) {
|
Chris@0
|
72 // Do not attempt to sort elements which have already been sorted.
|
Chris@0
|
73 $sort = isset($elements['#sorted']) ? !$elements['#sorted'] : $sort;
|
Chris@0
|
74
|
Chris@0
|
75 // Filter out properties from the element, leaving only children.
|
Chris@0
|
76 $count = count($elements);
|
Chris@0
|
77 $child_weights = [];
|
Chris@0
|
78 $i = 0;
|
Chris@0
|
79 $sortable = FALSE;
|
Chris@0
|
80 foreach ($elements as $key => $value) {
|
Chris@0
|
81 if ($key === '' || $key[0] !== '#') {
|
Chris@0
|
82 if (is_array($value)) {
|
Chris@0
|
83 if (isset($value['#weight'])) {
|
Chris@0
|
84 $weight = $value['#weight'];
|
Chris@0
|
85 $sortable = TRUE;
|
Chris@0
|
86 }
|
Chris@0
|
87 else {
|
Chris@0
|
88 $weight = 0;
|
Chris@0
|
89 }
|
Chris@0
|
90 // Supports weight with up to three digit precision and conserve
|
Chris@0
|
91 // the insertion order.
|
Chris@0
|
92 $child_weights[$key] = floor($weight * 1000) + $i / $count;
|
Chris@0
|
93 }
|
Chris@0
|
94 // Only trigger an error if the value is not null.
|
Chris@0
|
95 // @see https://www.drupal.org/node/1283892
|
Chris@0
|
96 elseif (isset($value)) {
|
Chris@17
|
97 trigger_error(new FormattableMarkup('"@key" is an invalid render array key', ['@key' => $key]), E_USER_ERROR);
|
Chris@0
|
98 }
|
Chris@0
|
99 }
|
Chris@0
|
100 $i++;
|
Chris@0
|
101 }
|
Chris@0
|
102
|
Chris@0
|
103 // Sort the children if necessary.
|
Chris@0
|
104 if ($sort && $sortable) {
|
Chris@0
|
105 asort($child_weights);
|
Chris@0
|
106 // Put the sorted children back into $elements in the correct order, to
|
Chris@0
|
107 // preserve sorting if the same element is passed through
|
Chris@0
|
108 // \Drupal\Core\Render\Element::children() twice.
|
Chris@0
|
109 foreach ($child_weights as $key => $weight) {
|
Chris@0
|
110 $value = $elements[$key];
|
Chris@0
|
111 unset($elements[$key]);
|
Chris@0
|
112 $elements[$key] = $value;
|
Chris@0
|
113 }
|
Chris@0
|
114 $elements['#sorted'] = TRUE;
|
Chris@0
|
115 }
|
Chris@0
|
116
|
Chris@0
|
117 return array_keys($child_weights);
|
Chris@0
|
118 }
|
Chris@0
|
119
|
Chris@0
|
120 /**
|
Chris@0
|
121 * Returns the visible children of an element.
|
Chris@0
|
122 *
|
Chris@0
|
123 * @param array $elements
|
Chris@0
|
124 * The parent element.
|
Chris@0
|
125 *
|
Chris@0
|
126 * @return array
|
Chris@0
|
127 * The array keys of the element's visible children.
|
Chris@0
|
128 */
|
Chris@0
|
129 public static function getVisibleChildren(array $elements) {
|
Chris@0
|
130 $visible_children = [];
|
Chris@0
|
131
|
Chris@0
|
132 foreach (static::children($elements) as $key) {
|
Chris@0
|
133 $child = $elements[$key];
|
Chris@0
|
134
|
Chris@0
|
135 // Skip value and hidden elements, since they are not rendered.
|
Chris@0
|
136 if (!static::isVisibleElement($child)) {
|
Chris@0
|
137 continue;
|
Chris@0
|
138 }
|
Chris@0
|
139
|
Chris@0
|
140 $visible_children[$key] = $child;
|
Chris@0
|
141 }
|
Chris@0
|
142
|
Chris@0
|
143 return array_keys($visible_children);
|
Chris@0
|
144 }
|
Chris@0
|
145
|
Chris@0
|
146 /**
|
Chris@0
|
147 * Determines if an element is visible.
|
Chris@0
|
148 *
|
Chris@0
|
149 * @param array $element
|
Chris@0
|
150 * The element to check for visibility.
|
Chris@0
|
151 *
|
Chris@0
|
152 * @return bool
|
Chris@0
|
153 * TRUE if the element is visible, otherwise FALSE.
|
Chris@0
|
154 */
|
Chris@0
|
155 public static function isVisibleElement($element) {
|
Chris@0
|
156 return (!isset($element['#type']) || !in_array($element['#type'], ['value', 'hidden', 'token']))
|
Chris@0
|
157 && (!isset($element['#access'])
|
Chris@0
|
158 || (($element['#access'] instanceof AccessResultInterface && $element['#access']->isAllowed()) || ($element['#access'] === TRUE)));
|
Chris@0
|
159 }
|
Chris@0
|
160
|
Chris@0
|
161 /**
|
Chris@0
|
162 * Sets HTML attributes based on element properties.
|
Chris@0
|
163 *
|
Chris@0
|
164 * @param array $element
|
Chris@0
|
165 * The renderable element to process. Passed by reference.
|
Chris@0
|
166 * @param array $map
|
Chris@0
|
167 * An associative array whose keys are element property names and whose
|
Chris@0
|
168 * values are the HTML attribute names to set on the corresponding
|
Chris@0
|
169 * property; e.g., array('#propertyname' => 'attributename'). If both names
|
Chris@0
|
170 * are identical except for the leading '#', then an attribute name value is
|
Chris@0
|
171 * sufficient and no property name needs to be specified.
|
Chris@0
|
172 */
|
Chris@0
|
173 public static function setAttributes(array &$element, array $map) {
|
Chris@0
|
174 foreach ($map as $property => $attribute) {
|
Chris@0
|
175 // If the key is numeric, the attribute name needs to be taken over.
|
Chris@0
|
176 if (is_int($property)) {
|
Chris@0
|
177 $property = '#' . $attribute;
|
Chris@0
|
178 }
|
Chris@0
|
179 // Do not overwrite already existing attributes.
|
Chris@0
|
180 if (isset($element[$property]) && !isset($element['#attributes'][$attribute])) {
|
Chris@0
|
181 $element['#attributes'][$attribute] = $element[$property];
|
Chris@0
|
182 }
|
Chris@0
|
183 }
|
Chris@0
|
184 }
|
Chris@0
|
185
|
Chris@0
|
186 /**
|
Chris@0
|
187 * Indicates whether the given element is empty.
|
Chris@0
|
188 *
|
Chris@0
|
189 * An element that only has #cache set is considered empty, because it will
|
Chris@0
|
190 * render to the empty string.
|
Chris@0
|
191 *
|
Chris@0
|
192 * @param array $elements
|
Chris@0
|
193 * The element.
|
Chris@0
|
194 *
|
Chris@0
|
195 * @return bool
|
Chris@0
|
196 * Whether the given element is empty.
|
Chris@0
|
197 */
|
Chris@0
|
198 public static function isEmpty(array $elements) {
|
Chris@0
|
199 return empty($elements) || (count($elements) === 1 && array_keys($elements) === ['#cache']);
|
Chris@0
|
200 }
|
Chris@0
|
201
|
Chris@0
|
202 }
|