2

I'm creating a line up listing page for a festival, across 4 nights. Acts are listed, per day, sorted by a date field in order of a start time field.

The issue I'm having is that midnight until 3am is still considered the night of an event but my time field obviously considers it otherwise.

e.g. an act is performing on the Friday morning at 1am but I need to list this at the end of the Thursday acts.

So the user adds it to the CMS on the Thursday but it appears at the start of the day instead of the end.

Essentially, I just need any acts that appear between 00:00 and 03:00 at the top of a list e.g

Thursday

  • Act 1 - 1am - 2am
  • Act 2 - 12am - 1am
  • Act 3 - 11pm - 12am
  • Act 4 - 10pm - 11pm

etc

This is the code I have so far

whatDateAreTheyPerforming is a date field

startTime and endTime are time fields

{% set entryIds = craft.entries.section('lineUp').ids() %}
{% set categories = craft.categories.relatedTo({ sourceElement: entryIds }).find() %}

{% for category in categories %}

  {# Fetch all of the entries related to this category #}
  {% set entries = craft.entries.relatedTo(category) %}

  {# Group entries by date and order by start time #}
  {% for date, entries in entries.order('whatDateAreTheyPerforming', 'time(startTime) desc')|
  group('whatDateAreTheyPerforming') %}
    <div>

      <h3>DAY HERE - {{ date | date('l') }} <span>{{ date | date('jS') }}</span></h3>

      <ul>

        {% for entry in entries %}

        <li>
          <h4>{{ entry.title }} 
              <span>{{ entry.whatDateAreTheyPerforming | date('l') }} - {{ entry.startTime | date('G:i') }} - {{ entry.endTime | date('G:i') }}</span>
          </h4>
        </li>

        {% endfor %}

      </ul>
    </div><!-- col -->
  {% endfor %} 

{% endfor %}

I hope this makes sense, any guidance will be appreciated.

1

I -think- one of the neatest ways to go about this is to introduce a "Show as last in list" lightswitch-field.

I'm currently working on a similar project (festival line-up) where I also needed this; here's the approach I went with.

Basically, I'll be going for two fields: eventTime (date/time-field) and showAsLastInList (lightswitch-field)

{% set events = craft.entries.section('events').showAsLastInList('NOT 1').orderBy('eventTime ASC').all %}

{% for date, eventsByDate in events|group('eventTime|date("Y-m-d")') %}
    {% set additionalEvents = craft.entries.section('events').eventTime(date).showAsLastInList('1').orderBy('eventTime ASC').all %}
    <div class="program__dateline" data-filter-dateline>{{ date|date('l j. F')|capitalize }}</div>
    {% for event in eventsByDate|merge(additionalEvents) %}
       {# PRINT YOUR HTML HERE #}
    {% endfor %}
{% endfor %}

It's not exactly pretty, but I can't really think of another way to achieve this without creating a custom plugin for it.

  • I like the simplicity of this! I think the only downside is that it requires user input, but an upside is that it gives them a little more control. Thanks for the suggestion :) – MikeeBee Feb 21 '18 at 4:03
1

Thanks for the answers! I ended up using the advice found in this article that I have found to be endlessly useful - Manipulating Craft's ElementCriteriaModel objects with Twig.

Gathering an array of ids of entries that pass the conditional, as well as another array of ones that don't. Then outputting them separately which allowed me to tweak the time order to time(startTime) asc in the postMidnight entries.

{% set entryIds = craft.entries.section('lineUp').ids() %}
{% set categories = craft.categories.relatedTo({ sourceElement: entryIds }).find() %}

{% for category in categories %}

  {# Fetch all of the entries related to this category #}
  {% set entries = craft.entries.relatedTo(category) %}

    {# Group entries by date and order by start time #}
    {% for date, entries in entries.order('whatDateAreTheyPerforming', 'time(startTime) desc')|
  group('whatDateAreTheyPerforming') %}
        <div>

            <h3>{{ date | date('l') }} <span>{{ date | date('jS') }}</span></h3>

            {# Arrays to store the IDs of the entries as we find them #}
            {% set postMidnightIDs = [] %}
            {% set preMidnightIDs = [] %}

            <ul>

                {% for entry in entries %}
                    {% if entry.startTime >= entry.startTime|date_modify('00:00') and entry.endTime <= entry.endTime|date_modify('05:00') %}
                       {% set postMidnightIDs = postMidnightIDs|merge([entry.id]) %}
                       {# Or of course you could output the data here but I like to keep building and output separately for clarity #}
                   {% else %}
                       {% set preMidnightIDs = preMidnightIDs|merge([entry.id])  %}
                   {% endif %}
                {% endfor %}

                {% set postMidnightIDsString = postMidnightIDs|join(', ') %}
                {% set preMidnightIDsString = preMidnightIDs|join(', ')  %}

                {# Entries that appear between midnight and 5am #}
                {% for entry in craft.entries.section('lineUp').id(postMidnightIDsString).order('time(startTime) asc').find() %}

                    {# Created a macro to avoid code duplication #}
                    {{ macros.lineUpItem(entry.artistLogo, entry.title, entry.url, entry.whatDateAreTheyPerforming, entry.startTime, entry.endTime, entry.sponsor) }}

                {% endfor %}

                {# Entries that appear between 5am and midnight #}
                {% for entry in craft.entries.section('lineUp').id(preMidnightIDsString).order('time(startTime) desc').find() %}

                    {{ macros.lineUpItem(entry.artistLogo, entry.title, entry.url, entry.whatDateAreTheyPerforming, entry.startTime, entry.endTime, entry.sponsor) }}

                {% endfor %}

            </ul>
        </div><!-- col -->
    {% endfor %} 

{% endfor %}
1

You could use the Preparse plugin to store the actual date of the event into a dedicated field (hidden in the CP). So for example store 2018-02-20 02:00 for an event with a whatDateAreTheyPerforming date set to 2018-02-19. Then use the Preparse field’s value for sorting within your groups.

This code should work to compute the date string with Preparse:

{% set actualDate = entry.whatDateAreTheyPerforming|date('Y-m-d')
    ~ entry.startTime|date(' H:i:s') %}

{%- set isTomorrow = actualDate|date('G') < 5 %}
{%- set actualDate = isTomorrow ? actualDate|date_modify('+1 day') : actualDate %}

{{- actualDate|date('Y-m-d H:i:s') }}

If you don’t want to use Preparse, another simple approach would be to simply loop twice:

{# Output events after 05:00 #}
{% for entry in entries if entry.startTime|date('G') >= 5 %}
{% endfor %}

{# Output events before 05:00 #}
{% for entry in entries if entry.startTime|date('G') < 5 %}
{% endfor %}
  • I ended up looping twice in the end, the code I had is very similar to yours conceptually. I also didn't realise that you could put conditionals straight in the {{ for }} loop so thanks for pointing that out! – MikeeBee Feb 21 '18 at 4:05
  • I'd actually recommend the Preparse approach, have the real date stored somewhere. – carlcs Feb 21 '18 at 6:23
  • I'm not sure I fully understand how Preparse works actually. So I would need to create a seperate date field? Would the user need to add 2 dates? There are a lot of entries that would need updating to include this new field, unless I'm misunderstanding? – MikeeBee Feb 21 '18 at 21:59
  • Yeah, it would be a separate field, but one that populates automatically and there’s a setting to hide it so the user wont even notice it exists. Populate it for existing entries by re-saving the section’s settings. I’ll update the answer with the Twig code that would go into the Preparse field. – carlcs Feb 22 '18 at 11:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.