1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
querybuilder.php
См. документацию.
1<?php
7namespace Bitrix\Iblock\PropertyIndex;
8
9use Bitrix\Iblock\PropertyTable;
10
12{
14 protected $facet = null;
16 protected $dictionary = null;
18 protected $storage = null;
19
20 protected $sectionFilter = null;
21 protected $priceFilter = null;
22 protected $distinct = false;
23 protected $options = array();
24 private array $propertyFilter;
25
29 public function __construct($iblockId)
30 {
31 $this->facet = new Facet($iblockId);
32 $this->dictionary = $this->facet->getDictionary();
33 $this->storage = $this->facet->getStorage();
34 }
35
41 public function isValid()
42 {
43 return $this->facet->isValid();
44 }
45
51 public function getDistinct()
52 {
53 return $this->distinct;
54 }
55
64 public function getFilterSql(&$filter, &$sqlSearch)
65 {
66 if (array_key_exists("FACET_OPTIONS", $filter))
67 {
68 if (is_array($filter["FACET_OPTIONS"]))
69 {
70 $this->options = $filter["FACET_OPTIONS"];
71 }
72 unset($filter["FACET_OPTIONS"]);
73 }
74
75 $this->distinct = false;
76 $fcJoin = "";
77 $toUnset = array();
78 if (
79 isset($filter["IBLOCK_ID"]) && !is_array($filter["IBLOCK_ID"]) && $filter["IBLOCK_ID"] > 0
80 && (
81 (isset($filter["SECTION_ID"]) && !is_array($filter["SECTION_ID"]) && $filter["SECTION_ID"] > 0)
82 || ($this->options && !isset($filter["SECTION_ID"]))
83 )
84 && isset($filter["ACTIVE"]) && $filter["ACTIVE"] === "Y"
85 )
86 {
87 $where = array();
88 $toUnset[] = array(&$filter, "SECTION_ID");
89
90 if (isset($filter["INCLUDE_SUBSECTIONS"]) && $filter["INCLUDE_SUBSECTIONS"] === "Y")
91 {
92 $subsectionsCondition = "";
93 $toUnset[] = array(&$filter, "INCLUDE_SUBSECTIONS");
94 }
95 else
96 {
97 $subsectionsCondition = "INCLUDE_SUBSECTIONS=1";
98 if (array_key_exists("INCLUDE_SUBSECTIONS", $filter))
99 $toUnset[] = array(&$filter, "INCLUDE_SUBSECTIONS");
100 }
101
102 $hasAdditionalFilters = false;
103 $this->fillWhere($where, $hasAdditionalFilters, $toUnset, $filter);
104
105 if (!$where)
106 {
107 $where[] = array(
108 "TYPE" => Storage::DICTIONARY,
109 "OP" => "=",
110 "FACET_ID" => 1,
111 "VALUES" => array(0),
112 );
113 }
114
115 if (
116 isset($filter["=ID"]) && is_object($filter["=ID"])
117 && $filter["=ID"]->arFilter["IBLOCK_ID"] == $this->facet->getSkuIblockId()
118 && $filter["=ID"]->strField === "PROPERTY_".$this->facet->getSkuPropertyId()
119 )
120 {
121 $hasAdditionalFilters = false;
122 $this->fillWhere($where, $hasAdditionalFilters, $toUnset, $filter["=ID"]->arFilter);
123 if (!$hasAdditionalFilters)
124 {
125 $toUnset[] = array(&$filter, "=ID");
126 }
127 }
128
129 if ($where)
130 {
131 $filter["SECTION_ID"] = (isset($filter["SECTION_ID"]) ? (int)$filter["SECTION_ID"] : 0);
132 $this->facet->setSectionId($filter["SECTION_ID"]);
133 if ($this->options)
134 {
135 if (isset($this->options["CURRENCY_CONVERSION"]) && $this->options["CURRENCY_CONVERSION"])
136 {
137 $this->facet->enableCurrencyConversion(
138 $this->options["CURRENCY_CONVERSION"]["TO"] ?? '',
139 $this->options["CURRENCY_CONVERSION"]["FROM"] ?? ''
140 );
141 }
142 }
143 $distinctSelectCapable = (\Bitrix\Main\Application::getConnection() instanceof \Bitrix\Main\DB\MysqlCommonConnection);
144 if (count($where) == 1 && $distinctSelectCapable)
145 {
146 $this->distinct = true;
147 $fcJoin = "INNER JOIN ".$this->storage->getTableName()." FC on FC.ELEMENT_ID = BE.ID";
148 foreach ($where as $facetFilter)
149 {
150 $sqlWhere = $this->facet->whereToSql($facetFilter, "FC", $subsectionsCondition);
151 if ($sqlWhere)
152 $sqlSearch[] = $sqlWhere;
153 }
154 }
155 elseif (count($where) <= 5)
156 {
157 $subJoin = "";
158 $subWhere = "";
159 $i = 0;
160 foreach ($where as $facetFilter)
161 {
162 if ($i == 0)
163 $subJoin .= "FROM ".$this->storage->getTableName()." FC$i\n";
164 else
165 $subJoin .= "INNER JOIN ".$this->storage->getTableName()." FC$i ON FC$i.ELEMENT_ID = FC0.ELEMENT_ID\n";
166
167 $sqlWhere = $this->facet->whereToSql($facetFilter, "FC$i", $subsectionsCondition);
168 if ($sqlWhere)
169 {
170 if ($subWhere)
171 $subWhere .= "\nAND ".$sqlWhere;
172 else
173 $subWhere .= $sqlWhere;
174 }
175
176 $i++;
177 }
178
179 $fcJoin = "
180 INNER JOIN (
181 SELECT ".($distinctSelectCapable? "DISTINCT": "")." FC0.ELEMENT_ID
182 $subJoin
183 WHERE
184 $subWhere
185 ) FC on FC.ELEMENT_ID = BE.ID
186 ";
187 }
188 else
189 {
190 $condition = array();
191 foreach ($where as $facetFilter)
192 {
193 $sqlWhere = $this->facet->whereToSql($facetFilter, "FC0", $subsectionsCondition);
194 if ($sqlWhere)
195 $condition[] = $sqlWhere;
196 }
197 $fcJoin = "
198 INNER JOIN (
199 SELECT FC0.ELEMENT_ID
200 FROM ".$this->storage->getTableName()." FC0
201 WHERE FC0.SECTION_ID = ".$filter["SECTION_ID"]."
202 AND (
203 (".implode(")OR(", $condition).")
204 )
205 GROUP BY FC0.ELEMENT_ID
206 HAVING count(DISTINCT FC0.FACET_ID) = ".count($condition)."
207 ) FC on FC.ELEMENT_ID = BE.ID
208 ";
209 }
210
211 foreach ($toUnset as $command)
212 {
213 unset($command[0][$command[1]]);
214 }
215 }
216 else
217 {
218 $fcJoin = "";
219 }
220 }
221 return $fcJoin;
222 }
223
234 private function fillWhere(&$where, &$hasAdditionalFilters, &$toUnset, &$filter)
235 {
236 $countUnset = count($toUnset);
237 $properties = null;
238 $propertyCodeMap = null;
239
240 $usePriceFilter = isset($this->options['PRICE_FILTER']) && $this->options['PRICE_FILTER'];
241
242 foreach ($filter as $filterKey => $filterValue)
243 {
244 if (preg_match("/^(=)PROPERTY\$/i", $filterKey, $keyDetails) && is_array($filterValue))
245 {
246 if ($properties === null)
247 $properties = $this->getFilterProperty();
248 if ($propertyCodeMap === null)
249 {
250 $propertyCodeMap = $this->getPropertyCodeMap();
251 }
252
253 foreach ($filterValue as $propertyId => $value)
254 {
255 $propertyId = $propertyCodeMap[$propertyId] ?? null;
256 if (
257 $propertyId === null
258 || !isset($properties[$propertyId])
259 )
260 {
261 continue;
262 }
263 $facetId = $this->storage->propertyIdToFacetId($propertyId);
264 if ($properties[$propertyId] == Storage::DICTIONARY || $properties[$propertyId] == Storage::STRING)
265 {
266 $sqlValues = $this->getInSql($value, $properties[$propertyId] == Storage::STRING);
267 if ($sqlValues)
268 {
269 $where[] = array(
270 "TYPE" => $properties[$propertyId],
271 "OP" => $keyDetails[1],
272 "FACET_ID" => $facetId,
273 "VALUES" => $sqlValues,
274 );
275 $toUnset[] = array(&$filter[$filterKey], $propertyId);
276 }
277 }
278 }
279 }
280 elseif (preg_match("/^(=)PROPERTY_(\\d+)\$/i", $filterKey, $keyDetails))
281 {
282 if ($properties === null)
283 $properties = $this->getFilterProperty();
284 if ($propertyCodeMap === null)
285 {
286 $propertyCodeMap = $this->getPropertyCodeMap();
287 }
288
289 $propertyId = $propertyCodeMap[$keyDetails[2]] ?? null;
290 if (
291 $propertyId === null
292 || !isset($properties[$propertyId])
293 )
294 {
295 continue;
296 }
297 $value = $filterValue;
298 $facetId = $this->storage->propertyIdToFacetId($propertyId);
299 if ($properties[$propertyId] == Storage::DICTIONARY || $properties[$propertyId] == Storage::STRING)
300 {
301 $sqlValues = $this->getInSql($value, $properties[$propertyId] == Storage::STRING);
302 if ($sqlValues)
303 {
304 $where[] = array(
305 "TYPE" => $properties[$propertyId],
306 "OP" => $keyDetails[1],
307 "FACET_ID" => $facetId,
308 "VALUES" => $sqlValues,
309 );
310 $toUnset[] = array(&$filter, $filterKey);
311 }
312 }
313 }
314 elseif (preg_match("/^(>=|<=)PROPERTY\$/i", $filterKey, $keyDetails) && is_array($filterValue))
315 {
316 if ($properties === null)
317 $properties = $this->getFilterProperty();
318 if ($propertyCodeMap === null)
319 {
320 $propertyCodeMap = $this->getPropertyCodeMap();
321 }
322
323 foreach ($filterValue as $propertyId => $value)
324 {
325 $propertyId = $propertyCodeMap[$propertyId] ?? null;
326 if (
327 $propertyId === null
328 || !isset($properties[$propertyId])
329 )
330 {
331 continue;
332 }
333 $facetId = $this->storage->propertyIdToFacetId($propertyId);
334 if ($properties[$propertyId] == Storage::NUMERIC)
335 {
336 if (is_array($value))
337 $doubleValue = doubleval(current($value));
338 else
339 $doubleValue = doubleval($value);
340 $where[] = array(
341 "TYPE" => Storage::NUMERIC,
342 "OP" => $keyDetails[1],
343 "FACET_ID" => $facetId,
344 "VALUES" => array($doubleValue),
345 );
346 $toUnset[] = array(&$filter[$filterKey], $propertyId);
347 }
348 elseif ($properties[$propertyId] == Storage::DATETIME)
349 {
350 if (is_array($value))
351 $timestamp = MakeTimeStamp(current($value), "YYYY-MM-DD HH:MI:SS");
352 else
353 $timestamp = MakeTimeStamp($value, "YYYY-MM-DD HH:MI:SS");
354 $where[] = array(
355 "TYPE" => Storage::DATETIME,
356 "OP" => $keyDetails[1],
357 "FACET_ID" => $facetId,
358 "VALUES" => array($timestamp),
359 );
360 $toUnset[] = array(&$filter[$filterKey], $propertyId);
361 }
362 }
363 }
364 elseif (preg_match("/^(><)PROPERTY\$/i", $filterKey, $keyDetails) && is_array($filterValue))
365 {
366 if ($properties === null)
367 $properties = $this->getFilterProperty();
368 if ($propertyCodeMap === null)
369 {
370 $propertyCodeMap = $this->getPropertyCodeMap();
371 }
372
373 foreach ($filterValue as $propertyId => $value)
374 {
375 $propertyId = $propertyCodeMap[$propertyId] ?? null;
376 if (
377 $propertyId === null
378 || !isset($properties[$propertyId])
379 )
380 {
381 continue;
382 }
383 $facetId = $this->storage->propertyIdToFacetId($propertyId);
384 if ($properties[$propertyId] == Storage::NUMERIC)
385 {
386 if (is_array($value) && count($value) == 2)
387 {
388 $doubleMinValue = doubleval(current($value));
389 $doubleMaxValue = doubleval(end($value));
390 $where[] = array(
391 "TYPE" => Storage::NUMERIC,
392 "OP" => $keyDetails[1],
393 "FACET_ID" => $facetId,
394 "VALUES" => array($doubleMinValue, $doubleMaxValue),
395 );
396 $toUnset[] = array(&$filter[$filterKey], $propertyId);
397 }
398 }
399 elseif ($properties[$propertyId] == Storage::DATETIME)
400 {
401 if (is_array($value) && count($value) == 2)
402 {
403 $timestamp1 = MakeTimeStamp(current($value), "YYYY-MM-DD HH:MI:SS");
404 $timestamp2 = MakeTimeStamp(end($value), "YYYY-MM-DD HH:MI:SS");
405 $where[] = array(
406 "TYPE" => Storage::DATETIME,
407 "OP" => $keyDetails[1],
408 "FACET_ID" => $facetId,
409 "VALUES" => array($timestamp1, $timestamp2),
410 );
411 $toUnset[] = array(&$filter[$filterKey], $propertyId);
412 }
413 }
414 }
415 }
416 elseif (
417 $usePriceFilter
418 && preg_match("/^(>=|<=)(?:CATALOG_|)PRICE_(\\d+)\$/i", $filterKey, $keyDetails)
419 && !is_array($filterValue)
420 )
421 {
422 $priceId = $keyDetails[2];
423 $value = $filterValue;
424 $facetId = $this->storage->priceIdToFacetId($priceId);
425 $doubleValue = doubleval($value);
426 $where[] = array(
427 "TYPE" => Storage::PRICE,
428 "OP" => $keyDetails[1],
429 "FACET_ID" => $facetId,
430 "VALUES" => array($doubleValue),
431 );
432 $toUnset[] = array(&$filter, $filterKey);
433 }
434 elseif (
435 $usePriceFilter
436 && preg_match("/^(><)(?:CATALOG_|)PRICE_(\\d+)\$/i", $filterKey, $keyDetails)
437 && is_array($filterValue)
438 )
439 {
440 $priceId = $keyDetails[2];
441 $value = $filterValue;
442 $facetId = $this->storage->priceIdToFacetId($priceId);
443 $doubleValueMin = doubleval($value[0]);
444 $doubleValueMax = doubleval($value[1]);
445 $where[] = array(
446 "TYPE" => Storage::PRICE,
447 "OP" => $keyDetails[1],
448 "FACET_ID" => $facetId,
449 "VALUES" => array($doubleValueMin, $doubleValueMax),
450 );
451 $toUnset[] = array(&$filter, $filterKey);
452 }
453 elseif (
454 $usePriceFilter
455 && is_numeric($filterKey)
456 && is_array($filterValue) && count($filterValue) === 3
457 && isset($filterValue["LOGIC"]) && $filterValue["LOGIC"] === "OR"
458 && isset($filterValue["=ID"]) && is_object($filterValue["=ID"])
459 && preg_match("/^(>=|<=)(?:CATALOG_|)PRICE_(\\d+)\$/i", key($filterValue[0][0]), $keyDetails)
460 && !is_array(current($filterValue[0][0]))
461 )
462 {
463 $priceId = $keyDetails[2];
464 $value = current($filterValue[0][0]);
465 $facetId = $this->storage->priceIdToFacetId($priceId);
466 $doubleValue = doubleval($value);
467 $where[] = array(
468 "TYPE" => Storage::PRICE,
469 "OP" => $keyDetails[1],
470 "FACET_ID" => $facetId,
471 "VALUES" => array($doubleValue),
472 );
473 $toUnset[] = array(&$filter, $filterKey);
474 $toUnset[] = array(&$filter, "CATALOG_SHOP_QUANTITY_".$priceId);
475 }
476 elseif (
477 $usePriceFilter
478 && is_numeric($filterKey)
479 && is_array($filterValue) && count($filterValue) === 3
480 && isset($filterValue["LOGIC"]) && $filterValue["LOGIC"] === "OR"
481 && isset($filterValue["=ID"]) && is_object($filterValue["=ID"])
482 && preg_match("/^(><)(?:CATALOG_|)PRICE_(\\d+)\$/i", key($filterValue[0][0]), $keyDetails)
483 && is_array(current($filterValue[0][0]))
484 )
485 {
486 $priceId = $keyDetails[2];
487 $value = current($filterValue[0][0]);
488 $facetId = $this->storage->priceIdToFacetId($priceId);
489 $doubleValueMin = doubleval($value[0]);
490 $doubleValueMax = doubleval($value[1]);
491 $where[] = array(
492 "TYPE" => Storage::PRICE,
493 "OP" => $keyDetails[1],
494 "FACET_ID" => $facetId,
495 "VALUES" => array($doubleValueMin, $doubleValueMax),
496 );
497 $toUnset[] = array(&$filter, $filterKey);
498 $toUnset[] = array(&$filter, "CATALOG_SHOP_QUANTITY_".$priceId);
499 }
500 elseif (
501 $filterKey !== "IBLOCK_ID"
502 && $filterKey !== "ACTIVE"
503 && $filterKey !== "ACTIVE_DATE"
504 )
505 {
506 $hasAdditionalFilters = true;
507 }
508 }
509 if ($hasAdditionalFilters)
510 {
511 while (count($toUnset) > $countUnset)
512 {
513 array_pop($toUnset);
514 }
515 }
516 }
517
526 protected function getInSql($value, $lookup)
527 {
528 $result = array();
529
530 if (is_array($value))
531 {
532 foreach ($value as $val)
533 {
534 if ((string)$val <> '')
535 {
536 if ($lookup)
537 {
538 $result[] = $this->dictionary->getStringId($val, false);
539 }
540 else
541 {
542 $result[] = (int)$val;
543 }
544 }
545 }
546 }
547 elseif ((string)$value <> '')
548 {
549 if ($lookup)
550 {
551 $result[] = $this->dictionary->getStringId($value, false);
552 }
553 else
554 {
555 $result[] = (int)$value;
556 }
557 }
558
559 return $result;
560 }
561
569 private function getFilterProperty(): array
570 {
571 //TODO: remove this code to \Bitrix\Iblock\Model\Property
572 if (!isset($this->propertyFilter))
573 {
574 $this->propertyFilter = [];
575 $propertyList = \Bitrix\Iblock\SectionPropertyTable::getList([
576 'select' => [
577 'PROPERTY_ID',
578 'PROPERTY_TYPE' => 'PROPERTY.PROPERTY_TYPE',
579 'USER_TYPE' => 'PROPERTY.USER_TYPE',
580 ],
581 'filter' => [
582 '=IBLOCK_ID' => [
583 $this->facet->getIblockId(),
584 $this->facet->getSkuIblockId(),
585 ],
586 '=SMART_FILTER' => 'Y',
587 ],
588 ]);
589 while ($link = $propertyList->fetch())
590 {
591 if ($link['PROPERTY_TYPE'] === PropertyTable::TYPE_NUMBER)
592 {
593 $this->propertyFilter[$link['PROPERTY_ID']] = Storage::NUMERIC;
594 }
595 elseif ($link['USER_TYPE'] === PropertyTable::USER_TYPE_DATETIME)
596 {
597 $this->propertyFilter[$link['PROPERTY_ID']] = Storage::DATETIME;
598 }
599 elseif ($link['PROPERTY_TYPE'] === PropertyTable::TYPE_STRING)
600 {
601 $this->propertyFilter[$link['PROPERTY_ID']] = Storage::STRING;
602 }
603 else
604 {
605 $this->propertyFilter[$link['PROPERTY_ID']] = Storage::DICTIONARY;
606 }
607 }
608 }
609
610 return $this->propertyFilter;
611 }
612
613 private function getPropertyCodeMap(): array
614 {
615 $result = [];
616
617 $iterator = \Bitrix\Iblock\PropertyTable::getList([
618 'select' => [
619 'ID',
620 'CODE',
621 ],
622 'filter' => [
623 '=IBLOCK_ID' => $this->facet->getIblockId(),
624 ],
625 ]);
626 while ($row = $iterator->fetch())
627 {
628 $id = (int)$row['ID'];
629 $result[$id] = $id;
630 $row['CODE'] = (string)$row['CODE'];
631 if ($row['CODE'] !== '')
632 {
633 $result[$row['CODE']] = $id;
634 }
635 }
636 unset($iterator);
637
638 $skuIblockId = $this->facet->getSkuIblockId();
639 if ($skuIblockId > 0)
640 {
641 $iterator = \Bitrix\Iblock\PropertyTable::getList([
642 'select' => [
643 'ID',
644 ],
645 'filter' => [
646 '=IBLOCK_ID' => $skuIblockId,
647 ],
648 ]);
649 while ($row = $iterator->fetch())
650 {
651 $id = (int)$row['ID'];
652 $result[$id] = $id;
653 }
654 unset($iterator);
655 }
656
657 return $result;
658 }
659}
getInSql($value, $lookup)
Определения querybuilder.php:526
getFilterSql(&$filter, &$sqlSearch)
Определения querybuilder.php:64
const TYPE_STRING
Определения propertytable.php:65
const USER_TYPE_DATETIME
Определения propertytable.php:76
const TYPE_NUMBER
Определения propertytable.php:66
static getConnection($name="")
Определения application.php:638
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$result
Определения get_property_values.php:14
$iblockId
Определения iblock_catalog_edit.php:30
$filter
Определения iblock_catalog_list.php:54
MakeTimeStamp($datetime, $format=false)
Определения tools.php:538
$value
Определения Param.php:39
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$i
Определения factura.php:643
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
$val
Определения options.php:1793
$iterator
Определения yandex_run.php:610