28 $this->facet =
new Facet($iblockId);
29 $this->dictionary = $this->facet->getDictionary();
30 $this->storage = $this->facet->getStorage();
40 return $this->facet->isValid();
63 if (array_key_exists(
"FACET_OPTIONS", $filter))
65 if (is_array($filter[
"FACET_OPTIONS"]))
67 $this->options = $filter[
"FACET_OPTIONS"];
69 unset($filter[
"FACET_OPTIONS"]);
72 $this->distinct =
false;
76 isset($filter[
"IBLOCK_ID"]) && !is_array($filter[
"IBLOCK_ID"]) && $filter[
"IBLOCK_ID"] > 0
78 (isset($filter[
"SECTION_ID"]) && !is_array($filter[
"SECTION_ID"]) && $filter[
"SECTION_ID"] > 0)
79 || ($this->options && !isset($filter[
"SECTION_ID"]))
81 && isset($filter[
"ACTIVE"]) && $filter[
"ACTIVE"] ===
"Y"
85 $toUnset[] = array(&$filter,
"SECTION_ID");
87 if (isset($filter[
"INCLUDE_SUBSECTIONS"]) && $filter[
"INCLUDE_SUBSECTIONS"] ===
"Y")
89 $subsectionsCondition =
"";
90 $toUnset[] = array(&$filter,
"INCLUDE_SUBSECTIONS");
94 $subsectionsCondition =
"INCLUDE_SUBSECTIONS=1";
95 if (array_key_exists(
"INCLUDE_SUBSECTIONS", $filter))
96 $toUnset[] = array(&$filter,
"INCLUDE_SUBSECTIONS");
99 $hasAdditionalFilters =
false;
100 $this->fillWhere($where, $hasAdditionalFilters, $toUnset, $filter);
108 "VALUES" => array(0),
113 isset($filter[
"=ID"]) && is_object($filter[
"=ID"])
114 && $filter[
"=ID"]->arFilter[
"IBLOCK_ID"] == $this->facet->getSkuIblockId()
115 && $filter[
"=ID"]->strField ===
"PROPERTY_".$this->facet->getSkuPropertyId()
118 $hasAdditionalFilters =
false;
119 $this->fillWhere($where, $hasAdditionalFilters, $toUnset, $filter[
"=ID"]->arFilter);
120 if (!$hasAdditionalFilters)
122 $toUnset[] = array(&$filter,
"=ID");
128 $filter[
"SECTION_ID"] = (isset($filter[
"SECTION_ID"]) ? (int)$filter[
"SECTION_ID"] : 0);
129 $this->facet->setSectionId($filter[
"SECTION_ID"]);
132 if (isset($this->options[
"CURRENCY_CONVERSION"]) && $this->options[
"CURRENCY_CONVERSION"])
134 $this->facet->enableCurrencyConversion(
135 $this->options[
"CURRENCY_CONVERSION"][
"TO"] ??
'',
136 $this->options[
"CURRENCY_CONVERSION"][
"FROM"] ??
''
140 $distinctSelectCapable = (\Bitrix\Main\Application::getConnection()->getType() ==
"mysql");
141 if (count($where) == 1 && $distinctSelectCapable)
143 $this->distinct =
true;
144 $fcJoin =
"INNER JOIN ".$this->storage->getTableName().
" FC on FC.ELEMENT_ID = BE.ID";
145 foreach ($where as $facetFilter)
147 $sqlWhere = $this->facet->whereToSql($facetFilter,
"FC", $subsectionsCondition);
149 $sqlSearch[] = $sqlWhere;
152 elseif (count($where) <= 5)
157 foreach ($where as $facetFilter)
160 $subJoin .=
"FROM ".$this->storage->getTableName().
" FC$i\n";
162 $subJoin .=
"INNER JOIN ".$this->storage->getTableName().
" FC$i ON FC$i.ELEMENT_ID = FC0.ELEMENT_ID\n";
164 $sqlWhere = $this->facet->whereToSql($facetFilter,
"FC$i", $subsectionsCondition);
168 $subWhere .=
"\nAND ".$sqlWhere;
170 $subWhere .= $sqlWhere;
178 SELECT ".($distinctSelectCapable?
"DISTINCT":
"").
" FC0.ELEMENT_ID
182 ) FC on FC.ELEMENT_ID = BE.ID
187 $condition = array();
188 foreach ($where as $facetFilter)
190 $sqlWhere = $this->facet->whereToSql($facetFilter,
"FC0", $subsectionsCondition);
192 $condition[] = $sqlWhere;
196 SELECT FC0.ELEMENT_ID
197 FROM ".$this->storage->getTableName().
" FC0
198 WHERE FC0.SECTION_ID = ".$filter[
"SECTION_ID"].
"
200 (".implode(
")OR(", $condition).
")
202 GROUP BY FC0.ELEMENT_ID
203 HAVING count(DISTINCT FC0.FACET_ID) = ".count($condition).
"
204 ) FC on FC.ELEMENT_ID = BE.ID
208 foreach ($toUnset as $command)
210 unset($command[0][$command[1]]);
231 private function fillWhere(&$where, &$hasAdditionalFilters, &$toUnset, &$filter)
233 $countUnset = count($toUnset);
235 $propertyCodeMap =
null;
237 $usePriceFilter = isset($this->options[
'PRICE_FILTER']) && $this->options[
'PRICE_FILTER'];
239 foreach ($filter as $filterKey => $filterValue)
241 if (preg_match(
"/^(=)PROPERTY\$/i", $filterKey, $keyDetails) && is_array($filterValue))
243 if ($properties ===
null)
244 $properties = $this->getFilterProperty();
245 if ($propertyCodeMap ===
null)
247 $propertyCodeMap = $this->getPropertyCodeMap();
250 foreach ($filterValue as $propertyId => $value)
252 $propertyId = $propertyCodeMap[$propertyId] ??
null;
255 || !isset($properties[$propertyId])
260 $facetId = $this->storage->propertyIdToFacetId($propertyId);
267 "TYPE" => $properties[$propertyId],
268 "OP" => $keyDetails[1],
269 "FACET_ID" => $facetId,
270 "VALUES" => $sqlValues,
272 $toUnset[] = array(&$filter[$filterKey], $propertyId);
277 elseif (preg_match(
"/^(=)PROPERTY_(\\d+)\$/i", $filterKey, $keyDetails))
279 if ($properties ===
null)
280 $properties = $this->getFilterProperty();
281 if ($propertyCodeMap ===
null)
283 $propertyCodeMap = $this->getPropertyCodeMap();
286 $propertyId = $propertyCodeMap[$keyDetails[2]] ??
null;
289 || !isset($properties[$propertyId])
294 $value = $filterValue;
295 $facetId = $this->storage->propertyIdToFacetId($propertyId);
302 "TYPE" => $properties[$propertyId],
303 "OP" => $keyDetails[1],
304 "FACET_ID" => $facetId,
305 "VALUES" => $sqlValues,
307 $toUnset[] = array(&$filter, $filterKey);
311 elseif (preg_match(
"/^(>=|<=)PROPERTY\$/i", $filterKey, $keyDetails) && is_array($filterValue))
313 if ($properties ===
null)
314 $properties = $this->getFilterProperty();
315 if ($propertyCodeMap ===
null)
317 $propertyCodeMap = $this->getPropertyCodeMap();
320 foreach ($filterValue as $propertyId => $value)
322 $propertyId = $propertyCodeMap[$propertyId] ??
null;
325 || !isset($properties[$propertyId])
330 $facetId = $this->storage->propertyIdToFacetId($propertyId);
333 if (is_array($value))
334 $doubleValue = doubleval(current($value));
336 $doubleValue = doubleval($value);
339 "OP" => $keyDetails[1],
340 "FACET_ID" => $facetId,
341 "VALUES" => array($doubleValue),
343 $toUnset[] = array(&$filter[$filterKey], $propertyId);
347 if (is_array($value))
348 $timestamp = MakeTimeStamp(current($value),
"YYYY-MM-DD HH:MI:SS");
350 $timestamp = MakeTimeStamp($value,
"YYYY-MM-DD HH:MI:SS");
353 "OP" => $keyDetails[1],
354 "FACET_ID" => $facetId,
355 "VALUES" => array($timestamp),
357 $toUnset[] = array(&$filter[$filterKey], $propertyId);
361 elseif (preg_match(
"/^(><)PROPERTY\$/i", $filterKey, $keyDetails) && is_array($filterValue))
363 if ($properties ===
null)
364 $properties = $this->getFilterProperty();
365 if ($propertyCodeMap ===
null)
367 $propertyCodeMap = $this->getPropertyCodeMap();
370 foreach ($filterValue as $propertyId => $value)
372 $propertyId = $propertyCodeMap[$propertyId] ??
null;
375 || !isset($properties[$propertyId])
380 $facetId = $this->storage->propertyIdToFacetId($propertyId);
383 if (is_array($value) && count($value) == 2)
385 $doubleMinValue = doubleval(current($value));
386 $doubleMaxValue = doubleval(end($value));
389 "OP" => $keyDetails[1],
390 "FACET_ID" => $facetId,
391 "VALUES" => array($doubleMinValue, $doubleMaxValue),
393 $toUnset[] = array(&$filter[$filterKey], $propertyId);
398 if (is_array($value) && count($value) == 2)
400 $timestamp1 = MakeTimeStamp(current($value),
"YYYY-MM-DD HH:MI:SS");
401 $timestamp2 = MakeTimeStamp(end($value),
"YYYY-MM-DD HH:MI:SS");
404 "OP" => $keyDetails[1],
405 "FACET_ID" => $facetId,
406 "VALUES" => array($timestamp1, $timestamp2),
408 $toUnset[] = array(&$filter[$filterKey], $propertyId);
415 && preg_match(
"/^(>=|<=)(?:CATALOG_|)PRICE_(\\d+)\$/i", $filterKey, $keyDetails)
416 && !is_array($filterValue)
419 $priceId = $keyDetails[2];
420 $value = $filterValue;
421 $facetId = $this->storage->priceIdToFacetId($priceId);
422 $doubleValue = doubleval($value);
425 "OP" => $keyDetails[1],
426 "FACET_ID" => $facetId,
427 "VALUES" => array($doubleValue),
429 $toUnset[] = array(&$filter, $filterKey);
433 && preg_match(
"/^(><)(?:CATALOG_|)PRICE_(\\d+)\$/i", $filterKey, $keyDetails)
434 && is_array($filterValue)
437 $priceId = $keyDetails[2];
438 $value = $filterValue;
439 $facetId = $this->storage->priceIdToFacetId($priceId);
440 $doubleValueMin = doubleval($value[0]);
441 $doubleValueMax = doubleval($value[1]);
444 "OP" => $keyDetails[1],
445 "FACET_ID" => $facetId,
446 "VALUES" => array($doubleValueMin, $doubleValueMax),
448 $toUnset[] = array(&$filter, $filterKey);
452 && is_numeric($filterKey)
453 && is_array($filterValue) && count($filterValue) === 3
454 && isset($filterValue[
"LOGIC"]) && $filterValue[
"LOGIC"] ===
"OR"
455 && isset($filterValue[
"=ID"]) && is_object($filterValue[
"=ID"])
456 && preg_match(
"/^(>=|<=)(?:CATALOG_|)PRICE_(\\d+)\$/i", key($filterValue[0][0]), $keyDetails)
457 && !is_array(current($filterValue[0][0]))
460 $priceId = $keyDetails[2];
461 $value = current($filterValue[0][0]);
462 $facetId = $this->storage->priceIdToFacetId($priceId);
463 $doubleValue = doubleval($value);
466 "OP" => $keyDetails[1],
467 "FACET_ID" => $facetId,
468 "VALUES" => array($doubleValue),
470 $toUnset[] = array(&$filter, $filterKey);
471 $toUnset[] = array(&$filter,
"CATALOG_SHOP_QUANTITY_".$priceId);
475 && is_numeric($filterKey)
476 && is_array($filterValue) && count($filterValue) === 3
477 && isset($filterValue[
"LOGIC"]) && $filterValue[
"LOGIC"] ===
"OR"
478 && isset($filterValue[
"=ID"]) && is_object($filterValue[
"=ID"])
479 && preg_match(
"/^(><)(?:CATALOG_|)PRICE_(\\d+)\$/i", key($filterValue[0][0]), $keyDetails)
480 && is_array(current($filterValue[0][0]))
483 $priceId = $keyDetails[2];
484 $value = current($filterValue[0][0]);
485 $facetId = $this->storage->priceIdToFacetId($priceId);
486 $doubleValueMin = doubleval($value[0]);
487 $doubleValueMax = doubleval($value[1]);
490 "OP" => $keyDetails[1],
491 "FACET_ID" => $facetId,
492 "VALUES" => array($doubleValueMin, $doubleValueMax),
494 $toUnset[] = array(&$filter, $filterKey);
495 $toUnset[] = array(&$filter,
"CATALOG_SHOP_QUANTITY_".$priceId);
498 $filterKey !==
"IBLOCK_ID"
499 && $filterKey !==
"ACTIVE"
500 && $filterKey !==
"ACTIVE_DATE"
503 $hasAdditionalFilters =
true;
506 if ($hasAdditionalFilters)
508 while (count($toUnset) > $countUnset)
527 if (is_array($value))
529 foreach ($value as $val)
531 if ((
string)$val <>
'')
535 $result[] = $this->dictionary->getStringId($val,
false);
539 $result[] = (int)$val;
544 elseif ((
string)$value <>
'')
548 $result[] = $this->dictionary->getStringId($value,
false);
552 $result[] = (int)$value;
566 private function getFilterProperty(): array
569 if (!isset($this->propertyFilter))
571 $this->propertyFilter = array();
572 $propertyList = \Bitrix\Iblock\SectionPropertyTable::getList(array(
573 "select" => array(
"PROPERTY_ID",
"PROPERTY.PROPERTY_TYPE",
"PROPERTY.USER_TYPE"),
575 "=IBLOCK_ID" => array($this->facet->getIblockId(), $this->facet->getSkuIblockId()),
576 "=SMART_FILTER" =>
"Y",
579 while ($link = $propertyList->fetch())
581 if ($link[
"IBLOCK_SECTION_PROPERTY_PROPERTY_PROPERTY_TYPE"] ===
"N")
583 elseif ($link[
"IBLOCK_SECTION_PROPERTY_PROPERTY_USER_TYPE"] ===
"DateTime")
584 $this->propertyFilter[$link["PROPERTY_ID"]] =
Storage::DATETIME;
585 elseif ($link["IBLOCK_SECTION_PROPERTY_PROPERTY_PROPERTY_TYPE"] === "S")
586 $this->propertyFilter[$link["PROPERTY_ID"]] =
Storage::STRING;
588 $this->propertyFilter[$link["PROPERTY_ID"]] =
Storage::DICTIONARY;
591 return $this->propertyFilter;
594 private function getPropertyCodeMap(): array
598 $iterator = \Bitrix\Iblock\PropertyTable::getList([
604 '=IBLOCK_ID' => $this->facet->getIblockId(),
607 while ($row = $iterator->fetch())
609 $id = (int)$row[
'ID'];
611 $row[
'CODE'] = (string)$row[
'CODE'];
612 if ($row[
'CODE'] !==
'')
614 $result[$row[
'CODE']] = $id;
619 $skuIblockId = $this->facet->getSkuIblockId();
620 if ($skuIblockId > 0)
622 $iterator = \Bitrix\Iblock\PropertyTable::getList([
627 '=IBLOCK_ID' => $skuIblockId,
630 while ($row = $iterator->fetch())
632 $id = (int)$row[
'ID'];