{-# LANGUAGE ExistentialQuantification #-}{-# LANGUAGE TypeFamilies #-}{-# LANGUAGE OverloadedStrings #-}{-# LANGUAGE RankNTypes #-}moduleDatabase.Persist.Class.PersistEntity(PersistEntity(..),Update(..),SelectOpt(..),BackendSpecificFilter,Filter(..),Key,Entity(..))whereimportDatabase.Persist.Types.BaseimportDatabase.Persist.Class.PersistFieldimportData.Text(Text)importqualifiedData.TextasTimportData.Aeson(ToJSON(..),FromJSON(..),object,(.:),(.=),Value(Object))importControl.Applicative((<$>),(<*>))importData.Monoid(mappend)-- | A single database entity. For example, if writing a blog application, a-- blog entry would be an entry, containing fields such as title and content.classPersistEntityvalwhere-- | Parameters: val and datatype of the fielddataEntityFieldval::*->*persistFieldDef::EntityFieldvaltyp->FieldDefSqlTypetypePersistEntityBackendval-- | Unique keys in existence on this entity.dataUniquevalentityDef::Monadm=>mval->EntityDefSqlTypetoPersistFields::val->[SomePersistField]fromPersistValues::[PersistValue]->EitherTextvalpersistUniqueToFieldNames::Uniqueval->[(HaskellName,DBName)]persistUniqueToValues::Uniqueval->[PersistValue]persistUniqueKeys::val->[Uniqueval]persistIdField::EntityFieldval(Keyval)fieldLens::EntityFieldvalfield->(forallf.Functorf=>(field->ffield)->Entityval->f(Entityval))dataUpdatev=foralltyp.PersistFieldtyp=>Update{updateField::EntityFieldvtyp,updateValue::typ,updateUpdate::PersistUpdate-- FIXME Replace with expr down the road}dataSelectOptv=foralltyp.Asc(EntityFieldvtyp)|foralltyp.Desc(EntityFieldvtyp)|OffsetByInt|LimitToInttypefamilyBackendSpecificFilterbv-- | Filters which are available for 'select', 'updateWhere' and-- 'deleteWhere'. Each filter constructor specifies the field being-- filtered on, the type of comparison applied (equals, not equals, etc)-- and the argument for the comparison.dataFilterv=foralltyp.PersistFieldtyp=>Filter{filterField::EntityFieldvtyp,filterValue::Eithertyp[typ]-- FIXME,filterFilter::PersistFilter-- FIXME}|FilterAnd[Filterv]-- ^ convenient for internal use, not needed for the API|FilterOr[Filterv]|BackendFilter(BackendSpecificFilter(PersistEntityBackendv)v)-- | Helper wrapper, equivalent to @Key (PersistEntityBackend val) val@.---- Since 1.1.0typeKeyval=KeyBackend(PersistEntityBackendval)val-- | Datatype that represents an entity, with both its 'Key' and-- its Haskell representation.---- When using a SQL-based backend (such as SQLite or-- PostgreSQL), an 'Entity' may take any number of columns-- depending on how many fields it has. In order to reconstruct-- your entity on the Haskell side, @persistent@ needs all of-- your entity columns and in the right order. Note that you-- don't need to worry about this when using @persistent@\'s API-- since everything is handled correctly behind the scenes.---- However, if you want to issue a raw SQL command that returns-- an 'Entity', then you have to be careful with the column-- order. While you could use @SELECT Entity.* WHERE ...@ and-- that would work most of the time, there are times when the-- order of the columns on your database is different from the-- order that @persistent@ expects (for example, if you add a new-- field in the middle of you entity definition and then use the-- migration code -- @persistent@ will expect the column to be in-- the middle, but your DBMS will put it as the last column).-- So, instead of using a query like the one above, you may use-- 'Database.Persist.GenericSql.rawSql' (from the-- "Database.Persist.GenericSql" module) with its /entity-- selection placeholder/ (a double question mark @??@). Using-- @rawSql@ the query above must be written as @SELECT ?? WHERE-- ..@. Then @rawSql@ will replace @??@ with the list of all-- columns that we need from your entity in the right order. If-- your query returns two entities (i.e. @(Entity backend a,-- Entity backend b)@), then you must you use @SELECT ??, ??-- WHERE ...@, and so on.dataEntityentity=Entity{entityKey::Keyentity,entityVal::entity}deriving(Eq,Ord,Show,Read)instanceToJSONe=>ToJSON(Entitye)wheretoJSON(Entitykv)=object["key".=k,"value".=v]instanceFromJSONe=>FromJSON(Entitye)whereparseJSON(Objecto)=Entity<$>o.:"key"<*>o.:"value"parseJSON_=fail"FromJSON Entity: not an object"instancePersistFieldentity=>PersistField(Entityentity)wheretoPersistValue(Entitykv)=casetoPersistValuevof(PersistMapalist)->PersistMap((idField,toPersistValuek):alist)_->error$T.unpack$errMsg"expected PersistMap"fromPersistValue(PersistMapalist)=caseafterof[]->Left$errMsg$"did not find "`mappend`idField`mappend`" field"("_id",k):afterRest->casefromPersistValue(PersistMap(before++afterRest))ofRightrecord->Right$Entity(Keyk)recordLefterr->Lefterr_->Left$errMsg$"impossible id field: "`mappend`T.pack(showalist)where(before,after)=break((==idField).fst)alistfromPersistValuex=Left$errMsg"Expected PersistMap, received: "`mappend`T.pack(showx)errMsg::Text->TexterrMsg=mappend"PersistField entity fromPersistValue: "-- | Realistically this is only going to be used for MongoDB,-- so lets use MongoDB conventionsidField::TextidField="_id"