Alan Scott
Alan Scott

Reputation: 305

Populating checkboxes in Symfony2 based on ArrayCollection()

EDIT - redefined question and included additional specific code

In my Symfony2 project, I have an entity (Configurations) that includes a OneToMany relationship with another entity (ConfigurationsFeatures). I'm trying to build a form that allows the user to change values in the Configurations entity and also select values to be persisted to the ConfigurationsFeatures entity. There are two scenarios in which the form is used:

1) Create a new configuration -> render a blank form with fields for the values in Configurations and a collection of checkboxes for the potential values in ConfigurationsFeatures; the source of potential values is actually a third entity, CoreCodes. Once validated, the Configurations-values of the form are persisted to the Configurations entity and the selected checkboxes are persisted to ConfigurationsFeatures.

2) Update an existing configuration -> render a form with the existing values of Configurations displayed in the fields and render a collection of checkboxes for all possible ConfigurationsFeatures values BUT have the existing values already selected.

I've built the form type and controller actions for scenario #1, tested it, and all is working. However, I cannot seem to solve the "have existing values already selected" in the checkbox portion of the form. I realize that as I've currently setup my controller, I'm basically comparing apples to oranges because $entity->getFeatures() returns an ArrayCollection of the ConfigurationsFeatures entity and there's nothing to map against in the form, since the form uses a collection of the CoreCodes entity.

So how do I bridge this gap?


Entity\Configurations (stores individual configurations)

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Lynux\AssetBundle\Entity\Core\Configurations
 * ... ORM declarations ...
 */
class Configurations
{
    // ... plain old properties, nothing fancy here... 

    /**
     * @ORM\OneToMany(targetEntity="Lynux\AssetBundle\Entity\Core\ConfigurationsFeatures", mappedBy="configurationFk")
     */
    private $features;

    public function __construct()
    {
        $this->features = new ArrayCollection();
    }

    // getters and setters

    /**
     * Set features
     */
    public function setFeatures(ArrayCollection $features)
    {
        $this->features = $features;
    }

    /**
     * Get features
     */
    public function getFeatures()
    {
        return $this->features;
    }
}

Entity\ConfigurationsFeatures (stores selected features per configuration)

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

/**
 * Lynux\AssetBundle\Entity\Core\ConfigurationsFeatures
 * ... ORM declarations ...
 */
class ConfigurationsFeatures
{
    /**
     * @var Codes
     *
     * @ORM\ManyToOne(targetEntity="Lynux\AssetBundle\Entity\Core\Codes")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="feature_fk", referencedColumnName="id")
     * })
     */
    private $featureFk;

    /**
     * @var Configurations
     *
     * @ORM\ManyToOne(targetEntity="Lynux\AssetBundle\Entity\Core\Configurations", inversedBy="features")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="configuration_fk", referencedColumnName="id")
     * })
     */
    private $configurationFk;

    /**
     * Set featureFk
     *
     * @param Lynux\AssetBundle\Entity\Core\Codes $featureFk
     * @return ConfigurationsFeatures
     */
    public function setFeatureFk(\Lynux\AssetBundle\Entity\Core\Codes $featureFk = null)
    {
        $this->featureFk = $featureFk;

        return $this;
    }

    /**
     * Get featureFk
     *
     * @return Lynux\AssetBundle\Entity\Core\Codes
     */
    public function getFeatureFk()
    {
        return $this->featureFk;
    }

    /**
     * Set configurationFk
     *
     * @param Lynux\AssetBundle\Entity\Core\Configurations $configurationFk
     * @return ConfigurationsFeatures
     */
    public function setConfigurationFk(\Lynux\AssetBundle\Entity\Core\Configurations $configurationFk = null)
    {
        $this->configurationFk = $configurationFk;

        return $this;
    }

    /**
     * Get configurationFk
     *
     * @return Lynux\AssetBundle\Entity\Core\Configurations
     */
    public function getConfigurationFk()
    {
        return $this->configurationFk;
    }
}

Entity\CoreCodes (source of potential values for ConfigurationsFeatures->featureFk)

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Lynux\AssetBundle\Entity\Core\Codes
 *
 * @ORM\Table(name="core_codes")
 */
class Codes
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer", nullable=false, unique=true)
     * @ORM\Id
     * @Assert\NotBlank(message="ID cannot be blank!")
     * @Assert\Regex(pattern="/^\d+$/", match=true, message="ID must be an integer!")
     * @Assert\MinLength(limit=8, message="ID must be 8 digits in length!")
     * @Assert\MaxLength(limit=8, message="ID must be 8 digits in length!")
     */
    private $id;

    // ... various properties, not applicable to question ...

    /**
     * Set id
     *
     * @param integer $id
     * @return Codes
     */
    public function setId($id)
    {
        $this->id = $id;

        return $this;
    }

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

    // ... getters and setters for other properties ...
}

Type\FormConfigurations

use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class FormConfigurations extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $features = $this->features;

        // ... builders for other properties ...

        $builder->add(
            'features',
            'entity',
            array(
                'class'         => 'LynuxAssetBundle:Core\Codes',
                'property_path' => false,
                'query_builder' => function(EntityRepository $er) use ($features)
                    {
                        return $er->createQueryBuilder('u')
                            ->where('u.submodule = :submodule')
                            ->setParameter('submodule', 'feature');
                    },
                'expanded' => true,
                'multiple' => true,
                'property' => 'title',));
    }

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

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(
            array(
                'data_class' => 'Lynux\AssetBundle\Entity\Core\Configurations',));
    }
}

Controller\ConfigurationsController::createAction()

public function createAction(Request $request)
{
    $config = $request->getSession()->get('configuration');
    $manage = $this->getDoctrine()->getEntityManager();

    $entity = new Configurations();
    $form   = $this->createForm(new FormConfigurations($features), $entity);
    $failed = null;

    $features = $manage
        ->getRepository('LynuxAssetBundle:Core\Codes')
        ->findBySubmodule('feature');

    if ('POST' == $request->getMethod()) {
        $form->bind($request);

        if ($form->isValid()) {
            $status = $manage
                ->getRepository('LynuxAssetBundle:Core\Codes')
                ->findOneById(10103022);

            $entity->setToken($entity->getName());
            $entity->setStatusFk($status);
            $entity->setCreateAccountFk($this->getUser());

            $features = $form->get('features')->getData();
            foreach($features as $feature) {
                $addFeature = new ConfigurationsFeatures();
                $addFeature->setConfigurationFk($entity);
                $addFeature->setFeatureFk($feature);
                $manage->persist($addFeature);
            }
            $manage->persist($entity);
            $manage->flush();

            return $this->redirect($this->generateUrl(
                'CoreConfigurationsConfirm',
                array(
                    'code'  => 'create',
                    'token' => $entity->getToken(),)));
        } else {
            $failed = true;
        }
    }

Controller\ConfigurationsController::updateAction()

public function updateAction(Request $request, $token)
{
    $manage = $this->getDoctrine()->getEntityManager();
    $entity = $manage
        ->getRepository('LynuxAssetBundle:Core\Configurations')
        ->findOneByToken($token);

    $form   = $this->createForm(new FormConfigurations($features), $entity);

    if ('POST' == $request->getMethod()) {
        $form->bind($request);

        if ($form->isValid()) {
            $currentFeatures = $manage
                ->getRepository('LynuxAssetBundle:Core\ConfigurationsFeatures')
                ->findByConfigurationFk($entity);
            foreach($currentFeatures as $currentFeature) {
                $manage->remove($currentFeature);
            }

            $features = $form->get('features')->getData();
            foreach($features as $feature) {
                $addFeature = new ConfigurationsFeatures();
                $addFeature->setConfigurationFk($entity);
                $addFeature->setFeatureFk($feature);
                $manage->persist($addFeature);
            }

            $manage->flush();
        }
    }

Upvotes: 4

Views: 2272

Answers (1)

Alan Scott
Alan Scott

Reputation: 305

Ahh patience and persistence... It occurred to me that I was converting the collection of CoreCodes to a collection of ConfigurationsFeatures when persisting to the database. Likewise it made sense to convert from ConfigurationsFeatures to CoreCodes when displaying the form.

// ConfigurationsController::updateAction()

$formFeatures    = new ArrayCollection();
$currentFeatures = $entity->getFeatures();
foreach($currentFeatures as $feature) {
    $formFeatures->add($feature->getFeatureFk());
}

if ('POST' != $request->getMethod()) { // first time the form is loaded
    $form->get('features')->setData($formFeatures);
}

Upvotes: 4

Related Questions