Aaron Shames
Aaron Shames

Reputation: 13

Can't get backpack to pass correct value when inserting/updating a many to many relationship

I have created a many to many relationship between two tables with a third pivot table. The thing that makes the situation a little difficult is I am linking the Apps table based on name and not ID. It is because I update the App list from a third party and app name will always be consistent, where ID can possibly change if App is removed at some point, and then re-added, etc.

Apps

Plans

apps_plans pivot table

I've finally got everything working perfectly in Laravel itself, but I cannot figure out at all how to get this to work correctly in Backpack for my Admin portal. I've gotten it to the point where everything works perfect until I try to update or create a new plan. The Apps I select using the select2 type, it tries to insert them into the pivot table with an ID number and not with the name.

Randomizing some names, my mistake if things don't match perfectly. This aspect works fine from all tests I've done:

Plans Model:

{
    use CrudTrait;
    protected $table = 'plans';
    protected $guarded = ['id'];



    public function apps()
    {
        return $this->belongsToMany('App\Apps', 'apps_plans', 'plans_id', 'apps_name', 'id', 'name');
    }
}

Apps Model:

    class Apps extends Model
{
    use CrudTrait;
    protected $table = 'apps';
    protected $guarded = ['id'];

    protected $casts = [
        'json' => 'array',
    ];

    public function plans()
    {
        return $this->belongsToMany('App\Plan', 'apps_plans', 'apps_name', 'plans_id', 'name', 'id');
    }
}

**Note I removed the fillable variable , I didn't want to expose all variables in my columns.

Backpack Plans CrudController:

    public function setup()
    {
        CRUD::setModel(\App\Plan::class);
        CRUD::setRoute(config('backpack.base.route_prefix') . '/plan');
        CRUD::setEntityNameStrings('plan', 'plans');

        $this->crud->addColumn([
            'name' => 'apps',
            'type' => 'relationship',
            'label' => 'Apps',
            'entity' => 'apps',
            'attribute' => 'label',
            'model' => \App\Apps::class,
        ]);
    }


    protected function setupCreateOperation()
    {
        CRUD::setValidation(PlanRequest::class);

        CRUD::setFromDb(); // fields
       
        $this->crud->addField('apps', [
            'name' => 'apps',
            'type' => 'select2_multiple',
            'entity' => 'apps',
            'attribute' => 'label',
            'label' => 'Apps',
            'pivot' => true,
        ]);

I removed quite a bit to keep my project details private, I hope it makes sense. I think all important details are still in. Anyone know if this is an issue with Backpack? Or did I miss an option somewhere, where you can set which column it uses for the relationship. It is clearly not taking it from the model because the models work just as intended on their own...

Thanks!

Edit: here is my migration I am using, it works flawlessly--even in phpmyadmin it gives me a drop down of items to select from

    {
        Schema::create('apps_plans', function (Blueprint $table) {
            $table->id();
            $table->string('apps_name');
            $table->foreign('apps_name')->references('name')->on('apps');
            $table->unsignedBigInteger('plans_id');
            $table->foreign('plans_id')->references('id')->on('plans');
        });
    }

EDIT 2:

This is the error I am getting when trying to do a Create or Update:


{
"error": "There is a problem with your request",
"message": "SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`api`.`apps_plans`, CONSTRAINT `apps_plans_apps_name_foreign` FOREIGN KEY (`apps_name`) REFERENCES `apps` (`name`)) (SQL: insert into `apps_plans` (`apps_name`, `plans_id`) values (2, 4))"
}

Again I removed some details that were very specific to my project but I don't think I changed any of the logic in the error message. You can see everything looks great about the query except that at the very end, it is inserting the App ID instead of the App name as it should be.

Upvotes: 1

Views: 816

Answers (1)

Wesley Smith
Wesley Smith

Reputation: 19571

I suspect that the current configuration will have the same result in Laravel directly. ie running something like $plan = Plan::find($somePlanId); $app = App::find($someAppId); $plan->apps()->attach($app); would result in the same error.

Since name is the key that matters for the apps table, consider dropping the autoincrementing id for that table and instead setting

In the migration for the apps table, do:

$table->string('name')->primary();

Then in your apps model, do:

protected $primaryKey = 'name';

public $incrementing = false;

protected $keyType = 'string';

Now, Laravel (and by proxy Backpack) should treat the relationship the way you expect.

Upvotes: 2

Related Questions