Reputation: 613
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...
Upvotes: 3
Views: 10917
Reputation: 17986
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()
Upvotes: 2
Reputation: 613
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');
}
}
Upvotes: 4