0
votes

I have three tables. Item, Manufacture and a join table for these two, ItemManufacture. Item has ID and Item Name. Manufacture has Manufacture ID and Manufacture Name. Item manufacture has Item ID and Manufacture ID.

I am using Yii's CGridView control to display all items with Item Name and Corresponding Manufacture Name for that item. I could able to succesfully display those things in the grid view using the tutorials given in http://www.yiiframework.com/wiki/385/displaying-sorting-and-filtering-hasmany-manymany-relations-in-cgridview/

I also able to add text field filter, as given in the tutorial. My problem is, I want to add a combobox filter for the manufacture name column. I tried by adding a filter like,

'filter'=>CHtml::listData(Manufacture::model()->findAll(), 'man_name', 'man_name')

But it didn't worked. But works for manufacture table, without any joins.

Then I tried,

'filter'=>CHtml::activeDropDownList(Manufacture::model(),'man_name', CHtml::listData(Manufacture::model()->findAll(), 'man_name', 'man_name'));

It will display a combobox with all names, but when I select any name, it is not filtering the output. Instead an empty gridview is displayed.

Can anybody explain what is going on and how to fix it?

PS:

When I print CHtml::listData(Manufacture::model()->findAll(), 'man_name', 'man_name') using print_r in the controller, it is showing all names of manufactures.

My ItemManufacture.php (Model) is

<?php

/**
 * This is the model class for table "item_manufacture".
 *
 * The followings are the available columns in table 'item_manufacture':
 * @property integer $id
 * @property integer $item_id
 * @property integer $manu_id
 *
 * The followings are the available model relations:
 * @property Manufacture $manu
 * @property Item $item
 */
class ItemManufacture extends CActiveRecord
{
    public $searchManu;
    public $searchItem;
    /**
     * @return string the associated database table name
     */
    public function tableName()
    {
        return 'item_manufacture';
    }

    /**
     * @return array validation rules for model attributes.
     */
    public function rules()
    {
        // NOTE: you should only define rules for those attributes that
        // will receive user inputs.
        return array(
            array('item_id, manu_id', 'required'),
            array('item_id, manu_id', 'numerical', 'integerOnly'=>true),
            // The following rule is used by search().
            // @todo Please remove those attributes that should not be searched.
            array('id, item_id, manu_id', 'safe', 'on'=>'search'),
        );
    }

    /**
     * @return array relational rules.
     */
    public function relations()
    {
        // NOTE: you may need to adjust the relation name and the related
        // class name for the relations automatically generated below.
        return array(
            'manu' => array(self::BELONGS_TO, 'Manufacture', 'manu_id'),
            'item' => array(self::BELONGS_TO, 'Item', 'item_id'),
        );
    }

    /**
     * @return array customized attribute labels (name=>label)
     */
    public function attributeLabels()
    {
        return array(
            'id' => 'ID',
            'item_id' => 'Item ID',
            'manu_id' => 'Manufacture ID',
        );
    }

    /**
     * Retrieves a list of models based on the current search/filter conditions.
     *
     * Typical usecase:
     * - Initialize the model fields with values from filter form.
     * - Execute this method to get CActiveDataProvider instance which will filter
     * models according to data in model fields.
     * - Pass data provider to CGridView, CListView or any similar widget.
     *
     * @return CActiveDataProvider the data provider that can return the models
     * based on the search/filter conditions.
     */
    public function search()
    {
        // @todo Please modify the following code to remove attributes that should not be searched.

        $criteria=new CDbCriteria;

        $criteria->compare('id',$this->id);
        $criteria->compare('item_id',$this->item_id);
        $criteria->compare('manu_id',$this->manu_id);
        $criteria->with =  array('item', 'manu');
        $criteria->compare('manu.man_name', $this->searchManu->man_name, true);
        $criteria->compare('item.item_name', $this->searchItem->item_name, true);
        $criteria->together = true;
        return new CActiveDataProvider($this, array(
            'criteria'=>$criteria,
            'sort'=>array(
            'attributes'=>array(
                'manu.man_name'=>array(
                    'asc'=>'manu.man_name',
                    'desc'=>'manu.man_name DESC',
                ),
                'item.item_name'=>array(
                    'asc'=>'item.item_name',
                    'desc'=>'item.item_name DESC',
                ),
                '*',
            ),
        ),
        ));
    }

    /**
     * Returns the static model of the specified AR class.
     * Please note that you should have this exact method in all your CActiveRecord descendants!
     * @param string $className active record class name.
     * @return ItemManufacture the static model class
     */
    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }

    public function getOptions()
    {
        echo "This is output: '". $this->searchItem->item_name. "'";
        print_r ( CHtml::activeDropDownList(Manufacture::model(),'man_name', CHtml::listData(Manufacture::model()->findAll(), 'man_name', 'man_name')));
        //return CHtml::activeDropDownList(Manufacture::model(),'man_name', CHtml::listData(Manufacture::model()->findAll(), 'man_name', 'man_name'));
        //return CHtml::listData(Manufacture::model()->findAll(), 'man_name', 'man_name');
        return CHtml::dropDownList('Manufacture[man_name]', 'man_name', CHtml::listData(Manufacture::model()->findAll(), 'man_name', 'man_name'));
    }
}

My ItemManufactureController.php is,

<?php

class ItemManufactureController extends Controller
{
    /**
     * @var string the default layout for the views. Defaults to '//layouts/column2', meaning
     * using two-column layout. See 'protected/views/layouts/column2.php'.
     */
    public $layout='//layouts/column2';

    /**
     * @return array action filters
     */
    public function filters()
    {
        return array(
            'accessControl', // perform access control for CRUD operations
            'postOnly + delete', // we only allow deletion via POST request
        );
    }

    /**
     * Specifies the access control rules.
     * This method is used by the 'accessControl' filter.
     * @return array access control rules
     */
    public function accessRules()
    {
        return array(
            array('allow',  // allow all users to perform 'index' and 'view' actions
                'actions'=>array('index','view'),
                'users'=>array('*'),
            ),
            array('allow', // allow authenticated user to perform 'create' and 'update' actions
                'actions'=>array('create','update'),
                'users'=>array('@'),
            ),
            array('allow', // allow admin user to perform 'admin' and 'delete' actions
                'actions'=>array('admin','delete'),
                'users'=>array('admin'),
            ),
            array('deny',  // deny all users
                'users'=>array('*'),
            ),
        );
    }

    /**
     * Displays a particular model.
     * @param integer $id the ID of the model to be displayed
     */
    public function actionView($id)
    {
        $this->render('view',array(
            'model'=>$this->loadModel($id),
        ));
    }

    /**
     * Creates a new model.
     * If creation is successful, the browser will be redirected to the 'view' page.
     */
    public function actionCreate()
    {
        $model=new ItemManufacture;

        // Uncomment the following line if AJAX validation is needed
        // $this->performAjaxValidation($model);

        if (isset($_POST['ItemManufacture'])) {
            $model->attributes=$_POST['ItemManufacture'];
            if ($model->save()) {
                $this->redirect(array('view','id'=>$model->id));
            }
        }

        $this->render('create',array(
            'model'=>$model,
        ));
    }

    /**
     * Updates a particular model.
     * If update is successful, the browser will be redirected to the 'view' page.
     * @param integer $id the ID of the model to be updated
     */
    public function actionUpdate($id)
    {
        $model=$this->loadModel($id);

        // Uncomment the following line if AJAX validation is needed
        // $this->performAjaxValidation($model);

        if (isset($_POST['ItemManufacture'])) {
            $model->attributes=$_POST['ItemManufacture'];
            if ($model->save()) {
                $this->redirect(array('view','id'=>$model->id));
            }
        }

        $this->render('update',array(
            'model'=>$model,
        ));
    }

    /**
     * Deletes a particular model.
     * If deletion is successful, the browser will be redirected to the 'admin' page.
     * @param integer $id the ID of the model to be deleted
     */
    public function actionDelete($id)
    {
        if (Yii::app()->request->isPostRequest) {
            // we only allow deletion via POST request
            $this->loadModel($id)->delete();

            // if AJAX request (triggered by deletion via admin grid view), we should not redirect the browser
            if (!isset($_GET['ajax'])) {
                $this->redirect(isset($_POST['returnUrl']) ? $_POST['returnUrl'] : array('admin'));
            }
        } else {
            throw new CHttpException(400,'Invalid request. Please do not repeat this request again.');
        }
    }

    /**
     * Lists all models.
     */
    public function actionIndex()
    {
        $manufacture = new Manufacture('search');
        $manufacture->unsetAttributes();
        if (isset($_GET['Manufacture'])) {
            $manufacture->attributes = $_GET['Manufacture'];
        }
        $item = new Item('search');
        $item->unsetAttributes();
        if (isset($_GET['Item'])) {
            $item->attributes = $_GET['Item'];
        }
        $dataProvider=new CActiveDataProvider('ItemManufacture');
        $dataProvider->model->searchManu = $manufacture;
        $dataProvider->model->searchItem = $item;
        $this->render('index',array(
            'dataProvider'=>$dataProvider,
        ));
    }

    /**
     * Manages all models.
     */
    public function actionAdmin()
    {
        $model=new ItemManufacture('search');
        $model->unsetAttributes();  // clear any default values
        if (isset($_GET['ItemManufacture'])) {
            $model->attributes=$_GET['ItemManufacture'];
        }

        $this->render('admin',array(
            'model'=>$model,
        ));
    }

    /**
     * Returns the data model based on the primary key given in the GET variable.
     * If the data model is not found, an HTTP exception will be raised.
     * @param integer $id the ID of the model to be loaded
     * @return ItemManufacture the loaded model
     * @throws CHttpException
     */
    public function loadModel($id)
    {
        $model=ItemManufacture::model()->findByPk($id);
        if ($model===null) {
            throw new CHttpException(404,'The requested page does not exist.');
        }
        return $model;
    }

    /**
     * Performs the AJAX validation.
     * @param ItemManufacture $model the model to be validated
     */
    protected function performAjaxValidation($model)
    {
        if (isset($_POST['ajax']) && $_POST['ajax']==='item-manufacture-form') {
            echo CActiveForm::validate($model);
            Yii::app()->end();
        }
    }
}

And My View is

<?php
/* @var $this ItemManufactureController */
/* @var $dataProvider CActiveDataProvider */
?>

<?php
$this->breadcrumbs=array(
    'Item Manufactures',
);

$this->menu=array(
    array('label'=>'Create ItemManufacture','url'=>array('create')),
    array('label'=>'Manage ItemManufacture','url'=>array('admin')),
);
?>

<h1>Item Manufactures</h1>

<?php
    $this->widget('bootstrap.widgets.TbGridView', array(
    'dataProvider' => $dataProvider->model->search(),
    'type'=>'striped bordered condensed',
    'pager'=>array(
        'maxButtonCount'=>'7',
    ),
    'filter'=>$dataProvider->model,
    'pager'=>array
     (
        'pageSize' => 50,
     ),
    'columns' => array
     (
        'id',
        array
        (
            'name' => 'manu.man_name',
            //'filter' => CHtml::activeTextField($dataProvider->model->searchManu, 'man_name'),
            //'filter'=>CHtml::listData($dataProvider->model->searchManu, 'man_name', 'man_name')
            //'filter'=>CHtml::listData(Manufacture::model()->findAll(), 'man_name', 'man_name')
            //'filter'=>CHtml::listData(Manufacture::model()->findAll(), 'man_name', 'man_name')
            //'filter'=>Manufacture::model()->manufactureNames
            //'filter'=>ItemManufacture::model()->options
            //'filter'=>Manufacture::model()->options
            'filter'=>CHtml::listData(Manufacture::model()->findAll(), 'man_id', 'man_name')
            //'filter'=>CHtml::dropDownList('Manufacture[man_name]', 'manu.man_name', array_merge (array(''=>''), CHtml::listData(Manufacture::model()->findAll(), 'man_name', 'man_name')))
            //'filter'=>CHtml::activeDropDownList($dataProvider->model,'manu_id', CHtml::listData(Manufacture::model()->findAll(), 'man_name', 'man_name'))
        ),
        array
        (
            'name' => 'item.item_name',
            'filter' => CHtml::activeTextField($dataProvider->model->searchItem, 'item_name'),
        ),
        array
        (
            'header' => 'Actions',
            'class'=>'bootstrap.widgets.TbButtonColumn',
        ),
    ),
    ));
?>
1

1 Answers

0
votes

The second parameter of CHtml::listData is valueField, it should be id if it is foreign key. Try this:

'filter'=>CHtml::listData(Manufacture::model()->findAll(), 'id', 'man_name')

UPD

Also, your field name is manu_id. So, right field would be:

array
(
    'name' => 'manu_id',
    'filter'=>CHtml::listData(Manufacture::model()->findAll(), 'man_id', 'man_name')
),