はじめに
前回の続きです。
今回はRails Tutorialの7章をやりたいと思います。
デバッグの部分などは省略しております。
Rails Tutorialでは、自分でゴリゴリ書いていきますが、ここではbake
コマンドを使用して作成した後、修正をしていく方針にします。
bakeコマンドでコントローラーとテンプレートを生成
今回作成したいものは、前回作成したUser
モデルに対するCRUDです。
まずは、コントローラーです。
$ bin/cake bake controller Users
# 以下のファイルが作成されます
/src/Controller/UsersController.php
tests/TestCase/Controller/UsersControllerTest.php
次にテンプレートです。
$ bin/cake bake template Users
# 以下のファイルが作成されます
src/Template/Users/index.ctp
src/Template/Users/view.ctp
src/Template/Users/add.ctp
src/Template/Users/edit.ctp
それぞれ、index
(一覧)、view
(詳細)、add
(追加)、edit
(編集)になります。
コントローラーにはdelete
もありますが、削除したら一覧にリダイレクトするので、テンプレートはありません。
サーバーを起動させた状態で、/users
にアクセスして下図のような画面が表示されれば、OKです。
ビューを修正
ビューの修正はBootstrapを基本としてスタイルをあてていきます。
Bootstrap以外の部分については、custom.css
に記載していきますので、後ほど記載する差分をご参照ください。
新規登録の入力フォームを修正
ただ、bake
で生成されたビューファイルでは、新規登録の入力フォームがpassword_digest
になってしまっています。
User
モデルはpassword
と確認用のpassword_confirmation
を受け取り、一致していたらハッシュ化してpassword_digest
に放り込みます。
そのため、add.php
のフォーム部分を修正します。
<?= $this->Form->create($user) ?>
<fieldset>
<?php
echo $this->Form->control('name',
['class' => 'form-control', 'require' => true]);
echo $this->Form->control('email',
['type' => 'email', 'class' => 'form-control', 'require' => true]);
echo $this->Form->control('password',
['type' => 'password', 'class' => 'form-control', 'require' => true]);
echo $this->Form->control('password_confirmation',
['type' => 'password', 'class' => 'form-control', 'require' => true]);
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
Form
$this->Form->control
を使用することで、input
タグを生成してくれますが、第二引数でHTML属性を連想配列で渡すことでお好みで設定できます。
エレメントを使用して編集画面でも使い回す
実は、add.php
とedit.php
はフォーム部分が(見た目上は)ほとんど変わらないので、エレメントにしたいと思います。
幸いなことに、$this->Form->create
は現在のURLからaction
属性を推測してくれます。
フォームの開始
例えば、
-
users/add
で呼ばれれば、action="users/add" method="post"
になります。 -
users/edit/2
で呼ばれれば、action="users/edit/2" method="post"
になります。
つまり、create
が勝手に判断してくれるので、add
とedit
にそれぞれ書く必要ななさそうです。
(もちろん、ケースバイケースだとは思いますが。。。)
<?= $this->Form->create($user) ?>
<fieldset>
<?php
echo $this->Form->control('name',
['class' => 'form-control', 'require' => true]);
echo $this->Form->control('email',
['type' => 'email', 'class' => 'form-control', 'require' => true]);
echo $this->Form->control('password',
['type' => 'password', 'class' => 'form-control', 'require' => true]);
echo $this->Form->control('password_confirmation',
['type' => 'password', 'class' => 'form-control', 'require' => true]);
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
<div class="users form col-lg-9 col-md-8 columns content">
<legend><?= __('Add User') ?></legend>
<?= $this->element('Users/form'); ?>
</div>
<div class="users form col-lg-9 col-md-8 columns content">
<legend><?= __('Edit User') ?></legend>
<?= $this->element('Users/form'); ?>
</div>
コントローラーの統合テストを書いてみる
基本的には、コントローラーの統合テストを参考にしながら記載しました。
テストの手順としては、下記の通りです。
#setup、#index、#view
public function setUp()
{
parent::setUp();
$this->base_title = 'Ruby on Rails Tutorial Sample App';
// UsersTableを使用することを宣言しておく
$this->users = TableRegistry::get('Users');
}
/**
* Test index method
*
* @return void
*/
public function testIndex()
{
// GETメソッドで/usersにアクセス
$this->get('/users');
// レスポンスコードが200で返ってくる
$this->assertResponseOk();
// 正しいテンプレートが選択されている
$this->assertTemplate('Users/index');
// タイトルタグの記載内容が正しい
$this->assertResponseContains("Users Index | {$this->base_title}");
}
/**
* Test view method
*
* @return void
*/
public function testView()
{
// GETメソッドで/users/view/1にアクセス
$this->get('/users/view/1');
// レスポンスコードが200で返ってくる
$this->assertResponseOk();
// 正しいテンプレートが選択されている
$this->assertTemplate('Users/view');
// タイトルタグの記載内容が正しい
$this->assertResponseContains("Users View | {$this->base_title}");
}
#add
テスト前後のレコード数で判断しています。
/**
* Test add method
*
* @return void
*/
public function testAdd()
{
// GETで/users/addにアクセス
$this->get('/users/add');
$this->assertResponseOk();
$this->assertTemplate('Users/add');
$this->assertResponseContains("Add User | {$this->base_title}");
// POSTで/users/addにアクセス
// バリデーションエラーになるようパラメーターをわたした場合
// まずはあらかじめのレコード数を取得しておく
$before_count = $this->users->find()->count();
$data = ['name' => 'hogehoge', 'email' => 'email@example.com',
'password' => 'hogehoge', 'password_confirmation' => 'fugafuga'];
$this->post('/users/add', $data);
$this->assertResponseOk();
// バリデーションエラーになるとaddに戻ってくるので、テンプレートがaddかどうか
$this->assertTemplate('Users/add');
// あらかじめのレコード数と変わらないか
$this->assertEquals($before_count, $this->users->find()->count());
// POST /users/add
// 適切なパラメーターを渡した場合
$before_count = $this->users->find()->count();
$data = ['name' => 'hogehoge', 'email' => 'email@example.com',
'password' => 'hogehoge', 'password_confirmation' => 'hogehoge'];
$this->post('/users/add', $data);
// あらかじめ取得したレコード数より1つ増えているはず
$this->assertEquals($before_count + 1, $this->users->find()->count());
}
#edit
テスト後のname
プロパティが予想された値になっているかどうかで判断しています。
なお、id = 1
を指定していますが、これはあらかじめフィクスチャの方で作成したものです。
tests/Fixture/UsersFixture.php
を見てみてください。
/**
* Test edit method
*
* @return void
*/
public function testEdit()
{
// GETで/users/edit/1にアクセス
$this->get('/users/edit/1');
$this->assertResponseOk();
$this->assertTemplate('Users/edit');
$this->assertResponseContains("Edit User | {$this->base_title}");
// POSTで/users/edit/1にアクセス
// パスワードが一致していない場合
$data = ['name' => 'hogehoge', 'email' => 'email@example.com',
'password' => 'hogehoge', 'password_confirmation' => 'fugafuga'];
$this->post('/users/edit/1', $data);
$this->assertResponseOk();
$this->assertTemplate('Users/edit');
// UsersTableからid=1を取得
$user = $this->users->get(1);
// nameがtest_nameのままであることを確認
$this->assertEquals('test_name', $user->name);
// POSTで/users/edit/1にアクセス
// 適切なパラメーターを渡した場合
$data = ['name' => 'hogehoge', 'email' => 'email@example.com',
'password' => 'hogehoge', 'password_confirmation' => 'hogehoge'];
$this->post('/users/edit/1', $data);
$user = $this->users->get(1);
// ちゃんと更新していれば、nameが変わっているはず
$this->assertEquals('hogehoge', $user->name);
}
#delete
当たり前ですが、id = 1
を消せば、id = 1
のレコード数が0になるので、そちらを確認します。
/**
* Test delete method
*
* @return void
*/
public function testDelete()
{
$this->post('/users/delete/1');
$user = $this->users->find()->where(['id' => 1]);
$this->assertEquals(0, $user->count());
}
さいごに
今回の差分はこちらになります。
よろしければご覧ください。
https://github.com/naoki85/cakephp_de_rails_tutorial/commit/31cd105006f3d882678368323b105809c7b21c8b