PageRenderTime 55ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Nelmio/Alice/Fixtures.php

https://bitbucket.org/akiraaisha/alice
PHP | 274 lines | 171 code | 43 blank | 60 comment | 22 complexity | 9217e4914c1e092e4fa2ece8e2b929ff MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Alice package.
  4. *
  5. * (c) Nelmio <hello@nelm.io>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Nelmio\Alice;
  11. use Doctrine\Common\Persistence\ObjectManager;
  12. use Nelmio\Alice\Persister\Doctrine;
  13. use Psr\Log\LoggerInterface;
  14. use Nelmio\Alice\Fixtures\Loader;
  15. class Fixtures
  16. {
  17. /**
  18. * @var Loader[]
  19. */
  20. private static $loaders = [];
  21. /**
  22. * @var PersisterInterface
  23. */
  24. protected $persister;
  25. /**
  26. * @var array
  27. */
  28. protected $defaultOptions = [
  29. 'locale' => 'en_US',
  30. 'providers' => [],
  31. 'seed' => 1,
  32. 'logger' => null,
  33. 'persist_once' => false,
  34. ];
  35. /**
  36. * @var ProcessorInterface[]
  37. */
  38. protected $processors;
  39. /**
  40. * @param PersisterInterface $persister
  41. * @param array $defaultOptions
  42. * @param array $processors
  43. *
  44. * @throws \InvalidArgumentException
  45. */
  46. public function __construct(PersisterInterface $persister, array $defaultOptions = [], array $processors = [])
  47. {
  48. $this->persister = $persister;
  49. $this->validateOptions($defaultOptions);
  50. $this->defaultOptions = array_merge($this->defaultOptions, $defaultOptions);
  51. foreach ($processors as $processor) {
  52. if (false === $processor instanceof ProcessorInterface) {
  53. throw new \InvalidArgumentException(
  54. 'Expected processor to implement Nelmio\Alice\Fixtures\ProcessorInterface.'
  55. );
  56. }
  57. }
  58. $this->processors = $processors;
  59. }
  60. /**
  61. * Loads a fixture file into an object persister.
  62. *
  63. * @param string|array $files filename, glob mask (e.g. *.yml) or array of filenames to load data from, or data array
  64. * @param object $persister object persister
  65. * @param array $options available options:
  66. * - providers: an array of additional faker providers
  67. * - locale: the faker locale
  68. * - seed: a seed to make sure faker generates data consistently across
  69. * runs, set to null to disable
  70. * - logger: a callable or Psr\Log\LoggerInterface object that will receive progress information
  71. * - persist_once: only persist objects once if multiple files are passed
  72. * @param array $processors optional array of ProcessorInterface instances
  73. */
  74. public static function load($files, $persister, array $options = [], array $processors = [])
  75. {
  76. $_persister = null;
  77. switch (true) {
  78. case $persister instanceof PersisterInterface:
  79. $_persister = $persister;
  80. break;
  81. case $persister instanceof ObjectManager:
  82. $_persister = new Doctrine($persister);
  83. break;
  84. default:
  85. throw new \InvalidArgumentException('Unknown persister type '.get_class($persister));
  86. }
  87. $fixtures = new static($_persister, $options, $processors);
  88. return $fixtures->loadFiles($files);
  89. }
  90. /**
  91. * @param $files
  92. * @param array $options
  93. *
  94. * @return array
  95. */
  96. public function loadFiles($files, array $options = [])
  97. {
  98. $this->validateOptions($options);
  99. $options = array_merge($this->defaultOptions, $options);
  100. // glob strings to filenames
  101. if (!is_array($files)) {
  102. $matches = glob($files, GLOB_BRACE);
  103. if (!$matches && !file_exists($files)) {
  104. throw new \InvalidArgumentException('The file could not be found: '.$files);
  105. }
  106. $files = $matches;
  107. }
  108. // wrap the data array in an array of one data array
  109. if (!is_string(current($files))) {
  110. $files = [$files];
  111. }
  112. $objects = [];
  113. foreach ($files as $file) {
  114. $loader = self::getLoader($options);
  115. if (isset($options['logger'])) {
  116. $loader->setLogger($options['logger']);
  117. }
  118. $loader->setPersister($this->persister);
  119. $set = $loader->load($file);
  120. if (!$options['persist_once']) {
  121. $this->persist($this->persister, $set);
  122. }
  123. $objects = array_merge($objects, $set);
  124. }
  125. if ($options['persist_once']) {
  126. $this->persist($this->persister, $objects);
  127. }
  128. return $objects;
  129. }
  130. public function addProcessor(ProcessorInterface $processor)
  131. {
  132. $this->processors[] = $processor;
  133. }
  134. protected function persist($persister, $objects)
  135. {
  136. foreach ($this->processors as $proc) {
  137. foreach ($objects as $obj) {
  138. $proc->preProcess($obj);
  139. }
  140. }
  141. $persister->persist($objects);
  142. foreach ($this->processors as $proc) {
  143. foreach ($objects as $obj) {
  144. $proc->postProcess($obj);
  145. }
  146. }
  147. }
  148. private static function generateLoaderKey(array $options)
  149. {
  150. $providers = '';
  151. if (!empty($options['providers'])) {
  152. foreach ($options['providers'] as $item) {
  153. if (is_object($item)) {
  154. $item = get_class($item);
  155. }
  156. // turn all of the class names into fully-qualified ones
  157. $item = '\\' . ltrim($item, '\\');
  158. $providers .= $item;
  159. }
  160. }
  161. return sprintf(
  162. '%s_%s_%s',
  163. (is_numeric($options['seed'])
  164. ? strval($options['seed'])
  165. : gettype($options['seed'])
  166. ),
  167. $options['locale'],
  168. (!strlen($providers)
  169. ? ''
  170. : md5($providers)
  171. )
  172. );
  173. }
  174. private static function getLoader(array $options)
  175. {
  176. // Generate an array key based on the options, so that separate loaders
  177. // will be created when we want to load several fixtures that use different
  178. // custom providers.
  179. $loaderKey = self::generateLoaderKey($options);
  180. if (!isset(self::$loaders[$loaderKey])) {
  181. self::$loaders[$loaderKey] = new Loader($options['locale'], $options['providers'], $options['seed']);
  182. }
  183. return self::$loaders[$loaderKey];
  184. }
  185. /**
  186. * Checks if the options are valid or not. If not throws an exception.
  187. *
  188. * @param array $options
  189. *
  190. * @throws \InvalidArgumentException
  191. */
  192. private function validateOptions(array $options)
  193. {
  194. foreach (array_keys($options) as $key) {
  195. if (false === array_key_exists($key, $this->defaultOptions)) {
  196. throw new \InvalidArgumentException(sprintf(
  197. 'Unknown key "%s", expected: %s',
  198. $key,
  199. implode(', ', array_keys($this->defaultOptions))
  200. ));
  201. }
  202. }
  203. if (isset($options['providers'])) {
  204. $providers = $options['providers'];
  205. if (false === is_array($providers)) {
  206. throw new \InvalidArgumentException('Expected "providers" option value to be an array');
  207. }
  208. foreach ($providers as $provider) {
  209. if (false === is_object($provider) && false === is_string($provider)) {
  210. throw new \InvalidArgumentException(sprintf(
  211. 'The provider should be a string or an object, got %s instead',
  212. is_scalar($provider) ? $provider : gettype($provider)
  213. ));
  214. }
  215. }
  216. }
  217. if (isset($options['logger'])) {
  218. $logger = $options['logger'];
  219. if (false === is_callable($logger) && false === $logger instanceof LoggerInterface) {
  220. throw new \InvalidArgumentException(
  221. 'Expected "logger" option value to be a callable or to implement Psr\Log\LoggerInterface'
  222. );
  223. }
  224. }
  225. if (isset($options['persist_once'])) {
  226. if (false === is_bool($options['persist_once'])) {
  227. throw new \InvalidArgumentException('Expected "persist_once" option value value to be a boolean.');
  228. }
  229. }
  230. }
  231. }