Mix and sort multiple collections with different date fields

Hi. I am having a hard time mixing and sorting 2 collections. One is a feed collection of instagram posts via the Instagram feed plugin (Instagram Feed | Kirby CMS) and the other is a collection of news articles in kirby.
The instagram feed has a timestamp field and the news have a date field.
My question is: how can i mix them and sort them by date? I wanted to show a list of news-items and insta-posts sorted by their dates.
I can only guess that i need to first create a new collection with ids & date-fields rewritten to match a sorting and then loop through them, connecting them to their original sources via id or slug…

Are the collections mergeable? If so, you could merge them, then run the map method to assign a custom method with a value and sort by this value, something like

$feed = collection('feed');
$articles = collection('articles');

$all = $feed->append($articles);
$sorted = $all->map(function($child) {
  $child->sortfield = $child->timestamp()->isNotEmpty() ? $child->timestamp() :     $child->date()->toDate();
  return $child;
})->sortBy('sortfield');

Not tested and you will have to adapt the fieldnames to what you are actually using.

It seems as if they can’t merge. If i use:
$feed->append($articles)
I get all the feed items as items, but all articles become one item.
If i use:
$articles->append($feed)
I get a cryptic message:
Argument 1 passed to Kirby\Cms\Collection::__set() must be of the type string, null given, called in /…/kirby/src/Toolkit/Collection.php on line 899

This is all without the map function, just the appending.

When i do a var_dump on $feed and $articles, it says that $feed is
object(Kirby\Toolkit\Collection)
and $articles is
object(Kirby\Cms\Pages)

I am not sure if i understand this, but it seems tricky to merge both. Is there a way to figure out if a collection can merge with another?

Further probing around:
If i use
$all = $feed->merge($articles);
and do a var_dump, the result is NULL
If i do:
$all = $articles->merge($feed);
i get all the articles, but the feed is completely gone.

And what sort of objects are contained in the feed collection?

foreach($feed as $item) {
  dump($item);
}

Can’t download the plugin because it’s behind a paywall.

Maybe back to your original idea. If you create a new collection or array from each of the collections:

$arrayToSort = [];

foreach($articles as $article) {
  $arrayToSort[$article->id()] = $article->date()->toDate();
}

foreach($feed as $item) {
  $arrayToSort[$item->id()] = $feed->timestamp();
}

sort($arrayToSort);

foreach($arrayToSort as $key => $value) {
  if ($page = $articles->findById($key)) {
    echo $page->title();
  }
  if ($item = $feed->findByKey('id', $key)) {
    echo $item->caption();
  }
}
1 Like

You can try:

$all = $articles->merge(site()->instagramFeed());

But I’m not sure about pages collection (\kirby\cms\collection) is mergable with not pages collection (\kirby\toolkit\collection). Even when merged, page and pages collection have their own different methods from feed collection.

This works nicely. I had to make some slight changes and it sorts nicely.

Now i am stuck with another problem: If i want to find the items again, i need to compare the new id’s (= unix timestamps) with the different dates of articles and feeditems.

Articles have this format: ‘Y-m-d.“T”.H:i:s’
While the feed looks like this: 2022-03-16T17:09:48+0000

I am still trying to figure out how i need to format a unix timestamp to look like the feed date, i am wondering: Is there a way to compare the timestamp from the new array with the date of an article or feeditem when it is formatted to a timestamp as well?

Right now i can only compare: $articles->findBy(‘date’, $key) and thus comparing:
2022-04-11 08:08:25 with 1649657305

Easiest would be: $articles->findBy(date->toDate(), $key) to compare just the timestamps…
Is there a way to do this? Would this involve a callback function like in the filtering compendium (Filtering compendium | Kirby CMS)?

I think i got it down:

<?
	$feed = //->path to feed;
	$articles = // path to articles;
	$arrayToSort = [];

	foreach($articles as $article) {
		$arrayToSort[$article->id()] = $article->date()->toDate();
	}
	foreach($feed as $item) {
		$arrayToSort[$item->id()] = strtotime($item->timestamp());
	}
	rsort($arrayToSort);
?>
<? foreach($arrayToSort as $key): ?>
	<? $article = $articles->filter(function ($child) use ($key) {return $child->date()->toDate() == $key;})->first();?>
	<? $insta = $feed->filter(function ($child) use ($key) {return strtotime($child->timestamp()) == $key;})->first();?>

	<? if($article): ?>

		<!-- Do something here with article object. -->

	<? elseif($insta): ?>

		<!-- Do something here with feed object. -->

	<? endif ?>

Works nice on my end now. I tried to directly compare dates, but the instagram time format never matched mine, i think timezones and details of formatting played into this.

Why did what I suggested above (findById, findByKey(‘id’)) not work?

Finding by date doesn’t really make sense, because dates are not necessarily unique.

If i use your script and paste it, nothing gets returned.

If i output the $key in the foreach loop, i get numbers from 1 - 124, and comparing these with the ids of the articles collection doesn’t match.

But i am also not really understanding the logic. First we write a new collection ($arrayToSort) by adding kirby objects (id) that have an id based on their date, right?
But these ids never existed in the original collections, where i need to match the newly sorted collection against.
Thats why i thought the unix timestamp should be as unique as possible… Also, since the ids have been created using the date field and timestamp respectively, doesn’t it make sense to match these back?