Bug Report
| Q |
A |
| BC Break |
no |
| Version |
3.2.0 |
Summary
Working with enums in matching criteria for collections does not work correctly for bot lazy and eager loading.
Current behavior
When filtering collections with $collection->matching($criteria), if the collection is not initialized, the values from the criteria object won't be converted to database types. So \BackedEnums are not replaced by their scalar value.
How to reproduce
- Setup the entities and the enum below:
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
class Page
{
#[ORM\OneToMany(targetEntity: Comment::class, mappedBy: 'page')]
private Collection $comments;
public function __construct()
{
$this->comments = new ArrayCollection();
}
public function getComments(): Collection
{
return $this->comments;
}
public function getBanishedComments(): Collection
{
$criteria = Criteria::create()
->andWhere(Criteria::expr()->eq('commentStatus', CommentStatus::BANISHED));
return $this->comments->matching($criteria);
}
}
// ...
class Comment
{
#[ORM\ManyToOne(targetEntity: Page::class, inversedBy: 'comments')]
private Page $page;
#[ORM\Column(nullable: false, enumType: CommentStatus::class)]
private CommentStatus $commentStatus = CommentStatus::OK;
public function getCommentStatus(): CommentStatus
{
return $this->commentStatus;
}
}
// ...
enum CommentStatus: string
{
case OK = 'ok';
case BANISHED = 'banished';
}
- Keep the default doctrine configuration. Below the config from my symfony application:
doctrine:
dbal:
default_connection: my_db
connections:
my_db:
dbname: '%env(DB_NAME)%'
host: '%env(DB_HOST)%'
port: '%env(DB_PORT)%'
user: '%env(DB_USER)%'
password: '%env(DB_PASSWORD)%'
driver: pdo_mysql
server_version: '5.7.42'
schema_filter: ~^(?!(logs)$)~ # Exclude logs table from doctrine management
default_table_options:
collation: utf8mb4_unicode_ci
types:
tinyint: App\Doctrine\DBAL\Types\TinyintType
mediumint: App\Doctrine\DBAL\Types\MediumintType
orm:
auto_generate_proxy_classes: true
enable_lazy_ghost_objects: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
report_fields_where_declared: true
auto_mapping: true
controller_resolver:
auto_mapping: true
mappings:
App:
is_bundle: false
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
- Add some records into your database
- Test the two cases:
/** @var EntityManagerInterface $em **/
/** @var Page $page **/
$page = $em->getRepository(PageRepository::class)->find(1);
// Case 1 : without initiliazed collection
$banishedComments = $page->getBanishedComments(); // ☠️ This will throw an exception and display the message "Object of class App\CommentStatus could not be converted to string"
// Case 1 : with initiliazed collection
$comments = $page->getComments();
// This loop will initialize the collection
foreach ($comments as $comment) {
echo $comment->getCommentStatus()->value . "\n";
}
$banishedComments = $page->getBanishedComments(); // 🆗 This works
There is the stack trace:
Object of class App\CommentStatus could not be converted to string
at vendor/doctrine/dbal/src/Driver/PDO/Statement.php:48
at PDOStatement->bindValue(2, object(CommentStatus), 2)
(vendor/doctrine/dbal/src/Driver/PDO/Statement.php:48)
at Doctrine\DBAL\Driver\PDO\Statement->bindValue(2, object(CommentStatus), 2)
(vendor/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php:35)
at Doctrine\DBAL\Driver\Middleware\AbstractStatementMiddleware->bindValue(2, object(CommentStatus), 2)
(vendor/doctrine/dbal/src/Logging/Statement.php:84)
at Doctrine\DBAL\Logging\Statement->bindValue(2, object(CommentStatus), 2)
(vendor/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php:35)
at Doctrine\DBAL\Driver\Middleware\AbstractStatementMiddleware->bindValue(2, object(CommentStatus), 2)
(vendor/symfony/doctrine-bridge/Middleware/Debug/DBAL3/Statement.php:54)
at Symfony\Bridge\Doctrine\Middleware\Debug\DBAL3\Statement->bindValue(2, object(CommentStatus), 2)
(vendor/doctrine/dbal/src/Connection.php:1809)
at Doctrine\DBAL\Connection->bindParameters(object(Statement), array(1, object(CommentStatus)), array('integer', 'string'))
(vendor/doctrine/dbal/src/Connection.php:1097)
at Doctrine\DBAL\Connection->executeQuery('...')
(vendor/doctrine/orm/src/Persisters/Collection/ManyToManyPersister.php:273)
at Doctrine\ORM\Persisters\Collection\ManyToManyPersister->loadCriteria(object(PersistentCollection), object(Criteria))
(vendor/doctrine/orm/src/PersistentCollection.php:575)
at Doctrine\ORM\PersistentCollection->matching(object(Criteria))
(src/Entity/Page.php:118)
at App\Entity\Page->getBanishedComments()
(vendor/symfony/property-access/PropertyAccessor.php:388)
at Symfony\Component\PropertyAccess\PropertyAccessor->readProperty(array(object(Page)), 'banishedComments', false)
(vendor/symfony/property-access/PropertyAccessor.php:99)
at Symfony\Component\PropertyAccess\PropertyAccessor->getValue(object(Page), 'banishedComments')
(vendor/easycorp/easyadmin-bundle/src/Field/Configurator/CommonPreConfigurator.php:50)
at EasyCorp\Bundle\EasyAdminBundle\Field\Configurator\CommonPreConfigurator->configure(object(FieldDto), object(EntityDto), object(AdminContext))
(vendor/easycorp/easyadmin-bundle/src/Factory/FieldFactory.php:107)
at EasyCorp\Bundle\EasyAdminBundle\Factory\FieldFactory->processFields(object(EntityDto), object(FieldCollection))
(vendor/easycorp/easyadmin-bundle/src/Factory/EntityFactory.php:43)
Expected behavior
I expect the same behavior between the two cases.
Bug Report
Summary
Working with enums in matching criteria for collections does not work correctly for bot lazy and eager loading.
Current behavior
When filtering collections with
$collection->matching($criteria), if the collection is not initialized, the values from the criteria object won't be converted to database types. So\BackedEnums are not replaced by their scalar value.How to reproduce
There is the stack trace:
Expected behavior
I expect the same behavior between the two cases.