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

时间:2019-01-12 06:01:25

标签: swift vapor

我有一种数据类型,例如文件夹,并且文件夹可以任意嵌套在彼此之间—多对多关系。为此,我建立了一个枢轴表(我正在使用MySQL)将parent_folder_idchild_folder_idfolders表上的外键)相关联。在Fluent中,我将其建模为FolderToSubfoldersPivot的方式与我的其他透视表类相同。

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

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

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

  

“兄弟姐妹(相关:通过:)”的歧义用法

我认为这是由于Folder的类型声明中Siblings类型的两种用法。我尝试了几种解决方法(类型别名,对带有参数的siblings(related:through:)方法的显式调用等)无济于事。

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

2 个答案:

答案 0 :(得分:1)

摆弄了一段时间之后,我找到了答案。

可以在this highlighted segment on GitHub中找到Fluent的siblings便利功能的实现。为了方便讨论,我在下面复制了它:

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是枢轴的右手类型,而Related是左手类型时,使用上述代码段中的第一个函数。同样,当情况相反时,使用第二个功能。

当我使用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来表示您正在寻找当前类型的孩子(也属于同一类型),而parentSiblings则是在寻找当前类型的孩子(即相同类型)。它们之间的区别在于对siblings的内部调用,其中Through.leftIDKeyThrough.rightIDKey在第二个函数中进行了切换。这是因为我如何构造数据透视表(即,左侧列为parent_folder_id,右侧列为child_folder_id)。

这些功能的用法与常规siblings函数的用法相似。就我在问题中的情况而言,我将Folder类型与其他Folder的类型相关联:

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

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

答案 1 :(得分:1)

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中使用。