laravel
DDD
laravel5

Laravel5を使ってドメイン駆動設計で作るサンプルアプリ。

More than 3 years have passed since last update.

Laravel5で変更に強いアプリケーションを作成する。

サンプルとして経費(本)を記録するだけのWebアプリケーションを作成。
日付、タイトル、価格、URLを1テーブルにしてMySQLへ格納。

Laravelインストール

% composer create-project laravel/laravel keihi
% cd keihi
% php artisan -V 
Laravel Framework version 5.1.20 (LTS)

名前空間を更新

Keihiという名前空間にします。

// namespaceを「App」から「Keihi」へ変更
% php artisan app:name Keihi

namespace指定してある箇所が全部以下のように変更されます。

composer.json
         "psr-4": {
-            "App\\": "app/"
+            "Keihi\\": "app/"
         }
app/Console/Kernel.php
 <?php

-namespace App\Console;
+namespace Keihi\Console;

Databaseを用意

MAMPでMYSQLサーバー起動、UTF8でDB作成。

以下、MAMPのソケットで繋ぐときに必要な処理。

UNIX_SOCKET追記
/config/database.php
        'mysql' => [
            'driver'    => 'mysql',
            'unix_socket' => env('DB_UNIX_SOCKET'),// 1行追記
            'host'      => env('DB_HOST', 'localhost'),
            'database'  => env('DB_DATABASE', 'forge'),
.envのDB設定を編集してDB_UNIX_SOCKETは追記。
.env
DB_UNIX_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sock
DB_HOST=localhost
DB_DATABASE=keihi_db
DB_USERNAME=test
DB_PASSWORD=test

マイグレーション作成

作成コマンド

% php artisan make:migration create_keihi_table --create=keihi 
Created Migration: 2015_10_23_235515_create_keihi_table

ファイル編集

/database/migrations/2015_10_23_235515_create_keihi_table.php
public function up()
{
    Schema::create('keihi', function (Blueprint $table) {
        $table->increments('id');       // ID
        $table->char('title', 100);     // タイトル
        $table->integer('price');       // 価格
        $table->text('url');            // URL
        $table->timestamps();           // 作成時刻
     });
}

マイグレーション実行

MySQLにテーブルが作成。(keihi以外はデフォルトで存在するもの)

% php artisan migrate
Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
Migrated: 2015_10_23_235515_create_keihi_table

エレクエント作成

Keihiモデル(/app/Keihi.php)を作成。

% php artisan make:model Keihi

エレクエント編集

Keihiモデルに紐づくテーブルを「keihi」に設定。

/app/Keihi.php
<?php

namespace Keihi;

use Illuminate\Database\Eloquent\Model;

class Keihi extends Model
{
    protected $table = 'keihi';
}

リポジトリ作成

インターフェース作成

/app/Repositories/KeihiInterface.php
<?php

/**
 * Interface KeihiInterface
 */
interface KeihiInterface
{
    /**
     * 取得
     * @param $id
     * @return mixed
     */
    public function get($id);

    /**
     * 一覧取得
     * @return mixed
     */
    public function getList();

    /**
     * 更新
     * @param $id
     * @param $data
     * @return mixed
     */
    public function update($id, $data);

    /**
     * 新規登録
     * @param $data
     * @return mixed
     */
    public function create($data);

    /**
     * 削除
     * @param $id
     * @return mixed
     */
    public function delete($id);
}

リポジトリ実装

インターフェースを使用して実装します。
コンストラクタでエレクエントモデルを依存注入します。

/app/Repositories/KeihiRepository.php
<?php

namespace Keihi\Repositories;

use Keihi\Repositories\KeihiInterface;
use Keihi\Keihi;

/**
 * Class KeihiRepository
 * @package Keihi\Repositories
 */
class KeihiRepository implements KeihiInterface
{
    /**
     * @var Keihi
     */
    protected $keihi;

    /**
     * @param Keihi $keihi
     */
    public function __construct(Keihi $keihi)
    {
        $this->keihi = $keihi;
    }

    /**
     * 取得
     * @param $id
     * @return mixed
     */
    public function get($id)
    {
     
    }

サービスプロバイダー作成

リポジトリ用のサービスプロバイダー作成

php artisan make:provider RepositoryProvider

サービスプロバイダー編集

/app/Providers/RepositoryProvider.php
    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        // 経費インターフェスと経費リポジトリの連結
        $this->app->bind(KeihiInterface::class, KeihiRepository::class);
    }

リポジトリサービスプロバイダーの読込

/config/app.php
        /*
         * Application Service Providers...
         */
        Keihi\Providers\AppServiceProvider::class,
        Keihi\Providers\AuthServiceProvider::class,
        Keihi\Providers\EventServiceProvider::class,
        Keihi\Providers\RouteServiceProvider::class,
        Keihi\Providers\RepositoryProvider::class,// 1行追加
    ],

テストデータ投入

シーダー作成

% php artisan make:seeder KeihiSeeder

シーダー編集

エレクエントモデルを利用してテスト用データを記述。

/database/seeds/KeihiSeeder.php
<?php

use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
use Keihi\Keihi;

class KeihiSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        Model::unguard();
        Keihi::create([
            'id' => 1,
            'title' => 'WEB+DB PRESS Vol.89',
            'price' => 1598,
            'url' => 'http://www.amazon.co.jp/gp/product/4774176389'
        ]);
        Keihi::create([
            'id' => 2,
            'title' => 'Docker実践入門――Linuxコンテナ技術の基礎から応用まで (Software Design plus)',
            'price' => 2894,
            'url' => 'http://www.amazon.co.jp/gp/product/4774176540'
        ]);
        Keihi::create([
            'id' => 3,
            'title' => 'HTML5 Web標準API バイブル',
            'price' => 3218,
            'url' => 'http://www.amazon.co.jp/gp/product/4774176540'
        ]);
    }
}

データ投入

% php artisan db:seed --class=KeihiSeeder

単体テスト(PHPUnit)

単体テスト作成

/tests/Repositories/KeihiRepositoryTest.php
<?php

use Keihi\Repositories\KeihiInterface;

class KeihiRepositoryTest extends TestCase
{
    protected $repo;

    public function setUp()
    {
        parent::setUp();
        $this->repo = $this->app->make(KeihiInterface::class);
    }

    public function testGet()
    {
        $result = $this->repo->get(1);
        $this->assertSame($result->title, 'WEB+DB PRESS Vol.89');
        $this->assertSame($result->price, 1598);
        $this->assertSame($result->url,   'http://www.amazon.co.jp/gp/product/4774176389');
    }

    public function testGetList()
    {
        $result = $this->repo->getList();
        $this->assertCount(3, $result);
        $this->assertSame($result[2]->title, 'HTML5 Web標準API バイブル');
        $this->assertSame($result[2]->price, 3218);
        $this->assertSame($result[2]->url,   'http://www.amazon.co.jp/gp/product/4774176540');
    }

}

単体テスト実行

% php vendor/bin/phpunit --tap
TAP version 13
ok 1 - ExampleTest::testBasicExample
ok 2 - KeihiRepositoryTest::testGet
ok 3 - KeihiRepositoryTest::testGetList
1..3

サービス作成

コーントローラー内でリポジトリを操作する為のサービスを作成。

インターフェースを作成。

/app/Services/KeihiServiceInterface.php
/**
 * Interface KeihiServiceInterface
 * @package Keihi\Services
 */
interface KeihiServiceInterface
{
    /**
     * 単体取得
     * @return mixed
     */
    public function get($id);

    /**
     * 一覧取得
     * @return mixed
     */
    public function getList();

    /**
     * 保存
     * @param $input
     * @param $id | null
     * @return $id | null
     */
    public function save($input, $id=null);

    /**
     * 削除
     * @param $id
     * @return $id
     */
    public function delete($id);

    /**
     * エンティティ作成
     * @return $entity
     */
    public function createEntity();

インターフェースを使用して実装。
データ検証の役割を持たせる。

/app/Services/KeihiService.php
<?php

namespace Keihi\Services;

use Keihi\Services\KeihiServiceInterface;
use Keihi\Repositories\KeihiInterface;
use Illuminate\Validation\Factory as ValidateFactory;

/**
 * Class KeihiService
 * @package Keihi\Services
 */
class KeihiService implements KeihiServiceInterface
{
    /**
     * @var KeihiInterface
     */
    protected $keihiInterface;

    /**
     * @var ValidateFactory
     */
    protected $validateFactory;

    /**
     * @var array
     */
    protected $rules = ["title" => "required|max: 100", "price" => "required|integer|min:0|max:100000", "url" => "required"];

    /**
     * @param KeihiInterface $keihiInterface
     * @param ValidateFactory $validateFactory
     */
    public function __construct(KeihiInterface $keihiInterface, ValidateFactory $validateFactory)
    {
        $this->keihiInterface = $keihiInterface;
        $this->validateFactory = $validateFactory;
    }

    /**
     * @param $id
     * @return mixed
     */
    public function get($id)
    {
        return $this->keihiInterface->get($id);
    }

    /**
     * @return mixed
     */
    public function getList()
    {
        return $this->keihiInterface->getList();
    }

    /**
     * @param $request
     * @param $id
     * @return $id
     */
    public function save($request, $id=null)
    {
        $input = $request->only(['title', 'price', 'url']);
        $v = $this->validateFactory->make($input, $this->rules);
        if ($v->fails()) {
            return null;
        }

        if (is_null($id)) {
            $id = $this->keihiInterface->create($input);
        } else {
            $id = $this->keihiInterface->update($id, $input);
        }

        return $id;
    }

    /**
     * @param $id
     * @return bool
     */
    public function delete($id)
    {
        $this->keihiInterface->delete($id);
        return true;
    }

    /**
     * @return mixed
     */
    public function createEntity()
    {
        return $this->keihiInterface->createEntity();
    }
}

Appサービスプロバイダーに登録。

/app/Providers/AppServiceProvider.php
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        // 経費サービス
        $this->app->singleton(KeihiServiceInterface::class, KeihiService::class);
    }

コントローラー作成。

経費サービスインターフェースを依存注入して使用します。

% php artisan make:controller KeihiController
/app/Http/Controllers/KeihiController.php
<?php

namespace Keihi\Http\Controllers;

use Illuminate\Http\Request;
use Keihi\Http\Requests;
use Keihi\Http\Controllers\Controller;
use Keihi\Services\KeihiServiceInterface;

/**
 * Class KeihiController
 * @package Keihi\Http\Controllers
 */
class KeihiController extends Controller
{
    /**
     * @var KeihiServiceInterface
     */
    private $keihiService;

    /**
     * @param KeihiServiceInterface $keihiServiceInterface
     */
    public function __construct(KeihiServiceInterface $keihiServiceInterface)
    {
        $this->keihiService = $keihiServiceInterface;
    }

    /**
     * 一覧
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $list = $this->keihiService->getList();
        return view('keihi.list', compact('list'));
    }

    /**
     * 新規入力画面
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        $keihi = $this->keihiService->createEntity();
        return view('keihi.edit', compact('keihi'));
    }

    /**
     * 新規登録処理
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $id = $this->keihiService->save($request);
        return $this->show($id);
    }

    /**
     * 詳細
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        $keihi = $this->keihiService->get($id);
        return view('keihi.detail', compact('keihi'));
    }

    /**
     * 編集画面
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        $keihi = $this->keihiService->get($id);
        return view('keihi.edit', compact('keihi'));
    }

    /**
     * 更新処理
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        $id = $this->keihiService->save($request, $id);
        return $this->show($id);
    }

    /**
     * 削除処理
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        $this->keihiService->delete($id);
        return $this->index();
    }
}

ルーティング

/app/Http/routes.php
Route::resource('keihi', 'KeihiController');

ルーティングの確認。

% php artisan route:list
+--------+----------+--------------------+---------------+------------------------------------------------+------------+
| Domain | Method   | URI                | Name          | Action                                         | Middleware |
+--------+----------+--------------------+---------------+------------------------------------------------+------------+
|        | GET|HEAD | /                  |               | Closure                                        |            |
|        | GET|HEAD | keihi              | keihi.index   | Keihi\Http\Controllers\KeihiController@index   |            |
|        | POST     | keihi              | keihi.store   | Keihi\Http\Controllers\KeihiController@store   |            |
|        | GET|HEAD | keihi/create       | keihi.create  | Keihi\Http\Controllers\KeihiController@create  |            |
|        | DELETE   | keihi/{keihi}      | keihi.destroy | Keihi\Http\Controllers\KeihiController@destroy |            |
|        | PATCH    | keihi/{keihi}      |               | Keihi\Http\Controllers\KeihiController@update  |            |
|        | GET|HEAD | keihi/{keihi}      | keihi.show    | Keihi\Http\Controllers\KeihiController@show    |            |
|        | PUT      | keihi/{keihi}      | keihi.update  | Keihi\Http\Controllers\KeihiController@update  |            |
|        | GET|HEAD | keihi/{keihi}/edit | keihi.edit    | Keihi\Http\Controllers\KeihiController@edit    |            |
+--------+----------+--------------------+---------------+------------------------------------------------+------------+