Commit 09bca146 authored by Jürgen Haas's avatar Jürgen Haas
Browse files

Issue #3280203 by jurgenhaas, mxh: Add action and condition for counting list items

parent 7b8a504a
Loading
Loading
Loading
Loading
+23 −1
Original line number Diff line number Diff line
@@ -6,7 +6,6 @@
 */

use Drupal\Component\Plugin\ConfigurableInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\eca\Plugin\ECA\Condition\ConditionInterface;

/**
@@ -139,3 +138,26 @@ function eca_update_8005() {
    }
  }
}

/**
 * Update bpmn ids.
 */
function eca_update_8006() {
  $storage = \Drupal::entityTypeManager()->getStorage('eca');
  /** @var \Drupal\eca\Entity\Eca $eca */
  foreach ($storage->loadMultiple() as $eca) {
    $model = $eca->getModel();
    $xml = $model->getModeldata();
    foreach (['event', 'condition', 'action'] as $type) {
      $items = $eca->get($type . 's') ?? [];
      foreach ($items as $item) {
        $search = 'org.drupal.' . $item['plugin'];
        $replace = 'org.drupal.' . $type . '.' . $item['plugin'];
        $xml = str_replace($search, $replace, $xml);
      }
    }
    $model
      ->setModeldata($xml)
      ->save();
  }
}
+70 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\eca_base\Plugin\Action;

use Drupal\Core\Form\FormStateInterface;
use Drupal\eca\Plugin\Action\ConfigurableActionBase;
use Drupal\eca_base\Plugin\ListCountTrait;

/**
 * Action to count items in a list and store resulting number as token.
 *
 * @Action(
 *   id = "eca_count",
 *   label = @Translation("Count list items")
 * )
 */
class ListCount extends ConfigurableActionBase {

  use ListCountTrait;

  /**
   * {@inheritdoc}
   */
  public function execute(): void {
    $result = $this->countValue($this->configuration['list_token']);
    $this->tokenServices->addTokenData($this->configuration['token_name'], $result);
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration(): array {
    return [
      'list_token' => '',
      'token_name' => '',
    ] + parent::defaultConfiguration();
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
    $form = parent::buildConfigurationForm($form, $form_state);
    $form['list_token'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Token containing the list'),
      '#description' => $this->t('Provide the name of the token that contains a list from which the number of items should be counted.'),
      '#default_value' => $this->configuration['list_token'],
      '#weight' => -10,
    ];
    $form['token_name'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Name of token'),
      '#description' => $this->t('Provide the name of a new token where the result should be stored.'),
      '#default_value' => $this->configuration['token_name'],
      '#weight' => -9,
    ];
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitConfigurationForm(array &$form, FormStateInterface $form_state): void {
    $this->configuration['list_token'] = $form_state->getValue('list_token');
    $this->configuration['token_name'] = $form_state->getValue('token_name');
    parent::submitConfigurationForm($form, $form_state);
  }

}
+62 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\eca_base\Plugin\ECA\Condition;

use Drupal\Core\Form\FormStateInterface;
use Drupal\eca_base\Plugin\ListCountTrait;

/**
 * ECA condition plugin for numerically comparing number of list items.
 *
 * @EcaCondition(
 *   id = "eca_count",
 *   label = @Translation("Compare number of list items")
 * )
 */
class ListCountComparison extends ScalarComparison {

  use ListCountTrait;

  /**
   * {@inheritdoc}
   */
  protected function getLeftValue(): string {
    return $this->countValue($this->configuration['left']);
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
    $form = parent::buildConfigurationForm($form, $form_state);
    $form['left']['#type'] = 'textfield';
    $form['left']['#title'] = $this->t('Token containing the list');
    $form['left']['#description'] = $this->t('Provide the name of the token that contains a list from which the number of items should be counted.');
    $form['right']['#type'] = 'textfield';
    $form['operator']['#default_value'] = static::COMPARE_EQUALS;
    $form['type']['#default_value'] = static::COMPARE_TYPE_NUMERIC;
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function getOptions(string $id): ?array {
    if ($id === 'operator') {
      return [
        static::COMPARE_EQUALS => $this->t('equals'),
        static::COMPARE_GREATERTHAN => $this->t('greater than'),
        static::COMPARE_LESSTHAN => $this->t('less than'),
        static::COMPARE_ATMOST => $this->t('at most'),
        static::COMPARE_ATLEAST => $this->t('at least'),
      ];
    }
    if ($id === 'type') {
      return [
        static::COMPARE_TYPE_NUMERIC => $this->t('Numeric order'),
      ];
    }
    return parent::getOptions($id);
  }

}
+35 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\eca_base\Plugin;

/**
 * Trait for count action and condition to do the count work.
 */
trait ListCountTrait {

  /**
   * Receives a token and counts the contained items.
   *
   * @param string $name
   *   Name of token object which contains the list of which the items should
   *   be counted.
   *
   * @return int
   *   Numnber of items if given token exists and is either countable or
   *   traversable, 0 otherwise.
   */
  protected function countValue(string $name): int {
    $result = 0;
    if ($this->tokenServices->hasTokenData($name)) {
      $data = $this->tokenServices->getTokenData($name);
      if (is_countable($data)) {
        $result = count($data);
      }
      elseif ($data instanceof \Traversable) {
        $result = iterator_count($data);
      }
    }
    return $result;
  }

}
+131 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\Tests\eca_base\Kernel;

use Drupal\eca\Plugin\ECA\Condition\StringComparisonBase;
use Drupal\eca\PluginManager\Condition;
use Drupal\eca\Token\TokenInterface;
use Drupal\KernelTests\KernelTestBase;

/**
 * Kernel tests for the "eca_count" condition plugin.
 *
 * @group eca
 * @group eca_base
 */
class CompareListCountTest extends KernelTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'eca',
    'eca_base',
  ];

  /**
   * ECA condition plugin manager.
   *
   * @var \Drupal\eca\PluginManager\Condition|null
   */
  protected ?Condition $conditionManager;

  /**
   * ECA token service.
   *
   * @var \Drupal\eca\Token\TokenInterface|null
   */
  protected ?TokenInterface $tokenServices;

  /**
   * {@inheritdoc}
   */
  public function setUp(): void {
    parent::setUp();
    $this->installConfig(static::$modules);
    $this->conditionManager = \Drupal::service('plugin.manager.eca.condition');
    $this->tokenServices = \Drupal::service('eca.token_services');
  }

  /**
   * Tests list item count comparison.
   *
   * @dataProvider listDataProvider
   *
   * @throws \Drupal\Component\Plugin\Exception\PluginException
   */
  public function testListItemCountValues($list, $right, $operator, $negate, $message, $assertTrue = TRUE): void {
    $this->tokenServices->addTokenData('list', $list);
    // Configure default settings for condition.
    $config = [
      'left' => 'list',
      'right' => $right,
      'operator' => $operator,
      'type' => StringComparisonBase::COMPARE_TYPE_NUMERIC,
      'negate' => $negate,
    ];
    /** @var \Drupal\eca_base\Plugin\ECA\Condition\ListCountComparison $condition */
    $condition = $this->conditionManager->createInstance('eca_count', $config);
    if ($assertTrue) {
      $this->assertTrue($condition->evaluate(), $message);
    }
    else {
      $this->assertFalse($condition->evaluate(), $message);
    }
  }

  /**
   * Provides multiple test cases for the testListItemCountValues method.
   *
   * @return array
   *   The list item count test cases.
   */
  public function listDataProvider(): array {
    return [
      [
        ['a', 'b', 'c'],
        '3',
        StringComparisonBase::COMPARE_EQUALS,
        FALSE,
        '3 and 3 are equal.',
      ],
      [
        ['a', 'b', 'c'],
        '2',
        StringComparisonBase::COMPARE_GREATERTHAN,
        FALSE,
        '3 is greater than 2.',
      ],
      [
        'no array',
        '1',
        StringComparisonBase::COMPARE_LESSTHAN,
        FALSE,
        '1 is greater than 0, compared with a string.',
      ],
      [
        [],
        '1',
        StringComparisonBase::COMPARE_LESSTHAN,
        FALSE,
        '1 is greater than 0, compared with an empty list.',
      ],
      [
        NULL,
        '1',
        StringComparisonBase::COMPARE_LESSTHAN,
        FALSE,
        '1 is greater than 0, compared with NULL.',
      ],
      [
        ['a', 'b', 'c'],
        '3',
        StringComparisonBase::COMPARE_EQUALS,
        TRUE,
        '3 and 3 are not unequal.',
        FALSE,
      ],
    ];
  }

}
Loading