<?php | |
/* | |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
* | |
* This software consists of voluntary contributions made by many individuals | |
* and is licensed under the MIT license. | |
*/ | |
namespace Application\View\Renderer; | |
use Doctrine\Common\Util\Inflector; | |
use Zend\Paginator\Paginator; | |
use Zend\Stdlib\Hydrator\HydratorPluginManager; | |
use Zend\View\HelperPluginManager; | |
use ZfrRest\Resource\Resource; | |
use ZfrRest\Resource\ResourceInterface; | |
use ZfrRest\View\Model\ResourceModel; | |
use ZfrRest\View\Renderer\AbstractResourceRenderer; | |
/** | |
* @author Michaël Gallego <mic.gallego@gmail.com> | |
* @licence MIT | |
*/ | |
class EmberResourceRenderer extends AbstractResourceRenderer | |
{ | |
/** | |
* @var HydratorPluginManager | |
*/ | |
protected $hydratorPluginManager; | |
/** | |
* @var HelperPluginManager | |
*/ | |
protected $helperManager; | |
/** | |
* @param HydratorPluginManager $hydratorPluginManager | |
* @param HelperPluginManager $helperManager | |
*/ | |
public function __construct(HydratorPluginManager $hydratorPluginManager, HelperPluginManager $helperManager) | |
{ | |
$this->hydratorPluginManager = $hydratorPluginManager; | |
$this->helperManager = $helperManager; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function render($nameOrModel, $values = null) | |
{ | |
if (!$nameOrModel instanceof ResourceModel) { | |
return; | |
} | |
$resource = $nameOrModel->getResource(); | |
if ($resource->isCollection()) { | |
$payload = $this->renderCollection($resource); | |
} else { | |
$payload = $this->renderItem($resource); | |
} | |
// EmberJS accepts a "meta" section where data like pagination is inserted | |
$payload = array_merge($payload, $this->renderMeta($resource)); | |
return json_encode($payload); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function renderItem(ResourceInterface $resource) | |
{ | |
$hydratorName = $resource->getMetadata()->getHydratorName(); | |
$hydrator = $this->hydratorPluginManager->get($hydratorName); | |
$data = $hydrator->extract($resource->getData()); | |
$payload = []; | |
// We now have the data, however we have to deal with associations. The hydrator may have | |
// embedded the associations data into the payload OR just returned identifier OR simply returned | |
// nothing | |
$resourceMetadata = $resource->getMetadata(); | |
$classMetadata = $resourceMetadata->getClassMetadata(); | |
$associations = $classMetadata->getAssociationNames(); | |
/* @var \Zend\View\Helper\Url $urlHelper */ | |
$urlHelper = $this->helperManager->get('Url'); | |
$links = []; | |
$embedded = []; | |
foreach ($associations as $association) { | |
// If the association is exposed in the REST metadata, we output it! | |
if (!$resourceMetadata->hasAssociationMetadata($association) && !isset($data[$association])) { | |
continue; | |
} | |
// Three cases can happen. | |
// FIRST: there is no data, so we just output the link | |
if (!isset($data[$association])) { | |
$links[$association] = $urlHelper(null, ['resource' => $resource, 'association' => $association]); | |
} | |
// Else, we have data as array, this means that the association is embedded | |
if (is_array($data[$association])) { | |
$embedded[$this->getKeyForClass($association)] = $data[$association]; | |
$data[$association] = $data[$association]['id']; | |
} | |
} | |
if (!empty($links)) { | |
$data['links'] = $links; | |
} | |
/*foreach ($associations as $association) { | |
if (!isset($data[$association])) { | |
// If nothing in the payload AND if the association has | |
} | |
$associationData = $data[$association]; | |
// If it's a string, then we need to generate a link | |
// If it's not associative array, then the association full representation has been included | |
// in the payload, so we extract it | |
if (array_values($associationData) === $associationData) { | |
unset($data[$association]); | |
$payload['comments'][] = $associationData; | |
continue; | |
} | |
// Otherwise, the identifier has been embedded so we just output the URL | |
}*/ | |
$rootKey = $this->getKeyForClass($classMetadata->getName()); | |
$payload[$rootKey] = $data; | |
foreach ($embedded as $key => $value) { | |
$payload[Inflector::pluralize($key)] = [$value]; | |
} | |
return $payload; | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
public function renderCollection(ResourceInterface $resource) | |
{ | |
$hydratorName = $resource->getMetadata()->getCollectionMetadata()->getHydratorName(); | |
$hydrator = $this->hydratorPluginManager->get($hydratorName); | |
$data = []; | |
$payload = []; | |
// We now have the data, however we have to deal with associations. The hydrator may have | |
// embedded the associations data into the payload OR just returned identifier OR simply returned | |
// nothing | |
$resourceMetadata = $resource->getMetadata(); | |
$classMetadata = $resourceMetadata->getClassMetadata(); | |
$associations = $classMetadata->getAssociationNames(); | |
/* @var \Zend\View\Helper\Url $urlHelper */ | |
$urlHelper = $this->helperManager->get('Url'); | |
$embedded = []; | |
foreach ($resource->getData() as $item) { | |
$links = []; | |
$datum = $hydrator->extract($item); | |
foreach ($associations as $association) { | |
$singleResource = new Resource($item, $resourceMetadata); | |
// If the association is exposed in the REST metadata, we output it! | |
if (!$resourceMetadata->hasAssociationMetadata($association) && !isset($datum[$association])) { | |
continue; | |
} | |
// Three cases can happen. | |
// FIRST: there is no data, so we just output the link | |
if (!isset($datum[$association])) { | |
$links[$association] = $urlHelper(null, ['resource' => $singleResource, 'association' => $association]); | |
} | |
// Else, we have data as array, this means that the association is embedded | |
if (is_array($datum[$association])) { | |
$embedded[$this->getKeyForClass($association)][] = $datum[$association]; | |
$datum[$association] = $datum[$association]['id']; | |
} | |
} | |
if (!empty($links)) { | |
$datum['links'] = $links; | |
} | |
$data[] = $datum; | |
} | |
$rootKey = $this->getKeyForClass($classMetadata->getName()); | |
// As this is a collection, we need to inflect the root key to be plural | |
$payload[Inflector::pluralize($rootKey)] = $data; | |
foreach ($embedded as $key => $value) { | |
$payload[Inflector::pluralize($key)] = $value; | |
} | |
return $payload; | |
} | |
/** | |
* Currently, the only meta we extract are the pagination data | |
* | |
* @param ResourceInterface $resource | |
* @return array | |
*/ | |
public function renderMeta(ResourceInterface $resource) | |
{ | |
$data = $resource->getData(); | |
$meta = []; | |
if ($data instanceof Paginator) { | |
$meta = array_merge($meta, [ | |
'limit' => $data->getItemCountPerPage(), | |
'offset' => ($data->getCurrentPageNumber() - 1) * $data->getItemCountPerPage(), | |
'total' => $data->getTotalItemCount() | |
]); | |
} | |
// Ember-Data expects meta to be wrapped around a "meta" top-level key | |
return ['meta' => $meta]; | |
} | |
/** | |
* Get the key from the class name | |
* | |
* @param $className | |
* @return string | |
*/ | |
private function getKeyForClass($className) | |
{ | |
$parts = explode('\\', $className); | |
return lcfirst(end($parts)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment