首页 > 解决方案 > 插入/更新多对多关系时无法让背包传递正确的值

问题描述

我用第三个数据透视表在两个表之间创建了多对多关系。让情况有点困难的是我正在根据名称而不是 ID 链接 Apps 表。这是因为我从第三方更新了 App 列表,并且应用名称将始终保持一致,如果 App 在某个时候被删除,然后重新添加等,ID 可能会发生变化。

应用

计划

apps_plans 数据透视表

我终于让 Laravel 本身的一切都完美运行了,但我根本不知道如何让它在我的管理员门户的 Backpack 中正常工作。在我尝试更新或创建新计划之前,我已经达到了一切正常的地步。我使用 select2 类型选择的应用程序,它尝试使用 ID 号而不是名称将它们插入到数据透视表中。

随机化一些名称,如果事情不完全匹配,我的错误。从我所做的所有测试来看,这方面都很好:

计划模型:

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



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

应用模型:

    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');
    }
}

**注意我删除了可填充变量,我不想在我的列中公开所有变量。

背包计划 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,
        ]);

我删除了很多内容以使我的项目细节保密,我希望这是有道理的。我认为所有重要的细节都还在。有人知道这是否是背包的问题吗?还是我在某个地方错过了一个选项,您可以在其中设置它用于关系的列。显然不是从模型中获取它,因为模型按照自己的预期工作......

谢谢!

编辑:这是我正在使用的迁移,它完美无缺——即使在 phpmyadmin 中,它也为我提供了一个可供选择的项目的下拉列表

    {
        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');
        });
    }

编辑2:

这是我在尝试创建或更新时遇到的错误:


{
"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))"
}

我再次删除了一些对我的项目非常具体的细节,但我认为我没有更改错误消息中的任何逻辑。您可以看到查询的一切看起来都很棒,除了在最后,它插入的是 App ID 而不是应有的 App 名称。

标签: phplaravellaravel-backpack

解决方案


我怀疑当前的配置会直接在 Laravel 中产生相同的结果。即运行类似的东西$plan = Plan::find($somePlanId); $app = App::find($someAppId); $plan->apps()->attach($app);会导致同样的错误。

由于name是对apps表很重要的键,因此请考虑删除该表的自动递增 id 并改为设置

在应用程序表的迁移中,执行以下操作:

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

然后在您的应用模型中,执行以下操作:

protected $primaryKey = 'name';

public $incrementing = false;

protected $keyType = 'string';

现在,Laravel(以及通过代理 Backpack)应该按照您期望的方式处理这种关系。


推荐阅读