En En

Связи


Ember Data включает несколько встроенных типов связей, которые помогают определить, как модели взаимодействуют друг с другом.

Один-к-одному

Чтобы объявить связь один-к-одному между двумя моделями, используйте DS.belongsTo:

app/models/user.js

import DS from 'ember-data';

export default DS.Model.extend({
  profile: DS.belongsTo('profile')
});

app/models/profile.js

import DS from 'ember-data';

export default DS.Model.extend({
  user: DS.belongsTo('user')
});

Один-ко-многим

Чтобы объявить связь один-ко-многим между двумя моделями, используйте DS.belongsTo в сочетании с DS.hasMany таким образом:

app/models/blog-post.js

import DS from 'ember-data';

export default DS.Model.extend({
  comments: DS.hasMany('comment')
});

app/models/comment.js

import DS from 'ember-data';

export default DS.Model.extend({
  blogPost: DS.belongsTo('blog-post')
});

Многие-ко-многим

Чтобы объявить связь многие-ко-многим между двумя моделями, используйте DS.hasMany:

app/models/blog-post.js

import DS from 'ember-data';

export default DS.Model.extend({
  tags: DS.hasMany('tag')
});

app/models/tag.js

import DS from 'ember-data';

export default DS.Model.extend({
  blogPosts: DS.hasMany('blog-post')
});

Заданные инверсии

Ember Data сделает все возможное, чтобы выявить, какие связи сопоставлены друг с другом. В коде один-ко-многим выше, например, Ember Data может выяснить, что изменение связи comments должно обновить связь blogPost по инверсии, так как blogPost — единственная связь у этой модели.

Но иногда у вас может быть несколько связей belongsTo/hasMany для одного типа. Вы можете указать, какое свойство соответствующей модели служит инверсией, с помощью параметра inversion в DS.belongsTo или DS.hasMany. Связи без инверсии можно отметить таковыми, если включить { inverse: null }.

app/models/comment.js

import DS from 'ember-data';

export default DS.Model.extend({
  onePost: DS.belongsTo('blog-post', { inverse: null }),
  twoPost: DS.belongsTo('blog-post'),
  redPost: DS.belongsTo('blog-post'),
  bluePost: DS.belongsTo('blog-post')
});

app/models/blog-post.js

import DS from 'ember-data';

export default DS.Model.extend({
  comments: DS.hasMany('comment', {
    inverse: 'redPost'
  })
});

Рефлексивные связи

Если вы хотите определить рефлексивную связь (модель, которая взаимодействует сама с собой), вы должны явно определить обратную связь (инверсию). Если обратной связи нет, то вы можете установить значение инверсии на null.

Вот пример рефлексивной связи один-ко-многим:

app/models/folder.js

import DS from 'ember-data';

export default DS.Model.extend({
  children: DS.hasMany('folder', { inverse: 'parent' }),
  parent: DS.belongsTo('folder', { inverse: 'children' })
});

Вот пример рефлексивной связи один-к-одному:

app/models/user.js

import DS from 'ember-data';

export default DS.Model.extend({
  name: DS.attr('string'),
  bestFriend: DS.belongsTo('user', { inverse: 'bestFriend' }),
});

Вы также можете определить рефлексивную связь, у которой нет инверсии:

app/models/folder.js

import DS from 'ember-data';

export default DS.Model.extend({
  parent: DS.belongsTo('folder', { inverse: null })
});

Вложенные данные с доступом только для чтения

У некоторых моделей могут быть свойства, которые представляют собой глубоко вложенные объекты с доступными только чтения данными. Самым простым решением будет определить модели для каждого вложенного объекта и использовать hasMany и belongsTo, чтобы воссоздать вложенную связь. Но так как доступные только для чтения данные не нужно обновлять и сохранять, это часто приводит к написанию большого количества кода, который не несет особой пользы. Альтернативный подход: определить эти связи с помощью атрибута без преобразования (DS.attr()). Так проще обратиться к доступным только для чтения значениям в вычисляемых свойствах и шаблонах без определения посторонних моделей.

Создание записей

Предположим, что у нас есть модели blog-post и comment, которые связаны друг с другом так:

app/models/blog-post.js

import DS from 'ember-data';

export default DS.Model.extend({
  comments: DS.hasMany('comment')
});

app/models/comment.js

import DS from 'ember-data';

export default DS.Model.extend({
  blogPost: DS.belongsTo('blog-post')
});

Когда пользователь комментирует публикацию в блоге (blogPost), нужно создать связь между двумя записями. Мы можем просто установить связь belongsTo в нашем новом комментарии:

let blogPost = this.get('store').peekRecord('blog-post', 1);
let comment = this.get('store').createRecord('comment', {
  blogPost: blogPost
});
comment.save();

Это создаст новую запись comment и сохранит ее на сервере. Ember Data также обновит blogPost, чтобы включить только что созданный комментарий в связь comments.

Мы также могли связать две записи, обновив связь hasMany в blogPost.

let blogPost = this.get('store').peekRecord('blog-post', 1);
let comment = this.get('store').createRecord('comment', {
});
blogPost.get('comments').pushObject(comment);
comment.save().then(function () {
  blogPost.save();
});

В этом случае связь belongsTo нового комментария будет установлена для родительской публикации.

Хотя createRecord — довольно простой метод, нужно учесть, что вы не можете назначить обещание как связь, на текущий момент.

Например, если вы хотите установить свойство author для публикации, это не сработает, если user с идентификатором еще не загружен в хранилище:

this.get('store').createRecord('blog-post', {
  title: 'Rails is Omakase',
  body: 'Lorem ipsum',
  author: this.get('store').findRecord('user', 1)
});

Но вы легко можете установить связь после разрешения обещания:

let blogPost = this.get('store').createRecord('blog-post', {
  title: 'Rails is Omakase',
  body: 'Lorem ipsum'
});

this.get('store').findRecord('user', 1).then(function(user) {
  blogPost.set('author', user);
});

Извлечение связанных записей

Когда вы запрашиваете данные с сервера для модели, у которой есть связь с одной или несколькими другими моделями, возможно, вы захотите сразу извлечь записи, относящиеся к этим моделям. Например, при извлечении публикации блога вам нужно получить доступ к комментариям, связанным с этой публикацией. Спецификация JSON API позволяет серверам принимать параметр запроса с ключом include в качестве требования включить эти связанные записи в ответ, возвращаемый клиенту. Значение параметра должно представлять собой разделенный запятыми список имен требуемых связей.

Если вы используете адаптер, который поддерживает JSON API, например исходный JSONAPIAdapter в Ember, вы можете добавлять параметр include в запросы к серверу, созданные методами findRecord(), findAll(), query() и queryRecord().

findRecord() и findAll() принимают аргумент options, в котором вы можете указать параметр include. Например, так как у модели post есть связь hasMany с моделью comment, при извлечении конкретной публикации мы можем сделать так, чтобы сервер также вернул комментарии к этой публикации следующим образом:

app/routes/post.js

export default Ember.Route.extend({
  model(params) {
   return this.store.findRecord('post', params.post_id, {include: 'comments'});
  }
});

Комментарии к публикации затем будут доступны в вашем шаблоне в качестве model.comments.

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

app/routes/post.js

export default Ember.Route.extend({
  model(params) {
   return this.store.findRecord('post', params.post_id, {include: 'comments,comments.author'});
  }
});

Методы query() и queryRecord() принимают аргумент query, который сериализуется прямо в строку запроса URL, и параметр include может составить часть этого аргумента. Например:

app/routes/adele.js

export default Ember.Route.extend({
  model() {
    // GET to /artists?filter[name]=Adele&include=albums
    this.store.query('artist', {
      filter: {name: 'Adele'},
      include: 'albums'
    }).then(function(artists) {
      return artists.get('firstObject');
    });
  }
});

Обновление существующих записей

Иногда приходится устанавливать связи для существующих записей. Мы можем легко установить связь belongsTo:

let blogPost = this.get('store').peekRecord('blog-post', 1);
let comment = this.get('store').peekRecord('comment', 1);
comment.set('blogPost', blogPost);
comment.save();

Или мы могли обновить связь hasMany, поместив запись в связь:

let blogPost = this.get('store').peekRecord('blog-post', 1);
let comment = this.get('store').peekRecord('comment', 1);
blogPost.get('comments').pushObject(comment);
blogPost.save();

Удаление связей

Чтобы удалить связь belongsTo, можно установить ее значение на null, что также удалит ее со стороны hasMany:

let comment = this.get('store').peekRecord('comment', 1);
comment.set('blogPost', null);
comment.save();

И можно удалить запись из связи hasMany:

let blogPost = this.get('store').peekRecord('blog-post', 1);
let comment = this.get('store').peekRecord('comment', 1);
blogPost.get('comments').removeObject(comment);
blogPost.save();

Как и в ранних примерах, Ember Data также уберет связь комментария belongsTo.

Связи как обещания

При работе со связями важно помнить, что они возвращают обещания.

Например, если мы работаем с асинхронными комментариями blogPost, нам придется ждать, пока разрешится обещание:

let blogPost = this.get('store').peekRecord('blog-post', 1);

blogPost.get('comments').then((comments) => {
  // now we can work with the comments
});

То же касается и связи belongsTo:

let comment = this.get('store').peekRecord('comment', 1);

comment.get('blogPost').then((blogPost) => {
  // the blogPost is available here
});

Шаблоны Handlebars автоматически обновятся, чтобы отразить разрешенное обещание. Мы можем отобразить список комментариев в blogPost так:

<ul>
  {{#each blogPost.comments as |comment|}}
    <li>{{comment.id}}</li>
  {{/each}}
</ul>

Ember Data запросит у сервера соответствующие записи и отобразит шаблон по-новому при получении данных.


Комментарии (0)

    Выделите опечатку и нажмите Ctrl + Enter, чтобы отправить сообщение об ошибке.