This repository has been archived on 2026-07-19. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
espocrm-base/application/Espo/ORM/RDBSelectBuilder.php
T
Yuri Kuznetsov 904830579c orm refactoring
2020-07-31 17:49:37 +03:00

375 lines
10 KiB
PHP

<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\ORM;
use Espo\Core\Exceptions\Error;
use Espo\ORM\{
Repositories\Findable,
Collection,
Entity,
};
/**
* Builds select parameters for an RDB repository and invokes findable methods.
*/
class RDBSelectBuilder implements Findable
{
protected $whereClause = [];
protected $havingClause = [];
protected $params = [];
protected $entityManager;
protected $repository;
protected $entityType = null;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function from(string $entityType) : self
{
if ($this->repository) {
throw new Error("SelectBuilder: Method 'from' can be called only once.");
}
$this->entityType = $entityType;
$this->repository = $this->entityManager->getRepository($entityType);
return $this;
}
protected function isExecutable() : bool
{
return (bool) $this->entityType;
}
protected function processExecutableCheck()
{
if (!$this->isExecutable()) {
throw new Error("SelectBuilder: Method 'from' must be called.");
}
}
public function find(array $params = []) : Collection
{
$this->processExecutableCheck();
$params = $this->getMergedParams($params);
return $this->repository->find($params);
}
public function findOne(array $params = []) : ?Entity
{
$this->processExecutableCheck();
$params = $this->getMergedParams($params);
return $this->repository->findOne($params);
}
public function count(array $params = []) : int
{
$this->processExecutableCheck();
$params = $this->getMergedParams($params);
return $this->repository->count($params);
}
public function max(string $attribute)
{
$this->processExecutableCheck();
$params = $this->getMergedParams();
return $this->repository->max($attribute, $params);
}
public function min(string $attribute)
{
$this->processExecutableCheck();
$params = $this->getMergedParams();
return $this->repository->min($attribute, $params);
}
public function sum(string $attribute)
{
$this->processExecutableCheck();
$params = $this->getMergedParams();
return $this->repository->sum($attribute, $params);
}
/**
* Add JOIN.
*
* @param string|array $relationName A relationName or table. A relationName is in camelCase, a table is in CamelCase.
*
* Usage options:
* * `join(string $relationName)`
* * `join(array $joinDefinitionList)`
*
* Usage examples:
* ```
* ->join($relationName)
* ->join($relationName, $alias, $conditions)
* ->join([$relationName1, $relationName2, ...])
* ->join([[$relationName, $alias], ...])
* ->join([[$relationName, $alias, $conditions], ...])
* ```
*/
public function join($relationName, ?string $alias = null, ?array $conditions = null) : self
{
if (empty($this->params['joins'])) {
$this->params['joins'] = [];
}
if (is_array($relationName)) {
$joinList = $relationName;
foreach ($joinList as $item) {
$this->params['joins'][] = $item;
}
return $this;
}
if (is_null($alias) && is_null($conditions)) {
$this->params['joins'][] = $relationName;
return $this;
}
if (is_null($conditions)) {
$this->params['joins'][] = [$relationName, $alias];
return $this;
}
$this->params['joins'][] = [$relationName, $alias, $conditions];
return $this;
}
/**
* Add LEFT JOIN.
*
* @param string|array $relationName A relationName or table. A relationName is in camelCase, a table is in CamelCase.
*
* This method works the same way as `join` method.
*/
public function leftJoin($relationName, ?string $alias = null, ?array $conditions = null) : self
{
if (empty($this->params['leftJoins'])) {
$this->params['leftJoins'] = [];
}
if (is_array($relationName)) {
$joinList = $relationName;
foreach ($joinList as $item) {
$this->params['leftJoins'][] = $item;
}
return $this;
}
if (is_null($alias) && is_null($conditions)) {
$this->params['leftJoins'][] = $relationName;
return $this;
}
if (is_null($conditions)) {
$this->params['leftJoins'][] = [$relationName, $alias];
return $this;
}
$this->params['leftJoins'][] = [$relationName, $alias, $conditions];
return $this;
}
/**
* Set DISTINCT parameter.
*/
public function distinct() : self
{
$this->params['distinct'] = true;
return $this;
}
/**
* Set to return STH collection. Recommended fetching large number of records.
*/
public function sth() : self
{
$this->params['returnSthCollection'] = true;
return $this;
}
/**
* Add a WHERE clause.
*
* Two usage options:
* * `where(array $whereClause)`
* * `where(string $key, string $value)`
*/
public function where($param1 = [], $param2 = null) : self
{
if (is_array($param1)) {
$this->whereClause = $param1 + $this->whereClause;
} else {
if (!is_null($param2)) {
$this->whereClause[] = [$param1 => $param2];
}
}
return $this;
}
/**
* Add a HAVING clause.
*
* Two usage options:
* * `having(array $havingClause)`
* * `having(string $key, string $value)`
*/
public function having($param1 = [], $param2 = null) : self
{
if (is_array($param1)) {
$this->havingClause = $param1 + $this->havingClause;
} else {
if (!is_null($param2)) {
$this->havingClause[] = [$param1 => $param2];
}
}
return $this;
}
/**
* Apply ORDER.
*
* @param string|array $attribute An attribute to order by or order definitions as an array.
* @param bool|string $direction TRUE for DESC order.
*/
public function order($attribute = 'id', $direction = 'ASC') : self
{
$this->params['orderBy'] = $attribute;
$this->params['order'] = $direction;
return $this;
}
/**
* Apply OFFSET and LIMIT.
*/
public function limit(?int $offset = null, ?int $limit = null) : self
{
$this->params['offset'] = $offset;
$this->params['limit'] = $limit;
return $this;
}
/**
* Specify SELECT. Which attributes to select. All attributes are selected by default.
*/
public function select(array $select) : self
{
$this->params['select'] = $select;
return $this;
}
/**
* Specify GROUP BY.
*/
public function groupBy(array $groupBy) : self
{
$this->params['groupBy'] = $groupBy;
return $this;
}
/**
* Builds result select parameters.
*/
public function build() : RDBSelectParams
{
$this->processExecutableCheck();
return new RDBSelectParams($this->entityType, $this->getMergedParams());
}
protected function getMergedParams(array $params = []) : array
{
if (isset($params['whereClause'])) {
$params['whereClause'] = $params['whereClause'];
if (!empty($this->whereClause)) {
$params['whereClause'][] = $this->whereClause;
}
} else {
$params['whereClause'] = $this->whereClause;
}
if (!empty($params['havingClause'])) {
$params['havingClause'] = $params['havingClause'];
if (!empty($this->havingClause)) {
$params['havingClause'][] = $this->havingClause;
}
} else {
$params['havingClause'] = $this->havingClause;
}
if (empty($params['whereClause'])) {
unset($params['whereClause']);
}
if (empty($params['havingClause'])) {
unset($params['havingClause']);
}
if (!empty($params['leftJoins']) && !empty($this->params['leftJoins'])) {
foreach ($this->params['leftJoins'] as $j) {
$params['leftJoins'][] = $j;
}
}
if (!empty($params['joins']) && !empty($this->params['joins'])) {
foreach ($this->params['joins'] as $j) {
$params['joins'][] = $j;
}
}
$params = array_replace_recursive($this->params, $params);
return $params;
}
}