Completed
Push — 2.1 ( 7c8525...0afc41 )
by Alexander
21:05 queued 16:02
created

FixtureTrait::getFixture()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3.072

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 4
cts 5
cp 0.8
rs 9.6666
c 0
b 0
f 0
cc 3
eloc 5
nc 4
nop 1
crap 3.072
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\test;
9
10
use Yii;
11
use yii\base\InvalidConfigException;
12
13
/**
14
 * FixtureTrait provides functionalities for loading, unloading and accessing fixtures for a test case.
15
 *
16
 * By using FixtureTrait, a test class will be able to specify which fixtures to load by overriding
17
 * the [[fixtures()]] method. It can then load and unload the fixtures using [[loadFixtures()]] and [[unloadFixtures()]].
18
 * Once a fixture is loaded, it can be accessed like an object property, thanks to the PHP `__get()` magic method.
19
 * Also, if the fixture is an instance of [[ActiveFixture]], you will be able to access AR models
20
 * through the syntax `$this->fixtureName('model name')`.
21
 *
22
 * For more details and usage information on FixtureTrait, see the [guide article on fixtures](guide:test-fixtures).
23
 *
24
 * @author Qiang Xue <[email protected]>
25
 * @since 2.0
26
 */
27
trait FixtureTrait
28
{
29
    /**
30
     * @var array the list of fixture objects available for the current test.
31
     * The array keys are the corresponding fixture class names.
32
     * The fixtures are listed in their dependency order. That is, fixture A is listed before B
33
     * if B depends on A.
34
     */
35
    private $_fixtures;
36
37
38
    /**
39
     * Declares the fixtures that are needed by the current test case.
40
     * The return value of this method must be an array of fixture configurations. For example,
41
     *
42
     * ```php
43
     * [
44
     *     // anonymous fixture
45
     *     PostFixture::class,
46
     *     // "users" fixture
47
     *     'users' => UserFixture::class,
48
     *     // "cache" fixture with configuration
49
     *     'cache' => [
50
     *          'class' => CacheFixture::class,
51
     *          'host' => 'xxx',
52
     *     ],
53
     * ]
54
     * ```
55
     *
56
     * Note that the actual fixtures used for a test case will include both [[globalFixtures()]]
57
     * and [[fixtures()]].
58
     *
59
     * @return array the fixtures needed by the current test case
60
     */
61
    public function fixtures()
62
    {
63
        return [];
64
    }
65
66
    /**
67
     * Declares the fixtures shared required by different test cases.
68
     * The return value should be similar to that of [[fixtures()]].
69
     * You should usually override this method in a base class.
70
     * @return array the fixtures shared and required by different test cases.
71
     * @see fixtures()
72
     */
73 10
    public function globalFixtures()
74
    {
75 10
        return [];
76
    }
77
78
    /**
79
     * Loads the specified fixtures.
80
     * This method will call [[Fixture::load()]] for every fixture object.
81
     * @param Fixture[] $fixtures the fixtures to be loaded. If this parameter is not specified,
82
     * the return value of [[getFixtures()]] will be used.
83
     */
84 15
    public function loadFixtures($fixtures = null)
85
    {
86 15
        if ($fixtures === null) {
87 10
            $fixtures = $this->getFixtures();
88
        }
89
90
        /* @var $fixture Fixture */
91 15
        foreach ($fixtures as $fixture) {
92 15
            $fixture->beforeLoad();
93
        }
94 15
        foreach ($fixtures as $fixture) {
95 15
            $fixture->load();
96
        }
97 15
        foreach (array_reverse($fixtures) as $fixture) {
98 15
            $fixture->afterLoad();
99
        }
100 15
    }
101
102
    /**
103
     * Unloads the specified fixtures.
104
     * This method will call [[Fixture::unload()]] for every fixture object.
105
     * @param Fixture[] $fixtures the fixtures to be loaded. If this parameter is not specified,
106
     * the return value of [[getFixtures()]] will be used.
107
     */
108 19
    public function unloadFixtures($fixtures = null)
109
    {
110 19
        if ($fixtures === null) {
111 9
            $fixtures = $this->getFixtures();
112
        }
113
114
        /* @var $fixture Fixture */
115 19
        foreach ($fixtures as $fixture) {
116 19
            $fixture->beforeUnload();
117
        }
118 19
        $fixtures = array_reverse($fixtures);
119 19
        foreach ($fixtures as $fixture) {
120 19
            $fixture->unload();
121
        }
122 19
        foreach ($fixtures as $fixture) {
123 19
            $fixture->afterUnload();
124
        }
125 19
    }
126
127
    /**
128
     * Initialize the fixtures
129
     * @since 2.0.12
130
     */
131 8
    public function initFixtures()
132
    {
133 8
        $this->unloadFixtures();
134 8
        $this->loadFixtures();
135 8
    }
136
137
    /**
138
     * Returns the fixture objects as specified in [[globalFixtures()]] and [[fixtures()]].
139
     * @return Fixture[] the loaded fixtures for the current test case
140
     */
141 10
    public function getFixtures()
142
    {
143 10
        if ($this->_fixtures === null) {
144 10
            $this->_fixtures = $this->createFixtures(array_merge($this->globalFixtures(), $this->fixtures()));
145
        }
146
147 10
        return $this->_fixtures;
148
    }
149
150
    /**
151
     * Returns the named fixture.
152
     * @param string $name the fixture name. This can be either the fixture alias name, or the class name if the alias is not used.
153
     * @return Fixture the fixture object, or null if the named fixture does not exist.
154
     */
155 9
    public function getFixture($name)
156
    {
157 9
        if ($this->_fixtures === null) {
158
            $this->_fixtures = $this->createFixtures(array_merge($this->globalFixtures(), $this->fixtures()));
159
        }
160 9
        $name = ltrim($name, '\\');
161
162 9
        return isset($this->_fixtures[$name]) ? $this->_fixtures[$name] : null;
163
    }
164
165
    /**
166
     * Creates the specified fixture instances.
167
     * All dependent fixtures will also be created.
168
     * @param array $fixtures the fixtures to be created. You may provide fixture names or fixture configurations.
169
     * If this parameter is not provided, the fixtures specified in [[globalFixtures()]] and [[fixtures()]] will be created.
170
     * @return Fixture[] the created fixture instances
171
     * @throws InvalidConfigException if fixtures are not properly configured or if a circular dependency among
172
     * the fixtures is detected.
173
     */
174 20
    protected function createFixtures(array $fixtures)
175
    {
176
        // normalize fixture configurations
177 20
        $config = [];  // configuration provided in test case
178 20
        $aliases = [];  // class name => alias or class name
179 20
        foreach ($fixtures as $name => $fixture) {
180 20
            if (!is_array($fixture)) {
181 20
                $class = ltrim($fixture, '\\');
182 20
                $fixtures[$name] = ['class' => $class];
183 20
                $aliases[$class] = is_int($name) ? $class : $name;
184
            } elseif (isset($fixture['class'])) {
185
                $class = ltrim($fixture['class'], '\\');
186
                $config[$class] = $fixture;
187
                $aliases[$class] = $name;
188
            } else {
189
                throw new InvalidConfigException("You must specify 'class' for the fixture '$name'.");
190
            }
191
        }
192
193
        // create fixture instances
194 20
        $instances = [];
195 20
        $stack = array_reverse($fixtures);
196 20
        while (($fixture = array_pop($stack)) !== null) {
197 20
            if ($fixture instanceof Fixture) {
198 20
                $class = get_class($fixture);
199 20
                $name = isset($aliases[$class]) ? $aliases[$class] : $class;
200 20
                unset($instances[$name]);  // unset so that the fixture is added to the last in the next line
201 20
                $instances[$name] = $fixture;
202
            } else {
203 20
                $class = ltrim($fixture['class'], '\\');
204 20
                $name = isset($aliases[$class]) ? $aliases[$class] : $class;
205 20
                if (!isset($instances[$name])) {
206 20
                    $instances[$name] = false;
207 20
                    $stack[] = $fixture = Yii::createObject($fixture);
208 20
                    foreach ($fixture->depends as $dep) {
209
                        // need to use the configuration provided in test case
210 20
                        $stack[] = isset($config[$dep]) ? $config[$dep] : ['class' => $dep];
211
                    }
212 2
                } elseif ($instances[$name] === false) {
213
                    throw new InvalidConfigException("A circular dependency is detected for fixture '$class'.");
214
                }
215
            }
216
        }
217
218 20
        return $instances;
219
    }
220
}
221