Save parent ID when inserting new pivot data relation

I have backend form created with rainlab builder

Basically I have model subject and level with many to many relations, so I have a pivot table named subjects_levels that has columns subject_id, level_id, description

Note that It has pivot data description

In the backend page, I have a form to create subject that looked like this

I render the Levels with a partial view using relationRender method

The problem is when I try to add levels before saving the subject, this form is throwing an error because the subject_id is null (of course its null because the subject itself has not been saved)

Any workaround to solve this ??

Second question :
Also when you try to order the Levels based on description it will throw an query error that column description is not exist. When I inspect the query it select from table levels try to order by a column named pivot_description.
- First : that column does not exist in table levels, but in table subjects_levels
- Second : the right column name is description not pivot_description

Hi @wibisono.indrawan

It sounds like most of these issues could be solved by setting the database columns to nullable using the ->nullable() method in the database schema. This would prevent the database from complaining when the unpopulated fields are empty.

This won’t solve the issue.

I mean yes, technically the database won’t complain anymore but it will break the functionality of this page form.

Are there any ways to save the parent data (the subject data) when I save the levels ?

I’ve found method onRelationButtonCreate in the controller, but I’ve no idea how to save the parent data inside that method

You can manipulate the model by hooking into its Relation events to populate the values. The model.relation.beforeAttach in the BelongsToMany relationship class comes to mind. Called before creating a new relationship between models. Example usage:

$model->bindEvent('model.relation.beforeAttach', function ($relationName, $ids, &$attributes) {
    $attributes['foo'] = 'bar';

where do I put that code ?

is it in the onRelationButtonCreate ?

Or are there any ways to hide the “Levels” input when in create form ?
so it will only appear on edit form only

Here is the documentation on extending models:

You can also specify context: update on the field so that it only displays during the update. Here is the docs on the available form field properties:

Hi daftspunky,

Thanks for highlighting the context field, it works perfectly

But about the models bind event, I’m not entirely sure how to implement it. I understand the basic usage of the model event, but I’m struggling to implement it in this use case.

Do I need to create a model for the pivot table and use beforeCreate event ?
Because it does not work in this use case

What I really need is to insert the subject data when user adds a level to the current subject, but the problem is the current subject has not been inserted into the database. So in my mind, I need to insert it manually by putting the logic inside onRelationManageAddPivot in Subject Controller.
Because level is the pivot of subject and it triggered correctly, but I’ve no idea how to call the inputted subject data inside onRelationManageAddPivot

And when you suggest using Models Event I have no clue what I should do about it.

The text here is not entirely clear on the subject data, so it’s difficult to offer concrete advice. Providing the actual fields and sample data can help with this.

As I gather, you want the subject to be updated when the level is added, but you can’t do that since the level does not exist. Something like this:

  1. Show a New Subject Form using an Unsaved Subject model
  2. Attach a Level to the subject, using a Deferred Level model (saved but orphaned)
  3. When the Subject is created, update the Subject data based on the Level

In step 2, it is impossible to associate any data to the Subject model because it does not exist yet. This is a given. However, it is possible to hook into the model events before the creation to add the Level data, and it is unclear which Level data will be used if there are multiple levels.

Using model events, you can hypothetically do the following. Before the Subject is saved, request the deferred levels and populate the subject data.

// Inside the Subject model
function beforeSave()
    // Problem: there can be multiple levels
    $levels = $this->levels()->withDeferred($this->sessionKey)->get();

    // Assumption: Take the first one
    $level = $levels[0];

    // Transfer data from level to subject
    $this->subject_data = $level->subject_data;

Okay I’ll explore more for another workaround

Thank you for your response