When having setter mutators in a model it would be convenient if the setters are also considered when querying the database.
Class Abc extends Eloquent {
public function setXXXAttribute($value) {
$this->attributes['XXX'] = ...some transformation function, i.e. ucfirst($value);
}
}
Whenever running a query with that model the setter gets considered.
Abc::where('XXX', 'something')->get();
My first try is to extend the where-method of the IlluminateDatabaseEloquentBuilder.php:
public function where($column, $operator = null, $value = null, $boolean = 'and')
{
// --> addition #1
$replace = 2;
if ($value == null) {
$value = $operator;
$replace = 1;
}
if (is_string($column) && $this->model->hasSetMutator($column)) {
$this->model->setAttribute($column, $value);
$value = $this->model->getAttributes()[$column];
}
// <--
if ($column instanceof Closure)
{
$query = $this->model->newQuery(false);
call_user_func($column, $query);
$this->query->addNestedWhereQuery($query->getQuery(), $boolean);
}
else
{
//call_user_func_array(array($this->query, 'where'), func_get_args());
// --> addition #2
$args = func_get_args();
$args[$replace] = $value;
call_user_func_array(array($this->query, 'where'), $args);
// <--
}
return $this;
}
I'm not really sure how to deal with the case when the column is a Closure or other unthought combinations, but for simple queries it works pretty nicely.
This is for sure not perfect, but i'm not fully familiar with all the query building in laravel.
I don't think set mutators can be applied in the query builder, mutators might rely on the the current model instance which won't be available until the model is actually fetched.
Hm, i get your point.
On the other hand the benefit would be, that you don't have to replicate all the setter methods somewhere in the controller.
Or, if placed in static methods in the Model, the controller still has to take care that these are called before the query.
So the controller get cluttered with non-controller-specific code.
Furthermore, if these transformation functions rely on the model instance (Data) you end up in the same dilemma as you thought of.
Guess this is inevitable and should be up the responsibility of the programmer...
I don't think set mutators should be applied in the query builder, mutators might rely on the the current model instance which won't be available until the model is actually fetched.
—
Reply to this email directly or view it on GitHub.
You can use scopes to achieve a similar functionality.
public function setFooAttribute($value)
{
$this->attributes['foo'] = $this->transformFoo($value);
}
public function scopeWhereFoo($query, $value)
{
return $query->where('foo', '=', $this->transformFoo($value));
}
protected function transformFoo($value)
{
return strtoupper($value);
}
That's definitely nicer than cluttering the controller and puts everything in the right spot.
But it hardcodes the comparison operator. Or you'd have to make a scope for every possible comparison. And, while using your object you always have to think about "Do i have to use a scope or can i simply use standard functions?".
My idea was that since the setter and getter methods somehow create a "data-abstraction-layer" between the program and the database. This circumstance should be respected in the query-builder as well...
You could easily add another parameter to the scope method that is the operator.
Hello,
I think I have an issue with that, we can't use firstOrCreate with mutated data.
Maybe I'm wrong but the query is made with a param non-mutated while for insertion the data is mutated.
Most helpful comment
You can use scopes to achieve a similar functionality.