Introducing View Components in Laravel, an alternative to View Composers

Tutorials

May 15th, 2018

laravel-view-components.png

In software development, one of the “best practices” is to create reusable code that can be implemented in different parts of your application if needed.

Imagine you have a blog, and you need to show a “highlights” widget on a sidebar.

The “highlights” will be populated with the response of an API.

So, in your homepage controller You’d probably have something similar to the following:

1<br></br><?php
2
3class HomeController extends Controller {
4
5 protected $blog;
6
7 public function __construct(BlogRepository $blog)
8 {
9 $this->blog = $blog
10 }
11
12 public function index()
13 {
14 return view('blog', [
15 'posts' => $blog->latest(),
16 'highlights' => $blog->highlights()
17 ]);
18 }
19}

Nice and clean, but this approach starts to become a problem when you need to pass the same “highlights” variable to every page of your site; for example, a contact page:

1<?php
2
3class ContactPageController extends Controller {
4
5 protected $blog;
6
7 public function __construct(BlogRepository $blog)
8 {
9 $this->blog = $blog
10 }
11
12 public function index()
13 {
14 return view('contact', [
15 'highlights' => $blog->highlights()
16 ]);
17 }
18}

Imagine what would happen if you have 20 different controllers; You might end up having a lot of code duplication and as your application grows it will be harder to maintain.

Using Laravel’s view composers

View composers allow you to move the logic outside your controller and pass the data to the specified set of views.

1<?php
2
3class HighLightsComposer
4{
5
6 protected $users;
7
8 public function __construct(BlogRepository $blog)
9 {
10 $this->blog = $blog
11 }
12
13 public function compose(View $view)
14 {
15 $view->with('highlights', $this->blog->highlights());
16 }
17}

And then in your service provider, You’ll have something like this:

1<?php
2
3class ComposerServiceProvider extends ServiceProvider
4{
5 public function boot()
6 {
7 View::composer(
8 'highlights', 'App\Http\ViewComposers\HighlighsComposer'
9 );
10 }
11}

At this point you can refactor your controllers like so:

1<?php
2
3class HomeController extends Controller {
4
5 protected $blog;
6
7 public function __construct(BlogRepository $blog)
8 {
9 $this->blog = $blog
10 }
11
12 public function index()
13 {
14 return view('blog', [
15 'posts' => $blog->latest()
16 ]);
17 }
18}
19
20
21class ContactPageController extends Controller {
22
23 public function index()
24 {
25 return view('contact');
26 }
27
28}

Thinking about the product

At first hand, this looks great, but let’s give it some thought.

When you use “view composers,” the process it’s a little magic and runs in a part of the app that is not that obvious, especially for those who do not have much experience using Laravel.

Imagine your client asks you to replace the content of the “highlights” widget for something static. In the current example, you can do it just by updating the content of your “highlights.blade.php” file.

The app will work as expected but, the API call is going to be running in the background (in the View Composer).

So, it doesn’t matter if you remove the logic from your views, you’ll need to change the name on the view, or update the ServiceProvider to stop the API call from the ViewComposer.

Using View Components

The following is an approach I’m using in one of the projects I’m working on right now, I made up the name, feel free to call it as you wish.

What we want is to reuse a common view that will be built using dynamic data (coming from any resource).

Creating a new Highlights component class

This “View Component” classes could share an interface or contract to specify the type of data we want to return. In this case, the Laravel’s Htmlable contract will suit perfectly.

1<?php
2
3namespace App\ViewComponents;
4
5use Illuminate\Support\Facades\View;
6use Illuminate\Contracts\Support\Htmlable;
7
8class HighlightsComponent implements Htmlable
9{
10 protected $blog;
11
12 public function __construct(BlogRepository $blog)
13 {
14 $this->blog = $blog;
15 }
16
17 public function toHtml()
18 {
19 return View::make('highlights')
20 ->with('highlights', $this->blog->highlights())
21 ->render();
22 }
23}

Creating a new blade directive to render the view components

As we are using dependency injection in the class above, it would be a good idea to use the IOC to resolve those dependencies for us.

1<?php
2
3class AppServiceProvider extends ServiceProvider
4{
5 public function register()
6 {
7 Blade::directive('render', function ($component) {
8 return "<?php echo (app($component))->toHtml(); ?>";
9 });
10 }
11}

Finally, you can “render” the view component, which will return an HTML partial, on any view.

1// home.blade.php
2
3@render(\App\ViewComponents\HighlightsComponent::class)

Wrapping up

Using this approach you can reuse complex components using dynamic data on any view within your application.

The component logic will only run if it’s included through the @render() blade directive.

If you work with a large team of developers, you can be sure that the performance of your application will not be affected if anyone changes the implementation of the widget in the view without updating the implementation in the backend.

Filed in:

Jeff

I'm a full-stack web developer and a part-time writer.

You can find more of my writing on https://medium.com/@jeffochoa.