I'm trying to sort the output of a repeater field from the WordPress plugin Advanced Custom Fields (ACF) using the WordPress Timber plugin implementation of Twig 1.34. The basic PHP example to sort from ACF below is from https://www.advancedcustomfields.com/resources/how-to-sorting-a-repeater-field/ and there are no usable answers for my question in the ACF forums.
So I'm trying to convert this function from the example to Timber/Twig:
// get repeater field data
$repeater = get_field('repeater');
// vars
$order = array();
// populate order
foreach( $repeater as $i => $row ) {
$order[ $i ] = $row['id'];
}
// multisort
array_multisort( $order, SORT_DESC, $repeater );
// loop through repeater
if( $repeater ): ?>
<ul>
<?php foreach( $repeater as $i => $row ): ?>
<li><?php echo $row['id']; ?>. <?php echo $row['name']; ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
What I have implemented in a view.twig
file that works is below. My ACF repeater field is called timeline, and has two subfields, date and description.
{% set repeater = timeline %}
{% for i, row in repeater %}
{{ row.date}}
{{ row.description}}
{% endfor %}
That outputs the existing fields (an array) of the ACF repeater timeline
- the two fields date and description - in the date order of oldest first, which is the default, like this:
Jan 2010
Lorum Ipsum blah blah blah
Jul 2011
Lorum Ipsum blah blah blah Lorum Ipsum
But I want to sort by newest date first.
According to this answer Twig_Error_Syntax for "Unknown filter" with a Twig filter in Timber I need to create a filter function that uses Twig_SimpleFilter
and does that sorting on the repeater field array.
I've tested that, and the sample rot13
filter from that answer works on {{ 'test text'|rot13 }}
.
But when trying to use the same Twig_SimpleFilter
structure with the code to sort the array by date, i.e. using this in my theme's functions.php
file:
add_filter('timber/twig', function($twig) {
$twig->addExtension(new Twig_Extension_StringLoader());
$twig->addFilter(
new Twig_SimpleFilter(
'sort_timeline',
function($string) {
$repeater = get_field('timeline');
$order = array();
foreach( $repeater as $i => $row ) {
$order[ $i ] = $row['date'];
}
array_multisort( $order, SORT_DESC, $repeater );
}
)
);
return $twig;
});
and calling the filter like this {% for i, row in repeater|sort_timeline %}
on the for loop in view.twig
{% set repeater = timeline %}
{% for i, row in repeater|sort_timeline %}
{{ row.date}}
{{ row.description}}
{% endfor %}
all I get is a whitescreen and no errors in the php log.
FWIW, using the rot13
filter like {% for i, row in repeater|rot13 %}
also shows a whitescreen, so something is wrong with the overall Twig_SimpleFilter
.
(As an aside, I also realize that I may need to convert the php date format in $row['date']
to correctly sort, maybe using strtotime
, since it is now simply month and year.)
Is using a filter in Twig the right way to try this? Or is it possible to adapt and use the array_multisort( $order, SORT_DESC, $repeater );
function directly in the .twig
template?
Edit 4/17/18
@num8er's code works for php5+. The php7 operator seems to be an issue.
And, this other function below will sort the backend rows of the repeater; change values and add to the theme's functions.php
file. See https://support.advancedcustomfields.com/forums/topic/sort-repeater-in-back-end-where-data-is-entered/
To get the MySQL field_123456789
name of the row you want to sort on, go to the ACF export page and export the field group. https://support.advancedcustomfields.com/forums/topic/how-to-retrieve-a-group-field-key/
add_filter('acf/load_value/name=timeline', 'my_acf_load_value', 10, 3); //repeater name is timeline
function my_acf_load_value( $rows)
{
foreach( $rows as $key => $row ) {
$column_id[ $key ] = $row['field_5967e7639a09b'];
}
array_multisort( $column_id, SORT_DESC, $rows );
return $rows;
}
Direct solution to Your question.
Try this code:
add_filter('timber/twig', function($twig) {
$twig->addExtension(new Twig_Extension_StringLoader());
$twig->addFilter(
new Twig_SimpleFilter(
'timeline_latest_first',
function($timeline) {
usort($timeline, function($a, $b) {
if(strtotime($a['date']) === strtotime($b['date'])) return 0;
return strtotime($a['date']) > strtotime($b['date']) ? -1 : 1;
// or simply (if php 7.x):
// return -1*(strtotime($a['date']) <=> strtotime($b['date']));
});
return $timeline;
}
)
);
return $twig;
});
{% set timeline_sorted = timeline|timeline_latest_first %}
{% for i, row in timeline_sorted %}
{{ row.date}}
{{ row.description}}
{% endfor %}