classes/Model/Request/Request.php

Summary

Maintainability
D
1 day
Test Coverage
<?php
/**
 * Fixin Framework
 *
 * Copyright (c) Attila Jenei
 *
 * http://www.fixinphp.com
 */

namespace Fixin\Model\Request;

use Fixin\Model\Entity\EntityInterface;
use Fixin\Model\Entity\EntitySetInterface;
use Fixin\Model\Repository\RepositoryInterface;
use Fixin\Model\Request\Where\WhereInterface;
use Fixin\Model\Storage\StorageResultInterface;
use Fixin\Resource\Prototype;
use Fixin\Support\DebugDescriptionTrait;

/**
 * @SuppressWarnings(PHPMD.TooManyPublicMethods)
 */
class Request extends Prototype implements RequestInterface
{
    use DebugDescriptionTrait;

    protected const
        COUNT_MASK = 'COUNT(%s)',
        JOIN_PROTOTYPE = '*\Model\Request\Join',
        THIS_SETS = [
            self::REPOSITORY => RepositoryInterface::class
        ],
        UNION_PROTOTYPE = '*\Model\Request\Union',
        WHERE_PROTOTYPE = '*\Model\Request\Where\Where';

    /**
     * @var string
     */
    protected $alias;

    /**
     * @var array
     */
    protected $columns = [];

    /**
     * @var bool
     */
    protected $distinctResult = false;

    /**
     * @var array
     */
    protected $groupBy = [];

    /**
     * @var WhereInterface|null
     */
    protected $having;

    /**
     * @var bool
     */
    protected $idFetchEnabled = true;

    /**
     * @var JoinInterface[]
     */
    protected $joins = [];

    /**
     * @var int|null
     */
    protected $limit;

    /**
     * @var integer
     */
    protected $offset = 0;

    /**
     * @var array
     */
    protected $orderBy = [];

    /**
     * @var RepositoryInterface
     */
    protected $repository;

    /**
     * @var int|null
     */
    protected $unionLimit;

    /**
     * @var integer
     */
    protected $unionOffset = 0;

    /**
     * @var array
     */
    protected $unionOrderBy = [];

    /**
     * @var Union[]
     */
    protected $unions = [];

    /**
     * @var WhereInterface|null
     */
    protected $where;

    public function __clone() {
        if ($this->having) {
            $this->having = clone $this->having;
        }

        if ($this->where) {
            $this->where = clone $this->where;
        }
    }

    protected function addJoin(string $type, RepositoryInterface $repository, string $left, string $operator, $right, string $alias = null): void
    {
        $this->addJoinItem($type, $repository, $this->resourceManager->clone(static::WHERE_PROTOTYPE, WhereInterface::class)->compare($left, $operator, $right, WhereInterface::TYPE_IDENTIFIER, WhereInterface::TYPE_IDENTIFIER), $alias);
    }

    protected function addJoinItem(string $type, RepositoryInterface $repository, WhereInterface $where = null, string $alias = null): void
    {
        $this->joins[] = $this->resourceManager->clone(static::JOIN_PROTOTYPE, JoinInterface::class, [
            JoinInterface::TYPE => $type,
            JoinInterface::REPOSITORY => $repository,
            JoinInterface::ALIAS => $alias ?? $repository->getName(),
            JoinInterface::WHERE => $where
        ]);
    }

    protected function addJoinWhere(string $type, RepositoryInterface $repository, callable $callback, string $alias = null): void
    {
        /** @var WhereInterface $where */
        $where = $this->resourceManager->clone(static::WHERE_PROTOTYPE, WhereInterface::class);
        $callback($where);

        $this->addJoinItem($type, $repository, $where, $alias);
    }

    protected function addUnion(string $type, RequestInterface $request): void
    {
        $this->unions[] = $this->resourceManager->clone(static::UNION_PROTOTYPE, UnionInterface::class, [
            UnionInterface::TYPE => $type,
            UnionInterface::REQUEST => $request
        ]);
    }

    public function count(): int
    {
        return $this->fetchValue($this->createExpression(sprintf(static::COUNT_MASK, implode(',', $this->columns ?: ['*']))));
    }

    public function createExpression(string $expression, array $parameters = []): ExpressionInterface
    {
        return $this->repository->createExpression($expression, $parameters);
    }

    /**
     * @return $this
     */
    public function crossJoin(RepositoryInterface $repository, string $alias = null): RequestInterface
    {
        $this->addJoinItem(JoinInterface::TYPE_CROSS, $repository, null, $alias);

        return $this;
    }

    public function delete(): int
    {
        return $this->repository->delete($this);
    }

    public function fetch(): EntitySetInterface
    {
        return $this->repository->select($this);
    }

    public function fetchColumn($column): StorageResultInterface
    {
        return $this->repository->selectColumn((clone $this)->setColumns([$column]));
    }

    public function fetchExistsValue(): bool
    {
        return $this->repository->selectExistsValue($this);
    }

    public function fetchFirst(): ?EntityInterface
    {
        return (clone $this)
            ->setLimit(1)
            ->fetch()
            ->current();
    }

    public function fetchRawData(): StorageResultInterface
    {
        return $this->repository->selectRawData($this);
    }

    public function fetchValue($column)
    {
        return (clone $this)
            ->setLimit(1)
            ->fetchColumn($column)
            ->current();
    }

    public function getAlias(): string
    {
        return $this->alias ?? ($this->alias = $this->repository->getName());
    }

    public function getColumns(): array
    {
        return $this->columns;
    }

    public function getGroupBy(): array
    {
        return $this->groupBy;
    }

    public function getHaving(): WhereInterface
    {
        return $this->having ?? ($this->having = $this->resourceManager->clone(static::WHERE_PROTOTYPE, WhereInterface::class));
    }

    public function getJoins(): array
    {
        return $this->joins;
    }

    public function getLimit(): ?int
    {
        return $this->limit;
    }

    public function getOffset(): int
    {
        return $this->offset;
    }

    public function getOrderBy(): array
    {
        return $this->orderBy;
    }

    public function getRepository(): RepositoryInterface
    {
        return $this->repository;
    }

    public function getUnionLimit(): ?int
    {
        return $this->unionLimit;
    }

    public function getUnionOffset(): int
    {
        return $this->unionOffset;
    }

    public function getUnionOrderBy(): array
    {
        return $this->unionOrderBy;
    }

    public function getUnions(): array
    {
        return $this->unions;
    }

    public function getWhere(): WhereInterface
    {
        return $this->where ?? ($this->where = $this->resourceManager->clone(static::WHERE_PROTOTYPE, WhereInterface::class));
    }

    public function hasHaving(): bool
    {
        return isset($this->having);
    }

    public function hasWhere(): bool
    {
        return isset($this->where);
    }

    public function insertInto(RepositoryInterface $repository): int
    {
        return $this->repository->insertInto($repository, $this);
    }

    public function isIdFetchEnabled(): bool
    {
        return $this->idFetchEnabled;
    }

    public function isDistinctResult(): bool
    {
        return $this->distinctResult;
    }

    /**
     * @param array|number|string $right
     * @return $this
     */
    public function join(RepositoryInterface $repository, string $left, string $operator, $right, string $alias = null): RequestInterface
    {
        $this->addJoin(JoinInterface::TYPE_INNER, $repository, $left, $operator, $right, $alias);

        return $this;
    }

    /**
     * @return $this
     */
    public function joinWhere(RepositoryInterface $repository, callable $callback, string $alias = null): RequestInterface
    {
        $this->addJoinWhere(JoinInterface::TYPE_INNER, $repository, $callback, $alias);

        return $this;
    }

    /**
     * @param array|number|string $right
     * @return $this
     */
    public function leftJoin(RepositoryInterface $repository, string $left, string $operator, $right, string $alias = null): RequestInterface
    {
        $this->addJoin(JoinInterface::TYPE_LEFT, $repository, $left, $operator, $right, $alias);

        return $this;
    }

    /**
     * @return $this
     */
    public function leftJoinWhere(RepositoryInterface $repository, callable $callback, string $alias = null): RequestInterface
    {
        $this->addJoinWhere(JoinInterface::TYPE_LEFT, $repository, $callback, $alias);

        return $this;
    }

    /**
     * @param array|number|string $right
     * @return $this
     */
    public function rightJoin(RepositoryInterface $repository, string $left, string $operator, $right, string $alias = null): RequestInterface
    {
        $this->addJoin(JoinInterface::TYPE_RIGHT, $repository, $left, $operator, $right, $alias);

        return $this;
    }

    /**
     * @return $this
     */
    public function rightJoinWhere(RepositoryInterface $repository, callable $callback, string $alias = null): RequestInterface
    {
        $this->addJoinWhere(JoinInterface::TYPE_RIGHT, $repository, $callback, $alias);

        return $this;
    }

    /**
     * @return $this
     */
    public function setAlias(string $alias): RequestInterface
    {
        $this->alias = $alias;

        return $this;
    }

    /**
     * @return $this
     */
    public function setColumns(array $columns): RequestInterface
    {
        $this->columns = $columns;

        return $this;
    }

    /**
     * @return $this
     */
    public function setDistinctResult(bool $distinctResult): RequestInterface
    {
        $this->distinctResult = $distinctResult;

        return $this;
    }

    /**
     * @return $this
     */
    public function setGroupBy(array $groupBy): RequestInterface
    {
        $this->groupBy = $groupBy;

        return $this;
    }

    /**
     * @return $this
     */
    public function setIdFetchEnabled(bool $idFetchEnabled): RequestInterface
    {
        $this->idFetchEnabled = $idFetchEnabled;

        return $this;
    }

    /**
     * @return $this
     */
    public function setLimit(?int $limit): RequestInterface
    {
        $this->limit = $limit;

        return $this;
    }

    /**
     * @return $this
     */
    public function setLimitForPage(int $page, int $itemsPerPage): RequestInterface
    {
        $this->offset = $page * $itemsPerPage;
        $this->limit = $itemsPerPage;

        return $this;
    }

    /**
     * @return $this
     */
    public function setOffset(int $offset): RequestInterface
    {
        $this->offset = $offset;

        return $this;
    }

    /**
     * @return $this
     */
    public function setOrderBy(array $orderBy): RequestInterface
    {
        $this->orderBy = $orderBy;

        return $this;
    }

    /**
     * @return $this
     */
    public function setUnionLimit(?int $unionLimit): RequestInterface
    {
        $this->unionLimit = $unionLimit;

        return $this;
    }

    /**
     * @return $this
     */
    public function setUnionLimitForPage(int $page, int $itemsPerPage): RequestInterface
    {
        $this->unionOffset = $page * $itemsPerPage;
        $this->unionLimit = $itemsPerPage;

        return $this;
    }

    /**
     * @return $this
     */
    public function setUnionOffset(int $unionOffset): RequestInterface
    {
        $this->unionOffset = $unionOffset;

        return $this;
    }

    /**
     * @return $this
     */
    public function setUnionOrderBy(array $unionOrderBy): RequestInterface
    {
        $this->unionOrderBy = $unionOrderBy;

        return $this;
    }

    /**
     * @return $this
     */
    public function union(RequestInterface $request): RequestInterface
    {
        $this->addUnion(UnionInterface::TYPE_NORMAL, $request);

        return $this;
    }

    /**
     * @return $this
     */
    public function unionAll(RequestInterface $request): RequestInterface
    {
        $this->addUnion(UnionInterface::TYPE_ALL, $request);

        return $this;
    }

    public function update(array $set): int
    {
        return $this->repository->update($set, $this);
    }
}