Задать вопрос
@HellWalk

Почему сущность не видит связанную созданную сущность,а пытается создать новую?

Вначале пример связи, где все работает нормально:

#[ORM\OneToMany(targetEntity: UploadFile::class, mappedBy: 'files', cascade: ['remove'])]
#[Serializer\Groups([Groups::LIST, Groups::DETAILS])]
#[ORM\JoinColumn(nullable: true)]
private ?Collection $files;


На фронте вначале загружается файл (и создается запись в базе), и затем приходит такой json на создание основной сущности:

// ...
            "files": [
                {
                    "id": "0198efe4-c9ef-76b2-b5d2-b3957f4d8cba",
                    "name": "1191fb00-8085-48c3-b728-cbd0d76847cb",
                    "originalName": "file",
                    "extension": "png",
                    "description": null,
                    "clientOriginalName": "file.png",
                    "createdDatetime": null,
                    "createdAt": "2025-08-28T11:56:45+03:00",
                    "createdBy": null
                }
            ]
// ...


Все создается успешно - симфони понимает, что это данные по уже существующей записи в базе, и просто создает связь.

Но есть другая сущность, тоже со связью с UploadFile, но OneToOne:

#[ORM\OneToOne(cascade: ['remove'])]
#[Serializer\Groups([Groups::LIST, Groups::DETAILS])]
#[ORM\JoinColumn(nullable: true)]
public ?UploadFile $file = null;


С фронта приходят данные:

"file": {
            "id": "0198efdc-5e9b-7d3b-86c6-47b68f0f665d",
            "name": "2c830a0d-c8ba-4655-afcd-566da343cac6",
            "originalName": "file",
            "extension": "png",
            "description": null,
            "clientOriginalName": "file.png",
            "createdDatetime": null,
            "createdAt": "2025-08-28T11:47:33+03:00",
            "createdBy": null
        },


И сохранение сущности ломается:

A new entity was found through the relationship 'App\Entity\Document#file' that was not configured to cascade persist operations for entity: App\Entity\UploadFile@1847. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example @ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'App\Entity\UploadFile#__toString()' to get a clue


Если добавляю cascade={"persist"}, то на один файл создается две записи - первая, корректная, созданная во время загрузки файла. И вторая, при создании основной сущности, когда симфони не видит, что связанная сущность уже существует, и создает новую.

Как сделать так, чтобы симфони поняла, что это уже существующая связанная сущность? id в базе и запросе совпадает. Но видимо этого недостаточно.

P.S. Все сущности создаются через сериалайзер:

$data = json_decode($request->getContent(), true);
$entity = $this->serializer->denormalize($data, $entityClass);
$errors = $this->validator->validate($entity);

    if (count($errors) > 0) {
        throw new ConstraintViolationListException($errors);
    }

$repository->add($entity);


Сохраняются также единым образом:

public function add(object $entity, bool $flush = true): void
    {
        if ($entity instanceof CreatedAtInterface) {
            if ($entity->getCreatedAt() === null) {
                $entity->setCreatedAt();
            }
        }
        $this->getEntityManager()->persist($entity);
        if ($flush) {
            $this->getEntityManager()->flush();
        }
    }


Т.е. из json через сериалайзер создается сущность, потом просто сохраняется
  • Вопрос задан
  • 65 просмотров
Подписаться 1 Простой 12 комментариев
Решения вопроса 1
@HellWalk Автор вопроса
Какой-то отдельной опции, которая бы указала доктрине "не создавай новую сущность по этим данным, найди её в базе по id" нет, по этому, чтобы исправить эту ошибку необходимо написать свой нормалайзер, который будет вручную получать сущность из базы по id и привязывать её к родительской сущности:

use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

class EntityNormalizer implements DenormalizerInterface, NormalizerInterface
{
    public function __construct(
        private readonly ObjectNormalizer $normalizer,
        private readonly EntityManagerInterface $em,
    ) {
    }

    public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): mixed
    {
        $object = $this->normalizer->denormalize($data, $type, $format, $context);
        
        if ($file = $entity->getFile()) {
            $entity->setFile(
                $this-em->getRepository(UploadFile::class)->findOneBy(['id' => $file->getId()])
            );
        }

        return $object;
    }

    // ...
}
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы