Laravel красноречивый метод служебной связи модели завершается с ошибкой в первый раз

#php #laravel #eloquent #relationship #contains

#php #laravel #красноречивый #связь #содержит

Вопрос:

В новой установке Laravel 8.20.1 я создал две модели Company User со сводной таблицей между ними, чтобы облегчить связь «многие ко многим». У стержня есть role атрибут.

Я могу добавить пользователя в компанию, используя:

 $company->users()->attach($user);
 

Я добавил служебный метод addUser , чтобы сначала проверить наличие связи, чтобы избежать дублирования:

 public function addUser(User $user) {
    if($this->users->contains($user)) {
        // if the user already has a role, update it
        Log::info("User #{$user->id} present - updating");
        $this->users()->updateExistingPivot($user, ['role' => 'user'], true);
    } else {
        // if the user doesn't have a role, add it
        Log::info("User #{$user->id} not present - adding");
        $this->users()->attach($user, ['role' => 'user'], true);
    }
}
 

В первый раз, когда я запускаю это с использованием обновленной базы данных, он должен увидеть, что пользователь еще не связан с компанией, и запустить else часть переключателя, чтобы добавить нового пользователя. Запустив это в Tinker, кажется, что он делает это — и в журналах отображается «Пользователь № 1 отсутствует — добавление», — но когда я проверяю наличие с помощью contains , он возвращает false:

 $user = User::factory()->create();
$company = Company::factory()->create();

$company->addUser($user);
print_r($company->users->contains($user)); //false
 

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

Кроме того, я могу видеть сводную запись для пользователя № 1 и компании № 1, и если я затем протестирую это в Tinker, я получу true:

 print_r(Company::find(1)->users->contains(User::find(1))); // true
 

Это почти как если бы база данных работала асинхронно, чего, я знаю, в PHP нет. Я использую Sqlite v3.31.0.

Проблема определенно в моем addUser методе, как будто я заменяю этот вызов в Tinker attach прямым вызовом, он работает.

Я действительно очень хочу использовать этот служебный метод (и другие), потому что:

  • Я хочу избежать нескольких сводных записей для одной и той же компании / пользователя (без использования составных индексов)
  • Мне нужно еще несколько служебных методов, таких как addAdmin , addOwner , и т. Д

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

1. Вы пробовали использовать этот syncWithoutDetaching() метод?

2. Это действительно решает проблему, спасибо! Все еще интересно, почему мой код не работает.

3. Возможно, это $company->users , будучи Коллекцией, не обновляется attach() . Если бы вы это сделали print_r($company->users()->get()->contains($user)) , вы могли бы увидеть true . Или $company->fresh()->users->contains($user) (т.е. перезагрузка отношения модели перед попыткой проверки contains() ). Не уверен, как это будет вести себя с заводской логикой…

4. Вы поняли это?