Edited at

CakePHPでRails Tutorialをやってみる〜その2 ほぼ静的なページの作成〜

More than 1 year has passed since last update.


はじめに

業務でCakePHPを使用することになったので、勉強を始めました。

本を買おうかとも思ったのですが、折角なのでRails TutorialをCakePHPで書き換えながら勉強をしようかと思いました。

自分の整理と、他の方たちへの参考になればと思い、記事にさせていただきます。

とばせる部分はとばしているので、全て実施するわけではありません。


コントローラーの生成

CakePHPにはbakeコマンドという、ファイルを生成してくれるコマンドがあります。

Cake(ケーキ)をbake(焼く)というのはなかなか洒落ているのではないかと思っています。

Bakeでコード生成

今回はコントローラーだけ生成したいと思います。

StaticPagesControllerという名前にします。

一緒にテストコードも生成してくれます。

$ php bin/cake.php bake controller StaticPages

Creating file /home/ubuntu/workspace/cakephp_de_rails_tutorial/src/Controller/StaticPagesController.php
Wrote `/home/ubuntu/workspace/cakephp_de_rails_tutorial/src/Controller/StaticPagesController.php`

生成されたコントローラーにはRESTなindexのようなメソッドが記載されているので、これを消して必要なものを追記していきます。

今回、StaticPagesにはhomehelpaboutcontactを追加します。


StaticPagesController.php

<?php

namespace App\Controller;

use App\Controller\AppController;

/**
* StaticPages Controller
*
*
* @method \App\Model\Entity\StaticPage[] paginate($object = null, array $settings = [])
*/

class StaticPagesController extends AppController
{

public function home()
{

}

public function help()
{

}

public function about()
{

}

public function contact()
{

}
}



ルーティングの設定

URLからコントローラーのアクションを紐づけるルーティングを設定します。

ファイルはconfig/routes.phpです。

ルーティング

ルートパスと各メソッドの設定をします。


config/routes.php

Router::scope('/', function (RouteBuilder $routes) {

/**
* Here, we are connecting '/' (base path) to a controller called 'Pages',
* its action called 'display', and we pass a param to select the view file
* to use (in this case, src/Template/Pages/home.ctp)...
*/
- $routes->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']);
+ $routes->connect('/', ['controller' => 'StaticPages', 'action' => 'home']);

/**
* ...and connect the rest of 'Pages' controller's URLs.
*/
+ $routes->connect('/home', ['controller' => 'StaticPages', 'action' => 'home']);
+ $routes->connect('/help', ['controller' => 'StaticPages', 'action' => 'help']);
+ $routes->connect('/about', ['controller' => 'StaticPages', 'action' => 'about']);
+ $routes->connect('/contact', ['controller' => 'StaticPages', 'action' => 'contact']);


テンプレートの作成

それぞれのテンプレートを作成します。

とりあえずのテストなので何でも良いかと思いますが、Rails Tutorialのものをそのまま使用しようと思います。

(Railsではないのにリンクができてしまっていますが。。。)

なお、各ファイルはsrc/Templateの下にStaticPagesというディレクトリを作成し、その中で作っていきます。


src/Template/StaticPages/home.ctp

<h1>Sample App</h1>

<p>
This is the home page for the
<a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a>
sample application.
</p>


src/Template/StaticPages/help.ctp

<h1>Help</h1>

<p>
Get help on the Ruby on Rails Tutorial at the
<a href="https://railstutorial.jp/help">Rails Tutorial help page</a>.
To get help on this sample app, see the
<a href="https://railstutorial.jp/#ebook"><em>Ruby on Rails Tutorial</em>
book</a>.
</p>


src/Template/StaticPages/about.ctp

<h1>About</h1>

<p>
<a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a>
is a <a href="https://railstutorial.jp/#ebook">book</a> and
<a href="https://railstutorial.jp/#screencast">screencast</a>
to teach web development with
<a href="http://rubyonrails.org/">Ruby on Rails</a>.
This is the sample application for the tutorial.
</p>


src/Template/StaticPages/contact.ctp

<h1>Contact</h1>

<p>
Contact the Ruby on Rails Tutorial about the sample app at the
<a href="https://railstutorial.jp/contact">contact page</a>.
</p>


表示確認

サーバーを起動して動作を確認します。

$ bin/cake server -H $IP -p $PORT

下図のようになっていれば問題ないと思います。

スクリーンショット 2017-08-31 2.46.05.png


PHPUnitでテストコードを書く


PHPUnitのインストール

ここまでのテストコードを書いてみたいと思います。

CakePHPではPHPUnitがサポートされているようなので、そちらを使用します。

テスト

プロジェクトディレクトリのcomposer.jsonを編集します。

suggestの中にあるphpunitと、ついでにcakephp-codesnifferrequire-devに移します。

cakephp-codesnifferは、コーディング規約に沿っているか確認してくれるものです。

特に記事にはしませんが、開発環境であれば合っても良いのかな、と思います。

(Rubyでいうrubocopみたいなものですかね。。。)

https://github.com/cakephp/cakephp-codesniffer


composer.json

"require-dev": {

"psy/psysh": "@stable",
"cakephp/debug_kit": "~3.2",
"cakephp/bake": "~1.1",
+ "phpunit/phpunit": "*",
+ "cakephp/cakephp-codesniffer": "*"
},
"suggest": {
"markstory/asset_compress": "An asset compression plugin which provides file concatenation and a flexible filter system for preprocessing and minification.",
"dereuromark/cakephp-ide-helper": "After baking your code, this keeps your annotations in sync with the code evolving from there on for maximum IDE and PHPStan compatibility.",
- "phpunit/phpunit": "Allows automated tests to be run without system-wide install.",
- "cakephp/cakephp-codesniffer": "Allows to check the code against the coding standards used in CakePHP."
},

この状態で、Composerをアップデートします。

$ sudo php composer.phar update

これで、phpunitコマンドが使用できるかと思います。

試しに下記が起動できるか確認します。

(恐らくエラーになるかと思います。)

$ vendor/bin/phpunit


テストコードの作成

先ほどbakeで生成されたStaticPagesControllerTest.phpというファイルがあるので、そちらを修正していきます。


tests/TestCase/Controller/StaticPagesControllerTest.php

<?php

namespace App\Test\TestCase\Controller;

use App\Controller\StaticPagesController;
use Cake\TestSuite\IntegrationTestCase;

/**
* App\Controller\StaticPagesController Test Case
*/

class StaticPagesControllerTest extends IntegrationTestCase
{

/**
* Test home method
*
* @return void
*/

public function testHome()
{
$this->get('/home');
$this->assertTemplate('StaticPages/home');
}

/**
* Test help method
*
* @return void
*/

public function testHelp()
{
$this->get('/help');
$this->assertTemplate('StaticPages/help');
}

/**
* Test about method
*
* @return void
*/

public function testAbout()
{
$this->get('/about');
$this->assertTemplate('StaticPages/about');
}

/**
* Test contact method
*
* @return void
*/

public function testContact()
{
$this->get('/contact');
$this->assertTemplate('StaticPages/contact');
}
}


基本的に静的なページなので、テストでやっていることは同じです。



  • getで対象のURLにアクセスする。

  • 表示されているテンプレートが正しいか確認する(assertTemplate)。

IntegrationTestCaseを継承していると便利なアサーションメソッドを使用できます。

下記のコマンドで対象のファイルだけテストできます。

GREENになればOKです。

$ vendor/bin/phpunit tests/TestCase/Controller/StaticPagesControllerTest.php

また、テスト単位で実行するには、filterオプションが使用できます。

filterに一致するテストのみ実行してくれます。

# Homeがつくテストだけ実行

$ vendor/bin/phpunit --filter="/Home/" tests/TestCase/Controller/StaticPagesControllerTest.php

vendor/bin/phpunitだけ実行すると、全てのテストを実行できます。

ただ、今回の場合、PagesControllerTest.phpが引っかかってしまいます。

そのため、コメントアウトするか、消してしまっても良いと思います。


少し動的なページ

タイトルタグの中身をページごとに変わるようにします。

headタグなどは、src/Template/Layout/default.ctpに書かれております。


まずはテストコードを追記してみる

Rails Tutorialのようにテスト駆動開発(TDD)っぽく、まずはテストから修正します。

今回はHome | Ruby on Rails Tutorial Sample Appのような文言がちゃんと含まれているか確認します。

共通文言はライフサイクルコールバックsetUpを使用し、テスト前にプロパティにセットします。


tests/TestCase/Controller/StaticPagesControllerTest.php

<?php

namespace App\Test\TestCase\Controller;

use App\Controller\StaticPagesController;
use Cake\TestSuite\IntegrationTestCase;

class StaticPagesControllerTest extends IntegrationTestCase
{
+ public $base_title;

+ public function setUp()
+ {
+ parent::setUp();
+ $this->base_title = 'Ruby on Rails Tutorial Sample App';
+ }

public function testHome()
{
$this->get('/home');
$this->assertTemplate('StaticPages/home');
+ $this->assertResponseContains("Home | {$this->base_title}");
}

public function testHelp()
{
$this->get('/help');
$this->assertTemplate('StaticPages/help');
+ $this->assertResponseContains("Help | {$this->base_title}");
}

public function testAbout()
{
$this->get('/about');
$this->assertTemplate('StaticPages/about');
+ $this->assertResponseContains("About | {$this->base_title}");
}

public function testContact()
{
$this->get('/contact');
$this->assertTemplate('StaticPages/contact');
+ $this->assertResponseContains("Contact | {$this->base_title}");
}
}


これでテストを実行すると、エラーになります。


テンプレートの修正

コントローラーから値を受け取り、動的にタイトルタグを変更したいと思います。


src/Template/Layout/default.ctp

<title>

- <?= $cakeDescription ?>:
- <?= $this->fetch('title') ?>
+ <?= "{$title} | Ruby on Rails Tutorial Sample App" ?>
</title>


コントローラーの修正

コントローラーからsetメソッドを使用して値を渡します。


src/Controller/StaticPagesController.php

<?php

namespace App\Controller;

use App\Controller\AppController;

class StaticPagesController extends AppController
{

public function home()
{
+ $this->set('title', 'Home');
}

public function help()
{
+ $this->set('title', 'Help');
}

public function about()
{
+ $this->set('title', 'About');
}

public function contact()
{
+ $this->set('title', 'Contact');
}
}


これでテストが通ったかと思います。


さいごに

色々端折ってしまいましたが、第3章をやってみました。

PHPUnitはRubyのminitestに近い感じで使えそうです。

次回は第4章をとばして、5章をやりたいと思います。

次回:その3 レイアウトの作成