Completed
Push — master ( e1c530...93bd80 )
by Nekrasov
01:25
created

BaseBitrixModel::resetEventErrors()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Arrilot\BitrixModels\Models;
4
5
use Arrilot\BitrixModels\ModelEventsTrait;
6
use Arrilot\BitrixModels\Queries\BaseQuery;
7
use LogicException;
8
9
abstract class BaseBitrixModel extends ArrayableModel
10
{
11
    use ModelEventsTrait;
12
13
    /**
14
     * Array of model fields keys that needs to be saved with next save().
15
     *
16
     * @var array
17
     */
18
    protected $fieldsSelectedForSave = [];
19
20
    /**
21
     * Array of errors that are passed to model events.
22
     *
23
     * @var array
24
     */
25
    protected $eventErrors = [];
26
27
    /**
28
     * Have fields been already fetched from DB?
29
     *
30
     * @var bool
31
     */
32
    protected $fieldsAreFetched = false;
33
34
    /**
35
     * Internal part of create to avoid problems with static and inheritance
36
     *
37
     * @param $fields
38
     *
39
     * @throws LogicException
40
     *
41
     * @return static|bool
42
     */
43
    protected static function internalCreate($fields)
44
    {
45
        throw new LogicException('internalCreate is not implemented');
46
    }
47
48
    /**
49
     * Save model to database.
50
     *
51
     * @param array $selectedFields save only these fields instead of all.
52
     *
53
     * @return bool
54
     */
55
    abstract public function save($selectedFields = []);
56
57
    /**
58
     * Determine whether the field should be stopped from passing to "update".
59
     *
60
     * @param string $field
61
     * @param mixed  $value
62
     * @param array  $selectedFields
63
     *
64
     * @return bool
65
     */
66
    abstract protected function fieldShouldNotBeSaved($field, $value, $selectedFields);
67
    
68
    /**
69
     * Get all model attributes from cache or database.
70
     *
71
     * @return array
72
     */
73
    public function get()
74
    {
75
        $this->load();
76
        
77
        return $this->fields;
78
    }
79
80
    /**
81
     * Load model fields from database if they are not loaded yet.
82
     *
83
     * @return $this
84
     */
85
    public function load()
86
    {
87
        if (!$this->fieldsAreFetched) {
88
            $this->refresh();
89
        }
90
        
91
        return $this;
92
    }
93
94
    /**
95
     * Get model fields from cache or database.
96
     *
97
     * @return array
98
     */
99
    public function getFields()
100
    {
101
        if ($this->fieldsAreFetched) {
102
            return $this->fields;
103
        }
104
        
105
        return $this->refreshFields();
106
    }
107
108
    /**
109
     * Refresh model from database and place data to $this->fields.
110
     *
111
     * @return array
112
     */
113
    public function refresh()
114
    {
115
        return $this->refreshFields();
116
    }
117
118
    /**
119
     * Refresh model fields and save them to a class field.
120
     *
121
     * @return array
122
     */
123
    public function refreshFields()
124
    {
125
        if ($this->id === null) {
126
            return $this->fields = [];
127
        }
128
        
129
        $this->fields = static::query()->getById($this->id)->fields;
130
        
131
        $this->fieldsAreFetched = true;
132
        
133
        return $this->fields;
134
    }
135
136
    /**
137
     * Fill model fields if they are already known.
138
     * Saves DB queries.
139
     *
140
     * @param array $fields
141
     *
142
     * @return void
143
     */
144
    public function fill($fields)
145
    {
146
        if (!is_array($fields)) {
147
            return;
148
        }
149
        
150
        if (isset($fields['ID'])) {
151
            $this->id = $fields['ID'];
152
        }
153
        
154
        $this->fields = $fields;
155
        
156
        $this->fieldsAreFetched = true;
157
        
158
        if (method_exists($this, 'afterFill')) {
159
            $this->afterFill();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Arrilot\BitrixModels\Models\BaseBitrixModel as the method afterFill() does only exist in the following sub-classes of Arrilot\BitrixModels\Models\BaseBitrixModel: Arrilot\BitrixModels\Models\ElementModel, Arrilot\BitrixModels\Models\UserModel. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
160
        }
161
    }
162
163
    /**
164
     * Set current model id.
165
     *
166
     * @param $id
167
     */
168
    protected function setId($id)
169
    {
170
        $this->id = $id;
171
        $this->fields['ID'] = $id;
172
    }
173
174
    /**
175
     * Create new item in database.
176
     *
177
     * @param $fields
178
     *
179
     * @throws LogicException
180
     *
181
     * @return static|bool
182
     */
183
    public static function create($fields)
184
    {
185
        return static::internalCreate($fields);
186
    }
187
188
    /**
189
     * Get count of items that match $filter.
190
     *
191
     * @param array $filter
192
     *
193
     * @return int
194
     */
195
    public static function count(array $filter = [])
196
    {
197
        return static::query()->filter($filter)->count();
198
    }
199
200
    /**
201
     * Get item by its id.
202
     *
203
     * @param int $id
204
     *
205
     * @return static|bool
206
     */
207
    public static function find($id)
208
    {
209
        return static::query()->getById($id);
210
    }
211
212
    /**
213
     * Update model.
214
     *
215
     * @param array $fields
216
     *
217
     * @return bool
218
     */
219
    public function update(array $fields = [])
220
    {
221
        $keys = [];
222
        foreach ($fields as $key => $value) {
223
            array_set($this->fields, $key, $value);
224
            $keys[] = $key;
225
        }
226
227
        return $this->save($keys);
228
    }
229
230
    /**
231
     * Create an array of fields that will be saved to database.
232
     *
233
     * @param $selectedFields
234
     *
235
     * @return array
236
     */
237
    protected function normalizeFieldsForSave($selectedFields)
238
    {
239
        $fields = [];
240
        if ($this->fields === null) {
241
            return [];
242
        }
243
244
        foreach ($this->fields as $field => $value) {
245
            if (!$this->fieldShouldNotBeSaved($field, $value, $selectedFields)) {
246
                $fields[$field] = $value;
247
            }
248
        }
249
250
        return $fields;
251
    }
252
253
    /**
254
     * Instantiate a query object for the model.
255
     *
256
     * @throws LogicException
257
     *
258
     * @return BaseQuery
259
     */
260
    public static function query()
261
    {
262
        throw new LogicException('public static function query() is not implemented');
263
    }
264
265
    /**
266
     * Handle dynamic static method calls into a new query.
267
     *
268
     * @param  string  $method
269
     * @param  array  $parameters
270
     * @return mixed
271
     */
272
    public static function __callStatic($method, $parameters)
273
    {
274
        return static::query()->$method(...$parameters);
275
    }
276
277
    /**
278
     * Reset event errors back to default.
279
     */
280
    protected function resetEventErrors()
281
    {
282
        $this->eventErrors = [];
283
    }
284
}
285