Search code examples
phpyiicgridview

Yii CGridview indexed filters


i have custom fields for CGridView. Every column is different Custom Field with it's id. Displaying them is just fine. Problem begins when i try to apply filter for these fields.

I have 4 filter with names CustomField[5], CustomField[6], CustomField[8] and CustomField[12] indexed by it's id. After filtering, yii generates wrong query:

Wrong query

As you can see, it's making loop till last element (since largest id is 12, so it is looping till 12). But if i add letter (making index as string) it generates correct query:

Correct query

Filter dropdown is generated with code:

echo CHtml::dropDownList(
    "CustomField[{$column->id}]",
    $this->getFilterValue($column),
    ['' => ''] + CustomFieldValue::getValue($column),
    $column->tagOptions
);

It generates this html:

 <tr class="filters">
    <td>
        <select id="CustomField_5" name="CustomField[5]">
            <option selected="selected" value=""></option>
            <option value="29">Olympic Male Fly -58</option>
            <option value="33">Olympic Female Bantam -57</option>
        </select>
    </td>
    <td>
        <select id="CustomField_6" name="CustomField[6]">
            <option selected="selected" value=""></option>
            <option value="4">Quarter-finals</option
        </select>
    </td>
    <td>
        <select id="CustomField_8" name="CustomField[8]">
            <option selected="selected" value=""></option>
            <option value="3">Bronze</option>
            <option value="1">Gold</option>
            <option value="4">Participation</option>
        </select>
    </td>
    <td>
        <select id="CustomField_12" name="CustomField[12]">
            <option selected="selected" value=""></option>
            <option value="2">Silver</option>
            <option value="1">Gold</option>
        </select>
    </td>
</tr>

Any idea what is wrong how to fix it?


Solution

  • The Problem

    This is an issue with the way jquery.ba-bbq.js's deparam function handles the url parameters. If the parameters are passed as a url string i.e &a=b&c=d.., it creates an object out of them. In the process it treats any parameters that are numerically indexed as arrays and pads them with empty elements.

    Here's the result of serialize on your filter html:

    CustomField[5]=&CustomField[6]=&CustomField[8]=&CustomField[12]=
    

    And here's the result of $.param.querystring on the serialized string

    CustomField[]=&CustomField[]=&CustomField[]=&CustomField[]=&CustomField[]=&
    CustomField[]=&CustomField[]=&CustomField[]=&CustomField[]=&CustomField[]=&
    CustomField[]=&CustomField[]=&CustomField[]=
    

    $(function(){
            $("#clickme").on("click", function(){
               var data = $(".filters select").serialize();
               $("#serialized").html(decodeURIComponent(data) + "\n\n" + decodeURIComponent($.param.querystring("", data)));
            });
        });
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <script src="https://raw.githubusercontent.com/yiisoft/yii/master/framework/web/js/source/jquery.ba-bbq.js"></script>
    
    <div class="filters">
        <select id="CustomField_5" name="CustomField[5]">
          <option selected="selected" value=""></option>
          <option value="29">Olympic Male Fly -58</option>
          <option value="33">Olympic Female Bantam -57</option>
        </select>
        <select id="CustomField_6" name="CustomField[6]">
          <option selected="selected" value=""></option>
          <option value="4">Quarter-finals</option>
        </select>
        <select id="CustomField_8" name="CustomField[8]">
          <option selected="selected" value=""></option>
          <option value="3">Bronze</option>
          <option value="1">Gold</option>
          <option value="4">Participation</option>
        </select>
        <select id="CustomField_12" name="CustomField[12]">
          <option selected="selected" value=""></option>
          <option value="2">Silver</option>
          <option value="1">Gold</option>
        </select><br/>
        <button id ="clickme">Serialize</button>
      </div>  
      <pre><div id="serialized"></div></pre>

    The Solutions

    1. Prefixing your indexes with a string solves the problem since it results in the parameter CustomField being treated as an object not an array.

    2. An alternative solution would be to ensure that your custom fields have continuous indexes i.e 0,1,2,3.

    3. Also, you could just ignore any empty CustomField parameters in your controller action.