Symfony 2でCRUDアプリケーションを作成する

Symfony 2.0



Symfony 2フレームワークの最近リリースされたバージョンには、多くの興味深い機能が含まれています。 この記事では、データベース内のレコードを作成、読み取り、更新、削除するためのWebインターフェースを作成する非常に一般的なタスクであるCRUDアプリケーションの作成について説明します。



Symfony 2のアーキテクチャとインストールについてはすでにハブにありました。そのため、Symfony 2 SEは既にインストールされており、使用される基本的な概念(バンドル、フォーム、テンプレートなど)はおなじみです。



Symfony 2で標準のCRUDアプリケーションを開発する際の主な課題



  1. データモデル開発
  2. データモデルエンティティの作成、読み取り、更新、削除を可能にするコントローラー、フォーム、テンプレートの開発


この記事では、例として、ニュースを編集するためのアプリケーションを作成します。各アプリケーションはカテゴリに関連付けられており、ニュースにリンクを添付できます。



データモデル開発



ymlまたはxmlを手動で記述してエンティティの説明を作成するのは退屈な作業です(もちろん、 Mysql Workbenchプラグインまたは専用ソフトウェアを使用できます)。 プロセスを高速化するために、Symfony 2には既存のデータベースをリバースエンジニアリングすることによりデータモデルの記述を生成する便利な手段があります



まず、TestNewsBundleバンドルを作成します。



php app/console generate:bundle --namespace=Test/NewsBundle --format=annotation --structure
      
      







Mysql Workbench (またはその他のデータベース設計ツール)でデータベーススキーマを作成します。







 SET FOREIGN_KEY_CHECKS=0; CREATE TABLE `news` ( `id` INT NOT NULL AUTO_INCREMENT , `news_category_id` INT NOT NULL , `title` VARCHAR(255) NULL , `announce` TEXT NULL , `text` TEXT NULL , `pub_date` DATE NULL , PRIMARY KEY (`id`) , INDEX `pub_date` (`pub_date` ASC) , INDEX `fk_news_news_category` (`news_category_id` ASC) , CONSTRAINT `fk_news_news_category` FOREIGN KEY (`news_category_id` ) REFERENCES `news_category` (`id` ) ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = InnoDB; CREATE TABLE `news_category` ( `id` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(255) NULL , PRIMARY KEY (`id`) ) ENGINE = InnoDB CREATE TABLE `news_link` ( `id` int(11) NOT NULL AUTO_INCREMENT, `news_id` INT NOT NULL , `url` varchar(255) DEFAULT NULL, `text` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) , INDEX `fk_news_link_news1` (`news_id` ASC) , CONSTRAINT `fk_news_link_news1` FOREIGN KEY (`news_id` ) REFERENCES `news` (`id` ) ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = InnoDB;
      
      





教義クラスを作成します。



 php app/console doctrine:mapping:import TestNewsBundle annotation
      
      







このコマンドの結果として、各テーブルに1つのクラスがTest / NewsBundle / Entityサブディレクトリに作成され、 Doctrine ORMのリレーショナルテーブルへのオブジェクトのマッピングの設定がクラスアノテーション、たとえばNewsクラスで説明されます:



 <?php namespace Test\NewsBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * Test\NewsBundle\Entity\News * * @ORM\Table(name="news") * @ORM\Entity */ class News { /** * @var integer $id * * @ORM\Column(name="id", type="integer", nullable=false) * @ORM\Id * @ORM\GeneratedValue(strategy="IDENTITY") */ private $id; /** * @var string $title * * @ORM\Column(name="title", type="string", length=255, nullable=true) */ private $title; /** * @var text $announce * * @ORM\Column(name="announce", type="text", nullable=true) */ private $announce; /** * @var text $text * * @ORM\Column(name="text", type="text", nullable=true) */ private $text; /** * @var date $pubDate * * @ORM\Column(name="pub_date", type="date", nullable=true) */ private $pubDate; /** * @var NewsCategory * * @ORM\ManyToOne(targetEntity="NewsCategory") * @ORM\JoinColumns({ * @ORM\JoinColumn(name="news_category_id", referencedColumnName="id") * }) */ private $newsCategory; }
      
      





クラスにゲッターとセッターを追加するには、次のコマンドを実行します。



 php app/console doctrine:generate:entities TestNewsBundle
      
      







CRUDのブランクの作成-アプリケーション



doctrine:generate:crudコマンドを使用して、ニュースを扱うための空白を作成します。 ルーティング形式-コントローラーファイル内の注釈(Test / NewsBundle / Controller / NewsController)。 コントローラーファイル内の注釈を介したルーティングは、 SensioFrameworkExtraバンドル(Symfony 2 SEに含まれています)を介して機能します。



 php app/console doctrine:generate:crud --entity=TestNewsBundle:News --route-prefix=news --with-write --format=annotation
      
      





これで、生成中に指定されたパスを入力すると(Symfony 2がwwwroot- http://localhost/Symfony/web/app_dev.php/news /に展開されている場合)、空のニュースリストが表示され、Create new entryリンクをクリックすると開きますデフォルトのレコード作成フォーム。



ニュースカテゴリを操作するために同じ空白を作成し、いくつかのカテゴリを入力してみましょう。



 php app/console doctrine:generate:crud --entity=TestNewsBundle:NewsCategory --route-prefix=newscategory --with-write --format=annotation
      
      







ニュースへのリンクを追加するためのフォームに空白を持たせるために、NewsLinkエンティティにも同様に空白を生成します。



 php app/console doctrine:generate:crud --entity=TestNewsBundle:NewsLink --route-prefix=newslink --with-write --format=annotation
      
      







そのため、ニュースを追加する形式でカテゴリをリストするときに、システムは選択に表示するフィールドを認識し、Test / NewsBundle / Entity / NewsCategoryクラスで、メソッドを追加する必要があります。



 function __toString() { return $this->getName(); }
      
      







形状クラスの変更



ここで、生成されたフォームTest / NewsBundle / Form / NewsTypeを修正します。 見出しをフィールド(ラベル)に追加し、必要なフィールドタイプ(テキスト(入力タイプ=テキストが表示されます)、textarea)を書き留めます。 pubDateおよびnewsCategoryフィールドでは、フィールドタイプをnullのままにします。この場合、Symfonyフォームコンポーネント自体はどのフィールドタイプを表示するかを「推測」します。



 <?php namespace Test\NewsBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilder; class NewsType extends AbstractType { public function buildForm(FormBuilder $builder, array $options) { $builder->add('title', 'text', array('label' => '')) ->add('announce', 'textarea', array('label' => '')) ->add('text', 'textarea', array('label' => '')) ->add('pubDate', null, array('label' => ' ')) ->add('newsCategory', null, array('label' => '')); } public function getName() { return 'news'; } }
      
      







フォームテンプレートの変更



フォームおよびフォームのテンプレートについて詳しくは、 フォームおよびフォームのカスタマイズのチュートリアルのセクションをご覧ください。 作成されたフォームでは、フォームの表示を表形式に置き換えます。このため、テンプレートで表レイアウトを使用する指示を追加する必要があります(デフォルトのレイアウトは設定で変更することもできます)。



 {% form_theme form 'form_table_layout.html.twig' %}
      
      





ただし、今後はフィールドの標準表示を再定義する必要があるため、ニュースを入力するためのテンプレートTest / NewsBundle / Resources / views / News / new.html.twigは次のようになります。



 {% use 'form_table_layout.html.twig' %} {% form_theme form _self %} <h1> </h1> <form action="{{ path('news_create') }}" method="post" {{ form_enctype(form) }}> {{ form_widget(form) }} <p> <button type="submit"> </button> </p> </form> <ul class="record_actions"> <li> <a href="{{ path('news') }}">    </a> </li> </ul>
      
      







フォームはテーブルレイアウトに表示されます。





関連する投稿を編集する



それでは、楽しい部分、つまりニュースリンクの編集に取り掛かりましょう。 リバースエンジニアリング中に、リンク「リンク->ニュース」のみが自動的にエンティティクラスに追加されました。 リンク「News-> Links」を追加するには、クラスTest \ NewsBundle \ Entity \ Newsに追加する必要があります。



 /** * @ORM\OneToMany(targetEntity="NewsLink", mappedBy="news", cascade={"all"}) */ protected $newsLinks; function __construct() { $this->newsLinks = new \Doctrine\Common\Collections\ArrayCollection(); }
      
      





次に、コマンドを実行します($ newsLinks属性のゲッターとセッターが生成されます):



 php app/console doctrine:generate:entities TestNewsBundle
      
      







クラスTest / NewsBundle / Form / NewsTypeに、「Collection」タイプのフィールドを追加します。これにより、関連するエンティティのセットを編集できます。



 $builder->add('title', 'text', array('label' => '')) .... ->add('newsLinks', 'collection', array( 'label' => '  ', 'type' => new NewsLinkType(), 'allow_add' => true, 'allow_delete' => true, 'prototype' => true ));
      
      





Test \ NewsBundle \ Form \ NewsLinkTypeクラスで、編集するエンティティのクラス名を指定する必要があります(data_classオプション):



 <?php namespace Test\NewsBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilder; class NewsLinkType extends AbstractType { public function buildForm(FormBuilder $builder, array $options) { $builder->add('url') ->add('text'); } public function getName() { return 'newsLinkType'; } public function getDefaultOptions(array $options) { return array( 'data_class' => 'Test\NewsBundle\Entity\NewsLink', ); } }
      
      







ただし、ここでフォームを見ると、「ニュースへのリンク」フィールドのタイトルのみが表示されます。 フォームが機能するためには、テンプレートを変更する必要があります。 同時に、別のテンプレートでフォームの定義を取り出します。 これを行うには、ファイルTest / NewsBundle / Resources / views / News / form.html.twigを作成します。

 {% use 'form_table_layout.html.twig' %} {% form_theme form _self %} <script language="JavaScript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script> <form action="{{ entity.id ? path('news_update', { 'id': entity.id }) : path('news_create') }}" method="post" {{ form_enctype(form) }}> {{ form_errors(form) }} <table> {{ form_row (form.title) }} {{ form_row (form.announce) }} {{ form_row (form.text) }} {{ form_row (form.pubDate) }} {{ form_row (form.newsCategory) }} <tr> <td valign="top">  </td> <td> <!--      /    --> {% macro linkRow(link) %} <tr> <td>{{ form_widget(link.url) }}</td> <td>{{ form_widget(link.text) }}</td> <td><a href="#" class="deleteRowLink">X</a></td> </tr> {% endmacro %} <!--       /    --> <!--     #addLink     --> <script type="text/html" id="nl">{{ _self.linkRow (form.newsLinks.get('prototype')) }} </script> <!--         --> <table id="linksTable"> <tr><td>Url</td><td> </td></tr> {% for key, link in form.newsLinks %} {{ _self.linkRow(link) }} {% endfor %} </table> <input type="button" id="addLink" value=" "> <script> $(function() { $("#addLink" ).click(function() { $('#linksTable tbody').append($('#nl').html().replace(/\$\$name\$\$/g, $('#linksTable tbody tr').length)); }); $("form a.deleteRowLink").live('click', function() { $(this).closest('tr').remove(); }); }); </script> </td> </tr> </table> {{ form_rest(form) }} <p><button type="submit"></button></p> </form>
      
      







レコードテンプレートTest / NewsBundle / Resources / views / News / new.html.twig:



 <h1> </h1> {% include 'TestNewsBundle:News:form.html.twig' %} <ul class="record_actions"> <li> <a href="{{ path('news') }}">    </a> </li> </ul>
      
      







エントリを編集するためのテンプレートTest / NewsBundle / Resources / views / News / edit.html.twig:



 <h1> </h1> {% include 'TestNewsBundle:News:form.html.twig' with { 'form' : edit_form } %} <ul class="record_actions"> <li> <a href="{{ path('news') }}"> Back to the list </a> </li> <li> <form action="{{ path('news_delete', { 'id': entity.id }) }}" method="post"> {{ form_widget(delete_form) }} <button type="submit">Delete</button> </form> </li> </ul>
      
      







もちろん、エントリの記録および編集用に1つのテンプレートを残すことができますが、この例では、CRUDジェネレーターが生成したテンプレートおよびコントローラーメソッドの構造は変更しません。



次に、コントローラークラスTest / NewsBundle / Controller / NewsControllerを変更する必要があります。 「News-Link」通信パラメーターでcascade = allオプションを指定しましたが(エンティティを保存する場合、関連するエンティティは保持されます)、それでもNewsLinkオブジェクトの親Newsオブジェクトへのバインドを定義する必要があります。



 public function createAction() { $entity = new News(); $request = $this->getRequest(); $form = $this->createForm(new NewsType(), $entity); $form->bindRequest($request); if ($form->isValid()) { $em = $this->getDoctrine()->getEntityManager(); //     foreach ($entity->getNewsLinks() as $link) { $link->setNews($entity); } $em->persist($entity); $em->flush(); return $this->redirect($this->generateUrl('news_show', array('id' => $entity->getId()))); } return array( 'entity' => $entity, 'form' => $form->createView()); }
      
      







updateActionメソッドの場合、関連レコードを削除する可能性も提供する必要があります。 htmlフォームでは、JQueryを使用したテーブル行の削除がありますが、これはレコードを削除するのに十分ではありません-これはコントローラーに明示的に実装する必要があります。



  public function updateAction($id) { $em = $this->getDoctrine()->getEntityManager(); $entity = $em->getRepository('TestNewsBundle:News')->find($id); if (!$entity) { throw $this->createNotFoundException('Unable to find News entity.'); } $beforeSaveLinks = $currentLinkIds = array(); foreach ($entity->getNewsLinks() as $link) $beforeSaveLinks [$link->getId()] = $link; $editForm = $this->createForm(new NewsType(), $entity); $deleteForm = $this->createDeleteForm($id); $request = $this->getRequest(); $editForm->bindRequest($request); if ($editForm->isValid()) { foreach ($entity->getNewsLinks() as $link) { $link->setNews($entity); //  -     (   id) if ($link->getId()) $currentLinkIds[] = $link->getId(); } $em->persist($entity); //          -   foreach ($beforeSaveLinks as $linkId => $link) if (!in_array( $linkId, $currentLinkIds)) $em->remove($link); $em->flush(); return $this->redirect($this->generateUrl('news_edit', array('id' => $id))); } return array( 'entity' => $entity, 'edit_form' => $editForm->createView(), 'delete_form' => $deleteForm->createView(), ); }
      
      







関連レコードを追加する機能を備えたフォーム:



関連レコードを追加する機能を持つフォーム



このアプリケーションでは、ニュースの入力、表示、更新、削除ができます。 この場合、ページをリロードすることなく、ニュースへの関連リンクの処理が行われます。 コーディングなしで完全に言うのではなく、かなり少ない労力で、この機能が開発されました。 同時に、フォームの外観は必要に応じてカスタマイズできます。 フォームのテーマメカニズムを使用して、フォームの行とフィールドを表示するための標準テンプレートをオーバーライドできます。



All Articles