CakePHP3ハンズオン用

# はじめに

このハンズオンは、次の宿題をやった人を前提にしています。

[ひよこソン宿題 - Qiita](https://qiita.com/itagakishintaro/items/b260d3b42def9a8b3fca)

上記宿題をしていなくても問題ありませんが、次の点は注意してください。

* [XAMPP](https://www.apachefriends.org/jp/index.html)がインストールされ、Apache, MySQLが起動している前提で説明する。
* `fuga/index.php`と記述した時は、XAMPPインストールフォルダ配下のhtdocsフォルダの中のという前提で説明する。
* 確認は、ブラウザで`http://localhost/fuga/index.php`のようにアクセスして確認する前提で説明する。
* 最後の方で、[Bootstrap](http://getbootstrap.com/)を使う。
* 最後の方で、[jQuery](http://jquery.com/)を使う。

# PHPの基本、参考サイト
なんといってもまずは本家を参照すべし。
[PHP マニュアル](http://php.net/manual/ja/)
ある程度感じがつかめたら、
[PHP: The Right Way](http://ja.phptherightway.com/) で正しいとされるやり方を知っておく。

参考：[CakePHPのコーディング規約] (http://book.cakephp.org/3.0/en/contributing/cakephp-coding-conventions.html)

以下、本家サイトからの引用で主な書き方を紹介。

## 絶対マスターしよう

### コメント
```php
echo 'テストです'; // C++型の単一行用のコメント
/* 複数行用のコメント
   もう一行分のコメント */
echo 'もうひとつのテストです';
echo '最後のテストです'; # シェル型の単一行用のコメント
```

### 文字列（結合と式展開）
```php
var $result = "foo" . "bar";
echo "Result is {$result}"; // Result is foobar
```

### 出力
```php
echo "Hello";
echo $result;
$a = array('apple', 'orange', 'grape');// 配列
print_r($a);
$results = print_r($a, true);// 変数に入れられる
var_dump($a);
```

### シングルクォート内の文字列
シングルクォートで囲まれた文字列では式展開されない

```php
$str = "123";
echo "$str"; // 123
echo '$str'; // $str
echo "\n";   // 改行
echo '\n';   // \n
```

### 演習
`hello/index.php`を作成し、`Hello, PHP`と表示される画面を作成する。
できたら、上記を参考に色々試してみる。

### 関数

```php
function makecoffee($type = "cappuccino") {
    return "Making a cup of $type.\n";
}
echo makecoffee("espresso");
```

### 配列とハッシュ
```php
$array = array(
    "foo" => "bar",
    "bar" => "foo",
);

// PHP 5.4 ではこのようにも書けます
$array = [
    "foo" => "bar",
    "bar" => "foo",
];

// 数値添字配列でキーを省略した書き方
$array = array("foo", "bar", "hello", "world");

$array = ["foo", "bar", "hello", "world"];
```

配列からリストに。

```php
$info = array('コーヒー', '茶色', 'カフェイン');
list($drink, $color, $power) = $info;
```

範囲指定で配列に。

```php
// array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
foreach (range(0, 12) as $number) {
    echo $number;
}
```

配列をカンマで連結して文字列に。

```php
$array = array('lastname', 'email', 'phone');
$comma_separated = implode(",", $array);
echo $comma_separated; // lastname,email,phone
```

### 演習
`fizzbuzz/index.php`を作成し、1から100までで[FizzBuzz](http://ja.wikipedia.org/wiki/Fizz_Buzz)が表示される画面を作成する。
ループや分岐は以下参考。

http://php.net/manual/ja/language.control-structures.php

できたら、関数、配列、ハッシュをあえて使ってやってみる。
ちなみに、CakePHPでは配列、ハッシュとforeachを頻繁に使うので、慣れておくこと。

## 理解はしておこう

### クラス

```php
class SimpleClass {
    // プロパティの宣言
    public $var = 'a default value';

    // メソッドの宣言
    public function displayVar() {
        echo $this->var;
    }
}
```

### コンストラクタ

```php
class BaseClass {
   function __construct() {
       print "In BaseClass constructor\n";
   }
}

class SubClass extends BaseClass {
   function __construct() {
       parent::__construct();
       print "In SubClass constructor\n";
   }
}

class OtherSubClass extends BaseClass {
    // BaseClass のコンストラクタを継承します
}

// In BaseClass constructor
$obj = new BaseClass();

// In BaseClass constructor
// In SubClass constructor
$obj = new SubClass();

// In BaseClass constructor
$obj = new OtherSubClass();
```

### クラス継承

```php
class ExtendClass extends SimpleClass {
    // 親クラスのメソッドを再定義
    function displayVar() {
        echo "Extending class\n";
        parent::displayVar();
    }
}

$extended = new ExtendClass();
$extended->displayVar();
```

# CakePHP3インストールと設定

参照：http://book.cakephp.org/3.0/en/installation.html

最初にcomposer（phpのライブラリ管理システム）をインストールする。

```bash
curl -s https://getcomposer.org/installer | php
```

composerのインストールといっても、上記コマンドで、今いるディレクトリにcomposer.pharというファイルが落ちてくるだけ。
これを使って、通常は自分がインストールしたいライブラリ情報を記入したcomposer.jsonファイルを同じ階層において、

```bash
php composer.phar install
```

とすることでライブラリをインストールする。
composer.jsonの例。

```js
{
    "require": {
        "monolog/monolog": "1.2.*"
    }
}
```
composerについて詳しく知りたい方はこちらから。[composer](https://getcomposer.org/)
*注）Macの人は`mv composer.phar /usr/local/bin/composer`としておけば、`php composer.phar`部分を`composer`に変えられるし、パスを意識しないでいいので、おすすめ。

OAuthエラー対応参考URL

http://qiita.com/fivestar/items/fc15e0bb379c791ff6b2


が、ここでは

```bash
php composer.phar create-project --prefer-dist -s dev cakephp/app [app_name]
```

とする。[app_name]のところは自分で考えた名前を入れる。
上記コマンドを実行すると[app_name]ディレクトリが作られて、その中にCakePHPが設置されている。
5分くらい時間がかかるので注意。
*注）`PHP Fatal error: Allowed memory size of XXXXXX bytes exhausted <...>`のようなエラーがでたら、
次のコマンドにする（[詳細](https://getcomposer.org/doc/articles/troubleshooting.md)）。
```bash
php -d memory_limit=-1 composer.phar create-project --prefer-dist -s dev cakephp/app [app_name]
```
*注）ここで、OSXユーザーの場合は、logsとtmpフォルダーに書き込み権限を与えておくのを忘れずに。*

```bash
chmod -R 777 tmp
chmod -R 777 logs
```

続いて

```bash
 bin/cake server
```

として開発用サーバーを起動する。

```bash
Welcome to CakePHP v3.0.0-beta3 Console
---------------------------------------------------------------
App : src
Path: C:\Users\yourname\project\src\
DocumentRoot: C:\Users\yourname\project\webroot
---------------------------------------------------------------
built-in server is running in http://localhost:8765/
You can exit with `CTRL-C`
```

というようなメッセージが出るので、ブラウザから

```html
http://localhost:8765/
```

に接続して、

![cakephp3.png](https://qiita-image-store.s3.amazonaws.com/0/4759/bdddf4d0-5de3-e724-5bd9-2044197444f2.png)

というような画面になればOKである。この画面になにがしかのエラーが出ている場合はそれを修正する。
サーバーを停止するときはコントロールキーを押しながらCを押す。

恐らくは以下のメッセージがでていると思うので、DBの設定を行う。

```html
CakePHP is NOT able to connect to the database.
```

```bash
vim config/app.php
```

として、エディタを起動し、以下のデータベース接続情報を編集する。
testを書く人はテスト用データベース接続情報も入れておくとよい。


```php:config/app.php
	'Datasources' => [
		'default' => [
			'className' => 'Cake\Database\Connection',
			'driver' => 'Cake\Database\Driver\Mysql',
			'persistent' => false,
			'host' => 'localhost',
			/*
			* CakePHP will use the default DB port based on the driver selected 
			* MySQL on MAMP uses port 8889, MAMP users will want to uncomment 
			* the following line and set the port accordingly
			*/
			//'port' => 'nonstandard_port_number',
			'username' => 'my_app',
			'password' => 'secret',
			'database' => 'my_app',
			'encoding' => 'utf8',
			'timezone' => 'UTC',
			'cacheMetadata' => true,

			/*
			* Set identifier quoting to true if you are using reserved words or
			* special characters in your table or column names. Enabling this
			* setting will result in queries built using the Query Builder having
			* identifiers quoted when creating SQL. It should be noted that this
			* decreases performance because each query needs to be traversed and
			* manipulated before being executed.
			*/
			'quoteIdentifiers' => false,

			/*
			* During development, if using MySQL < 5.6, uncommenting the
			* following line could boost the speed at which schema metadata is
			* fetched from the database. It can also be set directly with the
			* mysql configuration directive 'innodb_stats_on_metadata = 0'
			* which is the recommended value in production environments
			*/
			//'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
		],

```

# MySQL
データベースの操作にはphpMyAdminを使う方法があるが、動作が重いので、推奨しない。
XAMPPでインストールした場合は C:\xampp\mysql\bin 以下にある実行ファイルが実体なので、ここにパスを通しておけばよい。
（システム→システムの詳細設定→環境変数→Path→編集）
コマンドラインから

```bash
mysql -u root -p
```

パスワードを設定してない場合は、

```bash
mysql -u root
```

でDBに入れるので

データベース作成

```sql
CREATE DATABASE sample_db DEFAULT CHARACTER SET utf8;
```

ユーザー設定

```sql
GRANT ALL ON sample_db.* TO myuser@localhost IDENTIFIED BY 'password';
```

使用するデータベースを指定

```sql
use sample_db
```

あとは使いたいsqlを流せばよい。
参考：http://xref.jp/database
終了するときは

```sql
\q
```
もしくは

```sql
quit
```

### 演習
演習用のデータベースを作成し、接続用のユーザーを設定し、CakePHPの設定に記述して、CakePHPから接続できることを確認してみましょう。


# テーブル作成

例えばブログの記事を保存するテーブルは以下のようなsqlになる。

```sql
CREATE TABLE articles (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(50),
    body TEXT,
    created DATETIME DEFAULT NULL,
    modified DATETIME DEFAULT NULL
);
```
命名規約：http://book.cakephp.org/3.0/en/intro/conventions.html

* テーブル名は英単語の複数形で、アンダースコアを用いる。
* テーブルには必ずidという名前のプライマリーキーを持たせる。
* 2つ以上の単語を使うフィールド名はアンダースコアで連結：first_name。

* 外部キーはその関連する「テーブル名の単数形_id」とする。
* created と modified はお約束で、登録日時と更新日時が自動でインサート（アップデート）される。

### 演習
CREATE TABLE文を作成し、演習用のデータベースにテーブルを作成してみましょう。
ブログの記事とコメントなど、思いついたものをテーブルにしてみます。

# BakeとMVC

bakeコマンドを使って自動で、MVC(モデル、ビュー、コントローラ）を作成します。
今回articlesテーブルが作ってあるので、それについて操作する。
コマンドラインから、

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

と打つ。

```bash
Welcome to CakePHP v3.0.0-beta3 Console
---------------------------------------------------------------
App : src
Path: C:\Users\yourname\project\src\
---------------------------------------------------------------
One moment while associations are detected.

Baking table class for Articles...
Baking entity class for Article...
Baking test fixture for Articles...
Baking test case for App\Model\Table\ArticlesTable ...
```

というような表示が出て、モデルに必要なファイルが自動生成される。
アソシエーションについては現時点のbakeではuser_idなどの命名規則に従ったフィールド名を元に自動で生成されるので、モデル作成後必ずソースを確認すること。

続いて、コントローラを作成する。

```bash
bin/cake bake controller Articles
```

```bash
Welcome to CakePHP v3.0.0-beta3 Console
---------------------------------------------------------------
App : src
Path: C:\Users\yourname\project\src\
---------------------------------------------------------------

Baking controller class for Articles...
Baking test case for App\Controller\ArticlesController ...

```

コントローラが作られる。続いてビューも同様に。

```bash
bin/cake bake view Articles
```

ブラウザから生成物を確認する。

```html
http://localhost:8765/articles/
```

登録、編集、削除、一覧ができるようになっている。

この自動生成されたファイルを元に作っていくので、ざっくり中身を確認しておく。

### 演習
前項で作成したテーブルに関して、bakeコマンドでモデル、コントローラ、ビューを作成し、ブラウザから確認してみましょう。



# デバッグ

http://localhost:8765/articles/add

登録画面（New Article）からなにかデータを登録した後、一覧画面からViewをクリックし、画面右下にあるCakePHPアイコンをクリックしてみる。

![cakephp3-2.png](https://qiita-image-store.s3.amazonaws.com/0/4759/a3911578-e3f6-0b32-c969-0b90aeb130ff.png)

ブラウザの最下部に黄色のメニューバーが表示されるので、クリックして内容を確認する。
Sql Logではこのページで実行されたクエリーを確認できる。

変数に何が入っているのか手っ取り早く確認したい時は、

```php
debug($article);
```

のように書けばよい。

```php
debug($query = $this->Articles->find());
```

のように書けばこのfindメソッドで生成されるSQLも確認できる。

デバッグログ（ファイル）に出力したい時は、
以下を追加しておいて、

```php
use Cake\Log\Log;
```

出力したい場所で以下のように書く。

```php
Log::write('debug', print_r($article, true));
```

例えば以下のようにコントローラのviewメソッドにデバッグログ書き出しを仕込んでみると、

```php:src/Controller/ArticlesController.php
	public function view($id = null) {
		$article = $this->Articles->get($id, [
			'contain' => []
		]);
		Log::write('debug', print_r($article, true));
		$this->set('article', $article);
	}
```

ビューにsetされている$articleはオブジェクトになっていることが分かる。

### 演習
前項で自動生成されたソースを見て、変数をdebugしてみましょう。
                                     
# select 

ここでCakePHP3のモデルについての説明。

```php
$query = $this->Articles->find();
```

こういう書き方でクエリーオブジェクトなるものが作られる。
（まだSQLは実行されない）
で、

```php
$query->all();
```

と書けばSQLが実行される。
つまりこれでワンセット。

```php
$query = $this->Articles->find();
$query->all();
```

で、以下のように書けば

```php
$results = $query->all();
foreach ($results as $result) {
    echo $result->id;
    echo $result->created;
}
```

とすれば取り出せる。$resultsがビューに渡されて、ビュー側でにforeach以下で値を取り出しているということ。

```php
$query = $this->Reports->find();
foreach ($query as $result) {
    echo $result->id;
    echo $result->created;
}
```

こう書いても結果はおんなじ。all()は省略可能ってことです。

その他、クエリーのコンディション等はメソッドチェーンで記述する。以下を参照のこと。
参考：http://book.cakephp.org/3.0/en/orm/query-builder.html#selecting-data

### 演習
参考ページをみながら、独自のクエリーを実行するメソッドを作ってみましょう。

# insert delete update

Bakeで作成したコントローラのメソッドadd, delete, edit を参考にしてください。


# Form

フォームヘルパーは以下参照：
http://book.cakephp.org/3.0/en/views/helpers/form.html

大きな変更はないが、phpの配列の書き方が変わったので注意のこと。
例えば以下のようなaddフォームにBootstrapのform-controlクラスを追加する場合は、

```html+php:src/Template/Articles/add.ctp
	<?= $this->Form->create($article); ?>
	<fieldset>
		<legend><?= __('Add Article') ?></legend>
		<?php
			echo $this->Form->input('title');
			echo $this->Form->input('body');
		?>
	</fieldset>
	<?= $this->Form->button(__('Submit')) ?>
	<?= $this->Form->end() ?>
```

以下のようにすればよい。

```php
echo $this->Form->input('title', ['class' => 'form-control']);
```

出力結果

```html
<input type="text" id="title" maxlength="50" class="form-control" name="title">
```

### 演習
Bootstrapを組みこんで、スタイルを適用してみましょう。



# ビュー

BootstrapやjQueryを使う場合はLayoutのdefault.ctpに以下のように書いて、webroot以下のフォルダーにファイルを置けばよい。

```php
	<?= $this->Html->css('bootstrap.min') ?>
	<?= $this->Html->script('bootstrap.min') ?>
```

詳しくは、
http://book.cakephp.org/3.0/en/views.html

レイアウトを使わない時はコントローラに

```php
$this->layout = false;
```

そもそもビューを使わない時は

```php
$this->autoRender = false;
```

### 演習
ビューを使わずに、たんなるJSONデータを返すようなメソッドを作ってみましょう。
（Web APIとして利用できるので便利。）

答え：
https://gist.github.com/uedatakeshi/4856c087d105b25cc0f4

# 参考サイト


http://book.cakephp.org/3.0/en/contents.html
http://php.net/