<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>GANCHIKU.com &#187; symfony</title>
	<atom:link href="http://ganchiku.com/category/symfony/feed" rel="self" type="application/rss+xml" />
	<link>http://ganchiku.com</link>
	<description>renewal, baby!</description>
	<lastBuildDate>Wed, 16 May 2012 13:38:54 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Symfony2 クックブックの主観的なおさらい Symfony Advent Calender 2011 JP –14日目</title>
		<link>http://ganchiku.com/2011/12/symfony2-cookbook-advent-calendar2011.html</link>
		<comments>http://ganchiku.com/2011/12/symfony2-cookbook-advent-calendar2011.html#comments</comments>
		<pubDate>Wed, 14 Dec 2011 08:14:09 +0000</pubDate>
		<dc:creator>shin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://ganchiku.com/?p=723</guid>
		<description><![CDATA[Symfony Advent Calendar の14日目です！昨日は、@bakorerさんのPHPFog + Symfony2 でステージング環境を作る方法：Symfony Advent Calender 2011 J &#8230; <a href="http://ganchiku.com/2011/12/symfony2-cookbook-advent-calendar2011.html">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Symfony Advent Calendar の14日目です！昨日は、<a href="http://twitter.com/bakorer">@bakorer</a>さんの<a href="http://codenote.net/php/symfony/548.html" target="_blank">PHPFog + Symfony2 でステージング環境を作る方法：Symfony Advent Calender 2011 JP – 13日目</a>でした。</p>
<p>先日、東京のVoyage Groupさんで発表したLTの内容を、より詰めてまとめたいと思います。<br />
Symfony勉強のLTでも話をしましたが、10月末よりSymfony Cookbookの翻訳を進めて、現時点では全記事が翻訳されています。クックブックと言っても、実際に役に立つ記事、これはあまり使わないだろ、という記事があると思います。そんなことをまとめて、主観的に感想とオススメ度を書いていこうと思います。主観なのでツッコミどころはあると思いますが、まぁそこは教えてくださいな。実際は、全部ここで書ききれないので、24日までに間に合えば、後のAdvent Calendarでまた出てくるかもしれないです。</p>
<p>では。</p>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/workflow/new_project_git.html">git 上で、Symfony2 プロジェクトを作成し、管理する方法</a></h2>
<blockquote><p>
Symfony2 Standard Edition(http://symfony.com/download)で、without vendorをダウンロードする。<br />
.gitignoreファイルに追加する。</p>
<pre class="brush:text">
/web/bundles/
/app/bootstrap*
/app/cache/*
/app/logs/*
/vendor/
/app/config/parameters.yml
</pre>
<p>/app/config/parameters.yml.distを作っておけば、チェックアウトしてきて、コピーしてカスタマイズすれば、簡単にプロジェクトを始められる。</p>
<pre class="brush:text">
git init
git add .
git commit -m “Initial commit”
php bin/vendors install
</pre>
<p>Symfony2では、バックではgitを使ってライブラリを管理しているが、全てdepsとdeps.lockで特定のバージョンとidを管理しており、bin/vendorsでバージョンの整合性を見た方が良い。git submoduleでもできなくはないが、deps等を一纏めにしているため、bin/vendorsコマンドを使う方が良い。
</p></blockquote>
<h4>感想：</h4>
<p>最近の開発プロジェクトでは、Gitなどの採用は必須だと思われるので、プロジェクトの始め方として紹介してあるこの記事は非常に役に立つ。Symfony2では、depsとdeps.lockを使用して、いろいろなライブラリのバージョンの整合性を保っているのも特筆する点か。</p>
<div>オススメ度：☆☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/controller/error_pages.html">エラーページのカスタマイズ方法</a></h2>
<blockquote><p>
エラーページをカスタマイズする方法は２つあり、この記事では異なるエラーページのテンプレートを編集してカスタマイズを行う。<br />
app/Resources/TwigBundle/views/Exception/error.html.twigなどを作り、デフォルトのエラーページをオーバーライドする。デフォルトのエラーページのテンプレートは、vendor/symfony/src/SymfonyBundle/TwigBundle/views/Exception以下にあるので、これを参考にして作るのが良い。</p>
<p>エラーページのテンプレートが採用される順序</p>
<ul>
<li>１．フォーマット(html, xml, jsonなど)とステータスコード(404, 501など)の両方がマッチするテンプレートを探して、あれば使用する。</li>
<li>２．フォーマット(html, xml,jsonなど)のみにマッチするテンプレートを探して、あれば使用する。</li>
<li>３．error.html.twigを使用する。</li>
</ul>
<p>デバッグページもexception.html.twigなどをカスタマイズすることで変更することができる。
</p></blockquote>
<h4>感想：</h4>
<p>実際のアプリケーションでは、エラーページのカスタマイズは必須なので、知っていて良い内容だ。一度読めば、だいたいやり方はわかると思われる。基本的には、デフォルトのエラーページをコピーしてカスタマイズすることになりそう。</p>
<div>オススメ度：☆☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/routing/scheme.html">ルートで常に HTTPS を強制させる方法</a></h2>
<blockquote><p>
routing.ymlに requirementsで_schemeをhttpsと指定することでHTTPS通信を強制することが可能。
</p></blockquote>
<h4>感想：</h4>
<p>HTTPSを使用するサービスは作ったことがないので、今のところ私自身には役に立つことはないが、実際に使えば便利そうだ。なお、この記事は、ymlで指定してあるが、アノテーションで指定することもできる。その際には、以下のようにすれば良い。</p>
<pre class="brush:text">
* @Route("/demo/secured", requirements={"_scheme"="https"})
</pre>
<div>オススメ度：☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/routing/slash_in_parameter.html">ルートパラメターに “/” 文字を使えるようにする方法</a></h2>
<blockquote><p>
ルートのrequirementsでパラメターの正規表現パターンを変更する。<br />
デフォルトは、[^/]+ になっているので、例えば、”.+”とすれば、全ての文字を受け付けることができる。
</p></blockquote>
<h4>感想：</h4>
<p>たまにあるはまりどころであるが、そもそもルートパラメターに “/” を使わないといけない状況になることはあるのかな。逆に、数値だけとか、さらに厳しくする際にはこの正規表現を適宜変更すれば良いということがわかった。この辺はsymfony1と同じか。なお、この記事は、ymlで指定してあるが、アノテーションで指定することもできる。その際には、以下のようにすれば良い。</p>
<pre class="brush:text">
  * @Route("/hello/{name}", requirements={"name"=".+"})
</pre>
<div>オススメ度：☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/assetic/asset_management.html">Assetの管理にどうやってAsseticを使うか?</a></h2>
<blockquote>
<h5>アセット</h5>
<p>Asseticを使用すれば、CSS、JavaScript、画像を最適化することができる。CSS, JavaScriptは元が複数のディレクトリに分散していても、１ファイルにまとめて出力することができる。</p>
<h5>フィルター</h5>
<p>フィルターを使用すれば、アセットファイルが出力する前にyuicompressorなどの実行結果を返すことができる。</p>
<h5>AssetのURLを指定する</h5>
<p>デフォルトは、Asseticが自動的にファイル名を決めるが、outputパラメターを使用して指定することも可能である。</p>
<h5>出力のキャッシュ</h5>
<p>プロダクション環境では、 php app/console assetic:dumpで予め静的にファイルを置いておくと良い。
</p></blockquote>
<h4>感想：</h4>
<p>Assetic素晴らしい！symfony1系では、@hidenorigotoから教えてもらったnpAssetsOptimizerPluginを使用していたが、それよりも柔軟な指定ができるのが良い。</p>
<div>オススメ度：☆☆☆☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/assetic/yuicompressor.html"> YUI Compressorを使ってJavascriptやStylesheetのサイズを圧縮するには? </a></h2>
<blockquote><p>
・YUI Compressorのjarファイルをダウンロードして適当な場所に置いておく。あとはconfg.ymlで指定してフィルターとして出力する。</p>
<p>・フィルタ名の前に ? を付けると、デバッグ時にフィルターを無効にすることができる。
</p></blockquote>
<h4>感想：</h4>
<p>転送量を減らすために、JavaScriptとStylesheetの圧縮は、必要な機能であるが、実際にAssetic無しでやろうと思えば面倒なので非常に助かる。ただ、前の記事を呼んでおけば、この記事は必要ないのではないか、と思う。フィルタ名の前に ? を付けるというのは一応知っていてもいいかもしれない。</p>
<div>オススメ度：☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/assetic/jpeg_optimize.html">Twigの関数で画像の最適化にAsseticをどうやって使うか?</a></h2>
<blockquote>
<ul>
<li>jpegoptim をフィルターとして使用すれば、JPEGファイルを最適化することができる。</li>
<li>jpegoptimのオプションとして、EXIFデータを削除したり、最適化クオリティを下げることが可能。</li>
<li>また、twigを使用していれば、シンタックスシュガーとしてjpegoptimヘルパー関数を使用することもできる。</li>
<li>キャッシュの生成ディレクトリもoutputパラメターを使用して指定することができる。</li>
</ul>
</blockquote>
<h4>感想：</h4>
<p>jpegoptim素晴らしい。つか、知らんかった。これがあれば、アップロードされたファイルもオリジナルはウェブから見えないところに置いておいて、キャッシュをウェブから見えるところに出力すれば良いな。同様のサイズ変更などもあれば、クライアントやサムネイルも簡単にできそう。というか、普通に考えれそうな機能であるので、既にある？</p>
<div>オススメ度：☆☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/assetic/apply_to_option.html">特定の拡張子に Assetic フィルターを適用するには？<br />
</a></h2>
<blockquote>
<ul>
<li>Asseticフィルターには、CoffeeScriptのフィルターがあり、出力直前にJavaScriptにコンパイルすることができる。</li>
<li>さらに、複数のCoffeeScriptファイルを１つJavaScriptとしてコンパイルして、圧縮して出力することができる。</li>
<li>CoffeeScriptファイル、JavaScriptファイルが両方が混じっているときには、拡張子に基づいて、特定のフィルターを指定することができる。そうすれば、結果、1つのJavaScriptファイルとして出力される。</li>
</ul>
</blockquote>
<h4>感想：</h4>
<p>CoffeeScriptを書いたことはないが、JSと混同したときなどに便利そう。あったら便利だろうが、たぶん使うことはないかな。</p>
<div>オススメ度：☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/doctrine/file_uploads.html">Doctrine でファイルアップロードを扱う方法</a></h2>
<blockquote><p>
この記事では一般的なファイルアップロードの例を紹介している。まず最初のステップとして、仮想的なfileフィールドを使用し、バリデーション後に実行されるuploadメソッドで、ファイルを実際のファイル保存先へ移動し、保存先をpathとしてデータベースに保存している。</p>
<p>しかし、エンティティ保存時の問題とファイル移動の問題の対処をアトミックにするために、ライフサイクルコールバックを使用した例を次に挙げている。</p>
<p>(@ORM\HasLifecycleCallbacks)を使用して、PrePersist, PreUpdate, PostPersist, PostUpdate, PostRemove などでファイル移動等の処理を書いて、レコードの保存に合わせていること。</p>
<p>また、同様にファイル名もidにすることができる。</p>
</blockquote>
<h4>感想：</h4>
<p>この記事は、実際のアプリケーションを作る際に参考になる。特にフックの使い方とファイルアップロードをする際のプラクティスとして読んでおくことをお勧めする。</p>
<div>オススメ度：☆☆☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/doctrine/common_extensions.html">Doctrine エクステンション: Timestampable: Sluggable, Translatable, など<br />
</a></h2>
<blockquote><p>
Doctrineエクステンションの話で実際はそれぞれのドキュメントを参照すること。
</p></blockquote>
<h4>感想：</h4>
<p>特になし。</p>
<div>オススメ度：☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/doctrine/event_listeners_subscribers.html">イベントリスナーとサブスクライバーを登録する</a></h2>
<blockquote><p>
サービスとしてリスナーとサブスクライバーを別クラスに指定することができる。特定のイベントのフックをそのサービスに登録させる方法を説明している。
</p></blockquote>
<h4>感想：</h4>
<p>便利と言えば、便利だが多用すると複雑になってしまいそうである。記事にあるように保存時に検索インデックスを作成するといった用途以外に思いつかない。また、実際には、サブスクライバーの話はこの記事には書いていないのでは。。。</p>
<div>オススメ度：☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/doctrine/dbal.html">Doctrine の DBAL レイヤーの使用方法<br />
</a></h2>
<blockquote><p>
Doctrine DBALは、PDOの抽象レイヤーで、Doctrine ORMはDoctrine DBALのさらなる抽象レイヤーである。DBALを使うことでSQLをRDBMSに依存せずに使用することができる。
</p></blockquote>
<h4>感想：</h4>
<p>通常は、ORMを使用していればいいと思われるが、ORMにすることによってメモリが大量に消費されていたり、パフォーマンスの問題を解決する際にはDBALを直接叩いた方が速くなるので、知っていてもいい内容である。というか、アプリケーションによっては、あえてORMを使わないという選択もありかもしれない。</p>
<div>オススメ度：☆☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/doctrine/reverse_engineering.html">既にあるデータベースからエンティティを生成する方法</a></h2>
<blockquote><p>
・Doctrineにデータベースの内部を調べさせて、メタデータを作成することができる。次のコマンドでスキーマに書き出す。</p>
<pre class="brush:text">
php app/console doctrine:mapping:convert xml ./src/Acme/BlogBundle/Resources/config/doctrine/metadata/orm --from-database --force
</pre>
<p>・メタデータからエンティティクラスを作成することができる。次のコマンドでアノテーションマッピングをしたエンティティクラスを生成する</p>
<pre class="brush:text">
php app/console doctrine:mapping:import AcmeBlogBundle annotation
</pre>
<p>・次のコマンドでゲッターとセッターをエンティティクラスに追加する。</p>
<pre class="brush:text">
php app/console doctrine:generate:entities AcmeBlogBundle
</pre>
</blockquote>
<h4>感想：</h4>
<p>最初にデータベースを作ってある場合には参考になる。データベースの設計で、ツールを使い生のSQLを吐く仕組み等に便利そう。<br />
オススメ度:：☆☆☆</p>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/doctrine/multiple_entity_managers.html">複数のエンティティマネージャと連携する方法</a></h2>
<blockquote><p>
コンフィギュレーションで、doctrineのentity_managersを複数指定することができ、コントローラ等がそれぞれ呼び出すことができる。
</p></blockquote>
<h4>感想：</h4>
<p>実際はNoteにも書いてあるように、ほとんどのケースでは、複数のエンティティマネージャを使用することはないと思われる。もし使われるとすれば、既に動いているプロジェクトで使用しているデータベースを参照するときくらいか。</p>
<div>オススメ度:：☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/doctrine/custom_dql_functions.html">カスタム DQL 関数の登録方法<br />
</a></h2>
<blockquote><p>
Doctrineの話でSymfony2からは直接は関係ないが、カスタムDQLをコンフィギュレーションで指定することができる。
</p></blockquote>
<h4>感想：</h4>
<p>特に無し</p>
<div>オススメ度:：☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/form/form_customization.html">フォームのレンダリングのカスタマイズ方法</a></h2>
<blockquote>
<h5>フォームレンダリングの基本</h5>
<p>・各フィールドのラベル、エラー、ウィジェットを出力する際：<br />
   {{ form_label(form.age) }}<br />
   {{ form_errors(form.age) }}<br />
   {{ form_widget(form.age) }}<br />
・各フィールドをまとめて出力する際：<br />
   {{ form_row(form.age) }}<br />
・フォーム全体を出力する際：<br />
   {{ form_widget(form) }}</p>
<h5>フォームテーマ</h5>
<p>form_widgetなどのブロックはオーバーライドすることができる。FrameworkBundle/Resources/views/Formを参考にする。</p>
<h5>フォームのテーマ化</h5>
<p>・フォームと同じテンプレートの中でブロックをオーバーライドすることができる。<br />
・もしくは、別テンプレートでブロックをオーバーライドし、同テンプレートファイルを form_themeタグを使用して適用させることができる(twigの場合)。</p>
<h5>ベースフォームブロックを使用(twig)</h5>
<p>・ useタグを使って、form_widgetを別名(例えば、base_form_widget)とすることで、元のform_widgetを装飾することができる。</p>
<h5>アプリケーション全体でカスタマイズ</h5>
<p>コンフィギュレーションでtwig:form:resourcesを変更して、アプリケーション全体のフォームのテーマを変更することができる。</p>
</blockquote>
<h4>感想：</h4>
<p>この記事はフォームフィールドのデザインをスマートにする際に必須の記事である。クックブックというよりガイドブックに入っていてもおかしくない内容である。</p>
<div>オススメ度：☆☆☆☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/form/dynamic_form_generation.html">フォームイベントを使用してフォームを動的に生成する方法</a></h2>
<blockquote><p>
新規のときと編集のときに出力するフォームフィールドが異なるときにこの記事の内容が役に立つ。新規のときと編集のときで出力したり、しなかったりするフィールドをフォームイベントで動的に見て、設定する。<br />
・具体的には、フォームクラスで直接フィールドをaddするのではなく、addEventSubscriberでサブスクライバを指定させて、フォームフィールドを出力するかどうかを判断させている。</p>
</blockquote>
<h4>感想：</h4>
<p>ここもちょっと難しいが、実際に必要となることが多いと思われる。例えば、新規ではパスワードやメールアドレスを入力させるが、編集では、パスワードのフィールドやメールアドレスのフィールドは、変更できないようにする、といったことがしばしばある。フォームを２つ作ればいいという話もあるが、同じフォームフィールドを扱う可能性がある以上、この記事を参考にした方が良いと思う。</p>
<div>オススメ度：☆☆☆</div>
<hr />
<h2>
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/form/form_collections.html">フォームのコレクションを埋め込む方法</a></h2>
<blockquote><p>
ManyToManyのリレーションのあるテーブルを使用する際に、１つのフォームを表示で従属するテーブルのフォームを埋め込むことができる。実際に埋め込むには、フォームビルダーにaddする際に、指定するフィールドタイプにcollectionを使用する。
</p></blockquote>
<h4>感想：</h4>
<p>この記事はまだ完成はしていない。タグを新規に追加する方法や削除する方法がないので、今後の充実に期待。</p>
<div>オススメ度：☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/form/create_custom_field_type.html">カスタムフォームフィールドタイプの作成方法</a></h2>
<blockquote><p>
未執筆
</p></blockquote>
<h4>感想：</h4>
<p>執筆者求む！</p>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/configuration/environments.html">新しい環境を作成して、使いこなす方法</a></h2>
<blockquote><p>
dev環境、prod環境、test環境の他に異なる環境を作る方法をこの記事で説明している。この記事では、benchmark環境を実際に作成して説明している。
</p></blockquote>
<h4>感想：</h4>
<p>実際に新しく環境を作ることはあまり無いと思われるが、豆知識として知っておいてもいいと思う。symfony1では、ステージング環境を作成とかあったが、実際には個人的にあまり使わなかった。</p>
<div>オススメ度：☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/configuration/external_parameters.html">サービスコンテナで外部パラメターをセットする方法</a></h2>
<blockquote><p>
・Symfony は、 SYMFONY__ の接頭辞が付いたあらゆる環境変数をサービスコンテナのパラメターとしてセットすることができる。Apacheで、SetEnvするなどしておけば、特定のデータベースのパラメターを接続設定ファイルで読むことができる。
</p></blockquote>
<h4>感想：</h4>
<p>ウェブからのみの場合は確かにApacheでの指定でいいが、Symfony2を使う上で、コンソールからの指定も必要である。もちろん環境変数を指定することができるが、複数の場所（Apacheの設定とシェル環境変数）に分散されてしまい、より良い解決方法があった方がいいと思う。</p>
<div>オススメ度：☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/bundles/best_practices.html">バンドルの構造とベストプラクティス</a></h2>
<blockquote><p>
この記事では、バンドルを作る上で必要な情報を学ぶことができる。</p>
<ul>
<li>バンドル名の命名の規則</li>
<li>バンドルのディレクトリ構造</li>
<li>バンドルに追加するクラスやリソースの内容</li>
</ul>
</blockquote>
<h4>感想：</h4>
<p>この記事は、ガイドブックにあってもいい内容だ。命名規則、バンドルのディレクトリ構造や、必要なクラスやリソースをどのように配置するかを学ぶことができる。実際にアプリケーションを作ると言えば、バンドルを作るという作業になるので、この記事で書かれたことを参考にしてバンドルを作るのが良い。</p>
<div>オススメ度：☆☆☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/bundles/inheritance.html">バンドルの継承を使って既存のバンドルのパーツを上書きする方法</a></h2>
<blockquote><p>
オープンソースで公開されているバンドルなどは、そのまま使えるものもあるが、いくつか自分用にカスタマイズしたいときなどがある。そういった際には、継承を使ってコントローラやテンプレートなどのリソースファイルをオーバーライドできる。</p>
<ul>
<li>準備として、バンドルにgetParent()メソッドで継承したバンドル名を指定する。</li>
<li>コントローラでは、parent:メソッド名で継承元のメソッドを実行できるし、かぶせることが可能。そのまま取り替えることも可能。</li>
<li>その他リソースは、バンドル内の同じ構造の場所にファイルを置けば、特に変更する必要なく、継承できる。</li>
</blockquote>
<h4>感想：</h4>
<p>確かにバンドルは独立したものであるが、継承を使うことによって再利用が可能になり、その方法を学ぶことができるので、知っておいた方がいい内容である。</p>
<div>オススメ度：☆☆☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/bundles/override.html">バンドルのクラスや設定などの情報をオーバーライドする方法<br />
</a></h2>
<blockquote><p>
未執筆。
</p></blockquote>
<h4>感想：</h4>
<p>執筆者待つ。</p>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/email.html">メールの送信方法</a></h2>
<blockquote><p>
SwiftMailerライブラリを使用してメールを送信する方法を説明してある。</p>
<ul>
<li>SwiftMailerを使用するには、コンフィギュレーションでtransport, username, password, host, port, encrypition, auth_mode, spool, delivery_address, disabled_delivery を指定することができる。</li>
<li>設定がうまく行っていれば、実際のメール送信は、実際に送る内容だけに注力することができる。</li>
<li>メールの本文は、renderView()メソッドを使用して作成することができる。</li>
</ul>
</blockquote>
<h4>感想：</h4>
<p>未だ現状のSwiftMailerはデフォルトのままでは、日本語のISO-2022-JPを正しく扱うことができない。私が修正してプルリクエストを投げた内容のものもあるが、修正してフォークして、日本語圏のみで共有した方がいいかも、と思っている。</p>
<div>オススメ度：☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/gmail.html">Gmail を使用してメールを送信する方法</a></h2>
<blockquote><p>
特に開発環境などでは、SMTPを使用するよりもgmailを使用して行った方がメール送信が簡単になる。<br />
コンフィギュレーションにtransportにgmailをしていして、username, passwordにそのGmailのユーザ名、パスワードを入力しておけば、このまま使うことができる。
</p></blockquote>
<h4>感想：</h4>
<p>メール送信のテストは意外に面倒なのであれば、Gmailを簡単に指定することができれば、開発も速く進むような気がする。しかもコンフィギュレーションに書くだけなので、非常に手軽。</p>
<div>オススメ度：☆☆☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/email/dev_environment.html">開発中におけるメール送信の扱い方</a></h2>
<blockquote><p>
開発中は、実際の送信先のメールアドレスに送信することはしないため、次の２つの方法が用意されている。</p>
<p>１．メール送信を無効にする<br />
２．特定のメールアドレスに送信する。<br />
２の方法を使えば、実際にメールが送られるのかどうかをテストできるので、便利である。また、これもコンフィギュレーションに書くだけなので手軽。</p>
<p>メールの送信内容はウェブデバッグツールバーで見ることができ、config_dev.ymlでintercept_redirectsをtrueに指定すれば、リダイレクト前のメールを見ることができる。</p>
</blockquote>
<h4>感想：</h4>
<p>これは、痒い所に手の届く記事。特に実際のコードに触れることなく、dev環境では、特定のメールアドレスに送ることができるようになった。また、symfony1でもウェブでバッグツールバーで送信内容を見ることができたが、その後にリダイレクトした際には、見ることができなかったが、これが改善された点も特筆すべき点か。</p>
<div>オススメ度：☆☆☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/email/spool.html">メールをスプールする方法</a></h2>
<blockquote><p>
スプールする方法もコンフィギュレーションに書くだけで、簡単に行うことができ、あとは、コンソールから次のコマンドを実行すれば良い。</p>
<pre class="brush:text">
php app/console swiftmailer:spool:send
</pre>
</blockquote>
<h4>感想：</h4>
<p>スプールすることによって、メールの送信に負荷をかけなくても良くなった。実際は、メール送信に関しては、特に負荷はかかるような処理ではないと思うのだけど、携帯アドレスに送る際とかには、短時間に送りすぎると拒否されてしまうかもしれないので、オプションも見ておくと良さそうだ。</p>
<div>オススメ度：☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/testing/http_authentication.html">ファンクショナルテストで HTTP 認証をシミュレートする方法</a></h2>
<blockquote><p>
HTTP認証に必要なユーザ名とパスワードを、テスト全体として、または、各リクエストごとに指定することができる。
</p></blockquote>
<h4>感想：</h4>
<p>まだリリース前の段階や、内部のみに公開の場合にベーシック認証をかけておきテストをするのが便利そうだが、あまり用途はないかもしれない。</p>
<div>オススメ度：☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/testing/insulating_clients.html">複数のクライアントのインタラクションをテストする方法</a></h2>
<blockquote><p>
件名のまま。チャットシステムのような複数クライアントがおり、即時で反映されるかどうかを見るときに使うことができる。
</p></blockquote>
<h4>感想：</h4>
<p>あまり用途は思い浮かばないが、知っておいてもいいかもしれない。</p>
<div>オススメ度：☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/testing/profiling.html">ファンクショナルテストでプロファイラを使用する方法</a></h2>
<blockquote><p>
一般的なファンクショナルでは、レスポンスのみをテストするのみで良いが、本番サーバのパフォーマンス等をテストする際に、プロファイラの値を調べることもできる。<br />
プロファイラを使えば、クエリーの数、処理時間を調べることができ、チューニングに必要なものを洗い出すことが可能となる。
</p></blockquote>
<h4>感想：</h4>
<p>実際には、これは必要ないと思う。これはテストというよりもチューニングに関することなので。しかし、プロファイラの値をコンソールから見て調べることができれば、ボトルネックとなっている場所を探すことができるだろう。</p>
<div>オススメ度：☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/testing/doctrine.html">Doctrine のリポジトリクラスをテストする方法</a></h2>
<blockquote><p>
Doctrineのリポジトリクラスをテストするには、エンティティ、エンティティマネージャ、データベース接続などを準備しておく必要がある。</p>
<p>Unitテスト</p>
<ul>
<li>DoctrineTestsをオートーローダのネームスペースで指定する</li>
<li>Unitテストは、DoctrineTests内のOrmTestCaseを継承する</li>
<li>Unitテスト内のsetUpでAnnotationReaderをセットアップしてエンティティをパースしたり、エンティティマネージャを取得したりする。</li>
<li>QueryBuilderオブジェクトのクエリーの各パーツをテストする</li>
</ul>
<p>ファンクショナルテスト</p>
<ul>
<li>セットアップでデータベース接続を取得しておく。</li>
<li>コントローラでエンティティを取得するのと同じようにテストする</li>
</ul>
</blockquote>
<h4>感想：</h4>
<p>Unitテストは、セットアップが少々面倒くさい。もう少し楽にできるようになった方がいいと思う。個人的には、ここはUnitテストではなく、ファンクショナルテストのみでほとんどカバーできると思う。</p>
<div>オススメ度:：☆☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/templating/PHP.html">PHP をテンプレートエンジンとして使う</a></h2>
<blockquote><p>
コンフィギュレーションで、templatingのenginesにphpを追加することでPHPをテンプレートエンジンとして使うことができるようになる。</p>
<ul>
<li>Twigのときと同じように親のテンプレートを継承することができる。</li>
<li>Twigのブロックと同じようにPHPでは、スロットとして親のテンプレートに子で指定したテンプレートを埋め込むことができる。</li>
<li>Twigのときと同じように、別テンプレートや別コントローラを取り込むことができる。</li>
<li>テンプレートヘルパもあり、テンプレートエンジンとして、基本的なことがほとんどできる。</li>
</ul>
</blockquote>
<h4>感想：</h4>
<p>Twigが強力なテンプレートエンジンなため、PHPを直接テンプレートエンジンに採用するメリットはあまりないと思うのだが、オプションとして知っていてもいい内容か。</p>
<div>オススメ度：☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/templating/global_variables.html">変数を全てのテンプレートへ注入する(グローバル値)</a></h2>
<blockquote><p>
APIのキーなどをグローバルの設定値としてコンフィギュレーションとして書いておくことができる。
</p></blockquote>
<h4>感想：</h4>
<p>環境変数でSYMFONY__を指定すれば、外部パラメターをセットすることができるので、同様のことが可能だし、その方が実装が綺麗になると思う。</p>
<div>オススメ度：☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/logging/monolog.html">ロギングで Monolog を使用する方法</a></h2>
<blockquote><p>
Symfony2はデフォルトでMonologを使用している。</p>
<ul>
<li>Monologのほとんどの設定は、コンフィギュレーションでハンドラを追加することでロギングが有効になる。</li>
<li>フォーマッタを変更することができる。</li>
</ul>
</blockquote>
<h4>感想：</h4>
<p>この記事の内容は全ては試していないが、基本的なログの取得はここに書いてある内容で可能。ログメッセージの装飾は今後調査をする。<br />
オススメ度：☆☆</p>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/logging/monolog_email.html">エラーメールを送る Monolog の設定方法</a></h2>
<blockquote><p>
コンフィギュレーションで、指定すれば特定のアクションレベルでメールを送信することができる。500番台のエラーが起きたときなどに、管理者にメールを飛ばしてバグの調査をするときなどに役に立つ。<br />
また、メールを飛ばし、かつ、ログに書きこむことも可能。</p>
</blockquote>
<h4>感想：</h4>
<p>symfony1系でもエラーのイベントをリッスンしてメールを投げるといったことができたが、Symfony2ではコンフィギュレーションで、完結できるのが良い。</p>
<div>オススメ度：☆☆☆☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/profiler/data_collector.html">カスタムデータコレクタの作成方法</a></h2>
<blockquote><p>
Symfony2のプロファイラで得られるデータを集める方法もカスタマイズすることができる。</p>
<ul>
<li>Symfony\\Component\\HttpKernel\\DataCollector\\DataCollectorInterface を実装する。</li>
<li>サービスにdata_collectorタグを付けて登録する。</li>
<li>ウェブプロファイラテンプレートを作成する。</li>
</ul>
</blockquote>
<h4>感想：</h4>
<p>普通の用途では、プロファイラを拡張することはほとんどないのでは。。。</p>
<div>オススメ度：☆</div>
<hr />
<h2><a href="http://docs.symfony.gr.jp/symfony2/cookbook/symfony1.html">symfony1ユーザのためのSymfony2</a></h2>
<blockquote><p>
symfony1とSymfony2は別物だが、symfony1の哲学は、Symfony2に受け継がれているものも多いため、symfony1をマスターしていれば、それほど学習コストは高くはない。</p>
<p>ディレクトリ構造</p>
<h5>app/ </h5>
<p>Symfony2でのapp/ ディレクトリは、モジュールやライブラリファイルを置くところではなく、コンフィギュレーションや他のリソース（テンプレートや翻訳ファイル）を置くところになる。</p>
<h5>src/</h5>
<p>実際のコードを置く場所。Symfony2でのアプリケーションのコードは全てバンドルの中になり、バンドルがsrc/ 以下に置かれる。symfony1では、schema.yml、モデルファイル、フォームファイル等がプロジェクト内で共有されていたため、分散されていたが、Symfony2では、個々のバンドルとして独立されている。symfony1で、独自にplugins/ディレクトリに自分のアプリケーションを入れるなどしていれば、Symfony2のバンドルとよく似たものになる。</p>
<h5>vendor/</h5>
<p>symfony1のlib/vendorと概念は同じだが、入っているライブラリが異なる。</p>
<h5>web/</h5>
<p>フロントコントローラが置かれる。後にコンソールコマンド（php app/console assets:install web）を使って、個々のバンドル内のResources/publicディレクトリの複製orシンボリックリンクがweb/bundlesに作られる。</p>
<h5>オートローディング</h5>
<p>symfony1では、プロジェクト全体のPHPクラスファイルを１つの巨大な配列にキャッシュしていたので、変更があった際にキャッシュクリアが必要であったが、Symfony2では、UniversalClassLoaderを使用し、namespaceで管理しており、変更があってもキャッシュをクリアする必要がない。</p>
<h5>コンソール</h5>
<p>php symfonyコマンドから php app/consoleコマンドへ変更。</p>
<h5>アプリケーション</h5>
<p>symfony1では、frontend, backendのようにアプリケーションを分けていたが、Symfony2では別プロジェクトを作り、プロジェクト間でバンドルを共有することになる。</p>
<h5>バンドルとプラグイン</h5>
<p>symfony1では、config/ProjectConfiguration.class.phpでプラグインを有効にし、Symfony2では、app/AppKernel.phpでバンドルを有効にする。</p>
<h5>ルーティングとコンフィギュレーション</h5>
<p>symfony1では、プラグイン内のrouting.ymlとapp.ymlは自動的にロードされる。Symfony2では、routing.ymlもconfig.phpも、各バンドルの設定をインポートしてロードする。なお、symfony1のapp.ymlのような設定をするには、config.php にparametersキーで、指定すると良い。
</p></blockquote>
<h4>感想：</h4>
<p>実際は、Symfony2で遊びプロジェクトでも作ってみないといけないが、その後でこの記事を読めば、まぁ理解はできると思う。アプリケーションを分けない、というのはsymfony1系の人にとっては理解しにくいのかもしれない。あとバンドルの仕組みはプロジェクトと独立していて優れているとよくよく思う。</p>
<div>オススメ度：☆☆☆</div>
<hr />
<hr />
まだレビューしきれていない記事は以下の通りです。サービス系は、セキュリティ系は鬼門だなぁ。</p>
<p><a href="http://docs.symfony.gr.jp/symfony2/cookbook/controller/service.html">コントローラをサービスとして定義する方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/form/data_transformers.html">データトランスフォーマの使用</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/validation/custom_constraint.html">カスタムバリデーション制約の作成方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/service_container/factories.html">サービスを作成するファクトリの使用方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/service_container/parentservices.html">親サービスと共通の依存(dependency)をマネージする方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/service_container/scopes.html">スコープの使用方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/configuration/pdo_session_storage.html">PdoSessionStorage を使用してデータベースにセッションを格納する方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/bundles/extension.html">セマンティックコンフィギュレーションを通してバンドルを設定する方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/security/remember_me.html">“Remember Me” ログイン機能の追加方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/security/voters.html">IPアドレスのブラックリストの独自 Voter の実装方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/security/acl.html">アクセス制御リスト(ACLs)</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/security/acl_advanced.html">高度な ACL のコンセプト</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/security/force_https.html">異なる URL で HTTPS や HTTP を強制化させる方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/security/form_login.html">フォームログインをカスタマイズする方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/security/securing_services.html">アプリケーション内でサービスやメソッドをセキュアにする方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/security/entity_provider.html">データベースからセキュリティユーザをロードする方法(エンティティプロバイダ)</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/security/custom_provider.html">カスタムユーザプロバイダの作成方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/security/custom_authentication_provider.html">カスタム認証プロバイダの作成方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/cache/varnish.html">Varnish を使ってウェブサイトを高速化する方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/tools/autoloader.html">クラスのオートローディングの方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/tools/finder.html">ファイルの探し方</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/console.html">コンソール/コマンドラインツールとしてのコマンドの作成方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/debugging.html">デバッグするための開発環境に最適化する方</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/web_services/php_soap_extension.html">Symfony2 コントローラで SOAP ウェブサービスを作成する方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/event_dispatcher/class_extension.html">継承無しでクラスを拡張する方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/event_dispatcher/method_behavior.html">継承無しでメソッドの挙動をカスタマイズする方法</a><br />
<a href="http://docs.symfony.gr.jp/symfony2/cookbook/request/mime_type.html">新しいリクエストのフォーマットとマイムタイプの登録方法</a></p>
<hr />
<hr />
今後は、これらのまだレビューしていないものを見ていくと共に、実際に手で動かしてちゃんと使えるか、翻訳された言葉がおかしくないか（実際、今回レビューしていた中でいくつかおかしい翻訳も発見している）、を見ていきます。</p>
<p>今回の「オススメ度」というのは、完全に主観です。ただ、☆が５つあるのは、確実に知っておいた方がいい内容です。４つもぜひ読んでおいてください。１つ２つは、暇だったりビンゴなネタであったらやってみてください。</p>
<p>実際のところ、私は翻訳をしながら、Symfony2自体の理解、Githubの使い方の理解を深めてきました。GitHubデビューをするのに、翻訳が一番簡単な方法じゃないかなぁ、と最近思っています。変にTOEICの勉強するくらいなら、Symfonyの翻訳をして英語も技術も身につけた方が、実際役に立ちます。いや、本当に。</p>
<p>明日のSymfony Advent Calendar 2011 -15日目は、<a href="http://twitter.com/makotokaga">@makotokaga</a>さんです。</p>
]]></content:encoded>
			<wfw:commentRss>http://ganchiku.com/2011/12/symfony2-cookbook-advent-calendar2011.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>symfonyの中級者向けチュートリアルができましたね</title>
		<link>http://ganchiku.com/2009/12/symfony_advent_calendar2009.html</link>
		<comments>http://ganchiku.com/2009/12/symfony_advent_calendar2009.html#comments</comments>
		<pubDate>Wed, 02 Dec 2009 06:02:04 +0000</pubDate>
		<dc:creator>shin</dc:creator>
				<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://ganchiku.com/?p=459</guid>
		<description><![CDATA[それと、symfony1.4系(LTS版)が出ましたね。今から新しいプロジェクトをする人は、1.4系で作ってください。そして、リリースに合わせて中級者向けチュートリアルが出ましたよ。 More with symfony  &#8230; <a href="http://ganchiku.com/2009/12/symfony_advent_calendar2009.html">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>それと、symfony1.4系(LTS版)が出ましたね。今から新しいプロジェクトをする人は、1.4系で作ってください。そして、リリースに合わせて中級者向けチュートリアルが出ましたよ。</p>
<h3><a href="http://www.symfony-project.org/advent_calendar/">More with symfony</a></h3>
<p>さてさて、今回のチュートリアルで関わった著者、翻訳者、査読者の皆さん、お疲れさまでした。Fabienも書いていることなのですが、この本ってすごいんですね。中身ももちろんですが、Fabienが9月に思い立った「中級者向けの本を書く」ということを、12月1日までに実現してしまうのですから。しかも、5つの言語での翻訳版も同時に。</p>
<p>翻訳版も同時ということは、翻訳する時間も用意しておかないといけないのですね。なんというアジャイルなコミュニティなんだろう、と思いましたよ。日本語翻訳チームも同様で、symfonyをバリバリ使っているユーザの方が参加して、一気に進めることができました。本当にphp conferenceで会った人たちと、その場で話を始めたんですね。そして、その後に<a href="http://akimoto.jp/blog/">akky</a>が企画したsymfony meetupで一気に翻訳チームが結成されたのですね。やっぱり勢いだよ。勢い。</p>
<p>また、Fabienの人柄にも注目ですね。Fabienが「中級者向けの本をコミュニティベースで書きたい」と言ったことに対して、それに貢献をしてくれる人たち。著者、翻訳者、査読者の人が喜んでこの一大プロジェクトを成し遂げました。symfonyを本当に好きじゃないとこれはできないです。やっぱり愛だよ。愛。</p>
<p>そして、今回のチュートリアルを書いているメンバーは、バリバリsymfonyを使っている人たちで、言ってみれば、symfony userのオールスターなんですね。統一性が欠けるというものと、それぞれの著者の面白みが読めるということは、ある程度トレードオフだと思うのですが、後者の利点が大きすぎます。それに、全部独立していますし。ということで、統一性は個々の章で、ということでお願いします。まだ私も全部読んだわけではないのですが、超おすすめです。特に自分がメインで訳したコンフィグハンドラーの章は、お気に入りです。</p>
<p>そして、私個人としては、こうやって多くの人が関わって作る書籍やプロジェクトのダイナミズムというものを感じました。新しい本の書き方として、おもしろいプラクティスですよね。まぁ、その半面、私みたいな初めて訳をする人の文章があるなど、荒削りな面もありますが、そこは、適宜直していくということで、堪忍してください。</p>
<p>さて、中身ですが、symfonyを使っていろいろカスタマイズをしたい人向けなんですね。なので、かなり突っ込んだ内容が書かれています。全くもって、初心者向きではないです。でも、実際にアプリを書こうと思ったら、初心者用のチュートリアルだけでは無理がありますよね。そのための本です。</p>
<p>チュートリアルの中身などの技術的な興味もあるのですが、個人的には、コミュニティベースで、さらにオープンソースだと、こういう成果を上げることができるという経営的な興味もそそられる活動でした。無償が必ずしもいいとは思いませんし、フリーにしないといけないということではありません。ただ、このような組織形態だからこそ可能となったアジャイル性は、もっと注目されてもいいと思います。</p>
<p>本当にお疲れさまでした。私の初めての翻訳参加になりました。私の力もコミュニティに還元ができてうれしく思います。</p>
<p>関連する日本語翻訳者の方が以下のように喜びを書いています。</p>
<ul>
<li><a href="http://d.hatena.ne.jp/innx_hidenori/20091202/1259686344">2009年のsymfonyアドベントカレンダー「More with symfony」はすごい！</a></li>
<li><a href="http://d.hatena.ne.jp/brtRiver/20091202/1259708797">symfonyからのクリスマスプレゼント</a></li>
<li><a href="http://d.hatena.ne.jp/Fivestar/20091201/1259691721">そしてもう1つ&#8230; </a></li>
]]></content:encoded>
			<wfw:commentRss>http://ganchiku.com/2009/12/symfony_advent_calendar2009.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>admin generatorでrelationしているtableもfilterする</title>
		<link>http://ganchiku.com/2009/11/admin-generator_relational_table_filter.html</link>
		<comments>http://ganchiku.com/2009/11/admin-generator_relational_table_filter.html#comments</comments>
		<pubDate>Mon, 23 Nov 2009 08:40:19 +0000</pubDate>
		<dc:creator>shin</dc:creator>
				<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://ganchiku.com/?p=436</guid>
		<description><![CDATA[はじめに filterって名前が紛らわしいですね。Form Filterなのか、Filter Chainなのかで全然違いますし。今回は、Form Filterに関してです。 Form Filter使っていますか？私はまっ &#8230; <a href="http://ganchiku.com/2009/11/admin-generator_relational_table_filter.html">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<h3>はじめに</h3>
<p>filterって名前が紛らわしいですね。Form Filterなのか、Filter Chainなのかで全然違いますし。今回は、Form Filterに関してです。</p>
<p>Form Filter使っていますか？私はまったく使っていませんでしたw 使うのは、admin generatorで使うときのみでした。generator.ymlを編集して、filterさせたいフィールドだけ選択させたりくらいだけで、なかなか内部まで見れていませんでした。今でも、内部まで見ていないです。。。</p>
<p>しかし、ちょっと拡張してみると便利だということに気づきました。というわけで、これでもっとfilterを使おうかな、という気分になりました。今回は、propelベースで話をします。遊びプロジェクトは、doctrineで書いていたりするのですが、まだ、本番用でdoctrineを使えていないです。なんというか、doctrineで書くと、メソッドチェーンで書くことができるので、行数とか少なくて済むのですね。調子に乗ってactionに全部書いてしまって、modelにどうやって書こうか、悩んでしまうのですね。</p>
<h3>問題</h3>
<p>さて、デフォルトのadmin generatorの一覧画面には、右端に検索ボックスが出てきますよね。admin generatorの際に指定したmodelに関する検索です。なかなか便利だなー、とは思っていました。しかし、modelと一対なので、JOINしたりさせようとするとちょっと面倒なんですよね。一つのテーブルだけを検索できてもあまり使えないと思うので、JOINさせてfilterさせたいじゃないですか。</p>
<h3>方法</h3>
<p>いろんな方法が考えられるのですが、一番簡単な方法だと私が思っているのは、FormfFilterを拡張して、リレーションしたいテーブルのmodelに対応するFilterFormをmergeFormします。マジでmergeです。そして、そのFormFilterクラスのbuildCriteriaメソッドも拡張して、mergeしてあげたFilterFormでfilterしたいフィールドを追加してあげます。</p>
<h3>準備1</h3>
<p>つーか、簡単すぎてソースコードいらないね。。。それ用にアプリ書くの面倒ですし、プロダクトのコードそのまま持ってきたくないですし。。。と言いつつ、ここでテストアプリを作りながら、書いていくことにします。今回のは、symfony.1.2系で、propelです。すいません。<br />
まず、schema.ymlを追加してみましょう。</p>
<pre class="brush:plain">
propel:
  item:
    id:
    category_id:
    name:
      type: varchar(32)
      required: true
    description:
      type: longvarchar
      required: true
    created_at:
    updated_at:

  category:
    id:
    name:
      type: varchar(32)
      required: true
　　is_special:
      type: boolean
</pre>
<p>data/fixtures/fixture.yml</p>
<pre class="brush:php">
Category:
&lt;?php for ($i = 0; $i < 5; $i++): ?&gt;
  category_&lt;?php echo $i ?&gt;:
    name: category_name_&lt;?php echo $i ."\n" ?&gt;
    is_special: &lt;?php echo rand(0, 1) . "\n" ?&gt;
&lt;?php endfor; ?&gt;

Item:
&lt;?php for ($i = 0; $i < 30; $i++): ?&gt;
  item_&lt;?php echo $i ?&gt;:
    name: item_name_&lt;?php echo $i ."\n" ?&gt;
    description: "test description"
    category_id: category_&lt;?php echo rand(0, 2) ."\n" ?&gt;
&lt;?php endfor; ?&gt;
</pre>
<p>そして、build-all-loadして、model, form, filterも作って、データベースにも突っ込んでおきましょう。ついでに、テストデータも入れておきましょう。なお、最初にdatabases.ymlなどを編集して、データベースは接続できるようにしておいてください。</p>
<pre class="brush:plain">
$ ./symforny propel:build-all
</pre>
<p>そして、applicationを作ります。</p>
<pre class="brush:plain">
$ ./symfony generate:app backend
</pre>
<p>で、moduleは、admin generatorで作ってしまいます。</p>
<pre class="brush:plain">
$ ./symfony propel:generate-admin backend Item
</pre>
<p>生成されたモジュールを見ようとすると、「toStringを実装しろ」と怒られるので、実装してnameでも返してあげます。</p>
<pre class=:brush:php>
class Category extends BaseCategory
{
  public function __toString()
  {
    return $this->name;
  }
}
</pre>
<p><img src="http://ganchiku.com/wp-content/uploads/2009/11/before1.jpg" alt="before" title="before" width="600" height="323" class="alignnone size-full wp-image-448" /><br />
さて、これで準備ができました。上の画像のようになりますね。fixtureでテストデータの作り方とかが、もしかしたら参考になる人がいるかもしれないですね。つまり、forしたり、randしている内容はチェックしてみてください。改行コードを入れないといけないのが、ちょっと嫌ですが。</p>
<h3>準備2</h3>
<p>と言いつつ、まだ準備です。次に、一覧として表示したいものを選びます。created_atとか表示しなくてもいいじゃないですか。それに、descriptionが長いときもあるので、表示させないように削ってしまいましょう。代わりにcategoryの情報が欲しいですね。is_specialというフィールドがcategoryにありますが、これを表示したいと思いますので、listに追加してあげましょう。</p>
<p>まず、そのためには、モデルのItemクラスに、getHogehogeを追加してあげましょう。今回は、getCategoryNameとgetCategoryIsSpecialメソッドを追加して、admin moduleからそのまま持ってくるようにしました。</p>
<pre class="brush:php">
class Item extends BaseItem
{
  public function getCategoryName()
  {
    if (!is_null($this->getCategory())) {
      return $this->getCategory()->getName();
    }
    return null;
  }
  public function getCategoryIsSpecial()
  {
    if (!is_null($this->getCategory())) {
      return $this->getCategory()->getIsSpecial();
    }
    return null;
  }
}
</pre>
<p>あとは、itemモジュールのgenerator.ymlを修正しましょう。</p>
<pre class="brush:php">
generator:
  class: sfPropelGenerator
  param:
    model_class:           Item
    theme:                 admin
    non_verbose_templates: true
    with_show:             false
    singular:              ~
    plural:                ~
    route_prefix:          item
    with_propel_route:     1

    config:
      actions: ~
      fields:  ~
      list:
        peer_method: doSelectJoinCategory
        display: [ name, category_name, category_is_special, updated_at ]
      filter:  ~
      form:    ~
      edit:    ~
      new:     ~
</pre>
<p>これでとりあえず表示したいフィールドはできました。さらに、peer_methodを変更しないとクエリーを実行しすぎなので、joinするものに変えてあげました。<br />
ようやく準備ができたかな。labelも変えてあげたいところですが、面倒なのでry</p>
<h3>ソースコード</h3>
<p>さて、ようやく今回のネタです。<br />
lib/filter/ItemFormFilter.class.php</p>
<pre class="brush:php">
class ItemFormFilter extends BaseItemFormFilter
{
  public function configure()
  {
    $this->mergeForm(new CategoryFormFilter());
  }

  public function buildCriteria(array $values)
  {
    $criteria = parent::buildCriteria($values);
    $criteria->addJoin(ItemPeer::CATEGORY_ID, CategoryPeer::ID);
    if (isset($values['is_special'])) {
      $criteria->add(CategoryPeer::IS_SPECIAL, $values['is_special']);
    }
    return $criteria;
  }
}
</pre>
<p>そして、apps/backend/modules/item/config/generator.yml</p>
<pre class="brush:plain">
generator:
  class: sfPropelGenerator
  param:
    model_class:           Item
    theme:                 admin
    non_verbose_templates: true
    with_show:             false
    singular:              ~
    plural:                ~
    route_prefix:          item
    with_propel_route:     1

    config:
      actions: ~
      fields:  ~
      list:
        peer_method: doSelectJoinCategory
        display: [ name, category_name, category_is_special, updated_at ]
      filter:
        display: [ name, category_id, is_special ]
      form:    ~
      edit:    ~
      new:     ~
</pre>
<p>なんてこったい。今回の本ネタが一番短いな。。。まぁ、いいや。<br />
<img src="http://ganchiku.com/wp-content/uploads/2009/11/after1.jpg" alt="after" title="after" width="600" height="323" class="alignnone size-full wp-image-450" /></p>
<p>結果上のような見た目になりましたね。実際、SQL文を見ると以下のようになっていると思います。</p>
<pre class="brush:plain">
SELECT item.ID, item.CATEGORY_ID, item.NAME, item.DESCRIPTION, item.CREATED_AT, item.UPDATED_AT, category.ID, category.NAME, category.IS_SPECIAL FROM `item` LEFT JOIN category ON (item.CATEGORY_ID=category.ID) WHERE category.IS_SPECIAL=1 AND item.CATEGORY_ID=category.ID LIMIT 20
</pre>
<p>これで、完成です。</p>
<h3>解説</h3>
<p>FormFilterを拡張して、追加したいフィールドを持っているFormFilterをmergeしてあげます。これで、admin generatorを指定してあげると、filterの場所には、全フィールドが出てきますね？</p>
<p>そこで、filterを拡張したいモジュールのgenerator.ymlのfiltersを修正します。ここで、relationしているtableでfilterしたフィールドも追加してしまいます。もちろん対応するフィールドがFormFilterにないとエラーが出ます。前にmergeをしていましたので、実際はフィールドがFormFIlterにあることになります。</p>
<p>これでできたらうれしいのですが、実際は、もう少しだけ修正する必要があります。つまり、検索条件を加えるといったことです。そこで、またFormFilterに戻って、buildCriteriaをオーバーライドして、Criteriaに指定したいテーブルをJOINして、ついでに、条件を加えてあげましょう。</p>
<h3>まとめ</h3>
<p>admin generatorでmodelをまたいでfilterを追加する方法を説明しました。</p>
<p>方法としては、admin generatorで使用するFilterFormを拡張して、relationしているtableもfilterの項目に追加するようにしました。実際は、FilterFormを拡張して、追加したいFilterFormをmergeし、Criteriaを作成する際に、mergeしたFilterFormの値も追加して作成するために、buildCriteriaメソッドをオーバーライドしてみました。admin generatorが自動生成するactionなどははいじっていません。</p>
<p>さて、今回の実装は、具体的な実装の話でした。つまり、アプリレベルの開発の話です。どこまで抽象化するというのは、いつも悩みの種で、抽象化しすぎると複雑になるし、実際いらないということもよくあります。YAGNIです。YAGNI。抽象的にやろうと思えば、もっと複雑になるし、その辺は、DbFilterプラグインとかがよろしくやってくれそうなことを前に読んだ気もするけど、どうなんでしょうね。時間があれば使ってみます。</p>
<p>まぁ、今回のような話は、それぞれのアプリの仕様によることが多いので、具体的に実装してしまいましょう。</p>
<p>今後のちょっとした発展系としては、ヘッダの行をクリックするとソートできるようにしたらいいですね。同じような要領なので、がんばればできるでしょう。こちらは、actionを上書きしないといけないのかもしれません。</p>
<p>うーん。一番時間がかかってしまうのが、準備段階というのも考えものですね。。。</p>
]]></content:encoded>
			<wfw:commentRss>http://ganchiku.com/2009/11/admin-generator_relational_table_filter.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>conditionalBasicSecurityFilterってのもアリかな</title>
		<link>http://ganchiku.com/2009/10/conditionalbasicsecurityfilter.html</link>
		<comments>http://ganchiku.com/2009/10/conditionalbasicsecurityfilter.html#comments</comments>
		<pubDate>Fri, 30 Oct 2009 18:34:32 +0000</pubDate>
		<dc:creator>shin</dc:creator>
				<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://ganchiku.com/?p=419</guid>
		<description><![CDATA[はじめに conditionalシリーズをもう少し考えてみました。今回はキャッシュではなくて、securityFilterです。 symfonyでは、settings.ymlを用いて、ユーザがログインしていないときに呼び &#8230; <a href="http://ganchiku.com/2009/10/conditionalbasicsecurityfilter.html">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<h3>はじめに</h3>
<p>conditionalシリーズをもう少し考えてみました。今回はキャッシュではなくて、securityFilterです。</p>
<p>symfonyでは、settings.ymlを用いて、ユーザがログインしていないときに呼び出すアクションを指定することができます。その際に使用するのが、login_moduleとlogin_actionです。</p>
<p>また、ログインはしているが、権限がない(Credentialがない)ときに呼び出すアクションも指定することができます。その際に使用するのが、secure_module, secure_actionです。この二つとsecurity.ymlを使うことによって、is_secureなアクションにログインしていないユーザがアクセスした際に指定したログイン画面を出すことが可能となります。</p>
<p>今回は、この２つを拡張してみることにします。つまり、ユーザがログインしていないときに呼び出すアクションをもう少し条件を付けて、特定のページには違うlogin_actionを渡すようなことです。</p>
<h3>問題</h3>
<p>確かにsymfonyではlogin_module、login_actionで指定することができるのいいのですが、もっと細かに指定したいときってどうしたらいいんだろうという話を、先日耳にしました。今まで私はそんな需要がなかったので、考えたことはなかったのですが、「私だったらこうやるかな」という方法をいきなり午前1時より作り始めてしまいましたので、その勢いでブログにも書いていますw<br />
secureの方の動作は確認していないのでアレなのですが、loginの方は確認しましたので、これで対応することができたと思います。実装環境は相変わらずsymfony1.3-ALPHA2なのですが、たぶんsymfony1.2系でも動くと思います。</p>
<h3>方法</h3>
<p>login_module, login_actionなどの指定が実際どこで呼ばれているかを調査しますと、sfBasicSecurityFilter.class.phpの中で使われています。このsfBasicSecurityFilterは、filters.ymlに書かれているsecurityのデフォルトとして使われるfilterです。このsfBasicSecurityFilterを読んでみると、login_module, login_actionは固定値としてsfConfig::get(&#8216;sf_login_module&#8217;)のように使われていますので、簡単には変更することができないようになっています。というわけでこれではダメなので、考えてみました。</p>
<p>さて、symfonyのプラグインの中で一番ダウンロードが多いのがsfGuardPluginなのですが、そこでfilterのsecurityFilterを書き換えますよね？securityFilterは変更可能なのですね。というわけで、同様に、今回指定したconditionalBasicSecurityFilterというものをsecurityFilterとして使用できるように指定します。そして、一緒に渡すパラメータもfilters.ymlで指定してみます。yamlで指定する値が増えすぎてわからなくなってしまうかもしれないのですが、conditionalCacheFilterと同じようにpageごとに指定ができるようにしてみました。</p>
<h3>ソースコード</h3>
<p>/apps/frontend/config/filters.yml</p>
<pre class="brush:plain">
rendering: ~
#security:  ~
security:
  class: conditionalBasicSecurityFilter
  param:
    pages:
      - { module: post, action: new, login: { module: user, action: loginNew } secure: { module: default, action: error403 } }
      - { module: post, action: edit, login: { module: user, action: loginEdit } secure: { module: default, action: error403 } }

# insert your own filters here

cache:     ~
execution: ~
~
</pre>
<p>/apps/frontend/lib/conditionalBasicSecurityFilter.class.php</p>
<pre class="brush:php">
class conditionalBasicSecurityFilter extends sfBasicSecurityFilter
{

  public function execute($filterChain)
  {
    if (!$pages = ($this->getParameter('pages', false))) {
      parent::execute($filterChain);
      return;
    }

    foreach ($pages as $page) {
      $module = isset($page['module']) ? $page['module'] : null;

      $action = isset($page['action']) ? $page['action'] : $this->context->getActionName();

      $login = isset($page['login']) ? $page['login'] : null;

      $secure = isset($page['secure']) ? $page['secure'] : null;

      // skip
      if ($module !== $this->context->getModuleName()) {
        continue;
      }

      // skip
      if ($action != $this->context->getActionName()) {
        continue;
      }

      if (isset($login) and !$this->context->getUser()->isAuthenticated()) {
        $loginModule = isset($login['module']) ? $login['module'] : null;
        $loginAction = isset($login['action']) ? $login['action'] : null;
        if (isset($loginModule) and isset($loginAction)) {
          $this->forwardToAction($loginModule, $loginAction);
        }
      }

      $credential = $this->getUserCredential();
      if (isset($secure) and !is_null($credential) and !$this->context->getUser()->hasCredential($credential)) {
        $secureModule = isset($secure['module']) ? $secure['module'] : null;
        $secureAction = isset($secure['action']) ? $secure['action'] : null;
        if (isset($secureModule) and isset($secureAction)) {
          $this->forwardToAction($secureModule, $secureAction);
        }
      }
    }

    parent::execute($filterChain);
  }

  protected function forwardToAction($module, $action)
  {
    $this->context->getController()->forward($module, $action);

    throw new sfStopException();
  }
}
</pre>
<h3>解説</h3>
<p>filters.ymlでsecurityFilterで使用するクラスとしてconditionalBasicSecurityFilterを指定します。そして、その中で受け取るパラメータを同時に指定させます。今回はいいサンプルのアイデアがなかったのですが、postモジュールで、アクションがnewするときとeditするときに違うloginフォームを出したい、という要望があったとして考えてみました。つまり、newの時はuserモジュールのloginNewアクションを呼びたいとします。そして、editの時はuserモジュールのloginEditアクションを呼びたいとします。secureは、とりあえずこんな感じで指定できるかな、という感じで書いてみましたので、今回は説明しません。</p>
<p>conditionalBasicSecurityFilter.class.phpに関しては、前回書きましたconditionalCacheFilterと同じロジックでpagesの分だけループで回し、それがloginが必要なものであったり、credentialのチェックが必要なものであった際には、forwardをするというようにしました。これで、newのときには、loginNewにforwardされ、editのときにはloginEditにforwardされるようになります。</p>
<h3>まとめ</h3>
<p>今回は、そのsfBasicSecurityFilterを拡張してconditionalBasicSecurityFilterを作ってみました。そこでは、モジュールやアクションによって、login_module, login_action,security_module, security_actionを分けて指定ができるようにしました。<br />
今回は、午前1時になっていきなり、思い立ったように書いてみましたので、適当なことを言っているかもしれないのですが、せっかくなので、ソースコードまで落として、さらにブログにも書いてみました。ログインの際のロジックを拡張したい際にはsfBasicSecurityFilterを拡張して対応するというのは、結構使えるのではないかな、と思います。<br />
しかし、ここまで書いた後に、よく考えてみるとこれはアプリケーションを別にして作ればある程度は回避できる問題なんだろうなぁ、と少し思っています。それでも、上に書いたように分けたい場合はこんな感じで対応してみてはどうでしょうか？</p>
<p>そろそろ寝ないと。。。勢いで書いたので、間違っているところもあると思います。それは後ほど修正します。</p>
]]></content:encoded>
			<wfw:commentRss>http://ganchiku.com/2009/10/conditionalbasicsecurityfilter.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>conditionalCacheFilterをさらに考える</title>
		<link>http://ganchiku.com/2009/10/more_about_conditionalcache.html</link>
		<comments>http://ganchiku.com/2009/10/more_about_conditionalcache.html#comments</comments>
		<pubDate>Tue, 27 Oct 2009 04:46:01 +0000</pubDate>
		<dc:creator>shin</dc:creator>
				<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://ganchiku.com/?p=411</guid>
		<description><![CDATA[はじめに 前回、conditionalCacheを説明した後に、さらにそれを使った事例を考えていたのですが、考えていたところ直したいところがいくつか出てきたので、さらにconditionalCacheを考えてみました。一 &#8230; <a href="http://ganchiku.com/2009/10/more_about_conditionalcache.html">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<h3>はじめに</h3>
<p>前回、conditionalCacheを説明した後に、さらにそれを使った事例を考えていたのですが、考えていたところ直したいところがいくつか出てきたので、さらにconditionalCacheを考えてみました。一つは、enabledオプションによって、キャッシュをクリアすることは必要ないと考えたことです。本来の動作としては、enabledがfalseであれば、キャッシュを使わずに、そのまま表示するという方法を取るのが一番でしょうね。これはviewCacheManagerのisCacheableメソッドを直せば可能となると思います。とりあえずその方法は後に説明するということで、今回はenabledオプションを消しました。<br />
そして、今回は、セッション関係を持つuserクラスを使って、キャッシュをするか、どうかを追加してみることにしましたので、それを説明しようと思います。</p>
<h3>問題</h3>
<p>ユーザ認証しているかどうかだけではなくて、ユーザ認証して、かつ自分のコントロールできるページか否かというオプションがあったらウレシイですよね。つまり、そのユーザの投稿やプロフィールページであれば、「編集する」や「削除する」というアクションが表示されるのが普通でしょう。ユーザ認証をしていても、他のユーザの内容であれば、編集や削除ができたら困りますよね。ここでもconditionalCacheの出番が必要ではないか、と考えました。</p>
<h3>方法</h3>
<p>セッションを操るuserを使いましょう。filters.ymlで条件としてuser_conditionを追加してみます。そこで、user_conditionに、キャッシュ可能かどうかを条件判断させるメソッドを文字列で渡してみます。</p>
<h3>ソースコード</h3>
<p>/apps/frontend/config/filters.yml</p>
<pre class="brush:plain">
rendering: ~
security:  ~

# insert your own filters here
conditionalCacheFilter:
  class: conditionalCacheFilter
  param:
    pages:
      - { module: post, action: index, format: [xml] }
#      - { module: post, action: index, format: [html], user_condition: usesCache }
      - { module: post, action: index, format: [html], user_condition: isNotAuthenticated, with_layout: true }

cache:     ~
execution: ~
</pre>
<p>/apps/frontend/lib/conditonalCacheFilter.class.php</p>
<pre class="brush:php">
class conditionalCacheFilter extends sfFilter
{
  protected
    $cacheManager    = null,
    $request         = null,
    $user            = null,
    $uri             = null,
    $defaultLifetime = null;

  public function initialize($context, $parameters = array())
  {
    parent::initialize($context, $parameters);

    $this->cacheManager    = $context->getViewCacheManager();
    $this->request         = $context->getRequest();
    $this->user            = $context->getUser();
    $this->uri             = $context->getRouting()->getCurrentInternalUri();

    $lifetime = (isset($this->cacheManager)) ? $this->cacheManager->getLifeTime($this->uri) : 0;
    $this->defaultLifetime = ($lifetime > 0) ? $lifetime : 86400;
  }

  public function execute($filterChain)
  {
    if ((!isset($this->cacheManager)) or !($this->getParameter('pages', false))) {
      $filterChain->execute();
      return;
    }

    foreach ($this->getParameter('pages') as $page) {

      $module = isset($page['module']) ? $page['module'] : null;

      $action = isset($page['action']) ? $page['action'] : $this->request->getParameter('action');

      $format = isset($page['format']) ? $page['format'] : array();
      if (!is_array($format)) {
        $format = array($format);
      }

      $user_condition = isset($page['user_condition']) ? $page['user_condition'] : null;

      // maybe better to throw exception?
      if (is_null($module)) {
        continue;
      }
      // skip
      if ($module !== $this->request->getParameter('module')) {
        continue;
      }
      // skip
      if (!empty($format) and !in_array($this->request->getParameter('sf_format', 'html'), $format)) {
        continue;
      }

      // skip
      if (isset($user_condition)) {
        $func = array($this->user, $user_condition);
        if (!(is_callable($func) and call_user_func($func, $this->request))) {
          continue;
        }
      }

      $options['lifeTime'] = isset($page['lifetime']) ? $page['lifetime'] : $this->defaultLifetime;
      // if the page is not cached already, you can set with_layout option
      // xxx symfony page cache cannot have with_layout and without_layout cache same time
      if (!$this->cacheManager->isCacheable($this->uri)) {
        $options['withLayout'] = isset($page['with_layout']) ? $page['with_layout'] : false;
      }

      //add
      $this->cacheManager->addCache($module, $action, $options);
    }

    // chain
    $filterChain->execute();
  }
}
</pre>
<p>/apps/frontend/lib/myUser.class.php</p>
<pre class="brush:php">
class myUser extends sfBasicSecurityUser
{
// snip some methods for user signin or signout methods.
//
  public function getUsername()
  {
    if ($this->isAuthenticated()) {
      return $this->getAttribute('username', '', 'test');
    }
    return null;
  }

  public function usesCache($request)
  {
    $username = $request->getParameter('username', false);
    return $this->getUsername() !== $username;
  }

  public function isNotAuthenticated()
  {
    return !$this->isAuthenticated();
  }
}
</pre>
<h3>解説</h3>
<p>今回は、userクラスで条件判断をつけるuser_conditionのキーをfilters.ymlで選択させるようにしてさらに柔軟にしていました。つまり、user_conditionに与えるメソッドによって、キャッシュするかどうかを変更することができます。ここでは、isNotAuthenticatedによって、認証されていないユーザにはキャッシュをするというように与えています。前回のis_authenticatedオプションではなく、直接userクラスに判断させるようにしてあります。user_conditionが指定していなければ、認証していようが、いまいが、キャッシュが有効になります。そして、認証している際にさらに条件を加えたい場合は、user_conditionでもう少し詳しい内容を実装したメソッドを指定してみましょう。</p>
<p>例えば、user_conditionにusesCacheを指定すれば、リクエストパラメターにusernameがあった際に、セッションに入っているusername（ログインした際に持っておく）と比較をし、同じものであれば（そのユーザに関するページ）、キャッシュをしない、ということも可能となります。</p>
<p>さて、with_layoutオプションですが、これは実は少し厄介なのです。現在のsymfonyでは、同じページのキャッシュを複数持つことができないのですね。つまり、with_layoutとwithout_layoutのキャッシュが同時に持てずに、切り分けることができないのです。実際は、キャッシュといっても、シリアライズされた内容をキャッシュディレクトリに格納しているのですが、with_layoutの方は、オブジェクトなのに対し、without_layoutの方はarrayでシリアライズされているので、ここで問題が起きてしまいます。これは、すでにcache.ymlでキャッシュを指定していた際のwith_layoutの値と違う値を指定するとエラーが起きてしまうので、このようにすでになければ指定可能だが、すでにあれば、with_layoutオプションは使えない、というようにしました。</p>
<h3>まとめ</h3>
<p>前回の内容からさらにconditionalCacheを考えて、userクラスのメソッドにそれを判断させるキーをfilters.ymlに追加しました。このことにより、認証済みでも、さらに自分に関するページに関してはキャッシュをしない、等の処理が可能となりました。つまり、認証していても他のユーザのページであれば、キャッシュを使えるということになります。</p>
<h3>課題</h3>
<p>キャッシュに関しては、まだ考えているところがあります。cache.ymlにて、enabledをtrueにしているが、conditionalCacheFilterに渡すenabledをfalseにした際に、キャッシュをクリアするのではなく、そのまま置いておいて、そのページはキャッシュを使わずに表示させたいと思います。また、パーシャルキャッシュについても考えた方が良いでしょう。前回と今回に関しましては、ページキャッシュを扱いましたが、パーシャルキャッシュもログイン状態によって切り分けたいときがあると思います。それに関しても考えていきたいと思います。</p>
<p>ちなみに、ここで私が載せている内容は、正しい唯一の方法ではなく、「現在の私だったら、こうやって実装するかな。」という内容です。なので、時間が経つに連れて、より良い方法が見つかった際には、そちらに移行すると思います。その際には、また、ブログに書くでしょう。</p>
]]></content:encoded>
			<wfw:commentRss>http://ganchiku.com/2009/10/more_about_conditionalcache.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

