sonata-project / SonataAdminBundle

The missing Symfony Admin Generator
https://docs.sonata-project.org/projects/SonataAdminBundle
MIT License
2.11k stars 1.26k forks source link

Can not get the normalized identifier for App\Entity\BettingEventCategory since it is in state 2. #6535

Closed yleyvag closed 3 years ago

yleyvag commented 3 years ago

Environment

Sonata packages

$ composer show --latest 'sonata-project/*'
sonata-project/admin-bundle              dev-master 890ecee dev-master 8773923 The missing Symfony Admin Generator
sonata-project/block-bundle              4.4.0              4.4.0              Symfony SonataBlockBundle
sonata-project/cache                     2.0.1              2.0.1              Cache library
sonata-project/doctrine-extensions       1.10.1             1.10.1             Doctrine2 behavioral extensions
sonata-project/doctrine-orm-admin-bundle dev-master a4391d3 dev-master a4391d3 Integrate Doctrine ORM into the Sona...
sonata-project/exporter                  2.4.1              2.4.1              Lightweight Exporter library
sonata-project/form-extensions           1.6.0              1.6.0              Symfony form extensions
sonata-project/twig-extensions           1.4.1              1.4.1              Sonata twig extensions

Symfony packages

$ composer show --latest 'symfony/*'
symfony/apache-pack                      v1.0.1             v1.0.1             A pack for Apache support in Symfony
symfony/asset                            v5.1.7             v5.1.7             Symfony Asset Component
symfony/browser-kit                      v5.1.7             v5.1.7             Symfony BrowserKit Component
symfony/cache                            v5.1.7             v5.1.7             Symfony Cache component with PSR-6, ...
symfony/cache-contracts                  v2.2.0             v2.2.0             Generic abstractions related to caching
symfony/config                           v5.1.7             v5.1.7             Symfony Config Component
symfony/console                          v5.1.7             v5.1.7             Symfony Console Component
symfony/css-selector                     v5.1.7             v5.1.7             Symfony CssSelector Component
symfony/debug-bundle                     v5.1.7             v5.1.7             Symfony DebugBundle
symfony/dependency-injection             v5.1.7             v5.1.7             Symfony DependencyInjection Component
symfony/deprecation-contracts            v2.2.0             v2.2.0             A generic function and convention to...
symfony/doctrine-bridge                  v5.1.7             v5.1.7             Symfony Doctrine Bridge
symfony/dom-crawler                      v5.1.7             v5.1.7             Symfony DomCrawler Component
symfony/dotenv                           v5.1.7             v5.1.7             Registers environment variables from...
symfony/error-handler                    v5.1.7             v5.1.7             Symfony ErrorHandler Component
symfony/event-dispatcher                 v5.1.7             v5.1.7             Symfony EventDispatcher Component
symfony/event-dispatcher-contracts       v2.2.0             v2.2.0             Generic abstractions related to disp...
symfony/expression-language              v5.1.7             v5.1.7             Symfony ExpressionLanguage Component
symfony/filesystem                       v5.1.7             v5.1.7             Symfony Filesystem Component
symfony/finder                           v5.1.7             v5.1.7             Symfony Finder Component
symfony/flex                             v1.9.10            v1.9.10            Composer plugin for Symfony
symfony/form                             v5.1.7             v5.1.7             Symfony Form Component
symfony/framework-bundle                 v5.1.7             v5.1.7             Symfony FrameworkBundle
symfony/http-client                      v5.1.7             v5.1.7             Symfony HttpClient component
symfony/http-client-contracts            v2.3.1             v2.3.1             Generic abstractions related to HTTP...
symfony/http-foundation                  v5.1.7             v5.1.7             Symfony HttpFoundation Component
symfony/http-kernel                      v5.1.7             v5.1.7             Symfony HttpKernel Component
symfony/intl                             v5.1.7             v5.1.7             A PHP replacement layer for the C in...
symfony/mailer                           v5.1.7             v5.1.7             Symfony Mailer Component
symfony/maker-bundle                     v1.22.0            v1.22.0            Symfony Maker helps you create empty...
symfony/mime                             v5.1.7             v5.1.7             A library to manipulate MIME messages
symfony/monolog-bridge                   v5.1.7             v5.1.7             Symfony Monolog Bridge
symfony/monolog-bundle                   v3.6.0             v3.6.0             Symfony MonologBundle
symfony/notifier                         v5.1.7             v5.1.7             A library to notify messages
symfony/options-resolver                 v5.1.7             v5.1.7             Symfony OptionsResolver Component
symfony/phpunit-bridge                   v5.1.7             v5.1.7             Symfony PHPUnit Bridge
symfony/polyfill-intl-grapheme           v1.18.1            v1.19.0            Symfony polyfill for intl's grapheme...
symfony/polyfill-intl-icu                v1.18.1            v1.19.0            Symfony polyfill for intl's ICU-rela...
symfony/polyfill-intl-idn                v1.18.1            v1.19.0            Symfony polyfill for intl's idn_to_a...
symfony/polyfill-intl-normalizer         v1.18.1            v1.19.0            Symfony polyfill for intl's Normaliz...
symfony/polyfill-mbstring                v1.18.1            v1.19.0            Symfony polyfill for the Mbstring ex...
symfony/polyfill-php73                   v1.18.1            v1.19.0            Symfony polyfill backporting some PH...
symfony/polyfill-php80                   v1.18.1            v1.19.0            Symfony polyfill backporting some PH...
symfony/process                          v5.1.7             v5.1.7             Symfony Process Component
symfony/property-access                  v5.1.7             v5.1.7             Symfony PropertyAccess Component
symfony/property-info                    v5.1.7             v5.1.7             Symfony Property Info Component
symfony/routing                          v5.1.7             v5.1.7             Symfony Routing Component
symfony/security-acl                     v3.1.0             v3.1.0             Symfony Security Component - ACL (Ac...
symfony/security-bundle                  v5.1.7             v5.1.7             Symfony SecurityBundle
symfony/security-core                    v5.1.7             v5.1.7             Symfony Security Component - Core Li...
symfony/security-csrf                    v5.1.7             v5.1.7             Symfony Security Component - CSRF Li...
symfony/security-guard                   v5.1.7             v5.1.7             Symfony Security Component - Guard
symfony/security-http                    v5.1.7             v5.1.7             Symfony Security Component - HTTP In...
symfony/serializer                       v5.1.7             v5.1.7             Symfony Serializer Component
symfony/service-contracts                v2.2.0             v2.2.0             Generic abstractions related to writ...
symfony/stopwatch                        v5.1.7             v5.1.7             Symfony Stopwatch Component
symfony/string                           v5.1.7             v5.1.7             Symfony String component
symfony/translation                      v5.1.7             v5.1.7             Symfony Translation Component
symfony/translation-contracts            v2.3.0             v2.3.0             Generic abstractions related to tran...
symfony/twig-bridge                      v5.1.7             v5.1.7             Symfony Twig Bridge
symfony/twig-bundle                      v5.1.7             v5.1.7             Symfony TwigBundle
symfony/validator                        v5.1.7             v5.1.7             Symfony Validator Component
symfony/var-dumper                       v5.1.7             v5.1.7             Symfony mechanism for exploring and ...
symfony/var-exporter                     v5.1.7             v5.1.7             A blend of var_export() + serialize(...
symfony/web-link                         v5.1.7             v5.1.7             Symfony WebLink Component
symfony/web-profiler-bundle              v5.1.7             v5.1.7             Symfony WebProfilerBundle
symfony/webpack-encore-bundle            v1.7.3             v1.7.3             Integration with your Symfony app & ...
symfony/yaml                             v5.1.7             v5.1.7             Symfony Yaml Component

PHP version

$ php -v
PHP 7.3.12 (cli) (built: Nov 19 2019 13:58:02) ( ZTS MSVC15 (Visual C++ 2017) x64 )

Subject

Can not get the normalized identifier for App\Entity\BettingEventCategory since it is in state 2.

Steps to reproduce

BettingEventCategory class

<?php

namespace App\Entity;

use App\Repository\BettingEventCategoryRepository;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass=BettingEventCategoryRepository::class)
 */
class BettingEventCategory
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $categoryName;

    /**
     * @ORM\Column(type="boolean")
     */
    private $enabled;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getCategoryName(): ?string
    {
        return $this->categoryName;
    }

    public function setCategoryName(string $categoryName): self
    {
        $this->categoryName = $categoryName;

        return $this;
    }

    public function getEnabled(): ?bool
    {
        return $this->enabled;
    }

    public function setEnabled(bool $enabled): self
    {
        $this->enabled = $enabled;

        return $this;
    }
}

BettingEventCategoryAdmin class

<?php

namespace App\Admin;

use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\Form\Type\BooleanType;
use Symfony\Component\Form\Extension\Core\Type\TextType;

class BettingEventCategoryAdmin extends AbstractAdmin
{
    protected function configureFormFields(FormMapper $formMapper): void
    {
        $formMapper
            ->add('categoryName', TextType::class)
            ->add('enabled', BooleanType::class, array(
                'label' => 'Enabled',
                'transform' => true,
                'required' => true,
            ));
    }

    protected function configureDataGridFilters(DatagridMapper $datagridMapper): void
    {
        $datagridMapper
            ->add('id')
            ->add('categoryName')
            ->add('enabled');
    }

    protected function configureListFields(ListMapper $listMapper): void
    {
        $listMapper
            ->addIdentifier('id')
            ->addIdentifier('categoryName')
            ->addIdentifier('enabled');
    }
}

My service section

admin.betting_event_category:
    class: App\Admin\BettingEventCategoryAdmin
    arguments: [~, App\Entity\BettingEventCategory, ~]
    tags:
      - { name: sonata.admin, manager_type: orm, label: "Betting Event Category" }

Trying to create a new entity imagen

Expected results

The new entity should be created

Actual results

Error received imagen

InvalidArgumentException:
Can not get the normalized identifier for App\Entity\BettingEventCategory since it is in state 2.

  at C:\xampp\htdocs\vendor\sonata-project\doctrine-orm-admin-bundle\src\Model\ModelManager.php:396
  at Sonata\DoctrineORMAdminBundle\Model\ModelManager->getNormalizedIdentifier(object(BettingEventCategory))
     (C:\xampp\htdocs\vendor\sonata-project\admin-bundle\src\Admin\AbstractAdmin.php:2001)
  at Sonata\AdminBundle\Admin\AbstractAdmin->getNormalizedIdentifier(object(BettingEventCategory))
     (C:\xampp\htdocs\vendor\sonata-project\admin-bundle\src\Admin\AbstractAdmin.php:2006)
  at Sonata\AdminBundle\Admin\AbstractAdmin->id(object(BettingEventCategory))
     (C:\xampp\htdocs\vendor\sonata-project\admin-bundle\src\Admin\AbstractAdmin.php:1984)
  at Sonata\AdminBundle\Admin\AbstractAdmin->isGranted('CREATE', object(BettingEventCategory))
     (C:\xampp\htdocs\vendor\sonata-project\admin-bundle\src\Admin\AbstractAdmin.php:2195)
  at Sonata\AdminBundle\Admin\AbstractAdmin->checkAccess('create', object(BettingEventCategory))
     (C:\xampp\htdocs\vendor\sonata-project\admin-bundle\src\Controller\CRUDController.php:549)
  at Sonata\AdminBundle\Controller\CRUDController->createAction(object(Request))
     (C:\xampp\htdocs\vendor\symfony\http-kernel\HttpKernel.php:157)
  at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1)
     (C:\xampp\htdocs\vendor\symfony\http-kernel\HttpKernel.php:79)
  at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true)
     (C:\xampp\htdocs\vendor\symfony\http-kernel\Kernel.php:196)
  at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
     (C:\xampp\htdocs\index.php:28) 
phansys commented 3 years ago

I think we have 2 choices here:

  1. Add a ModelManagerInterface::hasNormalizedIdentifier();
  2. Let ModelManagerInterface::getNormalizedIdentifier() to return null for new or removed models.

@sonata-project/contributors, WDYT?

VincentLanglet commented 3 years ago

I think we have 2 choices here:

  1. Add a ModelManagerInterface::hasNormalizedIdentifier();
  2. Let ModelManagerInterface::getNormalizedIdentifier() to return null for new or removed models.

@sonata-project/contributors, WDYT?

getNormalizedIdentifier is used a lot:

In AbstractAdmin

    public function getNormalizedIdentifier(object $model): string
    {
        return $this->getModelManager()->getNormalizedIdentifier($model);
    }

    public function id(object $model): string
    {
        return $this->getNormalizedIdentifier($model);
    }

And then the id method is also use multiple times.

IMHO the simpler solution would be to allow null as a return value but I don't know the impact.

phansys commented 3 years ago

IMHO the simpler solution would be to allow null as a return value but I don't know the impact.

I think there is no critical impact. I've introduced this deprecation and exception in sonata-project/SonataDoctrineORMAdminBundle#1049, in order to narrow the API and avoid invalid values, but if this case can be considered legit, we could allow null as return type.

SilberMa commented 3 years ago

Does this mean that I am not able to create a new object until the milestone is reached?

I tried to allow null as return in the AbstractAdmin and the Interface, but this doesn't change anything. :/

Furthermore adding an id (in my case Uuid) in preValidate has no impact.

VincentLanglet commented 3 years ago

Does this mean that I am not able to create a new object until the milestone is reached?

Using the master branch is at your own risk, we provide stable 3.x branch and a unstable master.

We're putting a lot of effort working on Sonata 4. There is currently lot of BC-break on master, and some bug can appear. We appreciate a lot that you report the bugs you encounter with the master branch but can't guarantee to always solve quickly the bugs.

If you want to provide PR, I'll be happy to review it and merge it.

I tried to allow null as return in the AbstractAdmin and the Interface, but this doesn't change anything. :/

Furthermore adding an id (in my case Uuid) in preValidate has no impact.

Your exception is thrown by https://github.com/sonata-project/SonataDoctrineORMAdminBundle/blob/master/src/Model/ModelManager.php#L392

You can see that previously it was returning null https://github.com/sonata-project/SonataDoctrineORMAdminBundle/blob/3.x/src/Model/ModelManager.php#L452

In your case, the state 2 is the state NEW.

The call is made by AbstractAdmin::id. The question is why ?

The answer is...

public function isGranted($name, ?object $object = null): bool
    {
        $objectRef = $object ? sprintf('/%s#%s', spl_object_hash($object), $this->id($object)) : '';
        $key = md5(json_encode($name).$objectRef);

        if (!\array_key_exists($key, $this->cacheIsGranted)) {
            $this->cacheIsGranted[$key] = $this->securityHandler->isGranted($this, $name, $object ?: $this);
        }

        return $this->cacheIsGranted[$key];
    }

We could add try/catch, default value, etc... But when I look at getNormalizedIdentifiers I think it should never throw an exception. If no identifier can be determined, let's just return null or '' instead.

To me, null seems better. Does @sonata-project/contributors agree ?

So it requires a PR to change

getUrlSafeIdentifier
getNormalizedIdentifier
id

return type from string to ?string in AbstractAdmin and AdminInterface/UrlGeneratorInterface.

Then (in the same PR)

getUrlSafeIdentifier
getNormalizedIdentifier

return type from string to ?string in ModelManagerInterface

Then a PR in SonataDoctrineORM 3.x to remove the deprecation https://github.com/sonata-project/SonataDoctrineORMAdminBundle/blob/3.x/src/Model/ModelManager.php#L456-L469

And a PR in SonataDoctrineORM master to remove the exception and return null instead. You could add also the check

if (0 === \count($values)) {
            return null;
        }

again.

SilberMa commented 3 years ago

Using the master branch is at your own risk, we provide stable 3.x branch and a unstable master.

We're putting a lot of effort working on Sonata 4. There is currently lot of BC-break on master, and some bug can appear. We appreciate a lot that you report the bugs you encounter with the master branch but can't guarantee to always solve quickly the bugs.

You're totally right and I wanna say thank you for your effort explaining everything.

A PR is a good idea and I'll try to provide one.

Kind regards.

VincentLanglet commented 3 years ago

Should be fixed now.