前回は、CRUDアプリをファンクショナルテストでガードしました。
-
Symfony勉強会 #6 Silexチュートリアル ファンクショナルテスト
前回はSilexのCRUDアプリを作成しました。 今回はこのアプリをファンクショナルテストでガードします。 PHPUni ...
続きを見る
今回は、index.phpに集中しているロジックをMVCの形に分離するリファクタリングを行います。
目次
composer.jsonの編集
composer.jsonにautoloadの情報を追加します。
{
"repositories": [
{
"type": "pear",
"url": "http://pear.symfony-project.com"
},
{
"type": "pear",
"url": "http://pear.phpunit.de"
}
],
"minimum-stability": "dev",
"require": {
"silex/silex": "1.0.*",
"twig/twig": "1.8.*",
"doctrine/dbal": "2.2.*",
"doctrine/common": "2.2.*",
"pear-phpunit/PHPUnit": "*",
"symfony/browser-kit": "2.1.*",
"symfony/dom-crawler": "2.1.*",
"symfony/css-selector": "2.1.*"
},
"autoload": {
"psr-0": { "SilexTutorial": "src/"}
}
}
下記コマンドを実行します。
php composer.phar upate
コマンドを実行すると、 silex/vendor/composer/autoload_namespaces.php に 名前空間とディレクトリをマッピングするための情報が書き込まれます。
silex/vendor/composer/autoload_namespaces.php
return array(
// ...
'SilexTutorial' => $baseDir . '/src/',
// ..
);
index.phpのコードをコントローラに移す
MemberControllerProvider.php
vendorと同じ階層にsrcディレクトリを作成し、その中にコントローラを記述します。index.phpから、書式をあわせつつ移動します。
silex/src/SilexTutorial/Provider/MemberControllerProvider.php
<?php
namespace SilexTutorial\Provider;
use Silex\Application;
use Silex\ControllerProviderInterface;
use Silex\ControllerCollection;
class MemberControllerProvider implements ControllerProviderInterface
{
public function connect(Application $app)
{
$controllers = $app['controllers_factory'];
$controllers->get('/register', function(Application $app) {
return $app['twig']->render('member/register.html.twig');
});
$controllers->post('/register', function(Application $app) {
$request = $app['request'];
$member = $request->get('member');
$stmt = $app['db']->prepare("
INSERT INTO member SET
email = :email
,password = :password
");
$stmt->bindParam(':email', $member['email']);
$password = md5($member['password']);
$stmt->bindParam(':password', $password);
$stmt->execute();
return $app['twig']->render('member/finish.html.twig', array(
'member' => $member
));
});
return $controllers;
}
}
index.php
index.phpの情報は、コントローラに移動します。代わりに、Applicationクラスのmountメソッドでコントローラを読み込んでいます。
silex/web/index.php
<?php
require_once __DIR__.'/../vendor/autoload.php';
$app = new Silex\Application();
$app['debug'] = true;
// twig
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => __DIR__ . '/../views',
));
// doctrine
$app->register(new Silex\Provider\DoctrineServiceProvider(), array(
'db.options' => array(
'driver' => 'pdo_mysql',
'dbname' => 'silex',
'host' => 'localhost',
'user' => 'admin',
'password' => 'admin'
)
));
// member
$app->mount('/member', new SilexTutorial\Provider\MemberControllerProvider());
$app->run();
このあたりでテストを実行して、アプリケーションが壊れていないことを確認します。
DB更新ロジックをモデルに移す
コントローラができましたが、DBの更新ロジックがコントローラに書かれています。DB更新ロジックをモデルに移動します。
サービスモデルの作成
サービスモデルを作成します。
silex/src/SilexTutorial/Service/Member.php
<?php
namespace SilexTutorial\Service;
class Member
{
protected $db;
public function __construct($db)
{
$this->db = $db;
}
/**
* register
*
* @param array $data
* @return void
*/
public function register(array $data)
{
try
{
$this->db->beginTransaction();
$sql = "INSERT INTO member SET
email = :email,
password = :password,
created_at = now()";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(':email', $data['email']);
$stmt->bindParam(':password', $data['password']);
$stmt->execute();
$stmt->execute();
}
catch(Exception $e)
{
$this->db->rollback();
throw $e;
}
$this->db->commit();
}
}
MemberServiceProvider.php
上記で作成したサービスモデルを$app経由で呼び出すためのServiceProviderを作成します。
silex/src/SilexTutorial/Provider/MemberServiceProvider.php
<?php
namespace SilexTutorial\Provider;
use Silex\Application;
use Silex\ServiceProviderInterface;
use SilexTutorial\Service\Member;
class MemberServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['member'] = $app->share(function() use($app) {
return new Member($app['db']);
});
}
public function boot(Application $app)
{
}
}
index.php
MemberServiceProviderを登録します。
silex/web/index.php
//...
$app->register(new SilexTutorial\Provider\MemberServiceProvider());
$app->mount('/member', new SilexTutorial\Provider\MemberControllerProvider());
//...
MemberControllerProvider.php
Memberサービスモデルのregisterメソッドを呼び出します。
silex/src/SilexTutorial/Provider/MemberControllerProvider.php
//...
class MemberControllerProvider implements ControllerProviderInterface
{
public function connect(Application $app)
{
//...
$controllers->post('/register', function(Application $app) {
$data = $app['request']->get('member');
$app['member']->register($data);
return $app['twig']->render('member/finish.html.twig', array(
'member' => $data
));
});
//...
}
}
動作確認
テストを実行して、アプリケーションの動作が変わっていないことを確認します。
ディレクトリ構成
ディレクトリ構成は下記になります。参考までに。
silex
├── composer.json
├── composer.lock
├── composer.phar
├── phpunit
├── phpunit.xml.dist
├── src
│ └── SilexTutorial
│ ├── Provider
│ │ ├── MemberControllerProvider.php
│ │ └── MemberServiceProvider.php
│ └── Service
│ └── Member.php
├── tests
│ └── functional
│ └── MemberControllerTest.php
├── vendor
├── views
│ └── member
│ ├── finish.html.twig
│ └── register.html.twig
└── web
└── index.php
おわりに
チュートリアルは、検索画面やセッションを利用したログイン画面と続きます。興味のある方は続きをやってみてください。DB接続への設定値が分散していたり、課題はたくさんあります。
私はこのあたりまで試してみて、いったん学習をやめました。
先にSymfony2を勉強して、Silexを使用したいケースがあれば戻ってこようと思っています。