Сериализатор Symfony не сериализует все поля класса

У меня есть два класса: один для управления событиями, а другой — для пользователей. Пользователи могут участвовать в мероприятиях. Когда они участвуют, они становятся «Посетителями». Проблема в том, что когда я сериализую событие с участниками, ответ содержит все поля, кроме поля участников.

Класс событий:

<?php

namespace App\Entity;

use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\CustomIdGenerator;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\ManyToMany;
use Doctrine\ORM\Mapping\ManyToOne;
use Doctrine\ORM\Mapping\Table;
use Gedmo\Mapping\Annotation\Timestampable;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints\NotBlank;
use Vich\UploaderBundle\Mapping\Annotation\Uploadable;
use Vich\UploaderBundle\Mapping\Annotation\UploadableField;

#[Entity]
#[Table(name: 'event')]
#[Uploadable]
class Event
{
    #[Id]
    #[GeneratedValue(strategy: 'CUSTOM')]
    #[CustomIdGenerator(class: UUIDGenerator::class)]
    #[Column]
    #[Groups(['apiExposed'])]
    private string $id;

    #[Column]
    #[Groups(['apiExposed', 'searchable'])]
    #[NotBlank]
    private string $title;

    #[Column(type: 'point', nullable: true)]
    #[Groups(['apiExposed', 'searchable'])]
    private ?Point $location;

    #[ManyToOne(targetEntity: User::class)]
    #[Groups('apiExposed')]
    #[NotBlank]
    private User $organizer;

    #[ManyToMany(targetEntity: User::class, inversedBy: 'participatedEvents')]
    #[Groups('apiExposed')]
    private Collection $attendees;

    #[Column(type: 'text')]
    #[Groups(['apiExposed', 'searchable'])]
    #[NotBlank]
    private string $description;

    #[Column]
    #[Groups(['aîExposed', 'searchable'])]
    #[NotBlank]
    private string $address;

    #[UploadableField(mapping: 'event_picture', fileNameProperty: 'pictureName')]
    private ?File $pictureFile = null;

    #[Column(nullable: true)]
    #[Groups(['apiExposed', 'searchable'])]
    private ?string $pictureName = 'event.jpg';

    #[Column]
    #[Groups(['apiExposed', 'searchable'])]
    private bool $isSecret = false;

    #[Column]
    #[Groups(['apiExposed', 'searchable'])]
    private \DateTimeImmutable $startDate;

    #[Column]
    #[Groups('apiExposed')]
    #[Timestampable(on: 'create')]
    private \DateTimeImmutable $createdAt;

    #[Column]
    #[Groups('apiExposed')]
    #[Timestampable(on: 'update')]
    private \DateTimeImmutable $updatedAt;

    // Getters and setters
    /**
     * @return Collection
     */
     public function getAttendees(): Collection
     {
         return $this->attendees;
     }

     /**
      * @param User $user
      * @return $this
      */
     public function addAttendee(User $user): Event
     {
         $this -> attendees -> add($user);
         return $this;
     }

     /**
      * @param User $user
      * @return $this
      */
     public function removeAttendee(User $user): Event
     {
         $this -> attendees -> removeElement($user);
         return $this;
      }

}

Класс пользователя:

<?php

namespace App\Entity;

use App\Repository\UserRepository;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation\Timestampable;
use OpenApi\Attributes\Items;
use OpenApi\Attributes\Property;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Vich\UploaderBundle\Mapping\Annotation\Uploadable;
use Vich\UploaderBundle\Mapping\Annotation\UploadableField;

#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\Table(name: '`user`')]
#[Uploadable]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    #[Groups('apiExposed')]
    private ?int $id = null;

    #[ORM\Column(length: 180, unique: true)]
    #[Assert\NotBlank]
    #[Groups('apiExposed')]
    private ?string $username = null;

    #[ORM\Column]
    #[Groups('apiExposed')]
    #[Property(type: 'array', items: new Items(type: 'string'))]
    private array $roles = [];

    /**
     * @var string The hashed password
     */
    #[ORM\Column]
    #[Assert\NotBlank]
    private ?string $password = null;

    #[ORM\Column(nullable: true)]
    #[Groups('apiExposed')]
    private ?string $bio = null;

    #[ORM\Column(unique: true)]
    #[Assert\Email]
    #[Assert\NotBlank]
    #[Groups('apiExposed')]
    private ?string $email = null;

    #[UploadableField(mapping: 'user_picture', fileNameProperty: 'pictureName')]
    private ?File $pictureFile = null;

    #[ORM\Column(nullable: true)]
    #[Groups('apiExposed')]
    private ?string $pictureName = null;

    #[ORM\OneToMany(targetEntity: Car::class, mappedBy: 'user')]
    private Collection $cars;

    #[ORM\ManyToMany(targetEntity: Event::class, mappedBy: 'attendees')]
    private Collection $participatedEvents;

    #[ORM\Column]
    #[Timestampable(on: 'create')]
    #[Groups('apiExposed')]
    private ?\DateTimeImmutable $createdAt;

    #[ORM\Column]
    #[Timestampable(on: 'update')]
    #[Groups('apiExposed')]
    private ?\DateTimeImmutable $updatedAt;

    // Getters and setters

}

Метод сериализации события:

#[OA\Response(response: 200, description: 'Returns an event given by its ID.', content: new Model(type: Event::class, groups: ['apiExposed']))]
    #[OA\Response(response: 404, description: 'No event found with this ID.')]
    #[OA\Response(response: 403, description: 'Event is secret for the current user.')]
    #[OA\Tag(name: 'Event')]
    public function getByID(EntityManagerInterface $entityManager,
                            SerializerInterface $serializer,
                            string $eventId): JsonResponse
    {
        //getting event
        $event = $entityManager -> getRepository(Event::class) -> find($eventId);

        //checking if exists
        if (!$event) {
            return new JsonResponse([
                'code' => Response::HTTP_NOT_FOUND,
                'error' => 'Unknown event'
            ], Response::HTTP_NOT_FOUND);
        }

        //check if event is secret
        if ($event -> isSecret() && $event -> getOrganizer() != $this -> getUser()) {
            return new JsonResponse([
                'code' => Response::HTTP_FORBIDDEN,
                'error' => 'Event is secret'
            ], Response::HTTP_FORBIDDEN);
        }

        return JsonResponse::fromJsonString($serializer -> serialize($event, 'json', ['groups' => 'apiExposed']));
    }

Возможно, я что-то упускаю, используя отношения ManyToMany и сериализатор, но не знаю, где.

Спасибо

Не могли бы вы также показать свой добытчик собственности участников? Если он существует, пытались ли вы определить для него #[Groups('apiExpose')]?

Guillaume 06.05.2024 17:58

Я отредактировал исходный вопрос, указав геттеры и настройки для свойства посетителей. Да, группа сериализаторов уже определена.

Cyprien Aubry 06.05.2024 21:05

Заполнена ли ваша БД правильными данными? таблицы event, user и event_user с правильными идентификаторами. Я попробовал ваш код, и он работает здесь, не могли бы вы добавить часть, в которой вы вызываете сериализатор?

Guillaume 07.05.2024 09:22

Да, когда я добавляю участника, правильные идентификаторы сохраняются в таблице event_user. Я добавляю в вопрос ту часть, где вызываю сериализатор.

Cyprien Aubry 07.05.2024 20:54

Я думаю, что это может быть связано с тем, что коллекции по умолчанию загружаются отложенно, попробуйте получить коллекцию перед сериализацией, т. е. позвоните $event->getAttendees()

Arleigh Hix 07.05.2024 22:19
symfony.com/doc/current/comComponents/… Я знаю, что если вы сбросите объект, поля коллекции будут отображаться как пустые/неинициализированные, если вы ранее не обращались к этому полю.
Arleigh Hix 07.05.2024 22:28

При сбросе переменной $event отображается и инициализируется коллекция участников. Вызов $event -> getAttendees() перед сериализацией ничего не дает.

Cyprien Aubry 07.05.2024 22:38
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
1
7
83
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Использование JMSSerializerBundle, похоже, работает нормально с этими отношениями. Возможно, JMS поддерживает отношения ManyToMany лучше, чем сериализатор Symfony.

Другие вопросы по теме