【问题标题】:Creating a URL Alias or Making Deep-Urls Pretty创建 URL 别名或使深层 URL 更漂亮
【发布时间】:2014-01-26 13:31:02
【问题描述】:

我有相当深的带有 ID 的网址,我想看看我是否可以将它们转换成更好看的东西。我尝试研究如何为 Yesod 博客 (https://github.com/yesodweb/yesod/wiki/Slugs) 完成 Slugs,但不确定我是否知道如何将其翻译成我在这里寻找的内容。

假设我想显示热门小说书籍,我有一个如下所示的资源:

/topbooks/bookcategory/#BookCategoryId

如果我去/topbooks/bookcategory/1,我可能会得到小说类书籍,如果我去/topbooks/bookcategory/2,我可能会得到非小说类书籍等。

我的所有处理程序都在数据库查询中使用#BookCategoryId 输入参数来获取适当的记录。

理想情况下,我想创建一个如下所示的网址:/topbooks/fiction/topbooks/non-fictionetc。如果我将路由创建为/topbooks/#Text,我可以对字符串进行模式匹配并返回Key。但是,我必须使用#BookCategoryId 在每个处理程序中手动转换它。请注意,ID 用作外键,因此依赖 getBy 有点麻烦,就像在 Slug 示例中所做的那样。

所以我想知道是否有更好的方法来做到这一点:是否可以定义一个类似于Slug 的自定义类型,但不仅仅是在文本/字符串之间转换值,而是实际输出IDs?这样我就可以直接在我的查询中使用该参数。

更新:

鉴于迈克尔的评论澄清:

我知道如果不进行数据库查找,我们将无法获取 ID。事实上,对于这个例子,我可以对查找机制进行硬编码。我只是想看看PathPiece 机制是否会以某种方式简化转换过程。

例如,如果这样的事情有效,那么它会很好,但我当然会得到一个类型错误,因为当编译器期望 BookCategories 时我试图返回一个 Key。

 data BookCategories = FICTION | NONFICTION 

 instance PathPiece BookCategories where
    toPathPiece (FICTION)    = T.pack "fiction" 
    toPathPiece (NONFICTION) = T.pack "nonfiction"

    fromPathPiece s =
       let ups = map toUpper $ T.unpack s
       in  
        case reads ups of
         [(FICTION, "")]    -> Just $ Key $ PersistInt64 1
         [(NONFICTION, "")] -> Just $ Key $ PersistInt64 
         [] -> Nothing
         otherwise -> Nothing

当然,我可以直接返回 Just FICTION 并在我的处理程序中打开它。这在概念上与直接使用带有签名Text -> BookCategoryId 的函数对 Text 进行实际模式匹配并没有太大区别。

getBookCategoryR :: BookCategoryId -> Handler Html
getBookCategoryR bcId = do
     -- Normal use case when IDs are used in the URL
     books <- runDB $ selectList [ModelBookCategory ==. bcId] []

如果我切换到文本输入

getBookCategoryR :: Text -> Handler Html
getBookCategoryR bc = do
     bcId = convertToId (bc) -- This is the line I am trying to avoid everywhere 
     books <- runDB $ selectList [ModelBookCategory ==. bcId] []

单行转换代码是我试图避免的。 PathPiece 已经很好地处理了基于 id 的 URL 并保持代码干净。如果有办法通过一些 Type 魔法返回 Id,那就太好了。由于对 Haskell 的了解有限,我不知道它是否可行。

希望我的问题现在更清楚了。

谢谢!

【问题讨论】:

    标签: haskell yesod


    【解决方案1】:

    不,没有这样的方法可以做到这一点,原因很简单:不咨询数据库,就无法知道foo 是否作为 slug 存在,如果存在,它与哪个 ID 相关.您总是需要执行一些数据库操作才能将 slug 转换为 ID。


    更新我仍然不确定我是否理解您在寻找什么,但关于 PathPiece 的简短回答是它仅适用于 转换,没有有副作用。如果你想写一个像Text -&gt; Handler BookCategoryId 这样的函数,你当然可以这样做。如果你真的想要,你甚至可以用一个类型类来抽象它,虽然我不确定你是否会有所收获。

    这可能是错误的树,但这里有一个简短的想法可能会启发你一点:你可以为每个文本 slug 字段创建不同的 newtype 包装器,然后创建一个类型类来将文本 slug 字段转换为适当的实体,例如:

    newtype BookCatSlug = BookCatSlug Text
        deriving PathPiece
    
    BookCategory
        slug BookCatSlug
        title Text
        ...
        UniqueBookCat slug
    
    class Slug slug where
        type SlugEntity slug
        lookupSlug :: slug -> YesodDB App (Maybe (Entity (SlugEntity slug)))
    
    instance Slug BookCatSlug where
        type SlugEntity BookCatSlug = BookCategory
        lookupSlug = getBy . UniqueBookCat
    
    lookupSlug404 slug = runDB (lookupSlug slug) >>= maybe notFound return
    
    myHandler slug = do
        Entity bookCatId bookCat <- lookupSlug404 slug
    

    这些方面的东西应该可以工作,但我不确定“类型魔法”是否值得,因为拥有一个辅助函数并手动传入适当的 Unique 构造函数对于调用站点来说几乎同样容易,并导致更简单的错误消息。

    【讨论】:

    • 谢谢,@michael。我已经通过一些澄清更新了我的问题。我完全明白我无法摆脱数据库查找。我想看看是否有一种方法可以构建方便的函数来在后台进行转换,就像 PathPiece 对基于 Id 的 URL 所做的那样。
    • 我已经扩展了我的答案。
    • 非常感谢您花时间添加示例。虽然这并不是我想要的,但它对于解决我对路由的一些其他问题非常有帮助。在尝试了一些想法之后,我觉得答案是:'没有办法做到'。如果您同意,我会将这个答案保留为不接受,以免混淆其他人。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 2013-05-25
    • 2012-07-20
    • 1970-01-01
    • 1970-01-01
    • 2014-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多