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


# インストール

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

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

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

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

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

```bash
sudo chown -R mymac:staff myApp
```

ここで一旦 .gitignore を編集してコミットしておく。tmpとlogsをコメントアウトして、
自分の環境でよくあるやつを入れておく。


```git:.gitignore
/vendor/*
/config/app.php
#/tmp/*
#/logs/*

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

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

```bash
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のコメントアウトを外しておく。


```git:.gitignore
/tmp/*
/logs/*
```


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

```bash
cd myApp
bin/cake server
```


# テスト

次に、test(phpunit)ができるようにしておく。
composer.jsonを編集する。
suggest に入っている phpunit と codesniffer を require-dev に移す。

```php: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のアップデートをかける。

```bash
sudo php ../composer.phar update
```

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

```bash
vendor/bin/phpunit
```

この時点で（もっとそれより早くて構わないが）データベースを準備しておく。
開発用のDBにはテーブル（例：articles）も作っておく。
テスト用のDBは空っぽ（テーブル無し）でよい。

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

```php: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ディレクトリ以下にテストケースとフィクスチャーも自動生成される）

```bash
bin/cake bake model Articles
```

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


```bash
vendor/bin/phpunit
```

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

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

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

# コーディング規約


さらにコードスニッッファーの設定をする。
以下のコマンドを打つ。

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


さっきbakeで自動生成されたソースファイルを適当にいじる。
（コードスニッッファーにひっかかるようにコーディング規約を違反する）
いじったら、以下で実行。


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

こんな感じに出る。

```bash
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 をグローバルにインストールする
（が、これは多分やってある人もいるはず。なのでその人は次へ。）

```bash
npm install -g grunt-cli
```

次に初期化する。

```bash
npm init
```

いろいろ質問されるが、適当にエンターして完了。（まじめに入力してもよい）
これで package.json が作られる

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


```bash
npm install grunt --save-dev
npm install grunt-phpunit --save-dev
```

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


```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コマンド実行。

```bash
grunt
```

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

```bash
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.
```

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


```bash
npm install grunt-phpcs --save-dev
```

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

```bash
npm install time-grunt --save-dev
```

Gruntfile.js  に追加。

```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 を追加して、ファイルに編集があったら自動で実行されるようにしておく。

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

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

```js: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 というディレクトリに設置することにして、

```bash
mkdir -p src/assets/scripts
```

その中に main.js と Hello.js をひな形として設置。
（単にHelloを返すだけのHello.jsをmain.jsからrequireして実行してるだけ）

```js:src/assets/scripts/main.js
(function(){
	'use strict';
	var Hello = require('./components/Hello');
	var hello = new Hello();
	console.log(hello.message);
})();
```

```js:src/assets/scripts/components/Hello.js
function Hello() {
	'use strict';
	this.message = 'Hello!';
}

module.exports = Hello;
```

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

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

```bash
npm install grunt-browserify --save-dev
```

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


```bash
bower init
```

いろいろ質問されるが、適当にエンターして完了。
bower.json ができる。
引き続きjQueryをインストール

```bash
bower install jquery --save
```

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


```json:package.json
  "browser": {
	  "jquery": "./bower_components/jquery/dist/jquery.js"
  }
```

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


```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 の上の方に以下を追加。

```js:src/assets/scripts/main.js
var $ = require('jquery');
```

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

```bash
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でインストールする。

```bash
npm install -g mocha
```

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

```bash
mkdir tests/scripts
vim tests/scripts/test.js
```

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

```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 の動作確認。

```bash
mocha tests/scripts/test.js
```

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

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


  1 passing (10ms)
```

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


```js: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 を入れる。


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

Gruntfile.jsに以下を追加。


```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');
```

再び、

```bash
grunt
```

して、watch状態になったら、テストファイルを書き換えてみる。
以下のようになれば良い。


```bash
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モデルは作成済みなので、同じように、

```bash
bin/cake bake controller articles
bin/cake bake template articles
```

とやって、

```bash
bin/cake server
```


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

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

```php:src/Template/Layout/default.ctp
    <?= $this->Html->script('build') ?>
```


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

# JavaScript構文チェック

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

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

Gruntfile.jsに以下を追加。


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

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

```bash
grunt jshint
```

特に mocha のテストのところでエラーが大量にでる。
.jshintrc ファイルを作成してオプションを設定。
とりあえずこんな感じにしてみた。

```json
{
	"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のところに追加しておけばいいだろう。

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



# コードの圧縮とsourceMap

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

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

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

```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');
```

テンプレート（レイアウト）の書き換え。

```php:src/Template/Layout/default.ctp
    <?= $this->Html->script('build.min') ?>
```


# Gitリポジトリ

最後にリポジトリに登録する。
.gitignoreにいくつか追加する。

```git:.gitignore
/webroot/coverage/*
/node_modules/*
/bower_components/*
```

add して commit して pushする。

```bash
git add .
git commit -m "Improve project file"
git push origin master
```

他の開発メンバーは、

```bash
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
```

サーバー起動してみて。

```bash
bin/cake server
```

gruntでwatch開始。

```bash
grunt
```

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