PivotTrait::addPivot()   A
last analyzed

Complexity

Conditions 4
Paths 6

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 19
ccs 11
cts 11
cp 1
rs 9.9
c 0
b 0
f 0
cc 4
nc 6
nop 3
crap 4
1
<?php
2
3
namespace carono\yii2migrate\traits;
4
5
use carono\yii2migrate\helpers\SchemaHelper;
6
use yii\db\ActiveQuery;
7
use yii\db\ActiveRecord;
8
use yii\helpers\ArrayHelper;
9
10
/**
11
 * Trait PivotTrait
12
 *
13
 * @package carono\yii2migrate\traits
14
 */
15
trait PivotTrait
16
{
17
    protected $_storage = [];
18
19
    /**
20
     * @param string|ActiveRecord $pivotClass
21
     *
22
     * @return mixed
23
     */
24
    public function deletePivots($pivotClass, $condition = [])
25 2
    {
26
        return $pivotClass::deleteAll(array_merge([$this->getPivotMainPkField($this, $pivotClass) => $this->getMainPk()], $condition));
0 ignored issues
show
Bug introduced by
$this of type carono\yii2migrate\traits\PivotTrait is incompatible with the type yii\db\ActiveRecord expected by parameter $model of carono\yii2migrate\trait...::getPivotMainPkField(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

26
        return $pivotClass::deleteAll(array_merge([$this->getPivotMainPkField(/** @scrutinizer ignore-type */ $this, $pivotClass) => $this->getMainPk()], $condition));
Loading history...
27 2
    }
28
29
    /**
30
     * @param string $pivotClass
31
     *
32
     * @return ActiveRecord[]
33
     */
34
    public function getStoragePivots($pivotClass)
35 6
    {
36
        if (isset($this->_storage[$pivotClass])) {
37 6
            return array_values($this->_storage[$pivotClass]);
38 6
        }
39
40
        return [];
41 1
    }
42
43
    /**
44
     * @return array
45
     */
46
    public function getPivotStorage()
47 2
    {
48
        return $this->_storage;
49 2
    }
50
51
    /**
52
     * @param ActiveRecord|null $model
53
     * @param string|ActiveRecord $pivotClass
54
     * @return array
55
     */
56
    private function getPivotCondition($model, $pivotClass)
57 19
    {
58
        $mainPk = $this->getPivotMainPkField($this, $pivotClass);
0 ignored issues
show
Bug introduced by
$this of type carono\yii2migrate\traits\PivotTrait is incompatible with the type yii\db\ActiveRecord expected by parameter $model of carono\yii2migrate\trait...::getPivotMainPkField(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

58
        $mainPk = $this->getPivotMainPkField(/** @scrutinizer ignore-type */ $this, $pivotClass);
Loading history...
59 19
        $condition = [$mainPk => $this->getMainPk()];
60 19
        if ($model !== null) {
61 19
            $slavePk = $this->getPivotSlavePkField($model, $pivotClass);
62 19
            $condition[$slavePk] = $model->getAttribute($model->primaryKey()[0]);
63 19
        }
64
        return $condition;
65 19
    }
66
67
    /**
68
     * @param ActiveRecord $model
69
     * @param string|ActiveRecord $pivotClass
70
     * @param array $condition
71
     * @return ActiveRecord|null
72
     */
73
    public function getPivot($model, $pivotClass, $condition = [])
74 2
    {
75
        return $this->findPivot($model, $pivotClass)->andWhere($condition)->one();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->findPivot(...here($condition)->one() also could return the type array which is incompatible with the documented return type null|yii\db\ActiveRecord.
Loading history...
76 2
    }
77
78
    /**
79
     * @param string|ActiveRecord $pivotClass
80
     * @param array $condition
81
     * @return ActiveRecord[]
82
     */
83
    public function getPivots($pivotClass, $condition = [])
84 1
    {
85
        return $this->findPivots($pivotClass)->andWhere($condition)->all();
86 1
    }
87
88
    /**
89
     * @param ActiveRecord $model
90
     * @param string|ActiveRecord $pivotClass
91
     * @return ActiveQuery
92
     */
93
    public function findPivot($model, $pivotClass)
94 5
    {
95
        return $pivotClass::find()->andWhere($this->getPivotCondition($model, $pivotClass));
96 5
    }
97
98
    /**
99
     * @param string|ActiveRecord $pivotClass
100
     * @return ActiveQuery
101
     */
102
    public function findPivots($pivotClass)
103 2
    {
104
        return $this->findPivot(null, $pivotClass);
105 2
    }
106
107
    /**
108
     * @param string $pivotClass
109
     */
110
    public function clearStorage($pivotClass)
111 1
    {
112
        unset($this->_storage[$pivotClass]);
113 1
    }
114 1
115
    /**
116
     * @param ActiveRecord[] $models
117
     * @param string $pivotClass
118
     * @param array $attributes
119
     */
120
    public function storagePivots($models, $pivotClass, $attributes = [])
121 2
    {
122
        foreach ((array)$models as $model) {
123 2
            $this->storagePivot($model, $pivotClass, $attributes);
124 2
        }
125
    }
126 2
127
    /**
128
     * @param ActiveRecord $model
129
     * @param string $pivotClass
130
     * @param array $attributes
131
     */
132
    public function storagePivot($model, $pivotClass, $attributes = [])
133 9
    {
134
        $this->_storage[$pivotClass][spl_object_hash($model)] = ['model' => $model, 'attributes' => $attributes];
135 9
    }
136 9
137
    public function getStoragePivotAttribute($model, $pivotClass)
138 12
    {
139
        return ArrayHelper::getValue($this->_storage, $pivotClass . '.' . spl_object_hash($model) . '.attributes', []);
140 12
    }
141
142
    /**
143
     * @param bool $clear
144
     */
145
    public function savePivots($clear = false, $condition = [])
146 2
    {
147
        foreach ($this->getPivotStorage() as $pivotClass => $items) {
148 2
            if ($clear) {
149 2
                $this->deletePivots($pivotClass, $condition);
150 1
            }
151
            foreach ($items as $item) {
152 2
                $this->addPivot($item['model'], $pivotClass, $item['attributes']);
153 2
            }
154
        }
155
    }
156 2
157
    /**
158
     * @param $model
159
     * @param $pivotClass
160
     * @param array $attributes
161
     * @return array|null|ActiveRecord
162
     * @throws \Exception
163
     */
164
    public function addPivot($model, $pivotClass, $attributes = [])
165 15
    {
166
        /**
167
         * @var ActiveRecord $pv
168
         */
169
        $pv = new $pivotClass;
170 15
        $attributes = $attributes ? $attributes : $this->getStoragePivotAttribute($model, $pivotClass);
171 15
        $condition = $this->getPivotCondition($model, $pivotClass);
172 15
        if ($find = (new ActiveQuery($pivotClass))->andWhere($condition)->one()) {
173 15
            if ($attributes) {
174 1
                $find->setAttributes($attributes, false);
175 1
                $find->save();
176 1
            }
177
            return $find;
178 1
        }
179
180
        $pv->setAttributes(array_merge($condition, $attributes), false);
181 15
        $pv->save();
182 15
        return $pv;
183 15
    }
184
185
    /**
186
     * @param ActiveRecord $model
187
     * @param string|ActiveRecord $pivotClass
188
     * @return mixed
189
     */
190
    public function deletePivot($model, $pivotClass, $condition = [])
191 2
    {
192
        return $pivotClass::deleteAll(array_merge([
193 2
            $this->getPivotMainPkField($this, $pivotClass) => $this->getMainPk(),
0 ignored issues
show
Bug introduced by
$this of type carono\yii2migrate\traits\PivotTrait is incompatible with the type yii\db\ActiveRecord expected by parameter $model of carono\yii2migrate\trait...::getPivotMainPkField(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

193
            $this->getPivotMainPkField(/** @scrutinizer ignore-type */ $this, $pivotClass) => $this->getMainPk(),
Loading history...
194 2
            $this->getPivotSlavePkField($model, $pivotClass) => $model->{$model->primaryKey()[0]}
195 2
        ], $condition));
196
    }
197
198
199
    /**
200
     * @return mixed
201
     */
202
    protected function getMainPk()
203 20
    {
204
        /**
205
         * @var ActiveRecord $this
206
         */
207
        return $this->{$this->primaryKey()[0]};
208 20
    }
209
210
    /**
211
     * @param ActiveRecord $model
212
     * @param string|ActiveRecord $pivotClass
213
     * @return string
214
     */
215
    protected function getPivotMainPkField($model, $pivotClass)
216 20
    {
217
        return $this->getPivotPkField($model, $pivotClass, false);
218 20
    }
219
220
    /**
221
     * @param $model
222
     * @param string|ActiveRecord $pivotClass
223
     * @return string
224
     */
225
    protected function getPivotSlavePkField($model, $pivotClass)
226 20
    {
227
        return $this->getPivotPkField($model, $pivotClass, true);
228 20
    }
229
230
    /**
231
     * @param ActiveRecord $model
232
     * @param ActiveRecord|string $pivotClass
233
     * @param bool $slave
234
     * @return int|null|string
235
     */
236
    private function getPivotPkField($model, $pivotClass, $slave = false)
237 23
    {
238
        if ($field = $this->getPkFieldByModel($model, $pivotClass)) {
239 23
            return $field;
240 15
        }
241
242
        $pks = $pivotClass::getDb()->getTableSchema($pivotClass::tableName())->primaryKey;
243 10
        return $slave ? $pks[1] : $pks[0];
244 10
    }
245
246
    /**
247
     * @param ActiveRecord $model
248
     * @param ActiveRecord|string $pivotClass
249
     * @return string
250
     */
251
    private function getPkFieldByModel($model, $pivotClass)
252 23
    {
253
        $pks = $pivotClass::getDb()->getTableSchema($pivotClass::tableName())->primaryKey;
254 23
        $fks = static::formFkKeys($pivotClass::getDb()->getTableSchema($pivotClass::tableName())->foreignKeys);
255 23
        $fks = array_values(array_filter($fks, function ($data) use ($pks) {
256
            return in_array($data['field'], $pks, true);
257 23
        }));
258 23
        $table = SchemaHelper::expandTablePrefix($model::tableName(), $model::getDb()->tablePrefix);
259 23
        $field = null;
260 23
        foreach ($fks as $fk) {
261 23
            if ($fk['table'] === $table) {
262 23
                if ($field) {
263 23
                    return null;
264 10
                }
265
266
                $field = $fk['field'];
267 23
            }
268
        }
269
        return $field;
270 15
    }
271
272
    /**
273
     * @param $array
274
     * @return array
275
     */
276
    private static function formFkKeys($array)
277 23
    {
278
        $result = [];
279 23
        foreach ($array as $key => $data) {
280 23
            $result[$key] = [
281 23
                'table' => ArrayHelper::remove($data, 0),
282 23
                'field' => key($data),
283 23
                'reference' => current($data),
284 23
            ];
285
        }
286
        return $result;
287
    }
288
}