I'm working on a webapplication in Symfony2. I came to a point in which I need some advice/explanation from some one more advanced in Symfony.
I have a part of my database that is set up as follows:
I have cards that belong to a card attribute set and consists of card values.
I have card attribute sets that have many attributes, a card attribute can belong to many card attribute sets (obviously a many to many relationship).
Then depending on the card attribute the attribute has an attribute value, for example a text has a value_text of type varchar and a boolean has a value_boolean of type boolean.
You can imagine when making a form to create a new card, the form needs to generate input fields depending on the card attribute set it belongs to and depending on the attributes that belong to the attribute set right?
So here's my question; is there a way to dynamically generate input fields in a form depending the entity chosen by the user. I've read about events but I'm not sure that they satisfy my needs.
This is the code for my entities (I removed to Getters and Setters for a more simple view):
Card:
/**
* card
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="clientsBundle\Entity\cardRepository")
* @UniqueEntity(
* fields={"cardLabel"},
* message="A card with this label already exists"
* )
*/
class card
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="card_label", type="string", length=999)
*/
private $cardLabel;
/**
* @ORM\ManyToOne(targetEntity="project", inversedBy="project_cards")
* @ORM\JoinColumn(name="project_id", referencedColumnName="id", onDelete = "SET NULL")
*/
protected $card_project;
/**
* @ORM\ManyToOne(targetEntity="cardAttributeSet", inversedBy="cas_cards")
* @ORM\JoinColumn(name="cas_id", referencedColumnName="id")
**/
protected $cardAttrSet;
/**
* @ORM\OneToMany(targetEntity="cardAttrValue", mappedBy="card", cascade={"persist"}, orphanRemoval=true)
**/
protected $card_values;
/**
* @ORM\ManyToMany(targetEntity="user", mappedBy="cards")
*/
private $users;
public function __construct() {
$this->card_values = new ArrayCollection();
$this->users = new ArrayCollection();
}
}
Card Attribute:
/**
* cardAttribute
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="clientsBundle\Entity\cardAttributeRepository")
* @UniqueEntity(
* fields={"name"},
* message="An attribute with this name already exists"
* )
*/
class cardAttribute
{
/**
* @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;
/**
* @var string
*
* @ORM\Column(name="type", type="string", length=255)
*/
private $type;
}
Card Attribute Set
/**
* cardAttributeSet
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="clientsBundle\Entity\cardAttributeSetRepository")
* @UniqueEntity(
* fields={"casLabel"},
* message="An attribute set with this label already exists"
* )
*/
class cardAttributeSet
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* @var string
*
* @ORM\Column(name="cas_label", type="string", length=255)
*/
private $casLabel;
/**
* @ORM\OneToMany(targetEntity="card", mappedBy="cardAttrSet")
*/
private $cas_cards;
/**
* @ORM\ManyToMany(targetEntity="cardAttribute")
* @ORM\JoinTable(name="cas_attribute",
* joinColumns={@ORM\JoinColumn(name="cas_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="attribute_id", referencedColumnName="id")}
* )
*/
private $attributes;
public function __construct()
{
$this->cas_cards = new ArrayCollection();
$this->attributes = new ArrayCollection();
}
}
Card Attribute Value
/**
* cardAttrValue
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="clientsBundle\Entity\cardAttrValueRepository")
* @UniqueEntity(
* fields={"valueText"}
* )
*/
class cardAttrValue
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="value_text", type="string", length=255, nullable=true)
*/
private $valueText;
/**
* @var string
*
* @ORM\Column(name="value_varchar", type="string", length=255, nullable=true)
*/
private $valueVarchar;
/**
* @var integer
*
* @ORM\Column(name="value_int", type="integer", nullable=true, nullable=true)
*/
private $valueInt;
/**
* @var boolean
*
* @ORM\Column(name="value_boolean", type="boolean", nullable=true, nullable=true)
*/
private $valueBoolean;
/**
* @ORM\ManyToOne(targetEntity="card", inversedBy="card_values")
* @ORM\JoinColumn(name="card_id", referencedColumnName="id")
**/
private $card;
/**
* @ORM\ManyToOne(targetEntity="cardAttribute")
* @ORM\JoinColumn(name="cardAttributes_id", referencedColumnName="id")
**/
private $cardAttribute;
}
Create a form type CardAttributeValueType
for CardAttributeValue
entity, inside this form add fields depending on passed attribute type:
class CardAttributeValueType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
$value = $event->getData();
$form = $event->getForm();
if (!$value) {
return;
}
switch ($value->getCardAttribute()->getType()) {
case 'text':
$form->add('valueVarchar', 'text');
break;
// Same for other attribute types
}
}
}
Then, add collection
field type for card_values
inside CardType
form type and pass CardAttributeValueType
as collection item type.
In the Card
entity edit getCardValues()
method so it will return every attribute from CardAttributeSet
, not only ones for which value entities exist.
UPDATE
public function getCardValues()
{
$collection = new ArrayCollection();
if (!$this->cardAttrSet) {
return $collection;
}
// Add existing values
foreach ($this->card_values as $value) {
$collection[$value->getCardAttribute()->getId()] = $value;
}
// Get all attributes from the set and create values for missing attributes
foreach ($this->cardAttrSet->getAttributes() as $attr) {
if (!isset($collection[$attr->getId()])) {
$value = new cardAttrValue();
$value->setCardAttribute($attr);
$collection[$attr->getId()] = $value;
}
}
return $collection;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With