Attributes
Refine is a paid package. To purchase, head to hammerstone.dev.
Almost all of the standard conditions are applied against attributes, which are usually columns in your database. The only conditions that is not applied against an attribute is the relationship condition, which queries against the count of a related model.
All of the conditions that operate on attributes use a UsesAttributes
trait, which exposes an attribute
method that you can use to set the attribute's value to either a column name or a raw expression.
Setting an Attribute
By default, when you construct a condition that uses attributes, the attribute
is optimistically set to the same value as the id
.
For example, this:
DateCondition::make('published_at');
is exactly the same as:
DateCondition::make('published_at')->attribute('published_at');
If you are storing your filters, it is vitally important that your condition's ID
never change. If the ID
were to change in your code but not in your stored state, we would no longer be able to match the stored data to the condition.
For clarity, you may decide to always explicitly set the attribute using the attribute
method, even when the ID and attribute match. That is a perfectly reasonable decision!
Relationship Attributes
There may be times when you want to allow your users to query attributes on related models, instead of the model upon which the filter is based.
We take of this common scenario for you. Normally when you set an attribute, it's understood to be an attribute on the base model. But if you pass and attribute in the form of {relation}.{attribute}
then we'll handle the subquery required to query the relationship.
In the scenario where your filter is an Employee
filter, and there is a Manager
relationship, you can easily allow your users to query on the manager's name by setting your condition up as follows:
TextCondition::make('manager_name')->attribute('manager.name');
Every type of Laravel relationship is supported and any condition that uses attributes supports querying attributes on related models. Infinitely nested relationships are also supported, although you'll need to weigh the performance considerations of multiple levels of nesting.
The following is totally valid:
TextCondition::make('regional_manager_name')->attribute('manager.manager.name');
And if your user searches for employees who's regional manager is name "Michael Scott", something equivalent to the following would be applied to your query:
$query->whereHas('manager', function($subquery) { $subquery->whereHas('manager', function($subquery) { $subquery->where('name', 'Michael Scott'); });});
Note: we don't always rely on Laravel's
whereHas
method because under the hood that compiles down towhere exists
in SQL, creating a dependent subquery, which can cause a huge performance bottleneck. For some types of relationships, we instead create awhere in
with a subquery, which runs much much faster. All of this is totally transparent to you the developer.
Preventing Querying Relationships
Sometimes when your initial query joins a table in, you may have an attribute that requires a fully qualified {table}.{column}
identifier and therefore don't want Hammerstone to look for a relationship called table
on the model.
If that's the case, you can prevent querying on relationships by calling attributeIsNotRelationship
.
TextCondition::make('manager_name') ->attribute('t1.name') ->attributeIsNotRelationship();
JSON Attributes
If you attribute is a key in a JSON column, you can use the standard Laravel notation to denote that:
TextCondition::make('nested_json') ->attribute('column->key');
You can read more about the ->
operator in Laravel docs.
Raw Expression Attributes
In Laravel, you're able to use the DB
facade to query raw expressions instead of just attributes.
For example, if you wanted to find employees with summer birthdays, you could do that in vanilla Laravel using a raw expression:
$query->where(DB::raw(' MONTH(birthday) BETWEEN 6 AND 8 '));
This would give you employees with birthdays between June (6) and August (8).
If you'd like to expose that functionality to your users in a filter, you can do so by setting the attribute to a raw expression using the rawAttribute
method or by passing in an instance of Expression
:
BooleanCondition::make('summer_birthday') ->rawAttribute(' MONTH(birthday) BETWEEN 6 AND 8 '); // Or... BooleanCondition::make('summer_birthday') ->attribute(DB::raw(' MONTH(birthday) BETWEEN 6 AND 8 '));
Now the end user can choose Summer Birthday
: Is True
or Is False
.
Any condition that uses attributes can accept a DB::raw
expression instead of a string.