Issues (36)

src/MyAssignment.php (5 issues)

1
<?php
2
3
namespace andmemasin\myabstract;
4
5
use yii;
6
use yii\base\Model;
7
use andmemasin\myabstract\events\MyAssignmentEvent;
8
9
/**
10
 * This is a model to manage assignments to ParentHasChildren type of
11
 * entities. To assign & delete children to and from parent entities
12
 *
13
 * @property MyActiveRecord $lastChild @deprecated
14
 * @package app\models\myabstract
15
 * @author Tonis Ormisson <[email protected]>
16
 * {@inheritdoc}
17
 */
18
class MyAssignment  extends Model
19
{
20
21
    /** @var integer[] $children_ids*/
22
    public $children_ids = [];
23
24
    /** @var MyActiveRecord[] indexed by child PK */
25
    public $current_children;
26
27
    /** @var MyActiveRecord Last child by Time*/
28
    public $last_child;
29
30
    /** @var MyActiveRecord $parent */
31
    public $parent;
32
33
    /** @var MyActiveRecord $child */
34
    public $child;
35
36
    /** @var MyActiveRecord $assignment */
37
    public $assignment;
38
39
    /** @var MyActiveRecord $assignmentItem The Assignment item we process at the moment */
40
    public $assignmentItem;
41
42
    /** @var string $child_fk_colname */
43
    public $child_fk_colname;
44
45
    /** @var string $parent_fk_colname */
46
    public $parent_fk_colname;
47
48
    /** @var string $assignmentClassname */
49
    public $assignmentClassname;
50
51
    /** @var string $order_colname IF assignments need to be ordered, set this name */
52
    public $order_colname;
53
54
    /** @var bool $isChildIdInteger Whether child id is integer (to clean from input string for order comparison) */
55
    public $isChildIdInteger;
56
57
    /** @var array items order (if are ordered) */
58
    public $itemsOrder;
59
60
    /** @var boolean Whether Assignments have separate children table or assigner directly to parents*/
61
    public $hasChildTable = true;
62
63
    const EVENT_BEFORE_ITEM_SAVE = 'beforeItemSave';
64
65
    /** @var  array array or attribute & value pairs that will be assigned to all created children [['attributeName1'=>'defaultValue1'],['attributeNamen'=>'defaultValuen]] */
66
    public $defaultValues;
67
68
    /** {@inheritdoc} */
69
    public function init()
70
    {
71
        $this->on(self::EVENT_BEFORE_ITEM_SAVE, [$this, 'beforeItemSave']);
72
73
        if (!$this->parent) {
74
            throw new yii\base\InvalidArgumentException('Parent not defined in ' . self::class);
75
        }
76
77
        $this->setCurrentChildren();
78
        $this->itemsOrder = [];
79
        $this->assignmentClassname = get_class($this->assignment);
80
        parent::init();
81
    }
82
83
    /** {@inheritdoc} */
84
    public function rules()
85
    {
86
        return [
87
            [['parent', 'child', 'assignment'], 'required'],
88
            [['children_ids'], 'each', 'rule'=>['string', 'max'=>16]],
89
            [['itemsOrder'], 'each', 'rule'=>['integer']]
90
        ];
91
92
    }
93
94
95
    public function assignDefaultValues() {
96
        if (!empty($this->defaultValues)) {
97
            foreach ($this->defaultValues as $attribute =>$value) {
98
                $this->$attribute = $value;
99
            }
100
        }
101
    }
102
103
    /**
104
     * @param MyAssignmentEvent $event
105
     */
106
    public function beforeItemSave($event)
0 ignored issues
show
The parameter $event is not used and could be removed. ( Ignorable by Annotation )

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

106
    public function beforeItemSave(/** @scrutinizer ignore-unused */ $event)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
107
    {
108
109
    }
110
111
    public function save() {
112
        $i = 0;
113
        $this->cleanChildrenIds();
114
115
        if (is_array($this->children_ids)) {
116
            foreach ($this->children_ids as $childId) {
117
118
                if (!$this->childExists($childId)) {
119
                    $model = new $this->assignmentClassname;
120
                } else {
121
                    $model = $this->getCurrentChildById($childId);
122
                }
123
124
125
                $model->{$this->parent_fk_colname} = $this->parent->primaryKey;
126
                if ($this->hasChildTable) {
127
                    $model->{$this->child_fk_colname} = $childId;
128
                }
129
                $model->{$this->child_fk_colname} = $childId;
130
131
132
                // set order if order colname is set
133
                if ($this->order_colname <> "") {
134
                    $model->{$this->order_colname} = $i;
135
                }
136
137
                // assign default Value
138
                if (!empty($this->defaultValues)) {
139
                    foreach ($this->defaultValues as $attribute =>$value) {
140
                        $model->{$attribute} = $value;
141
                    }
142
                }
143
                // inject code before item save
144
                $this->assignmentItem = $model;
145
                $event = new MyAssignmentEvent;
146
                $event->item = $model;
147
                $this->trigger(self::EVENT_BEFORE_ITEM_SAVE, $event);
148
149
                if (!$this->assignmentItem->save()) {
150
                    $this->addErrors($this->assignmentItem->errors);
151
                    return false;
152
                }
153
                $i++;
154
            }
155
156
        }
157
158
        // delete what was unselected
159
        if (is_array($this->current_children)) {
0 ignored issues
show
The condition is_array($this->current_children) is always true.
Loading history...
160
            foreach ($this->current_children as $child) {
161
                if ((is_array($this->children_ids) && !in_array($child->{$this->child_fk_colname}, $this->children_ids))
162
                    or (!is_array($this->children_ids))) {
163
164
                    $child->delete();
165
                }
166
            }
167
168
        }
169
        $this->setCurrentChildren();
170
171
        return true;
172
173
    }
174
175
176
    public function childExists($childId) {
177
        $currentChildrenIds = $this->getCurrentChildrenIds(false);
178
        if (is_array($currentChildrenIds)) {
0 ignored issues
show
The condition is_array($currentChildrenIds) is always true.
Loading history...
179
            return in_array($childId, $currentChildrenIds);
180
        }
181
        return false;
182
    }
183
184
    public function setCurrentChildren() {
185
        $query = $this->identifyChildrenQuery();
186
187
        // if order column is set, we order it ascending
188
        if ($this->order_colname) {
189
            $query->orderBy([$this->order_colname=>SORT_ASC]);
190
        }
191
        $indexCol = (empty($this->child_fk_colname) ? $this->child->primaryKeySingle() : $this->child_fk_colname);
192
        $children = $query->indexBy($indexCol)->all();
193
        $this->current_children = $children;
194
        $this->getCurrentChildrenIds();
195
196
    }
197
198
199
200
    public function setLastChild() {
201
        $indexCol = (empty($this->child_fk_colname) ? $this->child->primaryKeySingle() : $this->child_fk_colname);
202
        $query = $this->identifyChildrenQuery();
203
        $query->orderBy([
204
            $this->assignment->timeCreatedCol => SORT_DESC,
205
            /**
206
             * if db does not record milliseconds, then we might have them
207
             * in the same second so we need to sort by id additionally
208
             */
209
            $this->assignment->{$indexCol} => SORT_DESC
210
        ]);
211
        /** @var MyActiveRecord $model */
212
        $model = $query->limit(1)->one();
213
        $this->last_child = $model;
214
    }
215
216
    /**
217
     * @return \yii\db\ActiveQuery
218
     */
219
    public function identifyChildrenQuery() {
220
        $query = $this->assignment->find()
221
            ->andWhere([$this->parent_fk_colname => $this->parent->primaryKey]);
222
        return $query;
223
224
    }
225
226
    /**
227
     * @param bool $set Whether we set the children_ids or not.
228
     * In case we get the id's before save - we do not want to set ids since we get
229
     * the ids externally (post)
230
     * @return array|bool
231
     */
232
    public function getCurrentChildrenIds($set = true) {
233
        if (is_array($this->current_children)) {
0 ignored issues
show
The condition is_array($this->current_children) is always true.
Loading history...
234
            $ids = [];
235
            foreach ($this->current_children as $child) {
236
237
                $ids[] = $child->{$this->child_fk_colname};
238
            }
239
            if ($set) {
240
                $this->children_ids = $ids;
241
            }
242
            return $ids;
243
        }
244
        return false;
245
246
    }
247
    private function getCurrentChildById($id) {
248
        if (is_array($this->current_children)) {
0 ignored issues
show
The condition is_array($this->current_children) is always true.
Loading history...
249
            foreach ($this->current_children as $child) {
250
                if ($child->{$this->child_fk_colname} == $id) {
251
                    return $child;
252
                }
253
            }
254
        }
255
        return false;
256
    }
257
258
259
    /**
260
     * Clean Ids to be integers
261
     */
262
    private function cleanChildrenIds() {
263
        if (is_array($this->children_ids) && $this->isChildIdInteger) {
264
            $clean = [];
265
            foreach ($this->children_ids as $id) {
266
                $clean[] = intval($id);
267
            }
268
            $this->children_ids = $clean;
269
        }
270
    }
271
272
273
    /**
274
     * Get the last child assignment by TIME
275
     * @return MyActiveRecord
276
     * @deprecated
277
     */
278
    public function getLastChild() {
279
        if (!$this->last_child) {
280
            $this->setLastChild();
281
        }
282
        return $this->last_child;
283
    }
284
}
285