From ffd05882f2b810cc4c451e2c2a624695e052860a Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Mon, 11 Apr 2022 12:57:24 +0300 Subject: [PATCH] select orderer, passing user to order converter --- .../Core/Select/Applier/Appliers/Order.php | 47 ++++++---- .../Select/Order/ItemConverterFactory.php | 47 ++++++---- .../Espo/Core/Select/Order/Orderer.php | 37 ++++++++ .../Espo/Core/Select/Order/OrdererFactory.php | 89 +++++++++++++++++++ .../Applier/Appliers/OrderApplierTest.php | 63 +++++++++++-- .../Select/Order/ItemConverterFactoryTest.php | 22 +++-- 6 files changed, 259 insertions(+), 46 deletions(-) create mode 100644 application/Espo/Core/Select/Order/Orderer.php create mode 100644 application/Espo/Core/Select/Order/OrdererFactory.php diff --git a/application/Espo/Core/Select/Applier/Appliers/Order.php b/application/Espo/Core/Select/Applier/Appliers/Order.php index 565adde175..4c280f95cf 100644 --- a/application/Espo/Core/Select/Applier/Appliers/Order.php +++ b/application/Espo/Core/Select/Applier/Appliers/Order.php @@ -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; diff --git a/application/Espo/Core/Select/Order/ItemConverterFactory.php b/application/Espo/Core/Select/Order/ItemConverterFactory.php index 5e710e722e..a1cfd89e33 100644 --- a/application/Espo/Core/Select/Order/ItemConverterFactory.php +++ b/application/Espo/Core/Select/Order/ItemConverterFactory.php @@ -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 */ - $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 */ - $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 */ - return $className; + return $className3; } return null; diff --git a/application/Espo/Core/Select/Order/Orderer.php b/application/Espo/Core/Select/Order/Orderer.php new file mode 100644 index 0000000000..884d2897a7 --- /dev/null +++ b/application/Espo/Core/Select/Order/Orderer.php @@ -0,0 +1,37 @@ +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 + */ + private function getClassName(string $entityType, string $field): ?string + { + /** @var ?class-string */ + return $this->metadata->get([ + 'selectDefs', $entityType, 'ordererClassNameMap', $field + ]); + } +} diff --git a/tests/unit/Espo/Core/Select/Applier/Appliers/OrderApplierTest.php b/tests/unit/Espo/Core/Select/Applier/Appliers/OrderApplierTest.php index b97d941fd4..4ccdbd2bfa 100644 --- a/tests/unit/Espo/Core/Select/Applier/Appliers/OrderApplierTest.php +++ b/tests/unit/Espo/Core/Select/Applier/Appliers/OrderApplierTest.php @@ -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); diff --git a/tests/unit/Espo/Core/Select/Order/ItemConverterFactoryTest.php b/tests/unit/Espo/Core/Select/Order/ItemConverterFactoryTest.php index e11e6eca4a..059551e2e4 100644 --- a/tests/unit/Espo/Core/Select/Order/ItemConverterFactoryTest.php +++ b/tests/unit/Espo/Core/Select/Order/ItemConverterFactoryTest.php @@ -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);