Current File : /home/pacjaorg/www/km/libraries/src/MVC/View/JsonApiView.php |
<?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\MVC\View;
use Joomla\CMS\Document\JsonapiDocument;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\View\Event\OnGetApiFields;
use Joomla\CMS\Router\Exception\RouteNotFoundException;
use Joomla\CMS\Serializer\JoomlaSerializer;
use Joomla\CMS\Uri\Uri;
use Tobscure\JsonApi\AbstractSerializer;
use Tobscure\JsonApi\Collection;
use Tobscure\JsonApi\Resource;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Base class for a Joomla Json List View
*
* Class holding methods for displaying presentation data.
*
* @since 4.0.0
*/
abstract class JsonApiView extends JsonView
{
/**
* The active document object (Redeclared for typehinting)
*
* @var JsonapiDocument
* @since 3.0
*/
public $document;
/**
* The content type
*
* @var string
*/
protected $type;
/**
* Item relationship
*
* @var array
*
* @since 4.0.0
*/
protected $relationship = [];
/**
* Serializer data
*
* @var AbstractSerializer
* @since 4.0.0
*/
protected $serializer;
/**
* The fields to render item in the documents
*
* @var array
* @since 4.0.0
*/
protected $fieldsToRenderItem = [];
/**
* The fields to render items in the documents
*
* @var array
* @since 4.0.0
*/
protected $fieldsToRenderList = [];
/**
* Constructor.
*
* @param array $config A named configuration array for object construction.
* contentType: the name (optional) of the content type to use for the serialization
*
* @since 4.0.0
*/
public function __construct($config = [])
{
if (\array_key_exists('contentType', $config)) {
$this->type = $config['contentType'];
}
if ($this->serializer === null) {
$this->serializer = new JoomlaSerializer($this->type);
}
parent::__construct($config);
}
/**
* Execute and display a template script.
*
* @param array|null $items Array of items
*
* @return string
*
* @since 4.0.0
*/
public function displayList(array $items = null)
{
/** @var \Joomla\CMS\MVC\Model\ListModel $model */
$model = $this->getModel();
// Get page query
$currentUrl = Uri::getInstance();
$currentPageDefaultInformation = ['offset' => 0, 'limit' => 20];
$currentPageQuery = $currentUrl->getVar('page', $currentPageDefaultInformation);
if ($items === null) {
$items = [];
foreach ($model->getItems() as $item) {
$items[] = $this->prepareItem($item);
}
}
$pagination = $model->getPagination();
// Check for errors.
if (\count($errors = $this->get('Errors'))) {
throw new GenericDataException(implode("\n", $errors), 500);
}
if ($this->type === null) {
throw new \RuntimeException('Content type missing');
}
// Set up links for pagination
$totalItemsCount = ($pagination->pagesTotal * $pagination->limit);
$this->getDocument()->addMeta('total-pages', $pagination->pagesTotal)
->addLink('self', (string) $currentUrl);
// Check for first and previous pages
if ($pagination->limitstart > 0) {
$firstPage = clone $currentUrl;
$firstPageQuery = $currentPageQuery;
$firstPageQuery['offset'] = 0;
$firstPage->setVar('page', $firstPageQuery);
$previousPage = clone $currentUrl;
$previousPageQuery = $currentPageQuery;
$previousOffset = $currentPageQuery['offset'] - $pagination->limit;
$previousPageQuery['offset'] = $previousOffset >= 0 ? $previousOffset : 0;
$previousPage->setVar('page', $previousPageQuery);
$this->getDocument()->addLink('first', $this->queryEncode((string) $firstPage))
->addLink('previous', $this->queryEncode((string) $previousPage));
}
// Check for next and last pages
if ($pagination->limitstart + $pagination->limit < $totalItemsCount) {
$nextPage = clone $currentUrl;
$nextPageQuery = $currentPageQuery;
$nextOffset = $currentPageQuery['offset'] + $pagination->limit;
$nextPageQuery['offset'] = ($nextOffset > ($pagination->pagesTotal * $pagination->limit)) ? $pagination->pagesTotal - $pagination->limit : $nextOffset;
$nextPage->setVar('page', $nextPageQuery);
$lastPage = clone $currentUrl;
$lastPageQuery = $currentPageQuery;
$lastPageQuery['offset'] = ($pagination->pagesTotal - 1) * $pagination->limit;
$lastPage->setVar('page', $lastPageQuery);
$this->getDocument()->addLink('next', $this->queryEncode((string) $nextPage))
->addLink('last', $this->queryEncode((string) $lastPage));
}
$eventData = ['type' => OnGetApiFields::LIST, 'fields' => $this->fieldsToRenderList, 'context' => $this->type];
$event = new OnGetApiFields('onApiGetFields', $eventData);
/** @var OnGetApiFields $eventResult */
$eventResult = Factory::getApplication()->getDispatcher()->dispatch('onApiGetFields', $event);
$collection = (new Collection($items, $this->serializer))
->fields([$this->type => $eventResult->getAllPropertiesToRender()]);
if (!empty($this->relationship)) {
$collection->with($this->relationship);
}
// Set the data into the document and render it
$this->getDocument()->setData($collection);
return $this->getDocument()->render();
}
/**
* Execute and display a template script.
*
* @param object $item Item
*
* @return string
*
* @since 4.0.0
*/
public function displayItem($item = null)
{
if ($item === null) {
/** @var \Joomla\CMS\MVC\Model\AdminModel $model */
$model = $this->getModel();
$item = $this->prepareItem($model->getItem());
}
if ($item->id === null) {
throw new RouteNotFoundException('Item does not exist');
}
// Check for errors.
if (\count($errors = $this->get('Errors'))) {
throw new GenericDataException(implode("\n", $errors), 500);
}
if ($this->type === null) {
throw new \RuntimeException('Content type missing');
}
$eventData = [
'type' => OnGetApiFields::ITEM,
'fields' => $this->fieldsToRenderItem,
'relations' => $this->relationship,
'context' => $this->type,
];
$event = new OnGetApiFields('onApiGetFields', $eventData);
/** @var OnGetApiFields $eventResult */
$eventResult = Factory::getApplication()->getDispatcher()->dispatch('onApiGetFields', $event);
$element = (new Resource($item, $this->serializer))
->fields([$this->type => $eventResult->getAllPropertiesToRender()]);
if (!empty($this->relationship)) {
$element->with($eventResult->getAllRelationsToRender());
}
$this->getDocument()->setData($element);
$this->getDocument()->addLink('self', Uri::current());
return $this->getDocument()->render();
}
/**
* Prepare item before render.
*
* @param object $item The model item
*
* @return object
*
* @since 4.0.0
*/
protected function prepareItem($item)
{
return $item;
}
/**
* Encode square brackets in the URI query, according to JSON API specification.
*
* @param string $query The URI query
*
* @return string
*
* @since 4.0.0
*/
protected function queryEncode($query)
{
return str_replace(['[', ']'], ['%5B', '%5D'], $query);
}
}