Hello,
On the frontend I am sorting $page->children() by using a ‘complex’ function as a custom page method, and I would like to sort the same pages in a pages section in the panel using the same or equivalent function which in pseudocode it goes like this:
create new pages collection
add all children who have field 'pinned' as 'true', sorted by field 'publicationDate' descending
add all children who have field 'pinned' as 'false', sorted by field 'publicationDate' descending
return the collection
According to the reference the sortBy option of pages section allows only for a field, and nothing else.
Could I extend the pages section in order to add a custom sorting option that uses my custom page method?
If yes, where can I find the pages section code ?
Thank you
Your custom method should then do the job, no?
sortBy: customethodname
The method extends $page, so I call it as $page->customMethod()
You mean I can just do as you suggest on the pages section and it will use the actual page where the pages section is in place of $this? and then it will load the custom collection ?
sortBy: customethodname
Does not seem to work.
This is the full blueprint at
site/pages/programme.yml
I am using the method below the ‘updates’ tab and the ‘public’ pages section.
title: Programme
tabs:
info:
label: Info
icon: text
fields:
info:
type: textarea
label: Info text
size: large
autofocus: false
buttons:
- link
- email
publicationDate:
type: hidden
updates:
label: Updates
icon: funnel
columns:
- width: 1/2
sections:
draft:
type: pages
headline: Draft
text: "{{ page.intendedTemplate }} | {{ page.title }}"
status: draft
sortable: false
sortBy: publicationDate
templates:
- announcement
- event
- call
- article
- width: 1/2
sections:
public:
type: pages
headline: Public
status: listed
sortBy: sortedUpdates
text: "{{ page.intendedTemplate }} | {{ page.title }}
And the custom method at
site/plugins/page-methods/index.php
<?php
Kirby::plugin('my/page-methods', [
'pageMethods' => [
'sortedUpdates' => function () {
$sortedUpdates = new Pages();
if($this->children()->listed()->count() > 0) {
$twoMonthsAgo = new DateTime();
$twoMonthsAgo->modify('-2 month');
$sortedUpdates = $this->children()->listed()->filterBy('pinned', 'true')->sortBy('publicationDate', 'desc');
foreach($this->children()->listed()->filterBy('pinned', 'false')->sortBy('publicationDate', 'desc') as $c) {
$dateIt = new DateTime($c->publicationDate());
if($dateIt > $twoMonthsAgo ) {
$sortedUpdates->append($c);
}
}
}
return $sortedUpdates;
}
]
]);
This is a screenshot of the result at frontend and backend:
Oh, alright, I didn’t look close enough. What you use here as a page method is not really a page method but more a pages method, so you can’t really sort by that. Your custom page method or model would have to return a value for each individual page that you can sort by.
Such a page model could look like this:
public function indexValue() {
$collection = $this->sortedUpdates();
return $collection->indexOf($this);
}
Then in blueprint:
sortBy: indexValue
Oh I see,
in your example $this is each page that I am sorting, right?
Perhaps I could use my method on $this siblings, then find the index of $this in the produced collection?
<?php
Kirby::plugin('my/page-methods', [
'pageMethods' => [
'sortedUpdates' => function () {
$sortedUpdates = new Pages();
if($this->siblings()->listed()->count() > 0) {
$twoMonthsAgo = new DateTime();
$twoMonthsAgo->modify('-2 month');
$sortedUpdates = $this->siblings()->listed()->filterBy('pinned', 'true')->sortBy('publicationDate', 'desc');
foreach($this->siblings()->listed()->filterBy('pinned', 'false')->sortBy('publicationDate', 'desc') as $c) {
$dateIt = new DateTime($c->publicationDate());
if($dateIt > $twoMonthsAgo ) {
$sortedUpdates->append($c);
}
}
}
return $sortedUpdates->indexOf($this);
}
]
]);
Seems quite overkill to do all that for each page, tho… It would make more sense to have the $sortedUpdates collection created once when the sorting starts, and use it in each instance of the loop to find $this indexOf.
I can’t create it beforehand because the code uses DateTime to get the actual date and time to calculate $twoMonthsAgo.
This is probably not possible, I assume?
Thanks
My example was meant as a page model, assuming your pages in the collection are using the same template.
class SomePage extends Page {
public function indexValue() {
$collection = $this->sortedUpdates();
return $collection->indexOf($this);
}
}
I can confirm using siblings() and indexOf() in the code I added to my last response does sort as expected.
So I understand your page model would extend the page object with a custom method only for the SomePage template.
I also see this allows me to not write the sortedUpdates() code again, and I understand I can also use it as sortBy: indexValue
But, this does process the same code, right? it processes sortedUpdates() once per loop, once per $this to be sorted.
And also, then I should call sortedUpdates() on the parent of $this right?
class SomePage extends Page {
public function indexValue() {
$collection = $this->parent()->sortedUpdates();
return $collection->indexOf($this);
}
}
Thanks
I’d consider using a custom collection that you cache and update the cache when something changes. That way while you still have to use the collection to get the index of the page, it doesn’t have to be recreated each time which might slow down if you have a lot of pages.
The problem, as I explain above is that I
I can’t create [the collection] beforehand because the code uses DateTime to get the actual date and time to calculate $twoMonthsAgo.
The actual date and time at the moment of sorting, that is. Which is why I asked for a way to create the collection when sorting starts, and then use the collection in each loop for each $this indexOf. But that does not seem to be something possible.
I did not know that I could use custom methods with sortby in blueprints. Is this explained explicitly in the reference or guide ? Could you please direct me there so I can summarize a full answer?
thank you
No, I don’t think so, but the query languages works basically in the same way as the API. Admittedly, blueprint queries are currently not as powerful as the API (will be better in 3.2), but the main functionality is the same.
1 Like