ios – 如何使用FireBase实现用户反馈?

我有一个应用程序,用户可以喜欢照片,评论等.像Instagram这样的功能.

我想实现用户!反馈!,用户可以看到信息,谁喜欢他的照片,谁开始关注等等.我实际上不知道在这种情况下我应该如何组织数据库的结构.

我的用户节点快照:

我的帖子节点快照:

我可以看到,我有下一个选项 – 我应该将所有与用户相关联的操作保存到内部节点反馈中的节点.但是我怎样才能保持同步呢?例如,有人可以关注我的用户,我会将其添加到此节点,用户将取消关注,但记录仍然存在.我认为,这是错误的方式.

我实际上没有其他想法,我找不到任何相关的东西.

任何建议和解决方案都非常感谢.

编辑:我需要了解,如何实现这个类似Instagram的应用程序选项卡:

如何从节点中检索数据?

UPD:我的例子中的数据库架构很糟糕(旧问题).小心(10.11.2017).

解决方法

首先,让我们考虑一下如何为此构建数据库

在为Firebase构建数据时,要遵循两个非常重要的原则:

>您应该以您想要的方式保存数据.
>您应该尽可能保持数据结构平坦 – 避免嵌套.

第1点是因为Firebase不是关系数据库.这意味着我们需要保持查询简单以实现性能.进行复杂查询可能需要向Firebase发出许多请求.

第2点是because of the way Firebase’s query model works:如果您观察一个节点,您也会获得该节点的所有子节点.这意味着,如果您的数据是深度嵌套的,那么您可能会获得许多不需要的数据.

因此,考虑到这些原则,让我们来看看你的情况.我们有用户,他们有照片.这些是数据库的两个主要实体.

我可以看到,目前,您将照片保留为用户属性.如果您希望能够快速查询用户的照片(请记住第1点),这是一个很好的方法.但是,如果我们希望用户能够“喜欢”照片,那么照片应该不仅仅是指向其Firebase存储位置的链接:它还应该包含其他属性,例如哪些用户已经将其收藏.此属性应为用户ID数组.此外,对于每个用户,您都希望存储哪些照片是该用户的收藏夹.这可能看起来像数据重复,但在使用Firebase时,it’s OK to duplicate some data if it’ll lead to simpler queries.

因此,使用上面示例中的数据索引,您的每个照片应如下所示:

{
  id: /* some ID */,location: /* Firebase Storage URL */,favorited_by: {
    /* some user ID */: true /* this value doesn't matter */,/* another user ID */: true,},/* other properties... */
}

您的用户应该有一个列出照片ID的收藏夹属性.现在,由于每张照片都有一个“拥有”它的用户,我们不需要为每张照片都有唯一的ID,我们只需要确保没有用户有两张具有相同ID的照片.这样,我们可以通过其用户ID和照片ID的组合来参考照片.

当然,请记住第1点:如果您希望能够在不获取用户照片的情况下获取用户信息,则应在根对象上为照片添加不同的属性,而不是将照片与用户关联.但是,对于这个答案,我会尽力坚持你现在的模型.

根据我上面所说的,用户的收藏夹属性将包含格式为’userId / photoId’的值数组.因此,例如,如果用户喜欢具有ID“CN7v0A2”的用户的ID为“3A”的照片,则他们的收藏夹阵列将保持值“CN7v0A2 / 3A”.这结束了我们的收藏结构.

现在,让我们看看你提到的一些操作在这种结构下会是什么样子:

>用户收藏照片:

>我们获取照片所有者的用户ID
>我们获得了偏好照片的用户用户ID
>我们得到照片的ID
>我们将喜欢ID的用户添加到照片的favorited_by数组中
>我们将photoOwnerID“/”photoID添加到收藏用户的收藏夹阵列中

如果用户稍后不喜欢这张照片,我们就是反过来:我们从用户的收藏中删除了photoOwnerID“/”photoID,我们从照片的favorited_by属性删除了收藏用户的ID.

这种逻辑足以实现喜欢,收藏和跟随. follower / liker / favoriter和followee / likee / favoritee都应该保持对另一方ID的引用,你应该封装“喜欢/喜欢/关注”和“不喜欢/喜欢/取消关注”操作,以便他们保留该数据库每次状态都是一致的(这样,您就不会遇到任何问题,例如您提到的情况,用户取消关注用户数据库仍保留“跟踪”记录).

最后,假设您有一个User模型类,这里有一些关于如何进行“收藏”和“不喜欢”操作的代码

extension User {
    func follow(_ otherUser: User) {
        let ref = FIRDatabase.database().reference()
        ref.child("users/\(otherUser.userId)/followers/")
           .child(self.userId).setValue(true)
        ref.child("user/\(self.userId)/following/")
           .child(otherUser.userId).setValue(true)
    }

    func unfollow(_ otherUser: User) {
        let ref = FIRDatabase.database().reference()
        ref.child("users/\(otherUser.userId)/followers/")
           .child(self.userId).remove()
        ref.child("user/\(self.userId)/following/")
           .child(otherUser.userId).remove()
    }
}

使用此模型,您可以获取查询用户的关注者属性并在生成的快照上使用.keys()方法用户的所有关注者用户ID,反之对于给定用户关注的用户.

添加内容:我们可以进一步构建此结构,以便添加简单的操作日志记录,这似乎是您希望用户在“反馈”选项卡中可用的内容.假设我们有一系列操作,例如喜欢,收藏和关注,我们希望显示反馈.

我们将再次遵循第1点:为了构建反馈数据,最好以我们想要检索它的方式存储这些数据.在这种情况下,我们通常会向用户显示他们自己的反馈数据.这意味着我们应该按用户ID存储反馈数据.此外,在第2点之后,我们应该将反馈数据存储为自己的表,而不是将其添加用户记录中.所以我们应该在我们的根对象上创建一个新表,对于每个用户ID,我们存储一个反馈条目列表.

它应该看起来像这样:

{
  Feedback: {
    userId1: /* this would be an actual user ID */ {
      autoId1: /* generated using Firebase childByAutoId */ {
        type: 'follow',from: /* follower ID */,timestamp: /* Unix time */,autoId2: {
        type: 'favorite',from: /* ID of the user who favorited the photo */
        on: /* photo ID */
        timestamp: /* Unix time */
      },/* ...other Feedback items */
    },userId2: { /* ...Feedback items for other user */ },/* ...other user's entries */
  },/* other top-level tables */
}

另外,我们需要更改收藏夹/喜欢/关注表.之前,我们只是存储了真实信号,以表示有人喜欢或喜欢照片或跟随用户.但由于我们使用的价值无关紧要,因为我们只检查密钥以找到用户喜欢或喜欢的内容以及他们关注的对象,我们可以开始使用类似/喜欢/关注的条目ID.所以我们会改变我们的“跟随”逻辑:

extension User {
    func makeFollowFeedbackEntry() -> [String: Any] {
        return [
            "type": "follow","from": self.userId,"timestamp": UInt64(Date().timeIntervalSince1970)
        ] 
    }

    func follow(_ otherUser: User) {
        let otherId = otherUser.userId
        let ref = FIRDatabase.database().reference()
        let FeedbackRef = ref.child("Feedback/\(otherId)").childByAutoId()
        let FeedbackEntry = makeFollowFeedbackEntry(for: otherId) 

        FeedbackRef.setValue(FeedbackEntry)
        FeedbackRef.setPriority(UInt64.max - FeedbackEntry["timestamp"])

        let FeedbackKey = FeedbackRef.key

        ref.child("users/\(otherUser.userId)/followers/")
           .child(self.userId).setValue(FeedbackKey)
        ref.child("user/\(self.userId)/following/")
           .child(otherUser.userId).setValue(FeedbackKey)
    }

    func unfollow(_ otherUser: User,completionHandler: () -> ()) {
        let ref = FIRDatabase.database().reference()
        let followerRef = ref.child("users/\(otherUser.userId)/followers/")
           .child(self.userId)
        let followingRef = ref.child("user/\(self.userId)/following/")
           .child(otherUser.userId)

        followerRef.observeSingleEvent(of: .value,with: { snapshot in
            if let followFeedbackKey = snapshot.value! as? String {
                // we have an associated follow entry,delete it
                ref.child("Feedback").child(otherUser.userId + "/" + followFeedbackKey).remove()
            } // if the key wasn't a string,there is no follow entry
            followerRef.remove()
            followingRef.remove()
            completionHandler()
        })
    }
}

这样,我们只需通过阅读带有该用户ID的“反馈”表条目就可以获得用户的“反馈”,并且由于我们使用了setPriority,它将首先按最近的条目排序,这意味着我们可以使用Firebase的queryLimited(toFirst) :)只获得最新的反馈.当用户取消关注时,我们可以轻松删除反馈条目,该反馈条目通知用户他们已被跟踪.您还可以轻松添加额外的字段来存储是否已读取反馈条目等.

即使您之前使用其他模型(将“followerId”设置为true),您仍然可以对新条目使用反馈条目,只需检查值为“followerId”是否为字符串,如上所述:)

您可以使用相同的逻辑,只需使用条目中的不同字段来处理收藏夹和喜欢.当您处理它以向用户显示数据时,只需检查“类型”字段中的字符串以了解要显示的反馈类型.最后,应该很容易为每个反馈条目添加额外的字段,以便存储,用户是否已经看到反馈.

相关文章

背景 前端时间产品经理决定使用百度统计,使得 工程B 中原统计sdk-友盟统计,需要被去除。之前尝试去除...
结论: alloc负责分配内存和创建对象对应的isa指针; init只是返回alloc生成的对象。 所以alloc后,多次...
更新 如果UI愿意把启动图切割成n份,按一定约束在launchscreen.storyboard中进行排版,启动图效果会更好...
最近在看一本书《Effective OC 2.0》,今天看到有个tip是OC适中循环各自优劣性,作者最终推荐此块循环。...
// // ViewController.m // paintCodeTestOC //gif // Created by LongMa on 2019/7/25. // #import &a...
背景介绍 一般情况下,出于省电、权限、合理性等因素考虑,给人的感觉是很多奇怪的需求安卓可以实现,但...