【Laravel】Usecaseを使ったコーディング紹介

Laravel,PHP

ミニマリスト_カミ

kamiです。
TwitterYoutubeもやってます。

今回はLaravelのUsecaseを使ったコーディング紹介です。

Usecaseとは?

ユースケースとは、アプリケーションが提供する具体的な機能や、その機能を実行するための手順を説明するものです。
DTOクラスについても詳しく書いてるので、合わせて覚えていきましょう。

DTO専用の記事もあるので、こちらの参考までに。

usecaseのコーディングの説明

まずはUsecaseのコーディングの説明をしますね

// app/Usecases/HogeUsecase.php

namespace App\Usecases;

use App\Dto\HogeDto;
use App\Dto\HogeResultDto;
use App\Services\HogeServiceInterface;

class HogeUsecase
{
    private $hogeService;

    // コンストラクタでHogeServiceInterfaceを受け取る
    public function __construct(HogeServiceInterface $hogeService)
    {
        $this->hogeService = $hogeService;
    }

    // ユースケースの実行メソッド
    public function execute(HogeDto $hogeDto): HogeResultDto
    {
        // HogeServiceのexecuteメソッドを呼び出してビジネスロジックを実行
        return $this->hogeService->execute($hogeDto);
    }
}

名前空間の宣言

クラスが属する名前空間を宣言します。これにより、他のクラスと区別されます。

namespace App\Usecases;

必要なクラスのインポート

HogeDto、HogeResultDto、およびHogeServiceInterfaceをインポートします。
useすることで、クラスを使用できるようになります。

use App\Dto\HogeDto;
use App\Dto\HogeResultDto;
use App\Services\HogeServiceInterface;

クラスの宣言

HogeUsecaseクラスを宣言します。
ビジネスロジックをカプセル化するユースケースクラスです。

class HogeUsecase

プロパティの定義

HogeServiceInterface型のプライベートプロパティ $hogeService を定義します。
依存性注入されるサービスを保持します。

private $hogeService;

コンストラクタの定義

コンストラクタは、HogeServiceInterfaceのインスタンスを引数として受け取ります。
これにより、クラス内でユースケースが必要とするサービスが注入されます。

public function __construct(HogeServiceInterface $hogeService)
{
    $this->hogeService = $hogeService;
}

executeメソッドの定義

executeメソッドは、ユースケースの主要なメソッドです。
HogeDtoを引数として受け取り、HogeServiceのexecuteメソッドを呼び出して、ビジネスロジックを実行します。
結果として、HogeResultDtoを返します。

public function execute(HogeDto $hogeDto): HogeResultDto
{
    // サービスのexecuteメソッドを呼び出し
    return $this->hogeService->execute($hogeDto);
}

まとめ

icon

コードの流れのまとめです

  1. インスタンスの生成:
    • HogeUsecaseクラスがインスタンス化されるとき、HogeServiceInterfaceを実装したサービスがコンストラクタに渡されます。
    • これにより、HogeUsecaseはHogeServiceのインスタンスを保持します。
  2. ビジネスロジックの実行:
    • クライアントコードがHogeUsecaseのexecuteメソッドを呼び出します。ここで、HogeDtoオブジェクトが渡されます。
    • executeメソッドは、渡されたHogeDtoオブジェクトを使って、内部で保持しているHogeServiceのexecuteメソッドを呼び出します。
  3. 結果の取得と返却:
    • HogeServiceのexecuteメソッドがビジネスロジックを実行し、HogeResultDtoオブジェクトを返します。
    • HogeUsecaseのexecuteメソッドは、受け取ったHogeResultDtoオブジェクトを呼び出し元に返します。

Tree

icon

今回のアプリのTreeです

今回はユーザーを検索する想定をして、コーディングをしていきます。
Usecaseに合わせて、Model、DTO、Service、Repository、Interfaceなども使っていきますので是非参考にしてみてください。

project-root/
├── app/
│   ├── Dto/
│   │   └── UserSearchDto.php
│   ├── Http/
│   │   ├── Controllers/
│   │   │   └── API/
│   │   │       └── UserController.php
│   ├── Models/
│   │   └── User.php
│   ├── Providers/
│   │   └── AppServiceProvider.php
│   ├── Repositories/
│   │   ├── UserRepositoryInterface.php
│   │   └── UserRepository.php
│   ├── Services/
│   │   ├── UserServiceInterface.php
│   │   └── UserService.php
│   └── Usecases/
│       └── SearchUsersUsecase.php
├── resources/
│   └── views/
│       └── search.blade.php
├── routes/
│   └── web.php

ファイル内容の概要

  • app/Dto/UserSearchDto.php: 検索条件を格納するデータ転送オブジェクト (DTO)。
  • app/Dto/UserSearchResultDto.php: 検索結果を格納するデータ転送オブジェクト (DTO)。
  • app/Http/Controllers/API/UserController.php: 検索リクエストを処理するコントローラー。
  • app/Models/User.php: usersテーブルに対応するEloquentモデル。
  • app/Providers/AppServiceProvider.php: サービスコンテナへのバインドを行うプロバイダー。
  • app/Repositories/UserRepositoryInterface.php: リポジトリのインターフェース。
  • app/Repositories/UserRepository.php: リポジトリの実装。
  • app/Services/UserServiceInterface.php: サービスのインターフェース。
  • app/Services/UserService.php: サービスの実装。
  • app/Usecases/SearchUsersUsecase.php: ユースケースの実装。
  • resources/views/search.blade.php: ユーザー検索のためのフロントエンドのBladeテンプレート。
  • routes/web.php: ルート設定ファイル。

スポンサードサーチ

Model

Userモデルは、データベースのusersテーブルと対応するEloquentモデルです。

// app/Models/User.php
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    use HasFactory;

    protected $fillable = ['name', 'id', 'password'];
}

DTO (Data Transfer Object)

UserSearchDtoは、ユーザー検索のためのデータ転送オブジェクトです。
リクエストからの検索条件を格納します。
※条件は名前とIDです。

// app/Dto/UserSearchDto.php

namespace App\Dto;

class UserSearchDto
{
    public function __construct(
        private ?string $name = null, // 名前フィールド
        private ?string $id = null    // IDフィールド
    ) {
    }

    // 名前を取得するメソッド
    public function name(): ?string
    {
        return $this->name;
    }

    // IDを取得するメソッド
    public function id(): ?string
    {
        return $this->id;
    }
}

UserSearchDtoクラスが生成されたときに、constructでnameとidを取得しています。

UserSearchResultDtoは、検索結果を格納するDTOクラスです。

// app/Dto/UserSearchResultDto.php

namespace App\Dto;

use Illuminate\Support\Collection;

class UserSearchResultDto
{
    private int $totalCount; // 検索結果の総数
    private Collection $results; // 検索結果のコレクション

    /**
     * コンストラクタ
     * 
     * @param int $totalCount 検索結果の総数
     * @param Collection $results 検索結果のコレクション
     */
    public function __construct(int $totalCount, Collection $results)
    {
        $this->totalCount = $totalCount; // プロパティの初期化
        $this->results = $results; // プロパティの初期化
    }

    /**
     * 検索結果の総数を取得する
     * 
     * @return int 検索結果の総数
     */
    public function totalCount(): int
    {
        return $this->totalCount;
    }

    /**
     * 検索結果のコレクションを取得する
     * 
     * @return Collection 検索結果のコレクション
     */
    public function results(): Collection
    {
        return $this->results;
    }
}

スポンサードサーチ

Repository

リポジトリとはデータベースや他のデータソースへのアクセスロジックを抽象化し、データの取得、保存、更新、削除などの操作を提供します。

Repositoryの主な役割

  • データの取得と保存:
    • データベースやその他の永続化メカニズム(例:ファイルシステム、外部APIなど)からデータを取得し、保存する責任を持ちます。
  • データアクセスロジックのカプセル化:
    • クエリの生成や実行、データマッピングなどのデータアクセスロジックをカプセル化します。

主にデータアクセス層で使用され、サービスやビジネスロジック層から呼び出されます。

Repository Interface

UserRepositoryInterfaceは、ユーザー検索に必要なリポジトリメソッドを定義します。
リポジトリパターンに従ってデータアクセスを抽象化します。

// app/Repositories/UserRepositoryInterface.php

namespace App\Repositories;

use App\Dto\UserSearchDto;
use Illuminate\Support\Collection;

interface UserRepositoryInterface
{
    // ユーザー検索メソッドのインターフェース
    public function execute(UserSearchDto $userSearchDto): Collection;
}

Repository Implementation

UserRepositoryは、UserRepositoryInterfaceを実装し、ユーザー検索のための具体的なデータアクセスロジックを提供します。
検索条件に基づいてデータベースクエリを実行します。

// app/Repositories/UserRepository.php

namespace App\Repositories;

use App\Dto\UserSearchDto;
use App\Models\User;
use Illuminate\Support\Collection;

class UserRepository implements UserRepositoryInterface
{
    // ユーザー検索メソッドの実装
    public function execute(UserSearchDto $userSearchDto): Collection
    {
        $query = User::query();

        // 名前で検索
        if ($userSearchDto->name()) {
            $query->where('name', 'like', '%' . $userSearchDto->name() . '%');
        }

        // IDで検索
        if ($userSearchDto->id()) {
            $query->where('id', $userSearchDto->id());
        }

        // 検索結果を取得
        return $query->get();
    }
}

Service

サービスとはデータアクセスだけでなく、複雑な計算や他のサービスとの統合など、ビジネスロジック全般を含みます。
サービスはアプリケーションのビジネスロジックを実装し、ドメインのルールや振る舞いを管理します。

サービスの主な役割

  • ビジネスロジックのカプセル化:
    • 複数のリポジトリを組み合わせたり、データの検証、トランザクション管理などのビジネスルールを実装します。
  • ユースケースのサポート:
    • アプリケーションのユースケースを実現するための主要なロジックを提供します。

コントローラーや他のユースケースから呼び出され、ビジネスロジックを実行します。

Service Interface

UserServiceInterfaceは、ユーザー検索サービスのインターフェースです。
ビジネスロジックを含むサービスメソッドを定義します。

// app/Services/UserServiceInterface.php

namespace App\Services;

use App\Dto\UserSearchDto;
use App\Dto\UserSearchResultDto;

interface UserServiceInterface
{
    /**
     * ユーザー検索を実行する
     *
     * @param UserSearchDto $userSearchDto
     * @return UserSearchResultDto
     */
    public function execute(UserSearchDto $userSearchDto): UserSearchResultDto;
}

Service Implementation

UserServiceは、UserServiceInterfaceを実装し、ユーザー検索のビジネスロジックを提供します。
サービスはリポジトリを利用してデータを取得します。

// app/Services/UserService.php

namespace App\Services;

use App\Dto\UserSearchDto;
use App\Dto\UserSearchResultDto;
use App\Repositories\UserRepositoryInterface;

class UserService implements UserServiceInterface
{
    private UserRepositoryInterface $userRepository;

    public function __construct(UserRepositoryInterface $userRepository)
    {
        $this->userRepository = $userRepository;
    }

    public function execute(UserSearchDto $userSearchDto): UserSearchResultDto
    {
        // リポジトリの検索メソッドを呼び出し
        $results = $this->userRepository->execute($userSearchDto);
        
        // 検索結果の件数を取得
        $totalCount = $results->count();
        
        // 結果をUserSearchResultDtoにラップして返す
        return new UserSearchResultDto($totalCount, $results);
    }
}

引数にDTOオブジェクト渡すことで、メソッド間でのデータのやり取りが効率的かつ明確になります。

スポンサードサーチ

Usecase

ユーザー検索のユースケースを実行するためのクラスです。
UserServiceInterface型のプライベートプロパティです。

コンストラクタは、UserServiceInterfaceの実装を引数として受け取ります。
これにより、SearchUsersUsecaseクラスはUserServiceInterfaceインターフェースに依存する形になります。

// app/Usecases/SearchUsersUsecase.php

namespace App\Usecases;

use App\Dto\UserSearchDto;
use App\Dto\UserSearchResultDto;
use App\Services\UserServiceInterface;

class SearchUsersUsecase
{
    private $userService;

    public function __construct(UserServiceInterface $userService)
    {
        $this->userService = $userService;
    }

    // ユースケースの実行メソッド
    public function execute(UserSearchDto $userSearchDto): UserSearchResultDto
    {
        // サービスのexecuteメソッドを呼び出し
        return $this->userService->execute($userSearchDto);
    }
}

executeメソッドについて紹介します

コントローラー内のexecute

コントローラー内でSearchUsersUsecaseのexecuteメソッドを実行すると、UserSearchDtoオブジェクトが引数として渡されます。

// ユースケースの実行メソッド
public function execute(UserSearchDto $userSearchDto): UserSearchResultDto
{
    // サービスのexecuteメソッドを呼び出し
    return $this->userService->execute($userSearchDto);
}

ユースケース内のexecute

executeメソッドはユースケースの主要な実行メソッドです。
引数としてUserSearchDtoオブジェクトを受け取り、そのオブジェクトを使ってユーザー検索のサービスメソッドを呼び出します。
サービスのexecuteメソッドは、検索結果をUserSearchResultDtoオブジェクトとして返します。

public function execute(UserSearchDto $userSearchDto): UserSearchResultDto
{
    // サービスのexecuteメソッドを呼び出し、検索結果を取得
    return $this->userService->execute($userSearchDto);
}

Controller

UserControllerは、ユーザー検索のためのAPIエンドポイントを提供します。
リクエストを受け取り、ユースケースのexecuteメソッドを呼び出して、結果を返します。

// app/Http/Controllers/API/UserController.php

namespace App\Http\Controllers\API;

use App\Dto\UserSearchDto;
use App\Usecases\SearchUsersUsecase;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    private $searchUsersUsecase;

    // コンストラクタでSearchUsersUsecaseを依存注入
    public function __construct(SearchUsersUsecase $searchUsersUsecase)
    {
        $this->searchUsersUsecase = $searchUsersUsecase;
    }

    // 検索リクエストを処理するメソッド
    public function search(Request $request)
    {
        // リクエストから名前とIDを取得してDTOを作成
        $userSearchDto = new UserSearchDto(
            $request->input('name'),
            $request->input('id')
        );

        // ユースケースの実行メソッドを呼び出してユーザー検索を実行
        $users = $this->searchUsersUsecase->execute($userSearchDto);

        // JSON形式で検索結果を返す
        return response()->json($users);
    }
}

値の取得するにはインスタンス化していれば取得できます

// リクエストから名前とIDを取得してDTOを作成
$userSearchDto = new UserSearchDto(
   $request->input('name'),
   $request->input('id')
);

 // DTOオブジェクトから名前とIDを取得
$name = $userSearchDto->name();
$id = $userSearchDto->id();

DTOを引数に渡す意味

  • データのカプセル化:
    • UserSearchDtoは、検索条件を一つのオブジェクトにカプセル化します。これにより、メソッド間でデータを渡す際に引数の数が減り、コードが簡潔になります。
  • 明示的な依存性:
    • メソッドの引数にDTOを渡すことで、そのメソッドがどのデータに依存しているかが明示的になります。これにより、コードの可読性とメンテナンス性が向上します。
  • 変更に強い:
    • 将来的に検索条件が増えた場合でも、DTOに属性を追加するだけで済みます。これにより、他の部分のコードに変更が及ぶ可能性が減ります。

UserSearchDtoをexecuteメソッドの引数として渡すことにより、検索条件を一つのオブジェクトとして扱い、メソッド間でのデータのやり取りを簡潔かつ明示的にしています。

インスタンスの生成

UserControllerクラスがインスタンス化されるとき、SearchUsersUsecaseがコンストラクタに渡されます。
UserControllerはSearchUsersUsecaseのインスタンスを保持します。
(引数の部分です)

検索リクエストの処理

検索リクエストがsearchメソッドに送信されたとき、リクエストからnameとidを取得し、使用してUserSearchDtoオブジェクトを作成します。

ビジネスロジックの実行

UserSearchDtoオブジェクトをユースケースのexecuteメソッドに渡します。
ユースケースのexecuteメソッドがユーザー検索のビジネスロジックを実行し、結果を返します。

結果

検索結果をJSON形式でクライアントに返します。

Service Provider

AppServiceProviderは、サービスコンテナにインターフェースと実装をバインドします。
依存注入が可能になります。

// app/Providers/AppServiceProvider.php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\UserService;
use App\Services\UserServiceInterface;
use App\Repositories\UserRepository;
use App\Repositories\UserRepositoryInterface;

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->singleton(UserServiceInterface::class, UserService::class);
        $this->app->singleton(UserRepositoryInterface::class, UserRepository::class);
    }

    public function boot()
    {
        //
    }
}

Routes設定

ルート設定ファイルに、Bladeテンプレートを表示するためのルートとAPIエンドポイントを定義します。

// routes/web.php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\API\UserController;

Route::view('/search-users', 'search');
Route::post('/api/search-users', [UserController::class, 'search']);

「/search-users」でviewを表示して、そこに「/api/search-users」を実行するボタンなどを追加するイメージになります。

まとめ

  • app/Dto/UserSearchDto.php:
    • 検索条件を格納するデータ転送オブジェクト (DTO)。
      • 検索条件(名前、IDなど)を保持するためのオブジェクトです。
  • app/Dto/UserSearchResultDto.php:
    • 検索結果を格納するデータ転送オブジェクト (DTO)。
      • 検索結果の総数と結果のコレクションを保持するオブジェクトです。
  • app/Http/Controllers/API/UserController.php:
    • 検索リクエストを処理するコントローラー。
      • 検索リクエストを受け取り、適切なユースケースを呼び出して結果を返します。
  • app/Models/User.php:
    • usersテーブルに対応するEloquentモデル。
      • データベースのusersテーブルとやり取りするためのモデルクラスです。
  • app/Providers/AppServiceProvider.php:
    • サービスコンテナへのバインドを行うプロバイダー。
      • サービスインターフェースとその実装をサービスコンテナにバインドします。
  • app/Repositories/UserRepositoryInterface.php:
    • リポジトリのインターフェース。
      • ユーザーデータの検索を行うためのリポジトリのインターフェースを定義します。
  • app/Repositories/UserRepository.php:
    • リポジトリの実装。
      • リポジトリインターフェースを実装し、データベースからユーザーデータを取得します。
  • app/Services/UserServiceInterface.php:
    • サービスのインターフェース。
      • ビジネスロジックを提供するサービスのインターフェースを定義します。
  • app/Services/UserService.php:
    • サービスの実装。
      • サービスインターフェースを実装し、ビジネスロジックを実行します。
  • app/Usecases/SearchUsersUsecase.php:
    • ユースケースの実装。
      • ユースケースを実行し、サービスを介してビジネスロジックを実行します。
  • resources/views/search.blade.php:
    • ユーザー検索のためのフロントエンドのBladeテンプレート。
      • ユーザー検索のUIを提供するフロントエンドテンプレートです。
  • routes/web.php:
    • ルート設定ファイル。
      • HTTPリクエストを適切なコントローラーにルーティングします。

Laravel,PHPLaravel,Usecase

Posted by kami