When I first started building a job portal in Laravel, I thought Blade templates were just views with a sprinkle of @if and @foreach. I dont really know that Blade had a component system that could make my code cleaner, reusable, and much easier to maintain.
Back then i think only React or Vue can do things like this for reusable component .
So whenever i need to display different type of jobs ( Feature , Premium , Hot , Normal ) my blade become really messy .
<div class="border p-4 rounded bg-white shadow-sm">
<h3>{{ $job->title }}</h3>
<p>{{ $job->company->name }}</p>
@if ($job->is_featured)
<span class="bg-blue-500 text-white px-2 py-1 rounded"> Featured</span>
@elseif ($job->is_premium)
<span class="bg-yellow-500 text-white px-2 py-1 rounded"> Premium</span>
@endif
</div>
It worked , but it will have a lot of duplicated code accros multiple page especially at this time also i dont use any card component for the job itself .
A small change of design mean that i need to ensure all place is updated also .
Then i discover that blade also can have components .
Blade Component
To know about it you can just read it laravel doc here . Blade component
In this write , im just gonna tell how i use it and implement it to our job portal .
Refactoring Job Card And Badge
Creating a Job Card Component
class JobCard extends Component
{
public function __construct(public Job $job) {}
public function label(): ?string
{
return match (true) {
$this->job->is_featured => '🔥 Featured',
$this->job->is_premium => '🌟 Premium',
default => null,
};
}
public function labelClass(): string
{
return match (true) {
$this->job->is_featured => 'bg-blue-500',
$this->job->is_premium => 'bg-yellow-500',
default => 'bg-gray-300',
};
}
public function variant(): string
{
return match (true) {
$this->job->is_featured => 'border-blue-400 bg-blue-50',
$this->job->is_premium => 'border-yellow-400 bg-yellow-50',
default => 'border-gray-200 bg-white',
};
}
public function render()
{
return view('components.job-card');
}
}
Now all conditional already in php and we can add unit test to test this . The blade will only handle the display .
Now our blade look like this
<div class="border p-4 rounded shadow-sm {{ $variant() }}">
<h3 class="font-semibold text-lg">{{ $job->title }}</h3>
<p class="text-gray-600">{{ $job->company->name }}</p>
@if ($label())
<span class="inline-block mt-2 px-2 py-1 text-xs font-bold text-white rounded {{ $labelClass() }}">
{{ $label() }}
</span>
@endif
</div>
Now you can see no more terniaries , no duplication and easy to read :)
So now we don't need long code anymore whenever we want to display the job advertisement . We just can do like this
<x-job-card :job="$job" />
Closing
Why this is better for me , no need to handle logic in blade , blade focus on display only . It also easier titest . It can be reusable and in future if we want to add more like urgent or sponsore we just need to update in one place only .
Top comments (2)
You don't need a component to make templates reusable, there is also the
@include
directive .Also this is a way of working that is backend heavy. When you are working with frontenders that manipulate the templates I would be fine with following template.
As a frontender I would rather work with classes in templates than having to change classes in component class methods.
Having a contract,
Job
, between backend and frontend is a good thing. But make it possible to change things in the frontend without needing backend changes. For example the label, it is fine the text comes from the backend but I would put the emoji in the template.When I see
match(true)
and the conditions are no comparisons that is a code smell for me.Booleans like is_featured and Is_premium can be grouped as an enum.
When you take the time to create a
Job
class think of the best way to communicate the intend of the variables.Thanks a lot for the feedback 🙌 Since I’m not heavy on frontend ! @xwero .
I still use @include but only for separate a template in smaller partials which usually for layout . The problem with @include is that it’s hard to see what variables are going in there. With components you can add @props to spec the variables. If you don’t pass them to you component you will get an exception.