0

Given a user, I am trying to select a list of events that are affiliated with organizations that the user is in. The UserOrg table describes which OrgIds correspond to a given UserId.

I have these tables:

User
    email Text
    name Text
    UniqueUser email
    deriving Typeable
Event
    name Text
    description Text
    dateTime UTCTime
    userId UserId
    orgId OrgId
    deriving Show
Org
    name Text
    description Text
    deriving Show
UserOrg
    userId UserId
    orgId OrgId

Currently I am trying this:

getEventR :: Handler Html
getEventsR = do
    muser <- maybeAuth
    eventList <- runDB $ 
        case muser of
            Nothing -> []
            (Just euser) -> selectList [ EventOrgId <-. (userOrgIds $ entityKey euser) ] []
    defaultLayout $ do
        setTitle "Events"
        $(widgetFile "events")

userOrgIds :: UserId -> [OrgId]
userOrgIds userid = do
    rows <- liftHandler $ runDB $ selectList [ UserOrgUserId ==. userid ] []
    return $ [ userOrgOrgId $ entityVal $ erow | erow <- rows ]

But I get a type error saying that the return of userOrgIds returns a [[OrgId]] rather than [OrgId], and concat doesn't work here

Am I going about this wrong? Should I just use rawQuery in this case?

Mark Judy
  • 3
  • 1
  • Since your function returns a list, the `do` block is interpreted in terms of the list Monad, and therefore `return` wraps its argument in a singleton list. I'm not particularly *au fait* with Yesod so I'm not sure what `liftHandler` does and whether using it to get a list will do what you want - but in terms of simply fixing the compile error then just omitting the `return $` will do it. – Robin Zigmond Nov 21 '19 at 19:14
  • Actually, having researched the functions and types a little, it doesn't look like it should be possible to use `liftHandler` to get a list at all. The monad you get from it has to be an instance of `MonadResource`, which is a subclass of `MonadIO`, and there's definitely no sensible instance of that for lists. Database queries fundamentally require `IO` (since the results can be different each time you run them), so it's impossible to get a pure value from it such as a list of numbers. – Robin Zigmond Nov 21 '19 at 19:40
  • @RobinZigmond That seemed to work, but now I get the type error "Couldn't match type 'BaseBackend (YesodPersistBackend (HandlerSite [])) with SqlBackend' arising from a use of 'selectList'" – Mark Judy Nov 21 '19 at 19:59
  • urrrgh, that's the kind of type error it takes me ages to debug, which I'm not particularly inclined to do for someone else I'm afraid (hopefully someone who knows Yesod/Persistent much better than me will be able to chime in). Note that I still think what you're trying to do will fundamentally not work for the reason I gave in my second comment - but it doesn't look to me like this latest error is related to that. – Robin Zigmond Nov 21 '19 at 21:35

1 Answers1

0

It's a good start, but I see a number of things wrong with this.

getEventR :: Handler Html
getEventsR = do

These function names don't match.

    muser <- maybeAuth
    eventList <- runDB $ 
        case muser of
            Nothing -> []
            (Just euser) -> selectList [ EventOrgId <-. (userOrgIds $ entityKey euser) ] []

I'm not sure this will work; you probably want your Nothing branch to provide pure [].

    defaultLayout $ do
        setTitle "Events"
        $(widgetFile "events")

This looks fine.

userOrgIds :: UserId -> [OrgId]
userOrgIds userid = do
    rows <- liftHandler $ runDB $ selectList [ UserOrgUserId ==. userid ] []
    return $ [ userOrgOrgId $ entityVal $ erow | erow <- rows ]

This definitely won't work. In your type signature, you've said that it's a pure function; it takes a UserId (which is a synonym for Key User), and returns a list of OrgId (again, synonymous with Key Org) values. However in your implementation, you're using liftHandler, runDB, and selectList. These are all effectful things!

Here's how I would write your Handler.

getEventsR :: Handler Html
getEventsR = do
  mUser <- maybeAuth
  eventList <- case mUser of
    Nothing -> pure []
    Just user -> runDB $ do
      orgs <- selectList [ UserOrgUserId ==. entityKey user ] []
      selectList [ EventOrgId <-. map entityKey orgs ] []
  defaultLayout $ do
    setTitle "Events"
    $(widgetFile "events")

I'm writing that without a compiler, but it should be correct.

Jezen Thomas
  • 13,619
  • 6
  • 53
  • 91