Posted at

CakePHPでRails Tutorialをやってみる〜その5 ユーザー登録〜

More than 1 year has passed since last update.


はじめに

前回の続きです。

今回は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です。

スクリーンショット 2017-09-08 20.34.52.png


ビューを修正

ビューの修正はBootstrapを基本としてスタイルをあてていきます。

Bootstrap以外の部分については、custom.cssに記載していきますので、後ほど記載する差分をご参照ください。


新規登録の入力フォームを修正

ただ、bakeで生成されたビューファイルでは、新規登録の入力フォームがpassword_digestになってしまっています。

Userモデルはpasswordと確認用のpassword_confirmationを受け取り、一致していたらハッシュ化してpassword_digestに放り込みます。

そのため、add.phpのフォーム部分を修正します。


src/Template/Users/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.phpedit.phpはフォーム部分が(見た目上は)ほとんど変わらないので、エレメントにしたいと思います。

幸いなことに、$this->Form->createは現在のURLからaction属性を推測してくれます。

フォームの開始

例えば、



  • users/addで呼ばれれば、action="users/add" method="post"になります。


  • users/edit/2で呼ばれれば、action="users/edit/2" method="post"になります。

つまり、createが勝手に判断してくれるので、addeditにそれぞれ書く必要ななさそうです。

(もちろん、ケースバイケースだとは思いますが。。。)


src/Template/Element/Users/form.ctp

<?= $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() ?>


src/Template/Users/add.ctp

<div class="users form col-lg-9 col-md-8 columns content">

<legend><?= __('Add User') ?></legend>
<?= $this->element('Users/form'); ?>
</div>


src/Template/Users/edit.ctp

<div class="users form col-lg-9 col-md-8 columns content">

<legend><?= __('Edit User') ?></legend>
<?= $this->element('Users/form'); ?>
</div>


コントローラーの統合テストを書いてみる

基本的には、コントローラーの統合テストを参考にしながら記載しました。

テストの手順としては、下記の通りです。


#setup、#index、#view


tests/TestCase/Controller/UsersControllerTest.php

    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

テスト前後のレコード数で判断しています。


tests/TestCase/Controller/UsersControllerTest.php

    /**

* 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を見てみてください。


tests/TestCase/Controller/UsersControllerTest.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になるので、そちらを確認します。


tests/TestCase/Controller/UsersControllerTest.php

    /**

* 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

スタイルもこんな感じになります。

スクリーンショット 2017-09-09 1.02.53.png

スクリーンショット 2017-09-09 1.03.21.png

スクリーンショット 2017-09-09 1.03.33.png