Persisting Entity with OneToOne relation #7142

Open
opened 2026-01-22 15:45:30 +01:00 by admin · 1 comment
Owner

Originally created by @jakublabno on GitHub (Apr 28, 2023).

Bug Report

Q A
BC Break no
Version 2.14.3

Summary

Invalid parameters binding while persisting new entity with OneToOneRelation

Current behavior

I have two entities: Invoice and InvoiceNumeration

<?php

declare(strict_types=1);

namespace Payments\Invoicing\Domain;

use Core\Database\DateTimeTrait;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\HasLifecycleCallbacks;
use Doctrine\ORM\Mapping\Id;
use JMS\Serializer\Annotation as Serializer;
use OpenApi\Attributes as OA;
use Payments\Domain\PaymentId;

#[OA\Schema]
#[Entity]
#[ORM\Table(name: 'invoices')]
#[HasLifecycleCallbacks]
class Invoice
{
    use DateTimeTrait;

    #[Id]
    #[ORM\Column(type: 'InvoiceIdType')]
    #[Serializer\Exclude]
    private InvoiceId $id;

    #[ORM\Column(name: 'payment_id', type: 'PaymentIdDoctrineType')]
    #[Serializer\Exclude]
    private PaymentId $paymentId;

    #[OA\Property]
    #[ORM\Column(name: 'type')]
    private string $invoiceType;

    #[OA\Property]
    #[ORM\Embedded]
    #[Serializer\Exclude]
    private InvoiceDetails $invoiceDetails;

    #[OA\Property]
    #[ORM\Column]
    private ?string $file = null;

    #[ORM\OneToOne(targetEntity: InvoiceNumeration::class, cascade: ['DETACH'], fetch: 'EAGER')]
    #[ORM\JoinColumn(name: 'id', referencedColumnName: 'invoice_id', nullable: true)]
    private InvoiceNumeration $numeration;

    public function __construct(PaymentId $paymentId, InvoiceDetails $invoiceDetails, InvoiceType $invoiceType)
    {
        $this->id = InvoiceId::generate();
        $this->paymentId = $paymentId;
        $this->invoiceDetails = $invoiceDetails;
        $this->invoiceType = $invoiceType->getName();
    }

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

    public function setFile(string $file): void
    {
        $this->file = $file;
    }

    public function getDetails(): InvoiceDetails
    {
        return $this->invoiceDetails;
    }

    public function getType(): InvoiceType
    {
        return InvoiceTypeFactory::fromName($this->invoiceType);
    }
}

<?php

declare(strict_types=1);

namespace Payments\Invoicing\Domain;

use Core\Database\DateTimeTrait;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Id;
use JMS\Serializer\Annotation as Serializer;

#[Entity]
#[ORM\Table(name: 'invoice_numeration')]
#[ORM\HasLifecycleCallbacks]
class InvoiceNumeration
{
    use DateTimeTrait;

    #[Id]
    #[ORM\Column(name: 'invoice_id', type: 'InvoiceIdType')]
    #[Serializer\Exclude]
    private InvoiceId $invoiceId;

    #[ORM\Column(length: 4)]
    private int $year;

    #[ORM\Column(length: 2)]
    private string $month;

    #[ORM\Column]
    private string $type;

    #[ORM\Column(length: 6)]
    private string $number;

    #[Serializer\Accessor(getter: 'getNumber')]
    private string $fullNumber;

    public function __construct(InvoiceId $invoiceId, InvoiceNumber $invoiceNumber, InvoiceType $invoiceType)
    {
        $this->invoiceId = $invoiceId;
        $this->type = $invoiceType->shortName();
        $this->year = $invoiceNumber->getYear();
        $this->month = $invoiceNumber->getMonth();
        $this->number = $invoiceNumber->getOrdinalNumber();
    }

    public function getNumber(): InvoiceNumber
    {
        return new InvoiceNumber("$this->type/$this->year/$this->month/$this->number");
    }
}

While I'm creating Invoice entity, doctrine throws a pdo exception

START TRANSACTION
INSERT INTO invoices (id, payment_id, type, file, date_created, date_updated, invoiceDetails_seller_name, invoiceDetails_seller_country, invoiceDetails_seller_city, invoiceDetails_seller_zipcode, invoiceDetails_description) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ROLLBACK
An exception occurred while executing a query: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'id' cannot be null: #0 /app/vendor/doctrine/dbal/src/Connection.php(1907): Doctrine\\DBAL…

When checking deeper, it seems id is not being passed to PDO, I see null there.
obraz

How to reproduce

Here is DDL:

create table invoices
(
    id                            binary(16)               not null
        primary key,
    payment_id                    binary(16)               not null,
    file                          varchar(255)             null,
    date_created                  datetime                 not null,
    date_updated                  datetime                 null,
    invoiceDetails_seller_name    varchar(255)             not null,
    invoiceDetails_seller_country varchar(255)             not null,
    invoiceDetails_seller_city    varchar(255)             not null,
    invoiceDetails_seller_zipcode varchar(255)             not null,
    invoiceDetails_description    varchar(255)             not null,
    type                          enum ('proforma', 'vat') not null
);

create table invoice_numeration
(
    invoice_id   binary(16)              not null
        primary key,
    year         int                     not null,
    month        varchar(2)              not null,
    type         enum ('FV', 'PROFORMA') not null,
    number       int                     not null,
    date_created datetime                not null,
    date_updated datetime                null,
    constraint invoice_numeration_pk
        unique (year, number, month, type)
);



Of course, everything worked before adding OneToOne and JoinColumn definition.
Maybe I'm doing something wrong?

I would not use JoinColumn, because it seems not be necessary, but when I don't it throws another exception

Could not resolve type of column \"id\" of class \"Payments\\Invoicing\\Domain\\InvoiceNumeration\"

However, it's the same type as in Invoice Entity Id

Originally created by @jakublabno on GitHub (Apr 28, 2023). ### Bug Report <!-- Fill in the relevant information below to help triage your issue. --> | Q | A |------------ | ------ | BC Break | no | Version | 2.14.3 #### Summary Invalid parameters binding while persisting new entity with OneToOneRelation #### Current behavior I have two entities: `Invoice` and `InvoiceNumeration` ``` <?php declare(strict_types=1); namespace Payments\Invoicing\Domain; use Core\Database\DateTimeTrait; use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\HasLifecycleCallbacks; use Doctrine\ORM\Mapping\Id; use JMS\Serializer\Annotation as Serializer; use OpenApi\Attributes as OA; use Payments\Domain\PaymentId; #[OA\Schema] #[Entity] #[ORM\Table(name: 'invoices')] #[HasLifecycleCallbacks] class Invoice { use DateTimeTrait; #[Id] #[ORM\Column(type: 'InvoiceIdType')] #[Serializer\Exclude] private InvoiceId $id; #[ORM\Column(name: 'payment_id', type: 'PaymentIdDoctrineType')] #[Serializer\Exclude] private PaymentId $paymentId; #[OA\Property] #[ORM\Column(name: 'type')] private string $invoiceType; #[OA\Property] #[ORM\Embedded] #[Serializer\Exclude] private InvoiceDetails $invoiceDetails; #[OA\Property] #[ORM\Column] private ?string $file = null; #[ORM\OneToOne(targetEntity: InvoiceNumeration::class, cascade: ['DETACH'], fetch: 'EAGER')] #[ORM\JoinColumn(name: 'id', referencedColumnName: 'invoice_id', nullable: true)] private InvoiceNumeration $numeration; public function __construct(PaymentId $paymentId, InvoiceDetails $invoiceDetails, InvoiceType $invoiceType) { $this->id = InvoiceId::generate(); $this->paymentId = $paymentId; $this->invoiceDetails = $invoiceDetails; $this->invoiceType = $invoiceType->getName(); } public function getId(): InvoiceId { return $this->id; } public function setFile(string $file): void { $this->file = $file; } public function getDetails(): InvoiceDetails { return $this->invoiceDetails; } public function getType(): InvoiceType { return InvoiceTypeFactory::fromName($this->invoiceType); } } ``` ``` <?php declare(strict_types=1); namespace Payments\Invoicing\Domain; use Core\Database\DateTimeTrait; use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; use JMS\Serializer\Annotation as Serializer; #[Entity] #[ORM\Table(name: 'invoice_numeration')] #[ORM\HasLifecycleCallbacks] class InvoiceNumeration { use DateTimeTrait; #[Id] #[ORM\Column(name: 'invoice_id', type: 'InvoiceIdType')] #[Serializer\Exclude] private InvoiceId $invoiceId; #[ORM\Column(length: 4)] private int $year; #[ORM\Column(length: 2)] private string $month; #[ORM\Column] private string $type; #[ORM\Column(length: 6)] private string $number; #[Serializer\Accessor(getter: 'getNumber')] private string $fullNumber; public function __construct(InvoiceId $invoiceId, InvoiceNumber $invoiceNumber, InvoiceType $invoiceType) { $this->invoiceId = $invoiceId; $this->type = $invoiceType->shortName(); $this->year = $invoiceNumber->getYear(); $this->month = $invoiceNumber->getMonth(); $this->number = $invoiceNumber->getOrdinalNumber(); } public function getNumber(): InvoiceNumber { return new InvoiceNumber("$this->type/$this->year/$this->month/$this->number"); } } ``` While I'm creating `Invoice` entity, doctrine throws a pdo exception ``` START TRANSACTION INSERT INTO invoices (id, payment_id, type, file, date_created, date_updated, invoiceDetails_seller_name, invoiceDetails_seller_country, invoiceDetails_seller_city, invoiceDetails_seller_zipcode, invoiceDetails_description) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ROLLBACK An exception occurred while executing a query: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'id' cannot be null: #0 /app/vendor/doctrine/dbal/src/Connection.php(1907): Doctrine\\DBAL… ``` When checking deeper, it seems id is not being passed to PDO, I see null there. ![obraz](https://user-images.githubusercontent.com/64330419/235223976-145015c7-8375-48a1-abf8-db7dcf5fc201.png) #### How to reproduce Here is DDL: ``` create table invoices ( id binary(16) not null primary key, payment_id binary(16) not null, file varchar(255) null, date_created datetime not null, date_updated datetime null, invoiceDetails_seller_name varchar(255) not null, invoiceDetails_seller_country varchar(255) not null, invoiceDetails_seller_city varchar(255) not null, invoiceDetails_seller_zipcode varchar(255) not null, invoiceDetails_description varchar(255) not null, type enum ('proforma', 'vat') not null ); create table invoice_numeration ( invoice_id binary(16) not null primary key, year int not null, month varchar(2) not null, type enum ('FV', 'PROFORMA') not null, number int not null, date_created datetime not null, date_updated datetime null, constraint invoice_numeration_pk unique (year, number, month, type) ); ``` <!-- What was the expected (correct) behavior? --> Of course, everything worked before adding OneToOne and JoinColumn definition. Maybe I'm doing something wrong? I would not use JoinColumn, because it seems not be necessary, but when I don't it throws another exception ``` Could not resolve type of column \"id\" of class \"Payments\\Invoicing\\Domain\\InvoiceNumeration\" ``` However, it's the same type as in `Invoice` Entity Id
Author
Owner

@mpdude commented on GitHub (May 31, 2023):

Thank you for providing a code example! That makes it easier to understand the situation.

As a first step, could you please try to simplify the code a bit more?

  • What is DateTimeTrait, and do you need it to show the problem?
  • Do you need the OpenAPI stuff, Serializer and/or the lifecycle callbacks to demonstrate the problem?
  • Can you remove getters/setters that are not relevant to the problem?
  • You're using two (custom?) column types without showing their definition.
@mpdude commented on GitHub (May 31, 2023): Thank you for providing a code example! That makes it easier to understand the situation. As a first step, could you please try to simplify the code a bit more? * What is `DateTimeTrait`, and do you need it to show the problem? * Do you need the OpenAPI stuff, Serializer and/or the lifecycle callbacks to demonstrate the problem? * Can you remove getters/setters that are not relevant to the problem? * You're using two (custom?) column types without showing their definition.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#7142