Accessing fields inside repeater inside an array of page fields.

Recommended Posts

creativejay    21

My brain is probably just tiring out on me right this moment, I'm hoping that by the time I write out my problem I'll see the way through it. If you're reading this, it didn't work. :lol:

Structure in question is:

  1. Series Page
    1. Product pages
      1. Page Fields for each product page
        1. Some of these fields are repeaters
          1. Fields within the repeater

I have made an array of page fields so I don't have to keep track of them all as I develop:

$listings = $page->children; // grab all the published children of the Series page
foreach($listings as $l) { // loop through the children
	foreach($l->fields as $f) { } // loop through each child's page fields where they have a value set

Then I break down how to handle each type of field:

if($f->type == 'FieldtypeFile') {  } 
elseif($f->type == 'FieldtypeDatetime'||$f->name == 'prod_status_pages'||$f->type == 'FieldtypeImage'){  } 
elseif($f->type == 'FieldtypePage'){  }

These are all largely working as expected (though I do have a couple of offset/isset exceptions to clean up...)

It's when I get to the repeaters that I run into trouble getting the API calls to work.

Just cycling through the fields as above, the output for a FieldtypeRepeater is the ID of the repeater in that field's array. Everything I read suggests I should treat a repeater the same as I would treat a page, which leads me to the following code.

elseif($f->type == 'FieldtypeRepeater'){ // Repeaters need special treatment otherwise output is just ID
	$th .= "<th><b>{$f->label}</b></th>\n";
	$trows = ""; // creating an empty variable to build my foreach into
	foreach($f->fields as $rf){ // looping, I hope, through the fields of the given Repeater ID
		$trows .= "{$rf->label}: {$f->get($rf)->title} ({$rf->type})<br />\n"; // add an entry to the variable
	$rows .= "<td style='padding: 8px 16px; vertical-align: middle;'>".$trows."<br />\n ({$f->type})</td>\n"; // back out to rendering the Repeater field


What I would hope would output in the HTML I've been building is something like:

<td style='...'>Lo temp: -40 (Integer)<br />
Hi temp: 75 (Integer)<br />
Storage Lo: -40 (Integer)<br />
Storage Hi: 85 (Integer)<br />
Functional to: 85 (Integer)<br />

So what I'm trying to do here is loop through the populated fields in the unknown Repeater field, and output them as a simple (so far) text list of the repeater.field and its value (and then its type for my reference).

I'm afraid typing this out has fixed some syntax but not enough to get this working as I'd hoped.

Please note not all Repeater fields are integers. Some also have floats, files, or options, and probably a couple others I'm forgetting.


I appreciate your time in taking a look at this!

Share this post

Link to post
Share on other sites
szabesz    1,378

Morning (here...),

9 hours ago, creativejay said:

foreach($f->fields as $rf){ // looping, I hope, through the fields of the given Repeater ID

As far as I can see, at this stage you are looping through the Pages of the Repeater and not the fields, yet. You need nested loops:

foreach($f as $repeater_page) {
	foreach($repeater_page->fields as $rf){ // looping, I hope, through the fields of the given Repeater ID

 Warning: written in the browser and without my morning coffee! ;) 

Edited by szabesz

Share this post

Link to post
Share on other sites
creativejay    21
6 hours ago, szabesz said:

As far as I can see, at this stage you are looping through the Pages of the Repeater and not the fields, yet. You need nested loops

Thank you for taking a look!

This is still not outputting anything, unfortunately. I'm fussing about with it and will update if I catch on an answer, but if anyone else is taking a look at this I am still seeking the solution.


Share this post

Link to post
Share on other sites
adrian    7,664

I think you are looking for something like this. You need to be making use of the names of the repeater fields and also referencing the repeater field relative to it's containing page.

foreach($l->{$f->name} as $repeater_item) {
    foreach($repeater_item->fields as $rf) {
        echo $repeater_item->{$rf->name};

Share this post

Link to post
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

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By MilenKo
      Hello guys. I am almost on the final line of building my second complete website with ProcessWire and got stuck a bit on the recipe nutrition presentation. I am not sure if I would be able to explain it promptly, but will do my best. So I have the code in my HTML template:
      So to fill up the values in the frontend, I am using a simple TextArea field and dropping the following content:
      As far as I needed to split the text field into separate lines and every line into 3 parts (nutrition name, quantity and measure) I used regex code to sort everything through and the results are OK. Now the issue I am facing is that as per the theme, I need to assign class to left or right block and on top of that to close the nutrition-detail div after every right-box div.
      For the class I added $counter and using odd:even managed to assign the class of left-box & right-box. But I am having difficulty to insert the opening div of nutrition-detail on the beginning of every row and the closing div after the second block. Here is the code that sort of works, but is not inserting the divs properly:
      Any suggestions how to go around or some simple function to introduce to make it work? I tried to add another check for odd:even and if the result is odd, to insert the <div class="nutrition-detail"> and close the div if even, but I am still missing something...
    • By pppws
      hey there,
      i have a collection (parent page) of persons (child pages of 'collection'). each person has several fields. two of them are repeater fields where the person can enter their 'jobs' and 'recidencies'. i'm trying to build a list of entries which looks like:
      Peter Maria Paul … Doctor
      Eva Julia William … for the first 5 persons everything worked smoothly. but now that i've reached about 20 entries the server slows down and i'm wondering if my loop is somehow cluttred up.

      <?php $langname = $user->language->title; //get current user language?> <?php $persons = $pages->get('/collection')->children->filter("lang=$langname") // get all children for current user language ?> <section class="profession"> <h1>professions</h1> <?php foreach ($persons as $child): ?> <?php foreach ($child->professions as $profession): // the repeaterfield is called: professions, the field itself is profession?> <?php $profAll[] = $profession->profession // store all entries; $profUnique = array_unique($profAll) // only unique entries ; sort($profUnique) // sort the entries; ?> <?php endforeach; ?> <?php endforeach; ?> <ul style="column-count: 2;"> <?php foreach ($profUnique as $profLetter): // loop through all professions ?> <li style="font-size: 2rem; list-style-type: none;" class="letter"><?= $profLetter // output one profession e.g. Actor?></li> <?php foreach ($persons->find("professions.profession=$profLetter")->sort('givenname') as $person): // find all persons who have the profession Actor ?> <li><a class="ajax" href="<?= $person->url ?>"><?= $person->givenname // output the name of person who fits the profession ?></a></li> <?php endforeach; ?> <?php endforeach; ?> </ul> </section>  
      is there a way to make this request faster? (i'll have at least two of them on the same page)
    • By dscONE
      Found myself needing to filter repeater items based upon start and end date fields.  These fields aren't required, so repeater items with no dates should be included and those with dates (or a just one date -- start or end) should be evaluated for inclusion.
      I slowly figured out that I was dealing with in-memory filtering (of a RepeaterPageArray) and not a database-driven operation.  So, that eliminated the possibility of using or-groups in my selector.  I still thought I could use the 'filter' and 'not' functions to get the job done.  Turns out the way selectors are handled for in-memory filtering is quite different from database-driven selectors.
      Here is what I want(ed) to do to filter the dates:
      // Start date not specified or in the past $repeater->filter('start_time<='.$now); // Not when end date is specified and has passed $repeater->not('end_time>0, end_time<'.$now); The first one worked exactly as expected.  The second it where I ran into problems.  The 'filter' function (which calls the 'filterData' function) takes a $selector argument.  From everything I read about selectors, I assumed that the entire selector would have to match.  In other words, each comma represented an 'and' scenario.  Turns out that each separate comma-separated-selector in the $selector as a whole gets evaluated independently.  In the case of the 'filterData' function, the elements are removed if any one of those selectors matches.  Essentially, we have an 'or' scenario instead of an 'and' scenario.
      In my case above, there was no way for me to filter for a non-empty date AND one that occurs before a given least that I could think of.
      So, my main question is if the filter (and related) function should operate on the entire selector passed to the function instead of its individual parts in some sequence.  That is what I would have assumed a selector to do.
      In my project, altering the segment of the WireArray::filterData function solved my problem for now. least till I forget about it and overwrite it when I upgrade next.  Here is the code I changed within the function
      // now filter the data according to the selectors that remain foreach($this->data as $key => $item) { $filter_item = true; foreach($selectors as $selector) { if(is_array($selector->field)) { $value = array(); foreach($selector->field as $field) $value[] = (string) $this->getItemPropertyValue($item, $field); } else { $value = (string) $this->getItemPropertyValue($item, $selector->field); } if($not === $selector->matches($value)) continue; $filter_item = false; break; } if($filter_item && isset($this->data[$key])) { $this->trackRemove($this->data[$key], $key); unset($this->data[$key]); } } I would love to hear what you all think.  If I have misunderstood something, then I would welcome a different solution.
    • By pppws
      hey there,
      i'm quite new to processwire but i'm having a great experience using it!
      right now i hit a point where i can't help myself out with google/searchfunction.
      to sketch the basic functions of my page:
      visitors can enter their e-mail in a form. when doing this pw creates a new page, using the e-mail's md5 hash as a name. the url of the page is sent to the user. the user now can edit the newly created page and can fill out fields like: name and year of birth (thanks to the docs those two already work like a charm!) but there are other fields like "residencies", which are repeaterfields containing three fields: city (text), country (text), current (checkbox). at my current version i can populate a new repeater field easily by using this code:
      $location = $page->locations->getNew(); $location->location = Munich; $location->country = Germany; $location->current = 1; $location->save(); $page->locations->add($location); now i want to populate the new reapter fields from input fields (using the simple form api). which leads me to my questions:
      1. can i somehow group/merge input fields to one "repeater input fields" right now i have:
      // create a text input -> locations $field = $modules->get("InputfieldText"); $field->label = __('City'); $field->attr('id+name','location'); $field->required = 1; $form->append($field); // append the field to the form // create a text input -> country $field = $modules->get("InputfieldText"); $field->label = __('Country'); $field->attr('id+name','country'); $field->required = 1; $form->append($field); // append the field to the form // create a checkbox -> locations $field = $this->modules->get('InputfieldCheckbox'); $field->attr('name', 'location_current'); $field->attr('autocheck', 1); $field->attr('uncheckedValue', 0); $field->attr('checkedValue', 1); $field->attr('value', $this->fname); $form->append($field); // append the field to the form 2. my desired layout for the form looks like this:

      by hitting the + button a new line shows. i'm wondering what's the best practice here. can is somehow use the above mentioned code itself as a repeater, or do i have to create several (uniqe) input fields in advance and hide them afterwards (javascript)?
      any help is appreciated!
    • By patricktsg
      Something probably off with my loop, the code below is on my "entries.php" template page, it's outputting the title and description of each entry fine but the repeater fields just repeats the first set of images from entry 1 throughout the loop.
      <?php // Functions $entries = $page->children; $entry_array = array(); $image_array = array(); foreach ($entries as $entry) { // Define the fields wanted $title = $entry->title; $description = $entry->entry_description; $images = $entry->images; foreach ($images as $image) { $imageUrl = $image->url; // make a thumb version $imageDescription = $image->description; // Build array from repeater $image_array[] = array( 'image_url' => $imageUrl, 'image_description' => $imageDescription, ); } $pageUrl = $entry->httpUrl; // Build the array from the fields $entry_array[] = array( 'title' => $title, 'description' => $description, 'images' => $image_array, 'page_url' => $pageUrl, ); } // Json encode the array $entries = json_encode($entry_array, true); // Dump the data // echo $data; ?>