How to query for multiple relations with SuperTable The relevant sections for this question are:


Event: One relevant field. guestAllocation (SuperTable). Guest allocation is a table which selects a Partner (Entries) and how many people that partner is allowed to invite to an event
GuestList - Two fields. relatedEvent (Entries) and guestNames (table)


Each Partner creates an Entry in the guestList channel. When they want to update the names of who they're inviting, they edit their post instead of creating a new one.

I'm trying to write a conditional which will let me check if there's already an entry in the guestList channel which is both related to that partner AND the event we're currently looking at. I need to check this as I have to update that Entry instead of creating a new one if it exists. The form is displayed on the Event page, so the entry variable is the current event we're dealing with.

I can't get my head round the relations documentation. How do I write this? The following is returning no results:

{% set partnerGuestList = craft.entries.section('guestList').relatedTo(
    { sourceElement: entry }, {# this event #}
    { targetElement: currentUser.partner.first() } {# current partner #}
) %}
It's a little hard to follow your example/description, but I'll take a stab at it.

The way I interpret your question, you need to pull a single guestList entry that is a) related to the current event, and b) shares a relationship with the partner entry set to the current user's partner field, via a guestAllocation SuperTable field owned by the event EntryModel (I'm assuming there's a guestAllocation.partner column, and that the event section has the event handle).

The first part is easy, although you seem to be confused about the targetElement and sourceElement parameters for the relatedTo method, so I'll try to clear that up:

Basically, in a relatedTo query you either specify the target element or the source element (never both) for the relationship – depending on which element owns the field where relation is created.

In your case, the event entry would be the target element, not the source, as the relatedEvent field (where the relation is created) belongs to the guestList entry model, not the event. In other words, the elements you want (the guest lists) should have the entry you have (the event entry) selected in a field (i.e. the guestLists entries "target" the event entry), not the other way around. If possible, you should also provide the field handle for the field where the relation is created, i.e. relatedEvent in your case. So, to pull all guestlists related to the current event entry, the correct relatedTo query would look like this:

{% set guestListsForEvent = craft.entries.section('guestList').relatedTo({
    targetElement: entry,
    field: 'relatedEvent'
}) %}


Obviously, you only want one guestlist – the one that is related to the current user's partner entry (phew!). Trouble is, that relationship doesn't really exist – it's actually more of a condition, where a) the guestList entry needs to be authored by a user with the correct partner relation, and b) the current event needs to have the correct partner entry (i.e. the current user's related partner entry) in its guestAllocations SuperTable field.

The second part is easy – you can set a canCurrentUserCreateGuestList flag inside a loop on the current event entry's guestAllocations field; if the correct partner event is found in the latter, the current user can create a new guestList entry and you should also query for an existing guestList entry:

{% set currentPartner = currentUser.partner.first %}
{% set currentUserCanCreateGuestList = false %}
{% for row in entry.guestAllocation %}
    {% if row.partner|length and row.partner.first.id == currentPartner.id %}
        {% set currentUserCanCreateGuestList = true %}
    {% endif %}
{% endfor %}


Once you have that flag, you can use it in a conditional:

{% if currentUserCanCreateGuestList %}
   {# Query for existing guest list, create new list etc #}
{% endif %}


For the latter – the actual query to pull the guestList entry – a single relatedTo call obviously won't do the job here, but fortunately the workaround is quite simple – keeping in mind that you need to make sure that a) only 1 guestList entry is returned, and b) that entry is authored by a user with the same partner entry as the current user.

Again, the first part is easy – simply use limit and first to pull a single entry (I like to add a |default(false) filter to suppress errors if no entries are returned).

For the second part, you'll first need a list of user IDs for all the users that are related to the current user's partner entry, and then feed that array to the authorId parameter for the craft.entries call:

{# Get the IDs for all users related to the correct partner #}
{% set partnerUserIds = craft.users.relatedTo({
    targetElement: currentUser.partner.first,
    field: 'partner'
}).ids() %}

{# Get existing partner guest list related to the current event #}
{% set partnerGuestList = partnerUserIds|length ? craft.entries.section('guestList').relatedTo({
    targetElement: entry,
    field: 'relatedEvent'
}).authorId(partnerUserIds).limit(1).first|default(false) : false %}


The above code will return either a single guestList EntryModel, or false, if no related guestlist entries authored by a user with the correct partner entry exists. Whether or not the event entry has the current user's partner entry in one of its SuperTable guestAllocation.partner columns is irrelevant to the query itself, once you've established that the current event does indeed include the correct partner entry in its guestAllocations field (the conditional above).

Here's how the whole thing could look in context:

{# Can the current user create a guest list for this event? #}
{% set currentPartner = currentUser.partner.first %}
{% set currentUserCanCreateGuestList = false %}
{% for row in entry.guestAllocation %}
    {% if row.partner|length and row.partner.first.id == currentPartner.id %}
        {% set currentUserCanCreateGuestList = true %}
    {% endif %}
{% endfor %}

{# Get existing guest list #}
{% set partnerGuestList = false %}
{% if currentUserCanCreateGuestList %}
    {# Get the IDs for all users related to the correct partner #}
    {% set partnerUserIds = craft.users.relatedTo({
        targetElement: currentUser.partner.first,
        field: 'partner'
    }).ids() %}

    {# Get existing partner guest list related to the current event #}
    {% set partnerGuestList = partnerUserIds|length ? craft.entries.section('guestList').relatedTo({
        targetElement: entry,
        field: 'relatedEvent'
    }).authorId(partnerUserIds).limit(1).first|default(false) : false %}
{% endif %}

{# Create, edit, etc #}
{% if partnerGuestList %}
    {# Edit existing guest list #}
{% elseif currentUserCanCreateGuestList %}
    {# Create new guest list #}
{% else %}
    {# Do nothing? #}
{% endif %}


Note that the above isn't tested and there's a good chance I misinterpreted your question – but hopefully you'll take something away from it.

Finally, as your question also touches on the correct usage of relatedTo queries with multiple relation criteria: Your original syntax for the relatedTo call is close, but when you have multiple relation criteria, you'll need to include either and or or as the first parameter, specifying if all or any of the criteria should be met, respectively.

In your case, this means the syntactically correct multiple relation query for the partner guestlist should look something like this:

{% set partnerGuestList = craft.entries.section('guestList').relatedTo('and', 
    { target: entry, field: 'relatedEvent' },
    { targetElement: currentUser.partner.first(), field: 'guestAllocation.partner' }
) %}


Obviously, for reasons already explained the above query will return nothing. There's also the fact that SuperTable fields (in your case, the guestAllocation.partner field) doesn't actually work with the built-in relations functionality in Craft – meaning that you can never use craft.entries.relatedTo when the relation is created in a SuperTable field. Instead, SuperTable provides its own method for relation queries, which is explained in this answer. Again, I don't believe a single relatedTo call is the correct approach for your particular use case, but just wanted to mention it.

Just for funsies – here's how you could use SuperTable's getRelatedElements method to pull event entries based on guestAllocation.partner relations:

{% set partnerEvents = craft.supertable.getRelatedElements({
    relatedTo: {
        targetElement: currentUser.partner.first,
        field: 'guestAllocation.partner'
    },
    criteria: {
        section: 'event'
    }
}) %}