PHPカンファレンス関西2015 〜AWSでのRESTfulアプリ開発ワークショップ実演〜


本投稿は、2015/5/30開催の[PHPカンファレンス関西2015 - 関西最大のPHPイベント](http://conference.kphpug.jp/2015/)のブーストラックで行われる弊社のセッション、[AWSでのRESTfulアプリ開発ワークショップ実演(13:00-13:30)](http://conference.kphpug.jp/2015/timetable/)の内容を公開するものです。

## メニュー

- 開発用サーバ作成
- 作ったサーバのPHPに関する設定は？
- GitHubからTODOアプリのソース取得
- 取得したソースの中身は？
- ソース詳細
- POSTMANでAPIの動作確認

## 作成する開発用サーバ

OSや基本的なソフトウェアがインストールされたマシンイメージをAWSではAMI(Amazon Machine Image)と呼びます。
今回は、弊社で作成したAMIを元にEC2インスタンスを作成します。
　:warning:弊社がそのAMIを作成した手順については
[AWS上で構築するRESTfulアプリ勉強会～Web開発ワークショップ～【第1回】の事前準備内容まとめ - Qiita](http://qiita.com/k_shimoji/items/72cec98b8030c59ac019)
で公開しています。参考にしてください。
　:warning:今回用に作成したAMIとはちょっと設定ファイルの記載内容などの違いは有ります。

### AMIにインストール済みのプロダクト

予め下記プロダクトをインストール済みです。
今回の手順で開発環境を作成すればすぐに使用可能となります。

|プロダクト     |バージョン|
|-------------|--------|
|apache			|2.4.10-1.59|
|php			|5.5.20-2.94|
|mysql			|5.6.14-3|
|phpMyAdmin	|4.0.10.7-1|
|git			|2.1.0|
|xdebug			|2.2.3-1|


## 使用するツールなど、必要なもの

- awsアカウント
	- 必須です！EC2インスタンスが作成できることを事前に確認しておくとベターです。

以下、別のツールでも代替できますが、説明は下記ツールをベースに行います。

- chromeブラウザ
- postman 
	- 作成したAPIのテストツールchromeブラウザの拡張機能です。
		- [Postman - REST Client - Chrome ウェブストア](https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm)

	- 似たようなツールはたくさんありますので、愛用のものがあればそちらで結構です。
- ターミナルソフトウェア
	- Mac, Linuxの方は標準のターミナルでよいです。
	- Windowsの方は、別途ターミナルソフトウェアをご準備ください。
		- 当資料では、Macの標準ターミナルと、Windows上のputtyでの実行例を併記しています。
			- 事前に[PuTTY Download Page](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html)よりインストールしておいてください(インストーラでインストールしたくない場合は[putty.zip](http://the.earth.li/~sgtatham/putty/latest/x86/putty.zip)をダウンロードしてすきなところに解凍すればOKです。

## 開発用サーバ作成

では、鈴木商店にて事前準備済みのAMIから以下の手順でEC2インスタンスを作成します。

### 1. AWSマネージメントコンソールにログイン

ログイン後、下図のEC2をクリックします。
![AWS_マネジメントコンソール.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/e3e9539e-0e93-695a-acb8-aa7b19cf8deb.png "AWS_マネジメントコンソール.png")

### 2. 元にするAMI(マシンイメージ)を検索

下記手順で当該のAMIを表示させます。

下記AMIの部分をクリックします。

![EC2_Management_Console2.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/6d88cd3b-0fbb-950a-a8c0-d74057d86612.png "EC2_Management_Console2.png")


次に、下記の通り操作します。

リージョンが東京になっていることを確認。

画面上部右端にリージョンの表示があります（東京になっている場合の図）。
![region.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/8c9c34eb-02bb-ecc9-9116-46b722276f9b.png "region.png")


東京になっていない場合、下図のように東京リージョンを選択してください。

![region_tokyo.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/252acf39-8120-faea-e636-f9a1282f1ac7.png "region_tokyo.png")

AMIを検索します。

- フィルタを[Public Images]に設定
- AMI ID `ami-fed908fe`で検索。php-conf-2015-suzukishoutenという名前のAMIが見つかります。

![ami.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/2c08b66f-f7a7-0202-7abe-dbbc55f8710b.png "ami.png")

見つかったら、選択して[作成]ボタンをクリック。
![launch.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/f0b0a0bf-9a73-790d-2184-48c3de4735d5.png "launch.png")


以降、作成するインスタンスの設定をしていきます。

### 3.インスタンスタイプを選択

t2.microを選択して[次の手順]。

![step2.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/9f0d101b-85fb-7051-db89-9693f28bca8b.png "step2.png")

### 4.インスタンス詳細設定

基本はデフォルトで[次の手順]クリックでOKです。。
`誤った削除から保護します`にチェックを入れていますが、ここにチェックしておくと、インスタンス完成後に間違ってい削除しようとした時に救われます。(このチェックを外してからでないと削除できなくなるので安全です）
ですので、チェックを入れることをおすすめします。

![step3.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/0c261a20-faee-c374-403f-9ea605a8bbfe.png "step3.png")


### 5.ストレージ設定

ストレージを追加します。デフォルトでOKです。[次の手順]クリック。

![step4.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/dc716f0f-2e8e-b9cb-e0b2-12cc9d95b0cb.png "step4.png")

### 6.タグ設定
 
 好きな名前をつけて[次の手順]クリック。 

:warning:ここでは`phpconf_2015`と命名。

![step5.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/2eea9391-36df-4fa1-41f3-0ecbc5bee502.png "step5.png")

### 7.セキュリティグループ設定

アクセスを許可するプロトコルやポートを設定します。
下記の通り設定し、[確認と作成]をクリック。

- 「新しいセキュリティグループを作成する」を選択
- 「セキュリティグループ名」 好きな名前(スクリーンショットでは、`web-public`としています。)
- 説明 好きな説明文(スクリーンショットでは、`phpconf_2015`としています。)
- SSH 任意:warning:
- Http 任意:warning:


:warning:ssh, httpは今回必須です。標準のポートで設定します。
ソースIPアドレスは、スクリーンショットでは`任意`（どこからでもアクセス可能）にしています。（セキュリティの警告が出ますが、今回はこれでOK）。
![step6.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/1e70121e-b26b-8807-943b-eff99b57be4d.png "step6.png")

### 8.確認画面

これまで設定した内容のサマリ。問題なければ[作成]クリック。
（警告が出てますが、上記と同様なのでOK）

![step7.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/e37f50af-e5ee-28c3-2497-7b6350b8242d.png "step7.png")

### 9.キーペア作成

SSHアクセスに必要なキーペアを作成します。
`新しいキーペアの作成`を選択し`キーペア名`に作成するキーペアの名前を入力します。
:warning:既に作成済みのキーがあり、それを使用するなら、`既存のキーペアの選択`を選択し、さらに`キーペアの選択`欄で鍵を選択します。。
:warning:ここでは、`phpconf_2015`と命名。

![key_pair.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/3c99e27a-b474-da66-6271-9ab74dc5c516.png "key_pair.png")

[キーペアのダウンロード]をクリックすると秘密鍵の書かれたテキストがダウンロードされるので、拡張子を`.pem`にして保存しておきます。
このあたり、MacとWindowsでは少し違いますので説明します。

#### Macの場合

MacやLinuxでは、sshの鍵は一般的にユーザのホームディレクトリの下の`.ssh`ディレクトリに保存します。(`~/.ssh`)。
今回はそれに倣います。
コピーした.pemファイルは読み取り専用にしておきます(`chmod 600`)。
通常、`.`付きのファイルやディレクトリはFinderからは見えませんので、ターミナルで作業します。
以下、Mac上での作業です。

- sshディレクトリ作成（ない場合)

`cd ~/`でユーザのホームディレクトリに移動、`mkdir`でディレクトリ作成。

```bash
cd ~/
mkdir .ssh
```

- ダウンロードした鍵ファイルをコピー

:warning:鍵のファイル名は`phpconf_2015.pem`としました。

```bash
cp ダウンロードした鍵ファイルのフルパス ~/.ssh/phpconf_2015.pem
chmod 600 ~/.ssh/phpconf_2015.pem
```

#### Windowsの場合

今回はputtyを使用した接続を行います。
puttyの場合、ダウンロードした鍵をputty用の形式に変換し、変換後の鍵を使用する必要があるので、いったん好きなところに適当な名前で保存してください。(鍵の変換方法は後述）
:warning:以降のスクリーンショットでは、デスクトップに`phpconf_2015.pem`で保存したイメージになっています。
:warning:以降の説明でも秘密鍵のファイル名は"phpconf_2015.pem"で表記します。

これで鍵の準備はOKです。


[インスタンスの作成]がクリック出来るようになっていますので、クリックします。

![create_instance.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/d1b50b1a-973e-3e8c-552a-f717d8b294d7.png "create_instance.png")

### 10.サーバ完成!

下記画面が出ればOKです![インスタンスの表示]をクリックします。

![finish.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/8b88e65d-fd7a-f092-d2cc-e8ecc97a1e54.png "finish.png")


インスタンス一覧に今作成したインスタンスが表示されました!

![view_instance.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/f2e4d8e7-2465-6033-c356-ab7712f07803.png "view_instance.png")

右の方にスクロールすると、[パプリックIP]が表示されています。
この後、ブラウザからのアクセス、SSHでのアクセスは全てこのIPアドレスに対して行います。
忘れたらこの画面で確認して下さい。
:warning:以降の説明では、このアドレス部分は"[Public IP]"と表記します。

![public_ip.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/09391479-9466-9fd8-13a2-f5c71afac39c.png "public_ip.png")


### 11. ブラウザでアクセスしてみる

http://[Public IP]/
にアクセスすると、apacheのデフォルトルートドキュメントが表示されます。

![apache.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/e021473f-36a7-d7ed-5b21-6483d6a69104.png "apache.png")


### 12. SSHでアクセスしてみる

Mac, Windowsでやりかたが違いますので、それぞれ記載します。
:warning:Linuxを使用している方はほぼMacと同じはずですので割愛します。

#### Macの場合

標準のターミナルを起動します。

ここからはターミナル上で操作します。

sshを実行し、`ec2-user`というユーザでログインします。
コマンドラインは下記の通り、`ssh -i 秘密鍵のパス ユーザ名@接続先ホスト名(IPアドレス)`です。

```bash
ssh -i ~/.ssh/phpconf_2015.pem ec2-user@[Public IP]
```

うまく行けばこんな感じで表示されます。
※パッケージをアップデートしろと言われますがとりあえず無視。

```bash

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2014.09-release-notes/
39 package(s) needed for security, out of 177 available
Run "sudo yum update" to apply all updates.
Amazon Linux version 2015.03 is available.
```

#### Windowsの場合

puttyでの例です。

##### ダウンロードした鍵をputty用に変換

PUTTYGEN.EXEを起動します。
![puttygen.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/4b0204fa-3db7-5615-5b56-6ddb0ffc4828.png "puttygen.png")

[Load]をクリックします。
![puttygen_1.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/813c5214-b7ad-4803-13da-8135663f2dbb.png "puttygen_1.png")

デフォルトでは、拡張子`.pem`は表示されないので、[All Files]を選択し、ダウンロードした鍵ファイルを選択し、開きます。

![win_pem.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/816068cf-2780-9985-ec50-6c81d71a62ab.png "win_pem.png")

これでOK。

![win_pem_ok.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/db633e19-e724-7973-e116-ff08af89a67b.png "win_pem_ok.png")

[Save private key]をクリック。
![puttygen_4.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/6e95d85b-f1c4-85ef-478c-83920fd16d66.png "puttygen_4.png")

パスフレーズがないという警告が出ますが構わず[はい]。
![puttygen_5.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/1ec37f2a-9059-1f0b-5d6b-c97875bf6b99.png "puttygen_5.png")

ファイル名を入力して保存（拡張子は、`.ppk`にします）。
これで鍵の変換は終了。
:warning:今回はデスクトップに保存していますが、本来はもう少し深いところにおいてください。
![win_ppk.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/1b17f43b-d4ab-7ade-9b57-b1531069c013.png "win_ppk.png")


##### sshで接続

PUTTY.EXEを起動します。
![putty.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/a3b88a85-3735-933f-9acf-e88d41bd8f56.png "putty.png")

Categoryから[Session]を選択し、下記の通り入力します。

- HostName `ec2-user@[Public IP]`
- Port `22`
- Connection type `SSH`

![putty_1.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/4b116dc7-d42a-c16d-a342-f6468d2ff042.png "putty_1.png")

Categoryから[Connection]>[SSH]>[Auth]と選択し、[Browse]ボタンをクリックして作成した`.ppk`のファイルを選択します。ここまででパラメータ設定は終わりです。続いてこの設定を保存します。

![win_session.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/964afaca-d3f2-84e6-69db-f462d364cc01.png "win_session.png")

再び[Session]を選択し、[Saved Sessions]欄に名前を入力し、[Save]ボタンをクリックすると、設定した接続設定に名前をつけて保存することができます。
[Open]をクリックすると接続します。

![win_save.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/9577fd32-8e38-2a2f-91b9-520f036ff054.png "win_save.png")
こんな画面がでればOK!

![win_conn.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/adee63ad-4035-cfdd-17a4-dad057f9d1fb.png "win_conn.png")



## 作ったサーバのPHPに関する設定は？

サーバが起動できたら、もうPHPは使える状態です。
どういう設定になってるのか、AMI作成時にやった設定をまとめておきます。

### PHPインストール、設定など

バージョンは5.5にしました。

以下rootで。

`yum install -y php55`を実行してインストール。

```bash:ログ
[root ~]# yum install -y php55
読み込んだプラグイン:priorities, update-motd, upgrade-helper
amzn-main/latest | 2.1 kB 00:00
amzn-updates/latest | 2.3 kB 00:00
依存性の解決をしています

依存性を解決しました

=========================================================================================================================================================================================================
 Package アーキテクチャー バージョン リポジトリー 容量
=========================================================================================================================================================================================================
インストール中:
 php55 x86_64 5.5.20-2.94.amzn1 amzn-updates 3.0 M
依存性関連でのインストールをします:
 json-c x86_64 0.11-6.8.amzn1 amzn-main 31 k
 libzip x86_64 0.10.1-1.3.amzn1 amzn-main 47 k
 php-pear noarch 1:1.9.5-2.17.amzn1 amzn-main 434 k


〜　中略　〜

インストール:
 php55.x86_64 0:5.5.20-2.94.amzn1

依存性関連をインストールしました:
 json-c.x86_64 0:0.11-6.8.amzn1 libzip.x86_64 0:0.10.1-1.3.amzn1 php-pear.noarch 1:1.9.5-2.17.amzn1 php55-cli.x86_64 0:5.5.20-2.94.amzn1 php55-common.x86_64 0:5.5.20-2.94.amzn1
 php55-pecl-jsonc.x86_64 0:1.3.6-1.12.amzn1 php55-process.x86_64 0:5.5.20-2.94.amzn1 php55-xml.x86_64 0:5.5.20-2.94.amzn1

完了しました!
[root ~]#
```

バージョン確認

```bash
[root ~]# php -v
PHP 5.5.20 (cli) (built: Dec 29 2014 18:02:29)
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
[root ~]#
```

#### php.ini編集

##### timezone

```ini:/etc/php.ini
[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone #←修正前
date.timezone = 'Asia/Tokyo' #←修正後
```

##### XDEBUG

XDEBUGは必ずしも必要ないが、IDEでリモートデバッグできて便利なので入れてます。

インストールは下記で。

`yum install php55-pecl-xdebug --enablerepo=epel`

php.iniを編集する。末尾に下記を追加。

```ini:/etc/php.ini
[XDebug]
zend_extension = /usr/lib64/php/5.5/modules/xdebug.so
xdebug.remote_enable = 1
xdebug.remote_mode = req
xdebug.remote_port = 9000
xdebug.idekey = "phpconf2015"
xdebug.remote_host=localhost
xdebug.remote_autostart=1
```

上記に関連して、OS設定も。

- TimeZone

/etc/localtimeを、/usr/share/zoneinfo/Asia/Tokyoで上書き
`cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime`


- LANG

/etc/sysconfig/i18nを編集

```ini:/etc/sysconfig/i18n
LANG=ja_JP.UTF-8
```

こんなところです。他にもapacheの設定でドキュメントルートを変えたり、もやってますが割愛します。



## GitHubからTODOアプリのソース取得

### 鈴木商店用GitHubアカウントページを参照

下記URLにアクセスしてください。鈴木商店の主催する勉強会でも使用しているGitHubリポジトリです。ここに今回使用するソースがあります。

[suzukishouten-study/phpconf-2015](https://github.com/suzukishouten-study/phpconf-2015)

### 開発用サーバにリポジトリをクローン

赤枠の部分にクローン元のURLがあります。今回はhttpsでのアクセスとしますので、https〜で始まるURLを使用します。

クローン元URL: `https://github.com/suzukishouten-study/phpconf-2015.git`


![github_rep.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/41afcb78-0746-ed43-653e-ecc230f0ccd9.png "github_rep.png")


このリポジトリを自分の開発用サーバにCloneします。
`ec2-user`ユーザでsshログインし、`/var/www/phpconf2015`に移動してから下記のように`git clone`コマンドを叩きます。

:warning: コマンドは、`git clone https://github.com/suzukishouten-study/phpconf-2015.git .`です。
最後の "." の打ち忘れに注意！忘れると、サブディレクトリ`phpconf2015`が作成され、その中にCloneされますので、ディレクトリ階層がひとつ深くなってしまいます。


```bash
$ cd /var/www/phpconf2015/
$ git clone https://github.com/suzukishouten-study/phpconf-2015.git .
Cloning into '.'...
remote: Counting objects: 1458, done.
remote: Compressing objects: 100% (808/808), done.
remote: Total 1458 (delta 440), reused 1455 (delta 440), pack-reused 0
Receiving objects: 100% (1458/1458), 1.73 MiB | 615.00 KiB/s, done.
Resolving deltas: 100% (440/440), done.
Checking connectivity... done.
$
```

これで、`/var/www/phpconf2015`にTODOリストアプリケーションのソースがcloneされました。


## 取得したソースの中身は？

鈴木商店の主催する勉強会でも使用している、TODOリストアプリケーションのソースです。
その作成過程について書いておきます。


### CakePHPの準備、設定


#### ダウンロード、展開

ログイン後、rootになる。

```
sudo -i
```
 以降の作業はrootで。

[公式サイト](http://cakephp.jp/)からcakephpをダウンロード。
2.5系の最新安定版の、2.5.8を使用しています。

`/var/www/`にcdしてからwgetでダウンロード。

```
wget https://github.com/cakephp/cakephp/archive/2.5.8.zip
```

これをunzipで解凍。

```
unzip 2.5.8.zip
```

`cakephp-2.5.8`ディレクトリができるので、`phpconf2015`に名前変更

```
mv cakephp-2.5.8 phpconf2015
```

オーナを変更しておく。

```
chown -R ec2-user:wheel phpconf2015
```

#### セキュリティ設定

これをやっとかないとCakeの警告が出ます。
ここからはrootじゃなくec2-userで。


`/app/Config/Core.php.php`を修正。

下記サイトの通り設定しました（ブログ主さま、ありがとうございます！）。
[[CakePHP] Security.salt と Security.cipherSeed を生成する | blog.shiten.info](http://blog.shiten.info/2013/03/cakephp-security-salt-%E3%81%A8-security-cipherseed-%E3%82%92%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B.html)

#### データベース接続設定

作成したAMIには既にデータベース`phpconf2015`が作成済みです。これに接続します。

`database.php`を作成します。`database.php.default`というファイルがあるので、それをファイル名変更して使用します。
次のように修正します。

```php:/app/Config/database.php`

	public $default = array(
	'datasource' => 'Database/Mysql',
	'persistent' => false,
	'host' => 'localhost',
	'login' => 'root',
	'password' => 'phpconf2015',
	'database' => 'phpconf2015',
	'prefix' => '',
	'encoding' => 'utf8',
	);
```

ここまでで基本的な設定は完了。


### TODOリストアプリケーション作成

TODOアプリを作成していきますが、前提知識を確認しておきます。

#### URL、Http Method

RESTアプリケーションでは、リソースに対してユニークなURIを割付け、それに対してアクセスします。
そのリソースに対してどのような処理を行うかをHttp Methodで指定します。

（例）

**URL**

- todoリストの一覧
	- /todo_lists.json
- todoリストのidが'1'の１件
	- /todo_lists/1.json

**Http Method**

|Http Method|処理|
|-----------|----|
|GET|取得する|
|POST|追加する|
|PUT|更新する|
|DELETE|削除する|


#### CakePHPのController、Actionを含めまとめると

下表の通り、URL、HttpMethod、行う処理、CakePHPのControllerとActionが対応します。
下に、今回作成するソースを載せていますが、この表の通り作成されています。

|URL|Http Method|処理|Controller|Action|
|---------------------------------------|-----------|---|----------|-------|
|/todo_lists.json|GET|TODOリスト一覧を取得する|TodoListsController|index|
|/todo_lists/1.json|GET|TODOリストのidが'1'の１件を取得する|TodoListsController|view|
|/todo_lists.json|POST|TODOリストに１件追加する|TodoListsController|add|
|/todo_lists/1.json|PUT|TODOリストのidが'1'の１件を更新する|TodoListsController|edit|
|/todo_lists/1.json|DELETE|TODOリストのidが'1'の１件を削除する|TodoListsController|delete|

#### HTTPレスポンスコード

APIの処理結果は、HTTPレスポンスコードで返すようにするのが基本です。
大雑把に言うと、

- 200番台 : 正常終了
- 300番台 : リダイレクト
- 400番台 : クライアントエラー
- 500番台 : サーバエラー

となります。
:warning:参考 [HTTPステータスコード - Wikipedia](http://ja.wikipedia.org/wiki/HTTP%E3%82%B9%E3%83%86%E3%83%BC%E3%82%BF%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89)

例)
リソースの追加を行い、成功した場合(POST)は、"201 Created"と返す。

では、REST APIによるTODOリストのソース、詳細です。

## ソース詳細

- 修正したファイル
	- app/Config/routes.php
	- app/Controller/AppController.php
- 追加したファイル
	- app/Controller/TodoListsController.php
	- app/Model/TodoList.php


### 修正したファイルのdiff

※diff表示中の`-`(ピンク色)の部分は、修正前のソースから削除し、`+`(緑色)の部分を追加。

#### app/Config/routes.php

ルーティングの設定です。

```diff:app/Config/routes.php
 /**
  * Load all plugin routes. See the CakePlugin documentation on
  * how to customize the loading of plugin routes.
  */
 	CakePlugin::routes();
 
+/*
+ * API
+ */
+Router::mapResources(array (
+	'todo_lists',
+));
+Router::parseExtensions('json');
+
 /**
  * Load the CakePHP default routes. Only remove this if you do not want to use
  * the built-in default routes.
  */
 	require CAKE . 'Config' . DS . 'routes.php';
```

- mapResources関数

RESTの場合、上で説明した`index`, `view`, `add`, `edit`, `delete`のアクションに対するルーティングを自動的に設定してくれる方法があり、それがこの`mapResources`関数です。
引数に`todo_lists`と指定しているので、`TodoListsController`の各メソッドにルーティングされます。

- parseExtensions関数

URLにつけられた拡張子によって表示するViewを切り替えるための設定です。
ここでは、`json`を指定しているので、URLに付けられた拡張子が`.json`の場合に、JSON表示用のビュー(`/lib/Cake/View/JsonView.php`)が使用されるようになります。


#### app/Controller/AppController.php

コントローラの親クラスです。コンポーネントを追加します。

```diff:app/Controller/AppController.php
 class AppController extends Controller {
-    public $components = array('DebugKit.Toolbar');
+    public $components = array(
+        'RequestHandler'
+    );
 }
```

- $components変数

使用するコンポーネント指定します。
`'RequestHandler'`というコンポーネントを指定しています。
これにより、クライアントからのリクエストを適切に処理し、さまざまな情報をを適切に取得できるようになります。


### 追加ファイル

以下は追加したファイルです。

#### app/Controller/TodoListsController.php


今回のAPIの本体ともいえるControllerです。
前述した`index`, `view`, `add`, `edit`, `delete`のアクションはここで実装しています。

```php:app/Controller/TodoListsController.php
<?php

App::uses('AppController', 'Controller');

class TodoListsController extends AppController {

	public function index() {
		$res = $this->TodoList->find('all');
		$this->set(compact('res'));
		$this->set('_serialize', 'res');
	}

	public function view($id = null) {
		$res = $this->TodoList->findById($id);
		$this->set(compact('res'));
		$this->set('_serialize', 'res');
	}

	public function add() {
		$data = $this->request->data;
		$res = $this->TodoList->save($data);
		$this->set(compact('res'));
		$this->set('_serialize', 'res');
	}
	public function delete($id) {
		$res = $this->TodoList->delete($id, false);
		$this->set(compact('res'));
		$this->set('_serialize', 'res');
	}

	public function edit($id) {
		$this->TodoList->id = $id;
		$data = $this->request->data;
		$res = $this->TodoList->save($this->request->data);
		$res = !empty($res);
		$this->set(compact('res'));
		$this->set('_serialize', 'res');
	}

}

```

データベースからの検索、保存は次に説明するモデル、`TodoList.php`を使用しています。
ポイントは、各アクションに共通している次の2行です。

```php:各アクション共通
    $this->set(compact('res'));
    $this->set('_serialize', 'res');
```

- `$this->set('変数名', データ);`という構文でコントローラからビューにデータを渡します。これでクライアントに返すデータをビューにセットします。
- `'_serialize'`というキーを指定することにより、ビューのテンプレート(.ctpのファイル)を使用せず、上でセットしたデータをそのままJSONで返すようになります。

#### app/Model/TodoList.php

モデルです。今回のAPIはシンプルなので、モデルでは何もしていません。
CakePHPの`AppModel`をそのまま継承したのみです。

```php:app/Model/TodoList.php
<?php

App::uses('AppModel', 'Model');

class TodoList extends AppModel {
}
```

以上で、超シンプルですがREST APIが実装できました。

### GitHubに登録

これらまとめてGitHubに登録しますが、ついでに`.gitignore`も登録します。
下記のGistを参考にしました。
[CakePhp - default - .gitignore](https://gist.github.com/arnold-almeida/359695#file-cakephp-default-gitignore)

では、動作確認。

## POSTMANでAPIの動作確認

### POSTMANの準備

:warning:POSTMAN、または同様の別ツールをインストール済みの方はここはとばしてください。

POSTMANはchromeブラウザの拡張機能です。
下記からインストールします。
:warning:インストールしたら、次回起動のためにブックマークしておいてください。


[Postman - REST Client - Chrome ウェブストア](https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm)

![postman.png](https://qiita-image-store.s3.amazonaws.com/0/65988/35a132cb-eb9f-348b-6b9b-e3dd35704a69.png "postman.png")


では、下記に全件取得、１件取得、追加、更新、削除する際のPOSTMANに設定するパラメータを記載します。
試しみましょう！

### 全件取得してみる(GETメソッド)

- URL:`http://[Public IP]/todo_lists.json`
- メソッド:GET

[Send]をクリック!
![postman1.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/d45e7578-769a-3323-a0e9-0c75ac19a5c1.png "postman1.png")

### １件取得してみる(GETメソッド:id付き)


- URL:`http://[Public IP]/todo_lists/1.json`
	- "1"のところは、更新したいデータのIDを指定する
- メソッド:GET

[Send]をクリック!
![postman2.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/f4c737e3-d136-c9f7-d8a0-7deefc1fac49.png "postman2.png")

### 追加してみる(POSTメソッド)

- URL:`http://[Public IP]/todo_lists.json`
- メソッド:POST
- HTTPヘッダ: Content-Type : application/json
- データ: ROW, JSON

```
{
    "todo": "鈴木商店に入社する",
    "status": "0"
}
```

[Send]をクリック!
![postman3.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/07ee6a4c-4e15-b038-6733-5ba0726c7a4c.png "postman3.png")


### 更新してみる(PUTメソッド)

- URL:`http://[Public IP]/todo_lists/1.json`
	- "1"のところは、更新したいデータのIDを指定する
- メソッド:PUT
- HTTPヘッダ: Content-Type : application/json
- データ: ROW, JSON

```
{
    "todo": "鈴木商店の面接を受ける",
    "status": "0"
}
```


[Send]をクリック!
![postman4.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/27ca2ebf-0940-c4bb-4049-ebb473561a12.png "postman4.png")


### 削除してみる

- URL:`http://[Public IP]/todo_lists/2.json`
	- "2"のところは、削除したいデータのIDを指定する
- メソッド:DELETE


[Send]をクリック!
![postman5.png](https://qiita-image-store.s3.amazonaws.com/2006/65988/0f430b8c-1d0b-b596-0403-7eb1ad1db79c.png "postman5.png")


以上です!

### :warning: Postmanでの、"form-data"と"form-datax-www-form-urlencoded"の違いについて

PUTメソッドで送信する場合の動作について。

#### (1) `form-data`の場合

"Content-type"が"multipart/form-data"に設定され、送信データが複数パートに分割される形式で送られます。
通常はファイルアップロード等を実行する際に指定する形式です。

下記の様になります。(id=1, status=0, todo=あいうえおとした場合)

```text:例
------WebKitFormBoundaryBKsP47JxDdxYnfHW
Content-Disposition: form-data; name="id"
1
------WebKitFormBoundaryBKsP47JxDdxYnfHW
Content-Disposition: form-data; name="status"
0
------WebKitFormBoundaryBKsP47JxDdxYnfHW
Content-Disposition: form-data; name="todo"
あいうえお
------WebKitFormBoundaryBKsP47JxDdxYnfHW--
```

この形式だと、今回のTODOリストサーバ側プログラムで正しく送信データが取得できません。

#### (2) "x-www-form-urlencoded"の場合

"Content-type"が"application/x-www-form-urlencoded"に設定され、送信データは"名前1=値1&名前2=値2..."の形式で送られます。また、送信データはURLエンコードされます。
いわゆるフツーのformのpostで使用する形式です。

```text:例
id=11&status=0&todo=%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A
```

#### (3) `raw` + `Content-type=application/json`（この記事で紹介している方式）

明示的ににContent-typeを指定してあげると、送信データは"{名前: 値1, 名前2: 値2, ...}と、書いた通り送られ、サーバはJson形式であるものとして認識します。
今回のRestアプリケーションでは、送受信とも全てJSONを想定しているので、この方法がベストです。

```text:例
{id: 1, todo: "あいうえお", status: "0"}
```

(2)でもちゃんと動くのは、CakePHPのRequestHandlerがよろしくやってくれて、アプリケーションからは(2)でも(3)でも、単なる配列（連想配列）として取得できるようになっているからですね。

参考URL [リクエストハンドリング — CakePHP Cookbook 2.x ドキュメント](http://book.cakephp.org/2.0/ja/core-libraries/components/request-handling.html)

## phpMyAdminについて

http://[Public IP]/phpMyAdmin/

にアクセスすると起動できます。

user : root
password : phpconf2015

でOKです。パスワードは変えておきましょう！

今回使用しているテーブルは、`phpconf2015/todo_lists`です。

## 以上です！

勉強会もやってますので、よかったら来てくださいね！

- 今後の予定
	- [AWS上で構築するRESTfulアプリ勉強会～Web開発ワークショップ～ - connpass](http://aws-rest-todo.connpass.com/)

- 過去の勉強会
	- [AWS+REST勉強会ブログ](https://d.suzukishouten.co.jp/blog/suzukishouten/)