Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ffb3d0f
Use object_to_populate constant in normalizers
tuanphpvn Oct 10, 2017
2d32ef9
Don't use dynamic values in service keys
dunglas Oct 24, 2017
a9672b0
Merge pull request #1458 from dunglas/dont-use-dynamic-param
dunglas Oct 24, 2017
7fc53b9
Use serializer AbstractItemNormalizer constants when related
soyuka Oct 13, 2017
d9d986d
Add a data persistence layer
meyerbaptiste Oct 25, 2017
c8f5c64
Fix dunglas' comments
meyerbaptiste Oct 30, 2017
c7e2cda
Merge pull request #1464 from meyerbaptiste/add_data_persister
dunglas Oct 30, 2017
e6b302e
Hydra: fix owl:allValuesFrom
dunglas Oct 30, 2017
0e221ff
Include swagger context even when type is null
Oct 30, 2017
2f2c569
Merge pull request #1467 from dunglas/fix-owl
dunglas Oct 31, 2017
290b619
Merge pull request #1466 from greg0ire/swagger_for_properties
dunglas Oct 31, 2017
4fa2e4a
Make it easier to configure operations
dunglas Oct 31, 2017
8a3b593
Merge pull request #1470 from dunglas/improved-operations
sroze Nov 1, 2017
32f65b7
Document what count() is supposed to return (#1468)
greg0ire Nov 2, 2017
497c668
Merge pull request #1419 from soyuka/fix/object_to_populate
dunglas Nov 2, 2017
ee30666
Update the changelog
dunglas Nov 2, 2017
140ba7a
fix eager loading filter with non-association composite identifier
Nov 2, 2017
de2c31e
[#1313] LIMIT and ORDER BY on a column from a fetch joined to-many as…
ambroisemaupate Oct 10, 2017
526edc5
Add test on #1449 query checker order by toMany
Nov 2, 2017
d3ee559
Merge pull request #1475 from soyuka/fix-filter-eager-loading
soyuka Nov 3, 2017
08a23be
Merge remote-tracking branch 'origin/2.1'
Nov 3, 2017
80c88f9
Fix Extension Test getDefaultContainer => getBaseContainer
Nov 3, 2017
d18ec0d
Merge pull request #1477 from soyuka/merge-2.1
soyuka Nov 3, 2017
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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@
* Automatically enable FOSUser support if the bundle is installed
* Add an `AbstractCollectionNormalizer` to help supporting custom formats
* Deprecate NelmioApiDocBundle 2 support (upgrade to v3, it has native API Platform support)
* Deprecate the `ApiPlatform\Core\Bridge\Doctrine\EventListener\WriteListener` class in favor of the new `ApiPlatform\Core\EventListener\WriteListener` class.
* Delete the `api_platform.doctrine.listener.view.write` event listener service.
* Add a data persistence layer with a new `ApiPlatform\Core\DataPersister\DataPersisterInterface` interface.

## 2.1.3

* Don't use dynamic values in Varnish-related service keys (improves Symfony 3.3 compatibility)
* Hydra: Fix the value of `owl:allValuesFrom` in the API documentation
* Swagger: Include the context even when the type is `null`
* Minor code and PHPDoc cleanups

## 2.1.2

Expand Down
82 changes: 82 additions & 0 deletions src/Bridge/Doctrine/Common/DataPersister.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Core\Bridge\Doctrine\Common;

use ApiPlatform\Core\DataPersister\DataPersisterInterface;
use ApiPlatform\Core\Util\ClassInfoTrait;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\Common\Persistence\ObjectManager as DoctrineObjectManager;

/**
* Data persister for Doctrine.
*
* @author Baptiste Meyer <baptiste.meyer@gmail.com>
*/
final class DataPersister implements DataPersisterInterface
{
use ClassInfoTrait;

private $managerRegistry;

public function __construct(ManagerRegistry $managerRegistry)
{
$this->managerRegistry = $managerRegistry;
}

/**
* {@inheritdoc}
*/
public function supports($data): bool
{
return null !== $this->getManager($data);
}

/**
* {@inheritdoc}
*/
public function persist($data)
{
if (!$manager = $this->getManager($data)) {
return;
}

$manager->persist($data);
$manager->flush();
}

/**
* {@inheritdoc}
*/
public function remove($data)
{
if (!$manager = $this->getManager($data)) {
return;
}

$manager->remove($data);
$manager->flush();
}

/**
* Gets the Doctrine object manager associated with given data.
*
* @param mixed $data
*
* @return DoctrineObjectManager|null
*/
private function getManager($data)
{
return is_object($data) ? $this->managerRegistry->getManagerForClass($this->getObjectClass($data)) : null;
}
}
3 changes: 3 additions & 0 deletions src/Bridge/Doctrine/EventListener/WriteListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace ApiPlatform\Core\Bridge\Doctrine\EventListener;

use ApiPlatform\Core\EventListener\WriteListener as BaseWriteListener;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\HttpFoundation\Request;
Expand All @@ -29,6 +30,8 @@ final class WriteListener

public function __construct(ManagerRegistry $managerRegistry)
{
@trigger_error(sprintf('The %s class is deprecated since version 2.2 and will be removed in 3.0. Use the %s class instead.', __CLASS__, BaseWriteListener::class), E_USER_DEPRECATED);

$this->managerRegistry = $managerRegistry;
}

Expand Down
12 changes: 7 additions & 5 deletions src/Bridge/Doctrine/Orm/Extension/EagerLoadingExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;

/**
* Eager loads relations.
Expand Down Expand Up @@ -99,8 +101,8 @@ public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterf
$contextType = isset($context['api_denormalize']) ? 'denormalization_context' : 'normalization_context';
$serializerContext = $this->getSerializerContext($context['resource_class'] ?? $resourceClass, $contextType, $options);

if (isset($context['groups'])) {
$groups = ['serializer_groups' => $context['groups']];
if (isset($context[AbstractNormalizer::GROUPS])) {
$groups = ['serializer_groups' => $context[AbstractNormalizer::GROUPS]];
} else {
$groups = $this->getSerializerGroups($options, $serializerContext);
}
Expand Down Expand Up @@ -137,7 +139,7 @@ private function joinRelations(QueryBuilder $queryBuilder, QueryNameGeneratorInt

foreach ($classMetadata->associationMappings as $association => $mapping) {
//Don't join if max depth is enabled and the current depth limit is reached
if (isset($context['enable_max_depth']) && 0 === $currentDepth) {
if (isset($context[AbstractObjectNormalizer::ENABLE_MAX_DEPTH]) && 0 === $currentDepth) {
continue;
}

Expand Down Expand Up @@ -280,10 +282,10 @@ private function getSerializerContext(string $resourceClass, string $contextType
*/
private function getSerializerGroups(array $options, array $context): array
{
if (empty($context['groups'])) {
if (empty($context[AbstractNormalizer::GROUPS])) {
return $options;
}

return ['serializer_groups' => $context['groups']];
return ['serializer_groups' => $context[AbstractNormalizer::GROUPS]];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGenerator
$queryBuilderClone->andWhere($queryBuilderClone->expr()->in($originAlias, $in->getDQL()));
} else {
// Because Doctrine doesn't support WHERE ( foo, bar ) IN () (https://github.com/doctrine/doctrine2/issues/5238), we are building as many subqueries as they are identifiers
foreach ($classMetadata->identifier as $identifier) {
foreach ($classMetadata->getIdentifier() as $identifier) {
if (!$classMetadata->hasAssociation($identifier)) {
continue;
}

$replacementAlias = $queryNameGenerator->generateJoinAlias($originAlias);
$in = $this->getQueryBuilderWithNewAliases($queryBuilder, $queryNameGenerator, $originAlias, $replacementAlias);
$in->select("IDENTITY($replacementAlias.$identifier)");
Expand Down
2 changes: 1 addition & 1 deletion src/Bridge/Doctrine/Orm/Util/EagerLoadingTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ private function hasFetchEagerAssociation(EntityManager $em, ClassMetadataInfo $
{
$checked[] = $classMetadata->name;

foreach ($classMetadata->associationMappings as $mapping) {
foreach ($classMetadata->getAssociationMappings() as $mapping) {
if (ClassMetadataInfo::FETCH_EAGER === $mapping['fetch']) {
return true;
}
Expand Down
10 changes: 7 additions & 3 deletions src/Bridge/Doctrine/Orm/Util/QueryChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,16 @@ public static function hasOrderByOnToManyJoin(QueryBuilder $queryBuilder, Manage
if (!isset($orderByAliases[$alias])) {
continue;
}

$relationship = QueryJoinParser::getJoinRelationship($join);

if (false !== strpos($relationship, '.')) {
$metadata = QueryJoinParser::getClassMetadataFromJoinAlias($alias, $queryBuilder, $managerRegistry);
if ($metadata->isCollectionValuedAssociation($relationship)) {
/*
* We select the parent alias because it may differ from the origin alias given above
* @see https://github.com/api-platform/core/issues/1313
*/
list($relationAlias, $association) = explode('.', $relationship);
$metadata = QueryJoinParser::getClassMetadataFromJoinAlias($relationAlias, $queryBuilder, $managerRegistry);
if ($metadata->isCollectionValuedAssociation($association)) {
return true;
}
} else {
Expand Down
21 changes: 11 additions & 10 deletions src/Bridge/NelmioApiDoc/Parser/ApiPlatformParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Nelmio\ApiDocBundle\Parser\ParserInterface;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;

/**
* Extract input and output information for the NelmioApiDocBundle.
Expand Down Expand Up @@ -117,15 +118,15 @@ private function parseResource(ResourceMetadata $resourceMetadata, string $resou
$options = [];
$attributes = $resourceMetadata->getAttributes();

if (isset($attributes['normalization_context']['groups'])) {
$options['serializer_groups'] = $attributes['normalization_context']['groups'];
if (isset($attributes['normalization_context'][AbstractNormalizer::GROUPS])) {
$options['serializer_groups'] = $attributes['normalization_context'][AbstractNormalizer::GROUPS];
}

if (isset($attributes['denormalization_context']['groups'])) {
if (isset($attributes['denormalization_context'][AbstractNormalizer::GROUPS])) {
if (isset($options['serializer_groups'])) {
$options['serializer_groups'] += $attributes['denormalization_context']['groups'];
$options['serializer_groups'] += $attributes['denormalization_context'][AbstractNormalizer::GROUPS];
} else {
$options['serializer_groups'] = $attributes['denormalization_context']['groups'];
$options['serializer_groups'] = $attributes['denormalization_context'][AbstractNormalizer::GROUPS];
}
}

Expand All @@ -135,12 +136,12 @@ private function parseResource(ResourceMetadata $resourceMetadata, string $resou
private function getGroupsContext(ResourceMetadata $resourceMetadata, string $operationName, bool $isNormalization)
{
$groupsContext = $isNormalization ? 'normalization_context' : 'denormalization_context';
$itemOperationAttribute = $resourceMetadata->getItemOperationAttribute($operationName, $groupsContext, ['groups' => []], true)['groups'];
$collectionOperationAttribute = $resourceMetadata->getCollectionOperationAttribute($operationName, $groupsContext, ['groups' => []], true)['groups'];
$itemOperationAttribute = $resourceMetadata->getItemOperationAttribute($operationName, $groupsContext, [AbstractNormalizer::GROUPS => []], true)[AbstractNormalizer::GROUPS];
$collectionOperationAttribute = $resourceMetadata->getCollectionOperationAttribute($operationName, $groupsContext, [AbstractNormalizer::GROUPS => []], true)[AbstractNormalizer::GROUPS];

return [
$groupsContext => [
'groups' => array_merge($itemOperationAttribute ?? [], $collectionOperationAttribute ?? []),
AbstractNormalizer::GROUPS => array_merge($itemOperationAttribute ?? [], $collectionOperationAttribute ?? []),
],
];
}
Expand All @@ -161,13 +162,13 @@ private function getGroupsForItemAndCollectionOperation(ResourceMetadata $resour

if (self::OUT_PREFIX === $io) {
return [
'serializer_groups' => !empty($operation['normalization_context']) ? $operation['normalization_context']['groups'] : [],
'serializer_groups' => !empty($operation['normalization_context']) ? $operation['normalization_context'][AbstractNormalizer::GROUPS] : [],
];
}

if (self::IN_PREFIX === $io) {
return [
'serializer_groups' => !empty($operation['denormalization_context']) ? $operation['denormalization_context']['groups'] : [],
'serializer_groups' => !empty($operation['denormalization_context']) ? $operation['denormalization_context'][AbstractNormalizer::GROUPS] : [],
];
}

Expand Down
2 changes: 2 additions & 0 deletions src/Bridge/Symfony/Bundle/ApiPlatformBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
namespace ApiPlatform\Core\Bridge\Symfony\Bundle;

use ApiPlatform\Core\Bridge\Symfony\Bundle\DependencyInjection\Compiler\AnnotationFilterPass;
use ApiPlatform\Core\Bridge\Symfony\Bundle\DependencyInjection\Compiler\DataPersisterPass;
use ApiPlatform\Core\Bridge\Symfony\Bundle\DependencyInjection\Compiler\DataProviderPass;
use ApiPlatform\Core\Bridge\Symfony\Bundle\DependencyInjection\Compiler\DoctrineQueryExtensionPass;
use ApiPlatform\Core\Bridge\Symfony\Bundle\DependencyInjection\Compiler\FilterPass;
Expand All @@ -34,6 +35,7 @@ public function build(ContainerBuilder $container)
{
parent::build($container);

$container->addCompilerPass(new DataPersisterPass());
$container->addCompilerPass(new DataProviderPass());
$container->addCompilerPass(new AnnotationFilterPass());
$container->addCompilerPass(new FilterPass());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use ApiPlatform\Core\DataPersister\DataPersisterInterface;
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
use ApiPlatform\Core\Exception\RuntimeException;
Expand All @@ -28,7 +29,6 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\Validator\Validator\ValidatorInterface;
Expand Down Expand Up @@ -86,9 +86,12 @@ public function load(array $configs, ContainerBuilder $container)

$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('api.xml');
$loader->load('data_persister.xml');
$loader->load('data_provider.xml');
$loader->load('filter.xml');

$container->registerForAutoconfiguration(DataPersisterInterface::class)
->addTag('api_platform.data_persister');
$container->registerForAutoconfiguration(ItemDataProviderInterface::class)
->addTag('api_platform.item_data_provider');
$container->registerForAutoconfiguration(CollectionDataProviderInterface::class)
Expand Down Expand Up @@ -466,17 +469,15 @@ private function registerHttpCache(ContainerBuilder $container, array $config, X

$loader->load('http_cache_tags.xml');

$references = [];
foreach ($config['http_cache']['invalidation']['varnish_urls'] as $url) {
$id = sprintf('api_platform.http_cache.purger.varnish_client.%s', $url);
$references[] = new Reference($id);

$definitions = [];
foreach ($config['http_cache']['invalidation']['varnish_urls'] as $key => $url) {
$definition = new ChildDefinition('api_platform.http_cache.purger.varnish_client');
$definition->addArgument(['base_uri' => $url]);
$container->setDefinition($id, $definition);

$definitions[] = $definition;
}

$container->getDefinition('api_platform.http_cache.purger.varnish')->addArgument($references);
$container->getDefinition('api_platform.http_cache.purger.varnish')->addArgument($definitions);
$container->setAlias('api_platform.http_cache.purger', 'api_platform.http_cache.purger.varnish');
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Core\Bridge\Symfony\Bundle\DependencyInjection\Compiler;

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

/**
* Registers data persisters.
*
* @internal
*
* @author Baptiste Meyer <baptiste@les-tilleuls.coop>
*/
final class DataPersisterPass implements CompilerPassInterface
{
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
$persisters = [];
$services = $container->findTaggedServiceIds('api_platform.data_persister', true);

foreach ($services as $serviceId => $tags) {
$persisters[] = new Reference($serviceId);
}

$container->getDefinition('api_platform.data_persister')->addArgument($persisters);
}
}
6 changes: 6 additions & 0 deletions src/Bridge/Symfony/Bundle/Resources/config/api.xml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="4" />
</service>

<service id="api_platform.listener.view.write" class="ApiPlatform\Core\EventListener\WriteListener">
<argument type="service" id="api_platform.data_persister" />

<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" priority="32" />
</service>

<service id="api_platform.listener.request.deserialize" class="ApiPlatform\Core\EventListener\DeserializeListener">
<argument type="service" id="api_platform.serializer" />
<argument type="service" id="api_platform.serializer.context_builder" />
Expand Down
11 changes: 11 additions & 0 deletions src/Bridge/Symfony/Bundle/Resources/config/data_persister.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
<service id="api_platform.data_persister" class="ApiPlatform\Core\DataPersister\ChainDataPersister" public="false" />
</services>

</container>
Loading