Symfony勉強会 #6 Silexチュートリアル リファクタリング

2012年7月13日

symfony-logo

前回は、CRUDアプリをファンクショナルテストでガードしました。

symfony-logo
Symfony勉強会 #6 Silexチュートリアル ファンクショナルテスト

前回はSilexのCRUDアプリを作成しました。 今回はこのアプリをファンクショナルテストでガードします。 PHPUni ...

続きを見る

今回は、index.phpに集中しているロジックをMVCの形に分離するリファクタリングを行います。

目次

  1. composer.jsonの編集
  2. index.phpのコードをコントローラに移す
  3. DB更新ロジックをモデルに移す
  4. 動作確認
  5. ディレクトリ構成

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を使用したいケースがあれば戻ってこようと思っています。

-技術ブログ
-,