Completed
Push — master ( 136cb7...59b361 )
by Nekrasov
02:22
created

BitrixModel::setEventErrorsOnFail()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
nop 2
1
<?php
2
3
namespace Arrilot\BitrixModels\Models;
4
5
use Arrilot\BitrixModels\Exceptions\ExceptionFromBitrix;
6
use Arrilot\BitrixModels\Queries\BaseQuery;
7
use LogicException;
8
9
abstract class BitrixModel extends BaseBitrixModel
10
{
11
    /**
12
     * Bitrix entity object.
13
     *
14
     * @var object
15
     */
16
    public static $bxObject;
17
18
    /**
19
     * Corresponding object class name.
20
     *
21
     * @var string
22
     */
23
    protected static $objectClass = '';
24
25
    /**
26
     * Fetch method and parameters.
27
     *
28
     * @var array|string
29
     */
30
    public static $fetchUsing = [
31
        'method' => 'Fetch',
32
        'params' => [],
33
    ];
34
35
    /**
36
     * Constructor.
37
     *
38
     * @param $id
39
     * @param $fields
40
     */
41
    public function __construct($id = null, $fields = null)
42
    {
43
        static::instantiateObject();
44
45
        $this->id = $id;
46
47
        $this->fill($fields);
48
    }
49
50
    /**
51
     * Activate model.
52
     *
53
     * @return bool
54
     */
55
    public function activate()
56
    {
57
        $this->fields['ACTIVE'] = 'Y';
58
59
        return $this->save(['ACTIVE']);
60
    }
61
62
    /**
63
     * Deactivate model.
64
     *
65
     * @return bool
66
     */
67
    public function deactivate()
68
    {
69
        $this->fields['ACTIVE'] = 'N';
70
71
        return $this->save(['ACTIVE']);
72
    }
73
74
    /**
75
     * Internal part of create to avoid problems with static and inheritance
76
     *
77
     * @param $fields
78
     *
79
     * @throws ExceptionFromBitrix
80
     *
81
     * @return static|bool
82
     */
83
    protected static function internalCreate($fields)
84
    {
85
        $model = new static(null, $fields);
86
        
87
        if ($model->onBeforeSave() === false || $model->onBeforeCreate() === false) {
88
            return false;
89
        }
90
        
91
        $bxObject = static::instantiateObject();
92
        $id = $bxObject->add($model->fields);
93
        $model->setId($id);
94
        
95
        $result = $id ? true : false;
96
97
        $model->setEventErrorsOnFail($result, $bxObject);
98
        $model->onAfterCreate($result);
99
        $model->onAfterSave($result);
100
        $model->throwExceptionOnFail($result, $bxObject);
101
        
102
        return $model;
103
    }
104
    
105
    /**
106
     * Delete model.
107
     *
108
     * @return bool
109
     * @throws ExceptionFromBitrix
110
     */
111
    public function delete()
112
    {
113
        if ($this->onBeforeDelete() === false) {
114
            return false;
115
        }
116
117
        $result = static::$bxObject->delete($this->id);
118
119
        $this->setEventErrorsOnFail($result, static::$bxObject);
120
        $this->onAfterDelete($result);
121
        $this->throwExceptionOnFail($result, static::$bxObject);
122
123
        return $result;
124
    }
125
    
126
    /**
127
     * Save model to database.
128
     *
129
     * @param array $selectedFields save only these fields instead of all.
130
     * @return bool
131
     * @throws ExceptionFromBitrix
132
     */
133
    public function save($selectedFields = [])
134
    {
135
        $this->fieldsSelectedForSave = is_array($selectedFields) ? $selectedFields : func_get_args();
136
        if ($this->onBeforeSave() === false || $this->onBeforeUpdate() === false) {
137
            return false;
138
        }
139
140
        $fields = $this->normalizeFieldsForSave($this->fieldsSelectedForSave);
141
        $result = !empty($fields) ? static::$bxObject->update($this->id, $fields) : false;
142
        if ($this instanceof ElementModel) {
143
            $savePropsResult = $this->saveProps($this->fieldsSelectedForSave);
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\BitrixModel as the method saveProps() does only exist in the following sub-classes of Arrilot\BitrixModels\Models\BitrixModel: Arrilot\BitrixModels\Models\ElementModel. 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...
144
            $result = $result || $savePropsResult;
145
        }
146
147
        $this->setEventErrorsOnFail($result, static::$bxObject);
148
        $this->onAfterUpdate($result);
149
        $this->onAfterSave($result);
150
        $this->throwExceptionOnFail($result, static::$bxObject);
151
152
        return $result;
153
    }
154
155
    /**
156
     * Scope to get only active items.
157
     *
158
     * @param BaseQuery $query
159
     *
160
     * @return BaseQuery
161
     */
162
    public function scopeActive($query)
163
    {
164
        $query->filter['ACTIVE'] = 'Y';
165
166
        return $query;
167
    }
168
169
    /**
170
     * Determine whether the field should be stopped from passing to "update".
171
     *
172
     * @param string $field
173
     * @param mixed  $value
174
     * @param array  $selectedFields
175
     *
176
     * @return bool
177
     */
178
    protected function fieldShouldNotBeSaved($field, $value, $selectedFields)
179
    {
180
        $blacklistedFields = [
181
            'ID',
182
            'IBLOCK_ID',
183
            'PROPERTIES',
184
            'GROUPS',
185
            'PROPERTY_VALUES',
186
        ];
187
188
        return (!empty($selectedFields) && !in_array($field, $selectedFields))
189
            || in_array($field, $blacklistedFields)
190
            || (substr($field, 0, 1) === '~')
191
            || (substr($field, 0, 9) === 'PROPERTY_');
192
    }
193
194
    /**
195
     * Instantiate bitrix entity object.
196
     *
197
     * @throws LogicException
198
     *
199
     * @return object
200
     */
201
    public static function instantiateObject()
202
    {
203
        if (static::$bxObject) {
204
            return static::$bxObject;
205
        }
206
207
        if (class_exists(static::$objectClass)) {
208
            return static::$bxObject = new static::$objectClass();
209
        }
210
211
        throw new LogicException('Object initialization failed');
212
    }
213
214
    /**
215
     * Destroy bitrix entity object.
216
     *
217
     * @return void
218
     */
219
    public static function destroyObject()
220
    {
221
        static::$bxObject = null;
222
    }
223
224
    /**
225
     * Set eventErrors field on error.
226
     *
227
     * @param bool $result
228
     * @param object $bxObject
229
     */
230
    protected function setEventErrorsOnFail($result, $bxObject)
231
    {
232
        if (!$result) {
233
            $this->eventErrors = (array) $bxObject->LAST_ERROR;
234
        }
235
    }
236
237
    /**
238
     * Throw bitrix exception on fail
239
     *
240
     * @param bool $result
241
     * @param object $bxObject
242
     * @throws ExceptionFromBitrix
243
     */
244
    protected function throwExceptionOnFail($result, $bxObject)
245
    {
246
        if (!$result) {
247
            throw new ExceptionFromBitrix($bxObject->LAST_ERROR);
248
        }
249
    }
250
}
251