Seditio Source
Root |
./othercms/croogo-4.0.7/vendor/friendsofcake/crud-json-api/src/Schema/JsonApi/DynamicEntitySchema.php
<?php
namespace CrudJsonApi\Schema\JsonApi;

use
Cake\Core\App;
use
Cake\Datasource\EntityInterface;
use
Cake\ORM\Association;
use
Cake\ORM\Table;
use
Cake\Routing\Router;
use
Cake\Utility\Inflector;
use
Cake\View\View;
use
Neomerx\JsonApi\Contracts\Factories\FactoryInterface;
use
Neomerx\JsonApi\Contracts\Schema\LinkInterface;
use
Neomerx\JsonApi\Schema\BaseSchema;
use
Neomerx\JsonApi\Schema\Identifier;

/**
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 */
class DynamicEntitySchema extends BaseSchema
{
   
/**
     * Holds the instance of Cake\View\View
     * @var \Cake\View\View
     */
   
protected $view;
   
/**
     * @var \Cake\ORM\Table
     */
   
protected $repository;

   
/**
     * Class constructor
     *
     * @param \Neomerx\JsonApi\Contracts\Factories\FactoryInterface $factory ContainerInterface
     * @param \Cake\View\View $view Instance of the cake view we are rendering this in
     * @param \Cake\ORM\Table $repository Repository to use
     */
   
public function __construct(
       
FactoryInterface $factory,
       
View $view,
       
Table $repository
   
) {
       
$this->view = $view;
       
$this->repository = $repository;

       
parent::__construct($factory);
    }

   
/**
     * @param \Cake\ORM\Table $repository The repository object
     *
     * @return mixed
     */
   
private function getTypeFromRepository(Table $repository)
    {
       
$repositoryName = App::shortName(get_class($repository), 'Model/Table', 'Table');
        [,
$entityName] = pluginSplit($repositoryName);
       
$method = $this->view->get('_inflect', 'dasherize');

        return
Inflector::$method($entityName);
    }

   
/**
     * @inheritDoc
     */
   
public function getType(): string
   
{
        return
$this->getTypeFromRepository($this->getRepository());
    }

   
/**
     * Get resource id.
     *
     * @param \Cake\ORM\Entity $entity Entity
     * @return string
     */
   
public function getId($entity): ?string
   
{
        return (string)
$entity->get($this->repository->getPrimaryKey());
    }

   
/**
     * @param \Cake\Datasource\EntityInterface $entity Entity
     * @return \Cake\ORM\Table
     */
   
protected function getRepository($entity = null): Table
   
{
        if (!
$entity) {
            return
$this->repository;
        }

       
$repositoryName = $entity->getSource();

        return
$this->view->get('_repositories')[$repositoryName];
    }

   
/**
     * Returns an array with all the properties that have been set
     * to this entity
     *
     * This method will ignore any properties that are entities.
     *
     * @param \Cake\Datasource\EntityInterface $entity Entity
     * @return array
     */
   
protected function entityToShallowArray(EntityInterface $entity)
    {
       
$result = [];
       
$properties = method_exists($entity, 'getVisible')
            ?
$entity->getVisible()
            :
$entity->visibleProperties();
        foreach (
$properties as $property) {
            if (
$property === '_joinData') {
                continue;
            }

           
$value = $entity->get($property);
            if (
is_array($value)) {
               
$result[$property] = [];
                foreach (
$value as $k => $innerValue) {
                    if (!
$innerValue instanceof EntityInterface) {
                       
$result[$property][$k] = $innerValue;
                    }
                }
            } else {
               
$result[$property] = $value;
            }
        }

        return
$result;
    }

   
/**
     * NeoMerx override used to pass entity root properties to be shown
     * as JsonApi `attributes`.
     *
     * @param \Cake\Datasource\EntityInterface $entity Entity
     * @return array
     */
   
public function getAttributes($entity): iterable
   
{
       
$entity->setHidden((array)$this->getRepository()->getPrimaryKey(), true);

       
$attributes = $this->entityToShallowArray($entity);

       
// remove associated data so it won't appear inside jsonapi `attributes`
       
foreach ($this->getRepository()->associations() as $association) {
           
$propertyName = $association->getProperty();

            if (
$association->type() === Association::MANY_TO_ONE) {
               
$foreignKey = (array)$association->getForeignKey();
               
$attributes = array_diff_key($attributes, array_flip($foreignKey));
            }

            unset(
$attributes[$propertyName]);
        }

       
// dasherize attribute keys (like `created_by`) if need be
       
if ($this->view->get('_inflect', 'dasherize') === 'dasherize') {
            foreach (
$attributes as $key => $value) {
               
$dasherizedKey = Inflector::dasherize($key);

                if (!
array_key_exists($dasherizedKey, $attributes)) {
                   
$attributes[$dasherizedKey] = $value;
                    unset(
$attributes[$key]);
                }
            }
        }

        return
$attributes;
    }

   
/**
     * NeoMerx override used to pass associated entity names to be used for
     * generating JsonApi `relationships`.
     *
     * JSON API optional `related` links not implemented yet.
     *
     * @param \Cake\Datasource\EntityInterface $entity Entity object
     * @return array
     */
   
public function getRelationships($entity): iterable
   
{
       
$relations = [];

        foreach (
$this->getRepository()->associations() as $association) {
           
$property = $association->getProperty();
           
$foreignKey = $association->getForeignKey();

           
$data = $entity->get($property);
           
//If no data, and it's not a BelongsTo relationship, skip
           
if (!$data && $association->type() !== Association::MANY_TO_ONE) {
                continue;
            }

           
//If there is no data, and the foreignKey field is null, skip
           
if (!$data && (is_array($foreignKey) || !$entity->get($foreignKey))) {
                continue;
            }

           
// change related  data in entity to dasherized if need be
           
if ($this->view->get('_inflect', 'dasherize') === 'dasherize') {
               
$dasherizedProperty = Inflector::dasherize($property);

                if (empty(
$entity->$dasherizedProperty)) {
                   
$entity->$dasherizedProperty = $entity->$property;
                    unset(
$entity->$property);
                   
$property = $dasherizedProperty;
                }
            }

            if (!
$data && !is_array($foreignKey)) {
               
$data = new Identifier($entity->get($foreignKey), $this->getTypeFromRepository($association->getTarget()));
            }

           
$isOne = \in_array($association->type(), [Association::MANY_TO_ONE, Association::ONE_TO_ONE]);
           
$relations[$property] = [
               
self::RELATIONSHIP_DATA => $data,
               
self::RELATIONSHIP_LINKS_SELF => $isOne,
               
self::RELATIONSHIP_LINKS_RELATED => !$isOne,
            ];
        }

        return
$relations;
    }

   
/**
     * NeoMerx override used to generate `self` links
     *
     * @param \Cake\ORM\Entity|null $entity Entity, null only to be compatible with the Neomerx method
     * @return string
     */
   
public function getSelfSubUrl($entity = null): string
   
{
        if (
$entity === null) {
            return
'';
        }

       
$keys = array_values($entity->extract((array)$this->getRepository()->getPrimaryKey()));

        return
Router::url($this->_getRepositoryRoutingParameters($this->repository) + $keys + [
           
'_method' => 'GET',
           
'action' => 'view',
        ],
$this->view->get('_absoluteLinks'));
    }

   
/**
     * @param string $name Relationship name in lowercase singular or plural
     *
     * @return \Cake\ORM\Association|null
     */
   
protected function getAssociationByProperty(string $name): ?Association
   
{
        if (
$this->view->get('_inflect', 'dasherize') === 'dasherize') {
           
$name = Inflector::underscore($name);
        }

        return
$this->getRepository()
            ->
associations()
            ->
getByProperty($name);
    }

   
/**
     * NeoMerx override to generate belongsTo and hasOne links
     * inside `relationships` node.
     *
     * Example: /cultures?country_id=1 (or /country/1/cultures if your routes are configured like this)
     *
     * @param \Cake\Datasource\EntityInterface $entity Entity
     * @param string $name Relationship name in lowercase singular or plural
     *
     * @return \Neomerx\JsonApi\Contracts\Schema\LinkInterface
     */
   
public function getRelationshipSelfLink($entity, string $name): LinkInterface
   
{
       
$association = $this->getAssociationByProperty($name);
        if (!
$association) {
            throw new \
InvalidArgumentException('Invalid association ' . $name);
        }

       
$relatedRepository = $association->getTarget();

       
// generate link for belongsTo relationship
       
if ($this->view->get('_jsonApiBelongsToLinks') === true) {
            [,
$controllerName] = pluginSplit($this->getRepository()->getRegistryAlias());
           
$sourceName = Inflector::underscore(Inflector::singularize($controllerName));

           
$url = Router::url($this->_getRepositoryRoutingParameters($relatedRepository) + [
               
'_method' => 'GET',
               
'action' => 'view',
               
$sourceName . '_id' => $entity->id,
               
'from' => $this->getRepository()->getRegistryAlias(),
               
'type' => $name,
            ],
$this->view->get('_absoluteLinks'));
        } else {
           
$name = Inflector::dasherize($name);
           
$relatedEntity = $entity->get($name);

            if (
$relatedEntity) {
               
$keys = array_values($relatedEntity->extract((array)$relatedRepository->getPrimaryKey()));
            } else {
               
$keys = array_values($entity->extract((array)$association->getForeignKey()));
            }

           
$url = Router::url($this->_getRepositoryRoutingParameters($relatedRepository) + $keys + [
               
'_method' => 'GET',
               
'action' => 'view',
            ],
$this->view->get('_absoluteLinks'));
        }

        return
$this->getFactory()->createLink(false, $url, false);
    }

   
/**
     * NeoMerx override to generate hasMany and belongsToMany links
     * inside `relationships` node.
     *
     * hasMany example"   /countries/1/currencies"
     *
     * @param \Cake\Datasource\EntityInterface $entity Entity
     * @param string $name Relationship name in lowercase singular or plural
     *
     * @return \Neomerx\JsonApi\Contracts\Schema\LinkInterface
     */
   
public function getRelationshipRelatedLink($entity, string $name): LinkInterface
   
{
       
$association = $this->getAssociationByProperty($name);
        if (!
$association) {
            throw new \
InvalidArgumentException('Invalid association ' . $name);
        }

       
$relatedRepository = $association->getTarget();

       
// generate the link for hasMany relationship
       
$foreignKeys = (array)$association->getForeignKey();
       
$primaryKeys = $entity->extract((array)$this->getRepository()->getPrimaryKey());
       
$keys = array_combine($foreignKeys, $primaryKeys);

       
$url = Router::url($this->_getRepositoryRoutingParameters($relatedRepository) + $keys + [
               
'_method' => 'GET',
               
'action' => 'index',
            ],
$this->view->get('_absoluteLinks'));

        return
$this->getFactory()
            ->
createLink(false, $url, false);
    }

   
/**
     * Parses the name of an Entity class to build a lowercase plural
     * controller name to be used in links.
     *
     * @param \Cake\Datasource\RepositoryInterface $repository Repository
     * @return array Array holding lowercase controller name as the value
     */
   
protected function _getRepositoryRoutingParameters($repository)
    {
       
$repositoryName = App::shortName(get_class($repository), 'Model/Table', 'Table');
        [
$pluginName, $controllerName] = pluginSplit($repositoryName);

        return [
           
'controller' => $controllerName,
           
'plugin' => $pluginName
       
];
    }
}