Не удается заставить backpack передавать правильное значение при вставке / обновлении отношения «многие ко многим»

#php #laravel #laravel-backpack

#php #laravel #laravel-backpack

Вопрос:

Я создал отношение «многие ко многим» между двумя таблицами с третьей сводной таблицей. Ситуация немного усложняется тем, что я связываю таблицу приложений на основе имени, а не идентификатора. Это потому, что я обновляю список приложений от третьей стороны, и название приложения всегда будет согласованным, где идентификатор может измениться, если приложение будет удалено в какой-то момент, а затем повторно добавлено и т.д.

Приложения

  • ID
  • name // Это имя приложения, оно никогда не изменится для конкретного приложения и является коротким, все в нижнем регистре, без пробелов и уникальным
  • label // Это удобное для пользователя имя

Планы

  • ID
  • Имя
  • и т. д

сводная таблица apps_plans

  • ID
  • apps_name
  • plans_id

Наконец-то у меня все отлично работает в самом Laravel, но я вообще не могу понять, как заставить это правильно работать в Backpack для моего портала администратора. Я дошел до того, что все работает идеально, пока я не попытаюсь обновить или создать новый план. Приложения, которые я выбираю, используя тип select2, он пытается вставить их в сводную таблицу с идентификационным номером, а не с именем.

Рандомизация некоторых имен, моя ошибка, если что-то не совпадает идеально. Этот аспект отлично работает во всех тестах, которые я выполнил:

Модель планов:

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



    public function apps()
    {
        return $this->belongsToMany('AppApps', '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('AppPlan', 'apps_plans', 'apps_name', 'plans_id', 'name', 'id');
    }
}

  

** Обратите внимание, что я удалил заполняемую переменную, я не хотел выставлять все переменные в своих столбцах.

Backpack планирует CrudController:

     public function setup()
    {
        CRUD::setModel(AppPlan::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' => AppApps::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,
        ]);
  

Я удалил совсем немного, чтобы сохранить детали моего проекта в тайне, надеюсь, это имеет смысл. Я думаю, что все важные детали все еще включены. Кто-нибудь знает, является ли это проблемой с Backpack? Или я где-то пропустил опцию, где вы можете установить, какой столбец он использует для отношения. Это явно не берет его из модели, потому что модели работают так, как задумано сами по себе…

Спасибо!

Редактировать: вот моя миграция, которую я использую, она работает безупречно — даже в 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))"
}

  

Опять же, я удалил некоторые детали, которые были очень специфичны для моего проекта, но я не думаю, что я изменил какую-либо логику в сообщении об ошибке. Вы можете видеть, что в запросе все выглядит великолепно, за исключением того, что в самом конце он вставляет идентификатор приложения вместо имени приложения, как и должно быть.

Комментарии:

1. У меня также есть идентификатор автоматического увеличения. Но позвольте мне попробовать это

2. Я пробовал это, это не помогло. однако имя не является первичным ключом, и я видел множество примеров сводной таблицы, созданной с отдельным автоматически увеличивающимся идентификатором и без него

3. Хм, что произойдет, если вы добавите 'model' => AppApps::class, // foreign key model в конфигурацию поля?

4. @WesleySmith Спасибо за ваш комментарий. Вчера во время отладки я действительно попробовал это, и в настоящее время оно находится в моем поле. Я не заметил эту документацию в Backpack: backpackforlaravel.com/docs/4.1/… После тщательного рассмотрения этого сегодня утром и попытки еще нескольких вещей, я думаю, что это ошибка в Backpack. Я никогда не показывал точную ошибку, которую получаю, хотя это несколько очевидно, но я сделаю еще одно редактирование моего вопроса с ошибкой.

5. @WesleySmith Это действительно сработало. Я рад отметить вас как принятый ответ, если вы просто хотите предложить сделать name первичным ключом. Мне пришлось внести пару изменений в свою модель, чтобы все остальное работало должным образом, я могу ответить на ваш ответ этими корректировками на случай, если кто-нибудь еще увидит это в будущем

Ответ №1:

Я подозреваю, что текущая конфигурация будет иметь тот же результат непосредственно в Laravel. т.Е. запуск чего-то подобного $plan = Plan::find($somePlanId); $app = App::find($someAppId); $plan->apps()->attach($app); приведет к той же ошибке.

Поскольку name это ключ, который имеет значение для apps таблицы, рассмотрите возможность удаления идентификатора автозаполнения для этой таблицы и вместо этого установите

В миграции для таблицы приложений выполните:

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

Затем в вашей модели приложений выполните:

 protected $primaryKey = 'name';

public $incrementing = false;

protected $keyType = 'string';
  

Теперь Laravel (и через прокси Backpack) должен обрабатывать отношения так, как вы ожидаете.

Комментарии:

1. Идеальное решение. Я был уверен, что это связано с рюкзаком, но с вашим методом устранения неполадок легко увидеть, что это не связано с рюкзаком.