Database.Persist の関数群を簡単に説明

Yesod で使われている Database.Persist ですが、どうも型シグニチャとかごちゃごちゃしててわかりにくいので、型を簡略化して説明を加えてみました。
参考: [persistent-1.1.3.2] (http://hackage.haskell.org/packages/archive/persistent/1.1.3.2/doc/html/Database-Persist.html)

## はじめに

型シグニチャは簡略化しています。haddock との対応は

- `値` → `val`
- `キー` → `Key val`
- `ユニークキー` → `Unique val`
- `フィルタ` → `[Filter val]`
- `オプション` → `[SelectOpt val]`
- `更新内容` → `[Update val]`

といった感じです。
また、値とエンティティの違いですが、値はデータベースと関係なく存在するデータとすると、エンティティはデータベース上のレコードに対応し、キーの情報を持っています。
型シグニチャ中の `m` は、その関数が使えるモナドみたいなもんです…(うまく説明できない)。Scaffold された Yesod サイトであれば、runDB $ do 〜 の中に書くやつです。

## PersistStore のメソッド

```haskell
insert :: 値 -> m キー
```
値を挿入して新規発行されたキーを返す

```haskell
insertKey :: キー -> 値 -> m ()
```
値を、キーを指定して挿入する

```haskell
repsert :: キー -> 値 -> m ()
```
キーに対応するエンティティが存在すれば置き換える。でなければ insertKey と同じ

```haskell
replace :: キー -> 値 -> m ()
```
キーに対応するエンティティが存在すれば置き換える。存在しない場合の動作は未定義

```haskell
delete :: キー -> m ()
```
指定したキーに対応するエンティティを削除する。存在しなければ何もしない

```haskell
get :: キー -> m (Maybe 値)
```
キーを指定して値を取得する。存在しないかもしれないので Maybe でくるんである。

## PersistUnique のメソッド

```haskell
getBy :: ユニークキー -> m (Maybe エンティティ)
```
ユニークキーをもとにエンティティを取得する。

```haskell
deleteBy :: ユニークキー -> m ()
```
ユニークキーをもとにエンティティを削除する。

```haskell
insertUnique :: 値 -> m (Maybe キー)
```
insert に似ているが、ユニークキー制約を満たさない場合は Nothing を返す

## PersistQuery のメソッド

```haskell
update ::  キー -> 更新内容 -> m ()
```
指定したキーに対応するエンティティを更新する

```haskell
updateGet :: キー -> 更新内容 -> m 値
```
update と同時に更新後の値を取得する

```haskell
updateWhere :: フィルタ -> 更新内容 -> m ()
```
フィルタに該当するエンティティをすべて更新する

```haskell
deleteWhere :: フィルタ -> m ()
```
フィルタに該当するエンティティをすべて削除する。

```haskell
selectSource :: フィルタ -> オプション -> Source m エンティティ
```
指定したフィルタとオプションでエンティティを選択し、結果を Source で受け取る(よく知らない)

```haskell
selectFirst :: フィルタ -> オプション -> m (Maybe エンティティ)
```
指定したフィルタとオプションで選択された最初のエンティティを Maybe で返す(該当するものがない場合は Nothing)。

```haskell
selectKeys :: フィルタ -> オプション -> Source m キー
```
指定したフィルタとオプションでキーを選択し、結果を Source で受け取る

```haskell
count :: フィルタ -> m Int
```
フィルタに該当するエンティティの数を Int で返す

## その他の関数

```haskell
insertBy :: 値 -> m (Either エンティティ キー)
```
値を挿入するが、ユニークキー制約を満たさない場合、その原因となったエンティティを Left で返す。成功すれば挿入されたエンティティのキーを Right で返す

```haskell
getJust :: キー -> m 値
```
get に似ているが、キーに対応するエンティティが存在することを想定しているので返り値は Maybe ではない。安全性に欠ける。

```haskell
belongsTo :: (子の値 -> Maybe 親のキー) -> 子の値 -> m (Maybe 親の値)
```
一対多の親子関係を形成するエンティティ同士において、親エンティティのキーを Maybe で定義している場合(取得関数を childParentMaybeId とする)に、belongsTo childParentMaybeId child などとして親の値を Maybe で得る

```haskell
belongsToJust :: (子の値 -> 親のキー) -> 子の値 -> m 親の値
```
一対多の親子関係を形成するエンティティ同士において、親エンティティのキー取得関数を childParentId とするとき、belongsToJust childParentId child などとして親の値を得る

```haskell
getByValue :: 値 -> m (Maybe エンティティ)
```
指定した値と同等のユニークキー制約を満たすエンティティを一つ取得する。ユニークキーを一つだけもつエンティティで意味を持つ。

```haskell
checkUnique :: 値 -> m Bool
```
指定した値(挿入前の値など)がユニークキー制約にひっかからなければ True を返す。

```haskell
selectList :: フィルタ -> オプション -> m [エンティティ]
```
指定したフィルタとオプションでエンティティを選択し、結果を List で受け取る。

```haskell
deleteCascadeWhere :: フィルタ -> m ()
```
関連モデルも削除するための機能のようだが、ちょっとわかりません。

## Yesod での使用例

```haskell
makeFormLesson :: Entity Lesson -> Handler FormLesson
makeFormLesson eLesson = do
    let (Entity lessonId lesson) = eLesson
    title <- runDB $ belongsToJust lessonTitleId lesson
    meLessonAttribute <- runDB $ getBy (UniqueAttribute lessonId)
    attributes <- case meLessonAttribute of
            Just eLessonAttribute -> return $
                (,,) <$> lessonAttributePartialAttendable
                     <*> lessonAttributeObservable
                     <*> lessonAttributeExperienceable
                     $ entityVal eLessonAttribute
            Nothing -> do
                let (pa, o, e) = defaultLessonAttributes
                _ <- runDB $ insert $ LessonAttribute lessonId pa o e
                return (pa, o, e)
    return $ FormLesson <$> const title
                        <*> lessonTeacherId
                        <*> lessonSeason
                        <*> lessonSchoolId
                        <*> const attributes
                        <*> lessonDescription
                        <*> lessonPrerequisite
                        $ lesson
```
`FormLesson`という型に意味はありませんので気にしないでください。`runDB`の呼び出しに注目してください 。関連するエンティティをいくつかの方法で取り出しています。