Extract view builder class to build view model

This commit is contained in:
Thibaud Fabre
2016-04-12 16:34:17 +02:00
parent cde66acb35
commit b4e6a4bcb7
6 changed files with 263 additions and 176 deletions

View File

@@ -20,10 +20,11 @@ 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\OrderElementView;
use Alchemy\Phrasea\Order\OrderFiller;
use Alchemy\Phrasea\Order\OrderTransformer;
use Alchemy\Phrasea\Order\OrderViewModel;
use Alchemy\Phrasea\Order\OrderView;
use Alchemy\Phrasea\Order\OrderViewBuilder;
use Alchemy\Phrasea\Record\RecordReference;
use Alchemy\Phrasea\Record\RecordReferenceCollection;
use Assert\Assertion;
@@ -74,7 +75,7 @@ class ApiOrderController extends BaseOrderController
{
$page = max((int) $request->get('page', '1'), 1);
$perPage = min(max((int)$request->get('per_page', '10'), 1), 100);
$fractal = $this->parseIncludes($request->get('includes', []));
$fractal = $this->buildFractalManager($request->get('includes', []));
$routeGenerator = function ($page) use ($perPage) {
return $this->app->path('api_v2_orders_index', [
@@ -95,8 +96,10 @@ class ApiOrderController extends BaseOrderController
;
}
$collection = $this->buildOrderViewModels($builder->getQuery()->getResult());
$this->fillViewModels($collection, $fractal->getRequestedIncludes());
$collection = $this->getViewBuilder()->buildViews(
$builder->getQuery()->getResult(),
$fractal->getRequestedIncludes()
);
$resource = new Collection($collection, $this->getOrderTransformer());
@@ -118,15 +121,13 @@ class ApiOrderController extends BaseOrderController
{
$order = $this->findOr404($orderId);
$fractal = $this->parseIncludes($request->get('includes', []));
$fractal = $this->buildFractalManager($request->get('includes', []));
if ($order->getUser()->getId() !== $this->getAuthenticatedUser()->getId()) {
throw new AccessDeniedHttpException(sprintf('Cannot access order "%d"', $order->getId()));
}
$model = $this->buildOrderViewModel($order);
$this->fillViewModels([$model], $fractal->getRequestedIncludes());
$model = $this->getViewBuilder()->buildView($order, $fractal->getRequestedIncludes());
$resource = new Item($model, $this->getOrderTransformer());
return $this->returnResourceResponse($request, $fractal, $resource);
@@ -209,7 +210,7 @@ class ApiOrderController extends BaseOrderController
* @param string|array $includes
* @return Manager
*/
private function parseIncludes($includes)
private function buildFractalManager($includes)
{
$fractal = new Manager();
@@ -226,7 +227,7 @@ class ApiOrderController extends BaseOrderController
*/
private function returnResourceResponse(Request $request, $includes, ResourceInterface $resource)
{
$fractal = $includes instanceof Manager ? $includes : $this->parseIncludes($includes);
$fractal = $includes instanceof Manager ? $includes : $this->buildFractalManager($includes);
return Result::create($request, $fractal->createData($resource)->toArray())->createResponse();
}
@@ -248,162 +249,12 @@ class ApiOrderController extends BaseOrderController
return $elementIds;
}
/**
* @param OrderViewModel[] $models
* @param array $includes
* @return void
*/
private function fillViewModels(array $models, array $includes)
private function getViewBuilder()
{
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
return new OrderViewBuilder(
$this->app,
$this->getApplicationBox(),
$this->app['service.media_subdef']
);
if (!$allElements) {
return;
}
$collectionToDataboxMap = $this->mapBaseIdToDataboxId($allElements);
$records = RecordReferenceCollection::fromListExtractor(
$allElements,
function (OrderElement $element) use ($collectionToDataboxMap) {
return isset($collectionToDataboxMap[$element->getBaseId()])
? [$collectionToDataboxMap[$element->getBaseId()], $element->getRecordId()]
: null;
},
function (array $data) {
list ($databoxId, $recordId) = $data;
return RecordReference::createFromDataboxIdAndRecordId($databoxId, $recordId);
}
);
$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);
$orderableSubdefs = [];
foreach ($subdefs as $subdef) {
$databoxId = $subdef->get_sbas_id();
$recordId = $subdef->get_record_id();
if (!isset($orderableSubdefs[$databoxId][$recordId])) {
$orderableSubdefs[$databoxId][$recordId] = [];
}
$orderableSubdefs[$databoxId][$recordId][] = $subdef;
}
foreach ($models as $model) {
foreach ($model->getElements() as $element) {
$databoxId = $collectionToDataboxMap[$element->getElement()->getBaseId()];
$recordId = $element->getElement()->getRecordId();
if (isset($orderableSubdefs[$databoxId][$recordId])) {
$element->setOrderableMediaSubdefs($orderableSubdefs[$databoxId][$recordId]);
}
}
}
}
/**
* @param Order[] $orders
* @return OrderViewModel[]
*/
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 ($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);
}
}
}

View File

@@ -31,7 +31,7 @@ class OrderElementTransformer extends TransformerAbstract
$this->urlGenerator = $urlGenerator;
}
public function transform(OrderElementViewModel $model)
public function transform(OrderElementView $model)
{
$element = $model->getElement();
$record = $model->getRecordReference();
@@ -54,7 +54,7 @@ class OrderElementTransformer extends TransformerAbstract
return $data;
}
public function includeResourceLinks(OrderElementViewModel $model, ParamBag $params = null)
public function includeResourceLinks(OrderElementView $model, ParamBag $params = null)
{
$ttl = null;

View File

@@ -15,7 +15,7 @@ use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Model\RecordReferenceInterface;
use Assert\Assertion;
class OrderElementViewModel
class OrderElementView
{
/**
* @var OrderElement
@@ -39,6 +39,7 @@ class OrderElementViewModel
/**
* OrderElementViewModel constructor.
*
* @param OrderElement $element
* @param RecordReferenceInterface $record
* @param User $user
@@ -50,16 +51,25 @@ class OrderElementViewModel
$this->user = $user;
}
/**
* @return OrderElement
*/
public function getElement()
{
return $this->element;
}
/**
* @return RecordReferenceInterface
*/
public function getRecordReference()
{
return $this->record;
}
/**
* @return User
*/
public function getAuthenticatedUser()
{
return $this->user;
@@ -75,6 +85,9 @@ class OrderElementViewModel
$this->subdefs = $subdefs instanceof \Traversable ? iterator_to_array($subdefs) : $subdefs;
}
/**
* @return \media_subdef[]
*/
public function getOrderableMediaSubdefs()
{
return $this->subdefs;

View File

@@ -28,7 +28,7 @@ class OrderTransformer extends TransformerAbstract
$this->elementTransformer = $elementTransformer;
}
public function transform(OrderViewModel $view)
public function transform(OrderView $view)
{
$order = $view->getOrder();
@@ -47,7 +47,7 @@ class OrderTransformer extends TransformerAbstract
return $data;
}
public function includeElements(OrderViewModel $order)
public function includeElements(OrderView $order)
{
$elements = $order->getElements();

View File

@@ -13,14 +13,15 @@ namespace Alchemy\Phrasea\Order;
use Alchemy\Phrasea\Model\Entities\Order;
use Assert\Assertion;
class OrderViewModel
class OrderView
{
/**
* @var Order
*/
private $order;
/**
* @var OrderElementViewModel[]
* @var OrderElementView[]
*/
private $viewElements = [];
@@ -33,11 +34,11 @@ class OrderViewModel
}
/**
* @param $viewElements
* @param OrderElementView[] $viewElements
*/
public function setViewElements($viewElements)
{
Assertion::allIsInstanceOf($viewElements, OrderElementViewModel::class);
Assertion::allIsInstanceOf($viewElements, OrderElementView::class);
$this->viewElements = $viewElements instanceof \Traversable ? iterator_to_array($viewElements) : $viewElements;
}
@@ -51,7 +52,7 @@ class OrderViewModel
}
/**
* @return OrderElementViewModel[]
* @return OrderElementView[]
*/
public function getElements()
{

View File

@@ -0,0 +1,222 @@
<?php
/*
* This file is part of alchemy/pipeline-component.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Order;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Databox\Subdef\MediaSubdefService;
use Alchemy\Phrasea\Model\Entities\Order;
use Alchemy\Phrasea\Model\Entities\OrderElement;
use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Model\RecordReferenceInterface;
use Alchemy\Phrasea\Record\RecordReference;
use Alchemy\Phrasea\Record\RecordReferenceCollection;
use Assert\Assertion;
class OrderViewBuilder
{
/**
* @var Application
*/
private $application;
/**
* @var \appbox
*/
private $applicationBox;
/**
* @var MediaSubdefService
*/
private $mediaSubdefService;
/**
* @param Application $application
* @param \appbox $appbox
* @param MediaSubdefService $subdefService
*/
public function __construct(Application $application, \appbox $appbox, MediaSubdefService $subdefService)
{
$this->application = $application;
$this->applicationBox = $appbox;
$this->mediaSubdefService = $subdefService;
}
public function buildView(Order $order, array $includes)
{
$view = new OrderView($order);
$this->fillViews([$view], $includes);
return $view;
}
/**
* @param Order[] $orders
* @param string[] $includes
* @return OrderView[]
*/
public function buildViews(array $orders, array $includes)
{
Assertion::allIsInstanceOf($orders, Order::class);
$views = array_map(function (Order $order) {
return new OrderView($order);
}, $orders);
$this->fillViews($views, $includes);
return $views;
}
/**
* @param OrderView[] $views
* @param array $includes
* @return void
*/
private function fillViews(array $views, array $includes)
{
if (!in_array('elements', $includes, true)) {
return;
}
$elements = $this->gatherElements($views);
$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(
$allElements,
function (OrderElement $element) use ($collectionToDataboxMap) {
return isset($collectionToDataboxMap[$element->getBaseId()])
? [$collectionToDataboxMap[$element->getBaseId()], $element->getRecordId()]
: null;
},
function (array $data) {
list ($databoxId, $recordId) = $data;
return RecordReference::createFromDataboxIdAndRecordId($databoxId, $recordId);
}
);
$this->createOrderElementViews($views, $elements, $records);
if (!in_array('elements.resource_links', $includes, true)) {
return;
}
// Load all records
$records->toRecords($this->applicationBox);
// Load all subdefs
$subdefs = $this->mediaSubdefService->findSubdefsFromRecordReferenceCollection($records);
\media_Permalink_Adapter::getMany($this->application, $subdefs);
$orderableSubdefs = [];
foreach ($subdefs as $subdef) {
$databoxId = $subdef->get_sbas_id();
$recordId = $subdef->get_record_id();
if (!isset($orderableSubdefs[$databoxId][$recordId])) {
$orderableSubdefs[$databoxId][$recordId] = [];
}
$orderableSubdefs[$databoxId][$recordId][] = $subdef;
}
foreach ($views as $model) {
foreach ($model->getElements() as $element) {
$databoxId = $collectionToDataboxMap[$element->getElement()->getBaseId()];
$recordId = $element->getElement()->getRecordId();
if (isset($orderableSubdefs[$databoxId][$recordId])) {
$element->setOrderableMediaSubdefs($orderableSubdefs[$databoxId][$recordId]);
}
}
}
}
/**
* @param OrderView[] $elements
* @return OrderElement[][]
*/
private function gatherElements(array $elements)
{
Assertion::allIsInstanceOf($elements, OrderView::class);
$elements = [];
foreach ($elements 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->application['repo.collection-references']->findMany($baseIds) as $collectionReference) {
$collectionToDataboxMap[$collectionReference->getBaseId()] = $collectionReference->getDataboxId();
}
return $collectionToDataboxMap;
}
/**
* @param OrderView[] $orderViews
* @param OrderElement[][] $elements
* @param RecordReferenceInterface[]|RecordReferenceCollection $records
* @return void
*/
private function createOrderElementViews(array $orderViews, $elements, $records)
{
$user = $this->application->getAuthenticatedUser();
foreach ($orderViews as $index => $model) {
$models = [];
/** @var OrderElement $element */
foreach ($elements[$index] as $elementIndex => $element) {
if (isset($records[$element->getId()])) {
$models[$elementIndex] = new OrderElementView($element, $records[$element->getId()], $user);
}
}
$model->setViewElements($models);
}
}
}