Custom Attribute Search Block

Permalink 4 users found helpful
Custom Attribute Search Block

For anyone struggling with making a custom page attribute search block, here's mine FYI. It may not be an ideal solution, but it can save you a lot of time. And it's MY block, that is I made it to suit MY site. You will need to change some form input fields and some PHP code to suit your needs. If you know how to improve it, please share.

This custom attribute search block is based on the C5 search block. When you add the block to your page, it will ask you the same questions, just enter the label, search button text and result page. Then real hacking begins. To get it to work I had to override some more C5 blocks. I'll only tell you what I've changed. I can't make a standard generic block though.

1. I added 4 page attributes to my site thru the Dashboard (NOTE the values of the attribute handlers, these are very important!): 'length', 'continent', 'adventure', 'interest'.
2. Then I added attribute 'select' values (NOTE the values of the attributes are also very important! see below):
3. Unzip the archive, you should get 4 folders: blocks, jobs, libraries, search. Copy all the contents into your root folders accordingly: /blocks, /jobs, /libraries, /search.
4. The blocks folder contains the actual custom_attribute_search block.
5. AFTER you add the custom_attribute_search block to your page (maybe a side bar, then you should see 4 dropdown boxes), go to this folder and open 2 files: view.php - contains the search form, and controller.php - processes the search form.
6. In view.php I added 4 dropdown boxes:
print $form->label('length', 'Trip Length: ');
print $form->select('length', array('' => '', '3 days' => '3 days', '1 week' => '1 week', '2 weeks' => '2 weeks', '4 weeks' => '4 weeks'), '') . '<br />';
print $form->label('continent', 'Continent: ');
print $form->select('continent', array('' => '', 'Australia' => 'Australia', 'Europe' => 'Europe', 'Asia' => 'Asia', 'North America' => 'North America', 'South America' => 'South America', 'Antarctica' => 'Antarctica'), '') . '<br />';
print $form->label('adventure', 'Adventure Level: ');
print $form->select('adventure', array('' => '', 'Low' => 'Low', 'Medium' => 'Medium', 'High' => 'High', 'Extreme' => 'Extreme'), '') . '<br />';
print $form->label('interest', 'Interests: ');
print $form->select('interest', array('' => '', 'Culture' => 'Culture', 'Architecture' => 'Architecture', 'Nature' => 'Nature', 'Adventure' => 'Adventure'), '') . '<br />';

NOTE: the form inputs names AND values MUST be exactly the same as you've set in the page attributes.

7. In controller.php, the 'function do_search()' does the search and you need to change the 4 variables in these:
function do_search() {
      ...
      switch($match){
      ...
            if($r['length'] == $length and $r['continent'] == $continent and $r['adventure'] == $adventure and $r['interest'] == $interest){
               $results[] = new IndexedSearchResult($r['cID'], $r['cName'], $r['cDescription'], $r['score'], $r['cPath'], $r['content'], $r['length'], $r['continent'], $r['adventure'], $r['interest']);
      ...
            if($r['length'] == $length or $r['continent'] == $continent or $r['adventure'] == $adventure or $r['interest'] == $interest){
               $results[] = new IndexedSearchResult($r['cID'], $r['cName'], $r['cDescription'], $r['score'], $r['cPath'], $r['content'], $r['length'], $r['continent'], $r['adventure'], $r['interest']);

and in this:
public function getSearchPage($p) {
      ...
         $results[] = array('cID' => $c->getCollectionID(), 'cName' => $c->getCollectionName(), 'cDescription' => $c->getCollectionDescription(), 'score' => $c->getPageIndexScore(), 'cPath' => $c->getCollectionPath(), 'content' => $c->getPageIndexContent(), 'length' => $c->getAttribute('length'), 'continent' => $c->getAttribute('continent'), 'adventure' => $c->getAttribute('adventure'), 'interest' => $c->getAttribute('interest'));

8. My pages are of type 'articles'. The standard search block searches by 1 or more keywords with a single text input. This new custom_search_block first filters all content by 'articles' page type and then searches by page attributes selected by user using the select dropdown boxes. The '$pl->filterByCollectionTypeHandle('articles');' line in the controller.php can be commented out to enable search of all content or it can be changed to whatever page type required to narrow down the search. The search is done by matching either all or any of the selected attributes based on the match radio button.
9. Change /jobs/index_search.php to add 'site' and 'Main' areas to the indexed search:
Loader::library('database_indexed_search');
      $is = new IndexedSearch();
      $is->addSearchableArea('site');
      $is->addSearchableArea('Main');
      $result = $is->reindexAll();
      return t('%s page(s) indexed.', $result->count);

10. Change these values in /libraries/database_indexed_search.php:
public function __construct($id, $name, $description, $score, $cPath, $content, $length, $continent, $adventure, $interest) {
   ...
      $this->length = $length;
      $this->continent = $continent;
      $this->adventure = $adventure;
      $this->interest = $interest;
   ...
   public function getLength() {return $this->length;}
   public function getContinent() {return $this->continent;}
   public function getAdventure() {return $this->adventure;}
   public function getInterest() {return $this->interest;}

11. IF you want to use the C5 'search' block on the same page, you need to change these values in /search/controller.php as well:
function do_search() {
      ...
      foreach($res as $r) { 
         $results[] = new IndexedSearchResult($r['cID'], $r['cName'], $r['cDescription'], $r['score'], $r['cPath'], $r['content'], $r['length'], $r['continent'], $r['adventure'], $r['interest']);

12. And also change all 'query' names to 'squery' (or whatever to avoid conflicts with the same variables in the custom search block) in the controller.php and view.php in either the default search or custom search blocks.

So, at the end, what you need to do is basically only to change/add/delete those values to suit your search criteria.

Note: Only single-choice page attributes are supported. And of course you have to set correct page attributes in the dashboard.

Feel free to improve and extend it (Hint: multiple-choice attributes, generic block form configuration installation script, multiple page types, add more input select options etc.).

[Hope I didn't miss anything. I've also included all of this text in the howto.txt in the attached archive.]

linuxoid
View Replies:
linuxoid replied on at Permalink Reply 1 Attachment
linuxoid
For some reason it doesn't let removing and adding files when editing posts. So here's the latest attached.
tallacman replied on at Permalink Reply
tallacman
linuxoid, Thanks for this. I don't have a firm grasp on php yet but Im learning. I think this will help me and your instructions look clear and concise. Im going to give this a try over the weekend.


steve
roa123 replied on at Permalink Reply
Good work, would the same method work on say the page list block, so you could pull in common attributes from different pages?
linuxoid replied on at Permalink Reply
linuxoid
It should, it's only a matter of overriding the core block. My block already filters all pages by "articles" page type. If you want to further filter by attributes, all you need to do is to go through each found page and check if its attribute matches the one you need. Unfortunately I haven't figured out how to search by multiple page attributes, that is if the attribute itself has more than 2 values.
linuxoid replied on at Permalink Reply
linuxoid
As the new version of Concrete5 has been released, I'm wondering if there's any better and easier way to make such custom attribute search rather than hard-core hacking of the code? Thanks.
ribeena replied on at Permalink Reply
I was just looking into this myself - and yes, it is much much much easier in 5.5.1.

After adding your attributes and ensuring they are indexable, the search engine has all the work done for us now, just need to make a new Search block template to change the search form.

You can copy the "view.php" file from the search block folder, and put it into "root/blocks/search/templates/[Name of your new search]/".

The just need to change the form - the main snippet of code you will need is along the lines of this provided a "amount" attribute.

<label for="query"><?php print t('Keywords'); ?></label> <input name="query" type="text" value="<?php echo htmlentities($query, ENT_COMPAT, APP_CHARSET)?>" class="ccm-search-block-text" /><br />
    <?php
   /*
    * We need to get the attribute id, rather than find the ID ourselves, lets make it easier and get it from the syste,.
    */
   $ak_t = CollectionAttributeKey::getByHandle('amount');
   /*
    * now we just need to make an input field for it, with "akID[" + Attribute ID + "]" as the name
    */ 
    ?>
    <label for="akID[<?php print $ak_t->getAttributeKeyID(); ?>]"><?php print t('Amount'); ?></label> <input name="akID[<?php print $ak_t->getAttributeKeyID(); ?>]" type="text" value="<?php echo htmlentities($_REQUEST['akID'][$ak_t->getAttributeKeyID()], ENT_COMPAT, APP_CHARSET)?>" class="ccm-search-block-text" />
   <br />
   <input name="submit" type="submit" value="<?php echo $buttonText?>" class="ccm-search-block-submit" />


In the above case, i've added a label to the general keywords search and a textbox to search an attribute called 'amount', there are no other changed needed for the view.php file.

This will do a "AND" search, so if you put a value in Amount and in Keywords, the results lay within both.

Not sure about more advanced options, but for basic it'll work well.