Add selectbox with values of relation in Symfony - php

I'm a Symfony beginner and I'm eager to learn more about Symfony, that's why I started a learning project.
I have a many-to-one relation (Type has many Games, but Game has 1 Type), and I would like to show a select box with a list of "types" to choose from.
I get this error:
Could not load type "AppBundle\Form\choiceType"
This is my code:
GameController.php
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\Game;
use AppBundle\Entity\Type;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;use Symfony\Component\HttpFoundation\Request;
/**
* Game controller.
*
* #Route("game")
*/
class GameController extends Controller
{
/**
* Lists all game entities.
*
* #Route("/", name="game_index")
* #Method("GET")
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$games = $em->getRepository('AppBundle:Game')->findAll();
return $this->render('game/index.html.twig', array(
'games' => $games,
));
}
/**
* Creates a new game entity.
*
* #Route("/new", name="game_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request)
{
$type = new Type();
$type->setName('test');
$game = new Game();
$game->setType($type);
$form = $this->createForm('AppBundle\Form\GameType', $game);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($type);
$em->persist($game);
$em->flush($game);
return $this->redirectToRoute('game_show', array('id' => $game->getId()));
}
return $this->render('game/new.html.twig', array(
'game' => $game,
'form' => $form->createView(),
));
}
/**
* Finds and displays a game entity.
*
* #Route("/{id}", name="game_show")
* #Method("GET")
*/
public function showAction(Game $game)
{
$deleteForm = $this->createDeleteForm($game);
return $this->render('game/show.html.twig', array(
'game' => $game,
'delete_form' => $deleteForm->createView(),
));
}
/**
* Displays a form to edit an existing game entity.
*
* #Route("/{id}/edit", name="game_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Game $game)
{
$deleteForm = $this->createDeleteForm($game);
$editForm = $this->createForm('AppBundle\Form\GameType', $game);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('game_edit', array('id' => $game->getId()));
}
return $this->render('game/edit.html.twig', array(
'game' => $game,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
/**
* Deletes a game entity.
*
* #Route("/{id}", name="game_delete")
* #Method("DELETE")
*/
public function deleteAction(Request $request, Game $game)
{
$form = $this->createDeleteForm($game);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->remove($game);
$em->flush($game);
}
return $this->redirectToRoute('game_index');
}
/**
* Creates a form to delete a game entity.
*
* #param Game $game The game entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm(Game $game)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('game_delete', array('id' => $game->getId())))
->setMethod('DELETE')
->getForm()
;
}
}
Update #3:
GameType.php
<?php
namespace AppBundle\Form;
use Doctrine\DBAL\Types\TextType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class GameType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add( 'name', EntityType::class, [
'class' => 'AppBundle:Game',
'choice_label' => 'name',
'label' => 'name'
] );
$builder
->add( 'name', EntityType::class, [
'class' => 'AppBundle:Type',
'choice_label' => 'name',
'multiple' => false,
'expanded' => false
] );
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Game'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_game';
}
}
Entity\Game.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Game
*
* #ORM\Table(name="game")
* #ORM\Entity(repositoryClass="AppBundle\Repository\GameRepository")
*/
class Game
{
/**
* #ORM\ManyToOne(targetEntity="Type", inversedBy="games")
* #ORM\JoinColumn(name="type_id", referencedColumnName="id")
*/
private $type;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Game
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #return mixed
*/
public function getType()
{
return $this->type;
}
/**
* #param mixed $type
*/
public function setType($type)
{
$this->type = $type;
}
}
Entity\Type.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Type
*
* #ORM\Table(name="type")
* #ORM\Entity(repositoryClass="AppBundle\Repository\TypeRepository")
*/
class Type
{
/**
* #ORM\OneToMany(targetEntity="Game", mappedBy="type")
*/
private $games;
public function __construct()
{
$this->games = new ArrayCollection();
}
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Type
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #return mixed
*/
public function getGames()
{
return $this->games;
}
/**
* #param mixed $games
*/
public function setGames($games)
{
$this->games = $games;
}
}

If you are using the symfony form component, then choiceType isn't correct. the class is ChoiceType, simply capitalize the C and add the use statement above class declaration.
EDIT: You also need to update your form component type if you are selecting an existing entity for that field.
use Symfony\Component\Form\Extension\Core\Type\EntityType;
class GameType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name');
$builder
->add( 'types', EntityType::class, [
'class' => 'AppBundle:Type',
'choice_label' => 'name',
'multiple' => false,
'expanded' => true
] );
}
extra reading on using entities and forms Symfony Docs

In your case, You should use EntityType instead of ChoiseType.
$builder
->add('type', EntityType::class, [
'class' => Type::class,
'choice_label' => 'name',
'multiple' => false,
'expanded' => true
Be aware of upper or lower.

Related

Symfony error when inserting new game: Entities passed to the choice field must be managed. Maybe persist them in the entity manager?

I'm trying to save a Game object with a Type object.
When I try to save with the form I get the following error:
Entities passed to the choice field must be managed. Maybe persist them in the entity manager?
One Game can have 1 Type while Type can have many Games.
I've managed to isolate the problem, it works fine when Game doesn't have a relation with Type so I think it has to do something with the Type object.
GameController:
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\Game;
use AppBundle\Entity\Type;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
/**
* Game controller.
*
* #Route("game")
*/
class GameController extends Controller
{
/**
* Lists all game entities.
*
* #Route("/", name="game_index")
* #Method("GET")
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$games = $em->getRepository('AppBundle:Game')->findAll();
return $this->render('game/index.html.twig', array(
'games' => $games,
));
}
/**
* Creates a new game entity.
*
* #Route("/new", name="game_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request)
{
$type = new Type();
$game = new Game();
$game->setType($type);
$form = $this->createForm('AppBundle\Form\GameType', $game);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($type);
$em->persist($game);
$em->flush($game);
return $this->redirectToRoute('game_show', array('id' => $game->getId()));
}
return $this->render('game/new.html.twig', array(
'game' => $game,
'form' => $form->createView(),
));
}
/**
* Finds and displays a game entity.
*
* #Route("/{id}", name="game_show")
* #Method("GET")
*/
public function showAction(Game $game)
{
$deleteForm = $this->createDeleteForm($game);
return $this->render('game/show.html.twig', array(
'game' => $game,
'delete_form' => $deleteForm->createView(),
));
}
/**
* Displays a form to edit an existing game entity.
*
* #Route("/{id}/edit", name="game_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Game $game)
{
$deleteForm = $this->createDeleteForm($game);
$editForm = $this->createForm('AppBundle\Form\GameType', $game);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('game_edit', array('id' => $game->getId()));
}
return $this->render('game/edit.html.twig', array(
'game' => $game,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
/**
* Deletes a game entity.
*
* #Route("/{id}", name="game_delete")
* #Method("DELETE")
*/
public function deleteAction(Request $request, Game $game)
{
$form = $this->createDeleteForm($game);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->remove($game);
$em->flush($game);
}
return $this->redirectToRoute('game_index');
}
/**
* Creates a form to delete a game entity.
*
* #param Game $game The game entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm(Game $game)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('game_delete', array('id' => $game->getId())))
->setMethod('DELETE')
->getForm()
;
}
}
GameType.php:
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class GameType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add( 'name', TextType::class);
$builder
->add( 'type', EntityType::class, [
'class' => 'AppBundle:Type',
'choice_label' => 'name',
'multiple' => false,
'expanded' => false
] );
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Game'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_game';
}
}
TypeType.php
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class TypeType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name') ;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Type'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_type';
}
}
Game.php:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Game
*
* #ORM\Table(name="game")
* #ORM\Entity(repositoryClass="AppBundle\Repository\GameRepository")
*/
class Game
{
/**
* #ORM\ManyToOne(targetEntity="Type", inversedBy="games")
* #ORM\JoinColumn(name="type_id", referencedColumnName="id")
*/
private $type;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Game
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #return mixed
*/
public function getType()
{
return $this->type;
}
/**
* #param mixed $type
*/
public function setType($type)
{
$this->type = $type;
}
}
Type.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Type
*
* #ORM\Table(name="type")
* #ORM\Entity(repositoryClass="AppBundle\Repository\TypeRepository")
*/
class Type
{
/**
* #ORM\OneToMany(targetEntity="Game", mappedBy="type")
*/
private $games;
public function __construct()
{
$this->games = new ArrayCollection();
}
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Type
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #return mixed
*/
public function getGames()
{
return $this->games;
}
/**
* #param mixed $games
*/
public function setGames($games)
{
$this->games = $games;
}
public function addGame(Game $game)
{
$this->games->add($game);
$game->setType($this);
}
public function removeGame(Game $game)
{
$this->games->removeElement($game);
}
}
game/new.html.twig:
{% extends 'base.html.twig' %}
{% block content%}
<h1>Game creation</h1>
{{ form_start(form) }}
{{ form_widget(form.name) }}
{{ form_widget(form.type) }}
<input type="submit" value="Create" />
{{ form_end(form) }}
<ul>
<li>
Back to the list
</li>
</ul>
{% endblock %}
In the new action you should comment 3 lines
//$type = new Type();
$game = new Game();
//$game->setType($type);
$form = $this->createForm('AppBundle\Form\GameType', $game);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
//$em->persist($type);
$em->persist($game);
$em->flush($game);
The type will be set from framework because you are going to choose it from the dropdown

Symfony error ("Catchable Fatal Error: Object of class Proxies__CG__\AppBundle\Entity\Type could not be converted to string")

I want the Game entity to appear with the related Type.
It's a many-to-one relation for the Game and a one-to-many for the Type entity.
So a Game can have 1 Type while a Type can have many Games.
I get the error:
An exception has been thrown during the rendering of a template
("Catchable Fatal Error: Object of class
Proxies__CG__\AppBundle\Entity\Type could not be converted to
string").
When scrolling down it points to line 14 of the views/game/show.html.twig file:
<tr>
<th>Type</th>
<td>{{ game.type}}</td>
</tr>
The question: how do I show the current Game's Type in the view?
Here's my code:
GameController.php:
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\Game;
use AppBundle\Entity\Type;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
/**
* Game controller.
*
* #Route("game")
*/
class GameController extends Controller
{
/**
* Lists all game entities.
*
* #Route("/", name="game_index")
* #Method("GET")
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$games = $em->getRepository('AppBundle:Game')->findAll();
return $this->render('game/index.html.twig', array(
'games' => $games,
));
}
/**
* Creates a new game entity.
*
* #Route("/new", name="game_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request)
{
$game = new Game();
$form = $this->createForm('AppBundle\Form\GameType', $game);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($game);
$em->flush($game);
return $this->redirectToRoute('game_show', array('id' => $game->getId()));
}
return $this->render('game/new.html.twig', array(
'game' => $game,
'form' => $form->createView(),
));
}
/**
* Finds and displays a game entity.
*
* #Route("/{id}", name="game_show")
* #Method("GET")
* #inheritdoc
*
*/
public function showAction(Game $game, Type $type)
{
$deleteForm = $this->createDeleteForm($game);
return $this->render('game/show.html.twig', array(
'game' => $game,
'type' => $game->getType(),
'delete_form' => $deleteForm->createView(),
));
}
/**
* Displays a form to edit an existing game entity.
*
* #Route("/{id}/edit", name="game_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Game $game)
{
$deleteForm = $this->createDeleteForm($game);
$editForm = $this->createForm('AppBundle\Form\GameType', $game);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('game_show', array('id' => $game->getId()));
}
return $this->render('game/edit.html.twig', array(
'game' => $game,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
/**
* Deletes a game entity.
*
* #Route("/{id}", name="game_delete")
* #Method("DELETE")
*/
public function deleteAction(Request $request, Game $game)
{
$form = $this->createDeleteForm($game);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->remove($game);
$em->flush($game);
}
return $this->redirectToRoute('game_index');
}
/**
* Creates a form to delete a game entity.
*
* #param Game $game The game entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm(Game $game)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('game_delete', array('id' => $game->getId())))
->setMethod('DELETE')
->getForm()
;
}
}
TypeController.php
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\Type;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;use Symfony\Component\HttpFoundation\Request;
/**
* Type controller.
*
* #Route("type")
*/
class TypeController extends Controller
{
/**
* Lists all type entities.
*
* #Route("/", name="type_index")
* #Method("GET")
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$types = $em->getRepository('AppBundle:Type')->findAll();
return $this->render('type/index.html.twig', array(
'types' => $types,
));
}
/**
* Creates a new type entity.
*
* #Route("/new", name="type_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request)
{
$type = new Type();
$form = $this->createForm('AppBundle\Form\TypeType', $type);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($type);
$em->flush($type);
return $this->redirectToRoute('type_show', array('id' => $type->getId()));
}
return $this->render('type/new.html.twig', array(
'type' => $type,
'form' => $form->createView(),
));
}
/**
* Finds and displays a type entity.
*
* #Route("/{id}", name="type_show")
* #Method("GET")
*/
public function showAction(Type $type)
{
$deleteForm = $this->createDeleteForm($type);
return $this->render('type/show.html.twig', array(
'type' => $type,
'delete_form' => $deleteForm->createView(),
));
}
/**
* Displays a form to edit an existing type entity.
*
* #Route("/{id}/edit", name="type_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Type $type)
{
$deleteForm = $this->createDeleteForm($type);
$editForm = $this->createForm('AppBundle\Form\TypeType', $type);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('type_edit', array('id' => $type->getId()));
}
return $this->render('type/edit.html.twig', array(
'type' => $type,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
/**
* Deletes a type entity.
*
* #Route("/{id}", name="type_delete")
* #Method("DELETE")
*/
public function deleteAction(Request $request, Type $type)
{
$form = $this->createDeleteForm($type);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->remove($type);
$em->flush($type);
}
return $this->redirectToRoute('type_index');
}
/**
* Creates a form to delete a type entity.
*
* #param Type $type The type entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm(Type $type)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('type_delete', array('id' => $type->getId())))
->setMethod('DELETE')
->getForm()
;
}
}
Game.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Game
*
* #ORM\Table(name="game")
* #ORM\Entity(repositoryClass="AppBundle\Repository\GameRepository")
*/
class Game
{
/**
* #ORM\ManyToOne(targetEntity="Type", inversedBy="games")
* #ORM\JoinColumn(name="type_id", referencedColumnName="id")
*/
private $type;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
* #Assert\NotBlank()
* #Assert\Length(
* min = "3",
* max = "100"
* )
* #ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Game
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #return mixed
*/
public function getType()
{
return $this->type;
}
/**
* #param mixed $type
*/
public function setType($type)
{
$this->type = $type;
}
}
Type.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Type
*
* #ORM\Table(name="type")
* #ORM\Entity(repositoryClass="AppBundle\Repository\TypeRepository")
*/
class Type
{
/**
* #ORM\OneToMany(targetEntity="Game", mappedBy="type")
*/
private $games;
public function __construct()
{
$this->games = new ArrayCollection();
}
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Type
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #return mixed
*/
public function getGames()
{
return $this->games;
}
/**
* #param mixed $games
*/
public function setGames($games)
{
$this->games = $games;
}
public function addGame(Game $game)
{
$this->games->add($game);
$game->setType($this);
}
public function removeGame(Game $game)
{
$this->games->removeElement($game);
}
}
GameType.php
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class GameType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add( 'name', TextType::class, [
'attr' => [
'class' => 'form-control',
],
] );
$builder
->add( 'type', EntityType::class, [
'class' => 'AppBundle:Type',
'choice_label' => 'name',
'multiple' => false,
'expanded' => false,
'attr' => [
'class' => 'form-control',
],
] );
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Game'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_game';
}
}
TypeType.php
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class TypeType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name') ;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Type'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_type';
}
}
You need a bit of work in your GameType.php form:
// GameType.php
$builder
->add('type', EntityType::class, [
'class' => 'AppBundle:Type',
'placeholder' => ' ',
'choice_label' => function($type){
return $type->getName();
},
'multiple' => false, // a user can select only one option per submission
'expanded' => false // options will be presented in a <select> element; set this to true, to present the data as checkboxes
])
;
Then, in the game/show.html.twig template:
{{ game.type.name }} //supposing you want to display the name column of the types table
Tip: Each time you are facing this kind of trouble, you can use {{ dump(variable) }} to check what's inside it.
For a full ManyToOne example check this article.

Symfony form choice default value from entity

I having some troubles with symfony forms.
I have a boolean variable in my entity. I cant find a way to set that value in my form when i'm editing it.
Entity
namespace AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="pages")
*/
class Page
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="text")
*/
protected $slug;
/**
* #ORM\Column(type="text")
*/
protected $title;
/**
* #ORM\Column(type="text")
*/
protected $content;
/**
* #ORM\Column(name="creation_date", type="datetime")
*/
protected $creationDate;
/**
* #ORM\Column(name="is_active", type="boolean")
*/
protected $isActive = true;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set slug
*
* #param string $slug
*
* #return Page
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get slug
*
* #return string
*/
public function getSlug()
{
return $this->slug;
}
/**
* Set title
*
* #param string $title
*
* #return Page
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set content
*
* #param string $content
*
* #return Page
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set creationDate
*
* #param \DateTime $creationDate
*
* #return Page
*/
public function setCreationDate($creationDate)
{
$this->creationDate = $creationDate;
return $this;
}
/**
* Get creationDate
*
* #return \DateTime
*/
public function getCreationDate()
{
return $this->creationDate;
}
/**
* Set isActive
*
* #param boolean $isActive
*
* #return Page
*/
public function setIsActive($isActive)
{
$this->isActive = $isActive;
return $this;
}
/**
* Get isActive
*
* #return boolean
*/
public function getIsActive()
{
return $this->isActive;
}
}
Controller function
public function editAction(Request $request, $pageId)
{
$em = $this->getDoctrine()->getManager();
$page = $em->getRepository('AdminBundle:Page')->find($pageId);
$form = $this->createForm('app_page', $page);
$form->handleRequest($request);
if($form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
$em->persist($page);
$em->flush();
return $this->redirectToRoute('admin_pages');
}
return $this->render('AdminBundle::editPage.html.twig', array('page' => $page, 'form' => $form->createView()));
}
PageFormType
namespace AdminBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class PageFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('title', 'text');
$builder->add('slug', 'text');
$builder->add('isActive', 'choice', array('choices' => array(
'true' => 'Active',
'false' => 'Inactive'
)));
$builder->add('content', 'textarea', array('required' => false));
}
public function getName()
{
return 'app_page';
}
}
As you can see i wrote my self in FormType class what choices it has. And now it is always displaying "Active" instead of displaying Entity's value. How should i do that?
Thanks!
Try to change your isActive in the Entity like
protected $isActive ;
and if you want to make a default true you can make it at the construct like :
public function __construct() {
$this->setIsActive = true ;
}
$builder->add('isActive', ChoiceType::class, [
'choices' => [
'Active' => true,
'Inactive' => false
],
'choices_as_values' => true,
'multiple' => true,
'expanded' => true,
'data' => array(true)
]);
A 2021 Solution: setting form choice default value (data) from entity
Sometimes your choice field values exist in another entity table, which means you cannot populate them using default form data_class. You only get the string value that saved in the form entity, resulting to error: ...must be an object or null, string given...
protected EntityManagerInterface $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
// In your form
->add('membershipType', EntityType::class, [
'class' => MembershipType::class,
'label' => 'Enter membership type here',
'data' => $this->em->getRepository(MembershipType::class)->findOneBy([
'name' => $your_value_here
])
])

Symfony Form Entity ManyToMany

I have entity Skill and ManyToMany Language and I need create form and in this form add name language for skill, now I add 'multiple'=>true, in form and have many language but when $form->isValid() I have error
This value is not valid.
in form
invalidValue={array}[2]
0 = "Java"
1 = "PHP"
Why in not valid I not understand ??
rhis is my entity skill
class Skill
{
use Timestampable;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="skill", type="string", length=255)
*/
private $skill;
/**
* #var \Artel\ProfileBundle\Entity\CodeDirectoryProgramLanguages
*
* #ORM\ManyToMany(targetEntity="CodeDirectoryProgramLanguages", inversedBy="skills")
*/
protected $language;
/**
* Constructor
*/
public function __construct()
{
$this->language = new \Doctrine\Common\Collections\ArrayCollection();
$this->platforms = new \Doctrine\Common\Collections\ArrayCollection();
$this->specialities = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add language
*
* #param \Artel\ProfileBundle\Entity\CodeDirectoryProgramLanguages $language
*
* #return Skill
*/
public function addLanguage(\Artel\ProfileBundle\Entity\CodeDirectoryProgramLanguages $language)
{
$this->language[] = $language;
return $this;
}
/**
* Remove language
*
* #param \Artel\ProfileBundle\Entity\CodeDirectoryProgramLanguages $language
*/
public function removeLanguage(\Artel\ProfileBundle\Entity\CodeDirectoryProgramLanguages $language)
{
$this->language->removeElement($language);
}
/**
* Get language
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getLanguage()
{
return $this->language;
}
this is my form
class SkillType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('mainSkill','checkbox');
$builder->add('language','entity',
array(
'class'=>'Artel\ProfileBundle\Entity\CodeDirectoryProgramLanguages',
'multiple'=>true,
'property'=>'languages', )
);
$builder
->add('edit','submit')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Artel\ProfileBundle\Entity\Skill',
'csrf_protection' => false
));
}
/**
* #return string
*/
public function getName()
{
return 'skill';
}
}
and my action when $form->submit($request); have this error
/**
* Edit skill.
*
* #Route("/skills/edit/{id}", name="skill_edit")
* #Method({"GET", "POST"})
* #Template()
* #ParamConverter("entity", class="MyBundle:Skill")
*/
public function editSkillsAction(Request $request, $entity)
{
$em = $this->getDoctrine()->getManager();
$form = $this->createForm(new SkillType(), $entity);
if ($request->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('skills_show'));
}
}
return array(
'debug' => true,
'form' => $form->createView(),
);
}
UPDATE
I try to add in form 'expanded' => true, and everything work, doctrine create ManyToMany relation in table. Maybe is there another way ?
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('mainSkill','checkbox');
$builder->add('language','entity',
array(
'class'=>'Artel\ProfileBundle\Entity\CodeDirectoryProgramLanguages',
'property'=>'languages',
'multiple'=>true,
'expanded' => true,
)
);
$builder
->add('edit','submit')
;
}

SonataAdminBundle embedded forms sonata_admin_type issue

I've got two entities, Quiz and QuizQuestion, with manyToMany one-sided relation. I'd like to embed questions form in quiz form. I'm on 2.0 branches. I am able to get sonata_type_model to work, getting list of id's in a dropdown, and working "add" button. However, I'm getting an error when trying to use sonata_type_admin:
Neither property "title" nor method "getTitle()" nor method "isTitle()" exists in class "Doctrine\ORM\PersistentCollection"
500 Internal Server Error - InvalidPropertyException
Here's my Quiz entity:
<?php
namespace Some\SiteBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Some\SiteBundle\Entity\Quiz
* #ORM\Table(name="quiz")
* #ORM\Entity(repositoryClass="Some\SiteBundle\Entity\QuizRepository")
*/
class Quiz
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="datetime", name="created_at")
*
* #var DateTime $createdAt
*/
protected $createdAt;
/**
* #var string $title
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string $body
*
* #ORM\Column(name="body", type="text")
*/
private $body;
/**
* #var QuizQuestion questions
* #ORM\ManyToMany(targetEntity="QuizQuestion", cascade={"persist", "remove"} )
**/
protected $questions;
public function __construct() {
$this->questions = new ArrayCollection();
$this->createdAt = new \DateTime();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Get quiz body.
*
* #return string body
*/
public function getBody()
{
return $this->body;
}
/**
* Sets body
*
* #param string $value body
*/
public function setBody($body)
{
$this->body = $body;
}
/**
* Gets an object representing the date and time the quiz was created.
*
* #return DateTime A DateTime object
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* Add questions
*
* #param Some\SiteBundle\Entity\QuizQuestion $questions
*/
public function addQuestion(\Some\SiteBundle\Entity\QuizQuestion $question)
{
$this->questions[] = $question;
}
/**
* set question
*
* #param Some\SiteBundle\Entity\QuizQuestion $questions
*/
public function setQuestion(\Some\SiteBundle\Entity\QuizQuestion $question)
{
foreach ($this->questions as $doc) {
$this->questions->removeElement($doc);
}
$this->questions[] = $question;
}
/**
* Get questions
*
* #return Doctrine\Common\Collections\Collection
*/
public function getQuestions()
{
return $this->questions;
}
/**
* #ORM\PrePersist
*/
public function beforePersist()
{
//$this->setCreatedAt(new \DateTime());
//$this->setModifiedAt(new \DateTime());
}
public function __toString()
{
return 'Quiz';
}
}
And QuizQuestion entity:
<?php
namespace Some\SiteBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Some\SiteBundle\Entity\QuizQuestion
* #ORM\Table(name="quiz_question")
* #ORM\Entity
*/
class QuizQuestion
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="datetime", name="created_at")
*
* #var DateTime $createdAt
*/
protected $createdAt;
/**
* #var string $title
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string $body
*
* #ORM\Column(name="body", type="text")
*/
private $body;
/**
* #var string $answer1
*
* #ORM\Column(name="answer1", type="text")
*/
private $answer1;
/**
* #var string $answer2
*
* #ORM\Column(name="answer2", type="text")
*/
private $answer2;
/**
* #var string $answer3
*
* #ORM\Column(name="answer3", type="text")
*/
private $answer3;
/**
* #var string $answer4
*
* #ORM\Column(name="answer4", type="text")
*/
private $answer4;
/**
* #var string $correctAnswer
*
* #ORM\Column(name="correct_answer", type="integer", length="1")
*/
private $correctAnswer;
public function __construct() {
$this->createdAt = new \DateTime();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Get question body.
*
* #return string body
*/
public function getBody()
{
return $this->body;
}
/**
* Sets body
*
* #param string $value body
*/
public function setBody($body)
{
$this->body = $body;
}
/**
* Get question answer1.
*
* #return string answer1
*/
public function getAnswer1()
{
return $this->answer1;
}
/**
* Sets answer1
*
* #param string $value answer1
*/
public function setAnswer1($answer1)
{
$this->answer1 = $answer1;
}
/**
* Get question answer2.
*
* #return string answer2
*/
public function getAnswer2()
{
return $this->answer2;
}
/**
* Sets answer2
*
* #param string $value answer2
*/
public function setAnswer2($answer2)
{
$this->answer2 = $answer2;
}
/**
* Get question answer3.
*
* #return string answer3
*/
public function getAnswer3()
{
return $this->answer3;
}
/**
* Sets answer3
*
* #param string $value answer3
*/
public function setAnswer3($answer3)
{
$this->answer3 = $answer3;
}
/**
* Get question answer4.
*
* #return string answer4
*/
public function getAnswer4()
{
return $this->answer4;
}
/**
* Sets answer4
*
* #param string $value answer4
*/
public function setAnswer4($answer4)
{
$this->answer4 = $answer4;
}
/**
* Get question correctAnswer.
*
* #return string correctAnswer
*/
public function getCorrectAnswer()
{
return $this->correctAnswer;
}
/**
* Sets answer1
*
* #param string $value correctAnswer
*/
public function setCorrectAnswer($correctAnswer)
{
$this->correctAnswer = $correctAnswer;
}
/**
* Gets an object representing the date and time the question was created.
*
* #return DateTime A DateTime object
*/
public function getCreatedAt()
{
return $this->createdAt;
}
public function __toString()
{
return $this->title;
}
}
And relevant admin classes. QuizAdmin first:
<?php
namespace Some\SiteBundle;
use Some\SiteBundle\Form\QuizQuestionType;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Validator\ErrorElement;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\Request;
class QuizAdmin extends Admin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('title', NULL, array('label' => 'tytuł:'))
->add('body', NULL, array('label' => 'opis:', 'required' => false, 'attr' => array(
'class' => 'tinymce', 'data-theme' => 'simple')
))
->add('questions', 'sonata_type_admin', array(), array('required' => false, 'edit' => 'inline'));
//->add('categories', NULL, array('label' => 'kategorie:'))
;
}
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper
->add('title')
->add('body')
;
}
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('id')
->add('title')
->add('_action', 'actions', array(
'actions' => array(
'view' => array(),
'edit' => array(),
)
))
//->add('body')
;
}
public function validate(ErrorElement $errorElement, $object)
{
$errorElement
->with('title')
->assertMinLength(array('limit' => 2))
->end()
;
}
}
And QuizQuestionAdmin:
<?php
namespace Some\SiteBundle;
use Some\SiteBundle\Form\QuizQuestionType;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Validator\ErrorElement;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\Request;
class QuizAdmin extends Admin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('title', NULL, array('label' => 'tytuł:'))
->add('body', NULL, array('label' => 'opis:', 'required' => false, 'attr' => array(
'class' => 'tinymce', 'data-theme' => 'simple')
))
->add('questions', 'sonata_type_admin', array(), array('required' => false, 'edit' => 'inline'));
//->add('categories', NULL, array('label' => 'kategorie:'))
;
}
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper
->add('title')
->add('body')
;
}
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('id')
->add('title')
->add('_action', 'actions', array(
'actions' => array(
'view' => array(),
'edit' => array(),
)
))
//->add('body')
;
}
public function validate(ErrorElement $errorElement, $object)
{
$errorElement
->with('title')
->assertMinLength(array('limit' => 2))
->end()
;
}
}
I tried to register quizQuestion as a service, but then I get Expected argument of type "Festus\SiteBundle\Entity\QuizQuestion", "Doctrine\ORM\PersistentCollection" given
500 Internal Server Error - UnexpectedTypeException
Couldn't find any solution after few hours of looking things up...
Ok, I solved it replacing this:
->add('questions', 'sonata_type_admin', array(), array('required' => false, 'edit' => 'inline'));
with this:
->add('questions','collection', array( 'type' => new QuizQuestionType(),
'allow_add' => true,
'prototype' => true,
'by_reference' => true,
));
but for me it looks rather like a workaround than solution :-|
Anyway, I checked bundles versions, symfony branch and found nothing incoherent, everything is on 2.0 branch.
PS - QuizQuestionType class, if anyone's interested... nothing fancy here, just regular form class:
<?php
namespace Some\SiteBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class QuizQuestionType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('title', NULL, array('label' => 'pytanie:'))
->add('body', NULL, array('label' => 'treść:', 'attr' => array(
'class' => 'tinymce', 'data-theme' => 'simple')
))
->add('answer1', NULL, array('label' => 'odp. 1:'))
->add('answer2', NULL, array('label' => 'odp. 2:'))
->add('answer3', NULL, array('label' => 'odp. 3:'))
->add('answer4', NULL, array('label' => 'odp. 4:'))
->add('correctAnswer', NULL, array('label' => 'prawidłowa odp.:'))
;
}
public function getName()
{
return 'quiz_question';
}
public function getDefaultOptions(array $options){
return array('data_class' => 'Some\SiteBundle\Entity\QuizQuestion');
}
}
It looks like error explains why you're getting exception of not existing methods.
You are trying to pass collection instead of an object. Probably, you call addQuestion() or setQuestion() method to the collection instead of an object from it.
This usually happens with relation-populated objects.
In the place where you use that methods, try to call $object->first()->addQuestion() instead of $object->addQuestion()

Categories