Need a quick Laravel app on the cloud? Deploy your Laravel application on Fly.io, you’ll be up and running in minutes!
Ever implemented logic that applied to multiple Livewire components? Why not use a middleware to make it available across Livewire components?
For example: Imagine having two Livewire components that handle two different form submissions. If, say, each of these components handle their form submissions through a submit()
method, how can we easily log the submissions they receive?
We might do something like log the submission details in the submit()
method of each component, right before processing the submission. But imagine having more than two components of —say three, five, or more? That’s going to be a ton of repetitive logging.
In this post, instead of repeating logic in each component’s submit()
method, we’ll apply our logic in a single middleware to make it available for Livewire components that trigger a submit()
method.
Version Notice. This article focuses on Livewire v2, details for Livewire v3 would be different though!
Let’s start
To start, let’s set up a simple Livewire component that handles a form submission. It will take in a name and email, and save it to the database.
Create the component with php artisan make:livewire ContactForm
and add the following view:
{{-- resources/views/livewire/contact-form.blade.php --}}
<div>
<form wire:submit.prevent="submit">
<input type="text" wire:model.defer="name">
@error('name') <span class="error">{{ $message }}</span> @enderror
<input type="text" wire:model.defer="email">
@error('email') <span class="error">{{ $message }}</span> @enderror
<button type="submit">Save Contact</button>
</form>
</div>
The form is wired to the submit()
method, while each of the text input field are wire:model.defer
-ed to their respective public properties in the component:
/* app\Http\Livewire\ContactForm.php */
class ContactForm extends Component
{
public $name;
public $email;
public function submit()
{
\App\Models\Contact::create([
'name' => $this->name,
'email' => $this->email
]);
}
}
The only time a request is made to the Livewire component’s route is during form submission, which calls the component’s submit()
method. This is how it looks like:
The request is sent to livewire/message/contact-form
, and we can see three items in its updates
payload. One syncInput type for each wired text input field, containing values for name
and email
, and finally a callMethod type to trigger a call to the submit()
method of the component.
Logging Requests for Submit Method
Let’s say, we want to log submission details made through the submit()
method of our Livewire component. Create a middleware with php artisan make:middleware LogInputRequests
:
/* App\Http\Middleware\LogInputRequests */
use Log;
class LogInputRequests
{
public function handle(Request $request, Closure $next): Response
{
// 1. Make sure Livewire's updates key exists
if( isset($request->updates) ){
foreach( $request->updates as $update ){
// 2.Find target "submit" method
if(
isset($update['payload']['method']) &&
$update['payload']['method'] == 'submit'
){
$userId = $request->user()? $request->user()->id : 'NA';
// 3. Log Stuff
Log::info(
"User: $userId, Path: $request->path(),
Submission:". json_encode($request->updates)
);
break;
}
}
}
// Proceed
return $next($request);
}
}
The above middleware processes requests specific to Livewire components. Of course, since it’s handling requests to Livewire components, we have to be a bit mindful in creating this middleware. When creating middlewares that handle Livewire component requests, we generally have to answer two questions:
The first question is, when should this middleware be applied? Livewire relies on configured middleware groups that’s applied on all Livewire components. This means we’ll have to specify conditions that will check whether to apply the middleware or not. Otherwise, it will run its logic for all Livewire components. And, do we want that?—no, not in this use case.
So, notice above, we check for the existence of a submit
method in the request’s updates
key. This key is Livewire’s magical bag that let’s it know about changes in the view that the server needs to sync, or trigger methods with.
We check for the submit
method because we want to log submission details, which are processed through this method. And, notice, we do not limit this to one specific component. Rather, it is applicable for any, so long as the submit
method was triggered. This allows us to log submission for not just our component above, but for other components triggering a submit()
method as well.
The second question is, what part of the Livewire request do we include in the intended logic? In our case, we want to log submission details. Submission details would of course be found in the updates
bag, so we simply log the updates
bag which will contain values for name
and email
, as well as the call to the submit()
method. Of course, we can further parse this to our liking or use case.
And speaking of parsing—please remember: We’re logging user input here. Make sure, and do not forget, to sanitize this.
Registering the Middleware in Livewire config
Livewire’s config file config/livewire.php
allows us to register middleware groups in its middleware_group
attribute.
In order to apply our middleware logger to requests made on our Livewire components, we create a new middleware group in app/Http/Kernel.php
, where we include our middleware class:
/* app/Http/Kernel.php */
protected $middlewareGroups = [
// other middleware groups
'for_livewire'=>[
\App\Http\Middleware\LogInputRequests::class,
]
Then, we pull Livewire’s config file into our project with php artisan livewire:publish --config
, and include our recently declared middleware group here:
/* config/livewire.php */
'middleware_group' => ['web','for_livewire'],
Every request made to a Livewire component via its route livewire/message/
will now pass through our middleware.
Of course, thanks to our logic of checking for the submit
method, only requests containing the submit
method in the updates
key will be logged.
So now, if we click on the submit button of our Livewire component, the submission details should now be logged, likeso:
And, just like that—with one middleware—we have our instant submission logger, globally available to any Livewire component—neat!
Conclusion
There are many other use cases for implementing logic into middleware, just like applying policies to safeguard access to your Livewire components.
Moving logic into middleware makes sense when that logic is repeated in multiple, different places. What’s more, Livewire automatically applies one Livewire-valid middleware to all Livewire components, making application of Livewire-wide logic a convenient task to do with middlewares.
Do you have any other Livewire-wide logic to implement, if so, why not implement it into a middleware?