WEBアプリケーション研究室 開発ノート TOP

WEBアプリケーション研究室 開発ノート 2008年11月

スポンサーサイト

-------- --:--

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

    このエントリーをはてなブックマークに追加

どっちかというとlinuxのコマンドラインの問題っぽいけど詰まったので書いておきます。
taskでコマンドラインのオプションを設定するにはaddOptionを使います。


$this->addOption(
'directory',
null,
sfCommandOption::PARAMETER_REQUIRED,
'package directory',
sfConfig::get('sf_plugins_dir')
);

こんな感じで指定するのですが、2番目の引数(上の例でnull)はshortcutを指定できるのですが
以下のコードで動きませんでした。

$this->addOption(
'directory',
'dir',
sfCommandOption::PARAMETER_REQUIRED,
'package directory',
sfConfig::get('sf_plugins_dir')
);

コマンドラインで
$sumfony gomo:task -dir="~" Target

これは'd' 'i' 'r'を指定したことになってしまいます。なので、そんなオプションはないよ、と怒られるわけです。つまりショートカットは1文字で、ということです。また、shortcutで指定するときは'='はいりません。つけると=まで変数に渡されます。

ちょっと考えれば、普段そうやってオプションを指定してコマンドを打っているのに、どっかでこんなshortcutをつけてるのを見たような気がして、勘違いしてしまいました。

[symfony 1.1.5]

    このエントリーをはてなブックマークに追加

デザイニング・インターフェース ―パターンによる実践的インタラクションデザインデザイニング・インターフェース ―パターンによる実践的インタラクションデザイン
(2007/01/23)
Jenifer Tidwell

商品詳細を見る
WEBに限らずアプリケーションのユーザーインターフェイスをカタログ化しデザインパターンにしようという試みの本。
良くできている。設計アイデアの助けになるだけでなく、例えば部下に、何となく悪いのはわかるけど、うまく説明できない人におすすめ。翻訳が微妙に読みづらいので☆一個マイナス

    このエントリーをはてなブックマークに追加


$output = "foo\nbar";
preg_match('/.+/s', $output, $matches);
print_r($matches);

sオプションを使います。
正確にはドットが改行文字もマッチするようになるオプションです。
詳しくはhttp://jp2.php.net/manual/ja/reference.pcre.pattern.modifiers.php

    このエントリーをはてなブックマークに追加

ファイルシステムをさまざまな条件で検索して、ファイル名やディレクトリ名を取得するsfFinderというクラスがあります。


$list = sfFinder::type('any')->name('/\.(php|yml)/')->in($dir);

タイプは directory か file か any のどれか。
name()はマッチするファイル名を指定するが、逆に除くファイル名を指定するnot_name()というのもある。
in()には検索するディレクトリ。
詳しくはhttp://www.symfony-project.org/api/1_1/sfFinder

[symfony 1.1.5]

    このエントリーをはてなブックマークに追加

cronで実行するPHPなど、コマンドラインから実行するPHPを作るのって、sessionやincludeパスの設定など以外に面倒ですよね。
symfonyには、そういうPHPを簡単に生成する機能があります。
taskと呼ばれているもので、propel-build-allなど、コマンドラインでsymfony ~と入力するのも、taskの一つです。symfonyコマンドで一覧を確認できます。


$symfony

taskを生成するコマンド(これもtaskですね)はsymfony generate:taskです。
まずhelpを見てみましょう。

$symfony help generate:task
Usage:
symfony generate:task [--dir[="..."]] [--use-database|-db[="..."]] [--brief-description|-bd[="..."]] task_name

Arguments:
task_name The task name (can contain namespace)

Options:
--dir The directory to create the task in (default: lib/task)
--use-database (-db) Whether the task needs model initialization to access database (default: true)
--brief-description (-bd) A brief task description (appears in task list) (default: )

まあ、見ればわかりますが、簡単に説明すると、
Arguments:
task_name 実行するときに指定するタスクの名前。namespace:task_nameと名前空間を指定できる。

Options:
--dir taskの生成先。pluginにするなら--dir=plugins/myPlugin/lib/taskとか指定するといいみたいです。
--use-database データベースを使うか?デフォルトがtrueなので使わないならfalseにしましょう。
--brief-description タスク一覧を出したときに出る簡単な説明。後で編集できます。

$symfony generate:task --use-database="false" gomo:build-package-xml

これで、lib/taskにファイルが出来上がるのでこれを編集します。
上の方、protected function configure()メソッドの中で色々設定できます。

protected function configure()
{
$this->namespace = 'gomo';
$this->name = 'build-package-xml';
$this->briefDescription = 'hogehogheo';
$this->detailedDescription = <<The [gomo:build-package-xml|INFO] task make package.xml for PAER.
Call it with:

[php symfony gomo:build-package-xml|INFO]
EOF;
// add arguments here, like the following:
$this->addArgument('name', sfCommandArgument::REQUIRED, 'package name');
// add options here, like the following:
$this->addOption('dir', 'd', sfCommandOption::PARAMETER_REQUIRED, 'package directory', sfConfig::get('sf_plugins_dir'));
}

$this->addArgument()はコマンドの引数
$this->addOption()はオプションを追加
上の例だと実行は

$symfony gomo:build-package-xml --dir="/path/to/package" package_name

自分で作ったtaskも先ほどのリストに出てきて、また、普通にhelpも表示できます。

$symfony
$symfony help gomo:build-package-xml

削除するときはlib/taskの中のtaskフォルダを削除すれば一覧から消えるみたいです。

sfで始まるコアクラスはオートロードが出来ましたが、libやpluginの独自クラスはrequireを書かないとだめみたいです。sfConfig::getで以下のディレクトリが取得できるのでrequire_onceするみたいです。

[sf_symfony_lib_dir] => /home/~
[sf_root_dir] => /home/~
[sf_apps_dir] => /home/~
[sf_lib_dir] => /home/~
[sf_log_dir] => /home/~
[sf_data_dir] => /home/~
[sf_config_dir] => /home/~
[sf_test_dir] => /home/~
[sf_doc_dir] => /home/~
[sf_plugins_dir] => /home/~
[sf_web_dir] => /home/~
[sf_upload_dir] => /home/~
[sf_cache_dir] => /home/~
[sf_logging_enabled] =>


[symfony 1.1.5]

    このエントリーをはてなブックマークに追加

Peerクラスでは以下の4つの形でカラム名がしまわれている。


/**
* phpname type
* e.g. 'AuthorId'
*/
const TYPE_PHPNAME = 'phpName';

/**
* column (peer) name type
* e.g. 'book.AUTHOR_ID'
*/
const TYPE_COLNAME = 'colName';

/**
* column fieldname type
* e.g. 'author_id'
*/
const TYPE_FIELDNAME = 'fieldName';

/**
* num type
* simply the numerical array index, e.g. 4
*/
const TYPE_NUM = 'num';


またお互いの変換をPeer::translateFieldnameというメソッドで出来るようになっている。
動的に呼び出すメソッドを変えなきゃいけないシチュエーションで使えます。


$field_name = 'user_name';
$php_name = SamplePeer::translateFieldname(
$field_name,
BasePeer::TYPE_FIELDNAME,
BasePeer::TYPE_PHPNAME
);


[symfony 1.1.5]

    このエントリーをはてなブックマークに追加

達人に学ぶ SQL徹底指南書 (CodeZine BOOKS)達人に学ぶ SQL徹底指南書 (CodeZine BOOKS)
(2008/02/07)
ミック

商品詳細を見る
これを読めば一皮むけます。
GROUP BYがよくわからない人にもおすすめ。

    このエントリーをはてなブックマークに追加

増補改訂版Java言語で学ぶデザインパターン入門増補改訂版Java言語で学ぶデザインパターン入門
(2004/06/19)
結城 浩

商品詳細を見る
たとえJAVAを知らなかったとしても、わざわざJAVAを勉強してでも読んだ方がいい本。必読。

    このエントリーをはてなブックマークに追加

PHPではあまり聞きなれないのでなかなか理解できなかったけどおぼろげながらわかってきました。
■何のためにあるのか?
PHPは二つのクラスを親クラスに持つことができません。(多重継承?)すでに親クラスを持っているクラスを拡張する時、別の(親クラスを持った)クラスのメソッドがそのまま使える場合があっても継承を使うことができません。そういう場合委譲(クラスの中に別クラスのインスタンスを持たせて処理させる)を使うのですが、それをsfEventDispatcherの中にまとめて持たせて集中管理するって感じでしょうか・・・(急に語尾に自信がない感じですが)

■イベントの種類
notify型
 通知側でsfEventコンストラクタの3番目の引数に連想配列を渡して、リスナーの中ではそれをイベントから配列形式で取り出せる。リスナーはなにも返さない。またすべてのリスナーを実行する。
notifyUntil型
 リスナーが一つ見つかった時点で、処理をやめる。sfEvent::isProcessed()で処理したかどうか調べられる。リスナーと値のやり取りはnotify型と同じ。リスナーはなにも返さない。
filter型
 リスナーが値を返すように作る。返した値は通知側でsfEvent()->getReturnValue()で何かに使われている。複数あっても処理はされるが、getReturnValue()は最後のもののみを返す。

リスナーをどこで登録するのかというとBookでは
apps/frontend/config/ApplicationConfiguration.class.php
に設置するように書いてある。また、プラグインの場合は
config/config.php
に書けばOK。

あらかじめ処理の途中でsfEventDispatcher::notify|notifyUntil|filterを呼び出すように設定しておいて、あとからconnectでそこに処理を足せるようにしてある感じですね。
逆に考えるとsfEventDispatcherにconnectで登録しておいたメソッドはどこからでもsfEventDispatcher::notify|notifyUntil|filterで呼び出せるともいえるでしょうか。
組み込み済みイベント一覧がこの辺にあります。

[symfony 1.1.5]

    このエントリーをはてなブックマークに追加

index.php(フロントコントローラー)からmoduleとactionが決まるまで
途中細かいところは省略をしていますが、ざっと整理してみました。

SF_ROOT_DIR/web/index.php

SF_ROOT_DIR/apps/名前/config/名前Configuration.class.phpをnew
ProjectConfiguration.class.phpのコンストラクタが呼ばれる
コアクラスオートロードの設定
sfEventDispatcherをnew
ルートディレクトリの初期化
オートロードの設定
プラグインのconfig.phpを読み込む
sfContextをnew
sfContextにConfigurationをセット
sfContextにEventDispatcherをセット
sfDatabaseManagerをnew
sfActionStackをnew
cach内のconfig/factories.ymlを読み込み実行
sfWebDebugLoggerをnewしてContextにセット
sfFileLoggerをnewしてContextにセット
sfI18NをnewしてContextにセット
sfPatternRoutingをnewしてContextにセット
ルーティングの設定の初期化・読み込み
sfFrontWebControllerをnewしてContextにセット
sfWebRequestをnewしてContextにセット
$_GETや$_POSTをrequest内にセット
同時にgetPathInfoからmoduleとactionを決定
sfWebResponseをnewしてContextにセット
sfSessionStorageをnewしてContextにセット
myUserをnewしてContextにセット
キャッシュがONならsfSessionStorageをnewしてContextにセット
sfFrontWebController::dispatch()をコール
sfWebRequestからmoduleとactionを取得
moduleとactionにforward

コードをたどってみて思ったのは意外に軽そうというか、工夫されてるなと思いました。
例えば$_SERVERなんかをsfWebRequestにセットするところも参照をセットしていたり、思っていたよりインスタンス化されてるクラスも少なかった。
またContextに全部セットしてどこからでもアクセスできるやり方はすごいと思った。
恐るべしマーティン・ファウラー。
気になったのはsfWebRequestでmoduleとactionを決定するところで

return $this->dispatcher->filter(new sfEvent(
  $this,
  'request.filter_parameters',
  array('path_info' => $this->getPathInfo())
), array())->getReturnValue();
というコードを呼び出してます。 何が行われてるのかよくわかりません。 とりあえず、今後イベントのコードを読み解いてみたいと思います。

[symfony 1.1.5]

    このエントリーをはてなブックマークに追加

ものすごいケアレスミスでお恥ずかしいのですが
なかなか気付かず、手間取ったので忘れないために書いておきます。

admin generatorを使ってgenerator.ymlでカスタマイズする時
存在しないカラム名をdisplayに書くとdisabledになります。

まあ、タイピングミスをしていたわけでして・・・

[symfony 1.1.5]

    このエントリーをはてなブックマークに追加

DBテーブルを作るようなプラグインを削除した時、
テーブルがいつまでも作られてしまうので気づいたのですが
~/data/sql
にSQLを書いたファイルを置いて
~/data/sql/sqldb.map
にそのファイルを書いてやると実行されるみたいです。
例えばafter.sqlを入れたら


# Sqlfile -> Database map
lib.model.schema.sql=propel
after.sql=propel

と書いてやると実行されます。

ただどういう順番で実行されるかがよくわかりません。
自分で置いたSQLは上に書いても下に書いても最後に実行されました。

頭の文字で認識してるのだろうか
例えば
lib->plugin->その他
とか・・・(あくまで想像です)

まあ、依存したSQLは同じところに書けばいいのでそれほど問題ないと思いますが。

[symfony 1.1.5]

    このエントリーをはてなブックマークに追加

forwardされたaction内で


$this->getModuleName()
$this->getActionName()

をすると、当たり前ですがforward先(最後のaction)の名前を取得してしまいます。
そこでforwardの場合URLは変わらないのでそこからmodule名を取得してみました。

sfContext::getInstance()->getRouting()->findRoute($this->getRequest()->getPathInfo());

これはルートがないときnullを返します。

すいません。最初に書いた情報は間違ってるっぽいです。parseは
"Parses a URL to find a matching route and sets internal state."
routerにデータをセットしちゃうのでこの場合適切じゃなかったです。
↓これは間違いです
URLからmodule名、action名を得るにはsfPatternRouting::parseを使います。
RoutingはContextから取得できるので


$route = sfContext::getInstance()->getRouting()->parse($request->getPathInfo());


で出来ました。
注意する点は、httpからのフルパスは受け付けないので


$request->getUri()


を渡すと、Exceptionが投げられます。

[symfony 1.1.5]

    このエントリーをはてなブックマークに追加

1対1のリレーショ ンでModelを変更してJOIN先のテーブルにデータを保存しましたが、これだとJOIN先に1対多、多対多のリレーションがあった時にadmin generatorを使えないことに気づきました。

propelはviewも普通のDBとして扱うので、スキーマに、さもテーブルのように設定を書いてbuild-modelしてやればいけるんですけど、MySQLはJOINしたviewにINSERTが出来ないのでこの案も廃案。

まあ、素直に別々のページにすれば良い感じですかね。

ちなみにDBがpostgresqlだったらルールを作ればいけます。

[symfony 1.1.5]

    このエントリーをはてなブックマークに追加

普通は同じテーブルにするのであんまりないかもしれないけど
sfGuardPluginを導入してみてProfile用のテーブルと1対1になってしまいました。
普通はactionでやるんでしょうけど
admingeneratorなんかでさくっと作成画面を作るならmodelいじっちゃった方が楽なのかなあ
とやってみました。
オブジェクトが新規のときにsfGuardUserをnewしてやれば良いのかな。


class Account extends BaseAccount
{
public function setPassword($value)
{
self::getsfGuardUser()->setPassword($value);
}

public function setEmail($value)
{
self::getsfGuardUser()->setUserName($value);
}

public function setIsActive($value)
{
self::getsfGuardUser()->setIsActive($value);
}

public function setIsSuperAdmin($value)
{
self::getsfGuardUser()->setIsSuperAdmin($value);
}

public function getPassword()
{
return null;
}

public function getEmail()
{
return self::getsfGuardUser()->getUserName();
}

public function getIsActive()
{
return self::getsfGuardUser()->getIsActive();
}

public function getIsSuperAdmin()
{
return self::getsfGuardUser()->getIsSuperAdmin();
}

public function getsfGuardUser($con = null)
{
if($this->asfGuardUser === null && $this->user_id === null)
{
$this->asfGuardUser = new sfGuardUser();
}
return parent::getsfGuardUser($con);
}
}

今まで自分が作ったシステムの拡張でマスターデータの管理画面を作る作業がたまにあるのですが、symfonyのadmingeneratorを試してからめんどくさくてしかたありません。これは便利です。
別サブドメインをあてて、マスターだけsymfonyを導入しようか検討中です。

[symfony 1.1.5]

    このエントリーをはてなブックマークに追加

メソッドを呼び出す。


call_user_func(array('クラス名',メソッド名'),引数.....)

プロパティを参照する。
クラス定数を参照する。

constant('クラス名::プロパティ');

    このエントリーをはてなブックマークに追加

symfonyにはデフォルトのO/Rマッパーとしてpropelが使用されていますが
一つの行を表すOMクラスとテーブルをあらわすPeerクラスがあります。
カラム名はPeerクラスから取り出します。
クラス名が動的に変わる場合
call_user_func
が便利です。


function get_primary_key($class_name)
{
$columns = call_user_func(array($class_name."Peer","getTableMap"))->getColumns();
$pk = array();
foreach($columns as $column)
{
if($column->isPrimaryKey()) $pk[] = $column->getColumnName();
}
return $pk;
}


[symfony 1.1.5]

    このエントリーをはてなブックマークに追加

symfonyはじめました

2008-11-16 11:37

かなり前後しますが、スタートページ?を書こうと思います。

PHPフレームワークのsymfonyを使おうと思いたちました。
詰まったところ、おさえておきたいところを備忘録として書き留めておくため
このブログをはじめます。
また、ブログを書くことによって、ノウハウの確認と定着が出来るかなと。
よく言いますが「知識はアウトプットしてはじめて自分のものになる」って感じでしょうか。

pluginなども公開できたらいいなあ、と考えておりますがどうなるかわかりません。

そもそもなぜsymfonyを使おうと思ったのかというと
今まで、自分の作るシステムではZend Frameworkの上に、自分でフレームワークもどきなものを少しづつ作って、構築していました。最初にフレームワークを選ぶとき、symfonyも試したのですが、なんか、勝手に出来ちゃうところというか、やらされてる感が嫌でZendにしたのですが、めんどくさいことを自動化していくうちに、ある日「なんか、俺の目指してるのってsymfonyなんじゃないかな」と思い立ちました。あの頃よりPHPのスキルが上がって、ソースをさらさら読めるようになったというのもsymfony嫌いが直った原因かもしれません。よく出来てると思います。気になるのは重さでしょうか?
次のシステムはフルsymfonyで作ります。がんばります。

    このエントリーをはてなブックマークに追加
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。