-
Notifications
You must be signed in to change notification settings - Fork 611
/
BaseContentFixture.php
157 lines (138 loc) · 4.42 KB
/
BaseContentFixture.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<?php
/**
* @link https://craftcms.com/
* @copyright Copyright (c) Pixel & Tonic, Inc.
* @license https://craftcms.github.io/license/
*/
namespace craft\test\fixtures\elements;
use Craft;
use craft\base\ElementInterface;
use craft\errors\InvalidElementException;
use yii\base\Exception;
use yii\base\InvalidConfigException;
use yii\test\DbFixture;
use yii\test\FileFixtureTrait;
/**
* BaseContentFixture is a base class for setting up fixtures for populating existing elements.
*
* Fixture classes that extend this class should set [[$elementType]] to the class name of an element type,
* and [[$dataFile]] to the path to a data file.
*
* The data file should return an array, where each item is a sub-array containing the following keys:
*
* - `criteria` – an array of element query param names/values that should be used to locate the element
* - `attributes` – an array of attribute names/values that should be set on the element
* - `fields` – an array of custom field handles/values that should be set on the element
*
* @author Pixel & Tonic, Inc. <support@pixelandtonic.com>
* @since 3.6.0
*/
abstract class BaseContentFixture extends DbFixture
{
use FileFixtureTrait;
/**
* @var string The element type this is for
* @phpstan-var class-string<ElementInterface>
*/
public string $elementType;
/**
* @var bool Whether to skip elements that can’t be found per the criteria in the [[$dataFile|data file]].
*
* If this is set to `false`, an exception will be thrown.
*/
public bool $skipMissingElements = true;
/**
* @var ElementInterface[]|null[] The loaded elements
*/
private array $_elements = [];
/**
* @inheritdoc
*/
public function init(): void
{
parent::init();
if (!isset($this->elementType) || !is_subclass_of($this->elementType, ElementInterface::class)) {
throw new InvalidConfigException('$elementType must set to a valid element class name');
}
}
/**
* @inheritdoc
*/
public function load(): void
{
foreach ($this->loadData($this->dataFile) as $key => $data) {
$element = $this->findElement($data);
if ($element === null) {
if ($this->skipMissingElements) {
continue;
}
throw new Exception("Couldn’t find element of type $this->elementType with the given criteria");
}
$this->populateElement($element, $data);
if (!$this->saveElement($element)) {
throw new InvalidElementException($element, implode(' ', $element->getErrorSummary(true)));
}
$this->_elements[$key] = $element;
}
}
/**
* @inheritdoc
*/
public function unload(): void
{
$this->_elements = [];
}
/**
* Get element model.
*
* @param string $key The key of the element in the [[$dataFile|data file]].
* @return ElementInterface|null
*/
public function getElement(string $key): ?ElementInterface
{
return $this->_elements[$key] ?? null;
}
/**
* Finds an element with the given criteria.
*
* @param array $data
* @return ElementInterface|null
*/
protected function findElement(array $data): ?ElementInterface
{
/** @var ElementInterface $class */
$class = $this->elementType;
$query = $class::find();
if (isset($data['criteria'])) {
Craft::configure($query, $data['criteria']);
}
return $query->one();
}
/**
* Populates an element with the given attributes/custom field values.
*
* @param ElementInterface $element
* @param array $data
*/
protected function populateElement(ElementInterface $element, array $data): void
{
if (isset($data['attributes'])) {
foreach ($data['attributes'] as $name => $value) {
$element->$name = $value;
}
}
if (isset($data['fields'])) {
$element->setFieldValues($data['fields']);
}
}
/**
* Saves an element.
*
* @param ElementInterface $element The element to be saved
* @return bool Whether the save was successful
*/
protected function saveElement(ElementInterface $element): bool
{
return Craft::$app->getElements()->saveElement($element, true, true, false);
}
}