packages feed

Hawk-0.0.2: src/Hawk/Model/WithForeignKey.hs

-- --------------------------------------------------------------------------
{- |
   Module      :  $Header$
   Copyright   :  Copyright (C) 2009 Björn Peemöller, Stefan Roggensack
   License     :  BSD3

   Maintainer  :  {inf6254, inf6509}fh-wedel.de
   Stability   :  experimental
   Portability :  portable
   Version     :  $Id: Main.hs 57 2009-05-29 11:33:59Z inf6254 $

-}
-- --------------------------------------------------------------------------

{-# LANGUAGE TypeFamilies, FlexibleContexts, MultiParamTypeClasses #-}

module Hawk.Model.WithForeignKey
    ( ForeignKeyRelationship (..)
    , getMaybeReferenced
    , getOneReferenced
    , setReferenced
    , removeReferenced
    , getReferencings
    , getMaybeReferencing
    , getOneReferencing
    , addReferencing
    , removeReferencing
    , replaceReferencing
    ) where

import Hawk.Model.Criteria
import Hawk.Model.Exception
import Hawk.Model.MonadDB
import Hawk.Model.Persistent
import Hawk.Model.WithPrimaryKey
import Hawk.Model.Types
import Hawk.Model.Util

import Control.Monad (liftM)

import Control.Exception (throw)



-- | Typeclass for ForeignKey relationships
{-
class (Persistent self, WithPrimaryKey other) =>
      WithForeignKey self relationshipName other |
      relationshipName -> self other where
-}
class (Persistent (Referencing r), WithPrimaryKey (Referenced r)) =>
      ForeignKeyRelationship r where
  type Referencing r :: *
  type Referenced  r :: *

  relationshipName :: r -> String

  -- | Get the 'ColumnName' containing the 'ForeignKey'
  fkColumn      :: r -> ColumnName

  -- | Get the value of the 'ForeignKey'
  foreignKey    :: r -> Referencing r -> ForeignKey

  -- | Set the value of the 'ForeignKey'
  setForeignKey :: r -> Referencing r -> ForeignKey -> Referencing r

  
-- --------------------------------------------------------------------------
-- Functions derived from WithForeignKey
-- --------------------------------------------------------------------------
getMaybeReferenced :: (MonadDB m, ForeignKeyRelationship r)
                   => r
                   -> Referencing r
                   -> m (Maybe (Referenced r))
getMaybeReferenced rel referencing
  = case foreignKey rel referencing of
      Nothing  -> return Nothing
      Just key -> findMaybeByPK key


getOneReferenced :: (MonadDB m, ForeignKeyRelationship r)
              => r
              -> Referencing r
              -> m (Referenced r)
getOneReferenced rel referencing
  = case foreignKey rel referencing of
      Nothing  -> throw $ RecordNotFound msg
      Just key -> findOneByPK key
  where msg =  "Persistent '" ++ persistentType referencing
            ++ "' has no foreign key for relationship '"
            ++ relationshipName rel ++ "'"


setReferenced :: (MonadDB m, ForeignKeyRelationship r)
              => r 
              -> Referencing r
              -> Maybe (Referenced r)
              -> m (Referencing r)
setReferenced rel referencing
  = return . setForeignKey rel referencing . fmap primaryKey


removeReferenced :: (MonadDB m, ForeignKeyRelationship r)
                 => r 
                 -> Referencing r
                 -> Referenced r
                 -> m (Referencing r)
removeReferenced rel referencing referenced
  = if Just (primaryKey referenced) == foreignKey rel referencing
       then setReferenced rel referencing Nothing
       else return referencing


getReferencings :: (MonadDB m, ForeignKeyRelationship r)
                => r
                -> Referenced r
                -> m [Referencing r]
getReferencings rel referenced
  = selectPersistents $ restrictionCriteria restr
  where restr = eqExpr (col $ fkColumn rel) (val $ primaryKey referenced)


getMaybeReferencing :: (MonadDB m, ForeignKeyRelationship r)
                    => r
                    -> Referenced r
                    -> m (Maybe (Referencing r))
getMaybeReferencing rel = liftM safeHead . getReferencings rel


getOneReferencing :: (MonadDB m, ForeignKeyRelationship r)
                  => r
                  -> Referenced r
                  -> m (Referencing r)
getOneReferencing rel parent
  = liftM (unsafeHeadMsg msg) $ getReferencings rel parent
  where msg =  "Persistent '" ++ persistentType parent
            ++ "' with primary key '" ++ show (primaryKey parent)
            ++ "' has no child in the foreign key relationship '"
            ++ show (relationshipName rel) ++ "'"


addReferencing :: (MonadDB m, ForeignKeyRelationship r)
               => r
               -> Referenced r
               -> Referencing r
               -> m (Referencing r)
addReferencing rel referenced referencing
  = setReferenced rel referencing (Just referenced)


removeReferencing :: (MonadDB m, ForeignKeyRelationship r)
                  => r
                  -> Referenced r
                  -> Referencing r
                  -> m (Referencing r)
removeReferencing = flip . removeReferenced


replaceReferencing :: (MonadDB m, ForeignKeyRelationship r)
                   => r
                   -> Referenced r
                   -> Maybe (Referencing r)
                   -> m (Maybe (Referencing r), Maybe (Referencing r))
replaceReferencing rel referenced newReferencing = do
  oldReferencing  <- getMaybeReferencing rel referenced
  oldReferencing' <- case oldReferencing of
                       Nothing -> return Nothing
                       Just o  -> liftM Just $ setReferenced rel o Nothing
  newReferencing' <- case newReferencing of
                       Nothing -> return Nothing
                       Just n  -> liftM Just $ setReferenced rel n (Just referenced)
  return (oldReferencing', newReferencing')