@@ -27,6 +27,7 @@ module Database.Persist.Class.PersistEntity
2727 , ViaPersistEntity (.. )
2828 , recordName
2929 , entityValues
30+ , assignAll
3031 , keyValueEntityToJSON
3132 , keyValueEntityFromJSON
3233 , entityIdToJSON
@@ -71,6 +72,9 @@ import qualified Data.Aeson.KeyMap as AM
7172import qualified Data.HashMap.Strict as AM
7273#endif
7374
75+ import Control.Applicative
76+ import Control.Monad
77+ import Control.Monad.Trans.Writer.CPS
7478import Data.Kind (Type )
7579import Data.List.NonEmpty (NonEmpty (.. ))
7680import Data.Maybe (isJust )
@@ -82,6 +86,7 @@ import qualified Data.Text.Lazy.Builder as TB
8286import GHC.Generics
8387import GHC.OverloadedLabels
8488import GHC.Records
89+ import GHC.Stack
8590import GHC.TypeLits
8691
8792import Database.Persist.Class.PersistField
@@ -175,7 +180,7 @@ class
175180 -- @since 2.14.0.0
176181 tabulateEntityA
177182 :: (Applicative f )
178- => (forall a . PersistField a => EntityField record a -> f a )
183+ => (forall a . ( PersistField a ) => EntityField record a -> f a )
179184 -- ^ A function that builds a fragment of a record in an
180185 -- 'Applicative' context.
181186 -> f (Entity record )
@@ -187,7 +192,7 @@ class
187192 -- @since 2.17.0.0
188193 tabulateEntityApply
189194 :: (Apply f )
190- => (forall a . PersistField a => EntityField record a -> f a )
195+ => (forall a . ( PersistField a ) => EntityField record a -> f a )
191196 -> f (Entity record )
192197
193198 -- | Unique keys besides the 'Key'.
@@ -207,7 +212,10 @@ class
207212 fieldLens
208213 :: EntityField record field
209214 -> ( forall f
210- . (Functor f ) => (field -> f field ) -> Entity record -> f (Entity record )
215+ . (Functor f )
216+ => (field -> f field )
217+ -> Entity record
218+ -> f (Entity record )
211219 )
212220
213221 -- | Extract a @'Key' record@ from a @record@ value. Currently, this is
@@ -265,7 +273,7 @@ instance (PersistEntity record) => PathMultiPiece (ViaPersistEntity record) wher
265273-- @since 2.14.0.0
266274tabulateEntity
267275 :: (PersistEntity record )
268- => (forall a . PersistField a => EntityField record a -> a )
276+ => (forall a . ( PersistField a ) => EntityField record a -> a )
269277 -> Entity record
270278tabulateEntity fromField =
271279 runIdentity (tabulateEntityA (Identity . fromField))
@@ -275,9 +283,7 @@ type family BackendSpecificUpdate backend record
275283-- Moved over from Database.Persist.Class.PersistUnique
276284
277285-- | Textual representation of the record
278- recordName
279- :: (PersistEntity record )
280- => record -> Text
286+ recordName :: (PersistEntity record ) => record -> Text
281287recordName = unEntityNameHS . entityHaskell . entityDef . Just
282288
283289-- | Updating a database entity.
@@ -391,6 +397,28 @@ entityValues (Entity k record) =
391397 where
392398 ent = entityDef $ Just record
393399
400+ -- | Create a list with an @Update field value Assign@ for every @field@ and
401+ -- @value@ in record, except its 'Key'.
402+ -- This is useful in combination with @upsert@.
403+ -- The implementation assumes 'tabulateEntityA' is not strict in the 'entityKey'
404+ -- of 'Entity' (and the default implementation indeed isn't).
405+ -- @since 2.13.2.0
406+ assignAll
407+ :: forall record . (PersistEntity record , HasCallStack ) => record -> [Update record ]
408+ assignAll r = snd $ runWriter $ tabulateEntityA $ \ field ->
409+ let
410+ fieldVal = getConst $ fieldLens field Const fakeEntity
411+ in
412+ fieldVal
413+ <$ when
414+ ( fieldHaskell (persistFieldDef field)
415+ /= fieldHaskell (persistFieldDef @ record persistIdField)
416+ )
417+ (tell [Update field fieldVal Assign ])
418+ where
419+ -- slightly hacky. The entity key is filtered out above and never used.
420+ fakeEntity = Entity (error " assignAll: tabulateEntityA was strict in the Entity's key" ) r
421+
394422-- | Predefined @toJSON@. The resulting JSON looks like
395423-- @{"key": 1, "value": {"name": ...}}@.
396424--
@@ -402,7 +430,8 @@ entityValues (Entity k record) =
402430-- @
403431keyValueEntityToJSON
404432 :: (PersistEntity record , ToJSON record )
405- => Entity record -> Value
433+ => Entity record
434+ -> Value
406435keyValueEntityToJSON (Entity key value) =
407436 object
408437 [ " key" .= key
@@ -420,7 +449,8 @@ keyValueEntityToJSON (Entity key value) =
420449-- @
421450keyValueEntityFromJSON
422451 :: (PersistEntity record , FromJSON record )
423- => Value -> Parser (Entity record )
452+ => Value
453+ -> Parser (Entity record )
424454keyValueEntityFromJSON (Object o) =
425455 Entity
426456 <$> o .: " key"
0 commit comments