Laravel View Models

Packages

September 18th, 2018

laravel-view-model-package.png

The Laravel View Models by Brent Roose of Spatie is a package that can move the work you might do in a controller to a “view model”:

Have you ever made a controller where you had to do a lot of work to prepare variables to be passed to a view? You can move that kind of work to a so-called view model. In essence, view models are simple classes that take some data and transform it into something usable for the view.

A view model class is designed to house the complex logic of your views and clean up view-related logic from controllers:

1class PostViewModel extends ViewModel
2{
3 public $indexUrl = null;
4
5 public function __construct(User $user, Post $post = null)
6 {
7 $this->user = $user;
8 $this->post = $post;
9
10 $this->indexUrl = action([PostsController::class, 'index']);
11 }
12
13 public function post(): Post
14 {
15 return $this->post ?? new Post();
16 }
17
18 public function categories(): Collection
19 {
20 return Category::canBeUsedBy($this->user)->get();
21 }
22}

The above view model class gets created in the controller:

1class PostsController
2{
3 public function create()
4 {
5 $viewModel = new PostViewModel(
6 current_user()
7 );
8
9 return view('blog.form', $viewModel);
10 }
11
12 public function edit(Post $post)
13 {
14 $viewModel = new PostViewModel(
15 current_user(),
16 $post
17 );
18
19 return view('blog.form', $viewModel);
20 }
21}

And together, the view model and the controller provide your view with the following abilities:

1<input type="text" value="{{ $post->title }}" />
2<input type="text" value="{{ $post->body }}" />
3
4<select>
5 @foreach ($categories as $category)
6 <option value="{{ $category->id }}">{{ $category->name }}</option>
7 @endforeach
8</select>
9
10<a href="{{ $indexUrl }}">Back</a>

All public methods and properties in a view model are automatically exposed to the view.

I think this package is an excellent abstraction of this logic, and instead of passing the view model to the view, you can return a view directly like this:

1class PostsController
2{
3 public function update(Request $request, Post $post)
4 {
5 // …
6
7 return (new PostViewModel($post))->view('post.form');
8 }
9}

And last but not least, you can expose functions which require extra parameters:

1class PostViewModel extends ViewModel
2{
3 public function formatDate(Carbon $date): string
4 {
5 return $date->format('Y-m-d');
6 }
7}

Which you can then reference in your template:

1{{ $formatDate($post->created_at) }}

Thanks to Brent Roose and the folks at Spatie for this excellent package! You can access the code and installation instructions from the GitHub repository spatie/laravel-view-models.

Filed in:

Paul Redmond

Full stack web developer. Author of Lumen Programming Guide and Docker for PHP Developers.