Completed
Push — develop ( 1fc05f...5ee0f5 )
by Nate
11:17
created

upsertEnvironmentsInternal()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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