Filtering PageList items by custom attribute

Permalink
Hi,

I'm trying to figure out how to filter down pages in a PageList block depending on the section the user is in.

In this example, I've created a dropdown / select custom attribute for the page type 'News' with two values 'Technical' and 'Stage', so if they're on the 'Technical' page or a child of that page, then I want to only show news pages with a custom attribute of 'Technical'.

Here's the code I'm putting in my controller.php file, but it's not working, so I thought I'd ask for some help if you'd be so kind:

/* SHOW APPROPRIATE NEWS FOR SECTION */
         // Current page ID
            $theCurrentPageID = array($c->getCollectionID());
         // Collect together pageIDs below each parent in an array (in this case, only Technical to keep things short)
            $childrenOfTechnical = Page::getByID(79)->getCollectionChildrenArray(true);
         // Checks whether current page ID is a child of a specific section (uses in_array() to see
         // if cID in the array).
            if(in_array($theCurrentPageID, $childrenOfTechnical)) {
               // We're in Technical news, so create a variable for the section to echo out in the template
               $newsScrollerPanel = '<div id="section">Technical news</div>';
               // Filter results to only those with Technical custom attribute selected
               $pl->filterByEcNewsType('Technical'); // custom attribute handle is 'ec_news_type' and is a select / dropdown
            }

osu
 
jordanlev replied on at Permalink Reply
jordanlev
How is it not working? Showing too many results? Not enough? Crashing your site?
osu replied on at Permalink Reply
osu
Hi Jordan,

Sorry, I should have mentioned that! It's not filtering anything i.e. all the news pages in the block are showing up (with no errors).

But that's not surprising as I've since done some more tests and can see that using print_r($childrenOfTechnical) results in an empty array when I echo it out in the controller.php.

So, it seems this code is not retrieving anything:

$childrenOfTechnical = Page::getByID(79)->getCollectionChildrenArray(true);


79 is definitely the correct ID for the parent page, but it's obviously not collecting the child page IDs.

On another note, I noticed that $theCurrentPageID correctly echos out the current page ID in the controller.php, but not my custom template - I thought variables and functions in my controller.php were available in custom templates? Or have I got that wrong?

Thanks for your help, hope all is well,

Osu
osu replied on at Permalink Reply
osu
Bump - can anyone help with this please?

Thanks
jordanlev replied on at Permalink Reply
jordanlev
Sorry -- a bit hard to understand the context here. Can you post your entire controller.php file (depending on where your code is located in that file has a huge impact on how it is supposed to work).

In regards to your question about variables in the controller being available to the view -- no, you need to explicitly pass them to the view using "$this->set('variable_name', $variable)" (the exception to this rule is if it's a field from the block's primary database record -- in which case there is behind-the-scenes code in the Concrete5 system that is doing this for you automatically).

Also note that passing "true" to getCollectionChildrenArray() is telling it to only traverse one level deep -- which shouldn't cause it not return ANY children, but something to keep in mind.

And looking at the code for that getCollectionChildrenArray() function, I see that it uses a SQL query with a bizarre WHERE clause:
...where cParentID = {$cID} and cIsTemplate = 0 and (Packages.pkgHandle <> 'core' or pkgHandle is null or Pages.ctID > 0)...

So... it appears that the function is ignoring system pages (I think that's what it means) -- probably doesn't matter in your case though.

Oh, just noticed something else -- if it's a select type attribute, you can't use normal filter comparisons, because the attribute could contain multiple values (and also C5 does this weird thing where it puts linebreaks around each value -- I guess to separate the multiple values), so you would need to do this:
$pl->filterByAttribute('ec_news_type', "%\nTechnical\n%", 'LIKE');


Finally, perhaps a simpler approach would be to not mess with the controller but instead in the view.php file (or a custom template you create), ignore the pages you don't want showing up. If you don't want to do that, though, at least try to keep html out of the controller -- that defeats the whole purpose of having a view file -- instead of passing the html to the view, pass a variable and the view should read that value and decide what html to output based on it.

Hope that helps.

-Jordan
osu replied on at Permalink Reply
osu
Hi Jordan,

Thanks for this, much appreciated. I think you're right that the simpler approach you mention (using a custom template only) would be a better approach. However, I'm not sure how I would go about 'ignoring' the pages I don't want.

In fact, I went into the controller thinking I should be putting any conditional logic in there instead of the custom template, but if I can do it all in the custom template, all the better.

So, with your filter code:

$pl->filterByAttribute('ec_news_type', "%\nTechnical\n%", 'LIKE');


That looks like it's perfect for 'ignoring' all the pages except the ones I'm looking for (news pages with the attribute 'Technical' selected).

Can I use this filter in the custom template? From another post I was reading on this subject, it seems I would need to put this in the controller.php?

To try and explain my aim a bit better, I'll show you an example sitemap:

SITEMAP

- Home
- About
- Technical
- - Lighting
- - Pro audio
- Stage
- - Props
- - Ropes
- News
- - *Multiple news pages*
- Contact

NEWS PAGE TYPE

This has the custom attributes 'Technical' and 'Stage' which is selected from a dropdown attribute (more will be added later).

AIM

Whenever the client creates a News page, they select 'Technical' or 'Stage'. I want to show only news pages with custom attribute 'Technical' in a hardcoded pagelist when a visitor is on one of these pages:

- Technical
- - Lighting
- - Pro audio

And only news pages with custom attribute 'Stage' when a visitor is on one of these pages:

- Stage
- - Props
- - Ropes

That's why I was trying to get all the page IDs of Technical and it's subpages to test it the current page ID is among them as I could use that to filter the results of the page list accordingly.

Do you think there's a simpler way to do what I'm trying to achieve? I'm not set on this approach, it just seemed like a logical one in my (non-coder-ish) mind!

Thanks for your help so far,

Osu
osu replied on at Permalink Reply
osu
Sorry to bump again, but I'm running out of time on this one - does anyone have any ideas for how I can do this, or is it not possible?

Need to know soon so I can communicate back to the client...

Thanks,

Osu
osu replied on at Permalink Reply
osu
Check it out! Almost a year later and I'm in another situation where I need to do this - can anyone chime in with how I can filter the results of a pagelist using a select / dropdown custom attribute *via a custom template* rather than the controller?
osu replied on at Permalink Best Answer Reply
osu
Ok, I've found the answer - I created two custom attributes:

- Type of Case Study (select / dropdown)
- Display these Case Studies in footer (select / dropdown *with the same values as the 'Type of Case Study' custom attribute above*)

For each page with a Page Type of 'Case Study', I chose a value for the 'Type of Case Study' custom attribute.

Then, in this file:

{site_root}/blocks/page_list/controller.php


I added this code:

global $c;
  $csCustomAtt = $c->getCollectionAttributeValue('case_studies');
  if($csCustomAtt != '') {
    $pl->filterByAttribute('case_studies', "\n" . $csCustomAtt . "\n");


Where 'case_studies' is the handle for the custom attribute 'Display these Case Studies in footer'. Now, all I needed to do was choose a value for this custom attribute on a page and it filters down the case study based on the 'Type of Case Study' custom attribute.

The only tricky thing is that you have to set the values for both the custom attributes to the same otherwise this doesn't work.

Hope this helps someone (I lost a few hairs and grew a beard while trying to figure this one out).

Osu