Customize Advanced Search Drop-Down Menus in Search-form.php

Hello,

I’m trying to customize the advanced-search form to include dropdown menus to select certain search filters and I think I’m in a little over my head.

I’ve used the “Hide Metadata” plugin to hide the item types that I don’t want to see in the menu and that has worked well. What I would like are additional drop-down menus based on particular elements in the metadata. I.e. in the example below, I would like the dropdown to list all of the different “Branch of philosophy” entries that exist and allow the user to select one to limit their search.

I’ve tried adapting what I saw in other sections of “search-form.php” such as:

  
<?php echo $this->formLabel('item-type-search', __('Record Type')); ?>
<?php echo $this->formSelect( 'type', @$_REQUEST['type'], array('id' => 'item-type-search'), get_table_options('ItemType') ); ?>

My failed adaptation is the code snippet below,

    
<?php echo $this->formLabel('branch-type-search', __('Branch of Philosophy')); ?>
<?php echo $this->formSelect( 'branch', @$_REQUEST['branch'], array('id' => 'branch-search'), get_table_options(metadata('Item Type Metadata', 'Branch of philosophy')) ); ?>

which causes Omeka to generate the following error:

Omeka_View_Exception
A current item_type_metadatum variable has not been set to this view.

I’ve also tried reading the documentation for Omeka and Zend for more information on formSelect() and get_table_options() but I’m still not understanding how to fix this/ make this work. What is $this calling? Are formLabel and formSelect setting variables or looking for something defined elsewhere?

Any help you could provide would be greatly appreciated! Thanks,

Bryan

Sorry to bug the forum again. Any ideas on how I might go about solving this? I keep trying to figure it out for myself, but can’t understand where to get started or what valid inputs I can pass to formSelect, formLabel, and get_table_options… Any pointers at all would be really appreciated!

Could you tell more about what distinguishes “Branch of philosophy” items? It sounds like it is an item type that you created, but I’m not 100% sure. Knowing more about that data and about the end result you are aiming for will help sort out how to create the selects.

Either way, the direct problem is coming from this [quote=“b.brazeau, post:1, topic:1170”]
metadata(‘Item Type Metadata’, ‘Branch of philosophy’)
[/quote]

metadata() is used for getting a specific value of metadata from an Omeka record. Since there’s no current record set on the page, that’s what’s probably causing the exception.

Hi Patrick,
Thanks for the reply. Yes, ‘Branch of Philosophy’ is an item type metadata element field I created for several item types in the database. I’d like users to be able to browse all possible entries for “Branch of Philosophy” in a dropdown menu on the search page, so they can restrict their searches only to items where “Branch of Philosophy” matches the branch they select.

The problem is that if we just use the traditional search interface with “is exactly” or “contains” - the user might not know all of the possible branches and we’d like them to be able to select this before performing a search. Does that make sense?

Thanks again for the help.

-Bryan

Makes sense indeed, and might be similar to something I worked on a short while ago. This page displays all of the Dublin Core Publishers data on the site, and links to them. It isn’t a search page or select, but it seems akin to what you are looking for.

If so, we can work on modifying the code to your needs.

Hi Patrick,

Yes, if we could find some way to get Omeka to generate a list like that, but in a drop-down menu that users can select as a search filter, that would be amazing. What I’m looking for is something similar to the “Record Type” dropdown that currently exists on the advanced search page, so the user can click and open the menu, select the branch of philosophy they want to apply to their search, and then run the search.

(I have a few more elements like this - such as genre, that I’d also like to add as drop-down menus, but I imagine once we figure out a code recipe for one menu, it’s just a matter of changing which item type elements we feed it).

Thanks again!

-Bryan

To put it into the search that way, you’ll need a plugin that does the following two things, though it could get a bit more tricky as you go along. It won’t be as simple as just adding another select to the form, though.

  1. Digs up the values for the select and adds it to the form with the public_items_search hook. This could be done without the plugin, but it will make more sense to go the plugin route, since you’ll have to have a plugin for

  2. Modifies the SQL query with the items_browse_sql hook.

For 1, the code could start with this:

        $db = get_db();
        $elementId = 45; //dc publisher
        $elTextTable = $db->getTable('ElementText');
        $select = $elTextTable->getSelectForFindBy(array('element_id' => $elementId));
        $select->group('text');
        
        $elTexts = $elTextTable->fetchObjects($select);
        $elTextsArray = array();
        foreach($elTexts as $elText) {
             $elTextsArray[$elText->id] = $elText->text;
        }


$elTextsArray should turn out to be what you need for the last parameter in $this->formSelect()

In the items_browse_sql hook, you’d then have to look up the value in the query params that passed to it and modify the sql query as needed – I’m not 100% sure that step would work at this point, though.

Hope that at least starts things moving along.

Thanks for the help, Patrick.

Since ‘branch of philosophy’ is a controlled vocabulary, only has around 10 options, and won’t grow or change with new items, might it just be easier to create a new select, defining each option right in the form? How would I go about doing this and making sure that when the user selects a particular value, the search function knows that I want that particular element text to be exactly equal to that value? I.e. could I do something like the following (I’m guessing at proper syntax here)

formSelect(
    array('advanced_search' => array(
        array(
            'element_id' => 182,
            'type' => 'is exactly',
            'terms' => 'Metaphysics'
        )
    )),); 

I managed to figure out that Branch of Philosophy is saved as elementId 182 in the mySQL database. Rather than creating a plugin, I tried using the code you provided in /omeka/application/views/helpers/itemSearchFilters.php creating a new case called ‘branch’, modifying it slightly since $db = get_db(); already existed above. Here’s what I put in:

    case 'branch':
                    	$element = get_db()->getTable('Element')->findByElementSetNameAndElementName('Item Type Metadata', 'Branch of philosophy');
						$id = $element->id;
                         $elementId = 182; //branch of philosophy
						 $elTextTable = $db->getTable('ElementText');
						 $select = $elTextTable->getSelectForFindBy(array('element_id' => $elementId));
						 $select->group('text');
        
						 $elTexts = $elTextTable->fetchObjects($select);
						 $elTextsArray = array();
						 foreach($elTexts as $elText) {
							 $elTextsArray[$elText->id] = $elText->text;
							 }
                        break;

When I tried to call $elTextsArray as the variable in $this->formSelect() in search-form.php, nothing showed up in the drop-down menu. I tried doing a var_dump which resulted in NULL - so I don’t think ItemSearchFilters.php is setting the variable - is this file only called after you submit the form?

Sorry to be so dense, been really trying to read through Zend_framework and Omeka_documentation and I feel I’m getting closer, but still not there yet.

Thanks again!

-Bryan

Trying the plugin route, I noticed that ExhibitBuilder seems to have some lines for adding a “search by exhibit” section to the advanced search form. Is there anyway we could adapt that?

Here’s my first stab at a plugin based on Exhibit builder (it crashes Omeka in its current form and doesn’t include a long section at the bottom of Exhibit Builder’s functions.php which sets the Item search filters for exhibits.

 <?php

class BOPSearchPlugin extends Omeka_Plugin_AbstractPlugin
{

protected $_hooks = 'public_items_search';

function public_items_search()
{
        $view->formLabel('branch', __('Branch of Philosophy'));
        $db = get_db();
        $elementId = 182; //ITM Branch of Philo
        $elTextTable = $db->getTable('ElementText');
        $select = $elTextTable->getSelectForFindBy(array('element_id' => $elementId));
        $select->group('text');
        $elTexts = $elTextTable->fetchObjects($select);
        $elTextsArray = array();
        foreach($elTexts as $elText) {$elTextsArray[$elText->id] = $elText->text;}; 
        $view->formSelect('branch', @$_GET['branch'], array($elTextsArray));
}
?>

So, the first issue, probably what’s causing it is the naming convention that the name of the function should be hookPublicItemsSearch(), and the property should be

protected $_hooks = array('public_items_search');

I think that should at least get that part working.

From your earlier post, hard-coding the select would conceivably work (though not with the syntax you have there), but you’d risk them being overridden when you do an update to Omeka. This way (if it works) will be a bit more manageable.

This guide to plugins might help catch other things as they come up.

When this bit for adding the form works, we’ll be on to the second part I described about modifying the search sql itself.

Hi Patrick,

Managed to get the plugin to load and install, but it doesn’t seem to be doing anything as of yet. The working plugin.php looks like this:

 <?php

class BOPSearchPlugin extends Omeka_Plugin_AbstractPlugin
{

protected $_hooks = array('public_items_search');

function hookPublicItemsSearch()
{
        
        $db = get_db();
        $elementId = 182; //ITM Branch of Philo
        $elTextTable = $db->getTable('ElementText');
        $select = $elTextTable->getSelectForFindBy(array('element_id' => $elementId));
        $select->group('text');
        $elTexts = $elTextTable->fetchObjects($select);
        $elTextsArray = array();
        foreach($elTexts as $elText) {$elTextsArray[$elText->id] = $elText->text;}; 
        
}
}

On search-form.php, the following code generates a “Branch of Philosophy” dropdown menu, but there’s nothing in it.

 
   
<?php echo $this->formLabel('branch-search', __('Branch of Philosophy')); ?>
<?php echo $this->formSelect($elTextsArray); ?>

When I do a var_dump on $elTextsArray I still get “NULL”

I’ve also tried using this instead - which doesn’t seem to do anything in terms of generating a search menu:

  <?php fire_plugin_hook('public_items_search', array('view' => $this)); ?>

Any ideas on where to go from here? Thanks again for the help!

-B

So, there’s a couple things going on with the public_items_search hook.

The way that it works is that it should echo out the html code that will get inserted at the bottom of the search form. Thus, you don’t need to make any changes to the search-form.php file. Part of what’s confusing is that changes are happening in both places.

In your plugin’s hook, then, you’ll want to build up the html then echo it out at the end.

I often do that in a pattern like this:

$html = "";
$html .= "<div class='field'>";
//build up more html 
$html .= "</div>";
echo $html;

So the //build up more html part is where the interesting stuff happens.

The hook takes an array of arguments that includes the $view object. You’ll need that to build the select, so start with

function hookPublicItemsSearch($args)
{
    $view = $args['view'];
....
}

Keep the stuff you have for digging up the $elTextsArray, then use the $view object to build up the complex html:

$html .= $view->formLabel('branch-search', __('Branch of Philosophy'));

Notice that $view here is just what $this is in the form itself as you describe what you did. Instead of echoing the result, just tack it on to the $html.

In the context of the plugin, then, $view->formSelect will work the same way. You posted code that’s pretty close above – the $elTextsArray will be the last parameter you pass to it.

Putting those together should get the select into the form.

Thanks, Patrick and apologies for the slow reply; was away on research for the past week.

I think we’re nearly there. I’ve amended the plugin’s code to read as follows:

 
<?php

class BOPSearchPlugin extends Omeka_Plugin_AbstractPlugin
{

protected $_hooks = array('public_items_search');

function hookPublicItemsSearch($args)
{
        $view = $args['view'];
        $db = get_db();
        $elementId = 182; //ITM Branch of Philo
        $elTextTable = $db->getTable('ElementText');
        $select = $elTextTable->getSelectForFindBy(array('element_id' => $elementId));
        $select->group('text');
        $elTexts = $elTextTable->fetchObjects($select);
        $elTextsArray = array();
        foreach($elTexts as $elText) {$elTextsArray[$elText->id] = $elText->text;};
        $srtdElTextsArray = sort($elTextsArray); 
        $html = "";
		$html .= "
"; //build up more html $html .= $view->formLabel('branch-search', __('Branch of Philosophy')); $html .= "
"; $html .= $view->formSelect('branch', @$_REQUEST[branch], array('id' => 'branch-search'), $elTextsArray); $html .= "
"; echo $html; } }

This is outputting the proper drop-down on the search form and correctly building the select (yay!). Here is the HTML it spits out:

  
Branch of Philosophy
Logic Moral Philosophy —Astronomy —Biology —Economics —Ethics —Physics —Politics —Psychology Astronomy Logic Metaphysics Moral Philosophy Natural Philosophy Poetics Political Philosophy Rhetoric

Certain values are appearing twice. I’ve already fixed the “Astronomy”/"—Astronomy" issue, but I’m still not sure why “Logic” and “Moral Philosophy” show up twice… In any case, I think I can fix that once we get the search working.

Before moving on to making sure it works in searching the DB, I’ve been trying with no luck to do two things:

  1. Replace select option “” with “Select Below”. I noticed that the Record Type drop-down select includes

     Select Below  
    Is there any way to get my “Branch of Philosophy” select to echo this?
  2. To arrange a custom sorting of options while keeping the same option values. Neither sort() nor asort() seem to work for this; the first ignores value associations, the second simply outputs a “1”. Ideally, I’d like to have it sort alphabetically; and (probably impossible, I realize), customize which sub-branches (indicated by a —) should appear after their main branch. If I don’t plan on ever adding any new branches to the DB, would you recommend that I tell the plugin to echo the HTML output as presented here, moving things around to my own liking, or is this bad practice?

Thanks again for your help!

-Bryan

For the first question, that’s just as easy adding it to the top of the array:

$elTextsArray = array();
$elTextsArray[] = "Select Below";

For the second question, that’s because asort() converts the array itself. What it returns is just whether or not it succeeded, so you don’t need to create a new variable.
Getting the sub-branches in the right place is impossible to do automatically. I’ve seen worse things than moving what you want around, which basically amounts to hard-coding the array now that you have the data you need. It’s just a risk that it’ll be a pain if things ever do change.

On the duplicates, since they have different ids they are considered different in the database, somehow there are values that differ very slightly, maybe a space or some special character. Once it’s all working, you can use the search to track down which items have the slight difference.

Hi Patrick,

Thanks for all of this help! I think we’ve now finished part 1.

To pass the user’s selection onto the sql query and restrict their search to the branch of philosophy they choose, you mentioned that I should modify the items_browse_sql hook. Would that look something like this? and is $args the right variable to pass along? I imagine that this bit of code would go below part 1?

 
protected $_hooks = array('items_browse_sql');

function hookItemsBrowseSql($args) {
???
}

I found this link in the documentation: http://omeka.readthedocs.io/en/latest/Reference/hooks/<model>_browse_sql.html

I’m confused, though, as to how I tell the hook to pick up the select object chosen by the user, and what params I would need to configure this correctly (documentation says that web requests will use GET and zend routing params but that internal queries will be passed to Omeke_Db_Table::findBy). Additional questions here: is this a web or internal request (seems to me that it would be internal) and is the final “e” a typo (i.e. should it be Omeka_Db_Table::findby)?

Is there any alternate documentation where we can see these elements in practice? Sorry to keep bothering you with every step!

Thanks again for walking me through this,

-Bryan

I just found this post which you commented on 3 years ago where a user was employing the same hook. http://omeka.org/forums-legacy/topic/custom-query-in-items-browse-action

I’ve now figured out to put ‘items_browse_sql’ within the array at the top after

 protected $_hooks = array(‘hook1’, ‘hook2’); 

The following section is tripping me up though. I tried using the code in your previous post:

 public function hookItemsBrowseSql($args)
{
    $select = $args['formSelect'];
    $select->where("branch-search = ?", 1);
}

but Omeka is throwing an error:

Fatal error: Call to a member function where() on a non-object in /var/www/html/omeka/plugins/BOPSearch/BOPSearchPlugin.php on line 35

I realize that “where” might not be the right function to use here, but I’m not sure what to put inside those curly brackets.

Hi Patrick,

After another few hours bashing away at this, I’ve managed to fix the error but still can’t get Omeka to pick up the user’s select as a filter for the items in search.

False - Error remains, I had simply forgotten to uncomment ‘items_browse_sql’ in protected $_hooks = array

Currently, part 2 looks like this:

 
function hookItemsBrowseSql($args)
{
    $select = $args['args'];
    $params = $args['params'];
    $db = get_db();
    $select->where('branch-search');
    return $select;   
}

I’ve played around with it and checked to see what happens in the URL. It seems that the user’s selection is showing up in the URL (branch=73061 changes with different selects and corresponds to branches selected by the user), but it’s not filtering the search results.

http://[…]/omeka/items/browse?search=&advanced%5B0%5D%5Belement_id%5D=&advanced%5B0%5D%5Btype%5D=&advanced%5B0%5D%5Bterms%5D=&type=&branch=73061&submit_search=Search+for+items

When I try to run a search using the existing field and “is exactly” for that branch of philosophy, I get the following URL:

http://[…]/omeka/items/browse?search=&advanced%5B0%5D%5Belement_id%5D=182&advanced%5B0%5D%5Btype%5D=is+exactly&advanced%5B0%5D%5Bterms%5D=Moral+Philosophy&type=&branch=0&submit_search=Search+for+items

I think the issue is that it’s not picking up the element_id of 182 (which was the element in the SQLdb that held the Branch of Philosophy options). Any idea on how to fix this in the plugin?

P.S.
(I promise to repay all of this help by uploading the finished plugin to git and by posting a “how to” guide in the forums for anyone interested in doing this in future!).

This is kinda cobbled together from how some of the other advanced search stuff works and I haven’t tested it, but hopefully it’ll get things rolling in the right direction.

From $params, you’ll be able to dig up the $elementId and $value you’ll need below.

$alias should just be something unique in the query, something like ‘branch_search’ would probably work.

$db = get_db();

$joinCondition = "{$alias}.record_id = items.id AND {$alias}.record_type = 'Item' AND {$alias}.element_id = $elementId";

$select->joinLeft(array($alias => $db->ElementText), $joinCondition, array());

$value = $db->quote($value);
$whereClause = " AND {$alias}.text = $value";

$select->where($whereClause);

Thanks, Patrick. This is really helpful.

I keep getting a similar error, though:

Fatal error: Call to a member function joinLeft() on a non-object in /var/www/html/omeka/plugins/BOPSearch/BOPSearchPlugin.php on line 43

From what I understand about PHP, this error is happening because the variable being assigned to joinLeft() —in this case $select— is undefined or null.

My main question right now is how do I grab the user-chosen value from the formSelect in part 1 (hookPublicItemsSearch) and use that in Part II (hookItemsBrowseSql), to tell Omeka that I want it to grab that value/text pair? I tried

 var_dump($_POST['formSelect']);

and

 var_dump($_POST['branch'] 

but no luck with either of those. It just spits out “NULL NULL”

Here’s the whole plugin as I have it so far:


<?php

class BOPSearchPlugin extends Omeka_Plugin_AbstractPlugin
{

protected $_hooks = array('public_items_search',
						  'items_browse_sql');

function hookPublicItemsSearch($args)
{
        $view = $args['view'];
        $db = get_db();
        $elementId = 182; //ITM Branch of Philo
        $elTextTable = $db->getTable('ElementText');
        $select = $elTextTable->getSelectForFindBy(array('element_id' => $elementId));
        $select->group('text');
        $elTexts = $elTextTable->fetchObjects($select);
        $elTextsArray = array();
        $elTextsArray[] = "Select Below";
        foreach($elTexts as $elText) {$elTextsArray[$elText->id] = $elText->text;};
        $html = "";
		$html .= "
"; //build up more html $html .= $view->formLabel('branch-search', __('Branch of Philosophy')); $html .= "
"; $html .= $view->formSelect('branch', @$_REQUEST['branch'], array('id' => 'branch-search'), $elTextsArray); $html .= "
"; echo $html; } public function hookItemsBrowseSql($args) { $select = $args['formSelect']; $params = $args['params']; $branch = $_POST['branch']; $select = $branch; $db = get_db(); $joinCondition = "{$branch_search}.record_id = items.id AND {$branch_search}.record_type = 'Item' AND {$branch_search}.element_id = $elementId"; $select->joinLeft(array($alias => $db->ElementText), $joinCondition, array()); $value = $db->quote($value); $whereClause = " AND {$branch_search}.text = $value"; $select->where($whereClause); } }

You’re right that $select ends up being undefined, which causes the first problem. Do

$select = $args['select'];

and that’ll solve that part of it.

I think that with that, putting something like this close to the top of the function will help (this refers back to the code I posted above)

$alias = 'branch_search';

That is, you don’t want $branch_search as another new variable, because that will also be undefined. You want to say what the alias is, as a string value – that turns into a part of the SQL query.

You should be able to do a var_dump or whatever to inspect the $params as you have it to see how to dig up the data you need. A little more info in the documentation (admittedly incomplete, alas).