Let’s Learn Laravel Blade: Components (Part 4)

In the last posts, we learned about layouts and partials, but Laravel provides even more functionality. You may ask: why use a component when you can just use partials? In Laravel, components are packed with additional functionality. Components give you separation of concerns, letting you outsource PHP logic to a method outside of your blade view. This helps declutter your main view, which should only be presentation logic in the first place. Components also give you useful helpers such as $attributes and slots, which let you use truly dynamic and reusable components. Let’s get started and figure this out.

Registering a Component

To start, let’s have Laravel create a component for us. The command php artisan make:component will generate a component for you. The component should end up in App\View\Components, and any other components will also be stored there. For our example, we’ll create a simple input component with front end validation. To create a component with the name “CustomInput”, use php artisan make:component CustomInput. To work with the component, you will need basic PHP logic. The class this command generates is pretty straightforward, I will walk through that next.

Walking Through The Component Class

Whether you create the component class file manually or automatically, it will look similar. Each component will have defined variables, a __construct class, and a render method. Let’s see what all of this looks like.

<?php

namespace App\View\Components;

use Illuminate\View\Component;

// Class name, make sure it matches the name of the file and uses the capital case format
class CustomInput extends Component
{
    // Define the possible attributes that can be passed into the component. In this case, you can pass id, class, and validationMessage
    public $id;
    public $className;
    public $validationMessage;

    // The construct method takes possible attributes as arguments
    public function __construct($id, $className, $validationMessage)
    {
        // The arguments are passed through and assigned to the component
        $this->id = $id;
        $this->className = $className;
        $this->validationMessage = $validationMessage;
    }

    public function render()
    {
        // If it's not obvious, this is where the actual blade component is rendered. The location can be anywhere, but the components folder is as good of a place as any.
        return view('components.custom-input');
    }
}

If you’ve ever seen a PHP class, you’ll see that nothing special is going on here. When you create a component, you’ll have a plan for what possible attributes can be passed to it. For a very simple input component, all we should need is an optional class name, an id and a front-end validation message. Typical HTML input attributes such as required and maxlength can be passed to the component without defining them in the class. We’ll see an example of that shortly.

Creating the Blade Component

If you look at the classes render method, you’ll see it loads a custom-input file. As long as you have a custom-input.blade.php file, it should render without a problem. Let’s create that file.

<div class="position-relative">
  <label class="form-label" for="{{$id}}">{{$label}}</label>
  <input type="text" class="form-control {{$className}}" name="{{$id}}" id="{{$id}}" {{$attributes}}/>

  <div class="invalid-feedback">
      <span>{{$validationMessage}}</span>
  </div>
</div>

{{-- Back end form validation --}}
@error($name)
  <div class="invalid-feedback d-block" role="alert">
    @foreach ($errors->get($id) as $message)
      <p>{{ $message }}</p>
    @endforeach
  </div>
@enderror

This may look like a lot, but all that is happening here is a label with a basic text input is being provided. The other bits of code are for front-end and back-end validation. In this example, we are using Bootstrap 5 form validation helper classes. The position-relative class on the parent is for future use cases and for certain types of validation, but we won’t go over that in too much detail.

Out of slight laziness, I made the name and ID attributes the same. For the future, adding and removing custom attributes as needed is pretty straightforward

Laravel back-end validation is typically repeatable code, and can take up a lot of room once your application gets 5, 10, 50 or 100 inputs! That’s why it’s useful to put that logic inside of your component. Let’s see how to call our component now.

<x-custom-input label="Our Input" id="our-input" validationMessage="Please enter a valid value for this input." class="my-3" required maxlength="3"/>

In just one line of code, we created an input with back-end and front-end validation baked into it along with a label! For every component you make, make sure to prefix it with x-., that is standard Laravel convention.

You may have noticed that required and maxlength were passed in but aren’t defined on the class. That is what $attributes is useful for. You can pass any amount of attributes through the x-custom-input and they will render in the blade component as long as $attributes is present. This is useful for keeping custom attributes in the PHP class decluttered and making your component easily reusable. Because we can pass in required, we don’t need a $required variable in the class. You can pass as many attributes as you’d like.

Using Slots in Components

The amount of flexibility we have for components is nearly unlimited. With slots, you can pass unique, non-component markup to your blade file. Here’s a simple example: What if we want to extend our input to also be a textarea? To do that, extend the PHP class to take in an input type with something like $inputType.

To extend the class with another custom attribute, you define the variable with public $inputType, pass it into the constructor and assign it: $this->inputType = $inputType. When passing in a custom attribute, we can have a default argument just like you would in regular PHP.

public function __construct($id, $className, $validationMessage, $inputType="input")
    {
        // The arguments are passed through and assigned to the component
        $this->id = $id;
        $this->className = $className;
        $this->validationMessage = $validationMessage;
        $this->inputType = $inputType;
    }

If we don’t pass anything into our blade component, the $inputType will default to "input". This is what we want for most of these components. Now a textarea can be made.

<x-custom-input label="Our Input" inputType="textarea" id="our-input" validationMessage="Please enter a valid value for this input." class="my-3" required maxlength="3"/>

The blade file should also be updated

<{{$inputName}} type="text" class="form-control {{$className}}" name="{{$id}}" id="{{$id}}" {{$attributes}}>{{$slot}}</{{$inputName}}>

Now that we have our text area, why would we want to use a slot? Sometimes you need to pass values to an input, either to set a default or to pass a value that was already filled on an update/edit view. For a regular input we’d pass something like value="3". Textareas work differently. We need to pass the value between the opening and closing input brackets for the value to apply. Since we can’t pass in the attribute, we need to use a slot. Whatever you pass in between the brackets of your component definition gets applied to the $slot variable. Here’s an example.

<x-custom-input label="Our Input" inputType="textarea" id="our-input" validationMessage="Please enter a valid value for this input." class="my-3" required maxlength="3">This will be applied to $slot</x-custom-input>

As you can imagine, a slot has more complex use cases than this, but this is a start. You can pass as much HTML through a component as you’d like. If you want to make a card or modal component, I’m sure you can imagine how useful slots can be. Modals will almost always have unique markup passed to it, but the close button, body, and main classes are repeatable.

Using Functions in Components

If you’ve done any kind of complex blade work, you’ve probably had multiple lines of PHP logic in your blade files. While this is fine and works, blade components give you a nice way of organizing it. In your component, you can call methods directly and create the logic in the PHP class. A simple example is making a conditional validation message.

We might do something like this

@if($validationMessage)
    {{ $validationMessage }}
@elseif($attributes['required'])
    The {{ ucwords(preg_replace('/_|-/', ' ', $name)) }} field is required.
@else
    Please provide a valid input.
@endif

The above code sets a custom validation message if it’s passed in and goes to a couple of generic fallbacks if it’s not. This works fine, but what if we only needed to call one variable to get all of this logic into the view? It’s pretty simple!

class CustomInput extends Component
{
...

// This is the same logic we had in the blade file converted to PHP
public function getValidationMessage($attributes, $message, $name) {
    if ($message) return $message;
    elseif ($attributes['required']) {
          return 'The ' . ucwords(preg_replace('/_|-/', ' ', $name)) . ' field is required.';
    }
    else return ' Please provide a valid input.';
}
...
}

Is it really as simple as defining a method in the class? Yup! Calling the method is about as simple as referring to an attribute. {{getValidationMessage($attributes, $validationMessage, $name)}}. You can have as many methods as you want. Using methods, your views will be as close to only displaying presentation logic as possible, which is best practice. In my experience, methods aren’t needed too often but will prove to be useful when they are.

So is That it?

While we did go over a lot here, there is a lot more to components that I couldn’t cover in only one post. You should check out the official docs to explore components more.

Now that we know about components and partials, there is no limit to what you can do with blade. You should be able to create just about any layout and view you want. The beautiful thing about Laravel is that there are always golden nuggets of functionality waiting to be found. Here’s to hoping you find them!

In the next post, I will walk you through creating a form with blade while helping you find the best methods to build them quickly. Hope to see you there!

Comments