好きなタイミングでパロメータの値を整形する方法を紹介します。
パロメータを管理するクラスを作成し、パロメータ加工の処理を委譲させます。
こんな悩みないですか?
下記のステータスの配列がPOSTされました。
$statuses = [1, 2, 3, 4, 5];
POSTされたこの配列をInsert()できる配列に変換したい。
return [
'notice_id' => $notice->id,
'status' => $status,
];
しかし、この処理をどこで書こうか?
元のint[]配列は、いろんな場所で使うんだよなあ
どこかで配列を整形したいですが、整形後に別のメソッドで元のint[]の配列が必要になることがあります。
仕様変わると構造変えなきゃいかん。
どうすれば、好きなタイミングでパロメータの値を整形できるだろうか?
Paramクラスのメリット
投稿されたパロメータを管理するParamsクラスを作ると非常に楽です。
どの階層でもデータ整形が可能
Paramsクラスにはデータを渡すだけでなく、データの整形する責務を持たせます。
その結果、任意の箇所でデータ整形が可能になります。
Service層、Domain層、Repository層でも好きな箇所でデータ整形ができるメリットは大きいです。
例えば、Service層でデータ整形するメソッドを定義し、Repository層に整形した配列データを渡すとします。
しかし仕様が複雑になり、Repository層でデータ整形をしなければいけないケースがあったとします。
その際、重複したメソッドが生まれます。
しかし、Paramsメソッドでデータ整形を委譲させておけば、引数から引数に渡されていき、どこでもデータ整形できるようになります。
つまり、仕様変更に柔軟に対応できます。
引数の変化に柔軟
Paramsクラスでパロメータを管理すると、引数が一つで完結します。
Paramsクラスなし
/**
* お知らせ更新
*
* @param int $notice_id お知らせ
* @param string $title タイトル
* @param string $body 本文
* @return bool
*/
public function update(
int $notice_id,
string $title,
string $body,
): bool;
Paramsクラスあり
/**
* お知らせ更新
*
* @param int $notice_id お知らせ
* @param NoticeParams $notice_params 保存する情報
* @return bool
*/
public function update(
int $notice_id,
NoticeParams $notice_params
): bool;
これは仕様が変わり、引数を増やさなければならないときに有益です。
例えば、カテゴリーである$categoriesを引数に追加する必要があるとします。
Paramsクラスなし:全部階層で引数追加
それぞれの階層の引数に$categoriesを追加する。
- Controller
- Service層
- Domain層
- Repository層
少々手間が発生する。
/**
* お知らせ更新
*
* @param int $notice_id お知らせ
* @param string $title タイトル
* @param string $body 本文
* @param array $categories カテゴリー
* @return bool
*/
public function update(
int $notice_id,
string $title,
string $body,
array $category,
): bool;
Paramsクラスあり:NoticeParamsだけを編集
NoticeParamsに$categoriesを追加するだけ完結
パロメータが増えても引数の変化を意識しなくてもいい。
/**
* お知らせ更新
*
* @param int $notice_id お知らせ
* @param NoticeParams $notice_params 保存する情報
* @return bool
*/
public function update(
int $notice_id,
NoticeParams $notice_params
): bool;
このように、パロメータが増えても各層の引数に影響しないメリットがあります。
実際にParamsクラスをつくる
下記のようなパロメータを扱うクラスを用意すると、非常にコードがスッキリします。
<?php
/**
* お知らせを保存するためのパラメータを管理する。
*
* @property int[] $statuses お知らせ対象のステータス
*/
class NoticeParams
{
/**
* お知らせ対象のステータス
*
* @var int[]
*/
public array $statuses;
/**
* 完全コンストラクタ
*
* @param int[] statuses お知らせ対象のステータス
*/
private function __construct(array $statuses)
{
$this->statuses = $statuses;
}
/**
* 生成処理
*
* @param int[] $statuses お知らせ対象のステータス
* @return self
*/
public static function make(array $statuses): self
{
return new self($statuses);
}
/**
* notice_statusesテーブルに保存可能な配列を生成
*
* @param Notice $notice お知らせid
* @return array
*/
public function makeStatusesValues(Notice $notice): array
{
$old_statuses = $notice->notice_statuses->pluck('status');
$new_statuses = $this->statuses;
return collect($new_statuses)
->diff($old_statuses)
->map(function (int $status) use ($notice) {
return [
'notice_id' => $notice->id,
'status' => $status,
];
})->toArray();
}
/**
* 更新前と更新後の差分から、削除対象のステータスの配列を取得
*
* @param Notice $notice お知らせ
* @return array
*/
public function getShouldDeletedStatuses(Notice $notice): array
{
$old_statuses = $notice->noticeStatuses->pluck('status');
$new_statuses = $this->statuses;
return $oldStatuses->diff($newStatuses)->toArray();
}
}
FormRequest内で生成する
このクラスをFormRequest内で生成します。
/**
* お知らせのパロメータを管理するクラスを生成
*
* @return NoticeParams
*/
public function makeNoticeParams(): NoticeParams
{
return NoticeParams::make($this->statuses);
}
このように、Request内で生成して、コントローラーに呼び出します。
このParamsクラスは、FormRequestの値を保持しているため、引数も一つで完結します。
$notice = $this->noticeSharedService->create(
$request->makeNoticeParams()
);
パロメータの値を加工する役割を持たせる。
配列の生成を委譲させているので、insert()に必要な配列やdelete()に必要な配列を生成することができます。
insertで一括保存したい場合の配列生成処理の例だと
/**
* notice_statusesテーブルに保存可能な配列を生成
*
* @param Notice $notice お知らせ
* @return array
*/
public function makeStatusesValues(Notice $notice): array
{
return collect($this->statuses)->map(function (int $status) use ($notice) {
return [
'notice_id' => $notice->id,
'status' => $status,
];
})->toArray();
}
上記箇所で、配列を生成します。
これをisnert文で利用することで、一括保存させます。
DB::table('notice_statuses')->insert(
$noticeParams->makeAdoptionStatusesValues($notice)
);
更新処理で削除したい場合は、下記の処理を利用します。
/**
* 更新前と更新後の差分から、削除対象のステータスの配列を取得
*
* @param Notice $notice お知らせ
* @return array
*/
public function getShouldDeletedStatuses(Notice $notice): array
{
$old_statuses = $notice->noticeStatuses->pluck('status');
$new_statuses = $this->statuses;
return $oldStatuses->diff($newStatuses)->toArray();
}
差分を見て削除対象の値を配列にまとめます。
これを削除処理に渡します。
DB::table('notice_statuses')
->where('notice_id', $notice->id)
->whereIn('status', $noticeParams->getShouldDeletedStatuses($notice))
->delete();
これで対象外のステータスを削除できます。
このようにパロメータから値を生成する役割を任せることで、ServiceやRepositoryがスッキリできます。。
また、処理を委譲させているため、このクラスを利用すれば好きな場所で配列生成ができ、仕様変更や他のクラスに配列が必要になった際にも柔軟に対応できるので非常に良いです。
終わりに
パロメータの値を加工させるクラスを用意すると、状況に合わせて必要な配列を生成できるので、非常に便利です。
シンプルな役割なので、どこで定義するのがベストなんだという悩みからも解放されます。
コメント