Home » Php » php – How to define relationships when using interfaces

php – How to define relationships when using interfaces

Posted by: admin July 12, 2020 Leave a comment

Questions:

The normal use of polymorphic relationships in Laravel is covered pretty well by the Post–User–Image example.

I’m trying to find a clean way to implement relationships for let’s say an Article/ContentA/ContentB relationship.

articles
  id

content_1
  id

content_2
  id

user_defined_content_n
  id

contentables
  article_id
  contentable_id
  contentable_type // content_2, user_defined_content_n

The content classes are not necessarily known by the article, so defining the article model with many MorphedByMany relationships is not how I want to do this.

Perhaps I am structuring my classes poorly. I could create a ContentEntity class which morphs to individual Content classes but I would like to avoid this if possible.


Maybe this better explains my problem.

class Article extends Model {
    public function contentEntities() {
        return $this->hasMany(ContentEntity::class);
    }
}

class ContentEntity extends Model {
    public function contentable() {
        return $this->morphTo();
    }
}

class Content extends Model {
    public function contentEntity() {
        return $this->morphOne(ContentEntity::class, 'contentable');
    }
}

class Video extends Model {
    public function contentEntity() {
        $this->morphOne(ContentEntity::class, 'contentable');
    }
}

This works but seems very unclean to me. I think it adds too much developer overhead to have to manage the ContentEntity parent.

Edit:
Unless someone provides a better solution, I went with an EAV style solution using ContentEntitys.

How to&Answers:

You could always extract the relationship to a trait for easier maintenance and extendability in the future:

Trait

trait Contentable
{
    public function contentEntity() {
        if(property_exists($this, 'contentable') && $contentable == 'many') {
            return $this->hasMany(ContentEntity::class);
        } else {
            return $this->morphOne(ContentEntity::class, 'contentable');
        }
    }

    // Future implementations
}

Then all you need to do is use the trait in various entities:

Models

class Content extends Model {
    use Contentable;
}

class Video extends Model {
    use Contentable;
}

class Article extends Model {
    use Contentable;

    protected $contentable = 'many';
}