在 Moralis 上存储数据是围绕 Moralis.Object
构建的。 每个 Moralis.Object
都包含 JSON 兼容数据的键值对。 此数据是无模式的,这意味着您无需提前指定每个 Moralis.Object
上存在哪些键。 您只需设置所需的任何键值对,我们的后端就会存储它们。
例如,假设您正在构建一个角色是Monster
的 NFT 游戏。 单个 Moralis.Object
可以包含:
strength
(强度):1024,ownerName
(所有者名称):“Aegon”,canFly
:true
键必须是字母数字字符串。 值可以是字符串、数字、布尔值,甚至是数组和字典——任何可以被 JSON 编码的东西。
每个 Moralis.Object
都是具有类名的特定子类的实例,您可以使用该类名来区分不同类型的数据。 例如,我们可以将该对象称为 LegendaryMonster
。
我们建议您使用 NameYourClassesLikeThis
和 nameYourKeysLikeThis
,只是为了让您的代码看起来更漂亮。
要创建新的子类,请使用 Moralis.Object.extend
方法。 任何 Moralis.Query
都将为具有相同类名的任何 Moralis.Object
返回新类的实例。 如果您熟悉 Backbone.Model
,那么您已经知道如何使用 Moralis.Object
。 它旨在以相同的方式创建和修改。
// Simple syntax to create a new subclass of Moralis.Object.
const LegendaryMonster = Moralis.Object.extend("Monster");
// Create a new instance of that class.
const monster = new LegendaryMonster();
// Alternatively, you can use the typical Backbone syntax.
const LegendaryMonster = Moralis.Object.extend({
className: "Monster",
});
您可以向 Moralis.Object
的子类添加其他方法和属性。
// A complex subclass of Moralis.Object
const Monster = Moralis.Object.extend(
"Monster",
{
// Instance methods
hasSuperHumanStrength: function () {
return this.get("strength") > 18;
},
// Instance properties go in an initialize method
initialize: function (attrs, options) {
this.sound = "Rawr";
},
},
{
// Class methods
spawn: function (strength) {
const monster = new Monster();
monster.set("strength", strength);
return monster;
},
}
);
const monster = Monster.spawn(200);
alert(monster.get("strength")); // Displays 200.
alert(monster.sound); // Displays Rawr.
要创建任何 Moralis Object
类的单个实例,您还可以直接使用 Moralis.Object
构造函数。 new Moralis.Object(className)
将创建具有该类名的单个 Moralis 对象。
如果您已经在代码库中使用 ES6。 您可以使用 extends
关键字对 Moralis.Object
进行子类化:
class Monster extends Moralis.Object {
constructor() {
// Pass the ClassName to the Moralis.Object constructor
super("Monster");
// All other initialization
this.sound = "Rawr";
}
hasSuperHumanStrength() {
return this.get("strength") > 18;
}
static spawn(strength) {
const monster = new Monster();
monster.set("strength", strength);
return monster;
}
}
但是,使用扩展时,SDK 不会自动识别您的子类。 如果您希望从查询返回的对象使用 Moralis.Object
的子类,则需要注册子类,类似于我们在其他平台上所做的。
// After specifying the Monster subclass...
Moralis.Object.registerSubclass("Monster", Monster);
同样,您可以将扩展与 Moralis.User
一起使用。
class CustomUser extends Moralis.User {
constructor(attributes) {
super(attributes);
}
doSomething() {
return 5;
}
}
Moralis.Object.registerSubclass("_User", CustomUser);
除了查询,logIn
和 signUp
将返回子类 CustomUser
。
const customUser = new CustomUser({ foo: "bar" });
customUser.setUsername("username");
customUser.setPassword("password");
customUser.signUp().then((user) => {
// user is an instance of CustomUser
user.doSomething(); // return 5
user.get("foo"); // return 'bar'
});
CustomUser.logIn
和 CustomUser.signUp
将返回子类 CustomUser
。
假设您想将上述Monster
保存到 Moralis Cloud
。 接口类似于 Backbone.Model
,包括 save
方法:
你可以使用JS
、React
、Unity
来实现
const Monster = Moralis.Object.extend("Monster");
const monster = new Monster();
monster.set("strength", 1024);
monster.set("ownerName", "Aegon");
monster.set("canFly", true);
monster.save().then(
(monster) => {
// Execute any logic that should take place after the object is saved.
alert("New object created with objectId: " + monster.id);
},
(error) => {
// Execute any logic that should take place if the save fails.
// error is a Moralis.Error with an error code and message.
alert("Failed to create new object, with error code: " + error.message);
}
);
import { useNewMoralisObject } from "react-moralis";
export default function App() {
const { save } = useNewMoralisObject("Monster");
const saveObject = async () => {
const data = {
strength: 1024,
ownerName: "Aegon",
canFly: true,
};
save(data, {
onSuccess: (monster) => {
// Execute any logic that should take place after the object is saved.
alert("New object created with objectId: " + monster.id);
},
onError: (error) => {
// Execute any logic that should take place if the save fails.
// error is a Moralis.Error with an error code and message.
alert("Failed to create new object, with error code: " + error.message);
},
});
};
return <button onClick={saveObject}>Call The Code</button>;
}
using Moralis.Platform.Objects;
using MoralisWeb3ApiSdk;
using Newtonsoft.Json;
public class Monster : MoralisObject
{
public int strength { get; set; }
public string ownerName { get; set; }
public bool canFly { get; set; }
}
public async void SaveObjectToDB()
{
try {
Monster monster = MoralisInterface.GetClient().Create<Monster>();
monster.strength = 1024;
monster.ownerName = "Aegon";
monster.canFly = true;
await character.SaveAsync();
print("Created new object");
}
catch (Exception e){
print("Failed to create new object, with error code: " + e);
}
}
这段代码运行后,您可能想知道是否真的发生了什么事。 为了确保数据已保存,您可以查看“Moralis Dashboard
”中的“Data Browser
”。 您应该看到如下内容:
objectId: "xWMyZ4YEGZ", strength: 1024, ownerName: "Aegon", canFly: true,
createdAt:"2011-06-10T18:33:42Z", updatedAt:"2011-06-10T18:33:42Z"
这里有两点需要注意:
Monster
的新类。 还有一些您不需要指定的字段是为了方便而提供的。 objectId
是每个已保存对象的唯一标识符。 createdAt
和 updatedAt
表示每个对象在云中创建和最后修改的时间。 这些字段中的每一个都由 Moralis 填写,因此在完成保存操作之前,它们不存在于 Moralis.Object
上。
如果您愿意,您可以直接在调用中设置属性以进行保存。
const Monster = Moralis.Object.extend("Monster");
const monster = new Monster();
monster
.save({
strength: 1024,
ownerName: "Aegon",
canFly: true,
})
.then(
(monster) => {
// The object was saved successfully.
},
(error) => {
// The save failed.
// error is a Moralis.Error with an error code and message.
}
);
默认情况下,您创建的类将没有权限设置——这意味着任何人都可以将数据写入类并从类中读取数据。
您可以将 Moralis.Object
添加为另一个 Moralis.Object
中的属性值。 默认情况下,当您在父对象上调用 save()
时,所有嵌套对象都将在批处理操作中创建和/或保存。 此功能使管理关系数据变得非常容易,因为您不必按照任何特定顺序创建对象。
const Child = Moralis.Object.extend("Child");
const child = new Child();
const Parent = Moralis.Object.extend("Parent");
const parent = new Parent();
parent.save({ child: child });
// Automatically the object Child is created on the server
// just before saving the Parent
在某些情况下,您可能希望阻止此默认链保存。 例如,当保存一个怪物的配置文件时,它的 owner
属性指向另一个用户拥有的帐户,而您没有写入权限。 在这种情况下,将选项 cascadeSave
设置为 false
可能很有用:
const Monster = Moralis.Object.extend("Monster");
const monster = new Monster();
monster.set("ownerAccount", ownerAccount); // Suppose `ownerAccount` has been created earlier.
monster.save(null, { cascadeSave: false });
// Will save `teamMember` wihout attempting to save or modify `ownerAccount`
您可以传递一个上下文字典,该字典可在云代码中访问该 Moralis.Object
的 beforeSave
和 afterSave
触发器。 如果您想根据不应与 Moralis.Object
一起保存在数据库中的临时信息来调整云代码触发器中的某些操作,这将非常有用。 上下文是短暂的,因为它在云代码触发特定 Moralis.Object
后消失,因为它已被执行。 例如:
const Monster = Moralis.Object.extend("Monster");
const monster = new Monster();
monster.set("species", "Dragon");
const context = { notifyMonsterGuild: true };
await monster.save(null, { context: context });
然后可以在云代码中访问上下文:
Moralis.Cloud.afterSave("Monster", async (req) => {
const notifyMonsterGuild = req.context.notifyMonsterGuild;
if (notifyMonsterGuild) {
// Notify the guild about new monster.
}
});
将数据保存到云端很有趣,但再次将这些数据取出会更有趣。
如果 Moralis.Object
已上传到服务器,您可以用 objectId
通过 Moralis.Query
检索它(可以通过JS
、React
实现):
const Monster = Moralis.Object.extend("Monster");
const query = new Moralis.Query(Monster);
//get monster with id xWMyZ4YEGZ
query.get("xWMyZ4YEGZ").then(
(monster) => {
// The object was retrieved successfully.
},
(error) => {
// The object was not retrieved successfully.
// error is a Moralis.Error with an error code and message.
}
);
import { useMoralisQuery } from "react-moralis";
export default function App() {
const { fetch } = useMoralisQuery(
"Monster",
(query) => query.equalTo("objectId", "xWMyZ4YEGZ"),
[],
{ autoFetch: false }
);
const objectIdQuery = () => {
fetch({
onSuccess: (monster) => {
// The object was retrieved successfully.
},
onError: (error) => {
// The object was not retrieved successfully.
// error is a Moralis.Error with an error code and message.
},
});
};
return <button onClick={objectIdQuery}>Call The Code</button>;
}
要从 Moralis.Object
中获取值,请使用 get
方法:
const strength = monster.get("strength");
const ownerName = monster.get("ownerName");
const canFly = monster.get("canFly");
或者,可以将 Moralis.Object
的 attributes
属性视为 JavaScript 对象,甚至可以解构。
const { strength, ownerName, canFly } = result.attributes;
四个特殊的保留值作为属性提供,不能使用“get
”方法检索,也不能使用“set
”方法修改:
const updatedAt = monster.updatedAt;
const objectId = monster.id;
const createdAt = monster.createdAt;
const acl = monster.getACL();
如果您需要使用 Moralis 云中当前的最新数据刷新已有的对象,您可以调用 fetch
方法,如下所示:
myObject.fetch().then(
(myObject) => {
// The object was refreshed successfully.
},
(error) => {
// The object was not refreshed successfully.
// error is a Moralis.Error with an error code and message.
}
);
如果你需要检查一个对象是否已经被获取,你可以调用 isDataAvailable()
方法:
if (!myObject.isDataAvailable()) {
await myObject.fetch();
}
更新对象很简单。 只需在其上设置一些新数据并调用 save
方法。 例如(可以通过JS
、React
实现):
// Create the object.
const Monster = Moralis.Object.extend("Monster");
const monster = new Monster();
monster.set("strength", 1024);
monster.set("energy", 1337);
monster.set("owner", "Aegon");
monster.set("canFly", false);
monster.set("skills", ["pwnage", "flying"]);
monster.save().then((monster) => {
// Now let's update it with some new data. In this case, only canFly and energy
// will get sent to the cloud. ownerName hasn't changed.
monster.set("canFly", true);
monster.set("energy", 1338);
return monster.save();
});
import { useNewMoralisObject } from "react-moralis";
export default function App() {
const { save } = useNewMoralisObject("Monster");
const updateObject = async () => {
const data = {
strength: 1024,
energy: 1337,
owner: "Aegon",
canFly: false,
skills: ["pwnage", "flying"],
};
save(data, {
onSuccess: (monster) => {
monster.set("canFly", true);
monster.set("strength", 1338);
return monster.save();
},
});
};
return <button onClick={updateObject}>Call The Code</button>;
}
Moralis 会自动计算出哪些数据发生了变化,因此只有dirty
字段会被发送到 Moralis 云。 您无需担心压缩您不打算更新的数据。
上面的示例包含一个常见的用例。 strength field是一个计数器,我们需要不断更新monster的最新能量。 使用上述方法是可行的,但它很麻烦,并且如果您有多个客户端尝试更新同一个计数器,可能会导致问题。
为了帮助存储计数器类型的数据,Moralis 提供了自动增加(或减少)任何数字字段的方法。 因此,相同的更新可以重写为:
monster.increment("energy");
monster.save();
您还可以通过将第二个参数传递给 increment
来增加任意数量。 未指定金额时,默认使用 1。
为了帮助存储数组数据,可以使用三种操作来自动更改与给定键关联的数组:
add
将给定对象附加到数组字段的末尾。addUnique
仅当它尚未包含在数组字段中时才添加给定对象。 不能保证插入的位置。 remove
从数组字段中删除给定对象的所有实例。monster.addUnique("skills", "flying");
monster.addUnique("skills", "kungfu");
monster.save();
请注意,目前无法在同一保存中自动从数组中添加和删除项目。 您必须在每种不同类型的数组操作之间调用 save
。
要从云中删除对象:
myObject.destroy().then(
(myObject) => {
// The object was deleted from the Moralis Cloud.
},
(error) => {
// The delete failed.
// error is a Moralis.Error with an error code and message.
}
);
您可以使用 unset
方法从对象中删除单个字段:
// After this, the ownerName field will be empty
myObject.unset("ownerName");
// Saves the field deletion to the Moralis Cloud.
// If the object's field is an array, call save() after every unset() operation.
myObject.save();
请注意,不建议使用 object.set(null)
从对象中删除字段,这会导致意外功能。
对象可能与其他对象有关系。 例如,在博客应用程序中,一个 Post
对象可能有许多 Comment
对象。 Moralis 支持各种关系:
一对一和一对多关系通过将 Moralis.Object
保存为另一个对象中的值来建模。
例如,博客应用程序中的每个评论可能对应一个帖子。
要使用单个评论创建新帖子,您可以编写(可以通过JS
、React
来实现):
// Declare the types.
const Post = Moralis.Object.extend("Post");
const Comment = Moralis.Object.extend("Comment");
// Create the post
const myPost = new Post();
myPost.set("title", "I'm Hungry");
myPost.set("content", "Where should we go for lunch?");
// Create the comment
const myComment = new Comment();
myComment.set("content", "Let's do Sushirrito.");
// Add the post as a value in the comment
myComment.set("parent", myPost);
// This will save both myPost and myComment
myComment.save();
import { useNewMoralisObject } from "react-moralis";
export default function App() {
const postObject = useNewMoralisObject("Post");
const commentObject = useNewMoralisObject("Comment");
const makePost = async () => {
const postData = {
title: "I'm Hungry",
content: "Where should we go for lunch?",
};
const commentData = {
content: "Let's do Sushirrito.",
parent: await postObject.save(postData),
};
commentObject.save(commentData, {
onSuccess: (comment) => console.log(comment),
onError: (error) => console.log(error),
});
};
return <button onClick={makePost}>Call The Code</button>;
}
在内部,Moralis 将引用对象仅存储在一个位置,以保持一致性。 您还可以使用它们的 objectId
链接对象,如下所示:
const post = new Post();
post.id = "1zEcyElZ80";
myComment.set("parent", post);
默认情况下,在获取对象时,不会获取相关的 Moralis.Object
。 这些对象的值在被获取之前无法被检索,如下所示:
const post = fetchedComment.get("parent");
await post.fetch();
const title = post.get("title");
多对多关系使用 Moralis.Relation
建模。 这类似于将 Moralis.Object
的数组存储在一个键中,只是您不需要一次获取关系中的所有对象。 此外,与 Moralis.Object
方法的数组相比,这允许 Moralis.Relation
扩展到更多的对象。
例如,用户可能有许多她可能喜欢的帖子。 在这种情况下,您可以使用关系存储用户喜欢的一组帖子。 要将帖子添加到用户的“喜欢”列表中,您可以执行以下操作:
const user = Moralis.User.current();
const relation = user.relation("likes");
relation.add(post);
user.save();
您可以从 Moralis.Relation
中删除帖子:
relation.remove(post);
user.save();
您可以在调用 save
之前多次调用 add
和 remove
:
relation.remove(post1);
relation.remove(post2);
user.save();
您还可以传入一个 Moralis.Object
数组来添加和删除:
relation.add([post1, post2, post3]);
user.save();
默认情况下,不会下载此关系中的对象列表。 您可以使用查询返回的 Moralis.Query
获取用户喜欢的帖子列表。 代码如下所示:
relation.query().find({success: function(list) {
// list contains the posts that the current user likes.
}
});
如果您只想要帖子的子集,您可以向查询返回的 Moralis.Query
添加额外的约束,如下所示:
const query = relation.query();
query.equalTo("title", "I'm Hungry");
query.find({
success: function (list) {
// list contains post liked by the current user which have the title "I'm Hungry".
},
});
到目前为止,我们已经使用了 String
、Number
和 Moralis.Object
类型的值。 Moralis 还支持 Dates
和 null
。 您可以嵌套 JSON 对象和 JSON 数组以在单个 Moralis.Object
中存储更多结构化数据。 总体而言,对象中的每个字段都允许以下类型:
String
Number
bool
JSON Array
JSON Object
Date
Moralis.File
Moralis.Object
Moralis.Relation
null
一些例子:
const number = 42;
const bool = false;
const string = "the number is " + number;
const date = new Date();
const array = [string, number];
const object = { number: number, string: string };
const pointer = MyClassName.createWithoutData(objectId);
const BigObject = Moralis.Object.extend("BigObject");
const bigObject = new BigObject();
bigObject.set("myNumber", number);
bigObject.set("myBool", bool);
bigObject.set("myString", string);
bigObject.set("myDate", date);
bigObject.set("myArray", array);
bigObject.set("myObject", object);
bigObject.set("anyKey", null); // this value can only be saved to an existing key
bigObject.set("myPointerKey", pointer); // shows up as Pointer <MyClassName> in the Data Browser
bigObject.save();
我们不建议在 Moralis.Object
上存储大量二进制数据,例如图像或文档。 我们建议您使用 Moralis.File
来存储图像、文档和其他类型的文件。 您可以通过实例化 Moralis.File
对象并将其设置在字段上来实现。