core icon indicating copy to clipboard operation
core copied to clipboard

[GraphQL] Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.

Open aelfannir opened this issue 3 years ago • 0 comments

API Platform version(s) affected: 2.6.8

Description

I have and entity named Route contains a self-reference relation parent/children, Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity exception throws when i call deleteRoute mutation;

  • When the entity has children it throws the exception after being deleted
  • When the entity has no child, it works.

How to reproduce

  • Route.php
<?php
declare(strict_types=1);

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiFilter;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\ExistsFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
use App\Entity\Classes\ApiPlatform;
use App\Entity\Traits\HasId;
use App\Entity\Traits\HasLocale;
use App\Entity\Traits\HasParentChildren;
use App\Entity\Traits\HasSortIndex;
use App\Entity\Traits\Tree\HasMaterializedPathTree;
use App\Filter\SimpleSearchFilter;
use App\Service\Validator;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Gedmo\Translatable\Entity\Translation;
use Gedmo\Tree\Entity\Repository\MaterializedPathRepository;
use Symfony\Component\Serializer\Annotation as Serializer;
use Symfony\Component\Validator\Constraints as Assert;

#[ORM\Entity(repositoryClass: MaterializedPathRepository::class)]
#[Gedmo\Tree(type: 'materializedPath')]
#[Gedmo\TranslationEntity(class: Translation::class)]
#[ApiFilter(SimpleSearchFilter::class, properties: ['title', 'routeKey'])]
#[ApiFilter(OrderFilter::class, properties: ['title', 'sortIndex', 'treeLevel'])]
#[ApiFilter(ExistsFilter::class, properties: ['parent'])]
#[ApiFilter(SearchFilter::class, properties: [
    'views' => 'partial',
    'parent' => 'exact',
    'roles.roleKey' => 'exact',
])]
#[ApiResource(attributes: ApiPlatform::DEFAULT_ATTRIBUTES)]
class Route
{
    use HasId;
    use HasSortIndex;
    use HasMaterializedPathTree;
    use HasLocale;
    use HasParentChildren;

    #[Assert\NotBlank(message: Validator::NOT_BLANK)]
    #[ORM\Column(unique: true)]
    private string $routeKey;

    #[Assert\NotBlank(message: Validator::NOT_BLANK)]
    #[ORM\Column]
    #[Gedmo\Translatable]
    private string $title;

    #[ORM\Column(nullable: true)]
    private ?string $icon;

    #[ORM\Column(type: 'array')]
    private array $views = [];

    #[ORM\JoinColumn(onDelete: 'SET NULL')]
    #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')]
    #[Gedmo\TreeParent]
    #[Gedmo\SortableGroup]
    private ?self $parent;

    #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
    #[ORM\OrderBy(['sortIndex' => 'ASC'])]
    private Collection $children;

    #[ORM\ManyToMany(targetEntity: Role::class, mappedBy: 'routes')]
    private Collection $roles;

    public function __construct()
    {
        $this->roles = new ArrayCollection;
        $this->children = new ArrayCollection;
    }

    #[Serializer\SerializedName('hasParams')]
    public function hasParams(): bool
    {
        return str_contains($this->getTreePath(), ':');
    }

    public function getRouteKey(): ?string
    {
        return $this->routeKey;
    }

    public function setRouteKey(string $routeKey): self
    {
        $this->routeKey = $routeKey;

        return $this;
    }

    public function getRoles(): Collection
    {
        return $this->roles;
    }

    public function addRole(Role $role): self
    {
        if (!$this->roles->contains($role)) {
            $this->roles[] = $role;
            $role->addRoute($this);
        }

        return $this;
    }

    public function removeRole(Role $role): self
    {
        if ($this->roles->removeElement($role)) {
            $role->removeRoute($this);
        }

        return $this;
    }

    public function getTitle(): ?string
    {
        return $this->title;
    }

    public function setTitle(string $title): self
    {
        $this->title = $title;

        return $this;
    }

    public function getIcon(): ?string
    {
        return $this->icon;
    }

    public function setIcon(?string $icon): self
    {
        $this->icon = $icon;

        return $this;
    }

    public function getViews(): ?array
    {
        return $this->views;
    }

    public function setViews(array $views): self
    {
        $this->views = $views;

        return $this;
    }
}

Possible Solution

Additional Context

Errors [ { "debugMessage": "Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.", "message": "Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.", "extensions": { "category": "internal" }, "locations": [ { "line": 2, "column": 3 } ], "path": [ "deleteRoute" ], "trace": [ { "file": "/var/www/AE-API/vendor/gedmo/doctrine-extensions/src/Mapping/Event/Adapter/ORM.php", "line": 130, "call": "Doctrine\\ORM\\UnitOfWork::recomputeSingleEntityChangeSet(instance of Doctrine\\ORM\\Mapping\\ClassMetadata, instance of Proxies\\__CG__\\App\\Entity\\Route)" }, { "file": "/var/www/AE-API/vendor/gedmo/doctrine-extensions/src/Mapping/MappedEventSubscriber.php", "line": 258, "call": "Gedmo\\Mapping\\Event\\Adapter\\ORM::recomputeSingleObjectChangeSet(instance of Doctrine\\ORM\\UnitOfWork, instance of Doctrine\\ORM\\Mapping\\ClassMetadata, instance of Proxies\\__CG__\\App\\Entity\\Route)" }, { "file": "/var/www/AE-API/vendor/gedmo/doctrine-extensions/src/Sortable/SortableListener.php", "line": 266, "call": "Gedmo\\Mapping\\MappedEventSubscriber::setFieldValue(instance of Gedmo\\Sortable\\Mapping\\Event\\Adapter\\ORM, instance of Proxies\\__CG__\\App\\Entity\\Route, 'sortIndex', 0, -1)" }, { "file": "/var/www/AE-API/vendor/symfony/doctrine-bridge/ContainerAwareEventManager.php", "line": 68, "call": "Gedmo\\Sortable\\SortableListener::postFlush(instance of Doctrine\\ORM\\Event\\PostFlushEventArgs)" }, { "file": "/var/www/AE-API/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php", "line": 3479, "call": "Symfony\\Bridge\\Doctrine\\ContainerAwareEventManager::dispatchEvent('postFlush', instance of Doctrine\\ORM\\Event\\PostFlushEventArgs)" }, { "file": "/var/www/AE-API/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php", "line": 477, "call": "Doctrine\\ORM\\UnitOfWork::dispatchPostFlushEvent()" }, { "file": "/var/www/AE-API/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php", "line": 392, "call": "Doctrine\\ORM\\UnitOfWork::commit(null)" }, { "file": "/var/www/AE-API/var/cache/dev/ContainerJFvMkSc/proxy-classes.php", "line": 137, "call": "Doctrine\\ORM\\EntityManager::flush(null)" }, { "file": "/var/www/AE-API/vendor/api-platform/core/src/Bridge/Doctrine/Common/DataPersister.php", "line": 76, "call": "ContainerJFvMkSc\\EntityManager_9a5be93::flush()" }, { "file": "/var/www/AE-API/vendor/api-platform/core/src/DataPersister/ChainDataPersister.php", "line": 78, "call": "ApiPlatform\\Core\\Bridge\\Doctrine\\Common\\DataPersister::remove(instance of App\\Entity\\Route, array(4))" }, { "file": "/var/www/AE-API/vendor/api-platform/core/src/Bridge/Symfony/Bundle/DataPersister/TraceableChainDataPersister.php", "line": 68, "call": "ApiPlatform\\Core\\DataPersister\\ChainDataPersister::remove(instance of App\\Entity\\Route, array(4))" }, { "file": "/var/www/AE-API/vendor/api-platform/core/src/GraphQl/Resolver/Stage/WriteStage.php", "line": 54, "call": "ApiPlatform\\Core\\Bridge\\Symfony\\Bundle\\DataPersister\\TraceableChainDataPersister::remove(instance of App\\Entity\\Route, array(4))" }, { "file": "/var/www/AE-API/vendor/api-platform/core/src/GraphQl/Resolver/Factory/ItemMutationResolverFactory.php", "line": 93, "call": "ApiPlatform\\Core\\GraphQl\\Resolver\\Stage\\WriteStage::__invoke(instance of App\\Entity\\Route, 'App\\Entity\\Route', 'delete', array(6))" }, { "file": "/var/www/AE-API/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php", "line": 623, "call": "ApiPlatform\\Core\\GraphQl\\Resolver\\Factory\\ItemMutationResolverFactory::ApiPlatform\\Core\\GraphQl\\Resolver\\Factory\\{closure}(null, array(1), null, instance of GraphQL\\Type\\Definition\\ResolveInfo)" }, { "file": "/var/www/AE-API/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php", "line": 550, "call": "GraphQL\\Executor\\ReferenceExecutor::resolveFieldValueOrError(instance of GraphQL\\Type\\Definition\\FieldDefinition, instance of GraphQL\\Language\\AST\\FieldNode, instance of Closure, null, instance of GraphQL\\Type\\Definition\\ResolveInfo)" }, { "file": "/var/www/AE-API/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php", "line": 474, "call": "GraphQL\\Executor\\ReferenceExecutor::resolveField(GraphQLType: Mutation, null, instance of ArrayObject(1), array(1))" }, { "file": "/var/www/AE-API/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php", "line": 857, "call": "GraphQL\\Executor\\ReferenceExecutor::GraphQL\\Executor\\{closure}(array(0), 'deleteRoute')" }, { "call": "GraphQL\\Executor\\ReferenceExecutor::GraphQL\\Executor\\{closure}(array(0), 'deleteRoute')" }, { "file": "/var/www/AE-API/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php", "line": 859, "function": "array_reduce(array(1), instance of Closure, array(0))" }, { "file": "/var/www/AE-API/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php", "line": 490, "call": "GraphQL\\Executor\\ReferenceExecutor::promiseReduce(array(1), instance of Closure, array(0))" }, { "file": "/var/www/AE-API/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php", "line": 263, "call": "GraphQL\\Executor\\ReferenceExecutor::executeFieldsSerially(GraphQLType: Mutation, null, array(0), instance of ArrayObject(1))" }, { "file": "/var/www/AE-API/vendor/webonyx/graphql-php/src/Executor/ReferenceExecutor.php", "line": 215, "call": "GraphQL\\Executor\\ReferenceExecutor::executeOperation(instance of GraphQL\\Language\\AST\\OperationDefinitionNode, null)" }, { "file": "/var/www/AE-API/vendor/webonyx/graphql-php/src/Executor/Executor.php", "line": 156, "call": "GraphQL\\Executor\\ReferenceExecutor::doExecute()" }, { "file": "/var/www/AE-API/vendor/webonyx/graphql-php/src/GraphQL.php", "line": 162, "call": "GraphQL\\Executor\\Executor::promiseToExecute(instance of GraphQL\\Executor\\Promise\\Adapter\\SyncPromiseAdapter, instance of GraphQL\\Type\\Schema, instance of GraphQL\\Language\\AST\\DocumentNode, null, null, array(1), 'DeleteRoute', null)" }, { "file": "/var/www/AE-API/vendor/webonyx/graphql-php/src/GraphQL.php", "line": 94, "call": "GraphQL\\GraphQL::promiseToExecute(instance of GraphQL\\Executor\\Promise\\Adapter\\SyncPromiseAdapter, instance of GraphQL\\Type\\Schema, 'mutation DeleteRoute($id: ID!) {\n deleteRoute(input: {id: $id}) {\n route {\n id\n __typename\n }\n __typename\n }\n}', null, null, array(1), 'DeleteRoute', null, null)" }, { "file": "/var/www/AE-API/vendor/api-platform/core/src/GraphQl/Executor.php", "line": 34, "call": "GraphQL\\GraphQL::executeQuery(instance of GraphQL\\Type\\Schema, 'mutation DeleteRoute($id: ID!) {\n deleteRoute(input: {id: $id}) {\n route {\n id\n __typename\n }\n __typename\n }\n}', null, null, array(1), 'DeleteRoute', null, null)" }, { "file": "/var/www/AE-API/vendor/api-platform/core/src/GraphQl/Action/EntrypointAction.php", "line": 86, "call": "ApiPlatform\\Core\\GraphQl\\Executor::executeQuery(instance of GraphQL\\Type\\Schema, 'mutation DeleteRoute($id: ID!) {\n deleteRoute(input: {id: $id}) {\n route {\n id\n __typename\n }\n __typename\n }\n}', null, null, array(1), 'DeleteRoute')" }, { "file": "/var/www/AE-API/vendor/symfony/http-kernel/HttpKernel.php", "line": 152, "call": "ApiPlatform\\Core\\GraphQl\\Action\\EntrypointAction::__invoke(instance of Symfony\\Component\\HttpFoundation\\Request)" }, { "file": "/var/www/AE-API/vendor/symfony/http-kernel/HttpKernel.php", "line": 74, "call": "Symfony\\Component\\HttpKernel\\HttpKernel::handleRaw(instance of Symfony\\Component\\HttpFoundation\\Request, 1)" }, { "file": "/var/www/AE-API/vendor/symfony/http-kernel/Kernel.php", "line": 202, "call": "Symfony\\Component\\HttpKernel\\HttpKernel::handle(instance of Symfony\\Component\\HttpFoundation\\Request, 1, true)" }, { "file": "/var/www/AE-API/public/index.php", "line": 25, "call": "Symfony\\Component\\HttpKernel\\Kernel::handle(instance of Symfony\\Component\\HttpFoundation\\Request)" } ] } ]

aelfannir avatar Mar 28 '22 10:03 aelfannir