Symfony 2.8 ltsでブログを作成する[パート4]






Githubプロジェクト
必要なマニュアルの一部をインストールする方法については、 リンクでリポジトリの説明を参照してください 。 (たとえば、前のレッスンを経ずにこのレッスンから始めたい場合)


ホームページ


この部分は、ホームページを作成することから始めます。 通常のブログでは、エントリは通常、新しいものから古いものへと並べ替えられます。 ブログページのリンクから投稿全体を利用できます。 すでにホームページのルート、コントローラー、ディスプレイを作成しているため、簡単に更新できます。

レコードを取得します。 データベースクエリの実行。


ブログエントリを表示するには、データベースからそれらを抽出する必要があります。 Doctrine 2はこの目標を達成するためにDoctrine Query Language(DQL)QueryBuilder提供します(Doctrine 2を介して通常のSQLクエリを作成することもできますが、Doctrine 2が提供するデータベース抽象化から私たちを遠ざけるため、この方法は推奨されません。データベースクエリに使用できるDQLを生成するための優れたオブジェクト指向の方法を提供するため、QueryBuilderを使用します。src src/Blogger/BlogBundle/Controller/PageController.phpにあるページコントローラーのindexActionメソッドを更新しましょう。 ba データをね。
次のコードを貼り付けます
 // src/Blogger/BlogBundle/Controller/PageController.php class PageController extends Controller { public function indexAction() { $em = $this->getDoctrine() ->getManager(); $blogs = $em->createQueryBuilder() ->select('b') ->from('BloggerBlogBundle:Blog', 'b') ->addOrderBy('b.created', 'DESC') ->getQuery() ->getResult(); return $this->render('BloggerBlogBundle:Page:index.html.twig', array( 'blogs' => $blogs )); } // .. } 


まず、ManagerからQueryBuilderのインスタンスを取得しました。 これにより、QueryBuilderが提供する多くのメソッドを使用してクエリの作成を開始できます。 利用可能なメソッドの完全なリストについては、QueryBuilderのドキュメントを参照してください。 ヘルパーメソッドから始めるのが最善です。 これらのメソッドは、select()、from()、およびaddOrderBy()です。 以前のDoctrine 2とのやり取りと同様に、短い注釈を使用してBloggerBlogBu​​ndle経由でBlogエンティティを参照できます:Blog (これはBlogger \ BlogBu​​ndle \ Entity \ Blogと同じです 。 完了したら、クエリの条件を指定し、DQLインスタンスを返すgetQuery()メソッドを呼び出します。 QueryBuilderオブジェクトから結果を取得することはできません;常に最初にこれをDQLインスタンスに変換する必要があります。 DQLオブジェクトのインスタンスは、ブログエントリのコレクションを返すgetResult()メソッドを提供します。 DQLオブジェクトのインスタンスには、getSingleResult()やgetArrayResult()など、結果を返すためのメソッドが多数あることが後でわかります。

ディスプレイ


レコードのコレクションができたので、それらを表示する必要があります。 src/Blogger/BlogBundle/Resources/views/Page/index.html.twigにあるホームページのコンテンツを置き換えsrc/Blogger/BlogBundle/Resources/views/Page/index.html.twig
次のように
 {# src/Blogger/BlogBundle/Resources/views/Page/index.html.twig #} {% extends 'BloggerBlogBundle::layout.html.twig' %} {% block body %} {% for blog in blogs %} <article class="blog"> <div class="date"><time datetime="{{ blog.created|date('c') }}">{{ blog.created|date('l, F j, Y') }}</time></div> <header> <h2><a href="{{ path('BloggerBlogBundle_blog_show', { 'id': blog.id }) }}">{{ blog.title }}</a></h2> </header> <img src="{{ asset(['images/', blog.image]|join) }}" /> <div class="snippet"> <p>{{ blog.blog(500) }}</p> <p class="continue"><a href="{{ path('BloggerBlogBundle_blog_show', { 'id': blog.id }) }}">Continue reading...</a></p> </div> <footer class="meta"> <p>Comments: -</p> <p>Posted by <span class="highlight">{{blog.author}}</span> at {{ blog.created|date('h:iA') }}</p> <p>Tags: <span class="highlight">{{ blog.tags }}</span></p> </footer> </article> {% else %} <p>There are no blog entries for symblog</p> {% endfor %} {% endblock %} 



Twig制御構造の1つ、 for..else..endforを導入しました 。 テンプレートエンジンを使用したことがない場合は、おそらく次のPHPコードに精通しているでしょう。
 <?php if (count($blogs)): ?> <?php foreach ($blogs as $blog): ?> <h1><?php echo $blog->getTitle() ?><?h1> <!-- rest of content --> <?php endforeach ?> <?php else: ?> <p>There are no blog entries</p> <?php endif ?> 


Twig for..else..endfor制御構造は、これを実現する簡単な方法です。 ホームページテンプレートのほとんどのコードは、HTML形式のブログ情報の出力に関するものです。 ただし、考慮すべき点がいくつかあります。 最初に、Twigパス関数を使用してルートを作成します。 ブログページにはURLに渡されたエントリのIDが必要なので、引数としてパス関数に挿入する必要があります。 これは、次のコードスニペットで確認できます。
 <h2><a href="{{ path('BloggerBlogBundle_blog_show', { 'id': blog.id }) }}">{{ blog.title }}</a></h2> 


次に、次を使用してコンテンツを返します
 <p>{{blog.blog(500) }}</p> 
数値500は、関数から取得したい最大のポストの長さです。 これを行うには、Doctrine 2が以前に生成したgetBlogメソッドを更新する必要があります。 見つかったブログエンティティのgetBlogメソッドを更新します
  src/Blogger/BlogBundle/Entity/Blog.php 

 // src/Blogger/BlogBundle/Entity/Blog.php public function getBlog($length = null) { if (false === is_null($length) && $length > 0) return substr($this->blog, 0, $length); else return $this->blog; } 


getBlogメソッドはブログ投稿全体を返す必要があるため、デフォルト値nullを持つパラメーター$ lengthを設定します。 nullが渡されると、レコード全体が返されます。
ブラウザにlocalhost:8000と入力すると、ホームページに最新のブログエントリが表示されます。 また、エントリのタイトルをクリックするか、「 Continue reading ... 」リンクをクリックして、完全なブログエントリに移動する機会が必要です



コントローラのエントリをリクエストできますが、これはこれに最適な場所ではありません。 リクエストは、いくつかの理由により、コントローラーの外部に配置する方が適切です。
QueryBuilderコードを複製せずに、アプリケーションの他の場所でクエリを再利用することはできません。
QueryBuilderコードを複製した場合、クエリを変更する必要がある場合、将来的にいくつかの変更を行う必要があります。
リクエストをコントローラーから分離することで、コントローラーとは独立してリクエストをテストできます。
Doctrine 2はこのタスクを容易にするリポジトリクラスを提供します。

Doctrine 2リポジトリ


前の章でブログページを作成したときに、Doctrine 2リポジトリクラスについて少し話しました。 find()メソッドを使用してデータベースからブログエントリを取得するために、 Doctrine \ ORM \ EntityRepositoryクラスのデフォルト実装を使用しました。 カスタムデータベースクエリが必要なので、カスタムリポジトリを作成する必要があります。 Doctrine 2はこのタスクに役立ちます。 src / Blogger / BlogBu​​ndle / Entity / Blog.phpでブログエンティティメタデータを更新します
 // src/Blogger/BlogBundle/Entity/Blog.php /** * @ORM\Entity(repositoryClass="Blogger\BlogBundle\Entity\Repository\BlogRepository") * @ORM\Table(name="blog") * @ORM\HasLifecycleCallbacks() */ class Blog { // .. } 


このエンティティが関連付けられているBlogRepositoryクラスのネームスペースが定義されていることがわかります。 BlogエンティティのDoctrine 2メタデータを更新したため、次のようにdoctrine:generate:entitiesコマンドを再実行する必要があります。
 $ php app/console doctrine:generate:entities Blogger\BlogBundle 

Doctrine 2はsrc/Blogger/BlogBundle/Entity/Repository/BlogRepository.phpあるBlogRepositoryクラスのラッパーを作成します

 <?php namespace Blogger\BlogBundle\Entity\Repository; /** * BlogRepository * * This class was generated by the Doctrine ORM. Add your own custom * repository methods below. */ class BlogRepository extends \Doctrine\ORM\EntityRepository { } 


BlogRepositoryクラスは、以前に使用したfind()メソッドを提供するEntityRepositoryクラスを拡張します。 QueryBuilderコードをページコントローラーからその中に移動して、BlogRepositoryクラスを更新しましょう。

 <?php namespace Blogger\BlogBundle\Entity\Repository; /** * BlogRepository * * This class was generated by the Doctrine ORM. Add your own custom * repository methods below. */ class BlogRepository extends \Doctrine\ORM\EntityRepository { public function getLatestBlogs($limit = null) { $qb = $this->createQueryBuilder('b') ->select('b') ->addOrderBy('b.created', 'DESC'); if (false === is_null($limit)) $qb->setMaxResults($limit); return $qb->getQuery() ->getResult(); } } 


QueryBuilderコードがコントローラーでこれを実行したのと同じ方法で、最新のブログエントリを返すgetLatestBlogsメソッドを作成しました。 リポジトリクラスでは、createQueryBuilder()メソッドを使用してQueryBuilderに直接アクセスできます。 また、デフォルトのパラメーター$ limitを追加したため、返される結果の数を制限できます。 クエリ結果は、コントローラーの場合と同じになります。 from()メソッドを使用してオブジェクトを指定する必要がないことに気づいたかもしれません。 これは、Blogエンティティに関連付けられているBlogRepositoryにあるためです。 EntityRepositoryクラスのcreateQueryBuilderメソッドの実装を見ると、from()メソッドが呼び出されていることがわかります。
 // Doctrine\ORM\EntityRepository public function createQueryBuilder($alias, $indexBy = null) { return $this->_em->createQueryBuilder() ->select($alias) ->from($this->_entityName, $alias, $indexBy); } 

最後に、BlogRepositoryを使用するようにページコントローラーのindexActionメソッドを更新しましょう。
 // src/Blogger/BlogBundle/Controller/PageController.php class PageController extends Controller { public function indexAction() { $em = $this->getDoctrine() ->getManager(); $blogs = $em->getRepository('BloggerBlogBundle:Blog') ->getLatestBlogs(); return $this->render('BloggerBlogBundle:Page:index.html.twig', array( 'blogs' => $blogs )); } // .. } 

これで、ホームページを更新すると、以前とまったく同じものが表示されます。 適切に設計されたクラスがタスクを正しく実行できるように、コードを再編成しました。

モデルの詳細:コメントエンティティの作成


エントリーはブログに関してはストーリーの半分にすぎません。 また、読者がブログ投稿にコメントする機会を許可する必要があります。 投稿には多くのコメントを含めることができるため、これらのコメントも保存してBlogエンティティにリンクする必要があります。
基礎となるエンティティクラスCommentを定義することから始めます。 src/Blogger/BlogBundle/Entity/Comment.php新しいファイルを作成して貼り付けます
次の
 <?php // src/Blogger/BlogBundle/Entity/Comment.php namespace Blogger\BlogBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity(repositoryClass="Blogger\BlogBundle\Entity\Repository\CommentRepository") * @ORM\Table(name="comment") * @ORM\HasLifecycleCallbacks */ class Comment { /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ protected $id; /** * @ORM\Column(type="string") */ protected $user; /** * @ORM\Column(type="text") */ protected $comment; /** * @ORM\Column(type="boolean") */ protected $approved; /** * @ORM\ManyToOne(targetEntity="Blog", inversedBy="comments") * @ORM\JoinColumn(name="blog_id", referencedColumnName="id") */ protected $blog; /** * @ORM\Column(type="datetime") */ protected $created; /** * @ORM\Column(type="datetime") */ protected $updated; public function __construct() { $this->setCreated(new \DateTime()); $this->setUpdated(new \DateTime()); $this->setApproved(true); } /** * @ORM\preUpdate */ public function setUpdatedValue() { $this->setUpdated(new \DateTime()); } } 


ここで見るもののほとんどは前の部分ですでに説明しましたが、メタデータを使用してBlogエンティティへのリンクを作成しました。 コメントは投稿を参照するため、Commentエンティティに、それが属するBlogエンティティへのリンクを設定します。 これを行うには、ManyToOneリンクをBlogエンティティに参照します。 また、このリンクへのフィードバックはコメントを通じて利用できることを示しました。 反転するには、Doctrine 2が投稿に多くのコメントを含めることができるようにBlogエンティティを更新する必要があります。 エンティティの更新Blog src/Blogger/BlogBundle/Entity/Blog.php
コードを貼り付け
 <?php // src/Blogger/BlogBundle/Entity/Blog.php namespace Blogger\BlogBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Doctrine\Common\Collections\ArrayCollection; /** * @ORM\Entity(repositoryClass="Blogger\BlogBundle\Entity\Repository\BlogRepository") * @ORM\Table(name="blog") * @ORM\HasLifecycleCallbacks */ class Blog { // .. /** * @ORM\OneToMany(targetEntity="Comment", mappedBy="blog") */ protected $comments; // .. public function __construct() { $this->comments = new ArrayCollection(); $this->setCreated(new \DateTime()); $this->setUpdated(new \DateTime()); } // .. } 


指摘する必要があるいくつかの変更があります。 最初に、$ commentsオブジェクトにメタデータを追加しました。 前の章では、Doctrine 2に保存させたくないので、このオブジェクトのメタデータを追加しなかったことを覚えていますか? それでもこれは当てはまりますが、Doctrine 2がこのオブジェクトに適切なコメントエントリを追加できるようにしたいのです。 それがメタデータの達成を可能にするものです。 第二に、Doctrine 2ではArrayCollectionの$ commentsオブジェクトのデフォルト値を設定する必要があります。 これはコンストラクターで行います。 また、ArrayCollectionクラスをインポートするuseステートメントに注意してください。

Commentエンティティを作成し、Blogエンティティを更新したので、アクセスメソッドを作成しましょう。 次のDoctrine 2コマンドを実行します。
 $ php app/console doctrine:generate:entities Blogger\BlogBundle 


両方のエンティティが正しいアクセス方法で更新されます。 また、
src/Blogger/BlogBundle/Entity/Repository/CommentRepository.php

メタデータでこれを示したため、CommentRepositoryクラスが作成されました。

最後に、エンティティの変更を反映するためにデータベースを更新する必要があります。 これを行うには、以下に示すdoctrine:schema:updateコマンドを使用できますが、代わりにDoctrine 2 Migrationsについて説明します。
 $ php app/console doctrine:schema:update --force 

Doctrine 2の移行


Doctrine 2 Migration ExtensionとバンドルにはSymfony2が付属していません。手動でインストールする必要があります。 プロジェクトのルートにあるcomposer.jsonファイルを開き、以下に示すようにDoctrine 2 Migrationsの依存関係とバンドルを挿入します。
 "require": { // ... "doctrine/doctrine-migrations-bundle": "dev-master", "doctrine/migrations": "dev-master" } 

次に、コマンドを使用してライブラリを更新します。
 $ composer update 

これにより、すべてのライブラリがGithubで更新され、必要なディレクトリにインストールされます。
app / AppKernel.phpにあるカーネルにバンドルを登録しましょう
 // app/AppKernel.php public function registerBundles() { $bundles = array( // ... new Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle(), // ... ); // ... } 


これで、データベースを更新してエンティティの変更を反映する準備ができました。このプロセスは2段階で行われます。 最初に、エンティティと現在のデータベーススキーマの違いを処理するようDoctrine 2 Migrationsに指示する必要があります。 これはdoctrine:migrations:diffコマンドで実行されます。 次に、最初のチームが作成したデータに基づいて移行を実行する必要があります。 これはdoctrine:migrations:migrateコマンドで実行されます。
次の2つのコマンドを実行して、データベーススキーマを更新します。
 $ php app/console doctrine:migrations:diff $ php app/console doctrine:migrations:migrate 

以下に示す警告はyesと答えます
警告! スキーマの変更とデータの損失を引き起こす可能性のあるデータベース移行を実行しようとしています。 続行しますか? (y / n): はい

これで、データベースに最新のエンティティの変更が反映され、新しいコメントテーブルが含まれます。

ご注意

また、データベースにmigration_versionsという新しいテーブルが表示されます。 移行バージョン番号が保存されるため、現在データベースのバージョンを確認できるコマンドがあります。


ヒント

Doctrine 2の移行は、プログラムで変更を加えることができるため、本番データベースをアップグレードする素晴らしい方法です。 つまり、このタスクをプロジェクト展開シナリオに統合できるため、アプリケーションの新しいバージョンを展開すると、データベースが自動的に更新されます。 また、Doctrine 2の移行では、それぞれの移行にアップとダウンの方法があるため、変更をロールバックできます。 前のバージョンにロールバックするには、戻すバージョン番号を指定する必要があります。以下に示すように、これを行うことができます。
 $ php app/console doctrine:migrations:migrate 20110806183439 


データフィクスチャ



これでCommentエンティティができました。データフィクスチャを追加しましょう。 これは、エンティティを作成する良い機会です。 メタデータで示したように、コメントにはBlogエンティティとの接続が必要であることがわかっているため、Commentエンティティのフィクスチャを作成するときは、Blogエンティティを指定する必要があります。 Blogエンティティのフィクスチャはすでに作成されているため、このファイルを更新してCommentエンティティを追加するだけです。 今は管理可能かもしれませんが、後でユーザーや他のエンティティをバンドルに追加するとどうなりますか? Commentエンティティのフィクスチャ用に新しいファイルを作成することをお勧めします。 このアプローチの問題は、ブログのフィクスチャからブログの投稿にアクセスする方法です。

幸いなことに、これは、他のフィクスチャファイルがアクセスできるフィクスチャファイル内のオブジェクトへの参照を追加することで簡単に実現できます。 src/Blogger/BlogBundle/DataFixtures/ORM/BlogFixtures.phpあるブログエンティティデータフィクスチャを更新します。
次のように
 <?php // src/Blogger/BlogBundle/DataFixtures/ORM/BlogFixtures.php namespace Blogger\BlogBundle\DataFixtures\ORM; use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\Common\Persistence\ObjectManager; use Blogger\BlogBundle\Entity\Blog; class BlogFixtures extends AbstractFixture implements OrderedFixtureInterface { public function load(ObjectManager $manager) { // .. $manager->flush(); $this->addReference('blog-1', $blog1); $this->addReference('blog-2', $blog2); $this->addReference('blog-3', $blog3); $this->addReference('blog-4', $blog4); $this->addReference('blog-5', $blog5); } public function getOrder() { return 1; } } 


ここで注目に値する変更は、AbstractFixtureクラスの拡張とOrderedFixtureInterfaceの実装です。 これらのクラスをインポートする2つの新しいuseステートメントにも注意してください。

addReference()メソッドを使用してブログエンティティリンクを追加します。 この最初のパラメーターは、後でオブジェクトを取得するために使用できるリンクの識別子です。 最後に、フィクスチャのロード順序を指定するgetOrder()メソッドを実装する必要があります。 エントリはコメントの前にアップロードする必要があるため、1を返します。

備品コメント



これで、Commentエンティティのフィクスチャを定義する準備ができました。 フィクスチャファイルの作成src/Blogger/BlogBundle/DataFixtures/ORM/CommentFixtures.php
そして貼り付け
次の
 <?php // src/Blogger/BlogBundle/DataFixtures/ORM/CommentFixtures.php namespace Blogger\BlogBundle\DataFixtures\ORM; use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\Common\Persistence\ObjectManager; use Blogger\BlogBundle\Entity\Comment; use Blogger\BlogBundle\Entity\Blog; class CommentFixtures extends AbstractFixture implements OrderedFixtureInterface { public function load(ObjectManager $manager) { $comment = new Comment(); $comment->setUser('symfony'); $comment->setComment('To make a long story short. You can\'t go wrong by choosing Symfony! And no one has ever been fired for using Symfony.'); $comment->setBlog($manager->merge($this->getReference('blog-1'))); $manager->persist($comment); $comment = new Comment(); $comment->setUser('David'); $comment->setComment('To make a long story short. Choosing a framework must not be taken lightly; it is a long-term commitment. Make sure that you make the right selection!'); $comment->setBlog($manager->merge($this->getReference('blog-1'))); $manager->persist($comment); $comment = new Comment(); $comment->setUser('Dade'); $comment->setComment('Anything else, mom? You want me to mow the lawn? Oops! I forgot, New York, No grass.'); $comment->setBlog($manager->merge($this->getReference('blog-2'))); $manager->persist($comment); $comment = new Comment(); $comment->setUser('Kate'); $comment->setComment('Are you challenging me? '); $comment->setBlog($manager->merge($this->getReference('blog-2'))); $comment->setCreated(new \DateTime("2011-07-23 06:15:20")); $manager->persist($comment); $comment = new Comment(); $comment->setUser('Dade'); $comment->setComment('Name your stakes.'); $comment->setBlog($manager->merge($this->getReference('blog-2'))); $comment->setCreated(new \DateTime("2011-07-23 06:18:35")); $manager->persist($comment); $comment = new Comment(); $comment->setUser('Kate'); $comment->setComment('If I win, you become my slave.'); $comment->setBlog($manager->merge($this->getReference('blog-2'))); $comment->setCreated(new \DateTime("2011-07-23 06:22:53")); $manager->persist($comment); $comment = new Comment(); $comment->setUser('Dade'); $comment->setComment('Your SLAVE?'); $comment->setBlog($manager->merge($this->getReference('blog-2'))); $comment->setCreated(new \DateTime("2011-07-23 06:25:15")); $manager->persist($comment); $comment = new Comment(); $comment->setUser('Kate'); $comment->setComment('You wish! You\'ll do shitwork, scan, crack copyrights...'); $comment->setBlog($manager->merge($this->getReference('blog-2'))); $comment->setCreated(new \DateTime("2011-07-23 06:46:08")); $manager->persist($comment); $comment = new Comment(); $comment->setUser('Dade'); $comment->setComment('And if I win?'); $comment->setBlog($manager->merge($this->getReference('blog-2'))); $comment->setCreated(new \DateTime("2011-07-23 10:22:46")); $manager->persist($comment); $comment = new Comment(); $comment->setUser('Kate'); $comment->setComment('Make it my first-born!'); $comment->setBlog($manager->merge($this->getReference('blog-2'))); $comment->setCreated(new \DateTime("2011-07-23 11:08:08")); $manager->persist($comment); $comment = new Comment(); $comment->setUser('Dade'); $comment->setComment('Make it our first-date!'); $comment->setBlog($manager->merge($this->getReference('blog-2'))); $comment->setCreated(new \DateTime("2011-07-24 18:56:01")); $manager->persist($comment); $comment = new Comment(); $comment->setUser('Kate'); $comment->setComment('I don\'t DO dates. But I don\'t lose either, so you\'re on!'); $comment->setBlog($manager->merge($this->getReference('blog-2'))); $comment->setCreated(new \DateTime("2011-07-25 22:28:42")); $manager->persist($comment); $comment = new Comment(); $comment->setUser('Stanley'); $comment->setComment('It\'s not gonna end like this.'); $comment->setBlog($manager->merge($this->getReference('blog-3'))); $manager->persist($comment); $comment = new Comment(); $comment->setUser('Gabriel'); $comment->setComment('Oh, come on, Stan. Not everything ends the way you think it should. Besides, audiences love happy endings.'); $comment->setBlog($manager->merge($this->getReference('blog-3'))); $manager->persist($comment); $comment = new Comment(); $comment->setUser('Mile'); $comment->setComment('Doesn\'t Bill Gates have something like that?'); $comment->setBlog($manager->merge($this->getReference('blog-5'))); $manager->persist($comment); $comment = new Comment(); $comment->setUser('Gary'); $comment->setComment('Bill Who?'); $comment->setBlog($manager->merge($this->getReference('blog-5'))); $manager->persist($comment); $manager->flush(); } public function getOrder() { return 2; } } 



BlogFixturesクラスに加えた変更により、CommentFixturesクラスはAbstractFixtureクラスを拡張し、OrderedFixtureInterfaceを実装します。 これは、getOrder()メソッドも実装する必要があることを意味します。今回は値2を返すため、レコードのフィクスチャの後にフィクスチャを確実にロードできます。

また、前に作成したBlogエンティティへのリンクをどのように使用するかを確認できます。
 $comment->setBlog($manager->merge($this->getReference('blog-2'))); 

これで、フィクスチャをデータベースにロードする準備ができました。
 $ php app/console doctrine:fixtures:load 

警告に対応します。はい、
注意してください。データベースは削除されます。y / Nを続行しますか?はい

コメントを表示


これで、各ブログ投稿に関連付けられたコメントを表示できます。まず、ブログに対して承認された最新のコメントを受け取るメソッドでCommentRepositoryを更新します。

コメントリポジトリ


見つかったCommentRepositoryクラスを開いてsrc/Blogger/BlogBundle/Entity/Repository/CommentRepository.php貼り付けます
次の
 <?php // src/Blogger/BlogBundle/Entity/Repository/CommentRepository.php namespace Blogger\BlogBundle\Entity\Repository; use Doctrine\ORM\EntityRepository; /** * CommentRepository * * This class was generated by the Doctrine ORM. Add your own custom * repository methods below. */ class CommentRepository extends EntityRepository { public function getCommentsForBlog($blogId, $approved = true) { $qb = $this->createQueryBuilder('c') ->select('c') ->where('c.blog = :blog_id') ->addOrderBy('c.created') ->setParameter('blog_id', $blogId); if (false === is_null($approved)) $qb->andWhere('c.approved = :approved') ->setParameter('approved', $approved); return $qb->getQuery() ->getResult(); } } 


作成したメソッドは、ブログ投稿でコメントを受け取ります。これを行うには、クエリにwhere句を追加する必要があります。where節は、setParameter()メソッドを使用して設定される名前付きパラメーターを使用します。クエリで値を直接設定するのではなく、常にパラメーターを使用する必要があります
->where('c.blog = ' . blogId)
この例では、$ blogIdの値は安全ではなく、SQLインジェクション攻撃に対してクエリが開いたままになる場合があります。

ブログコントローラー


次に、BlogコントローラーのshowActionメソッドを更新してコメントを取得する必要があります。ブログコントローラーの更新src/Blogger/BlogBundle/Controller/BlogController.php
挿入
 // src/Blogger/BlogBundle/Controller/BlogController.php public function showAction($id) { // .. if (!$blog) { throw $this->createNotFoundException('Unable to find Blog post.'); } $comments = $em->getRepository('BloggerBlogBundle:Comment') ->getCommentsForBlog($blog->getId()); return $this->render('BloggerBlogBundle:Blog:show.html.twig', array( 'blog' => $blog, 'comments' => $comments )); } 


CommentRepositoryの新しいメソッドを使用して、承認済みのコメントを取得します。$ commentsコレクションもテンプレートに渡されます。

ブログショーテンプレート


これでブログのコメントのリストができました。ブログの表示テンプレートを更新してコメントを表示できます。コメントの出力をブログショーテンプレートに直接入力することもできますが、コメントには独自の本質があるため、別のテンプレートで表示を分離し、このテンプレートに含めることをお勧めします。これにより、アプリケーション内の任意の場所でコメント出力テンプレートを再利用できます。ブログテンプレートを更新show src/Blogger/BlogBundle/Resources/views/Blog/show.html.twig
挿入する
 {# src/Blogger/BlogBundle/Resources/views/Blog/show.html.twig #} {# .. #} {% block body %} {# .. #} <section class="comments" id="comments"> <section class="previous-comments"> <h3>Comments</h3> {% include 'BloggerBlogBundle:Comment:index.html.twig' with { 'comments': comments } %} </section> </section> {% endblock %} 


新しいTwig includeタグが表示されます。これには、BloggerBlogBu​​ndleテンプレートのコンテンツ(コメント:index.html.twig)が含まれます。また、テンプレートに引数をいくつでも渡すことができます。この場合、視覚化のためにエンティティのコメントコレクションを調べる必要があります。

コメントショーテンプレート


BloggerBlogBu​​ndle:コメント:上記に含めたindex.html.twigはまだ存在しないため、作成する必要があります。これは単なるテンプレートであるため、このためのルートやコントローラーを作成する必要はありません。テンプレートファイルのみが必要です。新しいファイル
src/Blogger/BlogBundle/Resources/views/Comment/index.html.twig
作成して貼り付け
次の
 {# src/Blogger/BlogBundle/Resources/views/Comment/index.html.twig #} {% for comment in comments %} <article class="comment {{ cycle(['odd', 'even'], loop.index0) }}" id="comment-{{ comment.id }}"> <header> <p><span class="highlight">{{ comment.user }}</span> commented <time datetime="{{ comment.created|date('c') }}">{{ comment.created|date('l, F j, Y') }}</time></p> </header> <p>{{ comment.comment }}</p> </article> {% else %} <p>There are no comments for this post. Be the first to comment...</p> {% endfor %} 



ご覧のとおり、Commentエンティティのコレクションを反復処理し、コメントを表示します。別の良いTwigであるサイクル関数について話しましょう。この関数は、ループの各反復中に渡す配列の値を反復処理します。ループ反復の現在の値は、特殊変数loop.index0から取得されますこれは、0から始まるループの繰り返しをカウントします。ループ内にいる場合、他の多くの特殊変数を使用できます。また、記事要素のHTML-ID設定に気付くかもしれません。これにより、作成したコメントへのリンクを後で作成できます。

CSSのコメント表示



最後に、コメントをスタイリッシュに見せるためにCSSを追加しましょう。にあるスタイルを更新src/Blogger/BlogBundle/Resouces/public/css/blog.css
 /** src/Blogger/BlogBundle/Resorces/public/css/blog.css **/ .comments { clear: both; } .comments .odd { background: #eee; } .comments .comment { padding: 20px; } .comments .comment p { margin-bottom: 0; } .comments h3 { background: #eee; padding: 10px; font-size: 20px; margin-bottom: 20px; clear: both; } .comments .previous-comments { margin-bottom: 20px; } 




シンボリックリンク方式を使用してWebフォルダー内のバンドル資産にアクセスしない場合は、資産インストールコマンドを再実行して変更をコピーする必要があります。
 $ php app/console assets:install web 



たとえば、http:// localhost:8000/2などのshowページの1つを見ると、エントリのコメントの出力が表示されます。



コメントを追加


この章の最後の部分では、ユーザー向けの機能を拡張し、ブログエントリにコメントを追加します。これは、ブログのショーページのフォームのおかげで可能になります。コンタクトページでフォームを作成したときに、Symfony 2でフォームを作成することについて既に説明しました。コメントフォームを手動で作成する代わりに、Symfony2を使用して作成できます。次のコマンドを実行して、CommentエンティティのCommentTypeクラスを生成します。
 $ php app/console generate:doctrine:form BloggerBlogBundle:Comment 


コメントエンティティを定義するために、速記の使用に再び気付くでしょう。

ヒント

doctrine:generate:formコマンドも利用できることに気づいたかもしれませんこれは、名前が異なる同じチームです。


チームは、CommentTypeクラスを作成しました src/Blogger/BlogBundle/Form/CommentType.php

コードを表示
 <?php namespace Blogger\BlogBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class CommentType extends AbstractType { /** * @param FormBuilderInterface $builder * @param array $options */ public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('user') ->add('comment') ->add('approved') ->add('created', 'datetime') ->add('updated', 'datetime') ->add('blog') ; } /** * @param OptionsResolver $resolver */ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'data_class' => 'Blogger\BlogBundle\Entity\Comment' )); } } 



前のクラスであるRequest Typeで、ここで何が起こるかをすでに調べました。今すぐこのクラスをカスタマイズすることから始められますが、最初にフォームを表示することに取りかかりましょう。

コメントフォームを表示します。


ユーザーにショーブログページからコメントを追加してほしいので、ブログコントローラーのshowActionメソッドでフォームを作成し、ショーテンプレートに直接フォームを表示できます。ただし、コメントの表示で行ったように、このコードを分離することをお勧めします。コメントの表示とコメントフォームの表示の違いは、コメントフォームの処理が必要なため、コントローラーが必要なことです。

ルート


フォームを処理するための新しいルートを作成する必要があります。見つかった新しいルートを追加しますsrc/Blogger/BlogBundle/Resources/config/routing.yml
 BloggerBlogBundle_comment_create: path: /comment/{blog_id} defaults: { _controller: "BloggerBlogBundle:Comment:create" } requirements: methods: POST blog_id: \d+ 


コントローラー


次に、前述の新しいCommentControlerを作成する必要があります。src / Blogger / BlogBu​​ndle / Controller / CommentController.phpに新しいファイルを作成して貼り付けます
次の
 <?php // src/Blogger/BlogBundle/Controller/CommentController.php namespace Blogger\BlogBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Blogger\BlogBundle\Entity\Comment; use Blogger\BlogBundle\Form\CommentType; use Symfony\Component\HttpFoundation\Request; /** * Comment controller. */ class CommentController extends Controller { public function newAction($blog_id) { $blog = $this->getBlog($blog_id); $comment = new Comment(); $comment->setBlog($blog); $form = $this->createForm(CommentType::class, $comment); return $this->render('BloggerBlogBundle:Comment:form.html.twig', array( 'comment' => $comment, 'form' => $form->createView() )); } public function createAction(Request $request, $blog_id) { $blog = $this->getBlog($blog_id); $comment = new Comment(); $comment->setBlog($blog); $form = $this->createForm(CommentType::class, $comment); $form->handleRequest($request); if ($form->isValid()) { // TODO: Persist the comment entity return $this->redirect($this->generateUrl('BloggerBlogBundle_blog_show', array( 'id' => $comment->getBlog()->getId())) . '#comment-' . $comment->getId() ); } return $this->render('BloggerBlogBundle:Comment:create.html.twig', array( 'comment' => $comment, 'form' => $form->createView() )); } protected function getBlog($blog_id) { $em = $this->getDoctrine() ->getManager(); $blog = $em->getRepository('BloggerBlogBundle:Blog')->find($blog_id); if (!$blog) { throw $this->createNotFoundException('Unable to find Blog post.'); } return $blog; } } 



Commentコントローラーには、新規作成用と作成用の2つのメソッドを作成しました。新しいメソッドはコメントのフォームの表示に関連付けられ、作成メソッドはコメントフォームの処理に関連付けられます。これは大きなコードのように思えるかもしれませんが、ここでは新しいことは何もありません。連絡フォームを作成したときに、すべてが2番目の部分で説明されました。ただし、先に進む前に、コメントコントローラーで何が行われているかを完全に理解してください。

フォーム検証



ユーザーコメントのパラメーターに空の値を使用してコメントを残すことはできませんこれを実現するために、リクエストフォームを作成するときに2番目の部分で検討した検証を思い出します。見つかったコメントエンティティの更新src/Blogger/BlogBundle/Entity/Comment.php
コードを表示
 <?php // src/Blogger/BlogBundle/Entity/Comment.php // .. use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Constraints\NotBlank; // .. class Comment { // .. public static function loadValidatorMetadata(ClassMetadata $metadata) { $metadata->addPropertyConstraint('user', new NotBlank(array( 'message' => 'You must enter your name' ))); $metadata->addPropertyConstraint('comment', new NotBlank(array( 'message' => 'You must enter a comment' ))); } // .. } 


ここでは、ユーザーフィールドとコメントフィールドが入力されているかどうかがチェックされます。デフォルトのメッセージも再定義しました。上記のように、ClassMetadataおよびNotBlank名前空間を忘れずに追加してください。

ディスプレイ



次に、コントローラーの新しいメソッドとメソッドを作成するための2つのテンプレートを作成する必要があります。にある新しいファイルを作成しsrc/Blogger/BlogBundle/Resources/views/Comment/form.html.twigて貼り付けます
次の
 {# src/Blogger/BlogBundle/Resources/views/Comment/form.html.twig #} {{ form_start(form, { 'action': path('BloggerBlogBundle_comment_create' , { 'blog_id' : comment.blog.id }), 'method': 'POST', 'attr': {'class': 'blogger'} }) }} {{ form_widget(form) }} <p> <input type="submit" value="Submit"> </p> 



このテンプレートの目的は非常にシンプルで、コメントフォームを表示するだけです。また、フォームのアクションメソッドがPOSTであり、BloggerBlogBu​​ndle_comment_createを作成した新しいルートを参照していることにも気付くでしょう

次に、作成メソッドのテンプレートを作成しましょう。にある新しいファイルを作成しsrc/Blogger/BlogBundle/Resources/views/Comment/create.html.twigて貼り付けます
次の
 {% extends 'BloggerBlogBundle::layout.html.twig' %} {% block title %}Add Comment{% endblock%} {% block body %} <h1>Add comment for blog post "{{ comment.blog.title }}"</h1> {% include 'BloggerBlogBundle:Comment:form.html.twig' with { 'form': form } %} {% endblock %} 



CommentコントローラーcreateActionメソッドはフォームの処理を処理するため、エラーが発生する可能性があるため、フォームを表示できる必要があります。BloggerBlogBu​​ndle:Comment:form.html.twig再利用して、コードを複製しないようにフォームを表示します。

ブログのショーテンプレートを更新して、フォームを表示しましょう。テンプレートを更新src/Blogger/BlogBundle/Resources/views/Blog/show.html
 {# src/Blogger/BlogBundle/Resources/views/Blog/show.html.twig #} {# .. #} {% block body %} {# .. #} <section class="comments" id="comments"> {# .. #} <h3>Add Comment</h3> {{ render(controller('BloggerBlogBundle:Comment:new',{ 'blog_id': blog.id })) }} </section> {% endblock %} 


ここでは別のTwigタグ、renderを使用しました。このタグは、テンプレート内のコントローラーのコンテンツを表示します。この場合、BloggerBlogBu​​ndleの内容を表示します。Comment:new http:// localhost:8000/2

などのブログページの1つを見ると、次のような通知が表示されます。この投稿は、BloggerBlogBu​​ndle:Blog:show.html.twigテンプレートによってトリガーされます。BloggerBlogBu​​ndle:Blog:show.html.twigテンプレートの23行目を見ると、この行は問題が実際にBloggerBlogBu​​ndle:Comment:create controllerの埋め込みプロセスにあることを示しています




 {{ render(controller('BloggerBlogBundle:Comment:new',{ 'blog_id': blog.id })) }} 


エラーメッセージをより詳しく見ると、エラーが発生した理由に関する詳細情報が得られます。
彼女は、呼び出そうとしているフィールドには、関連するエンティティの__toString()メソッドがないことを教えてくれます。選択フィールドは、ユーザーにいくつかのオプション(選択要素(ドロップダウンリスト)など)の選択肢を提供するフォーム要素です。このようなフィールドをコメントの形式でどこに表示するのか疑問に思われるかもしれません。コメントフォームテンプレートをもう一度見ると、Twig {{form_widget(form)}}関数を使用してフォームを表示していることがわかります。この関数は、フォーム全体を表示します。 CommentTypeクラスから作成されたフォームクラスに戻りましょう。 FormBuilderオブジェクトを使用して、多くのフィールドがフォームに追加されていることがわかります。特に、ブログフィールドを追加しています。

ガイドの第2部では、FormBuilderがフィールドに関連するメタデータに基づいて出力用のフィールドのタイプを推測しようとする方法について覚えています。コメントエンティティとブログエンティティの間の関係を確立したため、FormBuilderは、コメントを、ユーザーがコメントを添付するエントリを指定できる選択フィールドにすることを提案しました。そのため、フォームに選択肢フィールドがあり、Symfony 2エラーがトリガーされました。この問題は、Blogエンティティに__toString()メソッドを実装することで解決できます。

 // src/Blogger/BlogBundle/Entity/Blog.php public function __toString() { return $this->getTitle(); } 


ヒント

Symfony2のエラーメッセージは、発生した問題を説明する際に非常に有益です。通常、エラーメッセージを読むと、デバッグプロセスが非常に簡単になります。エラーメッセージにはフルパスも表示されるため、エラーの原因となった手順を確認できます。


これで、ページを更新すると、コメントフォームの出力が表示されます。また、承認、作成、更新、ブログなどの不要なフィールドが出力されていることにも気付くでしょう。これは、以前に生成されたCommentTypeクラスを構成しなかったためです。

ヒント

表示されたすべてのフィールドは正しいタイプです。ユーザーフィールドテキスト、コメントフィールドテキストエリア、2つのDateTimeフィールドを使用すると、時刻などを指定できます。

これは、FormBuilderが表示するフィールドのタイプを推測できるためです。彼はあなたが提供するメタデータに基づいてこれを行うことができます。Commentエンティティに対して非常に具体的なメタデータを定義したため、FormBuilderはフィールドのタイプについて正確な仮定を立てることができます。


src / Blogger / BlogBu​​ndle / Form / CommentType.phpにあるクラスを更新して、必要なフィールドのみを表示します。
挿入する
 <?php namespace Blogger\BlogBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class CommentType extends AbstractType { /** * @param FormBuilderInterface $builder * @param array $options */ public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('user'); $builder->add('comment'); } /** * @param OptionsResolver $resolver */ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'data_class' => 'Blogger\BlogBundle\Entity\Comment' )); } public function getBlockPrefix() { return 'blogger_blogbundle_commenttype'; } } 


これで、ページを更新すると、ユーザー名のフィールドとコメントのフィールドのみが表示されます。今すぐフォームを送信すると、コメントはデータベースに保存されません。フォームが検証に合格した場合、フォームコントローラーはコメントエンティティに対して何もしないためです。では、どのようにコメントをデータベースに保存するのでしょうか?フィクスチャデータを作成するときに、これを行う方法は既に説明しました。以下に示すようにcreateActionメソッドを更新します。
 // src/Blogger/BlogBundle/Controller/CommentController.php public function createAction(Request $request, $blog_id) { //.. if ($form->isValid()) { $em = $this->getDoctrine() ->getManager(); $em->persist($comment); $em->flush(); return $this->redirect($this->generateUrl('BloggerBlogBundle_blog_show', array( 'id' => $comment->getBlog()->getId())) . '#comment-' . $comment->getId() ); } //.. } 

persist()およびflush()メソッドを呼び出すと、Commentエンティティが保存されます。フォームはPHPオブジェクトを処理し、Doctrine 2はこれらのオブジェクトを管理および保存することに注意してください。フォームの送信と送信されたデータのデータベースへの保存との間には直接的な関係はありません。

これで、ブログ投稿にコメントを追加できるはずです。



おわりに



この点で私たちは大きな進歩を遂げました。ブログは期待どおりに機能し始めています。ユーザーは、ブログ投稿にコメントを投稿したり、他のユーザーが投稿したコメントを閲覧できるようになりました。複数のフィクスチャファイルを参照できるフィクスチャを作成する方法を確認し、Doctrine 2 Migrationsを使用して、エンティティを変更するときに組み込みのデータベーススキーマを保持します。

次に、タグクラウドと最近のコメントを配置するためのサイドバーの作成について見ていきます。また、Twigの知識を拡張し、それを使用してカスタムフィルターを作成する方法を確認します。結論として、アセットライブラリの使用を検討します。これは、アセットの管理に役立ちます。

ソースとサポート資料:https :

//symfony.com/
http://tutorial.symblog.co.uk/
http://twig.sensiolabs.org/
http://www.doctrine-project.org/
http://odiszapc.ru/doctrine/

ポスト台本
, , , .


パート1-Symfony2とテンプレートの構成
パート2-連絡先ページ:バリデーター、フォーム、およびメール
パート3-Doctrine 2とデータフィクスチャ
パート5-Twig拡張、サイドバー、および資産
パート6-ユニットおよび機能テスト


また、マニュアルが気に入ったら、プロジェクトリポジトリにスターを付けるか、購読することができますありがとう

Source: https://habr.com/ru/post/J302602/


All Articles