CGridView renders buttons for the wrong model

Hi,

I have models called Monitor, Channel and a connection model called ChannelMonitor.

Monitor and Channel have an n:n relationship.

I now have tried to add a CGridView containing the ChannelMonitor model entries in the system.

The problem is that when the CGridView is added to the monitor update page the buttons related to every row in the table have links looking like this /index.php?r=monitor/view&id=2 instead of /index.php?r=channelMonitor/view&id=2.

Action in MonitorController:


	public function actionUpdate()

	{

		$model=$this->loadModel();


		//Get the selected channels

		$channelMonitorDataProvider=new CActiveDataProvider('ChannelMonitor');


		// Uncomment the following line if AJAX validation is needed

		// $this->performAjaxValidation($model);


		if(isset($_POST['Monitor']))

		{

			$model->attributes=$_POST['Monitor'];

			if($model->save())

				$this->redirect(array('view','id'=>$model->id));

		}


		$this->render('update',array(

			'model'=>$model, 'channelMonitorDataProvider'=>$channelMonitorDataProvider

		));

	}

Code in the view:


<?php

$this->breadcrumbs=array(

	'Monitors'=>array('index'),

	$model->name=>array('view','id'=>$model->id),

	'Update',

);


$this->menu=array(

	array('label'=>'List Monitor', 'url'=>array('index')),

	array('label'=>'Create Monitor', 'url'=>array('create')),

	array('label'=>'View Monitor', 'url'=>array('view', 'id'=>$model->id)),

	array('label'=>'Manage Monitor', 'url'=>array('admin')),

);

?>


<h1>Update Monitor <?php echo $model->id; ?></h1>


<?php echo $this->renderPartial('_form', array('model'=>$model)); ?>


<?php $this->widget('zii.widgets.grid.CGridView', array(

  'id'=>'monitor-channel-grid',

  'dataProvider'=>$channelMonitorDataProvider,

  'columns'=>array(

    'channel.id',

    array(

      'class'=>'CButtonColumn',

    ),

  ),

)); ?>

I have tried loading the channels in to the CgridView as well and it still uses monitor as the link to edit.

Is this a bug?

I know I can override it using something like this(but it doesn’t seem right that I have to do that):


<?php $this->widget('zii.widgets.grid.CGridView', array(

  'id'=>'monitor-channel-grid',

  'dataProvider'=>$channelMonitorDataProvider,

  'columns'=>array(

    'channel.id',

      array(

        'class'=>'CButtonColumn',

        'updateButtonUrl'=>'Yii::app()->createUrl("channelmonitor/update", array("id"=>$data->id))',

      ),

  ),

)); ?>

I really think that the CGridView should use the urls for the model that is selected in the CDataProvider and not use the main model urls.

I have tried this in the latest version of the trunk.

Thanks

/John

I extended CButtonColumn to deal with this and one other limitation of the hard-coded expression setting the URLs in CButtonColumn.

Thanks for that. I see that you are using PHP 5.3 to be able to run the static calls in PHP 5.2 we need to modify the code to look something like this.


class ButtonColumn extends CButtonColumn

{

    protected function initDefaultButtons()

    {

        $modelClass=$this->grid->dataProvider->modelClass;

        $controller=strtolower($modelClass);

        $theModel = call_user_func(array($modelClass, 'model'));

        if(is_array($theModel->primaryKey))

        {

            $paramExpression='",$data->primaryKey)';

        }

        else

        {

            $paramExpression='",array("id"=>$data->primaryKey))';

        }

        foreach(array('view','update','delete') as $id)

            $this->{$id.'ButtonUrl'}= 'Yii::app()->urlManager->createUrl("'."$controller/$id$paramExpression";

        parent::initDefaultButtons();

    }

}

I cannot see any reason for why this functionality shouldn’t be put in to the core. Am I wrong?

/John

I didn’t know that. :blink:

Seems reasonable to me; the way to find out is to submit a patch. As I’m still quite new to Yii (and, apparently, not very well up on the differences between PHP 5.2 and 5.3) I’m not going to do so – yet. But be my guest!

Hi,

I was not criticising your PHP knowledge ;). You did a great job with the extension. It took me some time to understand why this wasn’t working in my code.

I don’t really want to go in and create a patch, I think it is better if someone in the core team(Qiang maybe?) takes a look at this extension and decides how to properly integrate it(if it doesn’t break other functionality).

Thanks for your effort I think it is great when people share their experiences like this. The Yii community seems to be quite good at that :).

Thanks

Yes, it is by design that the default button URLs refer to some actions of the current controller.

If you want to use a different controller, you have to explicitly declare that by setting the URL expression.

We do not assume that there is a one-to-one mapping between controller and model.

Thanks for clarifying that. I just found it a bit confusing since the rest of the data in the grid belongs to the model assigned to the data provider. I almost accidentally deleted one of my Monitors that way because I didn’t look properly at the links.

Thanks

/John

I have added ‘$module=$this->grid->owner->module->id;’ and ‘$module/’ into your code in order to use with modules.

Thank you for sharing it!

Regards,

sis


class ButtonColumn extends CButtonColumn

{

    protected function initDefaultButtons()

    {

	$module=$this->grid->owner->module->id;

        $modelClass=$this->grid->dataProvider->modelClass;

        $controller=strtolower($modelClass);

        $theModel = call_user_func(array($modelClass, 'model'));

        if(is_array($theModel->primaryKey))

        {

            $paramExpression='",$data->primaryKey)';

        }

        else

        {

            $paramExpression='",array("id"=>$data->primaryKey))';

        }

        foreach(array('view','update','delete') as $id)

            $this->{$id.'ButtonUrl'}= 'Yii::app()->urlManager->createUrl("'."$module/$controller/$id$paramExpression";

        parent::initDefaultButtons();

    }

}

I was able to find my solution on stackoverflow (can’t post links yet since I am new):


$this->widget('zii.widgets.grid.CGridView', array(

      'id'=>'artwork-grid',

      'dataProvider'=>$dataProvider,

      'columns'=>array(

        'id',

        'artwork_id',

        'description',

        array(

          'class'=>'CButtonColumn',

          'viewButtonUrl'=>'Yii::app()->createUrl(\'admin/artwork/\'. $data->id)',

          'updateButtonUrl'=>'Yii::app()->createUrl(\'admin/artwork/update/\'. $data->id)',

          'deleteButtonUrl'=>'Yii::app()->createUrl(\'admin/artwork/delete/\'. $data->id)',

        ),

      ),

    ));