4

I am adding a Soundex featured to the advanced search. I am using the queryObjects hook to add this into the query. The documentation on this is spotty, only referencing CiviHR. I have downloaded that and gone though it, matching my code to it as closely as I can, but I am having issues getting my values to save/bind to the form.

The code adds nicely to the advanced search: enter image description here

However, I fill out the fields and submit the form, the fields are not populated after the results load. I was able to bind the fields to the form in the postProcess hook (below) so the values are repopulated in the search form, but the query and qill are not affected.

There are a couple of parts to this extension:

Query Objects Hook In my main extension main file

function myextenion_civicrm_queryObjects (&$queryObjects, $type = 'Contact') {
    if ($type == 'Contact') {
        $queryObjects[] = new CRM_Myextension_BAO_Query_Soundex();
    }
}

CRM/Myextension/BAO/Query/Soundex.php

<?php 

class CRM_Myextenion_BAO_Query_Soundex extends CRM_Contact_BAO_Query_Interface {

    public static $soundex_fields = [
        'soundex_first_name' => [
            'name'  => 'soundex_first_name', 
            'title' => 'First Name Sounds Like', 
            'type'  => CRM_Utils_Type::T_STRING, 
        ],
        'soundex_last_name' => [
            'name'  => 'soundex_last_name', 
            'title' => 'Last Name Sounds Like', 
            'type'  => CRM_Utils_Type::T_STRING, 
        ],
    ];

    public function &getFields() {

        $return = static::$soundex_fields;

        return $return;
    }

    public function from($name, $mode, $side) {
        return '';
    }

    public function where(&$query) {

        if (empty($query->_params)) return;

        foreach ($query->_params as $id => $param) {
            if (empty($param[0])) {
                continue;
            }

            $this->whereClauseSingle($param, $query);           
        }
    }

    public function whereClauseSingle(&$values, &$query) {

        list($name, $op, $value, $grouping, $wildcard) = $values;

        $fields = $this->getFields();

        if (!array_key_exists($name,$fields)) return;

        $field = $fields[$name];
        $fieldName = substr($field['name'],8); // strip off the soundex_ at the beginning

        $query->_qill[$grouping][] = ts($field['title'])." - '$value'";
        $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("SOUNDEX(contact_a.".CRM_Core_DAO::escapeString($fieldName).")", "=", soundex($value), "String");
    }    

    public function getPanesMapper(&$panes) {

        $panes['Soundex'] = 'civicrm_contact';
    }

    public function registerAdvancedSearchPane(&$panes) {

        $panes['Soundex'] = 'soundex';
    }

    public function buildAdvancedSearchPaneForm(&$form, $type) {

        // echo pre($form->_formValues);

        if (in_array($type,[
            'soundex',
        ])) {
            $fields = $this->getFields();
            foreach ($fields as $name => $label) {
                $form->add('text', $name, ts($label['title'])); 
            }  
        }
    }

    public function setAdvancedSearchPaneTemplatePath(&$paneTemplatePathArray, $type) {

        if (in_array($type,[
            'soundex',
        ])) {
            $template = ucfirst($type);
            $paneTemplatePathArray[$type] = "CRM/Contact/Form/Search/Criteria/{$template}.tpl";
        }
    } 

}

CRM/Contact/Form/Search/Criteria/Soundex.tpl

<table class="form-layout">
  <tr>
    <td>
      <label>{ts}{$form.soundex_first_name.label}{/ts}</label><br />
      {$form.soundex_first_name.html}
    </td>
    <td>
      <label>{ts}{$form.soundex_last_name.label}{/ts}</label><br />
      {$form.soundex_last_name.html}
    </td>
    <td>
      {* <label>{ts}Use OR on Soundex (default AND){/ts}</label><br />
      {$form.soundex_or_and.html} *}
    </td>
  </tr>
</table>

Post Process hook (only kinda of working)

function myextension_civicrm_postProcess($formName, &$form) {

    switch ($formName) {
        case 'CRM_Contact_Form_Search_Basic':
        case 'CRM_Contact_Form_Search_Advanced':

            // Soundex
            if (!empty($_POST)) {
                foreach(CRM_Myextension_BAO_Query_Soundex::$soundex_fields as $field => $label) {
                    $filterVal = CRM_Utils_Request::retrieve($field, 'String', $form);
                    if (empty($filterVal)) continue;
                    $form->_formValues[$field] = $filterVal;
                }
                $form->set('formValues', $form->_formValues);
            }

            break;
    }
}
4
  • Soundex is a phonetic algorithm for indexing names by sound, as pronounced in English. The goal is for homophones to be encoded to the same representation so that they can be matched despite minor differences in spelling (had no idea till i googled)
    – petednz - fuzion
    Commented Sep 9, 2017 at 5:14
  • Haha ya, sorry I got deep enough into the technical I forgot to explain the basics of what I was trying to accomplish. Commented Sep 9, 2017 at 11:05
  • That sounds like a really interesting extension. Do you happen to have a finished extension or a prototype to play with? Commented Apr 8, 2022 at 10:00
  • I never did roll it into a standalone extension, just a customization to a site. Commented May 2, 2023 at 19:10

1 Answer 1

3

Ok, I circled back around to this and figured it out. It was staring me right in the face.

The following function in CRM/Myextension/BAO/Query/Soundex.php needs changed. There needs to be a hidden field added to the search form for each advanced search pane. It was so simple in CiviHR example that I look right over it and went down the rabbit hole.

public function buildAdvancedSearchPaneForm(&$form, $type) {

    if ($type=='soundex') {

        $form->add('hidden', 'hidden_soundex', 1);

        $fields = $this->getFields();

        foreach ($fields as $name => $label) {
            $form->addElement('text', $name, ts($label['title'])); 
        }  
    }
}

Also, there is no need for a postProcess hook.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.