factory_lollipop-1.0.x-dev/src/FixtureFactory.php

src/FixtureFactory.php
<?php

namespace Drupal\factory_lollipop;

use Drupal\factory_lollipop\Resolver\ChainFactoryResolver;
use Drupal\factory_lollipop\Resolver\ChainFactoryTypeResolver;

/**
 * Define and create Factories based on resolved Factory Type for use in tests.
 */
class FixtureFactory {

  /**
   * The factory definitions.
   *
   * @var array
   */
  protected $definitions = [];

  /**
   * The factory type resolver.
   *
   * @var \Drupal\factory_lollipop\Resolver\ChainFactoryTypeResolver
   */
  protected $chainFactoryTypeResolver;

  /**
   * The factories resolver.
   *
   * @var \Drupal\factory_lollipop\Resolver\ChainFactoryResolver
   */
  protected $chainFactoryResolver;

  /**
   * Construct a new FixtureFactory object.
   *
   * @param \Drupal\factory_lollipop\Resolver\ChainFactoryTypeResolver $chain_factory_type_resolver
   *   The factory type resolver.
   * @param \Drupal\factory_lollipop\Resolver\ChainFactoryResolver $chain_factory_resolver
   *   The factories resolver.
   */
  public function __construct(ChainFactoryTypeResolver $chain_factory_type_resolver, ChainFactoryResolver $chain_factory_resolver) {
    $this->chainFactoryTypeResolver = $chain_factory_type_resolver;
    $this->chainFactoryResolver = $chain_factory_resolver;
  }

  /**
   * A method of registering factory definitions.
   *
   * A factory of type with name will be defined, allowing for calls to
   * $this->>create to simply look up and produce standard objects.
   *
   * @param string $type
   *   The type of the object to create, accepted values are:
   *   - taxonomy
   *   - term.
   * @param string $name
   *   The name to register for the factory (must be unique)
   * @param array $opts
   *   An array containing the default options for the factory.
   */
  public function define(string $type, string $name, array $opts): void {
    $this->definitions[$name] = ['type' => $type, 'opts' => $opts];
  }

  /**
   * A method for creating objects from factory definitions.
   *
   * The object is created with the supplied options, persisted,
   * and returned to the caller.
   *
   * @param string $name
   *   The name of the factory to generate.
   * @param array $opts
   *   An associative array with options for the factory.
   *
   * @return mixed
   *   The newly created or already existing Drupal Entity|Object.
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  public function create(string $name, array $opts = []) {
    $factory_array = array_merge($this->getDefaultOptions($name), $opts);

    $factory_object = (object) $factory_array;
    $factory_type = $this->getType($name);

    foreach ($this->chainFactoryTypeResolver->getResolvers() as $resolver) {
      if ($resolver->shouldApply($factory_type)) {
        return $resolver->create($factory_object);
      }
    }

    throw new \RuntimeException("Factories of type '{$factory_type}' are not supported.");
  }

  /**
   * Returns the factory type for a given definition.
   *
   * @param string $name
   *   The name of the definition.
   *
   * @return string|null
   *   The type of the factory. Null when the name has not been defined.
   */
  public function getType($name): ?string {
    return $this->definitions[$name]['type'] ?? NULL;
  }

  /**
   * Creates associated objects for a field.
   *
   * The association is a Callable function because it may need to run on
   * cascade for Factory creation (Eg. Node -> Node-Type).
   *
   * @param string $name
   *   The name of the factory to generate.
   * @param array $opts
   *   An associative array with options for the factory.
   *
   * @return Callable
   *   A callable function that return the unique ID of associated Factory.
   *
   * @throws \InvalidArgumentException
   *   May throw an error when given $name has never been defined before.
   */
  public function association(string $name, array $opts = []): callable {
    return function () use ($name, $opts) {
      $factory_type = $this->getType($name);
      $factory_object = $this->create($name, $opts);

      foreach ($this->chainFactoryTypeResolver->getResolvers() as $resolver) {
        if ($resolver->shouldApply($factory_type)) {
          return $resolver->getIdentifier($factory_object);
        }
      }

      throw new \InvalidArgumentException("Factories of type '{$factory_type}' are not supported.");
    };
  }

  /**
   * Returns the default options generated for a given factory.
   *
   * Each callable option will be processed in this method.
   *
   * @param string $factory_name
   *   The name of the factory options being fetched.
   *
   * @return array
   *   The generated options with all processed callable.
   *
   * @throws \InvalidArgumentException
   *   May throw an exception when attempting fetching unavailable factory.
   */
  public function getDefaultOptions(string $factory_name): array {
    if (!isset($this->definitions[$factory_name])) {
      throw new \InvalidArgumentException("There is no factory definition called {$factory_name}.");
    }

    $default_options = $this->definitions[$factory_name]['opts'];

    foreach ($default_options as $opt_name => $opt) {
      if ($opt instanceof \Closure) {
        $default_options[$opt_name] = $opt();
      }
    }

    return $default_options;
  }

  /**
   * This method loads and stores all factories definitions.
   *
   * @param array $excluded
   *   Factories name to avoid loading.
   */
  public function loadAllDefinitions(array $excluded = []): void {
    foreach ($this->chainFactoryResolver->getResolvers() as $resolver) {
      // Skip excluded factories.
      if (in_array($resolver->getName(), $excluded, TRUE)) {
        continue;
      }

      $resolver->resolve($this);
    }
  }

  /**
   * This method loads and stores specific factory definitions.
   *
   * @param string[] $factories
   *   Factories name to load.
   */
  public function loadDefinitions(array $factories): void {
    foreach ($this->chainFactoryResolver->getResolvers() as $resolver) {
      // Resolve only given factories.
      if (!in_array($resolver->getName(), $factories, TRUE)) {
        continue;
      }
      $resolver->resolve($this);
    }
  }

  /**
   * A method for inspecting currently defined factories.
   *
   * @return array
   *   Array of factory definitions.
   */
  public function getDefinitions(): array {
    return $this->definitions;
  }

  /**
   * Fetch one defined factories.
   *
   * @param string $name
   *   The Factory definition to inspect.
   *
   * @return array|null
   *   The defined factory as array or NULL when not found.
   */
  public function getDefinition(string $name): ?array {
    return $this->definitions[$name] ?? NULL;
  }

  /**
   * Creates a sequence string based on an incrementing integer.
   *
   * This is typically used to generate unique names such as usernames.
   *
   * The parameter may be a function that receives a counter value
   * each time the entity is created or it may be a string.
   *
   * If the parameter is a string string containing "%d" then it will be
   * replaced by the counter value. If the string does not contain "%d"
   * then the number is simply appended to the parameter.
   *
   * @param callable|string $generator
   *   The function or pattern to generate a value from.
   * @param int $start
   *   The first number to use.
   *
   * @return callable
   *   A callable function that to assist with uniqueness constraints.
   */
  public static function sequence($generator, $start = 1): callable {
    $n = $start - 1;
    if (is_callable($generator)) {
      return static function () use (&$n, $generator) {
        $n++;
        return $generator($n);
      };
    }

    if (strpos($generator, '%d') !== FALSE) {
      return static function () use (&$n, $generator) {
        $n++;
        return str_replace('%d', $n, $generator);
      };
    }

    return static function () use (&$n, $generator) {
      $n++;
      return $generator . $n;
    };
  }

}

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

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