Drupal - How can I count a multivalue field's values in twig?
{{ node.field_mytext.count }} => faced with error
This does not work, because the method count
is not allowed in twig policies:
core/lib/Drupal/Core/Template/TwigSandboxPolicy.php
{{ content.field_mytext | length }}?
This does not work, because content
is a render array with a lot of additional keys.
This works: Convert field to array and count
{{ node.field_mytext.getvalue | length }}
Easiest way is to get the ['#items']|length
. I do it all the time for counting items for view more instances and when loading sliders.
{{ content.field_mytext['#items']|length }}
I've used own Twig filters to support entity fields, with this you can use fields as native arrays:
{{ content.field_mytext|length }}
or
{{ content.field_mytext|first|value }}
or
{% if content.field_mytext is empty %}
You can easily add you own Twig filters via a custom module. You can learn more here: drupal.org/docs/8/creating-custom-modules. In short you need to create a module directory, for example path/to/drupal/modules/custom/common/
, put there common.info.yml
with module definition and common.services.yml
with definition of service (see comments in code) and put my code to /path/to/drupal/modules/custom/common/src/TwigExtension.php
.
<?php
namespace Drupal\common;
use Drupal\Core\Field\FieldItemInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\TypedData\ComplexDataInterface;
/**
* A class providing Twig extensions.
*
* This provides a Twig extension that registers various Field-API-specific
* extensions to Twig, overriding empty and array related filters.
*
* Don't forget about common.services.yml
* services:
* common.twig.TwigExtension:
* class: Drupal\common\TwigExtension
* tags:
* - { name: twig.extension }
*
* Usage (in *.html.twig file):
* - check is field empty {% if content.field_foo is empty %}
* - get field first value {{ content.field_foo|first|value }}
*/
class TwigExtension extends \Twig_Extension {
/**
* {@inheritdoc}
*/
public function getTests() {
return [
new \Twig_SimpleTest('empty', [$this, 'twigEmptyField']),
];
}
/**
* {@inheritdoc}
*/
public function getFilters() {
return [
new \Twig_SimpleFilter('length', [$this, 'twigLengthFilter'], ['needs_environment' => TRUE]),
new \Twig_SimpleFilter('slice', [$this, 'twigSlice'], ['needs_environment' => TRUE]),
new \Twig_SimpleFilter('first', [$this, 'twigFirst'], ['needs_environment' => TRUE]),
new \Twig_SimpleFilter('last', [$this, 'twigLast'], ['needs_environment' => TRUE]),
new \Twig_SimpleFilter('value', [$this, 'twigFieldValue']),
];
}
/**
* Check if value is field item object.
*
* @param mixed $value
* Mixed Twig variable.
*
* @return \Drupal\Core\Field\FieldItemListInterface|mixed
* FieldItemListInterface or same value as passed.
*/
private function checkItems($value) {
if (is_array($value) && !empty($value['#items']) && $value['#items'] instanceof FieldItemListInterface) {
return $value['#items'];
}
return $value;
}
/**
* Get field item value.
*
* @param object $field
* Field object.
*
* @return array|mixed
* List of values or value.
*/
public function twigFieldValue($field) {
if ($field instanceof FieldItemInterface) {
$prop = $field->mainPropertyName();
$value = $field->getValue();
return $prop ? $value[$prop] : $value;
}
if ($field instanceof FieldItemListInterface) {
$value = [];
foreach ($field as $item) {
$value[] = $this->twigFieldValue($item);
}
return $value;
}
return '';
}
/**
* Checks if a variable is empty.
*
* @see twig_test_empty
*/
public function twigEmptyField($value) {
$value = $this->checkItems($value);
if ($value instanceof ComplexDataInterface) {
return $value->isEmpty();
}
// Return TRUE, because there is no data only cache and weight.
elseif (!is_object($value) && isset($value['#cache']) && count($value) == 2) {
return TRUE;
}
return twig_test_empty($value);
}
/**
* Returns the length of a variable.
*
* @param \Twig_Environment $env
* Twig environment.
* @param mixed $item
* A variable.
*
* @return mixed
* The first element of the item.
*
* @see twig_length_filter
*/
public function twigLengthFilter(\Twig_Environment $env, $item) {
$item = $this->checkItems($item);
return twig_length_filter($env, $item);
}
/**
* Slices a variable.
*
* @param \Twig_Environment $env
* Twig environment.
* @param mixed $item
* A variable.
* @param int $start
* Start of the slice.
* @param int $length
* Size of the slice.
* @param bool $preserveKeys
* Whether to preserve key or not (when the input is an array)
*
* @return mixed
* The first element of the item.
*
* @see twig_slice
*/
public function twigSlice(\Twig_Environment $env, $item, $start, $length = NULL, $preserveKeys = FALSE) {
$item = $this->checkItems($item);
return twig_slice($env, $item, $start, $length, $preserveKeys);
}
/**
* Returns the first element of the item.
*
* @param \Twig_Environment $env
* Twig environment.
* @param mixed $item
* A variable.
*
* @return mixed
* The first element of the item.
*
* @see twig_first
*/
public function twigFirst(\Twig_Environment $env, $item) {
$item = $this->checkItems($item);
return twig_first($env, $item);
}
/**
* Returns the last element of the item.
*
* @param \Twig_Environment $env
* Twig environment.
* @param mixed $item
* A variable.
*
* @return mixed
* The first element of the item.
*
* @see twig_last
*/
public function twigLast(\Twig_Environment $env, $item) {
$item = $this->checkItems($item);
return twig_last($env, $item);
}
}