Completed
Push — master ( a85e3f...5414b4 )
by Nate
01:18
created

ActiveRecord::getDirtyAttributes()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 0
cts 10
cp 0
rs 9.8333
c 0
b 0
f 0
cc 4
nc 6
nop 1
crap 20
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
     * The table alias
62
     */
63
    const TABLE_ALIAS = '';
64
65
    /**
66
     * {@inheritdoc}
67
     */
68
    public static function tableAlias()
69
    {
70
        return static::TABLE_ALIAS;
71
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76
    public static function tableName()
77
    {
78
        return '{{%' . static::tableAlias() . '}}';
79
    }
80
81
82
    /*******************************************
83
     * OVERRIDE CONDITION HANDLING
84
     *******************************************/
85
86
    /**
87
     * Finds ActiveRecord instance(s) by the given condition.
88
     * This method is internally called by [[findOne()]] and [[findAll()]].
89
     * @param mixed $condition please refer to [[findOne()]] for the explanation of this parameter
90
     * @return ActiveQueryInterface the newly created [[ActiveQueryInterface|ActiveQuery]] instance.
91
     * @throws InvalidConfigException if there is no primary key defined.
92
     * @internal
93
     */
94
    protected static function findByCondition($condition)
95
    {
96
        $query = static::find();
97
98
        if (!ArrayHelper::isAssociative($condition)) {
99
            // query by primary key
100
            $primaryKey = static::primaryKey();
101
            if (isset($primaryKey[0])) {
102
                $pk = $primaryKey[0];
103
                if (!empty($query->join) || !empty($query->joinWith)) {
104
                    $pk = static::tableName() . '.' . $pk;
105
                }
106
                // if condition is scalar, search for a single primary key, if it is array, search for
107
                // multiple primary key values
108
                $condition = [$pk => is_array($condition) ? array_values($condition) : $condition];
109
            } else {
110
                throw new InvalidConfigException('"' . get_called_class() . '" must have a primary key.');
111
            }
112
        } elseif (is_array($condition)) {
113
            foreach ($condition as $key => $value) {
114
                if ($query->canSetProperty($key)) {
115
                    $query->{$key} = $value;
116
                    unset($condition[$key]);
117
                }
118
            }
119
120
            /** @noinspection PhpInternalEntityUsedInspection */
121
            $condition = static::filterCondition($condition);
122
        }
123
124
        return $query->andWhere($condition);
125
    }
126
127
128
    /*******************************************
129
     * FIND
130
     *******************************************/
131
132
    /**
133
     * @inheritdoc
134
     */
135
    public static function findOne($condition)
136
    {
137
        if ($condition instanceof self) {
138
            return $condition;
139
        }
140
141
        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 141 which is incompatible with the return type declared by the interface yii\db\ActiveRecordInterface::findOne of type yii\db\ActiveRecordInterface.
Loading history...
142
    }
143
144
145
    /*******************************************
146
     * GET
147
     *******************************************/
148
149
    /**
150
     * @param $condition
151
     * @return static|null
152
     * @throws RecordNotFoundException
153
     */
154
    public static function getOne($condition)
155
    {
156
        if (null === ($record = static::findOne($condition))) {
157
            throw new RecordNotFoundException(
158
                sprintf(
159
                    "Record not found with the following condition: %s",
160
                    Json::encode($condition)
161
                )
162
            );
163
        }
164
165
        return $record;
166
    }
167
168
    /**
169
     * @param $condition
170
     * @return static[]
171
     * @throws RecordNotFoundException
172
     */
173
    public static function getAll($condition)
174
    {
175
        $records = static::findAll($condition);
176
177
        if (empty($records)) {
178
            throw new RecordNotFoundException(
179
                sprintf(
180
                    "Records not found with the following condition: %s",
181
                    Json::encode($condition)
182
                )
183
            );
184
        }
185
186
        return $records;
187
    }
188
189
190
    /*******************************************
191
     * RULES
192
     *******************************************/
193
194
    /**
195
     * @inheritdoc
196
     */
197
    public function rules()
198
    {
199
        return array_merge(
200
            parent::rules(),
201
            $this->dateCreatedRules(),
202
            $this->dateUpdatedRules(),
203
            $this->uidRules()
204
        );
205
    }
206
207
    /*******************************************
208
     * ATTRIBUTES
209
     *******************************************/
210
211
    /**
212
     * @param string $name
213
     * @return mixed
214
     */
215
    public function __get($name)
216
    {
217
        if ($this->hasGetterPriority($name)) {
218
            $this->{'get' . $name}();
219
        }
220
221
        return parent::__get($name);
222
    }
223
224
    /**
225
     * @inheritdoc
226
     */
227
    public function getDirtyAttributes($names = null)
228
    {
229
        $attributes = $names ?: $this->getterPriorityAttributes;
230
231
        // Call each attribute to see if the 'getter' has a value
232
        foreach ($attributes as $attribute) {
233
            if ($this->hasGetterPriority($attribute)) {
234
                $this->{'get' . $attribute}();
235
            }
236
        }
237
238
        return parent::getDirtyAttributes($names);
239
    }
240
241
    /**
242
     * @param $name
243
     * @return bool
244
     */
245
    protected function hasGetterPriority($name)
246
    {
247
        return in_array($name, $this->getterPriorityAttributes, true) &&
248
            method_exists($this, 'get' . $name);
249
    }
250
}
251