Вот основной код:
/**
* Post.php
*/
class Post extends Illuminate\Database\Eloquent\Model {
public function tags() {
return $this->morphToMany('Tag', 'taggable', 'taggable_taggables')
->withTimestamps();
}
}
/**
* Tag.php
*/
class Tag extends Illuminate\Database\Eloquent\Model {
protected $table = 'taggable_tags';
public function taggable() {
return $this->morphTo();
}
}
Теперь возьмите следующий код:
// assume that both of these work (i.e. the models exist)
$post = Post::find(1);
$tag = Tag::find(1);
$post->tags()->attach($tag);
Все идет нормально. Отношения создаются в taggable_taggables
сводная таблица. Однако, если я сразу сделаю:
dd($post->tags);
Возвращает пустую коллекцию. attach()
кажется, создает отношения в база данных но не в текущем экземпляре модели.
Это можно проверить, загрузив модель снова:
$post = Post::find(1);
dd($post->tags);
И теперь отношения увлажнены.
Я уверен, что это сработало в Laravel 4.2 — то есть отношения были обновлены сразу после attach()
, Есть ли способ подтолкнуть Laravel 5 к тому же?
Laravel загрузит атрибут отношения только один раз, независимо от того, загружен он или загружен лениво. Это означает, что после того, как атрибут был загружен, любые изменения в отношении не будут отражены атрибутом, если отношение не будет явно перезагружено.
Точный код, который вы разместили, должен был сработать, как и ожидалось, поэтому я предполагаю, что есть фрагмент, который отсутствует. Например:
$post = Post::find(1);
$tag = Tag::find(1);
$post->tags()->attach($tag);
// This should dump the correct data, as this is the first time the
// attribute is being accessed, so it will be lazy loaded right here.
dd($post->tags);
Против:
$post = Post::find(1);
$tag = Tag::find(1);
// access tags attribute here which will lazy load it
var_dump($post->tags);
$post->tags()->attach($tag);
// This will not reflect the change from attach, as the attribute
// was already loaded, and it has not been explicitly reloaded
dd($post->tags);
Чтобы обойти эту проблему, если вам нужно обновить атрибут отношения, вы можете использовать load()
метод, вместо повторного получения родительского объекта:
$post = Post::find(1);
$tag = Tag::find(1);
// access tags attribute here which will lazy load it
var_dump($post->tags);
$post->tags()->attach($tag);
// refresh the tags relationship attribute
$post->load('tags');
// This will dump the correct data as the attribute has been
// explicitly reloaded.
dd($post->tags);
Насколько я знаю, нет никаких параметров или настроек, чтобы заставить Laravel автоматически обновлять отношения. Я также не могу вспомнить событие модели, к которому вы могли бы подключиться, поскольку вы на самом деле не обновляете родительскую модель. Есть три основных варианта, которые я могу придумать:
Создайте метод в модели, который выполняет присоединение и перезагрузку.
public function attachTags($tags) {
$this->tags()->attach($tags);
$this->load('tags');
}
$post = Post::find(1);
$tag = Tag::find(1);
$post->attachTags($tag);
dd($post->tags);
Создайте новый класс отношений, который расширяет класс отношений BelongsToMany и переопределяет attach
способ выполнить желаемую логику. Затем создайте новый класс модели, который расширяет класс Eloquent Model, и переопределите belongsToMany
метод для создания экземпляра вашего нового класса отношений. Наконец, обновите модель Post, чтобы расширить новый класс Model вместо класса Eloquent Model.
Просто убедитесь, что вы всегда перезагружаете ваши отношения, когда это необходимо.
Других решений пока нет …