Completed
Pull Request — master (#12)
by
unknown
01:18
created

ElementQuery::cache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
3
namespace Arrilot\BitrixModels\Queries;
4
5
use CIBlock;
6
use Bitrix\Main\Data\Cache;
7
use Illuminate\Support\Collection;
8
use Arrilot\BitrixModels\Models\ElementModel;
9
use Exception;
10
11
/**
12
 * @method ElementQuery active()
13
 * @method ElementQuery sortByDate(string $sort = 'desc')
14
 * @method ElementQuery fromSectionWithId(int $id)
15
 * @method ElementQuery fromSectionWithCode(string $code)
16
 */
17
class ElementQuery extends BaseQuery
18
{
19
    /**
20
     * CIblock object or test double.
21
     *
22
     * @var object.
23
     */
24
    public static $cIblockObject;
25
26
    /**
27
     * Query sort.
28
     *
29
     * @var array
30
     */
31
    public $sort = ['SORT' => 'ASC'];
32
33
    /**
34
     * Query group by.
35
     *
36
     * @var array
37
     */
38
    public $groupBy = false;
39
40
    /**
41
     * Iblock id.
42
     *
43
     * @var int
44
     */
45
    protected $iblockId;
46
47
    /**
48
     * List of standard entity fields.
49
     *
50
     * @var array
51
     */
52
    protected $standardFields = [
53
        'ID',
54
        'TIMESTAMP_X',
55
        'TIMESTAMP_X_UNIX',
56
        'MODIFIED_BY',
57
        'DATE_CREATE',
58
        'DATE_CREATE_UNIX',
59
        'CREATED_BY',
60
        'IBLOCK_ID',
61
        'IBLOCK_SECTION_ID',
62
        'ACTIVE',
63
        'ACTIVE_FROM',
64
        'ACTIVE_TO',
65
        'SORT',
66
        'NAME',
67
        'PREVIEW_PICTURE',
68
        'PREVIEW_TEXT',
69
        'PREVIEW_TEXT_TYPE',
70
        'DETAIL_PICTURE',
71
        'DETAIL_TEXT',
72
        'DETAIL_TEXT_TYPE',
73
        'SEARCHABLE_CONTENT',
74
        'IN_SECTIONS',
75
        'SHOW_COUNTER',
76
        'SHOW_COUNTER_START',
77
        'CODE',
78
        'TAGS',
79
        'XML_ID',
80
        'EXTERNAL_ID',
81
        'TMP_ID',
82
        'CREATED_USER_NAME',
83
        'DETAIL_PAGE_URL',
84
        'LIST_PAGE_URL',
85
        'CREATED_DATE',
86
    ];
87
88
    /**
89
     * Constructor.
90
     *
91
     * @param object $bxObject
92
     * @param string $modelName
93
     */
94
    public function __construct($bxObject, $modelName)
95
    {
96
        static::instantiateCIblockObject();
97
        parent::__construct($bxObject, $modelName);
98
99
        $this->iblockId = $modelName::iblockId();
100
    }
101
102
    /**
103
     * Instantiate bitrix entity object.
104
     *
105
     * @throws Exception
106
     *
107
     * @return object
108
     */
109
    public static function instantiateCIblockObject()
110
    {
111
        if (static::$cIblockObject) {
112
            return static::$cIblockObject;
113
        }
114
115
        if (class_exists('CIBlock')) {
116
            return static::$cIblockObject = new CIBlock();
117
        }
118
119
        throw new Exception('CIblock object initialization failed');
120
    }
121
122
    /**
123
     * Setter for groupBy.
124
     *
125
     * @param $value
126
     *
127
     * @return $this
128
     */
129
    public function groupBy($value)
130
    {
131
        $this->groupBy = $value;
132
133
        return $this;
134
    }
135
136
    public function cache($ttl)
137
    {
138
        $this->ttl = $ttl;
0 ignored issues
show
Bug introduced by
The property ttl does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
139
    
140
        return $this;
141
    }
142
143
    /**
144
     * Get list of items.
145
     *
146
     * @return Collection
147
     */
148 View Code Duplication
    private function getListSimple()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
149
    {
150
        if ($this->queryShouldBeStopped) {
151
            return new Collection();
152
        }
153
154
        $items = [];
155
156
        $rsItems = $this->bxObject->GetList(
157
            $this->sort,
158
            $this->normalizeFilter(),
159
            $this->groupBy,
160
            $this->navigation,
161
            $this->normalizeSelect()
162
        );
163
    
164
        while ($arItem = $rsItems->Fetch()) {
165
            $this->addItemToResultsUsingKeyBy($items, new $this->modelName($arItem['ID'], $arItem));
166
        }
167
168
        return new Collection($items);
169
    }
170
171
    public function getList()
172
    {
173
        if (!$this->ttl) return parent::getListSimple();
0 ignored issues
show
Bug introduced by
The method getListSimple() does not exist on Arrilot\BitrixModels\Queries\BaseQuery. Did you maybe mean getList()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getListSimple() instead of getList()). Are you sure this is correct? If so, you might want to change this to $this->getListSimple().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
174
175
        $cache = Cache::createInstance();
176
        if ($cache->initCache($this->ttl, $this->getKey(), '/models/')) {
177
            $result = $cache->getVars();
178
        } elseif ($cache->startDataCache()) {
179
            $result = parent::getListSimple();
0 ignored issues
show
Bug introduced by
The method getListSimple() does not exist on Arrilot\BitrixModels\Queries\BaseQuery. Did you maybe mean getList()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getListSimple() instead of getList()). Are you sure this is correct? If so, you might want to change this to $this->getListSimple().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
180
            $cache->endDataCache($result);
181
        }
182
183
        return $result;
0 ignored issues
show
Bug introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
184
    }
185
186
    private function getKey()
187
    {
188
        $data = [
189
            'sort' => $this->sort,
190
            'filter' => $this->normalizeFilter(),
191
            'groupBy' => $this->groupBy,
192
            'navigation' => $this->navigation,
193
            'select' => $this->normalizeSelect(),
194
        ];
195
196
        return md5( json_encode($data) );
197
    }
198
199
    /**
200
     * Get the first element with a given code.
201
     *
202
     * @param string $code
203
     *
204
     * @return ElementModel
205
     */
206
    public function getByCode($code)
207
    {
208
        $this->filter['CODE'] = $code;
209
210
        return $this->first();
211
    }
212
213
    /**
214
     * Get the first element with a given external id.
215
     *
216
     * @param string $id
217
     *
218
     * @return ElementModel
219
     */
220
    public function getByExternalId($id)
221
    {
222
        $this->filter['EXTERNAL_ID'] = $id;
223
224
        return $this->first();
225
    }
226
227
    /**
228
     * Get count of elements that match $filter.
229
     *
230
     * @return int
231
     */
232
    public function count()
233
    {
234
        if ($this->queryShouldBeStopped) {
235
            return 0;
236
        }
237
238
        return (int) $this->bxObject->GetList(false, $this->normalizeFilter(), []);
239
    }
240
241
//    /**
242
//     * Normalize properties's format converting it to 'PROPERTY_"CODE"_VALUE'.
243
//     *
244
//     * @param array $fields
245
//     *
246
//     * @return null
247
//     */
248
//    protected function normalizePropertyResultFormat(&$fields)
249
//    {
250
//        if (empty($fields['PROPERTIES'])) {
251
//            return;
252
//        }
253
//
254
//        foreach ($fields['PROPERTIES'] as $code => $prop) {
255
//            $fields['PROPERTY_'.$code.'_VALUE'] = $prop['VALUE'];
256
//            $fields['~PROPERTY_'.$code.'_VALUE'] = $prop['~VALUE'];
257
//            $fields['PROPERTY_'.$code.'_DESCRIPTION'] = $prop['DESCRIPTION'];
258
//            $fields['~PROPERTY_'.$code.'_DESCRIPTION'] = $prop['~DESCRIPTION'];
259
//            $fields['PROPERTY_'.$code.'_VALUE_ID'] = $prop['PROPERTY_VALUE_ID'];
260
//            if (isset($prop['VALUE_ENUM_ID'])) {
261
//                $fields['PROPERTY_'.$code.'_ENUM_ID'] = $prop['VALUE_ENUM_ID'];
262
//            }
263
//        }
264
//    }
265
266
    /**
267
     * Normalize filter before sending it to getList.
268
     * This prevents some inconsistency.
269
     *
270
     * @return array
271
     */
272
    protected function normalizeFilter()
273
    {
274
        $this->filter['IBLOCK_ID'] = $this->iblockId;
275
276
        return $this->filter;
277
    }
278
279
    /**
280
     * Normalize select before sending it to getList.
281
     * This prevents some inconsistency.
282
     *
283
     * @return array
284
     */
285 View Code Duplication
    protected function normalizeSelect()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
286
    {
287
        if ($this->fieldsMustBeSelected()) {
288
            $this->select = array_merge($this->standardFields, $this->select);
289
        }
290
291
        if ($this->propsMustBeSelected()) {
292
            $this->addAllPropsToSelect();
293
        }
294
295
        $this->select[] = 'ID';
296
        $this->select[] = 'IBLOCK_ID';
297
298
        return $this->clearSelectArray();
299
    }
300
301
    /**
302
     * Add all iblock property codes to select.
303
     *
304
     * return null
305
     */
306
    protected function addAllPropsToSelect()
307
    {
308
        $this->select[] = 'ID';
309
        $this->select[] = 'IBLOCK_ID';
310
311
        //dd (static::$cIblockObject);
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
312
        $rsProps = static::$cIblockObject->GetProperties($this->iblockId);
313
        while ($prop = $rsProps->Fetch()) {
314
            $this->select[] = 'PROPERTY_'.$prop['CODE'];
315
        }
316
    }
317
}
318