Laravel 7 Class Based Components – Part II

21/08/2020 7 min read
laravel
php

In my previous post about Laravel 7 Class Based Components I went over creating class based components as well as passing data to components and displaying them. In this post I will go over component class methods. And once again, class based components allow us to create robust components and harness the power of classes, so do more exploring on your own – you will not regrate it! ☝

Component Class Methods

Let’s say we have a news site with a news grid container and some news items in it. We would probably create 2 components: a news grid container component and a news grid item component. 

Let’s create our components:

php artisan make:component NewsGridContainer
php artisan make:component NewsGridItem

Then, we could pass a news items array with news items to our news grid container, inside the grid container.

In our welcome.blade.php:

@php
  $newsItems = [
   [
     'id' => 1,
     'title' => 'this is my title',
     'body' => '......',
     'featured_image' => '/images/featured.jpeg',
  ],
];
@endphp
<x-news-grid-container :news-items="$newsItems"/>

In order to pass our $newsItems array to news-grid-container.blade.php we first need to declare it as  public property in the app/Views/Components/NewsGridContainer.php class and assign it in the __construct() method:

<?php

namespace App\View\Components;

use Illuminate\View\Component;

class NewsGridContainer extends Component
{
    public $newsItems;
    /**
     * Create a new component instance.
     *
     * @return void
     */
    public function __construct($newsItems)
    {
        $this->newsItems = $newsItems;
    }
    ...
}

And in resources/views/components/news-grid-container.blade.php:

<div class="grid-container">
    @foreach ($newsItems as $item)
        <x-news-grid-item :news-item="$item" />
    @endforeach
</div>

Here are our news grid item class properties declarations and __construct():

<?php

namespace App\View\Components;

use Illuminate\View\Component;

class NewsGridItem extends Component
{

    public $title;
    public $body;
    public $featured_image;

    /**
     * Create a new component instance.
     *
     * @return void
     */
    public function __construct($newsItem)
    {
        $this->title = $newsItem['title'];
        $this->body = $newsItem['body'];
        $this->featured_image = $newsItem['featured_image'];
    }
    ...
}

And our resources/views/components/news-grid-item.blade.php:

<div class="news-grid-item">
    <img class="featured-image" src="{{ $featured_image }}" alt="featured image">     
    <div class="content-body">
        <h3 class="title">{{ $title }}</h3>
        <p class="excerpt">{{ $body }}</p>
        <button class="read-more">READ MORE</button> 
    </div>
</div>

Now that we have some components to work with, let’s go over some basic ways of using component class methods. 

First, let’s create a simple public function saySomething() that will simply return ‘Hello World’.

<?php

namespace App\View\Components;

use Illuminate\View\Component;

class NewsGridItem extends Component
{
    ...
    public function saySomething(){
        return 'Hello World';
    }
}

This is how you would call it in you blade component:

<div class="news-grid-item">
    {{ $saySomething }}
    ...
</div>

If we want to pass some data to this function, we first need to change it a bit:

<?php

namespace App\View\Components;

use Illuminate\View\Component;

class NewsGridItem extends Component
{
    ...
    public function saySomething($msg){
        return $msg;
    }
}

And in the component:

<div class="news-grid-item">
    {{ $saySomething('Hello World') }}
    ...
</div>

Now, let’s say our news item’s title is all in lower case letters and we want to “Title Case” it. We have a lot of ways of doing it: Modify the title already in our __construct method, or maybe inside our blade component itself. we can also create a public function called titleCase($title) in our class and pass the title to it:

<?php

namespace App\View\Components;

use Illuminate\Support\Str;
use Illuminate\View\Component;

class NewsGridItem extends Component
{
    ...
    public function titleCase($title){
        return Str::title($title);
    }
}

Then, we would need to call it like this in our blade component (or do something even uglier in our __construct method):

<div class="news-grid-item">
    {{ $titleCase($title) }}
    ...
</div>

However, there is a much nicer way of modifying properties. We can create a public function that shares the name of a declared property like this:

<?php

namespace App\View\Components;

use Illuminate\Support\Str;
use Illuminate\View\Component;

class NewsGridItem extends Component
{
    ...
    public function title(){
        return Str::title($this->title);
    }
}

And our blade component can remain nice and clean:

<div class="news-grid-item">
    {{ $title }}
    ...
</div>

I’ve added 2 more methods to this class just to show a bit more of what is possible – but this does not even scratch the surface. Here is the full result:

<?php

namespace App\View\Components;

use Illuminate\Support\Str;
use Illuminate\View\Component;

class NewsGridItem extends Component
{

    public $title;
    public $body;
    public $featured_image;

    /**
     * Create a new component instance.
     *
     * @return void
     */

    public function __construct($newsItem)
    {
        $this->title = $newsItem['title'];
        $this->body = $newsItem['body'];
        $this->featured_image = $newsItem['featured_image'];
    }

    /**
     * Get the view / contents that represent the component.
     *
     * @return \Illuminate\View\View|string
     */

    public function render()
    {
        return view('components.news-grid-item');
    }

    public function title()
    {
        return Str::title($this->title);
    }

    public function body()
    {
        return Str::limit($this->body, 40);
    }

    public function image(){
        // Check if image exists in public assets folder
        if(file_exists(public_path($this->featured_image))){
            return asset($this->featured_image);
        } 
        // Fallback
        return asset('/images/fallback.png');
    }
}

And our blade component:

<div class="news-grid-item">
    <img class="featured-image" src="{{ $image }}" alt="featured image">     
    <div class="content-body">
        <h3 class="title">{{ $title }}</h3>
        <p class="excerpt">{{ $body }}</p>
        <button class="read-more">READ MORE</button> 
    </div>
</div>

Read more about class-based components in the Laravel official docs:

👉 https://laravel.com/docs/7.x/blade#components

Related Posts