My problem could be more philosophical than concrete. I'm using Laravel 8, I have one Trait that is used by several classes and some of its methods perform two different computation by taking into account if object has or not a specific morphMany relationship (here called entries). This trait is almost implemented with the following code:
trait MyTrait {
// Some methods...
// Entries methods
abstract public function hasEntries();
abstract public function entries();
// Here is a method that depends on entries
public function computFunc() {
return $this->hasEntries()
? $this->computWithEntries()
: $this->computWithoutEntries();
}
private function computWithEntries() {
// Perform computation with entries by using entries() relationship...
}
private function computWithoutEntries() {
// Perform computation without entries...
}
// Some other methods...
}
Obviously, classes that have entries relationship also implement some criteria inside hasEntries() method to check if there are entries for the current object, as shown in this example:
class ModelA {
use MyTrait;
public function entries() {
return $this->morphMany(Entry::class, 'owner');
}
public function hasEntries() {
return $this->entries()->where([ /* Some condition */ ])->exists();
}
}
Now, my question is: what should return entries() method for classes that don't have entries relationship, meaning that is not possible/allowed for entries table to have column owner_type equal to one of those classes.
Here's an instance of this case (ModelB can't own any entry):
class ModelB {
use MyTrait;
public function entries() {
// Should return null?
return null;
}
public function hasEntries() {
return false;
}
}
What value/object should return entries() method for objects of class ModelB? I'm quite sure that to return null instead of relationship object isn't the correct thing to do, am I wrong?
I think you may accomplish this with a different approach. Try with this:
With that, you wont have problem in the computing part of the code. Hope my answer helps.
Edit - Super quick example:
//EntriesRelationTrait.php
trait EntriesRelationTrait {
public function entries() {
return $this->morphMany(Entry::class, 'owner');
}
}
//ComputeByEntriesTrait.php
trait ComputeByEntries {
public function computFunc() {
return $this->hasEntries()
? $this->computWithEntries()
: $this->computWithoutEntries();
}
private function computWithEntries() {
// Perform computation with entries by using entries() relationship...
}
private function computWithoutEntries() {
// Perform computation without entries...
}
}
//HasEntriesContract.php
interface HasEntriesContract
{
/**
* @return bool
*/
public function hasEntries(): bool;
//function type declaration available only after PHP 7.0
}
class ModelA implements HasEntriesContract {
use EntriesRelationTrait;
use ComputeByEntriesTrait;
public function hasEntries(): bool {
return $this->entries()->where([ /* Some condition */ ])->exists();
}
}
class ModelB implements HasEntriesContract {
use ComputeByEntriesTrait;
public function hasEntries(): bool {
return false;
}
}
The contract ensures that the created function follows a standard, in this case, a boolean response from the hasEntries
method.