Describe the bug
Reflection exception: Property id does not exist is thrown when attempting to show a list view of an entity mapped to a user super class where the id visibility is private.
(OPTIONAL) Additional context
Related to #3802
in ...\vendor\easycorp\easyadmin-bundle\src\Dto\EntityDto.php (line 83)
if (null !== $this->primaryKeyValue) {
return $this->primaryKeyValue;
}
$r = ClassUtils::newReflectionObject($this->instance);
$primaryKeyProperty = $r->getProperty($this->primaryKeyName); // line 83
$primaryKeyProperty->setAccessible(true);
$primaryKeyValue = $primaryKeyProperty->getValue($this->instance);
return $this->primaryKeyValue = $primaryKeyValue;
}
Note that this was done on purpose in #3888. Previously we hid the exception, so debugging was hard. Now we throw it. This can happen because of one of this reasons:
This case might be the usage in the app of an unsupported feature, because EasyAdmin doesn't support "Doctrine super classes".
"EasyAdmin doesn't support "Doctrine super classes"
This is true for version 3. I had no trouble with v2. Looks like I'll have to stick with 2.
Maybe I'm understanding this wrong 馃 because EA2 should not support "super classes" either.
All I can say is that in EA2 the list view of the subclass Representative (mapped super class User) shows all the entities, whereas in EA3 only one is shown. And that is even more curious because I would have thought that none would be displayed. I just don't understand all the "heavy voodoo" in determining what to show.
If Class Table Inheritance is not supported what would be the recommended approach?
I'm facing the same problem, I'm trying to make Class Table Inheritance work with EA 3.1.8 and getting the same exception.
Looks like replacing the primary key lookup logic with code below resolves the issue. Not sure if this is acceptable solution, but it resolves that problem.
src/Dto/EntityDto.php getPrimaryKeyValue()
$r = ClassUtils::newReflectionObject($this->instance);
$primaryKeyProperty = null;
while ($primaryKeyProperty === null && $r !== null) {
try {
$primaryKeyProperty = $r->getProperty($this->primaryKeyName);
$primaryKeyProperty->setAccessible(true);
$primaryKeyValue = $primaryKeyProperty->getValue($this->instance);
} catch (\Exception $e) {}
$r = $r->getParentClass();
}
if ($primaryKeyProperty == null)
throw new \Exception("Cannot find primary key property");
return $this->primaryKeyValue = $primaryKeyValue;
EDIT: or simply making the primary key field public also fixes the issue, though it would be nice if this worked out of the box with solution similar to above.
@enbyted how are you modifying it? fork and change the easyadmin package in composer.json?
I wanted to do it a less painful way. extend EntityFactory with a class of mine, and place that in container. But EntityFactory is declared as final. So no subclassing i guess :(
(why is it even final?)
No, I modified it locally to test. As a temporary fix I'm making the affected fields public - that doesn't require forking thus preserving automatic upgrades.
@javiereguiluz Could you please look over the proposed fix above? If it's acceptable I can make a PR out of it or you can simply integrate it - it's a small change.
Hi,
When I change getPrimaryKeyValue() in vendor...
It work to list elements, but when I click to "Create", a new error is rising.
Warning: get_class() expects parameter 1 to be object, null given

Could you show the full stack trace? It doesn't reproduce in my case. (that's probably why it's not a good fix...)
ErrorException:
Warning: get_class() expects parameter 1 to be object, null given
at C:\Users\myProject\vendor\doctrine\common\lib\Doctrine\Common\Util\ClassUtils.php:40
at Doctrine\Common\Util\ClassUtils::getClass(null)
(C:\Users\myProject\vendor\doctrine\common\lib\Doctrine\Common\Util\ClassUtils.php:76)
at Doctrine\Common\Util\ClassUtils::newReflectionObject(null)
(C:\Users\myProject\vendor\easycorp\easyadmin-bundle\src\Dto\EntityDto.php:74)
at EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto->getPrimaryKeyValue()
(C:\Users\myProject\vendor\easycorp\easyadmin-bundle\src\Field\Configurator\AssociationConfigurator.php:165)
at EasyCorp\Bundle\EasyAdminBundle\Field\Configurator\AssociationConfigurator->generateLinkToAssociatedEntity('App\\Controller\\Admin\\CategoryProductCrudController', object(EntityDto))
(C:\Users\myProject\vendor\easycorp\easyadmin-bundle\src\Field\Configurator\AssociationConfigurator.php:114)
at EasyCorp\Bundle\EasyAdminBundle\Field\Configurator\AssociationConfigurator->configureToOneAssociation(object(FieldDto))
(C:\Users\myProject\vendor\easycorp\easyadmin-bundle\src\Field\Configurator\AssociationConfigurator.php:57)
at EasyCorp\Bundle\EasyAdminBundle\Field\Configurator\AssociationConfigurator->configure(object(FieldDto), object(EntityDto), object(AdminContext))
(C:\Users\myProject\vendor\easycorp\easyadmin-bundle\src\Factory\FieldFactory.php:87)
at EasyCorp\Bundle\EasyAdminBundle\Factory\FieldFactory->processFields(object(EntityDto), object(FieldCollection))
(C:\Users\myProject\vendor\easycorp\easyadmin-bundle\src\Factory\EntityFactory.php:43)
at EasyCorp\Bundle\EasyAdminBundle\Factory\EntityFactory->processFields(object(EntityDto), object(FieldCollection))
(C:\Users\myProject\vendor\easycorp\easyadmin-bundle\src\Controller\AbstractCrudController.php:283)
at EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController->new(object(AdminContext))
(C:\Users\myProject\vendor\symfony\http-kernel\HttpKernel.php:157)
at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1)
(C:\Users\myProject\vendor\symfony\http-kernel\HttpKernel.php:79)
at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true)
(C:\Users\myProject\vendor\symfony\http-kernel\Kernel.php:196)
at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
(C:\Users\myProject\public\index.php:25)
Oh, did you replace the whole function?
The snippet I provided was meant to replace only this part:
$r = ClassUtils::newReflectionObject($this->instance);
$primaryKeyProperty = $r->getProperty($this->primaryKeyName);
$primaryKeyProperty->setAccessible(true);
$primaryKeyValue = $primaryKeyProperty->getValue($this->instance);
return $this->primaryKeyValue = $primaryKeyValue;
Ho, Sorry ! It's working now !
I posted a comment 2 days ago, seems like it didn't submit.
I came up with another better solution, that is to use symfony propertyAccessor.
public function getPrimaryKeyValue()
{
if (null === $this->instance) {
return null;
}
if (null !== $this->primaryKeyValue) {
return $this->primaryKeyValue;
}
$propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
->enableExceptionOnInvalidIndex()
->getPropertyAccessor();
$primaryKeyValue = $propertyAccessor->getValue($this->instance, $this->primaryKeyName);
return $this->primaryKeyValue = $primaryKeyValue;
}
not only this solves the inheritance issue, it also solves the need for reflection hack. PropertyAccess uses getter method for getting value, which is the indented way to do it.
I'll make a PR.
I can confirm the newly released v3.1.10 which includes the fix solves the issue in my project. Just doing an 'composer update` worked.
Most helpful comment
I posted a comment 2 days ago, seems like it didn't submit.
I came up with another better solution, that is to use symfony propertyAccessor.
not only this solves the inheritance issue, it also solves the need for reflection hack. PropertyAccess uses getter method for getting value, which is the indented way to do it.
I'll make a PR.