パート 6 – 関係

このアプリには、もう1つ機能があります。それは「リスト」です。リマインダーパネルの左側にあるボタンをクリックすると、サイドパネルが開き、このアプリのさまざまなリストが表示されます。

Failing

しかし、今のところ、アプリが/api/listsからリストをフェッチすることを期待しているため、コンソールにエラーが表示されるだけです。このエンドポイントをモックアウトしてみましょう。

まず、サーバーの先頭に新しいlistモデルを定義します。

models: {
  list: Model,
  reminder: Model,
}

/api/listsへのGET用の新しいルートハンドラーを追加し、すべてのリストを返します。

this.get("/api/lists", (schema, request) => {
  return schema.lists.all()
})

Mirageは200を返しますが、リストはありません。seeds()でいくつか作成しましょう。

seeds(server) {
  server.create("reminder", { text: "Walk the dog" });
  server.create("reminder", { text: "Take out the trash" });
  server.create("reminder", { text: "Work out" });

  server.create("list", { name: "Home" });
  server.create("list", { name: "Work" });
}

これで、サイドバーを開くと、「ホーム」と「仕事」の2つの新しいリストが表示されます。

リストをクリックしてみてください。アプリが別のAPIエンドポイントが存在することを期待しているため、エラーが表示されます。/api/lists/1/remindersです。これは、特定のリストのリマインダーをフェッチする方法です。モックアウトする方法を見てみましょう。

Mirageでリレーショナルデータを扱うエンドポイントをモックアウトする最も簡単な方法は、アソシエーションを使用することです。まず、Mirageのアソシエーションヘルパーを使用して、このデータをモデル化します。

このアプリでは、リストとリマインダーの間には1対多の関係(リストには多くのリマインダーを含めることができる)があるため、この関係を定義するためにhasManyヘルパーとbelongsToヘルパーを使用します。

ヘルパーをインポートし、この関係を定義するためにモデルを更新します。

import { createServer, Model, hasMany, belongsTo } from "miragejs"

export default function () {
  createServer({
    models: {
      list: Model.extend({
        reminders: hasMany(),
      }),

      reminder: Model.extend({
        list: belongsTo(),
      }),
    },

    // rest of server
  })
}

これでMirageは、list.remindersプロパティとreminder.listプロパティが関係であることを認識しているため、ルートハンドラー内で使用を開始できます。

新しい関係を使用して、/api/lists/:id/remindersをモックアウトしてみましょう。このエンドポイントは、対応するリストを検索し、そのリストに属するすべてのリマインダーを応答する必要があります。

これがコードです

this.get("/api/lists/:id/reminders", (schema, request) => {
  let listId = request.params.id
  let list = schema.lists.find(listId)

  return list.reminders
})

クリックしたリストに対応する:idの動的なセグメントを使用します。次に、そのIDを使用して、Mirageのデータレイヤーで対応するリストを検索します。最後に、そのリストに関連付けられているリマインダーを返します。

このルートハンドラーをMirageサーバー定義にコピーし、各リストをクリックしてみてください。もうエラーは表示されないはずです。しかし、リマインダーも表示されません。これは、これまで作成したリマインダーがどのリストにも関連付けられていないためです。リマインダーは単独で作成しただけです。

特定リストのリマインダーを作成するには、新しい関係を使用できます。

seeds(server) {
  // Unassociated reminders
  server.create("reminder", { text: "Walk the dog" });
  server.create("reminder", { text: "Take out the trash" });
  server.create("reminder", { text: "Work out" });

  let homeList = server.create("list", { name: "Home" });
  server.create("reminder", { list: homeList, text: "Do taxes" });

  let workList = server.create("list", { name: "Work" });
  server.create("reminder", { list: workList, text: "Visit bank" });
}

これで、「ホーム」をクリックすると、「税金を行う」リマインダーが表示されるはずです。また、「すべて」をクリックすると、アプリは/api/remindersエンドポイントにアクセスし、リストに関連付けられているものとそうでないものの両方を含む、システム内のすべてのリマインダーを返します。

最後に、UIで特定リストの新しいリマインダーを作成してみてください。「仕事」をクリックし、「ジルに返信する」リマインダーを追加します。アプリをクリックすると、それが仕事リストに適切に関連付けられていることがわかります。(仕事とすべてに表示されますが、ホームには表示されません)。

これは、UIがリストにある間にリマインダーを作成するときに{ listId }をすでに送信しているためです。これを確認するには、既存のPOSTルートハンドラーにログを追加して、リクエストからの属性を検査しましょう。

this.post("/api/reminders", (schema, request) => {
  let attrs = JSON.parse(request.requestBody)
  console.log(attrs)

  return schema.reminders.create(attrs)
})

関連付けられていないtodoを作成する場合、属性は{ text: "新しいtodo" }ですが、関連付けられたtodoには、{ text: "ジルに返信する", listId: "2" }のような属性があります。

関係を定義したとき、Mirageは外部キーとして知られる特別な属性をモデルに設定しました。これらは、どのモデルが互いに関連付けられているかを追跡する方法です。listIdreminderIds: []のような外部キーを設定するだけで、関係を更新し、すべてのMirageルートハンドラーに反映させることができます。

したがって、listIdreminders.create()に既に渡されているため、UIのリスト機能に対応するためにPOSTハンドラーを更新する必要はありません。

Mirageのアソシエーションヘルパーは、ほぼすべてのデータシナリオをモデル化できる柔軟性を備えており、関係を扱うエンドポイントをモックアウトする際に作業が簡単になります。

まとめ

  • Mirageを使用すると、リレーショナルデータを定義およびアクセスできます。
  • belongsToヘルパーとhasManyヘルパーを使用して、モデルに名前付きの関係を定義します。
  • ルートハンドラーで関係を使用して、他のモデルに関連付けられているモデルまたはコレクションを返します。
  • Mirageは、単純な外部キーを使用して、リレーショナルデータを追跡します。