Jump to content
celfred

Filtering / Type / Array issues

Recommended Posts

Hello, 

I've been struggling for hours with this, and it is driving me crazy. I guess I completely misunderstand basic concepts, but I'm facing a wall here hence this message... Here's what I'm trying to do :

  • I have a list of players in $allPlayers (pages)
  • Each player has (among others) 2 pageArray fields : 'group' and 'team'.
  • I'm trying to loop through all the players to build another pageArray (or WireArray or whatever...) to gather the players in groups/teams accordingly and deal with the different info I have on them and calculate a score.
  • My main problem is that some players are in a different team but their group has the same name (and I need to restrict players to their own team mates).

Here's my 'code' so far (which doesn't work) :

    // Build group list (for players having a team)
    $allPlayers->filter("team.name!=no-team")->sort("team.name");
    $allGroups = new PageArray();
    $uniqueGroups = [];
	$uGroups = new PageArray(); // This was a test, see below...
    $already = [];
    foreach($allPlayers as $p) {
      // Assign a groupId to each player
      $groupId = $p->team->id.$p->group->id;
      $p->groupId = $groupId;
      if (!in_array($groupId, $already)) {
        // The following lines were a test but didn't work and I DON'T UNDERSTAND WHY ?
		// $uG = new Page();
        // $uG->of(false);
        // $uG->template = 'player';
        // $uG->name = $groupId;
        // $uGroups->add($uG);
		// bd($uGroups->count()); // !! Keeps showing 1 ? My purpose was to be able to use $uGroups->karma, $uGroups->nbBonus... afterwards
        array_push($uniqueGroups, $groupId);
      }
      array_push($already, $groupId);
    }
 
	// Then, I loop through $uniqueGroups, get the players and calulate my score
    foreach($uniqueGroups as $group) {
      // Limit to groupId players for group scores
      $players = $allPlayers->find("groupId=$group");
      // Check for group bonus and calculate scores
		[...] // I cut to simplify my post
		// But my other problem is storing my score : I can't use $group->karma or $group->nbBonus since $group is a ragular PHP array ?
     }

	// Then, I wanted to create a new pageArray (hence my $uGroups above) to be able to sort them with the following
	$uGroups->sort("-karma");

And I'm stuck... More than 5 hours today already on this, and still blocked:(

I've tried playing with WireData, WireArray... but I'm all lost here. You can understand why I'm posting in the 'Getting started' forum !

For further information, this part of code is embedded in a setScoreboard() function and this is the 'group' part of it.

If anyone can devote a little time to give me a clue on how to think about this, I'd greatly appreciate !

Thanks !

PS : The more I write about this, the more I think I am able to get the group members and scores (with my awkward coding), but then I am unable of simply storing this information before rendering it. Here I'm referring to my comment in my code where I say that TracyDebugger keeps showing me only 1 $uGroups page. I wish I could dynamically build a 'uniqueGroup' page with Pagefields for Group, Team, pageArray for Members, Integer fields for calculated Karma, nbBonus...... sigh......  

Share this post


Link to post
Share on other sites
9 hours ago, celfred said:

I'm trying to loop through all the players to build another pageArray (or WireArray or whatever...) to gather the players in groups/teams accordingly and deal with the different info I have on them and calculate a score.

I think you'll find it easier if you use a plain PHP array so that you can make it nested/multidimensional. You can't have nested PageArrays you see.

I'm not sure I 100% understand your scenario, but hopefully this helps:

// Get $allPlayers somehow

$uniqueGroups = [];
foreach($allPlayers as $player) {
    // Define the $groupId
    // Use an underscore (or other non-integer) in the $groupId to force it to be a string, not an integer
    // That way when array_multisort() is used later the keys will be preserved
    $groupId = $player->team->id . '_' . $player->group->id;
    // Optional: add $groupId to $player as a custom property in case you need it later
    $player->groupId = $groupId;
    // Add player to $uniqueGroups using $groupId as key
    $uniqueGroups[$groupId]['players'][] = $player;
}
bd($uniqueGroups, 'uniqueGroups'); // have a look at what's in $uniqueGroups

foreach($uniqueGroups as $groupId => $data) {
    $uniqueGroups[$groupId]['karma'] = 0; // initialise to zero
    $uniqueGroups[$groupId]['bonus'] = 0; // initialise to zero
    // Tally up the group's karma
    foreach($data['players'] as $player) {
        $uniqueGroups[$groupId]['karma'] += $player->karma;
    }
    // Do the same for bonus
    //...
}
bd($uniqueGroups, 'uniqueGroups'); // have a look at what's in $uniqueGroups

// Sort the $uniqueGroups by karma (for example)
array_multisort(array_map(function($group) {
    return $group['karma']; // return the value you want to sort by
}, $uniqueGroups), SORT_ASC, $uniqueGroups);
bd($uniqueGroups, 'uniqueGroups'); // have a look at what's in $uniqueGroups

See this article for a good introduction to array_multisort() - in the code above array_map() is used to get the sort values instead of another foreach().

Edited by Robin S
Added code for sorting $uniqueGroups
  • Like 1

Share this post


Link to post
Share on other sites

Hello,

 

First of all, THANKS a lot for your answer which helps me better understand things. I'll go ahead and dig what you're mentionning with 'multisort'. It seems quite interesting and I have a feeling it fills a big lack in my knowledge ;) 

In the meantime, I have managed to do some stuff yesterday night which seems to do the job. In case you have a little time to read through it and see things I shouldn't do, I would be glad to have some kind of feedback on that (but as I said, I'll test your code above during the day). Roughly speaking, if I want the 'group' scoreboard, I loop through 'allPlayers' to get a list of groups and teams which I add to PageArrays. Then, I loop through all groups, get involved players and (here the thing !) I clone the group page to add it to a $uniqueGroup pageArray. And this page holds the calculated 'bonus' and 'karma'. Then, I do my thing with the 'reindex' (see code below) to try and get the logged in player's position and return all my variables to be able, then, to display a particular scoreboard (top 10, top 3 + 'surrounded' player...)

I know my explanations are hard to follow. Here's the code :

function setScoreboard($allPlayers, $player, $field) {
  if ($field != 'group') {
    $allPlayers->sort($field);
  } else {
    $allPlayers->filter("team.name!=no-team")->sort("team.name");
    $allGroups = new PageArray();
    $allTeams = new PageArray();
    $already = [];
    $uniqueGroups = new PageArray();
    foreach($allPlayers as $p) {
      $groupId = $p->team->id.$p->group->id;
      $p->groupId = $groupId;
      if (!in_array($groupId, $already)) {
        $allGroups->add($p->group);
        $allTeams->add($p->team);
      }
      array_push($already, $groupId);
    }
    foreach($allGroups as $group) {
      $index = 0;
      foreach($allTeams as $team) {
        $players = $allPlayers->find("group=$group, team=$team");
        if ($players->count() > 0) {
          $newGroup = clone $group;
          $newGroup->team = $team;
          $newGroup->id = $group->id.$team->id;
          $newGroup->focus = 0;
          // Check for group bonus
          $newGroup->nbBonus = groupBonus($players);
          $newGroup->yearlyKarma = $newGroup->nbBonus*30;
          // Add individual karmas
          foreach($players as $p) {
            // Karma is divided by number of players in the newGroup to be fair with smaller newGroups
            $newGroupYearlyKarma = round($p->yearlyKarma/$players->count);
            (int) $newGroup->yearlyKarma = $newGroup->yearlyKarma + $newGroupYearlyKarma;
            $newGroup->details .= $p->title.' ';
            if ($p->name == $player->name) {
              $newGroup->focus = 1;
            }
          }
          $uniqueGroups->add($newGroup);
          $index++;
        }
      }
    }
    $allPlayers = $uniqueGroups->sort('-yearlyKarma');
  }
  $allPlayersReindexed = new PageArray();
  $allPlayersReindexed->import($allPlayers);
  if ($player) {
    if ($field != 'group') {
      $playerPos = $allPlayersReindexed->getItemKey($player)+1;
    } else {
      $playerGroup = $allPlayersReindexed->get("focus=1");
      $playerPos = $allPlayersReindexed->getItemKey($playerGroup)+1;
    }
  } else {
    $playerPos = false;
  }
  if ($playerPos) { // Player is in the charts
    if ($playerPos <= 5) { // Good position, get Top 6
      $topPlayers = $allPlayersReindexed->slice(0, 6);
      $prevPlayers = false;
    } else {
      $topPlayers = $allPlayersReindexed->slice(0, 3); // Get top 3 players
      $prevIndex = $playerPos-3;
      $prevPlayers = $allPlayersReindexed->slice($prevIndex, 4);
    }
  } else { // No ranking (or complete scoreboard)
    $topPlayers = $allPlayersReindexed->slice(0, 10); // Get Top 10
    $prevPlayers = false;
  }
  return array($topPlayers, $prevPlayers, $playerPos, $allPlayersReindexed->count);
}

If you want to have a look of what is the final page, you can see there (on that page, I use Ajax requests to load all scoreboards individually (and the 'group' scoreboard is actually not functionnning well), but I intended to get rid of this server load since my $allPlayers is already loaded in my head.inc, I imagined optimizing my page by simply managing my scoreboards in PHP functions. 

Anyway, thanks again for your help ! And feel free to give me advice if you see horrors in my code :) (but again, I'll take the time to analyze the solution in @Robin S's answer)

Share this post


Link to post
Share on other sites

Still trying to understand things better. I've been looking at @Robin S's answer and I'm facing another issue. To set my nbBonus for each group, I had this function :

function groupBonus($players) {
  $nbBonus = 0;
  // Sort players by nb of places
  $players->sort('places.count');
  // Get min/max nb of places in the group
  $min = $players->first()->places->count;
  $max = $players->last()->places->count;
  if ($min == 0) { // 1 player has 0 places, so NO bonus possible
    return 0; 
  } else { // No player has 0 places, let's check if they all have 1,2,3... places
    for ($i=1; $i<=$min; $i++) {
      $nbPlaces = $players->find("places.count>=$i")->count;
      if ($nbPlaces == $players->count) {
        $nbBonus++;
      }
    }
  }
  return $nbBonus;
}

So my parameter $players was a PageArray() I had thanks to a 'find' request. But now, it is a regular PHP array and this throws an error because I'm trying to use lines such as $players->sort('places.count')...

All in all, I was trying to rework my function to work on regular PHP arrays, but then I have other headaches because I use many PW ways afterwards and I eventually wondered if I could simply turn my $players regular PHP array into a pageArray() again ?

And eventually, I wonder what you feel seems to be the best way for me ? Would it be preferable to work on regular PHP arrays all through my functions or should I 'convert' as soon as possible (if possible) so I can use PW way (or what I understand being PW way...)

And if you can give me a hint on what I should read more about (at this point) concerning my programming skills, I'd appreciate as well because I see that my skills are getting too low for what I want to do on my project ;)  

Thanks !

Share this post


Link to post
Share on other sites

@celfred, I don't think I'll be able to get my head around all the facets of your PW-based game, so will just answer in general terms.

First thing is that, broadly speaking, there's no reason to avoid using plain PHP arrays, functions, etc in your development if that is easier or clearer for whatever you're wanting to achieve. Behind the scenes PW will be using these native PHP types and functions anyway. So it comes down to choosing whatever you find easiest or most transparent.

So by all means use WireArrays/PageArrays where you find that easiest - in most circumstances that will be the way to go. But in your code I saw some places where you are creating new Page objects that are not actually going to be saved in the page tree anywhere. As if you are just creating these pages as a way to temporarily store some data. That doesn't make much sense to me and leads me to think there is probably some better way to do it.

And it also pays to think about creating some structure to your data so that if you need to debug it or come back to the code in six months time you can dump things and understand what is going on from the dump. So that's why I suggested using a PHP array where the key names and array structure reveal the purpose of what you're doing.

But if you need to take the 'players' element and do other things with it in the PW API then you're right that it would be easier if it were a PageArray rather than a plain PHP array of Page objects. So you could make that element a PageArray and then add pages to it...

//...
foreach($allPlayers as $player) {
    // Define the $groupId
    // Use an underscore (or other non-integer) in the $groupId to force it to be a string, not an integer
    // That way when array_multisort() is used later the keys will be preserved
    $groupId = $player->team->id . '_' . $player->group->id;
    // Optional: add $groupId to $player as a custom property in case you need it later
    $player->groupId = $groupId;
    // Add player to a PageArray in $uniqueGroups using $groupId as key
    if(!isset($uniqueGroups[$groupId]['players'])) $uniqueGroups[$groupId]['players'] = new PageArray();
    $uniqueGroups[$groupId]['players']->add($player);
}
//...

Then when you later need to work with the 'players' element it is a PageArray that you can use methods like sort() on.

 

And while looking into the issue I discovered something new: although you cannot create a nested PageArray, you can create a WireArray of PageArrays. So you could think about taking this approach if you are more comfortable sticking with PW API methods rather than doing sorting etc in plain PHP.

An example of what I mean about a WireArray of PageArrays...

// Create a new WireArray to hold the PageArrays
$my_wirearray = new WireArray();

// PageArray number 1
$pa1 = $pages->find("template=foo");

// PageArray number 2
$pa2 = $pages->find("template=bar");

// PageArray number 3
$pa3 = $pages->find("template=baz");

// Attach some custom data to the PageArrays
$pa1->data('karma', 25);
$pa2->data('karma', 13);
$pa3->data('karma', 37);

// Add the PageArrays to the WireArray
$my_wirearray->add($pa1);
$my_wirearray->add($pa2);
$my_wirearray->add($pa3);

// If you need to sort the WireArray by the custom data
$my_wirearray->sort('-karma');

// Or maybe you want to explode on that custom data
$karma_values = $my_wirearray->explode('karma', ['getMethod' => 'data']);

 

  • Like 2

Share this post


Link to post
Share on other sites

Hello,

First of all, thanks a lot for your great answer. You took the the time to post nice explanations and this will help me a lot to improve.

Quote

I don't think I'll be able to get my head around all the facets of your PW-based game, so will just answer in general terms.

No problem of course. This is exactly what I expected : general comments about some newbie mistakes you can quickly notice in my code.

Quote

First thing is that, broadly speaking, there's no reason to avoid using plain PHP arrays, functions, etc in your development if that is easier or clearer for whatever you're wanting to achieve. Behind the scenes PW will be using these native PHP types and functions anyway. So it comes down to choosing whatever you find easiest or most transparent.

Well, maybe this sounds obvious to a a lot of readers, but I guess I needed to be reminded of that advice :) 

On 4/30/2018 at 2:15 AM, Robin S said:

But in your code I saw some places where you are creating new Page objects that are not actually going to be saved in the page tree anywhere. As if you are just creating these pages as a way to temporarily store some data. That doesn't make much sense to me and leads me to think there is probably some better way to do it.

I do that sometimes because I feel better this way. I'll re-read my code and try to think differentely in the future by applying what you explain afterwards.

Quote

An example of what I mean about a WireArray of PageArrays...

Thanks for your nice example. It is very much inspiring to me.

I feel like understanding things better thanks to your reply so again, thanks a lot @Robin S for your help !

It's not always easy to learn coding by yourself and every time I end up asking the PW community for help, I get a nice answer that cheers me up and makes me understand things better. That's really cool !  

  • Like 1

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 michelangelo
      Hello there,
      I am building my website, which has a dozen projects with 10 images each. Basically, I need a filtering system but built in the most efficient and user-friendly way. You can see below that the images flow sideways so being hidden, JS lazy loading was a good tool, but I just wanted to try AJAX. Is it fit for this purpose or it's more for dynamic content?
       

    • By j__
      On a site that lists events, I am using a repeater field 'event_time', which contains two Inputfield Time fields 'event_time_start' and 'event_time_end'. Some events take place multiple times, others just once. Here's an example for the data structure:
      event1 title: First Event event_time (1): event_time_start: 08:00 event_time_end: 09:00 event2 title: Second Event event_time (1): event_time_start: 08:00 event_time_end: 09:00 event_time (2): event_time_start: 14:00 event_time_end: 18:00 event3 title: Third Event event_time (1): event_time_start: 07:00 event_time_end: 09:30 First, I'd like to generate a list of all events, sorted by event_time_start, with every repeater item added as an actual event (similar to a SQL JOIN clause). The desired output would be:
      07:00-09:30 Third Event 08:00-09:00 First Event 08:00-09:00 Second Event 14:00-18:00 Second Event A selector like
      $pages->find('template=event,event_time.event_time_start!=,sort=event_time.event_time_start'); would only return each event page once:
      07:00-09:30 Third Event 08:00-09:00 First Event 08:00-09:00 Second Event without
      14:00-18:00 Second Event Is there a possible alteration of the selector to take all different occurrences into account?
      Also I'd like to filter for events taking place in the morning, using a selector like:
      $pages->find('template=event,event_time.event_time_start>=06:00,event_time.event_time_start<=12:00,sort=event_time.event_time_start'); However, this would only return each 'event' page, which then contains also the afternoon version of event2.
      While I have the impression these are rather simple tasks, I struggle finding a selector-based solution to it. In this example on opening times are some similarities, but it does not deal with multiple occurrences. I'd appreciate your ideas to it.
    • By flod
      I'm working on a newsletter solution with WireMailSMTP and this Newsletter Tutorial.
      I only changed the lines 73 to 83 in newsletter.php, but there's a strange issue with the $subscribers array:
      $testMode = isset($_POST['test_email']) ? true : false; if ($testMode) { $subscribers = wire('input')->post('test_email'); if (strpos($subscribers, ',') !== false) { $subscribers = explode(',', $subscribers); } } else { $subscribers = $pages->find('template=subscriber'); $subscribers = $subscribers->explode('email'); } if (is_array($subscribers)) { $subscribers = array_map('trim', $subscribers); } $mail = wireMail(); $mail->sendSingle(true)->to('test@domain.de')->bcc($subscribers); $mail->from('xxx@domain.de'); $mail->subject($newsletter->title); $mail->bodyHTML($html); $numSent = $mail->send(); echo json_encode(count($subscribers)); $this->halt(); The email is sent to test@domain.de but not to bcc $subscribers. Errorlog: PHP Notice: Unknown: Invalid mailbox list: <> (errflg=3) in Unknown on line 0 I'm grateful for any advice.
      Greetings
      Florian
    • By Noel Boss
      Page Query Boss
      Build complex nested queries containing multiple fields and pages and return an array or JSON. This is useful to fetch data for SPA and PWA.
      You can use the Module to transform a ProcessWire Page or PageArray – even RepeaterMatrixPageArrays – into an array or JSON. Queries can be nested and contain closures as callback functions. Some field-types are transformed automatically, like Pageimages or MapMarker.
      Installation
      Via ProcessWire Backend
      It is recommended to install the Module via the ProcessWire admin "Modules" > "Site" > "Add New" > "Add Module from Directory" using the PageQueryBoss class name.
      Manually
      Download the files from Github or the ProcessWire repository: https://modules.processwire.com/modules/page-query-builder/
      Copy all of the files for this module into /site/modules/PageQueryBoss/ Go to “Modules > Refresh” in your admin, and then click “install” for the this module. Module Methods
      There are two main methods:
      Return query as JSON
      $page->pageQueryJson($query); Return query as Array
      $page->pageQueryArray($query); Building the query
      The query can contain key and value pairs, or only keys. It can be nested and 
      contain closures for dynamic values. To illustrate a short example:
      // simple query: $query = [ 'height', 'floors', ]; $pages->find('template=skyscraper')->pageQueryJson($query); Queries can be nested, contain page names, template names or contain functions and ProcessWire selectors:
      // simple query: $query = [ 'height', 'floors', 'images', // < some fileds contain default sub-queries to return data 'files' => [ // but you can also overrdide these defaults: 'filename' 'ext', 'url', ], // Assuming there are child pages with the architec template, or a // field name with a page relation to architects 'architect' => [ // sub-query 'name', 'email' ], // queries can contain closure functions that return dynamic content 'querytime' => function($parent){ return "Query for $parent->title was built ".time(); } ]; $pages->find('template=skyscraper')->pageQueryJson($query); Keys:
      A single fieldname; height or floors or architects 
      The Module can handle the following fields:
      Strings, Dates, Integer… any default one-dimensional value Page references Pageimages Pagefiles PageArray MapMarker FieldtypeFunctional A template name; skyscraper or city
      Name of a child page (page.child.name=pagename); my-page-name A ProcessWire selector; template=building, floors>=25
      A new name for the returned index passed by a # delimiter:
      // the field skyscraper will be renamed to "building": $query = ["skyscraper`#building`"]  
      Key value pars:
      Any of the keys above (1-5) with an new nested sub-query array:
      $query = [ 'skyscraper' => [ 'height', 'floors' ], 'architect' => [ 'title', 'email' ], ]  
      A named key and a closure function to process and return a query. The closure gets the parent object as argument:
      $query = [ 'architecs' => function($parent) { $architects = $parent->find('template=architect'); return $architects->arrayQuery(['name', 'email']); // or return $architects->explode('name, email'); } ] Real life example:
      $query = [ 'title', 'subtitle', // naming the key invitation 'template=Invitation, limit=1#invitation' => [ 'title', 'subtitle', 'body', ], // returns global speakers and local ones... 'speakers' => function($page){ $speakers = $page->speaker_relation; $speakers = $speakers->prepend(wire('pages')->find('template=Speaker, global=1, sort=-id')); // build a query of the speakers with return $speakers->arrayQuery([ 'title#name', // rename title field to name 'subtitle#ministry', // rename subtitle field to ministry 'links' => [ 'linklabel#label', // rename linklabel field to minlabelistry 'link' ], ]); }, 'Program' => [ // Child Pages with template=Program 'title', 'summary', 'start' => function($parent){ // calculate the startdate from timetables return $parent->children->first->date; }, 'end' => function($parent){ // calculate the endate from timetables return $parent->children->last->date; }, 'Timetable' => [ 'date', // date 'timetable#entry'=> [ 'time#start', // time 'time_until#end', // time 'subtitle#description', // entry title ], ], ], // ProcessWire selector, selecting children > name result "location" 'template=Location, limit=1#location' => [ 'title#city', // summary title field to city 'body', 'country', 'venue', 'summary#address', // rename summary field to address 'link#tickets', // rename ticket link 'map', // Mapmarker field, automatically transformed 'images', 'infos#categories' => [ // repeater matrix! > rename to categories 'title#name', // rename title field to name 'entries' => [ // nested repeater matrix! 'title', 'body' ] ], ], ]; if ($input->urlSegment1 === 'json') { header('Content-type: application/json'); echo $page->pageQueryJson($query); exit(); } Module default settings
      The modules settings are public. They can be directly modified, for example:
      $modules->get('PageQueryBoss')->debug = true; $modules->get('PageQueryBoss')->defaults = []; // reset all defaults Default queries for fields:
      Some field-types or templates come with default selectors, like Pageimages etc. These are the default queries:
      // Access and modify default queries: $modules->get('PageQueryBoss')->defaults['queries'] … public $defaults = [ 'queries' => [ 'Pageimages' => [ 'basename', 'url', 'httpUrl', 'description', 'ext', 'focus', ], 'Pagefiles' => [ 'basename', 'url', 'httpUrl', 'description', 'ext', 'filesize', 'filesizeStr', 'hash', ], 'MapMarker' => [ 'lat', 'lng', 'zoom', 'address', ], 'User' => [ 'name', 'email', ], ], ]; These defaults will only be used if there is no nested sub-query for the respective type. If you query a field with complex data and do not provide a sub-query, it will be transformed accordingly:
      $page->pageQueryArry(['images']); // returns something like this 'images' => [ 'basename', 'url', 'httpUrl', 'description', 'ext', 'focus'=> [ 'top', 'left', 'zoom', 'default', 'str', ] ]; You can always provide your own sub-query, so the defaults will not be used:
      $page->pageQueryArry([ 'images' => [ 'filename', 'description' ], ]); Overriding default queries:
      You can also override the defaults, for example
      $modules->get('PageQueryBoss')->defaults['queries']['Pageimages'] = [ 'basename', 'url', 'description', ]; Index of nested elements
      The index for nested elements can be adjusted. This is also done with defaults. There are 3 possibilities:
      Nested by name (default) Nested by ID Nested by numerical index Named index (default):
      This is the default setting. If you have a field that contains sub-items, the name will be the key in the results:
      // example $pagesByName = [ 'page-1-name' => [ 'title' => "Page one title", 'name' => 'page-1-name', ], 'page-2-name' => [ 'title' => "Page two title", 'name' => 'page-2-name', ] ] ID based index:
      If an object is listed in $defaults['index-id'] the id will be the key in the results. Currently, no items are listed as defaults for id-based index:
      // Set pages to get ID based index: $modules->get('PageQueryBoss')->defaults['index-id']['Page']; // Example return array: $pagesById = [ 123 => [ 'title' => "Page one title", 'name' => 123, ], 124 => [ 'title' => "Page two title", 'name' => 124, ] ] Number based index
      By default, a couple of fields are transformed automatically to contain numbered indexes:
      // objects or template names that should use numerical indexes for children instead of names $defaults['index-n'] => [ 'Pageimage', 'Pagefile', 'RepeaterMatrixPage', ]; // example $images = [ 0 => [ 'filename' => "image1.jpg", ], 1 => [ 'filename' => "image2.jpg", ] ] Tipp: When you remove the key 'Pageimage' from $defaults['index-n'], the index will again be name-based.
       
      Help-fill closures & tipps:
      These are few helpfill closure functions you might want to use or could help as a
      starting point for your own (let me know if you have your own):

      Get an overview of languages:
          $query = ['languages' => function($page){         $ar = [];         $l=0;         foreach (wire('languages') as $language) {             // build the json url with segment 1             $ar[$l]['url']= $page->localHttpUrl($language).wire('input')->urlSegment1;             $ar[$l]['name'] = $language->name == 'default' ? 'en' : $language->name;             $ar[$l]['title'] = $language->getLanguageValue($language, 'title');             $ar[$l]['active'] = $language->id == wire('user')->language->id;             $l++;         }         return $ar;     }]; Get county info from ContinentsAndCountries Module
      Using the [ContinentsAndCountries Module](https://modules.processwire.com/modules/continents-and-countries/) you can extract iso
      code and names for countries:
          $query = ['country' => function($page){         $c = wire('modules')->get('ContinentsAndCountries')->findBy('countries', array('name', 'iso', 'code'),['code' =>$page->country]);         return count($c) ? (array) $c[count($c)-1] : null;     }]; Custom strings from a RepeaterTable for interface
      Using a RepeaterMatrix you can create template string for your frontend. This is
      usefull for buttons, labels etc. The following code uses a repeater with the
      name `strings` has a `key` and a `body` field, the returned array contains the `key` field as,
      you guess, keys and the `body` field as values:
          // build custom translations     $query = ['strings' => function($page){         return array_column($page->get('strings')->each(['key', 'body']), 'body', 'key');     }]; Multilanguage with default language fallback
      Using the following setup you can handle multilanguage and return your default
      language if the requested language does not exist. The url is composed like so:
      `page/path/{language}/{content-type}` for example: `api/icf/zurich/conference/2019/de/json`
       
          // get contenttype and language (or default language if not exists)     $lang = wire('languages')->get($input->urlSegment1);     if(!$lang instanceof Nullpage){         $user->language = $lang;     } else {         $lang = $user->language;     }     // contenttype segment 2 or 1 if language not present     $contenttype = $input->urlSegment2 ? $input->urlSegment2 : $input->urlSegment1;     if ($contenttype === 'json') {         header('Content-type: application/json');         echo $page->pageQueryJson($query);         exit();     } Debug
      The module respects wire('config')->debug. It integrates with TracyDebug. You can override it like so:
      // turns on debug output no mather what: $modules->get('PageQueryBoss')->debug = true; Todos
      Make defaults configurable via Backend. How could that be done in style with the default queries?
      Module in alpha Stage: Subject to change
      This module is in alpha stage … Query behaviour (especially selecting child-templates, renaming, naming etc)  could change
    • By rgaikema
      Hi everyone,
      Currently I'm struggling with a filter option and I hope somebody can help or advise me the right way to this.

      What I want is an index page where campaigns are shown and where I can filter the campaigns by year.
      Each campaign has a datetime field with the output set to "j F Y", so it shows "1 April 2016" in the frontend.
      The dates are unique for each campaign, but there can be multiple campaigns in the same year.
      The page structure is:
      -client
      --campaign-a
      --campaign-b
      --campaign-c
      --campaign-d
      This is the PHP code I use on the client page to display all the campaigns.
      $campaigns = $page->children("sort=sort"); foreach ($campaigns as $campaign) { if($campaign->campaign_description) { echo '<div class="campaign-description info">'; echo '<h2>' . $campaign->title . '</h2>'; echo '<p>' . $campaign->campaign_description . '</p>'; // Todo remove only for testing $campaignDate = $campaign->campaign_date; $campaignYear = substr($campaignDate, -4); echo '<i>' . $campaignDate . '</i>'; echo '</div>'; } } $campaignYear is where I see the years, 2015, 2016, 2017 and so on.
      Here I create the dropdown to show the unique years:
      if (count($campaigns)) { // Store unique Years $arr = array(); foreach ($campaigns as $campaign) { $campaignDate = $campaign->campaign_date; $campaignYear = substr($campaignDate, -4); $arr[] = $campaignYear; } $unique_years = array_unique($arr); // Show dropdown with unique years echo '<select id="campaign-date-select">'; foreach($unique_years as $year) { echo '<option value="' . $year . '">' . $year . '</option>'; } echo '</select>'; }
      I read a couple of articles, for example this: https://processwire.com/talk/topic/8513-sort-with-select-dropdown/
      but I can't figure out how to dynamically change the output of the campaigns, based on the input of the select options.
      Maybe the datetime field is not the best option, or maybe I have to use URL segments and JavaScript to combine something...
      Any help or advise is welcome!
       
×
×
  • Create New...