From ae32c3e9649abb437d436a8fe1dcda5e8eecfd89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Burnichon?= Date: Mon, 11 Apr 2016 21:20:17 +0200 Subject: [PATCH] Refactor to use views --- .../Databox/Subdef/MediaSubdefService.php | 2 +- .../Order/Controller/ApiOrderController.php | 176 +++++++++++++++--- .../Phrasea/Order/OrderElementTransformer.php | 68 ++----- .../Phrasea/Order/OrderElementViewModel.php | 82 ++++++++ .../Phrasea/Order/OrderTransformer.php | 7 +- lib/Alchemy/Phrasea/Order/OrderViewModel.php | 60 ++++++ lib/Alchemy/Phrasea/Order/SubdefViewModel.php | 46 +++++ .../Record/RecordReferenceCollection.php | 77 ++++++-- 8 files changed, 416 insertions(+), 102 deletions(-) create mode 100644 lib/Alchemy/Phrasea/Order/OrderElementViewModel.php create mode 100644 lib/Alchemy/Phrasea/Order/OrderViewModel.php create mode 100644 lib/Alchemy/Phrasea/Order/SubdefViewModel.php diff --git a/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefService.php b/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefService.php index 6315e42568..60a3c9d9d8 100644 --- a/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefService.php +++ b/lib/Alchemy/Phrasea/Databox/Subdef/MediaSubdefService.php @@ -90,7 +90,7 @@ class MediaSubdefService foreach ($records->groupPerDataboxId() as $databoxId => $indexes) { $subdefs = $this->getRepositoryForDatabox($databoxId)->findByRecordIdsAndNames(array_keys($indexes)); - $carry = $process($carry, $subdefs, $indexes); + $carry = $process($carry, $subdefs, $indexes, $databoxId); } return $carry; diff --git a/lib/Alchemy/Phrasea/Order/Controller/ApiOrderController.php b/lib/Alchemy/Phrasea/Order/Controller/ApiOrderController.php index 5953dfcd21..9bd74cb8a6 100644 --- a/lib/Alchemy/Phrasea/Order/Controller/ApiOrderController.php +++ b/lib/Alchemy/Phrasea/Order/Controller/ApiOrderController.php @@ -18,9 +18,13 @@ use Alchemy\Phrasea\Core\PhraseaEvents; use Alchemy\Phrasea\Model\Entities\BasketElement; use Alchemy\Phrasea\Model\Entities\Order; use Alchemy\Phrasea\Model\Entities\OrderElement; +use Alchemy\Phrasea\Model\RecordReferenceInterface; use Alchemy\Phrasea\Order\OrderElementTransformer; +use Alchemy\Phrasea\Order\OrderElementViewModel; use Alchemy\Phrasea\Order\OrderFiller; use Alchemy\Phrasea\Order\OrderTransformer; +use Alchemy\Phrasea\Order\OrderViewModel; +use Alchemy\Phrasea\Order\SubdefViewModel; use Alchemy\Phrasea\Record\RecordReference; use Alchemy\Phrasea\Record\RecordReferenceCollection; use Assert\Assertion; @@ -92,11 +96,8 @@ class ApiOrderController extends BaseOrderController ; } - $collection = $builder->getQuery()->getResult(); - - if (in_array('elements.resource_links', $fractal->getRequestedIncludes(), false)) { - $this->fetchResourceLinksData($collection); - } + $collection = $this->buildOrderViewModels($builder->getQuery()->getResult()); + $this->fillViewModels($collection, $fractal->getRequestedIncludes()); $resource = new Collection($collection, $this->getOrderTransformer()); @@ -118,15 +119,21 @@ class ApiOrderController extends BaseOrderController { $order = $this->findOr404($orderId); - $includes = $request->get('includes', []); + $fractal = $this->parseIncludes($request->get('includes', [])); if ($order->getUser()->getId() !== $this->getAuthenticatedUser()->getId()) { throw new AccessDeniedHttpException(sprintf('Cannot access order "%d"', $order->getId())); } - $resource = new Item($order, $this->getOrderTransformer()); + $model = $this->buildOrderViewModel($order); - return $this->returnResourceResponse($request, $includes, $resource); + $resource = new Item($model, $this->getOrderTransformer()); + + if (in_array('elements.resource_links', $fractal->getRequestedIncludes(), false)) { + $this->fillViewModels([$resource]); + } + + return $this->returnResourceResponse($request, $fractal, $resource); } public function acceptElementsAction(Request $request, $orderId) @@ -199,7 +206,7 @@ class ApiOrderController extends BaseOrderController */ private function getOrderTransformer() { - return new OrderTransformer(new OrderElementTransformer($this->app)); + return new OrderTransformer(new OrderElementTransformer($this->app['media_accessor.subdef_url_generator'])); } /** @@ -246,26 +253,34 @@ class ApiOrderController extends BaseOrderController } /** - * @param Order[] $orders + * @param OrderViewModel[] $models + * @param array $includes + * @return void */ - private function fetchResourceLinksData(array $orders) + private function fillViewModels(array $models, array $includes) { - $elements = $this->gatherElements($orders); - - $baseIds = array_keys(array_reduce($elements, function (array &$baseIds, OrderElement $element) { - $baseIds[$element->getBaseId()] = true; - - return $baseIds; - }, [])); - - $collectionToDataboxMap = []; - - foreach ($this->app['repo.collection-references']->findMany($baseIds) as $collectionReference) { - $collectionToDataboxMap[$collectionReference->getBaseId()] = $collectionReference->getDataboxId(); + if (!in_array('elements', $includes, true)) { + return; } + $elements = $this->gatherElements($models); + + $allElements = $elements ? call_user_func_array('array_merge', $elements) : []; + $allElements = array_combine( + array_map(function (OrderElement $element) { + return $element->getId(); + }, $allElements), + $allElements + ); + + if (!$allElements) { + return; + } + + $collectionToDataboxMap = $this->mapBaseIdToDataboxId($allElements); + $records = RecordReferenceCollection::fromListExtractor( - $elements, + $allElements, function (OrderElement $element) use ($collectionToDataboxMap) { return isset($collectionToDataboxMap[$element->getBaseId()]) ? [$collectionToDataboxMap[$element->getBaseId()], $element->getRecordId()] @@ -278,30 +293,129 @@ class ApiOrderController extends BaseOrderController } ); + $this->createOrderElementViewModels($models, $elements, $records); + + if (!in_array('elements.resource_links', $includes, true)) { + return; + } + // Load all records $records->toRecords($this->getApplicationBox()); // Load all subdefs $subdefs = $this->app['service.media_subdef']->findSubdefsFromRecordReferenceCollection($records); - \media_Permalink_Adapter::getMany($this->app, $subdefs); + $links = \media_Permalink_Adapter::getMany($this->app, $subdefs); + + $orderableViews = []; + + $views = array_map(null, $subdefs, $links); + + foreach ($views as $view) { + /** + * @var \media_subdef $subdef + * @var \media_Permalink_Adapter $link + */ + list ($subdef, $link) = $view; + + $databoxId = $subdef->get_sbas_id(); + $recordId = $subdef->get_record_id(); + + if (!isset($orderableViews[$databoxId][$recordId])) { + $orderableViews[$databoxId][$recordId] = []; + } + + $orderableViews[$databoxId][$recordId][] = new SubdefViewModel($subdef, $link); + } + + foreach ($models as $model) { + foreach ($model->getElements() as $element) { + $databoxId = $collectionToDataboxMap[$element->getElement()->getBaseId()]; + $recordId = $element->getElement()->getRecordId(); + + if (isset($orderableViews[$databoxId][$recordId])) { + $element->setOrderableMediaSubdefs($orderableViews[$databoxId][$recordId]); + } + } + } } /** * @param Order[] $orders - * @return OrderElement[] + * @return OrderViewModel[] */ - private function gatherElements(array $orders) + private function buildOrderViewModels(array $orders) { Assertion::allIsInstanceOf($orders, Order::class); + return array_map(function (Order $order) { + return new OrderViewModel($order); + }, $orders); + } + + private function buildOrderViewModel(Order $order) + { + return new OrderViewModel($order); + } + + /** + * @param OrderViewModel[] $models + * @return OrderElement[][] + */ + private function gatherElements(array $models) + { + Assertion::allIsInstanceOf($models, OrderViewModel::class); + $elements = []; - foreach ($orders as $order) { - foreach ($order->getElements() as $element) { - $elements[] = $element; - } + foreach ($models as $index => $model) { + $elements[$index] = $model->getOrder()->getElements()->toArray(); } return $elements; } + + /** + * @param OrderElement[] $elements + * @return array + */ + private function mapBaseIdToDataboxId(array $elements) + { + $baseIds = array_keys(array_reduce($elements, function (array &$baseIds, OrderElement $element) { + $baseIds[$element->getBaseId()] = true; + + return $baseIds; + }, [])); + + $collectionToDataboxMap = []; + + foreach ($this->app['repo.collection-references']->findMany($baseIds) as $collectionReference) { + $collectionToDataboxMap[$collectionReference->getBaseId()] = $collectionReference->getDataboxId(); + } + + return $collectionToDataboxMap; + } + + /** + * @param OrderViewModel[] $orderViewModels + * @param OrderElement[][] $elements + * @param RecordReferenceInterface[]|RecordReferenceCollection $records + * @return void + */ + private function createOrderElementViewModels(array $orderViewModels, $elements, $records) + { + $user = $this->getAuthenticatedUser(); + + foreach ($orderViewModels as $index => $model) { + $models = []; + + /** @var OrderElement $element */ + foreach ($elements[$index] as $elementIndex => $element) { + if (isset($records[$element->getId()])) { + $models[$elementIndex] = new OrderElementViewModel($element, $records[$element->getId()], $user); + } + } + + $model->setViewElements($models); + } + } } diff --git a/lib/Alchemy/Phrasea/Order/OrderElementTransformer.php b/lib/Alchemy/Phrasea/Order/OrderElementTransformer.php index 88366ce3d5..7b49837daf 100644 --- a/lib/Alchemy/Phrasea/Order/OrderElementTransformer.php +++ b/lib/Alchemy/Phrasea/Order/OrderElementTransformer.php @@ -11,9 +11,7 @@ namespace Alchemy\Phrasea\Order; use Alchemy\Phrasea\Application; -use Alchemy\Phrasea\Databox\Subdef\MediaSubdefRepository; use Alchemy\Phrasea\Media\MediaSubDefinitionUrlGenerator; -use Alchemy\Phrasea\Model\Entities\OrderElement; use League\Fractal\ParamBag; use League\Fractal\TransformerAbstract; @@ -24,22 +22,25 @@ class OrderElementTransformer extends TransformerAbstract private $validParams = ['ttl']; /** - * @var Application + * @var MediaSubDefinitionUrlGenerator */ - private $app; + private $urlGenerator; - public function __construct(Application $app) + public function __construct(MediaSubDefinitionUrlGenerator $urlGenerator) { - $this->app = $app; + $this->urlGenerator = $urlGenerator; } - public function transform(OrderElement $element) + public function transform(OrderElementViewModel $model) { + $element = $model->getElement(); + $record = $model->getRecordReference(); + $data = [ 'id' => $element->getId(), 'record' => [ - 'databox_id' => $element->getSbasId($this->app), - 'record_id' => $element->getRecordId(), + 'databox_id' => $record->getDataboxId(), + 'record_id' => $record->getRecordId(), ], ]; @@ -53,7 +54,7 @@ class OrderElementTransformer extends TransformerAbstract return $data; } - public function includeResourceLinks(OrderElement $element, ParamBag $params = null) + public function includeResourceLinks(OrderElementViewModel $model, ParamBag $params = null) { $ttl = null; @@ -71,13 +72,11 @@ class OrderElementTransformer extends TransformerAbstract list ($ttl) = $params->get('ttl'); } - $generator = $this->getSubdefUrlGenerator(); - if (null === $ttl) { - $ttl = $generator->getDefaultTTL(); + $ttl = $this->urlGenerator->getDefaultTTL(); } - $urls = $generator->generateMany($this->app->getAuthenticatedUser(), $this->findOrderableMediaSubdef($element), $ttl); + $urls = $this->urlGenerator->generateMany($model->getAuthenticatedUser(), $model->getOrderableMediaSubdefs(), $ttl); $urls = array_map(null, array_keys($urls), array_values($urls)); return $this->collection($urls, function (array $data) use ($ttl) { @@ -88,45 +87,4 @@ class OrderElementTransformer extends TransformerAbstract ]; }); } - - /** - * @param OrderElement $element - * @return \media_subdef[] - */ - private function findOrderableMediaSubdef(OrderElement $element) - { - if (false !== $element->getDeny()) { - return []; - } - - $databox = $this->app->findDataboxById($element->getSbasId($this->app)); - $record = $databox->get_record($element->getRecordId()); - - $subdefNames = []; - - foreach ($databox->get_subdef_structure()->getSubdefGroup($record->getType()) as $databoxSubdef) { - if ($databoxSubdef->isOrderable()) { - $subdefNames[] = $databoxSubdef->get_name(); - } - } - - return $this->getMediaSubDefRepository($databox->get_sbas_id()) - ->findByRecordIdsAndNames([$element->getRecordId()], $subdefNames); - } - - /** - * @return MediaSubDefinitionUrlGenerator - */ - private function getSubdefUrlGenerator() - { - return $this->app['media_accessor.subdef_url_generator']; - } - - /** - * @return MediaSubdefRepository - */ - private function getMediaSubDefRepository($databoxId) - { - return $this->app['provider.repo.media_subdef']->getRepositoryForDatabox($databoxId); - } } diff --git a/lib/Alchemy/Phrasea/Order/OrderElementViewModel.php b/lib/Alchemy/Phrasea/Order/OrderElementViewModel.php new file mode 100644 index 0000000000..f7fe265619 --- /dev/null +++ b/lib/Alchemy/Phrasea/Order/OrderElementViewModel.php @@ -0,0 +1,82 @@ +element = $element; + $this->record = $record; + $this->user = $user; + } + + public function getElement() + { + return $this->element; + } + + public function getRecordReference() + { + return $this->record; + } + + public function getAuthenticatedUser() + { + return $this->user; + } + + /** + * @param SubdefViewModel[] $subdefs + */ + public function setOrderableMediaSubdefs($subdefs) + { + Assertion::allIsInstanceOf($subdefs, SubdefViewModel::class); + + $this->subdefs = $subdefs instanceof \Traversable ? iterator_to_array($subdefs) : $subdefs; + } + + public function getOrderableMediaSubdefs() + { + return $this->subdefs; + } +} diff --git a/lib/Alchemy/Phrasea/Order/OrderTransformer.php b/lib/Alchemy/Phrasea/Order/OrderTransformer.php index e9a106a148..9775031da1 100644 --- a/lib/Alchemy/Phrasea/Order/OrderTransformer.php +++ b/lib/Alchemy/Phrasea/Order/OrderTransformer.php @@ -10,7 +10,6 @@ namespace Alchemy\Phrasea\Order; -use Alchemy\Phrasea\Model\Entities\Order; use League\Fractal\TransformerAbstract; class OrderTransformer extends TransformerAbstract @@ -29,8 +28,10 @@ class OrderTransformer extends TransformerAbstract $this->elementTransformer = $elementTransformer; } - public function transform(Order $order) + public function transform(OrderViewModel $view) { + $order = $view->getOrder(); + $data = [ 'id' => (int)$order->getId(), 'owner_id' => (int)$order->getUser()->getId(), @@ -46,7 +47,7 @@ class OrderTransformer extends TransformerAbstract return $data; } - public function includeElements(Order $order) + public function includeElements(OrderViewModel $order) { $elements = $order->getElements(); diff --git a/lib/Alchemy/Phrasea/Order/OrderViewModel.php b/lib/Alchemy/Phrasea/Order/OrderViewModel.php new file mode 100644 index 0000000000..6acb27bb90 --- /dev/null +++ b/lib/Alchemy/Phrasea/Order/OrderViewModel.php @@ -0,0 +1,60 @@ +order = $order; + } + + /** + * @param $viewElements + */ + public function setViewElements($viewElements) + { + Assertion::allIsInstanceOf($viewElements, OrderElementViewModel::class); + + $this->viewElements = $viewElements instanceof \Traversable ? iterator_to_array($viewElements) : $viewElements; + } + + /** + * @return Order + */ + public function getOrder() + { + return $this->order; + } + + /** + * @return OrderElementViewModel[] + */ + public function getElements() + { + return $this->viewElements; + } +} diff --git a/lib/Alchemy/Phrasea/Order/SubdefViewModel.php b/lib/Alchemy/Phrasea/Order/SubdefViewModel.php new file mode 100644 index 0000000000..c42840a05d --- /dev/null +++ b/lib/Alchemy/Phrasea/Order/SubdefViewModel.php @@ -0,0 +1,46 @@ +subdef = $subdef; + $this->link = $link; + } + + /** + * @return \media_subdef + */ + public function getSubdef() + { + return $this->subdef; + } + + /** + * @return \media_Permalink_Adapter + */ + public function getLink() + { + return $this->link; + } +} diff --git a/lib/Alchemy/Phrasea/Record/RecordReferenceCollection.php b/lib/Alchemy/Phrasea/Record/RecordReferenceCollection.php index 69497f16b7..97bb80ceef 100644 --- a/lib/Alchemy/Phrasea/Record/RecordReferenceCollection.php +++ b/lib/Alchemy/Phrasea/Record/RecordReferenceCollection.php @@ -13,7 +13,7 @@ namespace Alchemy\Phrasea\Record; use Alchemy\Phrasea\Model\RecordReferenceInterface; use Assert\Assertion; -class RecordReferenceCollection implements \IteratorAggregate +class RecordReferenceCollection implements \IteratorAggregate, \ArrayAccess { /** * @param array $records @@ -25,11 +25,11 @@ class RecordReferenceCollection implements \IteratorAggregate $references = []; - foreach ($records as $record) { + foreach ($records as $index => $record) { if (isset($record['id'])) { - $references[] = RecordReference::createFromRecordReference($record['id']); + $references[$index] = RecordReference::createFromRecordReference($record['id']); } elseif (isset($record['databox_id'], $record['record_id'])) { - $references[] = RecordReference::createFromDataboxIdAndRecordId($record['databox_id'], $record['record_id']); + $references[$index] = RecordReference::createFromDataboxIdAndRecordId($record['databox_id'], $record['record_id']); } } @@ -56,7 +56,7 @@ class RecordReferenceCollection implements \IteratorAggregate }; } - foreach ($list as $item) { + foreach ($list as $index => $item) { $data = $extractor($item); if (null === $data) { @@ -66,7 +66,7 @@ class RecordReferenceCollection implements \IteratorAggregate $reference = $creator($data); if ($reference instanceof RecordReferenceInterface) { - $references[] = $reference; + $references[$index] = $reference; } } @@ -90,22 +90,35 @@ class RecordReferenceCollection implements \IteratorAggregate { Assertion::allIsInstanceOf($references, RecordReferenceInterface::class); - $this->references = $references instanceof \Traversable ? iterator_to_array($references, false) : array_values($references); + $this->references = $references instanceof \Traversable ? iterator_to_array($references, true) : $references; } - public function add(RecordReferenceInterface $reference) + /** + * @param RecordReferenceInterface $reference + * @param null|string|int $index + */ + public function add(RecordReferenceInterface $reference, $index = null) { - $this->references[] = $reference; $this->groups = null; + + if (null === $index) { + $this->references[] = $reference; + + return; + } + + $this->references[$index] = $reference; } /** * @param int $databoxId * @param int $recordId + * @param null|string|int $index + * @return void */ - public function addRecordReference($databoxId, $recordId) + public function addRecordReference($databoxId, $recordId, $index = null) { - $this->add(RecordReference::createFromDataboxIdAndRecordId($databoxId, $recordId)); + $this->add(RecordReference::createFromDataboxIdAndRecordId($databoxId, $recordId), $index); } public function getIterator() @@ -161,8 +174,48 @@ class RecordReferenceCollection implements \IteratorAggregate } } - ksort($records); + $indexes = array_flip(array_keys($this->references)); + + uksort($records, function ($keyA, $keyB) use ($indexes) { + $indexA = $indexes[$keyA]; + $indexB = $indexes[$keyB]; + + if ($indexA < $indexB) { + return -1; + } elseif ($indexA > $indexB) { + return 1; + } + + return 0; + }); return $records; } + + public function offsetExists($offset) + { + return isset($this->references[$offset]); + } + + /** + * @param mixed $offset + * @return RecordReferenceInterface + */ + public function offsetGet($offset) + { + return $this->references[$offset]; + } + + public function offsetSet($offset, $value) + { + Assertion::isInstanceOf($value, RecordReferenceInterface::class); + + $this->add($value, $offset); + } + + public function offsetUnset($offset) + { + unset($this->references[$offset]); + $this->groups = null; + } }