(jp) =
CRUD シリーズを超えた Laravel のこの章では、アプリケーション層を深く掘り下げます。 このシリーズ全体の主な傾向は、コードをクリーンで簡潔で管理しやすいものにすることです。 この章では、コントローラーをクリーンで適切な状態に保つ方法を見ていきます。
ここで使用するパターンは、ビュー モデル パターンと呼ばれます。 その名前が示すように、これらのクラスはビュー ファイルのモデルです。 それらは、ビューにデータを提供する責任があります。それ以外の場合は、コントローラーまたはドメイン モデルから直接取得されます。 さらに、関心の分離が向上し、開発者により多くの柔軟性が提供されます。
本質的に、ビュー モデルは、いくつかのデータを取得し、それをビューで使用できるものに変換する単純なクラスです。 この章では、パターンの基本原則を示し、それらが Laravel プロジェクトにどのように統合されるかを見ていき、最後に、プロジェクトの 1 つでパターンを使用する方法を示します。
始めましょう。
カテゴリを含むブログ投稿を作成するフォームがあるとします。 ビューの選択ボックスにカテゴリ オプションを入力する方法が必要です。 コントローラーはそれらを提供する必要があります。
public function create()
return view('blog.form', [
'categories' => Category::all(),
]);
上記の例は create メソッドで機能しますが、既存の投稿も編集できる必要があることを忘れないでください。
public function edit(Post $post)
return view('blog.form', [
'post' => $post,
'categories' => Category::all(),
]);
次に、新しいビジネス要件があります。ユーザーが投稿できるカテゴリを制限する必要があります。つまり、ユーザーに基づいてカテゴリの選択を制限する必要があります。
return view('blog.form', [
'categories' => Category::allowedForUser(
current_user()
)->get(),
]);
このアプローチはスケーリングしません。 両方のコードを変更する必要があります create
と edit
方法。 投稿にタグを追加する必要がある場合、何が起こるか想像できますか? または、投稿を作成および編集するための別の特別な管理フォームがある場合は?
次の解決策は、次のように、投稿モデル自体にカテゴリを提供させることです。
class Post extends Model
public static function allowedCategories(): Collection
return Category::query()
->allowedForUser(current_user())
->get();
これが悪い考えである理由はたくさんありますが、Laravel プロジェクトではよく起こります。 私たちのケースに最も関連する問題に焦点を当てましょう: それでも重複が許されます。
新しいモデルがあると言う News
同じカテゴリの選択も必要です。 これも重複を引き起こしますが、コントローラーではなくモデル レベルで発生します。
別のオプションは、メソッドを User
モデル。 これは最も理にかなっていますが、メンテナンスも難しくなります。 前述のようにタグを使用していると想像してください。 ユーザーに依存しません。 ここで、ユーザー モデルからカテゴリを取得し、別の場所からタグを取得する必要があります。
モデルをビューのデータ プロバイダーとして使用することも特効薬ではないことは明らかだと思います。
要約すると、どこからカテゴリを取得しようとしても、コードの重複が常にあるようです。 これにより、コードの保守と推論が難しくなります。
ここで、ビュー モデルの出番です。 このロジックはすべてカプセル化されているため、さまざまな場所で再利用できます。 ビューに正しいデータを提供するという 1 つの責任しかありません。
class PostFormViewModel
public function __construct(User $user, Post $post = null)
$this->user = $user;
$this->post = $post;
public function post(): Post
return $this->post ?? new Post();
public function categories(): Collection
return Category::allowedForUser($this->user)->get();
このようなクラスの主な機能をいくつか挙げてみましょう。
- すべての依存関係が注入されます。これにより、外部コンテキストに最大限の柔軟性がもたらされます。
- ビュー モデルは、ビューで使用できるいくつかのメソッドを公開します。
- によって提供される新しい投稿または既存の投稿があります。
post
投稿を作成しているか編集しているかによって異なります。
コントローラは次のようになります。
class PostsController
public function create()
$viewModel = new PostFormViewModel(
current_user()
);
return view('blog.form', compact('viewModel'));
public function edit(Post $post)
$viewModel = new PostFormViewModel(
current_user(),
$post
);
return view('blog.form', compact('viewModel'));
最後に、次のようにビューで使用できます。
<input value=" $viewModel->post()->title " />
<input value=" $viewModel->post()->body " />
<select>
@foreach ($viewModel->categories() as $category)
<option value=" $category->id ">
$category->name
</option>
@endforeach
</select>
# Laravel でモデルを表示
前の例では、ビュー モデルとしていくつかのメソッドを持つ単純なクラスを示しました。 パターンを使用するにはこれで十分ですが、Laravel プロジェクト内にはさらにいくつかの機能を追加できます。
たとえば、ビュー モデルを直接 view
ビューモデルが実装する場合の関数 Arrayable
.
public function create()
$viewModel = new PostFormViewModel(
current_user()
);
return view('blog.form', $viewModel);
ビューは、次のようなビュー モデルのプロパティを直接使用できるようになりました。 $post
と $categories
. 前の例は次のようになります。
<input value=" $post->title " />
<input value=" $post->body " />
<select>
@foreach ($categories as $category)
<option value=" $category->id ">
$category->name
</option>
@endforeach
</select>
実装することで、ビュー モデル自体を JSON データとして返すこともできます。 Responsable
. これは、AJAX 呼び出しを介してフォームを保存していて、呼び出しが完了した後に最新のデータを再入力したい場合に役立ちます。
public function update(Request $request, Post $post)
return new PostFormViewModel(
current_user(),
$post
);
ビュー モデルと Laravel リソースの間には類似点が見られるかもしれません。 リソースはモデル上で 1 対 1 でマッピングされますが、ビュー モデルは必要なデータを何でも提供できることに注意してください。
私たちのプロジェクトでは、実際にリソースとビュー モデルを組み合わせて使用しています。
class PostViewModel
public function values(): array
return PostResource::make(
$this->post ?? new Post()
)->resolve();
最後に、このプロジェクトでは、JSON データを必要とする Vue フォーム コンポーネントを使用します。 魔法の getter を呼び出すときに、オブジェクトや配列の代わりにこの JSON データを提供する抽象化を行いました。
abstract class ViewModel
public function __get($name): ?string
$name = Str::camel($name);
$values = $this->$name();
if (! is_string($values))
return json_encode($values);
return $values;
ビュー モデル メソッドを呼び出す代わりに、それらのプロパティを呼び出して JSON を取得できます。
<select-field
label=" __('Post category') "
name="post_category_id"
:options=" $postViewModel->post_categories "
></select-field>
# ちょっと待って、ビュー コンポーザーはどうするの?
Laravel のビュー コンポーザーと重複する部分があると思われるかもしれませんが、誤解しないでください。 Laravel のドキュメントでは、ビュー コンポーザについて次のように説明されています。
ビュー コンポーザーは、ビューがレンダリングされるときに呼び出されるコールバックまたはクラス メソッドです。 ビューがレンダリングされるたびにそのビューにバインドしたいデータがある場合、ビュー コンポーザーはそのロジックを 1 つの場所に整理するのに役立ちます。
ビュー コンポーザーは次のように登録されます (例は Laravel ドキュメントから取得されます)。
class ViewComposerServiceProvider extends ServiceProvider
public function boot()
View::composer(
'profile', ProfileComposer::class
);
View::composer('dashboard', function ($view)
);
ご覧のとおり、ビューに変数を追加するために使用できるクラスとクロージャーの両方を使用できます。
コントローラーでビュー コンポーザーを使用する方法を次に示します。
class ProfileController
public function index()
return view('profile');
見えますか? いいえ、もちろんそうではありません: ビュー コンポーザはグローバル状態のどこかに登録されており、その暗黙の知識がなければ、ビューでどの変数を使用できるかわかりません。
今私は 知る これは小さなプロジェクトでは問題になりません。 あなたが唯一の開発者で、20 個のコントローラーとおそらく 20 個のビュー コンポーザーを持っている場合、すべてが頭の中に収まります。
しかし、このシリーズで書いているようなプロジェクトについてはどうでしょうか? 数千行のコードを数えるコードベースで複数の開発者と作業している場合、そのすべてが頭に収まりません。確かにその規模ではありません。 さらに、私たちはあなたの同僚や、彼らが個人的にもチームとして直面する困難についても考慮していません!
そのため、ビュー モデル パターンが推奨されるアプローチです。 コントローラー自体から、ビューで使用できる変数が明確になります。 その上、複数のコンテキストで同じビュー モデルを再利用できます。
最後の利点 — あなたが考えもしなかったかもしれないもの —
ビューモデルにデータを明示的に渡すことができるということです。 ビューに渡されるデータを決定するためにルート引数またはバインドされたモデルを使用する場合は、明示的に行われます。
結論として、グローバルな状態を管理することは、特に同じプロジェクトで複数の開発者と作業している場合、大規模なアプリケーションでは苦痛です。 また、2 つの手段の最終結果が同じだからといって、それらが同じであるとは限らないことも覚えておいてください。