4

I have a section for holding calendar entries stored by postDate in the CMS. On the front end I want to basically display these in a calendar.

I have code which displays the calendar looping through each day of the month. For each day it currently queries if there is any entries for this day.

Is there a better way to do it? - my current method had a query for each day?

Current code is:

<table class="cal">
  <thead class="hide-for-small">
    <tr>
      <th width="300">Sun</th>
      <th width="300">Mon</th>
      <th width="300">Tue</th>
      <th width="300">Wed</th>
      <th width="300">Thu</th>
      <th width="300">Fri</th>
      <th width="300">Sat</th>
    </tr>
  </thead>
  <tbody>
    <tr>
    {% set time = "now"|date("U") %}        
    {% set daysInMonth = time|date('t') %}
    {% set startDow = time|date('F 1\\s\\t Y')|date('w') %}
    {% set dow = startDow %}
    {% for day in range(1,daysInMonth) %}
        {% if loop.first and startDow != 0 %}
            <td colspan="{{ startDow }}"></td>
        {% endif %}
        <td>
            <div>
                <span class="day">{{ day }}</span>
                    {% set today=thisyear ~ "/" ~ thismonth ~ "/" ~ day %}

                    {% if (category is defined) and (category is not empty) %}
                    {% set params = { section: 'calendar', status: null, postDate:today|date('Y-m-d'), relatedTo: category } %}
                    {% else %}
                    {% set params = { section: 'calendar', status: null, postDate:today|date('Y-m-d') } %}
                    {% endif %}
                    {% set entries = craft.entries(params)%}
                    {% if entries|length %}
                <div>
                    {% for entry in entries %}
                    <span class="event green">{{ entry.title }}</span>
                    {% endfor %}
                </div>
                    {% endif %}
            </div>
        </td>
        {% if loop.last and dow != 6 %}
            <td colspan="{{ 6 - dow }}">&nbsp;</td>
        {% endif %}
        {% if dow == 6 %}
            {% set dow = 0 %}
        </tr>
        <tr>
        {% else %}
            {% set dow = dow + 1 %}
        {% endif %}
    {% endfor %}
    </tr>
  </tbody>
</table>  
  • Hi mmc501. Did you ever finish the code with the answer from Stephen Lewis? I am looking to output a mini calendar with prev/next month etc. any help appreciated! – outline4 Jul 9 at 9:13
  • @outline4 yes I finished it and have a working version. Can get it when I return from holidays next week – mmc501 Jul 9 at 12:58
  • That'd be great! – outline4 Jul 9 at 16:11
  • are you back from your holidays? cheers stefan – outline4 Aug 7 at 12:47
  • Yes - I will post it as an additional answer – mmc501 Aug 8 at 7:58
9

I haven't tested this, but it should be a good starting point, at the very least.

{# Determine the number of days in the month #}
{% set daysInMonth = "now"|date("t") %}

{# Determine the date range spanning the month #}
{% set firstDayOfMonth = "now"|date("Y-m-01") %}
{% set firstDayOfNextMonth = "now"|date_modify("first day of next month")|date("Y-m-01") %}
{% set postDateParam = "and, >= " ~ firstDayOfMonth ~ ", < " ~ firstDayOfNextMonth %}

{# Create our parameters object #}
{% if (category is defined) and (category is not empty) %}
    {% set params = {section: 'calendar', postDate: postDateParam, status: null, order: 'postDate', relatedTo: category} %}
{% else %}
    {% set params = {section: 'calendar', postDate: postDateParam, status: null, order: 'postDate'} %}
{% endif %}

{# Retrieve all of the events for the month, and group them by day #}
{% set groupedEvents = craft.entries(params)|group('postDate|date("Y-m-d")') %}

{# Set a shortcut for the YYYY-MM portion of the current month #}
{% set yearMonth = "now"|date("Y-m") %}

{# Loop through all of the days in the month #}
{% for day in range(1, daysInMonth) %}
    {% set formattedDay = "%02d"|format(day) %}
    {% set dayOfMonth = yearMonth ~ "-" ~ formattedDay) %}

    {# If the current day is a key in our groupedEvents array, we have events #}
    {% if dayOfMonth in groupedEvents|keys %}
        <span class="day">{{ dayOfMonth }}</span>

        <div>
            {% for event in groupedEvents[dayOfMonth] %}
                <span class="event green">{{ event.title }}</span>
            {% endfor %}
        </div>
    {% endif %}
{% endfor %}

As I said, I haven't tested the above code, but the basic principle works (I've used it for grouping people by surname, and so forth).

Let me know if anything is unclear, I realise it's quite the chunk of Twig.

Stephen

  • 2
    Stephen, you need to change the date filtering to get all entries within a datetime range. See this Q/A for more info. – carlcs Aug 12 '14 at 20:48
  • Thanks Stephen - works well - needed to change the date filtering as carlcs said. But I'm not getting all events pulled in - missing one on the 1st of the month. will add code below – mmc501 Aug 15 '14 at 6:22
  • 1
    @mmc501 when did you add your events to Craft? There were a couple of timezone related bugs which got fixed with Craft version 2.0.2528, 1.2.2375 and 0.9.2235. I'd try to edit the date of that missing event on the 1st of the month with the timepicker (change it to the 2nd → save, change it back to the 1st → save). – carlcs Aug 15 '14 at 8:55
  • Within the last couple of weeks. Tried editing the date of the event - discovered that if it's the 1st - 9th of the month it will not display but from the 10th on it will - must be something to do with single figure date - will have a play with it not to see if can resolve. – mmc501 Aug 15 '14 at 9:54
  • @mmc501 I've updated my example to format the day as a two-digit number (take a look at the first line of the for day in range loop). – Stephen Lewis Aug 15 '14 at 10:35
2

Just an update to show final current version of code for calendar - re stefan comment.

  {% if year is defined and month is defined %}
      {% set time = (year ~ "-" ~ month ~ "-" ~ "01") %}
      {% set catslug = craft.app.request.segment(2) %}
      {% if catslug != 'all' %}
        {% set category = craft.categories.group('eventFilters').slug(catslug) %}
      {% endif %}
  {% endif %}
  {% if time is not defined%} 
      {% set time = "now" %}
  {% endif %}
  {% if catslug is not defined or catslug is empty%} 
      {% set catslug = "all" %}
  {% endif %}

  {% set thisyear = time|date("Y") %}
  {% set thismonth = time|date("m") %}
  {% set prevyear = time|date_modify("-1 month")|date("Y") %}
  {% set prevmonth = time|date_modify("-1 month")|date("m") %}
  {% set nextyear = time|date_modify("+1 month")|date("Y") %}
  {% set nextmonth = time|date_modify("+1 month")|date("m") %}



  <!-- CONTENT -->

  <div class="row calhead text-center align-items-center py-2 mb-3">
      <div class="col-auto">
        <a class="calchange calleft" href="#" data-slug="{{ catslug }}" data-month="{{ prevmonth }}" data-year="{{ prevyear }}">
          <span class="fa-stack ">
            <i class="fas fa-circle fa-stack-2x"></i>
            <i class="fas fa-angle-left fa-stack-1x fa-inverse"></i>
          </span>  
        </a>
      </div>

      <div class="col"><div class="caldate">{{ time|date("F")}} {{ time|date("Y")}}</div></div>
      <div class="col-auto">
        <a class="calchange calright" href="#" data-slug="{{ catslug }}" data-month="{{ nextmonth }}" data-year="{{ nextyear }}">
          <span class="fa-stack ">
            <i class="fas fa-circle fa-stack-2x"></i>
            <i class="fas fa-angle-right fa-stack-1x fa-inverse"></i>
          </span>          
        </a>
      </div>

  </div>


  <div class="dayscontainer clearfix pb-3">
    <div class="row text-center dayhead mb-3">
      <div class="col">M</div>
      <div class="col">T</div>
      <div class="col">W</div>    
      <div class="col">T</div>
      <div class="col">F</div>
      <div class="col">S</div>
      <div class="col">S</div>
    </div>

    <div class="row text-center mb-1">
        {% if year is defined and month is defined %}
            {% set time = (year ~ "-" ~ month ~ "-" ~ "01") %}
        {% endif%}
        {% if time is not defined%} 
            {% set time = "now" %}
        {% endif %}

            {% set daysInMonth = time|date('t') %}
            {% set startDow = time|date('F 1\\s\\t Y')|date('w') %}
            {% set dow = startDow %}



        {% set yearMonth = time|date("Y-m") %}                 

        {% set startdate=(yearMonth ~ "-" ~ "01") %}
        {% set enddate=(yearMonth ~ "-" ~ daysInMonth) %}
         {# Create our parameters object #}

        {% if (category is defined) and (category is not empty) %}
        {% set params = { section: 'events', orderBy: 'eventDate', relatedTo: category } %}
        {% else %}
        {% set params = { section: 'events', orderBy: 'eventDate' } %}
        {% endif %}          
        {% set j = 0 %}
        {# Retrieve all of the events for the month, and group them by day #}
        {% set groupedEvents = craft.entries(params).eventDate(['and', '>= ' ~ startdate|date('Y-m-d'), '<= ' ~ enddate|date('Y-m-d')]).all() | group('eventDate|date("Y-m-d")') %}



          {% for day in range(1,daysInMonth) %}
            {% set formattedDay = "%02d"|format(day) %}
            {% set dayOfMonth = yearMonth ~ "-" ~ formattedDay %}       

            {# set dayOfMonth = "#{yearMonth}-#{day}" #}
            {% if startDow == 0 %}
              {% set startDow = 7 %}
            {% endif %}


              {% if loop.first and startDow > 1 %}
                {% for i in range(1,startDow-1) %}
                  {% set j = j + 1 %}
                  <div class="col">&nbsp;</div>
                {% endfor %}              

              {% endif %}
              {% set j = j + 1 %}

              {% if j == 8 %}

                </div>
                <div class="row text-center mb-1">
                {% set j = 1 %}
              {% endif %}

              <div class="col">

                {# If the current day is a key in our groupedEvents array, we have events #}
                {% if dayOfMonth in groupedEvents|keys %}
                  {% set ccontent %}
                      {% for entry in groupedEvents[dayOfMonth] %}
                      {% set category = entry.eventFilter.one() %}

                      <span class="event"><a href="{{ entry.url }}">{{ entry.title }}</a></span>
                      {% endfor %}
                  {% endset %}

                  <span class="day"><a role="button" class="pop daylink" data-toggle="popover" data-trigger="click hover focus" data-html="true" title="Events" data-content='{{ ccontent }}'>{{ day }}</a></span>
                {% else %}
                  <span class="day">{{ day }}</span>  

                {% endif %}
              </div>

              {% if loop.last and j < 7 %}
                {% for i in range(1,7 -j) %}
                  <div class="col">&nbsp;</div>
                {% endfor %}              

              {% endif %}              

          {% endfor %}
    </div>
  </div>
  • Thanks a lot! I will give this a try as soon as possible! – outline4 Aug 8 at 11:23

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.