使用Yesod的Persistent存储现有数据类型

Lem*_*ing 9 haskell persistent yesod

我能找到的关于Persistent的所有教程和参考都非常详细地描述了Persistent如何在DSL中的单个定义中自动创建新的数据类型,模式,迁移等.但是,我找不到有关如何使Persistent处理现有数据类型的解释.

一个例子:假设我有一个已经存在的Haskell模块用于某些游戏逻辑.它包括玩家的记录类型.(它意味着通过镜头使用,因此是下划线.)

data Player = Player { _name   :: String
                     , _points :: Int
                     -- more fields ...
                     }
$(makeLenses ''Player)
Run Code Online (Sandbox Code Playgroud)

问题:使用Persistent将这种类型存储在数据库中的规范方法是什么?我可以实现一些类型类吗?或者我应该通过Persistent最好地定义一个新类型,例如

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
PlayerEntry
    name Text
    points Int
|]
Run Code Online (Sandbox Code Playgroud)

然后在这些类型之间手动映射?

playerToEntry :: Player -> PlayerEntry
playerToEntry pl = PlayerEntry (pl^.name) (pl^.points)

entryToPlayer :: PlayerEntry -> Player
entryToPlayer e = Player (name e) (points e)
Run Code Online (Sandbox Code Playgroud)

Lem*_*ing 1

我对这个问题的解决方案是通过 Yesod 添加一个新类型mkPersist,并在这些类型之间手动编组。

config/models:

PlayerEntry
    name Text
    points Int
    created UTCTime default=CURRENT_TIMESTAMP
Run Code Online (Sandbox Code Playgroud)

Marshalling.hs:

fromPlayerEntry :: PlayerEntry -> Player
fromPlayerEntry PlayerEntry {..} = Player { name = playerName
                                          , points = playerPoints
                                          }

createPlayerEntry :: Text -> YesodDB App (Entity PlayerEntry)
createPlayerEntry name = do
    currentTime <- liftIO getCurrentTime
    let player = PlayerEntry { playerName = name
                             , playerPoints = 0
                             , playerCreated = currentTime
                             }
    playerId <- insert player
    return $ Entity playerId player

updatePlayerEntry :: PlayerEntryId -> Player -> YesodDB App ()
updatePlayerEntry playerId Player {..} =
    update playerId [ PlayerName =. name
                    , PlayerPoints =. points
                    ]
Run Code Online (Sandbox Code Playgroud)

一个可能的优点是您的表中可以包含内部记录中不需要的字段。在我的示例中,将创建日期附加到播放器非常有用。但是,这仅在 Web 界面层中使用,从未在定义类型的内部游戏逻辑中使用Player。但是,由于手动编组,我仍然可以将该字段添加到同一个数据库表中。