Answers.Body: <p>Ok, I circled back around to this and figured it out. It was staring me right in the face.</p>
  
  <p>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.</p>
  
  <pre><code>public function buildAdvancedSearchPaneForm(&amp;$form, $type) {
  
      if ($type=='soundex') {
  
          $form-&gt;add('hidden', 'hidden_soundex', 1);
  
          $fields = $this-&gt;getFields();
  
          foreach ($fields as $name =&gt; $label) {
              $form-&gt;addElement('text', $name, ts($label['title'])); 
          }  
      }
  }
  </code></pre>
  
  <p>Also, there is no need for a postProcess hook.</p>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2017-11-15T08:44:32.967Z
Answers.LastActivityDate: 2017-11-15T08:44:32.967Z
Answers.UserDisplayName: David Hayes
Body: <p>I am adding a Soundex featured to the advanced search. I am using the <a href="https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_queryObjects/" rel="nofollow noreferrer">queryObjects</a> 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.</p>
  
  <p>The code adds nicely to the advanced search:
  <a href="https://i.sstatic.net/656NW.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/656NW.png" alt="enter image description here"></a></p>
  
  <p>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.</p>
  
  <p>There are a couple of parts to this extension:</p>
  
  <p><strong>Query Objects Hook In my main extension main file</strong></p>
  
  <pre><code>function myextenion_civicrm_queryObjects (&amp;$queryObjects, $type = 'Contact') {
      if ($type == 'Contact') {
          $queryObjects[] = new CRM_Myextension_BAO_Query_Soundex();
      }
  }
  </code></pre>
  
  <p><strong>CRM/Myextension/BAO/Query/Soundex.php</strong></p>
  
  <pre><code>&lt;?php 
  
  class CRM_Myextenion_BAO_Query_Soundex extends CRM_Contact_BAO_Query_Interface {
  
      public static $soundex_fields = [
          'soundex_first_name' =&gt; [
              'name'  =&gt; 'soundex_first_name', 
              'title' =&gt; 'First Name Sounds Like', 
              'type'  =&gt; CRM_Utils_Type::T_STRING, 
          ],
          'soundex_last_name' =&gt; [
              'name'  =&gt; 'soundex_last_name', 
              'title' =&gt; 'Last Name Sounds Like', 
              'type'  =&gt; CRM_Utils_Type::T_STRING, 
          ],
      ];
  
      public function &amp;getFields() {
  
          $return = static::$soundex_fields;
  
          return $return;
      }
  
      public function from($name, $mode, $side) {
          return '';
      }
  
      public function where(&amp;$query) {
  
          if (empty($query-&gt;_params)) return;
  
          foreach ($query-&gt;_params as $id =&gt; $param) {
              if (empty($param[0])) {
                  continue;
              }
  
              $this-&gt;whereClauseSingle($param, $query);           
          }
      }
  
      public function whereClauseSingle(&amp;$values, &amp;$query) {
  
          list($name, $op, $value, $grouping, $wildcard) = $values;
  
          $fields = $this-&gt;getFields();
  
          if (!array_key_exists($name,$fields)) return;
  
          $field = $fields[$name];
          $fieldName = substr($field['name'],8); // strip off the soundex_ at the beginning
  
          $query-&gt;_qill[$grouping][] = ts($field['title'])." - '$value'";
          $query-&gt;_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("SOUNDEX(contact_a.".CRM_Core_DAO::escapeString($fieldName).")", "=", soundex($value), "String");
      }    
  
      public function getPanesMapper(&amp;$panes) {
  
          $panes['Soundex'] = 'civicrm_contact';
      }
  
      public function registerAdvancedSearchPane(&amp;$panes) {
  
          $panes['Soundex'] = 'soundex';
      }
  
      public function buildAdvancedSearchPaneForm(&amp;$form, $type) {
  
          // echo pre($form-&gt;_formValues);
  
          if (in_array($type,[
              'soundex',
          ])) {
              $fields = $this-&gt;getFields();
              foreach ($fields as $name =&gt; $label) {
                  $form-&gt;add('text', $name, ts($label['title'])); 
              }  
          }
      }
  
      public function setAdvancedSearchPaneTemplatePath(&amp;$paneTemplatePathArray, $type) {
  
          if (in_array($type,[
              'soundex',
          ])) {
              $template = ucfirst($type);
              $paneTemplatePathArray[$type] = "CRM/Contact/Form/Search/Criteria/{$template}.tpl";
          }
      } 
  
  }
  </code></pre>
  
  <p><strong>CRM/Contact/Form/Search/Criteria/Soundex.tpl</strong></p>
  
  <pre><code>&lt;table class="form-layout"&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;label&gt;{ts}{$form.soundex_first_name.label}{/ts}&lt;/label&gt;&lt;br /&gt;
        {$form.soundex_first_name.html}
      &lt;/td&gt;
      &lt;td&gt;
        &lt;label&gt;{ts}{$form.soundex_last_name.label}{/ts}&lt;/label&gt;&lt;br /&gt;
        {$form.soundex_last_name.html}
      &lt;/td&gt;
      &lt;td&gt;
        {* &lt;label&gt;{ts}Use OR on Soundex (default AND){/ts}&lt;/label&gt;&lt;br /&gt;
        {$form.soundex_or_and.html} *}
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt;
  </code></pre>
  
  <p><strong>Post Process hook (only kinda of working)</strong></p>
  
  <pre><code>function myextension_civicrm_postProcess($formName, &amp;$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 =&gt; $label) {
                      $filterVal = CRM_Utils_Request::retrieve($field, 'String', $form);
                      if (empty($filterVal)) continue;
                      $form-&gt;_formValues[$field] = $filterVal;
                  }
                  $form-&gt;set('formValues', $form-&gt;_formValues);
              }
  
              break;
      }
  }
  </code></pre>
  
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2017-09-09T05:14:37.893Z
Comments.Text: 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)
Comments.UserDisplayName: petednz - fuzion
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2017-09-09T11:05:57.867Z
Comments.Text: Haha ya, sorry I got deep enough into the technical I forgot to explain the basics of what I was trying to accomplish.
Comments.UserDisplayName: David Hayes
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2022-04-08T10:00:56.937Z
Comments.Text: That sounds like a really interesting extension. Do you happen to have a finished extension or a prototype to play with?
Comments.UserDisplayName: Thomas Schüttler
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2023-05-02T19:10:54.877Z
Comments.Text: I never did roll it into a standalone extension, just a customization to a site.
Comments.UserDisplayName: David Hayes
Tags: <search><hooks><forms><advanced>
Title: Extending CiviCRM Advanced search (queryObjects hook)
ThreadText: # Extending CiviCRM Advanced search (queryObjects hook)
  
  (Asked by: username_0 on 2017-09-09)
  
  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:
  
  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
   [
              '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
  
    
      
        {ts}{$form.soundex_first_name.label}{/ts}
        {$form.soundex_first_name.html}
      
      
        {ts}{$form.soundex_last_name.label}{/ts}
        {$form.soundex_last_name.html}
      
      
        {* {ts}Use OR on Soundex (default AND){/ts}
        {$form.soundex_or_and.html} *}
      
    
  
  
  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;
      }
  }
  
  **Tags:** search, hooks, forms, advanced
  
  > **Comments:**
  > **username_1**: 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)  
  > **username_0**: Haha ya, sorry I got deep enough into the technical I forgot to explain the basics of what I was trying to accomplish.  
  > **username_2**: That sounds like a really interesting extension. Do you happen to have a finished extension or a prototype to play with?  
  > **username_0**: I never did roll it into a standalone extension, just a customization to a site.  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_0 on 2017-11-15. Score: 3 | Accepted answer
  
  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.
  
  
  ---
  
  