Jump to content

[SOLVED] Sorting repeater fields in API


MarkE
 Share

Recommended Posts

I am editing repeater field subpages for a page using the API. As I edit each one, I assign it a sequence. I then sort them by that sequence, viz:

$page->$repeaterName->sort('sequence');

When I look at the page in the UI, the sort seems to have worked OK. However, when I then process the repeaters, the processing happens in the original sequence, not my sort order. Inspecting the page with Tracy (see below), it is clear that the array keys retain the original order even though the displayed order is correct - and it is the array keys that determine the processing sequence. Any ideas how I fix this?

1856154272_Repeatersequence.jpg.515fc02abefe5daf0a74d93b17832789.jpg

Edited by MarkE
solved
Link to comment
Share on other sites

  • MarkE changed the title to [SOLVED] Sorting repeater fields in API

Now solved. For the benefit of others, you need to set the sort property of the repeater rather than use a custom property.

i.e. for each repeater page:

$subPage->sort = $j;  // where $j is the required sort index
$subPage->save();

then after all 'subPages' have been set:

$page->$repeaterName->sort('sort');

and save the page.

  • Like 3
  • Thanks 1
Link to comment
Share on other sites

  • 4 weeks later...
On 6/16/2021 at 10:03 AM, MarkE said:

and save the page.

Hi MarkE, sorry for bothering you...

Unfortunately, compared to expectations, it didn't work out as I hoped.

In my case, I have a matrix of scores stored by race, but when I add one or more races (repeater items) for all the competitors, some of this items shift randomly (4 digits are the id of the items, only for debug). Not all the times, but "sometimes"... also randomly... Completing the blank scores, the ID clarify the shift of the repeater items...

1153054547_Schermata2021-07-10alle17_17_32.jpg.36e84f3b4f6a575ea094c63f14c67125.jpg 831337889_Schermata2021-07-10alle17_18_29.jpg.043e99975f0b3c0af8e68d1f1dd2b2af.jpg 1443728810_Schermata2021-07-10alle17_19_25.jpg.37a19e4638509f0ba5ce79ca5d1d45ec.jpg

The ADD/REMOVE items process is done by hook, with getNew() and add() methods (also tried append(), but nothing changed)...

In Tracy Debug I can order the repeaters properly, but nothing more: it seems that after sorting the page is not saved ...

1114992945_Schermata2021-07-10alle17_34_54.jpg.e588baccd97ba982492994bc4bd3749d.jpg

And into the single competitor page, with Tracy all seems working good, but on each page refresh the repater items are always shifted. The repeater is not saved...

962546598_Schermata2021-07-10alle18_09_47.jpg.18149511a982ca52aaa9536936b4b29f.jpg 

 

 

 

Looking for other posts besides your, I found this one by @Robin S

but the sorting by index  didn't work... ?


Do you have any suggestion?

Link to comment
Share on other sites

@Cybermano, I don't understand what you are doing with this line inside the foreach:

$boat->barca_results->sort('id', $race);

This doesn't seem to correspond to the correct use of either $wirearray->sort() or $pages->sort() so maybe you are getting mixed up with the methods?

To go back to brass tacks (you may already know a lot of this but it might be useful for others)...

The first meaning of "sort" as it relates to pages is the sort value. This is a column in the "pages" database table.

2021-07-11_100710.png.7e18ee4a4ec10f712a31efd9d159afc6.png

Every page (including Repeater pages) has a sort value and this determines its sort position relative to its sibling pages (assuming no automatic sort has been set on the parent page or template). The sort value starts at zero, and the lower the number is the closer to the top of the sort order the page will be relative to its siblings. So in this page structure...

2021-07-11_101023.png.d28e812a2bf36b1c1dcd1882ad6cdd91.png

The sort value of "Red" will be 0, the sort value of "Green" will be 1, and so on. In some circumstances you can end up with gaps in the sort values as pages are deleted or moved and that is where the $pages->sort() method can be used to "rebuild" those sort values under a parent. But in 99% of cases you don't need to worry about sort values because PW just takes care of it and you don't have to use $pages->sort() to sort your Repeater items so we can ignore that.

You can set the sort value of a page the same as you would an integer field in the page's template:

$page->of(false);
$page->sort = 123;
$page->save();

 

The second meaning of "sort" is the WireArray::sort() method. When it comes to pages, a PageArray is a kind of WireArray, and a RepeaterPageArray is a kind of PageArray, so this means we can use WireArray::sort() to sort a RepeaterPageArray. The method works by sorting the WireArray by one or more properties of its items. In the case of a RepeaterPageArray this could be something like the "modified" timestamp of the items or it could be a field such as "title".

So you could do something like this to sort a field named "test_repeater" alphabetically by title (assuming that the Title field was used in the Repeater):

$page->test_repeater->sort('title');

And you can get more advanced by adding a temporary custom property to the items in a WireArray/PageArray/RepeaterPageArray and then using WireArray::sort() on that temporary property. I'll show an example of that in a moment.

If you have a page $p that contains a Repeater field "test_repeater" then you can sort the RepeaterPageArray as shown above in your template file and when you output the items in $p->test_repeater they'll be in the sort order you want. But if you wanted to save $p after you sorted test_repeater then it's not enough to just have the items in test_repeater in the right order and then save $p. And that's because PW uses the sort value of each Repeater page to determine the sorting of the Repeater field when it loads it from the database. So to make the sort order stick we need to save the sort value of each Repeater page in the RepeaterPageArray, and to do that we can use an incrementing counter that starts at zero.

A couple of code examples...

Sort test_repeater by title and save:

// Get the page containing the Repeater items
$p = $pages(1066);

// Turn off output formatting for the page because we are going to save it later
$p->of(false);

// Sort the Repeater items alphabetically by title
$p->test_repeater->sort('title');

// $i is a counter we will use to set the sort value of each Repeater item/page
$i = 0;
foreach($p->test_repeater as $repeater_item) {
	$repeater_item->of(false);
	$repeater_item->sort = $i;
	$repeater_item->save();
	$i++; // Increment the counter
}

// Save the test_repeater field for $p
$p->save('test_repeater');

More advanced sorting using a temporary property:

// Get the page containing the Repeater items
$p = $pages(1066);

// Turn off output formatting for the page because we are going to save it later
$p->of(false);

// More advanced sorting: sorting by title length
// We add a temporary "custom_sort" property to each Repeater item
// And then sort the RepeaterPageArray by that custom_sort property
foreach($p->test_repeater as $repeater_item) {
	$repeater_item->custom_sort = strlen($repeater_item->title);
}
$p->test_repeater->sort('custom_sort');

// $i is a counter we will use to set the sort value of each Repeater item/page
$i = 0;
foreach($p->test_repeater as $repeater_item) {
	$repeater_item->of(false);
	$repeater_item->sort = $i;
	$repeater_item->save();
	$i++; // Increment the counter
}

// Save the test_repeater field for $p
$p->save('test_repeater');

Hope this helps.

Edited by Robin S
Probably best to save the test_repeater field also
  • Like 4
  • Thanks 1
Link to comment
Share on other sites

17 hours ago, MarkE said:

Sorry to hear that @Cybermano. I’m away from my desk (and IDE) for a week, so can’t look into it just now.  Give me a prod next week if it’s still not working. 

Don't worry, in fact I'm sorry for invading your post. Now I'm looking deeper into RobinS indication and affectively it works fine. Surely the fault is mine. Thanks again. ?

Link to comment
Share on other sites

14 hours ago, Robin S said:

@Cybermano, I don't understand what you are doing with this line inside the foreach:

$boat->barca_results->sort('id', $race);

...

Hope this helps:

// Get the page containing the Repeater items
$p = $pages(1066);

// Turn off output formatting for the page because we are going to save it later
$p->of(false);

// Sort the Repeater items alphabetically by title
$p->test_repeater->sort('title');

// $i is a counter we will use to set the sort value of each Repeater item/page
$i = 0;
foreach($p->test_repeater as $repeater_item) {
	$repeater_item->of(false);
	$repeater_item->sort = $i;
	$repeater_item->save();
	$i++; // Increment the counter
}

// Save the test_repeater field for $p
$p->save('test_repeater');

 

Hi Robin, many thanks for your time and for your "academic" exposition. ?

You have clarified me a lot.

Surely I have misunderstood your exposition for the sorting by index, into the the linked thread. 

foreach($r_items as $index => $r) {
    // Update the sort value of the repeater page
    // See: https://processwire.com/api/ref/pages/sort/
    $pages->sort($r, $index);
}

I had assumed that in this foreach $pages->sort() stands for  $page->repeater_item->sort()

I beg your pardon... ?

And for the record, now with your help (?) my whole mess (?) is working perfectly ... 

  • Like 2
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...