skip to Main Content

Consider the following scenario:

There are couple of entities in my Laravel application like the following:

  • Post
  • Page
  • Image
  • Video

All the above entities can have CustomFieldValues, which is another entity. The structure of the custom_field_values table is as follows:

  • ID
  • entity_id
  • custom_field_definition_id
  • value
  • [Timestamp fields]

All the CustomFieldValues belong to a single CustomFieldDefinition entity. Its table custom_field_definitions looks like following:

  • ID
  • parent_entity_name
  • definition_name
  • [Timestamp fields]

Following are some sample data from the custom_field_definitions table:

| ID | parent_entity_name | definition_name   |
|----|--------------------|-------------------|
| 1  | Post               | AuthorTwitterUrl  |
| 2  | Page               | SeoTitle          |
| 3  | Image              | OriginalSourceUrl |
| 4  | Video              | MpaaRating        |

As you can see, CustomFieldDefinitions are definitions of extra data, that we can store about each type of entity.

Following are some sampel data from the custom_field_values table:

| ID | entity_id | custom_field_definition_id | value                             |
|----|-----------|----------------------------|-----------------------------------|
| 1  | 1         | 1                          | https://twitter.com/StackOverflow |
| 2  | 1         | 2                          | My Page's SEO Title               |
| 3  | 1         | 3                          | http://example.com/image.jpg      |
| 4  | 1         | 4                          | G – General Audiences             |

A little description about the data contained in the custom_field_values table:

  • CustomFieldValue:1: The value for CustomFieldDefinition:1 and its entity 1 (Post:1, in this case, because CustomFieldDefinition:1 is related to Post.) is “https://twitter.com/StackOverflow“.
  • CustomFieldValue:2: The value for CustomFieldDefinition:2 and its entity 1 (Page:1, in this case, because CustomFieldDefinition:2 is related to Page.) is “My Page’s SEO Title”.
  • CustomFieldValue:3: The value for CustomFieldDefinition:3 and its entity 1 (Image:1, in this case, because CustomFieldDefinition:3 is related to Image.) is “http://example.com/image.jpg“.
  • CustomFieldValue:4: The value for CustomFieldDefinition:4 and its entity 1 (Video:1, in this case, because CustomFieldDefinition:4 is related to Video.) is “G – General Audiences”.

custom_field_values table’s entity_id can refer to any entity class, therefore it is not a foreign key in the DB level. Only in combination with custom_field_definition_id we can find to which entity it actually refers to.


Now, all is well and good, until I need to add a relationship called customFieldDefinitions to any of the entities (Say Post.).

class Post extends Model {
 public function customFieldDefinitions(){
  $this -> hasMany ('CustomFieldDefinition');
 }
}

The above does not work, because the datapoint that indicates the CustomFieldDefinition‘s relationship is not a foreign key field in the custom_field_definitions table, named post_id. We have to somehow build the relationship based on the fact that some records in the custom_field_definitions table has “Post” as the value of the field parent_entity_name.

CustomFieldDefinition::where('parent_entity_name', '=', 'Post');

The above snippet fetches the CustomFieldDefinitions that are related to the Post, however, it is not possible to do something like the following with the relationship:

class Post extends Model {
 public function customFieldDefinitions(){
  $this
   -> hasMany ('CustomFieldDefinition')
   -> where ('parent_entity_name', '=', 'Post')
  ;
 }
}

The where constraint works. But Laravel also injects the ID of the current Post object into the set of constraints.

So, what I want to do is, not consider the current object’s ID at all, and build a “Class Leavel Relationship”, and not an “Object Level Relationship”.

Is this possible under Laravel?

2

Answers


  1. Instead of hasMany(), you can create One To Many (Polymorphic) relationship between Post, Page, Image, Video and CustomFieldDefinition.

    More about polymorphic relationships here.

    Login or Signup to reply.
  2. There might be a workaround but I’m not pretty sure about it.

    What you could try doing is to define a mutated attribute and set it as the local key of the relationship:

    class Post extends Model
    {
    
       public function getEntityNameAttribute()
       {
           return 'Post';
       }
    
       public function customFieldDefinitions()
       {
           return $this->hasMany(
               'CustomFieldDefinition',
               'parent_entity_name',
               'entity_name'
          );
       }
    }
    

    You could also go further and define a trait which could be used by all your models which have customFieldDefinitions. It could look like:

    trait HasCustomFieldDefinitionsTrait
    {
       public function getEntityNameAttribute()
       {
           return (new ReflectionClass($this))->getShortName();
       }
    
       public function customFieldDefinitions()
       {
           return $this->hasMany(
               'CustomFieldDefinition',
               'parent_entity_name',
               'entity_name'
          );
       }
    }
    

    Then you can use it wherever needed:

    class Post extends Model
    { 
        use HasCustomFieldDefinitionsTrait;
    }
    
    class Video extends Model
    { 
        use HasCustomFieldDefinitionsTrait;
    }
    
    class Page extends Model
    { 
        use HasCustomFieldDefinitionsTrait;
    }
    
    class Image extends Model
    { 
        use HasCustomFieldDefinitionsTrait;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search