1

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

  • app
    • (laravel app stuff)
  • src
    • Domain
      • Category
        • Actions
        • DataTransferObjects
        • Models
        • Resources

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

MrCujo
  • 1,218
  • 3
  • 31
  • 56

2 Answers2

1

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).


TL;DR:

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.


Long explanation:

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):

  • Every time you call $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)
  • You're still passing the current_user() and $post to the ViewModel and than, passing all of that to the view.
  • And last: When you need to response with JSON (Like API responses) you won't be using this ModelView, but rather a simple Resource
Orlando315
  • 207
  • 1
  • 8
  • Thank you! that's all I was looking for: a good explanation of what a `ModelView` is and this `When you need to response with JSON (Like API responses) you won't be using this ModelView, but rather a simple Resource` – MrCujo Aug 11 '22 at 09:52
0

A model is used as a way for questioning data to and from the table within the database. Resource classes are needed when building an API, you may need a transformation layer that sits between your Eloquent models and the JSON responses that are actually returned to your application's users.

Example:

$users = User:all();
return UserResource::collection($users);
Kevin
  • 1,152
  • 5
  • 13
  • 1
    The question is about `ViewModels` (see https://stitcher.io/blog/laravel-beyond-crud-08-view-models) which are a completely different thing than a regular Laravel Model. It's a way to expose data. – MrCujo Aug 02 '22 at 20:46