Reputation: 5066
I've noticed a strange behavior on a Symfony app.
We have an entity containing a nullable date field:
/**
* @var \DateTime
*
* @ORM\Column(name="foo", type="date", nullable=true)
*/
private $foo;
The corresponding query to get only entries where it's set looks like this:
public function findFoo()
{
$qb = $this->_em->createQueryBuilder();
$qb
->select('e')
->from($this->_entityName, 'e')
->where('e.foo IS NOT NULL');
return $qb->getQuery()->getResult();
}
This query however returns all entries, even those who are set to Null
in the db.
When dumping the value of the foo
field, we get this:
object(DateTime)[549]
public 'date' => string '-0001-11-30 00:00:00.000000' (length=27)
public 'timezone_type' => int 3
public 'timezone' => string 'UTC' (length=3)
We tried to modify the query to use this where clause instead:
->where($qb->expr()->isNotNull('e.foo'))
But the result was still the same. We've now settled for this "hack":
->where('e.foo > :dateSet')
->setParameter('dateSet', new \DateTime('1970-01-01'))
Which does the trick but kind of feels wrong.
Question: What is the correct way to check for IS NOT NULL
within a Doctrine Query, on a date field that's nullable? Is this a bug in doctrine or expected behavior? If it's expected, what's the reason for it?
Edit:
Followup question, how can we test for IS NULL
? Neither ->where('e.foo IS NULL');
nor ->where('e.foo < :dateSet');
or ->where('e.foo <= :dateSet');
seams to work in this case. Note: I've also tried to use the current date for dateSet
. Sure I could now use a NOT IN( ... )
and in there a query for the not null ones, but that would be even more hacky. For now I'll probably have to decide on the PHP side.
Edit: As requested, the relevant part of the class including the constructor
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* FooBar
*
* @ORM\Table(name="foo_bar")
* @ORM\Entity(repositoryClass="AppBundle\Repository\FooBarRepository")
*/
class FooBar
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
// [...]
/**
* @var \DateTime
*
* @ORM\Column(name="foo", type="date", nullable=true)
*/
private $foo;
public function __construct()
{
$this->foo = null;
// [...]
}
// [...]
public function isFoo(): bool
{
return !is_null($this->foo);
}
// [...]
/**
* Set foo
*
* @param \DateTime $foo
*
* @return FooBar
*/
public function setFoo($foo)
{
$this->foo = $foo;
return $this;
}
/**
* Get foo
*
* @return \DateTime|null
*/
public function getFoo()
{
return $this->foo;
}
}
Upvotes: 1
Views: 1839
Reputation: 5066
So the issue in the end was, that the field in question, used to be a not nullable boolean, which I changed into a nullable date. I thought all the values should be null, and indeed when checking with a SQL tool, it showed all values as null.
However internally it seams that they weren't null after all. I run a update foo_bar set foo = NULL;
which fixed all my issues.
I'm not sure if it's a bug in doctrine per se, I rather think it's a issue on the MySQL side.
Upvotes: 1