У меня есть два класса: один для управления событиями, а другой — для пользователей. Пользователи могут участвовать в мероприятиях. Когда они участвуют, они становятся «Посетителями». Проблема в том, что когда я сериализую событие с участниками, ответ содержит все поля, кроме поля участников.
Класс событий:
<?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 и сериализатор, но не знаю, где.
Спасибо
Я отредактировал исходный вопрос, указав геттеры и настройки для свойства посетителей. Да, группа сериализаторов уже определена.
Заполнена ли ваша БД правильными данными? таблицы event, user и event_user с правильными идентификаторами. Я попробовал ваш код, и он работает здесь, не могли бы вы добавить часть, в которой вы вызываете сериализатор?
Да, когда я добавляю участника, правильные идентификаторы сохраняются в таблице event_user. Я добавляю в вопрос ту часть, где вызываю сериализатор.
Я думаю, что это может быть связано с тем, что коллекции по умолчанию загружаются отложенно, попробуйте получить коллекцию перед сериализацией, т. е. позвоните $event->getAttendees()
При сбросе переменной $event отображается и инициализируется коллекция участников. Вызов $event -> getAttendees() перед сериализацией ничего не дает.






Использование JMSSerializerBundle, похоже, работает нормально с этими отношениями. Возможно, JMS поддерживает отношения ManyToMany лучше, чем сериализатор Symfony.
Не могли бы вы также показать свой добытчик собственности участников? Если он существует, пытались ли вы определить для него #[Groups('apiExpose')]?