Closed nevmerzhitsky closed 5 months ago
This annotated version work as expected:
class CustomerTelegramChat
{
#[Cycle\Column(type: 'uuid', primary: true)]
private string $customerId;
#[Cycle\Column(type: 'uuid', primary: true)]
private string $telegramChatId;
...
}
but as I mentioned I need also to add the BelongsTo
annotation else working with the entity is annoying.
With this info I can suggest that my issue is not about UUID at all, but about mixing the Column
and BelongsTo
annotations together for association tables. It's required to add the Column
annotation to mark the field as the primary key.
@nevmerzhitsky Hi, you can't use relation and column attributes on the same property. In this case, need to add a standalone field for pk:
#[Cycle\Entity]
#[Cycle\Table\Index(['telegram_chat_id'], unique: true)]
class CustomerTelegramChat
{
#[Cycle\Column(type: 'primary')] // or uuid
private int $id;
#[Cycle\Relation\BelongsTo(target: Customer::class, innerKey: 'customer_id')]
private Customer $customer;
#[Cycle\Relation\BelongsTo(target: TelegramChat::class, innerKey: 'telegram_chat_id')]
private TelegramChat $telegramChat;
}
Or don't add a pk property to the class and a field to the database, but add Column attributes to the entity that will refer to the customer_id and telegram_chat_id fields in the database, but in PHP 8.2 there may be problems with data hydration from the database. During hydration, these fields will be created dynamically (in this case, you can add private fields customer_id and telegram_chat_id without getters):
#[Cycle\Entity]
#[Cycle\Table\Index(['telegram_chat_id'], unique: true)]
#[Cycle\Column(type: 'uuid', name: 'customer_id', primary: true)]
#[Cycle\Column(type: 'uuid', name: 'telegram_chat_id', primary: true)]
class CustomerTelegramChat
{
#[Cycle\Relation\BelongsTo(target: Customer::class, innerKey: 'customer_id')]
public Customer $customer;
#[Cycle\Relation\BelongsTo(target: TelegramChat::class, innerKey: 'telegram_chat_id')]
public TelegramChat $telegramChat;
}
Similar issue: https://github.com/cycle/schema-builder/issues/63
In this case, need to add a standalone field for pk:
So, do you think ORMs (as a product) mustn't support link tables with multicolumn PK? Or is this only the cycle-orm
limitation, which may be fixed in 3.x or later?
Hello @nevmerzhitsky !
Cycle ORM supports relations using multiple columns - just list linked entities field names in arrays.
#[BelogsTo(..., innerKey: ['field1', 'field2'], outerKey: ['field1', 'field2'])]
@msmakouz talks about not using the same field simultaneously for the relationship entity and for mapping the table column.
By the way, about UuidX attributes:
Also, I've checked docs about UUID in the cycle-database 2.x version: https://cycle-orm.dev/docs/entity-behaviors-uuid/2.x/en#version-4-random. But I cannot apply this solution because
#[UuidX]
annotations cannot be used to the composite PK because these annotations are not repeatable so only one can be declared per class (it's a very strange limitation).
The IS_REPEATABLE
flag has been added to UUID attributes (issue).
Or don't add a pk property to the class and a field to the database, but add Column attributes
msmakouz talks about not using the same field simultaneously for the relationship entity and for mapping the table column.
Oh damn guys, it took me a few hours to wrap my head around this solution. 😄 Maybe it's a signal to cover this usecase in the documentation?
My final code of the entity that works excellent:
<?php
namespace App\Entities;
use Cycle\Annotated\Annotation as Cycle;
#[Cycle\Entity]
#[Cycle\Table\Index(['telegram_chat_id'], unique: true)]
#[Cycle\Column(type: 'uuid', name: 'customer_id', primary: true)]
#[Cycle\Column(type: 'uuid', name: 'telegram_chat_id', primary: true)]
class CustomerTelegramChat
{
#[Cycle\Relation\BelongsTo(target: Customer::class, innerKey: 'customer_id')]
private Customer $customer;
#[Cycle\Relation\BelongsTo(target: TelegramChat::class, innerKey: 'telegram_chat_id')]
private TelegramChat $telegramChat;
public function __construct(Customer $customer, TelegramChat $telegramChat)
{
$this->setCustomer($customer);
$this->setTelegramChat($telegramChat);
}
public function getCustomer(): Customer
{
return $this->customer;
}
public function setCustomer(Customer $value): static
{
$this->customer = $value;
return $this;
}
public function getTelegramChat(): TelegramChat
{
return $this->telegramChat;
}
public function setTelegramChat(TelegramChat $value): static
{
$this->telegramChat = $value;
return $this;
}
}
No duplicates 🥲.
Database
PostgreSQL
What happened?
I use UUID as the PK of every table in my DB (via my custom mapper). The problem appears when I try to define an entity which is an association table (it represents the many-to-many relation). So it contains just two fields
customer
andtelegramChat
:The usage code looks like this:
After the second
run()
call there is not a new row in thecustomer_telegram_chat
table.I've added a few
dump()
calls intoDatabaseMapper::queueCreate()
So the total debug log looks like this:
The last entry (marked by
app/TelegramBot/Listeners/UpdateListener.php:46
) is the lastdump()
call. So it looks fully proper to be inserted in the table, but it's not happened and no errors were reported.I've experimented a lot with this problem and I've tried to use my custom Mapper which generates UUID PK primary keys (which I use for all my other entities) as mentioned for the 1.x version of the cycle-database: https://cycle-orm.dev/docs/advanced-uuid/1.x/en#mapper
The big problem here is that the mapper always tries to override the real value of
customer[_id]
andtelegram_chat[_id]
before the insert and so it broke the proper FK ofCustomerTelegramChat
. It was the reason why I switched the entity to the default mapper. I think this is a bug that thenextPrimaryKey()
method has no access to actual field values to be able to check that thenull
must be returned instead of a new UUID generation (this may fix unwanted overriding of the real values).Also, I've checked docs about composite PKs in the cycle-database 2.x version: https://cycle-orm.dev/docs/advanced-composite-pk/2.x/en#declaration-via-annotations. But I'm not sure how to mix the
#[Column]
and the#[BelongsTo]
annotations together in the case of the composite key. I want my entity to support methods likesetCustomer()
andgetCustomer()
with eager load, not justsetCustomerId()
, etc.Also, I've checked docs about UUID in the cycle-database 2.x version: https://cycle-orm.dev/docs/entity-behaviors-uuid/2.x/en#version-4-random. But I cannot apply this solution because
#[UuidX]
annotations cannot be used to the composite PK because these annotations are not repeatable so only one can be declared per class (it's a very strange limitation).Sorry, I'm totally bloated up by the situation. Can you provide a guide on how to make a many-to-many table with composite UUIDs PK in cycle-database 2.x? Should I implement my own mapper with
nextPrimaryKey()
to use UUID or not?Version