I'm starting to work on an API service with Laravel. Now, I'm using a DDD approach (and learning it at the same time).
Currently my structure looks like this:
MyApp
As you can see I'm currently using Resources
. So for example, in my Categories' controller I've got:
public function index(): AnonymousResourceCollection
{
$categories = Category::all();
return CategoriesResource::collection($categories);
}
and my resource file looks like this:
<?php
namespace Domain\Category\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class CategoriesResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param Request $request
* @return array
*/
public function toArray($request): array
{
return [
'id' => (string)$this->id,
'type' => 'categories',
'attributes' => [
'name' => $this->name,
'parent' => $this->parent,
'description' => $this->description,
'image' => $this->image,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
]
];
}
}
which returns the JSON response that will be eventually expected by the frontend.
Now, I've been reading about ModelView
but still don't understand the concept or how could it work instead of (or along with) Resources
. Most of the examples I've seen so far don't actually return a whole JSON response from the ModelView
.
Thanks
They are 2 different things. Laravel Resources
are especially design for API responses, to transform your Eloquent models into JSON responses using any kind of logic that you may need. As simple as that (Like you're doing in your code).
Resource
: Transform, add or remove attributes, load relationships, into a "JSON representation of your models (Or collection
of models)".
ModelView
: It's just a file with a bunch of methods to be used in you view instead of passing each variable individually in every method of your controller.
Now, in the link you posted about ModelView
they give this examples as the ordinary way of passing data to a view:
public function create()
{
// Here we are just passing the $categories
return view('blog.form', [
'categories' => Category::allowedForUser(
current_user()
)->get()
]);
}
public function edit(Post $post)
{
// Here we are passing $post, and the $categories are
// the same like in the "create" method
return view('blog.form', [
'post' => $post,
'categories' => Category::allowedForUser(
current_user()
)->get()
]);
}
This "ViewModel" is just a way to encapsulate all of the logic and methods away from your controllers into a single file, which at the end, contains all the information passed to the view (Even data that you may not need).
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
{
// The same query as in the "ordinary" example
return Category::allowedForUser($this->user)->get();
}
}
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'));
}
}
And this, is how should be use in the view:
<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>
There are a few things I don't like about this approach (In this particular example):
$viewModel->categories()
you're making querys to the database (Instead of passing $categories and calling it as many times as you want in the view without making new querys)current_user()
and $post
to the ViewModel
and than, passing all of that to the view.ModelView
, but rather a simple Resource