<?php
namespace App\Repository;
use App\Entity\PackageOrderItem;
use App\Entity\PackageRecipe;
use App\Entity\Shop;
use App\Entity\ShopHasCategories;
use App\Entity\Shoppingmall;
use App\Entity\ShoppingmallCounts;
use App\Entity\User;
use App\Helpers\OutletTypesHelper;
use App\Services\Package\PackageQueryBuilder;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface as TokenStorage;
use function array_key_first;
use function sprintf;
use function Symfony\Component\String\u;
class ShoppingMallRepository extends ServiceEntityRepository
{
use BaseRepository;
/**
* @var User
*/
protected $_user;
/**
* @var TokenStorage
*/
protected $_tokenStorage;
/**
* @var PackageQueryBuilder
*/
protected $_packageQueryBuilder;
public function __construct(ManagerRegistry $registry, $entityClass, TokenStorage $tokenStorage, PackageQueryBuilder $packageQueryBuilder)
{
$this->_tokenStorage = $tokenStorage;
$this->_packageQueryBuilder = $packageQueryBuilder;
parent::__construct($registry, $entityClass);
}
/**
* @param $id
* @return \App\Entity\Shoppingmall[]|\Doctrine\Common\Collections\Collection
*/
public function getByCity($id)
{
$query = $this->_em->createQuery(
'SELECT DISTINCT s.shoppingMall
FROM App\Entity\Shop s
WHERE s.city = :city_id'
);
$query->setParameter('city_id', $id);
return $query->getResult();
}
public function totalShoppingMalls($historyFilter)
{
$qb = $this->createQueryBuilder('shopping_mall');
$qb->select("count(distinct(shopping_mall.id))");
// if (isset($historyFilter['type'])) {
// if ($historyFilter['type'] == HistoryStoresType::HISTORY_CLOSED) {
// // Vybere id storu, ktere byly vybrane v dany cas
// $historyFilter['dateFrom'] = null;
// $historyFilter['dateTo'] = null;
// $shopIds = $this->getIdsByHistoryFilter($historyFilter);
//
// $qb->andWhere('shop._id IN (:shopIds)');
// $qb->setParameter(':shopIds', $shopIds);
//
// } elseif ($historyFilter['type'] == HistoryStoresType::HISTORY_NEW) {
// $qb->andWhere('shop.isNew = true');
// }
// }
return (int)$qb->getQuery()->getSingleScalarResult();
}
/**
* @param array $params
* @param \Symfony\Component\HttpFoundation\Session\Session $session
* @return array
*/
public function findByFilter(array $params = [], Session $session) {
$session->remove('activeCity');
$session->remove('activeCountry');
$session->remove('activeActive');
$start = $params['start'] ?? null;
$length = $params['length'] ?? null;
$orders = $params['orders'] ?? [];
$columns = $params['columns'] ?? [];
$otherConditions = $params['otherConditions'] ?? '';
$withPaginator = $params['paginator'] ?? false;
$historyFilter = $params['historyFilter'] ?? [];
// Create Main Query
//$query = $this->createQueryBuilder('shop');
$query = $this->createQueryBuilder('shopping_mall')
// However, add a "always true" condition to keep an uniform treatment in all cases
// Add condition
->andwhere($otherConditions != '' ? $otherConditions : "1=1");
// Create inner joins
$query->leftJoin('shopping_mall.city', 'city');
$query->leftJoin('shopping_mall.operator', 'operator');
foreach ($columns as $key => $column) {
$values = [];
$prohibitedCols = ['updatedAt', 'createdAt'];
if ($column['search']['value'] != '' && $column['search']['value'] != '[]' && in_array($column['name'], $prohibitedCols) == false) {
$searchItem = $column['search']['value'];
if($column['name'] == 'kinds'){
continue;
}
$subquery = null;
$searchQueryLikeOrEq = '';
$searchValue = null;
$searchQueryPatterns = [
'label' => 'shopping_mall.label',
'brands' => 'shopping_mall.id IN ('. $subquery .')',
'stores' => 'shopping_mall.id IN ('. $subquery .')',
'kinds' => 'shopkinds.label',
'city' => 'city.label',
'country' => 'city.country',
'zip' => 'shopping_mall.zip',
'street' => 'shopping_mall.street',
'shoppingmall' => 'shopping_mall.label',
'extent_label' => 'shop.extentLabel',
'shoppingarea' => 'shoppingarea.label',
'categories' => 'category.label',
'isActive' => 'shopping_mall.isActive',
'isOutlet' => 'shopping_mall.isOutlet',
'operator' => 'operator.name',
'category_primary' => '(shophascategory.isPrimary = true AND category.label LIKE \'%'.$searchItem.'%\')',
];
if(substr($searchItem,0,2) == '["' && $column['name'] == 'brands') {
// json values
$values = json_decode($column['search']['value'], true);
$subquery = $this->_em->getRepository(Shop::class)->createQueryBuilder('s1');
$subquery
->select('shoppingMall_s1.id')
->leftJoin('s1.brands', 'brands_s1')
->leftJoin('s1.shoppingMall', 'shoppingMall_s1')
->where('brands_s1.label IN (:values'.$key.') ');
$subquery->setParameter('values'.$key, $values);
} else if(substr($searchItem,0,2) == '["') {
$values = json_decode($column['search']['value'], true);
//$searchQueryLikeOrEq = ' IN (:values' . $key . ') ';
} else if($column['name'] == 'stores'){
$subquery = $this->_em->getRepository(Shop::class)->createQueryBuilder('s2');
$subquery
->select('shoppingMall_s2.id')
->leftJoin('s2.shoppingMall', 'shoppingMall_s2');
if(strpos($column['search']['value'], 'eq:') !== false) {
$paramKey = uniqid('stores_');
$subquery->where(
$subquery
->expr()
->like('s2.name',
':' . $paramKey
)
);
$subquery->setParameter($paramKey, '%' . str_replace('eq:', '', $searchItem) . '%');
} else {
$paramKey = uniqid('stores_');
$subquery->where(
$subquery
->expr()
->like('s2.name', ':' . $paramKey)
);
$subquery->setParameter($paramKey, $searchItem);
}
} else if(strpos($column['search']['value'], 'eql:') !== false){
$searchValue = str_replace('eql:', '', $searchItem)."%";
$paramKey = uniqid('eql_');
$col1 = $searchQueryPatterns[$column['name']] ?? 'shopping_mall.' . $column['name'];
$query->andWhere(
$query->expr()->like($col1, ':' . $paramKey)
);
$query->setParameter($paramKey, $searchValue.'%');
} else if(strpos($column['search']['value'], 'eq:') !== false){
$searchValue = str_replace('eq:', '', $searchItem);
$paramKey = uniqid('eq_');
$col1 = $searchQueryPatterns[$column['name']] ?? 'shopping_mall.' . $column['name'];
$query->andWhere(
$query->expr()->eq($col1, ':' . $paramKey)
);
$query->setParameter($paramKey, $searchValue);
} else {
$paramKey = uniqid('standard_');
$col1 = $searchQueryPatterns[$column['name']] ?? 'shopping_mall.' . $column['name'];
$query->andWhere(
$query->expr()->like($col1, ':' . $paramKey)
);
$query->setParameter($paramKey, '%'.$searchItem.'%');
}
if($column['name'] == 'city'){
$session->set('activeCity', $searchItem);
}
if($column['name'] == 'operator'){
$session->set('operator', $searchItem);
}
if($column['name'] == 'country'){
$session->set('activeCountry', $searchItem);
}
if($column['name'] == 'isActive'){
$session->set('activeActive', $searchItem);
}
if(isset($values) && !empty($values)) {
if($column['name'] == "isOutlet") {
$final = [];
foreach($values as $value) {
$final[] = OutletTypesHelper::getOutletIdByType($value);
}
$values = $final;
}
$paramKey = uniqid('values_');
$col1 = $searchQueryPatterns[$column['name']] ?? 'shopping_mall.' . $column['name'];
$query->andWhere(
$query->expr()->in($col1, ':'.$paramKey)
);
$query->setParameter($paramKey, $values);
// $query->andWhere(
// $searchQueryPatterns[$column['name']] ?? 'shopping_mall.' . $column['name']
// )->setParameter('values' . $key, $values);
}
}
if($column['name'] == 'updatedAt'){
$updatedAtDate = $column['search']['value'];
}
if($column['name'] == 'createdAt'){
$createdAtDate = $column['search']['value'];
}
}
if (!is_null($updatedAtDate) && $updatedAtDate != '' && $updatedAtDate != '-') {
$explDate = explode('-', $updatedAtDate);
if(isset($explDate[0]) && $explDate[0] != ''){
$dateFrom = \DateTime::createFromFormat('Y/m/d', $explDate[0]);
$query->andWhere('shopping_mall.lastSyncTime >= (:updatedAtFrom)');
$query->setParameter(':updatedAtFrom', $dateFrom);
}
if(isset($explDate[1]) && $explDate[1] != ''){
$dateTo = \DateTime::createFromFormat('Y/m/d', $explDate[1]);
$query->andWhere('shopping_mall.lastSyncTime <= (:updatedAtTo)');
$query->setParameter(':updatedAtTo', $dateTo);
}
}
if (!is_null($createdAtDate) && $createdAtDate != '' && $createdAtDate != '-') {
$explDate = explode('-', $createdAtDate);
if(isset($explDate[0]) && $explDate[0] != ''){
$dateFrom = \DateTime::createFromFormat('Y/m/d', $explDate[0]);
$query->andWhere('shopping_mall.createdAt >= (:createdAtFrom)');
$query->setParameter(':createdAtFrom', $dateFrom);
}
if(isset($explDate[1]) && $explDate[1] != ''){
$dateTo = \DateTime::createFromFormat('Y/m/d', $explDate[1]);
$query->andWhere('shopping_mall.createdAt <= (:createdAtTo)');
$query->setParameter(':createdAtTo', $dateTo);
}
}
$countResult = $query->select('count(distinct(shopping_mall.id))')->getQuery()->getSingleScalarResult();
// Limit
if($length != "NaN")
$query->select('shopping_mall')->setFirstResult($start)->setMaxResults($length);
// $qbShops = $this->_em->getRepository(Shop::class)->createQueryBuilder('shop');
// // pocty brandu
// $qbShops->select('count(distinct(brands.id))')
// ->join('shop.shoppingMall', 'shoppingmall')
// ->leftJoin('shop.brands', 'brands')
// ->where('shoppingmall.id = shopping_mall.id')
// ->andWhere('shop.isActive = true')
// ->groupBy('shopping_mall.id');
//
// // pocty stores
// $qbShops2 = $this->_em->getRepository(Shop::class)->createQueryBuilder('shop2');
// $qbShops2->select('count(distinct(shop2.id))')
// ->join('shop2.shoppingMall', 'shoppingmall2')
// ->where('shoppingmall2.id = shopping_mall.id')
// ->andWhere('shop2.isActive = true')
// ->groupBy('shopping_mall.id');
// pocty brandu a obchodu
$query->leftJoin(ShoppingmallCounts::class, 'smc', 'WITH', 'smc.shoppingmallId = shopping_mall.id');
$query->select('shopping_mall')
->addSelect('smc.brandCount AS brandsCount')
->addSelect('smc.shopCount AS storesCount')
->addSelect('CAST(REPLACE(shopping_mall.specialOffer, \' \', \'\')) AS specialOfferSorting')
->addSelect('CAST(REPLACE(shopping_mall.zip, \' \', \'\')) AS zipSorting')
->addSelect('(CASE WHEN shopping_mall.specialOffer = \'\' THEN 1 ELSE 0 END) AS HIDDEN specialOfferSortingPre')
;
// Ordering
$ordersPatterns = [
'type' => 'shoptype.label',
'brands' => 'brandsCount',
'stores' => 'storesCount',
'kinds' => 'shopkinds.label',
'city' => 'city.label',
'country' => 'city.country',
'zip' => 'zipSorting',
'shoppingmall' => 'shopping_mall.label',
'shoppingarea' => 'shoppingarea.label',
'categories' => 'category.label',
'category_primary' => 'category.label',
'extent_label' => 'shop.extentLabel',
'specialOffer' => 'specialOfferSorting'
];
foreach ($orders as $key => $order) {
// $order['name'] is the name of the order column as sent by the JS
if($order['name'] != '') {
if($order['name'] == 'specialOffer'){
$query->orderBy('specialOfferSortingPre', 'ASC');
$query->addOrderBy($ordersPatterns[$order['name']] ?? 'shopping_mall.' . $order['name'], $order['dir']);
}else {
$query->orderBy($ordersPatterns[$order['name']] ?? 'shopping_mall.' . $order['name'], $order['dir']);
}
}
}
if(!isset($orders[0]['name'])){
$query->orderBy('shopping_mall.label', 'ASC');
}
$query->groupBy('shopping_mall.id');
// echo $query->getQuery()->getSQL();
// exit;
// Execute
//echo $query->getQuery()->getSQL();
$results = $query->getQuery()->getResult();
//if($withPaginator == false) {
//$countResult = count($countQuery->getQuery()->getResult());
// }else{
// $countResult = count($results);
// }
// if(isset($orders[0]['name']) && isset($orders[0]['name']) == 'specialOffer'){
// usort($results, function ($a, $b) {
// $numA = (int) str_replace(' ', '', $a[0]->getSpecialOffer());
// $numB = (int) str_replace(' ', '', $b[0]->getSpecialOffer());
// return $numA <=> $numB;
// });
// }
return array(
"results" => $results,
"countResult" => $countResult,
);
}
/**
* @param $selections
* @return array
* @throws \Doctrine\ORM\NonUniqueResultException
*/
public function compareSelections(array $selections, Session $session, $sortByCount = true, $categoryFilter = null){
$result = [];
$resultCount = [];
$resultBrands = [];
$resultBrandsCount = [];
$repoShops = $this->_em->getRepository(Shop::class);
$repoCategory = $this->_em->getRepository(ShopHasCategories::class);
$firstSelectionCode = array_key_first($selections);
foreach($selections as $index => $selectionFilter){
// nactu mally dle vstupniho filtru, nasledne z nich vyberu obchody a vlozim pro rozdeleni
$resultSelectionFilter = ($this->findByFilter($selectionFilter, $session))['results'];
$mallsIds = [];
/** @var Shoppingmall $mall */
foreach($resultSelectionFilter as $mall) {
$mallsIds[] = $mall[0]->getId();
}
$shops = $repoShops->findBy(['shoppingMall' => $mallsIds, 'isActive' => true]);
$shopHasCategories = $repoCategory->findBy(['shop' => $shops]);
$categories = [];
foreach ($shopHasCategories as $shopHasCategory) {
if($shopHasCategory->getCategory() != null) {
$categories[$shopHasCategory->getCategory()->getId()] = $shopHasCategory->getCategory()->getLabel();
}
}
asort($categories);
foreach ($shops as $item) {
// Category Filter
if(isset($categoryFilter) && $categoryFilter != '') {
$filteredCollectionCategories = $item->getShopHasCategories()->filter(function (ShopHasCategories $shopHasCategory) use ($categoryFilter) {
return $shopHasCategory->getCategory() != null && $shopHasCategory->getCategory()->getId() == $categoryFilter;
});
// pokud po filtraci nic nezbyde - shop neobsahuje kategorii - preskocim obchod
if (count($filteredCollectionCategories) == 0) {
continue;
}
}
// reseni shop in shop, vsechny obchody jsou v nazvu oddeleny pomlckou
$nameExp = explode(' - ', $item->getName());
$nameTrim = trim($nameExp[0]);
if(!isset($resultCount[$nameTrim][$index]['count'])){
$resultCount[$nameTrim][$index]['count'] = 0;
}
$result[$nameTrim][$index] = $item;
$resultCount[$nameTrim][$index]['count']++;
// pocitani brandů
foreach ($item->getBrands() as $brand) {
$brandLabel = $brand->getLabel();
if(!isset($resultBrandsCount[$brandLabel][$index])){
$resultBrandsCount[$brandLabel][$index]['count'] = 0;
}
$resultBrands[$brandLabel][$index] = $brand;
$resultBrandsCount[$brandLabel][$index]['count']++;
}
}
}
if($sortByCount) {
uasort($result, array(ShopRepository::class, "sortArrayByCountName"));
uasort($resultBrands, array(ShopRepository::class, "sortArrayByCountLabel"));
}else{
uasort($result, array(ShopRepository::class, "sortArrayByName"));
uasort($resultBrands, array(ShopRepository::class, "sortArrayByLabel"));
}
return [
'results' => $result,
'resultsCount' => $resultCount,
'resultsBrands' => $resultBrands,
'resultsBrandsCount' => $resultBrandsCount,
'firstSelectionCode' => $firstSelectionCode,
'categories' => $categories
];
}
public function createQueryBuilder($alias, $indexBy = null)
{
$this->_packageQueryBuilder->resetTableAliases();
$qb = parent::createQueryBuilder($alias, $indexBy);
$this->_user = $this->_tokenStorage->getToken()->getUser();
if($this->_user instanceof User) {
$roles = $this->_user->getRoles();
if(in_array('ROLE_ADMIN', $roles)){
return $qb;
}
$orXStat = $qb->expr()->orX();
$activePackages = $this->_user->getActivePackages();
$addOr = false;
if(count($activePackages) == 0) {
$packageRecipeRepo = $this->_em->getRepository(PackageRecipe::class);
$freePackage = $packageRecipeRepo->findOneBy(['defaultUserPackage' => true, 'active' => true])
?: $packageRecipeRepo->findOneBy(['class_name' => 'FreePackage']);
if ($freePackage) {
$exprFunc = $this->_packageQueryBuilder->createByPackage($freePackage, $qb, $alias, PackageQueryBuilder::BASE_ENTITY_MALL);
if ($exprFunc != false) {
$orXStat->add($exprFunc);
$addOr = true;
}
}
}
/** @var PackageOrderItem $activePackage */
foreach ($activePackages as $activePackage) {
// prida do query builderu potrebne podminky dle zaplacenych balicku
$expr = $this->_packageQueryBuilder->createByPackage($activePackage->getPackageRecipe(), $qb, $alias, PackageQueryBuilder::BASE_ENTITY_MALL);
if($expr != false) {
$addOr = true;
$orXStat->add($expr);
}
}
if($addOr) {
$qb->where($orXStat);
}
}
return $qb;
}
/**
* GAP analysis over an explicit list of mall IDs. One selection per mall.
* Builds the same shop/brand × selection matrix as compareSelections(), but
* skips the session/filter layer.
*/
public function compareByMallIds(array $mallIds, bool $sortByCount = true, $categoryFilter = null): array
{
$mallIds = array_values(array_unique(array_filter(array_map('intval', $mallIds))));
$result = [];
$resultCount = [];
$resultBrands = [];
$resultBrandsCount = [];
$categories = [];
$repoShops = $this->_em->getRepository(Shop::class);
$repoCategory = $this->_em->getRepository(ShopHasCategories::class);
foreach ($mallIds as $mallId) {
$index = (string) $mallId;
$shops = $repoShops->findBy(['shoppingMall' => $mallId, 'isActive' => true]);
$shopHasCategories = $repoCategory->findBy(['shop' => $shops]);
foreach ($shopHasCategories as $shopHasCategory) {
if ($shopHasCategory->getCategory() !== null) {
$categories[$shopHasCategory->getCategory()->getId()] = $shopHasCategory->getCategory()->getLabel();
}
}
foreach ($shops as $item) {
if (isset($categoryFilter) && $categoryFilter !== '') {
$matches = $item->getShopHasCategories()->filter(function (ShopHasCategories $shc) use ($categoryFilter) {
return $shc->getCategory() !== null && $shc->getCategory()->getId() == $categoryFilter;
});
if (count($matches) === 0) {
continue;
}
}
$nameExp = explode(' - ', $item->getName());
$nameTrim = trim($nameExp[0]);
if (!isset($resultCount[$nameTrim][$index]['count'])) {
$resultCount[$nameTrim][$index]['count'] = 0;
}
$result[$nameTrim][$index] = $item;
$resultCount[$nameTrim][$index]['count']++;
foreach ($item->getBrands() as $brand) {
$brandLabel = $brand->getLabel();
if (!isset($resultBrandsCount[$brandLabel][$index])) {
$resultBrandsCount[$brandLabel][$index]['count'] = 0;
}
$resultBrands[$brandLabel][$index] = $brand;
$resultBrandsCount[$brandLabel][$index]['count']++;
}
}
}
asort($categories);
if ($sortByCount) {
uasort($result, [\App\Repository\ShopRepository::class, 'sortArrayByCountName']);
uasort($resultBrands, [\App\Repository\ShopRepository::class, 'sortArrayByCountLabel']);
} else {
uasort($result, [\App\Repository\ShopRepository::class, 'sortArrayByName']);
uasort($resultBrands, [\App\Repository\ShopRepository::class, 'sortArrayByLabel']);
}
return [
'results' => $result,
'resultsCount' => $resultCount,
'resultsBrands' => $resultBrands,
'resultsBrandsCount' => $resultBrandsCount,
'firstSelectionCode' => isset($mallIds[0]) ? (string) $mallIds[0] : null,
'categories' => $categories,
];
}
}