Search code examples
symfonysymfony-formssymfony-2.3

Cannot set form with populated Entity object get The form's view data is expected to be of type scalar, array or an instance of \ArrayAccess


I have created the EntityClass:

<?php

namespace Rota\Bundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\component\OptionsResolver\OptionsResolverIntrerface;
use Doctrine\Common\Collections\ArrayCollection;

/**
* RotaObj
*
* @ORM\Table()
* @ORM\Entity
*/
class RotaMain
{
   public function __construct()
 {
     $this->rotaShifts = new ArrayCollection();
 }

/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var integer
 *
 * @ORM\Column(name="MyUserId", type="integer")
 */
private $myUserId;

/**
 * @var date
 * 
 * @ORM\Column(name="RotaStartDate", type="date")
 */
private $startDate;

/**
 * @var date
 * 
 * @ORM\Column(name="RotaReviewDate", type="date")
 */
private $reviewDate;

/**
 * @var integer
 * 
 * @ORM\Column(name="RotaRollingWeeks", type="integer")
 */
 private $rollingWeeks;

 /**
  * @var integer
  * 
  * @ORM\Column(name="restDayCount", type="integer")
  */
 private $restDayCount;

 /**
  * @var integer
  * 
  * @ORM\Column(name="totalDayCount", type="integer")
  */
 private $totalDayCount;


 /**
  * @ORM\OneToMany(targetEntity="RotaShift",mappedBy="rotaMain",cascade={"persist"})
  */
 protected $rotaShifts;

 /**
  * @var string
  * 
  * @ORM\Column(name="shiftOrder",type="string",length=4000,nullable=true)
  */
 private $shiftOrder;

/**
 * Get id
 *
 * @return integer 
 */
public function getId()
{
    return $this->id;
}

/**
 * Set myUserId
 *
 * @param integer $myUserId
 * @return RotaObj
 */
public function setMyUserId($myUserId)
{
    $this->myUserId = $myUserId;

    return $this;
}

/**
 * Get myUserId
 *
 * @return integer 
 */
public function getMyUserId()
{
    return $this->myUserId;
}

/**
 * Set startDate
 *
 * @param \DateTime $startDate
 * @return RotaObj
 */
public function setStartDate($startDate)
{
    $this->startDate = $startDate;

    return $this;
}

/**
 * Get startDate
 *
 * @return \DateTime 
 */
public function getStartDate()
{
    return $this->startDate;
}

/**
 * Set reviewDate
 *
 * @param \DateTime $reviewDate
 * @return RotaObj
 */
public function setReviewDate($reviewDate)
{
    $this->reviewDate = $reviewDate;

    return $this;
}

/**
 * Get reviewDate
 *
 * @return \DateTime 
 */
public function getReviewDate()
{
    return $this->reviewDate;
}

/**
 * Set rollingWeeks
 *
 * @param integer $rollingWeeks
 * @return RotaObj
 */
public function setRollingWeeks($rollingWeeks)
{
    $this->rollingWeeks = $rollingWeeks;

    return $this;
}

/**
 * Get rollingWeeks
 *
 * @return integer 
 */
public function getRollingWeeks()
{
    return $this->rollingWeeks;
}

/**
 * Add rotaShifts
 *
 * @param \Rota\Bundle\Entity\RotaShift $rotaShifts
 * @return RotaMain
 */
public function addRotaShift(\Rota\Bundle\Entity\RotaShift $rotaShift)
{
    $this->rotaShifts[] = $rotaShift;

    return $this;
}

/**
 * Remove rotaShifts
 *
 * @param \Rota\Bundle\Entity\RotaShift $rotaShifts
 */
public function removeRotaShift(\Rota\Bundle\Entity\RotaShift $rotaShifts)
{
    $this->rotaShifts->removeElement($rotaShifts);
}

/**
 * Get rotaShifts
 *
 * @return \Doctrine\Common\Collections\Collection 
 */
public function getRotaShifts()
{
    return $this->rotaShifts;
}



/**
 * Set restDayCount
 *
 * @param integer $restDayCount
 * @return RotaMain
 */
public function setRestDayCount($restDayCount)
{
    $this->restDayCount = $restDayCount;

    return $this;
}

/**
 * Get restDayCount
 *
 * @return integer 
 */
public function getRestDayCount()
{
    return $this->restDayCount;
}

/**
 * Set totalDayCount
 *
 * @param integer $totalDayCount
 * @return RotaMain
 */
public function setTotalDayCount($totalDayCount)
{
    $this->totalDayCount = $totalDayCount;

    return $this;
}

/**
 * Get totalDayCount
 *
 * @return integer 
 */
public function getTotalDayCount()
{
    return $this->totalDayCount;
}



/**
 * Set shiftOrder
 *
 * @param string $shiftOrder
 * @return RotaMain
 */
public function setShiftOrder($shiftOrder)
{
    $this->shiftOrder = $shiftOrder;

    return $this;
}

/**
 * Get shiftOrder
 *
 * @return string 
 */
public function getShiftOrder()
{
    return $this->shiftOrder;
}
}

This class holds collection of:

<?php

namespace Rota\Bundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
* RotaShift
* 
* @ORM\Table()
* @ORM\Entity
*/
class RotaShift
{
/**
 * @var integer
 * 
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $_id;

/**
 * @var integer
 * 
 * @ORM\Column(name="startTime", type="integer")
 */
private $_startTime;

/**
 * @var integer
 * 
 * @ORM\Column(name="endTime", type="integer")
 */
private $endTime;

/**
 * @var string
 * 
 * @ORM\Column(name="name", type="string", length=50)
 */
private $name;


/**
 * @ORM\ManyToOne(targetEntity="RotaMain", inversedBy="rotaShifts")
 */
private $rotaMain;


/**
 * Get _id
 *
 * @return integer 
 */
public function getId()
{
    return $this->_id;
}

/**
 * Set _startTime
 *
 * @param integer $startTime
 * @return RotaShift
 */
public function setStartTime($startTime)
{
    $this->_startTime = $startTime;

    return $this;
}

/**
 * Get _startTime
 *
 * @return integer 
 */
public function getStartTime()
{
    return $this->_startTime;
}

/**
 * Set endTime
 *
 * @param integer $endTime
 * @return RotaShift
 */
public function setEndTime($endTime)
{
    $this->endTime = $endTime;

    return $this;
}

/**
 * Get endTime
 *
 * @return integer 
 */
public function getEndTime()
{
    return $this->endTime;
}

/**
 * Set name
 *
 * @param string $name
 * @return RotaShift
 */
public function setName($name)
{
    $this->name = $name;

    return $this;
}

/**
 * Get name
 *
 * @return string 
 */
public function getName()
{
    return $this->name;
}

/**
 * Set rotaMain
 *
 * @param \Rota\Bundle\Entity\RotaMain $rotaMain
 * @return RotaShift
 */
public function setRotaMain(\Rota\Bundle\Entity\RotaMain $rotaMain = null)
{
    $this->rotaMain = $rotaMain;

    return $this;
}

/**
 * Get rotaMain
 *
 * @return \Rota\Bundle\Entity\RotaMain 
 */
public function getRotaMain()
{
    return $this->rotaMain;
}
}

I have then created a Form EntityType class for the RotaMain object as follows:

<?php

namespace Rota\Bundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class RotaMainType extends AbstractType
{
   public function buildForm(FormBuilderInterface $builder, array $options)
   {
    $builder->  add('ReviewDate','date');
    $builder->add('RollingWeeks','integer');
    $builder->add('StartDate','date');
    $builder->add('RestDayCount','integer',array('label' => 'Number of rest days'));
    $builder->add('TotalDayCount','integer',array('label' => 'Total number of days in     shift pattern'));
    $builder->add('rotaShifts','collection',array('type'=> new RotaShiftType(),'label' => false,'allow_add' => true,));
    $builder->add('Save','submit');
}

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
     $resolver->setDefaults(array(
    'data_class' => 'Rota\Bundle\Entity\RotaMain',
));
}


public function getName()
{
    return 'rotaMain';
}
}

When I try to set a form with a fully populated RotaMain object:

$form = $this->createForm(new RotaMainType(),$RotaMain);

I get the following error:

The form's view data is expected to be of type scalar, array or an instance of \ArrayAccess, but is an instance of class Rota\Bundle\Entity\RotaMain. You can avoid this error by setting the "data_class" option to "Rota\Bundle\Entity\RotaMain" or by adding a view transformer that transforms an instance of class Rota\Bundle\Entity\RotaMain to scalar, array or an instance of \ArrayAccess.

I have set the data_class in the RotaMainType object so am not sure where I am going wrong? If I pass an empty RotaMain object to the form it builds as expected.

The population of RotaMain happens in the controller. The form is part one of two part process. If user has completed first part gone away and come back I want reutnr the first part fo them to review before going onto the second part of the process. Controller action:

public function setRotaAction(Request $request,$userId)
{
    //Check to see if user has part set up a rota - return completed form for review
     $RotaMainRepos = $this->getDoctrine()->getManager()->getRepository("RotaBundle:RotaMain");
     $id = $this->getUser()->getID();
     $criteria = array("myUserId"=>$id);
     $result = $RotaMainRepos->findBy($criteria);
     if($result == null)
     {
       $RotaMain = new RotaMain();
       $RotaMain->setmyUserId($userId);
       $form = $this->createForm(new RotaMainType(),$RotaMain);
     }
     else
     {
        $RotaMain = $result[0];
        $rotashiftRepo = $this->getDoctrine()->getManager()->getRepository("RotaBundle:RotaShift");
        $rotaShifts = $rotashiftRepo->findByRotaMain($RotaMain);
        foreach($rotaShifts as $rotaShift)
        {
            $RotaMain->addRotaShift($rotaShift);
        }
        $form = $this->createForm(new RotaMainType(),$RotaMain);

     }


     $form->handleRequest($request);

     if($form->isValid())
     {
        $early = $form->getData();
        $em = $this->getDoctrine()->getManager();
        $em->persist($early);

        $shifts = $early->getRotaShifts();
        foreach($shifts as $shift)
        {
         $shift->setRotaMain($early);
         $em->persist($shift);
        }
        $em->flush();
        $shiftOrder = new ShiftOrder($shifts,$early->getRestDayCount(),$early->getTotalDayCount());
        return $this->render("RotaBundle:Default:shiftOrder.html.twig",array("shiftOrder" => $shiftOrder));
     }

     return $this->render('RotaBundle:Default:setRota.html.twig',array("form" => $form->createView(),));




}

Solution

  • You have to bind a newly created or existing entity/object of the same type as the configured data_class to your form.

    If you don't set the variable you're passing to your form to an object of the expected data_class symfony won't create the object but throw this exception.

    In your case you might want to use a SensioFrameworkExtraBundle's @ParamConverter to fetch your entity from database and have your controller cleaned up.

    /**
     * @Route("/whatever/{id}")
     * @ParamConverter("rotaMain", class="RotaBundle:RotaMain", options={"mapping": {"MyUserId": "id"}})
     */
    public function editAction(RotaMain $rotaMain, Request $request)
    {
    
        $form = $this->createForm(new RotaMainType(), $rotaMain);       
        $form->handleRequest($request);
        if ($form->isValid()) {
            //  
        }
    }
    

    ... or ...

    public function newAction(Request $request)
    {
    
        $form = $this->createForm(new RotaMainType(), $rotaMain = new RotaMain());       
        $form->handleRequest($request);
        if ($form->isValid()) {
            //  
        }
    }