Symfony 6 & VichUploaderBundle : "default" UploadedFile - symfony

I'm learning Symfony by working on a personal project. I'm using Symfony 6.1.
I have a page containing two forms :
the "main" form ($bookForm) deals the Book entity and persist it to the database
the second one ($isbnForm) takes a string and search the Google Books API.
When submitting the second form ($isbnForm), the page is reloaded and $bookForm is preloaded with data.
Here is the code of the BookController :
#[Route('/create', name: 'create', methods: ['GET', 'POST'])]
public function new(
Request $request,
BookshelfRepository $bookshelfRepository,
AuthorRepository $authorRepository,
PublisherRepository $publisherRepository,
BookRepository $bookRepository
): Response {
$this->denyAccessUnlessGranted('edit', $this->getUser());
$book = new Book();
// Retrieving the Bookshelf passed along the request
// and associating it to the Book
if ($request->query->get('bksid')) {
$bookshelf = $bookshelfRepository->findOneBy(['ulid' => $request->query->get('bksid')]);
$book->setBookshelf($bookshelf);
}
// Dealing with the ISBN form
$isbnForm = $this->createForm(IsbnType::class);
$isbnForm->handleRequest($request);
if ($isbnForm->isSubmitted() && $isbnForm->isValid()) {
$isbnTools = new IsbnTools();
$isbn = $isbnTools->format($isbnForm->getData()['isbn']);
$book->setIsbn($isbn);
// Getting book's details from the Google Books API using the ISBN
$gbapi = new GoogleBooksApiUtils();
$details = $gbapi->gettingVolumeInfoByIsbn($isbn);
$book->setTitle($details->getTitle());
$book->setSubtitle($details->getSubtitle());
$book->setDescription($details->getDescription());
$book->setPages($details->getPageCount());
$book->setPublicationDate(substr($details->getPublishedDate(), 0, 4));
if ($details->getPublisher()) {
$publisher = $publisherRepository->findOneBy(['name' => $details->getPublisher()]);
if (!$publisher) {
$publisher = new Publisher();
$publisher->setName($details->getPublisher());
$publisherRepository->save($publisher, true);
}
$book->setPublisher($publisher);
}
foreach ($details->getAuthors() ?? [] as $dga) {
$author = $authorRepository->findOneBy(['name' => $dga]);
if (!$author) {
$author = new Author();
$author->setName($dga);
$authorRepository->save($author, true);
}
$book->addAuthor($author);
}
}
// Dealing with the main form, dealing with the Book entity
$bookForm = $this->createForm(BookType::class, $book);
$bookForm->handleRequest($request);
if ($bookForm->isSubmitted() && $bookForm->isValid()) {
$bookRepository->save($book, true);
return $this->redirectToRoute('bks_book_view', [
'ulid' => $book->getUlid(),
], Response::HTTP_SEE_OTHER);
}
return $this->renderForm('book/create.html.twig', [
'isbn_form' => $isbnForm,
'form' => $bookForm,
'book' => $book
]);
}
It works nicely until I tried to submit the thumbnail get from the Google Books API.
I've installed the VichUploaderBundle, set up a mapping, modified my Book entity accordingly to the documentation.
vich_uploader:
db_driver: orm
metadata:
type: attribute
mappings:
books:
uri_prefix: /uploads/books
upload_destination: '%kernel.project_dir%/public/uploads/books'
namer: Vich\UploaderBundle\Naming\SmartUniqueNamer
inject_on_load: false
delete_on_update: true
delete_on_remove: true
<?php
namespace App\Entity;
use App\Repository\BookRepository;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Uid\Ulid;
use Vich\UploaderBundle\Mapping\Annotation\Uploadable;
use Vich\UploaderBundle\Mapping\Annotation\UploadableField;
#[ORM\Entity(repositoryClass: BookRepository::class)]
#[ORM\HasLifecycleCallbacks]
#[Uploadable]
class Book
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $title = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $subtitle = null;
#[ORM\ManyToMany(targetEntity: Author::class, inversedBy: 'books')]
private Collection $authors;
#[ORM\ManyToOne(inversedBy: 'books')]
#[ORM\JoinColumn(nullable: false)]
private ?Publisher $publisher = null;
#[ORM\Column(length: 4, nullable: true)]
private ?string $publication_date = null;
#[ORM\Column(type: Types::TEXT, nullable: true)]
private ?string $description = null;
#[ORM\Column(length: 13, nullable: true)]
private ?string $isbn = null;
#[ORM\Column(nullable: true)]
private ?int $pages = null;
#[ORM\Column(type: 'ulid')]
private ?Ulid $ulid = null;
#[ORM\ManyToOne(inversedBy: 'books')]
#[ORM\JoinColumn(nullable: false)]
private ?Bookshelf $bookshelf = null;
#[UploadableField(mapping: 'books', fileNameProperty: 'imageName', size: 'imageSize')]
private ?File $imageFile = null;
#[ORM\Column(type: 'string', nullable: true)]
private ?string $imageName = null;
#[ORM\Column(type: 'integer', nullable: true)]
private ?int $imageSize = null;
#[ORM\Column(type: 'datetime')]
private ?\DateTimeInterface $updatedAt = null;
public function __construct()
{
$this->authors = new ArrayCollection();
$this->updatedAt = new DateTime();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getSubtitle(): ?string
{
return $this->subtitle;
}
public function setSubtitle(?string $subtitle): self
{
$this->subtitle = $subtitle;
return $this;
}
/**
* #return Collection<int, Author>
*/
public function getAuthors(): Collection
{
return $this->authors;
}
public function addAuthor(Author $author): self
{
if (!$this->authors->contains($author)) {
$this->authors->add($author);
}
return $this;
}
public function removeAuthor(Author $author): self
{
$this->authors->removeElement($author);
return $this;
}
public function getPublisher(): ?Publisher
{
return $this->publisher;
}
public function setPublisher(?Publisher $publisher): self
{
$this->publisher = $publisher;
return $this;
}
public function getPublicationDate(): ?string
{
return $this->publication_date;
}
public function setPublicationDate(?string $publication_date): self
{
$this->publication_date = $publication_date;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
public function getIsbn(): ?string
{
return $this->isbn;
}
public function setIsbn(?string $isbn): self
{
$this->isbn = $isbn;
return $this;
}
public function getPages(): ?int
{
return $this->pages;
}
public function setPages(?int $pages): self
{
$this->pages = $pages;
return $this;
}
public function getUlid(): ?Ulid
{
return $this->ulid;
}
#[ORM\PrePersist]
public function setUlid(): void
{
$this->ulid = new Ulid();
}
public function getBookshelf(): ?Bookshelf
{
return $this->bookshelf;
}
public function setBookshelf(?Bookshelf $bookshelf): self
{
$this->bookshelf = $bookshelf;
return $this;
}
/**
* If manually uploading a file (i.e. not using Symfony Form) ensure an instance
* of 'UploadedFile' is injected into this setter to trigger the update. If this
* bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
* must be able to accept an instance of 'File' as the bundle will inject one here
* during Doctrine hydration.
*
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile|null $imageFile
*/
public function setImageFile(?File $imageFile = null): void
{
$this->imageFile = $imageFile;
if (null !== $imageFile) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->updatedAt = new \DateTimeImmutable();
}
}
public function getImageFile(): ?File
{
return $this->imageFile;
}
public function setImageName(?string $imageName): void
{
$this->imageName = $imageName;
}
public function getImageName(): ?string
{
return $this->imageName;
}
public function setImageSize(?int $imageSize): void
{
$this->imageSize = $imageSize;
}
public function getImageSize(): ?int
{
return $this->imageSize;
}
}
Then I modify my BookController in order to add the imageFile to the Book entity :
#[Route('/create', name: 'create', methods: ['GET', 'POST'])]
public function new(
Request $request,
BookshelfRepository $bookshelfRepository,
AuthorRepository $authorRepository,
PublisherRepository $publisherRepository,
BookRepository $bookRepository
): Response {
$this->denyAccessUnlessGranted('edit', $this->getUser());
$book = new Book();
// Retrieving the Bookshelf passed along the request
// and associating it to the Book
if ($request->query->get('bksid')) {
$bookshelf = $bookshelfRepository->findOneBy(['ulid' => $request->query->get('bksid')]);
$book->setBookshelf($bookshelf);
}
// Dealing with the ISBN form
$isbnForm = $this->createForm(IsbnType::class);
$isbnForm->handleRequest($request);
if ($isbnForm->isSubmitted() && $isbnForm->isValid()) {
$isbnTools = new IsbnTools();
$isbn = $isbnTools->format($isbnForm->getData()['isbn']);
$book->setIsbn($isbn);
// Getting book's details from the Google Books API using the ISBN
$gbapi = new GoogleBooksApiUtils();
$details = $gbapi->gettingVolumeInfoByIsbn($isbn);
$book->setTitle($details->getTitle());
$book->setSubtitle($details->getSubtitle());
$book->setDescription($details->getDescription());
$book->setPages($details->getPageCount());
$book->setPublicationDate(substr($details->getPublishedDate(), 0, 4));
if ($details->getPublisher()) {
$publisher = $publisherRepository->findOneBy(['name' => $details->getPublisher()]);
if (!$publisher) {
$publisher = new Publisher();
$publisher->setName($details->getPublisher());
$publisherRepository->save($publisher, true);
}
$book->setPublisher($publisher);
}
foreach ($details->getAuthors() ?? [] as $dga) {
$author = $authorRepository->findOneBy(['name' => $dga]);
if (!$author) {
$author = new Author();
$author->setName($dga);
$authorRepository->save($author, true);
}
$book->addAuthor($author);
}
$thumb = $details->getImageLinks()->getThumbnail() ?? null;
if ($thumb) {
$path = 'uploads/books';
$filename = 'gbapi_' . $book->getIsbn() . '.jpg';
file_put_contents("$path/$filename", file_get_contents($thumb));
$file = new UploadedFile("$path/$filename", $filename, 'image/jpeg', null, false);
$book->setImageFile($file);
$book->setImageName($file->getFilename());
$book->setImageSize($file->getSize());
}
}
// Dealing with the main form, dealing with the Book entity
$bookForm = $this->createForm(BookType::class, $book);
$bookForm->handleRequest($request);
if ($bookForm->isSubmitted() && $bookForm->isValid()) {
$bookRepository->save($book, true);
return $this->redirectToRoute('bks_book_view', [
'ulid' => $book->getUlid(),
], Response::HTTP_SEE_OTHER);
}
return $this->renderForm('book/create.html.twig', [
'isbn_form' => $isbnForm,
'form' => $bookForm,
'book' => $book
]);
}
When I submit the $isbnForm, the data are well passed to the Book entity, and the image is displayed inside the $bookForm. But when I submit it, the imageFile (imageSize and imageName fields) are not persisted to the database.
I've tried to add the UploadedFile to the $request (something like that : $request->files->set('book', ['imageFile' => ['file' => $file]])) but without success...
I have been trying for two days to solve this issue and I'm not even sure I've identified clearly the issue...
Thanks in advance for any lead :-)

Related

How to insert into Many Many junction table

I'm new on symfony and i would like to understand how to add data on my junction table.
I got Mission entity and User Entity.
After clicking on one button i want to add the current idmission and iduser to the junction table named mission_user.
How can i do ?
This is my entity mission :
<?php
namespace App\Entity;
use App\Repository\MissionRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: MissionRepository::class)]
#[ORM\Table(name: 'mission')]
class Mission
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $titlemission = null;
#[ORM\Column(length: 255)]
private ?string $descriptionmission = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
private ?\DateTimeInterface $onsetdate = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
private ?\DateTimeInterface $deadline = null;
#[ORM\Column]
private ?int $budgetmission = null;
/*#[ORM\Column(length: 255)]
private ?string $codeapemission = null;*/
#[ORM\Column(length: 255)]
private ?string $prioritymission = null;
#[ORM\ManyToMany(targetEntity: Tag::class, inversedBy: 'missions', cascade: ['persist'])]
#[ORM\JoinTable(name: 'mission_tag')]
#[ORM\JoinColumn(name: 'mission_id',referencedColumnName: 'id')]
#[ORM\InverseJoinColumn(name:'tag_id', referencedColumnName: 'id')] //tag_id qui est couplé avec id de tag
private Collection $tags;
#[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'missions', cascade: ['persist'], fetch:"EAGER")]
#[ORM\JoinColumn(nullable: false)]
private User $iduser;
#[ORM\ManyToMany(targetEntity: User::class, inversedBy: 'missionsassign')]
#[ORM\JoinTable(name: 'mission_user')]
#[ORM\JoinColumn(name: 'mission_id',referencedColumnName: 'id')]
#[ORM\InverseJoinColumn(name:'user_id', referencedColumnName: 'id')]
private Collection $idmissionassign;
#[ORM\Column(length: 100)]
private ?string $remote = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
private ?\DateTimeInterface $datecreation = null;
public function __construct()
{
$this->tags = new ArrayCollection();
$this->idmissionassign = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitlemission(): ?string
{
return $this->titlemission;
}
public function setTitlemission(string $titlemission): self
{
$this->titlemission = $titlemission;
return $this;
}
public function getDescriptionmission(): ?string
{
return $this->descriptionmission;
}
public function setDescriptionmission(string $descriptionmission): self
{
$this->descriptionmission = $descriptionmission;
return $this;
}
public function getOnsetdate(): ?\DateTimeInterface
{
return $this->onsetdate;
}
public function setOnsetdate(\DateTimeInterface $onsetdate): self
{
$this->onsetdate = $onsetdate;
return $this;
}
public function getDeadline(): ?\DateTimeInterface
{
return $this->deadline;
}
public function setDeadline(\DateTimeInterface $deadline): self
{
$this->deadline = $deadline;
return $this;
}
public function getBudgetmission(): ?int
{
return $this->budgetmission;
}
public function setBudgetmission(int $budgetmission): self
{
$this->budgetmission = $budgetmission;
return $this;
}
/*public function getCodeapemission(): ?string
{
return $this->codeapemission;
}
public function setCodeapemission(string $codeapemission): self
{
$this->codeapemission = $codeapemission;
return $this;
}*/
public function getPrioritymission(): ?string
{
return $this->prioritymission;
}
public function setPrioritymission(string $prioritymission): self
{
$this->prioritymission = $prioritymission;
return $this;
}
/**
* #return Collection<int, tag>
*/
public function getIdtagmissionassign(): Collection
{
return $this->tags;
}
public function addIdtagmissionassign(tag $tags): self
{
if (!$this->tags->contains($tags)) {
$this->tags->add($tags);
}
return $this;
}
public function removeIdtagmissionassign(tag $tags): self
{
$this->tags->removeElement($tags);
return $this;
}
public function getIduser(): ?user
{
return $this->iduser;
}
public function setIduser(?user $iduser): self
{
$this->iduser = $iduser;
return $this;
}
/**
* #return Collection<int, user>
*/
public function getIdmissionassign(): Collection
{
return $this->idmissionassign;
}
public function addIdmissionassign(user $idmissionassign): self
{
if (!$this->idmissionassign->contains($idmissionassign)) {
$this->idmissionassign->add($idmissionassign);
}
return $this;
}
public function removeIdmissionassign(user $idmissionassign): self
{
$this->idmissionassign->removeElement($idmissionassign);
return $this;
}
public function getRemote(): ?string
{
return $this->remote;
}
public function setRemote(string $remote): self
{
$this->remote = $remote;
return $this;
}
public function __toString(){
return $this->titlemission;
}
public function getDatecreation(): ?\DateTimeInterface
{
return $this->datecreation;
}
public function setDatecreation(\DateTimeInterface $datecreation): self
{
$this->datecreation = $datecreation;
return $this;
}
}
This is my user Entity :
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\Table(name: 'user')]
#[UniqueEntity(fields: ['email'], message: 'Un compte existe déjà avec cette adresse email')]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 180, unique: true)]
private ?string $email = null;
#[ORM\Column]
private array $roles = [];
/**
* #var string The hashed password
*/
#[ORM\Column]
private ?string $password = null;
#[ORM\Column(length: 100)]
private ?string $name = null;
#[ORM\Column(length: 100)]
private ?string $firstname = null;
#[ORM\Column(length: 150)]
private ?string $company = null;
#[ORM\Column(type: 'boolean')]
private $isVerified = false;
#[ORM\Column(length: 255)]
private ?string $companyadress = null;
#[ORM\OneToMany(mappedBy: 'iduser', targetEntity: Mission::class)]
private Collection $missions;
#[ORM\OneToMany(mappedBy: 'iduser', targetEntity: Service::class)]
private Collection $services;
#[ORM\ManyToMany(targetEntity: Service::class, mappedBy: 'idserviceassign')]
private Collection $servicesassign;
#[ORM\ManyToMany(targetEntity: Mission::class, mappedBy: 'idmissionassign')]
#[ORM\JoinTable(name: 'mission_user')]
#[ORM\JoinColumn(name: 'user_id',referencedColumnName: 'id')]
#[ORM\InverseJoinColumn(name:'mission_id', referencedColumnName: 'id')]
private Collection $missionsassign;
public function __construct()
{
$this->missions = new ArrayCollection();
$this->services = new ArrayCollection();
$this->servicesassign = new ArrayCollection();
$this->missionsassign = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->email;
}
/**
* #see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'USER_DISCOVER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #see PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getFirstname(): ?string
{
return $this->firstname;
}
public function setFirstname(string $firstname): self
{
$this->firstname = $firstname;
return $this;
}
public function getCompany(): ?string
{
return $this->company;
}
public function setCompany(string $company): self
{
$this->company = $company;
return $this;
}
public function isVerified(): bool
{
return $this->isVerified;
}
public function setIsVerified(bool $isVerified): self
{
$this->isVerified = $isVerified;
return $this;
}
public function getCompanyadress(): ?string
{
return $this->companyadress;
}
public function setCompanyadress(string $companyadress): self
{
$this->companyadress = $companyadress;
return $this;
}
public function __toString(){
return $this->name;
}
/**
* #return Collection<int, Mission>
*/
public function getMissions(): Collection
{
return $this->missions;
}
public function addMission(Mission $missions): self
{
if (!$this->missions->contains($missions)) {
$this->missions->add($missions);
$missions->setIduser($this);
}
return $this;
}
public function removeMission(Mission $missions): self
{
if ($this->missions->removeElement($missions)) {
// set the owning side to null (unless already changed)
if ($missions->getIduser() === $this) {
$missions->setIduser(null);
}
}
return $this;
}
/**
* #return Collection<int, Service>
*/
public function getServices(): Collection
{
return $this->services;
}
public function addService(Service $service): self
{
if (!$this->services->contains($service)) {
$this->services->add($service);
$service->setIduser($this);
}
return $this;
}
public function removeService(Service $service): self
{
if ($this->services->removeElement($service)) {
// set the owning side to null (unless already changed)
if ($service->getIduser() === $this) {
$service->setIduser(null);
}
}
return $this;
}
/**
* #return Collection<int, Service>
*/
public function getServicesassign(): Collection
{
return $this->servicesassign;
}
public function addServicesassign(Service $servicesassign): self
{
if (!$this->servicesassign->contains($servicesassign)) {
$this->servicesassign->add($servicesassign);
$servicesassign->addIdserviceassign($this);
}
return $this;
}
public function removeServicesassign(Service $servicesassign): self
{
if ($this->servicesassign->removeElement($servicesassign)) {
$servicesassign->removeIdserviceassign($this);
}
return $this;
}
/**
* #return Collection<int, Mission>
*/
public function getMissionsassign(): Collection
{
return $this->missionsassign;
}
public function addMissionsassign(Mission $missionsassign): self
{
if (!$this->missionsassign->contains($missionsassign)) {
$this->missionsassign->add($missionsassign);
$missionsassign->addIdmissionassign($this);
}
return $this;
}
public function removeMissionsassign(Mission $missionsassign): self
{
if ($this->missionsassign->removeElement($missionsassign)) {
$missionsassign->removeIdmissionassign($this);
}
return $this;
}
}
This is the controller i use to :
<?php
namespace App\Controller;
use App\Entity\Mission;
use App\Entity\Tag;
use App\Entity\User;
use App\Form\AddMissionFormType;
use App\Form\AddMissionUserType;
use App\Form\RegistrationFormType;
use App\Repository\MissionRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class AccueilProspectorController extends AbstractController
{
#[Route('/accueil/prospector', name: 'app_accueil_prospector')]
public function index(Request $request,ManagerRegistry $doctrine,Security $security): Response
{
//Récupération de toutes les missions.
$allmission = $doctrine->getManager()->getRepository(Mission::class)->findAll();
//dump($allmission[4]);
//Création du formulaire pour ajouter une mission
$form = $this->createForm(AddMissionFormType::class, new Mission)->handleRequest($request);
$missionassigment = $this->createForm(AddMissionUserType::class, new Mission)->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$mission = $form->getData();
$mission->setIduser($security->getUser());
$mission->setDatecreation(date_create());
$entityManager = $doctrine->getManager();
$entityManager->persist($mission);
$entityManager->flush();
return $this->redirectToRoute('app_accueil_prospector');
}
// if($missionassigment->isSubmitted() && $missionassigment->isValid()){
// $missionuser = $missionassigment->getData();
// $missionuser->addIdmissionassign($security->getUser());
// $entityManager = $doctrine->getManager();
// $entityManager->persist($missionuser);
// $entityManager->flush();
// }
return $this->render('accueil_prospector/index.html.twig', [
'controller_name' => 'AccueilProspectorController',
'addmissionForm' => $form->createView(),
'missionassigment' => $missionassigment->createView(),
'missionsvalues' => $allmission,
]);
}
#[Route('/accueil/prospector/1',name:'app_accueil_mission_1')]
public function Getmission1d(Request $request,ManagerRegistry $doctrine,Security $security): Response{
$mission1d = $doctrine->getManager()->getRepository(Mission::class)->selectMission1d();
$form = $this->createForm(AddMissionFormType::class, new Mission)->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$mission = $form->getData();
$mission->setIduser($security->getUser());
$mission->setDatecreation(date_create());
$entityManager = $doctrine->getManager();
$entityManager->persist($mission);
$entityManager->flush();
return $this->redirectToRoute('app_accueil_prospector');
}
//dump($mission1d);
return $this->render('accueil_prospector/index.html.twig',[
'controller_name' => 'AccueilProspectorController',
'addmissionForm' => $form->createView(),
'missionsvalues' => $mission1d,
//'mission1d' => $mission1d
]);
}
#[Route('/accueil/prospector/7',name:'app_accueil_mission_7')]
public function Getmission7d(Request $request,ManagerRegistry $doctrine,Security $security): Response{
$mission7d = $doctrine->getManager()->getRepository(Mission::class)->selectMission7d();
$form = $this->createForm(AddMissionFormType::class, new Mission)->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$mission = $form->getData();
$mission->setIduser($security->getUser());
$mission->setDatecreation(date_create());
$entityManager = $doctrine->getManager();
$entityManager->persist($mission);
$entityManager->flush();
return $this->redirectToRoute('app_accueil_prospector');
}
dump($mission7d);
return $this->render('accueil_prospector/index.html.twig',[
'controller_name' => 'AccueilProspectorController',
'addmissionForm' => $form->createView(),
'missionsvalues' => $mission7d,
//'mission1d' => $mission1d
]);
}
#[Route('/accueil/prospector/21',name:'app_accueil_mission_21')]
public function Getmission3s(Request $request,ManagerRegistry $doctrine,Security $security): Response{
$mission3s = $doctrine->getManager()->getRepository(Mission::class)->selectMission3s();
$form = $this->createForm(AddMissionFormType::class, new Mission)->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$mission = $form->getData();
$mission->setIduser($security->getUser());
$mission->setDatecreation(date_create());
$entityManager = $doctrine->getManager();
$entityManager->persist($mission);
$entityManager->flush();
return $this->redirectToRoute('app_accueil_prospector');
}
dump($mission3s);
return $this->render('accueil_prospector/index.html.twig',[
'controller_name' => 'AccueilProspectorController',
'addmissionForm' => $form->createView(),
'missionsvalues' => $mission3s,
//'mission1d' => $mission1d
]);
}
}
I don't know how to retreive the idmission by the way. Do i need to use ajax with post method or symfony can do the stuff for me ?
Many thanks
To add entries in the joining table between your two tables, you have to add your mission in your user object (or vice versa), then you have to persist the object that has been modified (in the controller):
$user->addMissionsassign($mission);
$entityManager->persist($user);
$entityManager->flush();
Or
$mission->addIdmissionassign($user);
$entityManager->persist($user);
$entityManager->flush();

How to persist a polymorphic collection with API Platform

I'm developping a quotation tool with NextJS and an API developed with Symfony and API Platform.
A Quotation has a collection of QuotationRow which is extended by three others entities : QuotationRowProduct, QuotationRowText, QuotationRowSubtotal.
Below, the entire entities:
Quotation.php
<?php
namespace App\Entity\Quotation;
use DateTime;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\Quotation\QuotationRow;
use App\Repository\QuotationRepository;
use ApiPlatform\Core\Annotation\ApiFilter;
use Doctrine\Common\Collections\Collection;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping\HasLifecycleCallbacks;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\DateFilter;
use App\Entity\Client;
use App\Entity\ClientAgency;
#[ORM\Entity(repositoryClass: QuotationRepository::class)]
#[ApiResource(
normalizationContext: [
"groups" => ["Quotation:get"]
],
denormalizationContext: [
"groups" => ["Quotation:post"]
]
)]
#[ApiFilter(DateFilter::class, properties: ['createdAt' => DateFilter::EXCLUDE_NULL])]
#[HasLifecycleCallbacks]
class Quotation
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column()]
#[Groups(["Quotation:get"])]
private ?int $id = null;
#[ORM\Column(length: 255)]
#[Groups(["Quotation:get", "Quotation:post"])]
private ?string $status = null;
#[ORM\Column]
#[Groups(["Quotation:get", "Quotation:post"])]
private ?int $validityPeriod = null;
#[ORM\Column(type: Types::TEXT, nullable: true)]
#[Groups(["Quotation:get", "Quotation:post"])]
private ?string $comment = null;
#[ORM\OneToMany(mappedBy: 'quotation', targetEntity: QuotationRow::class, orphanRemoval: true, cascade: ["all"])]
#[Groups(["Quotation:get", "Quotation:post"])]
#[ORM\OrderBy(["position" => "ASC"])]
private $quotationRows;
#[ORM\ManyToOne]
#[Groups(["Quotation:get", "Quotation:post"])]
#[Assert\NotBlank(message: "Le client est requis")]
private ?Client $client = null;
#[ORM\ManyToOne]
#[Groups(["Quotation:get", "Quotation:post"])]
private ?ClientAgency $clientAgency = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
#[Groups(["Quotation:get"])]
private ?\DateTimeInterface $createdAt = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
#[Groups(["Quotation:get"])]
private ?\DateTimeInterface $updatedAt = null;
public function __construct()
{
$this->quotationRows = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updatedAt;
}
public function setUpdatedAt(\DateTimeInterface $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
public function getStatus(): ?string
{
return $this->status;
}
public function setStatus(string $status): self
{
$this->status = $status;
return $this;
}
public function getValidityPeriod(): ?int
{
return $this->validityPeriod;
}
public function setValidityPeriod(int $validityPeriod): self
{
$this->validityPeriod = $validityPeriod;
return $this;
}
public function getComment(): ?string
{
return $this->comment;
}
public function setComment(?string $comment): self
{
$this->comment = $comment;
return $this;
}
public function getClient(): ?Client
{
return $this->client;
}
public function setClient(?Client $client): self
{
$this->client = $client;
return $this;
}
/**
* #return Collection<int, QuotationRow>
*/
public function getQuotationRows(): Collection
{
return $this->quotationRows;
}
public function addQuotationRow(QuotationRow $quotationRow): self
{
if (!$this->quotationRows->contains($quotationRow)) {
$this->quotationRows[] = $quotationRow;
$quotationRow->setQuotation($this);
}
return $this;
}
public function removeQuotationRow(QuotationRow $quotationRow): self
{
if ($this->quotationRows->removeElement($quotationRow)) {
// set the owning side to null (unless already changed)
if ($quotationRow->getQuotation() === $this) {
$quotationRow->setQuotation(null);
}
}
return $this;
}
public function getClientAgency(): ?ClientAgency
{
return $this->clientAgency;
}
public function setClientAgency(?ClientAgency $clientAgency): self
{
$this->clientAgency = $clientAgency;
return $this;
}
#[ORM\PrePersist]
public function prePersist()
{
$currDateTime = new DateTime();
$this->createdAt = $currDateTime;
$this->updatedAt = $currDateTime;
$this->status = "waiting";
}
#[ORM\PreUpdate]
public function preUpdate()
{
$this->updatedAt = new DateTime();
}
}
QuotationRow.php
<?php
namespace App\Entity\Quotation;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\Quotation\Quotation;
use App\Entity\Quotation\QuotationRowText;
use App\Repository\QuotationRowRepository;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Entity\Quotation\QuotationRowProduct;
use App\Entity\Quotation\QuotationRowSubtotal;
use Doctrine\ORM\Mapping\HasLifecycleCallbacks;
use Symfony\Component\Serializer\Annotation\Groups;
#[ORM\Entity(repositoryClass: QuotationRowRepository::class)]
#[ApiResource]
#[ORM\InheritanceType("SINGLE_TABLE")]
#[ORM\DiscriminatorColumn(name: "type", type: "string")]
#[ORM\DiscriminatorMap([
"product" => QuotationRowProduct::class,
"subtotal" => QuotationRowSubtotal::class,
"text" => QuotationRowText::class,
])]
class QuotationRow
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
#[Groups(["Quotation:get"])]
protected int $id;
#[ORM\ManyToOne(targetEntity: Quotation::class, inversedBy: 'quotationRows')]
#[ORM\JoinColumn(nullable: false)]
protected ?Quotation $quotation;
#[Groups(["Quotation:get", "Quotation:post"])]
#[ORM\Column]
protected ?int $position;
public function getId(): ?int
{
return $this->id;
}
public function getQuotation(): ?Quotation
{
return $this->quotation;
}
public function setQuotation(?Quotation $quotation): self
{
$this->quotation = $quotation;
return $this;
}
public function getPosition(): int
{
return $this->position;
}
public function setPosition(int $position)
{
$this->position = $position;
}
public function setType($type = "unknown"): self
{
$this->type = "unknown";
return $this;
}
}
QuotationRowProduct.php
<?php
namespace App\Entity\Quotation;
use App\Entity\VAT;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\QuotationRowRepository;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping\HasLifecycleCallbacks;
use Symfony\Component\Serializer\Annotation\Groups;
#[ORM\Entity(repositoryClass: QuotationRowRepository::class)]
#[ApiResource]
class QuotationRowProduct extends QuotationRow
{
const TYPE = "product";
#[ORM\Column]
#[Groups(["Quotation:get", "Quotation:post"])]
private float $preTaxPrice;
#[ORM\Column]
#[Groups(["Quotation:get", "Quotation:post"])]
private int $quantity;
#[ORM\Column(type: Types::TEXT)]
#[Groups(["Quotation:get", "Quotation:post"])]
private string $description;
#[ORM\Column]
#[Groups(["Quotation:get", "Quotation:post"])]
private float $preTaxDiscount;
#[ORM\Column]
#[Groups(["Quotation:get", "Quotation:post"])]
private float $discountRate;
#[ORM\ManyToOne(targetEntity: VAT::class)]
#[ORM\JoinColumn(nullable: true)]
#[Groups(["Quotation:get", "Quotation:post"])]
private ?VAT $vat;
#[Groups(["Quotation:get", "Quotation:post"])]
public function getType(): string
{
return self::TYPE;
}
public function getPreTaxPrice(): ?float
{
return $this->preTaxPrice;
}
public function setPreTaxPrice(float $preTaxPrice): self
{
$this->preTaxPrice = $preTaxPrice;
return $this;
}
public function getQuantity(): ?int
{
return $this->quantity;
}
public function setQuantity(int $quantity): self
{
$this->quantity = $quantity;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
public function getPreTaxDiscount(): ?float
{
return $this->preTaxDiscount;
}
public function setPreTaxDiscount(float $preTaxDiscount): self
{
$this->preTaxDiscount = $preTaxDiscount;
return $this;
}
public function getDiscountRate(): ?float
{
return $this->discountRate;
}
public function setDiscountRate(float $discountRate): self
{
$this->discountRate = $discountRate;
return $this;
}
public function getVat(): ?VAT
{
return $this->vat;
}
public function setVat(?VAT $vat): self
{
$this->vat = $vat;
return $this;
}
public function setType($type = "product"): self
{
$this->type = "product";
return $this;
}
// Utility
public function getSubtotalWithoutVAT()
{
$price = $this->preTaxPrice * $this->quantity;
return $price - ($price * $this->discountRate / 100);
}
public function getSubtotalWithVAT()
{
$price = $this->getSubtotalWithoutVAT();
return $price + ($price * $this->vat->getRate() / 100);
}
}
QuotationRowSubtotal.php
<?php
namespace App\Entity\Quotation;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\Quotation\QuotationRow;
use App\Repository\QuotationRowRepository;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping\HasLifecycleCallbacks;
use Symfony\Component\Serializer\Annotation\Groups;
#[ORM\Entity(repositoryClass: QuotationRowRepository::class)]
#[ApiResource]
class QuotationRowSubtotal extends QuotationRow
{
const TYPE = "subtotal";
#[Groups(["Quotation:get", "Quotation:post"])]
public function getType(): string
{
return self::TYPE;
}
private function getAllRows()
{
return $this->quotation->getQuotationRows();
}
/**
* Return all subtotals in the Quotation#rows list
* #return QuotationRowSubtotal[]
*/
private function getSubtotalRows()
{
$filtered = [];
foreach ($this->getAllRows() as $row) {
if ($row instanceof QuotationRowSubtotal) {
$filtered[] = $row;
}
}
return $filtered;
}
/**
* Get the previous Subtotal priority
* #return integer
*/
private function getPrevSubtotalPos()
{
// Reverse result for find more quicker the last subtotal
$subtotalRows = array_reverse($this->getSubtotalRows());
foreach ($subtotalRows as $row) {
if ($this->position > $row->getPosition()) {
return $row->getPosition();
}
}
// Return -1 if any result for the next function condition
return -1;
}
/**
* Return the Subtotal from the last Subtotal without VAT
* #return float
*/
#[Groups(["Quotation:get"])]
public function getSubtotal()
{
$lastPosition = $this->getPrevSubtotalPos();
$total = 0;
foreach ($this->getAllRows() as $row) {
if ($row->getPosition() < $this->position && $row->getPosition() > $lastPosition) {
if ($row instanceof QuotationRowProduct) {
$total += $row->getSubtotalWithoutVAT();
}
}
}
return $total;
}
/**
* Return the Subtotal from the last Subtotal with VAT
* #return float
*/
#[Groups(["Quotation:get"])]
public function getSubtotalWithVat()
{
$lastPosition = $this->getPrevSubtotalPos();
$total = 0;
foreach ($this->getAllRows() as $row) {
if ($row->getPosition() < $this->position && $row->getPosition() > $lastPosition) {
if ($row instanceof QuotationRowProduct) {
$total += $row->getSubtotalWithVAT();
}
}
}
return $total;
}
}
QuotationRowText.php
<?php
namespace App\Entity\Quotation;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\Quotation\QuotationRow;
use App\Repository\QuotationRowRepository;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping\HasLifecycleCallbacks;
use Symfony\Component\Serializer\Annotation\Groups;
#[ORM\Entity(repositoryClass: QuotationRowRepository::class)]
#[ApiResource]
class QuotationRowText extends QuotationRow
{
const TYPE = "text";
#[ORM\Column(type: Types::TEXT)]
#[Groups(["Quotation:get", "Quotation:post"])]
private string $text;
#[Groups(["Quotation:get", "Quotation:post"])]
public function getType(): string
{
return self::TYPE;
}
public function getText(): ?string
{
return $this->text;
}
public function setText(string $text): self
{
$this->text = $text;
return $this;
}
}
As you can see above in Quotation.php, the collection of QuotationRow is persisted by the Quotation entity.
The probleme is when I do a POST request to the API on /api/quotations endpoint, the API throw an SQL error : "column 'type' cannot be null", but 'type' is the discriminator column.
So I tried somethings before write this topic:
Send 'type' in the body request to the API: it's ignored by the API
Map $type as a protected property of QuotationRow : Makes an error because column 'type' already exsists in table 'quotation_row'.
I would like persist all QuotationRow from the Quotation API endpoint . Is there a way to persist a polymorphic collection in OneToMany context from the owner side ? Maybe by wrting a custom API Controller ?
Note that: there is no problem to update QuotationRow when they already exist in database, only to persist them.

Symfony simple form filter question only allow unique entries

I'm stuck for a while now in trying to get the following scenario to work.
I have a registration system for a sporting team.
Each team can register for any available contest. The problem is that each team is only allowed once to register on a contest. I simply want to filter the teams that are already registered for the current selected contest. I'm using the Symfony framework together with Doctrine ORM and the symfony form system.
I tried to create a custom function inside the teamRepository to only get the teams that are not registered for a given contest. But i can't seem to find a way to gt this to work.
I'm using 3 entities for this: User, Team, Registration, Contest
The registration entity holds all te submitted registrations with a relation to the Contest and the User (team). I hope that someone can help me here. For reference, this is my registration entity:
class Registrations
{
use TimestampableEntity;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\ManyToOne(targetEntity: Team::class, inversedBy: 'registrations')]
private $Team;
#[ORM\ManyToMany(targetEntity: Dancers::class, inversedBy: 'registrations', fetch: 'EAGER')]
private $Dancers;
#[ORM\Column(type: 'text', nullable: true)]
private $Comments;
#[ORM\ManyToOne(targetEntity: Contest::class, fetch: 'EAGER', inversedBy: 'registrations')]
#[ORM\JoinColumn(nullable: false)]
private $Contest;
#[ORM\Column(type: 'string', length: 255)]
#[Gedmo\Blameable(on: 'create')]
private $RegisteredBy;
#[ORM\OneToMany(mappedBy: 'Registration', targetEntity: Orders::class)]
private $orders;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private $MusicFile;
public function __construct()
{
$this->Dancers = new ArrayCollection();
$this->orders = new ArrayCollection();
}
public function __toString()
{
return $this->id;
}
public function getId(): ?int
{
return $this->id;
}
public function getTeam(): ?Team
{
return $this->Team;
}
public function setTeam(?Team $Team): self
{
$this->Team = $Team;
return $this;
}
/**
* #return Collection|Dancers[]
*/
public function getDancers(): Collection
{
return $this->Dancers;
}
public function addDancer(Dancers $dancer): self
{
if (!$this->Dancers->contains($dancer)) {
$this->Dancers[] = $dancer;
}
return $this;
}
public function removeDancer(Dancers $dancer): self
{
$this->Dancers->removeElement($dancer);
return $this;
}
public function getComments(): ?string
{
return $this->Comments;
}
public function setComments(?string $Comments): self
{
$this->Comments = $Comments;
return $this;
}
public function getRegisteredBy(): ?string
{
return $this->RegisteredBy;
}
public function setRegisteredBy(?string $RegisteredBy): self
{
$this->RegisteredBy = $RegisteredBy;
return $this;
}
public function getContest(): ?Contest
{
return $this->Contest;
}
public function setContest(?Contest $Contest): self
{
$this->Contest = $Contest;
return $this;
}
/**
* #return Collection<int, Orders>
*/
public function getOrders(): Collection
{
return $this->orders;
}
public function addOrder(Orders $order): self
{
if (!$this->orders->contains($order)) {
$this->orders[] = $order;
$order->setRegistration($this);
}
return $this;
}
public function removeOrder(Orders $order): self
{
if ($this->orders->removeElement($order)) {
// set the owning side to null (unless already changed)
if ($order->getRegistration() === $this) {
$order->setRegistration(null);
}
}
return $this;
}
public function getMusicFile(): ?string
{
return $this->MusicFile;
}
public function setMusicFile(string $MusicFile): self
{
$this->MusicFile = $MusicFile;
return $this;
}
}
This is the repo function as mentioned:
public function getTeamsNotRegistered($organisation, $contest)
{
return $this->createQueryBuilder('t')
->leftJoin('t.Registrations', 'r')
->andWhere('t.Organisation = :org')
->andWhere('r.Contest = :contest')
->setParameter('org', $organisation)
->setParameter('contest', $contest)
->getQuery()
->getResult();
}

How to update an entity related to another one after inserting new data?

I'm almost getting desperate. I have made this question few times hoping for new answers to come, but all of them did not help me. I have tried many times but I only get errors, based on these answers, but no success. I'm stuck at it for more than a week, and I have a limited time.
I have a table called Client, related to Budget. First, showing the Client entity:
class Client
{
private $id;
private $name;
private $phone;
private $email;
private $streetName;
private $district;
private $number;
private $city;
private $zipCode;
private $budget;
public function __toString()
{
return $this->name;
}
public function getId()
{
return $this->id;
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function getName()
{
return $this->name;
}
public function setPhone($phone)
{
$this->phone = $phone;
return $this;
}
public function getPhone()
{
return $this->phone;
}
public function setEmail($email)
{
$this->email = $email;
return $this;
}
public function getEmail()
{
return $this->email;
}
public function setStreetName($streetName)
{
$this->streetName = $streetName;
return $this;
}
public function getStreetName()
{
return $this->streetName;
}
public function setDistrict($district)
{
$this->district = $district;
return $this;
}
public function getDistrict()
{
return $this->district;
}
public function setNumber($number)
{
$this->number = $number;
return $this;
}
public function getNumber()
{
return $this->number;
}
public function setCity($city)
{
$this->city = $city;
return $this;
}
public function getCity()
{
return $this->city;
}
public function setZipCode($zipCode)
{
$this->zipCode = $zipCode;
return $this;
}
public function getZipCode()
{
return $this->zipCode;
}
function setBudget($budget)
{
$this->budget = $budget;
}
function getBudget()
{
return $this->budget;
}
}
And now, the Budget entity:
class Budget
{
private $id;
private $clientId;
private $address;
private $installments;
private $checkDays;
private $dateStart;
private $dateCreated;
private $totalValue;
public function __construct()
{
$this->dateCreated = new \DateTime();
}
public function getId()
{
return $this->id;
}
public function setClientId(Client $clientId)
{
$this->clientId = $clientId;
return $this;
}
public function getClientId()
{
return $this->clientId;
}
public function setAddress($address)
{
$this->address = $address;
return $this;
}
public function getAddress()
{
return $this->address;
}
public function setInstallments($installments)
{
$this->installments = $installments;
return $this;
}
public function getInstallments()
{
return $this->installments;
}
public function setCheckDays($checkDays)
{
$this->checkDays = $checkDays;
return $this;
}
public function getCheckDays()
{
return $this->checkDays;
}
public function setDateStart($dateStart)
{
$this->dateStart = $dateStart;
return $this;
}
public function getDateStart()
{
return $this->dateStart;
}
public function setDateCreated($dateCreated)
{
$this->dateCreated = $dateCreated;
return $this;
}
public function getDateCreated()
{
return $this->dateCreated;
}
public function setTotalValue($totalValue)
{
$this->totalValue = $totalValue;
return $this;
}
public function getTotalValue()
{
return $this->totalValue;
}
}
Fine. Now, their relationship. First, the Client.orm.yml:
CDG\PanelBundle\Entity\Client:
type: entity
table: client
repositoryClass: CDG\PanelBundle\Entity\ClientRepository
id:
id:
type: integer
id: true
generator:
strategy: AUTO
fields:
name:
type: string
length: 255
phone:
type: string
length: 255
email:
type: string
length: 255
streetName:
type: string
length: 255
column: street_name
district:
type: string
length: 255
number:
type: string
length: 255
city:
type: string
length: 255
zipCode:
type: string
length: 255
column: zip_code
oneToMany:
budget:
targetEntity: Budget
mappedBy: clientId
lifecycleCallbacks: { }
And now, the Budget.orm.yml:
CDG\PanelBundle\Entity\Budget:
type: entity
table: budget
repositoryClass: CDG\PanelBundle\Entity\BudgetRepository
id:
id:
type: integer
id: true
generator:
strategy: AUTO
fields:
address:
type: string
length: 255
installments:
type: integer
checkDays:
type: integer
column: check_days
dateStart:
type: datetime
column: date_start
dateCreated:
type: datetime
column: date_created
totalValue:
type: decimal
column: total_value
nullable: true
manyToOne:
clientId:
targetEntity: Client
inversedBy: budget
joinColumn:
name: client_id
referencedColumnName: id
lifecycleCallbacks: { }
So far so good. Explaining, I must update the field budget from Client entity right after inserting new data into Budget. For this, I have tried MANY different ways, erros, but sadly I don't remember all of them. If you feel like, please check my previous questions here, here and if you are Brazilian, here, to see the how I have tried to solve this.
Below is my current BudgetController.php and its addAction() method, used to persist the form data into Budget table. The way it is now gives no errors, but the budget field from Client is not updated.
public function addAction(Request $request)
{
$form = $this->createForm(new BudgetType());
$manager = $this->getDoctrine()->getManager();
$Client = $manager->getRepository('PanelBundle:Client');
$Budget = $manager->getRepository('PanelBundle:Budget');
if ($request->getMethod() == 'POST') {
$form->handleRequest($request);
if ($form->isValid()) {
$manager->persist($form->getData());
$manager->flush();
$Client = $manager->find('PanelBundle:Client', $form['client_id']->getData()->getId());
$Client->setBudget($manager->getRepository('PanelBundle:Budget')->getLastId());
$this->addFlash('success', 'Novo orçamento adicionado');
return $this->redirect($this->generateUrl('panel_budgets'));
}
}
return $this->render('PanelBundle:Budget:add.html.twig', array(
'clients' => $Client->findAll(),
'form' => $form->createView()
));
}
If I try to persist the $Client and flush(), I get the error:
Warning: spl_object_hash() expects parameter 1 to be object, string given
I have written a function addBudgetToClient() in my BudgetRepository.php, that has two parameters, one for the selected Client, and other for the recently added Budget ids. Here it is:
public function addBudgetToClient($clientId, $budgetId)
{
$qb = $this->createQueryBuilder('PanelBundle:Client')
->update('PanelBundle:Client', 'c')
->set('c.budget', $budgetId)
->where('c.id = ' . $clientId)
->getQuery()
->execute();
}
Then, in my Controller, if I try:
$Budget->addBudgetToClient($form['client_id']->getData()->getId(), $Budget->getLastId());
I get the error:
Semantical Error] line 0, col 34 near 'budget = 4 WHERE': Error: Invalid PathExpression. StateFieldPathExpression or SingleValuedAssociationField expected.
[2/2] QueryException: [Semantical Error] line 0, col 34 near 'budget = 4 WHERE': Error: Invalid PathExpression. StateFieldPathExpression or SingleValuedAssociationField expected. +
[1/2] QueryException: UPDATE PanelBundle:Client c SET c.budget = 4 WHERE c.id = 3 +
The SQL seems right, then why?
It is many errors, many ways, but zero success. Thank you.
EDITED
As requested, my getLastId():
public function getLastId()
{
return $this->createQueryBuilder('b')
->select('MAX(b.id)')
->getQuery()
->getSingleScalarResult();
}
You are storing your entity in a wrong way. Instead, try this:
$budget = new Budget();
$form = $this->createForm(new BudgetType(), $budget, array("method" => "POST");
$manager = $this->getDoctrine()->getManager();
$form->handleRequest($request);
if ($form->isValid()) {
$form->handleRequest($request);
$manager->persist($budget);
$client = $manager->find('PanelBundle:Client', $form['client_id']->getData()->getId());
// do something if was not found
$client->setBudget($budget);
$manager->flush();
$this->addFlash('success', 'Novo orçamento adicionado');
return $this->redirect($this->generateUrl('panel_budgets'));
}

modify the entity object wrapped by a Symfony 2.0 FormBuilder

Here's my code:
$form = $this->createFormBuilder($signupAttempt)
->add('email', 'text', array("label" => "your email:"))
->add('password', 'password', array("label" => "your password:"))
->add('passwordRepeat', 'password', array("label" => "repeat password:"))
->getForm();
if ($request->isMethod('POST')) {
$form->bindRequest($request);
$attempt = $form->getData();
$this->changeSomeAttributesOfSignupAttempt($attempt); // this does not work
if ($form->isValid()) { // this is not taking into account the modification made inside changeSomeAttributesOfSignupAttempt
return new Response("data provided are valid - u signiged up!");
}
}
See my problem? I'd like to make some changes to the entity and expect the form to be aware of such changes. Unfortunately it looks like the changes that I make are not perceived and, as a result, the rules defined in validaition.xml for the class SignupAttempt are not fulfilled.
here's my validation.xml for the entity SignupAttempt:
<getter property="emailInUseAlready">
<constraint name="False">
<option name="message">signup_attempt.whole.email_in_use</option>
</constraint>
</getter>
and the entity class itself:
class SignupAttempt {
protected $id = null;
protected $email = null;
protected $password = null;
protected $passwordRepeat = null;
protected $emailInUseAlredy = true;
public function __construct($email = null, $password = null, $passwordReapeat = null) {
$this->email = $email;
$this->password = $password;
$this->passwordRepeat = $passwordReapeat;
}
public function getId() {
return $this->id;
}
public function setId($id) {
$this->id = $id;
}
public function getEmail() {
return $this->email;
}
public function setEmail($email) {
$this->email = $email;
}
public function getPassword() {
return $this->password;
}
public function setPassword($password) {
$this->password = $password;
}
public function getPasswordRepeat() {
return $this->passwordRepeat;
}
public function setPasswordRepeat($passwordRepeat) {
$this->passwordRepeat = $passwordRepeat;
}
public function setEmailInUseAlready($bool) {
$this->emailInUseAlredy = $bool;
}
public function isEmailInUseAlready() {
return $this->emailInUseAlredy;
}
public function isSecondPasswordMatching() {
return $this->password === $this->passwordRepeat;
}
public function import(array $data) {
throw new \RuntimeException("implement this");
}
}
any idea?
When one performs $form->isValid(), the returned (boolean) value is in fact pre-evaluated at the time when the request was bound to the form.
As a result, changing values of the entity returned by $form->getData() is totally useless as validation happens beforehand and on the initial values held by the entity object when it is originally created.

Resources