8

私は現在、SonataAdminBundle、1 対多の関係、およびファイルのアップロードで課題に直面しています。と呼ばれるエンティティとClientと呼ばれるエンティティがありExchangeFileます。1 つClientは複数ExchangeFileの を持つことができるため、ここでは 1 対多の関係があります。ファイルのアップロードにはVichUploaderBundleを使用しています。

これはClientクラスです:

/**
 * @ORM\Table(name="client")
 * @ORM\Entity()
 * @ORM\HasLifecycleCallbacks
 */
class Client extends BaseUser
{    
    // SNIP

    /**
     * @ORM\OneToMany(targetEntity="ExchangeFile", mappedBy="client", orphanRemoval=true, cascade={"persist", "remove"})
     */
    protected $exchangeFiles;

    // SNIP
}

これはExchangeFileクラスです:

/**
 * @ORM\Table(name="exchange_file")
 * @ORM\Entity
 * @Vich\Uploadable
 */
class ExchangeFile
{
    // SNIP

    /**
     * @Assert\File(
     *     maxSize="20M"
     * )
     * @Vich\UploadableField(mapping="exchange_file", fileNameProperty="fileName")
     */
    protected $file;

    /**
     * @ORM\Column(name="file_name", type="string", nullable=true)
     */
    protected $fileName;

    /**
     * @ORM\ManyToOne(targetEntity="Client", inversedBy="exchangeFiles")
     * @ORM\JoinColumn(name="client_id", referencedColumnName="id")
     */
    protected $client;

    // SNIP
}

私のClientAdminクラスではexchangeFiles、次の方法でフィールドを追加しました。

protected function configureFormFields(FormMapper $formMapper)
{
    $formMapper
        // SNIP
        ->with('Files')
            ->add('exchangeFiles', 'sonata_type_collection', array('by_reference' => false), array(
                    'edit' => 'inline',
                    'inline' => 'table',
                ))
        // SNIP
}

これにより、クライアント編集フォームでさまざまな交換ファイルをインライン編集できます。そして、これまでのところうまくいきます: 1 対多の関係とファイルのアップロードを備えた Sonata Admin.

問題

しかし、注意点が 1 つあります。緑の「+」記号を 1 回押して (新しい交換ファイル フォーム行を追加)、ファイルシステム内のファイルを選択してから、もう一度「+」記号を押します(新しいフォーム行が Ajax 経由で追加されます)。 )、別のファイルを選択し、[更新] (現在のクライアントを保存) をクリックすると、最初のファイルは保持されません。2 番目のファイルのみがデータベースとファイル システムで見つかります。

私が知る限り、これには次の理由があります: 緑色の「+」記号が 2 回目にクリックされると、現在フォームにあるデータ (クライアントとすべての交換ファイル) を含む現在のフォームが Web サーバーにポストされます。 )。新しいフォームが作成され、リクエストがフォームにバインドされます (これは にあるAdminHelperクラスで行われますSonata\AdminBundle\Admin)。

public function appendFormFieldElement(AdminInterface $admin, $subject, $elementId)
{
    // retrieve the subject
    $formBuilder = $admin->getFormBuilder();

    $form = $formBuilder->getForm();
    $form->setData($subject);
    $form->bind($admin->getRequest()); // <-- here
    // SNIP
}

したがって、フォーム全体がバインドされ、フォーム行が追加され、フォームがブラウザーに送り返され、フォーム全体が新しいフォームで上書きされます。ただし、<input type="file" />セキュリティ上の理由からファイル入力 ( ) を事前設定できないため、最初のファイルは失われます。VichUploaderBundleファイルは、エンティティが永続化されている場合にのみファイルシステムに保存されます (これには Doctrine を使用していると思いますprePersist) が、フォーム フィールド行が追加された場合はまだ発生しません。

私の最初の質問は次のとおりです。この問題をどのように解決できますか、またはどの方向に進むべきですか? 次の使用例を機能させたいと思います: 新しいクライアントを作成したいのですが、3 つのファイルをアップロードすることがわかっています。「新しいクライアント」をクリックし、クライアント データを入力し、緑色の「+」ボタンを 1 回押して、最初のファイルを選択します。次に、「+」記号をもう一度押して、2 番目のファイルを選択します。3 番目のファイルについても同様です。3 つのファイルはすべて永続化する必要があります。

2 番目の質問: 1 対多の関係で 1 つのフォーム行のみを追加したいのに、Sonata Admin がフォーム全体を投稿するのはなぜですか? これは本当に必要ですか?これは、ファイル入力がある場合、新しいフォーム行が追加されるたびに、フォームに存在するすべてのファイルがアップロードされることを意味します。

よろしくお願いします。詳細が必要な場合は、お知らせください。

4

3 に答える 3

3

SonataMediaBundleについての私のコメントに加えて...

このルートに進む場合は、次のような新しいエンティティを作成する必要があります。

/**
 * @ORM\Table
 * @ORM\Entity
 */
class ClientHasFile
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var Client $client
     *
     * @ORM\ManyToOne(targetEntity="Story", inversedBy="clientHasFiles")
     */
    private $client;

    /**
     * @var Media $media
     *
     * @ORM\ManyToOne(targetEntity="Application\Sonata\MediaBundle\Entity\Media")
     */
    private $media;

    // SNIP
}

次に、Client エンティティで次のようにします。

class Client
{
    // SNIP

    /**
     * @var \Doctrine\Common\Collections\ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="ClientHasFile", mappedBy="client", cascade={"persist", "remove"}, orphanRemoval=true)
     */
    protected $clientHasFiles;


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

    // SNIP
}

... そして ClientAdmin の configureFormFields:

protected function configureFormFields(FormMapper $form)
{
    $form

    // SNIP

    ->add('clientHasFiles', 'sonata_type_collection', array(
        'required' => false,
        'by_reference' => false,
        'label' => 'Media items'
    ), array(
        'edit' => 'inline',
        'inline' => 'table'
    )
    )
;
}

...そして最後になりましたが、ClientHasFileAdmin クラス:

class ClientHasFileAdmin extends Admin
{
    /**
     * @param \Sonata\AdminBundle\Form\FormMapper $form
     */
    protected function configureFormFields(FormMapper $form)
    {
        $form
            ->add('media', 'sonata_type_model_list', array(), array(
                'link_parameters' => array('context' => 'default')
            ))
        ;
    }

    /**
     * {@inheritdoc}
     */
    protected function configureListFields(ListMapper $list)
    {
        $list
            ->add('client')
            ->add('media')
        ;
    }
}
于 2012-11-30T16:18:33.753 に答える
0

私はさまざまなアプローチと回避策を試しましたが、最終的に、ここに記載されている解決策が最適であることがわかりましたhttps://stackoverflow.com/a/25154867/4249725

不要な場合は、ファイル選択の周りにある不要なリスト/削除ボタンをすべて非表示にするだけです。

フォーム内でファイルを直接選択するその他のすべてのケースでは、フォームの検証、フォームのプレビューなどで、遅かれ早かれ他の問題に直面します。これらすべてのケースで、入力フィールドはクリアされます。

したがって、メディア バンドルと sonata_type_model_list を使用することは、かなりのオーバーヘッドがありますが、おそらく最も安全なオプションです。

誰かが私が探していた方法で解決策を探している場合に備えて投稿しています。

この正確な問題に対する Java スクリプトの回避策もいくつか見つけました。「+」ボタンを押して元に戻すと、基本的にファイル入力の名前が変更されました。

この場合でも、何らかの検証が失敗した場合などにフォームを再表示するという問題が残っているため、メディアバンドルのアプローチを強くお勧めします。

于 2015-02-19T19:10:27.570 に答える