2011年11月2日水曜日

Symfony2でxml表示

Symfony2でhtmlではなく、xmlを表示するように作りたいので調べてみました。

助けてもらってばかりで足を向けて寝れない、日本symfonyユーザー会クイックツアー コントローラーにそれっぽい記述があるので試してみました。

# src/Acme/PagesBundle/Controller/PagesController.php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;

...

 /**
 * @Route("/xml", defaults={"_format"="xml"}, name="_xml")
 * @Template()
 */
 public function xmlAction()
 {
  return $this->render('AcmePagesBundle:Pages:xml.xml.twig');
 }

# src/Acme/PagesBundle/Resources/views/Pages/
pc_home.html.twig
...
<a href="{{ path('_xml') }}">XML test</a>
...

# src/Acme/PagesBundle/Resources/views/Pages/xml.xml.twig
<test>
 xml test
</test>


これでOKかなと、ブラウザでアクセスするとエラーが表示される。
An exception has been thrown during the rendering of a template ("Route "_xml" does not exist.") in "AcmePagesBundle:Pages:pc_home.html.twig" at line 15.

「_xml」っていう名前のルートは設定されてないよ。って意味かな?多分。...速く英語勉強しないとダメだなぁ。

そんなこんなで色々と試して結局以下のように書いたら動いた。
# src/Acme/PagesBundle/Controller/PagesController.php
 public function xmlAction()
 {
  return $this->render('AcmePagesBundle:Pages:xml.xml.twig');
 }
useとアノテーションを消す。

# src/Acme/PagesBundle/Resources/views/Pages/
pc_home.html.twig
...
<a href="{{ path('AcmePagesBundle_xml') }}">XML test</a>
...
このルーティングの名前を変更。

# src/Acme/PagesBundle/Resources/views/Pages/xml.xml.twig
<test>
 xml test
</test>
このファイルは変更なし。

# src/Acme/PagesBundle/Resources/config/routing.yml
...
AcmePagesBundle_xml:
 pattern: /xml
 defaults: { _controller: AcmePagesBundle:Pages:xml, _format: xml }
通常のルーティングの設定に「_format: xml」を追加。

これで何とかxmlで表示されるように動いた。

FirefoxのFirebugでコンテンツタイプを見てみても
Content-Type text/xml; charset=UTF-8
って出てる。ヨシヨシ。

...ただ、アノテーション使う方式は何がダメだったのかなぁ。謎は残ったままです。

Symfony2で機能拡張 訂正

昨日、Symfony2で機能拡張という記事を投稿したらコメント頂きました。Hidenori GOTO さん有難うございます。

以下コメント抜粋
まずドキュメントのサービスコンテナのページで説明しているMailerクラスの配置場所ですが、バンドルのディレクトリ直下に配置しているようですね。

また、今回作られているようにコントローラ内で直接利用するライブラリクラスは、DependencyInjectionディレクトリではなくて、単純にバンドルディレクトリ直下や、Util、Request等ライブラリに合わせたディレクトリを作って配置すればOKです。
バンドルディレクトリ直下で良かったのか。なるほど、なるほど。

という訳で、Symfony2のお作法に従って書き直しする。

#\src\Acme\PagesBundle\Carrier.php
namespace Acme\PagesBundle;

use Symfony\Component\HttpFoundation\Request;

class Carrier
{
}
保存場所を変更して名前空間も変更。

#\src\Acme\PagesBundle\Controller\PagesContller.php
use Acme\PagesBundle\Carrier;
...
 public function indexAction()
 {
  $carrier = new Carrier();
 }
useの設定変更

とこんな感じでした。ちゃんちゃん。

2011年11月1日火曜日

Symfony2のRequestクラスのserver値の取り出し

コントローラー内でRequestクラスを取得するのは

$request = $this->getRequest();

でOKですが、その後でつまづきました。

今回欲しい値は$_SERVER['HTTP_USER_AGENT']だったのですが、

$sever = $request->server;

で$_SERVER変数の値は取れているようですが、

$agent = $request->server->parameters['HTTP_USER_AGENT'];

で取得しようとするとエラーが出ました。
なんかparametersはprotectedだからダメって感じです。

でvendor¥symfony¥src¥Symfony¥Component¥HttpFoundation¥Request.phpを開いてのぞく。
 ↓
Request->serverはコメントから¥vendor¥symfony¥src¥Symfony¥Component¥HttpFoundation¥ServerBag.phpらしいのでを開いてのぞく。
 ↓
メソッドが1つポツンとあるだけなので、親クラスの¥vendor¥symfony¥src¥Symfony¥Component¥ParameterBag.phpを開いてのぞく。

メソッドがたくさん。
all()でparametersが返すらしい。試しに使ってみるが全部はいらないんだよね。
get($path, $default, $deep)でparametersの値を返すっぽい。

$agent = $request->server->get('HTTP_USER_AGENT');

で無事値を拾えた。フゥ〜、一件落着です。

Symfony2でviewsのファイル名を変更するとでるエラー

symfony2でバンドル内のビューのファイル名を
index.html.twig
↓
pc_index.html.twig
に変更したら

Warning: filemtime() [<a href='function.filemtime'>function.filemtime</a>]: stat failed for C:\...\src\Acme\PagesBundle/Resources/views/Pages/index.html.twig in C:\...\vendor\symfony\src\Symfony\Bundle\FrameworkBundle\Templating\Loader\FilesystemLoader.php line 68

っていうエラーが出ました。

意味がわからず途方に暮れてましたが、コマンドコンソールで

php app/console cache:clear

を実行したらエラーがなくなりました。
キャッシュが悪さしていたようです。
良かった、良かった。

それにしても、cakephpと比べてsymfony2のエラーメッセージは分かりづらいなぁ...

symfony2でキャリアの判別

キャリアというかuser_agentというかですけれど、とにかくリクエストからユーザーエージェントをを調べて利用している端末を調べてガラパゴス携帯なら文字のエンコードをshift-jisで吐き出す処理。

関連するphpファイル

#\src\Acme\PagesBundle\DependencyInjection\Carrier.php

namespace Acme\PagesBundle\DependencyInjection;

use Symfony\Component\HttpFoundation\Request;

class Carrier
{
 protected $agent;
 protected $carrier_type;

 public function __construct(Request $request)
 {
  $this->agent = $request->server->get('HTTP_USER_AGENT');
  $this->initialize();
 }

 public function initialize()
 {
  $this->carrier_type = "pc";

  if(preg_match("/^DoCoMo/i", $this->agent)){
   $this->carrier_type = "docomo";
  }else if(preg_match("/^(J\-PHONE|Vodafone|MOT\-[CV]|SoftBank)/i", $this->agent)){
   $this->carrier_type = "softbank";
  }else if(preg_match("/^KDDI\-/i", $this->agent) || preg_match("/UP\.Browser/i", $this->agent)){
   $this->carrier_type = "au";
  }else if(preg_match("/iPad/i",$this->agent)){
   $this->carrier_type = "ipad";
  }else if(preg_match("/iPhone/i",$this->agent)){
   $this->carrier_type = "iphone";
  }else if(preg_match("/Galaxy/i",$this->agent)){
   $this->carrier_type = "iphone";
  }else if(preg_match("/Android/i",$this->agent)){
   $this->carrier_type = "android";
  }
 }

 public function setCarrierType($type)
 {
  $this->carrier_type = $type;
 }

 public function isDocomo()
 {
  if($this->carrier_type === "docomo"){
   return true;
  }else{
   return false;
  }
 }

 public function isSoftbank()
 {
  if($this->carrier_type === "softbank"){
   return true;
  }else{
   return false;
  }
 }

 public function isAu()
 {
  if($this->carrier_type === "au"){
   return true;
  }else{
   return false;
  }
 }

 public function isIpad()
 {
  if($this->carrier_type === "ipad"){
   return true;
  }else{
   return false;
  }
 }

 public function isIphone()
 {
  if($this->carrier_type === "iphone"){
   return true;
  }else{
   return false;
  }
 }

 public function isAndroid()
 {
  if($this->carrier_type === "android"){
   return true;
  }else{
   return false;
  }
 }

 public function isPc()
 {
  if($this->carrier_type === "pc"){
   return true;
  }else{
   return false;
  }
 }

 public function isLegacy()
 {
  if($this->isDocomo() || $this->isSoftbank() || $this->isAu()){
   return true;
  }else{
   return false;
  }
 }

 public function isSmartphone()
 {
  if($this->isIphone() || $this->isAndroid()){
   return true;
  }else{
   return false;
  }
 }

 public function isSmartpad()
 {
  if($this->isIpad()){
   return true;
  }else{
   return false;
  }
 }
}


#\src\Acme\PagesBundle\Controller\PagesContller.php

namespace Acme\PagesBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Acme\PagesBundle\DependencyInjection\Carrier;


class PagesController extends Controller
{

 public function indexAction()
 {
  //リクエストを取得
  $request = $this->getRequest();
  //キャリアのインスタンスを生成
  $carrier = new Carrier($request);
  //ガラパゴス携帯か判断する
  if($carrier->isLegacy()){
   $content = $this->renderView('AcmePagesBundle:Pages:legacy_index.html.twig');
   $content = mb_convert_encoding($content, "SJIS", "auto");
   return new Response($content);
  }
  return $this->render('AcmePagesBundle:Pages:pc_index.html.twig');


あと当たり前ですが、これとは別にビューを用意します。

...っていうか本当にshift-jisに変わってるのかな?今更ながら不安になってきた。

Symfony2で機能拡張

この記事は訂正されました。Symfony2で機能拡張  訂正も御覧下さい。
symfony2で機能拡張はどうすれば良いのだろうか。
cakePHPで言うところのコンポーネント、ビヘイビア辺りをイメージしているのですがいまいち不明。

やっぱり日本symfonyユーザー会のsymfony2日本語ドキュメントを読む。
サービスコンテナを取り敢えず読んでみる。

ふむふむ。
  1. app/config/config.ymlにserviceを追加して
  2. コントローラーに $mailer = $this->get('my_mailer');って書く

...あれ?Mailerクラスはどこ書くの?

違うとこも読む。
セマンティック...
???Acme/HelloBundle/DependencyInjection/HelloExtension.phpをMailerとして書くの?

意味不明すぎる。英語が読めたらなぁ...


取り敢えず自力で頑張ってみる。
以下一応できたのでその結果。symfony2の規約として正しいかどうか不明。でも出来た。
#\src\Acme\PagesBundle\DependencyInjection\Carrier.php

namespace Acme\PagesBundle\DependencyInjection;

use Symfony\Component\HttpFoundation\Request;

class Carrier
{
}


#\src\Acme\PagesBundle\Controller\PagesContller.php

use Acme\PagesBundle\DependencyInjection\Carrier;
...
public function indexAction()
{
$carrier = new Carrier();
}


DependencyInjectionは依存性の注入って意味らしいです。なのでこの直下に作りたいクラスを書く。
コントローラーでuseで書いたクラスを呼び出せるように設定。new Carrier();でインスタンス生成。
って感じです。


これで良いのか不明。でも出来たし、よしとする。

Symfony2でshift-jis表示

次の仕事の案件でSymfony2を使って、主にモバイルをターゲットにしたサイトを作る事になった。
ちなみにSymfony自体初心者で分からない事だらけ...

Symfony日本ユーザー会の日本語ドキュメントを読みあさって何とか簡単なページを使えるようにはなった。
感謝感謝。

で当然モバイル用なのでShift-jisのページを表示する必要があるのだが、これが中々分からなかったので防備録を兼ねて投稿。

通常コントローラー内でレスポンス返すには

return $this->render('AcmePagesBundle:Pages:index.html.twig');

とするのだがこれだと当然保存したエンコード形式になってしまう。
PC用のページも作るし、eclipse使っているのでファイル自体はUTF-8にして、htmlを吐き出す時だけshift-jisにしたい。
なんだかんだで

use Symfony\Component\HttpFoundation\Response;

...

$content = $this->renderView('AcmePagesBundle:Pages:index.html.twig');
$content = mb_convert_encoding($content, "SJIS", "auto");
return new Response($content);

で出来るようになったっぽい。

...これで本当にいいのか?なにせ始めたばかりで自信がない。
もし他に良い方法知っている方いらっしゃたらコメントお願いします。m(_ _)m