Doctrine fails with column can't be null error; Can't see how the value _can_ be null

Viewed 1052

I have a Symfony 4 application that uses Doctrine for all database access. The table in question has a number of columns that are defined with "NOT NULL".

All of the getter methods in the entity class are written such that they return an empty string if the current value is null

public function getApartment(): string
{
  return $this->apartment ?? '';
}

It is my understanding that Doctrine calls the entity's getter methods to retrieve the current values when persisting the object into the database.

I don't see how getters written like the one above can possibly result in null values, yet this error occasionally happens. Note that it doesn't happen all the time, only sometimes and I haven't spotted any sort of pattern yet.

In addition to the getters, several of the entity's field definitions are tagged with @Assert\NotBlank annotations so the Form component shouldn't return true from the isValid() method.

Here is a (slightly edited) example error from var/log/prod.log:

[2020-03-02 22:23:22] request.CRITICAL: Uncaught PHP Exception 
  Doctrine\DBAL\Exception\NotNullConstraintViolationException: 
  "An exception occurred while executing 
    'INSERT INTO FreeClassQueries 
    (apartment, city, comment, dateCreated, email, migrated, name, phone, state, street, zip, spamInfo) 
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' 
    with params [null, null, null, "2020-03-02 22:23:20", null, null, null, null, null, null, null, null]:  
  SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'apartment' cannot be null" 
    at /var/www/prod/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php line 103 
    {"exception":"[object] (Doctrine\\DBAL\\Exception\\NotNullConstraintViolationException(code: 0): 
    An exception occurred while executing 
      'INSERT INTO FreeClassQueries 
      (apartment, city, comment, dateCreated, email, migrated, name, phone, state, street, zip, spamInfo) 
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' 
      with params [null, null, null, \"2020-03-02 22:23:20\", null, null, null, null, null, null, null, null]:
  SQLSTATE[23000]: Integrity constraint violation: 1048 
  Column 'apartment' cannot be null 
    at /var/www/prod/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php:103, 
  Doctrine\\DBAL\\Driver\\PDOException(code: 23000): SQLSTATE[23000]: 
    Integrity constraint violation: 1048 Column 'apartment' cannot be null 
    at /var/www/prod/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:123, 
  PDOException(code: 23000): SQLSTATE[23000]: Integrity constraint violation: 1048
    Column 'apartment' cannot be null at /var/www/prod/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:121)"} []

As you can see, the only value that is not null is dateCreated. it is set via a method that is tagged with an @ORM\PrePersist annotation.

The action method only attempts to persist the object if the form has been submitted and is valid, meaning that all fields tagged with @Verify\NotBlank can not be blank.

$form->handleRequest( $request );
if( $form->isSubmitted() && $form->isValid())
{
  $em = $this->getEntityManager();
  $repository = $em->getRepository( FreeClassQuery::class );
  $em->persist( $classQuery );
  $em->flush();
  ...
}
2 Answers

You are assuming Doctrine will use the entity's getters, but that's not the case. From Doctrine's docs:

Doctrine does NEVER touch the public API of methods in your entity classes (like getters and setters) nor the constructor method. Instead, it uses reflection to get/set data from/to your entity objects. When Doctrine fetches data from DB and saves it back, any code put in your get/set methods won't be implicitly taken into account.

Instead of putting this logic in the getter, just create a default valid value for the object properties. This will also help you having a cleaner getter without having to resort to tricks:

class Foo
{
    private string $apartment = '';

    public function getApartment(): string
    {
        return $this->apartment;
    }
}

You have a basic Php problem: your variable is seen as 'falsy'. A variable is a variable is considered empty if it's undefined, null, false, 0 or an empty string.

Possible workaround: set a default value.

Anyway, try what's best for your app and take care.

Related