web-dev-qa-db-ja.com

symfony:オブジェクトの配列で満たされた選択タイプフィールド

エンティティProductがあります。私の製品は、異なる言語で複数の名前を持つことができます。フランス語の名前、英語の名前など。自動翻訳は使いたくありません。

ユーザーは製品フォームに名前を記入し、対応する言語を選択する必要があります。 [追加]ボタンのおかげで、彼は好きなだけ名前を追加できます。

すべての言語は、管理者ユーザーによって(別の形式で)作成されます。したがって、Languageは、名前(例:英語)とコード(例:EN)を持つエンティティでもあります。

名前と言語(ユーザーが製品フォームに入力する内容に準拠)を持つエンティティProductNameを作成しました。

その場合、エンティティProductNameをエンティティLanguageに関連付ける必要はありません。言語コードが欲しいだけです。したがって、私のProductNameエンティティには、次のプロパティがあります。

/**
 * @ORM\Column(name="Language_Code", type="string", length=2)
 */
private $language;

私の製品フォーム(ProductType)には、いくつかの名前を追加するためのCollectionTypeフィールドがあります。

// Form/ProductType.php

    ->add('infos',      CollectionType::class, array(
        'entry_type'    => ProductInfosType::class,
        'allow_add'     => true,
        'allow_delete'  => true,
        'prototype'     => true,
        'label'         => false,
        'mapped'        => false
    ))

また、ProductInfosTypeフォームには2つのフィールドがあります。

// Form/ProductInfosType.php

        ->add('name',           TextType::class, array(
            'attr'              => array('size' => 40)
        ))
        ->add('language',       EntityType::class, array(
            'placeholder'       => '',
            'class'             => 'AppBundle:Language',
            'choice_label'      => 'code',
            'attr'              => array('class' => 'lang'),
            'query_builder'     => function (EntityRepository $er) {
                return $er->createQueryBuilder('l')->orderBy('l.code', 'ASC');
            }
        ))

したがって、フォームページに移動すると、入力テキストフィールド(名前)と選択フィールド(言語)を含むブロックがあります。選択フィールドは次のようになります。

<select id="product_infos_0_language" required="required" name="product[infos][0][language]">
    <option value=""></option>
    <option value="DE">DE</option>
    <option value="EN">EN</option>
    <option value="ES">ES</option>
    <option selected="selected" value="FR">FR</option>
</select> 

この時点で、すべてが正常に機能します。ユーザーが他の名前などを追加できるように、追加ボタンを作成しました。

しかし、フォームを送信したときに、ProductControllerでフォームデータを確認したところ、データベースに保存したいものと一致していないことに気付きました。

print_r($form->get('infos')->getData());

// returns :
Array
(
    [0] => AppBundle\Entity\ProductName Object
        ( 
            [language:AppBundle\Entity\ProductName:private] => AppBundle\Entity\Language Object
                (
                    [code:AppBundle\Entity\Language:private] => FR
                    [name:AppBundle\Entity\Language:private] => Français
                )

            [name:AppBundle\Entity\ProductName:private] => Ceinture lombaire LombaSkin
        )
)

私が欲しいのは:

Array
(
    [0] => AppBundle\Entity\ProductName Object
        ( 
            [language:AppBundle\Entity\ProductName:private] => FR    
            [name:AppBundle\Entity\ProductName:private] => Ceinture lombaire LombaSkin
        )
)

言語オブジェクトは必要ありませんが直接言語コード

そのため、ProductNameTypeフォームではEntityFieldを使用するのではなく、ChoiceTypeフィールドを使用する必要があると思います。

dbに格納されているすべての言語を選択フィールドにロードするにはどうすればよいですか?この説明がより理解しやすいことを願っています;-)

6
Eve

この投稿のおかげで解決策を見つけました: Symfony 2.8/3.0のbuildForm()にデータを渡す

ProductController.phpcreateForm()メソッドのオプションとしてカスタムデータを渡します。

_// ...

// build the form
$em = $this->getDoctrine()->getManager();
$product = new Product();
$languages = $em->getRepository('AppBundle:Language')->findAllOrderedByCode();

$form = $this->createForm(ProductType::class, $product, array(
    'languages' => $languages
));
_

ProductType form:オプションリゾルバーでカスタムデータを渡します

_public function configureOptions(OptionsResolver $resolver) {
    $resolver->setDefaults(array(
        'data_class' => 'AppBundle\Entity\Product',
        'languages'  => null
    ));
}
_

次に、buildForm()関数で、CollectionTypeフィールドに_entry_options_オプションを追加します。

_$builder->add('infos',  CollectionType::class, array(
    'entry_type'    => ProductInfosType::class,
    'entry_options' => array('languages' => $options['languages']),
    'allow_add'     => true,
    'allow_delete'  => true,
    'prototype'     => true,
    'label'         => false,
    'by_reference'  => false
));
_

ProductInfosType form:オプションリゾルバーでカスタムデータを渡します(ProductFormとまったく同じです)

_public function configureOptions(OptionsResolver $resolver) {
    $resolver->setDefaults(array(
        'data_class' => 'AppBundle\Entity\ProductName',
        'languages'  => null
    ));
}
_

ここで、2つの選択肢があります。フォームがエンティティを返すか、単純な文字列を返すかです。

私の例では、言語コード(FR、ENなど)が必要です。

ケース1:フォームが投稿されたときに言語コードのみを返します:

_// Form/ProductInfosType.php

// ...

// Convert array of objects in an array of strings
$choices = array();
foreach ($options['languages'] as $lang) {
    $code = $lang->getCode();
    $choices[$code] = $code;
}

$builder->add('language', ChoiceType::class, array(
    'placeholder'       => '',
    'choices'           => $choices
));

// returns :
Array
(
    [0] => AppBundle\Entity\ProductName Object
        ( 
            [name:AppBundle\Entity\ProductName:private] => Ceinture lombaire LombaSkin
            [language:AppBundle\Entity\ProductName:private] => FR    
        )
)
_

ケース2:フォームが投稿されたときに言語エンティティを返す:

_// Form/ProductInfosType.php

// ...

$builder->add('language', ChoiceType::class, array(
    'placeholder'       => '',
    'choices'           => $options['languages'],
    'choice_label'      => 'code',
    'choice_value'      => 'code'
));

// returns :
Array
(
    [0] => AppBundle\Entity\ProductName Object
        ( 
            [name:AppBundle\Entity\ProductName:private] => Ceinture lombaire LombaSkin
            [language:AppBundle\Entity\ProductName:private] => AppBundle\Entity\Language Object
                (
                    [code:AppBundle\Entity\Language:private] => FR
                    [name:AppBundle\Entity\Language:private] => Français
                )    
        )
)
_

このソリューションでは、エンティティマネージャーを引数として渡すために、フォームをサービスとして作成する必要はありません。すべてはコントローラーとフォームオプションで管理されます。

7
Eve

EntityTypeの choice_value を使用する必要があります。

'choice_value' => function ($language) {
    return $language->getCode();
},

EDIT:編集内容を読んだ後は、確かに正しいです。EntityTypeではなくChoiceTypeを使用してください。 choicesにデータを入力するには、DependencyInjectionを使用してフォーム内にLanguageRepositoryを挿入し、リポジトリにクエリを作成してすべての言語コードをフェッチする必要があると思います。

1
Terenoth