ラベル CakePHP の投稿を表示しています。 すべての投稿を表示
ラベル CakePHP の投稿を表示しています。 すべての投稿を表示

2016年4月14日木曜日

CakePHPとRailsの比較

ずっとCakePHPを使ってきましたが、周りにRailsを使え使えという人が多かったので、ここ1年くらいはRails漬けです。

簡単にですが、両者を比較してみたいと思います。

CakePHPのメリット

  • エンジニアの数が多く採用がしやすい
  • 環境構築が簡単
  • PHPは独特な概念が少ないので習得しやすい

Railsのメリット

  • エンジニアのレベルが高く、一人でなんでもできちゃう人が多い
  • オープンソース活動が活発でライブラリがたくさんあるし、情報も多い
  • 独特な概念は多いが、その分コードが綺麗になる
  • コードが少なくなるのでメンテナンスがしやすい

ほんとにざっくりいうとこんな感じですね。

正直、CakePHPにある程度慣れた人には、Railsを触ってみることをお勧めします。

MVCなどの概念はとても似ているので、理解はしやすいですし、この手のフレームワークの本家はRailsなので、学ぶと色々と腑に落ちること多いと思います。これって、こういう目的なのかぁ、と思うことが多いです。

とはいえ、これだけの比較だと足りないと思いますので、もっと詳しく比較した記事をご紹介します。

2014年7月9日水曜日

[CakePHP]v3.0.0-alpha1導入@XAMPP

ここを参考にした。
http://book.cakephp.org/3.0/en/installation.html

1.コンポーザやってみると。

composer create-project -s dev cakephp/app

以下のエラーが。

Warning: This development build of composer is over 30 days old. It is recommended to update it by running "/usr/local/bin/composer self-update" to get the latest version.

Installing cakephp/app (dev-master d15c9b13b6088a4bf3d52a3342fbce878a6cc3a5)

  - Installing cakephp/app (dev-master master)

    Cloning master

Created project in /Users/araky/git/test03/app

Loading composer repositories with package information

Installing dependencies (including require-dev)

Your requirements could not be resolved to an installable set of packages.

  Problem 1

    - Installation request for cakephp/cakephp 3.0.*-dev -> satisfiable by cakephp/cakephp[3.0.x-dev].

    - cakephp/cakephp 3.0.x-dev requires ext-intl * -> the requested PHP extension intl is missing from your system.

2.intlをダウンロード

http://pecl.php.net/package/intl

gunzip intl-3.0.0.tgz
tar xvf intl-3.0.0.tar

3.phpize

/Applications/XAMPP/bin/phpize

4.configure

./configure --with-php-config=/Applications/XAMPP/bin/php-config-5.5.9

こんなエラーが。。。
configure: error: Unable to detect ICU prefix or no failed. Please verify ICU install prefix and make sure icu-config works.

5.icuをinstall

brew install icu4c

6.もう一度configure

 ./configure --with-php-config=/Applications/XAMPP/bin/php-config-5.5.9 --with-icu-dir=/usr/local/Cellar/icu4c/52.1

7.sudo make

8.sudo make test

なんかFailでてるけどいいや。
=====================================================================

TEST RESULT SUMMARY
---------------------------------------------------------------------
Exts skipped    :    0
Exts tested     :   59
---------------------------------------------------------------------
Number of tests :  388               357
Tests skipped   :   31 (  8.0%) --------
Tests warned    :    0 (  0.0%) (  0.0%)
Tests failed    :   22 (  5.7%) (  6.2%)
Expected fail   :    0 (  0.0%) (  0.0%)
Tests passed    :  335 ( 86.3%) ( 93.8%)
---------------------------------------------------------------------
Time taken      :   36 seconds
=====================================================================
=====================================================================
FAILED TEST SUMMARY
---------------------------------------------------------------------
Bug #58756: w.r.t MessageFormatter [tests/bug58756_MessageFormatter.phpt]
IntlCalendar::getDayOfWeekType() basic test [tests/calendar_getDayOfWeekType_basic.phpt]
asort() [tests/collator_asort.phpt]
compare() [tests/collator_compare.phpt]
get_locale() icu >= 4.8 [tests/collator_get_locale2.phpt]
collator_get_sort_key() [tests/collator_get_sort_key.phpt]
sort() [tests/collator_sort.phpt]
sort_with_sort_keys() [tests/collator_sort_with_sort_keys.phpt]
IntlDateFormatter: several forms of the calendar arg [tests/dateformat_create_cal_arg.phpt]
IntlDateFormatter::formatObject(): IntlCalendar tests [tests/dateformat_formatObject_calendar.phpt]
IntlDateFormatter::formatObject(): DateTime tests [tests/dateformat_formatObject_datetime.phpt]
datefmt_format_code() (5.3+, ICU >= 50) [tests/dateformat_format_50+.phpt]
datefmt_format_code() and datefmt_parse_code() (ICU >= 50) [tests/dateformat_format_parse_50+.phpt]
IntlDateFormatter: setCalendar()/getCalendar()/getCalendarObject() [tests/dateformat_get_set_calendar.phpt]
IntlDateFormatter: get/setTimeZone() [tests/dateformat_get_set_timezone.phpt]
IntlDateFormatter: several forms of the timezone arg [tests/dateformat_timezone_arg_variations.phpt]
numfmt_format() icu >= 4.8 [tests/formatter_format2.phpt]
locale_get_display_name() ICU >= 50 [tests/locale_get_display_name_50+.phpt]
locale_get_display_script()  icu >= 49 [tests/locale_get_display_script3.phpt]
MessageFormat accepts IntlCalendar args [tests/msgfmt_format_intlcalendar.phpt]
msgfmt_parse() tests [tests/msgfmt_parse.phpt]
IntlTimeZone::getDisplayName(): type parameter (ICU >= 49) [tests/timezone_getDisplayName_variant2-49+.phpt]
=====================================================================

9.sudo make install

10.php.ini編集

extension=intl.so

11.改めて

composer create-project -s dev cakephp/app


12. Cake のserver起動。Railsっぽい。


Console/cake server -p 9999

※localhostが衝突したのでport指定した。

とりあえず、起動しました。あとはデータベースの設定

2014年6月30日月曜日

[CakPHP]ControlloerでsetするとDebugKitのVariablesタブで捕捉されないことがある

※version2.4.9の話

ControllerのCallback関数には、

Controller::beforeFilter()
Controller::beforeRender()
Controller::afterFilter

があるけど、beforeRenderとafterFilterでController::setしてもDebugKitのVariablesタグで捕捉されることはない。afterFilterでsetしてもViewで使えないので全く意味はないが。
beforeRenderでsetしたいケースはあると思う。たとえば、Authの情報をどのページでもsetしているが、logout画面ではする必要がない(したくない)ので、その場合はbeforeRenderにset処理を入れておく。すると、beforeRender以前にlogoutのロジックが終わっているので、Auth情報はセッションからなくなっているので、setせずに済む。

これをbeforeFilterでsetしていると、logout処理の前でセッションからAuth情報がある状態なのでsetされてしまい、View画面で使われてしまったりする。「ログアウトしました。」画面なのに、「マイページ」とか表示されてたりする。

話はそれたけど、beforeRenderでController::setしてもDebugKitのVariablesからは見えないけど、ViewではきちんとRenderされます。


2014年6月7日土曜日

[CakePHP]CakeDC/migrationsの微妙な仕様

ここによるとModelが存在しないテーブルのmigrationは以下のように-fをつけてあげるとできる。これをつけないと、差分として検知されない。
ここでは例えば、Aというテーブルを作成した場合とする。


cake Migrations.migration generate -f

このコマンドから始まるプロンプトを進めて行くと、以下のように

Do you want update the schema.php file? (y/n)

schema.phpを更新するか聞かれて、自分はいつも通り、yを選択する。

で、次にDB定義変更をしてmigrationをする機会が訪れたとする。

今度は普通にModelが存在するテーブルBだったので、以下のように-fはつけない。

cake Migrations.migration generate

そうすると何が起こるかというと、①で作成したテーブルAをdropするmigrationが作成されてしまう。

なぜかというと、②でも-fが有効になっているらしく、ここで生成されるschema.phpにはテーブルAが入ってしまう。migrationの仕組みは現実のDB定義とschema.phpを比較して差分をmigrationファイルとする。なので、③で-fをつけないのからモデルのないテーブルは検知されないため、schema.phpにあるAテーブルは、現実のDBには存在しないよ、となり、じゃあ削除するマイグレーションファイル作りますね、と判断してしまうのだ。

イケてないですね。

なので、 -fをつける場合は②はnを選択したほうがよさそうですね。





2014年6月6日金曜日

[CakePHP]find()のオプションのpageはlimitと併用しないと恐ろしい事に。。。

※CakePHP2.4.9のお話。
$page = 1;
do{
    $ids = $this->find('list',array(
        'page' => $page,
        'fields' => array('user_id'),
        'conditions' => //ある条件
    ));
    //ある処理
    $page++;
}while (!empty($ids));

こんな感じで、特定の条件全てのレコードに対してある処理をするバッチを作っていて、1pageあたりの数はデフォルト値を当然使ってくれるだろうと思っていたんだけど、これを実行したら、なかなか処理終わらずおかしいなと思ってたら、あるテーブルにありえない数のレコードがインサートされてる。。。無限ループ状態になっていました。あわてて、ApacheとMySQLをシャットダウンして、止めた。

というわけで、デバッグしたら、limitを明示的に定義していないと、実際SQLで使われるoffsetとlimitに値が入らないってことがわかった。

model.php 2942行目
if ($query['page'] > 1 && !empty($query['limit'])) {
    $query['offset'] = ($query['page'] - 1) * $query['limit'];
}
なので、以下のようにlimitを明示的に指定して直った。恐ろしや〜。
$page = 1;
do{
    $ids = $this->find('list',array(
        'page' => $page,
        'limit' => 20,
        'fields' => array('user_id'),
        'conditions' => //ある条件
    ));
    //ある処理
    $page++;
}while (!empty($ids));

[Composer]installer-pathsの優先順位について

Composerでここで説明されているように、packageごとにinstallされるpathをカスタマイズできるんだけど、なかなか反映されなくて困った。

"extra": {
    "installer-paths": {
       "plugins/{$name}/": ["type:cakephp-plugin"],
       "app/Plugin/{$name}/": ["somevendor/some-package"]
    }
}



こんな感じだったんだけど、上記のsome-package(もちろん仮名ね)っていうpluginもcakephp-pluginなんだけど、上にあるcakephp-plugin用のpathが先に適用されてしまったからでした。

なので、以下みたいにしたら適用されました。

"extra": {
    "installer-paths": {
         "app/Plugin/{$name}/": ["somevendor/some-package"],
         "plugins/{$name}/": ["type:cakephp-plugin"]
    }
}

2014年6月5日木曜日

[CakePHP]test.phpのCode coverageが表示されない件

テストケースのファイル名をPost01Test.phpみたいに、番号をつけたらcode coverageが表示されなくなった。テスト自体は正常に実行することができるのに。
なので、番号をつけるのをやめてPostTest.phpに戻した。

2014年6月4日水曜日

[CakePHP]find('list')はDISTINCT使えないのね。

$this->Comment->find('list',array(
  fields' => array('DISTINCT user_id'),
  'conditions' => $conditions,
));


こんな感じでやったら、以下のようなエラーが。

PDOEXCEPTION
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DISTINCT `Comment. user_id` FROM `test`.`comments` AS `Comment` WHERE 1 = 1 ' at line 1



このサイトによると、DISTINCTってSELECTの直後に来ないといけないいたい。でもCakePHPのfind('list')ってデフォルトでは、SELECTの直後でIDを指定しているので、DISTINCTがその後になってしまうんですよね。

なので、group byを使うといいみたい。

$this->Comment->find('list',array(
  fields' => array('user_id'),
  'conditions' => $conditions,
  'gruop' => 'user_id'
));

※Group Byの豆知識
 デフォルトだとGROUP BYで指定されたカラムの昇順になる。いきなり順番変わってびっくりした。ORDER BY を使って任意のカラムの昇順、降順を指定できる。

2014年5月29日木曜日

[CakePHP]Controllerのテストで主要モデル以外のモデルのモックを作る方法

※CakePHP2.4.9の話

例えば、AmazonsControllerのテストの場合、

AmazonsControllerTest.php内
$this->controller = $this->generate('Amazons', array(
    'models' => array(
        'Amazon' => array('hardToTest')
    )
));
$this->controller->Amazon->expects($this->once())
        ->method('hardToTest')
        ->will($this->returnValue(true));

主要モデル(コントローラー名に名前が入るモデル)のAmazonモデルの場合は、こんな感じでモックを生成できる。(hardToTestというメソッドのモックを作りたい時)

でも、それ以外のモデル、例えば、Amazonモデルにアソシエーションで紐づくモデルやControllerの処理内でloadModel()したモデル、さらにそれにアソシエーションに紐づくモデルetcはこのやりかたで生成できない。

上記でいう、6行目のexpects()で下記のエラーが発生する。

Error: Call to a member function expects() on a non-object

これに対処する方法のひとつにテスト対象のControllerの$usesにモックを生成したいモデルを入れる、というものもある。ようは、モック化対象のモデルを主要モデルの一つにしちゃうという方法だ。これなら上記のような形で生成できる。
でもそもそもテストのためにプロダクトのコードに手を入れるのってやだよね。

そんな場合は、getMockForModel()を使う。これで簡単にControllerのテスト内で任意のモデルのモックを生成できる。

$this->Controller = $this->generate('Amazons');
$this->getMockForModel('Vote', array('hardToTest'))
        ->expects($this->any())->method('hardToTest');

上記で登場するVoteモデルはちなみにAmazonモデルには直接アソシエーションで紐づいていないモデルだ。Controllerの処理内でloadModelされるItemというModelにアソシエーションで紐づいているモデルである。

でも上記のコードでモックを生成することができる。
注意点としては、かならず1行目でControllerのモックを生成しておく必要があること。

余談だが、上記の$this->Controllerて変数はどんな変数名にしてもOK。$Amazonsにしてもいいし$aでも$bでもOK。generate()メソッドは自動的に$this->Controllerを生成してくれるみたいです。※参考 CakePHP 2.xのtestActionをちゃんと理解しよう

もう一つ余談ですが、当然ですが、モック化したメソッド以外のメソッドは本物のメソッド使ってくれるみたいですね。

2014年5月26日月曜日

[CakePHP]deleteAllはcounterCache更新してくれないみたいね。

※CakePHP2.4.9の話

これは古い記事だけど、こういう対処法があるんだね。

counterCache and deleteAll: How to Make Them Friends

※追記
すみません。引数でcallbackを動かすか指定できるので、それをtrueにするとcoutercacheも更新されます。

http://book.cakephp.org/2.0/en/models/deleting-data.html#deleteall

2014年5月13日火曜日

[CakePHP]Validation の'required' => trueは外部キーにはいれないほうがいい。

なんでかというと、$Model->saveAssociated()等でassociation先のモデルと同時に保存する場合、外部キーを自動的に補ってくれる。
でもvalidationの処理が走るのは、外部キーを補う前なので、'required' => trueになってしまい、validation errorが発生してしまうから。


※CakePHP2.4.9の話

[CakePHP]Validationってそもそも配列にfieldの添字がないと発火しないんだね。

※CakePHP2.4.9のお話

何が言いたいかって、例えば以下のようなvalidationを設定している時。

public $validate = array(
  'user_id' =>array(
    'rule' => 'notEmpty',
    'message' => 'このフィールドは必ず入力してください。',
  ),
  ),
);


下記みたいにvalidation対象のuser_idが無い配列を保存しようとしたら、notEmptyが発生すると思ていたんだけど、発生せずに保存しちゃった。user_idにはDBでフォルトの0が入っていた。

array(
  'theme_id'=> '22'
)

要するに、validationするには対象のフィールドの添字が配列内に存在しないと発火しないのね。

※追記
色々実験してわかったけど、以下みたいに'required' => trueを入れると添字がない場合にvalidation errorを発生させてくれる。
public $validate = array(
  'user_id' =>array(
    'rule' => 'notEmpty',
    'message' => 'このフィールドは必ず入力してください。',
    'required' => true,
  ),
  ),
);


'required' => trueの不思議なのって、どのvalidationの種類にも入れられるので、例えばemailの形式を求めるvalidationに入れた場合、メッセージは「emailの形式で入れて下さい」みたいに出るんだよね。notEmptyだったらわりと自然なメッセージになるけど。


2014年5月7日水曜日

[CakePHP]Fatal Error Error: Cannot redeclare class ThemesItemFixture

CakePHPでブラウザ上からのUNITテストをしていたらこんなエラーが出ました。

いくつかのテストファイルをまとめている「All tests」実行時のみ発生しました。
単体でそれぞれでテストを実行した場合は発生しませんでした。

以下のように修正したら直りました。なんか衝突してたみたいですね。
単体のファイルの実行だとどちらもOKというのが不思議な感じがしますが。

before
public $fixtures = array(
    'app.themesitem'
);


afiter
public $fixtures = array(
    'app.themes_item'
);



2014年5月6日火曜日

[CakePHP]外部サービスのAPIを使ったdatasourceのテスト

自分はAmazon AssociateのAPIを使ったデータソースを作った。
でもこのあたりのテストを書こうとしてたら問題がおこった。

MISSINGTABLEEXCEPTION

Table amazons for model Amazon was not found in datasource test.

こんなエラーが発生した。

以下を参考にしたら解決策がわかった。
[PHP] CakePHP の $useDbConfig にはまった(備忘録)
フィクスチャの作成

単純にdatabase.php内に作成したデータソースの接続設定に対応する設定を作ってあげればOKです。

例えば、database.php内に以下のようなデータソースの設定を追加したなら、

public $amazon = array(
  'datasource' => 'AmazonSource'
);


合わせてdatabase.php内に以下のようなデータソースを設定する必要があります。

public $test_amazon = array(
  'datasource' => 'AmazonSource'
);

変数名は上記のように、$test_[テストしたい設定名]にしなくてはなりません。
これで終わりです。fixtureを作成する必要もありません。
あとはテストを書くだけです。

2014年4月23日水曜日

[CakePHP]AppControllerのbeforeFilterが2回以上呼び出されている時。

「Missing View」のエラーがでる時なんかは、継承しているAppControllerのbeforeFilterが2回以上呼ばれていることがわかった。

また、それぞれのControllerでbeforeFilterを完全に上書きoverrideしている時でも(つまり、parent::beforeFilter();を書かないケース)、Missing View等のエラー画面を出すのにAppControllerのbeforeFilterが呼ばれてしまう。(せっかく完全上書きしてAppControllerのbeforeFilterを使わないようにしているのに)

具体的に言うと、AppControllerでは各Controllerで共通で使う、cookieを使った自動ログイン処理を入れたりするが、UserControllerだけはlogout処理等があるため自動ログインしたくない場合などに困る。(logout処理した直後に自動ログイン処理が走ってしまう。cookieはresponseが完了するまで反映されないため)

2014年4月1日火曜日

[CakPHP]]Warning (512): SplFileInfo::openFile(/path/to/app/tmp/cache/cake_toolbar_cache20dabd4b42c7aea65f12ba6b3ed09c3b): failed to open stream: Permission denied [CORE/Cake/Cache/Engine/FileEngine.php, line 355]

こんな感じのエラーが出たら、とりあえず、

cd app/tmp/cache
chmod 666 cake_toolbar_cache20dabd4b42c7aea65f12ba6b3ed09c3b

をやればエラーは出なくなる。

SplFileInfo::openFile(/app/tmp/cache/persistent/cake_core_cake_console_):failed to open stream:Permission denied in /lib/…/FileEngine.php line 293

ここを参考にした。core.phpをいじることで恒常的に直せそうだけども今回は暫定対処で。

2014年3月31日月曜日

[CakePHP]Access forbidden!が出た時

画面に以下の様に出て、
Access forbidden!

要求されたオブジェクトへのアクセス権がありません。 読み込みが許可されていないか、 サーバが読み込みに失敗したかでしょう。

サーバーの障害と思われる場合は、ウェブ管理者までご連絡ください。

Error 403

error_logに以下のように出てる時、

[Mon Mar 31 18:07:45.124983 2014] [authz_core:error] [pid 35957] [client 127.0.0.1:51200] AH01630: client denied by server configuration: /path/to/git/inqueue/home

対象アプリのhttpd.confのDirectoryタグ、virtulhostを利用している場合は、httpd-vhosts.confのVirtualHostタグ内のDirectoryタグに、以下を入れる。

Require all granted

[CakePHP]pcfg_openfile: unable to check htaccess file, ensure it is readable and that 'app/webroot/' is executable

[Mon Mar 31 13:57:42.425142 2014] [core:crit] [pid 11472] (13)Permission denied: [client ::1:55072] AH00529: /path/to/git/test01/app/webroot/.htaccess pcfg_openfile: unable to check htaccess file, ensure it is readable and that '/path/to/git/test01/app/webroot/' is executable


error_logにこんなようなエラーが出たら、webrootをchmod 777したなおりました。

2014年3月16日日曜日

[CakePHP]error.logのfailed to open stream: Permission denied

以下のようなエラーが出た。



なので、以下で対応。

sudo chmod 774 app/tmp/logs/error.log

このあたりを参考にした。

2014年3月14日金曜日

[CakePHP]Console/cake実行時のエラーへの対処

※CakePHP2.4.6を使用

migrationを実行しようとし、以下のコマンドを実行した。

 $ Console/cake Migrations.migration generate

 そしたら次のようなエラーが。

PHP Warning: strtotime(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected 'Asia/Tokyo' for 'JST/9.0/no DST' instead in /Users/araky/git/test01/Vendor/cakephp/cakephp/lib/Cake/Cache/CacheEngine.php on line 60
Warning: strtotime(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected 'Asia/Tokyo' for 'JST/9.0/no DST' instead in /Users/araky/git/test01/Vendor/cakephp/cakephp/lib/Cake/Cache/CacheEngine.php on line 60
PHP Warning: strtotime(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected 'Asia/Tokyo' for 'JST/9.0/no DST' instead in /Users/araky/git/test01/Vendor/cakephp/cakephp/lib/Cake/Cache/CacheEngine.php on line 60
Warning: strtotime(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected 'Asia/Tokyo' for 'JST/9.0/no DST' instead in /Users/araky/git/test01/Vendor/cakephp/cakephp/lib/Cake/Cache/CacheEngine.php on line 60
Cake Migration Shell
---------------------------------------------------------------
Do you want generate a dump from current database? (y/n) [y] >

 以下のサイトの通りに対処で消えた。
CakePHPのインストールでエラーを消していく


他のサイトで、php.iniの設定をいじるのがあったけど直らなかった。
また、core.phpではなく、bootstrap.phpに同じ設定を入れてみたが、エラーは消えなかった。