Photo by Hans-Peter Gauster on Unsplash

Simplify Filters with Clean OOP Practices

#[Pragmatic(Kiwi)]
4 min readApr 3, 2024

--

Have you ever struggled to maintain complex filtering logic in your Laravel applications? Procedural filtering, a common approach, can quickly become messy and difficult to manage as your application grows.

Procedurally check and apply filters as needed

While Laravel’s when method offers a way to improve readability in procedural filtering, for complex scenarios, it can still lead to scattered logic.

Procedurally check filters but with Laravel syntactic sugar

This article introduces a solution using Object-Oriented Programming (OOP) principles. By creating reusable filter classes, we can improve the maintainability, readability, and testability of your filter logic.

Refactoring to an OOP approach offers several advantages:

  • Promotes modularity — making your code easier to reason about
  • Unit testing becomes more straightforward — ensuring the reliability of your filtering system

An Important Consideration

While we delve into the OOP solution, it’s worth noting that in Laravel, the Query Builder is passed by reference.

This means that modifications made to the query within a function can affect its behavior elsewhere in your code. We’ll keep this in mind as we explore how to effectively utilize OOP with the Query Builder for filtering.

To address this challenge, we’ll use the Strategy Pattern. This pattern allows us to define a family of filtering algorithms encapsulated within separate classes.

These classes will implement a common interface, ensuring a consistent approach to applying filters.

The goal of our code is to take what we discussed above and turn it into something like this:

Our goal is to create a consistent and reusable approach to applying filters.

To achieve this, we’ll define a single interface that all our filter classes will implement.

Building the interface

This interface will specify a method called applyTo that takes a Laravel Query Builder instance as an argument.

Building the Submissions Filter

Now that we have the interface defined, we can create reusable filter classes.

Note: My experience with large projects has shown that filter requirements often involve a variety of heterogeneous filtering criteria. This means filters have unique logic specific to their purpose.

The Strategy Pattern excels in such scenarios for several reasons:

  • Encapsulation of Filtering Logic — each filter class encapsulates specific functionality
  • Flexibility and Extensibility — new filters can be easily implemented

Our filter classes will typically receive an array containing filter options.

However, it’s common practice to send null values within this array to indicate that a particular filter isn't being applied.

To ensure clean and efficient filtering, we'll implement logic to filter out any empty values (including null) before processing the remaining valid filters.

This keeps our filter classes focused on handling the actual filtering logic without needing to account for the absence of filters.

The filters we will work with are the reportId and projectId. These filters will be passed as an array containing their respective values or null if not applied.

We'll transform this array into a Laravel collection for easier manipulation within our filter classes.

Once we have a collection of valid filters (excluding empty values), we can iterate through them to extract the filter name and value.

Now that we have this, we can employ the Variable Functions that PHP has to avoid overengineering this solution by adding a factory pattern to match the filters with the method that should apply them.

By using the Strategy Pattern and a common interface, we’ve established a flexible and maintainable foundation for filter implementation in our Laravel application.

This approach allows to easily add new filter types without modifying existing code, promoting long-term code health.

Sample Usage

Now that we have reusable filter classes, using them in our application becomes straightforward.

Here’s a basic demonstration:

Endings Notes

This article has explored the benefits of refactoring procedural filter logic to an object-oriented approach using the Strategy Pattern and interfaces.

We’ve seen how this approach promotes code readability, maintainability, and extensibility. By defining a common interface for filters and creating dedicated filter classes, you can effectively manage complex filtering requirements in your Laravel applications.

--

--