IHP如何实现自定义订单?

How to achieve a user-defined order with IHP?

我需要对页面进行排序(由用户定义,拖放),例如 ("Hello", order 1), ("Bye", order 2) 然后插入,比方说 ("Good", order 1),这样集合就会变为 ("Good", order 1), ("Hello", order 2), ("Bye", order 3) . 有什么想法可以用 IHP/Postgres 实现吗?

我看过

https://begriffs.com/posts/2018-03-20-user-defined-order.html

这些是我们迄今为止最好的解决方案吗?

我通常使用整数 position 列方法。这是来自现实世界 IHP 应用程序的示例代码:

在这种情况下,我们有 offers 列和 position 整数列,如下所示:

CREATE TABLE offers (
    id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
    name TEXT NOT NULL,
    "location" TEXT NOT NULL,
    contact TEXT NOT NULL,
    description TEXT NOT NULL,
    "position" INT NOT NULL
);

控制器看起来像这样:

module Web.Controller.Offers where

import Web.Controller.Prelude
import Web.View.Offers.Edit
import Web.View.Offers.Show
import Web.View.Offers.New
import Web.View.Offers.Index
import qualified Application.Offer as Offer


import qualified Control.Monad.State.Lazy as State

instance Controller OffersController where
    beforeAction = ensureIsUser

    action NewOfferAction = do
        let backField :: Offer = newRecord
        render NewView { .. }

    action OffersAction = do
        backFields <- query @Offer |> orderBy #position |> fetch
        render IndexView { .. }

    action ShowOfferAction { .. } = do
        backField <- fetch backFieldId
        render ShowView { .. }

    action EditOfferAction { .. } = do
        backField <- fetch backFieldId
        render EditView { .. }

    action UpdateOfferAction { .. } = do
        backField <- fetch backFieldId
        backField
            |> buildOffer
            |> ifValid \case
                Left backField -> render EditView { .. }
                Right backField -> do
                    backField <- updateRecord backField
                    setSuccessMessage "Offer updated"
                    redirectTo EditOfferAction { .. }

    action CreateOfferAction = do
        nextPosition <- Offer.nextPosition
        newRecord @Offer
            |> buildOffer
            |> ifValid \case
            Left backField -> render NewView { .. }
            Right backField -> do
                backField <- backField
                    |> createRecord
                setSuccessMessage "Angebot erstellt"
                redirectTo OffersAction

    action DeleteOfferAction { .. } = do
        backField <- fetch backFieldId
        deleteRecord backField
        setSuccessMessage "Deleted Offer successfully"
        redirectTo OffersAction

    action OfferMoveUpAction { .. } = do
        backField <- fetch backFieldId
        prevOffer <- backField |> Offer.prevOffer
        case prevOffer of
            Just prevOffer -> do
                let backFieldPosition = get #position backField
                let prevOfferPosition = get #position prevOffer
                updateRecord (backField |> set #position prevOfferPosition)
                updateRecord (prevOffer |> set #position backFieldPosition)
                return ()
            Nothing -> return ()
        redirectTo OffersAction

    action OfferMoveDownAction { .. } = do
        backField <- fetch backFieldId
        nextOffer <- backField |> Offer.nextOffer
        case nextOffer of
            Just nextOffer -> do
                let backFieldPosition = get #position backField
                let nextOfferPosition = get #position nextOffer
                updateRecord (backField |> set #position nextOfferPosition)
                updateRecord (nextOffer |> set #position backFieldPosition)
                return ()
            Nothing -> return ()
        redirectTo OffersAction


buildOffer :: _ => backField -> backField
buildOffer backField =
    backField
    |> fill @'["name", "description", "position", "location", "contact"]
    |> validateField #name nonEmpty

控制器中使用的一些辅助函数在另一个模块中定义(但如果需要,您可以将它们放入控制器中):

module Application.Offer (nextPosition, prevOffer, nextOffer) where

import IHP.Prelude
import IHP.ModelSupport
import IHP.QueryBuilder
import Generated.Types
import qualified Database.PostgreSQL.Simple as PG

instance DefaultScope "offers" where
    defaultScope = orderBy #position

nextPosition :: (?modelContext :: ModelContext) => IO Int
nextPosition = sqlQueryScalar "SELECT COUNT(*) FROM offers" ()

prevOffer :: (?modelContext :: ModelContext) => Offer -> IO (Maybe Offer)
prevOffer offer = do
    results <- sqlQuery "SELECT * FROM offers WHERE position < ? ORDER BY position DESC LIMIT 1" (PG.Only (get #position offer))
    return $ headMay results

nextOffer :: (?modelContext :: ModelContext) => Offer -> IO (Maybe Offer)
nextOffer offer = do
    results <- sqlQuery "SELECT * FROM offers WHERE position > ? ORDER BY position ASC LIMIT 1" (PG.Only (get #position offer))
    return $ headMay results