flow-1.0.0-beta8/src/Flow.php

src/Flow.php
<?php

namespace Drupal\flow;

use Drupal\Core\Entity\EntityInterface;
use Drupal\flow\Event\FlowBeginEvent;
use Drupal\flow\Event\FlowEndEvent;
use Drupal\flow\Entity\EntitySaveHandler;
use Drupal\flow\Entity\Flow as Entity;
use Drupal\flow\Entity\FlowInterface;
use Drupal\flow\Event\FlowRuntimeContext;
use Drupal\flow\Exception\TaskRecursionException;
use Drupal\flow\Helpers\EventDispatcherTrait;
use Drupal\flow\Plugin\flow\Subject\Qualified;
use Drupal\flow\Plugin\FlowSubjectCollection;

/**
 * Engine for applying configured flow.
 */
class Flow {

  use EventDispatcherTrait;

  /**
   * A static stack of entities where Flow is being applied on.
   *
   * Keyed by task mode. This stack may be used by plugins, such as the "create"
   * subject plugin.
   *
   * @var \Drupal\Core\Entity\EntityInterface[][]
   */
  public static array $stack = [];

  /**
   * A static collection of entities that need to be saved.
   *
   * When you have an entity that needs to be saved, and you want the Flow
   * engine to properly take care of that, call \Drupal\flow\Flow::needsSave().
   *
   * @var \Drupal\Core\Entity\EntityInterface[]
   */
  public static array $save = [];

  /**
   * A filter to use when loading relevant tasks.
   *
   * @var array
   */
  public static array $filter = ['active' => TRUE];

  /**
   * Whether the engine is active and applies existing configurations.
   *
   * @var bool
   *
   * @see \Drupal\flow\Flow::isActive()
   */
  protected static bool $isActive = TRUE;

  /**
   * Get the Flow engine service.
   *
   * @return \Drupal\flow\Flow
   *   The Flow engine service.
   */
  public static function service(): Flow {
    return \Drupal::service('flow');
  }

  /**
   * Returns the Flow configuration entity for the given parameters.
   *
   * This method is just an alias and the same as when calling
   * \Drupal\flow\Entity\Flow::getFlow().
   *
   * @param string $entity_type_id
   *   The entity type ID.
   * @param string $bundle
   *   The bundle.
   * @param string $task_mode
   *   The task mode.
   *
   * @return \Drupal\flow\Entity\FlowInterface|null
   *   The config entity. Could be a new one. Returns NULL if the entity type
   *   is not supported for having Flow configurations.
   */
  public static function getFlow(string $entity_type_id, string $bundle, string $task_mode): ?FlowInterface {
    return Entity::getFlow($entity_type_id, $bundle, $task_mode);
  }

  /**
   * Tells the Flow engine that an entity needs to be saved.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity that needs to be saved.
   * @param object $component
   *   (optional) The component that is telling Flow about this need.
   */
  public static function needsSave(EntityInterface $entity, $component = NULL): void {
    foreach ($entity->referencedEntities() as $referenced) {
      if ($referenced->isNew() && !in_array($referenced, self::$save, TRUE)) {
        array_push(self::$save, $referenced);
      }
    }
    if (($index = array_search($entity, self::$save, TRUE)) !== FALSE) {
      unset(self::$save[$index]);
    }
    array_push(self::$save, $entity);
    EntitySaveHandler::service()->saveIfRequired(self::$save);
  }

  /**
   * Get the info whether the engine is active or not.
   *
   * @return bool
   *   Returns TRUE when the engine active and existing configuration is being
   *   applied, FALSE otherwise.
   */
  public static function isActive(): bool {
    return self::$isActive;
  }

  /**
   * Set the engine to be active or not.
   *
   * @param bool $active
   *   Whether the engine should be active. Default is TRUE.
   */
  public static function setActive(bool $active = TRUE): void {
    self::$isActive = $active;
  }

  /**
   * Applies configured flow on the given entity using the specified task mode.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity where to apply the flow.
   * @param string $task_mode
   *   The task mode to apply.
   *
   * @throws \Drupal\flow\Exception\TaskRecursionException
   *   When task recursion occurs. Usually the Flow task queue is always
   *   involved when calling this method, and takes care of this exception.
   */
  public function apply(EntityInterface $entity, string $task_mode): void {
    if (!self::isActive() || !($flow = Entity::getFlow($entity->getEntityTypeId(), $entity->bundle(), $task_mode))) {
      return;
    }
    if (!$flow->getStatus()) {
      return;
    }

    // For detecting task recursion, look at all stacked entities, no matter the
    // task mode. For example, when an entity is in the "create" or "delete"
    // stack, we also don't want it to be saved - as it makes no sense to do so.
    foreach (self::$stack as &$stacked_entities) {
      if (in_array($entity, $stacked_entities, TRUE)) {
        throw new TaskRecursionException($task_mode, $entity);
      }
    }

    if (!isset(self::$stack[$task_mode])) {
      self::$stack[$task_mode] = [];
    }
    $stack = &self::$stack[$task_mode];
    array_push($stack, $entity);
    $runtime_context = new FlowRuntimeContext($flow);
    $this->getEventDispatcher()->dispatch(new FlowBeginEvent($entity, $task_mode, $runtime_context), FlowEvents::BEGIN);

    if (self::isActive() && $flow->getStatus()) {
      $queue = FlowTaskQueue::service();

      $prepare_subject = static function (Entity $flow, FlowSubjectCollection $subjects, $i) {
        /** @var \Drupal\flow\Plugin\FlowSubjectInterface $subject */
        $subject = $subjects->get($i);

        // Custom flow includes a mechanic for qualified subjects. Therefore
        // prepare qualifying subjects according to this configuration.
        if ($subject instanceof Qualified) {
          $qualifiers = $flow->getQualifiers(self::$filter);
          /** @var \Drupal\flow\Plugin\FlowSubjectInterface $qualifying */
          foreach ($flow->getQualifyingSubjects(self::$filter) as $k => $qualifying) {
            $qualifier = $qualifiers->get($k);
            if (($subject->getEntityTypeId() === $qualifying->getEntityTypeId()) && ($subject->getEntityBundle() === $qualifying->getEntityBundle())) {
              $subject->addQualifying($qualifying, $qualifier);
            }
          }
        }

        return $subject;
      };

      // Add the tasks with their according subjects.
      $tasks = $flow->getTasks(self::$filter);
      $subjects = $flow->getSubjects(self::$filter);
      foreach ($tasks as $i => $task) {
        $subject = $prepare_subject($flow, $subjects, $i);
        $item = new FlowTaskQueueItem($entity, $task_mode, $task, $subject);
        $queue->add($item);
        $runtime_context->addTaskQueueItem($item);
      }

      // Also add tasks from custom flow, if any.
      if (!$flow->isCustom()) {
        foreach ($flow->getCustomFlow() as $custom_flow) {
          if (!$custom_flow->getStatus()) {
            continue;
          }

          $tasks = $custom_flow->getTasks(self::$filter);
          $subjects = $custom_flow->getSubjects(self::$filter);
          foreach ($tasks as $i => $task) {
            $subject = $prepare_subject($flow, $subjects, $i);
            $item = new FlowTaskQueueItem($entity, $task_mode, $task, $subject);
            $queue->add($item);
            $runtime_context->addTaskQueueItem($item);
          }
        }
      }

      // We now have all imminent tasks, so process them one by one.
      self::setActive(\Drupal::getContainer()->getParameter('flow.allow_nested_flow'));
      try {
        $queue->process($entity, $task_mode);
        // Finally make sure, that changed entities are being saved.
        if (!empty(self::$save)) {
          EntitySaveHandler::service()->ensureSave(self::$save);
        }
      }
      finally {
        self::setActive(TRUE);
      }
    }

    $this->getEventDispatcher()->dispatch(new FlowEndEvent($entity, $task_mode, $runtime_context), FlowEvents::END);
    array_pop($stack);
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc