doctrine Создать свой тип данных для БД. Как сохранить в БД массив объектов - не энтити
Primary tabs
Текст ошибки
The given entity of type ... has no identity/no id values set. It cannot be added to the identity map
Возможно, у Вас она связана с тем, что Вы пытаетесь положить в одну сущность другую, которая ещё не была сохранена, и соответственно ещё не получила id. Тогда либо сохраняйте объекты в БД поступательно, либо укажите cascade="persist" полю связи.
У меня же возникла при попытке положить в БД коллекцию объектов - не Entity, а ВО. Пишет, что id не найден (собственно, Value-Object и не имеет id, а только одно поле - $value).
Пыталась сохранить массив моих объектов в поле $merchantStatuses, Entity:
<?php namespace App\Entity; use App\VO\Status\MerchantStatus; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity * @ORM\Table(name="agents_report") */ class AgentsReport { /** * @var int * * @ORM\Column(type="integer") * @ORM\Id * @ORM\GeneratedValue */ private $id; /** * @var Collection | MerchantStatus[] * * @ORM\Column(type="merchant_status") */ private $merchantStatuses; // ... }
Ошибка в строке
$this->em->persist($agentsReport);
Решение
Я решила проблему с помощью создания Типа доктрины (Doctrine Types) для поля, содержащего коллекцию того, что мне нужно. (тип поля, который мы указываем доктрине в аннотации @ORM\Column(type="..")).
Что в этом Типе есть? В классе типа описывается, как обработать объект при записи в БД, и соответственно, как его обработать, когда из БД достаёшь, чтобы он выглядел "как новенький".
- Создаём класс. Вот так:
<?php namespace App\Types; use App\VO\Status\MerchantStatus; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type; use InvalidArgumentException; class TypeMerchantStatus extends Type { private const TYPE_NAME = 'merchant_status'; /** * @param array $fieldDeclaration * @param AbstractPlatform $platform * * @return string */ public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string { return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); } /** * @param string $stringMrchantStatuses * @param AbstractPlatform $platform * * @return Collection | ReportOperationType[] * * @throws ConversionException */ public function convertToPHPValue($stringMrchantStatuses, AbstractPlatform $platform): Collection { $decoded = json_decode($stringMrchantStatuses); if (JSON_ERROR_NONE !== json_last_error()) { throw ConversionException::conversionFailedSerialization( $stringMrchantStatuses, 'json', json_last_error_msg() ); } $merchantStatuses = array_map( function (string $merchantStatus): MerchantStatus { return new MerchantStatus($merchantStatus); }, $decoded ); return new ArrayCollection($merchantStatuses); } /** * @param ArrayCollection $merchantStatuses * @param AbstractPlatform $platform * * @return string * * @throws InvalidArgumentException */ public function convertToDatabaseValue($merchantStatuses, AbstractPlatform $platform): ?string { $arrayMerchantStatuses = $merchantStatuses->toArray(); if (empty($arrayMerchantStatuses)) { return json_encode([]); } $encoded = json_encode($arrayMerchantStatuses); if (JSON_ERROR_NONE !== json_last_error()) { throw ConversionException::conversionFailedSerialization( $merchantStatuses, 'json', json_last_error_msg() ); } return $encoded; } /** * @return string */ public function getName(): string { return self::TYPE_NAME; } }
Когда Doctrine видит тип поля merchant_statuses, она лезет в методы convertToPHPValue() и convertToDatabaseValue() и понимает, как именно конвертировать этого зверя в понятный ей формат и обратно. :)
- Не забываем зарегистиовать новый тип в конфиге doctrine.yaml:
doctrine: dbal: types: merchant_status: App\Types\TypeMerchantStatus
- Ну и объект ВО надо модифицировать: добавляем ему реализацию интерфейса JsonSerializable (чтобы он мог прыгнуть в тип json) и метод jsonSerialize():
<?php namespace App\VO\Status; use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\Embeddable; use InvalidArgumentException; use JsonSerializable; /** * @Embeddable */ class MerchantStatus implements JsonSerializable { // В этом объекте нет id, и это не Entity, но в БД нам надо положить коллекцию таких объектов // ... /** * @return string */ public function jsonSerialize(): string { return $this->getValue(); } }
Источники
- Log in to post comments
- 2089 reads