Symfony2でデータトランスフォーマーを使用する

フォーム-Symfony2の最も強力なツールの1つであり、多くの機能を表しています。 Symfony2で作業するための多くの秘密はRecet Bookに記載されています。 Symfony 2でフォームを操作するための1つのレシピの翻訳、つまり日付変換の使用を紹介したいと思います。

多くの場合、ユーザーが入力したデータを、プログラムで使用するために別の形式のフォームに変換する必要があります。 コントローラーでこれを簡単に手動で行うことができますが、このフォームを別の場所で使用したい場合はどうでしょうか? 「課題」(問題)オブジェクトとのコード対1の関係に関連付けられた「タスク」オブジェクトがあり、「タスク」ごとに「課題」を指定して解決できるとします。 Issue問題のドロップダウンリストをタスク編集フォームに追加すると、ナビゲートするのが非常に難しくなります。 ドロップダウンリストの代わりにテキストフィールドを追加し、発行番号を入力するだけです。

コントローラーで変換を試みることができますが、これは最良のアイデアではありません。 Issue番号がIssueオブジェクトを自動的に変換した場合、はるかに良いでしょう。 この場合、「データトランスフォーマー」が役立ちます。



トランスフォーマーの作成。



最初に、IssueToNumberTransformerクラスを作成します-このクラスは、Issue番号からIssueオブジェクトへの変換を担当します。

// src/Acme/TaskBundle/Form/DataTransformer/IssueToNumberTransformer.php namespace Acme\TaskBundle\Form\DataTransformer; use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\Exception\TransformationFailedException; use Doctrine\Common\Persistence\ObjectManager; use Acme\TaskBundle\Entity\Issue; class IssueToNumberTransformer implements DataTransformerInterface { /** * @var ObjectManager */ private $om; /** * @param ObjectManager $om */ public function __construct(ObjectManager $om) { $this->om = $om; } /** * Transforms an object (issue) to a string (number). * * @param Issue|null $issue * @return string */ public function transform($issue) { if (null === $issue) { return ""; } return $issue->getNumber(); } /** * Transforms a string (number) to an object (issue). * * @param string $number * @return Issue|null * @throws TransformationFailedException if object (issue) is not found. */ public function reverseTransform($number) { if (!$number) { return null; } $issue = $this->om ->getRepository('AcmeTaskBundle:Issue') ->findOneBy(array('number' => $number)) ; if (null === $issue) { throw new TransformationFailedException(sprintf( 'An issue with number "%s" does not exist!', $number )); } return $issue; } }
      
      





ユーザーが不明な番号を入力し、TransformationFailedExceptionをスローしない場合、新しい「Issue」オブジェクトを作成できます。



トランスフォーマーを使用する



これでトランスフォーマーができました。それを何らかの形で「Issue」フィールドに追加するだけです。

 use Symfony\Component\Form\FormBuilderInterface; use Acme\TaskBundle\Form\DataTransformer\IssueToNumberTransformer; class TaskType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { // ... // this assumes that the entity manager was passed in as an option $entityManager = $options['em']; $transformer = new IssueToNumberTransformer($entityManager); // add a normal text field, but add our transformer to it $builder->add( $builder->create('issue', 'text') ->addModelTransformer($transformer) ); } // ... }
      
      







この例では、フォームを作成するときに、EntityManagerをオプションとして渡す必要があります。 後で、EntityManagerを渡す必要がないように、問題番号のカスタムフィールドを作成する方法を学習します。

 $taskForm = $this->createForm(new TaskType(), $task, array( 'em' => $this->getDoctrine()->getEntityManager(), ));
      
      







かっこいい、やった! これで、ユーザーはテキストボックスに数値を入力できるようになり、オブジェクト「Issue」に変換されます。 これは、バインドが成功した後($ form-> bindRequest($ request))、フォームフレームワークが実際の「Issue」オブジェクトを「Issue」番号ではなく、setIssue()メソッドに送信することを意味します。

トランスフォーマーを追加する場合、フィールドを追加する場合よりも少し複雑な構文を使用する必要があることに注意してください。 以下は、特定のフィールドではなく、フォーム全体にトランスフォーマーを適用する方法の正しい例ではありません。

 //    -       //        $builder->add('issue', 'text') ->addModelTransformer($transformer);
      
      







モデルビューとトランスフォーマー



バージョン2.1の新機能:トランスフォーマーメソッドの名前はSymfony 2.1で変更されました。 prependNormTransformerはaddModelTransformerになり、appendClientTransformerはaddViewTransformerになりました。

上記の例では、変圧器が「モデル変圧器」として使用されました。 実際、2種類のトランスフォーマーと3種類の入力データがあります。

どの形式でも、データには3つの異なるタイプがあります。


  1. モデルデータは、アプリケーションで使用される形式のデータです(たとえば、 "Issue"タイプのオブジェクトなど)。 フォームで:: GetDataまたは:: SetDataメソッドを呼び出す場合、「モデルデータ」を処理します。
  2. ノルムデータデータの正規化されたバージョンであり、通常は「モデルデータ」データと同じです(ただし、この例ではそうではありません)。 直接、それらは頻繁には使用されません。

  3. データの表示は、フォームフィールドに入力するために使用される形式です。 これは、ユーザーがデータを送信する形式でもあります(送信フォーム)。 Form :: bind($ data)メソッドを呼び出すと、$データは「View Data」形式で表示されます


あるビューから別のビューにデータを変換するのに役立つ2種類のトランスフォーマーがあります。



どのトランスが必要かは、特定の状況によって異なります。

View Transformerを使用するには、addViewTransformerメソッドを呼び出します。



では、なぜデータトランスフォーマーを使用するのですか?



この例では、フィールドはテキストフィールドであり、テキストフィールドは常に「norm」および「view」形式のスカラーデータを返すと予想されます。 この場合、最も許容できるトランスフォーマーは「モデルトランスフォーマー」であり、これは「ノルムデータ」を「モデルデータ」に、またはその逆に変換します(発行番号を「Isuuse」オブジェクトに、またはその逆)。

トランスフォーマーの違いは非常に微妙であり、常に正規化されたデータは「標準」であると考えるべきです。 たとえば、「norm」テキストフィールドの場合、正規化されたデータはテキスト文字列であり、「date」フィールドの場合はDateTimeオブジェクトです。



「カスタムフィールド」でのトランスフォーマーの使用



上記で説明した例では、テキストフィールドにトランスフォーマーを使用しています。 これは非常に簡単でしたが、このアプローチには2つの欠点があります。

  1. isuueフィールドを使用する場合、トランスフォーマーの使用について常に覚えておく必要があります。
  2. トランスフォーマーを使用するフォームを作成するときは常に、em => EntityManagerオプションを渡すよう注意してください。


したがって、おそらくカスタムの「カスタム」タイプのフィールドを作成する必要があります。 最初に、カスタムフィールドタイプのクラスを作成します。

 // src/Acme/TaskBundle/Form/Type/IssueSelectorType.php namespace Acme\TaskBundle\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Acme\TaskBundle\Form\DataTransformer\IssueToNumberTransformer; use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\OptionsResolver\OptionsResolverInterface; class IssueSelectorType extends AbstractType { /** * @var ObjectManager */ private $om; /** * @param ObjectManager $om */ public function __construct(ObjectManager $om) { $this->om = $om; } public function buildForm(FormBuilderInterface $builder, array $options) { $transformer = new IssueToNumberTransformer($this->om); $builder->addModelTransformer($transformer); } public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'invalid_message' => 'The selected issue does not exist', )); } public function getParent() { return 'text'; } public function getName() { return 'issue_selector'; } }
      
      







次に、サービスタイプを登録し、form.typeタグでマークして、フィールドがユーザータイプとして認識されるようにします。

 <service id="acme_demo.type.issue_selector" class="Acme\TaskBundle\Form\Type\IssueSelectorType"> <argument type="service" id="doctrine.orm.entity_manager"/> <tag name="form.type" alias="issue_selector" /> </service>
      
      







これで、特別なissue_selectorタイプを使用できます。

 // src/Acme/TaskBundle/Form/Type/TaskType.php namespace Acme\TaskBundle\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; class TaskType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('task') ->add('dueDate', null, array('widget' => 'single_text')); ->add('issue', 'issue_selector'); } public function getName() { return 'task'; } }
      
      






All Articles