業務では何かしらのデザインパターンを取り入れることが多いです。
特に大規模なアプリではどこに何のコードがあるのかわかりづらくなりがちなので、
一定の規則を定めてコーディングします。
この規則は様々なものがあり、デザインパターンと呼ばれています。
今回は、Laravelのプロジェクトで最も採用されているデザインパターンであるリポジトリパターンを取り入れてみましょう。
そして初心者の方でもわかりやすいようハンズオン形式でTodoリストを作っていきましょう!
完成品
一部JSのコードも入っているので、JS以外の部分を解説していきます。
リポジトリパターンとは?
リポジトリパターンとはビジネスロジックとデータ操作のロジックを切り離し、
データ操作を抽象化したレイヤに任せるデザインパターンのことです。
・テストがしやすくなる
・データ操作が1つにまとまるため保守管理がしやすくなる
といった、メリットがあります。
インストール
3行で環境開発ができるためこちらを参考にしてください。
もしエラーが発生した場合はメッセージください。
初期設定
Laravelでやっておくべき初期設定は以下の5つです。
- タイムゾーン
- 言語設定
- DBの文字コード
- エラーメッセージの日本語訳
- Models/User.phpの設置
タイムゾーン
タイムゾーンを日本時間に変更します。
'timezone' => 'Asia/Tokyo'
言語設定
言語設定を日本語に変更します。
'locale' => 'ja'
DBの文字コード
文字コードをUTF-8に変更します。
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci'
エラーメッセージの日本語訳
resources/lang/en
内にエラーメッセージのファイルがあるがこれらすべて英語となっています。
そのため、日本語訳するために以下のサイトからダウンロードします。
ダウンロードしたファイルをresources/lang/ja
内に格納します。
任意修正箇所
エラーメッセージが一部英語で表示されます。
エラーメッセージの変更は以下のように行います。
"attributes" => [
"password" => "パスワード"
]
Models/User.phpの設置
ディレクトリを作成し、User.phpを移動する
$ mkdir Models
$ mv User.php Models
ファイルの修正
namespace App\Models;
use App\Models\User;
use App\Models\User;
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
],
use App\Models\User;
まずはModelとMigrationを作成しよう
以下のコマンドで、まずはモデルファイルとマイグレーションファイルを作成します。
$ docker-compose exec app bash
$ php artisan make:model Models/Task --migration
マイグレーションファイルに追記
class CreateTasksTable extends Migration
{
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
// id
$table->bigIncrements('id')->comment("id");
// タスク
$table->string("task", 255)->comment("タスク");
// ステータス
$table->boolean("status")->default(false)->comment("状況(0: 作業中, 1:完了)");
// タイムスタンプ
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('tasks');
}
}
モデルファイルに追記
データベースの操作で、fillを使うのでfillableに値を代入してます。
class Task extends Model
{
// $fillable
protected $fillable = ["task", "status"];
}
【今回の本題】Repositoryの作成をしよう
フォルダの構成は以下のようになります。
├── Models
│ ├── Task.php
│
├── Repositories
└── Task
├── TaskRepository.php
└── TaskRepositoryInterface.php
ということで、フォルダとファイルを作成していきましょう。
// appディレクトリに移動
$ cd src/app
// Repositoriesフォルダを作成
$ mkdir Repositories
// Repositories/Taskフォルダを作成
$ mkdir Repositories/Task
// TaskRepository.phpを作成
$ touch Repositories/Task/TaskRepository.php
// TaskRepositoryInterface.phpを作成
$ touch Repositories/Task/TaskRepositoryInterface.php
インターフェイス設計
<?php
namespace App\Repositories\Task;
interface TaskRepositoryInterface
{
// tasksテーブルをすべて取得
public function getTasks();
// tasksテーブルに追加
public function createTask($request);
// tasksテーブルの要素を更新
public function updateTask($id);
// tasksテーブルの要素を削除
public function destroyTask($id);
}
インプリメントクラス
<?php
namespace App\Repositories\Task;
// models
use App\Models\Task;
class TaskRepository implements TaskRepositoryInterface
{
// tasksテーブルをすべて取得
public function getTasks()
{
return Task::all();
}
// tasksテーブルに追加
public function createTask($request)
{
// instance
$task = new Task;
// 値の代入
return $task->fill($request->all())->save();
}
// tasksテーブルの要素を更新
public function updateTask($id)
{
// 取得
$value = Task::find($id);
// ステータスを更新
return $value->fill(["status" => !$value->status])->save();
}
// tasksテーブルの要素を削除
public function destroyTask($id)
{
// 取得
$value = Task::find($id);
// 取得して削除
return $value->delete();
}
}
Providerファイルにインターフェイスとインプリメントを登録
class AppServiceProvider extends ServiceProvider
{
public function register()
{
//Task
$this->app->bind(
\App\Repositories\Task\TaskRepositoryInterface::class,
\App\Repositories\Task\TaskRepository::class
);
}
public function boot()
{
}
}
Viewファイルを作成
フォルダの構成は以下のようになります。
├── views
└── tasks
├── index.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>Laravel</title>
</head>
<body>
<h2>ToDoリスト</h2>
<form name="sort">
<input type="radio" name="type" id="all" checked value="すべて" onclick="clickType()">
<label for="all">すべて</label>
<input type="radio" name="type" id="work" value="作業中" onclick="clickType()">
<label for="work">作業中</label>
<input type="radio" name="type" id="complete" value="完了" onclick="clickType()">
<label for="complete">完了</label>
</form>
<table>
<thead>
<tr>
<th>ID</th>
<th>コメント</th>
<th>状態</th>
</tr>
</thead>
<tbody>
<form action="{{ route('tasks.index') }}" method="GET">
@csrf
@foreach ($values as $value)
@if ($value->status === 0)
<tr class="workTask">
<td>{{ $loop->iteration - 1 }}</td>
<td>{{ $value->task }}</td>
<form action="{{ route('tasks.update', $value->id) }}" method="POST">
@csrf
@method("PUT")
<td><input type="submit" value="作業中"></td>
</form>
<form action="{{ route('tasks.destroy', $value->id) }}" method="POST">
@csrf
@method("DELETE")
<td><input type="submit" value="削除"></td>
</form>
</tr>
@else
<tr class="completeTask">
<td>{{ $loop->iteration - 1 }}</td>
<td>{{ $value->task }}</td>
<form action="{{ route('tasks.update', $value->id) }}" method="POST">
@csrf
@method("PUT")
<td><input type="submit" value="完了"></td>
</form>
<form action="{{ route('tasks.destroy', $value->id) }}" method="POST">
@csrf
@method("DELETE")
<td><input type="submit" value="削除"></td>
</form>
</tr>
@endif
@endforeach
</form>
</tbody>
</table>
<h2>新規タスクの追加</h2>
<form action="{{ route('tasks.store') }}" method="POST">
@csrf
<input type="text" name="task">
<input type="submit" value="追加">
</form>
@error('task')
<span>
<strong style="color: red">{{ $message }}</strong>
</span>
@enderror
<script src="{{ asset('/js/index.js') }}"></script>
</body>
</html>
コントローラーを作成
実装したリポジトリパターンを呼び出しましょう!
インターフェイスをインジェクションするだけなので簡単です。
$ docker-compose exec app bash
$ php artisan make:controller TaskController --resource
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
// Validation
use App\Http\Requests\StoreTaskPost;
// Repositories
use App\Repositories\Task\TaskRepositoryInterface;
class TaskController extends Controller
{
// construct
public function __construct(TaskRepositoryInterface $task_repository)
{
$this->task_repository = $task_repository;
}
public function index()
{
// Repositories->getTasks()
$values = $this->task_repository->getTasks();
return view("tasks.index", compact("values"));
}
public function store(StoreTaskPost $request)
{
// Repositories->createTask()
$this->task_repository->createTask($request);
return redirect("/tasks");
}
public function update(Request $request, $id)
{
// Repositories->updateTask()
$this->task_repository->updateTask($id);
return redirect("/tasks");
}
public function destroy($id)
{
// Repositories->destroyTask()
$this->task_repository->destroyTask($id);
return redirect("/tasks");
}
}
最後にルーティング処理を記載しましょう
<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
*/
// Task::resource
Route::resource('tasks', 'TaskController', ['only' => ['index', 'store', 'update', 'destroy']]);
これで完了です。
JavaScriptとバリデーションの部分を省略してますが、、
ここが気になる方がいましたらご連絡ください。
感想
モデルとコントローラーがスッキリするとともに管理しやすくなった気がします!