Completed
Push — master ( 19b2ae...0e0de4 )
by Nate
04:43
created

saveEnvironments()   B

Complexity

Conditions 8
Paths 10

Size

Total Lines 44

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
dl 0
loc 44
ccs 0
cts 22
cp 0
rs 7.9715
c 0
b 0
f 0
cc 8
nc 10
nop 1
crap 72
1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://flipboxfactory.com/software/patron/license
6
 * @link       https://www.flipboxfactory.com/software/patron/
7
 */
8
9
namespace flipbox\patron\records;
10
11
use Craft;
12
use craft\helpers\ArrayHelper;
13
use flipbox\craft\ember\helpers\ObjectHelper;
14
use flipbox\craft\ember\helpers\QueryHelper;
15
use flipbox\craft\ember\records\ActiveRecordTrait;
16
use yii\db\ActiveQueryInterface;
17
use yii\db\ActiveRecord;
18
use yii\db\ActiveRecordInterface;
19
20
/**
21
 * @author Flipbox Factory <[email protected]>
22
 * @since 1.0.0
23
 *
24
 * @property ProviderEnvironment[] $environments
25
 */
26
trait RelatedEnvironmentsAttributeTrait
27
{
28
    use ActiveRecordTrait;
29
30
    /**
31
     * Populates the named relation with the related records.
32
     * Note that this method does not check if the relation exists or not.
33
     * @param string $name the relation name, e.g. `orders` for a relation
34
     * defined via `getOrders()` method (case-sensitive).
35
     * @param ActiveRecordInterface|array|null $records the related records to be
36
     * populated into the relation.
37
     * @see getRelation()
38
     */
39
    abstract public function populateRelation($name, $records);
40
41
    /**
42
     * Adds a new error to the specified attribute.
43
     * @param string $attribute attribute name
44
     * @param string $error new error message
45
     */
46
    abstract public function addError($attribute, $error = '');
47
48
    /**
49
     * @var bool
50
     */
51
    public $autoSaveEnvironments = true;
52
53
    /**
54
     * Environments that are temporarily set during the save process
55
     *
56
     * @var null|array
57
     */
58
    private $insertEnvironments;
59
60
    /**
61
     * @return string
62
     */
63
    abstract protected static function environmentRecordClass(): string;
64
65
    /**
66
     * @param array $config
67
     * @return array
68
     */
69
    abstract protected function prepareEnvironmentRecordConfig(array $config = []): array;
70
71
    /**
72
     * @return ActiveQueryInterface
73
     */
74
    abstract protected function environmentRelationshipQuery(): ActiveQueryInterface;
75
76
    /**
77
     * Get all of the associated environments.
78
     *
79
     * @param array $config
80
     * @return \yii\db\ActiveQueryInterface|\yii\db\ActiveQuery
81
     */
82
    public function getEnvironments(array $config = [])
83
    {
84
        $query = $this->environmentRelationshipQuery();
85
86
        if (!empty($config)) {
87
            QueryHelper::configure(
88
                $query,
89
                $config
90
            );
91
        }
92
93
        return $query;
94
    }
95
96
    /**
97
     * @param array $environments
98
     * @return $this
99
     */
100
    public function setEnvironments(array $environments = [])
101
    {
102
        $environments = array_filter($environments);
103
104
        // Do nothing
105
        if (empty($environments)) {
106
            $this->populateRelation('environments', []);
107
            return $this;
108
        }
109
110
        $currentEnvironments = (array)$this->environments;
111
        $currentEnvironments = ArrayHelper::index($currentEnvironments, 'environment');
112
113
        $records = [];
114
        foreach ($environments as $key => $environment) {
115
            $environment = $this->resolveEnvironment($environment);
116
117
            // Already set, use it
118
            if (array_key_exists($environment->getAttribute('environment'), $currentEnvironments)) {
119
                $environment = $currentEnvironments[$environment->getAttribute('environment')];
120
            }
121
122
            $records[] = $environment;
123
        }
124
125
        $this->populateRelation('environments', $records);
126
        return $this;
127
    }
128
129
    /**
130
     * @param $environment
131
     * @return ActiveRecord
132
     */
133
    protected function resolveEnvironment($environment)
134
    {
135
        $class = static::environmentRecordClass();
136
137
        if (is_subclass_of($environment, $class)) {
138
            return $environment;
139
        }
140
141
        // New record
142
        $record = new $class;
143
144
        // Force an array
145
        if (is_string($environment)) {
146
            $environment = ['environment' => $environment];
147
        }
148
149
        if (!is_array($environment)) {
150
            $environment = ArrayHelper::toArray($environment, [], false);
151
        }
152
153
        /** @noinspection PhpIncompatibleReturnTypeInspection */
154
        return ObjectHelper::populate(
155
            $record,
156
            $this->prepareEnvironmentRecordConfig($environment)
157
        );
158
    }
159
160
    /**
161
     * @param bool $force
162
     * @return bool
163
     * @throws \Throwable
164
     * @throws \yii\db\StaleObjectException
165
     */
166
    protected function saveEnvironments(bool $force = false): bool
167
    {
168
        if ($force === false && $this->autoSaveEnvironments !== true) {
169
            return true;
170
        }
171
172
        // Perhaps we explicitly want to ignore (set an empty array to remove)
173
        if (null === ($environments = $this->environments)) {
174
            return true;
175
        }
176
177
        $successful = true;
178
179
        /** @var ActiveRecord[] $allRecords */
180
        $allRecords = $this->getEnvironments()
181
            ->indexBy('environment')
182
            ->all();
183
184
        /** @var ActiveRecord $model */
185
        foreach ((array)$environments as $model) {
186
            ArrayHelper::remove($allRecords, $model->getAttribute('environment'));
187
188
            if (!$model->save()) {
189
                $successful = false;
190
191
                $error = Craft::t(
192
                    'patron',
193
                    "Couldn't save environment due to validation errors:"
194
                );
195
                foreach ($model->getFirstErrors() as $attributeError) {
196
                    $error .= "\n- " . Craft::t('patron', $attributeError);
197
                }
198
199
                $this->addError('environments', $error);
200
            }
201
        }
202
203
        // Delete old records
204
        foreach ($allRecords as $record) {
205
            $record->delete();
206
        }
207
208
        return $successful;
209
    }
210
211
    /*******************************************
212
     * EVENTS
213
     *******************************************/
214
215
    /**
216
     * @inheritdoc
217
     */
218
    protected function beforeSaveEnvironments($insert): bool
219
    {
220
        if ($insert !== true ||
221
            $this->isRelationPopulated('environments') !== true ||
222
            $this->autoSaveEnvironments !== true
223
        ) {
224
            return true;
225
        }
226
227
        $this->insertEnvironments = $this->environments;
228
229
        return true;
230
    }
231
232
    /**
233
     * We're extracting the environments that may have been explicitly set on the record.  When the 'id'
234
     * attribute is updated, it removes any associated relationships.
235
     *
236
     * @inheritdoc
237
     * @throws \Throwable
238
     */
239
    protected function insertInternalEnvironments($attributes = null)
240
    {
241
        if (null === $this->insertEnvironments) {
242
            return true;
243
        }
244
245
        $this->setEnvironments($this->insertEnvironments);
246
        $this->insertEnvironments = null;
247
248
        return $this->upsertEnvironmentsInternal($attributes);
249
    }
250
251
    /**
252
     * @param null $attributes
253
     * @return bool
254
     * @throws \Throwable
255
     * @throws \yii\db\StaleObjectException
256
     */
257
    protected function upsertEnvironmentsInternal($attributes = null): bool
258
    {
259
        if (empty($attributes)) {
260
            return $this->saveEnvironments();
261
        }
262
263
        if (array_key_exists('environments', $attributes)) {
264
            return $this->saveEnvironments(true);
265
        }
266
267
        return true;
268
    }
269
}
270