phpsymfonyormdoctrine-ormformbuilder

Creating mappings in Doctrine2 for meta table structure for use in FormBuilder


I have two tables:

Branches:

+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(11)      | NO   | PRI | NULL    | auto_increment |
| name        | varchar(255) | NO   |     | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+

BranchMeta:

+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment |
| branch_id  | int(11)      | YES  | MUL | NULL    |                |
| metaname   | varchar(255) | NO   |     | NULL    |                |
| metavalue  | varchar(255) | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+

I want to have different meta fields, like "phone", "email", etc. that may be multiple as well.

Currently I have this code (setters/getters are omitted):

// Entity/Branch.php

<?php
namespace Acme\Bundle\ConsysBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * Branch
 *
 * @ORM\Entity(repositoryClass="Acme\Bundle\ConsysBundle\Entity\BranchRepository")
 * @ORM\Table(name="branch")
 */
class Branch
{
    /**
     * @var integer
     *
     * @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;

    /**
     * 
     * @ORM\OneToMany(targetEntity="BranchMeta", mappedBy="branch")
     */
    private $metadata;

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

// Entity/BranchMeta.php

<?php

namespace Acme\Bundle\ConsysBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * BranchMeta
 *
 * @ORM\Entity(repositoryClass="Acme\Bundle\ConsysBundle\Entity\BranchMetaRepository")
 * @ORM\Table(name="branchmeta")
 */
class BranchMeta
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="Branch", inversedBy="metadata")
     * @ORM\JoinColumn(name="branch_id", referencedColumnName="id")
     */
    private $branch;

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

    /**
     * @var string
     *
     * @ORM\Column(name="metavalue", type="string", length=255)
     */
    private $value;
?>

And then I need to build a form to add a Branch like this:

Branch Name:  [_________]
Branch Phone: [_________] [-]
Branch Phone: [_________] [+]
Branch Email: [_________] [-]
Branch Email: [_________] [+]

[Submit]

Where "Branch Phone" fields point to branchmeta table with meta name "phone" and correspondent values, and "Branch Email" fields point to branchmeta table with meta name "email". Both can be dynamically added/deleted.

How can I build this using Symfony2's FormBuilder? I created BranchType like this:

class BranchType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name');
    }

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

And BranchMetaType:

class BranchMetaType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name');
        $builder->add('value');
    }

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

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

But then I got stuck... How to properly use BranchMetaType to build the desired form in my case? Maybe I'm missing something in data mapping?


Solution

  • In fact, there is a much more better solution:

    /**
     * Meta
     * 
     * @ORM\Entity
     * @ORM\InheritanceType("SINGLE_TABLE")
     * @ORM\DiscriminatorColumn(name="metaname", type="string")
     * @ORM\DiscriminatorMap({"phone" = "MetaPhone", "email" = "MetaEmail", "dummy" = "MetaDummy"})
     * @ORM\MappedSuperclass
     */
    class Meta
    {
        // ...
    }
    
    /**
     * @ORM\Entity
     */
    class MetaPhone
    {
    }
    
    /**
     * @ORM\Entity
     */
    class MetaEmail
    {
    }
    
    /**
     * @ORM\Entity
     */
    class MetaDummy
    {
    }
    

    Doing this, we will have the same table, the same entities (+ one superclass entity) and we will work like with normal entities with them. No hacky ways anymore. I wish I new of this feature 7 months ago...