初めてのsymfony〜PEARインストール編パート2

実践編もパート2を迎えいよいよクライマックスの3っつ前ぐらいです。

symfonyで動くものを作ってみるお話。


では早速動くものを作っていくとします。
Hello World?何それ?時間の無駄です。
というより、とりあえず表示させるだけも、動くものを作るのもあまり時間はかわらない。しかし!理解は数百倍深まるので、いきなり動くものを作る。これ真理かも。


ここからプロジェクトのディレクトリにいるとして、相対パスで書いていきます。
ちなみに私は、すべてターミナルから作業しました。(ホントです)


さてさて、モジュールを生成から。

symfony init-module myapp mymodule

もう慣れたと思いますが、上のコマンドだけでモジュールのひな形が自動生成されます。
ここで、htaccessの設定を行います。
すでに、web以下に自動生成されているものを修正します。
少し余分な設定があるので、以下の行を残して残りの設定をコメントアウトします。
web/.htaccess
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]

http://symfony.localserver/mymodule
ここにアクセスすると、moduleなんとかsuccess!と表示されるはずです。
これでモジュールができました!


ここでは簡単な投稿システムを作りながら、symfonyのすごさを体感しましょう。

symfony」でデータベースを生成してテーブルを作ります。

CREATE TABLE `posts` (
  `id` int(11) NOT NULL auto_increment,
  `title` varchar(255) NOT NULL,
  `text` text NOT NULL,
  `modified` timestamp NULL default CURRENT_TIMESTAMP,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

手抜きですみません。。。

DBの設定をyamlに記述します。
yamlの事はすっ飛ばしますが、インデットが命です!解らない人は、必ず正確にコピペ職人に徹しましょう。
スキーマの設定。まぁテーブルの設定と言うことで。
config/schema.yml

propel:
  posts :
    _attributes: { phpName: Post }
    id:
    title:       varchar(255)
    text:        longvarchar
    modified: timestamp

DBの基本設定です。
config/databases.yml

all:
  propel:
    class:          sfPropelDatabase
    param:
      phptype:      mysql
      hostspec:     localhost
      database:     symfony
      username:     symfony_user
      password:     secret_pass

DBのモデルを生成します。これまた自動です。cache削除を忘れずに。

symfony propel-build-model
symfony clear-cache

キャッシュ削除と書きましたが、symfonyではyamlの設定とか、クラスファイルの場所とか、なんでもキャッシュ化しているので、ちょっと変更するたびにキャッシュをクリアする必要があるんですね。


次にコントローラを修正します。
apps/myapp/modules/mymodule/actions/actions.class.php
executeIndex内のforwardを行っている行をコメントアウトします。

さぁこれで、もう一度さっきのURLにアクセス。
http://symfony.localserver/mymodule

何が表示されましたか?
何も表示されない??そうす!それが正解です。
forwardして表示されていたさっきのsymfony画面が、コメントアウトしたことで表示されなくなったのです。
続いて、templateを修正しましょう。

apps/myapp/modules/mymodule/templates/indexSuccess.php
ここにてきとーに「Hello Worl」とでも入力して、mymoduleに再びアクセスしましょう。
ちゃんと入力した内容が表示されていますね。

ここで、表示された画面のソースを見てみましょう。
そうです!htmlのヘッダが出力されています。
symfonyでは最初からレイアウト構造ができているのです。いやー便利ですねー。


つーか、「Hello World!」やんないとか言っておきながら、、、


長くなったのでここから一気です。
各ファイルを以下の様に修正、及び新規作成します。
apps/myapp/modules/mymodule/actions/actions.class.php

<?php

/**
 * mymodule actions.
 *
 * @package    myproject
 * @subpackage mymodule
 * @author     Your name here
 * @version    SVN: $Id: actions.class.php 2692 2006-11-15 21:03:55Z fabien $
 */
class mymoduleActions extends sfActions
{
  /**
   * Executes index action
   *
   */
  public function executeIndex()
  {
    //$this->forward('default', 'module');
        $posts = PostPeer::doSelect(new Criteria());
        $this->posts = array();
        foreach ($posts as $post) {
                $this->posts[] = array(
                        'id' => $post->getId(),
                        'title' => $post->getTitle(),
                        'modified' => $post->getModified() ,
                );
        }
  }

  public function executeRead()
  {
    $id = (int) $this->getRequestParameter('id');
        $post = PostPeer::retrieveByPk($id);
        if ($post) {
                $this->title = $post->getTitle();
                $this->text = $post->getText();
        }
  }
  public function executeWrite()
  {
        $this->title = $this->getRequestParameter('title');
        $this->text = $this->getRequestParameter('text');
        if ( $this->title && $this->text) {
                $post = new Post();
                $post->setTitle($this->title);
                $post->setText($this->text);
                $post->save();
                $this->id = $post->getId();
        }
}


apps/myapp/modules/mymodule/templates/indexSuccess.php

<h2><?php echo link_to("It's BLAHG!",'mymodule/index') ?></h2>
<table>
<tr>
<th>Title</th>
<th>Last Modified</th>
<?php foreach ($posts as $post) { ?>
        <tr>
        <td><a href='mymodule/read/id/<?php echo $post['id'] ?>'><?php echo $post['title'] ?></a></td>
        <td><?php echo $post['modified'] ?></td>
        </tr>
<?php } ?>
</table>
<h4><?php echo link_to('write','mymodule/write') ?></h4>


apps/myapp/modules/mymodule/templates/readSuccess.php

<h2><?php echo link_to("It's BLAHG!",'mymodule/index') ?></h2>
<?php if (isset($title)) { ?>
<h4><?php echo $title ?></h4>
<pre>
<?php echo $text ?>
</pre>
<?php } else { ?>
<h4>Post not found</h4>
<?php } ?>


apps/myapp/modules/mymodule/templates/writeSuccess.php

<h2><?php echo link_to("It's BLAHG!",'mymodule/index') ?></h2>
<?php if (isset($id)) { ?>
<h4>Post Submitted.  <a href='read/id/<?php echo $id?>'>view post</a>
<?php } else { ?>
<?php echo form_tag('mymodule/write') ?>
<table>
<tr>
        <tr>
                <td><?php echo label_for('title', 'title') ?></td><td><?php echo input_tag('title') ?></td>
        </tr>
        <tr>
                <td /><td><?php echo textarea_tag('text') ?></td>
        </tr>
        <tr>
                <td /><td><?php echo submit_tag('submit') ?></td>
        </tr>
</form>
<?php } ?>
</table>


ここまで書いたら、モジュールにアクセスしてみましょう。
ちゃんと表示されていますね。
writeから投稿画面に入って、適当に入力して登録してみて下さい。
TOPに戻ると、登録した内容がちゃんと表示されているはずです。


一気に書いた内容も特に難しい事はないですよね?読めば何となくは意味がつかめるはずです。


1つ言わせてもらうと、DBクラスは素晴らしいですね。ここまで簡素化してるなんて。ZendFrameworkよりも、シンプルで分かりやすい。ここまで可読性が高いのにやってることはとっても高度な感じします。まぁあくまで、基本的な動作を見ただけの感想ですが。

これで見事ブログみたいな投稿機能が実装されました。
「こんな簡単にできるの!?」と思うか、「これだけやって、たったこんだけの内容!?」って思うかは人それぞれですが、symfonyのすごさはまだまだここからだったりします。

次回はこれをベースに高機能ブログモジュールを作っていきます!

次回、symfony実践編パート3。You can advance?

お楽しみに。


※この記事は、私の大嫌いなIBM様の「PHPフレームワーク」の連載を大いに参考にさせて頂いています。
http://www.ibm.com/developerworks/jp/views/opensource/libraryview.jsp?search_by=PHP+フレームワーク
symfonyのヴァージョンも変わってるせいか、ぼろぼろなのであまり見る価値ないかもです。

初めてのsymfony〜symfony実践編

連載もとうとう第三回!
今回は、symfony実践編。
symfonyのプロジェクトとアプリケーションを作っていくお話。

symfonyプロジェクトとアプリケーション

プロジェクト製作

mkdir /var/www/myproject
cd /var/www/myproject
symfony init-project myproject

アプリケーションのセットアップ

symfony init-app myapp

これで、appsの下にmyappが作成された。


次に、apacheの設定で今回は完了です。

DocumentRoot /var/www/myproject/web
ServerName symfony.localserver
ServerAlias symfony.localserver
ErrorLog logs/symfony_error_log
CustomLog logs/symfony_log common

クライアントのhostsに「symfony.localserver」とIPのマッピングをして、アクセスすると、「Symfony Project Created」って表示されるよ。

このままだと、画像のリンクが切れた状態で表示される。
別に気にしなくとも良いのだが、デバッグとかでも使うのでエイリアスも追加しておく。
上のアパッチの設定のVirtualHostの中に

Alias /sf /usr/share/php/PEAR/data/symfony/web/sf

と1行入れておく。
これでsymfonyのdataにある画像やcssにリンクが繋がる。

htaccessを有効にしたり、アクセス権に問題があるのなら、以下をDIRの設定に追加。
AllowOverride All
Allow from All

ここまでやればバッチリ表示されるだろう。

また、デバッグ用の表示を行いたいなら、こっちにアクセスしてみよう。いろいろな情報を表示させることができる。
これは、アプリケーションを作ってからも使えるので忘れないでメモ。
http://symfony.localserver/myapp_dev.php

そろそろsymfonyのスゴサが見え隠れしてきましたね。
でもまだまだ、ここからが感動の嵐です。

さて、次回はなんと、いきなり動くものを作っていきます!symfony実践編パート2。混沌の先に見えるは光か闇かー

お楽しみに。

初めてのsymfony〜symfonyインストール編

PEARを使ってsymfonyを簡単インストールするお話。


PEARにchannellを追加して、インストール実行

# pear channel-discover pear.symfony-project.com
# pear remote-list -c symfony
# pear install symfony/symfony
# pear install http://phing.info/pear/phing-current.tgz

これで完了。続いてsymfonyコマンドにシンボリックリンクをつける。

# ln -s /usr/share/php/bin/symfony /usr/bin/symfony

これで、

# symfony -T

コマンド情報が表示されればOK。

いやー簡単ですね。
さて、次回はいよいよsymfonyでアプリケーションを作っていきます!symfony実践編。

お楽しみに。

イントラのサーバにsymfonyをインストールする時のお話。
第一回、PEARインストール編

symfonyのインストール方法はいろいろあるが、一番簡単なPEARを使う方法で説明します。

実際に作業を行った環境はfedora11です。
ディレクトリとかは標準的だと思います。

PEARインストール

/usr/share/php/
以下にインストール

$ curl http://pear.php.net/go-pear > go-pear.php
# php -q go-pear

設定を少し変更。
1. Installation prefix ($prefix) : /usr/share/php

# /usr/share/php/bin/pear

動作を確認。

# ln -s /usr/share/php/bin/pear /usr/bin/pear

/user/binにリンクを張って、pearコマンドを有効にする。

これで、pearを直接たたける様になるのです。

さて、次回はいよいよ大詰め!symfonyインストール編。
お楽しみに。

ZendFrameWorkでエラーコントローラを使おうとした時のお話し。

ZendFrameWorkのコントローラは初めからエラー処理が組み込まれてるから、ErrorControllerとerrorActionを作ってあげるだけで、本当なら終わりなのですが、このErrorControllerはデフォルトのモジュールの物が呼ばれる仕様なのに、実行中のモジュールの物が呼ばれると勘違いしてマニュアルを漁ってしまいました。

以上で終わりなので、後はてきとーにサンプルコード(for me)。


defaultモジュール以外のエラーコントローラを呼ぶ方法

$front = Zend_Controller_Front::getInstance();
$plugin = new Zend_Controller_Plugin_ErrorHandler();
$plugin->setErrorHandlerModule('nonedefault');
$front->registerPlugin($plugin);

よく忘れるのが、これ。ZendFFのプラグインはなんか最後にレジストしてあげないとダメなのね。

もちろん、「setErrorHandlerController()」や「setErrorHandlerAction()」を使ってアクションやコントローラの設定も可。


開発環境とかで、例外が発生した場合にエラー内容を表示させる設定にしてると、エラーコントローラは呼び出されないようなので気をつけましょう。

$front = Zend_Controller_Front::getInstance();
$front->throwExceptions(true);// 例外を出力 = エラーアクションを不使用

smartyの値の中の演算子の謎

たまにsmartyのテンプレートでちょっとだけ複雑な事やろうとした時とかに、毎回ハマってしまうお話し。

値に演算子(計算式)を入れると、計算されずに文字列で帰ってくることがある。
マニュアルにもらしい解説がないので毎回毎回悩まされるので、分かり易くまとめておく。

{assign var="no" value="`124+$item.no`"}

↑ダメな例

{assign var="no" value="`$item.no+124`"}

↑OKな例

そう、順番です。valueの値に演算子(変数と数値の)を挿入する時は、変数が先頭にこないといけないのです。

たったこれだけのことを毎回忘れてしまい、マニュアルを読みあさったりしてしまうのは、この挙動が僕には理解できない仕様だからなのかな。

他にも似たような謎の仕様に悩まされた事があるが、忘れた。思い出したら、また書こうと思う。

ユーザーデータを集計して、ランキングを取りたかった時のお話し。つづき

前回のjoinとgroup byを使った方法は、対象件数が増えると乗算で処理に時間が掛かるため、数万件の処理にはとても耐えられなかったので、新しくsqlを作り直した時のお話し。つまり、前回の続きです。

select-insert(なんて呼ぶのかよく分からないが、insertのvalueをselect文にする事)を使って、やる場合はとても簡単シンプル&超高速。

INSERT INTO rank
SELECT null, uid FROM `user` ORDER BY `user`.`count` DESC

rankテーブルは、keyにAUTO_INCREMENTかけて、これが順位。「null, uid」でnull指定で連番を挿入させて簡単にランキングを作成できた。5万件でも0.00何秒の世界。

で、これをアップデート文にすればよいかと単純に考えたはいいが、できない。やはり、Mysqlのupdate構文に問題ありな予感?それとも僕の頭?

仕方ないのでhttp://dev.mysql.com/doc/refman/5.1/ja/user-variables.html:ユーザ変数なる物を引っ張り出して、順位を計算する方法を編み出した。いや、oracleなら普通に関数であるんだけどね。

set @rownum = 0;
select @rownum := @rownum + 1 rownum, uid, count
from user order by count;

これで順位を一発で取得できる。しかも超早。
http://dev.mysql.com/doc/refman/5.1/ja/user-variables.html:ユーザ変数すごいよ。ちょっとした小細工なら何でもこなせそう。

次は、これをupdateする。

set @rownum = 0;
update rank
 inner join (
  select @rownum := @rownum + 1 rownum, uid
  from user order by count DESC
 ) b
 ON rank.uid = b.uid
set rank.battle = b.rownum

またまた変なところで結合させなくちゃいけないMysqlの罠に嵌りながら作ってみたところ、5万件のデータでも0.1秒以下という何の問題もない結果が出てくれました。
しかもこれ、limitで制限掛けて回せば、たぶん億単位のデータでも可能なんじゃないかな?