<?php
/**
* Manager
*
* @author Vincent van Waasbergen <v.vanwaasbergen@visualmedia.nl>
*/
namespace VisualMedia\LisaBundle\Component;
use Exception;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Tools\Pagination\Paginator;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\ResultSetMapping;
use VisualMedia\LisaBundle\Component\Interfaces\EntityInterface;
use VisualMedia\LisaBundle\Component\Interfaces\ManagerInterface;
use VisualMedia\LisaBundle\Doctrine\Arguments\GetResultsEventArgs;
use VisualMedia\LisaBundle\Doctrine\Arguments\ProcessConditionsEventArgs;
use VisualMedia\LisaBundle\Doctrine\Arguments\MoveEventArgs;
/**
* Manager
*/
abstract class Manager implements ManagerInterface
{
/**
* @var EntityManagerInterface
*/
protected $entityManager;
/**
* @var EventManager
*/
protected $eventManager;
/**
* @var string
*/
protected $entityName;
/**
* @var string
*/
protected $alias;
/**
* @var array
*/
protected $identifiers;
/**
* @var string
*/
public $querytype;
/**
* @param EntityManagerInterface $entityManager
*/
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
$this->eventManager = $entityManager->getEventManager();
$metaData = $entityManager->getClassMetadata(static::getEntityName());
$this->entityName = $metaData->getReflectionClass()->getName();
$explodedEntityName = explode('\\', $this->entityName);
$this->alias = sprintf('_%s', strtolower(end($explodedEntityName)));
$this->identifiers = $metaData->getIdentifier();
}
/**
* {@inheritdoc}
*/
public function getAlias(): string
{
return $this->alias;
}
/**
* {@inheritdoc}
*/
public function getFullFieldName($field): string
{
if (strpos($field, '.') !== false) {
return $field;
}
else {
return sprintf('%s.%s', $this->alias, $field);
}
}
/**
* {@inheritdoc}
*/
public function getEntityManager(): EntityManagerInterface
{
if ($this->entityManager === null) {
throw new Exception(sprintf('The entity manager has not been set for Manager "%s". Please make sure the constructor is called.', get_class($this)));
}
return $this->entityManager;
}
/**
* {@inheritdoc}
*/
public function initializeQueryBuilder($querytype = ManagerInterface::QUERYTYPE_MULTIPLE): QueryBuilder
{
$this->querytype = $querytype;
$qb = $this->getEntityManager()->createQueryBuilder();
$alias = $this->alias;
$identifier = $this->getFullFieldName($this->identifiers[0]);
if ($querytype === static::QUERYTYPE_SINGLE || $querytype === static::QUERYTYPE_MULTIPLE) {
$qb->select($alias);
$qb->from($this->entityName, $alias);
$qb->groupBy($identifier);
}
elseif ($querytype === static::QUERYTYPE_COUNT) {
$qb->select(sprintf('count(distinct %s)', $alias));
$qb->from($this->entityName, $alias);
}
elseif ($querytype === static::QUERYTYPE_DISTINCT) {
$qb->select(sprintf('DISTINCT %s', $identifier));
$qb->from($this->entityName, $alias);
}
elseif ($querytype === static::QUERYTYPE_MIN) {
$qb->select(sprintf('%s, MIN(%s)', $alias, $identifier));
$qb->from($this->entityName, $alias);
}
elseif ($querytype === static::QUERYTYPE_MAX) {
$qb->select(sprintf('%s, MAX(%s)', $alias, $identifier));
$qb->from($this->entityName, $alias);
}
return $qb;
}
/**
* {@inheritdoc}
*/
public function getQB(string $querytype, ManagerData $data = null): QueryBuilder
{
$data = $data ?? new ManagerData();
$data->setQuerytype($querytype);
$qb = $this->initializeQueryBuilder($data->getQuerytype(), $data->getConditions());
$this->processConditions($qb, $data->getConditions());
$this->eventManager->dispatchEvent('processConditions', new ProcessConditionsEventArgs($this, $qb, $data->getConditions()));
if ($data->getQuerytype() === static::QUERYTYPE_MULTIPLE) {
$this->processOrdering($qb, $data->getConditions(), $data->getOrdering());
}
return $qb;
}
/**
* {@inheritdoc}
*/
public function getQuery(QueryBuilder $qb, array $conditions = array()): Query
{
$query = $qb->getQuery();
return $query;
}
/**
* {@inheritdoc}
*/
public function getResults(Query $query, ManagerData $data = null): array
{
$result = array();
if ($this->querytype === static::QUERYTYPE_MULTIPLE) {
$paginator = new Paginator($query, $fetchJoinCollection = true);
foreach ($paginator as $row) {
$result[] = $row;
}
}
else {
$result = $query->getResult();
}
$this->eventManager->dispatchEvent('getResults', new GetResultsEventArgs($this, $result, $data ?? new ManagerData()));
return $result;
}
/**
* {@inheritdoc}
*/
public function find($id, $lockMode = null, $lockVersion = null)
{
return $this->getEntityManager()->find(static::getEntityName(), $id, $lockMode, $lockVersion);
}
/**
* {@inheritdoc}
*/
public function findAll()
{
return $this->getIndex();
}
/**
* {@inheritdoc}
*/
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null)
{
return $this->getIndex(new ManagerData($criteria, $orderBy, $limit, $offset));
}
/**
* {@inheritdoc}
*/
public function findOneBy(array $criteria = array(), array $orderBy = array()): ?EntityInterface
{
return $this->getFirst(new ManagerData($criteria, $orderBy));
}
/**
* {@inheritdoc}
*/
public function getIndex(ManagerData $data = null): array
{
$data = $data ?? new ManagerData();
$qb = $this->getQB(static::QUERYTYPE_MULTIPLE, $data);
$query = $this->getQuery($qb, $data->getConditions());
if (null !== $limit = $data->getLimit()) {
$query->setMaxResults($limit);
$query->setFirstResult($data->getOffset());
}
$result = $this->getResults($query, $data);
return $result;
}
/**
* {@inheritdoc}
*/
public function getFirst(ManagerData $data = null): ?EntityInterface
{
$data = $data ?? new ManagerData();
$qb = $this->getQB(static::QUERYTYPE_SINGLE, $data);
$query = $this->getQuery($qb, $data->getConditions());
$result = $this->getResults($query, $data);
if (count($result) == 0) {
return null;
}
return $result[0];
}
/**
* {@inheritdoc}
*/
public function getCount(ManagerData $data = null): int
{
$data = $data ?? new ManagerData();
$qb = $this->getQB(static::QUERYTYPE_COUNT, $data);
$query = $this->getQuery($qb, $data->getConditions());
@list($entity, $count) = $query->getOneOrNullResult();
return (int)$count;
}
/**
* {@inheritdoc}
*/
public function getDistinct($field, ManagerData $data = null): array
{
$data = $data ?? new ManagerData();
$originalIdentifiers = $this->identifiers;
$this->identifiers = array($field);
$qb = $this->getQB(static::QUERYTYPE_DISTINCT, $data);
$query = $this->getQuery($qb, $data->getConditions());
$result = $this->getResults($query, $data);
$this->identifiers = $originalIdentifiers;
@list($field, $alias) = array_reverse(explode('.', $field));
$processed = array();
foreach ($result as $index=>$row) {
$processed[$index] = $row[$field] ?? null;
}
return $processed;
}
/**
* {@inheritdoc}
*/
public function getMin($field, ManagerData $data = null): int
{
$data = $data ?? new ManagerData();
$originalIdentifiers = $this->identifiers;
$this->identifiers = array($field);
$qb = $this->getQB(static::QUERYTYPE_MIN, $data);
$query = $this->getQuery($qb, $data->getConditions());
@list($entity, $min) = $query->getOneOrNullResult();
$this->identifiers = $originalIdentifiers;
return (int)$min;
}
/**
* {@inheritdoc}
*/
public function getMax($field, ManagerData $data = null): int
{
$data = $data ?? new ManagerData();
$originalIdentifiers = $this->identifiers;
$this->identifiers = array($field);
$qb = $this->getQB(static::QUERYTYPE_MAX, $data);
$query = $this->getQuery($qb, $data->getConditions());
@list($entity, $max) = $query->getOneOrNullResult();
$this->identifiers = $originalIdentifiers;
return (int)$max;
}
/**
* {@inheritdoc}
*/
public function processOrdering(QueryBuilder &$qb, array $options, ?array $ordering = null): void
{
if($ordering !== null) {
foreach ($ordering as $key=>$value) {
if (!empty($key) && is_string($key) && in_array($value, array(static::ORDER_DIR_ASC, static::ORDER_DIR_DESC))) {
$qb->addOrderBy($this->getFullFieldName($key), $value);
}
}
}
}
/**
* {@inheritdoc}
*/
public function persist(EntityInterface $entity): void
{
$this->eventManager->dispatchEvent('persist', new LifecycleEventArgs($entity, $this->getEntityManager()));
$this->getEntityManager()->persist($entity);
}
/**
* {@inheritdoc}
*/
public function remove(EntityInterface $entity): void
{
$this->getEntityManager()->remove($entity);
}
/**
* {@inheritdoc}
*/
public function flush(): void
{
$this->getEntityManager()->flush();
}
/**
* {@inheritdoc}
*/
public function execute(string $sql, array $parameters = array(), ResultSetMapping $rsm = null)
{
$sql = trim(preg_replace('/\s+/', ' ', $sql));
// Set parameters on sql.
foreach ($parameters as $key=>$value) {
if (is_array($value)) {
$value = sprintf('(%s)', implode(',', array_map(function($v) {
return preg_match("/[a-z]/i", $v) ? sprintf('\'%s\'', $v) : $v;
}, $value)));
}
elseif (preg_match("/[a-z]/i", $value)) {
$value = sprintf('\'%s\'', $value);
}
$sql = str_replace(sprintf(':%s', $key), $value, $sql);
}
// Get connection.
$connection = $this->getEntityManager()->getConnection();
// Execute select query through a native query.
if ((true === $select = strpos($sql, 'SELECT') !== false) && (true === $native = $parameters['native'] ?? true)) {
$rsm = $rsm ?: new ResultSetMapping();
$query = $this->getEntityManager()->createNativeQuery($sql, $rsm);
return $query->getResult();
}
// Execute query through the connection.
if ($select) {
return $connection->executeQuery($sql)->fetchAll();
}
else {
$connection->executeUpdate($sql);
}
}
/**
* {@inheritdoc}
*/
public function moveDown(EntityInterface $entity = null): void
{
$this->eventManager->dispatchEvent('move', new MoveEventArgs($this, 'down', $entity));
}
/**
* {@inheritdoc}
*/
public function moveUp(EntityInterface $entity = null): void
{
$this->eventManager->dispatchEvent('move', new MoveEventArgs($this, 'up', $entity));
}
/**
* {@inheritdoc}
*/
public function getOrderingQueryBuilder(EntityInterface $entity = null): QueryBuilder
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('e');
$qb->from($this->getEntityName(), 'e');
if ($entity !== null && property_exists($entity, 'parent')) {
if ($entity->parent) {
$qb->andWhere(sprintf('e.parent = %d', $entity->parent->id));
}
else {
$qb->andWhere(sprintf('e.parent IS NULL'));
}
}
return $qb;
}
/**
* {@inheritdoc}
*/
public function getClassName()
{
return static::getEntityName();
}
}