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

iamfaiz's avatar

How to insert relations based on a nested array in Laravel?

I have 3 tables tests, questions, options.

As you can imagine

  1. a test has many questions
  2. a question belongs to a test
  3. a question has many options
  4. an option belongs to a question

These relations are set up in the models already.

I got the data from the front end in this form:

array:3 [
  "name" => "First Test"
  "preparation" => "First Test prep"
  "questions" => array:2 [
    0 => array:2 [
      "title" => "Some question"
      "options" => array:4 [
        0 => "a"
        1 => "b"
        2 => "c"
        3 => "d"
      ]
    ]
    1 => array:2 [
      "title" => "Another question"
      "options" => array:4 [
        0 => "e"
        1 => "f"
        2 => "g"
        3 => "h"
      ]
    ]
  ]
]

This data perfectly represents these relationships. In fact if I were using a NoSql database I would simply store this in the database.

My question is "what is the best way to store all of this data at once while using eloquent in Laravel"?

Note: It is in the form of Laravel's collection.

0 likes
2 replies
kfirba's avatar
kfirba
Best Answer
Level 50

@iamfaiz hey.

I believe there isn't any method which will parse that input and figure out on its own how to persist this data. However, you can pretty easily create one.

If you want and it feels better for you, we can try to use the available functionality and when we execute Test::create($data); we will automatically let it know how to parse its relationships. Please note that what I'm going to try here isn't tested and I haven't tested it but the idea seems to be right.

To make it work we need to add to the fillable fields on Test.php the questions value - yes, I know there is no such a column in the database. We will specify this so we can then just pass the whole data and make the Test class know how to save its relationship.

Now let's add a method to the Test class:

public function setQuestionsAttribute($questions)
{
    foreach ($questions as $question) {
        $this->questions()->create($question);
    }
}

Now go to Question.php and add options to the fillable fields.

We will now add a method to Question.php

public function setOptionsAttribute($options)
{
    foreach ($options as $option) {
        $this->options()->create($option);
    }
}

That should do the trick. Note that this way is assuming that the input fields name are the same as your DB column names.

By the way, if it doesn't work, you can just create a method on the Test class which will just have a nested foreach loop and save each relationship manually. Should be fairly easy to do that. It may take the shape of:

public static function publish($data)
{
    $test = new static($data);

    foreach ($data['questions'] as $obj) {
        $question = $test->questions->create($obj);
        
        foreach ($obj['options'] as $option) {
            $question->options()->create($option);
        }
    }
}

We can refactor it to be a recursive function and use the Grammar helper to figure out the relationship names based on the keys that holds an array but let's try to get it work first before refactoring.

Hope that it helps you!

1 like
troelskn's avatar

I realise this is an older post, but I created an add-on for Laravel that will solve this issue. I found this post while looking for such a solution initially, so figured I would post back here in case any one else stumbles by.

Code lives at https://github.com/troelskn/laravel-fillable-relations

I used the particular question asked here as a test case, the following being a fully working implementation:

use Illuminate\Database\Eloquent\Model;
use LaravelFillableRelations\Eloquent\Concerns\HasFillableRelations;

class Question extends Model
{
    use HasFillableRelations;
    protected $fillable = ['test_id', 'title', 'options'];
    protected $fillable_relations = ['test'];
    protected $casts = [
        'options' => 'array',
    ];

    function test()
    {
        return $this->belongsTo(Test::class, 'test_id');
    }
}

class Test extends Model
{
    use HasFillableRelations;
    protected $fillable = ['name', 'preparation'];
    protected $fillable_relations = ['questions'];
    protected $with = ['questions'];

    function questions()
    {
        return $this->hasMany(Question::class, 'test_id');
    }
}

And you can now pass the data from the original question directly to fill:

$test = Test::find(...);
$test->fill($data);
2 likes

Please or to participate in this conversation.