Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

fsk's avatar
Level 1

Display additional fields for eloquent model using relation

I have two tables that extend models with additional fields.

Schema:

additional_fields

id  
table_name (table associated with every model in my app)
type
key
desc

additional_fields_values

id
field_id (references on additional_fields.id)
model_id
value

Now I want to extend many tables with some additional fields and display them with their values. I've tried using hasManyThrough relation on model I wanted to extend:

public function additional_fields(){
    return $this->hasManyThrough(
        AdditionalField::class, 
        AdditionalFieldValue::class, 
        'field_id', 
        'id', 
        'id', 
        'model_id'
    );
}

but it only gives me all values for one specific additional field, without its "parent". I would like to display all additional fields associated with table and values that are inserted for current instance. I was wondering about polymorphic relation (morphMany) but it will return only additional field related to the model without it's value. In that case it wouldn't work properly, because I would be supposed to define same field for the same single model everytime.

Is there any way to do this efficiently using relation? It's (probably) necessary to make these fields translatable. If something is incomprehensible I can provide specific example to make things clear.

Thanks for your help!

0 likes
4 replies
aurawindsurfing's avatar

Hi,

I'm not sure I understand. The table that has a "child" table. If so then you need to stop thinking in terms of tables in Laravel your Model will reflect your table. Then you will define relationships between your models. Have a look here:

https://laravel.com/docs/master/eloquent-relationships#one-to-one

and

https://laravel.com/docs/master/eloquent-relationships#one-to-many

It looks like you are overcomplicating things with hasManyThrough and morphMany, to be honest I still did not find myself in a situation to use those ;-)

Can you show exactly what model relates to what please?

Hope it helps!

fsk's avatar
Level 1

Hello @aurawindsurfing,

thanks for your reply. As I thought, I will have to explain specifically what effect I would like to achieve :-D

Imagine that we have two models:

Product (table: products)

basic fields that currently exist in this table:

['name', 'slug', 'price_netto', 'price_brutto', 'desc', 'short_desc', 'image']

and Page (table: pages)

basic fields that currently exist in this table:

['author_id', 'active', 'layout', 'name', 'slug', 'short_desc', 'desc', 'image'];

I launch CMS on production server for my customer. And I want to give him ability to extend thes models with additional fields without digging in into code and sql. So I create a controller where he chooses to what model he wants to add an additional field, so structure looks like this:

additional_fields

+------+------------+----------+--------------+-------------------------------+
| id   | table_name |   type   |     key      |             desc              |
+------+------------+----------+--------------+-------------------------------+
|    1 | product    | text     | field1       | First test field for product  |
|    2 | product    | text     | field2       | Second test field for product |
|    3 | pages      | textarea | rick_roll    | First test field for page     |
|    4 | pages      | checkbox | page_lalalla | Second test field for page    |
+------+------------+----------+--------------+-------------------------------+


And now, while editing single product in admin panel you will have all "base" fields, and section with additional fields. I think that it should render like this:


@foreach($product->additional_fields() as $additional_field)
    <div class="form-control">
        <label for="{{$additional_field->key}}">{{$additional_field->desc}}</label>
        <input type="{{$additional_field->type}}" value="{{$additional_field->value}}" />
    </div>
@endforeach

Then customer puts some values into previously created additional fields:


additional_fields_values
+-----+-----------+-----------+---------------------------------+
| id  | field_id  | model_id  |              value              |
+-----+-----------+-----------+---------------------------------+
|   1 |         2 |        55 | Field value for Product (id:55) |
|   2 |         4 |         3 | Field value for Page (id:3)     |
+-----+-----------+-----------+---------------------------------+

And it should return all additional_fields related only with "products" table and show inserted values, so in this particular case (edit product with id: 55) we should render two fields:


<div class="form-control">
    <label for="field1">First test field for product</label>
    <input type="text" value="(null)" />
</div>

<div class="form-control">
    <label for="field2">Second test field for product</label>
    <input type="text" value="Field value for Product (id:55)" />
</div>


I don't want to extend base table for these fields because it will be quite complicated when using Dimsav translatable package and it will also bring problems while pushing updates because every customer will have different fields in their base tables.

Is it even able to do? Maybe you can suggest me some other way to acheive similiar effect in less painful way?

aurawindsurfing's avatar

Ok but how do you store the fields he added, do you store it as an json field on a database?

fsk's avatar
Level 1

No, every single field has its own record in additional_fields table. I cant use JSON because of translation issues.

edit: I got some idea, but it still needs to be customized and I have not enough knowledge to do it properly.

I create polymorphic relation to additional_fields_values, so structure is now:


additional_fields_values

id
field_id
value
additionable_id
additionable_type

Then model:


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

Then "Product" model:


class Product extends Model
{
   
    public function additional_fields_values()
    {
        return $this->morphMany(AdditionalFieldValue::class, 'additionable');
    }

}

And now additional_fields_values function will return all values inserted for current instance.

My idea is to create a function inside Product model:


public function getAdditionalFields(){
        return AdditionalField::where('table_name','=',self::getTable());
}

And it will display all fields, without values. My question is: is there any way to merge these two collections (additional field + value) in model level and render it in the way I showed in previous post?

@foreach($product->additional_fields() as $additional_field)
    <div class="form-control">
        <label for="{{$additional_field->key}}">{{$additional_field->desc}}</label>
        <input type="{{$additional_field->type}}" value="{{$additional_field->value}}" />
    </div>
@endforeach

Let me know what you think.

Please or to participate in this conversation.