如何优雅地查询记录并在记录不存在时创建它?

How to elegantly query for a record and create it if it does not exist yet?

目标

startChat(partner_profile) 的目标是获取两个用户之间的聊天 ID,然后能够重定向到此聊天。 有两种不同的情况[​​=46=]:

  1. 他们之间的聊天已经存在

    a) partner_profile 是第一个参与者

    b) partner_profile 是第二个参与者

  2. 需要先创建他们之间的聊天

到目前为止我得到了什么

我知道如何为上面列出的每个案例获取 ID,但我不知道如何将它们全部组合起来。到目前为止,这是我的代码:

 startChat(partner_profile) {
      // case 1a
      this.get('store').queryRecord('chat', {
              first_participant: partner_profile.id
       }).then(function(chat) {
              let id = chat.get('id');
              onSaveSuccess(id);
       }).catch(function(){
      });

      // case 1b
     this.get('store').queryRecord('chat', {
             second_participant: partner_profile.id
     }).then(function(chat) {
            let id = chat.get('id');
            onSaveSuccess(id);
            return;
     }).catch(function(){
         // **error handling* 
    });

    // case 2
    let chat = this.get('store').createRecord('chat', {
        second_participant: partner_profile
    });

    let onSaveSuccess = (id) => this.transitionToRoute('chats.chat',id);

    chat.save()
        .then(function(success) {
            let id = success.get('id');
            onSaveSuccess(id);
         })
        .catch((error) => {
           // **error handling*
      }
    });

如何合并这些案例?

现在真的很难看,因为每个案例都被执行了,当然有两个失败了。我怎样才能以更好的方式做到这一点?有没有办法一次 get_or_create 一条记录(就像在 Django 中那样)? 感谢您的帮助:-)

编辑:更具体

更具体地说,我很难找到一种好方法来检查聊天是否已存在于我们的数据库中。看这个例子:

   let existingChat =  this.get('store').queryRecord('chat', {
      first_participant: partner_profile.id
  }).catch(function(){

   });
  if(!existingChat){
    // ** check for case 1b and 2
  };

在这个例子中,我首先查询商店的聊天。存储 returns 我在 existingChat 中保存的 Promise,当我想检查是否已存在与 if(!existingChat) 的聊天时,它尚未解决。

我不知道我是否有足够的代码,但这是我过去处理 get_or_create 的方式。

var database = [];

function getOrCreate(rowId) {
  var rowIndex = database.findIndex(r => rowId === r.Id);  
  if (rowIndex < 0) {
    database.push({
      Id: rowId
    });
    return database[database.length - 1];
  } else {
    return database[rowIndex];
  }
}

var item1 = getOrCreate(1);
var item2 = getOrCreate(2);
var item3 = getOrCreate(3);
var item4 = getOrCreate(1);

console.log(item4);

所以最好的方法是先查询该聊天,然后在找不到聊天时处理创建聊天(404):

import Route from '@ember/routing/route';

export default Route.extend({
  // params is { first_participant, second_participant }
  model(params) {
    return this.store.queryRecord('chat', params)
      .catch(() => {
        // a 404 from the server would trigger a catch
        return this.store.createRecord('chat', params);
      });
  }
});

基本上就是这个概念,但如果您需要创建两个聊天,它可能会涉及更多。在这种情况下,您可以使用 'rsvp'.

中的 allhash 助手
import Route from '@ember/routing/route';
import { hash } from 'rsvp';

export default Route.extend({
  // params is { first_participant, second_participant }
  model(params) {
    return this.store.queryRecord('chat', params)
      .catch(() => {
        // a 404 from the server would trigger a catch
        return hash({
          chatA: this.store.createRecord('chat', { first_participant: params.first_participant }),
          chatB: this.store.createRecord('chat', { second_participant: params.second_participant })
        });
      });
  }
});

以上会将您的 model 设置为 { chatA, chatB }。您需要对 promise 执行的操作的复杂性还取决于您的后端 API 的复杂程度。有时这是一种气味,让您知道您的 API 不是最好的。


另一种更优雅的解决方案是使用async/await。 查看本指南:https://spin.atomicobject.com/2016/12/29/async-await-ember-project/

Async/await 已准备好迎接 IMO 的黄金时段。上面的例子看起来像这样:

import Route from '@ember/routing/route';

export default Route.extend({
  // params is { first_participant, second_participant }
  async model(params) {
    let chat;

    try {
      chat = await this.store.queryRecord('chat', params);
    } catch(e) {
      // a 404 from the server would trigger a catch
      chat = await this.store.createRecord('chat', params);
    }

    return chat;
  }
});

这允许您以同步的方式编写异步代码,使其更易于编写和理解。

要开始使用上面的内容,您需要像上面的博客 post 指定的那样修改 ember-cli-build.js 文件,即

let app = new EmberApp(defaults, {
  // Add options here
  babel: {
    includePolyfill: true
  }
});

有关详细信息,我推荐这篇 in-depth 文章 http://rwjblue.com/2017/10/30/async-await-configuration-adventure/

这是根据@knownasilya 的建议重新组织的代码:

getChatFirstParticipant().then(transitionToChat)
    .catch(() => {
      getChatSecondParticipant().then(transitionToChat)
         .catch(() => {
           createChat(partner_profile).then(transitionToChat)
             .catch((error) => {
               // ** error handling** 
             });
     });