How to?? Want a dropdown to return a lit of pages based on selected attributes

Permalink 2 users found helpful
Could some kind soul give me a high level overview of how I might recreate this functionality in C5 (see the "search by category" dropdown in upper right corner)
http://www.hicwny.com/

I am looking at the plugin easy_news as an example.... I want to have a series of (presumably) pages tat have a category attribute. I choose what category(s) each page should belong to.

On the frontend of the site there would be a dropdown, choosing which would reload page and populate list of the pages that have been tagged that particular category.

Any advice woul dbe most appreciated, thanks!

adavis
 
jordanlev replied on at Permalink Best Answer Reply 1 Attachment
jordanlev
High-level overview:

1) Create a page attribute of type "select", and allow multiple values. For the sake of example, let's call it "search_category".

2) Create a block that outputs a form containing a dropdown menu which lists every value of the "search_category" attribute. You will need to hard-code in a destination page for this form to post to, or if you're feeling adventurous you could add a sitemap page selector field to the block's edit interface so admin user can choose destination page (not too hard to do actually, look to other blocks' code for examples).

3) Create either a single_page or a block that lists out all pages having a certain value for the "search_category" attribute. A single_page is easier to code, but the block is more portable to other sites, marketplace, etc. The form from step 2 will post the user's chosen category, then in this here page/block you retrieve that from $_GET (assuming step 2's form had method="get", which it should), and use a page_list object to retrieve all pages that have that search_category chosen and output them to the page/block.


I'm attaching a block I made which contains a lot of this functionality, although it's trying to do something slightly different than what you are -- with my block, the site admin chooses what category of pages to list when they add/edit the block; but this won't work for you because you don't want to have to create a new page to put this block on every time a new category is added -- instead you want the block (or single_page) to dynamically display the proper things based on the form submission from step 2.

But I had to wade through a lot of wonky C5 behavior to get the select attribute filtering working properly so if nothing else this should save you a bunch of time there.

One of these days I'm hoping to put this thing up on the marketplace, just haven't had time to write up a description, do screenshots, make an icon, etc.

Good luck.

-Jordan
adavis replied on at Permalink Reply
adavis
Wow thanks a bunch, this will definitely help. Please let me know how I may be able to buy you a beer ;-).
-Andrew
adavis replied on at Permalink Reply
adavis
Getting there, slow but sure. I am having a problem getting pl filtering working however. I wonder if its something obvious?? Here is a snippet of my code. I have already added an attribute (category_listing_categories) to 2 pages with values (like Doors). If I comment out the line that does the filterByAttribute, it is lists the pages and correct attribute values. But with the filter it does not work. Maddening =/

$pl = new PageList();
$pl->filterByAttribute('category_listing_categories','Doors','='); 
$pages = $pl->get();
foreach( $pages AS $page){
   echo '<p>'.$page->getCollectionName().', '. $page->getAttribute('category_listing_categories').'</p>';
         }
adavis replied on at Permalink Reply
adavis
Ah, this code for the filter makes it work. Onward!

$pl->filterByAttribute('category_listing_categories','%Doors%', 'like');
adavis replied on at Permalink Reply
adavis
One thing I am concerned with, with this method, is that if an attribute value is similar enough to another it would get returned in the filter.

Ex.
$pl->filterByAttribute('category_listing_categories','%Doors%', 'like');

would return anything with the word "Doors" in it:

Doors
New Doors
Old Doors

would all be returned. Is there any way to prevent this behavior?
jordanlev replied on at Permalink Reply
jordanlev
It's all in the code I attached previously (along with detailed comments explaining why):
$pl->filter(false, "(ak_category_listing_categories LIKE '%\nDoors\n%')");
jordanlev replied on at Permalink Reply
jordanlev
Hope that didn't come off as annoyed :)

Here are the comments from the aforementioned code, in case you're interested:

//DEV NOTES:
// * PageList filter() takes an attributeValue's name, not its ID
// * Attributes of type "select" wrap values in newline characters when saved to the database -- seehttp://www.concrete5.org/developers/bugs/5-4-1-1/select-type-attrib...
// * We can't use magic method filterByRelatedContentCategory() because that only searches on equality
//   (which isn't appropriate with a multi-select option like this -- there could be more than 1 value saved).
// * PageList filter() method has an undocumented feature where you pass in false for the 1st arg 
//   and a SQL "WHERE" clause (well, a portion of one) for the 2nd arg -- seehttp://www.concrete5.org/developers/pro-accounts/community-leaders-...
adavis replied on at Permalink Reply
adavis
I thank you for your patience and assistance ;-) If the values are delimited by newline characters that would be fine for my needs.

However, for me this works WITHOUT the newline characters (and NOT WITH them)

foo($ak,$av){
  $pl = new PageList();
   $pl->filterByAttribute($ak, '%'.$av.'%', 'like'); 
   $pages = $pl->get();
  return $pages;
}


I see in your other posts that you say that
"I pass in the value I'm searching for, though, nothing comes up -- because the value got saved to the CollectionSearchIndexAttributes table with linefeed characters before and after it."

Do the attribute values automagically get saved to the CollectionSearchIndexAttributes table or is there something I have to do to enable that?

I tried checking the enable in the search choices in the attribute settings. In the CollectionSearchIndexAttributes table I see a column for the attribute, but no values are saved there even though I have 2 pages with that attribute multiselected...
adavis replied on at Permalink Reply
adavis
Ah, but this does work:
$pl->filterByAttribute($ak, "%\n{$av}\n%", "like");

Thanks again for your example code, most helpful!
jordanlev replied on at Permalink Reply
jordanlev
Heh, I like your solution better than mine (the code is much cleaner). Thank you for posting that followup.

In response to your question about the CollectionSearchIndexAttributes table -- yes, things should be saved there automagically. I could be wrong, but I don't think checking the "enable search" choices in the attribute settings has anything to do with that (I think they're just for the "page search" in the dashboard if you go to Sitemap and click the "Page Search" tab) -- but don't quote me on that because I'm not 100% sure.
adavis replied on at Permalink Reply
adavis
Thanks, I plan on posting the final code and/or making a free add-on for this, it will be really useful. Another question tho...

Any idea if multiple filters will work? ex: If i wanted to filter on size AND color....

foo($ak,$av){
  $pl = new PageList();
   $pl->filterByAttribute($ak1, '%'.$av1.'%', 'like'); 
   $pl->filterByAttribute($ak2, '%'.$av2.'%', 'like'); 
   $pages = $pl->get();
  return $pages;
}
jordanlev replied on at Permalink Reply
jordanlev
Multiple filters work, but they're "AND" filters -- so if you filter by size and color, you will only get items that match both the given size and the given color (as opposed to either/or). This sounds like what you're after, so you should be good. If not, you have to revert to the technique I originally responded with and construct the OR statement as a SQL WHERE clause in the 2nd argument.
adavis replied on at Permalink Reply
adavis
This is perfect, actually.
My next question is this (and its more for graceful failover...)
I have setup my block(s) so that I can have multiple filters. right now, I have 2 but could be as many as would make sense. When a selection is made, the form is submitted and variables passed like this

/listing/?ak1=Category&av1=all&ak2=Price&av2=all

in my controller I want to check that the ak and av actually exist before I use them for the PL filter... Theoretically if my select form is working right, only valid ak and av will be passed, but if an invalid one is, then there will be no results at all.

Not sure how to fetch a list of available attribute (keys).
jordanlev replied on at Permalink Reply
jordanlev
I think you could do this:
$ak1 = CollectionAttributeKey::getByHandle('your_attribute_handle');
if (!is_null($ak1)) {
  //do the filter for your_attribute_handle here
}
$ak2 = CollectionAttributeKey::getByHandle('your_other_attribute_handle');
if (!is_null($ak2)) {
  //do the filter for your_other_attribute_handle here
}
adavis replied on at Permalink Reply
adavis
That would work great to check if they exist, and I will implement that.

Do you know how to fetch a list of available page attributes? I want to male a select chooser for the block to make setup easier. Currently you have to type in the attribute handle, and it would be a problem if you didn't know which one you wanted to choose ;-)
jordanlev replied on at Permalink Reply
jordanlev
It's all in the code I posted up above :)

Check out the "setCategories()" function in the block controller. Specifically:
Loader::model('attribute/type');
Loader::model('attribute/categories/collection');
$ak = CollectionAttributeKey::getByHandle('your_attribute_handle_goes_here');
$satc = new SelectAttributeTypeController(AttributeType::getByHandle('select'));
$satc->setAttributeKey($ak);
$options = $satc->getOptions();
adavis replied on at Permalink Reply
adavis
But I want to have a list of the attributes setup in the system, so that I can choose the attribute handle of one of them. I dont want the available choices in a particular attribute.

In my block I have to specify (as a text field) the attribute handle of the (select)attribute I want to use. I want to make a dropdown, populated with attribute handles I can choose from.
Something like this (in pseudo code...)
$aks = CollectionAttributeKey::getAllKeys();
echo '<select>';
foreach( $aks AS $key ){
  echo "<option value=\"{$key->handle}\">{$key->name}</option>";
}
echo '</select>';
jordanlev replied on at Permalink Reply
jordanlev
I don't know how to do that -- sorry. I would try posting a new topic to the forum, specifically about "retrieving a list of all page attributes".
adavis replied on at Permalink Reply
adavis
Thanks I will do that
jordanlev replied on at Permalink Reply
jordanlev
NOTE: See this thread about how you should escape the $tag (or whatever) variable in the filter query:
http://www.concrete5.org/community/forums/customizing_c5/pagelist_f...
Cahueya replied on at Permalink Reply
Hey there,

did you ever finish this add-on? It is something I need right now, but I fear putting all these pieces of code together myself :)

What can I do?

Thanks :)
edgedesign replied on at Permalink Reply
edgedesign
I'm unable to install :(
C5 Install menu says package is corrupt. How can i update the controller.php to make it compatible for v5.7.5.13?
RadiantWeb replied on at Permalink Reply
RadiantWeb
oddly, I hadn't seen this. but I just issued a really nice block in the PRB that dynamically allows you to choose from any select attribute and filter pages by any number of chosen tags. It's called related pages.

C
jordanlev replied on at Permalink Reply
jordanlev
Heh -- great minds think alike I guess :)

Well I doubt I'll ever get to packaging this up, so I'm glad you did something similar.
rosie607 replied on at Permalink Reply
rosie607
Hi
I was wondering how I would edit this add-on so it also lists external links as well as actual pages?
Thanks
jordanlev replied on at Permalink Reply
jordanlev
Edit the view.php file in the block directory, find this line:
$link = $nh->getLinkToCollection($page);

...and replace it with the following few lines:
if (!is_null($page->getCollectionPointerExternalLink())) {
    $link = $page->getCollectionPointerExternalLink();
} else {
    $link = $nh->getLinkToCollection($page);
}
rosie607 replied on at Permalink Reply 1 Attachment
rosie607
Thanks for the help. I tried this but it didn't do anything.
I'm not sure I explained very well what I want to do.
So the block lists all pages with the specified Content Is Related To Category check box ticked.
I have an external link with the Content Is Related To Category check box ticked and I'd like it to appear in the outputted list like the real pages (as it does on a page list block).
jordanlev replied on at Permalink Reply
jordanlev
Sorry, I have no idea why external links wouldn't be coming up in the results like they do in the page list block -- the code that retrieves the pages is exactly the same.