phpstan-doctrine icon indicating copy to clipboard operation
phpstan-doctrine copied to clipboard

Type matching mismatch in child class with SINGLE TABLE INHERITANCE

Open DesLynx opened this issue 2 years ago • 3 comments

Hi!

In the case of SINGLE TABLE INHERITANCE the doctrine documentation states that:

For Single-Table-Inheritance to work in scenarios where you are using either a legacy database schema or a self-written database schema you have to make sure that all columns that are not in the root entity but in any of the different sub-entities has to allow null values. Columns that have NOT NULL constraints have to be on the root entity of the single-table inheritance hierarchy.

So I think that the analysis shouldn't report an "type matching mismatch" for fields in child class that are not nullable even if the database field can be NULL.

What is your opinion on that matter?

DesLynx avatar Nov 17 '23 13:11 DesLynx

Please show a piece of code that is affected by this behaviour, I'm having a hard time imagining it without an example.

ondrejmirtes avatar Nov 17 '23 14:11 ondrejmirtes

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: BaseEntityRepository::class)]
#[ORM\InheritanceType(value: 'SINGLE_TABLE')]
#[ORM\DiscriminatorColumn(name: 'type', type: 'string')]
#[ORM\DiscriminatorMap(value: [
    'child_class' => ChildClass::class, // just one for the exemple
])]
abstract class BaseEntity
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    protected ?int $id = null;

    #[ORM\Column(type: 'string')]
    protected string $commonField;

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

    public function setId(int $id): static
    {
        $this->id = $id;

        return $this;
    }

    public function getCommonField(): string
    {
        return $this->commonField;
    }

    /**
     * @param string $commonField
     *
     * @return static
     */
    public function setCommonField(string $commonField): static
    {
        $this->commonField = $commonField;

        return $this;
    }
}


#[ORM\Entity(repositoryClass: ChildClassRepository::class)]
class ChildClass extends BaseEntity
{
    // this field is mandatory in the logic of the app but exists only in this child class so must be nullable in the database
    #[ORM\Column(type: 'string', nullable: true)]
    private string $mandatoryFieldInChildClass;

    public function getMandatoryFieldInChildClass(): string
    {
        return $this->mandatoryFieldInChildClass;
    }

    public function setMandatoryFieldInChildClass(string $mandatoryFieldInChildClass): self
    {
        $this->mandatoryFieldInChildClass = $mandatoryFieldInChildClass;

        return $this;
    }
}

Generated error:

 ------ --------------------------------------------------------------------------------------------------------------------------------------------------- 
  Line   src/Entity/ChildClass.php                                                                                                                          
 ------ --------------------------------------------------------------------------------------------------------------------------------------------------- 
  XX       Property App\Entity\ChildClass::$mandatoryFieldInChildClass type mapping mismatch: database can contain string|null but property expects string.  
 ------ --------------------------------------------------------------------------------------------------------------------------------------------------- 

DesLynx avatar Nov 17 '23 16:11 DesLynx