3

私は2つのテーブルを持っています:

ブランチ:

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

「電話」、「メール」など、複数のメタフィールドが必要です。

現在私はこのコードを持っています(セッター/ゲッターは省略されています):

// 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;
?>

次に、次のようなブランチを追加するためのフォームを作成する必要があります。

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

[Submit]

ここで、「Branch Phone」フィールドは、メタ名「phone」と対応する値を持つbranchmetaテーブルを指し、「Branch Email」フィールドは、メタ名「email」を持つbranchmetaテーブルを指します。どちらも動的に追加/削除できます。

Symfony2のFormBuilderを使用してこれを構築するにはどうすればよいですか?私は次のようにBranchTypeを作成しました:

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

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

そして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';
    }
}

しかし、それから私は立ち往生しました...私の場合、BranchMetaTypeを適切に使用して目的のフォームを構築する方法は?たぶん私はデータマッピングに何かが欠けていますか?

4

3 に答える 3

1

実際、はるかに優れた解決策があります。

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

これを行うと、同じテーブル、同じエンティティ(+ 1つのスーパークラスエンティティ)が作成され、通常のエンティティと同じように機能します。もうハッキーな方法はありません。7か月前にこの機能を新しくしたいのですが...

于 2013-09-05T17:41:25.590 に答える
0

どういうわけか、情報はクックブックhttp://symfony.com/doc/current/cookbook/form/form_collections.htmlの追加のメモセクションDoctrine:CascadingRelationsと「Inverse」側の保存に散在しています。

この問題は私に感銘を与え、実際のコーディング/調査を行いました。:)
これが私が見つけたものです。

Entity\Branch、メタデータプロパティに対する永続化操作をカスケードし、永続化setMetadata()される各メタデータの現在のブランチを設定するように変更するようにDoctrineに指示します。

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

public function setMetadata(ArrayCollection $metadata) {
    foreach ($metadata as $m) {
        $m->setBranch($this);
    }

    $this->metadata = $metadata;
}

Form\BranchType、適切なオプションを使用してメタデータフィールドを追加し、もちろんデフォルトdata_classをに設定しますEntity\BranchBranchType上記にはこれがありません)。このフォームタイプでは、永続化中に関数が呼び出される'by_reference' => falseようにするためのオプションが重要です( http://symfony.com/doc/current/reference/forms/types/collection.html#by-reference)。setMetadata()

public function buildForm(FormBuilderInterface $builder, array $options) {
    $builder->add('name');

    $builder->add('metadata', 'collection',
                    array(
                        'type' => new BranchMetaType(),
                        'allow_add' => true,
                        'by_reference' => false,
                    ));
}

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

そして最後に、コントローラーにダミーのbranchmetaを追加してみてください

public function newAction()
{
    $entity = new Branch();

    $branchmeta1 = new BranchMeta();
    $branchmeta1->setMetaname('dummy meta name #1');
    $branchmeta1->setMetavalue('dummy meta value #1');
    $entity->getMetadata()->add($branchmeta1);

    $branchmeta2 = new BranchMeta();
    $branchmeta2->setMetaname('dummy meta name #2');
    $branchmeta2->setMetavalue('dummy meta value #2');
    $entity->getMetadata()->add($branchmeta2);

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

    return array(
        'entity' => $entity,
        'form'   => $form->createView(),
    );
}
于 2013-02-28T09:40:43.920 に答える
0

ついに私はエレガントとは言えない解決策にたどり着きましたが、少なくともそれは機能します。

Branch.php:

namespace Acme\Bundle\ConsysBundle\Entity;

//use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

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

    /**
     * 
     * @ORM\OneToMany(targetEntity="BranchMeta", mappedBy="branch", cascade={"persist", "remove"})
     */
    private $metadata;

    /**
     * Virtual OneToMany field
     */
    private $telephones;

    /**
     * Virtual OneToMany field
     */
    private $faxes;

    /**
     * Virtual OneToMany field
     */
    private $emails;


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

    /* ...setters and getters are skipped... */

    /**
     * @ORM\PostLoad
     */
    public function onPostLoad()
    {
        foreach (array('telephone' => 'Telephones', 'fax' => 'Faxes', 'email' => 'Emails') as $metakey => $metaname) {
            $metadata = $this->getMetadata()->filter(
                function($entry) use ($metakey) {
                    return $entry->getName() == $metakey;
                }
            );
            $meta = call_user_func(array($this, 'set'.$metaname), $metadata);
        }
    }
}

コントローラコード:

private function checkBranchAdded($branch)
{
    foreach (array('telephone' => 'Telephones', 'fax' => 'Faxes', 'email' => 'Emails') as $metakey => $metaname) {
        $metadata = call_user_func(array($branch, 'get'.$metaname));
        foreach ($metadata as $item) {
            if (!$item->getBranch()) {
                $item->setBranch($branch);
                $branch->getMetadata()->add($item);
            }
        }
    }
}

private function checkBranchDeleted($branch)
{
    $newMeta = array();
    foreach (array('telephone' => 'Telephones', 'fax' => 'Faxes', 'email' => 'Emails') as $metakey => $metaname) {
        $metadata = call_user_func(array($branch, 'get'.$metaname));
        $newMeta = array_merge($newMeta, array_keys($metadata->toArray()));
    }

    $em = $this->getDoctrine()->getEntityManager();

    foreach($branch->getMetadata() as $key => $item) {
        if (!in_array($key, $newMeta)) {
            $em->remove($item);
            $branch->getMetadata()->remove($key);
        }
    }
}

/**
 * 
 * @Template()
 */
public function editAction($activeMenu, $activeSubmenu, $activeSubsubmenu, $id, Request $request)
{
    /* ... some code ... */
    $repository = $this->getDoctrine()->getRepository('AcmeConsysBundle:Branch');
    $branch = $repository->find($id);

    if (!$branch) {
        throw $this->createNotFoundException('No branch found for id '.$id);
    }
    $form = $this->createForm(new BranchType(), $branch);

    if ($request->isMethod('POST')) {
        $form->bind($request);
        $this->checkBranchDeleted($branch);
        $this->checkBranchAdded($branch);
        /* ... some code ... */
    }
    /* ... some code ... */
}

フォームビルダーコード。BranchType.php:

class BranchType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('telephones', 'collection', array(
            'type' => new BranchMetaType('telephone'),
            'allow_add' => true,
            'allow_delete' => true,
            'prototype' => true,
            'options' => array(
            ),
        ));

        $builder->add('faxes', 'collection', array(
            'type' => new BranchMetaType('fax'),
            'allow_add' => true,
            'allow_delete' => true,
            'prototype' => true,
            'options' => array(
            ),
        ));

        $builder->add('emails', 'collection', array(
            'type' => new BranchMetaType('email'),
            'allow_add' => true,
            'allow_delete' => true,
            'prototype' => true,
            'options' => array(
            ),
        ));

        /* ... other fields ... */
    }

    /* ... other methods ... */
}

BranchMetaType.php:

class BranchMetaType extends AbstractType
{
    protected $name;

    public function __construct($name)
    {
        $this->name = $name;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name', 'hidden', array(
            'data' => $this->name,
        ));
        $builder->add('value', 'text', array(
        ));
    }

    /* ... other methods ... */
}
于 2013-03-02T11:04:40.810 に答える