Красноречивые отношения - прикрепить (но не сохранять) к Has Many

У меня установлены следующие отношения:

class Page {
    public function comments() {
        return $this->hasMany('Comment');
    }
}

class Comment {
    public function page() {
        return $this->belongsTo('Page');
    }
}

Довольно болотный стандарт. На одной странице может быть много комментариев, и один комментарий относится к одной странице.

Я хотел бы иметь возможность создать новую страницу:

$page = new Page;

и комментарий

$comment = new Comment;

и прикрепите комментарий к странице, не сохраняя его.

$page->comments->associate($comment);

Я пробовал следующее:

// These are for one-to-many from the MANY side (eg. $comment->page->associate...)
$page->comments->associate($comment);   // Call to undefined method Illuminate\Database\Eloquent\Collection::associate()
$page->comments()->associate($comment); // Call to undefined method Illuminate\Database\Query\Builder::associate()

// These 2 are for many-to-many relations, so don't work
$page->comments->attach($comment);      // Call to undefined method Illuminate\Database\Eloquent\Collection::attach()
$page->comments()->attach($comment);    // Call to undefined method Illuminate\Database\Query\Builder::attach()

// These 2 will (if successful) save to the DB, which I don't want
$page->comments->save($comment);        // Call to undefined method Illuminate\Database\Eloquent\Collection::save()
$page->comments()->save($comment);      // Integrity constraint violation: 1048 Column 'page_id' cannot be null

Самое странное, что обратное действие (прикрепление страницы к комментарию) работает корректно:

$comment->page()->associate($page);

Соответствующие документы находятся здесь, но в них не упоминается присоединение к ОДНОЙ стороне модели. -слишком много. Это вообще возможно? (Я чувствую, что так и должно быть)


person Joe    schedule 30.05.2014    source источник


Ответы (3)


Похоже, вы просто хотите добавить новый объект комментария в коллекцию комментариев страницы — вы можете сделать это легко, используя базовый метод добавления коллекции:

$page = new Page;
$comment = new Comment;
$page->comments->add($comment);
person Benubird    schedule 30.05.2014
comment
Отлично, делает именно то, что мне нужно :) Спасибо - person Joe; 30.05.2014
comment
Как потом сохранить комментарии? - person Boedy; 07.10.2014
comment
@Boedy Это отдельный вопрос - речь идет только об объектах, которые не связаны с базой данных. Если вы хотите сохранить комментарий, вам нужно сначала сохранить страницу, а затем использовать метод ›save(), чтобы связать их. То, что я делаю, на самом деле не свяжет их в базе данных, потому что у них нет идентификаторов; это просто добавление объекта комментария в коллекцию. - person Benubird; 07.10.2014
comment
Утверждая здесь очевидное... вы должны иметь в виду, что вызов свойства comments в $page вызовет запрос в базе данных, поэтому он может быть медленнее, чем вы ожидаете. - person edpaez; 25.11.2014
comment
Вы также должны иметь возможность использовать $page-›push() для сохранения страницы и всех связанных с ней комментариев, а не сохранять каждый по отдельности. - person rich remer; 26.02.2015
comment
@rich-remer Но когда я делаю $page-›push(), он возвращает ошибку, что внешний ключ для $comment (то есть page_id) отсутствует. Что с этим делать? По-видимому, я не могу поставить его, потому что у меня его нет. - person Huzaib Shafi; 20.07.2016

Вы не можете этого сделать, так как нет идентификаторов для ссылки.

Итак, сначала вам нужно сохранить родительскую модель ($page), а затем сохранить дочернюю модель:

// $page is existing model, $comment don't need to be
$page->comments()->save($comment); // saves the comment

или наоборот, на этот раз без сохранения:

// again $page exists, $comment don't need to
$comment->page()->associate($page); // doesn't save the comment yet
$comment->save();
person Jarek Tkaczyk    schedule 30.05.2014
comment
Это имеет смысл, но если я сделаю $comment->page()->associate($page), то он прикрепит его правильно, несмотря на то, что страница также не имеет идентификатора - person Joe; 30.05.2014
comment
Нет, он не выдает никакой ошибки, но и не связывает ее правильно. На самом деле он устанавливает page_id в null, поскольку, как вы сказали, у объекта $page нет идентификатора. - person Jarek Tkaczyk; 30.05.2014
comment
Нет, правильно связывает. page_id действительно равно нулю, но массив отношений заполнен - ​​dd($comment) печатает это: grab.by/xksA - person Joe; 30.05.2014
comment
Я бы не назвал его правильным, если он не работает. На самом деле должна быть какая-то ошибка, но Eloquent в этом случае ее не выдает. - person Jarek Tkaczyk; 30.05.2014
comment
Это не подводит :) Идентификатор страницы - null, поэтому он устанавливает $background->page_id в ноль. Затем он добавляет Page в отношение. Что все имеет смысл и правильно, не терпит неудачу - person Joe; 30.05.2014
comment
Не могу согласиться, этот метод всегда используется в контексте отношения. В отличие от принятого ответа, не имеет смысла устанавливать для FK значение null. - person Jarek Tkaczyk; 30.05.2014
comment
Контекст — это раздел страницы создания для администратора. Комментарий на самом деле является фоном, и страница должна иметь один (или несколько) фонов. Когда я создаю страницу, я хочу, чтобы на нее автоматически добавлялся фон, поэтому остальной части приложения не нужно беспокоиться о логике отношений. - person Joe; 30.05.2014
comment
Это совершенно нормально, я никоим образом не спорю с вашим случаем. Я имею в виду, что associate используется в контексте отношения, поэтому с нулевым FK для меня не имеет смысла. - person Jarek Tkaczyk; 30.05.2014
comment
А, теперь я вижу, откуда ты. Я думаю, что есть много способов аргументировать за и против этого, и это целая банка червей. Интересные черви, но все же черви :P Спасибо за вашу помощь, потому что было полезно просто критически подумать об этом и обсудить - person Joe; 30.05.2014
comment
FYI Redbean не имеет этого ограничения - person malhal; 09.09.2014

согласно Benubird, я просто хотел кое-что добавить, когда наткнулся на это сегодня:

Вы можете вызвать метод добавления для коллекции, как заявил Benubird. Чтобы учесть проблемы edpaaz (дополнительный запущенный запрос), я сделал следующее:

$collection = $page->comments()->getEager(); // Will return the eager collection
$collection->add($comment) // Add comment to collection

Насколько я вижу, это предотвратит дополнительный запрос, поскольку мы используем только объект отношения.

В моем случае один из объектов был постоянным, а первый (в вашем случае) - нет (и должен быть создан). Поскольку мне нужно было обработать несколько вещей и я хотел обработать это объектным образом, я хотел добавить постоянный объект сущности к непостоянному. Однако должно работать и с непостоянными.

Спасибо, Бенуберд, за то, что указали мне правильное направление. Надеюсь, мое дополнение поможет кому-то так же, как и мне.

Пожалуйста, имейте в виду, что это мой первый пост о stackoverflow, поэтому, пожалуйста, оставьте свой отзыв с небольшим беспокойством.

person Julius Blatt    schedule 21.06.2017