フレームワークへの依存度を下げるFLUDパターン (3)

フレームワークへの依存度を下げるFLUDパターン (2) の続きです。

CodeIgniter4のチュートリアルのコードをFLUDパターンに変更しています。

次は、個別の記事の表示を変更します。

ドメイン層

NewsRepositoryInterface

NewsRepositoryInterface に個別のNewsを取得するメソッドを追加します。

--- a/packages/news/src/Domain/News/NewsRepositoryInterface.php
+++ b/packages/news/src/Domain/News/NewsRepositoryInterface.php
@@ -10,4 +10,6 @@ interface NewsRepositoryInterface
      * @return News[]
      */
     public function getNewsList(): array;
+
+    public function getNewsBySlug(string $slug): ?News;
 }

getNewsBySlug() メソッドは指定されたslugに対して News または null を返します。

ユースケース層

GetNewsItemUseCase

News を取得するユースケースを追加します。

packages/news/src/UseCase/News/GetNewsItemUseCase.php

<?php

declare(strict_types=1);

namespace Acme\News\UseCase\News;

use Acme\News\Domain\News\News;
use Acme\News\Domain\News\NewsRepositoryInterface;

class GetNewsItemUseCase
{
    /**
     * @var NewsRepositoryInterface
     */
    private $newsRepository;

    public function __construct(NewsRepositoryInterface $newsRepositry)
    {
        $this->newsRepository = $newsRepositry;
    }

    public function run(string $slug): ?NewsDto
    {
        /** @var News|null $news */
        $news = $this->newsRepository->getNewsBySlug($slug);

        if ($news !== null) {
            return new NewsDto(
                $news->id,
                $news->title,
                $news->slug,
                $news->body
            );
        }

        return null;
    }
}

リポジトリから News を取得した場合、NewsDto に詰め替えて返しています。

フレームワーク/ライブラリ層

モデル

フレームワークのモデルです。

NewsRepository

NewsRepositorygetNewsBySlug() メソッドを追加します。

--- a/app/Models/NewsRepository.php
+++ b/app/Models/NewsRepository.php
@@ -31,4 +31,22 @@ class NewsRepository extends Model implements NewsRepositoryInterface

         return $newsList;
     }
+
+    public function getNewsBySlug(string $slug): ?News
+    {
+        $stdClass = $this->where(['slug' => $slug])->asObject()->first();
+
+        if ($stdClass === null) {
+            return null;
+        }
+
+        $news = new News(
+            $stdClass->id,
+            $stdClass->title,
+            $stdClass->slug,
+            $stdClass->body
+        );
+
+        return $news;
+    }
 }

CodeIgniter\Modelfirst() メソッドでデータベースから条件にマッチする最初のレコードを取得し、News インスタンスに変換して返します。

コントローラ

フレームワークのコントローラです。

Newsコントローラ

view() メソッドを GetNewsItemUseCase を使うように変更します。

--- a/app/Controllers/News.php
+++ b/app/Controllers/News.php
@@ -5,6 +5,7 @@ namespace App\Controllers;
 use App\Models\NewsRepository;
 use Acme\News\UseCase\News\NewsDto;
 use Acme\News\UseCase\News\GetNewsListUseCase;
+use Acme\News\UseCase\News\GetNewsItemUseCase;

 class News extends BaseController
 {
@@ -28,15 +29,16 @@ class News extends BaseController

     public function view($slug = null)
     {
-        $model = model(NewsModel::class);
+        $repository = model(NewsRepository::class);
+        $useCase = new GetNewsItemUseCase($repository);

-        $data['news'] = $model->getNews($slug);
+        $data['news'] = $useCase->run($slug);

         if (empty($data['news'])) {
             throw new \CodeIgniter\Exceptions\PageNotFoundException('Cannot find the news item: ' . $slug);
         }

-        $data['title'] = $data['news']['title'];
+        $data['title'] = $data['news']->title;

         echo view('templates/header', $data);
         echo view('news/view', $data);

GetNewsItemUseCase を生成し、実行しています。

view() メソッド全体は以下のようになります。

    public function view($slug = null)
    {
        $repository = model(NewsRepository::class);
        $useCase = new GetNewsItemUseCase($repository);

        $data['news'] = $useCase->run($slug);

        if (empty($data['news'])) {
            throw new \CodeIgniter\Exceptions\PageNotFoundException('Cannot find the news item: ' . $slug);
        }

        $data['title'] = $data['news']->title;

        echo view('templates/header', $data);
        echo view('news/view', $data);
        echo view('templates/footer', $data);
    }

ビュー

フレームワークのビューです。

配列だった $newsNewsDto インスタンスに変えたため、プロパティを表示するようにコードを変更しています。

--- a/app/Views/news/view.php
+++ b/app/Views/news/view.php
@@ -1,2 +1,2 @@
-<h2><?= esc($news['title']) ?></h2>
-<p><?= esc($news['body']) ?></p>
+<h2><?= esc($news->title) ?></h2>
+<p><?= esc($news->body) ?></p>

これで、個別の記事も表示されるようになりました。

ディレクトリ構成

現状のディレクトリ構成は以下になります。

app/
├── Config
│   ├── Autoload.php
├── Controllers
│   ├── News.php
├── Models
│   └── NewsRepository.php
└── Views
     └── news
          ├── overview.php
          └── view.php

packages/
└── news ... Newsパッケージ
     └── src
         ├── Domain ... ドメイン層
         │   └── News
         │       ├── News.php
         │       └── NewsRepositoryInterface.php
         └── UseCase ... ユースケース層
             └── News
                 ├── GetNewsItemUseCase.php ← 追加
                 ├── GetNewsListUseCase.php
                 └── NewsDto.php

app/ 以下のファイルはモデルのファイル名を変更しただけで配置はそのままにしています。

packages/ 以下にドメイン層とユースケース層のコードが追加されています。

フレームワークへの依存度を下げるFLUDパターン (4) へ続く。

参考

Date: 2021/12/22

Tags: codeigniter, codeigniter4, ddd, mvc, php