Welcome to the fourth and final part of our Livewire series! Now that we’ve built the basic product filtering system and added functional filters, we’ll optimize the product filter for performance and usability. We’ll add features like pagination, loading indicators, and animations to create a polished, user-friendly experience.
Part 3: Enhancing the Livewire Product Filter: Advanced Features
What We’ll Cover
- Implementing pagination for better performance.
- Optimizing Eloquent queries for filtering products.
- Implementing loading indicators for better feedback.
- Debouncing user inputs to reduce unnecessary updates.
Adding Pagination
For applications with many products, pagination is essential. Livewire makes it easy to add pagination with minimal configuration.
Update the Component Class
Modify the ProductFilter
class to use pagination. Add the WithPagination
trait:
<?php
namespace App\Livewire;
use Livewire\Component;
use Livewire\WithPagination; // Import the trait
use App\Models\Product;
class ProductFilter extends Component {
use WithPagination; // Use the trait
public $category = '';
public $availability = '';
public $minPrice = 0;
public $maxPrice = 10000;
public function clearFilters() {
$this->category = '';
$this->availability = '';
$this->minPrice = 0;
$this->maxPrice = 10000;
}
public function render() {
$products = Product::query()
->when($this->category, function ($query) {
$query->where('category', $this->category);
})
->when($this->availability, function ($query) {
$query->where('available', $this->availability === 'available');
})
->when($this->minPrice, function ($query) {
$query->where('price', '>=', $this->minPrice);
})
->when($this->maxPrice, function ($query) {
$query->where('price', '<=', $this->maxPrice);
})
->paginate(9); // Paginate results
return view('livewire.product-filter', ['products' => $products]);
}
public function updating($property) {
$this->resetPage(); // Reset to page 1 when a filter is updated
}
}
The WithPagination
trait handles pagination logic. The resetPage()
method ensures the component resets to the first page when filters are updated.
Update the Blade Template
Add the pagination controls to the product-filter.blade.php
file:
<div>
<!-- Filters (same as before) -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
@foreach ($products as $product)
<div class="bg-gray-100 border rounded p-4 text-center">
<h3 class="font-bold">{{ $product->name }}</h3>
<p>Category: {{ $product->category }}</p>
<p>Price: ${{ number_format($product->price, 2) }}</p>
<p>Status: {{ $product->available ? 'Available' : 'Unavailable' }}</p>
</div>
@endforeach
</div>
<!-- Pagination Controls -->
<div class="mt-6">
{{ $products->links() }} <!-- Display pagination links -->
</div>
</div>
Livewire integrates seamlessly with Laravel’s pagination system, so the links()
method automatically generates the controls.
Implementing Loading Indicators
To give users feedback while the products are loading, Livewire provides a built-in wire:loading
directive. Add a loading indicator to the template:
<div wire:loading wire:target="category, availability, minPrice, maxPrice"
class="text-center text-blue-500 font-semibold">
Loading products...
</div>
The wire:loading
directive shows the text only when the specified properties are being updated. You can customize the message or replace it with a spinner or animation.
Optimizing Eloquent Queries
Efficient queries are the backbone of a performant application. Let’s revisit the ProductFilter
component and ensure our queries are optimized for performance.
Update the Component Logic
Modify the render
method in the ProductFilter
class to include optimized query logic:
<?php
namespace App\Livewire;
use Livewire\Component;
use Livewire\WithPagination;
use App\Models\Product;
class ProductFilter extends Component
{
use WithPagination;
public $category = '';
public $availability = '';
public $minPrice = 0;
public $maxPrice = 10000;
public function clearFilters()
{
$this->category = '';
$this->availability = '';
$this->minPrice = 0;
$this->maxPrice = 10000;
}
public function render()
{
// Fetch filtered products with optimized queries
$products = Product::query()
->when($this->category, fn ($query) => $query->where('category', $this->category))
->when($this->availability, fn ($query) => $query->where('available', $this->availability === 'available'))
->when($this->minPrice, fn ($query) => $query->where('price', '>=', $this->minPrice))
->when($this->maxPrice, fn ($query) => $query->where('price', '<=', $this->maxPrice))
->orderBy('created_at', 'desc') // Sort results for consistency
->paginate(9); // Limit results per page
return view('livewire.product-filter', compact('products'));
}
public function updating($property)
{
$this->resetPage(); // Reset to page 1 when a filter is updated
}
}
This method uses conditional clauses with when()
to dynamically apply filters only when necessary. The paginate()
method ensures that the query retrieves a manageable number of products per page.
Debouncing User Inputs
Livewire processes updates immediately when a user types in a field. For fields like minPrice
and maxPrice
, this can lead to excessive database queries. Use the debounce
modifier to delay updates until the user pauses typing:
<input type="number" wire:model.change.debounce.500ms.number="minPrice" placeholder="Min Price" class="border rounded p-2 w-24" />
<input type="number" wire:model.change.debounce.500ms.number="maxPrice" placeholder="Max Price" class="border rounded p-2 w-24" />
This delays updates by 500 milliseconds, reducing unnecessary queries.
Testing the Performance
To test the performance optimizations:
- Populate the database with a large number of products.
- Navigate to the
/products
page. - Test each filter option and ensure the results are fast and accurate.
- Verify that pagination works seamlessly and updates the results dynamically.
For even more insights, you can use Laravel Debugbar to profile your application and ensure minimal queries are executed per request.
Conclusion
Congratulations! You’ve optimized the Livewire product filter with efficient queries, pagination, and input debouncing. These enhancements improve performance and user experience, making your application more responsive and user-friendly.
Feel free to share your progress or ask questions in the comments below.