Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ language: php
sudo: false

php:
- "7.0"
- "7.1"
- "7.2"
- "nightly"

env:
global:
- COMPOSER_MEMORY_LIMIT=-1

matrix:
allow_failures:
- php: nightly
Expand All @@ -30,7 +33,7 @@ before_install:
install:
- composer install
- cd test/
- composer install
- composer update
- rm -rf vendor/pomm-project/api-platform
- ln -s ../../../ vendor/pomm-project/api-platform

Expand Down
32 changes: 32 additions & 0 deletions src/DependencyInjection/Compiler/ResourceClassResolverPass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
/**
* This file is part of the pomm-api-platform-bridge package.
*
*/

namespace PommProject\ApiPlatform\DependencyInjection\Compiler;


use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

/**
* @author Mikael Paris <[email protected]>
*/
final class ResourceClassResolverPass implements CompilerPassInterface
{
/**
* {@inheritDoc}
*/
public function process(ContainerBuilder $container)
{
if ($container->hasDefinition('api_platform.resource_class_resolver')) {
$definition = new Definition('PommProject\ApiPlatform\ResourceClassResolver');
$definition->addArgument(new Reference('api_platform.metadata.resource.name_collection_factory'));

$container->setDefinition('api_platform.resource_class_resolver', $definition);
}
}
}
102 changes: 102 additions & 0 deletions src/ItemDataPersister.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php
/**
* This file is part of the pomm-api-platform-bridge package.
*
*/

namespace PommProject\ApiPlatform;


use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use PommProject\Foundation\Pomm;
use PommProject\ModelManager\Model\FlexibleEntity\FlexibleEntityInterface;
use ReflectionClass;

/**
* @author Mikael Paris <[email protected]>
*/
final class ItemDataPersister implements ContextAwareDataPersisterInterface
{
/**
* @var Pomm
*/
protected $pomm;

public function __construct(Pomm $pomm)
{
$this->pomm = $pomm;
}

/**
* {@inheritdoc}
*/
public function supports($data, array $context = []): bool
{
$proxyClass = new ReflectionClass($data);

return $proxyClass->implementsInterface(FlexibleEntityInterface::class);
}

/**
* {@inheritdoc}
*/
public function persist($data, array $context = [])
{
$model = $this->getModel($data, $context);

if (null === $model) {
return $data;
}

if (isset($context['collection_operation_name']) && 'post' === $context['collection_operation_name']) {
$model->insertOne($data);
} elseif (isset($context['item_operation_name']) && 'put' === $context['item_operation_name']) {
$fields = array_keys($data->fields());
$model->updateOne($data, $fields);
}

return $data;
}

/**
* {@inheritdoc}
*/
public function remove($data, array $context = [])
{
$model = $this->getModel($data, $context);

if (null === $model) {
return $data;
}

if (isset($context['item_operation_name']) && 'delete' === $context['item_operation_name']) {
$model->deleteOne($data);
}

return $data;
}

protected function getModel($data, $context)
{
$proxyClass = new ReflectionClass($data);
$class = $proxyClass->getName();

if (isset($context['session:name'])) {
$session = $this->pomm->getSession($context['session:name']);
} else {
$session = $this->pomm->getDefaultSession();
}

if (isset($context['model:name'])) {
$model_name = $context['model:name'];
} else {
$model_name = "${class}Model";
}

if (!class_exists($model_name)) {
return;
}

return $session->getModel($model_name);
}
}
12 changes: 11 additions & 1 deletion src/ItemDataProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@
namespace PommProject\ApiPlatform;

use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use ApiPlatform\Core\Exception\ResourceClassNotSupportedException;
use PommProject\Foundation\Pomm;
use PommProject\ModelManager\Model\FlexibleEntity\FlexibleEntityInterface;
use ReflectionClass;

class ItemDataProvider implements ItemDataProviderInterface
class ItemDataProvider implements ItemDataProviderInterface, RestrictedDataProviderInterface
{
private $pomm;

Expand All @@ -24,6 +27,13 @@ public function __construct(Pomm $pomm)
$this->pomm = $pomm;
}

public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
{
$proxyClass = new ReflectionClass($resourceClass);

return $proxyClass->implementsInterface(FlexibleEntityInterface::class);
}

/**
* {@inheritdoc}
*/
Expand Down
73 changes: 73 additions & 0 deletions src/Metadata/Property/PommPropertyMetadataFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php
/**
* This file is part of the pomm-api-platform-bridge package.
*
*/

namespace PommProject\ApiPlatform\Metadata\Property;


use ApiPlatform\Core\Exception\PropertyNotFoundException;
use ApiPlatform\Core\Exception\ResourceClassNotFoundException;
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
use PommProject\Foundation\Pomm;

/**
* @author Mikael Paris <[email protected]>
*/
final class PommPropertyMetadataFactory implements PropertyMetadataFactoryInterface
{
private $pomm;

private $decorated;

public function __construct(Pomm $pomm, PropertyMetadataFactoryInterface $decorated)
{
$this->pomm = $pomm;
$this->decorated = $decorated;
}

/**
* Creates a property metadata.
*
* @throws PropertyNotFoundException
*/
public function create(string $resourceClass, string $property, array $options = []): PropertyMetadata
{
$propertyMetadata = $this->decorated->create($resourceClass, $property, $options);

if (null !== $propertyMetadata->isIdentifier()) {
return $propertyMetadata;
}

$session = $this->pomm->getDefaultSession();
$modelName = "${resourceClass}Model";

if (!class_exists($modelName)) {
return $propertyMetadata;
}

$model = $session->getModel($modelName);
$fieldNames = $model->getStructure()
->getFieldNames();

$primaryKeys = $model->getStructure()
->getPrimaryKey();

if (in_array($property, $primaryKeys)) {
$propertyMetadata = $propertyMetadata->withIdentifier(true);
}

if (null === $propertyMetadata->isIdentifier()) {
$propertyMetadata = $propertyMetadata->withIdentifier(false);
}

if (in_array($property, $fieldNames)) {
$propertyMetadata = $propertyMetadata->withReadable(true);
$propertyMetadata = $propertyMetadata->withWritable(true);
}

return $propertyMetadata;
}
}
8 changes: 8 additions & 0 deletions src/PommApiPlatformBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,16 @@

namespace PommProject\ApiPlatform;

use PommProject\ApiPlatform\DependencyInjection\Compiler\ResourceClassResolverPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use \Symfony\Component\HttpKernel\Bundle\Bundle;

class PommApiPlatformBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);

$container->addCompilerPass(new ResourceClassResolverPass());
}
}
95 changes: 95 additions & 0 deletions src/ResourceClassResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php
/**
* This file is part of the pomm-api-platform-bridge package.
*
*/

namespace PommProject\ApiPlatform;

use ApiPlatform\Core\Api\ResourceClassResolverInterface;
use ApiPlatform\Core\Exception\InvalidArgumentException;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
use ApiPlatform\Core\Util\ClassInfoTrait;
use PommProject\ModelManager\Model\FlexibleEntity\FlexibleEntityInterface;

/**
* @author Mikael Paris <[email protected]>
*/
final class ResourceClassResolver implements ResourceClassResolverInterface
{
use ClassInfoTrait;

private $resourceNameCollectionFactory;
private $localIsResourceClassCache = [];

public function __construct(ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory)
{
$this->resourceNameCollectionFactory = $resourceNameCollectionFactory;
}

/**
* {@inheritdoc}
*/
public function getResourceClass($value, string $resourceClass = null, bool $strict = false): string
{
if ($strict && null === $resourceClass) {
throw new InvalidArgumentException('Strict checking is only possible when resource class is specified.');
}

$actualClass = \is_object($value) && (!$value instanceof \Traversable || $value instanceof FlexibleEntityInterface) ? $this->getObjectClass($value) : null;

if (null === $actualClass && null === $resourceClass) {
throw new InvalidArgumentException('Resource type could not be determined. Resource class must be specified.');
}

if (null !== $actualClass && !$this->isResourceClass($actualClass)) {
throw new InvalidArgumentException(sprintf('No resource class found for object of type "%s".', $actualClass));
}

if (null !== $resourceClass && !$this->isResourceClass($resourceClass)) {
throw new InvalidArgumentException(sprintf('Specified class "%s" is not a resource class.', $resourceClass));
}

if ($strict && null !== $actualClass && !is_a($actualClass, $resourceClass, true)) {
throw new InvalidArgumentException(sprintf('Object of type "%s" does not match "%s" resource class.', $actualClass, $resourceClass));
}

$targetClass = $actualClass ?? $resourceClass;
$mostSpecificResourceClass = null;

foreach ($this->resourceNameCollectionFactory->create() as $resourceClassName) {
if (!is_a($targetClass, $resourceClassName, true)) {
continue;
}

if (null === $mostSpecificResourceClass || is_subclass_of($resourceClassName, $mostSpecificResourceClass)) {
$mostSpecificResourceClass = $resourceClassName;
}
}

if (null === $mostSpecificResourceClass) {
throw new \LogicException('Unexpected execution flow.');
}

return $mostSpecificResourceClass;
}

/**
* {@inheritdoc}
*/
public function isResourceClass(string $type): bool
{
if (isset($this->localIsResourceClassCache[$type])) {
return $this->localIsResourceClassCache[$type];
}

foreach ($this->resourceNameCollectionFactory->create() as $resourceClass) {
if (is_a($type, $resourceClass, true)) {
return $this->localIsResourceClassCache[$type] = true;
}
}

return $this->localIsResourceClassCache[$type] = false;
}
}

Loading