異なるDB間をモデル内でリレーション定義→利用不可

■環境 
 CakePHP 1.3.x 

■やりたいこと
 モデル内で異なるDB間(複数のDB)をリレーションし、
 それぞれのDBのカラムを出力する関数を定義する。
 
■結論
 普通の仕組みではムリ。ハックすればできる(らしい)。
 
■現状の整理
 異なるDB間のリレーション自体ができないわけではない。
 
 異なるDB間のモデルの紐付け(hasManyとか) → ○
 異なるDB間のモデル紐付け後にmodelsに関数作成 → ×(unknown columnと言われる)
 異なるDB間のモデル紐付け後にcontrollersに関数作成 → ○(model以外なら実行可)

■自分が取った対策
 modelsで関数定義はあきらめて、component化した。
 (ファットモデルはあらきめた) 

 【app/config/database.phpの設定】 
  こんな感じで設定 → CakePHP で複数のデータベースを使用する - mallowlabsの備忘録
 
 【app/models/読み込むモデルの設定】
  同上
 
 【app/controllers/components/読み込むコンポーネントの設定】
  ここを参考に異なるDBのカラムを出力する関数を作成
  第12回 コンポーネントの作り方:CakePHPで高速Webアプリ開発|gihyo.jp … 技術評論社 
  
 
 【app/controllers/コントローラの設定】 

  冒頭でこんな感じで呼び出し

  var $useDbConfig = 'userdb';
  var $uses = array('DB1のモデル', 'DB2のモデル';
  var $components = array('DB1とDB2両方を参照する関数を書いたコンポーネント');
 
  そのあと、コンポーネントを呼び出す。
 
■参考資料
 本件についての議論など(ハックすればできるよ的な内容含む)。
 ※日本語での内容は見当たらず
 CakePHP - querying across models with multiple databases and useDbConfig
 cakephp - Association and model data saving problem - Stack Overflow
 Multiple Databases Model Relationship - CakePHP | Google Groups 
 
■補足
 自分がCakePHPの仕組みをあまり分かっていない可能性もあり、
 そもそも、そういう動きをさせない(こういう疑問が出ないのが普通?)のが
 普通かもしれません。

画像アップロード関連メモ

以下、完全に自分用メモ

■CakePHPで画像アップロードした場合の中身
 参照ボタンで画像を選択してアップロード後に debug($this->data) すると、

    [image] => Array
        (
            [name] => Bonsai.gif
            [type] => image/gif
            [tmp_name] => /Applications/MAMP/tmp/php/phpCXMUmf
            [error] => 0
            [size] => 2105
        )

 [tmp_name]が画像を一時的に設置するテンポラリフォルダの場所 になっている。
 画像アップ後は move_uploaded_file で移動させる処理を行う。

■テンポラリフォルダの取得方法
 テンポラリフォルダは2つある。
 1)php.ini で upload_tmp_dir にパスを記載している場合
   → var_dump(ini_get(‘upload_tmp_dir’));
     でパスを取得できる
     ※記述がない場合は no value となりbool(false)が返り
      システムのテンポラリフォルダが自動的に利用される。
 2)システムのテンポラリフォルダ
   →  echo sys_get_temp_dir();
     でシステムが利用するテンポラリフォルダのパスを取得できる

■URLを指定して画像を取得

 $author_img_url = 'http://images.apple.com/iphone/home/images/a6_hero.png';
 $tmp_author_img_data = file_get_contents($author_img_url);
 file_put_contents('./test-aaa.jpg', $tmp_author_img_data); 

■参考URL
 PHP: sys_get_temp_dir – Manual
 [PHP]画像をダウンロードしてサーバーの特定のディレクトリに保存する « Codaholic

CakePHP x さくら x サブドメイン で Internal Server Error

下記構成で、

 CakePHP 1.3.x
 さくらインターネット
 サブドメイン
 
Internal Server Error が出た。
 
 
↓この設定通りにやったら一発で解決(ありがとうございます)

 [CakePHP] さくら で「500 Internal Server Error」 Web Sytem | AIDREAM

他のサイトにも設定方法が載ってたが、
上記サイトの設定でしか動かなかった。

CakePHP Auth でパスワード変更フォームの暗号化周り(暗号化されずに登録される場合など)

Authコンポーネントでパスワード変更フォーム作ったら
保存した時に暗号化されなかったり、暗号化された
パスワードが入力欄に入ったりしたので備忘録。

■現象
【1つ目】
 Authコンポーネント使用後、パスワード変更フォームで
 パスワード入力して保存すると、DB側にはパスワードが
 裸のまま保存される(「aaa」と入力したらそのまま保存)。

 ※ユーザー新規登録画面からユーザー名と
  パスワードを入力した場合はちゃんと暗号化(ハッシュ化)
  されて保存される。

【2つ目】
 パスワード入力欄のvalueが暗号化されたパスワードが
 入ってるため、そのまま保存するとハッシュ化後の
 パスワードが二重に暗号化して保存されてしまう。

■原因
【1つ目】 
 パスワード変更時は暗号化されないっぽい。

【2つ目】
 DB保存のパスワードが暗号化後の値のため、
 当然そのまま引っ張ってきてしまっている。
 (ちなみに暗号化を戻す復号化して表示みたいなのはできない)

■対策
【1つ目】
 パスワード変更時に$this->Auth->hashPasswords($this->data)で
 丸ごと暗号化をかけてDB保存する。

【2つ目】
 パスワード入力欄をデフォ空白にしておき、
 「新しいパスワード」みたいなフォームにする。
 空白だったらパスワード変更なし、
 空白以外だったらパスワード変更あり とする。
 (但し、そうすると今度はバリデーションに引っかかるが、
  ここでは設けない)

■対策後のソース(UsersController) RESOLUTION Source
    function edit() {
        $id = $this->Auth->User(‘id’);

        if (!$id && empty($this->data)) {
            $this->Session->setFlash(‘ユーザーが見つかりません’, ‘flash_message’, array(‘result’ => ‘error’));
            $this->redirect(array(‘action’ => ‘edit’));
        }

        if ($id && !empty($this->data)) {
            // パスワード入力欄が空白だったら配列削除  delete array if empty the password input form
            if (empty($this->data[‘User’][‘password’])) {
                unset($this->data[‘User’][‘password’]);
            }

            // userのidとusernameを定義しないと暗号化されない hashing need both username and password
            $this->data[‘User’] += array(
                ‘id’ => $this->Auth->User(‘id’),
                ‘username’ => $this->Auth->User(‘username’),
            );

            // AuthのhashPasswords関数でパスワードを暗号化してから保存 hashing password
            if ($this->User->save($this->Auth->hashPasswords($this->data))) {
                $this->Session->setFlash(‘変更内容を保存しました’, ‘flash_message’, array(‘result’ => ‘success’));
                //$this->redirect(array(‘action’ => ‘edit’));
            } else {
                $this->Session->setFlash(‘変更内容を保存できませんでした。もう一度実行してみてください。’, ‘flash_message’, array(‘result’ => ‘error’));
            }
        }

        if (empty($this->data)) {
            $this->data = $this->User->read(null, $id);
        }
    }

■参考サイト
 5.2.5.4 hashPasswords
 CakePHP 1.3 API – hashPasswords
 cakePHP | Authコンポーネントのパスワード暗号化に対応する

■for English
I have problem two things that change password page on CakePHP Auth component.

[CAUSE]
1. I couldn’t save hashing password.
2. password form show the hashing password.

[RESOLUTION]
1. use hassPassword class.
   $this->Auth->hashPasswords($this->data)
2.  set blank password form then check the form. check the my source.

CakePHPで複数joinする方法 INNER JOIN

複数テーブルからリレーション関係にある
カラム同士を紐付けて(INNER JOIN)クエリー実行。

            $options =  array(
                ‘fields’ => $val[‘fields’],
                ‘conditions’ => array(
                    ‘GrouponList.category_id’ => $category_id,
                ),
                ‘limit’ => $val[‘limit’],
                ‘order’ => $val[‘order’],
                ‘group’ => $val[‘group’],
                ‘joins’ => array(
                    array(
                        ‘type’ => ‘INNER’,
                        ‘table’ => ‘groupon_lists’,
                        ‘alias’ => ‘GrouponList’,
                        ‘conditions’ => array(
                            ‘SoBookmark.groupon_list_id = GrouponList.id’,
                        )
                    ),
                    array(
                        ‘type’ => ‘INNER’,
                        ‘table’ => ‘tw_counts’,
                        ‘alias’ => ‘TwCount’,
                        ‘conditions’ => array(
                            ‘SoBookmark.groupon_list_id = TwCount.id’,
                        )
                    ),
                ),
            );

        $this->set($key, $this->SoBookmark->find(‘all’, $options) );

CakePHPのACLで権限一覧を見る時のMySQLクエリー

# MySQLクエリー – パーミッション一覧
SELECT
  aros_acos.id AS aros_acos_id,
  aros.model,
  groups.name,
  acos.alias,
  acos.lft,
  acos.rght,
  aros_acos._create,
  aros_acos._read,
  aros_acos._update,
  aros_acos._delete
FROM
  aros_acos
LEFT JOIN aros   ON aros_acos.aro_id = aros.id
LEFT JOIN acos   ON aros_acos.aco_id = acos.id
LEFT JOIN groups ON aros.foreign_key = groups.id
;

↓↓↓ こんな感じの結果になる ↓↓↓

aros_acos_id model name alias lft rght _create _read _update _delete
115 Group users edit 81 82 1 1 1 1
97 Group administrators logout 51 52 -1 -1 -1 -1
98 Group administrators view 47 48 -1 -1 -1 -1

CakePHP 1.3.x のACLサンプルプログラム

結論から書くと、勉強用や参考になるかなと思い、
CakePHPのACLサンプルプログラムを
ダウンロードできるようにgithubへアップしました。

I uploaded to GitHub CakePHP ACL sample programs
for study.

■GitHubサンプルプログラム(GitHub sample programs)
  256hax/CakePHP-1.3.x-ACL-samples – GitHub

アクセスしたら左上の [ZIP] ボタンを押して
ダウンロードできます。
そのあと、同じページに「README」が
書いてありますので、それをご参照ください。

■経緯
CakePHPのACLをチュートリアルをもとに
実装していたが、ややこしいうえに自分のやり方が
悪いのか、ちゃんと動作しなかった。
幸いにもエンジニアの人が解説したブログが
たくさんあったので、なんとか動くようになった。
ただ、意外にも「ダウンロードしてすぐ動かせる」
っていうファイル群が見つからなかったので、
ACLのサンプルプログラムを丸ごとアップしてみようと思う。

■参考サイト
 ACL を制御するシンプルなアプリケーション :: 開発例 :: マニュアル :: 1.3コレクション
 CakePHP ACL – アクセス制御リストを攻略する | hijiriworld Web Creators
 CakePHP – ACLの権限管理を超簡単にするプラグイン『Plugin ACL』 – 高橋です
 cakePHP チュートリアル「ACL を制御するシンプルなアプリケーション」 その1 – SystemGathering
 CakePHP – Auth component loop redirect 

■リダイレクト無限ループについて
 CakePHP 1.3.x のACLチュートリアルを実装後、
 UsersControllerの「$this->Auth->allow(‘*’);」を
 外すと、どのページにアクセスしても
 リダイレクトループ(ページ転送がループ)してしまい
 ダメだった。結局、下記のようにし、あとは
 パーミッションで対応(但し、下記方法が合ってるか不明)。

function beforeFilter() {
parent::beforeFilter();
//$this->Auth->allow(‘*’); 
if(isset($this->params[‘requested’])) {
$this->Auth->allow($this->action);
}
}
 
 

 あと、CakePHPチュートリアルの パーミッションの手動設定
 追加のみ実行可能で、変更はできないようで、
 色々いじったところ、混乱してハマった。

 なので、 ACL Plugin を使ったほうが確実。

CakeDC Searchプラグインで入力フォームにURLエンコードされた文字列が入ってしまう

■環境
 MAMP
 CakePHP 1.3.13
 プラグイン CakeDC Search

■現象
 CakeDC Searchプラグラインを使って、検索入力フォームに
 日本語を入れてPOSTで検索をすると、入力フォームの
 日本語がURLエンコードされた値に変換されてしまう。
 その間、URLパラメータは逆に日本語が入る。

■原因
 CakeDC SearchプラグインがURLエンコードを
 かけない処理にしているため。

■対策
 ContrlloerでCakeDC Searchプラグインを使う際に
 ’encode’ => true をセットすればOK。

 var $presetVars = array(
  array(‘field’ => ‘name’, ‘type’ => ‘value’, ‘encode’ => true),
 );

 対策など詳細。
 #23: URL Encoding – Issues – CakeDC/search – GitHub
 → For example, “word1 word2” queries correctly, but is displayed in the input field as “word1%20word2”.

これの対策 → CakePHP: URL rewriting is not properly configured on your server

■現象
CakePHPインストール&設定直後にエラー

URL rewriting is not properly configured on your server

■原因
リライト周り、.htaccess周りの設定ミス

■対策
ここ見て設定通りになっているか確認する。
※英語ですが、なんとなく見ていけば分かると思います。

3.3.4 Apache and mod_rewrite (and .htaccess)

ケース1.
.htaccessの置き場所が間違ってた。
(色々自分でいじってて、うっかり移動させてた)
ケース2.
CakePHPルート直下の.htaccessファイルがなかった。
(なぜかデフォルトで設置されてなかったので
上記リンクの手順書をもとに作成した)

■参考サイト
これも英語ですがなんとなく流し読みで
今回の原因が特定できました。

stackoveflow – CakePHP: URL rewriting is not properly configured on your server

stackoverflowは技術関連の質問サイトで、英語なのですが
このサイトで結構、解決することが多い。英読の敷居が高いので
アレですが、stackoverflowだけはちゃんと読むようにしています。

CakePHPのroutesでアクション名をURLに含めずパラメータをいきなり指定する方法

■やりたいこと
    通常のアクセス → http://test.local/index/fassion/
    こうしたい →  http://test.local/fassion/
    (アクション名をURLで定義しないでパラメータをいきなり指定して実行させたい)

■tweetsコントローラ
    index($category) {
        なんか処理

    }

■routesの定義
    Router::connect(‘/:category/*’,
        array(
            ‘controller’ => ‘tweets’,
            ‘action’ => ‘index’),
        array(
            ‘pass’ => array(
                ‘category’
            )
        )
    );

passを指定すればOK。
パラメータ名はコントローラの変数名となる。それを確認したい場合は、
$thisのparames変数を参照。

■参考資料
3.4.5.4 アクションにパラメータを渡す : マニュアル :: 1.2コレクション