Сохранить существующий тип данных с помощью Yesod's Persistent

Все учебные пособия и ссылки, которые я смог найти о Persistent, подробно описывают, как Persistent может автоматически создавать новый тип данных, схему, миграцию и т. Д. Из единого определения в своем DSL. Однако я не смог найти объяснения о том, как заставить Persistent обрабатывать уже существующие типы данных.

Пример: предположим, у меня уже есть модуль Haskell для некоторой игровой логики. Он включает в себя тип записи для игрока. (Он предназначен для использования через линзы, поэтому подчеркивание.)

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

Вопрос: Какой канонический способ хранить такой тип в базе данных с помощью Persistent? Есть ли какой-то тип-класс, который я могу реализовать. Или мне лучше всего определить новый тип через Persistent, например

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
PlayerEntry
    name Text
    points Int
|]

а потом вручную сопоставлять эти типы?

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

entryToPlayer :: PlayerEntry -> Player
entryToPlayer e = Player (name e) (points e)

2 ответа

Решение

Мое решение этой проблемы было добавить новый тип через Yesod's mkPersistи вручную маршал между ними.

config/models:

PlayerEntry
    name Text
    points Int
    created UTCTime default=CURRENT_TIMESTAMP

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
                    ]

Одним из возможных преимуществ является то, что в вашей таблице могут быть поля, которые не требуются во внутренней записи. В моем примере было полезно прикрепить дату создания к плееру. Однако это использовалось только на уровне веб-интерфейса, оно никогда не использовалось во внутренней игровой логике, которая определяла Player тип. Тем не менее, благодаря ручной сортировке я мог бы добавить это поле в ту же таблицу базы данных.

От: http://www.yesodweb.com/book/persistent

{-# LANGUAGE TemplateHaskell #-}
module Employment where

import Database.Persist.TH

data Employment = Employed | Unemployed | Retired
    deriving (Show, Read, Eq)
derivePersistField "Employment"

derivePersistField Функция шаблона Haskell magic, которая заставляет его работать.

Обратите внимание, вам нужно сделать derivePersistField вещь в отдельном файле, где вы делаете mkPersist чтобы избежать фазовой ошибки TH.

Другие вопросы по тегам