background image
HomeRecent PostsDrupalSearchTagsRSSContactAboutAccount

In this tutorial I'll show how you to create a custom Lucene Search facet from taxonomy and integrate into the search form block. A great first step would be to review the Drupal Lucene API documentation (see luceneapi.api.php PHP file in the luceneapi module folder).

I assigned the search form block to a region in my theme (admin/build/block), which generates this form:

Search form block

I added a new vocabulary called "Topics", and added a few terms (admin/content/taxonomy).

Topic and terms

The goal of this code is to integrate the Topic vocabulary into the search form block to allow the user to select a taxonomy term as they search. To start, you need to implement a hook_luceneapi_facet_realm() and a callback function in your custom module.

<?php
/**
* Implements hook_luceneapi_facet_realm()
*/
function MYMODULE_luceneapi_facet_realm() {

 
$realms = array();

 
$realms['form'] = array(
   
'title' => t('Search form block'),
   
'callback' => 'MYMODULE_luceneapi_facet_realm_callback_search_form_block',
   
'callback arguments' => array(),
   
'allow empty' => TRUE,
   
'description' => t('Displays facets in the search form block.'),
  );
 
  return
$realms;
 
}

/**
* Implements hook_luceneapi_facet_realm() callback function
*/
function MYMODULE_luceneapi_facet_realm_callback_search_form_block($facets, $realm, $module) {

 
$form = array();

 
// loop through facets
 
foreach ($facets as $name => $facet) {

   
// NOTE: luceneapi_facet_to_fapi_convert() converts a Lucene facet to Drupal Form API data
   
$form = array_merge_recursive($form, luceneapi_facet_to_fapi_convert($facet));

  }

  return
$form;

}
?>

At this point, if you go to the facets admin page (admin/settings/luceneapi_node/facets), you can see the newly created realm. I assigned the taxonomy vocabulary "Topic" to this realm.

Assigning facets to realms

Up next is implementing a hook_form_alter() to integrate the facet into the search form.

<?php
/**
* Implementation of hook_form_FORM_ID_alter().
*/
function MYMODULE_form_search_block_form_alter(&$form, &$form_state) {
 
 
// get default search module (IE: luceneapi_node) 
 
$module = luceneapi_setting_get('default_search');

 
// check if default search module is defined in lucene searchable module list
 
if (array_key_exists($module, luceneapi_searchable_module_list())) {

   
// get index type (IE: node)
   
$type = luceneapi_index_type_get($module);

   
// fetch realm facets
   
$elements = luceneapi_facet_realm_render('form', $module, $type);

   
// if facet form elements exist, recursively merge with current form object
   
if (!empty($elements)) {
     
$form = array_merge_recursive($form, $elements);
    }

  }

}
?>

The search from block should now show an empty "Topic" facet.

Search form block, empty topic

The last piece of code implements hook_luceneapi_facet_postrender_alter() which gives you the opportunity to modify the facet, and in the this case, add its options.

Immediately after implementing this hook, if you krumo() or dsm() the $items argument, you'll see the form element has no options.

Using krumo to see empty form element

The next section of code copied the contrib module code in "Lucene Node". [See file: luceneapi/contrib/luceneapi_node/luceneapi_node.module; function: function luceneapi_node_luceneapi_facet_postrender_alter()]

<?php
/**
* Implements hook_luceneapi_facet_postrender_alter()
*/
function MYMODULE_luceneapi_facet_postrender_alter(&$items, $realm, $module, $type = NULL) {

  if (
$realm == 'form' && $module == 'luceneapi_node' && $type == 'node' && is_array($items['category'])) {
   
   
// get taxonomy form data
   
$taxonomy = module_invoke('taxonomy', 'form_all', 1);
   
   
// get enabled facets
   
$facets_enabled = luceneapi_facet_enabled_facets_get($module, $realm);
   
   
// loop through enabled facets, validate, and fetch weight
   
$weights = array();
    foreach (
$facets_enabled as $name => $value) {
   
     
// check for "category" facet
      // FORMAT: category_{VOCABID}
     
if (preg_match('/^category_(\d+)$/', $name, $match)) {
     
       
// load taxonomy vocabulary     
       
if ($vocabulary = taxonomy_vocabulary_load($match[1])) {
       
         
// ensure category and vocab id is enabled for this module and realm
         
if (luceneapi_facet_enabled($match[0], $module, 'form')) {
                    
           
// fetch weight
           
$variable = sprintf('luceneapi_facet:%s:%s:%s:weight', $module, $realm, $name);
           
$weights[$vocabulary->name] = variable_get($variable, 0);
         
          }
       
        }
     
      }
   
   
// end foreach
   
}
   
   
// gets weighted taxonomy array
   
asort($weights);
   
$taxonomy_weighted = array();
    foreach (
$weights as $vocab_name => $weight) {
     
$taxonomy_weighted[$vocab_name] = $taxonomy[$vocab_name];
    }

   
// create array of fapi data to override
   
$category_data = array(
     
'#prefix' => '<div class="criterion">',
     
'#suffix' => '</div>',
     
//'#size' => 10,
     
'#options' => $taxonomy_weighted,
     
'#multiple' => TRUE,
     
'#default_value' => luceneapi_facet_value_get('category', array()),
     
'#title' => NULL,
     
'#description' => NULL,
    );

   
// merge data
   
$items['category'] = array_merge($items['category'], $category_data);
   
   
// sets weight as the lowest weight of all taxonomy facets
   
if (is_array($items['category']['#weight'])) {
     
$items['category']['#weight'] = min($items['category']['#weight']);
    }   
         
 
// end if
 
}

}
?>

Reloading the page will now show the search form with a completed facet.

Search form with facet

Submitting the search form block with a selected taxonomy term will now take the user to the search results page with the taxonomy facet pre-selected!

Special thanks to Chris Pliakas for all of his great Lucene API work! (I miss working with you Chris)

In this tutorial I'll show you how to upload an image using the Forms API, create a new node, and attach the image to the CCK (filefield/imagefield) field. I wrote this code to work with the modules I primarily use for image processing: cck, filefield, imageapi, imagecache, imagefield, mimedetect, and transliteration.

After I installed those modules, I created a new node type (admin/content/types/add) called "Image" and added a single imagefield field.

Image node fields

Next, I created a custom module with a hook_menu() implementation:

<?php
// NOTE: this variable is used through the code,
// so I thought it would be better to put it in a constant
define('IMAGE_UPLOAD_CONTAINER', 'image_upload');

/**
* Implements hook_menu()
*/
function helper_menu() {

 
// create a blank array of menu items
 
$items = array();
 
 
// define page callback for upload form
  // NOTE: you'll want to restrict permission better [see: access arguments]
 
$items['upload'] = array(
   
'title' => t('Upload'),
   
'description' => t('Upload'),
   
'page callback' => 'drupal_get_form',
   
'page arguments' => array('helper_page_callback_upload_form'),
   
'access arguments' => array('access content'),
   
'type' => MENU_CALLBACK,
  );
 
 
// return menu items
 
return $items;

}
?>

I defined the form function page callback:

<?php
/**
* Implements page callback for upload form
*/
function helper_page_callback_upload_form() {

 
// create an empty form array
 
$form = array();
 
 
// set the form encoding type
 
$form['#attributes']['enctype'] = "multipart/form-data";
 
 
// add a file upload file
 
$form[IMAGE_UPLOAD_CONTAINER] = array(
   
'#type' => 'file',
   
'#title' => t('Upload an image'),
  );
  
 
// add a submit button
 
$form['submit'] = array(
   
'#type' => 'submit',
   
'#value' => 'Submit',
  );
 
 
// return form array
 
return $form;

}
?>

This page callback function results in the following form:

Image node form

Then I added the form validation and submit handler functions:

<?php
/**
* Implements form validation handler
*/
function helper_page_callback_upload_form_validate($form, &$form_state) {

 
// if a file was uploaded, process it.
 
if (isset($_FILES['files']) && is_uploaded_file($_FILES['files']['tmp_name'][IMAGE_UPLOAD_CONTAINER])) {

   
// validate file extension
    // NOTE: you can ellaborate on this code and add additional validation
   
if ($_FILES['files']['type'][IMAGE_UPLOAD_CONTAINER] != 'image/jpeg') {
     
form_set_error(IMAGE_UPLOAD_CONTAINER, 'Invalid file extension.');
      return;
    }

   
// attempt to save the uploaded file
   
$file = file_save_upload(IMAGE_UPLOAD_CONTAINER, array(), file_directory_path());

   
// set error if file was not uploaded
   
if (!$file) {
     
form_set_error(IMAGE_UPLOAD_CONTAINER, 'Error uploading file.');
      return;
    }
      
   
// set files to form_state, to process when form is submitted
   
$form_state['storage'][IMAGE_UPLOAD_CONTAINER] = $file;
      
  }
  else {
   
// set error
   
form_set_error(IMAGE_UPLOAD_CONTAINER, 'Error uploading file.');
    return;  
  }

}

/**
* Implements form submit handler
*/
function helper_page_callback_upload_form_submit($form, &$form_state) {
 
 
// create new node object
 
$new_node = (object) array(
   
'type' => 'image',
   
'uid' => $GLOBALS['user']->uid,
   
'name' => $GLOBALS['user']->name,
   
'title' => t('YOUR NODE TITLE'),
   
'status' => 1,
   
'field_image' => array(
      (array)
$form_state['storage'][IMAGE_UPLOAD_CONTAINER],
    ),
  );
   
 
// save node
 
node_save($new_node);
 
 
// clear form storage, to allow form to submit
 
$form_state['storage'] = array();
 
 
// redirect user, set message, etc!

}
?>

After using the form to upload an image, the following node was created:

New image node

Syndicate content