I've added an avatar image to my User class. When I wanted to render my edit form, I got this error
Serialization of 'Symfony\Component\HttpFoundation\File\File' is not allowed
I tried to solve the problem by implementing \Serializable in my User class according to Symfony Official Documentation. but didint work.
Here is my UserEntity, Artsite.php:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use
Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
use Serializable;
/**
*
#ORM\Entity(repositoryClass="App\Repository\ArtisteRepository")
* #UniqueEntity(fields={"username"}, message="There is already
an account with this username")
*/
class Artiste implements UserInterface, Serializable
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $NomPrenom;
/**
* #ORM\Column(type="string", length=255)
*/
private $adresse;
/**
* #ORM\Column(type="integer")
*/
private $numero1;
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $numero2;
/**
* #ORM\Column(type="string", length=255)
*/
private $username;
/**
* #ORM\Column(type="string", length=255)
*/
private $password;
/**
* #ORM\Column(type="string", nullable=true)
* #Assert\File(mimeTypes={ "image/*" },mimeTypesMessage="C'est n'est pas une image")
*/
private $image;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Produit", mappedBy="artiste_id")
*/
private $produits;
public function __construct()
{
$this->produits = new ArrayCollection();
}
public function getImage()
{
return $this->image;
}
public function setImage($image)
{
$this->image = $image;
return $this;
}
public function getId(): ?int
{
return $this->id;
}
public function getNomPrenom(): ?string
{
return $this->NomPrenom;
}
public function setNomPrenom(string $NomPrenom): self
{
$this->NomPrenom = $NomPrenom;
return $this;
}
public function getAdresse(): ?string
{
return $this->adresse;
}
public function setAdresse(string $adresse): self
{
$this->adresse = $adresse;
return $this;
}
public function getNumero1(): ?int
{
return $this->numero1;
}
public function setNumero1(int $numero1): self
{
$this->numero1 = $numero1;
return $this;
}
public function getNumero2(): ?int
{
return $this->numero2;
}
public function setNumero2(?int $numero2): self
{
$this->numero2 = $numero2;
return $this;
}
public function getUsername(): ?string
{
return $this->username;
}
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
public function getPassword(): ?string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* #return Collection|Produit[]
*/
public function getProduits(): Collection
{
return $this->produits;
}
public function addProduit(Produit $produit): self
{
if (!$this->produits->contains($produit)) {
$this->produits[] = $produit;
$produit->setArtisteId($this);
}
return $this;
}
public function removeProduit(Produit $produit): self
{
if ($this->produits->contains($produit)) {
$this->produits->removeElement($produit);
// set the owning side to null (unless already changed)
if ($produit->getArtisteId() === $this) {
$produit->setArtisteId(null);
}
}
return $this;
}
public function eraseCredentials()
{
}
public function getSalt()
{
}
public function getRoles(): array
{
$roles[] = 'ROLE_Etudiant';
return array_unique($roles);
}
/** #see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->id,
$this->image,
$this->username,
$this->password,
));
}
/** #see \Serializable::unserialize() */
public function unserialize($serialized)
{
list (
$this->id,
$this->image,
$this->username,
$this->password,
) = unserialize($serialized, array('allowed_classes' => false));
}
}
Here is my UserController.php
<?php
namespace App\Controller;
use App\Entity\Artiste;
use App\Form\ArtisteType;
use App\Repository\ArtisteRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use
Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use
Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\HttpFoundation\File\File;
class ArtisteController extends AbstractController
{
/**
* #Route("/", name="artiste_index", methods={"GET"})
*/
public function index(ArtisteRepository $artisteRepository,Security $security)
{
$user = $security->getUser();
return $this->render('artiste/index.html.twig', [
'client' =>$user
]);
}
/**
* #Route("/login", name="artiste_login", methods={"GET","POST"})
*/
public function login(AuthenticationUtils $authenticationUtils)
{
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('artiste/login.html.twig', [
'last_username' => $lastUsername,
'error' => $error,
]);
}
/**
* #Route("/logout", name="artiste_logout")
*/
public function logout()
{
}
/**
* #Route("/new", name="artiste_new", methods={"GET","POST"})
*/
public function new(Request $request,UserPasswordEncoderInterface $encoder): Response
{
$artiste = new Artiste();
$form = $this->createForm(ArtisteType::class, $artiste);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$hash=$encoder->encodePassword($artiste,$artiste->getPassword());
$artiste->setPassword($hash);
$file = $artiste->getImage();
if($file=="")
$artiste->setImage("default.jpg");
else{
$fileName = $this->generateUniqueFileName().'.'.$file->guessExtension();
// Move the file to the directory where brochures are stored
try {
$file->move(
$this->getParameter('images_directory'),
$fileName
);
} catch (FileException $e) {
// ... handle exception if something happens during file upload
}
$artiste->setImage($fileName);
}
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($artiste);
$entityManager->flush();
return $this->redirectToRoute('artiste_login');
}
return $this->render('artiste/new.html.twig', [
'artiste' => $artiste,
'form' => $form->createView(),
]);
}
/**
* #return string
*/
private function generateUniqueFileName()
{
// md5() reduces the similarity of the file names generated by
// uniqid(), which is based on timestamps
return md5(uniqid());
}
/**
* #Route("/profile", name="artiste_profile", methods={"GET"})
*/
public function show(Security $security)
{
$user = $security->getUser();
return $this->render('artiste/profile.html.twig', [
'client' => $user,
]);
}
/**
* #Route("/edit", name="artiste_edit", methods={"GET","POST"})
*/
public function edit(Request $request,Security $security,UserPasswordEncoderInterface $encoder)
{
$artiste=$security->getUser();
$artiste->setImage( new File($this->getParameter('images_directory').'/'.$artiste->getImage()));
$form = $this->createForm(ArtisteType::class, $artiste);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$hash=$encoder->encodePassword($artiste,$artiste->getPassword());
$artiste->setPassword($hash);
$file = $artiste->getImage();
$fileName = $this->generateUniqueFileName().'.'.$file->guessExtension();
// Move the file to the directory where brochures are stored
try {
$file->move(
$this->getParameter('images_directory'),
$fileName
);
} catch (FileException $e) {
// ... handle exception if something happens during file upload
}
$artiste->setImage($fileName);
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('artiste_profile', [
'client' => $artiste,
]);
}
return $this->render('artiste/edit.html.twig', [
'client' => $artiste,
'form' => $form->createView(),
]);
}
Here is my UserForm, UserType.php
<?php
namespace App\Form;
use App\Entity\Artiste;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
class ArtisteType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array
$options)
{
$builder
->add('NomPrenom')
->add('adresse')
->add('image', FileType::class, array('required' => false))
->add('numero1')
->add('numero2')
->add('username')
->add('password',PasswordType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Artiste::class,
]);
}
}
Related
I'm creating a website with Symfony 5. I have a problem that I can't solve.
I need to be able to create entities and for that I want to use a form generated by Symfony.
Symfony sends me this error:
Can use "yield from" only with arrays and Traversables
If I modify anything in my "Entities" entity then symfony sends me back:
My field I was waiting for
Great, that's exactly what I need, but the problem is that if I refresh the page the error comes back.
<?php
namespace App\Controller\Admin;
use App\Entity\Entities;
use App\Entity\Events;
use App\Form\EntitiesType;
use App\Form\EventsType;
use App\Repository\CriteriasRepository;
use App\Repository\EntitiesRepository;
use App\Repository\EventsRepository;
use Doctrine\Persistence\ObjectManager;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
Class AdminHome extends AbstractController {
protected $event;
protected $entity;
protected $criteria;
protected $twig;
public function __construct(EventsRepository $event, EntitiesRepository $entity, CriteriasRepository $criteria, ObjectManager $em)
{
$this->event = $event;
$this->entity = $entity;
$this->criteria = $criteria;
$this->em = $em;
}
public function index(){
$event = $this->event->findOneBy(['current' => 1]);
$criterias = $this->indexCriteria($event);
$entities = $this->indexEntity($event);
return new Response($this->render('admin/home.html',[ 'event' => $event, 'entities' => $entities, 'criterias' => $criterias ]));
}
public function indexEntity(Events $event){
return $event->getEntity();
}
public function indexCriteria(Events $event){
return $event->getCriterias();
}
/**
* #Route("/admin/event/create", name="admin.event.new")
*/
public function newEvent(Request $request){
$event = new Events();
$form = $this->createForm(EventsType::class, $event);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$this->em->persist($event);
$this->em->flush();
return $this->redirectToRoute('adminHome');
}
return $this->render('admin/event/eventNew.html', ['event' => $event, 'form' => $form->createView()]);
}
/**
* #Route("/admin/entity/create", name="admin.entity.new")
*/
public function newEntity(Request $request){
$entity = new Entities();
$form = $this->createForm(EntitiesType::class, $entity);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$this->em->persist($entity);
$this->em->flush();
return $this->redirectToRoute('adminHome');
}
return $this->render('admin/entity/entityNew.html', ['entity' => $entity, 'form' => $form->createView()]);
}
// /**
// * #Route("/admin/entity/edit", name="admin.entity.edit")
// */
// public function editEntity(Entities $entity, Request $request){
// $form = $this->createForm(EntityType::class, $entity);
// $form->handleRequest($request);
// if($form->isSubmitted() && $form->isValid()){
// $this->em->flush();
// return $this->redirectToRoute('adminHome');
// }
// return $this->render('admin/entityEdit.html', ['entity' => $entity, 'form' => $form->createView()]);
// }
}
?>
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=EntitiesRepository::class)
*/
class Entities
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $entityName;
/**
* #ORM\OneToMany(targetEntity=Rates::class, mappedBy="entity")
*/
private $rates;
/**
* #ORM\ManyToMany(targetEntity=Events::class, mappedBy="entity")
*/
private $event;
/**
* #ORM\Column(type="string", length=255)
*/
private $descriptionFr;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $descriptionEn;
/**
* #ORM\ManyToMany(targetEntity=Themes::class, inversedBy="entities")
*/
private $theme;
public function __construct()
{
$this->rates = new ArrayCollection();
$this->event = new ArrayCollection();
$this->theme = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getEntityName(): ?string
{
return $this->entityName;
}
public function setEntityName(string $entityName): self
{
$this->entityName = $entityName;
return $this;
}
/**
* #return Collection|Rates[]
*/
public function getRates(): Collection
{
return $this->rates;
}
public function addRate(Rates $rate): self
{
if (!$this->rates->contains($rate)) {
$this->rates[] = $rate;
$rate->setEntity($this);
}
return $this;
}
public function removeRate(Rates $rate): self
{
if ($this->rates->removeElement($rate)) {
// set the owning side to null (unless already changed)
if ($rate->getEntity() === $this) {
$rate->setEntity(null);
}
}
return $this;
}
/**
* #return Collection|Events[]
*/
public function getEvent(): Collection
{
return $this->event;
}
public function addEvent(Events $event): self
{
if (!$this->event->contains($event)) {
$this->event[] = $event;
$event->addEntity($this);
}
return $this;
}
public function removeEvent(Events $event): self
{
if ($this->event->removeElement($event)) {
$event->removeEntity($this);
}
return $this;
}
public function getDescriptionFr(): ?string
{
return $this->descriptionFr;
}
public function setDescriptionFr(string $descriptionFr): self
{
$this->descriptionFr = $descriptionFr;
return $this;
}
public function getDescriptionEn(): ?string
{
return $this->descriptionEn;
}
public function setDescriptionEn(?string $descriptionEn): self
{
$this->descriptionEn = $descriptionEn;
return $this;
}
/**
* #return Collection|Themes[]
*/
public function getTheme(): Collection
{
return $this->theme;
}
public function addTheme(Themes $theme): self
{
if (!$this->theme->contains($theme)) {
$this->theme[] = $theme;
}
return $this;
}
public function removeTheme(Themes $theme): self
{
$this->theme->removeElement($theme);
return $this;
}
}
<?php
namespace App\Form;
use App\Entity\Entities;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class EntitiesType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('entityName')
->add('descriptionFr' )
->add('descriptionEn')
->add('event')
->add('theme')
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Entities::class,
]);
}
}
{{ form_start(form) }}
{{ form_rest(form) }}
<button class="btn">{{ button|default('Créer')}}</button>
{{ form_end(form) }}
I am trying to migrate a some parts of a project made with Typescript + Express + Firebase to Symfony 5 and MySQL. But I don't get why the ValidatorInterface isn't working.
When I submit the form with unexpected characters (i.e.: a password with 3 charactesrs) it creates the user despites the validation constraints assigned in the User entity. The only constraint validation that actually works despites it does not show any form errors, is the UniqueEntity.
This is how my controller looks like:
/**
* GET: Display form
* POST: Creates a user
*
* #Route("/users/crear", name="create_user", methods={"GET", "POST"})
*/
public function create_user(Request $request, EntityManagerInterface $entityManager, ValidatorInterface $validator)
{
$user = new User();
// Form
if ($request->isMethod('GET')) {
$form = $this->createForm(CrearUserType::class, $user);
return $this->render('user/create.html.twig', [
'form' => $form->createView(),
'errors' => []
]);
}
// Form submit
$data = $request->request->get('create_user');
$errors = [];
$user->setName($data['name']);
$user->setEmail($data['email']);
$user->setPassword($data['password']);
$user->setCreatedAt(new \DateTime());
$user->setUpdatedAt(new \DateTime());
$form = $this->createForm(CrearUserType::class, $user);
// Entity validation
$errors = $validator->validate($user);
if (count($errors) > 0) { // This is always 0
$user->setPassword('');
return $this->render('user/create.html.twig', [
'form' => $form->createView(),
'user' => $user,
'errors' => $errors
]);
}
try {
$user->setPassword(password_hash($user->getPassword(), PASSWORD_BCRYPT));
$entityManager->persist($user);
$entityManager->flush();
$this->addFlash(
'success',
'User '.$user->getName().' has been saved.'
);
return $this->redirectToRoute('users');
} catch(\Exception $exception) {
$user->setPassword('');
$this->addFlash(
'error',
'Server error: ' . $exception->getMessage()
);
return $this->render('user/create.html.twig', [
'form' => $form->createView(),
'user' => $user,
'errors' => $errors
]);
}
}
And this is my entity:
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #UniqueEntity("email")
*/
class User
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank(message="Name is mandatory")
*/
private $name;
/**
* #ORM\Column(type="string", length=255, unique=true)
* #Assert\NotBlank(message="Email is mandatory")
* #Assert\Email(
* message="Invalid email address"
* )
*/
private $email;
/**
* #ORM\Column(type="string", length=60)
* #Assert\NotBlank(message="Password is mandatory")
* #Assert\GreaterThanOrEqual(
* value=6,
* message="The password has to be at least 6 chars long"
* )
*/
private $password;
/**
* #ORM\Column(type="date")
*/
private $createdAt;
/**
* #ORM\Column(type="date")
*/
private $updatedAt;
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 getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
public function getPassword(): ?string
{
return $this->password;
}
public function setPassword(?string $password): self
{
$this->password = $password;
return $this;
}
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;
}
}
So, any clues on what is wrong here?
I'm not sure of my response, because I never handle request with a ValidatorInterface. I use the handlerequest method provided by controller.
You created the form
$form = $this->createForm(CrearUserType::class, $user);
But you forgot to handle request
$form->handleRequest($request);
So your form does not validate data forwarded by request. Try to add this line just after the $form creation. Then, you can test that the user entity is valid with
if ($form->isValid()) {
//no errors
}
//error exists
I'm trying to display a quiz style form where end-user gets presented with questions that can have one or multiple correct answers.
Entities:
Exam Question
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use Ramsey\Uuid\UuidInterface;
/**
* #ORM\Entity(repositoryClass="App\Repository\ExamQuestionRepository")
*/
class ExamQuestion
{
use TimestampableEntity;
/**
* #var UuidInterface
*
* #ORM\Id
* #ORM\Column(type="uuid", unique=true)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Module", inversedBy="questions")
* #ORM\JoinColumn(nullable=false)
*/
private $module;
/**
* #ORM\Column(type="text")
*/
private $text;
/**
* #ORM\OneToMany(targetEntity="App\Entity\ExamQuestionAnswer", mappedBy="question", orphanRemoval=true, cascade={"persist"})
*/
private $examQuestionAnswers;
/**
* #ORM\Column(type="boolean")
*/
private $hasMultipleAnswers;
public function __construct()
{
$this->examQuestionAnswers = new ArrayCollection();
}
public function __toString()
{
return $this->text;
}
public function getId(): ?UuidInterface
{
return $this->id;
}
public function getModule(): ?Module
{
return $this->module;
}
public function setModule(?Module $module): self
{
$this->module = $module;
return $this;
}
public function getText(): ?string
{
return $this->text;
}
public function setText(string $text): self
{
$this->text = $text;
return $this;
}
/**
* #return Collection|ExamQuestionAnswer[]
*/
public function getExamQuestionAnswers(): Collection
{
return $this->examQuestionAnswers;
}
public function addExamQuestionAnswer(ExamQuestionAnswer $examQuestionAnswer): self
{
if (!$this->examQuestionAnswers->contains($examQuestionAnswer)) {
$this->examQuestionAnswers[] = $examQuestionAnswer;
$examQuestionAnswer->setQuestion($this);
}
return $this;
}
public function removeExamQuestionAnswer(ExamQuestionAnswer $examQuestionAnswer): self
{
if ($this->examQuestionAnswers->contains($examQuestionAnswer)) {
$this->examQuestionAnswers->removeElement($examQuestionAnswer);
// set the owning side to null (unless already changed)
if ($examQuestionAnswer->getQuestion() === $this) {
$examQuestionAnswer->setQuestion(null);
}
}
return $this;
}
public function getHasMultipleAnswers(): ?bool
{
return $this->hasMultipleAnswers;
}
public function setHasMultipleAnswers(bool $hasMultipleAnswers): self
{
$this->hasMultipleAnswers = $hasMultipleAnswers;
return $this;
}
}
Exam Question Answer
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use Ramsey\Uuid\UuidInterface;
/**
* #ORM\Entity(repositoryClass="App\Repository\ExamQuestionRepository")
*/
class ExamQuestionAnswer
{
use TimestampableEntity;
/**
* #var UuidInterface
*
* #ORM\Id
* #ORM\Column(type="uuid", unique=true)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\ExamQuestion", inversedBy="examQuestionAnswers")
* #ORM\JoinColumn(nullable=false)
*/
private $question;
/**
* #ORM\Column(type="boolean")
*/
private $isCorrect;
/**
* #ORM\Column(type="text")
*/
private $text;
/**
* #ORM\Column(type="boolean", nullable=true)
*/
private $selected;
public function __toString()
{
return $this->text;
}
public function getId(): ?UuidInterface
{
return $this->id;
}
public function getQuestion(): ExamQuestion
{
return $this->question;
}
public function setQuestion(ExamQuestion $question): self
{
$this->question = $question;
return $this;
}
public function getIsCorrect(): ?bool
{
return $this->isCorrect;
}
public function setIsCorrect(bool $isCorrect): self
{
$this->isCorrect = $isCorrect;
return $this;
}
public function getText(): ?string
{
return $this->text;
}
public function setText(string $text): self
{
$this->text = $text;
return $this;
}
public function getSelected(): ?bool
{
return $this->selected;
}
public function setSelected(?bool $selected): self
{
$this->selected = $selected;
return $this;
}
}
Exam Take
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use Ramsey\Uuid\UuidInterface;
/**
* #ORM\Entity(repositoryClass="App\Repository\ExamTakeRepository")
*/
class ExamTake
{
use TimestampableEntity;
/**
* #var UuidInterface
*
* #ORM\Id
* #ORM\Column(type="uuid", unique=true)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\ExamQuestion")
*/
private $questions;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Module", inversedBy="examTakes")
* #ORM\JoinColumn(nullable=false)
*/
private $module;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Student", inversedBy="examTakes")
* #ORM\JoinColumn(nullable=false)
*/
private $student;
/**
* #ORM\Column(type="boolean", nullable=true)
*/
private $passed;
/**
* #ORM\OneToMany(targetEntity="App\Entity\ExamQuestionStudentAnswer", mappedBy="examTake", orphanRemoval=true)
*/
private $examQuestionStudentAnswers;
public function __construct()
{
$this->questions = new ArrayCollection();
$this->examQuestionStudentAnswers = new ArrayCollection();
}
public function getId(): ?UuidInterface
{
return $this->id;
}
/**
* #return Collection|ExamQuestion[]
*/
public function getQuestions(): Collection
{
return $this->questions;
}
public function addQuestion(ExamQuestion $question): self
{
if (!$this->questions->contains($question)) {
$this->questions[] = $question;
}
return $this;
}
public function removeQuestion(ExamQuestion $question): self
{
if ($this->questions->contains($question)) {
$this->questions->removeElement($question);
}
return $this;
}
public function getModule(): ?Module
{
return $this->module;
}
public function setModule(?Module $module): self
{
$this->module = $module;
return $this;
}
public function getStudent(): ?Student
{
return $this->student;
}
public function setStudent(?Student $student): self
{
$this->student = $student;
return $this;
}
public function getPassed(): ?bool
{
return $this->passed;
}
public function setPassed(?bool $passed): self
{
$this->passed = $passed;
return $this;
}
/**
* #return Collection|ExamQuestionStudentAnswer[]
*/
public function getExamQuestionStudentAnswers(): Collection
{
return $this->examQuestionStudentAnswers;
}
public function addExamQuestionStudentAnswer(ExamQuestionStudentAnswer $examQuestionStudentAnswer): self
{
if (!$this->examQuestionStudentAnswers->contains($examQuestionStudentAnswer)) {
$this->examQuestionStudentAnswers[] = $examQuestionStudentAnswer;
$examQuestionStudentAnswer->setExamTake($this);
}
return $this;
}
public function removeExamQuestionStudentAnswer(ExamQuestionStudentAnswer $examQuestionStudentAnswer): self
{
if ($this->examQuestionStudentAnswers->contains($examQuestionStudentAnswer)) {
$this->examQuestionStudentAnswers->removeElement($examQuestionStudentAnswer);
// set the owning side to null (unless already changed)
if ($examQuestionStudentAnswer->getExamTake() === $this) {
$examQuestionStudentAnswer->setExamTake(null);
}
}
return $this;
}
}
And the forms:
class ExamTakeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('questions', CollectionType::class, [
'entry_type' => ExamQuestionType::class,
'allow_add' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => ExamTake::class,
]);
}
}
class ExamQuestionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('text', TextType::class, [
'attr' => ['readonly' => true],
])
->add('examQuestionAnswers', CollectionType::class, [
'entry_type' => ExamQuestionAnswerType::class,
'allow_add' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => ExamQuestion::class,
]);
}
}
class ExamQuestionAnswerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('text');
$builder->addEventListener(FormEvents::POST_SET_DATA, static function (FormEvent $event) {
$form = $event->getForm();
/** #var ExamQuestion $question */
$question = $event->getData()->getQuestion();
if ($question->getHasMultipleAnswers()) {
$form
->add('select', ChoiceType::class, [
'expanded' => true,
'multiple' => true,
'mapped' => false,
]);
} else {
$form
->add('select', ChoiceType::class, [
'expanded' => true,
'multiple' => false,
'mapped' => false,
]);
}
});
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => ExamQuestionAnswer::class,
]);
}
}
It seems i can't get the examQuestionAnswers field in collection displayed correctly. What i get is a collection of unrelated fields(inputs) if question has only one answer, and if question has multiple answers checkboxes are not shown. Any help is much appreciated!
The ChoiceType expects the choices attribute"
if ($question->getHasMultipleAnswers()) {
$form
->add('select', ChoiceType::class, [
'choices' => $question->getExamQuestionAnswers(),
'expanded' => true,
'multiple' => true,
'mapped' => false,
]);
} else {
$form
->add('select', ChoiceType::class, [
'choices' => $question->getExamQuestionAnswers(),
'expanded' => true,
'multiple' => false,
'mapped' => false,
]);
}
This will result in putting $examQuestionAnswers as choices to your ChoiceType. In case you get an exception change the type to EntityType(it inherits from ChoiceType) so choices should still work the same. You might also needed to implement either a __toString() method in your ExamQuestionAnswer entity or define choice_label attribute this should solve your problem in case it doesn't add a comment about what does not work and I'll implement it myself
I build a management app of bagages and voyages with Symfony 4, I created a "ManyToMany" relation between this entities.
When I want to add a new bagage with a destination (voyage) I have this error :
I have addVoyage and removeVoyage in my classes Voyage and Bagage.
You will find above my classes Bagage.php, my form BagageType.php and my controller BagageController.php
Bagage.php
<?php
namespace App\Entity;
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="App\Repository\BagageRepository")
*/
class Bagage
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string")
* #Assert\Length(min=5, max=50)
*/
private $nom;
/**
* #ORM\Column(type="json", nullable=true)
*/
private $objets = [];
/**
* #ORM\Column(type="datetime")
*/
private $dateCreation;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Voyage", mappedBy="bagages")
*/
private $voyages;
public function __construct()
{
$this->voyages = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getNom(): ?string
{
return $this->nom;
}
public function setNom(string $nom): self
{
$this->nom = $nom;
return $this;
}
public function getObjets(): ?array
{
return $this->objets;
}
public function setObjets(?array $objets): self
{
$this->objets = $objets;
return $this;
}
public function getDateCreation(): ?\DateTimeInterface
{
return $this->dateCreation;
}
public function setDateCreation(\DateTimeInterface $dateCreation): self
{
$this->dateCreation = $dateCreation;
return $this;
}
/**
* #return Collection|Voyage[]
*/
public function getVoyages(): Collection
{
return $this->voyages;
}
public function addVoyage(Voyage $voyage): self
{
if (!$this->voyages->contains($voyage)) {
$this->voyages[] = $voyage;
$voyage->addBagage($this);
}
return $this;
}
public function removeVoyage(Voyage $voyage): self
{
if ($this->voyages->contains($voyage)) {
$this->voyages->removeElement($voyage);
$voyage->removeBagage($this);
}
return $this;
}
}
?>
BagageType.php
<?php
namespace App\Form;
use App\Entity\Voyage;
use App\Entity\Bagage;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class BagageType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('nom')
->add('voyages', EntityType::class, [
'class' => Voyage::class,
'choice_label' => 'lieu'
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Bagage::class,
]);
}
}
?>
BagageController.php
<?php
/**
* #Route("/bagages/nouveau", name="bagage_creation")
* #Route("/bagages/{id}/edit", name="bagage_edit")
*/
public function form(Bagage $bagage = null, Request $request, ObjectManager $manager)
{
if(!$bagage){
$bagage = new Bagage();
}
$form = $this->createForm(BagageType::class, $bagage);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
if(!$bagage->getId()){
$bagage->setDateCreation(new \Datetime());
}
$manager->persist($bagage);
$manager->flush();
return $this->redirectToRoute('bagage_show', [
'id' => $bagage->getId()
]);
}
return $this->render('bagages/create.html.twig', [
//creation d'une vue pour twig pour afficher le formulaire
'formBagage' => $form->createView(),
'editMode' => $bagage->getId() !== null
]);
}
?>
Does it miss an addVoyage call in my controller ?
EDIT
Voyage.php
<?php
namespace App\Entity;
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="App\Repository\VoyageRepository")
*/
class Voyage
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $lieu;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Bagage", inversedBy="voyages")
*/
private $bagages;
public function __construct()
{
$this->bagages = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getLieu(): ?string
{
return $this->lieu;
}
public function setLieu(string $lieu): self
{
$this->lieu = $lieu;
return $this;
}
/**
* #return Collection|Bagage[]
*/
public function getBagages(): Collection
{
return $this->bagages;
}
public function addBagage(Bagage $bagage): self
{
if (!$this->bagages->contains($bagage)) {
$this->bagages[] = $bagage;
}
return $this;
}
public function removeBagage(Bagage $bagage): self
{
if ($this->bagages->contains($bagage)) {
$this->bagages->removeElement($bagage);
}
return $this;
}
}
It looks like you should set multiple attribute to true, making the field's value an array instead of a single entity.
->add('voyages', EntityType::class, [
'class' => Voyage::class,
'choice_label' => 'lieu',
'multiple' => true,
]);
I've a problem about datas on my form view
The principle is the same as this video (https://www.youtube.com/watch?v=MRfsHix1eRA&t=1257s), that is to say: when I select a "management" I want the "services" associated to this "management".
These must be selected in a "report Form" (Report Entity).
OneOrMany service is linked to One management, and a report is linked to a management and a service.
Entities
Management.php
<?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\ManagementRepository")
*/
class Management
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Service", mappedBy="management")
*/
private $services;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Report", mappedBy="management")
*/
private $reports;
public function __construct()
{
$this->services = new ArrayCollection();
$this->reports = 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;
}
/**
* #return Collection|Service[]
*/
public function getServices(): Collection
{
return $this->services;
}
public function addService(Service $service): self
{
if (!$this->services->contains($service)) {
$this->services[] = $service;
$service->setManagement($this);
}
return $this;
}
public function removeService(Service $service): self
{
if ($this->services->contains($service)) {
$this->services->removeElement($service);
// set the owning side to null (unless already changed)
if ($service->getManagement() === $this) {
$service->setManagement(null);
}
}
return $this;
}
/**
* #return Collection|Report[]
*/
public function getReports(): Collection
{
return $this->reports;
}
public function addReport(Report $report): self
{
if (!$this->reports->contains($report)) {
$this->reports[] = $report;
$report->setManagement($this);
}
return $this;
}
public function removeReport(Report $report): self
{
if ($this->reports->contains($report)) {
$this->reports->removeElement($report);
// set the owning side to null (unless already changed)
if ($report->getManagement() === $this) {
$report->setManagement(null);
}
}
return $this;
}
}
Service.php
<?php
namespace App\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\ReportRepository")
*/
class Report
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="date")
*/
private $assigned_date;
/**
* #ORM\Column(type="decimal", precision=7, scale=2, nullable=true)
*/
private $time;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Game", inversedBy="reports")
*/
private $game;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Management", inversedBy="reports")
*/
private $management;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Service", inversedBy="reports")
*/
private $service;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Format", inversedBy="reports")
*/
private $format;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Category", inversedBy="reports")
* #ORM\JoinColumn(nullable=false)
*/
private $category;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="reports")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
public function getId(): ?int
{
return $this->id;
}
public function getAssignedDate(): ?\DateTimeInterface
{
return $this->assigned_date;
}
public function setAssignedDate(\DateTimeInterface $assigned_date): self
{
$this->assigned_date = $assigned_date;
return $this;
}
public function getTime()
{
return $this->time;
}
public function setTime($time): self
{
$this->time = $time;
return $this;
}
public function getGame(): ?Game
{
return $this->game;
}
public function setGame(?Game $game): self
{
$this->game = $game;
return $this;
}
public function getManagement(): ?Management
{
return $this->management;
}
public function setManagement(?Management $management): self
{
$this->management = $management;
return $this;
}
public function getService(): ?Service
{
return $this->service;
}
public function setService(?Service $service): self
{
$this->service = $service;
return $this;
}
public function getFormat(): ?Format
{
return $this->format;
}
public function setFormat(?Format $format): self
{
$this->format = $format;
return $this;
}
public function getCategory(): ?Category
{
return $this->category;
}
public function setCategory(?Category $category): self
{
$this->category = $category;
return $this;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
}
Report.php
<?php
namespace App\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\ReportRepository")
*/
class Report
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="date")
*/
private $assigned_date;
/**
* #ORM\Column(type="decimal", precision=7, scale=2, nullable=true)
*/
private $time;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Game", inversedBy="reports")
*/
private $game;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Management", inversedBy="reports")
*/
private $management;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Service", inversedBy="reports")
*/
private $service;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Format", inversedBy="reports")
*/
private $format;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Category", inversedBy="reports")
* #ORM\JoinColumn(nullable=false)
*/
private $category;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="reports")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
public function getId(): ?int
{
return $this->id;
}
public function getAssignedDate(): ?\DateTimeInterface
{
return $this->assigned_date;
}
public function setAssignedDate(\DateTimeInterface $assigned_date): self
{
$this->assigned_date = $assigned_date;
return $this;
}
public function getTime()
{
return $this->time;
}
public function setTime($time): self
{
$this->time = $time;
return $this;
}
public function getGame(): ?Game
{
return $this->game;
}
public function setGame(?Game $game): self
{
$this->game = $game;
return $this;
}
public function getManagement(): ?Management
{
return $this->management;
}
public function setManagement(?Management $management): self
{
$this->management = $management;
return $this;
}
public function getService(): ?Service
{
return $this->service;
}
public function setService(?Service $service): self
{
$this->service = $service;
return $this;
}
public function getFormat(): ?Format
{
return $this->format;
}
public function setFormat(?Format $format): self
{
$this->format = $format;
return $this;
}
public function getCategory(): ?Category
{
return $this->category;
}
public function setCategory(?Category $category): self
{
$this->category = $category;
return $this;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
}
FormType
ReportType.php
<?php
namespace App\Form;
use App\Entity\Report;
use App\Entity\Game;
use App\Entity\Management;
use App\Entity\Service;
use App\Entity\Format;
use App\Entity\Category;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
class ReportType extends AbstractType
{
protected $tokenStorage;
public function __construct(TokenStorage $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$user = $this->tokenStorage->getToken()->getUser();
$builder->add('assigned_date', DateType::class, array(
'widget' => 'single_text',
// prevents rendering it as type="date", to avoid HTML5 date pickers
'html5' => false,
// adds a class that can be selected in JavaScript
'attr' => ['class' => 'js-datepicker'],
));
$builder->add('time');
$builder->add('game', EntityType::class, array(
'class' => Game::class,
'placeholder' => '',
'choice_label' => 'name'));
$builder->add('management', EntityType::class, array(
'class' => Management::class,
'placeholder' => 'Select a management',
'mapped' => false,
'choice_label' => 'name'));
$builder->get('management')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event)
{
$form = $event->getForm();
$form->getParent()->add('service',EntityType::class, array(
'class' => Service::class,
'placeholder' => 'Select a service',
'choices' => $form->getData()->getServices()
));
}
);
$builder->add('format', EntityType::class, array(
'class' => Format::class,
'placeholder' => '',
'choice_label' => 'name'));
$builder->add('category', EntityType::class, array(
'class' => Category::class,
'placeholder' => '',
'choice_label' => 'name'));
$builder->add('user', EntityType::class, array(
'class' => User::class,
'query_builder' => function (EntityRepository $er) {
$user = $this->tokenStorage->getToken()->getUser();
return $er->createQueryBuilder('u')
->Where('u.id='. $user->getId());
},
'choice_label' => 'id',
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Report::class,
]);
}
}
Twig associated
_form.twig.php
<?php
namespace App\Controller;
use App\Entity\Report;
use App\Form\ReportType;
use App\Repository\ReportRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* #Route("/report")
*/
class ReportController extends AbstractController
{
/**
* #Route("/", name="report_index", methods="GET")
*/
public function index(ReportRepository $reportRepository): Response
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$user = $this->getUser()->getId();
return $this->render('report/index.html.twig', ['reports' => $reportRepository->findAllByIdUser($user)]);
}
/**
* #Route("/new", name="report_new", methods="GET|POST")
*/
public function new(Request $request): Response
{
$report = new Report();
$form = $this->createForm(ReportType::class, $report);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($report);
$em->flush();
return $this->redirectToRoute('report_index');
}
return $this->render('report/new.html.twig', [
'report' => $report,
'form' => $form->createView(),
]);
}
/**
* #Route("/{id}", name="report_show", methods="GET")
*/
public function show(Report $report): Response
{
return $this->render('report/show.html.twig', ['report' => $report]);
}
/**
* #Route("/{id}/edit", name="report_edit", methods="GET|POST")
*/
public function edit(Request $request, Report $report): Response
{
$form = $this->createForm(ReportType::class, $report);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('report_edit', ['id' => $report->getId()]);
}
return $this->render('report/edit.html.twig', [
'report' => $report,
'form' => $form->createView(),
]);
}
/**
* #Route("/{id}", name="report_delete", methods="DELETE")
*/
public function delete(Request $request, Report $report): Response
{
if ($this->isCsrfTokenValid('delete'.$report->getId(), $request->request->get('_token'))) {
$em = $this->getDoctrine()->getManager();
$em->remove($report);
$em->flush();
}
return $this->redirectToRoute('report_index');
}
}
When I dump() my $form->getData()->getServices(), this return a collection. but i've this message : Neither the property "service" nor one of the methods "service()", "getservice()"/"isservice()"/"hasservice()" or "__call()" exist and have public access in class "Symfony\Component\Form\FormView".
on {{ form_widget(form.service, { 'attr': {'placeholder': "Service"} }) }}
Some people have a solution ?
I can provide more information if you need it
Thank you.