[CakePHP]read-onlyなモデルを作る

Posted under CakePHP by uechoco on 水曜日 2 2月 2011 at 16 : 26 : 33

アプリを作っていると、複数のDBを使い分けたり、master-slave構成だったりすることは良くありますよね。そういったDB構成の場合、心配なのはread-onlyなテーブルに間違って更新をかけてしまわないかということです。

そこで、とっても簡易ですが、更新してしまわないようにする予防策をしておきます。read-onlyなモデルは、下記AppReadOnlyModelを継承するだけで安心できます(更新系処理のコールバック関数って以下の関数くらいでいいんですよね?)。CakePHP 1.3.7で試しています。

php:
  1. class AppReadOnlyModel extends AppModel
  2. {
  3.     function _read_only_error()
  4.     {
  5.         trigger_error(sprintf('Cannot execute write query to <b>%s:%s</b>.', $this->useDbConfig, $this->name), E_USER_WARNING);
  6.     }
  7.    
  8.     /**
  9.      * save (WRITE系クエリを無効化する)
  10.      *
  11.      * @access public
  12.      */
  13.     final function save()
  14.     {
  15.         $this->_read_only_error();
  16.         return false;
  17.     }
  18.    
  19.     /**
  20.      * saveAll (WRITE系クエリを無効化する)
  21.      *
  22.      * @access public
  23.      */
  24.     final function saveAll()
  25.     {
  26.         $this->_read_only_error();
  27.         return false;
  28.     }
  29.    
  30.     /**
  31.      * saveField (WRITE系クエリを無効化する)
  32.      *
  33.      * @access public
  34.      */
  35.     final function saveField()
  36.     {
  37.         $this->_read_only_error();
  38.         return false;
  39.     }
  40.    
  41.     /**
  42.      * updateAll (WRITE系クエリを無効化する)
  43.      *
  44.      * @access public
  45.      */
  46.     final function updateAll()
  47.     {
  48.         $this->_read_only_error();
  49.         return false;
  50.     }
  51.    
  52.     /**
  53.      * delete (WRITE系クエリを無効化する)
  54.      *
  55.      * @access public
  56.      */
  57.     final function delete()
  58.     {
  59.         $this->_read_only_error();
  60.         return false;
  61.     }
  62.    
  63.     /**
  64.      * deleteAll (WRITE系クエリを無効化する)
  65.      *
  66.      * @access public
  67.      */
  68.     final function deleteAll()
  69.     {
  70.         $this->_read_only_error();
  71.         return false;
  72.     }
  73. }

php4系ならばfinalキーワードを外してください。


[勉強会]CakePHP新春勉強会に参加してきました

Posted under CakePHP by uechoco on 木曜日 20 1月 2011 at 09 : 19 : 28

少し日がたってしまいましたが、2011/01/17(月)に、CakePHP新春勉強会 東京に参加してきました。会場は、二日前にも勉強会でお邪魔させて頂いたジンガジャパンさんです。毎度、会場提供ありがとうございます!今回は20分の発表x2と、LTたくさんという形式でやりました。福岡にもサテライト会場があり、福岡からのLTもあったりしました。

CakePHP 2.0の話はなかなか魅力的でした。互換性を極力意識しつつもphp 5系に特化することで様々な改良点を加えているそうです。リリース予定は未定ですが、期待できそうです。

livlisのCakePHPの話もいいですね。どちらかというとAmazonのクラウドサービスに依存しまくっているインフラ周りのほうが皆さんの興味を惹いていたようです。

福岡のサテライト会場からのLTでは、ZENPREというスライドとUstreamを融合させたプレゼン用のWebサービスを使って行われました。これにはみなさんビックリでしたね。東京会場の方でもBasterCMSやアクション毎にModelをつくる話など、みなさんの知恵がすごく詰まった面白いLTがありました。

会場の都合により時間が限られていたために、LT枠が減らされてしまっていましたが、CakeLiveというオンラインLT大会の告知もあり、そちらで発表してねという流れになりました。

CakePHP関連の勉強会は久しぶりなので楽しかったですね。やっぱりSymfony2の勉強会とはエンジニアの層が違うので、お話しするときも話題がちょっとライトな感じがしました。

当日の詳細は・・・どっかにまとまってるのかな?w


[CakePHP]paginateオプションをモデルで生成する

Posted under CakePHP by uechoco on 木曜日 23 12月 2010 at 00 : 32 : 26

ネットサーフィンしていて、hiromi2424さんのPaginateオプションをモデルに移行する - 24時間CakePHPという記事を見つけて衝動的に記事を書いています。

私も普段CakePHPでpaginateを使用しているときは、モデル側でpaginateオプションを生成しています。この方針は上記記事とは変わりありません。ただ私の場合はpaginateTypeといった指標を持たずに、getPaginateStandard($options, $data)という関数を作って、パラメータを返すようにして、以下のように呼んでいます。

php:
  1. function index(){
  2.         $options = array('limit'=>10, /* 省略 */);
  3.         $this->paginate = $this->Group->getPaginateStandard($options,$this->data);
  4.         $this->set('groups', $this->paginate('Group'));
  5.     }

先程の例と違うのは、

  1. $this->paginateオプションはcontrollerに持たせている
  2. $optionsや$dataを与えることで、関数内でページネーションを拡張できる

$this->paginateオプションは先の例のように完全にモデル側に持たせるのも有りだとは思うのですが、CakePHPの標準的な仕様はController側が先に持っていて、それを処理してModel側に渡しています。出来る限りCakePHPの標準的な仕様に合わせたほうが、プログラムが複雑になっていったときに有利になってくるし、仕様が分かりやすいのではないかと思っています。(もしかしたら他の外部ライブラリと整合性が取れなくなったりするかも知れませんし)

もう1つ、$optionsや$dataを与えているのは、ページネーションの初期値があった上で、POSTやGET、namedなどのデータを元にページネーションを拡張していきたい場合に便利だからです。もちろん、先程の例でもできなくはないのですが、配列できっちり決まっているものを、別のタイミングで後からいじるのは私はあまり好きではなく、最初から関数だったら弄りやすいかなっという感覚の違いだと思います。あと、そもそもページネーションの内容が外的要因でコロコロ変わるプログラムだからという違いなのかも知れません。

ちなみに、なぜ「getPaginateStandard」という名前にしているのか気になるところだと思いますが、これは「(モデル内で)標準的なパラメータを取得する」という意味でこういう名前になっています。実際に作りたいものにも寄るとは思いますが、私が最近つくっているプログラムでは、モデル内にfindXxxYyyZzz()やgetListXxxYyyZzz的な関数をロジック毎に作っていますが、ほとんどのSELECT系関数は

php:
  1. $params = array(/* その関数内での独自の定義 */);
  2. return $this->find('all', $this->getPaginateStandard($params));

みたいな感じで、パラメータを作る部分を1つの関数に集約しています。1回直すと全箇所に影響が出るというふうに思われるかも知れませんが、1つの関数を直せば全箇所に適用可能というメリットのほうを取っています。(わりと複雑な関連付けロジックを様々な箇所に使うので、共通で直せると嬉しいんです)。

今思えば「Paginate」って単語は余計だった(別のものにしたかった)と思っていますが、そのうちリファクタリングでもして直しますかね。


[CakePHP]AppViewクラスの作成

Posted under CakePHP by uechoco on 木曜日 16 12月 2010 at 11 : 33 : 14

AppControllerとかAppModelはよく聞くけど、AppViewはあんまり聞きません。あんまり拡張する必要性がないってのが一番の理由ですが、やっぱり変えたいときはあります。

AppViewは作れるようです。

  1. app.phpをviewsフォルダ直下に作成
  2. AppControllerでViewクラス名を変更

app.phpをviewsフォルダ直下に作成

/.../views/app.php

php:
  1. <?php
  2. class AppView extends View
  3. {
  4. }

AppControllerでViewクラス名を変更

/...//app_controller.php

php:
  1. <?php
  2.  
  3. class AppController extends Controller
  4. {
  5.     /**
  6.      * Viewクラスの定義
  7.      *
  8.      * @var string
  9.      * @access public
  10.      */
  11.     var $view = 'App'; // AppView
  12. }

こんな感じです。意外と簡単でした。


[CakePHP]モデルに振られる

Posted under CakePHP by uechoco on 日曜日 5 12月 2010 at 00 : 31 : 21

この記事は、CakePHP Advent Calendar 2010に参加しています。この記事は5日目です。

こんばんわ。uechocoです。CakePHP歴は3ヶ月くらいのbakerです。Symfonyアドベントカレンダー 2010に参加したノリでCakePHP Advent Calendar 2010にも参加表明してみました。

4日目はremoreさんによるRe: Best Practices in MVC Design with CakePHPでした。英語とか!英語とか!モデルに重点を置いてコードを組むってのは納得ですね。そういえば3日目はshin1x1さんによるModelとの付き合い方でした。CakePHPにおいてモデルは大事な存在です。モデルをどう使うかによって付き合い方も変わってくるわけですよね。

この2つの記事を見て私が思ったのは・・・モデルといい関係でお付き合いをしたいけれど、やっぱりモデルに振られることもあるよねってことです。僕のような初心者はカリスマモデルとお付き合いなんてしてもらえないですかね。そういうわけで、3ヶ月の間に、こんな風なことしたらモデルに振られたけど、こんなカンジで寄りを戻したっていうお話を紹介させていただきます。

モデルとの思い出を全部消そうとしたが断られた(truncate文が発行できない)

当然です。こんなことをしたら振られちゃいますよね。でもやっぱりこちらの都合で思い出を全部消したいことも有りますよね。こっそりモデルの部屋に忍びこんでいつでも消せるようにしとくのがいいと思います。

(モデルにはなぜかtruncateに対応する命令がないんですが、DataSource側にはちゃんとあるようです。AppModelにtruncate()ラッパー関数をつくると、データが抹消できるようになります。)

// AppModel

php:
  1. /**
  2.      * モデルで正しくTRUNCATE(truncate)を行う
  3.      *
  4.      * @return void
  5.      */
  6.     function truncate()
  7.     {
  8.         $db =& ConnectionManager::getDataSource($this->useDbConfig);
  9.         $db->truncate($this);
  10.     }

モデルとのデート中に下手こいたのでリセットしたかったが断られた(トランザクション命令が発行できない)

お付き合いはゲームではないのでリセットなんでできないんです。でもデートはその都度一発勝負なんて草食系の僕には怖いんです。こっそりモデルの部屋に忍び込んで時計の針を戻してやればリセット出来ますかね。

(モデルはトランザクションにも対応していないみたいなんです。でもトランザクション無しって困る場合もあります。DataSource側は対応しているようなのでAppModelにトランザクション用のラッパー関数を作ると、うまく動くようです。)

// AppModel

php:
  1. /**
  2.      * モデルで正しくTRUNCATE(truncate)を行う
  3.      *
  4.      * @return void
  5.      */
  6.     function truncate()
  7.     {
  8.         $db =& ConnectionManager::getDataSource($this->useDbConfig);
  9.         $db->truncate($this);
  10.     }
  11.    
  12.     /**
  13.      * モデルで正しくトランザクション(begin)を行う
  14.      *
  15.      * @return void
  16.      * @link http://d.hatena.ne.jp/bobchin/20080805/1217913768
  17.      */
  18.     function begin()
  19.     {
  20.         $db =& ConnectionManager::getDataSource($this->useDbConfig);
  21.         $db->begin($this);
  22.     }
  23.    
  24.     /**
  25.      * モデルで正しくトランザクション(commit)を行う
  26.      *
  27.      * @return void
  28.      * @link http://d.hatena.ne.jp/bobchin/20080805/1217913768
  29.      */
  30.     function commit()
  31.     {
  32.         $db =& ConnectionManager::getDataSource($this->useDbConfig);
  33.         $db->commit($this);
  34.     }
  35.    
  36.     /**
  37.      * モデルで正しくトランザクション(rollback)を行う
  38.      *
  39.      * @return void
  40.      * @link http://d.hatena.ne.jp/bobchin/20080805/1217913768
  41.      */
  42.     function rollback()
  43.     {
  44.         $db =& ConnectionManager::getDataSource($this->useDbConfig);
  45.         $db->rollback($this);
  46.     }

(これらのメソッドは、CakePHP 1.2 でトランザクション - bobchinの日記を参考にしています。)

モデルとのデートを頭の中で整理しながら思い出させたら何回デートしたか曖昧になってた(paginateでgroup byしたときにcount値が変になる

デートの思い出を振り返っているとき、動物園には1回行って、遊園地には1回行って・・・あれ、ホントに1回だっけ?って言われた時、悲しいですよね。ホントは動物園は3回だし、遊園地は2回だし。こんな時は、うまくフォローしてあげるのもいいお付き合いの秘訣だと思います。

(paginateの時にgroup byが含まれているようなパラメータを与えているとき、paginateCount()がどうも値がずれていることがありました。そんな時はpaginateCount()関数をAppModelにつくってうまく動くようにすれば直ります。)

// AppModel

php:
  1. /**
  2.      * group by に対応した pagenateCount
  3.      *http://d.hatena.ne.jp/aroundthedistance/20090728/1248784179
  4.      *
  5.      * @params array $conditions 検索条件
  6.      * @params integer $recursive 再帰階層数
  7.      * @params array $extra 追加条件
  8.      */
  9.     function paginateCount($conditions = null, $recursive = 0, $extra = array())
  10.     {
  11.         $params = compact('conditions');
  12.         $this->recursive = $recursive;
  13.         $count = $this->find('count', array_merge($params, $extra));
  14.         if (isset($extra['group'])) {
  15.             $count = $this->getAffectedRows();
  16.         }
  17.         return $count;
  18.     }

(このpagenateCount()は、Group Byしている時にpaginator-&gt;number()が表示されない件 - Life is Really Short, Have Your Life!!という記事を参考にしています。記事名のとおり、group byしたときの個数がおかしくなってしまうので、正しい値を取るための工夫になっています。ちなみにpaginateCount()だけじゃなくて、paginate()というメソッドをモデルに作ってもページネーション時にコントローラからそのメソッドが呼ばれるようになります。)

Datumという名前のモデルと付き合ったら不幸な目にあった(Datumモデルをつくると途中でつまずいた

Datumっていう名前のモデルと付き合っていたことがあったんです。最初のうちはうまく付き合えていたんですけど、関係が続いていくうちに、なぜか急に言うことを聞いてくれなくなって、結局そのまま別れてしまいました。

("data"テーブルに対応する、"Datum"モデルを作っていたんです。dataの単数形はdatumなんですよ!とりあえず、find()とか発行できて問題なく動いていたんですが、いろいろといじっていたら、突然動かなくなりました。
Cakeのコードを追っていったらDataSourceの中か忘れましたがリレーション系の処理で実際のテーブル名で処理する場所があったんです。つまり、あるモデルにひもづいたDatumモデルを扱うときに、$Model->data->find()的な感じでアクセスされたんです。でも、Modelって$dataって変数持ってるじゃないですか。
つまり、$data変数と、Datumモデルの$dataインスタンスが混同してしまって動かなくなったんです。さすがにどうしようもなくて、テーブル名もモデル名も変更することで回避しました。)

まとめ

とりあえず、3ヶ月くらいでモデルに振られたり、喧嘩したりした特に思い出深い出来事を上げてみました。でもちゃんとうまく取り計らってモデルとはいいお付き合いを続けています。

なんだかんだいったって、モデルのこと好きなんです。愛してるよーーー\(^o^)/

できれば

モデルみたいにかわいい彼女募集しています\(^o^)/

最後に

だいぶ遊びすぎました^^;;ごめんなさい。俺はこんなふうに振られたぜ!とか、もっといい回避方法があるよ!とかそういう議論とかに発展すると嬉しです。ちなみにCakePHP 1.3系で動作確認しています。さてさて、CakePHP Advent Calendar 2010の6日目はtfmagicianさんです。まだまだモデルの話は続くのかな?楽しみです!


次ページへ »

Copyright © 2012 うえちょこ@ぼろぐ. WP Theme created by Web Top.