Skip to content

Commit

Permalink
Updated the join documentation to highlight the (:.) type constructor.
Browse files Browse the repository at this point in the history
  • Loading branch information
Montmorency committed Apr 22, 2022
1 parent 3cbe970 commit 738a7ab
Showing 1 changed file with 50 additions and 0 deletions.
50 changes: 50 additions & 0 deletions Guide/relationships.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,56 @@ data LabeledData a b = LabeledData { labelValue :: a, contentValue :: b }

In the case above, `a` would be instantiated by (Id' "tags") and `b` by `Post`.

### Simple Joins and Outer Joins
An alternative approach to joining data in IHP can be accomplished by using the [postresql-simple (:.)](https://hackage.haskell.org/package/postgresql-simple-0.6.4/docs/Database-PostgreSQL-Simple-Types.html#t::.)
and a custom sql query.

For example say there is a `Student`, `StudentDeskCombo`, and `Desk` data type derived by IHP from
`students`, `student_desk_combos`, and `desks` tables.

If the application wished to get a list of all the desks and whether a student
is associated with that desk a `left outer join` on the three tables would be a simple
way of accomplishing this. The postgresql data type `(:.)` allows for a compound data
structure to be created without having to define any `newtype` wrappers or define
functions that do any type level computations.

All that is required is that a `FromRow` instance for any potentially nullable return value in the query, e.g. `Maybe Student`,
is manually defined in the IHP application:

```haskell
instance FromRow (Maybe Student) where
fromRow = (null *> null *> null *> pure Nothing) <|> (Just <$> fromRow)
where null = field :: RowParser Null
```

At the moment the postgresql-simple library does not derive this instance generically.

Once you define this instance, preferably in `Application.Helper.Controller`, you can then
access the IHP derived data types directly by writing a custom sql query:

```
deskStudentCombos :: [Desk :. Maybe StudentDeskCombo :. Maybe Student] <- sqlQuery [select * from desks
left outer join on studentdeskcombo.desk_id = desks.id
left outer join on studentdeskcombo.student_id = students.id
]()
```


the result data type can be unpacked and rendered using straight forward pattern matching with the `(:.)`
data type/type constructor:
```
renderStudentDesk :: (Desk :. Maybe StudentDeskCombo :. Student) -> Html
renderStudentDesk (desk :. Just studentDeskCombo :. Just student) = [hsx|{get #name student} {get #id desk}|]
renderStudentDesk (desk :. Nothing :. Nothing) = [hsx|<p>No student assigned to this Desk: {get #id desk}.</p>|]
```

In the case of inner joins the process is even simpler and does not require
defining the `instance FromRow Maybe a`. This approach to joins allows
for custom queries to leverage the autogenerated schema/IHP derived data types directly
and cuts down on clutter from `newtype` definitions.



### Many-to-many relationships and views

Let's say we have the following schema:
Expand Down

0 comments on commit 738a7ab

Please sign in to comment.