Loading modules/content/src/Plugin/ECA/Condition/EntityFieldValueEmpty.php +47 −4 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ namespace Drupal\eca_content\Plugin\ECA\Condition; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Field\EntityReferenceFieldItemListInterface; use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; use Drupal\Core\Form\FormStateInterface; use Drupal\eca\Plugin\ECA\Condition\ConditionBase; use Drupal\eca\TypedData\PropertyPathTrait; Loading @@ -29,12 +31,53 @@ class EntityFieldValueEmpty extends ConditionBase { public function evaluate(): bool { $entity = $this->getValueFromContext('entity'); $field_name = $this->tokenServices->replaceClear($this->configuration['field_name']); $property_path = $this->normalizePropertyPath($field_name); $options = ['access' => FALSE, 'auto_item' => FALSE]; if (($entity instanceof EntityInterface) && ($property = $this->getTypedProperty($entity->getTypedData(), $field_name, $options))) { return $this->negationCheck(method_exists($property, 'isEmpty') ? $property->isEmpty() : empty($property->getValue())); // Setting the default return result to be false, stops execution chain // when either the entity, field or property does not exist. $result = FALSE; if ($entity instanceof EntityInterface) { $is_empty = NULL; while ($property_path && !($property = $this->getTypedProperty($entity->getTypedData(), $property_path, $options))) { $is_empty = TRUE; // Property does not exist, which means it's empty. $property_path = implode('.', array_slice(explode('.', $property_path), 0, -1)); } if (($is_empty === NULL) && $property) { $is_empty = method_exists($property, 'isEmpty') ? $property->isEmpty() : empty($property->getValue()); } if (!$is_empty) { // For stale entity references, the empty check may return a false // negative. Load the referenced entities to make sure, that they // really exist. if ($property instanceof EntityReferenceFieldItemListInterface) { $is_empty = empty($property->referencedEntities()); } elseif ($property instanceof EntityReferenceItem) { $items = $property->getParent(); if ($items instanceof EntityReferenceFieldItemListInterface) { $entities = $items->referencedEntities(); if ($entities) { foreach ($items as $delta => $item) { if (($item === $property) || ($item && ($item->getValue() === $property->getValue()))) { $is_empty = !isset($entities[$delta]); break; } } } // Stop execution chain when field or property does not exist. return FALSE; else { $is_empty = TRUE; } } } } if ($is_empty !== NULL) { $result = $this->negationCheck($is_empty); } } return $result; } /** Loading modules/content/tests/src/Kernel/EntityFieldValueEmptyTest.php 0 → 100644 +178 −0 Original line number Diff line number Diff line <?php namespace Drupal\Tests\eca_content\Kernel; use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; use Drupal\KernelTests\KernelTestBase; use Drupal\node\Entity\Node; use Drupal\node\Entity\NodeType; use Drupal\user\Entity\User; /** * Kernel tests for the "eca_entity_field_value_empty" action plugin. * * @group eca * @group eca_content */ class EntityFieldValueEmptyTest extends KernelTestBase { /** * The modules. * * @var string[] * The modules. */ protected static $modules = [ 'system', 'user', 'field', 'filter', 'text', 'node', 'eca', 'eca_content', ]; /** * {@inheritdoc} */ public function setUp(): void { parent::setUp(); $this->installEntitySchema('user'); $this->installEntitySchema('node'); $this->installSchema('node', ['node_access']); $this->installConfig(static::$modules); User::create(['uid' => 0, 'name' => 'guest'])->save(); User::create(['uid' => 1, 'name' => 'admin'])->save(); // Create the Article content type with a standard body field. /** @var \Drupal\node\NodeTypeInterface $node_type */ $node_type = NodeType::create(['type' => 'article', 'name' => 'Article']); $node_type->save(); node_add_body_field($node_type); // Create a multi-value text field. FieldStorageConfig::create([ 'field_name' => 'field_string_multi', 'type' => 'string', 'entity_type' => 'node', 'cardinality' => FieldStorageConfig::CARDINALITY_UNLIMITED, ])->save(); FieldConfig::create([ 'field_name' => 'field_string_multi', 'label' => 'A string field having multiple values.', 'entity_type' => 'node', 'bundle' => 'article', ])->save(); // Create a single-value entity reference field. $field_storage = FieldStorageConfig::create([ 'field_name' => 'field_content', 'type' => 'entity_reference', 'entity_type' => 'node', 'settings' => [ 'target_type' => 'node', ], 'module' => 'core', 'cardinality' => 1, ]); $field_storage->save(); FieldConfig::create([ 'label' => 'A single-value node reference.', 'entity_type' => 'node', 'bundle' => 'article', 'field_storage' => $field_storage, ])->save(); // Create a multi-value entity reference field. $field_storage = FieldStorageConfig::create([ 'field_name' => 'field_content_multi', 'type' => 'entity_reference', 'entity_type' => 'node', 'settings' => [ 'target_type' => 'node', ], 'module' => 'core', 'cardinality' => FieldStorageConfig::CARDINALITY_UNLIMITED, ]); $field_storage->save(); FieldConfig::create([ 'label' => 'A multi-value node reference.', 'entity_type' => 'node', 'bundle' => 'article', 'field_storage' => $field_storage, ])->save(); } /** * Tests EntityFieldValueEmpty on a node. */ public function testEntityFieldValueEmpty(): void { /** @var \Drupal\eca\PluginManager\Condition $condition_manager */ $condition_manager = \Drupal::service('plugin.manager.eca.condition'); $string = $this->randomMachineName(32); $text = $this->randomMachineName(32); $summary = $this->randomMachineName(16); $node = Node::create([ 'type' => 'article', 'uid' => 1, 'title' => '123', 'body' => ['summary' => $summary, 'value' => $text], 'field_string_multi' => [$string, $string . '2', $string . '3'], ]); $node->save(); /** @var \Drupal\eca_content\Plugin\ECA\Condition\EntityFieldValueEmpty $condition */ $condition = $condition_manager->createInstance('eca_entity_field_value_empty', [ 'field_name' => 'body', 'negate' => FALSE, ]); $condition->setContextValue('entity', $node); $this->assertFalse($condition->evaluate()); $node->get('body')->setValue(NULL); $this->assertTrue($condition->evaluate()); // Now test for entity references. /** @var \Drupal\eca_content\Plugin\ECA\Condition\EntityFieldValueEmpty $condition */ $condition = $condition_manager->createInstance('eca_entity_field_value_empty', [ 'field_name' => 'field_content_multi', 'negate' => FALSE, ]); $condition->setContextValue('entity', $node); /** @var \Drupal\eca_content\Plugin\ECA\Condition\EntityFieldValueEmpty $condition_item */ $condition_item = $condition_manager->createInstance('eca_entity_field_value_empty', [ 'field_name' => 'field_content_multi.0', 'negate' => FALSE, ]); $condition_item->setContextValue('entity', $node); $another_node = Node::create([ 'type' => 'article', 'uid' => 1, 'title' => '456', 'body' => ['summary' => $summary, 'value' => $text], 'field_string_multi' => [$string, $string . '4', $string . '5'], ]); $another_node->save(); $this->assertTrue($condition->evaluate()); $this->assertTrue($condition_item->evaluate()); $node->get('field_content_multi')->setValue($another_node); $this->assertFalse($condition->evaluate()); $this->assertFalse($condition_item->evaluate()); $node->save(); $this->assertFalse($condition->evaluate()); $this->assertFalse($condition_item->evaluate()); $another_node->delete(); $this->assertTrue($condition->evaluate()); $this->assertTrue($condition_item->evaluate()); } } src/TypedData/PropertyPathTrait.php +1 −1 Original line number Diff line number Diff line Loading @@ -80,7 +80,7 @@ trait PropertyPathTrait { if ($data instanceof ComplexDataInterface) { $data = $this->getDataProperties($data, $property); } if (($data instanceof \ArrayAccess || is_array($data)) && isset($data[$property])) { if ((($data instanceof \ArrayAccess) || is_array($data)) && isset($data[$property])) { $data = $data[$property]; if ($data instanceof DataReferenceInterface) { // Directly jump to the contained target, if any is present. Loading Loading
modules/content/src/Plugin/ECA/Condition/EntityFieldValueEmpty.php +47 −4 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ namespace Drupal\eca_content\Plugin\ECA\Condition; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Field\EntityReferenceFieldItemListInterface; use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; use Drupal\Core\Form\FormStateInterface; use Drupal\eca\Plugin\ECA\Condition\ConditionBase; use Drupal\eca\TypedData\PropertyPathTrait; Loading @@ -29,12 +31,53 @@ class EntityFieldValueEmpty extends ConditionBase { public function evaluate(): bool { $entity = $this->getValueFromContext('entity'); $field_name = $this->tokenServices->replaceClear($this->configuration['field_name']); $property_path = $this->normalizePropertyPath($field_name); $options = ['access' => FALSE, 'auto_item' => FALSE]; if (($entity instanceof EntityInterface) && ($property = $this->getTypedProperty($entity->getTypedData(), $field_name, $options))) { return $this->negationCheck(method_exists($property, 'isEmpty') ? $property->isEmpty() : empty($property->getValue())); // Setting the default return result to be false, stops execution chain // when either the entity, field or property does not exist. $result = FALSE; if ($entity instanceof EntityInterface) { $is_empty = NULL; while ($property_path && !($property = $this->getTypedProperty($entity->getTypedData(), $property_path, $options))) { $is_empty = TRUE; // Property does not exist, which means it's empty. $property_path = implode('.', array_slice(explode('.', $property_path), 0, -1)); } if (($is_empty === NULL) && $property) { $is_empty = method_exists($property, 'isEmpty') ? $property->isEmpty() : empty($property->getValue()); } if (!$is_empty) { // For stale entity references, the empty check may return a false // negative. Load the referenced entities to make sure, that they // really exist. if ($property instanceof EntityReferenceFieldItemListInterface) { $is_empty = empty($property->referencedEntities()); } elseif ($property instanceof EntityReferenceItem) { $items = $property->getParent(); if ($items instanceof EntityReferenceFieldItemListInterface) { $entities = $items->referencedEntities(); if ($entities) { foreach ($items as $delta => $item) { if (($item === $property) || ($item && ($item->getValue() === $property->getValue()))) { $is_empty = !isset($entities[$delta]); break; } } } // Stop execution chain when field or property does not exist. return FALSE; else { $is_empty = TRUE; } } } } if ($is_empty !== NULL) { $result = $this->negationCheck($is_empty); } } return $result; } /** Loading
modules/content/tests/src/Kernel/EntityFieldValueEmptyTest.php 0 → 100644 +178 −0 Original line number Diff line number Diff line <?php namespace Drupal\Tests\eca_content\Kernel; use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; use Drupal\KernelTests\KernelTestBase; use Drupal\node\Entity\Node; use Drupal\node\Entity\NodeType; use Drupal\user\Entity\User; /** * Kernel tests for the "eca_entity_field_value_empty" action plugin. * * @group eca * @group eca_content */ class EntityFieldValueEmptyTest extends KernelTestBase { /** * The modules. * * @var string[] * The modules. */ protected static $modules = [ 'system', 'user', 'field', 'filter', 'text', 'node', 'eca', 'eca_content', ]; /** * {@inheritdoc} */ public function setUp(): void { parent::setUp(); $this->installEntitySchema('user'); $this->installEntitySchema('node'); $this->installSchema('node', ['node_access']); $this->installConfig(static::$modules); User::create(['uid' => 0, 'name' => 'guest'])->save(); User::create(['uid' => 1, 'name' => 'admin'])->save(); // Create the Article content type with a standard body field. /** @var \Drupal\node\NodeTypeInterface $node_type */ $node_type = NodeType::create(['type' => 'article', 'name' => 'Article']); $node_type->save(); node_add_body_field($node_type); // Create a multi-value text field. FieldStorageConfig::create([ 'field_name' => 'field_string_multi', 'type' => 'string', 'entity_type' => 'node', 'cardinality' => FieldStorageConfig::CARDINALITY_UNLIMITED, ])->save(); FieldConfig::create([ 'field_name' => 'field_string_multi', 'label' => 'A string field having multiple values.', 'entity_type' => 'node', 'bundle' => 'article', ])->save(); // Create a single-value entity reference field. $field_storage = FieldStorageConfig::create([ 'field_name' => 'field_content', 'type' => 'entity_reference', 'entity_type' => 'node', 'settings' => [ 'target_type' => 'node', ], 'module' => 'core', 'cardinality' => 1, ]); $field_storage->save(); FieldConfig::create([ 'label' => 'A single-value node reference.', 'entity_type' => 'node', 'bundle' => 'article', 'field_storage' => $field_storage, ])->save(); // Create a multi-value entity reference field. $field_storage = FieldStorageConfig::create([ 'field_name' => 'field_content_multi', 'type' => 'entity_reference', 'entity_type' => 'node', 'settings' => [ 'target_type' => 'node', ], 'module' => 'core', 'cardinality' => FieldStorageConfig::CARDINALITY_UNLIMITED, ]); $field_storage->save(); FieldConfig::create([ 'label' => 'A multi-value node reference.', 'entity_type' => 'node', 'bundle' => 'article', 'field_storage' => $field_storage, ])->save(); } /** * Tests EntityFieldValueEmpty on a node. */ public function testEntityFieldValueEmpty(): void { /** @var \Drupal\eca\PluginManager\Condition $condition_manager */ $condition_manager = \Drupal::service('plugin.manager.eca.condition'); $string = $this->randomMachineName(32); $text = $this->randomMachineName(32); $summary = $this->randomMachineName(16); $node = Node::create([ 'type' => 'article', 'uid' => 1, 'title' => '123', 'body' => ['summary' => $summary, 'value' => $text], 'field_string_multi' => [$string, $string . '2', $string . '3'], ]); $node->save(); /** @var \Drupal\eca_content\Plugin\ECA\Condition\EntityFieldValueEmpty $condition */ $condition = $condition_manager->createInstance('eca_entity_field_value_empty', [ 'field_name' => 'body', 'negate' => FALSE, ]); $condition->setContextValue('entity', $node); $this->assertFalse($condition->evaluate()); $node->get('body')->setValue(NULL); $this->assertTrue($condition->evaluate()); // Now test for entity references. /** @var \Drupal\eca_content\Plugin\ECA\Condition\EntityFieldValueEmpty $condition */ $condition = $condition_manager->createInstance('eca_entity_field_value_empty', [ 'field_name' => 'field_content_multi', 'negate' => FALSE, ]); $condition->setContextValue('entity', $node); /** @var \Drupal\eca_content\Plugin\ECA\Condition\EntityFieldValueEmpty $condition_item */ $condition_item = $condition_manager->createInstance('eca_entity_field_value_empty', [ 'field_name' => 'field_content_multi.0', 'negate' => FALSE, ]); $condition_item->setContextValue('entity', $node); $another_node = Node::create([ 'type' => 'article', 'uid' => 1, 'title' => '456', 'body' => ['summary' => $summary, 'value' => $text], 'field_string_multi' => [$string, $string . '4', $string . '5'], ]); $another_node->save(); $this->assertTrue($condition->evaluate()); $this->assertTrue($condition_item->evaluate()); $node->get('field_content_multi')->setValue($another_node); $this->assertFalse($condition->evaluate()); $this->assertFalse($condition_item->evaluate()); $node->save(); $this->assertFalse($condition->evaluate()); $this->assertFalse($condition_item->evaluate()); $another_node->delete(); $this->assertTrue($condition->evaluate()); $this->assertTrue($condition_item->evaluate()); } }
src/TypedData/PropertyPathTrait.php +1 −1 Original line number Diff line number Diff line Loading @@ -80,7 +80,7 @@ trait PropertyPathTrait { if ($data instanceof ComplexDataInterface) { $data = $this->getDataProperties($data, $property); } if (($data instanceof \ArrayAccess || is_array($data)) && isset($data[$property])) { if ((($data instanceof \ArrayAccess) || is_array($data)) && isset($data[$property])) { $data = $data[$property]; if ($data instanceof DataReferenceInterface) { // Directly jump to the contained target, if any is present. Loading