如何在基本类型和相关类型相同的 Vapor 3 中的 Fluent 模型上声明 Siblings 扩展?

How do I declare a Siblings extension on a Fluent model in Vapor 3 where the base and related types are the same?

我有一种数据类型,例如文件夹,文件夹可以任意嵌套在彼此之间 — 多对​​多关系。为了支持这一点,我构建了一个枢轴 table(我正在使用 MySQL)将 parent_folder_idchild_folder_id 相关联(folders [= 上的外键37=]).在 Fluent 中,我将其建模为 FolderToSubfoldersPivot,其方式与我的其他枢轴 类.

相同

我现在想使用 Siblings 扩展我的 Folder 类型以具有 subfolders 属性。代码如下:

extension Folder {
    var subFolders: Siblings<Folder, Folder, FoldersToSubfoldersPivot> {
        return siblings()
    }
}

这类似于我为其他自定义类型编写 Siblings 类型的属性的方式,它们都有效。但是,对于这种情况 Xcode 给出以下错误:

Ambiguous use of 'siblings(related:through:)'

我认为这是因为 Siblings 的类型声明中 Folder 类型的两次使用。我已经尝试通过几种方式解决这个问题(类型别名,显式调用带参数的 siblings(related:through:) 方法等)无济于事。

如何让 siblings() 功能正常工作?还是我需要从头开始重新实现 Siblings struct 才能做我想做的事?

经过一段时间的摆弄,我找到了答案。

Fluent 的 siblings 便利功能的实现可以在 this highlighted segment on GitHub 中找到。为了讨论的清晰,我将其复制如下:

extension Model {
    ...

    /// Free implementation where pivot constraints are met.
    /// See `Model.siblings(_:_:)`.
    public func siblings<Related, Through>(
        related: Related.Type = Related.self,
        through: Through.Type = Through.self
    ) -> Siblings<Self, Related, Through>
        where Through.Right == Self, Through.Left == Related
    {
        return siblings(Through.rightIDKey, Through.leftIDKey)
    }

    /// Free implementation where pivot constraints are met.
    /// See `Model.siblings(_:_:)`.
    public func siblings<Related, Through>(
        related: Related.Type = Related.self,
        through: Through.Type = Through.self
    ) -> Siblings<Self, Related, Through>
        where Through.Left == Self, Through.Right == Related
    {
        return siblings(Through.leftIDKey, Through.rightIDKey)
    }
}

我认为问题是我想要的用法不明确。当 Self 是枢轴的 right-hand 类型并且 Related 是 left-hand 类型时,使用上面代码片段中的第一个函数。同理,反之则用第二个函数

由于我使用的是 Siblings<X, X, XPivot> 类型,Swift 无法确定哪个函数更好,因为每个函数都满足条件。

为了解决这个问题,我实现了自己的扩展:

extension Model {
    public func childrenSiblings<Through>(
        through: Through.Type = Through.self
    ) -> Siblings<Self, Self, Through>
        where Through.Left == Self, Through.Right == Self
    {
        return siblings(Through.leftIDKey, Through.rightIDKey)
    }

    public func parentSiblings<Through>(
        through: Through.Type = Through.self
    ) -> Siblings<Self, Self, Through>
        where Through.Left == Self, Through.Right == Self
    {
        return siblings(Through.rightIDKey, Through.leftIDKey)
    }
}

我用childrenSiblings来表示你在寻找当前类型的children(也是同一类型),用parentSiblings来寻找parents 当前类型(属于同一类型)。它们之间的区别在于对 siblings 的内部调用,其中 Through.leftIDKeyThrough.rightIDKey 在第二个函数中切换。这是因为我如何构建枢轴 table(即 left-hand 列是 parent_folder_id,right-hand 列是 child_folder_id)。

这些函数的用法与常规siblings函数的用法类似。在我的问题中,我将 Folder 类型与其他 Folders:

相关联
extension Folder {
    var subFolders: Siblings<Folder, Folder, FoldersToSubfoldersPivot> {
        return childrenSiblings()
    }

    var superFolders: Siblings<Folder, Folder, FoldersToSubfoldersPivot> {
        return parentSiblings()
    }
}

Pierce Darragh,您的自我回答非常好,使用 childrenparent 标签完全符合个人偏好。另一种选择是简单地使 属性 名称与您对事物的思考方式保持一致(主要点仍然是关键位置的反转)。

例如,<User, User, FollowsPivot> 社交媒体类型关系可以表示为:

extension User {
    var following: Siblings<User, User, FollowsPivot> {
        return siblings(FollowsPivot.leftIDKey, FollowsPivot.rightIDKey)
    }

    var followers: Siblings<User, User, FollowsPivot> {
        return siblings(FollowsPivot.rightIDKey, FollowsPivot.leftIDKey)
    }
}

没有任何额外的扩展。我相信 siblings() 有 4 个变体,可以直接在 return.

中使用