Jump to content


Photo

Get specific page attributes/fields from pagearray set

pagearray child attributes

  • Please log in to reply
33 replies to this topic

#1 Bill

Bill

    Jr. Member

  • Members
  • PipPip
  • 36 posts
  • 4

Posted 20 March 2012 - 02:46 PM

I have a pageArray "$p" - that contains 10 pages; how can i get any particular attribute from each one without formally setting up a foreach/iteration over the 'set'.

something like

$p = $pageArray->getField('id');

... where it would return all the id's of all child pages / pages within the pageArray?

I'm sure this already exists. thanks!

Edited by Bill, 20 March 2012 - 02:47 PM.


#2 Pete

Pete

    Administrator

  • Administrators
  • 1,755 posts
  • 655

  • LocationChester, England

Posted 20 March 2012 - 02:59 PM

I don't think you could do it without iteration, but if you're trying to get a subset, you could possibly do something like this (though I've not tested it):

// Let's assume $p is your 10 pages in a page array as you say, and $myfield is the field you're trying to filter on
$subset = $p->find("$myfield!=");

I seem to recall that that will return all pages where $myfield is not empty (as in it's not equal to "nothing" ;)).

$subset therefore now contains your filtered list. Let me know if that works.

My query would be why you would want to get a subset without iterating through them? Surely you have to iterate through them at some point to output some content?

#3 WillyC

WillyC

    Sr. Member

  • Members
  • PipPipPipPip
  • 163 posts
  • 201

Posted 20 March 2012 - 03:01 PM

Whyno for each? That what.it for. Already invented wheel is.
$ids=array();
foreach($pageArray as $p) $ids[]=$p->id;

Now bingo $ids have.what u need

#4 Bill

Bill

    Jr. Member

  • Members
  • PipPip
  • 36 posts
  • 4

Posted 20 March 2012 - 03:21 PM

Whyno for each? That what.it for. Already invented wheel is.

$ids=array();
foreach($pageArray as $p) $ids[]=$p->id;

Now bingo $ids have.what u need


Thanks!
Yeah, i don't mind doing it but i was hoping that there was a method like....

$pageArray->children()->getAttr('id');


#5 Bill

Bill

    Jr. Member

  • Members
  • PipPip
  • 36 posts
  • 4

Posted 20 March 2012 - 03:27 PM

I don't think you could do it without iteration, but if you're trying to get a subset, you could possibly do something like this (though I've not tested it):

// Let's assume $p is your 10 pages in a page array as you say, and $myfield is the field you're trying to filter on
$subset = $p->find("$myfield!=");

I seem to recall that that will return all pages where $myfield is not empty (as in it's not equal to "nothing" ;)).

$subset therefore now contains your filtered list. Let me know if that works.

My query would be why you would want to get a subset without iterating through them? Surely you have to iterate through them at some point to output some content?


Gotcha - yep aware of the find method, and thanks for explaining more thoroughly for this application of it.

I am mainly interested in if there's a pre-established method i might be missing is all... like "->children('')->getAttr('id')" or something of that nature... that would 'internally' to the Wire/PageArray class would iterate thru the pages to get an array/object of all the id's for that particular pageArray set, like ... 3, 14, 23, 154, 161 -> allowing me to use them in a successive query based on those.

In particular i'm wanting to do a ->getRandom() of all the pages, but in my pagination results (of those pages) below, i don't want the random one to be of the same as those listed in the page[d] results below.

So, if the paginated listing below (showing page 2 of 10, the page id's are: 3, 14, 23, 154, 161 - then the random id i want to be still random but just including them).

I know i can DO it, i was just wanting to Quickly (one-liner) Methodize it is all :), like

$pagedIDs = $pageArray->children()->getAttr('id');
(pseudo-hack code :P)

#6 Pete

Pete

    Administrator

  • Administrators
  • 1,755 posts
  • 655

  • LocationChester, England

Posted 20 March 2012 - 03:42 PM

Well all you need to do is use something like what I posted and then keep chaining on successive filters if you then want to narrow it down more.

I've got to say though that out of any context it is very confusing to work out what you are trying to achieve. I've read it a few times now and I'm still scratching my head ;)

I honestly don't understand what you mean by this: ->children('')->getAttr('id')

Are you saying in that case that you want to get the ID's of all the children of the current set of pages (which is 10 pages in your example)? In which case you could be returning a rather large number of ID's if each page can have multiple children.

I think in this case you would have to use a foreach anyway if all you want is the 10 pages' child IDs - something like this:

// $p is your pagearray with 10 pages again
$childIDs = array();
foreach ($p->children as $child) {
	$childIDs[] = $child->id;
}

If that's what you were trying to do, you won't find a method for it I don't think, mainly because I can't see a reason why you'd want to reinvent what PHP already does so well with just a couple of lines of code. Of course I may be wrong here and ryan may have built something in like this but after some testing I couldn't find one.

#7 Bill

Bill

    Jr. Member

  • Members
  • PipPip
  • 36 posts
  • 4

Posted 20 March 2012 - 03:58 PM

// $p is your pagearray with 10 pages again
$childIDs = array();
foreach ($p->children as $child) {
	$childIDs[] = $child->id;
}


Right, i can use the above, and i init. had that in there (my code) in the template itself, and was looking to remove 3 lines from it and just do (what you assumed already from my pseudo-hack-code above) which was

"get all the id's of the children associated with the pageArray object"

So,...

My next thought cuz i'd like to potentially do this for a multitude of uses, and to keep my code as DRY as possible... would you all recommend, when creating helpers (i know, i know... the CI in me coming thru - lol) like this (the foreach -> method by extending the PagesArray / WireArray class) that it's better to just simply create a catch-all 'helper' module? or do a general include int he head.inc (for ex.)?

#8 Pete

Pete

    Administrator

  • Administrators
  • 1,755 posts
  • 655

  • LocationChester, England

Posted 20 March 2012 - 04:38 PM

I did end up having a lot of code that I needed to run to check various things on a site I'm currently building so I just went with the include file in head.inc like you say. Keeps things very neat and tidy in the template and is the easiest way of doing it.

#9 Bill

Bill

    Jr. Member

  • Members
  • PipPip
  • 36 posts
  • 4

Posted 20 March 2012 - 04:57 PM

I did end up having a lot of code that I needed to run to check various things on a site I'm currently building so I just went with the include file in head.inc like you say. Keeps things very neat and tidy in the template and is the easiest way of doing it.


Interest'd in others' thoughts as well, but appreciate your K.I.S.S. approach and promptness (+1 @Pete! :))
Truly! Thx.

#10 Bill

Bill

    Jr. Member

  • Members
  • PipPip
  • 36 posts
  • 4

Posted 20 March 2012 - 05:43 PM

Just fyi - here's what i'm doing...
(please save the 'stoning' for after i leave this forum... and get a head start on din-din with the family so i can proceed into heaven with a full stomache :P)



abstract class PageArrayHelper
{
  static function getAttr(PageArray $PageArray, $key='id')
  {
   $pageKeys = array();
   foreach ($PageArray as $page)
   {
    $pageKeys[] = $page->$key; // all pages have one! <img src='http://processwire.com/talk/public/style_emoticons/<#EMO_DIR#>/smile.png' class='bbc_emoticon' alt=':)' />
   }
   return $pageKeys;
  }
}
Then by just calling it as such...

$IDs = PageArrayHelper::getAttr($randomChildren);
// returns (array) of ids <img src='http://processwire.com/talk/public/style_emoticons/<#EMO_DIR#>/smile.png' class='bbc_emoticon' alt=':)' />

now i can include that in the header, and i'll use throughout... my thoughts on this were if i'm displaying content and/or random content elsewhere on the page, and/or footer type - 'also check this out -' i don't want to have that same content be placed all over the place, i want it to be in one spot and one spot only but the pagination would be more of the uber spot, with the other random spots being secondary... (right now only 1 random spot, but you get the idea).

#11 Soma

Soma

    Hero Member

  • Moderators
  • 3,186 posts
  • 1739

  • LocationSH, Switzerland

Posted 20 March 2012 - 07:40 PM

Bill, if I look at your code I just keep thinking that I would do this. And look! Without foreach!

$rand_children = $pages->find("template=basic-page")->getRandom(3);
echo $rand_children; // returns 102|230|323
$ids = explode("|",$rand_children);
print_r($ids); // array with id's

..so a one liner would be this:
$ids = explode("|", $pages->find("template=basic-page")->getRandom(3));

@somartist | modules created | support me, flattr my work flattr.com


#12 Bill

Bill

    Jr. Member

  • Members
  • PipPip
  • 36 posts
  • 4

Posted 21 March 2012 - 07:49 AM

Bill, if I look at your code I just keep thinking that I would do this. And look! Without foreach!

$rand_children = $pages->find("template=basic-page")->getRandom(3);
echo $rand_children; // returns 102|230|323
$ids = explode("|",$rand_children);
print_r($ids); // array with id's

..so a one liner would be this:
$ids = explode("|", $pages->find("template=basic-page")->getRandom(3));


Right - thanks Soma - i'm honored with your reply, you're an active person in these forums, and in developing kick-butt modules/snippets :)...

My whole idea of grabbing the random would be to grab a random value was to on each page shown to the browser/user, that i have a paginated result set... (ie, showing page 2 of 10), then i want to show a random one (ie, at the top, at the bottom, or in the sidebar area) but I don't want the random one to be the same as any of those shown on that particular page #2 list.

So, my idea was to grab the paginated list - that's the main priority, and then from that list grab their ID's and then grab a random page from this template, where NOT IN in the list of ID's shown.

I'll play with the find() feature a bit more, and see how i can incorporate that into my page -

... will follow-up shortly.

[followup]

not sure what i'm doing wrong, but whenever i do a lookup using find("template={template_name}") or even a $page->children("template={template_name}") the system just goes into this churning fit to leave me with a blank freakin' page (lol).

still at it... i have a work around using my solution above, incorporating a find(!=) syntax, but was wanting to fully use what you're telling me to see which one is more effic. and as close to native PW as possible.

... i'm on it :)

#13 diogo

diogo

    Hero Member

  • Moderators
  • 1,982 posts
  • 1061

  • LocationPorto, Portugal

Posted 21 March 2012 - 08:37 AM

I think you can use array_diff() to compare $pages->find("template=basic-page") with $pages->find("template=basic-page, limit=10") and return the difference.

I didn't test it, and can't do it now.

#14 Bill

Bill

    Jr. Member

  • Members
  • PipPip
  • 36 posts
  • 4

Posted 21 March 2012 - 08:55 AM

I think you can use array_diff() to compare $pages->find("template=basic-page") with $pages->find("template=basic-page, limit=10") and return the difference.

I didn't test it, and can't do it now.


okay but that'll probably do an internal foreach as well... negating the impact, but certainly one-line it ;).
also, as i mentioned in my updated post above, i'm getting WIERD 'churning without any results, blank screen' activity on my use of ->find(), or ->children() - when using the selector of "template=cool-page" (which is the page template i'm using for this particular page).

very wild...

also -
as an update to my code above...
here's the most succinct way i've found thus far to accomplish things (yes, using the helper class above, and i'm still open to getting ideas more closely knit to PW.... but here goes:

$children = $page->children('limit=3');
$ids = PageArrayHelper::getAttr($children,'id');
$randomChildNotInChildren = $page->children()->find("id!=".implode("|",$ids))->getRandom();

... let the stone throwin' begin! ;)

#15 ryan

ryan

    Hero Member

  • Administrators
  • 5,771 posts
  • 3107

  • LocationAtlanta, GA

Posted 21 March 2012 - 10:49 AM

i'm getting WIERD 'churning without any results, blank screen' activity on my use of ->find(), or ->children() - when using the selector of "template=cool-page" (which is the page template i'm using for this particular page).


This is not what it's supposed to do. This is probably the most common selector usage there is. Need some more information to determine what's going on here. Try turning on debug mode (/site/config.php) and edit the $config->debug line to be true. That should at least give a better sense of what error is getting thrown. Though if not, tell me more about this case. How many pages are there using 'template=cool-page' and how many 'autojoin' fields are part of it? If you are dealing with hundreds of pages you may want to place a limit=n on it.

Just fyi - here's what i'm doing...


This looks like a good approach. If you want to add your getAttr() method directly to the PageArray, so that you can call it like this:

$IDs = $randomChildren->getAttr('id');

...you can do it with an autoload module:

class YourModule implements Module {

static public function getModuleInfo() { 
   return array(
       'title' => 'Your Module',
       'version' => 100, 
       'summary' => 'Adds a getAttr() method to all PageArrays',
       'singular' => true,
       'autoload' => true
       ); 
}

public function init() {
    $this->addHook('PageArray::getAttr', $this, 'getAttr'); 
}

public function getAttr(HookEvent $event) {
    $pageArray = $event->object;
    $key = $event->arguments[0];
    // ...the rest of your code goes here...
    $event->return = $pageKeys; 
}

}


#16 ryan

ryan

    Hero Member

  • Administrators
  • 5,771 posts
  • 3107

  • LocationAtlanta, GA

Posted 21 March 2012 - 10:53 AM

I can't seem to edit my last message without having it convert everything to entities (something not working right with the forum). I just wanted to mention the class line should be this instead:

class YourModule extends Wire implements Module {


#17 Bill

Bill

    Jr. Member

  • Members
  • PipPip
  • 36 posts
  • 4

Posted 21 March 2012 - 11:36 AM

This is not what it's supposed to do. This is probably the most common selector usage there is. Need some more information to determine what's going on here. Try turning on debug mode (/site/config.php) and edit the $config->debug line to be true. That should at least give a better sense of what error is getting thrown. Though if not, tell me more about this case. How many pages are there using 'template=cool-page' and how many 'autojoin' fields are part of it? If you are dealing with hundreds of pages you may want to place a limit=n on it.


Right this is with the debug already set to TRUE. It's like it churns, churns, etc... on my localhost and then stalls out... nothing, no unseen html either... the page source is blank. Only (i mean ONLY) when i do the find('template=cool-page') syntax. As you can see from above, the find('id!=...') works like a champ - and fast as heck!

This looks like a good approach. If you want to add your getAttr() method directly to the PageArray, so that you can call it like this:

$IDs = $randomChildren->getAttr('id');

...you can do it with an autoload module:

// editted already as per your update above
class YourModule extends Wire implements Module {

static public function getModuleInfo() {
   return array(
	   'title' => 'Your Module',
	   'version' => 100,
	   'summary' => 'Adds a getAttr() method to all PageArrays',
	   'singular' => true,
	   'autoload' => true
	   );
}

public function init() {
	$this->addHook('PageArray::getAttr', $this, 'getAttr');
}

public function getAttr(HookEvent $event) {
	$pageArray = $event->object;
	$key = $event->arguments[0];
	// ...the rest of your code goes here...
	$event->return = $pageKeys;
}

}


By autoload, you're stating that this would be a module that i could put inside my site's modules' folder and install it and it then autoloads it?

Thanks,... i'm still a bit unclear as to how to make my own module... i'm assuming that what you've done above it does essentailly that.
I'd put it there, inside my site/modules folder, as such, check for new modules, install that module, and it'll autoload. And, upon teh use of the 'PageArray' instance, i'll now have the 'hook' for getAttr, which will be a part of the extension of Wire - as does PageArray.

Right?
(sorry, feelin' n00b to this PW stuff still, but testing a ton of things out for my next project... it's all swirling in my head right now...) ;)

THANKS for taking the time to do this above tho.

[EDIT]
Just curious, why is it an abstract call to that method, and not directly... conversely, if i used it to only extend the PageArray and implement Module, then wouldn't it be a direct call?
(sorry, my lack of uber-PHP genius is showing... )

#18 Bill

Bill

    Jr. Member

  • Members
  • PipPip
  • 36 posts
  • 4

Posted 21 March 2012 - 01:01 PM

just a follow up for clarity's sake only -

I implemented (no pun intended) the above module, created my module as such, invoked the $page->children()->getAttr('id') request and voila, the EXACT same id's that i expect! Only now, instead of foreach's, extra php within the template/header, i have my *very own* (blush! tear runs down the cheek) module ;) that i can now tweak and incorporate new goodness within..., now gets invoked when i call.

This rawks! to no limited level, Ryan. Thanks.

#19 Bill

Bill

    Jr. Member

  • Members
  • PipPip
  • 36 posts
  • 4

Posted 21 March 2012 - 01:21 PM

continuing on this ePic journey...

how would i grab the $input->urlSegment1 stuff/lingo from within the module itself..
i init. did the lazy way of 'global $input' but got nuttin' :)

#20 Soma

Soma

    Hero Member

  • Moderators
  • 3,186 posts
  • 1739

  • LocationSH, Switzerland

Posted 21 March 2012 - 01:45 PM

continuing on this ePic journey...

how would i grab the $input->urlSegment1 stuff/lingo from within the module itself..
i init. did the lazy way of 'global $input' but got nuttin' :)


In a module it's not the same as in template level, in a module you use
wire("input")->urlSegment1;
$wire->input->urlSegment1;

or $this
$this->input->urlSegment1;

@somartist | modules created | support me, flattr my work flattr.com





0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users