Проверить отношение hasMany к другому hasMany - PullRequest
0 голосов
/ 03 мая 2018

У меня есть 3 модели: type, restriction и item.

A type прост и имеет только id:

app/models/type.js

import Model from 'ember-data/model';
export default Model.extend({});

A restriction может иметь множество type, описывающих допустимые типы для элемента с этим ограничением:

app/models/restriction.js

import Model from 'ember-data/model';
import { hasMany } from 'ember-data/relationships';

export default Model.extend({
  allowedTypes: hasMany( "type" )
});

An item может иметь много type, но также может иметь много restriction и type должно быть только подмножеством пересечения разрешенных типов для всех ограничений (и если есть хотя бы одно ограничение, то оно должно иметь хотя бы один тип).

Я реализовал проверку для этого с помощью вычисляемого свойства:

app/models/item.js

import Model from 'ember-data/model';
import { computed } from '@ember/object';
import { hasMany } from 'ember-data/relationships';
import { isEmpty } from '@ember/utils';

const peekHasMany    = attr => ( item => item.hasMany( attr ).ids() );
const hasItems       = array => !isEmpty( array );
const includedIn     = array => ( item => array.indexOf( item ) >= 0 );
const intersectionOf = ( array1, array2, index ) => index >= 0 ? array1.filter( includedIn( array2 ) ) : array2;

export default Model.extend({
  types:        hasMany( "type" ),
  restrictions: hasMany( "restriction" ),
  isValidTypes: computed(
    "types.[]",
    "restrictions.@each.allowedTypes",
    function(){
      let restrictions = this.hasMany( "restrictions" ).value();

      if ( isEmpty( restrictions ) )
      {
        return true;
      }

      let allowed = restrictions
                      .map( peekHasMany( "allowedTypes" ) )
                      .filter( hasItems );

      if ( isEmpty( allowed ) )
      {
        return true;
      }

      let types = this.hasMany( "types" ).ids();
      if ( isEmpty( types ) )
      {
        return false;
      }

      let allowedTypes = allowed.reduce( intersectionOf );
      return types.every( includedIn( allowedTypes ) );
    }
  )
});

При этом используется DS.Model.hasMany( attributeName ) для синхронного получения HasManyReference для отношений, основанных на загружаемых ссылочных моделях.

Как я могу изменить вычисляемое свойство, чтобы использовать this.get() для асинхронного получения обоих атрибутов (и дочерних атрибутов) вместо использования this.hasMany() синхронно?

1 Ответ

0 голосов
/ 03 мая 2018
let value = this.hasMany( attributeName ).value();
/* following code */

можно заменить на

this.get( attributeName ).then( value => { /* following code */ } );

Осложнение приходит со строками:

const peekHasMany = attr => ( item => item.hasMany( attr ).ids() );
let allowed = restrictions.map( peekHasMany( "allowedTypes" ) )
/* following code */

Что, при изменении, приведет к массиву обещаний. Это может быть заключено в одно обещание, используя Promise.all( arrayOfPromises )

const getAll = attr => ( item => item.get( attr ) );
Promise.all( restrictions.map( getAll( "allowedTypes" ) ) )
  .then( allowed => {
    /* following code */
  } );

Код становится:

import Model from 'ember-data/model';
import { computed } from '@ember/object';
import { hasMany } from 'ember-data/relationships';
import { isEmpty } from '@ember/utils';

const getAll         = attr => ( item => item.get( attr ) );
const hasItems       = array => !isEmpty( array );
const includedIn     = array => ( item => array.indexOf( item ) >= 0 );
const intersectionOf = ( array1, array2, index ) => index >= 0 ? array1.filter( includedIn( array2 ) ) : array2;

export default Model.extend({
  types:        hasMany( "type" ),
  restrictions: hasMany( "restriction" ),
  isValidTypes: computed(
    "types.[]",
    "restrictions.@each.allowedTypes",
    function(){
      return this.get( "restrictions" )
        .then( restrictions => {
          if ( isEmpty( restrictions ) )
          {
            return true;
          }

          return Promise.all( restrictions.map( getAll( "allowedTypes" ) ) )
            .then( allowed => {
              allowed = allowed.filter( hasItems );
              if ( isEmpty( allowed ) )
              {
                return true;
              }

              return this.get( "types" )
                .then( types => {
                  if ( isEmpty( types ) )
                  {
                    return false;
                  }

                  let allowedTypes = allowed.reduce( intersectionOf );
                  return types.every( includedIn( allowedTypes ) );
                } );
            } );
        } );
    }
  )
});

Или используя синтаксис async и await:

import Model from 'ember-data/model';
import { computed } from '@ember/object';
import { hasMany } from 'ember-data/relationships';
import { isEmpty } from '@ember/utils';

const getAll         = attr => ( item => item.get( attr ) );
const hasItems       = array => !isEmpty( array );
const includedIn     = array => ( item => array.indexOf( item ) >= 0 );
const intersectionOf = ( array1, array2, index ) => index >= 0 ? array1.filter( includedIn( array2 ) ) : array2;

export default Model.extend({
  types:        hasMany( "type" ),
  restrictions: hasMany( "restriction" ),
  isValidTypes: computed(
    "types.[]",
    "restrictions.@each.allowedTypes",
    async function(){
      let restrictions = await this.get( "restrictions" );
      if ( isEmpty( restrictions ) )
      {
        return true;
      }

      let allowed = ( await Promise.all( restrictions.map( getAll( "allowedTypes" ) ) ) )
                      .filter( hasItems );
      if ( isEmpty( allowed ) )
      {
        return true;
      }

      let types = await this.get( "types" );
      if ( isEmpty( types ) )
      {
        return false;
      }

      let allowedTypes = allowed.reduce( intersectionOf );
      return types.every( includedIn( allowedTypes ) );
    }
  )
});
...