first commit
This commit is contained in:
636
bundles/KupShop/CatalogBundle/View/AutocompleteView.php
Normal file
636
bundles/KupShop/CatalogBundle/View/AutocompleteView.php
Normal file
@@ -0,0 +1,636 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\CatalogBundle\View;
|
||||
|
||||
use KupShop\CatalogBundle\Search\FulltextElastic;
|
||||
use KupShop\CatalogBundle\Util\Product\ProductDiscountCalculator;
|
||||
use KupShop\ContentBundle\Entity\ProductUnified;
|
||||
use KupShop\I18nBundle\Entity\Country;
|
||||
use KupShop\I18nBundle\Translations\ParametersListTranslation;
|
||||
use KupShop\KupShopBundle\Config;
|
||||
use KupShop\KupShopBundle\Context\CountryContext;
|
||||
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
||||
use KupShop\KupShopBundle\Util\System\UrlFinder;
|
||||
use Query\Operator;
|
||||
use Query\QueryBuilder;
|
||||
use Query\Translation;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
|
||||
class AutocompleteView
|
||||
{
|
||||
public $query;
|
||||
|
||||
/** @var UrlFinder */
|
||||
protected $urlFinder;
|
||||
|
||||
/** @required */
|
||||
public CountryContext $countryContext;
|
||||
|
||||
/** @required */
|
||||
public ProductDiscountCalculator $productDiscountCalculator;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->query = new \Query();
|
||||
}
|
||||
|
||||
public function handleAutocomplete()
|
||||
{
|
||||
$type = getVal('type');
|
||||
// Remove '-' and '_', Capitalize each word
|
||||
$type = strtr(ucwords(strtr($type, ['_' => ' ', '-' => ' '])), [' ' => '']);
|
||||
if (empty($type)) {
|
||||
return new JsonResponse();
|
||||
}
|
||||
|
||||
$search = $this->prepareSearch(getVal('term'));
|
||||
|
||||
$this->query->limit = getVal('limit', 10) ?: 100;
|
||||
|
||||
$result = $this->handle($type, $search);
|
||||
|
||||
return new JsonResponse($result);
|
||||
}
|
||||
|
||||
public function handle($type, $search)
|
||||
{
|
||||
// Store search term to query data
|
||||
$this->query->data['search'] = $search;
|
||||
$this->query->data['search_both'] = "%{$search}%";
|
||||
$this->query->data['search_left'] = "{$search}%";
|
||||
|
||||
if (method_exists($this, 'handle'.ucfirst($type))) {
|
||||
return call_user_func([$this, 'handle'.ucfirst($type)]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function handleProduct()
|
||||
{
|
||||
$this->query->fields = 'title as label';
|
||||
$this->query->from = 'products p';
|
||||
$this->query->where = 'p.figure="Y" AND p.title LIKE :search_both';
|
||||
$this->query->order = 'p.title';
|
||||
|
||||
return $this->getQueryData();
|
||||
}
|
||||
|
||||
public function handleProductCategories()
|
||||
{
|
||||
return $this->handleProductCategoriesFulltext();
|
||||
}
|
||||
|
||||
public function handleProductCategoriesFulltext()
|
||||
{
|
||||
// Tady si schválně říkám o FulltextElastic a ne Interface, protože nechci aby mi přišel LuigisBox. Ten nepoužívá naše autocomplete
|
||||
$fulltext = ServiceContainer::getService(FulltextElastic::class);
|
||||
$fulltext->setCurlTimeout(10);
|
||||
|
||||
$term = $this->query->data['search'];
|
||||
|
||||
$enabledTopics = $fulltext->getIndexTypes();
|
||||
if ($disabled_topics = Config::get()['Modules'][\Modules::SEARCH]['disabled_topics'] ?? null) {
|
||||
$enabledTopics = array_values(array_diff($enabledTopics, $disabled_topics));
|
||||
}
|
||||
|
||||
$multiSearchResult = $fulltext->search(
|
||||
$term,
|
||||
$this->getFulltextSearchConfig($enabledTopics),
|
||||
$enabledTopics
|
||||
);
|
||||
|
||||
return $this->processFulltextResults($multiSearchResult, $fulltext->multiSearchResultTotals);
|
||||
}
|
||||
|
||||
public function handleDeliveryInPersonSellers(): array
|
||||
{
|
||||
if (!findModule(\Modules::SELLERS)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$this->query->fields = 'id, psc as value, title as name, CONCAT_WS(", ", psc, city, street, number) as address';
|
||||
$this->query->from = 'sellers';
|
||||
|
||||
$search = get_search_query($this->query->data['search'], [
|
||||
['field' => 'title', 'match' => 'both'],
|
||||
['field' => 'psc', 'match' => 'both'],
|
||||
['field' => 'city', 'match' => 'both'],
|
||||
['field' => 'street', 'match' => 'both'],
|
||||
]);
|
||||
|
||||
$this->query->data = $search['data'];
|
||||
$this->query->where = $search['where'];
|
||||
|
||||
return $this->getQueryData();
|
||||
}
|
||||
|
||||
public function handleDeliveryBalikDoRuky()
|
||||
{
|
||||
$this->query->fields = 'psc as value, name, region, time_zone, address';
|
||||
$this->query->order = 'psc';
|
||||
$this->query->from = '`kupshop_shared`.`delivery_balik_na_postu`';
|
||||
$this->query->where = 'name LIKE :search_left OR psc LIKE :search_left';
|
||||
|
||||
return $this->getQueryData();
|
||||
}
|
||||
|
||||
public function handleDeliveryBalikovna()
|
||||
{
|
||||
$this->query->fields = 'zip as value, name, city, city_part, address';
|
||||
$this->query->order = 'zip';
|
||||
$this->query->from = '`kupshop_shared`.`delivery_balikovna`';
|
||||
$this->query->where = 'name LIKE :search_left OR city LIKE :search_left OR city_part LIKE :search_left OR address LIKE :search_both OR zip LIKE :search_left';
|
||||
|
||||
return $this->getQueryData();
|
||||
}
|
||||
|
||||
public function handleDeliveryZasilkovna()
|
||||
{
|
||||
$this->query->fields = 'zip, name, hours, labelRouting, id as value';
|
||||
$this->query->order = 'zip';
|
||||
$this->query->from = '`kupshop_shared`.`delivery_zasilkovna`';
|
||||
$this->query->where = '(name LIKE :search_both OR zip LIKE :search_both)';
|
||||
|
||||
$onlyVisible = getVal('onlyVisible', null, 'Y');
|
||||
if ($onlyVisible == 'Y') {
|
||||
$this->query->where .= " AND visible = 'Y' ";
|
||||
}
|
||||
|
||||
$except = getVal('except');
|
||||
|
||||
if (!empty($except)) {
|
||||
$except = array_filter(
|
||||
explode(',', $except),
|
||||
function ($id) {
|
||||
return (is_numeric($id)) ? true : false;
|
||||
}
|
||||
);
|
||||
|
||||
$this->query->where .= ' AND id NOT IN ('.implode(',', $except).')';
|
||||
}
|
||||
|
||||
if ($currency = getVal('currency')) {
|
||||
$currencyCode = $currency;
|
||||
} else {
|
||||
$currencyContext = ServiceContainer::getService(\KupShop\KupShopBundle\Context\CurrencyContext::class);
|
||||
$currencyCode = $currencyContext->getActiveId();
|
||||
}
|
||||
|
||||
if (!empty($currencyCode)) {
|
||||
switch ($currencyCode) {
|
||||
case 'CZK': $this->query->where .= ' AND country=\'cz\'';
|
||||
break;
|
||||
case 'EUR': $this->query->where .= ' AND country=\'sk\'';
|
||||
break;
|
||||
case 'HUF': $this->query->where .= ' AND country=\'hu\'';
|
||||
break;
|
||||
case 'RON': $this->query->where .= ' AND country=\'ro\'';
|
||||
break;
|
||||
case 'PLN': $this->query->where .= ' AND country=\'pl\'';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->getQueryData();
|
||||
}
|
||||
|
||||
public function handleDeliveryPplparcelshop()
|
||||
{
|
||||
$this->query->fields = "zip, city, street, type, id as value, CONCAT(city, ', ', street, ', ', name) AS name";
|
||||
$this->query->order = 'zip';
|
||||
$this->query->from = '`kupshop_shared`.`delivery_pplparcelshop`';
|
||||
$this->query->where = '(zip LIKE :search_both OR city LIKE :search_both OR street LIKE :search_both OR name LIKE :search_both)';
|
||||
|
||||
$countries = "'".implode("','", array_map(function (Country $c) {return $c->getId(); }, $this->countryContext->getSupported()))."'";
|
||||
if ($country = getVal('country')) {
|
||||
$countries = "'{$country}'";
|
||||
}
|
||||
if (!empty($countries)) {
|
||||
$this->query->where .= ' AND country IN ('.$countries.')';
|
||||
}
|
||||
|
||||
$except = getVal('except');
|
||||
|
||||
if (!empty($except)) {
|
||||
$except = array_filter(
|
||||
explode(',', $except),
|
||||
function ($id) {
|
||||
return (is_numeric($id)) ? true : false;
|
||||
}
|
||||
);
|
||||
|
||||
$this->query->where .= ' AND id NOT IN ('.implode(',', $except).')';
|
||||
}
|
||||
|
||||
return $this->getQueryData();
|
||||
}
|
||||
|
||||
public function handleDeliveryUlozenka()
|
||||
{
|
||||
$this->query->fields = "zip, city, street, type, id as value, CONCAT(city, ', ', street, ', ', name) AS name";
|
||||
$this->query->order = 'zip';
|
||||
$this->query->from = '`kupshop_shared`.`delivery_ulozenka`';
|
||||
$this->query->where = '(zip LIKE :search_both OR city LIKE :search_both OR street LIKE :search_both OR name LIKE :search_both)';
|
||||
|
||||
$except = getVal('except');
|
||||
|
||||
if (!empty($except)) {
|
||||
$except = array_filter(
|
||||
explode(',', $except),
|
||||
function ($id) {
|
||||
return (is_numeric($id)) ? true : false;
|
||||
}
|
||||
);
|
||||
|
||||
$this->query->where .= ' AND id NOT IN ('.implode(',', $except).')';
|
||||
}
|
||||
|
||||
return $this->getQueryData();
|
||||
}
|
||||
|
||||
public function handleDeliveryInTime()
|
||||
{
|
||||
$this->query->fields = "zip, city, street, type, id as value, CONCAT(city, ', ', street, ', ', name) AS name";
|
||||
$this->query->order = 'zip';
|
||||
$this->query->from = '`kupshop_shared`.`delivery_intime`';
|
||||
$this->query->where = '(zip LIKE :search_both OR city LIKE :search_both OR street LIKE :search_both OR name LIKE :search_both)';
|
||||
|
||||
$except = getVal('except');
|
||||
|
||||
if (!empty($except)) {
|
||||
$except = array_filter(
|
||||
explode(',', $except),
|
||||
function ($id) {
|
||||
return (is_numeric($id)) ? true : false;
|
||||
}
|
||||
);
|
||||
|
||||
$this->query->where .= ' AND id NOT IN ('.implode(',', $except).')';
|
||||
}
|
||||
|
||||
return $this->getQueryData();
|
||||
}
|
||||
|
||||
public function handleDeliveryDpdpickup()
|
||||
{
|
||||
$this->query->fields = 'place, zip, city, street, id as value';
|
||||
$this->query->order = 'zip';
|
||||
$this->query->from = '`kupshop_shared`.`delivery_dpdpickup`';
|
||||
$this->query->where = '(LPAD(zip, 5, 0) LIKE :search_both OR city LIKE :search_both OR street LIKE :search_both OR place LIKE :search_both)';
|
||||
|
||||
$except = getVal('except');
|
||||
|
||||
if (!empty($except)) {
|
||||
$except = array_filter(
|
||||
explode(',', $except),
|
||||
function ($id) {
|
||||
return (is_numeric($id)) ? true : false;
|
||||
}
|
||||
);
|
||||
|
||||
$this->query->where .= ' AND id NOT IN ('.implode(',', $except).')';
|
||||
}
|
||||
|
||||
return $this->getQueryData();
|
||||
}
|
||||
|
||||
public function handleDeliverySpbalikobox()
|
||||
{
|
||||
$this->query->fields = 'zip, city, street, type, id as value, name';
|
||||
$this->query->order = 'zip';
|
||||
$this->query->from = '`kupshop_shared`.`delivery_sp_balikobox`';
|
||||
$this->query->where = '(zip LIKE :search_both OR city LIKE :search_both OR street LIKE :search_both OR name LIKE :search_both)';
|
||||
|
||||
$except = getVal('except');
|
||||
|
||||
if (!empty($except)) {
|
||||
$except = array_filter(
|
||||
explode(',', $except),
|
||||
function ($id) {
|
||||
return (is_numeric($id)) ? true : false;
|
||||
}
|
||||
);
|
||||
|
||||
$this->query->where .= ' AND id NOT IN ('.implode(',', $except).')';
|
||||
}
|
||||
|
||||
return $this->getQueryData();
|
||||
}
|
||||
|
||||
public function handleDeliveryGlsparcelshop()
|
||||
{
|
||||
$this->query->fields = "zip, city, street, type, id as value, CONCAT(city, ', ', street, ', ', name) AS name";
|
||||
$this->query->order = 'zip';
|
||||
$this->query->from = '`kupshop_shared`.`delivery_glsparcelshop`';
|
||||
$this->query->where = '(zip LIKE :search_both OR city LIKE :search_both OR street LIKE :search_both OR name LIKE :search_both)';
|
||||
|
||||
$except = getVal('except');
|
||||
|
||||
if (!empty($except)) {
|
||||
$except = array_filter(
|
||||
explode(',', $except),
|
||||
function ($id) {
|
||||
return (is_numeric($id)) ? true : false;
|
||||
}
|
||||
);
|
||||
|
||||
$this->query->where .= ' AND id NOT IN ('.implode(',', $except).')';
|
||||
}
|
||||
|
||||
return $this->getQueryData();
|
||||
}
|
||||
|
||||
public function handleDeliveryGeispoint()
|
||||
{
|
||||
$this->query->fields = "zip, city, street, type, id as value, CONCAT(city, ', ', street, ', ', name) AS name";
|
||||
$this->query->order = 'zip';
|
||||
$this->query->from = '`kupshop_shared`.`delivery_geispoint`';
|
||||
$this->query->where = '(zip LIKE :search_both OR city LIKE :search_both OR street LIKE :search_both OR name LIKE :search_both)';
|
||||
|
||||
$except = getVal('except');
|
||||
|
||||
if (!empty($except)) {
|
||||
$except = array_filter(
|
||||
explode(',', $except),
|
||||
function ($id) {
|
||||
return (is_numeric($id)) ? true : false;
|
||||
}
|
||||
);
|
||||
|
||||
$this->query->where .= ' AND id NOT IN ('.implode(',', $except).')';
|
||||
}
|
||||
|
||||
return $this->getQueryData();
|
||||
}
|
||||
|
||||
public function handleDeliveryPaczkomaty()
|
||||
{
|
||||
$this->query->fields = "zip, city, street, type, id as value, CONCAT(city, ', ', street, ', ', name) AS name";
|
||||
$this->query->order = 'zip';
|
||||
$this->query->from = '`kupshop_shared`.`delivery_paczkomaty`';
|
||||
$this->query->where = '(zip LIKE :search_both OR city LIKE :search_both OR street LIKE :search_both OR name LIKE :search_both)';
|
||||
|
||||
$except = getVal('except');
|
||||
|
||||
if (!empty($except)) {
|
||||
$except = array_filter(
|
||||
explode(',', $except),
|
||||
function ($id) {
|
||||
return (is_numeric($id)) ? true : false;
|
||||
}
|
||||
);
|
||||
|
||||
$this->query->where .= ' AND id NOT IN ('.implode(',', $except).')';
|
||||
}
|
||||
|
||||
return $this->getQueryData();
|
||||
}
|
||||
|
||||
public function handleParameterValues(): array
|
||||
{
|
||||
$term = $this->query->data['search'] ?? '';
|
||||
|
||||
$search = [$term];
|
||||
if (getVal('fulltext')) {
|
||||
$search = array_filter(explode(' ', $term));
|
||||
}
|
||||
|
||||
$qb = sqlQueryBuilder()
|
||||
->select('pl.id as value, pl.position')
|
||||
->from('parameters_list', 'pl')
|
||||
->andWhere(Operator::equals(['pl.id_parameter' => getVal('parameterId')]))
|
||||
->andWhere(
|
||||
Translation::joinTranslatedFields(
|
||||
ParametersListTranslation::class,
|
||||
function (QueryBuilder $qb, $columnName, $translatedField) use ($search) {
|
||||
$searchField = Operator::coalesce($translatedField, "pl.{$columnName}");
|
||||
|
||||
$andX = [];
|
||||
foreach ($search as $value) {
|
||||
$andX[] = Operator::like([$searchField => "%{$value}%"]);
|
||||
}
|
||||
|
||||
$qb->andWhere(Operator::andX($andX));
|
||||
}, ['value' => 'name']
|
||||
)
|
||||
)
|
||||
->orderBy('position');
|
||||
|
||||
if ($limit = getVal('limit')) {
|
||||
$qb->setMaxResults((int) $limit);
|
||||
}
|
||||
|
||||
return $qb->execute()->fetchAllAssociative();
|
||||
}
|
||||
|
||||
public function getQueryData()
|
||||
{
|
||||
return sqlFetchAll($this->query->execute());
|
||||
}
|
||||
|
||||
public function prepareSearch($search)
|
||||
{
|
||||
$search = trim(urldecode($search));
|
||||
|
||||
return $search;
|
||||
}
|
||||
|
||||
protected function processProducts(array $sphinxResult)
|
||||
{
|
||||
$productList = $this->getProductList();
|
||||
|
||||
$sphinxIds = array_keys($sphinxResult);
|
||||
$productList->andSpec(function (\Query\QueryBuilder $qb) use ($sphinxIds) {
|
||||
$qb->setParameter('productIds', $sphinxIds, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY);
|
||||
|
||||
return $qb->expr()->in('p.id', ':productIds');
|
||||
});
|
||||
$productList->orderBy('FIELD(p.id, :productIds)');
|
||||
|
||||
$productList->fetchProducers();
|
||||
$productList->fetchImages(2);
|
||||
$productList->fetchSets();
|
||||
$productList->fetchStoresInStore();
|
||||
|
||||
return array_map(function (\Product $product) {
|
||||
return $this->getProductResult($product);
|
||||
}, $productList->getProducts()->getValues());
|
||||
}
|
||||
|
||||
protected function processSections(array $sphinxResult)
|
||||
{
|
||||
foreach ($sphinxResult as &$section) {
|
||||
$section['label'] = $section['path'] ?? '';
|
||||
}
|
||||
|
||||
return array_values($sphinxResult);
|
||||
}
|
||||
|
||||
protected function processProducers(array $sphinxResult)
|
||||
{
|
||||
foreach ($sphinxResult as &$producer) {
|
||||
$producer['label'] = $producer['name'] ?? '';
|
||||
}
|
||||
|
||||
return array_values($sphinxResult);
|
||||
}
|
||||
|
||||
protected function processArticles(array $result)
|
||||
{
|
||||
foreach ($result as &$article) {
|
||||
$article['label'] = $article['title'];
|
||||
}
|
||||
|
||||
return array_values($result);
|
||||
}
|
||||
|
||||
protected function processPages(array $result): array
|
||||
{
|
||||
foreach ($result as &$page) {
|
||||
$page['label'] = $page['name'];
|
||||
}
|
||||
|
||||
return array_values($result);
|
||||
}
|
||||
|
||||
protected function getProductList(): \ProductList
|
||||
{
|
||||
$productList = ServiceContainer::getService(\KupShop\CatalogBundle\ProductList\ProductList::class);
|
||||
$productList->applyDefaultFilterParams();
|
||||
|
||||
return $productList;
|
||||
}
|
||||
|
||||
protected function getProductResult(\Product $product): array
|
||||
{
|
||||
$productPrice = $product['productPrice'];
|
||||
|
||||
$price = $productPrice['price_with_vat'];
|
||||
$priceWithoutVat = $productPrice['price_without_vat'];
|
||||
|
||||
$productDiscountResult = $this->productDiscountCalculator->calculate(
|
||||
new ProductUnified($product)
|
||||
);
|
||||
|
||||
$priceOriginal = $productPrice->getOriginalPrice()->getPriceWithoutDiscount();
|
||||
|
||||
return [
|
||||
'id' => $product->id,
|
||||
'image' => ($product->image ? $this->urlFinder->staticUrl($product->image['src']) : ''),
|
||||
'label' => $product->title,
|
||||
'discount' => $product->discount->asFloat(),
|
||||
'price' => printPrice($price),
|
||||
'price_without_vat' => printPrice($priceWithoutVat),
|
||||
'price_array' => $product->price_array,
|
||||
'priceOriginal' => printPrice($priceOriginal),
|
||||
'annotation' => $product->descr,
|
||||
'producer' => $product->producer ?? [],
|
||||
'inStore' => $product->inStore,
|
||||
'deliveryTime' => $product->deliveryTime,
|
||||
'deliveryTimeText' => $product->deliveryTimeText,
|
||||
'deliveryTimeRaw' => $product->deliveryTimeRaw,
|
||||
'storesInStore' => $product->storesInStore ?? [],
|
||||
'productDiscount' => [
|
||||
'discount' => $productDiscountResult->discount->asFloat(),
|
||||
'priceForDiscount' => $productDiscountResult->priceForDiscount ? printPrice($productDiscountResult->priceForDiscount) : null,
|
||||
'priceOriginal' => $productDiscountResult->priceOriginal ? printPrice($productDiscountResult->priceOriginal) : null,
|
||||
'priceCommon' => $productDiscountResult->priceCommon ? printPrice($productDiscountResult->priceCommon) : null,
|
||||
],
|
||||
'campaign_codes' => $product->campaign_codes ?? [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setUrlFinder(UrlFinder $urlFinder): void
|
||||
{
|
||||
$this->urlFinder = $urlFinder;
|
||||
}
|
||||
|
||||
public function getFulltextSearchConfig(array $enabledTopics): array
|
||||
{
|
||||
$maxResults = 6;
|
||||
|
||||
$config = [];
|
||||
|
||||
foreach ($enabledTopics as $type) {
|
||||
$config[$type] = [
|
||||
'count' => $maxResults,
|
||||
'offset' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$config[FulltextElastic::INDEX_PRODUCTS]['order'] = '-weight';
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
public function processFulltextResults(array $multiSearchResult, array $totals): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($multiSearchResult as $type => $results) {
|
||||
if (!$results) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case FulltextElastic::INDEX_PRODUCTS:
|
||||
$result['Produkty'] = [
|
||||
'label' => 'Produkty',
|
||||
'rows' => $totals[$type] ?? 0,
|
||||
'items' => $this->processProducts($results),
|
||||
];
|
||||
break;
|
||||
|
||||
case FulltextElastic::INDEX_SECTIONS:
|
||||
$result['Kategorie'] = [
|
||||
'label' => 'Kategorie',
|
||||
// min(total, 20), protoze SearchView zobrazuje max 20 vysledku
|
||||
'rows' => min($totals[$type] ?? 0, 20),
|
||||
'items' => $this->processSections($results),
|
||||
];
|
||||
break;
|
||||
|
||||
case FulltextElastic::INDEX_PRODUCERS:
|
||||
$result['Vyrobci'] = [
|
||||
'label' => 'Výrobci',
|
||||
// min(total, 20), protoze SearchView zobrazuje max 20 vysledku
|
||||
'rows' => min($totals[$type] ?? 0, 20),
|
||||
'items' => $this->processProducers($results),
|
||||
];
|
||||
break;
|
||||
|
||||
case FulltextElastic::INDEX_ARTICLES:
|
||||
$result['Clanky'] = [
|
||||
'label' => 'Články',
|
||||
// min(total, 20), protoze SearchView zobrazuje max 20 vysledku
|
||||
'rows' => min($totals[$type] ?? 0, 20),
|
||||
'items' => $this->processArticles($results),
|
||||
];
|
||||
break;
|
||||
|
||||
case FulltextElastic::INDEX_PAGES:
|
||||
$result['Stranky'] = [
|
||||
'label' => 'Stránky',
|
||||
// min(total, 20), protoze SearchView zobrazuje max 20 vysledku
|
||||
'rows' => min($totals[$type] ?? 0, 20),
|
||||
'items' => $this->processPages($results),
|
||||
];
|
||||
break;
|
||||
|
||||
default:
|
||||
$result[$type] = [
|
||||
'label' => '$type',
|
||||
// min(total, 20), protoze SearchView zobrazuje max 20 vysledku
|
||||
'rows' => min($totals[$type] ?? 0, 20),
|
||||
'items' => $results,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
64
bundles/KupShop/CatalogBundle/View/CategoryListView.php
Normal file
64
bundles/KupShop/CatalogBundle/View/CategoryListView.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: ondra
|
||||
* Date: 19.1.18
|
||||
* Time: 12:30.
|
||||
*/
|
||||
|
||||
namespace KupShop\CatalogBundle\View;
|
||||
|
||||
use KupShop\CatalogBundle\Section\SectionTree;
|
||||
use KupShop\ComponentsBundle\View\ComponentsViewInterface;
|
||||
use KupShop\ComponentsBundle\View\ComponentsViewTrait;
|
||||
use KupShop\KupShopBundle\Views\View;
|
||||
|
||||
class CategoryListView extends View implements ComponentsViewInterface
|
||||
{
|
||||
use ComponentsViewTrait;
|
||||
|
||||
protected string $entrypoint = 'section_list';
|
||||
|
||||
protected $template = 'category_list.tpl';
|
||||
|
||||
public function getTemplate(): string
|
||||
{
|
||||
if (findModule(\Modules::COMPONENTS)) {
|
||||
return 'view/section_list.html.twig';
|
||||
}
|
||||
|
||||
return parent::getTemplate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @var SectionTree
|
||||
*/
|
||||
private $sectionTree;
|
||||
|
||||
public function __construct(SectionTree $sectionTree)
|
||||
{
|
||||
$this->sectionTree = $sectionTree;
|
||||
}
|
||||
|
||||
public function getBodyVariables()
|
||||
{
|
||||
$vars = parent::getBodyVariables();
|
||||
|
||||
if (!findModule(\Modules::COMPONENTS)) {
|
||||
$vars['category_list'] = $this->sectionTree->getTree();
|
||||
}
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return translate('title', 'category_list');
|
||||
}
|
||||
|
||||
public function getBreadcrumbs()
|
||||
{
|
||||
return getReturnNavigation(-1, 'NO_TYPE', [$this->getTitle()]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\CatalogBundle\View;
|
||||
|
||||
interface CategoryRequestHandler
|
||||
{
|
||||
public const SEARCH_CATEGORY_ID = -1;
|
||||
|
||||
/** @param string $campaign */
|
||||
public function setCampaign($campaign);
|
||||
|
||||
/** @param int|string $producerId */
|
||||
public function setProducerId($producerId);
|
||||
|
||||
/** @param int|string $parameterValueId */
|
||||
public function setParameterValueId($parameterValueId);
|
||||
|
||||
/** @param int|string $categoryId */
|
||||
public function setCategoryId($categoryId);
|
||||
|
||||
public function setFilterData(array $filterData): self;
|
||||
|
||||
public function getCorrectUrl(): ?string;
|
||||
|
||||
/**
|
||||
* @param array|mixed $indexedFilterData filterData {@see \Filter::setFilterData()} from indexed_filter
|
||||
*/
|
||||
public function setParsedURLData($indexedFilterData);
|
||||
}
|
||||
1815
bundles/KupShop/CatalogBundle/View/CategoryView.php
Normal file
1815
bundles/KupShop/CatalogBundle/View/CategoryView.php
Normal file
File diff suppressed because it is too large
Load Diff
20
bundles/KupShop/CatalogBundle/View/FavoriteView.php
Normal file
20
bundles/KupShop/CatalogBundle/View/FavoriteView.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\CatalogBundle\View;
|
||||
|
||||
use KupShop\CatalogBundle\ProductList\ProductList;
|
||||
use KupShop\CatalogBundle\Util\ActiveCategory;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
class FavoriteView extends CategoryView
|
||||
{
|
||||
protected $template = 'category.favorites.tpl';
|
||||
|
||||
public function __construct(EventDispatcherInterface $dispatcher, ProductList $productList, ActiveCategory $activeCategory)
|
||||
{
|
||||
parent::__construct($dispatcher, $productList, $activeCategory);
|
||||
$this->proxyCacheEnabled = false;
|
||||
}
|
||||
}
|
||||
72
bundles/KupShop/CatalogBundle/View/ProducersListView.php
Normal file
72
bundles/KupShop/CatalogBundle/View/ProducersListView.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\CatalogBundle\View;
|
||||
|
||||
use KupShop\CatalogBundle\Repository\ProducersRepository;
|
||||
use KupShop\ComponentsBundle\View\ComponentsViewInterface;
|
||||
use KupShop\KupShopBundle\Views\View;
|
||||
use Symfony\Contracts\Service\Attribute\Required;
|
||||
|
||||
class ProducersListView extends View implements ComponentsViewInterface
|
||||
{
|
||||
protected $template = 'producers.tpl';
|
||||
protected string $entrypoint = 'producers';
|
||||
|
||||
#[Required]
|
||||
public ProducersRepository $producersRepository;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->proxyCacheEnabled = findModule(\Modules::PROXY_CACHE, 'content');
|
||||
}
|
||||
|
||||
public function getBodyVariables(): array
|
||||
{
|
||||
$vars = parent::getBodyVariables();
|
||||
$vars['producers'] = $this->producersRepository->getProducers();
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
public function fetchPhotos($size = 0): array
|
||||
{
|
||||
$this->producersRepository->setImageSize($size);
|
||||
|
||||
return $this->producersRepository->getProducers(true);
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return translate('returnNav', 'producers');
|
||||
}
|
||||
|
||||
public function getWpjToolbar(): array
|
||||
{
|
||||
$arr = [
|
||||
'url' => getAdminUrl('list', ['type' => 'producers']),
|
||||
'title' => 'Upravit výrobce',
|
||||
'translateUrl' => getAdminUrl('list', [
|
||||
'type' => 'translateProducers',
|
||||
'iframe' => 1,
|
||||
]),
|
||||
'translateTitle' => 'Překlad výrobců',
|
||||
];
|
||||
|
||||
return array_merge(parent::getWpjToolbar(), $arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ProducersListView
|
||||
*/
|
||||
public function setSorting($sorting)
|
||||
{
|
||||
$this->producersRepository->setSorting($sorting);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTemplates(): iterable
|
||||
{
|
||||
yield $this->template;
|
||||
}
|
||||
}
|
||||
72
bundles/KupShop/CatalogBundle/View/ReviewsListView.php
Normal file
72
bundles/KupShop/CatalogBundle/View/ReviewsListView.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\CatalogBundle\View;
|
||||
|
||||
use KupShop\CatalogBundle\Util\ReviewsUtil;
|
||||
use KupShop\KupShopBundle\Context\LanguageContext;
|
||||
use KupShop\KupShopBundle\Util\Contexts;
|
||||
use KupShop\KupShopBundle\Views\View;
|
||||
|
||||
class ReviewsListView extends View
|
||||
{
|
||||
protected $template = 'reviews/reviews.list.tpl';
|
||||
|
||||
private int $page = 1;
|
||||
|
||||
public function __construct(private readonly ReviewsUtil $reviewsUtil)
|
||||
{
|
||||
}
|
||||
|
||||
public function setPage(int $page): self
|
||||
{
|
||||
$this->page = $page;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return translate('reviewsList', 'reviews');
|
||||
}
|
||||
|
||||
public function getBodyVariables(): array
|
||||
{
|
||||
$vars = parent::getBodyVariables();
|
||||
|
||||
$pager = $this->getPager();
|
||||
|
||||
$languageContext = Contexts::get(LanguageContext::class);
|
||||
|
||||
[$_, $reviews] = $this->reviewsUtil->getReviews(
|
||||
$languageContext->getActiveId(),
|
||||
$this->getReviewsLanguages(),
|
||||
spec: $pager->getSpec()
|
||||
);
|
||||
|
||||
$vars['reviews'] = $reviews;
|
||||
$vars['pager'] = $pager;
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
protected function getReviewsLanguages(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function getItemsOnPage(): int
|
||||
{
|
||||
return 50;
|
||||
}
|
||||
|
||||
private function getPager(): \Pager
|
||||
{
|
||||
$totals = $this->reviewsUtil->getReviewsTotals();
|
||||
|
||||
$pager = new \Pager(page: $this->page);
|
||||
$pager->setOnPage($this->getItemsOnPage());
|
||||
$pager->setTotal($totals['count']);
|
||||
|
||||
return $pager;
|
||||
}
|
||||
}
|
||||
248
bundles/KupShop/CatalogBundle/View/ReviewsView.php
Normal file
248
bundles/KupShop/CatalogBundle/View/ReviewsView.php
Normal file
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\CatalogBundle\View;
|
||||
|
||||
use KupShop\CatalogBundle\Util\ReviewsUtil;
|
||||
use KupShop\ContentBundle\Util\Captcha;
|
||||
use KupShop\ContentBundle\View\Exception\ValidationException;
|
||||
use KupShop\KupShopBundle\Context\LanguageContext;
|
||||
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
||||
use KupShop\KupShopBundle\Views\Traits\RequestTrait;
|
||||
use KupShop\KupShopBundle\Views\View;
|
||||
use Product;
|
||||
use Query\Operator;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
use Symfony\Contracts\Service\Attribute\Required;
|
||||
|
||||
class ReviewsView extends View
|
||||
{
|
||||
use \DatabaseCommunication;
|
||||
use RequestTrait;
|
||||
|
||||
protected ReviewsUtil $reviewsUtil;
|
||||
|
||||
// tohle by bylo potřeba asi udělat jinak ?? > ale na X shopech bych ideálně potřeboval tuhle šablonu pro přidání hodnocení
|
||||
protected $template = 'focus/focus.review-form.tpl';
|
||||
|
||||
private $user;
|
||||
|
||||
private $checkUserLog;
|
||||
protected $productId;
|
||||
|
||||
/**
|
||||
* @var SessionInterface
|
||||
*/
|
||||
protected $session;
|
||||
|
||||
#[Required]
|
||||
public function setReviewsUtil(ReviewsUtil $reviewsUtil): void
|
||||
{
|
||||
$this->reviewsUtil = $reviewsUtil;
|
||||
}
|
||||
|
||||
public function getBodyVariables()
|
||||
{
|
||||
$vars = [];
|
||||
|
||||
if (!$this->productId) {
|
||||
$this->productId = $this->request->get('id_product');
|
||||
}
|
||||
$product = $this->getProduct();
|
||||
if (!$product) {
|
||||
return $vars;
|
||||
}
|
||||
|
||||
$review = $this->reviewsUtil;
|
||||
|
||||
$fields = ['id_product', 'id_product_variation', 'date', 'rating', 'summary', 'pros', 'cons',
|
||||
'id_user', 'figure', 'id_order', 'data', 'source', ];
|
||||
|
||||
if (findModule(\Modules::SALES)) {
|
||||
$fields[] = 'id_sale';
|
||||
}
|
||||
|
||||
$vars['product'] = $product;
|
||||
$vars['id_product_variation'] = null;
|
||||
$vars['date'] = date('Y-m-d H:i:s');
|
||||
$vars['figure'] = ReviewsUtil::RANK_UNCONFIRMED;
|
||||
$vars['id_user'] = null;
|
||||
$vars['verified'] = false;
|
||||
$vars['data'] = null;
|
||||
$vars['source'] = null;
|
||||
if ($name = $this->request->get('name')) {
|
||||
$fields[] = 'name';
|
||||
$vars['name'] = $name;
|
||||
}
|
||||
|
||||
$data = $this->request->request->all();
|
||||
$boughtProduct = null;
|
||||
if (!empty($data['email']) && !empty($data['order_no'])) {
|
||||
$vars['data']['email'] = $data['email'];
|
||||
$vars['data']['order_no'] = $data['order_no'];
|
||||
// nacteni objednavky pro uzivatele bez registrace
|
||||
$boughtProduct = $review->hasUserBoughtProduct($this->productId, null, $data['email'], $data['order_no']);
|
||||
if ($boughtProduct) {
|
||||
$this->user = \User::createFromSpec(Operator::equals(['email' => $data['email']]));
|
||||
$vars['id_user'] = $this->user ? $this->user->id : null;
|
||||
$this->setTemplate('block.reviews.form.tpl');
|
||||
} else {
|
||||
$vars['error'] = translate('error_order_not_found', 'returns');
|
||||
}
|
||||
} else {
|
||||
$user = $this->getUser();
|
||||
$vars['id_user'] = $user ? $user->id : null;
|
||||
$boughtProduct = $review->hasUserBoughtProduct($this->productId, $vars['id_user']);
|
||||
}
|
||||
if ($boughtProduct) {
|
||||
$vars['verified'] = true;
|
||||
// possible types: order, sale
|
||||
$vars["id_{$boughtProduct['type']}"] = $boughtProduct['id'];
|
||||
if ($this->productId != $boughtProduct['id_product']) {
|
||||
$this->setProductId($boughtProduct['id_product']);
|
||||
$vars['product'] = $this->getProduct();
|
||||
}
|
||||
}
|
||||
|
||||
// Allow rating only once per product
|
||||
if ($review->hasUserAlreadyRated($this->productId, $vars['id_user'])) {
|
||||
$vars['already_rated'] = true;
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
$acn = $data['acn'] ?? null;
|
||||
if ($acn == 'SendReview') {
|
||||
// Rating is missing
|
||||
if (!$this->request->get('rating')) {
|
||||
return $vars;
|
||||
}
|
||||
|
||||
if ($this->checkCaptcha($data)) {
|
||||
$this->addErrorMessage(translate('errorCaptcha', 'form'));
|
||||
$vars['error'] = translate('errorCaptcha', 'form');
|
||||
$vars['data'] = $data;
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
$data = array_merge($this->request->request->all(), $vars);
|
||||
|
||||
if (findModule(\Modules::TRANSLATIONS)) {
|
||||
$languagesContext = ServiceContainer::getService(LanguageContext::class);
|
||||
$data['id_language'] = $languagesContext->getActiveId();
|
||||
$fields[] = 'id_language';
|
||||
}
|
||||
|
||||
if (!$this->addReview($review, $data, $fields)) {
|
||||
return $vars;
|
||||
}
|
||||
|
||||
$vars['sent'] = true;
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
$verified_only = \Settings::getDefault()['review_verified_only'] ?? 'N';
|
||||
if (($verified_only == 'Y') && !$vars['verified']) {
|
||||
$this->setTemplate('focus/focus.review-verify-form.tpl');
|
||||
}
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
public function setCheckUserLog($checkUserLog)
|
||||
{
|
||||
$this->checkUserLog = $checkUserLog;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function addReview(ReviewsUtil $review, array $data, array $fields): ?int
|
||||
{
|
||||
$reviewId = sqlGetConnection()->transactional(
|
||||
function () use ($review, $data, $fields) {
|
||||
if (!empty($data['data'])) {
|
||||
$data['data'] = json_encode($data['data']);
|
||||
}
|
||||
$this->insertSQL('reviews', $this->filterFields($data, $fields));
|
||||
$reviewId = (int) sqlInsertId();
|
||||
|
||||
// Confirm review without comments
|
||||
if (!$review->hasComment($data)) {
|
||||
$this->updateSQL('reviews', ['figure' => ReviewsUtil::RANK_CONFIRMED], ['id' => $reviewId]);
|
||||
}
|
||||
|
||||
return $reviewId;
|
||||
}
|
||||
);
|
||||
|
||||
$this->session->set(
|
||||
'review',
|
||||
[
|
||||
'email' => getVal('email', $data),
|
||||
'firstname' => getVal('name', $data),
|
||||
]
|
||||
);
|
||||
|
||||
return $reviewId;
|
||||
}
|
||||
|
||||
protected function checkUserLog()
|
||||
{
|
||||
if ($this->checkUserLog) {
|
||||
// Allow rating only to logged in users
|
||||
if (!$this->getUser()) {
|
||||
redirection('LOGIN');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function checkCaptcha(array $data)
|
||||
{
|
||||
// Check CAPTCHA
|
||||
if (!$this->getUser()) {
|
||||
try {
|
||||
Captcha::checkCaptcha($data, 'shared');
|
||||
} catch (ValidationException $e) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private function getUser()
|
||||
{
|
||||
if ($this->user) {
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
return $this->user = \User::getCurrentUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setSession(SessionInterface $session)
|
||||
{
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
public function setProductId(int $id): void
|
||||
{
|
||||
$this->productId = $id;
|
||||
}
|
||||
|
||||
private function getProduct(): ?\Product
|
||||
{
|
||||
if ($this->productId) {
|
||||
$product = new \Product();
|
||||
|
||||
if ($product->createFromDB($this->productId)) {
|
||||
return $product;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
97
bundles/KupShop/CatalogBundle/View/SearchPreloadView.php
Normal file
97
bundles/KupShop/CatalogBundle/View/SearchPreloadView.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\CatalogBundle\View;
|
||||
|
||||
use KupShop\CatalogBundle\ProductList\ProductCollection;
|
||||
use KupShop\CatalogBundle\ProductList\ProductList;
|
||||
use KupShop\CatalogBundle\Section\SectionTree;
|
||||
use KupShop\KupShopBundle\Views\View;
|
||||
use Query\Operator;
|
||||
use Query\Product;
|
||||
|
||||
class SearchPreloadView extends View
|
||||
{
|
||||
protected $template = 'search/search.preload.tpl';
|
||||
|
||||
protected ProductList $productList;
|
||||
protected SectionTree $sectionTree;
|
||||
|
||||
public function __construct(ProductList $productList, SectionTree $sectionTree)
|
||||
{
|
||||
$this->productList = $productList;
|
||||
$this->sectionTree = $sectionTree;
|
||||
}
|
||||
|
||||
public function getBodyVariables()
|
||||
{
|
||||
$vars = parent::getBodyVariables();
|
||||
|
||||
return array_merge($vars, $this->getPreloadData());
|
||||
}
|
||||
|
||||
protected function getPreloadData()
|
||||
{
|
||||
$vars = [];
|
||||
|
||||
$dbcfg = \Settings::getDefault();
|
||||
|
||||
$config = $dbcfg->fulltext['preload'] ?? [];
|
||||
|
||||
foreach (['products', 'sections', 'producers'] as $type) {
|
||||
$method = 'preload'.ucfirst($type);
|
||||
if (method_exists($this, $method)) {
|
||||
$vars[$type] = $this->{$method}($config[$type] ?? []);
|
||||
}
|
||||
}
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
protected function preparePreloadProductList(array $ids): ProductList
|
||||
{
|
||||
$this->productList->applyDefaultFilterParams();
|
||||
$this->productList->andSpec(Product::productsIds($ids));
|
||||
$this->productList->fetchImages(2);
|
||||
|
||||
return $this->productList;
|
||||
}
|
||||
|
||||
protected function preloadProducts(array $ids): ProductCollection
|
||||
{
|
||||
return $this->preparePreloadProductList($ids)->getProducts();
|
||||
}
|
||||
|
||||
protected function preloadSections(array $ids): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($ids as $id) {
|
||||
$result[$id] = $this->sectionTree->getSectionById($id);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function preloadProducers(array $ids): array
|
||||
{
|
||||
if (!findModule(\Modules::PRODUCERS)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$qb = sqlQueryBuilder()
|
||||
->select('pr.id, pr.name')
|
||||
->from('producers', 'pr')
|
||||
->where(Operator::inIntArray($ids, 'pr.id'));
|
||||
|
||||
$result = [];
|
||||
foreach ($qb->execute() as $item) {
|
||||
$result[$item['id']] = [
|
||||
'id' => (int) $item['id'],
|
||||
'name' => $item['name'],
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
680
bundles/KupShop/CatalogBundle/View/SearchView.php
Normal file
680
bundles/KupShop/CatalogBundle/View/SearchView.php
Normal file
@@ -0,0 +1,680 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\CatalogBundle\View;
|
||||
|
||||
use KupShop\CatalogBundle\Event\CatalogEvent;
|
||||
use KupShop\CatalogBundle\ProductList\ProductList;
|
||||
use KupShop\CatalogBundle\Query\Search;
|
||||
use KupShop\CatalogBundle\Search\Exception\FulltextException;
|
||||
use KupShop\CatalogBundle\Search\FulltextElastic;
|
||||
use KupShop\CatalogBundle\Search\FulltextInterface;
|
||||
use KupShop\ContentBundle\Util\ArticleList;
|
||||
use KupShop\ContentBundle\Util\MenuUtil;
|
||||
use KupShop\I18nBundle\Translations\MenuLinksTranslation;
|
||||
use KupShop\KupShopBundle\Config;
|
||||
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
||||
use KupShop\KupShopBundle\Util\Logging\SentryLogger;
|
||||
use KupShop\KupShopBundle\Views\Traits\RequestTrait;
|
||||
use KupShop\KupShopBundle\Views\View;
|
||||
use Query\Operator;
|
||||
use Query\Product;
|
||||
use Query\QueryBuilder;
|
||||
use Query\Translation;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class SearchView extends View
|
||||
{
|
||||
use RequestTrait;
|
||||
|
||||
protected $template = 'search.tpl';
|
||||
|
||||
/** @var string String to search */
|
||||
protected $searchTerm;
|
||||
|
||||
/** @var FulltextElastic */
|
||||
protected $fulltextSearch;
|
||||
|
||||
protected $enabledTopics;
|
||||
|
||||
protected $topicsModules = [
|
||||
'products' => [\Modules::SEARCH],
|
||||
'articles' => [\Modules::ARTICLES],
|
||||
'sections' => [\Modules::PRODUCTS_SECTIONS],
|
||||
'producers' => [\Modules::PRODUCERS],
|
||||
'pages' => [\Modules::MENU],
|
||||
];
|
||||
|
||||
protected $multiSearchResult = [];
|
||||
|
||||
/** @var SentryLogger */
|
||||
private $sentryLogger;
|
||||
|
||||
/** @var ProductList */
|
||||
private $productList;
|
||||
|
||||
private EventDispatcherInterface $eventDispatcher;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->proxyCacheEnabled = findModule(\Modules::PROXY_CACHE, 'search', findModule(\Modules::PROXY_CACHE, 'category'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setSentryLogger(SentryLogger $sentryLogger): void
|
||||
{
|
||||
$this->sentryLogger = $sentryLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setProductList(ProductList $productList): void
|
||||
{
|
||||
$this->productList = $productList;
|
||||
}
|
||||
|
||||
/** @required */
|
||||
public function setEventDispatcher(EventDispatcherInterface $eventDispatcher): void
|
||||
{
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->searchTerm ?? translate('title', 'search');
|
||||
}
|
||||
|
||||
public function setSearchTerm(string $searchTerm): SearchView
|
||||
{
|
||||
$this->searchTerm = $searchTerm;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHeaderVariables()
|
||||
{
|
||||
return parent::getHeaderVariables() + ['searchString' => $this->searchTerm];
|
||||
}
|
||||
|
||||
public function getBreadcrumbs()
|
||||
{
|
||||
return getReturnNavigation(-1, 'NO_TYPE', [translate('returnNav', 'search')]);
|
||||
}
|
||||
|
||||
public function getBodyVariables()
|
||||
{
|
||||
$pageVars = array_merge(parent::getBodyVariables(), [
|
||||
'title' => translate('h1_title', 'search'),
|
||||
'search' => $this->searchTerm,
|
||||
'searching' => 0,
|
||||
'listShow' => $this->getListShowType(),
|
||||
]);
|
||||
|
||||
// # VYHLEDAVANI V ESHOPU
|
||||
if ($this->searchTerm) {
|
||||
$pageVars['searching'] = 1;
|
||||
|
||||
$pageVars['results'] = $this->getSearchResults();
|
||||
}
|
||||
|
||||
return $pageVars;
|
||||
}
|
||||
|
||||
/** Search all enabled topics.
|
||||
* @return array
|
||||
*/
|
||||
protected function getSearchResults()
|
||||
{
|
||||
$results = [];
|
||||
|
||||
// process multi-search
|
||||
$enabledTopics = array_merge($this->fulltextSearch->getIndexTypes(), ['pages']);
|
||||
|
||||
if ($disabled_topics = Config::get()['Modules'][\Modules::SEARCH]['disabled_topics'] ?? null) {
|
||||
$enabledTopics = array_diff($enabledTopics, $disabled_topics);
|
||||
}
|
||||
|
||||
$this->setEnabledTopics($enabledTopics);
|
||||
|
||||
$enabledTypes = [];
|
||||
foreach ($this->fulltextSearch->getIndexTypes() as $type) {
|
||||
if ($this->isTopicEnabled($type)) {
|
||||
$enabledTypes[] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
$this->fulltextSearch->setDynamicFilters($this->request->get('dynamic_filter', []));
|
||||
|
||||
try {
|
||||
$this->multiSearchResult = $this->fulltextSearch->search(
|
||||
$this->searchTerm,
|
||||
$this->getSearchConfig(),
|
||||
$enabledTypes
|
||||
);
|
||||
} catch (FulltextException $e) {
|
||||
$this->multiSearchResult = [];
|
||||
$this->sentryLogger->captureException($e);
|
||||
}
|
||||
|
||||
foreach ($enabledTypes as $type) {
|
||||
switch ($type) {
|
||||
case FulltextElastic::INDEX_PRODUCTS:
|
||||
if ($this->fulltextSearch->supportsFilters()) {
|
||||
$result = $this->searchProductsByIds();
|
||||
} else {
|
||||
$result = $this->searchProducts();
|
||||
}
|
||||
break;
|
||||
|
||||
case FulltextElastic::INDEX_ARTICLES:
|
||||
$result = $this->searchArticles();
|
||||
break;
|
||||
|
||||
default:
|
||||
$result = $this->searchDefault($type);
|
||||
}
|
||||
$results[$type] = $result;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
protected function searchDefault($type)
|
||||
{
|
||||
return ['list' => $this->multiSearchResult[$type] ?? []];
|
||||
}
|
||||
|
||||
protected function getSearchConfig(): array
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
$productsPage = (int) getVal('page', null, 1);
|
||||
$productsNoOnPage = $dbcfg->cat_show_products_search ?: $dbcfg->cat_show_products;
|
||||
$productsOffset = ceil(($productsPage - 1) * $productsNoOnPage);
|
||||
$productsOrder = $this->getProductsOrder();
|
||||
|
||||
$dynamicFilters = $this->request->get('dynamic_filter', []);
|
||||
|
||||
if (!$this->fulltextSearch->supportsFilters() && (!empty($dynamicFilters) || findModule(\Modules::SEARCH, \Modules::SUB_FILTER))) {
|
||||
$productsNoOnPage = 1000;
|
||||
$productsOffset = 0;
|
||||
$productsOrder = null;
|
||||
}
|
||||
|
||||
$config = [];
|
||||
foreach ($this->fulltextSearch->getIndexTypes() as $type) {
|
||||
$config[$type] = [
|
||||
'count' => 20,
|
||||
'offset' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$config[FulltextElastic::INDEX_PRODUCTS] = [
|
||||
'count' => (int) $productsNoOnPage,
|
||||
'offset' => (int) $productsOffset,
|
||||
'order' => $productsOrder,
|
||||
'exact' => getVal('exact') ? true : false,
|
||||
];
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
protected function isTopicEnabled($topic): bool
|
||||
{
|
||||
if (!in_array($topic, $this->enabledTopics)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->topicsModules[$topic] ?? [] as $module) {
|
||||
if (findModule($module)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return empty($this->topicsModules[$topic]);
|
||||
}
|
||||
|
||||
protected function setEnabledTopics($topics)
|
||||
{
|
||||
$this->enabledTopics = $topics;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getProductList(): ProductList
|
||||
{
|
||||
$productList = clone $this->productList;
|
||||
|
||||
// TODO: Direct copy from class.Category - split class.Category to View and "FilteredProductList" and use FilteredProductList here
|
||||
// start
|
||||
$productList->fetchImages(2);
|
||||
$productList->fetchProducers();
|
||||
|
||||
// Variations in product list
|
||||
$variations = findModule('products_variations', 'category_variations');
|
||||
if ($variations) {
|
||||
$productList->fetchVariations($variations);
|
||||
}
|
||||
|
||||
// Parameters in product list
|
||||
$parameters = findModule('products_parameters', 'category_params');
|
||||
if ($parameters) {
|
||||
$productList->fetchParameters($parameters);
|
||||
}
|
||||
|
||||
// Product sets in product list
|
||||
if (findModule('products_sets')) {
|
||||
$productList->fetchSets();
|
||||
}
|
||||
|
||||
// Product sets in product list
|
||||
if (findModule('reviews', 'show_in_category')) {
|
||||
$productList->fetchReviews();
|
||||
}
|
||||
// end
|
||||
|
||||
return $productList;
|
||||
}
|
||||
|
||||
protected function searchProductsByIds()
|
||||
{
|
||||
$productList = $this->getProductList();
|
||||
$products = $this->multiSearchResult[FulltextElastic::INDEX_PRODUCTS] ?? [];
|
||||
$productList
|
||||
->andSpec(function (QueryBuilder $qb) use ($products) {
|
||||
$qb->setParameter('products', array_keys($products), \Doctrine\DBAL\Connection::PARAM_INT_ARRAY)
|
||||
->andWhere('p.id IN (:products)');
|
||||
});
|
||||
|
||||
$noProducts = $this->fulltextSearch->getRowsCount();
|
||||
|
||||
return [
|
||||
'list' => $productList->getProducts(),
|
||||
'pager' => $this->getPager($noProducts),
|
||||
];
|
||||
}
|
||||
|
||||
protected function getPager($noProducts): \Pager
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
$noOnPage = $dbcfg->cat_show_products_search ?: $dbcfg->cat_show_products;
|
||||
$page = getVal('page', null, 1);
|
||||
$noPages = ceil($noProducts / $noOnPage);
|
||||
|
||||
$param = [
|
||||
'URL' => 'launch.php',
|
||||
's' => 'search',
|
||||
'search' => $this->searchTerm,
|
||||
'submit' => 'ok',
|
||||
'so' => 'products',
|
||||
'ESCAPE' => 'NO',
|
||||
];
|
||||
if (isset($_GET['show'])) {
|
||||
$param['show'] = $_GET['show'];
|
||||
}
|
||||
$url = createScriptURL($param);
|
||||
$dynamic_filter = $this->request->get('dynamic_filter', []);
|
||||
if (!empty($dynamic_filter)) {
|
||||
$url .= '&'.http_build_query(['dynamic_filter' => $dynamic_filter]);
|
||||
}
|
||||
|
||||
$pager = new \Pager($noPages, $page);
|
||||
$pager->setUrl($url);
|
||||
$pager->setTotal($noProducts, $noOnPage);
|
||||
|
||||
if (!isAjax() && $noOnPage == 0 && $page != 1) {
|
||||
throw new NotFoundHttpException('Page \''.$page.'\' is out of range.');
|
||||
}
|
||||
|
||||
return $pager;
|
||||
}
|
||||
|
||||
protected function searchProducts()
|
||||
{
|
||||
$noProducts = null;
|
||||
$dbcfg = \Settings::getDefault();
|
||||
$orderBy = getVal('orderby', null, getVal('order_by'));
|
||||
$orderDir = getVal('orderdir', null, getVal('order_dir'));
|
||||
|
||||
$order = (int) getVal('order');
|
||||
if ($order) {
|
||||
$orderBy = abs($order);
|
||||
$orderDir = $order >= 0 ? 1 : 2;
|
||||
}
|
||||
|
||||
// ProductList
|
||||
$page = getVal('page', null, 1);
|
||||
$noOnPage = $dbcfg->cat_show_products_search ?: $dbcfg->cat_show_products;
|
||||
$dynamic_filter = $this->request->get('dynamic_filter', []);
|
||||
|
||||
$productList = $this->getProductList();
|
||||
$filterParams = $productList->applyDefaultFilterParams();
|
||||
|
||||
$this->eventDispatcher->dispatch(
|
||||
new CatalogEvent($productList, $filterParams, CatalogEvent::TYPE_SEARCH)
|
||||
);
|
||||
|
||||
// Show both hidden and not in store products to administrators
|
||||
if ($dbcfg->prod_show_admin_hidden == 'Y' && getAdminUser()) {
|
||||
$filterParams->setVisible(null);
|
||||
$filterParams->setInStore(null);
|
||||
}
|
||||
|
||||
// HACK: Když chce admin vidět ve vyhledávání i skrytý produkty, nepoužiju fulltext ale staré hledání
|
||||
if (!($dbcfg->prod_show_admin_hidden == 'Y' && getAdminUser())) {
|
||||
$products = $this->multiSearchResult[FulltextElastic::INDEX_PRODUCTS] ?? [];
|
||||
|
||||
if (!empty($dynamic_filter) || findModule(\Modules::SEARCH, \Modules::SUB_FILTER)) {
|
||||
$filter = new \Filter($filterParams);
|
||||
$filter->setFilterData($dynamic_filter);
|
||||
if (!empty($dynamic_filter)) {
|
||||
$this->setEnabledTopics(['products']);
|
||||
}
|
||||
$result['filter'] = $filter;
|
||||
|
||||
$filterParams->andSpec(function (QueryBuilder $qb) use ($products) {
|
||||
$qb->andWhere(Product::productsIds(array_keys($products)));
|
||||
});
|
||||
|
||||
if ($orderBy) {
|
||||
// Sort by user defined sort
|
||||
$sortParams = $this->getSortParams($orderBy, $orderDir, $productList);
|
||||
$productList->orderBy($sortParams['orderby'], $sortParams['orderdir']);
|
||||
} else {
|
||||
// Sort by fulltext
|
||||
$productList->orderBy($this->getOrderBy());
|
||||
}
|
||||
|
||||
$productList
|
||||
->limit($noOnPage, ceil(($page - 1) * $noOnPage));
|
||||
|
||||
$result['list'] = $productList->getProducts($noProducts);
|
||||
} else {
|
||||
$noProducts = $this->fulltextSearch->getRowsCount();
|
||||
if (!$noProducts) {
|
||||
$result['suggestion'] = $this->fulltextSearch->suggestTerm($this->searchTerm);
|
||||
if ($result['suggestion']) {
|
||||
$products = $this->fulltextSearch->searchProducts($result['suggestion'], $noOnPage, ceil(($page - 1) * $noOnPage), $this->getProductsOrder());
|
||||
$noProducts = $this->fulltextSearch->getRowsCount();
|
||||
}
|
||||
}
|
||||
|
||||
$productList
|
||||
->andSpec(function (QueryBuilder $qb) use ($products) {
|
||||
$qb->setParameter('products', array_keys($products), \Doctrine\DBAL\Connection::PARAM_INT_ARRAY)
|
||||
->andWhere('p.id IN (:products)');
|
||||
})
|
||||
->orderBy('FIELD(p.id, :products)')
|
||||
->limit($noOnPage);
|
||||
|
||||
$result['list'] = $productList->getProducts($noProductsReal);
|
||||
// Pokud mám vrácených položek z fulltextu jen na jednu stránku, můžu zobrazit reálný počet produktů
|
||||
// (ponížených například o skryté produkty stále v indexu a podobně)
|
||||
if ($noProducts < $noOnPage) {
|
||||
$noProducts = $noProductsReal;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// HACK: Když chce admin vidět ve vyhledávání i skrytý produkty, nepoužiju fulltext ale staré hledání
|
||||
$sortParams = $this->getSortParams($orderBy, $orderDir, $productList);
|
||||
|
||||
// Ordering by positions and in_store
|
||||
if ($dbcfg->prod_subtract_from_store) {
|
||||
$sortParams['order_start'] = 'IF(p.in_store > 0, COALESCE(p.position, '.POSITION_STANDARD.'), COALESCE(p.position, '.POSITION_STANDARD.') + 10000)';
|
||||
} else {
|
||||
$sortParams['order_start'] = 'COALESCE(p.position, '.POSITION_STANDARD.')';
|
||||
}
|
||||
|
||||
$sortParams['orderby'] = "{$sortParams['order_start']}, {$sortParams['orderby']}";
|
||||
|
||||
// In store
|
||||
if (getVal('filter_insupplier') || getVal('inSupplier', $dynamic_filter)) {
|
||||
$filterParams->setInStore(\Filter::IN_STORE_SUPPLIER);
|
||||
} elseif (getVal('filter_instore') || getVal('inStore', $dynamic_filter)) {
|
||||
$filterParams->setInStore(\Filter::IN_STORE);
|
||||
}
|
||||
|
||||
$fields = [
|
||||
['field' => 'p.title', 'match' => 'both', 'order' => true],
|
||||
['field' => 'p.short_descr', 'match' => 'both'],
|
||||
['field' => 'p.code', 'match' => 'left', 'order' => true],
|
||||
['field' => 'pr.name', 'match' => 'both'],
|
||||
];
|
||||
|
||||
$productList->andSpec(function (QueryBuilder $qb) {
|
||||
$qb->joinProducersOnProducts();
|
||||
});
|
||||
|
||||
if (is_numeric($this->searchTerm)) {
|
||||
$fields[] = ['field' => 'p.ean', 'match' => 'numeric', 'order' => true];
|
||||
|
||||
if (findModule(\Modules::PRODUCTS_VARIATIONS)) {
|
||||
$fields[] = ['field' => 'pv.ean', 'match' => 'numeric', 'order' => true];
|
||||
}
|
||||
}
|
||||
|
||||
if (findModule(\Modules::PRODUCTS_VARIATIONS, 'variationCode')) {
|
||||
$fields[] = ['field' => 'pv.code', 'match' => 'left', 'order' => true];
|
||||
}
|
||||
|
||||
$productList->andSpec(function (QueryBuilder $qb) use ($sortParams, $dbcfg) {
|
||||
$qb->addOrderBy($sortParams['orderby'], $sortParams['orderdir']);
|
||||
|
||||
if (!($dbcfg->prod_show_admin_hidden == 'Y' && getAdminUser())) {
|
||||
return Operator::equals(['p.show_in_search' => 'Y']);
|
||||
}
|
||||
})->andSpec(Search::searchFields($this->searchTerm, $fields));
|
||||
|
||||
$productList->limit($noOnPage, ceil(($page - 1) * $noOnPage));
|
||||
|
||||
$result['list'] = $productList->getProducts($noProducts);
|
||||
}
|
||||
|
||||
// #####################################################
|
||||
|
||||
if (!empty($dynamic_filter)) {
|
||||
$result['dynamic_filter'] = $dynamic_filter;
|
||||
}
|
||||
|
||||
$result['pager'] = $this->getPager($noProducts);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getOrderBy(): string
|
||||
{
|
||||
return 'FIELD(p.id, :products)';
|
||||
}
|
||||
|
||||
protected function searchPages()
|
||||
{
|
||||
$fields = [
|
||||
['field' => 'ml.name', 'match' => 'both'],
|
||||
['field' => 'b.content', 'match' => 'both'],
|
||||
];
|
||||
|
||||
$search = get_search_query($this->searchTerm, $fields);
|
||||
|
||||
$qb = sqlQueryBuilder()
|
||||
->select('ml.id')
|
||||
->from('menu_links', 'ml')
|
||||
->join('ml', 'blocks', 'b', 'b.id_root = ml.id_block')
|
||||
->where($search['where'])
|
||||
->andWhere(Operator::equals(['ml.type' => MenuUtil::TYPE_PAGE]))
|
||||
->andWhere(Translation::coalesceTranslatedFields(
|
||||
MenuLinksTranslation::class,
|
||||
['name', 'url']
|
||||
))
|
||||
->andWhere(Operator::Not(Operator::equals(['ml.figure' => 'N'])))
|
||||
->andWhere(Operator::Not(Operator::equals(['ml.show_in_search' => 'N'])))
|
||||
->addParameters($search['data'])
|
||||
->groupBy('ml.id')
|
||||
->execute();
|
||||
|
||||
$result = sqlFetchAll($qb);
|
||||
|
||||
foreach ($result as &$page) {
|
||||
$page['url'] = '/'.$page['url'];
|
||||
}
|
||||
|
||||
return [
|
||||
'list' => $result,
|
||||
];
|
||||
}
|
||||
|
||||
protected function searchArticles()
|
||||
{
|
||||
$result['list'] = [];
|
||||
$result['pager'] = [];
|
||||
|
||||
$searchResult = $this->multiSearchResult[FulltextElastic::INDEX_ARTICLES] ?? [];
|
||||
if (!empty($searchResult)) {
|
||||
$articleIds = array_map(function ($x) {
|
||||
return $x['id'];
|
||||
}, $searchResult);
|
||||
/** @var ArticleList $articleList */
|
||||
$articleList = ServiceContainer::getService(ArticleList::class);
|
||||
|
||||
$result['list'] = $articleList->getArticles(function (\Query\QueryBuilder $qb) use ($articleIds) {
|
||||
$qb->andWhere(Operator::inIntArray($articleIds, 'a.id'))
|
||||
->setParameter('articles', $articleIds, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY)
|
||||
->addOrderBy('FIELD(a.id, :articles)');
|
||||
});
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function getListShowType()
|
||||
{
|
||||
if (!empty($_COOKIE['cat_show']) && empty($_GET['show'])) {
|
||||
$_GET['show'] = $_COOKIE['cat_show'];
|
||||
} else {
|
||||
if (empty($_GET['show']) || !preg_match('/^[[:digit:]]{1}$/i', $_GET['show'])) {
|
||||
$_GET['show'] = \Settings::getDefault()->cat_show_style;
|
||||
} else {
|
||||
SetCookies('cat_show', $_GET['show'], 86400);
|
||||
}
|
||||
}
|
||||
|
||||
return $_GET['show'];
|
||||
}
|
||||
|
||||
protected function getProductsOrder(): ?string
|
||||
{
|
||||
$orderBy = getVal('orderby', null, getVal('order_by'));
|
||||
$orderDir = getVal('orderdir', null, getVal('order_dir'));
|
||||
|
||||
$order = null;
|
||||
switch ($orderBy) {
|
||||
case 1:
|
||||
$order = 'code';
|
||||
break;
|
||||
case 2:
|
||||
$order = 'title';
|
||||
break;
|
||||
case 3:
|
||||
$order = 'price';
|
||||
break;
|
||||
case 5:
|
||||
$order = 'sold';
|
||||
break;
|
||||
}
|
||||
|
||||
if ($order && $orderDir == 2) {
|
||||
$order = '-'.$order;
|
||||
}
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setFulltextSearch(FulltextInterface $fulltextSearch): SearchView
|
||||
{
|
||||
$this->fulltextSearch = $fulltextSearch;
|
||||
$this->fulltextSearch->setCurlTimeout(10);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSortParams($orderBy, $orderDir, ProductList $productList): array
|
||||
{
|
||||
switch ($orderBy) {
|
||||
case 1:
|
||||
$result['orderby'] = 'p.code';
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$result['orderby'] = 'p.title';
|
||||
break;
|
||||
|
||||
case 3:
|
||||
$productList->andSpec(function (QueryBuilder $qb) {
|
||||
$qb->joinVatsOnProducts();
|
||||
});
|
||||
$result['orderby'] = 'MIN(COALESCE(pv.price, p.price))*(1+v.vat/100)*(1-p.discount/100)';
|
||||
break;
|
||||
|
||||
case 4:
|
||||
$result['orderby'] = 'p.date_added';
|
||||
break;
|
||||
|
||||
case 5:
|
||||
$result['orderby'] = 'p.pieces_sold';
|
||||
break;
|
||||
|
||||
case 6:
|
||||
$result['orderby'] = 'p.updated';
|
||||
break;
|
||||
|
||||
case 7:
|
||||
$result['orderby'] = 'p.in_store > 0 DESC';
|
||||
|
||||
// if (findModule('products_suppliers'))
|
||||
// $query['orderby'] .= ", in_store_suppliers DESC";
|
||||
|
||||
$result['orderby'] .= ', p.title';
|
||||
break;
|
||||
|
||||
default:
|
||||
$result['orderby'] = 'p.title';
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($orderDir) {
|
||||
case 1:
|
||||
$result['orderdir'] = ' ASC ';
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$result['orderdir'] = ' DESC ';
|
||||
break;
|
||||
|
||||
default:
|
||||
$result['orderdir'] = ' ASC ';
|
||||
break;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getFilters(): array
|
||||
{
|
||||
return $this->fulltextSearch->getFilters();
|
||||
}
|
||||
|
||||
public function getCorrectUrl(): ?string
|
||||
{
|
||||
$urlCorrect = createScriptURL([
|
||||
'URL' => 'launch.php',
|
||||
's' => 'search',
|
||||
'search' => $this->searchTerm,
|
||||
]);
|
||||
|
||||
return $urlCorrect;
|
||||
}
|
||||
}
|
||||
41
bundles/KupShop/CatalogBundle/View/SimpleProductListView.php
Normal file
41
bundles/KupShop/CatalogBundle/View/SimpleProductListView.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\CatalogBundle\View;
|
||||
|
||||
class SimpleProductListView extends CategoryView
|
||||
{
|
||||
protected $template = 'products.list.tpl';
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return translate('title', 'category')['productsList'] ?? '';
|
||||
}
|
||||
|
||||
public function getMetaTitle()
|
||||
{
|
||||
return translate('title', 'category')['productsList'] ?? '';
|
||||
}
|
||||
|
||||
public function getBreadcrumbs()
|
||||
{
|
||||
return getReturnNavigation(-1, 'NO_TYPE', [translate('title', 'category')['productsList'] ?? '']);
|
||||
}
|
||||
|
||||
public function getBodyVariables()
|
||||
{
|
||||
return array_merge_recursive(
|
||||
parent::getBodyVariables(),
|
||||
['category' => ['is_simple' => true]]
|
||||
);
|
||||
}
|
||||
|
||||
protected function getPagerOptions(): array
|
||||
{
|
||||
return [
|
||||
'noOnPage' => 9999,
|
||||
'page' => 1, 9,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\CatalogBundle\View\Wrapper;
|
||||
|
||||
use KupShop\KupShopBundle\Template\ObjectWrapper;
|
||||
|
||||
class FilterParamsWrapper extends ObjectWrapper
|
||||
{
|
||||
/** @var \FilterParams */
|
||||
protected $object;
|
||||
|
||||
protected static $objectType = \FilterParams::class;
|
||||
|
||||
public function isSelectedSectionByLevel(
|
||||
?int $ancestorID = null,
|
||||
int $level,
|
||||
?int $sectionIDToCheck = null,
|
||||
) {
|
||||
$sectionsByLevel = $this->object->getSectionsByLevel();
|
||||
$sections = isset($sectionsByLevel[$ancestorID][$level]) ? $sectionsByLevel[$ancestorID][$level] : [];
|
||||
|
||||
return is_null($sectionIDToCheck) && count($sections) == 0 || in_array($sectionIDToCheck, $sections);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user