select orderer, passing user to order converter

This commit is contained in:
Yuri Kuznetsov
2022-04-11 12:57:24 +03:00
parent f8eb7679fd
commit ffd05882f2
6 changed files with 259 additions and 46 deletions
@@ -39,33 +39,31 @@ use Espo\Core\{
Select\Order\Item as OrderItem,
Select\Order\ItemConverterFactory,
Select\Order\MetadataProvider,
Select\Order\OrdererFactory,
};
use Espo\{
ORM\Query\SelectBuilder as QueryBuilder,
Entities\User,
};
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
class Order
{
protected string $entityType;
private string $entityType;
protected User $user;
private MetadataProvider $metadataProvider;
protected MetadataProvider $metadataProvider;
private ItemConverterFactory $itemConverterFactory;
protected ItemConverterFactory $itemConverterFactory;
private OrdererFactory $ordererFactory;
public function __construct(
string $entityType,
User $user,
MetadataProvider $metadataProvider,
ItemConverterFactory $itemConverterFactory
ItemConverterFactory $itemConverterFactory,
OrdererFactory $ordererFactory
) {
$this->entityType = $entityType;
$this->user = $user;
$this->metadataProvider = $metadataProvider;
$this->itemConverterFactory = $itemConverterFactory;
$this->ordererFactory = $ordererFactory;
}
public function apply(QueryBuilder $queryBuilder, OrderParams $params): void
@@ -88,7 +86,9 @@ class Order
}
}
$orderBy = $orderBy ?? $this->metadataProvider->getDefaultOrderBy($this->entityType);
if ($orderBy === null) {
$orderBy = $this->metadataProvider->getDefaultOrderBy($this->entityType);
}
if (!$orderBy) {
return;
@@ -97,7 +97,7 @@ class Order
$this->applyOrder($queryBuilder, $orderBy, $params->getOrder());
}
protected function applyDefaultOrder(QueryBuilder $queryBuilder, ?string $order): void
private function applyDefaultOrder(QueryBuilder $queryBuilder, ?string $order): void
{
$orderBy = $this->metadataProvider->getDefaultOrderBy($this->entityType);
@@ -124,13 +124,28 @@ class Order
$this->applyOrder($queryBuilder, $orderBy, $order);
}
protected function applyOrder(QueryBuilder $queryBuilder, string $orderBy, ?string $order): void
private function applyOrder(QueryBuilder $queryBuilder, string $orderBy, ?string $order): void
{
if (!$orderBy) {
throw new Error("Could not apply order.");
throw new Error("Could not apply empty order.");
}
$order = $order ?? SearchParams::ORDER_ASC;
if ($order === null) {
$order = SearchParams::ORDER_ASC;
}
$hasOrderer = $this->ordererFactory->has($this->entityType, $orderBy);
if ($hasOrderer) {
$orderer = $this->ordererFactory->create($this->entityType, $orderBy);
$orderer->apply(
$queryBuilder,
OrderItem::create($orderBy, $order)
);
return;
}
$resultOrderBy = $orderBy;
@@ -29,11 +29,14 @@
namespace Espo\Core\Select\Order;
use Espo\Core\{
Exceptions\Error,
InjectableFactory,
Utils\Metadata,
};
use Espo\Entities\User;
use Espo\Core\Exceptions\Error;
use Espo\Core\InjectableFactory;
use Espo\Core\Utils\Metadata;
use Espo\Core\Binding\BindingContainerBuilder;
use Espo\Core\Binding\ContextualBinder;
class ItemConverterFactory
{
@@ -41,10 +44,13 @@ class ItemConverterFactory
private Metadata $metadata;
public function __construct(InjectableFactory $injectableFactory, Metadata $metadata)
private User $user;
public function __construct(InjectableFactory $injectableFactory, Metadata $metadata, User $user)
{
$this->injectableFactory = $injectableFactory;
$this->metadata = $metadata;
$this->user = $user;
}
public function has(string $entityType, string $field): bool
@@ -60,9 +66,14 @@ class ItemConverterFactory
throw new Error("Order item converter class name is not defined.");
}
return $this->injectableFactory->createWith($className, [
'entityType' => $entityType,
]);
$container = BindingContainerBuilder::create()
->bindInstance(User::class, $this->user)
->inContext($className, function (ContextualBinder $binder) use ($entityType) {
$binder->bindValue('$entityType', $entityType);
})
->build();
return $this->injectableFactory->createWithBinding($className, $container);
}
/**
@@ -71,12 +82,12 @@ class ItemConverterFactory
private function getClassName(string $entityType, string $field): ?string
{
/** @var ?class-string<ItemConverter> */
$className = $this->metadata->get([
$className1 = $this->metadata->get([
'selectDefs', $entityType, 'orderItemConverterClassNameMap', $field
]);
if ($className) {
return $className;
if ($className1) {
return $className1;
}
$type = $this->metadata->get([
@@ -88,19 +99,19 @@ class ItemConverterFactory
}
/** @var ?class-string<ItemConverter> */
$className = $this->metadata->get([
$className2 = $this->metadata->get([
'app', 'select', 'orderItemConverterClassNameMap', $type
]);
if ($className) {
return $className;
if ($className2) {
return $className2;
}
$className = 'Espo\\Core\\Select\\Order\\ItemConverters\\' . ucfirst($type) . 'Type';
$className3 = 'Espo\\Core\\Select\\Order\\ItemConverters\\' . ucfirst($type) . 'Type';
if (class_exists($className)) {
if (class_exists($className3)) {
/** @var class-string<ItemConverter> */
return $className;
return $className3;
}
return null;
@@ -0,0 +1,37 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii 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\Core\Select\Order;
use Espo\ORM\Query\SelectBuilder;
interface Orderer
{
public function apply(SelectBuilder $queryBuilder, Item $item): void;
}
@@ -0,0 +1,89 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii 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\Core\Select\Order;
use Espo\Core\Exceptions\Error;
use Espo\Core\InjectableFactory;
use Espo\Core\Utils\Metadata;
use Espo\Entities\User;
use Espo\Core\Binding\BindingContainerBuilder;
use Espo\Core\Binding\ContextualBinder;
class OrdererFactory
{
private InjectableFactory $injectableFactory;
private Metadata $metadata;
private User $user;
public function __construct(InjectableFactory $injectableFactory, Metadata $metadata, User $user)
{
$this->injectableFactory = $injectableFactory;
$this->metadata = $metadata;
$this->user = $user;
}
public function has(string $entityType, string $field): bool
{
return (bool) $this->getClassName($entityType, $field);
}
public function create(string $entityType, string $field): Orderer
{
$className = $this->getClassName($entityType, $field);
if (!$className) {
throw new Error("Orderer class name is not defined.");
}
$container = BindingContainerBuilder::create()
->bindInstance(User::class, $this->user)
->inContext($className, function (ContextualBinder $binder) use ($entityType) {
$binder->bindValue('$entityType', $entityType);
})
->build();
return $this->injectableFactory->createWithBinding($className, $container);
}
/**
* @return ?class-string<Orderer>
*/
private function getClassName(string $entityType, string $field): ?string
{
/** @var ?class-string<Orderer> */
return $this->metadata->get([
'selectDefs', $entityType, 'ordererClassNameMap', $field
]);
}
}
@@ -41,20 +41,23 @@ use Espo\Core\{
Select\Order\ItemConverterFactory,
Select\Order\ItemConverter,
Select\Order\MetadataProvider,
Select\Order\OrdererFactory,
Select\Order\Orderer,
};
use Espo\{
ORM\Query\SelectBuilder as QueryBuilder,
Entities\User,
};
class OrderApplierTest extends \PHPUnit\Framework\TestCase
{
private ?OrdererFactory $ordererFactory = null;
protected function setUp(): void
{
$this->user = $this->createMock(User::class);
$this->metadataProvider = $this->createMock(MetadataProvider::class);
$this->itemConverterFactory = $this->createMock(ItemConverterFactory::class);
$this->ordererFactory = $this->createMock(OrdererFactory::class);
$this->queryBuilder = $this->createMock(QueryBuilder::class);
$this->params = $this->createMock(OrderParams::class);
@@ -62,9 +65,9 @@ class OrderApplierTest extends \PHPUnit\Framework\TestCase
$this->applier = new OrderApplier(
$this->entityType,
$this->user,
$this->metadataProvider,
$this->itemConverterFactory
$this->itemConverterFactory,
$this->ordererFactory
);
}
@@ -190,8 +193,54 @@ class OrderApplierTest extends \PHPUnit\Framework\TestCase
$this->applier->apply($this->queryBuilder, $this->params);
}
public function testApplyWithOrderer()
{
$order = SearchParams::ORDER_DESC;
$orderBy = 'testField';
$this->params
->expects($this->any())
->method('forceDefault')
->willReturn(false);
$this->params
->expects($this->any())
->method('getOrder')
->willReturn($order);
$this->params
->expects($this->any())
->method('getOrderBy')
->willReturn($orderBy);
$this->ordererFactory
->expects($this->once())
->method('has')
->with($this->entityType, $orderBy)
->willReturn(true);
$orderer = $this->createMock(Orderer::class);
$this->ordererFactory
->expects($this->once())
->method('create')
->with($this->entityType, $orderBy)
->willReturn($orderer);
$orderer
->expects($this->once())
->method('apply')
->with($this->queryBuilder, Item::create($orderBy, $order));
$this->applier->apply($this->queryBuilder, $this->params);
}
protected function initApplyOrderTest(
string $orderBy, string $order, string $fieldType, ?OrderList $converterResult = null, bool $notExisting = false
string $orderBy,
string $order,
string $fieldType,
?OrderList $converterResult = null,
bool $notExisting = false
) {
$this->metadataProvider
->expects($this->any())
@@ -235,8 +284,8 @@ class OrderApplierTest extends \PHPUnit\Framework\TestCase
->method('convert')
->with($item)
->willReturn($converterResult);
} else {
}
else {
if ($notExisting) {
$this->expectException(Error::class);
@@ -29,6 +29,8 @@
namespace tests\unit\Espo\Core\Select\Order;
use Espo\Entities\User;
use Espo\Core\{
Select\Order\ItemConverterFactory,
Select\Order\ItemConverter,
@@ -37,16 +39,21 @@ use Espo\Core\{
InjectableFactory,
};
use Espo\Core\Binding\BindingContainerBuilder;
use Espo\Core\Binding\ContextualBinder;
class ItemConverterFactoryTest extends \PHPUnit\Framework\TestCase
{
protected function setUp() : void
{
$this->injectableFactory = $this->createMock(InjectableFactory::class);
$this->metadata = $this->createMock(Metadata::class);
$this->user = $this->createMock(User::class);
$this->factory = new ItemConverterFactory(
$this->injectableFactory,
$this->metadata
$this->metadata,
$this->user
);
}
@@ -105,14 +112,19 @@ class ItemConverterFactoryTest extends \PHPUnit\Framework\TestCase
$object = $this->createMock($className);
$container = BindingContainerBuilder::create()
->bindInstance(User::class, $this->user)
->inContext($className, function (ContextualBinder $binder) use ($entityType) {
$binder->bindValue('$entityType', $entityType);
})
->build();
$this->injectableFactory
->expects($this->once())
->method('createWith')
->method('createWithBinding')
->with(
$className,
[
'entityType' => $entityType,
]
$container
)
->willReturn($object);