Posts tagged CakePHP

CakePHP 1.2 の deleteAll

1

CakePHP 1.2 の saveAll その1
CakePHP 1.2 の saveAll その2
のエントリーを書いているときに deleteAll というメソッドがあり気になったので調べてみました。

CakePHP 1.1 で開発時に delete するときに ID を指定するしか方法がなく、ある条件でまとめて削除したいときなどは findAll してからループで delete していました。このときも条件を指定して削除する方法がないかとソースを調べたのですがありませんでした。

使い方は非常に簡単です。findAll のように条件を指定して deleteAll を実行するだけです。

$conditions = array('User.name'=>'suzuki');
if ($this->User->deleteAll($conditions)) {
    $this->Session->setFlash('削除しました');
} else {
    $this->Session->setFlash('削除に失敗しました');
}

実行される SQL は以下のような感じです。

SELECT `User`.`id` FROM `users` AS `User` WHERE `User`.`name` = 'suzuki'
DELETE `User` FROM `users` AS `User` WHERE `User`.`id` IN (8, 10)

SELECT で条件に合う ID を抽出して WHERE IN で DELETE しています。
ただし CakePHP1.2 の削除ですが、MySQL 4.0 では SQL の DELETE 文でエラーになってしまいます。
MySQL 5.0 ではエラーにならずに削除できました。エラーになる原因は “DELETE” の後ろにテーブルのエイリアス名があるためです。

これは “cakephp1.2でのPostgreSQLエラー。” フォーラム – CakePHP Users in Japan でもあるように既に修正されているようですが、昨日 CakePHP のサイトからダウンロードした CakePHP 1.2.0.6311 beta ではまだ修正されていないようです。

CakePHP 1.2 の saveAll その2

3

CakePHP 1.2 の saveAll その1 では同一モデルへの複数レコードを saveAll で保存しました。今回はアソシエーションのモデルのデータを saveAll で保存する方法です。

アソシエーションのモデルのデータを保存

モデル
user.php

< ?php
class User extends AppModel {
    var $name = 'User';
    var $hasMany = array('Comment');
}
?>

comment.php

< ?php
class Comment extends AppModel {
    var $name = 'Comment';
    var $belongsTo = array('User');
}
?>

コントローラ
users_controller.php

function add() {
    if (!empty($this->data)) {
        $this->cleanUpFields();
        $this->User->create();
        if ($this->User->saveAll($this->data)===false) {
            $this->Session->setFlash('保存に失敗しました);
        } else {
            $this->Session->setFlash('保存しました');
        }
        $this->redirect(array('action'=>'index'), null, true);
    }
}

ビュー
users/add.ctp

< ?php echo $form->create('User');?>
< ?php echo $form->input('User.name');?>
< ?php echo $form->input('Comment.body'); ?>
< ?php echo $form->end('Submit');?>

CakePHP 1.2 の saveAll その1 の同一モデルへの複数レコードの保存よりも使い道は多いのではないかと思います。

追記
このエントリーは CakePHP 1.2.0.6311 beta で検証しています。

CakePHP 1.2 の saveAll その1

1

CakePHP 1.2 から model に saveAll メソッドが追加されました。
同一モデルへの複数データの一括保存やアソシエーションのモデルのデータの保存が saveAll でできます。

同一モデルへの複数データの保存

$data = array(
    array('name'=>'tanaka'),
    array('name'=>'suzuki'),
    array('name'=>'yamada')
);
if ($this->User->saveAll($data)===false) {
    $this->Session->setFlash('保存に失敗しました');
} else {
    $this->Session->setFlash('保存しました');
}

ただβバージョンのためか、saveAll の戻り値が成功時には NULL 失敗したときには false が返って来ます。
そのために
If ($this->User->saveAll($data)) {
とやると成功の判定が正しく行えません。

CSV を読み込んでデータをインポートするようなときにいいかもしれません。

追記
このエントリーは CakePHP 1.2.0.6311 beta で検証しています。

CakePHP 携帯専用サイトを作成する

6

CakePHP で携帯用のページを作成する際に CakePHP 携帯用ビューを表示する | Shin x blog のページが大変参考になります。

しかし、/m/ のような URL ではなく携帯専用サイトにしたかったので下記のような方法で実装しました。なお、PC でアクセスしたときには /pc.html という静的なページを表示するようにしてあります。
また、PC、携帯の振り分けはユーザエージェントで行っています。

携帯用コンポーネント

app/controller/component/mobile.php を作成し、PEAR の Net_UserAgent_Mobile を使用しています。このコンポーネントでユーザエージェントを判定して PC だったら pc.html へリダイレクトします。

PEAR の Net_UserAgent_Mobile はサーバにインストールしてもいいのですが、今回は app/vendors/ ディレクトリに入れました。また、app/vendors/ に include_path を通すために、CakePHPガイドブック を参考に include_path_vendors.php を作成しました。

app/controller/component/mobile.php

vendor("include_path_vendors");
vendor("Net/UserAgent/Mobile");
class MobileComponent extends Object {
    function startup(&$controller) {
        $this->controller = $controller;
        $mobile = &Net_UserAgent_Mobile::factory();
        if ($mobile->isNonMobile()) {
            $this->controller->redirect("/pc.html");
        }
    }
}

今回は簡単に PC か携帯でアクセス振り分けているだけですが、画面の大きさやキャリアなどによって色々な処理の振り分けが考えられます。

携帯用ヘルパー

app/views/helper/mobile.php を作成して HTML 出力時に文字コードを Shift-JIS へ変換します。ヘルパーの afterRender メソッドを使用しています。

class MobileHelper extends Helper {
    function afterRender() {
        $out = ob_get_clean();
        $out = mb_convert_kana($out, "rak", "UTF-8");
        $out = mb_convert_encoding($out, "SJIS", "UTF-8");
        ob_start();
        echo $out;
    }
}

コントローラで携帯用コンポーネント、ヘルパーを使用する

app/app_controller.php で携帯用コンポーネントと携帯用ヘルパーを使用します。

var $components = array('Mobile');
var $helpers = array('Mobile');

app_controller.php で設定しておけば全てのコントローラで共通に読み込むので各コントローラにその都度書く必要がなくなります。DB の管理画面など PC 用のコントローラが必要な場合は各コントローラに書いた方がいいでしょう。今回は完全に携帯用にしています。PC 用の管理画面もあるのですが、サブドメインを変えて app ディレクトリも違うものを使用しています。

また、各コントローラ内で設定するタイトルの文字コードを変換しないといけないので、app_controller.php の beforeRenderメソッドでタイトルの文字コードを変換します。

function beforeRender() {
    $this->pageTitle = mb_convert_encoding($this->pageTitle, "SJIS", "UTF-8");
    parent::beforeRender();
}

以上で携帯専用のサイトが CakePHP で作成できました。

コントローラの afterFilter で文字コードを変換する方法

コントローラの afterFilter で文字コードを変換することもできます。

app/controller.php

function afterFilter() {
    parent::afterFilter();
    $out = ob_get_clean();
    $out = mb_convert_kana($out, "rak", "UTF-8");
    $out = mb_convert_encoding($out, "SJIS", "UTF-8");
    ob_start();
    echo $out;
}

この場合は、beforeRender でのタイトルの文字コード変換の処理が必要ないのと、携帯用ヘルパーは必要ありません。
PC 用のコントローラも作りたいときは携帯用コンポーネント、携帯用ヘルパーを使用する方法がよいかと思います。

CakePHP アクションでエレメントを出力する方法

2

Rendering elements from controllers – cakebaker

上記エントリでエレメントのみを出力する方法が紹介されています。

バージョン1.2 の場合

app/views/elements/controller/hoge.thtml のエレメントを出力する場合
$this->render(DS.'elements'.DS.'controller'.DS.'hoge');

バージョン1.1 の場合

app/views/elements/controller/hoge.thtml のエレメントを出力する場合
$this->render(null, null, ELEMENTS.'controller'.DS.'hoge.thtml');

注意点

レイアウトは指定しないと default になります。Ajax などでエレメントのみを出力したい場合はレイアウトに ‘ajax’ を指定してhead タグや body タグが出力されないようにする必要があります。

v1.2$this->render(DS.'elements'.DS.'controller'.DS.'hoge', 'ajax');
v1.1$this->render(null, 'ajax', ELEMENTS."controller".DS."hoge.thtml");

CakePHP JQuery ヘルパー

3

JQuery helper for CakePHP ( PQuery port ) at NGCoders
CakePHP から JQuery を簡単に使うことができます。

正確には PQuery ヘルパーかもしれません。PQuery は JQuery を PHP から簡単に使用するライブラリで PQuery ヘルパーと同じ開発者が開発しています。
参考:PQuery – PHP and JQuery at NGCoders

インストール

JQuery helper for CakePHP ( PQuery port ) at NGCoders から JQuery ヘルパーをダウンロードし、解凍した pquery.php を /app/views/helpers にコピーします。
そのほかに jquery.js ファイルも必要になります。こちらもダウンロードして /app/webroot/js にコピーします。

コントローラ

Pquery ヘルパーと Javascritp ヘルパーを使用します
var $helpers = array('Pquery', 'Javascript');

ビュー

jquery.js の読み込み
<?php echo $javascript->link('jquery.js'); ?>

使用例

トグルボタン

<div id='msg'>Message...</div>
<?php echo $pquery->link_to_function('toggle', $pquery->toggle('#msg'));?>

フォームで送信された内容により HTML を更新
入力したテキストを /controller/action/ に GET で送信し、id=idtoupdate に受け取った HTML を表示する

<?php echo $pquery->form_remote_tag(array('url'=>'/controller/action/','update'=>'#idtoupdate'));?>
<input type='text' name='field' />
<input type='submit' />

<div id='idtoupdate'></div>

pquery.php を見ると色々なメソッドがあります。使い方はソースを見ればすぐに分かると思います。

CakePHP Security コンポーネントのまとめ

1

CakePHP の Security コンポーネント の動作を調べたのでまとめておきます。
この Security コンポーネントをうまく使用すればクロスサイトリクエストフォージェリ(CSRF) を防ぐことができるでしょう。

トークンの使用

フォームにワンタイムトークンを実装する方法です。

コントローラの beforeFilter メソッドでトークンをチェックするアクションを指定
function beforeFilter() {
    $this->Security->requireAuth('login');
}
ビューのフォーム内にトークンを設定

<?php echo $html->formTag(); ?>
トークンが hidden 属性で生成される

トークンが一致しない場合

requireAuth で指定したアクションに POST でアクセスがあるとセションに保存したトークンとフォームから送られてきたトークンが一致するかチェックします。またそのほかにトークンの有効期間もチェックします。有効期間は CAKE_SECURITY の設定により違います。

CAKE_SECURITY 有効期間
high 10分
medium 100分
low 300分

トークンが一致しないと SecurityComponent の blackHole メソッドが実行されます。このメソッドでは
header('HTTP/1.0 404 Not Found');
を出力して exit します。(画面は空白)

任意の処理を実行したい場合は blackHoleCallback でコールバック関数を指定します。
設定できるのは同じコントローラ内のアクションのみになります。

function beforeFilter() {
    $this->Security->blackHoleCallback = "securityError";
    $this->Security->requireAuth('login');
}

function securityError() {
	die("security error!");
}
トークンチェックをするアクションを複数指定するときはカンマでつなげる

$this->requireAuth('login', 'delete');

特定のコントローラからのポストのみ許可する

$this->Security->allowedControllers = array("users");
これを指定するとたとえトークンが一致しても許可のないコントローラからのポストの場合は blackHole メソッドを実行します。

特定のアクションからのポストのみ許可する

$this->Security->allowedActions = array("action");
これを指定するとトークンが一致しても許可のないアクションからのポストの場合は blackHole メソッドを実行します。

ポストのみ受け付けるようにする

$this->requirePost('login');
これはトークンとは違い、POST のみ許可して GET でのアクセスを不可にします。
/users/login/ のような URL でのアクセスも GET なので不可になります。
POST の処理だけを実行したいようなアクションに使用します。

CakePHP Textヘルパーの truncate を全角文字に対応させてみた

5

CakePHP の Text ヘルパーに truncate という指定された文字列を任意の長さに省略するメソッドがあります。
しかし、このメソッドは全角文字を考慮していないため全角文字に用いると文字化けすることがあります。
そこでこのメソッドを全角文字に対応させてみました。

function truncate($text, $length, $ending = '…', $exact = true) {
    if (strlen($text) < = $length) {
        return $text;
    } else {
        mb_internal_encoding("UTF-8");
        if (mb_strlen($text) > $length) {
            $length -= mb_strlen($ending);
            if (!$exact) {
            $text = preg_replace('/\s+?(\S+)?$/', '', mb_substr($text, 0, $length+1));
            }
            return mb_substr($text, 0, $length).$ending;
        } else {
            return $text;
        }
    }
}

直接 Text ヘルパーを修正するとバージョンアップなどのときに困るので、
cake/libs/view/helpers/text.php を app/views/helpers/mb_text.php にコピーしクラス名を
class MbTextHelper extends Helper{
として、truncate メソッドを上記のように修正しました。

使用するときはコントローラ内で、
var $helpers = array("MbText");
view で
echo $mbText->truncate("あいうえおかきくけこ", 5, "…", true);
とすると
あいうえ…
と表示されます。

CakePHP find や findAll の条件指定にちょっとだけ便利な postConditions

1

CakePHP の コントローラに postConditions というちょっとだけ便利なメソッドがありました。

cake/libs/controllers.php

function postConditions($data) {
    if (!is_array($data) || empty($data)) {
        return null;
    }
    $conditions = array();

    foreach($data as $model => $fields) {
        foreach($fields as $field => $value) {
            $conditions[$model . '.' . $field] = $value;
        }
    }
    return $conditions;
}

例えば検索をするフォームなどで User.name で検索するような場合

view で
input('User/name');
と指定すると$this->data は

Array
(
    [User] => Array
        (
            [name] => 山田
        )
)

この $this->data を postConditions に渡すと
$data = $this->postConditions($this->data);
$data は

Array
(
    [User.name] => 山田
)

となり、このまま find や findAll に渡すことができます。
$this->User->find($data);

参考:CakePHP マニュアル
7.2. コントローラの関数

11月に読んだ本

2

最近は仕事関係の本や自己啓発関係の本が多い。特に今年は小説を読んでないので、今月は少し小説を読もうと思っています。

決算書がスラスラわかる 財務3表一体理解法 (朝日新書 44) (朝日新書 44)

決算書がスラスラわかる 財務3表一体理解法 (朝日新書 44) (朝日新書 44)

あたりまえだけどなかなかできない仕事のルール (アスカビジネス)

あたりまえだけどなかなかできない仕事のルール (アスカビジネス)

ウェブ時代をゆく ─いかに働き、いかに学ぶか (ちくま新書 687)

ウェブ時代をゆく ─いかに働き、いかに学ぶか (ちくま新書 687)

なぜ、エグゼクティブはゴルフをするのか?

なぜ、エグゼクティブはゴルフをするのか?

ちょいデキ! (文春新書 591)

ちょいデキ! (文春新書 591)

すごい「実行力」

すごい「実行力」

なぜ、エグゼクティブはゴルフをするのか?

なぜ、エグゼクティブはゴルフをするのか?

仕事に活かす!本200%活用ブック

仕事に活かす!本200%活用ブック

WEB+DB PRESS Vol.41

WEB+DB PRESS Vol.41

お金は銀行に預けるな 金融リテラシーの基本と実践 (光文社新書)

お金は銀行に預けるな   金融リテラシーの基本と実践 (光文社新書)

プログラミングPHP 第2版

プログラミングPHP 第2版

CakePHPガイドブック

CakePHPガイドブック

忙しいパパのための子育てハッピーアドバイス

忙しいパパのための子育てハッピーアドバイス

ケータイ業界52人が語る「戦略」の裏側

ケータイ業界52人が語る「戦略」の裏側

カリスマ・コンサルタントの稼ぐ超思考法 ~仕事と人生に効く「問題解決力」が身につく20の方法~

カリスマ・コンサルタントの稼ぐ超思考法 ~仕事と人生に効く「問題解決力」が身につく20の方法~

自転車生活の愉しみ

自転車生活の愉しみ

Go to Top