Completed
Push — master ( 59527d...93c6d6 )
by Nate
10:09 queued 08:52
created

ActiveRecord::hasSetterPriority()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 0
cts 5
cp 0
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 6
1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://github.com/flipboxfactory/craft-ember/blob/master/LICENSE
6
 * @link       https://github.com/flipboxfactory/craft-ember/
7
 */
8
9
namespace flipbox\craft\ember\records;
10
11
use craft\helpers\Json;
12
use flipbox\craft\ember\exceptions\RecordNotFoundException;
13
use flipbox\craft\ember\models\DateCreatedRulesTrait;
14
use flipbox\craft\ember\models\DateUpdatedRulesTrait;
15
use flipbox\craft\ember\models\UidRulesTrait;
16
use yii\base\InvalidConfigException;
17
use yii\db\ActiveQueryInterface;
18
use yii\helpers\ArrayHelper;
19
20
/**
21
 * This class provides additional functionality to Craft's ActiveRecord:
22
 *
23
 * Table Alias - By default the table alias is the name of the table, without the opening '{{%' and closing '}}'
24
 * syntax.  Additionally, the table alias (and therefore table name) is set via a constant.
25
 *
26
 * Audit Attributes - Craft defines 'dateCreated', 'dateUpdated' and 'UID' as audit attributes which are automatically
27
 * accounted for.  It is important to note that these attributes are also set as 'safe' for the 'default' scenario;
28
 * therefore making them easily set-able and accessible.
29
 *
30
 * Getter Priority Attributes - When set, the attribute will call the 'getter' method instead of the traditional
31
 * 'getAttribute' method.  Examine the case when using relational Id attributes; we'll use 'userId' as an example.
32
 * Calling the attribute directly `$this->userId` would call `$this->getUserId()` which should look at the relation
33
 * `$this->user->getId()` to ensure the related object and Id are the same.  Commonly, this is useful when continuity
34
 * between the Id an object need to be upheld.
35
 *
36
 * @author Flipbox Factory <[email protected]>
37
 * @since 2.0.0
38
 */
39
abstract class ActiveRecord extends \craft\db\ActiveRecord
40
{
41
    use DateCreatedRulesTrait,
42
        DateUpdatedRulesTrait,
43
        UidRulesTrait;
44
45
    /**
46
     * These attributes will have their 'getter' methods take priority over the normal attribute lookup.  It's
47
     * VERY important to note, that the value returned from the getter should NEVER be different than the raw
48
     * attribute value set.  If, for whatever reason, the getter determines the attribute value is
49
     * incorrect, it should set the new value prior to returning it.
50
     *
51
     * These getters are commonly used to ensure an associated model and their identifier are in sync.  For example,
52
     * a userId attribute and a user object (with an id attribute).  An operation may have saved and set an Id on the
53
     * model, but the userId attribute remains null.  The getter method may check (and set) the value prior to
54
     * returning it.
55
     *
56
     * @var array
57
     */
58
    protected $getterPriorityAttributes = [];
59
60
    /**
61
     * These attributes will have their 'setter' methods take priority over the normal attribute setting.
62
     *
63
     * These setters are commonly used to ensure an associated model and their identifier are in sync.  For example,
64
     * a userId attribute and a user object (with an id attribute).
65
     *
66
     * @var array
67
     */
68
    protected $setterPriorityAttributes = [];
69
70
    /**
71
     * The table alias
72
     */
73
    const TABLE_ALIAS = '';
74
75
    /**
76
     * {@inheritdoc}
77
     */
78
    public static function tableAlias()
79
    {
80
        return static::TABLE_ALIAS;
81
    }
82
83
    /**
84
     * {@inheritdoc}
85
     */
86
    public static function tableName()
87
    {
88
        return '{{%' . static::tableAlias() . '}}';
89
    }
90
91
92
    /*******************************************
93
     * OVERRIDE CONDITION HANDLING
94
     *******************************************/
95
96
    /**
97
     * Finds ActiveRecord instance(s) by the given condition.
98
     * This method is internally called by [[findOne()]] and [[findAll()]].
99
     * @param mixed $condition please refer to [[findOne()]] for the explanation of this parameter
100
     * @return ActiveQueryInterface the newly created [[ActiveQueryInterface|ActiveQuery]] instance.
101
     * @throws InvalidConfigException if there is no primary key defined.
102
     * @internal
103
     */
104
    protected static function findByCondition($condition)
105
    {
106
        $query = static::find();
107
108
        if (!ArrayHelper::isAssociative($condition)) {
109
            // query by primary key
110
            $primaryKey = static::primaryKey();
111
            if (isset($primaryKey[0])) {
112
                $pk = $primaryKey[0];
113
                if (!empty($query->join) || !empty($query->joinWith)) {
114
                    $pk = static::tableName() . '.' . $pk;
115
                }
116
                // if condition is scalar, search for a single primary key, if it is array, search for
117
                // multiple primary key values
118
                $condition = [$pk => is_array($condition) ? array_values($condition) : $condition];
119
            } else {
120
                throw new InvalidConfigException('"' . get_called_class() . '" must have a primary key.');
121
            }
122
        } elseif (is_array($condition)) {
123
            foreach ($condition as $key => $value) {
124
                if ($query->canSetProperty($key)) {
125
                    $query->{$key} = $value;
126
                    unset($condition[$key]);
127
                }
128
            }
129
130
            /** @noinspection PhpInternalEntityUsedInspection */
131
            $condition = static::filterCondition($condition);
132
        }
133
134
        return $query->andWhere($condition);
135
    }
136
137
138
    /*******************************************
139
     * FIND
140
     *******************************************/
141
142
    /**
143
     * @inheritdoc
144
     */
145
    public static function findOne($condition)
146
    {
147
        if ($condition instanceof self) {
148
            return $condition;
149
        }
150
151
        return parent::findOne($condition);
0 ignored issues
show
Bug Compatibility introduced by
The expression parent::findOne($condition); of type yii\db\ActiveRecordInterface|array|null adds the type array to the return on line 151 which is incompatible with the return type declared by the interface yii\db\ActiveRecordInterface::findOne of type yii\db\ActiveRecordInterface|null.
Loading history...
152
    }
153
154
155
    /*******************************************
156
     * GET
157
     *******************************************/
158
159
    /**
160
     * @param $condition
161
     * @return static|null
162
     * @throws RecordNotFoundException
163
     */
164
    public static function getOne($condition)
165
    {
166
        if (null === ($record = static::findOne($condition))) {
167
            throw new RecordNotFoundException(
168
                sprintf(
169
                    "Record not found with the following condition: %s",
170
                    Json::encode($condition)
171
                )
172
            );
173
        }
174
175
        return $record;
176
    }
177
178
    /**
179
     * @param $condition
180
     * @return static[]
181
     * @throws RecordNotFoundException
182
     */
183
    public static function getAll($condition)
184
    {
185
        $records = static::findAll($condition);
186
187
        if (empty($records)) {
188
            throw new RecordNotFoundException(
189
                sprintf(
190
                    "Records not found with the following condition: %s",
191
                    Json::encode($condition)
192
                )
193
            );
194
        }
195
196
        return $records;
197
    }
198
199
200
    /*******************************************
201
     * RULES
202
     *******************************************/
203
204
    /**
205
     * @inheritdoc
206
     */
207
    public function rules()
208
    {
209
        return array_merge(
210
            parent::rules(),
211
            $this->dateCreatedRules(),
212
            $this->dateUpdatedRules(),
213
            $this->uidRules()
214
        );
215
    }
216
217
    /*******************************************
218
     * ATTRIBUTES
219
     *******************************************/
220
221
    /**
222
     * @param string $name
223
     * @return mixed
224
     */
225
    public function __get($name)
226
    {
227
        if ($this->hasGetterPriority($name)) {
228
            $this->{'get' . $name}();
229
        }
230
231
        return parent::__get($name);
232
    }
233
234
    /**
235
     * @param string $name
236
     * @param mixed $value
237
     */
238
    public function __set($name, $value)
239
    {
240
        if ($this->hasSetterPriority($name)) {
241
            $this->{'set' . $name}($value);
242
            return;
243
        }
244
245
        parent::__set($name, $value);
246
    }
247
248
    /**
249
     * @inheritdoc
250
     */
251
    public function getDirtyAttributes($names = null)
252
    {
253
        $attributes = $names ?: $this->getterPriorityAttributes;
254
255
        // Call each attribute to see if the 'getter' has a value
256
        foreach ($attributes as $attribute) {
257
            if ($this->hasGetterPriority($attribute)) {
258
                $this->{'get' . $attribute}();
259
            }
260
        }
261
262
        return parent::getDirtyAttributes($names);
263
    }
264
265
    /**
266
     * @param $name
267
     * @return bool
268
     */
269
    protected function hasSetterPriority($name)
270
    {
271
        return in_array($name, $this->setterPriorityAttributes, true) &&
272
            method_exists($this, 'set' . $name);
273
    }
274
275
    /**
276
     * @param $name
277
     * @return bool
278
     */
279
    protected function hasGetterPriority($name)
280
    {
281
        return in_array($name, $this->getterPriorityAttributes, true) &&
282
            method_exists($this, 'get' . $name);
283
    }
284
}
285