5丁目通信(仮称)

とある5丁目で活動する還暦を過ぎたWebプログラマーの覚え書きです。それとかかってくる迷惑電話や、家業のアパート経営について。

タグ: CakePHP

  • BaserCMSは正しい方向かもしれないが、まだまだという話し

    CakePHPで実現するBaserCMSというCMS(コンテンツ管理システム)をここのサイトから知りました。

    BaserCMSのサイトでマニュアルを見てみても、どんなことができるかわかりませんでした(見落としているかもしれません)。おそらくメニューの生成と、ページをWebの管理ページから作成、ブログやフォーラムの配置ができるものと推測されます。BaserCMSは、企業向けのサイトができるとは謳っていますが、具体的な機能があるか書かれていないのは残念です。やはり自分でBaserCMSをインストールしてみないといけないのでしょうか? 自由に管理ページまで触れるデモサイトがあればいいのですが。あと、ユーザーフォーラムに記事が0というのも寂しいです。

    CMSは、ユーザーにとってはWeb上でページを追加できることが一番のメリットと思いますが、開発側からはモジュールの追加による機能の追加がメリットかもしれません。用意されているモジュールを組み合わせてサイトを構築できるというのはありがたいです。

    CakePHPの上で開発されていることは、PHPで有名な某CMSよりは、モジュールを作成してカスタマイズの見通しがよいと考えられます。この点は、BaserCMSはCMSの正しい方向かもしれません。

    実は今日、ニュースリリースやお知らせのページをユーザーが作成したり、カレンダーで日程を表示したりなど、CMSで実現できそうな案件がお客様から来ましたが、一から開発ということになりそうで、費用とスケジュールが合わないのでお断りの連絡を入れたところでした。BaserCMSのようなCMSが具体的になっていけばいいかもしれません。

    著:山田祥寛
    ¥3,366 (2025/03/27 13:40時点 | Amazon調べ)
    著:松浦 健一郎, 著:司 ゆき
    ¥2,574 (2025/03/27 13:40時点 | Amazon調べ)
    著:谷藤賢一, 著:徳丸浩
    ¥2,750 (2025/03/30 15:22時点 | Amazon調べ)
  • XAMPPのバーチャルホストを設定してCakePHPのテストサイトを構築した話し

    CakePHPで開発していて、ローカルのサーバーにテストサイトで動かしたいとき、まともにlocalhost直下に置ければいいのですが、いろいろとプロジェクトを抱えていると、たくさんのテストサイトを同時に動かしたいときがあります。サブディレクトリごとに設定してあげてもいいのですが、これだと.htaccessの設定が面倒であったり、絶対アドレスでリンクを指定できないとか制限があります。

    ドメイン直下にドキュメントルートを配置できればいいのですが、一つ考えられるのはサブドメインを指定するというのがあります。しかし、これではlocalhostでは指定できないととか、わざわざローカルのPCにサブドメインを指定するのもやっかいであるとなります。

    次に考えられるのはポート番号で分けてしまう方法があります。例えば

    • Aプロジェクトは、http://localhost:8080/
    • Bプロジェクトは、http://localhost:8082/

    などでアクセスすることになります。これですと、ポート番号を適当に追加していけばプロジェクトを分けてアクセスすることが可能にになります。

    簡単にローカルのPCにCakePHPのテスト環境を構築するにはXAMPPをインストールしてしまうのが一番簡単です、XAMPPを一つインストールしてしまえば、CakePHPのテスト環境で必要なApacheや、PHP、MySQLも動かすことがことができます。

    ポート番号で分けてCakePHPの環境で動かすには、Apacheのhttpd.confにバーチャルホストの設定を追加してしまいます(ここからが本題)。上記のAプロジェクトの場合はApacheのhttpd.confに以下のバーチャルホストの設定を追加します。

    Listen 8080
    
    DocumentRoot C:/workspace/TestProject/app/webroot
    DirectoryIndex index.html index.php
    ServerName localhost
    
    <Directory C:/workspace/TestProject/app/webroot>
    Options Indexes FollowSymLinks MultiViews Includes ExecCGI
    AllowOverride All
    Order deny,allow
    Allow from all
    </Directory>

    この場合、ダウンロードしたCakePHPは、c:\workspace\TestProjectにコピーしてしますと(EclipseのPDTで開発しているためこんなディレクトリにプロジェクトを入れています)、ドキュメントルートはその下の\app\webrootになります。プロジェクトを増やす場合は、赤字の部分を変更すればいいでしょう。

    Optionsは、適当に動作させる環境に合わせて設定してください。これは、テスト環境を自分のローカルPC以外からアクセスできないようにしたり、ましてインターネットからアクセスできないようにしたり(もちろん当たり前ですが)してしまえば、あまり気にしなくてもよいかもしれません。

    以上、いつもhttpd.confの設定でつまずく覚え書きです。

    著:山田祥寛
    ¥3,366 (2025/03/27 13:40時点 | Amazon調べ)
    著:松浦 健一郎, 著:司 ゆき
    ¥2,574 (2025/03/27 13:40時点 | Amazon調べ)
    著:谷藤賢一, 著:徳丸浩
    ¥2,750 (2025/03/30 15:22時点 | Amazon調べ)

    追記

    せっかくこの記事にたどり着いて読んでくれたところ申し訳ありませんが、現在(2014/04/28)のところ、この方法は使っていません。今はVirtual Box+Vagrant+Chefでローカルのテストサイトを構築しています。

    追記2

    またまた、上記の追記のVirtual Box+Vagrant+Chefはもう使っていません。現在は、Dockerでテストサイトを実現しています。その方が簡単です。

  • CakePHP:paginateのsortに複数のソート項目を追加した話し

    CakePHPで用意されているpaginateのsortのメモです。paginateのsortは便利ですが、最初はテーブルの1項目しかソートの対象になりません。お客さんから、「価格」と並び替えるときに、もう一つ「価格条件」でも合わせてソートしてくれという入り用とのこと。しかし、CakePHPのレファレンスによると、ソートするには一つの項目だけしかソート項目を指定できません。・・・/cake/libs/controller/controller.phpのpaginate()のソースを追っても、複数のソート項目を設定できるようにはなっていません。

    そこで、再度調べてみると、検索する前にモデルでbeforeFind()を呼んくれるとのこと。そこでモデルでbeforeFind()を定義して、ここでソート項目を追加してしまうことにしました。例えばこんな感じ(だいぶコードを省略しています)。

    function beforeFind($queryData) {
    
    array_unshift($queryData['order'], array('XXX.item' => ‘desc’));
    
    return($queryData);
    
    }

    ORDER BY句の最初に

    XXX.item desc

    が追加されます。$queryData[‘order’]は配列で、配列の順番でORDER BY句に追加していきますので、

    $queryData['order'][] = array('XXX.item' => ‘desc’);

    とすると、ORDER BY句の最後に、

    , XXX.item desc

    が追加されます。これで、複数の項目でSORTできます。

    ここからついでの話しです。ORDER BY句に、ちょっとしたSQL関数を追加したい場合、例えば、

    XXX.item is NULL desc

    を追加した場合(あまりありませんが・・・)、

    array('XXX.item is NULL' => ‘desc’)

    と指定してしまうと、

    “XXX”.”item” is NULL” dess

    と解析されてSQLエラーになってしまいます。これは、

    array('(XXX.item) is NULL' => ‘desc’)

    として括弧で括ってあげれば、ORDER BY句には、期待通り、

    (XXX.item) is NULL desc

    にしてくれます。

  • CakePHPのpaginateの最適化の話し

    ページングが簡単にできてしまうCakePHPのpaginateはとても便利です。パラメーターをちょっと設定してあげるだけなので、誰でも使いたくなるはず。

    しかし、このpaginateには、大きな落とし穴があります。いくつものアソシエーションを設定されているモデルに対してpaginateを使うと、とても遅いのです。

    これはpaginateだけではなくfindAllでも言えること。何も考えずに使うと、結果が返ってこないのです。

    デバッグレベルを3にするとわかるのですが、大量にSQLが生成されます。この辺りをきちんとチューニングしてあげないといけません。

    paginateの最適化を調べましたが、なかなか検索にひっかかりません。仕方ないので、試行錯誤。結局は以下の通り。

    必要のないアソシエーションは実行する前に削除しておく。モデルクラスで最初に$hasManyを定義しますが、このままですと勝手にそのモデルへの抽出のためのSQLを発行してくれます。paginateのfieldsオプションに、抽出したい項目を並べておけば、それだけをSQL一つで抽出してくれるかと思っていましたが、これは大間違え。$hasManyに定義されているモデルに対して1レコードづつ改めてSQLを生成します。

    あと、アソシエーションが3つになると、例えばA->B->Cと各モデルがhasMany,belongsToのアソシエーションを定義されているとすると、Aに対してpaginateをすると、A->Bで参照されたSQLを発行して、その後各レコードでCを参照するSQLが発行されてしまいます。したがって、Cがあまり大きなテーブルではなくて、ほとんど更新がないようなテーブルだと、データを配列に格納して参照させたほうがいいかもしれません。

    とにかく、CakePHPのpaginateやfindAllは、余計なSQLを発行してくれます。いかに無駄なSQLを削除するかがパフォーマンスの分かれ目です。

    今回、以上の方法でCakePHPから発行しているSQLの数を一桁以上減らすことで、paginateのパフォーマンスをあげました。一番のCakePHPのチューニング方法は、core.phpでデバッグレベルを3にして表示されるデバッグ情報のSQLダンプを見ながら調整していくことをお勧めします。

  • CakePHPでデータベースの文字コードの話し

    今開発しているWebサイトは、別の開発会社が別の機能を開発している。時間の関係で私がすべて開発できないので、発注元が別の開発会社を割り当てている。

    その会社からの質問。

    本番サーバーのDBにアクセスしたら文字コードの関係でデータが抽出できない。

    そういえば、本番サーバーはTurboLinuxで文字コードがEUCだっけ。インストールされているPostgreSQLも7.x系で、テストサーバーとは違っていたような。テストサーバーは8.x。この辺りは自分がサーバーの面倒を見ている訳ではないから伝えていなかった。失敗。

    PostgreSQL上にUTF-8でデータベースをcreatedbで作成しておいて、UTF-8でダンプしたSQLでリストアしておいた。システムの想定する文字コードはUTF-8。この辺りが怪しそうなので連絡しておいた。

    私はCakePHPで開発していて、別の開発会社はperlのCGIで開発。CakePHPの経験がないので、perlのCGIになったようだ。

    こちらは全く文字コードの意識はしていない。なにかうまく動いている。結果オーライ。apps/config/database.phpでencodingの設定もしていない。データベースが絡んだ案件は、必ず最初に文字コードでトラブルが起こる。

    後から、私は非協力的だと開発会社から言われるのでしょうね。psqlコマンドで一発でわかりそうなものだが、そういえば発注元はその開発会社に、サーバーにシェルでアクセスさせる権限が与えていないかも・・・。

  • HTMLの特殊文字はまじめに書きましょう、という話し

    現在開発中のWebサイトはCakePHPで開発しています。今回は、PCと携帯電話の両方をサポートしています。どちらもコントローラーとアクションは共通でビューだけが異なります。携帯かどうかの判定をして、携帯電話であったらm_*.ctpという名前のビューを取り込むようにしています。

    携帯電話の場合の文字コードは、基本的にシフトJISになります。通常はPHP、HTML、DBはUTF-8にしていますので、表示するときに強制的にシフトJISに文字コードを変換して、英数字カタカナを1バイト文字に変換してしまいます。この辺りはレイアウトに処理を書いておけばCakePHPだと簡単です。

    ここから本題です。失敗したこと。

    携帯電話の画面で矢印の代わりに<と>を使っていました。<>は2バイトコードです。例えば

    <前へ 後ろへ>

    のようにリンクを設定します。

    しかし、お察しのとおり携帯電話のブラウザだとおかしなコードが表示されてしまいます。つまり<>がHTMLタグの始まりと終わりになって認識されてしまいます。

    解決方法は簡単です。面倒がらずにHTMLの特殊文字でまじめに指定することです。<は&lt;>は&gt;です。これで正しく表示できます。

    やはり基本は大事です。

    技術評論社
    ¥1,980 (2025/03/25 15:17時点 | Amazon調べ)
  • jQueryのrollovers.jsとprototype.jsの競合の話し

    現在、外部の会社から出てきたHTMLのコードにCakePHPのAjaxの処理を埋め込むと、メニューの画像がロールオーバーしなくなります。

    HTMLを見てみると、rollovers.jsというJavascriptでロールオーバーをしているようです。prototype.jsを読み込むところを削除すると、うまくロールオーバーしてくれます。しかし、Ajaxの処理はしてくれなくなります。

    CakePHPでAjaxを実装すると、prototype.jsが必要です。CakePHPの機能を使わずにAjaxの処理を書くのはとても面倒です。では、rollovers.jsを取るか、CakePHPのAjaxを取るかをどちらかにするかと言われればCakePHPのAjaxをとりました。

    もっとシンプルにJavascriptでロールオーバーできないかと探したところ、ありました。シンプルロールオーバー。

    メニューの画像ファイルの命名が

    *_o.gif //ロールオーバー前
    *_h.gif // ロールオーバー後

    になっていますので、少々修正をするだけでOK。無事にAjaxの処理とメニューのロールオーバーが実現できました。シンプルロールオーバーの作者に感謝です。

    技術評論社
    ¥1,980 (2025/03/25 15:17時点 | Amazon調べ)

    追記(2022年10月16日)

    記事中のシンプルロールオーバーのサイトはどこかになくなりました。

  • CakePHPでpaginate()での検索結果の引き継ぎの話し

    引き続きCakePHPの話し。覚え書きです。

    検索結果をリストにして、このリストをページ替えしたり、ある項目でソートしたりしたいということはよくあります。CakePHPではページング処理は、コントローラーでpaginate()に検索条件を与えてあげるだけで実現できます。

    しかし、ソートしたり次へリンクを押してページ替えすると、検索条件を忘れてしまうのです。検索条件の引き継ぎはここでも書きましたが、getパラメータの処理を書いてあげなくてはなりません。セレクトメニューや、ラジオボタンは、検索項目に1つにつき1つの値しかありませんのでいいのですが、チェックボックスは複数の値がありますので、すべての項目をパラメータにするのはURLが長くなってしまいます。

    最初は、チェックボックスの項目を配列に格納し、シリアライズ化してGETパラメーターに置いてみましたが、GETパラメーターを解析するpassedArgs()がうまく解析してくれませんでした。

    そこで、チェックボックスをグループにして、この値をコンマ区切りに変換してからパラメーターに渡してしまいます。しかし、最初に検索条件を入力するページはPUTで各項目が配列で渡してきますので、検索条件を生成する処理を共通にするために、GETで来たパラメータはPUTで来たパラメータに合わせるように、コンマ区切りからPUTパラメーターの配列に変換してあげます。

    こんなことをしてあげないと、複数の値を持ったチェックボックスでの検索結果がpaginate()で処理できないのは、CakePHPでの面倒なところです。

    PS. 以上の内容を読んでみると、理解できなさそうですね。自分が理解できるだけのメモ書きですね。該当するソースを公開して説明できればいいのですが・・・。

    著:山田祥寛
    ¥3,366 (2025/03/27 13:40時点 | Amazon調べ)
    著:松浦 健一郎, 著:司 ゆき
    ¥2,574 (2025/03/27 13:40時点 | Amazon調べ)
    著:谷藤賢一, 著:徳丸浩
    ¥2,750 (2025/03/30 15:22時点 | Amazon調べ)
  • 昨日のCakePHPでhasManyでの検索の続きの話し

    昨日のCakePHPでhasManyでの検索の続きです。うまくいったと思いましたが、検索結果に重複項目があります。hasManyのモデルにまともにSQLを実行したら重複するのは当たり前ですね。

    そこで、pagenate()でページングの処理をしているので、コントローラーのメンバー$pagenateのfieldsに、キーとなる項目にDISTINCTを付けてみる。これで、重複されなくなった。

    しかし、新たな問題。検索個数が合わない。こちらもcount(*)になっているところをcount(DISTINCT キー項目)にすればいいのだけど、$pagenateのfieldsからは影響されないようでした。

    いろいろ調べてみると、やはりありました。ここです。モデルにpaginateCount()を定義してあげればいいのでした。pagenate()のソースを見ると、モデルにpaginateCount()があるかどうかをチェックして、あればpaginateCount()を実行してくれます。無ければfindCount()を実行します。ここはSQLをゴリゴリ書いて、該当する条件でレコード数を返してあげるようにしました。配列に格納されている条件は次のようにWHERE句に変換できます。

    $Db = ConnectionManager::getDataSource($this->useDbConfig);
    $where = $Db->conditions($conditions);

    これで、重複せずに検索結果を表示できました。

    いまいち具体的な説明ではないので、もしご興味ある方はコメントに入れてください。

    著:山田祥寛
    ¥3,366 (2025/03/27 13:40時点 | Amazon調べ)
    著:松浦 健一郎, 著:司 ゆき
    ¥2,574 (2025/03/27 13:40時点 | Amazon調べ)
    著:谷藤賢一, 著:徳丸浩
    ¥2,750 (2025/03/30 15:22時点 | Amazon調べ)
  • CakePHPでhasManyのモデルに対して条件式を書いた話し

    CakePHPロゴ

    ただ今、CakePHPでWebサイトを開発しています。今回は、検索条件を設定して抽出して結果をリストにするという、定型的な検索サイトです。

    あるモデルに対して検索条件を与えるのは普通です。CakePHPの場合、belongsToのような外部参照の関係であるモデルにも検索条件を与えても、簡単に抽出してくれます。

    しかし、hasManyの関係にあるモデルの項目に検索条件を書いてあげるとSQLを発行しているところでエラーになります。SQLのデバックログによると、hasManyのモデルに対しては、SQLでleft joinを作ってくれません。これで、未定義のテーブルということでSQLエラーになります。

    Googleで検索してみると、このように書けばOKと書いている。しかし、これではhasMany以外の関係のモデルを検索してから、hasManyのモデルにフィルターをかけているだけなので、検索結果が違ってくる。

    無理矢理SQLを生成してみることもやりましたが、ビューに渡す結果を生成するのが大変なので止めました。

    いろいろと調べてみると、ここにありました。後からhasManyのモデルに対してjoinを設定すればいいのです。モデルに対して、beforeFind()を定義してあげることが必要でした。CakePHPのソースを追ってみると、findAllのところでオプションにjoinが使えるように定義されていました。これで無事にhasManyのモデルでも条件式を書くことができました。

    すべてのサイトに感謝です。

    著:山田祥寛
    ¥3,366 (2025/03/27 13:40時点 | Amazon調べ)
    著:松浦 健一郎, 著:司 ゆき
    ¥2,574 (2025/03/27 13:40時点 | Amazon調べ)
    著:谷藤賢一, 著:徳丸浩
    ¥2,750 (2025/03/30 15:22時点 | Amazon調べ)