数据模型

Mirage 还允许您使用扁平数据模型文件来为您的数据库播种数据。

通常,我们建议在大多数情况下使用工厂,因为它们往往使您的模拟数据更容易维护。但当然,数据模型在某些时候是有意义的。

数据模型只不过是完成以下操作的一种传统方法

import { createServer } from "miragejs"

createServer({
  seeds(server) {
    server.db.loadData({
      countries: [
        { id: 1, name: "China" },
        { id: 2, name: "India" },
        { id: 3, name: "United States" },
      ],
    })
  },
})

让我们看看如何使用数据模型做同样的事情。

基本用法

数据模型数据可以使用 fixtures 键传递到 Mirage 服务器定义中

import { createServer } from "miragejs"

createServer({
  fixtures: {
    countries: [
      { id: 1, name: "China" },
      { id: 2, name: "India" },
      { id: 3, name: "United States" },
    ],
  },
})

除非您定义了 seeds 函数,否则这些数据将自动加载到 Mirage 的数据库中,作为其初始数据。

如果您定义了 seeds 函数,Mirage 假设您希望使用工厂来播种数据

import { createServer } from "miragejs"

createServer({
  fixtures: {
    countries: [
      { id: 1, name: "China" },
      { id: 2, name: "India" },
      { id: 3, name: "United States" },
    ],
  },

  seeds(server) {
    // Fixtures won't be loaded automatically, because this function is defined

    server.create("post")
  },
})

但是,您可以通过在默认场景中调用 server.loadFixtures 来将数据模型与工厂结合使用

import { createServer } from "miragejs"

createServer({
  fixtures: {
    countries: [
      { id: 1, name: "China" },
      { id: 2, name: "India" },
      { id: 3, name: "United States" },
    ],
  },

  seeds(server) {
    // Load all fixture data into the development db
    server.loadFixtures()

    // Also create some db data using factories
    server.create("post")
  },
})

通常,数据模型会被提取到单独的文件中。我们可以将国家数据模型数据放到自己的 fixtures/countries.js 文件中,并将其导出为数组

// mirage/fixtures/countries.js
export default [
  { id: 1, name: "China", largestCity: "Shanghai" },
  { id: 2, name: "India", largestCity: "Mumbai" },
  { id: 3, name: "United States", largestCity: "New York City" },
  { id: 4, name: "Indonesia", largestCity: "Jakarta" },
  { id: 5, name: "Pakistan", largestCity: "Karachi" },
  { id: 6, name: "Brazil", largestCity: "São Paulo" },
  { id: 7, name: "Nigeria", largestCity: "Lagos" },
  { id: 8, name: "Bangladesh", largestCity: "Dhaka" },
  { id: 9, name: "Russia", largestCity: "Moscow" },
  { id: 10, name: "Mexico", largestCity: "Mexico City" },
]

现在我们可以导入它并将其传递到服务器定义中

import { createServer } from "miragejs"
import countries from "./fixtures/countries"

createServer({
  fixtures: {
    countries,
  },
})

许多团队发现将某些种子数据存储为单独的数据模型文件很有用。

属性格式化

由于数据模型数据直接读入 Mirage 的数据库,因此对于所有多字属性都使用 camelCase 非常重要。(Mirage 使用 camelCasing 约定来避免对标识外键等事物的配置。)

如果您生产 API 格式不使用 camelCase,请不要担心。我们将在序列化器层中自定义 Mirage 的 API 格式。

loadFixtures 助手

如上所述,如果 Mirage 检测到数据模型和默认场景,它将不会自动加载数据模型数据。

要在开发期间将数据模型加载到数据库中,请在默认场景中调用 server.loadFixtures

seeds(server) {
  server.loadFixtures()
}

server.loadFixtures() 将加载所有数据模型。您可以通过将数据模型名称的实参列表传递到 loadFixtures 来选择性地加载数据模型

import { createServer, Model } from "miragejs"
import cities from "./fixtures/cities"
import countries from "./fixtures/countries"
import users from "./fixtures/users"

createServer({
  models: {
    country: Model,
    city: Model,
    user: Model,
  },

  fixtures: {
    countries: countries,
    cities: cities,
    users: users,
  },

  seeds(server) {
    // only load the countries and cities fixtures
    server.loadFixtures("countries", "cities")
  },
})

与默认场景一样,数据模型在测试期间将被忽略。如果您想在测试中加载数据模型数据,可以调用 server.loadFixtures

test("I can see the countries", async function (assert) {
  server.loadFixtures("countries")

  await visit("/")

  assert.dom("option.country").exists({ length: 100 })
})

关系

没有专门的 API 用于使用数据模型创建关系,您只需要了解 Mirage 如何使用外键来连接关系。

假设我们有以下模型

import { createServer, Model } from "miragejs"

createServer({
  models: {
    user: Model,

    post: Model.extend({
      author: belongsTo("user"),
    }),
  },
})

使用 ORM,我们可以创建两个相关的模型

let chris = schema.users.create({ name: "Chris Garrett" })

schema.posts.create({
  author: chris,
  title: "Coming Soon in Ember Octane",
})

如果我们在此之后查看 Mirage 的数据库,我们将看到以下数据

// server.db.dump()

{
  "users": [{ "id": "1", "name": "Chris Garrett" }],
  "posts": [
    { "id": "1", "authorId": "1", "title": "Coming Soon in Ember Octane" }
  ]
}

如您所见,Mirage 在 post 中添加了一个 authorId 外键。belongsTo 外键的约定是

belongsToForeignKey = `${relationshipName}Id`

在本例中,post 获取了一个 authorId,即使该关系指向 User 模型。始终使用关系名称而不是模型名称,因为模型可以具有指向同一类型模型的多个关系。

查看上面的数据库转储,如果您想仅使用数据模型重新创建相同的关系图,您的数据只需要匹配它

import { createServer, Model } from "miragejs"

createServer({
  models: {
    user: Model,

    post: Model.extend({
      author: belongsTo("user"),
    }),
  },

  fixtures: {
    users: [{ id: "1", name: "Chris Garrett" }],
    posts: [{ id: "1", authorId: "1", title: "Coming Soon in Ember Octane" }],
  },
})

一旦这些数据被加载到 Mirage 中,所有 ORM 方法、快捷方式和序列化器将按预期工作。

如果这是一个双向关系

  models: {
    user: Model.extend({
+     posts: hasMany()
    }),

    post: Model.extend({
      author: belongsTo("user"),
    }),
  },

那么 Mirage 将为新的 hasMany 关联添加一组外键

// server.db.dump()

{
  "users": [{ "id": "1", "name": "Chris Garrett", "postIds": ["1"] }],
  "posts": [
    { "id": "1", "authorId": "1", "title": "Coming Soon in Ember Octane" }
  ]
}

hasMany 关系外键的约定是

hasManyForeignKey = `${singularize(relationshipName)}Ids`

所有关联都有自己的键,因为 Mirage 支持任意单向关系。如果两个关联是彼此的倒数,如上例所示,Mirage 将在使用 ORM 方法的情况下保持每个模型上的键同步。

如您所见,维护外键并使其在 fixture 文件中保持同步可能会变得有点混乱,这就是为什么 Mirage 建议您在大多数数据创建中使用工厂的原因。

尽管如此,fixture 在某些情况下仍然非常有用,因此它们是您工具箱中的一款好工具。


接下来,我们将通过学习序列化器来结束本节指南,序列化器允许我们自定义 Mirage 在将数据发回以响应我们的应用程序之前如何格式化数据。