Edited at

CakePHP3 で開発環境準備(実務編究極チュートリアル)

More than 3 years have passed since last update.


インストール

まずはcomposerでプロジェクト作成。

sudo php composer.phar create-project --prefer-dist -s dev cakephp/app myApp

途中で以下のように質問されるようになった。

Set Folder Permissions ? (Default to Y) [Y,n]? Y

無事終了したら、パーミッションを変えておく(OSXのみ)

sudo chown -R mymac:staff myApp

ここで一旦 .gitignore を編集してコミットしておく。tmpとlogsをコメントアウトして、

自分の環境でよくあるやつを入れておく。


.gitignore

/vendor/*

/config/app.php
#/tmp/*
#/logs/*

.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
Icon?
ehthumbs.db
Thumbs.db
*.swp
*.un~
*~
*BAK


あとはいつものgit処理で。

git init

git add .
git commit -m "Initialize repository"
git remote add origin https://github.com/xxxxxxx/myApp.git
git push -u origin master

pushしたらtmpとlogsのコメントアウトを外しておく。


.gitignore

/tmp/*

/logs/*

一旦サーバー起動してみて動作確認。

cd myApp

bin/cake server


テスト

次に、test(phpunit)ができるようにしておく。

composer.jsonを編集する。

suggest に入っている phpunit と codesniffer を require-dev に移す。


myApp/composer.json

    "require-dev": {

"d11wtq/boris": "1.0.*",
"cakephp/debug_kit": "3.0.*-dev",
"cakephp/bake": "dev-master",
"phpunit/phpunit": "*",
"cakephp/cakephp-codesniffer": "*"
},

composerのアップデートをかける。

sudo php ../composer.phar update

無事入ったら、phpunitの動作確認。

vendor/bin/phpunit

この時点で(もっとそれより早くて構わないが)データベースを準備しておく。

開発用のDBにはテーブル(例:articles)も作っておく。

テスト用のDBは空っぽ(テーブル無し)でよい。

config/app.php を編集してDB接続情報を入れておく。


config/app.php

    'Datasources' => [

'default' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
'username' => 'root',
'password' => 'password',
'database' => 'sample_db',
'encoding' => 'utf8',
//'timezone' => 'UTC',
'cacheMetadata' => true,

],

/**
* The test connection is used during the test suite.
*/

'test' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
'username' => 'root',
'password' => 'password',
'database' => 'test_sample_db',
'encoding' => 'utf8',
//'timezone' => 'UTC',
'cacheMetadata' => true,
'quoteIdentifiers' => false,
],
],


で、以下のコマンドで、テーブルのモデルをbakeコマンドで自動生成。

(するとtestsディレクトリ以下にテストケースとフィクスチャーも自動生成される)

bin/cake bake model Articles

再びテストしてみる。(以下でテスト結果が出るはず)

vendor/bin/phpunit

ついでにカバレッジも作ってみる。

vendor/bin/phpunit --coverage-html webroot/coverage

webroot/coverage 以下に index.html が生成されるのでブラウザで開いてみる。


コーディング規約

さらにコードスニッッファーの設定をする。

以下のコマンドを打つ。

sudo vendor/bin/phpcs --config-set installed_paths vendor/cakephp/cakephp-codesniffer

さっきbakeで自動生成されたソースファイルを適当にいじる。

(コードスニッッファーにひっかかるようにコーディング規約を違反する)

いじったら、以下で実行。

vendor/bin/phpcs --standard=CakePHP src/

こんな感じに出る。

FILE: ...myApp/src/Model/Table/ArticlesTable.php

----------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
----------------------------------------------------------------------
24 | ERROR | Space found after object operator
----------------------------------------------------------------------

Time: 343ms; Memory: 6.75Mb


タスクランナー

まずは Grunt をグローバルにインストールする

(が、これは多分やってある人もいるはず。なのでその人は次へ。)

npm install -g grunt-cli

次に初期化する。

npm init

いろいろ質問されるが、適当にエンターして完了。(まじめに入力してもよい)

これで package.json が作られる

次に grunt と grunt-phpunit をインストールする。

npm install grunt --save-dev

npm install grunt-phpunit --save-dev

同じく直下に以下のような Gruntfile.js を作成する。


Gruntfile.js

module.exports = function(grunt) {

grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
phpunit: {
classes: {
dir: 'tests/TestCase/'
},
options: {
bin: 'vendor/bin/phpunit',
bootstrap: 'tests/bootstrap.php',
colors: true
}
},
});

grunt.loadNpmTasks('grunt-phpunit');

// Default task(s).
grunt.registerTask('default', ['phpunit']);
};


で、gruntコマンド実行。

grunt

結果は以下。よさそうである。

Running "phpunit:classes" (phpunit) task

Starting phpunit (target: classes) in tests/TestCase/
PHPUnit 4.5.0 by Sebastian Bergmann and contributors.

Configuration read from /Users/mymac/myApp/phpunit.xml.dist

IIIII

Time: 1.79 seconds, Memory: 8.50Mb

OK, but incomplete, skipped, or risky tests!
Tests: 5, Assertions: 0, Incomplete: 5.

Done, without errors.

コーディング規約の方も同じく設定する。

npm install grunt-phpcs --save-dev

ついでなので、見た目をグラフィカルにしてみる。(以下のプラグインで実行時間の棒グラフ表示が追加になる)

npm install time-grunt --save-dev

Gruntfile.js に追加。


Gruntfile.js

module.exports = function(grunt) {

require('time-grunt')(grunt);

grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
phpunit: {
classes: {
dir: 'tests/TestCase/'
},
options: {
bin: 'vendor/bin/phpunit',
bootstrap: 'tests/bootstrap.php',
colors: true
}
},
phpcs: {
application: {
dir: ['src/**/*.php']
},
options: {
bin: 'vendor/bin/phpcs',
standard: 'CakePHP'
}
}
});

grunt.loadNpmTasks('grunt-phpunit');
grunt.loadNpmTasks('grunt-phpcs');

// Default task(s).
grunt.registerTask('default', ['phpunit', 'phpcs']);
};


最後に watch を追加して、ファイルに編集があったら自動で実行されるようにしておく。

npm install grunt-contrib-watch --save-dev

以下追加・編集部分のみ。


Gruntfile.js

    grunt.initConfig({

watch: {
phpunit: {
files: ['src/**/*.php', 'src/**/*.ctp', 'tests/TestCase/**/*.php'],
tasks: ['phpunit'],
options: {
spawn: false,
},
},
phpcs: {
files: ['src/**/*.php'],
tasks: ['phpcs'],
options: {
spawn: false,
},
},
}
});

grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['watch']);



JavaScript設置

JavaScript は Browserify を使ってモジュール単位で開発したいので、その準備をする。

とりあえずソースファイルは、 src/assets/scripts というディレクトリに設置することにして、

mkdir -p src/assets/scripts

その中に main.js と Hello.js をひな形として設置。

(単にHelloを返すだけのHello.jsをmain.jsからrequireして実行してるだけ)


src/assets/scripts/main.js

(function(){

'use strict';
var Hello = require('./components/Hello');
var hello = new Hello();
console.log(hello.message);
})();


src/assets/scripts/components/Hello.js

function Hello() {

'use strict';
this.message = 'Hello!';
}

module.exports = Hello;


これを Browserify で合体して webroot/js 以下に書きだそうという狙いである。

browserifyのgruntプラグインを入れる。

npm install grunt-browserify --save-dev

だがここで jquery も使えた方が便利なので、bower でインストールしておく。

bower init

いろいろ質問されるが、適当にエンターして完了。

bower.json ができる。

引き続きjQueryをインストール

bower install jquery --save

これでjqueryが入ったので、jqueryの場所をpackage.jsonに記述しておく。


package.json

  "browser": {

"jquery": "./bower_components/jquery/dist/jquery.js"
}

最後、Gruntfile.js に以下追加。


Gruntfile.js

        browserify : {

dist : {
src : 'src/assets/scripts/main.js',
dest : 'webroot/js/build.js' // 出力するファイル名
}
},
watch: {
browserify: {
files: ['src/assets/**/*.js'],
tasks: ['browserify'],
options: {
spawn: false,
},
},
}

grunt.loadNpmTasks('grunt-browserify');


jquery使うぞってことで main.js の上の方に以下を追加。


src/assets/scripts/main.js

var $ = require('jquery');


grunt コマンドを走らせて、watch状態になったら、先ほどのHello.jsを適当に編集してみる。

結果、

Running "watch" task

Waiting...
>> File "src/assets/scripts/components/Hello.js" changed.

Running "browserify:dist" (browserify) task

となって、webroot/js/ 以下に build.js が生成されていれば成功だ。


JavaScriptテスト

qunit は今さら〜ってことなので mocha にしてみる。

まずは npmでインストールする。

npm install -g mocha

テスト用jsの設置先を tests/scripts 以下にする。(新規ディレクトリ作成)

mkdir tests/scripts

vim tests/scripts/test.js

エディターで以下のようなテストスクリプトを保存する。


tests/scripts/test.js

var assert = require("assert");

describe('Array', function(){
describe('#indexOf()', function(){
it('should return -1 when the value is not present', function(){
assert.equal(-1, [1,2,3].indexOf(5));
assert.equal(-1, [1,2,3].indexOf(0));
});
});
});


ここで mocha の動作確認。

mocha tests/scripts/test.js

以下のように出ればOKだ。

  Array

#indexOf()
✓ should return -1 when the value is not present

1 passing (10ms)

実際的なテストを追加する。


tests/scripts/test.js

var assert = require("assert");

var Hello = require('../../src/assets/scripts/components/Hello');
var hello = new Hello();

describe('Array', function(){
describe('#hello()', function(){
it('should return Hello when we call hello.message', function(){
assert.equal('Hello!!', hello.message);
});
});
});


続いてgruntから実行するのに、 grunt-simple-mocha を入れる。

npm install grunt-simple-mocha --save-dev

Gruntfile.jsに以下を追加。


Gruntfile.js

        simplemocha: {

options: {
ui: 'bdd',
reporter: 'nyan'
},
all: { src: ['tests/scripts/**/*.js'] }
},
watch: {
simplemocha: {
files: ['src/assets/**/*.js', 'tests/scripts/**/*.js'],
tasks: ['simplemocha'],
options: {
spawn: false,
},
},
}

grunt.loadNpmTasks('grunt-simple-mocha');


再び、

grunt

して、watch状態になったら、テストファイルを書き換えてみる。

以下のようになれば良い。

Running "watch" task

Waiting...
>> File "tests/scripts/test.js" changed.

Running "simplemocha:all" (simplemocha) task
4 -_-_-_,------,
0 -_-_-_| /\_/\
0 -_-_-^|__( ^ .^)
-_-_- "" ""

4 passing (17ms)

この辺で、だんだんGruntfile.jsが大きくなってきて見づらくなってきたと思う。

それがgruntの弱点だとも言われているが、そもそも先人たちが作り上げた巨大化したGruntfile.jsを最初っからバーンと渡されてこれでやるんだと説明されても、もう既に中身を読む気にならない状態な訳である。

なのでここではこうやって、たらたらと一つずつ設定している。

自分で一つずつ設定していって、最後、消すなら消す、使うなら残す、追加するなら追加する、自分で考えて決めればよい。


コントローラとテンプレート

JavaScriptのテストまでやったが、まだその結合したjsが実際に動いているのかどうか、確認していない。

ということで、ここで再びbakeコマンドを使ってコントローラとテンプレートを作成し、jsファイルを埋め込んでみる。

Articlesモデルは作成済みなので、同じように、

bin/cake bake controller articles

bin/cake bake template articles

とやって、

bin/cake server

でサーバーを起動、 http://localhost:8765/ にアクセスする。

src/Template/Layout/default.ctp を開いて、ヘッダー部分に以下を追加。


src/Template/Layout/default.ctp

    <?= $this->Html->script('build') ?>


http://localhost:8765/articles を開いてみて、コンソールにHelloと出ていればOKである。

動いている。


JavaScript構文チェック

引き続きjshintで構文チェックを行う。

npm install grunt-contrib-jshint --save-dev

Gruntfile.jsに以下を追加。


Gruntfile.js

        jshint: {

options: {
jshintrc: true
},
files: ['src/assets/**/*.js', 'tests/scripts/**/*.js']
},
grunt.loadNpmTasks('grunt-contrib-jshint');

とりあえず実行してみると、

grunt jshint

特に mocha のテストのところでエラーが大量にでる。

.jshintrc ファイルを作成してオプションを設定。

とりあえずこんな感じにしてみた。

{

"curly": true,
"eqnull": true,
"eqeqeq": true,
"undef": true,
"expr" : true,
"globals": {
"jQuery": true,
"module": false,
"require": false,
"console": false,
"describe": false,
"it": false,
"before": false,
"beforeEach": false,
"after": false,
"afterEach": false
}
}

watch はさっきのsimplemochaのところに追加しておけばいいだろう。


Gruntfile.js

            simplemocha: {

files: ['src/assets/**/*.js', 'tests/scripts/**/*.js'],
tasks: ['simplemocha', 'jshint'],
options: {
spawn: false,
},
},


コードの圧縮とsourceMap

grunt-contrib-uglify のインストール。

npm install grunt-contrib-uglify --save-dev

Gruntfileの編集。圧縮して build.min.js を作る。


Gruntfile.js

        uglify: {

my_target: {
options: {
sourceMap: true
},
files: {
'webroot/js/build.min.js': ['webroot/js/build.js']
}
}
},
watch: {
simplemocha: {
files: ['src/assets/**/*.js', 'tests/scripts/**/*.js'],
tasks: ['simplemocha', 'jshint', 'uglify'],
options: {
spawn: false,
},
},
}

grunt.loadNpmTasks('grunt-contrib-uglify');


テンプレート(レイアウト)の書き換え。


src/Template/Layout/default.ctp

    <?= $this->Html->script('build.min') ?>



Gitリポジトリ

最後にリポジトリに登録する。

.gitignoreにいくつか追加する。


.gitignore

/webroot/coverage/*

/node_modules/*
/bower_components/*

add して commit して pushする。

git add .

git commit -m "Improve project file"
git push origin master

他の開発メンバーは、

git clone https://github.com/xxxxxxxxx/myApp.git

curl -sS https://getcomposer.org/installer | php
cd myApp/
sudo php ../composer.phar install
sudo
vendor/bin/phpcs --config-set installed_paths vendor/cakephp/cakephp-codesniffer
npm install
bower install

サーバー起動してみて。

bin/cake server

gruntでwatch開始。

grunt

以上終了。さあ、作るぞと。