From 9d5e1b7d3351139fed1b19da3ee786bcc6f4e369 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Sun, 16 Oct 2022 17:40:33 +0300 Subject: [PATCH] ref --- application/Espo/Controllers/Stream.php | 89 +++++++----- .../Espo/Core/Record/SearchParamsFetcher.php | 1 - application/Espo/Core/Select/SearchParams.php | 7 +- .../Espo/Core/Select/Where/Applier.php | 2 - application/Espo/Core/Select/Where/Item.php | 11 +- .../Espo/Core/Select/Where/Item/Type.php | 57 -------- application/Espo/Tools/Stream/FindParams.php | 90 ++++++++++++ application/Espo/Tools/Stream/Service.php | 136 +++++++----------- 8 files changed, 201 insertions(+), 192 deletions(-) create mode 100644 application/Espo/Tools/Stream/FindParams.php diff --git a/application/Espo/Controllers/Stream.php b/application/Espo/Controllers/Stream.php index 7b857e500d..fb086d61fa 100644 --- a/application/Espo/Controllers/Stream.php +++ b/application/Espo/Controllers/Stream.php @@ -31,12 +31,14 @@ namespace Espo\Controllers; use Espo\Core\Exceptions\BadRequest; -use Espo\Core\{ - Api\Request, - Exceptions\Forbidden, - Exceptions\NotFound, - Record\SearchParamsFetcher}; +use Espo\Core\Api\Request; +use Espo\Core\Exceptions\Forbidden; +use Espo\Core\Exceptions\NotFound; +use Espo\Core\Field\DateTime; +use Espo\Core\Record\SearchParamsFetcher; +use Espo\Entities\User as UserEntity; +use Espo\Tools\Stream\FindParams; use Espo\Tools\Stream\Service as Service; use stdClass; @@ -63,27 +65,35 @@ class Stream */ public function getActionList(Request $request): stdClass { - $params = $request->getRouteParams(); + $id = $request->getRouteParam('id'); + $scope = $request->getRouteParam('scope'); - $scope = $params['scope']; - $id = isset($params['id']) ? $params['id'] : null; + if ($scope === null) { + throw new BadRequest(); + } + + if ($id === null && $scope !== UserEntity::ENTITY_TYPE) { + throw new BadRequest("No ID."); + } $searchParams = $this->searchParamsFetcher->fetch($request); - $offset = $searchParams->getOffset(); - $maxSize = $searchParams->getMaxSize(); - $after = $request->getQueryParam('after'); $filter = $request->getQueryParam('filter'); $skipOwn = $request->getQueryParam('skipOwn') === 'true'; - $result = $this->service->find($scope, $id, [ - 'offset' => $offset, - 'maxSize' => $maxSize, - 'after' => $after, - 'filter' => $filter, - 'skipOwn' => $skipOwn, - ]); + /** @todo Use named params. */ + $findParams = new FindParams( + $searchParams, + $skipOwn, + $after ? + DateTime::fromString($after) : null, + $filter + ); + + $result = $scope === UserEntity::ENTITY_TYPE ? + $this->service->findUserStream($id, $findParams) : + $this->service->find($scope, $id ?? '', $findParams); return (object) [ 'total' => $result->getTotal(), @@ -98,30 +108,33 @@ class Stream */ public function getActionListPosts(Request $request): stdClass { - $params = $request->getRouteParams(); + $id = $request->getRouteParam('id'); + $scope = $request->getRouteParam('scope'); - $scope = $params['scope']; - $id = isset($params['id']) ? $params['id'] : null; - - $searchParams = $this->searchParamsFetcher->fetch($request); - - $offset = $searchParams->getOffset(); - $maxSize = $searchParams->getMaxSize(); - - $after = $request->getQueryParam('after'); - $where = $request->getQueryParams()['where'] ?? null; - - if ($where !== null && !is_array($where)) { + if ($scope === null) { throw new BadRequest(); } - $result = $this->service->find($scope, $id, [ - 'offset' => $offset, - 'maxSize' => $maxSize, - 'after' => $after, - 'filter' => 'posts', - 'where' => $where, - ]); + if ($id === null && $scope !== UserEntity::ENTITY_TYPE) { + throw new BadRequest("No ID."); + } + + $after = $request->getQueryParam('after'); + + $searchParams = $this->searchParamsFetcher->fetch($request); + + /** @todo Use named params. */ + $findParams = new FindParams( + $searchParams, + false, + $after ? + DateTime::fromString($after) : null, + FindParams::FILTER_POSTS + ); + + $result = $scope === UserEntity::ENTITY_TYPE ? + $this->service->findUserStream($id, $findParams) : + $this->service->find($scope, $id ?? '', $findParams); return (object) [ 'total' => $result->getTotal(), diff --git a/application/Espo/Core/Record/SearchParamsFetcher.php b/application/Espo/Core/Record/SearchParamsFetcher.php index f114f3e464..99d4f7f111 100644 --- a/application/Espo/Core/Record/SearchParamsFetcher.php +++ b/application/Espo/Core/Record/SearchParamsFetcher.php @@ -45,7 +45,6 @@ class SearchParamsFetcher private const MAX_SIZE_LIMIT = 200; private Config $config; - private TextMetadataProvider $textMetadataProvider; public function __construct(Config $config, TextMetadataProvider $textMetadataProvider) diff --git a/application/Espo/Core/Select/SearchParams.php b/application/Espo/Core/Select/SearchParams.php index af4d941005..e7fd0e0d5f 100644 --- a/application/Espo/Core/Select/SearchParams.php +++ b/application/Espo/Core/Select/SearchParams.php @@ -37,16 +37,15 @@ use stdClass; /** * Search parameters. + * + * @immutable */ class SearchParams { - /** - * @var array - */ + /** @var array */ private array $rawParams = []; public const ORDER_ASC = 'ASC'; - public const ORDER_DESC = 'DESC'; private function __construct() {} diff --git a/application/Espo/Core/Select/Where/Applier.php b/application/Espo/Core/Select/Where/Applier.php index f1525ac2be..81f0ce2b72 100644 --- a/application/Espo/Core/Select/Where/Applier.php +++ b/application/Espo/Core/Select/Where/Applier.php @@ -46,9 +46,7 @@ class Applier private string $entityType; private User $user; - private ConverterFactory $converterFactory; - private CheckerFactory $checkerFactory; public function __construct( diff --git a/application/Espo/Core/Select/Where/Item.php b/application/Espo/Core/Select/Where/Item.php index 553b23f987..d8fa2f0cf0 100644 --- a/application/Espo/Core/Select/Where/Item.php +++ b/application/Espo/Core/Select/Where/Item.php @@ -35,21 +35,18 @@ use Espo\Core\Select\Where\Item\Data\DateTime as DateTimeData; use InvalidArgumentException; use RuntimeException; +/** + * @immutable + */ class Item { public const TYPE_AND = Item\Type::AND; - public const TYPE_OR = Item\Type::OR; private string $type; - private ?string $attribute = null; - - /** - * @var mixed - */ + /** @var mixed */ private $value = null; - private ?Data $data = null; /** diff --git a/application/Espo/Core/Select/Where/Item/Type.php b/application/Espo/Core/Select/Where/Item/Type.php index 6de8c5c1f5..0cecd8d16b 100644 --- a/application/Espo/Core/Select/Where/Item/Type.php +++ b/application/Espo/Core/Select/Where/Item/Type.php @@ -32,118 +32,61 @@ namespace Espo\Core\Select\Where\Item; class Type { public const AND = 'and'; - public const OR = 'or'; - public const NOT = 'not'; - public const SUBQUERY_NOT_IN = 'subQueryNotIn'; - public const SUBQUERY_IN = 'subQueryIn'; - public const EXPRESSION = 'expression'; - public const IN = 'in'; - public const NOT_IN = 'notIn'; - public const EQUALS = 'equals'; - public const NOT_EQUALS = 'notEquals'; - public const ON = 'on'; - public const NOT_ON = 'notOn'; - public const LIKE = 'like'; - public const NOT_LIKE = 'notLike'; - public const STARTS_WITH = 'startsWith'; - public const ENDS_WITH = 'endsWith'; - public const CONTAINS = 'contains'; - public const GREATER_THAN = 'greaterThan'; - public const LESS_THAN = 'lessThan'; - public const GREATER_THAN_OR_EQUALS = 'greaterThanOrEquals'; - public const LESS_THAN_OR_EQUALS = 'lessThanOrEquals'; - public const AFTER = 'after'; - public const BEFORE = 'before'; - public const BETWEEN = 'between'; - public const ANY = 'any'; - public const NONE = 'none'; - public const IS_NULL = 'isNull'; - public const IS_NOT_NULL = 'isNotNull'; - public const IS_TRUE = 'isTrue'; - public const IS_FALSE = 'isFalse'; - public const TODAY = 'today'; - public const PAST = 'past'; - public const FUTURE = 'future'; - public const LAST_X_DAYS = 'lastXDays'; - public const NEXT_X_DAYS = 'nextXDays'; - public const OLDER_THAN_X_DAYS = 'olderThanXDays'; - public const AFTER_X_DAYS = 'afterXDays'; - public const CURRENT_MONTH = 'currentMonth'; - public const NEXT_MONTH = 'nextMonth'; - public const LAST_MONTH = 'lastMonth'; - public const CURRENT_QUARTER = 'currentQuarter'; - public const LAST_QUARTER = 'lastQuarter'; - public const CURRENT_YEAR = 'currentYear'; - public const LAST_YEAR = 'lastYear'; - public const CURRENT_FISCAL_YEAR = 'currentFiscalYear'; - public const LAST_FISCAL_YEAR = 'lastFiscalYear'; - public const CURRENT_FISCAL_QUARTER = 'currentFiscalQuarter'; - public const LAST_FISCAL_QUARTER = 'lastFiscalQuarter'; - public const ARRAY_ANY_OF = 'arrayAnyOf'; - public const ARRAY_NONE_OF = 'arrayNoneOf'; - public const ARRAY_ALL_OF = 'arrayAllOf'; - public const ARRAY_IS_EMPTY = 'arrayIsEmpty'; - public const ARRAY_IS_NOT_EMPTY = 'arrayIsNotEmpty'; - public const IS_LINKED_WITH = 'linkedWith'; - public const IS_NOT_LINKED_WITH = 'notLinkedWith'; - public const IS_LINKED_WITH_ALL = 'linkedWithAll'; - public const IS_LINKED_WITH_ANY = 'isLinked'; - public const IS_LINKED_WITH_NONE = 'isNotLinked'; } diff --git a/application/Espo/Tools/Stream/FindParams.php b/application/Espo/Tools/Stream/FindParams.php new file mode 100644 index 0000000000..a71819614d --- /dev/null +++ b/application/Espo/Tools/Stream/FindParams.php @@ -0,0 +1,90 @@ +searchParams = $searchParams; + $this->skipOwn = $skipOwn; + $this->after = $after; + $this->filter = $filter; + } + + public function getSearchParams(): SearchParams + { + return $this->searchParams; + } + + public function getMaxSize(): ?int + { + return $this->searchParams->getMaxSize(); + } + + public function getOffset(): ?int + { + return $this->searchParams->getOffset(); + } + + public function skipOwn(): bool + { + return $this->skipOwn; + } + + public function getAfter(): ?DateTime + { + return $this->after; + } + + public function getFilter(): ?string + { + return $this->filter; + } +} diff --git a/application/Espo/Tools/Stream/Service.php b/application/Espo/Tools/Stream/Service.php index 284d459154..7c72f06d00 100644 --- a/application/Espo/Tools/Stream/Service.php +++ b/application/Espo/Tools/Stream/Service.php @@ -388,22 +388,16 @@ class Service } /** - * @param array{ - * offset?: int|null, - * maxSize: int|null, - * skipOwn?: bool, - * where?: ?array, - * after?: ?string, - * filter?: ?string, - * } $params * @throws NotFound * @throws Forbidden * @return RecordCollection */ - public function findUserStream(string $userId, array $params): RecordCollection + public function findUserStream(?string $userId, FindParams $params): RecordCollection { - $offset = intval($params['offset'] ?? 0); - $maxSize = intval($params['maxSize']); + $userId ??= $this->user->getId(); + + $offset = $params->getOffset() ?? 0; + $maxSize = $params->getMaxSize(); $sqLimit = $offset + $maxSize + 1; @@ -412,15 +406,13 @@ class Service $this->entityManager->getRDBRepositoryByClass(User::class)->getById($userId); if (!$user) { - throw new NotFound(); + throw new NotFound("User not found."); } if (!$this->acl->checkUserPermission($user, 'user')) { - throw new Forbidden(); + throw new Forbidden("No user permission access."); } - $skipOwn = $params['skipOwn'] ?? false; - $teamIdList = $user->getTeamIdList(); $select = [ @@ -447,11 +439,14 @@ class Service $additionalQuery = null; - if (!empty($params['where'])) { - $searchParams = SearchParams::fromRaw([ - 'where' => $params['where'], - ]); + $searchParams = $params->getSearchParams(); + if ( + $searchParams->getWhere() || + $searchParams->getTextFilter() || + $searchParams->getPrimaryFilter() || + $searchParams->getBoolFilterList() !== [] + ) { $additionalQuery = $this->selectBuilderFactory ->create() ->from(Note::ENTITY_TYPE) @@ -780,7 +775,7 @@ class Service ->build(); } - if ($skipOwn) { + if ($params->skipOwn()) { foreach ($queryList as $i => $query) { $queryList[$i] = $this->entityManager ->getQueryBuilder() @@ -803,7 +798,7 @@ class Service ]) ->build(); - if ( + if ( (!$user->isPortal() || $user->isAdmin()) && !$user->isApi() ) { @@ -892,39 +887,24 @@ class Service } /** - * @param array{ - * offset?: int|null, - * maxSize: int|null, - * skipOwn?: bool, - * where?: ?array, - * after?: ?string, - * filter?: ?string, - * } $params * @return array */ - private function getUserStreamWhereClause(array $params, User $user): array + private function getUserStreamWhereClause(FindParams $params, User $user): array { $whereClause = []; - if (!empty($params['after'])) { - $whereClause[]['createdAt>'] = $params['after']; + if ($params->getAfter()) { + $whereClause[]['createdAt>'] = $params->getAfter()->getString(); } - if (!empty($params['filter'])) { - switch ($params['filter']) { - case 'posts': - $whereClause[]['type'] = Note::TYPE_POST; - - break; - - case 'updates': - $whereClause[]['type'] = [ - Note::TYPE_UPDATE, - Note::TYPE_STATUS, - ]; - - break; - } + if ($params->getFilter() === FindParams::FILTER_POSTS) { + $whereClause[]['type'] = Note::TYPE_POST; + } + else if ($params->getFilter() === FindParams::FILTER_UPDATES) { + $whereClause[]['type'] = [ + Note::TYPE_UPDATE, + Note::TYPE_STATUS, + ]; } return $whereClause; @@ -936,29 +916,17 @@ class Service } /** - * @param array{ - * offset?: int|null, - * maxSize: int|null, - * skipOwn?: bool, - * where?: ?array, - * after?: ?string, - * filter?: ?string, - * } $params * @throws NotFound * @throws Forbidden * @return RecordCollection */ - public function find(string $scope, ?string $id, array $params): RecordCollection + public function find(string $scope, string $id, FindParams $params): RecordCollection { if ($scope === User::ENTITY_TYPE) { - if (empty($id)) { - $id = $this->user->getId(); - } - - return $this->findUserStream($id, $params); + throw new Forbidden(); } - $entity = $this->entityManager->getEntity($scope, $id); + $entity = $this->entityManager->getEntityById($scope, $id); $onlyTeamEntityTypeList = $this->getOnlyTeamEntityTypeList($this->user); $onlyOwnEntityTypeList = $this->getOnlyOwnEntityTypeList($this->user); @@ -973,11 +941,14 @@ class Service $additionalQuery = null; - if (!empty($params['where'])) { - $searchParams = SearchParams::fromRaw([ - 'where' => $params['where'], - ]); + $searchParams = $params->getSearchParams(); + if ( + $searchParams->getWhere() || + $searchParams->getTextFilter() || + $searchParams->getPrimaryFilter() || + $searchParams->getBoolFilterList() !== [] + ) { $additionalQuery = $this->selectBuilderFactory ->create() ->from(Note::ENTITY_TYPE) @@ -1116,21 +1087,16 @@ class Service } } - if (!empty($params['filter'])) { - switch ($params['filter']) { - case 'posts': - $where['type'] = Note::TYPE_POST; + $filter = $params->getFilter(); - break; - - case 'updates': - $where['type'] = [ - Note::TYPE_ASSIGN, - Note::TYPE_STATUS, - ]; - - break; - } + if ($filter === FindParams::FILTER_POSTS) { + $where['type'] = Note::TYPE_POST; + } + else if ($filter === FindParams::FILTER_UPDATES) { + $where['type'] = [ + Note::TYPE_ASSIGN, + Note::TYPE_STATUS, + ]; } $ignoreScopeList = $this->getIgnoreScopeList($this->user); @@ -1162,16 +1128,20 @@ class Service $builder->where($where); - if (!empty($params['after'])) { + $after = $params->getAfter(); + $offset = $params->getOffset(); + $maxSize = $params->getMaxSize(); + + if ($after) { $builder->where([ - 'createdAt>' => $params['after'], + 'createdAt>' => $after->getString(), ]); } $countBuilder = clone $builder; $builder - ->limit($params['offset'] ?? 0, $params['maxSize']) + ->limit($offset ?? 0, $maxSize) ->order('number', 'DESC'); $collection = $this->entityManager