My filename is not persisted in my database with Vichuploader - symfony

I tried to upload images in my database with Symfony 5 and Vichuploader bundle but my filename is not persisted in my database and I don't know why.
This the error that I have : An exception occurred while executing a query: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'filename' cannot be null
This is my entity Person.php :
namespace App\Entity;
use App\Repository\PersonRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity(repositoryClass=PersonRepository::class)
*/
class Person
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\Column(type="datetime")
*/
private $birthDate;
/**
* #ORM\ManyToMany(targetEntity=Movie::class, mappedBy="writers")
*/
public $writedMovies;
/**
* #ORM\ManyToMany(targetEntity=Movie::class, mappedBy="directors")
*/
private $directedMovies;
/**
* #ORM\ManyToMany(targetEntity=Movie::class, mappedBy="producers")
*/
private $producedMovies;
/**
* #ORM\OneToMany(targetEntity=MovieActor::class, mappedBy="person", orphanRemoval=true, cascade={"persist"})
*/
private $movieActors;
/**
* #ORM\Column(type="boolean")
*/
private $sex;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $pseudo;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $deathDate;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $firstname;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $middleName;
/**
* #ORM\Column(type="text", nullable=true)
*/
private $biography;
/**
* #ORM\OneToMany(targetEntity=MovieMusicComposer::class, mappedBy="person")
*/
private $movieMusicComposers;
/**
* #ORM\OneToMany(targetEntity=AwardPerson::class, mappedBy="person")
*/
private $awardPeople;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $award1;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $award2;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $award3;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $award4;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $award5;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $award6;
/**
* #ORM\Column(type="string", length=100, nullable=true)
*/
private $nationality;
/**
* #ORM\OneToMany(targetEntity=PicturePerson::class, mappedBy="person", orphanRemoval=true, cascade={"persist"})
*/
private $picturePeople;
/**
* #Assert\All({
* #Assert\Image(mimeTypes="image/jpeg")
* })
*/
private $pictureFiles;
/**
* #ORM\Column(type="datetime")
*/
private $created_at;
/**
* #ORM\Column(type="datetime")
*/
private $updated_at;
public function __construct()
{
$this->writedMovies = new ArrayCollection();
$this->movieActors = new ArrayCollection();
$this->movieMusicComposers = new ArrayCollection();
$this->awardPeoples = new ArrayCollection();
$this->directedMovies = new ArrayCollection();
$this->producedMovies = new ArrayCollection();
$this->awardPeople = new ArrayCollection();
$this->picturePeople = new ArrayCollection();
$this->created_at = new \DateTime();
$this->updated_at = new \DateTime();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getBirthDate(): ?\DateTimeInterface
{
return $this->birthDate;
}
public function setBirthDate(\DateTimeInterface $birthDate): self
{
$this->birthDate = $birthDate;
return $this;
}
/**
* #return Collection|Movie[]
*/
public function getWritedMovies(): Collection
{
return $this->writedMovies;
}
public function addWritedMovie(Movie $writedMovie): self
{
if (!$this->writedMovies->contains($writedMovie)) {
$this->writedMovies[] = $writedMovie;
}
return $this;
}
public function removeWritedMovie(Movie $writedMovie): self
{
$this->writedMovies->removeElement($writedMovie);
return $this;
}
/**
* #return Collection|MovieActor[]
*/
public function getMovieActors(): Collection
{
return $this->movieActors;
}
public function addMovieActor(MovieActor $movieActor): self
{
if (!$this->movieActors->contains($movieActor)) {
$this->movieActors[] = $movieActor;
$movieActor->setPerson($this);
}
return $this;
}
public function removeMovieActor(MovieActor $movieActor): self
{
if ($this->movieActors->removeElement($movieActor)) {
// set the owning side to null (unless already changed)
if ($movieActor->getPerson() === $this) {
$movieActor->setPerson(null);
}
}
return $this;
}
public function getSex(): ?bool
{
return $this->sex;
}
public function setSex(bool $sex): self
{
$this->sex = $sex;
return $this;
}
public function getPseudo(): ?string
{
return $this->pseudo;
}
public function setPseudo(?string $pseudo): self
{
$this->pseudo = $pseudo;
return $this;
}
public function getDeathDate(): ?\DateTimeInterface
{
return $this->deathDate;
}
public function setDeathDate(?\DateTimeInterface $deathDate): self
{
$this->deathDate = $deathDate;
return $this;
}
public function getFirstname(): ?string
{
return $this->firstname;
}
public function setFirstname(?string $firstname): self
{
$this->firstname = $firstname;
return $this;
}
public function getMiddleName(): ?string
{
return $this->middleName;
}
public function setMiddleName(string $middleName): self
{
$this->middleName = $middleName;
return $this;
}
public function getBiography(): ?string
{
return $this->biography;
}
public function setBiography(?string $biography): self
{
$this->biography = $biography;
return $this;
}
/**
* #return Collection|MovieMusicComposer[]
*/
public function getMovieMusicComposers(): Collection
{
return $this->movieMusicComposers;
}
public function addMovieMusicComposer(MovieMusicComposer $movieMusicComposer): self
{
if (!$this->movieMusicComposers->contains($movieMusicComposer)) {
$this->movieMusicComposers[] = $movieMusicComposer;
$movieMusicComposer->setPerson($this);
}
return $this;
}
public function removeMovieMusicComposer(MovieMusicComposer $movieMusicComposer): self
{
if ($this->movieMusicComposers->removeElement($movieMusicComposer)) {
// set the owning side to null (unless already changed)
if ($movieMusicComposer->getPerson() === $this) {
$movieMusicComposer->setPerson(null);
}
}
return $this;
}
/**
* #return Collection|Movie[]
*/
public function getDirectedMovies(): Collection
{
return $this->directedMovies;
}
public function addDirectedMovie(Movie $directedMovie): self
{
if (!$this->directedMovies->contains($directedMovie)) {
$this->directedMovies[] = $directedMovie;
$directedMovie->addDirector($this);
}
return $this;
}
public function removeDirectedMovie(Movie $directedMovie): self
{
if ($this->directedMovies->removeElement($directedMovie)) {
$directedMovie->removeDirector($this);
}
return $this;
}
/**
* #return Collection|Movie[]
*/
public function getProducedMovies(): Collection
{
return $this->producedMovies;
}
public function addProducedMovie(Movie $producedMovie): self
{
if (!$this->producedMovies->contains($producedMovie)) {
$this->producedMovies[] = $producedMovie;
$producedMovie->addProducer($this);
}
return $this;
}
public function removeProducedMovie(Movie $producedMovie): self
{
if ($this->producedMovies->removeElement($producedMovie)) {
$producedMovie->removeProducer($this);
}
return $this;
}
/**
* #return Collection|AwardPerson[]
*/
public function getAwardPeople(): Collection
{
return $this->awardPeople;
}
public function addAwardPerson(AwardPerson $awardPerson): self
{
if (!$this->awardPeople->contains($awardPerson)) {
$this->awardPeople[] = $awardPerson;
$awardPerson->setPerson($this);
}
return $this;
}
public function removeAwardPerson(AwardPerson $awardPerson): self
{
if ($this->awardPeople->removeElement($awardPerson)) {
// set the owning side to null (unless already changed)
if ($awardPerson->getPerson() === $this) {
$awardPerson->setPerson(null);
}
}
return $this;
}
public function getAward1(): ?string
{
return $this->award1;
}
public function setAward1(?string $award1): self
{
$this->award1 = $award1;
return $this;
}
public function getAward2(): ?string
{
return $this->award2;
}
public function setAward2(?string $award2): self
{
$this->award2 = $award2;
return $this;
}
public function getAward3(): ?string
{
return $this->award3;
}
public function setAward3(?string $award3): self
{
$this->award3 = $award3;
return $this;
}
public function getAward4(): ?string
{
return $this->award4;
}
public function setAward4(?string $award4): self
{
$this->award4 = $award4;
return $this;
}
public function getAward5(): ?string
{
return $this->award5;
}
public function setAward5(?string $award5): self
{
$this->award5 = $award5;
return $this;
}
public function getAward6(): ?string
{
return $this->award6;
}
public function setAward6(?string $award6): self
{
$this->award6 = $award6;
return $this;
}
public function getNationality(): ?string
{
return $this->nationality;
}
public function setNationality(?string $nationality): self
{
$this->nationality = $nationality;
return $this;
}
/**
* #return Collection|PicturePerson[]
*/
public function getPicturePeople(): Collection
{
return $this->picturePeople;
}
public function addPicturePerson(PicturePerson $picturePerson): self
{
if (!$this->picturePeople->contains($picturePerson)) {
$this->picturePeople[] = $picturePerson;
$picturePerson->setPerson($this);
}
return $this;
}
/**
* #return mixed
*/
public function getPictureFiles()
{
return $this->pictureFiles;
}
/**
* #param mixed $pictureFiles
* #return Person
*/
public function setPictureFiles($pictureFiles): self
{
foreach($pictureFiles as $pictureFile) {
$picture = new PicturePerson();
$picture->setImageFile($pictureFile);
$this->addPicturePerson($picture);
}
$this->pictureFiles = $pictureFiles;
return $this;
}
public function removePicturePerson(PicturePerson $picturePerson): self
{
if ($this->picturePeople->removeElement($picturePerson)) {
// set the owning side to null (unless already changed)
if ($picturePerson->getPerson() === $this) {
$picturePerson->setPerson(null);
}
}
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->created_at;
}
public function setCreatedAt(\DateTimeInterface $created_at): self
{
$this->created_at = $created_at;
return $this;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updated_at;
}
public function setUpdatedAt(\DateTimeInterface $updated_at): self
{
$this->updated_at = $updated_at;
return $this;
}
}
This is my Entity PicturePerson.php :
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Validator\Constraints as Assert;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* #ORM\Entity(repositoryClass=PicturePersonRepository::class)
*/
class PicturePerson
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var File|null
* #Assert\Image(
* mimeTypes="image/jpeg"
* )
* #Vich\UploadableField(mapping="person_image", fileNameProperty="filename")
*/
private $imageFile;
/**
* #ORM\Column(type="string", length=255)
*/
private $filename;
/**
* #ORM\ManyToOne(targetEntity=Person::class, inversedBy="picturePeople")
* #ORM\JoinColumn(nullable=false)
*/
private $person;
public function getId(): ?int
{
return $this->id;
}
public function getFilename(): ?string
{
return $this->filename;
}
public function setFilename(?string $filename): self
{
$this->filename = $filename;
return $this;
}
public function getPerson(): ?Person
{
return $this->person;
}
public function setPerson(?Person $person): self
{
$this->person = $person;
return $this;
}
/**
* #return null|File
*/
public function getImageFile(): ?File
{
return $this->imageFile;
}
/**
* #param null|File $imageFile
* #return self
*/
public function setImageFile(?File $imageFile): self
{
$this->imageFile = $imageFile;
return $this;
}
}
This is my config file vich_uploader.yaml :
db_driver: orm
mappings:
movie_image:
uri_prefix: /images/movies
upload_destination: '%kernel.project_dir%/public/images/movies'
namer: Vich\UploaderBundle\Naming\UniqidNamer
movie_video:
uri_prefix: /videos/movies
upload_destination: '%kernel.project_dir%/public/videos/movies'
namer: Vich\UploaderBundle\Naming\UniqidNamer
movie_audio:
uri_prefix: /audios/movies
upload_destination: '%kernel.project_dir%/public/audios/movies'
namer: Vich\UploaderBundle\Naming\UniqidNamer
person_image:
uri_prefix: /images/person_image
upload_destination: '%kernel.project_dir%/public/images/person_image'
namer: Vich\UploaderBundle\Naming\UniqidNamer
This is my form PersonType.php:
namespace App\Form;
use App\Entity\Person;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\BirthdayType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PersonType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('pseudo', TextType::class, [
"label" => "Pseudonyme",
"required" => false
])
->add('name', TextType::class, [
"label" => "Nom",
"required" => true
])
->add('firstname', TextType::class, [
"label" => "Prénom",
"required" => true
])
->add('middleName', TextType::class, [
"label" => "2ème Prénom",
"required" => false
])
->add('birthDate', BirthdayType::class, [
"widget" => "single_text",
"label" => "Date de naissance",
])
->add('deathDate', BirthdayType::class, [
"widget" => "single_text",
"label" => "Date de mort",
"required" => false
])
->add('sex', ChoiceType::class, [
"label" => "Genre",
'choices' => [
'Homme' => true,
'Femme' => false,
],
])
->add('nationality', TextType::class, [
'required' => false,
"label" => "Nationalité"
])
->add('award1', TextType::class, [
'required' => false,
"label" => "Récompense 1"
])
->add('award2', TextType::class, [
'required' => false,
"label" => "Récompense 2"
])
->add('award3', TextType::class, [
'required' => false,
"label" => "Récompense 3"
])
->add('award4', TextType::class, [
'required' => false,
"label" => "Récompense 4"
])
->add('award5', TextType::class, [
'required' => false,
"label" => "Récompense 5"
])
->add('award6', TextType::class, [
'required' => false,
"label" => "Récompense 6"
])
->add('pictureFiles', FileType::class, [
'required' => false,
'multiple' => true,
'label' => 'Chargement d\'images (jpeg uniquement) '
])
->add('biography', TextareaType::class, [
'required' => false,
"label" => "Biographie",
])
// ->add('awardPeople', TextType::class, [
// "label" => "Prix remporté(s)"
// ])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Person::class,
]);
}
}

Related

Modify POST data before storing in database with Symfony and API-Platform

I'm fairly new to the Symfony universe, so please bear with me if this question has already been answered.
I have provided endpoints with the api-platform to create a RegistrationRequest. A RegistrationRequest has a user that is connected via a ManyToOne relation, so it is stored in another table. In the API, the user or user_id is read-only, this is why the user can not be set in the post data. If a RegistrationRequest is made via the API, the creation fails because the user_id is obviously null.
This is why I would like to set the user manually after a registration request is made via the API but before the RegistrationRequest is stored in the database.
The user is known via the global REMOTE_USER from where I can derive the corresponding user object.
src/Entity/RegistrationRequest.php:
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=App\Repository\RegistrationRequestRepository::class)
* #ApiResource(
* normalizationContext={"groups" = {"read"}},
* denormalizationContext={"groups" = {"write"}},
* paginationEnabled=false,
* )
*/
class RegistrationRequest
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"read"})
*/
private $id;
/**
* #ORM\Column(type="string", length=64, nullable=true)
* #Groups({"read", "write"})
*/
private $opt_email;
/**
* #ORM\Column(type="string", length=255, nullable=true)
* #Groups({"read", "write"})
*/
private $title;
/**
* #Gedmo\Mapping\Annotation\Timestampable(on="create")
* #ORM\Column(type="datetime")
* #Groups({"read"})
*/
private $created_at;
/**
* #Gedmo\Mapping\Annotation\Timestampable(on="update")
* #ORM\Column(type="datetime")
* #Groups({"read"})
*/
private $updated_at;
/**
* #ORM\Column(type="text", nullable=true)
* #Groups({"read", "write"})
*/
private $notes;
/**
* #ORM\Column(type="string", length=16)
* #Groups({"read", "write"})
*/
private $language_code;
/**
* #ORM\Column(type="text")
* #Groups({"read", "write"})
*/
private $data;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="registrationRequests")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
public function getId(): ?int
{
return $this->id;
}
public function getOptEmail(): ?string
{
return $this->opt_email;
}
public function setOptEmail(?string $opt_email): self
{
$this->opt_email = $opt_email;
return $this;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(?string $title): self
{
$this->title = $title;
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->created_at;
}
public function setCreatedAt(\DateTimeInterface $created_at): self
{
$this->created_at = $created_at;
return $this;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updated_at;
}
public function setUpdatedAt(\DateTimeInterface $updated_at): self
{
$this->updated_at = $updated_at;
return $this;
}
public function getNotes(): ?string
{
return $this->notes;
}
public function setNotes(?string $notes): self
{
$this->notes = $notes;
return $this;
}
public function getLanguageCode(): ?string
{
return $this->language_code;
}
public function setLanguageCode(string $language_code): self
{
$this->language_code = $language_code;
return $this;
}
public function getData(): ?string
{
return $this->data;
}
public function setData(string $data): self
{
$this->data = $data;
return $this;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
}
src/Controller/RegistrationRequestController.php:
<?php
namespace App\Controller;
use App\Service\IGSNService;
use App\Entity\RegistrationRequest;
use App\Form\RegistrationRequestType;
use App\Repository\RegistrationRequestRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* #Route("/registration_request")
*/
class RegistrationRequestController extends AbstractController
{
/**
* #Route("/")
*/
public function indexNoLocale(): Response
{
return $this->redirectToRoute('app_registration_request_index', ['_locale' => 'de']);
}
/**
* #Route("/{_locale<%app.supported_locales%>}/", name="app_registration_request_index", methods={"GET"})
*/
public function index(RegistrationRequestRepository $registrationRequestRepository): Response
{
return $this->render('registration_request/index.html.twig', [
'registration_requests' => $registrationRequestRepository->findAll(),
]);
}
/**
* #Route("/{_locale<%app.supported_locales%>}/new", name="app_registration_request_new", methods={"GET", "POST"})
*/
public function new(Request $request, RegistrationRequestRepository $registrationRequestRepository, IGSNService $igsnService, TranslatorInterface $translator): Response
{
$registrationRequest = new RegistrationRequest();
$form = $this->createForm(RegistrationRequestType::class, $registrationRequest);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$json_data = json_decode($registrationRequest->getData());
$err = $igsnService->validate_data($json_data);
if ($json_data !== null && empty($err)) {
$registrationRequestRepository->add($registrationRequest, true);
$this->addFlash(
'success',
$translator->trans('Your changes were saved!')
);
return $this->redirectToRoute('app_registration_request_index', [], Response::HTTP_SEE_OTHER);
} else {
$this->addFlash(
'schema_error',
$err
);
}
}
return $this->renderForm('registration_request/new.html.twig', [
'registration_request' => $registrationRequest,
'form' => $form,
]);
}
/**
* #Route("/{_locale<%app.supported_locales%>}/{id}", name="app_registration_request_show", methods={"GET"})
*/
public function show(RegistrationRequest $registrationRequest): Response
{
return $this->render('registration_request/show.html.twig', [
'registration_request' => $registrationRequest,
]);
}
/**
* #Route("/{_locale<%app.supported_locales%>}/{id}/edit", name="app_registration_request_edit", methods={"GET", "POST"})
*/
public function edit(Request $request, RegistrationRequest $registrationRequest, RegistrationRequestRepository $registrationRequestRepository, IGSNService $igsnService, TranslatorInterface $translator): Response
{
$form = $this->createForm(RegistrationRequestType::class, $registrationRequest);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$json_data = json_decode($registrationRequest->getData());
$err = $igsnService->validate_data($json_data);
if ($json_data !== null && empty($err)) {
$registrationRequestRepository->add($registrationRequest, true);
$this->addFlash(
'success',
$translator->trans('Your changes were saved!')
);
return $this->redirectToRoute('app_registration_request_index', [], Response::HTTP_SEE_OTHER);
} else {
$this->addFlash(
'schema_error',
$err
);
}
}
return $this->renderForm('registration_request/edit.html.twig', [
'registration_request' => $registrationRequest,
'form' => $form,
]);
}
/**
* #Route("/{_locale<%app.supported_locales%>}/{id}", name="app_registration_request_delete", methods={"POST"})
*/
public function delete(Request $request, RegistrationRequest $registrationRequest, RegistrationRequestRepository $registrationRequestRepository, TranslatorInterface $translator): Response
{
if ($this->isCsrfTokenValid('delete' . $registrationRequest->getId(), $request->request->get('_token'))) {
$registrationRequestRepository->remove($registrationRequest, true);
$this->addFlash(
'success',
$translator->trans('Request successfully deleted!')
);
}
return $this->redirectToRoute('app_registration_request_index', [], Response::HTTP_SEE_OTHER);
}
}
config/packages/api_platform.yaml
api_platform:
mapping:
paths: ['%kernel.project_dir%/src/Entity']
patch_formats:
json: ['application/merge-patch+json']
swagger:
versions: [3]
# Fixes empty api endpoint list with error:
# No operations defined in spec!
# See https://github.com/api-platform/core/issues/4485
metadata_backward_compatibility_layer: false
I have now got it solved with a DataPersister, as suggested by Julien B. in the comments.
namespace App\DataPersister;
use ApiPlatform\Core\DataPersister\DataPersisterInterface;
use App\Entity\RegistrationRequest;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Security;
final class RequestDataPersister implements DataPersisterInterface
{
private $security;
public function __construct(Security $security, EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
$this->security = $security;
}
public function supports($data): bool
{
return $data instanceof RegistrationRequest;
}
public function persist($data)
{
$data->setUser($this->security->getUser());
$this->entityManager->persist($data);
$this->entityManager->flush();
return $data;
}
public function remove($data): void
{
// no action needed
}
}

Can't create new User

When I try to registering a new user, I had this error
An exception occurred while executing 'INSERT INTO user (username, password, email, gender, newsletter, is_blocked, role_id, purchase_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)' with params ["momo", "$2y$13$qCw4ZjU6k4s/dIwTeseEVud/zdjVu3xVper8oHAqsd/fKihq05Ga2", "momo#test.fr", "1", 0, 0, null, null]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'role_id' cannot be null
Thank's in advance.
Here is my User entity:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* * #UniqueEntity(
* fields = {"username"},
* message = "Le nom d'utilisateur existe déjà",
* )
* #UniqueEntity(
* fields = {"email"},
* message = "L'email existe déjà",
* )
*/
class User implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=180, unique=true)
*/
private $username;
private $roles = [];
/**
* #var string The hashed password
* #ORM\Column(type="string")
*/
private $password;
/**
* #ORM\Column(type="string", length=180, unique=true)
*/
private $email;
/**
* #ORM\Column(type="string", length=255)
*/
private $gender;
/**
* #ORM\Column(type="boolean", nullable=true)
*/
private $newsletter;
/**
* #ORM\Column(type="boolean")
*/
private $isBlocked;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Role", inversedBy="users")
* #ORM\JoinColumn(nullable=false)
*/
private $role;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Article", inversedBy="users")
*/
private $article;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Firmware", inversedBy="users")
*/
private $firmware;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Purchase", inversedBy="users")
*/
private $purchase;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Note", mappedBy="user",
orphanRemoval=true)
*/
private $notes;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Comment", mappedBy="user",
orphanRemoval=true)
*/
private $comments;
public function __construct()
{
$this->article = new ArrayCollection();
$this->firmware = new ArrayCollection();
$this->notes = new ArrayCollection();
$this->comments = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUsername(): string
{
return (string) $this->username;
}
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
/**
* #see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #see UserInterface
*/
public function getPassword(): string
{
return (string) $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* #see UserInterface
*/
public function getSalt()
{
// not needed when using the "bcrypt" algorithm in security.yaml
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it
here
// $this->plainPassword = null;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
public function getGender(): ?string
{
return $this->gender;
}
public function setGender(string $gender): self
{
$this->gender = $gender;
return $this;
}
public function getNewsletter(): ?bool
{
return $this->newsletter;
}
public function setNewsletter(bool $newsletter): self
{
$this->newsletter = $newsletter;
return $this;
}
public function getIsBlocked(): ?bool
{
return $this->isBlocked;
}
public function setIsBlocked(bool $isBlocked): self
{
$this->isBlocked = $isBlocked;
return $this;
}
public function getRole(): ?Role
{
return $this->role;
}
public function setRole(?Role $role): self
{
$this->role = $role;
return $this;
}
/**
* #return Collection|Article[]
*/
public function getArticle(): Collection
{
return $this->article;
}
public function addArticle(Article $article): self
{
if (!$this->article->contains($article)) {
$this->article[] = $article;
}
return $this;
}
public function removeArticle(Article $article): self
{
if ($this->article->contains($article)) {
$this->article->removeElement($article);
}
return $this;
}
/**
* #return Collection|Firmware[]
*/
public function getFirmware(): Collection
{
return $this->firmware;
}
public function addFirmware(Firmware $firmware): self
{
if (!$this->firmware->contains($firmware)) {
$this->firmware[] = $firmware;
}
return $this;
}
public function removeFirmware(Firmware $firmware): self
{
if ($this->firmware->contains($firmware)) {
$this->firmware->removeElement($firmware);
}
return $this;
}
public function getPurchase(): ?Purchase
{
return $this->purchase;
}
public function setPurchase(?Purchase $purchase): self
{
$this->purchase = $purchase;
return $this;
}
/**
* #return Collection|Note[]
*/
public function getNotes(): Collection
{
return $this->notes;
}
public function addNote(Note $note): self
{
if (!$this->notes->contains($note)) {
$this->notes[] = $note;
$note->setUser($this);
}
return $this;
}
public function removeNote(Note $note): self
{
if ($this->notes->contains($note)) {
$this->notes->removeElement($note);
// set the owning side to null (unless already changed)
if ($note->getUser() === $this) {
$note->setUser(null);
}
}
return $this;
}
/**
* #return Collection|Comment[]
*/
public function getComments(): Collection
{
return $this->comments;
}
public function addComment(Comment $comment): self
{
if (!$this->comments->contains($comment)) {
$this->comments[] = $comment;
$comment->setUser($this);
}
return $this;
}
public function removeComment(Comment $comment): self
{
if ($this->comments->contains($comment)) {
$this->comments->removeElement($comment);
// set the owning side to null (unless already changed)
if ($comment->getUser() === $this) {
$comment->setUser(null);
}
}
return $this;
}
}
Role Entity
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\RoleRepository")
*/
class Role
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\Column(type="string", length=255)
*/
private $description;
/**
* #ORM\OneToMany(targetEntity="App\Entity\User", mappedBy="role")
*/
private $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
/**
* #return Collection|User[]
*/
public function getUsers(): Collection
{
return $this->users;
}
public function addUser(User $user): self
{
if (!$this->users->contains($user)) {
$this->users[] = $user;
$user->setRole($this);
}
return $this;
}
public function removeUser(User $user): self
{
if ($this->users->contains($user)) {
$this->users->removeElement($user);
// set the owning side to null (unless already changed)
if ($user->getRole() === $this) {
$user->setRole(null);
}
}
return $this;
}
public function __toString()
{
return $this->name;
}
}
UserType
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array
$options)
{
$builder
->add('username', TextType::class, [
'empty_data' => '',
])
->add('password', RepeatedType::class, [
'type' => PasswordType::class,
'empty_data' => '',
'invalid_message' => 'Valeur no correcte',
'options' => ['attr' => ['class' => 'password-field']],
'required' => true,
'first_options' => ['label' => 'Password','empty_data' =>
''],
'second_options' => ['label' => 'Repeat Password','empty_data'
=> ''],
])
->add('email',EmailType::class, [
'empty_data' => '',
])
->add('gender', ChoiceType::class, [
'help' => 'Un choix possible',
'choices' => [
'Femme' => true,
'Homme' => true,
'Autre' => true,
],
])
->add('newsletter')
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}
SecutityController
<?php
namespace App\Controller;
use App\Entity\User;
use App\Form\UserType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class SecurityController extends AbstractController
{
/**
* #Route("/login", name="app_login")
*/
public function login(AuthenticationUtils $authenticationUtils): Response
{
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', ['last_username' =>
$lastUsername, 'error' => $error]);
}
/**
* #Route("/inscription", name="registration")
*/
public function registration(Request $request, EntityManagerInterface $em,
UserPasswordEncoderInterface $passwordEncoder)
{
$user = new User();
// $role = $user->getRoles();
$form = $this->createForm(UserType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$password = $passwordEncoder->encodePassword($user, $user-
>getPassword());
$user->setPassword($password);
$user->setIsBlocked(false);
// $user->setRole($role);
$em->persist($user);
$em->flush();
$this->addFlash('success', 'Votre compte à bien été enregistré.');
}
return $this->render('security/registration.html.twig', ['form' =>
$form->createView()]);
}
/**
* #Route("/logout", name="logout")
*/
public function logout() {}
}
On role property Change nullable=false to nullable=true And update the database
* #ORM\JoinColumn(nullable=true)
*/
private $role;

Submiting data in a ManyToMany relation

I have 2 entities, Parking and Agent, each parking can have many Agents and each Agent can administer many parkings.
After I created the relationship, Doctrine automatically added a join table called parking-Agent.
Now I'm trying to populate that table through a form, like when creating a new Agent I can give him one or many parkings, or Vice-Versa. I tried adding a choicetype with multiple choices to the form but it didn't work.
Can you guys help me ?
Entity Agent:
<?php
/**
* #ORM\Entity
* #UniqueEntity(fields="username", message="Username already taken")
*/
class Agent implements UserInterface
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
public function getId()
{
return $this->id;
}
/**
* #ORM\Column(type="string", length=191, unique=true)
* #Assert\NotBlank()
*/
private $username;
/**
* #Assert\Length(max=191)
*/
private $plainPassword;
/**
* The below length depends on the "algorithm" you use for encoding
* the password, but this works well with bcrypt.
*
* #ORM\Column(type="string", length=64)
*/
private $password;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Parking", mappedBy="agents")
*/
private $parkings;
public function __construct()
{
$this->parkings = new ArrayCollection();
}
public function getUsername()
{
return $this->username;
}
public function setUsername($username)
{
$this->username = $username;
}
public function getPlainPassword()
{
return $this->plainPassword;
}
public function setPlainPassword($password)
{
$this->plainPassword = $password;
$this->password = null;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
if (!is_null($password)) {
$this->password = $password;
}
return $this;
}
public function getSalt()
{
return null;
}
public function eraseCredentials()
{
}
/**
* #return Collection|Parking[]
*/
public function getParkings(): Collection
{
return $this->parkings;
}
public function addParking(Parking $parking): self
{
if (!$this->parkings->contains($parking)) {
$this->parkings[] = $parking;
$parking->addAgent($this);
return $this;
}
return $this;
}
public function removeParking(Parking $parking): self
{
if ($this->parkings->contains($parking)) {
$this->parkings->removeElement($parking);
$parking->removeAgent($this);
}
return $this;
}
}
Entity Parking:
<?php
/**
* #ORM\Entity(repositoryClass="App\Repository\ParkingRepository")
*/
class Parking
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=55)
*/
private $libelle;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\agent", inversedBy="parkings")
*/
private $agents;
public function __construct()
{
$this->agents = new ArrayCollection();
$this->voitures = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getLibelle(): ?string
{
return $this->libelle;
}
public function setLibelle(string $libelle): self
{
$this->libelle = $libelle;
return $this;
}
/**
* #return Collection|agent[]
*/
public function getAgents(): Collection
{
return $this->agents;
}
public function addAgent(Agent $agent): self
{
if (!$this->agents->contains($agent)) {
$this->agents[] = $agent;
}
return $this;
}
public function removeAgent(Agent $agent): self
{
if ($this->agents->contains($agent)) {
$this->agents->removeElement($agent);
}
return $this;
}
}
My Form:
<?php
namespace App\Form;
ass ParkingType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('libelle');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Parking::class,
]);
}
}
You can try it with a field where you can select multiple elements. The Select2:
->add('personsconcerned', ChoiceType::class, [
'label' => 'form.personsconcerned',
'choices' => $this->groupService->getMailGroups(),
'multiple' => 'multiple',
'mapped' => false,
'choice_translation_domain' => false,
'attr' => [
'data-select' => 'true'
],
'data' => $mailgroups
])
In this example you can see an Element with the posibillity to select more than one thing.
The important thing is the attribute 'multiple', set this to 'multiple' or true.

Session die after email validation error Symfony 4

I have form that user can change it is own email. But when validation error occured or some constrait failed session die and user is redirected to login page. Important to note that user is authenticated by user and password from database. Thanks for helping
UPDATE
I just added User.php file for more details.
namespace App\Form;
class ChangeEmailType extends AbstractType
{
private $translator;
private $transformer;
public function __construct(TranslatorInterface $translator, DateTimeToStringTransformer $transformer)
{
$this->transformer = $transformer;
$this->translator = $translator;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('email', EmailType::class, array(
'required' => true,
'label' => $this->translator->trans('email'),
'attr' => array('class' => 'form-control')
))
->add('updationDate', HiddenType::class, array(
'required' => true,
'label' => false,
));
$builder->get('updationDate')->addModelTransformer($this>transformer);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'App\Entity\User',
'attr' => array('class' => 'change-email-form')
));
}
}
UserController.php
/**
* #Security("has_role('ROLE_USER')")
* #Route("/profile/change-email", name="change email")
*/
public function changeEmail(Request $request)
{
$user = $this->getUser();
$form = $this->createForm(ChangeEmailType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid())
{
try
{
$this->entityManager->lock($this->getUser(), LockMode::OPTIMISTIC, $user->getUpdationDate());
$this->entityManager->flush();
$this->addFlash('success', $this->translator->trans('email_is_successfully_changed'));
return $this->redirectToRoute('dashboard');
}
catch (OptimisticLockException $exception)
{
$this->addFlash('success', $this->translator->trans('data_is_outdated_try_again'));
return $this->redirectToRoute('change email');
}
}
return $this->render('app/pages/profile/change-email.html.twig', array(
'form' => $form->createView()
));
}
User.php
namespace App\Entity;
use DateTime;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #ORM\HasLifecycleCallbacks()
* #ORM\Table(name="users")
* #UniqueEntity(fields={"email"})
*/
class User implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #Assert\Length(
* min = 6,
* minMessage = "Password should by at least 6 characters long",
* groups={"registration"}
* )
* #Assert\Type(
* type="alnum",
* message="Password should contains only numbers and letters",
* groups={"registration"}
* )
* #Assert\NotBlank()
* #ORM\Column(type="text", nullable=true)
*/
private $password;
/**
* #Assert\NotBlank(message="field_is_required")
* #Assert\Email(message="email_is_invalid")
* #ORM\Column(type="string", length=255, unique=true)
*/
private $email;
/**
* #ORM\Column(type="string", length=255)
*/
private $theme;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $facebookId;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $linkedinId;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $passwordRecoveryHash;
/**
* #ORM\Column(type="integer")
*/
private $status;
/**
* #ORM\Column(type="string", length=255)
*/
private $emailConfirmationHash;
/**
* #ORM\Column(type="datetime")
* #ORM\Version
*/
private $updationDate;
/**
* #ORM\Column(type="datetime")
*/
private $creationDate;
/**
* #ORM\PrePersist()
*/
public function prePersist(): void
{
$this->creationDate = new DateTime();
$this->theme = 'light';
$this->status = 0;
}
public function getRoles()
{
return ['ROLE_USER'];
}
public function getPassword()
{
return $this->password;
}
public function getSalt()
{
return 'salt';
}
public function getUsername()
{
return $this->email;
}
public function eraseCredentials()
{
// TODO: Implement eraseCredentials() method.
}
public function getId()
{
return $this->id;
}
public function setId($id): void
{
$this->id = $id;
}
public function setPassword($password): void
{
$this->password = $password;
}
public function getEmail()
{
return $this->email;
}
public function setEmail($email): void
{
$this->email = $email;
}
public function getUpdationDate()
{
return $this->updationDate;
}
public function setUpdationDate($updationDate): void
{
$this->updationDate = $updationDate;
}
public function getCreationDate()
{
return $this->creationDate;
}
public function setCreationDate($creationDate): void
{
$this->creationDate = $creationDate;
}
public function getTheme()
{
return $this->theme;
}
public function setTheme($theme): void
{
$this->theme = $theme;
}
public function getPasswordRecoveryHash()
{
return $this->passwordRecoveryHash;
}
public function setPasswordRecoveryHash($passwordRecoveryHash): void
{
$this->passwordRecoveryHash = $passwordRecoveryHash;
}
public function getFacebookId()
{
return $this->facebookId;
}
public function setFacebookId($facebookId): void
{
$this->facebookId = $facebookId;
}
public function getLinkedinId()
{
return $this->linkedinId;
}
public function setLinkedinId($linkedinId): void
{
$this->linkedinId = $linkedinId;
}
public function getEmailConfirmationHash()
{
return $this->emailConfirmationHash;
}
public function setEmailConfirmationHash($emailConfirmationHash): void
{
$this->emailConfirmationHash = $emailConfirmationHash;
}
public function getStatus()
{
return $this->status;
}
public function setStatus($status): void
{
$this->status = $status;
}
}
So I had the same problem but I fixed it by to not passing object User to the formType.
This is my code
$user = $this->getUser();
// don't pass pass $user as parameter
$form = $this->createForm(ChangeEmailType::class);
$form->handleRequest($request);
if ($form->isSubmitted()) {
$em = $this->getDoctrine()->getManager();
$currentEmail = $user->getEmail();
$newEmail = $form->get('email')->getData();
$currentPassword = $form->get('currentPassword')->getData();
// verify if the tow emails are different
if ($currentEmail === $newEmail) {
$form->get('email')->addError(new FormError('You must choose a different email.'));
}
// verify if the email is used by another user
$userHasAlreadyThisEmail = $em->getRepository('App:User')->findOneBy(array('email' => $newEmail));
if ($userHasAlreadyThisEmail && $userHasAlreadyThisEmail !== $user) {
$form->get('email')->addError(new FormError('This email is already used.'));
}
// verify if the password is correct
$verify = $this->verify($currentPassword, $user->getPassword());
if ($verify === false) {
$form->get('currentPassword')->addError(new FormError('Incorrect password.'));
}
if ($form->isValid()) {
$this->addFlash('success', 'The email has been successfully modified.');
$user->setEmail($newEmail);
$em->flush();
return $this->redirectToRoute('profile_change_email');
}
}
public function verify($input, $existingHash)
{
$hash = password_verify($input, $existingHash);
return $hash === true;
}

Symfony2 try to insert ManyToMany relation before insert one part of both sides

I have this Entities:
<?php
namespace ProductBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use ProductBundle\DBAL\Types\StatusType;
use ProductBundle\DBAL\Types\FieldType;
use Fresh\Bundle\DoctrineEnumBundle\Validator\Constraints as DoctrineAssert;
/**
* #ORM\Entity
* #ORM\Table(name="product_detail")
*/
class ProductDetail {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="ProductDetail")
* #ORM\JoinColumn(name="parent", referencedColumnName="id")
*/
protected $parent;
/**
* #ORM\Column(type="string", length=255)
*/
protected $description;
/**
* #ORM\Column(type="string", length=255)
*/
protected $label;
/**
* #var string $field_type
* #DoctrineAssert\Enum(entity="ProductBundle\DBAL\Types\FieldType")
* #ORM\Column(name="field_type", type="FieldType", nullable=false)
*/
protected $field_type;
/**
* #ORM\Column(name="values_text", type="string")
*/
protected $values_text;
/**
* #ORM\Column(type="string", length=255)
*/
protected $measure_unit;
/**
* #var string $status
* #DoctrineAssert\Enum(entity="ProductBundle\DBAL\Types\StatusType")
* #ORM\Column(name="status", type="StatusType", nullable=false)
*/
protected $status;
/**
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="created", type="datetime")
*/
protected $created;
/**
* #Gedmo\Timestampable(on="update")
* #ORM\Column(name="modified", type="datetime")
*/
protected $modified;
/**
* #ORM\ManyToMany(targetEntity="CategoryBundle\Entity\Category", inversedBy="pd_detail")
* #ORM\JoinTable(name="product_detail_has_category",
* joinColumns={#ORM\JoinColumn(name="category", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="detail", referencedColumnName="id")}
* )
*/
protected $category;
/**
* #ORM\ManyToMany(targetEntity="ProductBundle\Entity\DetailGroup", inversedBy="productDetail")
* #ORM\JoinTable(name="detail_group_has_product_detail",
* joinColumns={#ORM\JoinColumn(name="group", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="detail", referencedColumnName="id")}
* )
*/
protected $detail_group;
public function __construct() {
$this->detail_group = new \Doctrine\Common\Collections\ArrayCollection();
$this->category = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId() {
return $this->id;
}
public function setParent(ProductDetail $parent = null) {
$this->parent = $parent;
}
public function getParent() {
return $this->parent;
}
public function setDescription($description) {
$this->description = $description;
}
public function getDescription() {
return $this->description;
}
public function setLabel($label) {
$this->label = $label;
}
public function getLabel() {
return $this->label;
}
public function setFieldType($field_type) {
$this->field_type = $field_type;
}
public function getFieldType() {
return $this->field_type;
}
public function setValuesText($values_text) {
$this->values_text = $values_text;
}
public function getValuesText() {
return $this->values_text;
}
public function setMeasureUnit($measure_unit) {
$this->measure_unit = $measure_unit;
}
public function getMeasureUnit() {
return $this->measure_unit;
}
public function setStatus($status) {
$this->status = $status;
}
public function getStatus() {
return $this->status;
}
public function setCreated($param) {
$this->created = $param;
return true;
}
public function getCreated() {
return $this->created;
}
public function setModified($param) {
$this->modified = $param;
return true;
}
public function getModified() {
return $this->modified;
}
public function setCategory(Category $category) {
$this->category[] = $category;
}
public function getCategory() {
return $this->category;
}
public function setDetailGroup(DetailGroup $detailGroup) {
$this->detail_group[] = $detailGroup;
}
public function getDetailGroup() {
return $this->detail_group;
}
}
namespace ProductBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Fresh\Bundle\DoctrineEnumBundle\Validator\Constraints as DoctrineAssert;
/**
* #ORM\Entity
* #ORM\Table(name="detail_group")
*/
class DetailGroup {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="DetailGroup")
* #ORM\JoinColumn(name="parent", referencedColumnName="id")
*/
protected $parent;
/**
* #ORM\Column(type="string", length=255)
*/
protected $name;
/**
* #ORM\Column(type="string", length=255)
*/
protected $description;
/**
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="created", type="datetime")
*/
protected $created;
/**
* #Gedmo\Timestampable(on="update")
* #ORM\Column(name="modified", type="datetime")
*/
protected $modified;
/**
* #ORM\ManyToMany(targetEntity="ProductBundle\Entity\ProductDetail", mappedBy="detail_group", cascade={"all"})
*/
protected $productDetail;
public function __construct() {
$this->productDetail = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId() {
return $this->id;
}
public function setParent(DetailGroup $parent = null) {
$this->parent = $parent;
}
public function getParent() {
return $this->parent;
}
public function setName($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
public function setDescription($description) {
$this->description = $description;
}
public function getDescription() {
return $this->description;
}
public function setCreated($param) {
$this->created = $param;
return true;
}
public function getCreated() {
return $this->created;
}
public function setModified($param) {
$this->modified = $param;
return true;
}
public function getModified() {
return $this->modified;
}
public function setProductDetail(ProductDetail $productDetail) {
$this->productDetail[] = $productDetail;
}
public function getProductDetail() {
return $this->productDetail;
}
}
This is my ProductDetailType.php form:
<?php
namespace ProductBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ProductDetailType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('category', 'entity', array('class' => 'CategoryBundle:Category', 'property' => 'name', 'required' => false, 'multiple' => true, 'expanded' => false))
->add('detail_group', 'entity', array('class' => 'ProductBundle:DetailGroup', 'property' => 'name', 'required' => false, 'multiple' => true, 'expanded' => false))
->add('parent', 'entity', array('class' => 'ProductBundle:ProductDetail', 'property' => 'label', 'required' => false))
->add('description', 'text', array('required' => false))
->add('label')
->add('field_type', 'choice', ['choices' => \ProductBundle\DBAL\Types\FieldType::getChoices()])
->add('values_text', 'collection', array('type' => 'text', 'allow_add' => true, 'allow_delete' => true, 'by_reference' => false))
->add('measure_unit', 'text', array('required' => false))
->add('status', 'choice', ['choices' => \ProductBundle\DBAL\Types\StatusType::getChoices()]);
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'ProductBundle\Entity\ProductDetail'
));
}
public function getName() {
return 'prod_detail_create';
}
}
And this is my createAction():
/**
* Handle product details creation
*
* #Route("/product_detail/create", name="product_detail_create")
* #Method("POST")
* #Template("ProductBundle:ProductDetail:new.html.twig")
*/
public function createAction(Request $request) {
$entity = new ProductDetail();
$form = $this->createForm(new ProductDetailType(), $entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$entity->setValuesText(serialize($form->get('values_text')->getData()));
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('product_detail_list'));
}
return $this->render('ProductBundle:ProductDetail:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
When I try to insert new records I get this errors:
An exception occurred while executing 'INSERT INTO
product_detail_has_category (category, detail) VALUES (?, ?)' with
params [7, 2]:
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or
update a child row: a foreign key constraint fails
(product_detail_has_category, CONSTRAINT
fk_product_detail_has_category_product_detail1 FOREIGN KEY
(detail) REFERENCES product_detail (id) ON UPDATE CASCADE)
I check the logs and see this:
DEBUG - "START TRANSACTION"
DEBUG - INSERT INTO product_detail (description, label, field_type, values_text, measure_unit, status, created, modified, parent) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
DEBUG - INSERT INTO product_detail_has_category (category, detail) VALUES (?, ?)
DEBUG - "ROLLBACK"
I don't know why it doesn't insert product_detail and the tries to create the relation, what is wrong?
You can either:
set on $category field set cascade={"PERSIST"} in #ManyToMany.
This requires no chnages to controller itself but it depends on whether you like defining cascades.
persist Category object manually before persisting ProductDetail.
$em = ...; // your entity manager
// first, manually persist any category that is found
foreach ( $productDetail->getCategory() as $c ){
$em->persist($c);
}
// then, persist top-level object
$em->persist($productDetail);
// finally, flush everything
$em->flush();
Hope this helps a bit...
I've found where the errors was, see the changes below in annotations:
/**
* #ORM\ManyToMany(targetEntity="CategoryBundle\Entity\Category", inversedBy="pd_detail", cascade={"persist"})
* #ORM\JoinTable(name="product_detail_has_category",
* joinColumns={#ORM\JoinColumn(name="detail", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="category", referencedColumnName="id")}
* )
*/
protected $category;
/**
* #ORM\ManyToMany(targetEntity="ProductBundle\Entity\DetailGroup", inversedBy="productDetail", cascade={"persist"})
* #ORM\JoinTable(name="detail_group_has_product_detail",
* joinColumns={#ORM\JoinColumn(name="detail", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="kgroup", referencedColumnName="id")}
* )
*/
protected $detail_group;
I've inverted the relations in joinColumns and inversedJoinColumns and all works fine, also has to rename group to kgroup due to MySQL query errors!!

Resources