If anyone has tried to use the @inject
directive in a Blade file, they have probably noticed that it will fail. The reason for this is that the directive looks for services that are bound to the Laravel Application container. Specifically, it is using the app()
helper from Laravel.
To get around this, we need to overwrite that directive in setup.php
just after the Blade Provider is bound to the Sage Container.
/**
* Overwrite @inject() Blade directive
* IMPORTANT: This needs to follow the binding of the Blade Provider above.
*/
sage('blade')->compiler()->directive('inject', function($expression) {
$segments = explode(',', preg_replace("/[\(\)\\\"\']/", '', $expression));
$variable = trim($segments[0]);
$service = trim($segments[1]);
return "<?php \${$variable} = App\sage('{$service}'); ?>";
});
Now you can bind a service to the Sage Container (again in setup.php
)
/**
* Bind a service to container
*/
sage()->singleton('sage.acfservice', function () {
return new \App\Acf\Acf();
});
Create your service class. In this example, I am creating a simple ACF service class that will just dump data from an ACF function. You will probably do more with this data, like transformations, date formatting, and such.
<?php
namespace App\Acf;
class Acf
{
public function __construct()
{
//
}
/**
* Get field objects.
*
* @return array $data
*/
public function getFieldObjects()
{
return \get_field_objects();
}
}
Now the fun stuff. Create a reusable ACF component, in this example it will be a simple ‘card’ in resources\views\acf\card.blade.php
. (This is a Bootstrap4 card, btw)
@inject( 'acf', 'acfservice' )
@if($acf)
@if($fields = $acf->getFieldObjects() )
<div class="card" style="width: 20rem;">
<div class="card-body">
<h4 class="card-title">{{$fields['caption']['value']}}</h4>
<h6 class="card-subtitle mb-2 text-muted">{{$fields['location']['value']}}</h6>
<span class="badge badge-info">{{$fields['mood']['value']}}</span>
</div>
</div>
@endif
@else
<div class="alert alert-warning" role="alert">
Acf object not found!
</div>
@endif
There are few things happening in this component. We are injecting the ACF service (acfservice
) into the variable $acf
which can be used by the component. Then we do the obligatory check to see if anything was passed back (the service will return false
instead of an array if the field objects are not a part of the post).
Even if an ACF field group exists, it will pass an empty array if the user did not put a value into any one field. So we do the a simple check and assign statement @if($fields = $acf->getFieldObjects() )
. Now we can use the data.
You can reuse this component anywhere in your Blade files with a simple @include
. Please note that I have a custom directive for @loop
in this example.
@extends('layouts.app')
@section('content')
@loop
@include('partials.page-header')
@include('content.page')
{{-- Acf card --}}
@include('acf.card')
@endloop
@endsection
While @inject
may not be appropriate for everything, there are a number of situations where this technique will prove to be quite useful. As they say over in Laravel-land, “With power, comes great responsibility”