Just wondering if it is possible that some kind of findOrNew for relationships exist in Eloquent (in case if relationship do not exist attach new model instance)?
What that mean: Lets say that we have devices and specifications tables. Device belongs to specification. Specification_id is an FK (Know that is not best approach, but I have something like this left by previous programmer). Under id 11 we have device that do not have specification but we have to display that for user anyway.
$device = Device::find(11);
echo $device->specification->cpu;
In this case it will throw an error because specification will be null - it do not exist for device with id 11.
Know that I could check first if it exist but there a a lot of similar lines and app is pretty big. I need to move it from Kohana to Laravel. It works in Kohana because empty object is loaded then and 2nd line just return null. For Laravel I can just check if relationship exist and load new model then but I am curios if maybe there is any other and better way?
I would go for creating extra method in Device
model this way:
public function getSpecification()
{
if ($device->specification) {
return $device->specification;
}
return Specification::find(20); // some default specification
// or
// return new Specification(['cpu' => 'Not provided']);
}
And now you could use it this way:
$device = Device::find(11);
$device->getSpecification()->cpu;
Of course it depends how would you need to use it. If you have many properties, you should run this method just once for object to not run multiple queries and in case you would use it for big collections you should also rethink improvements to lower database queries.
This doesn't quite create the related object as you requested, but for the purposes of outputting the data or replicating Kohana's null output in the absence of a related model, I tend to use the data_get()
or object_get()
helpers for this purpose.
$device = Device::find(11);
echo object_get($device->specification, 'cpu');
// You could probably do this too (untested)
echo object_get($device, 'specification.cpu');
Having had a bit of a look, you can override the getRelationshipFromMethod()
method in Illuminate\Database\Eloquent\Model
protected function getRelationshipFromMethod($method)
{
// Different relationships return different types of data so
// tweak this as necessary. In theory you only care if the relationship
// type is a single entity rather than a collection.
$results = parent::getRelationshipFromMethod($method);
if ($results instanceOf Illuminate\Database\Eloquent\Collection) {
return $results;
}
// Generate a null value for any missing attributes
// PHP7 anonymous class. Return a real class < 7.0
return $this->relations[$method] = new class {
public function __get($attribute) {
return null;
}
};
// Or perhaps actually create a relationship with a specification
$this->relations[$method] = Specification::where('default', true)->first();
$this->specification()->associate($this->relations[$method]);
return $this->relations[$method];
}