Code snippets for symfony 1.x

Navigation

Quick Search for admin_double_list

Sometimes in the generator you have a large listing of elements in a admin_double_list.

Well I wanted to write a quick little autocomplete text box that would search for results and when you selected one, it would be added into the associated select box.

In your generator add a new partial: edit: display: [ _quick_search ]

Create a file in your templates directory called _quick_search.php

Copy this code in there:

===CODE===

<?php $response = sfContext::getInstance()->getResponse(); $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/prototype'); $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/effects'); $response->addJavascript(sfConfig::get('sf_prototype_web_dir').'/js/controls'); $response->addStylesheet(sfConfig::get('sf_prototype_web_dir').'/css/input_auto_complete_tag'); ?> <input type="text" id="quick_search" name="quick_search"/>

<div class="auto_complete" id="quick_search_auto_complete" style="position: absolute; left: 445px; top: 421px; width: 113px; opacity: 0.17329; display: none;">

</div>

<script type="text/javascript">

//<![CDATA[

function addSelection (li)

{

$('associated_titles').options[ $('associated_titles').options.length] = new Option (li.childNodes[0].nodeValue, li.id);

}

new Ajax.Autocompleter('quick_search', 'quick_search_auto_complete', '/backend_dev.php/search', { updateElement: addSelection });

//]]>

</script>

===CODE===

Make sure you change your url from /backend_dev.php/search to whatever your action that will search your database and your good to go!

Just FYI: You can use the Symfony helper autocomplete_tag () because it does not allow for passing the updateElement parameter.

by Myke Hines on 2007-06-29, tagged admin  admindoublelist  admingenerator  ajax  generator 

Comments on this snippet

gravatar icon
#1 Leon van der Ree on 2007-08-08 at 12:55

Hi Myke,

thanks for your example, I don't understand your last sentence though, I think you CAN use the symfony helper input_auto_complete_tag, which DOES allow for passing the updateElement parameter.

This is my code for the sf_guard_group linked to the group-permissions double-list:

  <?php use_helper('Javascript') ?>
 
 
<script type="text/javascript">
//<![CDATA[
function addSelection(li)
{
// select_box = associated_permissions
$('associated_permissions').options[ $('associated_permissions').options.length] = new Option (li.childNodes[0].nodeValue, li.id);
}
//]]>
</script>
 
  <?php echo input_auto_complete_tag('quick_search', '', 
    'sfGuardGroup/autocomplete?id='.$sf_guard_group->getId(), 
    array('autocomplete' => 'off'),
    array('use_style' => 'true', 
          'update_element' => 'addSelection'
         )
  ) ?>

and for the people who haven't figured out how to write your actions.class, see this function:

    public function executeAutocomplete() 
    {
    $suggestions = array(); 
 
        if (true) // ($this->hasRequestParameter('id')) && ($this->hasRequestParameter('quick_search')) )
        {
            $id = $this->getRequestParameter('id');
            $starts_with = $this->getRequestParameter('quick_search');
 
        $c = new Criteria();
        $c->add(sfGuardGroupPermissionPeer::GROUP_ID, $id, Criteria::NOT_EQUAL);
        $c->add(sfGuardPermissionPeer::NAME, $starts_with.'%', Criteria::LIKE);
        $ps = sfGuardGroupPermissionPeer::doSelectJoinsfGuardPermission($c);
 
        foreach ($ps as $p)
        {
          $permission = $p->getsfGuardPermission();
          $suggestions[] = $permission->getName();
        }
        }
      $this->suggestions = $suggestions;
 
    }

which requires the following autocompleteSucess.php template:

    <ul>
    <?php foreach($suggestions as $suggestion): ?>
      <li><?php echo $suggestion ?></li>
    <?php endforeach; ?>
    </ul>
 
<?php
 
    // stop here, don't load any views and in dev-mode don't show symfony-bar.
    exit; 
 
?>

But at the moment this code still is pretty useless, because no ID-value is set for the added option.

I'm going to try to filter the unassociated_titles with the help of this

gravatar icon
#2 Leon van der Ree on 2007-08-08 at 01:28

A little update, I found out that you did take care of setting the ID-value, but my suggestion-overview was lacking support for it.

Here is the working code:

the helper in your partial (this time with the javascript-function included in the helper):

  <?php use_helper('Javascript') ?>
 
 
  <?php echo input_auto_complete_tag('quick_search', '', 
    'sfGuardGroup/autocomplete?id='.$sf_guard_group->getId(), 
    array('autocomplete' => 'off'),
    array('use_style' => 'true', 
          'update_element' => "function (li) { $('associated_permissions').options[ $('associated_permissions').options.length] = new Option (li.childNodes[0].nodeValue, li.id); }" 
         )
  ); ?>

The function in the actions.class

    public function executeAutocomplete() 
    {
    $suggestions = array(); 
 
        if (true) // ($this->hasRequestParameter('id')) && ($this->hasRequestParameter('quick_search')) )
        {
            $id = $this->getRequestParameter('id');
            $starts_with = $this->getRequestParameter('quick_search');
 
        $c = new Criteria();
        $c->add(sfGuardGroupPermissionPeer::GROUP_ID, $id, Criteria::NOT_EQUAL);
        $c->add(sfGuardPermissionPeer::NAME, $starts_with.'%', Criteria::LIKE);
        $ps = sfGuardGroupPermissionPeer::doSelectJoinsfGuardPermission($c);
 
        foreach ($ps as $p)
        {
          $permission = $p->getsfGuardPermission();
            $key = $permission->getId();
          $suggestions[$key] = $permission->getName();
        }
        }
      $this->suggestions = $suggestions;
 
    }

and the autocompleteSucess.php template

    <ul>
    <?php foreach($suggestions as $key => $suggestion): ?>
      <li id="<?= $key ?>"><?= $suggestion ?></li>
    <?php endforeach; ?>
    </ul>
 
<?php
    // stop here, don't load any views and in dev-mode don't show symfony-bar.
    exit;
?>
gravatar icon
#3 Anonymous on 2007-08-08 at 06:25

Unfortunately you cannot edit your posts in these snippets, so again an update.

I found out that my sql-query contained an error, thereby not returning the complete number of suggestions. Further I improved the code with an additional search-field which can also un-associate. And finally I also added the ability to remove the option from the other list.

One problem that remains is that this actually should not be a AJAX implementation, but only a local-javascript implementation which defines the suggestion on the content of the two listboxes only. Now you can get a suggestion which is already moved, but is not submitted yet. And you probably don't want to submit, until you pressed the save button of your forum.

Anyway the code is a nice example, so here is it again:

the helper in your partial (this time two search-fields and the javascript also removes the option from the other listbox):

  <?php use_helper('Javascript') ?>
 
 
  <?php
  echo input_auto_complete_tag('add_association_search', '', 
    'sfGuardGroup/autocomplete?associated=false&group_id='.$sf_guard_group->getId(), 
    array('autocomplete' => 'off'),
    array('use_style' => 'true', 
          'update_element' => "function (li) { " .
                "src=$('unassociated_permissions'); ".
                "dest=$('associated_permissions'); ".
                "for (var i = 0;i < src.options.length;i++) {".
                "if (src.options[i].value==li.id) { src.options[i] = null; break; } }".
                "dest.options[ dest.options.length] = new Option (li.childNodes[0].nodeValue, li.id); }"
 
         )
  ); ?>
 
  <?php 
  echo input_auto_complete_tag('remove_association_search', '', 
    'sfGuardGroup/autocomplete?associated=false&group_id='.$sf_guard_group->getId(), 
    array('autocomplete' => 'off'),
    array('use_style' => 'true', 
          'update_element' => "function (li) { " .
                "src=$('associated_permissions'); ".
                "dest=$('unassociated_permissions'); ".
                "for (var i = 0;i < src.options.length;i++) {".
                "if (src.options[i].value==li.id) { src.options[i] = null; break; } }".
                "dest.options[ dest.options.length] = new Option (li.childNodes[0].nodeValue, li.id); }"
 
         )
    ); ?>

The function in the actions.class

    public function executeAutocomplete() 
    {
    $associated_perms = array(); 
    $unassociated_perms= array(); 
 
        if ($this->hasRequestParameter('group_id')) 
        {
            $id = $this->getRequestParameter('group_id');
 
            if ($this->hasRequestParameter('add_association_search'))
            {
                $substring = $this->getRequestParameter('add_association_search');
            } elseif ($this->hasRequestParameter('remove_association_search')) {
                $substring = $this->getRequestParameter('remove_association_search');
            } else {
                //no search, no glory
                exit;
            }
 
            // select all permissions           
        $c = new Criteria();
        $c->add(sfGuardPermissionPeer::NAME, '%'.$substring.'%', Criteria::LIKE);
        $c->addAscendingOrderByColumn(sfGuardPermissionPeer::NAME);
        $ps = sfGuardPermissionPeer::doSelect($c);
 
        // get te permissions associated to this group
        $c = new Criteria();
      $c->add(sfGuardGroupPermissionPeer::GROUP_ID, $id);
      $c->setDistinct(true);
        $gps = sfGuardGroupPermissionPeer::doSelect($c);
 
        //split the permissions in two arrays (un/associated)
        foreach ($ps as $permission)
        {
            $key = $permission->getId();
 
                //find if permission is already associated
                $contains = false;
                foreach ($gps as $gp) {
                if ($gp->getPermissionId()==$key) { 
                    // contains
                    $contains = true;
                    break;
                }
                }
 
                // add permission to corresponding array
            if ($contains) { 
            $associated_perms[$key] = $permission->getName();
            } else {
            $unassociated_perms[$key] = $permission->getName();
            }
 
        }
 
            // return the requested array.
            if ($this->hasRequestParameter('add_association_search'))
            $this->suggestions = $unassociated_perms;
          else
            $this->suggestions = $associated_perms;
 
        } else {
            //no group given, no results will return 
            $this->suggestions = array();
        }
 
    }

and the autocompleteSucess.php template

    <ul>
    <?php foreach($suggestions as $key => $suggestion): ?>
      <li id="<?= $key ?>"><?= $suggestion ?></li>
    <?php endforeach; ?>
    </ul>
 
<?php
    // stop here, don't load any views and in dev-mode don't show symfony-bar.
    exit;
?>

If someone ever creates the non-ajax version, which defines the suggestions on the content of the listbox only, I would appreciate to see the result...