Laravel サービスクラスを活用したメソッドの実装方法
はじめに
Laravelで開発を進めていると、コントローラーが肥大化する問題に直面することがあります。
このような場合、処理をサービスクラスに分離することで、可読性や保守性が向上します。
本記事では、コメント機能を題材にサービスクラスを活用した実装方法をご紹介します。
特に以下の課題を解決することを目的としています。
- コントローラーの肥大化を防ぐ
- テストしやすいコードを実現する
- 再利用可能な処理を実装する
なぜサービスクラスを利用するのか?
サービスクラスを導入することで、以下のようなメリットがあります。
メリット
- コントローラーがスリムになり、可読性が向上
- ビジネスロジックを再利用可能にできる
特に、ビジネスロジックが複雑になる場合は、サービスクラスを導入することで保守や拡張がしやすくなります。
実行環境
PHP 8.3
Laravel 11
MySQL 8.0
JavaScript (fetch API使用)
上記を使用してフォロー機能を実装していきます。
また、サイトはログインしなければ使用できない仕様になっています。
実装の全体像
今回実装するコメント機能は、以下の責務分担で進めます。
ファイル | 役割 |
---|---|
CommentController | リクエスト処理・サービス呼び出し |
CommentService | ビジネスロジックの処理 |
Commentモデル | データベース操作 |
1. サービスクラスの作成
フォルダ構造の準備
サービスクラスは通常、app/Services ディレクトリに配置します。
mkdir app/Services
サービスクラスの作成
以下のコマンドでファイルを作成します
touch app/Services/CommentService.php
CommentService.php
<?php
namespace App\Services;
use App\Models\Comment;
use Illuminate\Support\Facades\Auth;
class CommentService
{
/*コメントを作成する*/
public function createComment(int $materialId, string $content): Comment
{
return Comment::create([
'material_id' => $materialId,
'user_id' => Auth::id(),
'content' => $content,
]);
}
/*特定の教材に関連するコメントを取得す*/
public function getCommentsForMaterial(int $materialId)
{
return Comment::where('material_id', $materialId)
->with('user.profile')
->latest()
->get();
}
}
2.コントローラーからサービスを呼び出す
次に、CommentController からサービスクラスを呼び出すように変更します。
CommentController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Services\CommentService;
class CommentController extends Controller
{
protected $commentService;
/*コンストラクタでサービスを注入*/
public function __construct(CommentService $commentService)
{
$this->commentService = $commentService;
}
/*コメントを投稿する*/
public function store(OriginalProductCommentRequest $request, Original_product $product)
{
// バリデーション
$validatedData = $request->validated();
// dd([$validatedData, $product->id]);
$this->commentService->createComment($validatedData, $product->id);
// リダイレクト(リロードしてコメントを反映)
return redirect()->route('products.show', $product->id);
}
}
ポイント解説
- 依存注入: コンストラクタで CommentService を注入しています。
3. ビューの準備
original_productのモデルにコメントを取得するリレーションを記述します。
app/Models/Original_product.php
public function comments()
{
return $this->hasMany(Original_product_comment::class, 'original_product_id');
}
app/Http/Controllers/ProductController.php
public function show(string $id)
{
// プロダクトをIDで取得し、関連するタグと画像も一緒に取得
$product = Original_product::with([
'technologies',
'images',
'profile',
'comments' => function ($query) {
$query->orderBy('created_at', 'desc'); // 新しい順に並べ替え
},
'comments.user.profile'
])->findOrFail($id);
// 現在のログインユーザーのプロフィールを取得
$profile = auth()->user()->profile;
return view('products.show', compact('product','profile'));
}
show.blade.phpを編集してコメントを表示できるようにします
show.blade.php
<div class="comment-container">
<div class="select-comment original-product-view">
<p class="comment-count">{{ $product->comments->count() }}件のコメント</p>
@foreach ($product->comments as $comment)
<div class="comment">
<div class="comment-left">
<a href="#" class="comment-user-image">
<!-- リファクタリングしてくださいね -->
<img class="comment-user-image" src="{{ asset('assets/material_images/user_profile_image.png') }}" alt="M">
</a>
</div>
<div class="comment-right">
<div class="comment-user-info">
<a href="#" class="comment-user-name">{!! nl2br(e($comment->user->profile->username)) !!}</a>
<p class="comment-date">{{ $comment->created_at }}</p>
</div>
<p class="comment-detail">{!! nl2br(e($comment->comment)) !!}</p>
</div>
</div>
@endforeach
</div>
<div class="comment-form-container">
<form class="comment-form" action="{{ route('comments.store', $product->id) }}" method="POST">
@csrf
<textarea name="original-product-comment" id="original-product-comment" placeholder="コメントを入力"></textarea>
<button class="original-product-comment-button" type="submit">コメントを送信</button>
</form>
</div>
</div>
上記のように表示されます。
4. 実装後のメリット
サービスクラス導入の効果
- コントローラーが簡潔になり、読みやすくなった
- ビジネスロジックの再利用が容易になった
- 単体テストが書きやすくなった
まとめ
本記事では、サービスクラスを活用したコメント機能の実装方法をご紹介しました。
サービスクラスを導入することで、以下の効果が得られます。
- コードの可読性向上
- 保守性・再利用性の向上
- テストが容易になる
今は個人開発で規模は大きくないですが、業務システムや大規模なアプリケーションでは、この構成が有効になると思います。
今後もコードを書くときは保守性・再利用性・運用性のことを考えてうまく使い分けていこうと思います。
Discussion