<?php
namespace App\Service;
use App\Entity\CalendarEvent;
use App\Entity\Client;
use App\Entity\Company;
use App\Entity\Form\AnySearch;
use App\Entity\Machine;
use App\Entity\Person;
use App\Entity\Session;
use App\Entity\Student;
use App\Library\Route\RouteParamObjects;
use App\Library\Utils\DateTimeProvider\DateTimeProviderInterface;
use App\Library\Utils\DateTimeProvider\SystemDateTimeProvider;
use App\Library\Utils\Dev\DoctrineUtils\DoctrineUtils;
use App\Library\Utils\Dev\ReflectionUtils\ReflectionUtils;
use App\Library\Utils\Other\Other;
use App\Library\Utils\StringUtils;
use App\Service\CarWash\CarWashService;
use App\Service\Data\Paginator;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ObjectRepository;
use Symfony\Component\Serializer\SerializerInterface;
class BaseEntityService
{
const MANDATORY_PARAMS = [
];
const SEARCH_FIELDS = [
"name"
];
const ORDER_ID_DESC = ["id", "DESC"];
private static $serviceSecFields = [];
/** @var EntityManagerInterface */
protected $em;
/**
* @var DateTimeProviderInterface
*/
protected $dateTimeProvider;
protected $entityClassName = null;
protected $entityShortClassName = null;
protected $entityAlias = null;
protected $dateFieldName = "createdAt";
protected $tableName = null;
/**
* @deprecated Устарело. Не использовать.
*/
protected $searchDql = 'item.id = :text or item.name like :text_like';
/**
* @var BaseEntityServiceDefault
*/
private $default;
private $sqlFields = [];
/**
* @var SerializerInterface
*/
private $serializer;
/**
* @var ServiceRetriever|null
*/
protected $serviceRetriever;
public function __construct(EntityManagerInterface $em,
SerializerInterface $serializer = null,
ServiceRetriever $serviceRetriever = null)
{
$this->em = $em;
$this->default = new BaseEntityServiceDefault($this);
$this->serializer = $serializer;
$this->serviceRetriever = $serviceRetriever;
}
public function initialize(string $entityClassName, string $dateFieldName = "createdAt", string $searchDql = null): void
{
$this->dateFieldName = $dateFieldName;
$this->entityClassName = $entityClassName;
$this->entityShortClassName = self::getEntityShortName($entityClassName);
$this->entityAlias = self::getEntityAlias($this->entityShortClassName);
$classMetadata = $this->em->getClassMetadata($entityClassName);
$this->tableName = $classMetadata->getTableName();
$this->searchDql = $searchDql ?? $this->searchDql;
}
public function getRepository(): EntityRepository
{
return $this->em->getRepository($this->entityClassName);
}
/**
* @return BaseEntityServiceDefault
*/
public function getBaseService(): BaseEntityServiceDefault
{
return $this->default;
}
public function getRandom(int $count = 1, bool $getCount = false, array $excludeIds = null, string $where = null)
{
$sql = 'SELECT ' . ($getCount ? "count(*)" : "*") . ' FROM ' . $this->tableName . ' item '
. ($excludeIds || $where ? "WHERE " : "")
. ($excludeIds ? " item.id not in (0, " . implode(",", $excludeIds) . ")" : "")
. ($where ? " $where" : "")
. ' ORDER BY RAND() LIMIT ' . $count;
$connection = $this->em->getConnection();
$stmt = $connection->prepare($sql);
$result = $stmt->executeQuery();
return ($getCount ? (int)$result->fetchNumeric()[0] : $result->fetchAllAssociative());
}
public function hasItems(Company $company = null, string $whereSql = null): bool
{
$sql = 'SELECT count(*) as count FROM ' . $this->tableName . " item where"
. ($company ? " item.company_id = {$company->getId()}" : "")
. ($whereSql ? " $whereSql" : "")
. " LIMIT 1";
$connection = $this->em->getConnection();
$stmt = $connection->prepare($sql);
$result = $stmt->executeQuery();
return (int)$result->fetchNumeric()[0] != 0;
}
public function hasNotDeletedItems(Company $company): bool
{
$sql = 'SELECT count(*) as count FROM ' . $this->tableName . " item where item.company_id = {$company->getId()} and item.status != 'deleted' LIMIT 1";
$connection = $this->em->getConnection();
$stmt = $connection->prepare($sql);
$result = $stmt->executeQuery();
return (int)$result->fetchNumeric()[0] != 0;
}
protected static $sortedDefaultQueryParams = [
"company" => null,
"machine" => null,
];
/**
* @param array{string, string}|null $orderBy
* @return array{list: array, page: int, perPage: int, totalItems: int, pagesCount: int}
* @throws \Exception
*/
public function getItemsWithPaginationDefault(int $page, array $params = null,
array $orderBy = null, $onBeforeQueryChange = null, array $excludeMandatoryParams = null,
$isSql = false, array $joinClasses = null, int $perPage = Paginator::PER_PAGE): array
{
$query = $this->getQuery($params, $orderBy, $onBeforeQueryChange,
$excludeMandatoryParams, $isSql, $joinClasses);
$result = Paginator::getResult($query, $page, $perPage, false, $this->em->getConnection());
if ($isSql && $joinClasses) {
foreach ($result['list'] as $index => $itemData) {
$newData = [];
$jointTableNames = [];
foreach ($itemData as $key => $value) {
$keyArr = explode("__", $key, 2);
$tableName = $keyArr[0];
$columnName = $keyArr[1];
if (!isset($newData[$tableName])) {
$newData[$tableName] = [];
if ($tableName != $this->tableName) {
$jointTableNames[] = $tableName;
}
}
if (in_array($columnName, ["created_at", "last_online", "last_time"])) {
$value = new \DateTime($value);
}
$newData[$tableName][$columnName] = $value;
}
$result['list'][$index] = $newData[$this->tableName];
foreach ($jointTableNames as $name) {
if (isset($result['list'][$index][$name])) {
throw new \Exception("'$name' field is already set");
}
$result['list'][$index][$name] = $newData[$name];
}
}
}
return $result;
}
public function summaryDefault(array $params = null, string $field = "amount", $onBeforeQueryChange = null,
array $excludeMandatoryParams = null, array $joinClasses = null)
{
$query = $this->summaryQueryDefault($params, $onBeforeQueryChange, $excludeMandatoryParams, $joinClasses, $field);
$result = (int)current($query->getQuery()->getResult()[0]);
return $result;
}
public function getDefault(array $params = null, $onBeforeQueryChange = null, array $excludeMandatoryParams = null,
array $joinClasses = null, int $limit = null, array $orderBy = null): array {
$query = $this->getQuery($params, $orderBy, $onBeforeQueryChange,
$excludeMandatoryParams, false, $joinClasses, $limit);
$result = $query->getQuery()->getResult();
return $result;
}
public function getFirst(array $params = null, $onBeforeQueryChange = null, array $excludeMandatoryParams = null,
array $joinClasses = null, array $orderBy = null)
{
$result = $this->getDefault($params, $onBeforeQueryChange, $excludeMandatoryParams, $joinClasses, 1,
$orderBy);
if ($result) {
return $result[0];
} else {
return null;
}
}
public function summaryQueryDefault(array $params = null, $onBeforeQueryChange = null, array $excludeMandatoryParams = null,
array $joinClasses = null, string $field = "amount")
{
$query = $this->getQuery($params, null, $onBeforeQueryChange, $excludeMandatoryParams, false, $joinClasses);
$query->select("sum(item.$field)");
return $query;
}
/**
* @param $paramName
* @param $paramValue
* @param \Doctrine\DBAL\Query\QueryBuilder|\Doctrine\ORM\QueryBuilder $query
* @param $sortedParams
* @return false
*/
protected function handleNextQueryParam($paramName, $paramValue, $query, $sortedParams, $isSql) { //need override
return false;
}
/**
* @param array|null $params
* @param null|array{string, string} $orderBy
* @param $onBeforeQueryChange
* function(string $paramName, $paramValue, \Doctrine\DBAL\Query\QueryBuilder|\Doctrine\ORM\QueryBuilder $query): bool {}
* @param array|null $excludeMandatoryParams
* @param $isSql
* @param array|null $sortedDefaultQueryParams
* @return \Doctrine\DBAL\Query\QueryBuilder|QueryBuilder
* @throws \Exception
*/
public function getQuery(array $params = null,
array $orderBy = null, $onBeforeQueryChange = null, array $excludeMandatoryParams = null, $isSql = false, array $joinClasses = null,
int $limit = null)
{
$sortedParams = $this->getSortedParams($params, $excludeMandatoryParams);
$query = ($isSql
? $this->em->getConnection()->createQueryBuilder()
: $this->em->getRepository($this->entityClassName)->createQueryBuilder('item'));
$select = null;
if ($joinClasses) {
if ($isSql) {
$select .= implode(", ", array_map(function ($current) {
return "item.$current";
}, DoctrineUtils::getAllEntityFields($this->em, $this->getEntityClassName(), $this->tableName)));
}
$lastJoinAlias = "item";
foreach ($joinClasses as $class) {
$table = StringUtils::toCamel(self::getEntityShortName($class));
$alias = self::getEntityAliasByTableName($table, false);
$metadata = $this->em->getClassMetadata($class);
if ($isSql) {
$select .= ", " . implode(", ", array_map(function ($current) use ($alias) {
return "$alias.$current";
}, DoctrineUtils::getAllEntityFields($this->em, $class, $table)));
}
$field = null;
switch ($class) {
case Machine::class:
$field = "machine_id";
break;
case Company::class:
$field = $isSql ? "company_id" : "company";
break;
case Client::class:
$field = $isSql ? "client_id" : "client";
break;
case Person::class:
$field = $isSql ? "person_id" : "person";
break;
case CalendarEvent::class:
$field = $isSql ? "calendar_event_id" : "calendarEvent";
break;
case Student::class:
$field = $isSql ? "student_id" : "student";
break;
default:
throw new \Exception("Unknown: " . $class . ". Entity: " . $this->entityClassName);
}
if ($isSql) {
$query->innerJoin($lastJoinAlias, $table, $alias, "$lastJoinAlias.$field = $alias.id");
} else {
$query->innerJoin($class, $alias, Join::WITH, "$lastJoinAlias.$field = $alias");
}
$lastJoinAlias = $alias;
}
} else {
if ($isSql) {
$select = "*";
}
}
if ($isSql) {
$query->select($select);
$query->from($this->tableName, "item");
}
foreach ($sortedParams as $paramName => $paramValue) {
if ($onBeforeQueryChange) {
if ($onBeforeQueryChange($paramName, $paramValue, $query)) {
continue;
}
}
/** @var \Doctrine\DBAL\Query\QueryBuilder|\Doctrine\ORM\QueryBuilder $query */
if (!$this->handleNextQueryParam($paramName, $paramValue, $query, $sortedParams, $isSql)) {
switch ($paramName) {
case "id":
if (is_array($paramValue)) {
if ($isSql) {
$query->andWhere("item.id IN (" . Other::objectToStringId($paramValue) . ")");
} else {
$query->andWhere("item.id IN (:ids)")
->setParameter('ids', $paramValue);
}
} else {
if ($isSql) {
$query->andWhere("item.id = :id")
->setParameter('id', $paramValue);
} else {
$query->andWhere("item.id = :id")
->setParameter('id', $paramValue);
}
}
break;
// case "type":
// case "status":
// if ($isSql) {
// throw new \Exception("Not implemented");
// } else {
// $query->andWhere("item.$paramName = :$paramName")
// ->setParameter("$paramName", $paramValue);
// }
// break;
case "company":
if ($paramValue !== null) {
if ($isSql) {
$query->andWhere("item.company_id = :companyId")
->setParameter('companyId', $paramValue);
} else {
$query->andWhere("item.company = :company")
->setParameter("company", $paramValue);
}
}
break;
case "start":
if ($paramValue !== null) {
if ($isSql) {
$query->andWhere("item.{$this->getSqlField($this->dateFieldName)} >= :start and item.{$this->getSqlField($this->dateFieldName)} <= :end")
->setParameter('start', $paramValue->format("Y-m-d H:i:s"))
->setParameter('end', $sortedParams['end']->format("Y-m-d H:i:s"));
} else {
$query->andWhere("item.{$this->dateFieldName} >= :start and item.{$this->dateFieldName} <= :end")
->setParameter('start', $paramValue)
->setParameter('end', $sortedParams['end']);
}
}
break;
case "end":
//do nothing
break;
case "startTime":
if ($paramValue !== null) {
if ($isSql) {
$query->andWhere("time(item.{$this->getSqlField($this->dateFieldName)}) >= :startTime and time(item.{$this->getSqlField($this->dateFieldName)}) <= :endTime")
->setParameter('startTime', $paramValue->format('H:i:s'))
->setParameter('endTime', $sortedParams['endTime']->format('H:i:s'));
} else {
throw new \Exception("Not available");
}
}
break;
case "endTime":
//do nothing`
break;
case "machine":
if ($paramValue !== null) {
if (is_object($paramValue) && method_exists($paramValue, "toArray") && get_class($paramValue) != Machine::class) {
$paramValue = $paramValue->toArray();
}
if (!is_array($paramValue)) {
$paramValue = [$paramValue];
}
if ($isSql) {
$query->andWhere("item.machine_id in (" . implode(", ",
array_map(function (Machine $machine) {return $machine->getId();}, $paramValue)) . ")");
} else {
$query->andWhere("item.machine in (:machines)")
->setParameter("machines", $paramValue);
}
}
break;
case "name":
if ($paramValue !== null) {
if ($isSql) {
throw new \Exception("Not implemented");
} else {
$query->andWhere("item.name = :name")
->setParameter("name", $paramValue);
}
}
break;
case "search":
if ($paramValue !== null && $paramValue != "") {
if ($isSql) {
throw new \Exception("Not implemented");
} else {
$where = "";
foreach ($this->getSearchFields() as $field) {
$where .= ($where ? " or " : "") . "item.$field like :search";
}
$query->andWhere($where)
->setParameter("search", "%" . $paramValue . "%");
}
}
break;
default:
if (is_bool($paramValue) && preg_match("/^is[A-Z0-9]/", $paramName)) {
if ($paramValue === false) {
$query->andWhere("item.$paramName = 0 or item.$paramName is null");
} else {
$query->andWhere("item.$paramName = 1");
}
} else if (is_array($paramValue) && (isset($paramValue["startDate"]) || isset($paramValue["endDate"]))) {
if ($isSql) {
throw new \Exception("Not implemented");
} else {
$tmpWhere = "";
if (isset($paramValue["startDate"])) {
$tmpWhere .= "item.$paramName >= :{$paramName}StartDate";
$query->setParameter("{$paramName}StartDate", $paramValue['startDate']);
}
if (isset($paramValue["endDate"])) {
$tmpWhere .= ($tmpWhere ? " and " : "") . "item.$paramName <= :{$paramName}EndDate";
$query->setParameter("{$paramName}EndDate", $paramValue['endDate']);
}
$query->andWhere($tmpWhere);
}
} else if (is_array($paramValue) && ReflectionUtils::isEntity(current($paramValue))) {
$alias = self::getEntityAlias($paramName);
$query
->join("item.$paramName", $alias)
->andWhere("$alias.id in (:{$paramName}Ids)")
->setParameter(":{$paramName}Ids", Other::objectToStringId($paramValue));
} else if (is_array($paramValue) && is_string(current($paramValue))) {
$query->andWhere("item.$paramName in (:{$paramName}Values)")
->setParameter(":{$paramName}Values", $paramValue);
} elseif (is_array($paramValue) && is_int(current($paramValue))) {
if ($isSql) {
$query->andWhere("item.$paramName in (" . implode(", ", $paramValue) . ")");
} else {
$query->andWhere("item.$paramName in (:{$paramName}Ids)")
->setParameter(":{$paramName}Ids", $paramValue);
}
} else {
if ($isSql) {
throw new \Exception("Not implemented");
} else {
if (strpos($paramName, ".") !== false) {
$query->andWhere("$paramName = :" . str_replace(".", "_", $paramName))
->setParameter(str_replace(".", "_", $paramName), $paramValue);
} else {
$query->andWhere("item.$paramName = :$paramName")
->setParameter("$paramName", $paramValue);
}
}
}
}
}
}
if ($orderBy) {
$query->orderBy("item.{$orderBy[0]}", $orderBy[1]);
}
if ($limit !== null) {
$query->setMaxResults($limit);
}
if (Other::canAddDebugLog()) {
Other::appendQueryLog(
(get_class($query) == "Doctrine\ORM\QueryBuilder" ? "DQL: " . $query->getDQL() : "")
. "\nSQL: " . (get_class($query) == "Doctrine\ORM\QueryBuilder" ? $query->getQuery()->getSQL() : $query->getSQL()));
}
return $query;
}
protected function getSqlField(string $field): string
{
if (isset($this->sqlFields[$field])) {
return $this->sqlFields[$field];
}
$this->sqlFields[$field] = StringUtils::toCamel($field);
return $this->sqlFields[$field];
}
public function getSortedParams(?array $params, ?array $excludeMandatoryParams): array
{
$sortedParams = array_merge($this::$sortedDefaultQueryParams, ($params ?: []));
foreach ($this->getMandatoryParams() as $paramName) {
if ((!isset($sortedParams[$paramName]) || $sortedParams[$paramName] === null) && (!$excludeMandatoryParams || !in_array($paramName, $excludeMandatoryParams))) {
throw new \Exception("Обязательный параметр \"$paramName\" не задан. Добавьте параметр в \$params или добавьте в исключение \$excludeMandatoryParams"
. ". Сущность: " . $this->entityClassName);
}
}
return $sortedParams;
}
public function getMandatoryParams(): array {
return self::MANDATORY_PARAMS;
}
protected function getSearchFields(): array {
return self::SEARCH_FIELDS;
}
/**
* @deprecated Устарело. Не использовать.
* Calls in getItemsBySearchWithPagination.
* Override if you need to modify query before search query execution.
* Example:
* For other entity joining.
* $query->innerJoin(Client::class, "client_", Join::WITH, "item.client = client_");
* @param QueryBuilder $query
* @return void
*/
protected function setSearchQueryParams(QueryBuilder $query): void
{
}
public function setDateTimeProvider(DateTimeProviderInterface $dateTimeProvider)
{
$this->dateTimeProvider = $dateTimeProvider;
}
public function setSystemTimeProvider()
{
$this->dateTimeProvider = new SystemDateTimeProvider();
}
public static function getEntityShortName(string $entityClassName): string
{
return (new \ReflectionClass($entityClassName))->getShortName();
}
public static function getEntityAlias(string $entityShortClassName): string
{
return lcfirst($entityShortClassName) . "_";
}
public static function getEntityAliasByTableName(string $tableName, bool $addUnderscore = true): string
{
if (!str_contains($tableName, "_")) {
return $tableName . ($addUnderscore ? "_" : "");
}
$substrings = explode("_", $tableName);
return $substrings[0] .
implode("", array_map(function ($current) {return ucfirst($current);}, array_splice($substrings, 1))) .
($addUnderscore ? "_" : "");
}
/**
* @deprecated Устарело. Не использовать.
*/
public function setQueryParams(?QueryBuilder $query = null, ?AnySearch $search = null, ?Company $company,
?\DateTime $start = null, ?\DateTime $end = null, ?array $orderBy = null,
?array $statuses = null, ?Client $client = null, array $carWashes = null): QueryBuilder
{
return self::setQueryParamsByEm($this->em, $this->entityClassName, $this->dateFieldName,
$query, $search, $company, $start, $end, $orderBy, $statuses, $client, $this->searchDql, $carWashes);
}
/**
* @deprecated Устарело. Не использовать.
*/
public static function setQueryParamsByEm(EntityManagerInterface $em, string $entityClassName, string $dateFieldName = null,
?QueryBuilder $query = null, ?AnySearch $search = null, ?Company $company,
?\DateTime $start = null, ?\DateTime $end = null, ?array $orderBy = null,
?array $statuses = null, ?Client $client = null, string $searchDql = null,
array $carWashes = null): QueryBuilder
{
$lostClients = $search && $search->isLost() && $search->getLostDays();
if (!$query) {
$query = $em->getRepository($entityClassName)->createQueryBuilder('item');
}
if ($company) {
$query->where('item.company = :company')
->setParameter("company", $company);
}
if ($carWashes && !$lostClients) {
$query->andWhere($query->expr()->in("carWash_", ":carWashes"))->setParameter("carWashes", $carWashes);
}
if ($search && $search->getSearch() && $search->getSearch() != '') {
$query->andWhere($searchDql ?? 'item.id = :text or item.name like :text_like')
->setParameter('text', $search->getSearch())
->setParameter('text_like', '%' . $search->getSearch() . '%');
}
if ($start && $end && (!$search || !$search->isLost())) {
$query->andWhere("item.{$dateFieldName} >= :start and item.{$dateFieldName} <= :end")
->setParameter('start', $start)
->setParameter('end', $end);
}
if ($lostClients) {
$ids = [0];
$now = new \DateTime();
foreach ($em->getRepository(Client::class)->findBy(['company' => $company]) as $tmpClient) {
/** @var Session $session */
$session = $em->getRepository(Session::class)->findOneBy(array_merge(['client' => $tmpClient],
(!$carWashes ? [] : ['machine' => CarWashService::getCarWashesMachines($carWashes, false)])), ['id' => 'DESC']);
if ($session) {
$dt = clone $session->getEndTime();
$dt->modify('+'. $search->getLostDays() .' days');
if ($dt < $now) {
$ids[] = $tmpClient->getId();
$search->clientLastSessionsDates[$tmpClient->getId()] = $session->getEndTime();
}
}
}
$query->andWhere('item.id in (:ids)')
->setParameter('ids', $ids);
}
if ($statuses) {
if (!is_array($statuses)) {
$statuses = [$statuses];
}
$query->andWhere($query->expr()->in('item.status', ':statuses'))
->setParameter('statuses', $statuses);
}
if ($client) {
$query->andWhere('item.client = :client')
->setParameter("client", $client);
}
if ($orderBy) {
$query->orderBy("item.{$orderBy[0]}", $orderBy[1]);
}
return $query;
}
/**
* @deprecated Устарело. Не использовать.
* @psalm-param array<string, string>|null $orderBy
* @return array
*/
public function getItemsBySearchWithPagination(?AnySearch $search = null, ?Company $company = null,
?\DateTime $start = null, ?\DateTime $end = null, ?array $orderBy = null,
?array $statuses = null, $queryCallback = null, array $carWashes = null,
Client $client = null, array $options = null, RouteParamObjects $routeObjects = null): array
{
if ($routeObjects && $routeObjects->getCarWash()) {
$carWashes = [$routeObjects->getCarWash()];
}
$query = $this->createQuery($search, $company, $start, $end, $orderBy, $statuses, $client, $queryCallback, true,
$carWashes);
$result = Paginator::getResult($query, ($search->getPage() && $search->getPage() > 0) ? $search->getPage() : 1,
Paginator::PER_PAGE, false);
if (isset($options['sortByPostId']) && $options['sortByPostId']) {
$result["list"] = $this->sortMachinesByPostId($result["list"]);
}
return $result;
}
/**
* @deprecated Устарело. Не использовать.
* @psalm-param array<string, string>|null $orderBy
*/
public function createQuery(?AnySearch $search = null, ?Company $company = null,
?\DateTime $start = null, ?\DateTime $end = null, ?array $orderBy = null,
?array $statuses = null, $client = null, $queryCallback = null, bool $setCommonSearchQueryParams = false,
array $carWashes = null): QueryBuilder
{
$query = $this->em->getRepository($this->entityClassName)->createQueryBuilder('item');
if ($setCommonSearchQueryParams) {
$this->setSearchQueryParams($query);
}
/** @var QueryBuilder $q */
$q = $this->setQueryParams($query, $search, $company, $start, $end, $orderBy, $statuses, $client, $carWashes);
if ($queryCallback) {
$queryCallback($q);
}
return $query;
}
/**
* @deprecated Устарело. Не использовать.
* @psalm-param array<string, string>|null $orderBy
* @return array
*/
public function getQueryBySearchWithPagination(?AnySearch $search = null, ?Company $company = null,
?\DateTime $start = null, ?\DateTime $end = null, ?array $orderBy = null,
?array $statuses = null): QueryBuilder
{
$query = $this->em->getRepository($this->entityClassName)->createQueryBuilder('item');
$this->setSearchQueryParams($query);
$q = $this->setQueryParams($query, $search, $company, $start, $end, $orderBy, $statuses);
return $q;
}
/**
* @deprecated Устарело. Не использовать.
* @param $fieldName
* @param AnySearch $search
* @param Company|null $company
* @param \DateTime|null $start
* @param \DateTime|null $end
* @return float|int|mixed|string
* @throws \Doctrine\ORM\NoResultException
* @throws \Doctrine\ORM\NonUniqueResultException
*/
public function getSummaryByField($fieldName = "amount", ?AnySearch $search = null, ?Company $company = null,
?\DateTime $start = null, ?\DateTime $end = null, ?Client $client = null,
?array $statuses = null, $queryCallback = null, bool $setCommonSearchParams = false)
{
$q = $this->createQuery($search, $company, $start, $end, null, $statuses, $client, $queryCallback, $setCommonSearchParams);
$q->select("sum(item.$fieldName)");
$result = $q->getQuery()->getSingleScalarResult();
if ($result === null) {
$result = 0;
}
return $result;
}
/**
* @param string|array $whereConditions
* @param array|null $joinConditions
* @param string|null $select
* @param string|null $groupBy
* @return array
*/
public function getByExpression($whereConditions = [], array $joinConditions = null, string $select = null,
string $groupBy = null): array
{
return self::getByExpressionWithEm($this->em, $this->entityClassName, $this->entityAlias, $whereConditions,
$joinConditions, $select, $groupBy);
}
/**
* @param array $whereConditions
* @param array|null $joinConditions
* @param string|null $select
* @param string|null $groupBy
* @return mixed
*/
public function getOnyByExpression(array $whereConditions = [], array $joinConditions = null, string $select = null,
string $groupBy = null)
{
$result = self::getByExpressionWithEm($this->em, $this->entityClassName, $this->entityAlias, $whereConditions,
$joinConditions, $select, $groupBy);
if ($result == []) {
return null;
} else {
if (count($result) > 1) {
throw new \Exception('Found more than one');
}
return $result[0];
}
}
/**
* @param array $whereConditions
* @param array|null $joinConditions
* @param string|null $select
* @param string|null $groupBy
* @return QueryBuilder
*/
public function getQueryByExpression(array $whereConditions = [], array $joinConditions = null, string $select = null,
string $groupBy = null)
{
return self::getQueryByExpressionWithEm($this->em, $this->entityClassName, $this->entityAlias, $whereConditions,
$joinConditions, $select, $groupBy);
}
/**
* @deprecated Устарело. Не использовать.
* @param EntityManagerInterface $em
* @param string $entityClassName
* @param string|null $entityAlias
* @param string|array $whereConditions
* @param array|null $joinExpressions
* @param string|null $select
* @param string|null $groupBy
* @return array
*/
public static function getByExpressionWithEm(EntityManagerInterface $em, string $entityClassName, string $entityAlias = null,
$whereConditions = [], $joinExpressions = null, string $select = null,
string $groupBy = null, int $limit = 0): array
{
$q = self::getQueryByExpressionWithEm($em, $entityClassName, $entityAlias, $whereConditions, $joinExpressions, $select, $groupBy, $limit);
$result = $q->getQuery()->getResult();
return $result;
}
/**
* @deprecated Устарело. Не использовать.
* @param string|array $whereConditions
* [Key = 'left expression part', value = 'expression value'].
* Example: [
* "bonusPercentUpdatedAt !=" => "null",
* "status" => [Status::STATUS1, Status::STATUS2]
* ]
*
* @param array|null $joinExpressions
* ['joinEntityName', 'joinEntityCondition', 'joinEntityWhereExpression', 'joinEntityWhereValue']
* Example: ['cashbackRule', "id = client_.cashbackRule", 'status =', Status::STATUS_ACTIVE]
*
* @return QueryBuilder
*/
public static function getQueryByExpressionWithEm(EntityManagerInterface $em, string $entityClassName, string $entityAlias = null,
$whereConditions = [], array $joinExpressions = null, string $select = null,
string $groupBy = null, int $limit = 0)
{
if (!$entityAlias) {
$entityAlias = self::getEntityAlias(self::getEntityShortName($entityClassName));
}
$q = $em->getRepository($entityClassName)->createQueryBuilder($entityAlias);
if ($select) {
$q->select($select);
}
if ($joinExpressions) {
if (!is_array($joinExpressions[0])) {
$joinExpressions = [$joinExpressions];
}
$joinParamIndex = 0;
foreach ($joinExpressions as $joinParams) {
$joinEntity = $joinParams[0];
$joinEntityAlias = "{$joinEntity}_";
$joinCondition = $joinParams[1];
$joinWhereExpression = $joinParams[2] ?? null;
$joinWhereValue = $joinParams[3] ?? null;
$q = $q->innerJoin("$entityAlias.$joinEntity", $joinEntityAlias, "WITH", "$joinEntityAlias.$joinCondition");
$joinParamIndex++;
$joinParamName = "joinParam$joinParamIndex";
if ($joinWhereExpression) {
$q->andWhere("$joinEntityAlias.$joinWhereExpression :$joinParamName")
->setParameter($joinParamName, $joinWhereValue);
}
}
}
if (is_string($whereConditions)) {
$q->where($whereConditions);
} else {
$paramIndex = 0;
foreach ($whereConditions as $leftExpressionPart => $value) {
$or = false;
if (str_starts_with(strtolower($leftExpressionPart), "or ")) {
$or = true;
$leftExpressionPart = substr($leftExpressionPart, 3);
}
$paramIndex++;
$paramName = "param$paramIndex";
if (is_object($value) && strpos(get_class($value), "App\Entity") !== 0 && method_exists($value, "toArray")) {
$value = $value->toArray();
}
if ($or) {
if (is_array($value)) {
$q
->orWhere($q->expr()->in("$entityAlias.$leftExpressionPart", ":$paramName"))
->setParameter($paramName, $value);
} else {
if ($value === null) {
$q
->orWhere("$entityAlias.$leftExpressionPart null");
} else {
$q
->orWhere("$entityAlias.$leftExpressionPart :$paramName")
->setParameter($paramName, $value);
}
}
} else {
if (is_array($value)) {
$q
->andWhere($q->expr()->in("$entityAlias.$leftExpressionPart", ":$paramName"))
->setParameter($paramName, $value);
} else {
if ($value === null) {
$q
->andWhere("$entityAlias.$leftExpressionPart null");
} else {
$q
->andWhere("$entityAlias.$leftExpressionPart :$paramName")
->setParameter($paramName, $value);
}
}
}
}
}
if ($groupBy) {
$q->groupBy($groupBy);
}
if ($limit) {
$q->setMaxResults($limit);
}
return $q;
}
/**
* @param array $whereConditions
* @param array|null $joinConditions
* @param string|null $select
* @param string|null $groupBy
* @return array
*/
public function getBySqlExpression(array $whereConditions = [], array $joinConditions = null, string $select = null,
string $groupBy = null, string $tableName = null, int $limit = null): array
{
return self::getBySqlExpressionWithEm($this->em, $this->entityClassName,
(!$tableName ? $this->entityAlias : self::getEntityAliasByTableName($tableName)),
$whereConditions, $joinConditions, $select, $groupBy, ($tableName ?? $this->tableName), $limit);
}
/**
* @deprecated Устарело. Не использовать.
* @param array $whereConditions
* @param array|null $joinConditions
* @param string|null $select
* @param string|null $groupBy
* @return \Doctrine\DBAL\Query\QueryBuilder
*/
public function getQueryBySqlExpression(array $whereConditions = [], array $joinConditions = null, string $select = null,
string $groupBy = null, string $tableName = null, int $limit = null)
{
return self::getQueryBySqlExpressionWithEm($this->em, $this->entityClassName,
(!$tableName ? $this->entityAlias : self::getEntityAliasByTableName($tableName)), $whereConditions,
$joinConditions, $select, $groupBy, ($tableName ?? $this->tableName), $limit);
}
/**
* @deprecated Устарело. Не использовать.
* @param EntityManagerInterface $em
* @param string $entityClassName
* @param string|null $entityAlias
* @param array $whereConditions
* @param array|null $joinExpressions
* @param string|null $select
* @param string|null $groupBy
*/
public static function getBySqlExpressionWithEm(EntityManagerInterface $em, string $entityClassName, string $entityAlias = null,
array $whereConditions = [], array $joinExpressions = null, string $select = null,
string $groupBy = null, string $tableName = null, int $limit = null): array
{
$q = self::getQueryBySqlExpressionWithEm($em, $entityClassName, $entityAlias, $whereConditions, $joinExpressions, $select, $groupBy, $tableName, $limit);
$stmt = $q->execute();
$result = is_bool($stmt) ? $q->fetchAll() : $stmt->fetchAll();
return $result;
}
/**
* @deprecated Устарело. Не использовать.
* @param array $whereConditions
* [Key = 'left expression part', value = 'expression value'].
* Example: [
* "bonusPercentUpdatedAt !=" => "null",
* "status" => [Status::STATUS1, Status::STATUS2]
* ]
*
* @param array|null $joinExpressions
* ['joinTableName', 'joinColumnCondition', 'joinColumnWhereExpression', 'joinColumnWhereValue', 'joinType']
* Example: ['client', "id = bonus.client_id", 'company_id =', 116, "left"]
*
* @return \Doctrine\DBAL\Query\QueryBuilder
*/
public static function getQueryBySqlExpressionWithEm(EntityManagerInterface $em, string $entityClassName, string $entityAlias = null,
array $whereConditions = [], array $joinExpressions = null, string $select = null,
string $groupBy = null, string $tableName = null, int $limit = null)
{
if (!$entityAlias) {
$entityAlias = self::getEntityAlias(self::getEntityShortName($entityClassName));
}
if (!$tableName) {
throw new \Exception("\$tableName is not set");
}
$connection = $em->getConnection();
$q = $connection->createQueryBuilder()
->select($select ?? "$entityAlias.id")
->from($tableName, $entityAlias);
if ($joinExpressions) {
if (!is_array($joinExpressions[0])) {
$joinExpressions = [$joinExpressions];
}
foreach ($joinExpressions as $joinParams) {
$joinTableName = $joinParams[0];
$joinEntityAlias = self::getEntityAliasByTableName($joinTableName);
$joinCondition = $joinParams[1];
$joinWhereExpression = $joinParams[2] ?? null;
$joinWhereValue = $joinParams[3] ?? null;
$preparedValue = self::prepareSqlValue($joinWhereValue);
$joinType = $joinParams[4] ?? null;
switch ($joinType) {
case null:
case "inner":
$q = $q->innerJoin($entityAlias, $joinTableName, $joinEntityAlias, "$joinEntityAlias.$joinCondition");
break;
case "left":
$q = $q->leftJoin($entityAlias, $joinTableName, $joinEntityAlias, "$joinEntityAlias.$joinCondition");
break;
default:
throw new \Exception("Unknown: $joinType" . ". Entity: " . $entityClassName);
}
if ($joinWhereExpression) {
if (is_array($joinWhereValue)) {
$q->andWhere("$joinEntityAlias.$joinWhereExpression in ($preparedValue)");
} else {
$q->andWhere("$joinEntityAlias.$joinWhereExpression $preparedValue");
}
}
}
}
foreach ($whereConditions as $leftExpressionPart => $value) {
$or = false;
if (str_starts_with(strtolower($leftExpressionPart), "or ")) {
$or = true;
$leftExpressionPart = substr($leftExpressionPart, 3);
}
$isFullExpression = is_numeric($leftExpressionPart);
$preparedValue = self::prepareSqlValue($value);
if ($isFullExpression) {
$q->andWhere($value);
} else {
if ($or) {
if (is_array($value)) {
$q->orWhere("$entityAlias.$leftExpressionPart in ($preparedValue)");
} else {
$q->orWhere("$entityAlias.$leftExpressionPart $preparedValue");
}
} else {
if (is_array($value)) {
$q->andWhere("$entityAlias.$leftExpressionPart in ($preparedValue)");
} else {
$q->andWhere("$entityAlias.$leftExpressionPart $preparedValue");
}
}
}
}
if ($groupBy) {
$q->groupBy($groupBy);
}
if ($limit) {
$q->setMaxResults($limit);
}
return $q;
}
/**
* @param $value
* @return mixed|string|int
*/
private static function prepareSqlValue($value)
{
if ($value === null) {
$value = "null";
} else if (is_string($value) && !str_starts_with($value, "'") && !str_starts_with($value, "\"")) {
$value = "'" . $value . "'";
} else if (is_array($value)) {
$value = implode(", ", $value);
}
return $value;
}
/**
* @deprecated Устарело. Не использовать.
*/
public function getBy(array $criteria, ?Company $company = null): array
{
if ($company) {
$criteria['company'] = $company;
}
return $this->em->getRepository($this->entityClassName)->findBy($criteria);
}
/**
* @deprecated Устарело. Не использовать.
*/
public function getOneBy(array $criteria, ?Company $company = null)
{
if ($company) {
$criteria['company'] = $company;
}
return $this->em->getRepository($this->entityClassName)->findOneBy($criteria);
}
public function getMachineIds(array $machines, $addZeroId = false): array
{
$result = array_map(function ($machine) {return $machine->getId();}, $machines);
if ($addZeroId) {
array_unshift($result, 0);
}
return $result;
}
public function getMultiDimensionalSqlResultsByMachineIdAndDate(array $sqlResults, array $additionalFieldsBefore = null,
array $additionalFieldsAfter = null): array
{
return DoctrineUtils::getMultiDimensionalSqlResults($sqlResults,
array_merge(($additionalFieldsBefore ?? []), ["machine_id", "date"], ($additionalFieldsAfter ?? [])));
}
public function getSessionsServiceSecSumSelectSql(string $tableAlias): string
{
$select = '';
for ($num = 1; $num <= Machine::maxServiceNum; $num++) {
$select .= ($select ? ",\n" : '') . "SUM($tableAlias.service{$num}sec) as service{$num}sec";
}
return $select;
}
public function getSessionServiceSecSumSelectSql(string $tableAlias, bool $paidPauseSec = false): string
{
$select = '';
for ($num = 1; $num <= Machine::maxServiceNum; $num++) {
$select .= ($select ? " + " : '') . "$tableAlias.service{$num}sec";
}
if ($paidPauseSec) {
$select .= " + $tableAlias.paid_pause_sec";
}
return $select;
}
public function getSessionAmountsSumSelectSql(string $tableAlias): string
{
$arrayFields = ['coin_amount', 'note_amount', 'no_cash_amount', 'cash_sess', 'card_sess', 'bonus_for_session'];
$select = "";
foreach ($arrayFields as $field) {
$select .= ($select ? ",\n " : "") . "IFNULL(SUM($tableAlias.$field), 0) as $field";
}
return $select;
}
public function getMachinePaymentsSummary(array $data): float
{
return $data["payments_qr_amount"] + $data["payments_yandex_amount"];
}
public function getMachinesMethodParameter($machine)
{
$machines = $machine;
if (!is_array($machines)) {
$machines = [$machine];
}
return $machines;
}
/**
* @return null
*/
public function getEntityClassName()
{
return $this->entityClassName;
}
public function getIncomeMoneyFields(bool $refusual = false): array
{
$result = ["coin_amount", "note_amount", "no_cash_amount", "ping_amount", "qr_amount"];
if ($refusual) {
$result[] = "refusual";
}
return $result;
}
public function getAllMoneyFields(): array
{
return ["note_amount", "no_cash_amount", "payments_qr_amount", "payments_yandex_amount", "refusual",
"gross_profit", "server_bonus", "bonus_for_session", "card_sess"];
}
public function getServiceSecFields(): array
{
if (self::$serviceSecFields) {
return self::$serviceSecFields;
}
$result = [];
for ($i = 1; $i <= Machine::maxServiceNum; $i++) {
$result[] = "service{$i}sec";
}
$result[] = "paid_pause_sec";
self::$serviceSecFields = $result;
return $result;
}
public function createOrGetDefault(array $params)
{
$item = $this->getFirst($params);
if (!$item) {
$item = (new $this->entityClassName());
foreach ($params as $key => $value) {
$setter = 'set' . ucfirst($key);
$item->$setter($value);
}
$this->saveDefault($item);
}
return $item;
}
public function saveDefault($item)
{
$this->em->persist($item);
$this->em->flush();
}
public function removeDefault($item, $flush = true)
{
$this->em->remove($item);
if ($flush) {
$this->em->flush();
}
}
public function serializeDefault($item, array $includeFields = null, array $excludeFields = null): string
{
return $this->serializer->serialize($item, 'json', [
'attributes' => $includeFields,
'ignored_attributes' => $excludeFields
]);
}
public function getSameItemCheckFields(): array
{
throw new \Exception("Not implemented");
}
public function hasSameDefault($item): bool
{
$fields = $this->getSameItemCheckFields();
$criteria = [];
foreach ($fields as $field) {
$getter = 'get' . ucfirst($field);
if ($item->$getter() !== null) {
$criteria[$field] = $item->$getter();
}
}
$same = $this->getDefault($criteria);
foreach ($same as $sameItem) {
if ($sameItem->getId() != $item->getId()) {
return true;
}
}
return false;
}
public function removeForeignKeyConstraintEntities($entity, $foreignEntity, string $foreignEntityField)
{
if ($foreignEntity) {
$this->em->remove($foreignEntity);
}
if ($entity) {
$setter = 'set' . ucfirst($foreignEntityField);
$entity->$setter(null);
$this->em->flush();
} else {
$this->em->flush();
}
$this->removeDefault($entity);
}
}