Зависимости в формах Symfony2


Я работаю над веб-приложением в Symfony2. Я подошел к тому моменту, когда мне нужен совет/объяснение от кого-то более продвинутого в Symfony.

У меня есть часть моей базы данных, которая настроена следующим образом:

У меня есть карты, которые принадлежат набору атрибутов карты и состоят из значений карт.

У меня есть наборы атрибутов карт, которые имеют множество атрибутов, атрибут карты может принадлежать многим наборам атрибутов карт (очевидно, многим ко многим отношения).

Затем, в зависимости от атрибута карты, атрибут имеет значение атрибута, например, текст имеет значение value_text типа varchar, а логическое значение имеет значение value_boolean типа boolean.

Вы можете себе представить, что при создании формы для создания новой карты форма должна генерировать поля ввода в зависимости от набора атрибутов карты, к которому она принадлежит, и в зависимости от атрибутов, которые принадлежат набору атрибутов правильно?

Итак, вот мой вопрос; есть ли способ динамически создайте поля ввода в форме, зависящей от сущности, выбранной пользователем. Я читал о событиях, но не уверен, что они удовлетворяют моим потребностям.

Это код для моих сущностей (я удалил геттеры и сеттеры для более простого представления):

Карточка:

/**
 * 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();
    }
}

Атрибут карты:

 /**
 * 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;
}

Набор атрибутов карты

 /**
 * 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();
    }
}

Значение атрибута карты

 /**
 * 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;
}
Author: JackWhiteIII, 2015-06-18

1 answers

Создайте тип формы CardAttributeValueType для сущности CardAttributeValue, внутри этой формы добавьте поля в зависимости от переданного типа атрибута:

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
        }
     }
}

Затем добавьте collection тип поля для card_values внутри CardType типа формы и передайте CardAttributeValueType как тип элемента коллекции.

В методе Card сущности отредактируйте getCardValues(), чтобы он возвращал каждый атрибут из CardAttributeSet, а не только те, для которых существуют сущности значений.

ОБНОВЛЕНИЕ

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;
}
 3
Author: Vadim Ashikhman, 2015-06-22 10:09:16