
Yii2 еще только в бета-тестировани, но, видимо, это никого не пугает. Кто-то просто изучает новые возможности, но многие начали использовать его даже в продакшене и под дулом пистолета отказываются даже смотреть на код первой версии.
На
форуме русского сообщества всё больше вопросов и обсуждения. Оказалось что у многих возникли трудности с ActiveRecord и связанными данными.
Я решил написать свой рецепт. Возможно, вы посчитаете его полезным, возможно поймёте как делать не надо. В любом случае, надеюсь, материал будет полезен.
Что нам предлагает фреймворк для работы со связями?
Связи в новой версии фреймворка объявляются при помощи геттеров:
public function getCategory()
{
return $this->hasOne(Category::className(), ['id' => 'category_id']);
}
Геттер возвращает ActiveQuery, который можно дополнительно настроить перед загрузкой связанной модели:
$posts = Category::find($id)->getPosts()->limit(5)->order('created_at')->all();
Замечание:
Когда Вы используете магию $post->category, вместо геттера, помните, так вы получаете результат запроса Query-объекта.
Другими словами $post->category === $post->getCategory()->one()
Методы работы со связями
populateRelation($relationName, $relatedModelOrArray) -добавляет связанную модель в родительскую.
Замечание:
Этот метод не проверяет, объявлена ли связь между этими моделями (геттер), а также не устанавливает нужные значения в атрибуты.
$post = new Post();
$post->populateRelation('category', new Category());
$post->populateRelation('tags', [new Tag(), new Tag()]);
link($relationName, relatedModel, $extraColumns = []) — в отличии от populateRelation, этот метод, кроме добавления связанной модели, также привязывает модели, расставляя нужные индексы. Сразу он сохраняет ТОЛЬКО связанную модель. $extraColumns сохранятся в pivot table, если связь осуществляется через неё.
$post = new Post();
$post->link('category', new Category());
$post->link('tags', new Tag());
$post->link('tags', new Tag());
Вам, возможно, захочется сохранять модели вместе со связями в одной транзакции. Для этого в Yii2 есть встроенные средства:
public function transactions()
{
return [
self::SCENARIO_DEFAULT => self::OP_INSERT | self::OP_UPDATE,
];
}
Это лишь некоторые методы. Остальные Вы найдёте в официальной документации.
Пример
Теперь я хочу показать как их можно использовать на примере.
Модельclass Post extends ActiveRecord
{
public function transactions()
{
return [
self::SCENARIO_DEFAULT => self::OP_INSERT | self::OP_UPDATE,
];
}
public function getTags()
{
return $this->hasMany(Tag::className(), ['id' => 'tag_id'])
->viaTable('post_tag', ['post_id' => 'id']);
}
public function setTags($tags)
{
$this->populateRelation('tags', $tags);
$this->tags_count = count($tags);
}
public function setTagsString($value)
{
$tags = [];
foreach (explode(',' $value) as $name) {
$tag = new Tag();
$tag->name = $name;
$tags[] = $tag;
}
$this->setTags($tags);
}
public function getCover()
{
return $this->hasOne(Image::className(), ['id' => 'cover_id']);
}
public function setCover($cover)
{
$this->populateRelation('cover', $cover);
}
public function getImages()
{
return $this->hasMany(Image::className(), ['post_id' => 'id']);
}
public function setImages($images)
{
$this->populateRelation('images', $images);
if (!$this->isRelationPopulated('cover') && !$this->getCover()->one()) {
$this->setCover(reset($images));
}
}
public function loadUploadedImages()
{
$images = [];
foreach (UploadedFile::getInstances(new Image(), 'image') as $file) {
$image = new Image();
$image->name = $file->name;
$images[] = $image;
}
$this->setImages($images);
}
public function beforeSave($insert)
{
if (!parent::beforeSave($insert)) {
return false;
}
$relatedRecords = $this->getRelatedRecords();
if (isset($relatedRecords['cover'])) {
$this->link('cover', $relatedRecords['cover']);
}
return true;
}
public function afterSave($insert)
{
$relatedRecords = $this->getRelatedRecords();
if (isset($relatedRecords['tags'])) {
foreach ($relatedRecords['tags'] as $tag) {
$this->link('tags', $tag);
}
}
if (isset($relatedRecords['images'])) {
foreach ($relatedRecords['images'] as $image) {
$this->link('images', $image);
}
}
}
}
Контролерclass PostController extends Controller
{
public function actionCreate()
{
$post = new Post();
if ($post->load(Yii::$app->request->post())) {
$post->loadUploadedImages();
if ($post->save()) {
return $this->redirect(['view', 'id' => $post->id]);
}
}
return $this->render('create', [
'post' => $post,
]);
}
}
Вместо заключения
Если вы знаете что такое
Yii Framework, живете в
Кишиневе (Молдова) или поблизости, присоединяйтесь к нам! Мы хотим собраться в оффлайне.
Подробности здесь!
Ждем всех!
комментарии (6)