Reputation: 675
Is there any way in Eloquent to have a model which has some sort of parent model, where both have an identical field, nullable on the child. And if I get the value $child->field
I get the childs value if it's not null
, otherwise I get the parent value? Something like this:
$parent = new Parent();
$parent->info = 'parent';
$parent->save();
$child = new Child();
$child->info = 'child';
$child->parent()->associate($parent);
$child->save();
echo $child->info
Prints 'child'
And opposite:
$parent = new Parent();
$parent->info = 'parent';
$parent->save();
$child = new Child();
$child->parent()->associate($parent);
$child->info = null;
$child->save();
echo $child->info
Prints 'parent'
It must be a pattern somewhere to have one table rows values 'overrule' another, I just can't seem to find what to search for.
Upvotes: 0
Views: 1403
Reputation: 6544
You simply need a custom accessor on the model of your choice:
class Child
{
public function getInfoAttribute($value)
{
if ($value === null) {
return $this->parent->info;
}
return $value;
}
}
This will allow you to still access the property via $child->info
.
Please be aware that this will not cast the attribute value according to the $casts
array. If you need this casting logic as well, you should use a custom getter method instead of the magic accessor:
class Child
{
public function getInfo()
{
$info = $this->info;
if ($info === null) {
$info = $this->parent->info;
}
return $info;
}
}
If you need to multiple properties, you can either duplicate the code and put it into a trait to remove the clutter from your model. Or instead, you can try overriding the magic __get($key)
method:
class Child
{
$parentInheritedAttributes = ['info', 'description'];
// solution 1: using normal model properties like $child->info
public function __get($key)
{
if (in_array($key, $this->parentInheritedAttributes)) {
$value = parent::__get($key);
if ($value === null) {
// this will implicitely use __get($key) of parent
$value = $this->parent->$key;
}
return $value;
}
return parent::__get($key);
}
// solution 2: using getters like $child->getInfo()
public function __call($method, $parameters)
{
if (\Illuminate\Support\Str::startsWith($method, 'get')) {
$attribute = \Illuminate\Support\Str::snake(lcfirst(substr($method, 3)));
in_array($attribute, $this->parentInheritedAttributes)) {
$value = $this->$attribute;
if ($value === null) {
$value = $this->parent->$attribute;
}
return $value;
}
}
}
}
Upvotes: 1