Как мне объявить расширение Siblings для модели Fluent в Vapor 3, где базовый и связанные типы совпадают? - PullRequest
0 голосов
/ 12 января 2019

У меня есть тип данных, например, папка, и папки могут быть произвольно вложены друг в друга - отношение «многие ко многим». Чтобы поддержать это, я построил сводную таблицу (я использую MySQL), относящуюся от parent_folder_id к child_folder_id (внешние ключи в таблице folders). В Fluent я смоделировал это как FolderToSubfoldersPivot так же, как и мои другие классы сводной таблицы.

Теперь я хочу расширить свой тип Folder, чтобы он имел атрибут subfolders, используя Siblings . Код выглядит так:

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

Это похоже на то, как я написал Siblings -типированные атрибуты для других пользовательских типов, которые все работают. Однако для этого случая Xcode выдает следующую ошибку:

Неоднозначное использование «братьев и сестер (связанных: через:)»

Я предполагаю, что это из-за двух использований типа Folder в объявлении типа для Siblings. Я попытался обойти это несколькими способами (псевдонимы типов, явный вызов метода siblings(related:through:) с параметрами и т. Д.) Безрезультатно.

Как мне заставить функционал siblings() работать правильно? Или мне нужно будет заново реализовать структуру Siblings с нуля, чтобы делать то, что я хочу?

Ответы [ 2 ]

0 голосов
/ 31 января 2019

Пирс Дарра, ваш самоотверженный ответ в порядке, и это вполне законное личное предпочтение использовать маркировку children и parent. Другой вариант - просто привести имена свойств в соответствие с тем, как вы хотите думать о вещах (основной точкой по-прежнему является изменение ключевых позиций).

Например, <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)
    }
}

без каких-либо дополнительных расширений. Существует, я полагаю, 4 варианта siblings(), которые можно использовать непосредственно в return.

0 голосов
/ 13 января 2019

Поработав немного над этим, я нашел ответ.

Реализацию удобных функций Fluent siblings можно найти в этом выделенном сегменте на 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 - это правый тип оси, а Related - левый тип. Аналогично, вторая функция используется, когда имеет место обратное.

Поскольку я использовал тип Siblings<X, X, XPivot>, Свифт не смог определить, какая функция лучше, так как условия были выполнены для каждой.

Чтобы исправить это, я реализовал собственное расширение:

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.leftIDKey и Through.rightIDKey переключаются во второй функции. Это связано с тем, как я структурировал сводную таблицу (т. Е. Левый столбец равен parent_folder_id, а правый столбец - child_folder_id).

Использование этих функций аналогично использованию обычных функций siblings. В моем случае в вопросе, где я отношу Folder типов к другим Folder s:

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

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