Hi dear community,
I’m currently coding the website of a small french movie theater (already online at https://cinemalasalamandre.fr). In the panel, each movie has its own page with screening schedules listed in a structure field that looks like:
schedules:
type: structure
fields:
day:
type: date
display: DD/MM/YYYY
hour:
type: time
display: H:mm
In the front, I’d like to create a page displaying a calendar with movies grouped by month, by day and by four time slots (depending on when the screenings begin). The generated HTML for each month should be:
<h2>July 2021</h2>
<ul class="days">
<li>
<h3>Wednesday 7</h3>
<ul class="slots">
<li>
<h4>From 14:00 to 16:00</h4>
<p>Movie title</p>
<p>Movie title</p>
</li>
<li>
<h4>From 16:00 to 18:00</h4>
<p>Movie title</p>
<p>Movie title</p>
<p>Movie title</p>
</li>
<li>
<h4>From 18:00 to 20:00</h4>
<p>Movie title</p>
<p>Movie title</p>
<p>Movie title</p>
</li>
<li>
<h4>From 20:00 to 22:00</h4>
<p>Movie title</p>
<p>Movie title</p>
<p>Movie title</p>
<p>Movie title</p>
</li>
</ul>
</li>
<li>
<h3>Thursday 8</h3>
…
Currently, all the scheduled movies are stored in a collection called ‘scheduledmovies’ (original isn’t it ).
I really don’t know how to code properly this calendar as my PHP skills are limited when it comes to complex foreach loops. Is there a kind dev who could help me please?
Thank you
Kirby has a group()
function that helps you group a collection into whatever categories they fall into.
You can find an example of how to use it for pages:
And more here:
1 Like
That’s a good start! Thanks for pointing to these grouping functions pixelijn.
I’m sure I’ll find a way to manage this, even if it takes a week and my code is dirty
1 Like
On a side note, you may find my Recurr plugin useful. It is for recurring dates like events and film showings. GitHub - HashandSalt/kirby-recurr: Plugin for listing recurring dates
Its also capable of generating standard RRules which you can feed straight into something like FullCalendar on the front end. https://fullcalendar.io/
I’ve played around with the grouping functions but I miss the logic to make it work as expected. My brain is giving me pain right now
Should I first create a new collection from ALL the schedules listed in ALL movies, group it per month, then filter the ‘scheduledmovies’ collection for each day and time slot?
Or is it better/possible to group the schedules from the ‘scheduledmovies’ collection directly per time slot > day > month?
I’m completely lost, sorry…
Thanks Jim for your input. But I’d prefer to create the calendar from scratch instead of relying on an existing plugin. This way I can improve my PHP skills and, more important, because the final result will not be used as a standard calendar. It will become a tool for users to apply as volunteers in the movie theater. See attached a preview of the page I’d like to build.
Sure… the plugin just gives you a PHP array of all the dates and times. How you display those is up to you.
1 Like
Yes, first you need a collection of all structure items filtered for the given month.
And then this collection needs to be grouped by day and month.
Hi dear Kirby community!
I did not expect this to take me sooooo long (9 days to type the following lines) but the calendar starts to look like the capture above
However, I still have an issue which drives me crazy… I will explain it below. But first, I need to explain a bit my (ugly) functions so you get the context.
- I created a new collection including ALL upcoming screening dates:
<?php
$alldates = new Collection();
$scheduledmovies = $kirby->collection('scheduledmovies');
foreach($scheduledmovies as $scheduledmovie){
// Add to the new collection all the items of the scheduled movies structure that have a day in the future and an associated hour
$alldates->add($scheduledmovie->schedules()->toStructure()->filter(function ($schedule) {
return $schedule->day()->toDate('%F') >= date('Y-m-d') && $schedule->hour()->isNotEmpty();
}));
} ?>
- Then I group the new collection based on the dates:
<?php
// Per month
$groupedSchedulesPerMonth = $alldates->sortBy('day')->group(function($p) {
return $p->day()->toDate('%Y-%m');
});
foreach($groupedSchedulesPerMonth as $month => $schedulesPerMonth): ?>
<h2><?= strftime('%B %Y', strtotime($month)) ?></h2>
<ul class="days">
<?php
// Per day
$groupedSchedulesPerDay = $schedulesPerMonth->group(function($p) {
return $p->day()->toDate('%F');
});
foreach($groupedSchedulesPerDay as $day => $schedule): ?>
<li>
<h3><?= str_replace('.', '',strftime('%A %d', strtotime($day)))?></h3>
<ul class="slots">
- And now I try to display the scheduled movies in the appropriate time slots depending on when the screenings begin:
<li>
<h4>From 14:00 to 16:00</h4>
<?php
// Movies begining before 16:00
$filteredScheduledMovies = $scheduledmovies->filter(function ($child) use($day) {
return $child->schedules()->toStructure()->filter(function ($item) use ($day) {
return ($item->day()->toDate('%F') == $day) && ($item->hour()->toDate('%H') < 16);
});
});
foreach($filteredScheduledMovies as $scheduledmovie){
echo '<p>'. $scheduledmovie->title() . '</p>';
} ?>
</li>
<li>
<h4>From 16:00 to 18:00</h4>
<?php
// Movies begining between 16:00 and 18:00
$filteredScheduledMovies = $scheduledmovies->filter(function ($child) use($day) {
return $child->schedules()->toStructure()->filter(function ($item) use ($day) {
return ($item->day()->toDate('%F') == $day) && ($item->hour()->toDate('%H') >= 16) && ($item->hour()->toDate('%H') < 18);
});
});
foreach($filteredScheduledMovies as $scheduledmovie){
echo '<p>'. $scheduledmovie->title() . '</p>';
} ?>
</li>
<li>
<h4>From 18:00 to 20:00</h4>
<?php
// Movies begining between 18:00 and 20:00
$filteredScheduledMovies = $scheduledmovies->filter(function ($child) use($day) {
return $child->schedules()->toStructure()->filter(function ($item) use ($day) {
return ($item->day()->toDate('%F') == $day) && ($item->hour()->toDate('%H') >= 18) && ($item->hour()->toDate('%H') < 20);
});
});
foreach($filteredScheduledMovies as $scheduledmovie){
echo '<p>'. $scheduledmovie->title() . '</p>';
} ?>
</li>
<li>
<h4>From 20:00 to 22:00</h4>
<?php
// Movies begining after 20:00
$filteredScheduledMovies = $scheduledmovies->filter(function ($child) use($day) {
return $child->schedules()->toStructure()->filter(function ($item) use ($day) {
return ($item->day()->toDate('%F') == $day) && ($item->hour()->toDate('%H') >= 16) && ($item->hour()->toDate('%H') < 18);
});
});
foreach($filteredScheduledMovies as $scheduledmovie){
echo '<p>'. $scheduledmovie->title() . '</p>';
} ?>
</li>
</ul>
</li>
<?php endforeach ?>
</ul>
<?php endforeach ?>
The code above (3) is not working properly. Currently, the $filteredScheduledMovies collection echoes ALL the scheduled movies in each time slot as if the filter per day and hour was not working.
Could someone here understand why? What should I change in my code?
This is the issue I’m facing today. But I’m sure I’ll have another one soon. I prefer to explain it right now in case this lead to a completely different logic:
Users should be able to click one or more time slots in the calendar to apply as volunteer at the movie theater… and I don’t know how to manage that. Should I generate a unique ID for each time slot and store them in the user’s page once clicked? Should I create virtual pages for that? Structure? Collection? I’m a bit lost… again… sorry…
Thank you all for your time and your help
PS: If you plan to go to Morlaix (France), the movie theater La Salamandre has a really great programming, and their website can help you decide what you gonna watch: https://cinemalasalamandre.fr
I think here you should filter $schedule
(because that is you current day context), not $scheduledmovies
.
Thanks (again) for your help pixelijn. I tried your proposal but it didn’t work…
I finally and hopefully managed to make my functions echo the right movies in the right month/day/time slots by adding a simple boolean, as suggested by Sonja in this post
$filteredScheduledMovies = $scheduledmovies->filter(function ($child) use($day) {
return $child->schedules()->toStructure()->filter(function ($item) use ($day) {
return ($item->day()->toDate('%F') == $day) && ($item->hour()->toDate('%H') >= 16) && ($item->hour()->toDate('%H') < 18);
})->count();
});
Here is the current result:
Now I’ll have to find a proper way to make each time slot clickable by users so the movie theater team will know who will be contributing as a volunteer