Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
<?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
You can’t perform that action at this time.