Group function with two values

Hello,
i would like to group a collection by two values. I am not sure, if this is the right way to do it at all, but here is what i would like to archive:

I have a list of events with a From and To date, and sometimes the events have the same dates.
So i would like to group these events with the exact same From and To date.
With goupBy thats not possible i think, but how about the group function?
Is group even the right way?
How can i check both dates, not only one?

Thanks in advance,
Matthias

So you have events that end start on the same dates and some that last multiple days, correct? What you want is to have a group of same day events and another of multiple days events?

hello pedro,
this is what i have:

event 1
01.01.2017 - 01.05.2017

event 2
01.01.2017 - 01.05.2017

event 3
10.02.2017 - 01.09.2017

this how i would like to group them:

01.01.2017 - 01.05.2017
event 1
event 2

10.02.2017 - 01.09.2017
event 3

Okay, you can map the collection first to add the two values together then use the groupBy() method:

$pages = $pages->map(function($page) {
    $page->eventDate = $page->date('d.m.Y', 'from') . ' - ' . $page->date('d.m.Y', 'to');

    return $page;
})->groupBy('eventDate');

I’m not able try this solution right now. Let me know if that works for you.

hey pedro, thanks.
unfortunately this gives me an error message:
Invalid grouping value for key: exhibitions/ressentiment

so it seems it is not accepted…

How are your events stored? As subpages, as structure field?

Please explain how your events are stored and post the grouping code so we can better help you.

ok, ok, sure:

i store the dates in normal fields of subpages, so each event is a subpage. this gives me a collection of events that have date_from() & date_to() stored in normal fields.

here is my code so far (according to pedro):

		<?php 
			$pages = $site->page('events')->children()->visible()->map(function($page) {
				$page->eventDate = $page->date_from().'–'.$page->date_to();
				return $page;
			})->groupBy('eventDate')
		?>

on a sidenote:
It is also important to know (since i don’t know much about the map function, that i need to work with the original dates as well to determine wether the event is in the future, past or happening at the moment…

thanks

Try this instead:

<?php
  $pages = page('events')->children()->visible()->map(function($page) {
    $page->eventDate = $page->date_from() . '–' . $page->date_to();
    return $page;
  })->group(function($p){
    return $p->eventDate();
  });

  foreach($pages as $group => $itemList) {
    echo $group;
    foreach($itemList as $item) {
      echo $item->title();
    }
  }
?>
1 Like

YES! :grinning: its working. Thanks a lot…

I am afraid, i need one more answer, because its slightly more complex than i thought:
first: i realize have two kinds of events:
1.with date_from() & date_to() values
2.with only one date_from() (these last only one day)

i would like to show both in a list, the first kind of events grouped by its dates, but all of them together sortedBy by one date, the date_from().

This is how i thought it would work:

<?php
  $pages = page('events-1')->children()->visible()->map(function($page) {
    $page->eventDate = $page->date_from() . '–' . $page->date_to();
    return $page;
  })->group(function($p){
    return $p->eventDate();
  });
  $events2 = page('events-2')->children()->visible()
  $allevents = new Pages(array($pages, $events2));


  foreach($allevents->sortBy('date_from', 'desc') as $group => $itemList) {
    echo $group;
    foreach($itemList as $item) {
      echo $item->title();
    }
  }
?>

But this is wrong on a couple of levels. The events-2 don’t have that group jacket, so the foreach loop won’t work. Also, the $pages collection doesn’t have a date_from() value.
So my last (promise) question about this is: how can i get these two very different collections: $pages & $events2 in a collection, where i could sort them by date? if i would merge them before the group function, i get strange results with the eventDate() value (for example: 10.10.2017- )…

I think this question is very very specific for my needs, so don’t bother answering if this gets too complicated. But if you see a solution right away, please let me know, i’d be very happy to get it…

Matthias

This should work:

    <?php
      $events1 = page('events')->children();
      $events2 = page('events-2')->children();
      $allevents = new Pages(array($events1, $events2));

      $pages = $allevents->map(function($page) {
        if($page->to()->isNotEmpty()) {
          $page->eventDate = $page->from() . '–' . $page->to();
        } else {
          $page->eventDate = $page->from();
        }
        return $page;
      })->group(function($p){
        return $p->eventDate();
      });


      foreach($pages->sortBy('from', 'desc') as $group => $itemList) {
        echo $group;
        foreach($itemList as $item) {
          echo $item->title();
        }
      }
    ?>

Please note that I have renamed the field names for testing (and now I’m too lazy to rename them again)

:slight_smile: thanks texnixe,
this works up untill the sorting:
The sortBy doesn’t seem to make any impressions on the actual sorting. desc or asc result in the same list. in fact, any value has the same effect, so i believe my value (date_from) is somehow not recognized at that place…
is there a mistake on my part? did you sortBy change direction with a change to ‘asc’ ?

Maybe i can add this here as well: It would be very helpful also, if i would be able to ask for $group->date_from() and $group->date_to() to determine if the event is in the future, past or present.
and asking for that value results in this message:
Call to a member function date_from() on a non-object

Aaaaaaand on top of this: Since you have helped me quite a lot already (also in the past), i’d be happy to send you some gifts. I have a little publishing house, doing artbooks mainly.
Here you can see the books i am making:
http://possible-books.com/en/publications

If you like, i can send you a few books as a Thank You.

Matthias

Maybe do the sorting first:

$allevents = new Pages(array($events1, $events2));
$allevents = $allevents->sortBy('date_from', 'desc');

As regards the filtering by date, what exactly do you want to do? Only list future and present events? Anyway, you would have to use a filter with a callback function and do that before you start grouping.

For example, to filter future events:

$allevents = $allevents->filter(function($child) {
  return $child->date(null, 'date_from') > time();
})->sortBy('date_from', 'desc');

Thanks for your kind offer, that would be very nice :slight_smile:

wow, this all works really nicely.
may i ask again? if i want to differentiate between dates of: past, future, present (from is past, to is future) and today, i can define that as filters, right?
is there an elegant way to send them through the grouping, or do i have to write the same code 4 times?
right now, it would look like this:

    <?php
		$events1 = page('events1')->children()->visible();
		$events2 = page('events2')->children()->visible();
		$allevents = new Pages(array($events1, $events2));

		$futureevents = $allevents->filter(function($child) {
  			return $child->date(null, 'date_from') > time();
		})->sortBy('date_from', 'desc');

		$pastevents = $allevents->filter(function($child) {
  			return $child->date(null, 'date_from') < time();
		})->sortBy('date_from', 'desc');

		$presentevents = $allevents->filter(function($child) {
  			return $child->date(null, 'date_from') < time() && $child->date(null, 'date_to') > time();
		})->sortBy('date_from', 'desc');

		$todayevents = $allevents->filter(function($child) {
  			return $child->date(null, 'date_from') == date('d.m.Y');
		})->sortBy('date_from', 'desc');


		$futureevents =$futureevents->map(function($page) {
			if($page->date_to()->isNotEmpty()) {
				$page->eventDate = $page->date('d.m.Y', 'date_from') . '–' . $page->date('d.m.Y', 'date_to');
			} else {
				$page->eventDate = $page->date('d.m.Y', 'date_from');
			}
			return $page;
			})->group(function($p){
			return $p->eventDate();
		});
		$pastevents = $pastevents->map(function($page) {
			if($page->date_to()->isNotEmpty()) {
				$page->eventDate = $page->date('d.m.Y', 'date_from') . '–' . $page->date('d.m.Y', 'date_to');
			} else {
				$page->eventDate = $page->date('d.m.Y', 'date_from');
			}
			return $page;
			})->group(function($p){
			return $p->eventDate();
		});
		$presentevents = $presentevents->map(function($page) {
			if($page->date_to()->isNotEmpty()) {
				$page->eventDate = $page->date('d.m.Y', 'date_from') . '–' . $page->date('d.m.Y', 'date_to');
			} else {
				$page->eventDate = $page->date('d.m.Y', 'date_from');
			}
			return $page;
			})->group(function($p){
			return $p->eventDate();
		});
		$todayevents = $todayevents->map(function($page) {
			if($page->date_to()->isNotEmpty()) {
				$page->eventDate = $page->date('d.m.Y', 'date_from') . '–' . $page->date('d.m.Y', 'date_to');
			} else {
				$page->eventDate = $page->date('d.m.Y', 'date_from');
			}
			return $page;
			})->group(function($p){
			return $p->eventDate();
		});
	?>
		<?php foreach($futureevents as $group => $itemList): ?> ....

I have the feeling there must be a simpler way to do this…

You could define a custom pages method

<?php
    pages::$methods['groupByDate'] = function($pages) {
      return $pages->map(function($page) {
        if($page->to()->isNotEmpty()) {
          $page->eventDate = $page->from() . '–' . $page->to();
        } else {
          $page->eventDate = $page->from();
        }
        return $page;
      })->group(function($p){
        return $p->eventDate();
      });
    };

Then you can use it like this:

$futureevents = $futureevents->groupByDate();.

Looks like a lot less code, doesn’t it?

Looks beautiful!

The only thing is, somehow the values for date_from & date_to seem to be lost, so it groups now ALL past events into one group.
Does this has to do with the fact, that i name the different times now: $future, $past, $present and in the custom page method it is callled $pages???
I am sorry, but i really don’t know what is being transferred or not, and what clicks together (like a puzzle).

Maybe the code could be helpful:

	<?php 
	$exhibitions = page('exhibitions')->children()->visible();
	$events = page('events')->children()->visible();
	$allevents = new Pages(array($exhibitions, $events));

	$future = $allevents->filter(function($child) {
		return $child->date(null, 'date_from') > time();
	})->sortBy('date_from', 'desc');
	$past = $allevents->filter(function($child) {
		return $child->date(null, 'date_from') < time();
	})->sortBy('date_from', 'desc');
	$present = $allevents->filter(function($child) {
		return $child->date(null, 'date_from') < time() and $child->date(null, 'date_to') > time();
	})->sortBy('date_from', 'desc');
	$today = $events->filter(function($child) {
		return $child->date_from() == date('Y-m-d');
	})->sortBy('date_from', 'desc');

	$future = $future->groupByDate();
	$past = $past->groupByDate();
	$present = $present->groupByDate();
	$today = $today->groupByDate();
?>

the custom page method

<?php
	pages::$methods['groupByDate'] = function($pages) {
		return $pages->map(function($page) {
			if($page->to()->isNotEmpty()) {
					$page->eventDate = $page->from() . '–' . $page->to();
				} else {
					$page->eventDate = $page->from();
				}
				return $page;
			})->group(function($p){
				return $p->eventDate();
			});
		};