2009年04月29日

冴えたページングテクニック

ただものではないmarianoのページングテクニックが公開されています。CakePHPユーザなら一度は見るべき。

marianoiglesias
Pagination with custom find types in CakePHP


protected $_types = array(
'published' => array(
'conditions' => array('Post.published' => 1),
'order' => array('Post.created' => 'desc')
)
);

とするだけで、


$posts = $this->Post->find('published');

と、

$this->paginate['Post'] = array(
'published',
'limit' => 10
);

ができてしまうという、超絶テクニックを公開しています。

タグ:cakephp
posted by SDozono at 21:58| 東京 ☀| Comment(0) | TrackBack(0) | CakePHP Model | このブログの読者になる | 更新情報をチェックする

2008年10月24日

MySQLの関数を利用

'last_login' => '!-NOW()'
という書き方は使えなくなって、
$conditions = array(
'FooModel.foo_important_type = NOW()'
);
にしたらダイジョウブらしい。

save()の場合には、
if ($this->Post->save($this->data)) {
$this->Post->updateAll(
array('Post.title' => 'Now()'),
array("Post.id" => $this->Post->getLastInsertID())
);
みたいにしたらNow()が使えます。これでいいんだろうか…。
タグ:cakephp
posted by SDozono at 16:14| 東京 ☔| Comment(0) | TrackBack(0) | CakePHP Model | このブログの読者になる | 更新情報をチェックする

2008年06月25日

Behaviors

今週、日本ではCakePHP勉強会第三回が開催されるそうです。どんな発表があるか、楽しみです。

MVCにそれぞれ対応した、Behavior(ビヘイビア), Helper(ヘルパー), Component(コンポーネント)は、CakePHP用語では“便利ライブラリ”を部品化したモジュールで、プラガブルに使うことのできる便利なものです。そして、bakery.cakephp.org には、じわじわと使えるライブラリとして情報があつまってきています。

Helper や Component はその性質からして、「後から使うかもしれないから、一応見ておこう」的な要素が強いのですが、Behaviorに関しては、モデルに直接、働きかけるだけあって、アプリの動作そのものに影響を与えるものが多く、使うことを真剣に考える機会もより多いのではないでしょうか。

Soft Deletable Behavior
By Mariano Iglesias (mariano)
http://bakery.cakephp.org/articles/view/soft-delete-behavior
このビヘイビアを入れてやると、delete() の挙動が論理削除になります。ただそれだけですが、かなり便利だと思います。
既存コードをさわらずにアプリの動作自体が変更できるのは、まさにフレームワークならではだと思います。

AliasBehavior
By Matthew Harris (kuja)
http://bakery.cakephp.org/articles/view/aliasbehavior
これもある意味、すごいビヘイビアなのではないかと。
擬似的にDBフィールドの別名を付けることができてしまいます。
これを使えば、WordpressでもXoopsでもOSCommerceでも、既存のDBをそのまま使ってCakePHPアプリをかぶせてしまえるのではないでしょうか。

話題変更。
jQueryで実現している下記のテーブルもなかなかすごいと思います。
decorateを1.2対応で作るなら、これを使いたい。
http://www.webplicity.net/flexigrid/
CakePHPで使う、という話題はあるだろうか、と調べたところ、こちらですでに議論されていました。ただしポルトガル語ですが。
http://groups.google.com/group/cake-php-pt/browse_thread/thread/262675845011a4bc
posted by SDozono at 14:12| 東京 ☁| Comment(0) | TrackBack(0) | CakePHP Model | このブログの読者になる | 更新情報をチェックする

2008年05月21日

検索データの文字コード変換

1.2からは、“直接findされているのか、アソシエーションで芋づるfindされているのか”をafterFindの第二引数でチェックできます。なので、app_model内にこんなコードを書くと、アソシエーションでひっぱってきたデータまで一括でデータの文字コードを変換できます。(文字コードを2回変換しないようにしています。)


function esc2utf8(&$val,$key) {
$val = mb_convert_encoding($val,"UTF-8","EUCJP-win");
}

function afterFind($results, $primary){
if($primary){
array_walk_recursive($results,array($this,'esc2utf8'));
}
return $results;
}


1.1では一工夫しないといけなかったのが、1.2では簡単にできるようになっていました。
タグ:cakephp Model
posted by SDozono at 12:33| 東京 ☁| Comment(0) | TrackBack(0) | CakePHP Model | このブログの読者になる | 更新情報をチェックする

2006年09月01日

Single Table Inheritance を CakePHP で実現するには

CakePHP で 単一テーブル継承(Single Table Inheritance)を実現する方法(力技?)

無料会員と有料会員の違いのように、"似たような"データがある場合にどうやってそれを整理してゆくか。

Rails では単一テーブル継承をサポートしていて考え方としては、データテーブルは一つで、そこにいくつかの種類のモデルクラスがくっついてゆくようなイメージ。

事例:http://www.interq.or.jp/engineer/ishikura/sb/log/eid46.html
など。

例えば、Digg というサイトでは、ほぼ同じ内容のニュース項目が、“発表された”表ページと、“まだ日の目を見ていない補欠ニュース”の裏ページに分類されている。評価が高くなると表ページに移動する。

クラスを一つにして、パラメータを切り替えるというアイデアも悪くはないが、

$発表されたニュース->findAll();
$未発表ニュース->findall();

と書きたい、というアイデアもある。これをやりすぎるとどんどん複雑になってゆくので適当なところでやめたほうがよいと思うが、"CakePHPでもこれができる"ようだ、ということを知っておくのはよいと思う。


Single Table Inheritance の効用書き。

CakePHP で実現する方法。

フレームワークで想定している書き方ではないように思うので、スマートな解決方法ではないと思いますが、知識としては知っておいてよいと思います。CakePHP 2.0 ではこのあたり、どうなるんでしょうか……。





posted by SDozono at 12:18| ロサンゼルス 🌁| Comment(0) | TrackBack(1) | CakePHP Model | このブログの読者になる | 更新情報をチェックする

2006年07月22日

タグ機能のあるWEBアプリを最小限のコードで製作する方法

もし、タグ付けのできるサイトを製作するのであれば、CakePHP の Association を使いこなしてみましょう。

例えば、
・users テーブル(ユーザの基本データ)
・profiles テーブル(ユーザの詳細情報)
・posts テーブル(投稿データ)
・tags テーブル(タグの種類)
・posts_tags テーブル(投稿データとタグの関連情報)

があったとします。CakePHP の model にきちんとこの関連情報を設定すると、

【パターンその1】
・$this->User->findById(ユーザID)で、users と profiles の両方の情報が取ってこれます。

・$this->Profile->findByID(ユーザID)で、users と profiles の両方の情報が取ってこれます。

※つまり、親子関係のあるテーブルのどちらからでも両方まとめて情報を取ってこれますし、細かい条件を指定できます。

また、

【パターンその2】
・$this->User->findById(ユーザID)で、users と profiles の両方の情報、またすべての投稿データも取ってこれます。

・$this->Post->findById(ID)で、投稿したデータと、それについているタグデータをすべて取ってこれます。

※これに関しても細かく条件が設定されているので、例えば、“承認された投稿(コメント)のみ出力”とか、“時間軸で並べ替えて出力”といったこともできます。

【パターンその1】と【パターンその2】で違うところは、“1対1の関係”か、“1対多の関係”かというところです。

【パターンその1】用として、
・hasOne    (親から子を見る)
・belongsTo  (子から親を見る)

【パターンその2】用として、
・hasMany   (親から子どもたちを見る)
・hasAndBelongsToMany[HABTMとも言う。](二つのテーブルが、もう一つのテーブルで関連づけられている状況をまとめあげる)

があります。

posted by SDozono at 12:06| ロサンゼルス 🌁| Comment(0) | TrackBack(0) | CakePHP Model | このブログの読者になる | 更新情報をチェックする

2006年07月21日

データベーステーブル間の関連付け/ORM/オブジェクト・リレーショナル・マッピング/Table Association

Excel であれば、1枚の上にすべての情報を載せてゆきます。でも、データベースの場合には、効率的に管理するために“正規化”という作業を行ないます。すると少ない労力で大量のデータを管理できるようになりますが、副作用としてテーブルの数が増えてしまいます。ですので、実際のデータベース・アプリケーションを作成する場合には、この部分をどうやって効率的に管理するかが一つのポイントになります。

正規化したために数の増えたテーブル管理を、フレームワークが面倒を見てくれたっていいはずです。

というわけで、今日は、ORM(オブジェクト・リレーショナル・マッピング)についてです。これは、“関連し合っている複数データベーステーブルを、まるで1枚の Excel の表を扱っているように簡単に扱うための手法”と言ってもよいかもしれません。

これまでの単純PHPプログラミングで正規化したテーブルを扱う場合には、
1)複数回(テーブルの数だけ)、Query を呼ぶ!
2)SQL の"JOIN"をうまく使う。
という方法がありました。ただし、問題点として、1)の場合にはもちろんコード(作業)量が増えます。また、2)の場合には、JOIN であちこちをつなげてしまうと、データベースの種類によっては、“データベースサーバを複数に分けて運用することが難しくなる”場合がありました。

このあたりを、フレームワークがうまく働いてくれればよいのですが、現状としてはこんな具合でした。(最新状況が変わったり、記述が間違っていたりしたらコメントしてください。)

・Ruby on Rails(言語:Ruby) …… ORM(テーブル間の関連付け) をサポート。
(日本語情報としては、http://www.goodpic.com/mt/archives2/2005/10/railsdbor.html など。)
・Ethna        …… データベース関連は Ethna_DB_PEAR,Ethna_DB_PDO など。関連付けなどはない?
http://dozo.matrix.jp/pear/index.php?Framework%2FEthna%2FPDO
・Maple        …… BaseDAO(超α版) 関連付けのできるモジュールを作った方はいるようです。正式サポートではない?
 (http://www.wikihouse.com/hacking/index.php?%A5%DE%A5%C3%A5%D4%A5%F3%A5%B0%A5%B3%A5%F3%A5%DD%A1%BC%A5%CD%A5%F3%A5%C8BaseDAO
・Code Igniter     …… ORM があったらいいなぁという議論はあるが、将来的にもライブラリの1ファイル程度になりそう。
http://www.codeigniter.com/forums/viewthread/569/
・Symfony     …… 自前でサポートするのではなく、propel/creole という別のOSSプロジェクトの成果を組み込む。xml か yml で記述する必要がある。
http://www.symfony-project.com/content/book/page/model.html
・PHP on Trax     …… ORM を自前でサポート。ちょっと意外。ただし、ActiveRecord のファイルが 96.5KBもあるのは大きすぎ?
http://svn.phpontrax.com/wiki/ActiveRecordTableAssociationUsage
・ちなみに Django(言語:python)…… ORM のサポートあり。
http://www-128.ibm.com/developerworks/linux/library/l-django/index.html など。)

というわけで、CakePHP の場合には?
・CakePHP …… ORM を自前でサポート。PHP4用モジュール 40KB PHP5用モジュール 39KB。
http://manual.cakephp.org/chapter/6 の Section 4 "Associations" を参照。)

と、きっちりサポートされています。
posted by SDozono at 15:50| ロサンゼルス ☁| Comment(0) | TrackBack(0) | CakePHP Model | このブログの読者になる | 更新情報をチェックする

2006年07月16日

form と $data の連携メカニズム

通常、素のPHP で入力フォームを作る際、名前などは任意に付けてしまいます。そして受ける側で、それを読み取り、データベースに入れる、という動作をします。

例えば:

1)入力フォーム:<input name="name" >

2)入力フォームから受ける.php: $name = $_POST['name'];

そして、3)サニタイズ+4)(データの加工)+5)データの検証+6)データベースへ、という形になります。

問題として、

★1)の名前がどんなものでもよい。ルール(規則)が何もない。⇒ページごとに、または開発者ごとに名前の付け方がばらばらになる可能性がある。
★5)で失敗した場合にどうやって1)まで戻すか〔他の正しいデータフィールドをどうするか〕という問題が生じる。

などが往々にして生じます。これまではみんなそれで苦労しました。ところが、CakePHP には、そのあたりを解決する手法が組み込まれています。

★基本となるのは、
--------------------------------------------------------------
・フォームからのデータは、名前を“data['テーブル名']['フィールド名']”として送信すると、自動的に変数$dataに格納してくれる。
・Model の save メソッドは、$変数名['テーブル名']['フィールド名'] = "値"というデータを一括格納できる。
--------------------------------------------------------------
というルールです。

例えば、users テーブルの中に、name フィールドがあるとします。入力フォーム(ビュー)の中に、
--------------------------------------------------------------
echo $html->input('User/name', array('id' => 'user_name', 'size' => '40', 'value' => $users['User']['name'], ))
--------------------------------------------------------------
と書いておくと、実際の HTML には、
--------------------------------------------------------------
<input name="data[User][name]" id="user_name" size="40" value="" type="text" />
--------------------------------------------------------------
と展開されます。

ここにデータが入ってきた場合、コントローラ側では、

empty($this->User) …… データが入力されていなければ True.

という関数でデータが入ってきたかどうかを調べ、もしデータが入力されているようであれば、

$this->User->save($this->data['User'])

という一文で、“usersテーブルへの1行分のデータをすべて”、保存することができます。これを理解していると、保存前にデータを加工したり、独自のデータを計算・追加することなども可能になります。

さて、データバリデーションはどういう仕組みになっているんでしょうか……。このあたりもかなり考え抜かれています。次回へ続く。


posted by SDozono at 11:11| ロサンゼルス ☀| Comment(0) | TrackBack(0) | CakePHP Model | このブログの読者になる | 更新情報をチェックする

2006年07月07日

Blogチュートリでつまづいたら

CakePHP のサイトに行ったら、まずは Blogの作り方、というチュートリアルを見ると思います。
http://wiki.cakephp.org/tutorials:blog_tutorial_-_1

※注:現在は、http://manual.cakephp.org/chapter/20 が最新版のようです。※

しかし、この記事、「マジックを見せるけど、たねあかしはしない」ようなところがあるので、「こんなに簡単に作成できるのか〜」と思わせるのと同時に、「どうしてこう動くようになるのか、よく分からなかった」と感じてしまう人も多いようです。

例えば、最初の部分で、モデルを作るには、

class Post extends AppModel
{
  var $name = 'Post';
}


とすればよいとあります。これを見て、「クラスを作ったのに、中身が変数一個なんておかしい」と思う人も結構いるわけです。
実は、「実際のプログラムでは、ここにどんどん自分のコードを追加してゆくことになる」んですが、何も追加しなくても、「既に機能が追加されているので、ある程度のことはできてしまう」というのが事実です。

"extends AppModel"というのがミソで、AppModel という基本クラスの持っている性質をすべて受け継いでいます。
ですから、すでに下記のような関数が使えるようになっています。
(Model に関するAPIの解説は、http://api.cakephp.org/class_model.html にあります。)

$this->Post->findAll(...); //すべてのデータを取得
$this->Post->find(...); //条件で検索
$this->Post->findAllByフィールド名(..); // 例えば、name というデータベースフィールドであれば、 $this->findAllByName('山・太郎'); で検索できる。
$this->Post->findNeighbours(...); // その条件のお隣さんを見つけてくる(次へ>などの表村用)
$this->Post->field(...); //あるフィールドの情報だけを取得
$this->Post->findCount(...); //件数を取得
$this->Post->generateList(...); //検索データを配列で受け取る
$this->Post->read(...); //あるIDのデータをフィールド情報+データで受け取る

などなどがあります。これだけでも結構なことができてしまいますね。
でも、自分の欲しいメソッドを作成したり、バリデーション(データが正しくなければ警告する・保存するためのルール)を追加したりしたい場合には、Post クラスの中にいろいろと書き込むことになります。

例えば、


class Post extends AppModel
{
  var $name = 'Post';

  function mymethod(){
   //ここに自分の扱いたい内容
  }
}

とすれば、 $this->Post->mymethod() という関数の誕生です。

ブログチュートリアルでもう一つ説明がないのは、コントローラのメソッドに対して、$id がどこからやってくるか、という説明です。
例えば、app/controllers/posts_controller.php にこう書けとあります。

class PostsController extends AppController
{
  function view($id)
  {
    $this->Post->id = $id;
    $this->set( 'data', $this->Post->read() );
  }
}

ここで、なぜか $id という変数がどこからともなく現れているのですが、これはどうなっているんでしょうか。
これを知るには、CakePHP の mod_url の使い方を学ぶことが必要です。
簡単に説明すると、URLは次のようなルールで指定します。

http:// Cakeのパス/コントローラ名/アクション名/変数1/変数2/変数3/...

もし、「該当するURLで村されるファイルが存在しない(=Cakeへの指令だ!)」という時には、CakePHP が自動的にコントローラの該当する指令に振り分け、変数をはめこんで実行する、という流れになります。ですから、

http:// cakeのパス/samples/save/1/2/3/4

とした場合、CakePHP は、「sample コントローラーの save メソッドを実行します。データとしては、1,2,3,4を入れます」
ということになります。

それを

class SamplesController extends AppController
{
  function save($id1,$id2,$id3,$id4)
  {
    echo "このメソッドが実行されます!データは、".$id1."と".$id2."と".$id3."と".$id4."です。";

  }
}

などのようにして、コントローラは受けることができます。
こんな説明でいかがでしょうか。少し分かりやすくなったでしょうか……?
次回は、set 命令の秘密に迫ってみたいと思います。
posted by SDozono at 13:07| ロサンゼルス ☀| Comment(1) | TrackBack(0) | CakePHP Model | このブログの読者になる | 更新情報をチェックする