How to retrieve a random structure field's entry?

Ok… I’ve tried to achieve this using only kirby helpers or the toolkit API… and failed miserably.

I have a simple structure field with some (unknown) number of entries:

Intros: 
- 
  category: cwk
  title: My title 1
  link: events/test-3000
- 
  category: school
  title: My title 2 
  link: events/test2
- 
  category: shop
  title: My title 3
  link: events/test-9000
...

I’ve tried everything I could find in the docs… i.e.:

<?php  
    $randIntros = a::shuffle(  yaml($page->intros())  ); 
    $firstIntro = a::first($randIntros); 
    a::show($firstIntro); 
?>

<?php foreach( $firstIntro as $intro): ?>
    <h1><?php echo $intro['category']; ?></h1>  
<?php endforeach ?>

It trows “Warning: Illegal string offset ‘category’ in…” when I try to access any associative array’s elements like “category”… it also retrieves the first letter of each element along with these errors.

Could someone shed some light here?

[edit] fixed the syntax highlighting

Look at http://forum.getkirby.com/t/negated-isempty-and-isnotempty-seem-to-work-differently/1632/3 for the hint.
I hope it helps you.

Good luck!

You can fetch a single element of the array like this:

$number = 0;
$intro[$number]['category'];

So you could use a random number from an array of numbers between 0 and number of elements in the array - 1. Don’t know if this is the most elegant way, but at least it works.

Thanks @HeinerEF, either I didn’t get it… or it doesn’t works.

Before posting here I fiddled with toStructure()… but it expects a string (yaml) and there’s no way to shuffle it (that I know)… that’s why I was converting it to an Array and shuffling it before extracting one entry (the first).

Kinda lame, but I thought it’d work… :unamused:

I event tried to convert it back to yaml before using the toStructure():

<?php  
   $randIntros = a::shuffle(  yaml($page->intros())  ); 
   $firstIntro = a::first($randIntros); 
   $introYaml = yaml::encode($firstIntro);
   a::show($introYaml); 
?>
<?php foreach( $introYaml->toStructure() as $intro ): ?>
   <h1><?php echo $intro->category(); ?></h1>  
<?php endforeach ?>

Which throws a different error… (Fatal error: Call to a member function toStructure() on string in…) but yeah…

[edit] fixed the syntax highlighting

Yeah, I had tried shuffling with the toStructure method in the past as well and never got it to work or got an answer to my post here.

Interesting approach… is there a (kirby) way to get the array.lenght?
This way I wouldn’t get out of bounds results.

Other thing… I’ve tested manually:

<?php foreach( $firstIntro as $intro): ?>
   <h1><?php echo $intro[0]['category']; ?></h1>  
<?php endforeach ?>

Since my array have only one element… I thought it’d work. but instead it throws the “Warning: Illegal string offset ‘category’ in…” error.

[edit] fixed the syntax highlighting

What is $firstIntro in this case? Shouldn’t it be

<?php foreach($page->intros()->yaml()) { ... }

And you can use a simple php function to count the element in the array

$intros = $page->intros()->yaml();
$number = count($intros)-1;

But actually, I don’t quite know where your problem actually is? So the shuffling works alright as outlined above? If so, you don’t need to shuffle the numbers and using 0 for the first element should suffice?

Edit: Something like the following works:

<?php $test = page('test')->intros()->yaml()); 
	shuffle($test);
	echo $test[0]['title'];
?>

Sorry, it’s from the initial post, i.e.:

<?php  
  $randIntros = a::shuffle( yaml($page->intros()) ); 
  $firstIntro = a::first($randIntros); 
  a::show($firstIntro); 
?>

It does the same as you written ( foreach($page->intros()->yaml() )… I’m declaring first and just making the foreach later.


When I try to access the first array element (inserting a [0] before) it throws that error:

<h1><?php echo $intro[0]['title']; ?></h1>   

This way you’re shuffling the pages containing the “intros” structure fields… not the fields’ content itself.

OK, maybe I got you completely wrong.

I thought you wanted to get a random element from the intro structure field, but maybe you want something different.

In fact, your code above

<?php  
  $randIntros = a::shuffle( yaml($page->intros()) ); 
  $firstIntro = a::first($randIntros); 
  a::show($firstIntro); 
?>

already gives you what you want and all you would have to do, is echo the category or whatever like so:

<?php echo $firstIntro['category']; ?>

The thing that does not make sense to me, is that you want to loop through an array that only contains one element because in your code

$randIntros = a::shuffle( yaml($page->intros()) ); 
//here you shuffle all the elements in the array
$firstIntro = a::first($randIntros);
//here you pick the first of these elements, so there will never be more than one element in this array

Oh boy… spot on!!
I was trying to foreach loop a single element array!!


I told beforehand… I don’t know what I’m doing :smile: (and got desperate). I’m sure there should be some more elegant way to accomplish this…

For now I’ll use this other solution (array_rand()) I’ve found here:

<?php 
  $array_key = array_rand($page->intros()->yaml());
  $array_intros = $page->intros()->yaml();
?>

One var with the structure field as yaml and other with the random key.

Then, as you explained, just use it accessing directly:

<h1><?php echo $array_intros[$array_key]['title']; ?></h1>  

Is it any better? Less expensive? Same thing?

Well, so now we have 3 solutions :grinning: I don’t know which of the methods is faster or if there is any difference at all.

1 Like

Nevertheless, I would optimize the code a little bit:

<?php 
  $array_intros = $page->intros()->yaml();
  $array_key = array_rand($array_intros);
?>
<h1><?php echo $array_intros[$array_key]['title']; ?></h1>
1 Like