Completed
Push — master ( 04ac75...81967b )
by Nate
17:06
created

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