<?php
namespace App\Repository;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Tools\Pagination\Paginator;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Contracts\Cache\CacheInterface;
use App\Misc\CustomEntityManager;
use App\Service\CommonService;
use AutoMapperPlus\AutoMapperInterface;
use Gedmo\Translatable\Query\TreeWalker\TranslationWalker;
/**
* @property AutoMapperInterface autoMapper
*/
class BaseRepository extends ServiceEntityRepository
{
public function __construct(
ManagerRegistry $registry,
CustomEntityManager $customEntityManager,
CommonService $commonService,
CacheInterface $appCache,
AutoMapperInterface $autoMapper,
ContainerInterface $container
) {
$this->commonService = $commonService;
$thisName = $this->commonService->getClassName($this, true);
if ($thisName !== 'Base') {
parent::__construct($registry, 'App\Entity\\' . $thisName);
}
$this->_em = $customEntityManager;
$this->autoMapper = $autoMapper;
$this->container = $container;
$this->cache = $appCache;
}
public function getEntityManager()
{
return $this->_em;
}
public function save($entity, $inTransaction = false)
{
$this->getEntityManager()->persist($entity);
if (!$inTransaction) {
$this->getEntityManager()->flush();
}
return $entity;
}
public function persist($entity)
{
$this->getEntityManager()->persist($entity);
}
public function flush()
{
$this->getEntityManager()->flush();
}
public function clear()
{
$this->getEntityManager()->clear();
}
public function delete($entities, $inTransaction = false)
{
if (!is_array($entities)) {
$entities = [$entities];
}
foreach ($entities as $entity) {
$this->getEntityManager()->remove($entity);
}
if (!$inTransaction) {
$this->getEntityManager()->flush();
}
}
/**
* @return \Doctrine\DBAL\Connection
*/
public function getConnection()
{
return $this->getEntityManager()->getConnection();
}
public function __get($propertyName)
{
if($propertyName == '_entityName') {
return parent::__get('_entityName');
}
preg_match('/([a-zA-Z0-9]+)Service/i', $propertyName, $serviceMatches);
if (count($serviceMatches) > 0) {
return $this
->container
->get('App\Service\\' . ucfirst($serviceMatches[1]) . 'Service');
}
preg_match('/([a-zA-Z0-9]+)Repo/i', $propertyName, $repositoryMatches);
if (count($repositoryMatches) > 0) {
return $this
->container
->get('App\Service\\' . ucfirst($repositoryMatches[1]) . 'Service')
->repository;
}
$entityName = $this->commonService->getClassName($this, true);
$cacheKey = 'Const.' . $entityName . '.' . $propertyName;
$cachedConstantItem = $this->cache->getItem($cacheKey);
if ($cachedConstantItem->isHit()) {
return $cachedConstantItem->get();
}
if($propertyName !== '_entityName') {
$constant = null;
if(defined('App\Entity\\' . $entityName . '::' . $propertyName)) {
$constant = constant('App\Entity\\' . $entityName . '::' . $propertyName);
}
if (!is_null($constant)) {
$cachedConstantItem->set($constant);
$this->cache->save($cachedConstantItem);
return $constant;
}
}
$metas = $this->_em->getMetadataFactory()->getAllMetadata();
foreach ($metas as $meta) {
$classPath = $meta->getName();
$name = strtoupper($this->commonService->toSnakeCase(str_replace('App\Entity\\', '', $classPath)));
preg_match('/(' . $name . ')_(.+)/i', $propertyName, $matches);
if (count($matches) > 0) {
$prop = $matches[2];
if(defined($classPath . '::' . $prop)) {
$constant = @constant($classPath . '::' . $prop);
}
if (!is_null($constant)) {
$cachedConstantItem->set($constant);
$this->cache->save($cachedConstantItem);
return $constant;
}
}
}
trigger_error('Could not found property ' . $propertyName . ' in ' . $this->commonService->getClassName($this), E_USER_ERROR);
}
public function getPagingData($queryBuilder, $paging = null, $DTO = null, $fetchJoinCollection)
{
if (is_null($paging)) {
$paging = [
'page' => 1,
'limit' => 50
];
}
if (!isset($paging['page'])) {
$paging['page'] = 1;
}
if (!isset($paging['limit'])) {
$paging['limit'] = 50;
} else if (((int)$paging['limit']) <= 0) {
$paging['limit'] = pow(10, 12);
}
$queryBuilder
->setFirstResult(($paging['page'] - 1) * $paging['limit'])
->setMaxResults($paging['limit']);
$paginator = new Paginator($queryBuilder, $fetchJoinCollection);
$paginatorQuery = $paginator->getQuery();
$this->addTranslationWalkerToQuery($paginatorQuery);
$total = count($paginator);
$list = [];
foreach ($paginator as $entity) {
if (is_null($DTO)) {
$outputDTO = 'App\DTO\\' . $this->getEntityName() . '\\' . $this->getEntityName() . 'Output';
if (class_exists($outputDTO)) {
$DTO = $outputDTO;
}
}
if (is_null($DTO) || $DTO === 'ENTITY') {
$list[] = $entity;
} else {
$array = (array)$this->autoMapper->map($entity, $DTO);
foreach ($array as $key => $value) {
if (is_string($value)) {
$array[$key] = mb_convert_encoding($value, "UTF-8");
}
}
$list[] = $array;
}
}
return [
'total' => $total,
'totalPages' => ceil($total / $paging['limit']),
'list' => $list,
'currentPage' => 1 * $paging['page']
];
}
public function setFilter(QueryBuilder $queryBuilder, $requestData)
{
foreach ($requestData as $key => $value) {
if ($value === '') {
continue;
}
if (strpos($key, 'filter_') === 0) {
$filter = str_replace('filter_', '', $key);
if (strpos($filter, '_like') !== false) {
$filter = str_replace('_like', '', $filter);
if ($filter === 'namefull') {
$queryBuilder
->andWhere(
$queryBuilder
->expr()
->orX(
$queryBuilder
->expr()
->like($this->getEntityName() . '.firstName', ':firstName'),
$queryBuilder
->expr()
->like($this->getEntityName() . '.lastName', ':lastName')
)
)
->setParameter(':firstName', $value)
->setParameter(':lastName', $value);
} else {
// or like
if (strpos($filter, '_or_') !== -1) {
$orx = $queryBuilder->expr()->orX();
foreach (explode('_or_', $filter) as $orFilter) {
$orx->add(
$queryBuilder
->expr()
->like($this->getEntityName() . '.' . $orFilter, ':' . $orFilter)
);
$queryBuilder->setParameter($orFilter, '%' . $value . '%');
}
$queryBuilder->andWhere($orx);
} else {
$fieldName = $this->getEntityName() . '.' . $filter;
$queryBuilder->andWhere(
$queryBuilder
->expr()
->like($fieldName, ':' . $filter)
);
$queryBuilder->setParameter($filter, '%' . $value . '%');
}
}
} else {
$fieldName = $this->getEntityName() . '.' . $filter;
if (is_array($value)) {
$queryBuilder
->andWhere(
$queryBuilder
->expr()
->in($fieldName, ':in' . $filter)
)
->setParameter(
'in' . $filter,
$value,
\Doctrine\DBAL\Connection::PARAM_INT_ARRAY
);
} else if ($value === 'falseOrNull') {
$queryBuilder->andWhere($fieldName . ' = 0 or ' . $fieldName . ' is NULL');
} else if ($value === 'isNull') {
$queryBuilder->andWhere($fieldName . ' is NULL');
} else if ($value === 'true' || $value === 'false') {
$queryBuilder->andWhere(
$queryBuilder->expr()->eq($fieldName, ':' . $filter)
);
$queryBuilder->setParameter($filter, $value === 'true');
} else if ($value === 'notNull') {
$queryBuilder->andWhere(
$queryBuilder->expr()->isNotNull($fieldName)
);
} else {
$queryBuilder->andWhere(
$queryBuilder->expr()->eq($fieldName, ':' . $filter)
);
$queryBuilder->setParameter($filter, $value);
}
}
} else if (strpos($key, 'except_') === 0) {
$except = str_replace('except_', '', $key);
$isInt = is_int(explode(',', $value)[0]);
if ($isInt) {
$value = array_map('intval', explode(',', $value));
} else {
$value = array_map('strval', explode(',', $value));
}
$fieldName = $this->getEntityName() . '.' . $except;
$queryBuilder
->andWhere(
$queryBuilder
->expr()
->orX(
$queryBuilder->expr()->isNull($fieldName),
$queryBuilder
->expr()
->notIn($fieldName, ':notIn' . $except)
)
)
->setParameter(
'notIn' . $except,
$value,
$isInt
? \Doctrine\DBAL\Connection::PARAM_INT_ARRAY
: \Doctrine\DBAL\Connection::PARAM_STR_ARRAY
);
} else if (strpos($key, 'has_') === 0) {
$paramName = 'p' . bin2hex(random_bytes(5));
$has = str_replace('has_', '', $key);
$relationName = explode('_', $has)[0];
$relationProperty = explode('_', $has)[1];
$fieldName = $this->getEntityName() . '.' . $relationName;
$queryBuilder
->leftJoin($fieldName, $paramName)
->andWhere($paramName . '.' . $relationProperty . ' = :' . $paramName)
->setParameter($paramName, $value);
} else if (strpos($key, 'hasAny_') === 0) {
$paramName = 'p' . bin2hex(random_bytes(5));
$has = str_replace('hasAny_', '', $key);
$relationName = explode('_', $has)[0];
$relationProperty = explode('_', $has)[1];
$fieldName = $this->getEntityName() . '.' . $relationName;
$queryBuilder
->leftJoin($fieldName, $paramName)
->andWhere($paramName . '.' . $relationProperty . ' = :' . $paramName)
->setParameter($paramName, $value);
} else if (strpos($key, 'hasAny2_') === 0) {
$paramName = 'p' . bin2hex(random_bytes(5));
$has = str_replace('hasAny2_', '', $key);
$relationName = explode('_', $has)[0];
$relationProperty = explode('_', $has)[1];
$fieldName = $this->getEntityName() . '.' . $relationName;
$queryBuilder
->leftJoin($fieldName, $paramName)
->andWhere(':' . $paramName . ' member of ' . $paramName . '.' . $relationProperty)
->setParameter($paramName, $value);
} else if (strpos($key, 'memberOf_') === 0) {
$relationName = str_replace('memberOf_', '', $key);
$paramName = 'p66' . $relationName;
$fieldName = $this->getEntityName() . '.' . $relationName;
if ($value === 'isNull') {
$queryBuilder->andWhere($fieldName . ' is empty');
}elseif ($value === 'notNull') {
$queryBuilder
->leftJoin($fieldName, $paramName);
$queryBuilder->andWhere($queryBuilder->expr()->isNotNull($paramName));
} else {
$queryBuilder
->leftJoin($fieldName, $paramName)
->andWhere(':' . $paramName . ' member of ' . $fieldName)
->setParameter($paramName, $value);
}
} else if (strpos($key, 'hasLike_') === 0) {
$paramName = 'p' . bin2hex(random_bytes(5));
$has = str_replace('hasLike_', '', $key);
$relationName = explode('_', $has)[0];
$relationProperty = explode('_', $has)[1];
$fieldName = $this->getEntityName() . '.' . $relationName;
$queryBuilder
->leftJoin($fieldName, $paramName)
->andWhere($paramName . '.' . $relationProperty . ' LIKE :' . $paramName)
->setParameter($paramName, '%' . $value . '%');
}
}
}
public function setSort(QueryBuilder $queryBuilder, $requestData)
{
foreach ($requestData as $key => $value) {
if (strpos($key, 'sort_') === false && strpos($key, 'sorts_') === false) {
continue;
}
$direction = strpos($key, '_asc') !== false ? 'ASC' : 'DESC';
$isRelation = strpos($value, 'Relation') !== false;
if ($isRelation) {
$value = str_replace('Relation', '', $value);
$fieldName = $this->getEntityName() . '.' . $value;
$queryBuilder->leftJoin($fieldName, $value);
$queryBuilder->addOrderBy($value . '.id', $direction);
} else {
$fieldName = $this->getEntityName() . '.' . $value;
$queryBuilder
->addSelect('CASE WHEN ' . $fieldName . ' IS NULL THEN 1 ELSE 0 END as HIDDEN ' . $value . '_is_null')
->addOrderBy($value . '_is_null', 'ASC')
->addOrderBy($fieldName, $direction)
;
}
}
}
public function getList($requestData, $DTO = null, callable $extraFilter = null, $fetchJoinCollection = true)
{
if ($requestData instanceof Request) {
$requestData = $requestData->query->all();
}
$queryBuilder = $this->createQueryBuilder($this->getEntityName());
if (!is_null($extraFilter)) {
$extraFilter($queryBuilder);
}
$this->setFilter($queryBuilder, $requestData);
$this->setSort($queryBuilder, $requestData);
return $this->getPagingData($queryBuilder, $requestData, $DTO, $fetchJoinCollection);
}
public function getAll($requestData, $DTO = null, callable $extraFilter = null)
{
if ($requestData instanceof Request) {
$requestData = $requestData->query->all();
}
$queryBuilder = $this->createQueryBuilder($this->getEntityName());
if (!is_null($extraFilter)) {
$extraFilter($queryBuilder);
}
$this->setFilter($queryBuilder, $requestData);
$this->setSort($queryBuilder, $requestData);
$entities = $queryBuilder->getQuery()->execute();
$list = [];
foreach ($entities as $entity) {
if (is_null($DTO)) {
$outputDTO = 'App\DTO\\' . $this->getEntityName() . '\\' . $this->getEntityName() . 'Output';
if (class_exists($outputDTO)) {
$DTO = $outputDTO;
}
}
if (is_null($DTO) || $DTO === 'ENTITY') {
$list[] = $entity;
} else {
$array = (array)$this->autoMapper->map($entity, $DTO);
foreach ($array as $key => $value) {
if (is_string($value)) {
$array[$key] = mb_convert_encoding($value, "UTF-8");
}
}
$list[] = $array;
}
}
return $list;
}
public function getDistinctAttributeOfList($requestData, $DTO = null, callable $extraFilter = null)
{
$entityName = $this->getEntityName();
if ($requestData instanceof Request) {
$requestData = $requestData->query->all();
}
if (isset($requestData['distinct_attribute'])) {
$attributeType = 'attribute';
} else if (isset($requestData['distinct_relation'])) {
$attributeType = 'relation';
} else return [];
$queryBuilder = $this->createQueryBuilder($entityName);
if (!is_null($extraFilter)) {
$extraFilter($queryBuilder);
}
$this->setFilter($queryBuilder, $requestData);
$this->setSort($queryBuilder, $requestData);
if ($attributeType == 'attribute') {
$queryBuilder
->select($entityName . '.' . $requestData['distinct_attribute'])
->groupBy($entityName . '.' . $requestData['distinct_attribute']);
} else if ($attributeType == 'relation') {
$queryBuilder
->join($entityName . '.' . $requestData['distinct_relation'], $requestData['distinct_relation'])
->addSelect($requestData['distinct_relation'])
->addSelect('COUNT(' . $entityName . ') as count')
->groupBy($requestData['distinct_relation']);
}
$result = $queryBuilder->getQuery()->getResult();
$attributeList = [];
foreach ($result as $entity) {
$attributeName = $requestData[$attributeType == 'attribute' ? 'distinct_attribute' : 'distinct_relation'];
$attribute = $entity[0]->{'get' . $attributeName}();
if ($attributeType == 'attribute') {
$attributeList[] = [
'value' => $attribute,
'count' => (int)$entity['count'],
];
} else if ($attributeType == 'relation') {
$array = !is_null($DTO)
? (array)$this->autoMapper->map($attribute, $DTO)
: (array)$attribute;
foreach ($array as $key => $value) {
if (is_string($value)) {
$array[$key] = mb_convert_encoding($value, "UTF-8");
}
}
$array['count'] = (int)$entity['count'];
$attributeList[] = $array;
}
}
return $attributeList;
}
public function entityToDtoArray($entity, $DTO = null)
{
$array = (array)$this->autoMapper->map($entity, $DTO);
foreach ($array as $key => $value) {
if (is_string($value)) {
$array[$key] = mb_convert_encoding($value, "UTF-8");
}
}
return $array;
}
public function getEntityName()
{
return $this->commonService->getClassName($this, true);
}
public function checkExistSlug($entity, $value)
{
$id = $entity->getId();
$conn = $this->getEntityManager()->getConnection();
$stmt = $conn->executeQuery(
'select count(id) as id from news where id != :id and slug = :slug',
[
'id' => $id,
'slug' => $value
]
);
$result = $stmt->fetchOne();
return (int) $result;
}
public function findOneBy(array $criteria, array $orderBy = null)
{
$qb = $this->createQueryBuilder('o');
foreach ($criteria as $criterion => $value) {
$qb
->andWhere('o.' . $criterion . ' = :' . $criterion)
->setParameter($criterion, $value);
}
$qb->setMaxResults(1);
$query = $qb->getQuery();
$this->addTranslationWalkerToQuery($query);
return $query->getOneOrNullResult();
}
public function addTranslationWalkerToQuery($query)
{
// News, Program
$translatableEntities = [];
if (!in_array($this->getEntityName(), $translatableEntities)) return;
$config = $this->_em->getConfiguration();
if ($config->getCustomHydrationMode(TranslationWalker::HYDRATE_OBJECT_TRANSLATION) === null) {
$config->addCustomHydrationMode(
TranslationWalker::HYDRATE_OBJECT_TRANSLATION,
'Gedmo\\Translatable\\Hydrator\\ORM\\ObjectHydrator'
);
}
$query->setHint(
\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER,
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
);
$query->setHint(
\Gedmo\Translatable\TranslatableListener::HINT_TRANSLATABLE_LOCALE,
$this->container->get('gedmo.listener.translatable')->getListenerLocale() // take locale from session or request etc.
);
$query->setHydrationMode(TranslationWalker::HYDRATE_OBJECT_TRANSLATION);
$query->setHint(\Doctrine\ORM\Query::HINT_REFRESH, true);
}
}