Skip to content

Commit

Permalink
Implement collectionFetchRelatedOrNothing
Browse files Browse the repository at this point in the history
  • Loading branch information
joshua-obritsch committed Jul 20, 2021
1 parent 2214ba4 commit 64073fe
Showing 1 changed file with 65 additions and 1 deletion.
66 changes: 65 additions & 1 deletion IHP/FetchRelated.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This modules provides helper functions to access relationshops for a model.
See https://ihp.digitallyinduced.com/Guide/relationships.html for some examples.
-}
module IHP.FetchRelated (fetchRelated, collectionFetchRelated, fetchRelatedOrNothing, maybeFetchRelatedOrNothing) where
module IHP.FetchRelated (fetchRelated, collectionFetchRelated, collectionFetchRelatedOrNothing, fetchRelatedOrNothing, maybeFetchRelatedOrNothing) where

import IHP.Prelude
import Database.PostgreSQL.Simple.ToField
Expand All @@ -35,6 +35,23 @@ class CollectionFetchRelated relatedFieldValue relatedModel where
KnownSymbol relatedField
) => Proxy relatedField -> [model] -> IO [Include relatedField model]

-- | This class provides the collectionFetchRelatedOrNothing function
--
-- This function is provided by this class as we have to deal with two cases:
--
-- 1. the related field is an id, e.g. like the company ids in @users |> collectionFetchRelated #companyId@
-- 2. the related field is a query builder, e.g. in @posts |> collectionFetchRelated #comments@
class CollectionFetchRelatedOrNothing relatedFieldValue relatedModel where
collectionFetchRelatedOrNothing :: forall model relatedField. (
?modelContext :: ModelContext,
HasField relatedField model (Maybe relatedFieldValue),
UpdateField relatedField model (Include relatedField model) (Maybe relatedFieldValue) (Maybe (FetchResult relatedFieldValue relatedModel)),
Fetchable relatedFieldValue relatedModel,
KnownSymbol (GetTableName relatedModel),
PG.FromRow relatedModel,
KnownSymbol relatedField
) => Proxy relatedField -> [model] -> IO [Include relatedField model]

-- | Provides collectionFetchRelated for ids, e.g. @collectionFetchRelated #companyId@
--
-- When we want to fetch all the users with their companies, we can use collectionFetchRelated like this:
Expand Down Expand Up @@ -84,6 +101,53 @@ instance (
result = map assignRelated model
pure result

-- | Provides collectionFetchRelatedOrNothing for nullable ids, e.g. @collectionFetchRelatedOrNothing #companyId@
--
-- When we want to fetch all the users with their companies, we can use collectionFetchRelatedOrNothing like this:
--
-- > users <- query @User
-- > |> fetch
-- > >>= collectionFetchRelated #companyId
--
-- This will query all users with their company. The type of @users@ is @[Include "companyId" User]@.
--
-- This example will trigger only two SQL queries:
--
-- > SELECT * FROM users
-- > SELECT * FROM companies WHERE id IN (?)
instance (
Eq (PrimaryKey tableName)
, ToField (PrimaryKey tableName)
, Show (PrimaryKey tableName)
, HasField "id" relatedModel (Id' tableName)
, relatedModel ~ GetModelByTableName (GetTableName relatedModel)
) => CollectionFetchRelatedOrNothing (Id' tableName) relatedModel where
collectionFetchRelatedOrNothing :: forall model relatedField. (
?modelContext :: ModelContext,
HasField relatedField model (Maybe (Id' tableName)),
UpdateField relatedField model (Include relatedField model) (Maybe (Id' tableName)) (Maybe (FetchResult (Id' tableName) relatedModel)),
Fetchable (Id' tableName) relatedModel,
KnownSymbol (GetTableName relatedModel),
PG.FromRow relatedModel,
KnownSymbol relatedField
) => Proxy relatedField -> [model] -> IO [Include relatedField model]
collectionFetchRelatedOrNothing relatedField model = do
relatedModels :: [relatedModel] <- query @relatedModel |> filterWhereIn (#id, mapMaybe (getField @relatedField) model) |> fetch
let
assignRelated :: model -> Include relatedField model
assignRelated model =
let
relatedModel :: Maybe (FetchResult (Id' tableName) relatedModel)
relatedModel = find (\r -> Just (getField @"id" r :: (Id' tableName)) == targetForeignKey) relatedModels
targetForeignKey = (getField @relatedField model :: Maybe (Id' tableName))
in
updateField @relatedField relatedModel model

let
result :: [Include relatedField model]
result = map assignRelated model
pure result

-- | Provides collectionFetchRelated for QueryBuilder's, e.g. @collectionFetchRelated #comments@
--
-- When we want to fetch all the comments for a list of posts, we can use collectionFetchRelated like this:
Expand Down

0 comments on commit 64073fe

Please sign in to comment.