<?php
namespace EshopBundle\DependencyInjection\Shopping;
use EshopBundle\Interfaces\ProductListInterface;
use EshopBundle\Entity\Category;
use EshopBundle\Entity\ActionOffer;
use EshopBundle\Entity\Feature;
use EshopBundle\Entity\Parameters;
use EshopBundle\Entity\ParametersValues;
use EshopBundle\Entity\Product;
use EshopBundle\Entity\ProductTypeFeature;
use EshopBundle\Model\ProductList;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\HttpFoundation\Request;
use EshopBundle\Interfaces\SeoFieldsInterface;
use Doctrine\ORM\Query\ResultSetMapping;
/**
* Nakupni asistent eshopu
* @author DJ
*/
class Assistant implements ProductListInterface
{
const DEFAULT_VISIBLE_BRANDS = 111;
use Traits\AssistantTrait {
#Traits\AssistantTrait::getLink AS private tGetLink;
Traits\AssistantTrait::initByRequest AS private tInitByRequest;
Traits\AssistantTrait::getActualParams AS private tGetActualParams;
Traits\AssistantTrait::getParams AS private tGetParams;
}
/**
* @var SeoFieldsInterface
*/
private $customPageEntity;
/**
* @return SeoFieldsInterface
*/
public function getCustomPageEntity()
{
return $this->customPageEntity;
}
/**
* @param SeoFieldsInterface $customPageEntity
*
* @return self
*/
public function setCustomPageEntity(SeoFieldsInterface $customPageEntity)
{
$this->customPageEntity = $customPageEntity;
return $this;
}
/**
* @param string $sortType
*
* @return self
*/
public function setSortType($sortType)
{
$this->sortType = $sortType;
return $this;
}
/**
* Vraci seznam stitku akce, novinka, vyprodej, top a baraz a pocty.
*
* @return array
*/
public function getProductTags()
{
$freeDeliveryWithVat = $this->getCurrencyOptions() ? $this->getCurrencyOptions()->freeDeliveryLimit : false;
$freeDelivery = $this->getFreeDeliveryVat($freeDeliveryWithVat);
$slot = $this->productList->getPriceSlot();
$builder = $this->productList
->build(array('allTags' => true))
->select(
'SUM(case when p.tagAction = \'t\' then 1 else 0 end) / COUNT(p.id), ' .
'SUM(case when p.tagNew = \'t\' then 1 else 0 end) / COUNT(p.id), ' .
'SUM(case when p.tagSale = \'t\' then 1 else 0 end) / COUNT(p.id), ' .
'SUM(case when p.tagFreeDelivery = \'t\' then 1 else ' .
($freeDelivery ? 'CASE WHEN p.' . $slot . 'Min > ' . $freeDelivery . ' THEN 1 ELSE 0 END' : '0') .
' end) / COUNT(p.id), ' .
'SUM(case when p.tagTop = \'t\' then 1 else 0 end) / COUNT(p.id), ' .
'SUM(case when p.tagBazaar = \'t\' then 1 else 0 end) / COUNT(p.id)'
)
->groupBy('p.id');
$sql = //vypnuti... (docasne?)
/*. 'SUM(sclr_3) AS tagFreeDelivery, '*/ 'SELECT SUM(sclr_0) AS tagAction, ' .
'SUM(sclr_1) AS tagNew, ' .
'SUM(sclr_2) AS tagSale, ' .
'IF(SUM(sclr_3)>0, -1 , 0) AS tagFreeDelivery, ' .
'SUM(sclr_4) AS tagTop, ' .
'SUM(sclr_5) AS tagBazaar ' .
'FROM (' .
$builder->getQuery()->getSQL() .
') AS tmp';
$em = $this->container->get('doctrine')->getManager();
$rsm = new ResultSetMapping();
$rsm->addScalarResult('tagAction', 'tagAction');
$rsm->addScalarResult('tagNew', 'tagNew');
$rsm->addScalarResult('tagSale', 'tagSale');
$rsm->addScalarResult('tagFreeDelivery', 'tagFreeDelivery');
$rsm->addScalarResult('tagTop', 'tagTop');
$rsm->addScalarResult('tagBazaar', 'tagBazaar');
$query = $em->createNativeQuery($sql, $rsm);
$parameters = $builder->getParameters();
foreach ($parameters as $i => $param) {
$query->setParameter($i, $param->getValue());
}
if ($this->cacheTtl) {
$query->useResultCache(true, $this->cacheTtl);
}
$tags = array();
$catLink = $this->category->getLink();
if ($result = $query->getSingleResult()) {
$translator = $this->container->get('eshop_config.translator');
$indexes = array(
'tagAction' => $translator->getValue('ASS_ACTION'),
'tagNew' => $translator->getValue('ASS_NEW'),
'tagSale' => $translator->getValue('ASS_SALE'),
'tagTop' => $translator->getValue('BESTSELLING'),
// 'tagFreeDelivery' => $translator->getValue('FREE_DELIVERY'),
'tagBazaar' => $translator->getValue('ASS_BAZAAR'),
);
foreach ($indexes as $index => $name) {
$tmp = new \stdClass();
$tmp->id = $index;
$tmp->name = $name;
$tmp->checked = $this->productList->getTag($index);
$tmp->cnt = intval($result[$index]);
$tmp->link = $catLink . '?' . $index . '=1';
$tags[] = $tmp;
}
}
return $tags;
}
/**
* Vraci aktualni nastaveni filtru
* Box s aktuálně vybranými parametry nad výpisem
*
* @return array
*/
public function getActualParams()
{
$translator = $this->container->get('eshop_config.translator');
$items = $this->tGetActualParams();
if ($items && empty(end($items)['id'])) {
array_pop($items);
}
if ($parameters = $this->productList->getSelectedParameters()) {
foreach ($parameters as $parameterId => $values) {
$p = $this->container
->get('doctrine')
->getManager()
->getRepository('EshopBundle:Parameters')
->findOneById($parameterId);
// pokud náhodou příjde neexistující parametr tak aby to nespadlo do 500
if ($p === null) {
continue;
}
if ($p->isNumericParameter()) {
if ($p->getFilterMethod() === "range") {
if ($values['rMin'] == $values['min'] && $values['rMax'] == $values['max']) {
continue;
}
$items[] = array(
'id' => 'p-' . $parameterId,
'name' => $this->getParametersRangeName($p, $values),
'link' => $this->getLink($this->getSortType(), $this->getListType(), 'p-' . $parameterId),
);
} else {
foreach ($values as $val) {
$items[] = array(
'id' => 'p-' . $parameterId . '-' . $val,
'name' => $val,
'link' => $this->getLink($this->getSortType(), $this->getListType(), 'p-' . $parameterId, $val),
);
}
}
} else {
foreach ($values as $val) {
$items[] = array(
'id' => 'p-' . $parameterId . '-' . $val,
'name' => $this->getParameterValueNameById($val),
'link' => $this->getLink($this->getSortType(), $this->getListType(), 'p-' . $parameterId, $val),
);
}
}
}
}
# Reset all btn - na začátek
if (count($items) > 1 && $this->category) {
array_unshift($items, [
'id' => '',
'name' => $translator->getValue('ASSISTANT_FILTER_RESET'),
'link' => $this->category->getLink(),
]);
}
return $items;
}
/**
* Inicializace parametru nakupniho pomocnika na zaklade requestu.
*
* @param Request $request
*
* @return self
*/
public function initCustomPageListByRequest(Request $request)
{
$this->productList
->setInStock($request->get('inStock', false))
->setTagAction($request->get('tagAction', false))
->setTagNew($request->get('tagNew', false))
->setTagSale($request->get('tagSale', false))
->setTagTop($request->get('tagTop', false))
->setTagBazaar($request->get('tagBazaar', false))
->setTagFreeDelivery($request->get('tagFreeDelivery', false))
->setCustom($request->get('custom'));
if ($brand = $request->get('brand', 0)) {
$this->productList->addBrand($brand);
}
if (($brands = $request->get('brands', 0)) && is_array($brands)) {
foreach ($brands as $brand) {
$this->productList->addBrand($brand);
}
}
$this->allBrands = $request->get('allBrands', 0);
$config = $this->container->get('eshop_config.client_config');
if ($config->isTrue('PROD_COMPARABLE_FEATURES')) {
$features = $request->get('f');
if (is_array($features)) {
$this->productList->setFeatures($features);
}
}
if ($config->isTrue('PRODUCT_CATALOG_TYPES')) {
$types = $request->get('t');
if (is_array($types)) {
$this->productList->setTypes($types);
}
}
$this->closed = $request->get('closed');
if ($listType = $request->get('list')) {
$this->setListType($listType);
}
if ($sortType = $request->get('sort')) {
$this->setSortType($sortType);
}
$parameters = $request->get('p');
if (is_array($parameters)) {
$this->productList->setSelectedParameters($parameters);
}
return $this;
}
/**
* Inicializace parametru nakupniho pomocnika na zaklade requestu.
*
* @param Request $request
*
* @return self
*/
public function initByRequest(Request $request)
{
$this->tInitByRequest($request);
$parameters = $request->get('p');
if (is_array($parameters)) {
$this->productList->setSelectedParameters($parameters);
}
return $this;
}
/**
* Vraci aktualni link dle aktualnich parametru asistenta.
*
* @param string $sortType
* @param string $listType
*
* @return string
*/
public function getLink($sortType = null, $listType = null, $exclude = null, $excludeId = null)
{
if ($this->customPageEntity) {
$link = $this->customPageEntity->getLink();
} elseif ($this->category) {
$link = $this->category->getLink();
} elseif ($this->actionOffer) {
$link = $this->actionOffer->getLink();
} elseif ($this->brand instanceof Brand) {
$link = $this->brand->getLink();
} else {
$link = '';
}
$params = array();
// znacka
$brands = $this->productList->getBrands();
$brandsCnt = count($brands);
if ($brandsCnt == 1) {
if ($exclude != 'brands' || $excludeId != $brands[0]) {
$brand = $this->container
->get('doctrine')
->getRepository('EshopBundle:Brand')
->find($brands[0]);
if ($this->brand instanceof Brand && $this->brand->getId() === $brands[0]) {
// Aby se pro brand nepřidával tag do url už je v klasické url obsazen
} elseif ($this->category === null || !$this->linkExists($this->category->getId(), $brands[0])) {
$tmp[] = $brands[0];
} elseif (!is_null($brand) && $this->linkExists($this->category->getId(), $brands[0])) {
$link = $this->getCategoryBrandLink($this->category->getId(), $brands[0])->getUri();
} elseif (!is_null($brand)) {
$link .= '/' . $brand->getSlug();
}
}
} elseif ($brandsCnt > 1) {
$tmp = array();
foreach ($brands as $brand) {
if ($exclude != 'brands' || $excludeId != $brand) {
$tmp[] = $brand;
}
}
}
if (!empty($tmp)) {
$params['brands'] = $tmp;
}
// stitky
if (empty($this->actionOffer)) {
if ($exclude != 'inStock' && $this->productList->getInStock()) {
$params['inStock'] = 1;
}
if ($exclude != 'tagAction' && $this->productList->getTagAction()) {
$params['tagAction'] = 1;
}
if ($exclude != 'tagNew' && $this->productList->getTagNew()) {
$params['tagNew'] = 1;
}
if ($exclude != 'tagSale' && $this->productList->getTagSale()) {
$params['tagSale'] = 1;
}
if ($exclude != 'tagTop' && $this->productList->getTagTop()) {
$params['tagTop'] = 1;
}
if ($exclude != 'tagBazaar' && $this->productList->getTagBazaar()) {
$params['tagBazaar'] = 1;
}
if ($exclude != 'tagFreeDelivery' && $this->productList->getTagFreeDelivery()) {
$params['tagFreeDelivery'] = 1;
}
}
// hodnoceni
if ($exclude != 'rating' && ($rating = $this->productList->getRating())) {
$params['rating'] = $rating;
}
// cena min
if ($exclude != 'priceMin' && ($priceMin = $this->productList->getPriceMin())) {
$params['priceMin'] = $priceMin;
}
// cena max
if ($exclude != 'priceMax' && ($priceMax = $this->productList->getPriceMax())) {
$params['priceMax'] = $priceMax;
}
// parametry
if ($features = $this->productList->getFeatures()) {
$tmp = array();
foreach ($features as $feature => $values) {
if ($exclude != 'f-' . $feature || $excludeId !== null) {
foreach ($values as $key => $val) {
if ($exclude != 'f-' . $feature || $excludeId != $val) {
$tmp[$feature][$key] = $val;
}
}
}
}
if (!empty($tmp)) {
$params['f'] = $tmp;
}
}
// vlastnosti typu
if ($types = $this->productList->getTypes()) {
$tmp = array();
foreach ($types as $type => $values) {
foreach ($values as $val) {
if ($exclude != 't-' . $type || $excludeId != $val) {
$tmp[$type][] = $val;
}
}
}
if (!empty($tmp)) {
$params['t'] = $tmp;
}
}
// custom filtr
if ($custom = $this->productList->getCustom()) {
if (is_array($custom)) {
$tmp = array();
foreach ($custom as $c) {
if ($exclude != 'custom' || $excludeId != $c) {
$tmp[] = $c;
}
}
if (!empty($tmp)) {
$params['custom'] = $tmp;
}
} elseif ($exclude != 'custom') {
$params['custom'] = $custom;
}
}
// zpusob razeni
if ($sortType) {
if ($sortType != $this->defaultSortType) {
$params['sort'] = $sortType;
}
} elseif ($this->sortType && $this->sortType != $this->defaultSortType) {
$params['sort'] = $this->sortType;
}
// typ vypisu
if ($listType) {
if ($listType != $this->defaultListType) {
$params['list'] = $listType;
}
} elseif ($this->listType && $this->listType != $this->defaultListType) {
$params['list'] = $this->listType;
}
// parametry
if ($parameters = $this->productList->getSelectedParameters()) {
$tmp = array();
foreach ($parameters as $parameterId => $values) {
if ($exclude != 'p-' . $parameterId || $excludeId !== null) {
foreach ($values as $key => $val) {
if ($exclude != 'p-' . $parameterId || $excludeId != $val) {
$tmp[$parameterId][$key] = $val;
}
}
}
}
if (!empty($tmp)) {
$params['p'] = $tmp;
}
}
if (!empty($params)) {
$link .= '?' . str_replace(array('%5B', '%5D'), array('[', ']'), http_build_query($params));
}
return $link;
}
/**
* Vraci aktualni nastaveni filtru.
*
* @return array
*/
public function getParams()
{
$params = $this->tGetParams();
$params['p'] = $this->productList->getSelectedParameters();
return $params;
}
/**
* Vraci seznam custom parametrů pro filtraci v asistentovi
*
* @return array
*/
public function getParameters()
{
$em = $this->container->get('doctrine')->getManager();
$catLink = $this->category->getLink();
# Nacteni vsech dostupnych filtračních parametrů v rámci dané kategorie
$builder = $em
->getRepository('EshopBundle:ProductParameters')
->createQueryBuilder('ppar')
->join('EshopBundle:Parameters', 'par', 'WITH', 'par.id = ppar.parameterId')
->join('EshopBundle:Product', 'p', 'WITH', 'p.id = ppar.product');
$builder = $this->productList->build([], $builder, true);
$builder->andWhere($builder->expr()->isNotNull('par.filterMethod'));
$builder->orderBy('par.sequence', 'ASC');
$query = $builder->getQuery();
if ($this->cacheTtl) {
$query->useResultCache(true, $this->cacheTtl);
}
$result = $query->getResult();
#dump($query->getSQL());die;
# Výstupní pole parametrů
$parameters = [];
$collator = new \Collator('cs_CZ'); // české locale
foreach ($result as $productParameter) {
$parameterId = $productParameter->getParameterId();
if (!isset($parameters[$parameterId])) {
$parameterEntity = $productParameter->getParameter();
$tmp = new \stdClass();
$tmp->id = $parameterId;
$tmp->name = $parameterEntity->getName();
$tmp->isNumeric = $parameterEntity->isNumericParameter();
$tmp->filter = $parameterEntity->getFilterMethod();
$tmp->unit = $parameterEntity->getUnit() ? $parameterEntity->getUnit()->getName() : null;
$tmp->values = [];
$parameters[$parameterId] = $tmp;
}
# Naplnění všemi values pro tuto kategorii
if ($value = $productParameter->getValue()) {
$tmp = new \stdClass();
$tmp->id = $value->getId();
$tmp->name = $value->getName();
$tmp->sequence = $value->getSequence();
$tmp->image = $value->imageExists() ? $value->getImageTnUri() : null;
$tmp->checked = false;
$tmp->disabled = true;
$tmp->link = $catLink . '?p[' . $parameterId . '][]=' . $value->getId();
$parameters[$parameterId]->values[$value->getId()] = $tmp;
} else {
$parameters[$parameterId]->values[] = $productParameter->getFloatValue();
}
}
foreach ($parameters as $parameterId => $parameterEnvelope) {
# Seřadíme values
usort($parameterEnvelope->values, function($a, $b) use ($collator) {
if ($a instanceof \stdClass) {
return $collator->compare($a->name, $b->name);
} else {
return $a - $b;
}
});
# Omezíme všechny values dle aktuálních podmínek filtru
if ($parameterEnvelope->isNumeric && $parameterEnvelope->filter === "range") {
$values = $this->getCurrentParameterRange($parameterEnvelope->id);
$values['overallMin'] = min($parameterEnvelope->values);
$values['overallMax'] = max($parameterEnvelope->values);
$parameterEnvelope->values = $values;
} elseif ($parameterEnvelope->isNumeric) {
# Ošetření pro stav kdy numerické zobrazujeme jako checkboxy
$normalizedValues = [];
foreach ($parameterEnvelope->values as $value) {
$tmp = new \stdClass();
$tmp->id = $value;
$tmp->name = $value;
$tmp->image = null;
$tmp->checked = false;
$tmp->disabled = true;
$tmp->link = $catLink . '?p[' . $parameterId . '][]=' . $value;
$normalizedValues[] = $tmp;
}
$parameterEnvelope->values = $normalizedValues;
$values = $this->getCurrentParameterFloatValues($parameterEnvelope->id);
$activeValues = $this->productList->getSelectedValuesByParameterId($parameterEnvelope->id);
array_walk(
$parameterEnvelope->values,
function($value) use ($values, $activeValues) {
$value->disabled = !in_array($value->id, $values);
$value->checked = in_array($value->id, $activeValues);
});
} else {
$values = $this->getCurrentParameterValues($parameterEnvelope->id);
$activeValues = $this->productList->getSelectedValuesByParameterId($parameterEnvelope->id);
array_walk(
$parameterEnvelope->values,
function($value) use ($values, $activeValues) {
$value->disabled = !array_key_exists($value->id, $values);
$value->checked = in_array($value->id, $activeValues);
});
}
}
return $parameters;
}
/**
* Načte aktuální numrické hodnoty na výběr daného parametru
*/
protected function getCurrentParameterRange($parameterId)
{
$em = $this->container->get('doctrine')->getManager();
$builder = $em
->getRepository('EshopBundle:ProductParameters')
->createQueryBuilder('ppar')
->join('EshopBundle:Product', 'p', 'WITH', 'p.id = ppar.product');
$builder = $this->productList->build(['parameter_'.$parameterId => true], $builder);
$builder->andWhere($builder->expr()->isNotNull('ppar.floatValue'));
$builder->select('(MIN(ppar.floatValue) + 0) AS rangeMin, (MAX(ppar.floatValue) + 0) AS rangeMax');
$query = $builder->getQuery();
if ($this->cacheTtl) {
$query->useResultCache(true, $this->cacheTtl);
}
$result = $query->getSingleResult();
$values = $this->productList->getSelectedValuesByParameterId($parameterId);
if (isset($values['min']) && $values['min'] > $result['rangeMin'] && $values['min'] < $result['rangeMax']) {
$result['selectedMin'] = (float) $values['min'];
} else {
$result['selectedMin'] = (float) $result['rangeMin'];
}
if (isset($values['max']) && $values['max'] > $result['rangeMin'] && $values['max'] < $result['rangeMax']) {
$result['selectedMax'] = (float) $values['max'];
} else {
$result['selectedMax'] = (float) $result['rangeMax'];
}
return $result;
}
/**
* Načte aktuální možnosti daného parametru
*/
protected function getCurrentParameterValues($parameterId)
{
$em = $this->container->get('doctrine')->getManager();
$builder = $em
->getRepository('EshopBundle:ProductParameters')
->createQueryBuilder('ppar')
->join('EshopBundle:Product', 'p', 'WITH', 'p.id = ppar.product');
$builder = $this->productList->build(['parameter_'.$parameterId => true], $builder);
$builder->andWhere($builder->expr()->eq('ppar.parameterId', $parameterId));
$builder->andWhere($builder->expr()->isNotNull('ppar.valueId'));
$result = $builder->getQuery()->getResult();
$values = [];
foreach ($result as $result) {
$valueEntity = $result->getValue();
$values[$valueEntity->getId()] = $valueEntity;
}
return $values;
}
/**
* Načte aktuální možnosti daného parametru float hodnot
*/
protected function getCurrentParameterFloatValues($parameterId)
{
$em = $this->container->get('doctrine')->getManager();
$builder = $em
->getRepository('EshopBundle:ProductParameters')
->createQueryBuilder('ppar')
->join('EshopBundle:Product', 'p', 'WITH', 'p.id = ppar.product');
$builder = $this->productList->build(['parameter_'.$parameterId => true], $builder);
$builder->andWhere($builder->expr()->eq('ppar.parameterId', $parameterId));
$builder->andWhere($builder->expr()->isNotNull('ppar.floatValue'));
$result = $builder->getQuery()->getResult();
$values = [];
foreach ($result as $result) {
$values[] = $result->getFloatValue();
}
return $values;
}
/**
* Helpers
*/
protected function getParametersRangeName(Parameters $feature, array $values)
{
$translator = $this->container->get('eshop_config.translator');
$text[] = "{$feature->getName()}:";
if ($values['rMin'] < $values['min']) {
$text[] =
$translator->getValue('OD', 'od') .
' ' .
$values['min'] .
(!empty($feature->getUnit()) ? " {$feature->getUnit()}" : '');
}
if ($values['rMax'] > $values['max']) {
$text[] =
$translator->getValue('Do', 'do') .
' ' .
$values['max'] .
(!empty($feature->getUnit()) ? " {$feature->getUnit()}" : '');
}
return implode(' ', $text);
}
private function getParameterValueNameById($id)
{
$value = $this->container
->get('doctrine')
->getRepository('EshopBundle:ParametersValues')
->find($id);
if ($value) {
return $value->getName();
} else {
return $id;
}
}
}