laravelflux /
laravel-fixtures
| 1 | <?php |
||
| 2 | |||
| 3 | namespace LaravelFlux\Fixture\Traits; |
||
| 4 | |||
| 5 | use LaravelFlux\Fixture\Exceptions\InvalidConfigException; |
||
| 6 | use LaravelFlux\Fixture\Fixture; |
||
| 7 | |||
| 8 | /** |
||
| 9 | * FixtureTrait provides functionality for loading, unloading and accessing fixtures for a test case. |
||
| 10 | * |
||
| 11 | * By using FixtureTrait, a test class will be able to specify which fixtures to load by overriding |
||
| 12 | * the [[fixtures()]] method. It can then load and unload the fixtures using [[loadFixtures()]] and [[unloadFixtures()]]. |
||
| 13 | */ |
||
| 14 | trait FixtureTrait |
||
| 15 | { |
||
| 16 | /** |
||
| 17 | * @var array the list of fixture objects available for the current test. |
||
| 18 | * The array keys are the corresponding fixture class names. |
||
| 19 | * The fixtures are listed in their dependency order. That is, fixture A is listed before B |
||
| 20 | * if B depends on A. |
||
| 21 | */ |
||
| 22 | private $_fixtures; |
||
| 23 | |||
| 24 | /** |
||
| 25 | * Declares the fixtures that are needed by the current test case. |
||
| 26 | * |
||
| 27 | * The return value of this method must be an array of fixture configurations. For example, |
||
| 28 | * |
||
| 29 | * ```php |
||
| 30 | * [ |
||
| 31 | * // anonymous fixture |
||
| 32 | * ArticleFixture::class, |
||
| 33 | * // "articles" fixture |
||
| 34 | * 'articles' => ArticleFixture::class, |
||
| 35 | * ] |
||
| 36 | * ``` |
||
| 37 | * |
||
| 38 | * @return array the fixtures needed by the current test case |
||
| 39 | */ |
||
| 40 | public function fixtures(): array |
||
| 41 | { |
||
| 42 | return []; |
||
| 43 | } |
||
| 44 | |||
| 45 | /** |
||
| 46 | * Loads the specified fixtures. |
||
| 47 | * This method will call [[Fixture::load()]] for every fixture object. |
||
| 48 | * |
||
| 49 | * @param Fixture[] $fixtures the fixtures to be loaded. If this parameter is not specified, |
||
| 50 | * the return value of [[getFixtures()]] will be used. |
||
| 51 | * |
||
| 52 | * @throws InvalidConfigException |
||
| 53 | */ |
||
| 54 | public function loadFixtures($fixtures = null): void |
||
| 55 | { |
||
| 56 | if ($fixtures === null) { |
||
| 57 | $fixtures = $this->getFixtures(); |
||
| 58 | } |
||
| 59 | |||
| 60 | /* @var $fixture Fixture */ |
||
| 61 | foreach ($fixtures as $fixture) { |
||
| 62 | $fixture->beforeLoad(); |
||
| 63 | } |
||
| 64 | |||
| 65 | foreach ($fixtures as $fixture) { |
||
| 66 | $fixture->load(); |
||
| 67 | } |
||
| 68 | |||
| 69 | foreach (array_reverse($fixtures) as $fixture) { |
||
| 70 | $fixture->afterLoad(); |
||
| 71 | } |
||
| 72 | } |
||
| 73 | |||
| 74 | /** |
||
| 75 | * Unloads the specified fixtures. |
||
| 76 | * This method will call [[Fixture::unload()]] for every fixture object. |
||
| 77 | * |
||
| 78 | * @param Fixture[] $fixtures the fixtures to be loaded. If this parameter is not specified, |
||
| 79 | * the return value of [[getFixtures()]] will be used. |
||
| 80 | * |
||
| 81 | * @throws InvalidConfigException |
||
| 82 | */ |
||
| 83 | public function unloadFixtures($fixtures = null): void |
||
| 84 | { |
||
| 85 | if ($fixtures === null) { |
||
| 86 | $fixtures = $this->getFixtures(); |
||
| 87 | } |
||
| 88 | |||
| 89 | /* @var $fixture Fixture */ |
||
| 90 | foreach ($fixtures as $fixture) { |
||
| 91 | $fixture->beforeUnload(); |
||
| 92 | } |
||
| 93 | |||
| 94 | $fixtures = array_reverse($fixtures); |
||
| 95 | |||
| 96 | foreach ($fixtures as $fixture) { |
||
| 97 | $fixture->unload(); |
||
| 98 | } |
||
| 99 | |||
| 100 | foreach ($fixtures as $fixture) { |
||
| 101 | $fixture->afterUnload(); |
||
| 102 | } |
||
| 103 | } |
||
| 104 | |||
| 105 | /** |
||
| 106 | * Initialize the fixtures |
||
| 107 | * |
||
| 108 | * @throws InvalidConfigException |
||
| 109 | */ |
||
| 110 | public function initFixtures(): void |
||
| 111 | { |
||
| 112 | $this->unloadFixtures(); |
||
| 113 | $this->loadFixtures(); |
||
| 114 | } |
||
| 115 | |||
| 116 | /** |
||
| 117 | * Returns the fixture objects as specified in [[globalFixtures()]] and [[fixtures()]]. |
||
| 118 | * |
||
| 119 | * @throws InvalidConfigException |
||
| 120 | * |
||
| 121 | * @return Fixture[] the loaded fixtures for the current test case |
||
| 122 | */ |
||
| 123 | public function getFixtures() |
||
| 124 | { |
||
| 125 | if ($this->_fixtures === null) { |
||
| 126 | $this->_fixtures = $this->createFixtures($this->fixtures()); |
||
| 127 | } |
||
| 128 | |||
| 129 | return $this->_fixtures; |
||
| 130 | } |
||
| 131 | |||
| 132 | /** |
||
| 133 | * Returns the named fixture. |
||
| 134 | * |
||
| 135 | * @param $name |
||
| 136 | * |
||
| 137 | * @return Fixture|null the fixture object, or null if the named fixture does not exist |
||
| 138 | * |
||
| 139 | * @throws InvalidConfigException |
||
| 140 | */ |
||
| 141 | public function getFixture(string $name): Fixture |
||
| 142 | { |
||
| 143 | if ($this->_fixtures === null) { |
||
| 144 | $this->_fixtures = $this->createFixtures($this->fixtures()); |
||
| 145 | } |
||
| 146 | |||
| 147 | $name = ltrim($name, '\\'); |
||
| 148 | |||
| 149 | return $this->_fixtures[$name] ?? null; |
||
|
0 ignored issues
–
show
Bug
Best Practice
introduced
by
Loading history...
|
|||
| 150 | } |
||
| 151 | |||
| 152 | /** |
||
| 153 | * Creates the specified fixture instances. |
||
| 154 | * All dependent fixtures will also be created. |
||
| 155 | * |
||
| 156 | * @param array $fixtures the fixtures to be created. You may provide fixture names or fixture configurations. |
||
| 157 | * If this parameter is not provided, the fixtures specified in [[globalFixtures()]] and [[fixtures()]] will be created. |
||
| 158 | * |
||
| 159 | * @throws InvalidConfigException if fixtures are not properly configured or if a circular dependency among |
||
| 160 | * the fixtures is detected |
||
| 161 | * |
||
| 162 | * @return Fixture[] the created fixture instances |
||
| 163 | */ |
||
| 164 | protected function createFixtures(array $fixtures) |
||
| 165 | { |
||
| 166 | // normalize fixture configurations |
||
| 167 | $config = []; // configuration provided in test case |
||
| 168 | $aliases = []; // class name => alias or class name |
||
| 169 | |||
| 170 | foreach ($fixtures as $name => $fixture) { |
||
| 171 | if (!is_array($fixture)) { |
||
| 172 | $class = ltrim($fixture, '\\'); |
||
| 173 | $fixtures[$name] = ['class' => $class]; |
||
| 174 | $aliases[$class] = is_int($name) ? $class : $name; |
||
| 175 | } elseif (isset($fixture['class'])) { |
||
| 176 | $class = ltrim($fixture['class'], '\\'); |
||
| 177 | $config[$class] = $fixture; |
||
| 178 | $aliases[$class] = $name; |
||
| 179 | } else { |
||
| 180 | throw new InvalidConfigException("You must specify 'class' for the fixture '$name'."); |
||
| 181 | } |
||
| 182 | } |
||
| 183 | |||
| 184 | // create fixture instances |
||
| 185 | $instances = []; |
||
| 186 | $stack = array_reverse($fixtures); |
||
| 187 | |||
| 188 | while (($fixture = array_pop($stack)) !== null) { |
||
| 189 | if ($fixture instanceof Fixture) { |
||
| 190 | $class = get_class($fixture); |
||
| 191 | $name = isset($aliases[$class]) ? $aliases[$class] : $class; |
||
| 192 | unset($instances[$name]); // unset so that the fixture is added to the last in the next line |
||
| 193 | $instances[$name] = $fixture; |
||
| 194 | } else { |
||
| 195 | $class = ltrim($fixture['class'], '\\'); |
||
| 196 | $name = isset($aliases[$class]) ? $aliases[$class] : $class; |
||
| 197 | if (!isset($instances[$name])) { |
||
| 198 | $instances[$name] = false; |
||
| 199 | |||
| 200 | $stack[] = $fixture = app()->make($fixture['class']); |
||
| 201 | foreach ($fixture->depends as $dep) { |
||
| 202 | // need to use the configuration provided in test case |
||
| 203 | $stack[] = isset($config[$dep]) ? $config[$dep] : ['class' => $dep]; |
||
| 204 | } |
||
| 205 | } elseif ($instances[$name] === false) { |
||
| 206 | throw new InvalidConfigException("A circular dependency is detected for fixture '$class'."); |
||
| 207 | } |
||
| 208 | } |
||
| 209 | } |
||
| 210 | |||
| 211 | return $instances; |
||
| 212 | } |
||
| 213 | } |
||
| 214 |