
When working with .NET Framework MVC applications, I used to use bound view models, which let me use classes on the Razor templates very easily wit...
For further actions, you may consider blocking this person and/or reporting abuse
I think the ViewModel concept is very tricky to get right, because it is easy to cause SRP violations or separation of concerns violations.
In an ideal situation to create a view, there is a storage query combiner to reduce the number of queries and a data transformer to prepare the data from the storage that is still in a raw form. These mechanisms should be configurable to make them as maintainable as possible.
Moving from an array to a class has not much added value.
If you want a root for the view data just wrap the keys,
return view('index', ['page' => $data]);
.The reason an array is the most common data structure for views is because views are prone to change. Having a set schema makes it harder to make changes.
If you want more control over the array keys you could use enums.
For me the ViewModel is an abstraction that will hurt you in the long run. If you look at the Spatie controller example I already see problems when the
create
andedit
pages will not contain the same fields.You may be right but I disagree. I am going to post a 5-post series on this topic next. This approach will also give us autocompletion in blade views which is not possible with arrays. Moreover, I am going to introduce fully-typed blade views and the blade views will always expect specific type of data. If not provided or provided different data, they will complain. The main purpose is to follow strict structure and set a standard for the whole team to follow.
While I agree creating a DTO like object makes it easier to autocomplete and allows to check the type. I would go all out with the DTO concept instead of having a mix of storage retrieval and a DTO.
Yes DTO is an excellent option. I have thought it through. ViewModels can have properties have DTO type classes. Viewmodels in my views are about defining a strict type shape the views can depend on. Moreover if you have used graphql that is initiator for the required data, here viewmodels will act same and ask for data required by view.
The main reason I don't like the ViewModel is the data retrieval, because you're adding the whole class to the view. Nothing is stopping you to do the data retrieval in the view, and that could be a source of problems.
if you look at the Spatie example they also have no data retrieval. The model,
Post
is added during instantiation.About the shape, you can do that with DTOs too.
I think graphql is a bad comparison because it is a an independent layer, while the ViewModel is a part of the view.
I should clarify that in my approach, the ViewModel isn’t responsible for fetching data you can do it anyhow. Retrieval stays in services, repositories, or wherever your domain logic lives. The ViewModel’s job begins after the data exists — it simply provides a typed, predictable contract for the view. That’s the same reason the Spatie example avoids retrieval inside the ViewModel, and I agree with that boundary.
On shape, DTOs can do this as well, but I find that tying the contract directly to the view makes the intent clearer for teams. DTOs are more general-purpose, while ViewModels are focused on ensuring the Blade side always receives exactly what it expects.
And about GraphQL, I only bring it up as a conceptual analogy. Both enforce a strict definition of "this is the data a view will get". The implementation context is different, but the discipline of shaping data is the same.
In the
handle
function you call data retrieval methods, so you are contradicting your own boundary.A DTO is what you want it to be, it can be general purpose but it can also be as specific as you want it to be. The first examples of the ViewModel classes are DTOs.
A way to structure it to make the DTOs focused is with a ViewData suffix for the classes or group them in a ViewData directory.
You are just changing the graphql concept to fit your narrative. In the first comment you mention it asks for the data, and with the latest comment you mention it is to shape the data.
This makes you look like you didn't really think it through.
The
handle()
in my examples isn’t about querying storage — it just assembles data that’s already been fetched into a strict shape for the view. I’ll make that clearer going forward.On DTOs, the distinction I’m drawing is intent. A PostViewModel communicates,
this is exactly what the post Blade template will always receive.
It’s a narrower, view-focused contract, even if it looks similar to a DTO.As of GraphQL,
it both initiates requests and specifies only the fields needed
. That’s the mindset I want ViewModels to bring to Blade, clear, minimal and predictable data.The section of the post where the
handle
example is named; filling the viewmodel with data. And you are using your design pattern that is just a layer that wraps the builder methods from Laravels Model class. That for me is a tight coupling with the data storage.So in this post you are showing how to abuse the pattern, to show it the right way in further posts? That seems a bad way to introduce a pattern.
I also think the focus on Blade templates receiving the correct information is the wrong one. It is the job of the backend to send the right data to the templates. It should not matter what template engine you are using.
I think the reason for the ViewModel is to have the two mechanisms I mentioned in my first comment or at least the data transformer. But because they are not separated I see potential problems in the long run.
Let me simplify what I mean by a ViewModel.
That’s the core idea. Forget the handle method if it’s distracting — fetch the data however you want. The point isn’t how you retrieve it, but that the view always gets a clearly defined, consistent contract.
or alternately
or
You could call this a DTO if you want, but when it’s bound directly to a view I find “ViewModel” communicates intent better. Yes, the backend is responsible for providing data, but the ViewModel draws the line: this is exactly how much and what kind of data the view needs. Also in my first example
$products
can be of typeProductDTO
.I don't understand what's going on here
How does the $params get passed to the
GetAllBrands::handle
etc. And where are they defined?They are supposed to be like this from controller.
Once done, pass down the params.