Symfony OneToMany Fetch: Eager with ULID primary key?

Ссылка скопирована
1 ответ

Добрый день.

Подскажите как исправить.

Есть 2 сущности
Product

<?php namespace AppEntity;  use SymfonyComponentUidUlid; use SymfonyBridgeDoctrineTypesUlidType;  use DoctrineORMMapping as ORM; use DoctrineCommonCollections{ ArrayCollection, Collection };  use AppRepositoryProductRepository;  #[ORMEntity(repositoryClass: ProductRepository::class)] #[ORMTable(name: 'product')] class Product {     #[ORMId]     #[ORMColumn(type: UlidType::NAME, unique: true)]     #[ORMGeneratedValue(strategy: 'CUSTOM')]     #[ORMCustomIdGenerator(class: 'doctrine.ulid_generator')]     private ?Ulid $id = null;      #[ORMColumn]     private ?string $name = null;      #[ORMOneToMany(targetEntity: Image::class, mappedBy: 'product', cascade: [ 'persist', 'remove' ], fetch: 'EAGER')]     #[ORMJoinColumn(name: 'product_id', referencedColumnName: 'id')]     private Collection $images;      public function __construct()     {         $this->images = new ArrayCollection();     }      public function getId(): ?Ulid     {         return $this->id;     }      public function getUlid(): ?string     {         return $this->id->toRfc4122();     }      public function getName(): ?string     {         return $this->name;     }      public function setName(?string $name): static     {         $this->name = $name;          return $this;     }      public function getImages(): Collection     {         return $this->images;     } }

<?php namespace AppEntity; use SymfonyComponentUidUlid; use SymfonyBridgeDoctrineTypesUlidType; use DoctrineORMMapping as ORM; use DoctrineCommonCollections{ ArrayCollection, Collection }; use AppRepositoryProductRepository; #[ORMEntity(repositoryClass: ProductRepository::class)] #[ORMTable(name: 'product')] class Product { #[ORMId] #[ORMColumn(type: UlidType::NAME, unique: true)] #[ORMGeneratedValue(strategy: 'CUSTOM')] #[ORMCustomIdGenerator(class: 'doctrine.ulid_generator')] private ?Ulid $id = null; #[ORMColumn] private ?string $name = null; #[ORMOneToMany(targetEntity: Image::class, mappedBy: 'product', cascade: [ 'persist', 'remove' ], fetch: 'EAGER')] #[ORMJoinColumn(name: 'product_id', referencedColumnName: 'id')] private Collection $images; public function __construct() { $this->images = new ArrayCollection(); } public function getId(): ?Ulid { return $this->id; } public function getUlid(): ?string { return $this->id->toRfc4122(); } public function getName(): ?string { return $this->name; } public function setName(?string $name): static { $this->name = $name; return $this; } public function getImages(): Collection { return $this->images; } }

Image

<?php namespace AppEntity;  use SymfonyComponentUidUlid; use SymfonyBridgeDoctrineTypesUlidType;  use DoctrineORMMapping as ORM;  use AppEntityProduct; use AppRepositoryImageRepository;  #[ORMEntity(repositoryClass: ImageRepository::class)] #[ORMTable(name: 'image')] class Image {     #[ORMId]     #[ORMColumn(type: UlidType::NAME, unique: true)]     #[ORMGeneratedValue(strategy: 'CUSTOM')]     #[ORMCustomIdGenerator(class: 'doctrine.ulid_generator')]     private ?Ulid $id = null;      #[ORMManyToOne(targetEntity: Product::class, inversedBy: 'images')]     private ?Product $product = null;          public function getId(): ?Ulid     {         return $this->id;     }      public function getUlid(): ?string     {         return $this->id->toRfc4122();     }      public function getProduct(): ?Product     {         return $this->product;     }      public function setProduct(?Product $product): static     {         $this->product = $product;          return $this;     } }

<?php namespace AppEntity; use SymfonyComponentUidUlid; use SymfonyBridgeDoctrineTypesUlidType; use DoctrineORMMapping as ORM; use AppEntityProduct; use AppRepositoryImageRepository; #[ORMEntity(repositoryClass: ImageRepository::class)] #[ORMTable(name: 'image')] class Image { #[ORMId] #[ORMColumn(type: UlidType::NAME, unique: true)] #[ORMGeneratedValue(strategy: 'CUSTOM')] #[ORMCustomIdGenerator(class: 'doctrine.ulid_generator')] private ?Ulid $id = null; #[ORMManyToOne(targetEntity: Product::class, inversedBy: 'images')] private ?Product $product = null; public function getId(): ?Ulid { return $this->id; } public function getUlid(): ?string { return $this->id->toRfc4122(); } public function getProduct(): ?Product { return $this->product; } public function setProduct(?Product $product): static { $this->product = $product; return $this; } }

Стандартная выборка из репозитория
ProductController

<?php namespace AppController;  use SymfonyBundleFrameworkBundleControllerAbstractController; use SymfonyComponentHttpFoundation{ Request, Response }; use SymfonyComponentRoutingAnnotationRoute;  use AppRepositoryProductRepository;  class ProductController extends AbstractController {     #[Route('/products', name: 'products')]     public function index(ProductRepository $productRepository): Response     {         $output = [ 'data' => [], 'included' => [] ];      	$productCollection = $productRepository->findAll();      	foreach ($productCollection as $entity) {     		$item = [     			'id' => $entity->getUlid(),     			'attributes' => [     				'name' => $entity->getName()     			]     		];      		$images = $entity->getImages();      		foreach ($images as $image) {     			$output['included']['images'][] = [     				'id' => $image->getUlid(),     				'relationships' => [     					'product' => $entity->getUlid()     				]     			];     		}      		$output['data'][] = $item;     	}          return $this->json($output);     } }

<?php namespace AppController; use SymfonyBundleFrameworkBundleControllerAbstractController; use SymfonyComponentHttpFoundation{ Request, Response }; use SymfonyComponentRoutingAnnotationRoute; use AppRepositoryProductRepository; class ProductController extends AbstractController { #[Route('/products', name: 'products')] public function index(ProductRepository $productRepository): Response { $output = [ 'data' => [], 'included' => [] ]; $productCollection = $productRepository->findAll(); foreach ($productCollection as $entity) { $item = [ 'id' => $entity->getUlid(), 'attributes' => [ 'name' => $entity->getName() ] ]; $images = $entity->getImages(); foreach ($images as $image) { $output['included']['images'][] = [ 'id' => $image->getUlid(), 'relationships' => [ 'product' => $entity->getUlid() ] ]; } $output['data'][] = $item; } return $this->json($output); } }

При использовании SymfonyComponentUidUlid вместо первичного ключа результат выборки следующий

Symfony OneToMany Fetch: Eager with ULID primary key?

При этом $images пустой

Если же просто изменить первичный ключ на

#[ORMId] #[ORMGeneratedValue] #[ORMColumn] private ?int $id = null;

#[ORMId] #[ORMGeneratedValue] #[ORMColumn] private ?int $id = null;

То все работает как надо, запрос тот же самый, но вместо SymfonyComponentUidUlid в параметрах там INT, но при этом $image полноценная коллекция из которой можно получить все данные.

Composer require

"require": {         "php": ">=8.2",         "ext-ctype": "*",         "ext-iconv": "*",         "doctrine/dbal": "^3",         "doctrine/doctrine-bundle": "^2.11",         "doctrine/doctrine-migrations-bundle": "^3.3",         "doctrine/orm": "^3.1",         "symfony/console": "7.0.*",         "symfony/dotenv": "7.0.*",         "symfony/flex": "^2",         "symfony/framework-bundle": "7.0.*",         "symfony/runtime": "7.0.*",         "symfony/uid": "7.0.*",         "symfony/yaml": "7.0.*"     }

"require": { "php": ">=8.2", "ext-ctype": "*", "ext-iconv": "*", "doctrine/dbal": "^3", "doctrine/doctrine-bundle": "^2.11", "doctrine/doctrine-migrations-bundle": "^3.3", "doctrine/orm": "^3.1", "symfony/console": "7.0.*", "symfony/dotenv": "7.0.*", "symfony/flex": "^2", "symfony/framework-bundle": "7.0.*", "symfony/runtime": "7.0.*", "symfony/uid": "7.0.*", "symfony/yaml": "7.0.*" }

Дополнительно:

Ответы:

Стоило бы посмотреть как sql запрос в итоге формируется и где происходит несоответствие форматов.

При передаче в качестве параметра запроса можно явно сконвертировать Ulid в нужный формат:
$entity->getUlid()->toBase32()

  • Формируется вот так
    SELECT t0.id AS id_1, t0.product_id AS product_id_2 FROM image t0 WHERE t0.product_id IN ('01HS0RHTWAVDXTPBV9VZ5W09ME', '01HS0RHTWAVDXTPBV9VZ5W09MM', '01HS0RHTWAVDXTPBV9VZ5W09MV', '01HS0RHTWAVDXTPBV9VZ5W09N3', '01HS0RHTWAVDXTPBV9VZ5W09N9', '01HS0RHTWAVDXTPBV9VZ5W09NF', '01HS0RHTWAVDXTPBV9VZ5W09NN', '01HS0RHTWAVDXTPBV9VZ5W09NY', '01HS0RHTWAVDXTPBV9VZ5W09P4', '01HS0RHTWAVDXTPBV9VZ5W09PE', '01HS0RHTWAVDXTPBV9VZ5W09PP', '01HS0RHTWAVDXTPBV9VZ5W09PZ', '01HS0RHTWAVDXTPBV9VZ5W09QA', '01HS0RHTWAVDXTPBV9VZ5W09QN', '01HS0RHTWBPNVM1R8Y8DM3F12Q', '01HS0RHTWBPNVM1R8Y8DM3F12Z', '01HS0RHTWBPNVM1R8Y8DM3F135', '01HS0RHTWBPNVM1R8Y8DM3F13C', '01HS0RHTWBPNVM1R8Y8DM3F13K', '01HS0RHTWBPNVM1R8Y8DM3F13S', '01HS0RHTWBPNVM1R8Y8DM3F13Z', '01HS0RHTWBPNVM1R8Y8DM3F14A', '01HS0RHTWBPNVM1R8Y8DM3F14N', '01HS0RHTWBPNVM1R8Y8DM3F14Y', '01HS0RHTWBPNVM1R8Y8DM3F159', '01HS0RHTWBPNVM1R8Y8DM3F15M', '01HS0RHTWBPNVM1R8Y8DM3F15T', '01HS0RHTWBPNVM1R8Y8DM3F165', '01HS0RHTWBPNVM1R8Y8DM3F16G', '01HS0RHTWBPNVM1R8Y8DM3F16V');

    SELECT t0.id AS id_1, t0.product_id AS product_id_2 FROM image t0 WHERE t0.product_id IN ('01HS0RHTWAVDXTPBV9VZ5W09ME', '01HS0RHTWAVDXTPBV9VZ5W09MM', '01HS0RHTWAVDXTPBV9VZ5W09MV', '01HS0RHTWAVDXTPBV9VZ5W09N3', '01HS0RHTWAVDXTPBV9VZ5W09N9', '01HS0RHTWAVDXTPBV9VZ5W09NF', '01HS0RHTWAVDXTPBV9VZ5W09NN', '01HS0RHTWAVDXTPBV9VZ5W09NY', '01HS0RHTWAVDXTPBV9VZ5W09P4', '01HS0RHTWAVDXTPBV9VZ5W09PE', '01HS0RHTWAVDXTPBV9VZ5W09PP', '01HS0RHTWAVDXTPBV9VZ5W09PZ', '01HS0RHTWAVDXTPBV9VZ5W09QA', '01HS0RHTWAVDXTPBV9VZ5W09QN', '01HS0RHTWBPNVM1R8Y8DM3F12Q', '01HS0RHTWBPNVM1R8Y8DM3F12Z', '01HS0RHTWBPNVM1R8Y8DM3F135', '01HS0RHTWBPNVM1R8Y8DM3F13C', '01HS0RHTWBPNVM1R8Y8DM3F13K', '01HS0RHTWBPNVM1R8Y8DM3F13S', '01HS0RHTWBPNVM1R8Y8DM3F13Z', '01HS0RHTWBPNVM1R8Y8DM3F14A', '01HS0RHTWBPNVM1R8Y8DM3F14N', '01HS0RHTWBPNVM1R8Y8DM3F14Y', '01HS0RHTWBPNVM1R8Y8DM3F159', '01HS0RHTWBPNVM1R8Y8DM3F15M', '01HS0RHTWBPNVM1R8Y8DM3F15T', '01HS0RHTWBPNVM1R8Y8DM3F165', '01HS0RHTWBPNVM1R8Y8DM3F16G', '01HS0RHTWBPNVM1R8Y8DM3F16V');

    а надо чтобы формировался вот так

    SELECT t0.id AS id_1, t0.product_id AS product_id_2 FROM image t0 WHERE t0.product_id IN (0x018E4188EB8ADB7BAB2F69DFCBC0268E, 0x018E4188EB8ADB7BAB2F69DFCBC02694, 0x018E4188EB8ADB7BAB2F69DFCBC0269B, 0x018E4188EB8ADB7BAB2F69DFCBC026A3, 0x018E4188EB8ADB7BAB2F69DFCBC026A9, 0x018E4188EB8ADB7BAB2F69DFCBC026AF, 0x018E4188EB8ADB7BAB2F69DFCBC026B5, 0x018E4188EB8ADB7BAB2F69DFCBC026BE, 0x018E4188EB8ADB7BAB2F69DFCBC026C4, 0x018E4188EB8ADB7BAB2F69DFCBC026CE, 0x018E4188EB8ADB7BAB2F69DFCBC026D6, 0x018E4188EB8ADB7BAB2F69DFCBC026DF, 0x018E4188EB8ADB7BAB2F69DFCBC026EA, 0x018E4188EB8ADB7BAB2F69DFCBC026F5, 0x018E4188EB8BB57740E11E4368378457, 0x018E4188EB8BB57740E11E436837845F, 0x018E4188EB8BB57740E11E4368378465, 0x018E4188EB8BB57740E11E436837846C, 0x018E4188EB8BB57740E11E4368378473, 0x018E4188EB8BB57740E11E4368378479, 0x018E4188EB8BB57740E11E436837847F, 0x018E4188EB8BB57740E11E436837848A, 0x018E4188EB8BB57740E11E4368378495, 0x018E4188EB8BB57740E11E436837849E, 0x018E4188EB8BB57740E11E43683784A9, 0x018E4188EB8BB57740E11E43683784B4, 0x018E4188EB8BB57740E11E43683784BA, 0x018E4188EB8BB57740E11E43683784C5, 0x018E4188EB8BB57740E11E43683784D0, 0x018E4188EB8BB57740E11E43683784DB);

    SELECT t0.id AS id_1, t0.product_id AS product_id_2 FROM image t0 WHERE t0.product_id IN (0x018E4188EB8ADB7BAB2F69DFCBC0268E, 0x018E4188EB8ADB7BAB2F69DFCBC02694, 0x018E4188EB8ADB7BAB2F69DFCBC0269B, 0x018E4188EB8ADB7BAB2F69DFCBC026A3, 0x018E4188EB8ADB7BAB2F69DFCBC026A9, 0x018E4188EB8ADB7BAB2F69DFCBC026AF, 0x018E4188EB8ADB7BAB2F69DFCBC026B5, 0x018E4188EB8ADB7BAB2F69DFCBC026BE, 0x018E4188EB8ADB7BAB2F69DFCBC026C4, 0x018E4188EB8ADB7BAB2F69DFCBC026CE, 0x018E4188EB8ADB7BAB2F69DFCBC026D6, 0x018E4188EB8ADB7BAB2F69DFCBC026DF, 0x018E4188EB8ADB7BAB2F69DFCBC026EA, 0x018E4188EB8ADB7BAB2F69DFCBC026F5, 0x018E4188EB8BB57740E11E4368378457, 0x018E4188EB8BB57740E11E436837845F, 0x018E4188EB8BB57740E11E4368378465, 0x018E4188EB8BB57740E11E436837846C, 0x018E4188EB8BB57740E11E4368378473, 0x018E4188EB8BB57740E11E4368378479, 0x018E4188EB8BB57740E11E436837847F, 0x018E4188EB8BB57740E11E436837848A, 0x018E4188EB8BB57740E11E4368378495, 0x018E4188EB8BB57740E11E436837849E, 0x018E4188EB8BB57740E11E43683784A9, 0x018E4188EB8BB57740E11E43683784B4, 0x018E4188EB8BB57740E11E43683784BA, 0x018E4188EB8BB57740E11E43683784C5, 0x018E4188EB8BB57740E11E43683784D0, 0x018E4188EB8BB57740E11E43683784DB);

    А все потому что видимо "по умолчанию" происходит так как я написал в ответ Виктор Кожухарь

Нужно решить такую задачу?

Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.

Заказать помощь
Лучший ответ
1
Мария Код Ответ

Для использования связи OneToMany с жадной загрузкой (Eager) в Symfony с ULID в качестве первичного ключа, вам нужно сделать небольшие настройки.

ULID (Universally Unique Lexicographically Sortable Identifier) - это тип уникального идентификатора, который можно использовать в качестве первичного ключа в Symfony. Однако, жадная загрузка (Eager loading) не является поддерживаемой функцией в Symfony для таких типов ключей.

Вместо этого, вам следует использовать ленивую загрузку (Lazy loading) для связи OneToMany. Для этого вам нужно убедиться, что ваша сущность настроена правильно:

```php
/**
* @ORM\Entity
*/
class ParentEntity
{
// ...

/**
* @ORM\OneToMany(targetEntity="ChildEntity", mappedBy="parent", fetch="LAZY")
*/
private $children;

// ...
}
```

Таким образом, при обращении к свойству $children сущности ParentEntity, данные будут загружаться лениво только при необходимости.

Если же вам все-таки необходимо использовать жадную загрузку с ULID, вам придется написать собственный запрос с использованием Doctrine Query Builder или DQL, чтобы явно указать условия загрузки данных. Например:

```php
$query = $entityManager->createQuery(
'SELECT p, c FROM ParentEntity p
JOIN p.children c
WHERE p.id = :parentId'
)->setParameter('parentId', $parentId);

$parent = $query->getOneOrNullResult();
```

Таким образом, вы сможете использовать жадную загрузку с ULID в Symfony, хотя это потребует дополнительных усилий по настройке запросов. Надеюсь, это поможет вам решить проблему!

Другие ответы (0)

Пока нет других ответов. Будьте первым, кто поможет автору.

Ответить на вопрос

комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Вам также может быть интересно