I’m pretty sure, I’ve asked this question about a year ago, but simply can’t find that thread.
After changing datasource of CGridView from CActiveDataProvider to CSqlDataProvider, it becomes unusable and attempt to render view containing it ends with PHP Error saing "Trying to get property of non-object".
the comments paragraph there exists your anwser ; the reason is this: when using CActiveDataProvider
in the CButtonColumn , CDataColumn,…xxColumn . they all depend on the "$data" variable . if you use CActiveDataProvider the $data represents the CActiveRecord subclass instance, when you replace it to CSqlDataProvider the "$data" represents a row of an resultSet from the sql query . so the statement such as
$data->xxx cause the error
only the CDataColumn has little effect:
protected function renderDataCellContent($row,$data)
{
if($this->value!==null)
$value=$this->evaluateExpression($this->value,array('data'=>$data,'row'=>$row));
else if($this->name!==null)
$value=CHtml::value($data,$this->name);
echo $value===null ? $this->grid->nullDisplay : $this->grid->getFormatter()->format($value,$this->type);
}
// you read the CHtml::value implements:
public static function value($model,$attribute,$defaultValue=null)
{
foreach(explode('.',$attribute) as $name)
{
if(is_object($model))
$model=$model->$name;
else if(is_array($model) && isset($model[$name]))
$model=$model[$name];
else
return $defaultValue;
}
return $model;
}
// this function can handle both object and array situation !
other XXXColumn only assume that the $data represent a instance of some CModel class . so this cause the error you met : trying to get property of non-object
But isn’t that a nasty bug? After all, CDetailView works perfectly with both model (CActiveDataProvider) and associative array (CSqlDataProvider) passed as data source. So, what causes CGridView to be unable to mimic the very same behaviour?
Of course… I can… hm… mimic the behaviour of CButtonColumn with CDataColumn.type set to raw and with huge, enormous code to be evaluated in value. But, somehow I’m feeling that this is not the way this should be solved.
EDIT: Combining this thread with your answer to my another question, about converting between CSqlDataProvider and CActiveDataProvider, we got conclusion that making CButtonColumn work also on CSqlDataProvider is just a few extra lines of code, that is --> use populateRecord fuction.
No, it isn’t, because the problem is not in CButtonColumn’s code. It’s in your code (a button url expression or the value of “visible” property), that passed as a string to CButtonColumn and evaluated in CButtonColumn::evaluateExpression().
EDIT: False, sorry. Button url expressions have default values, and this default values use object-syntax. You can avoid the error by explicitly specifying viewButtonUrl/updateButtonUrl/deleteButtonUrl in CButtonColumn options.
Well… I’m getting this exception thrown, if CGridView is fed with CSqlDataProvider and my CButtonColumn definition is like that:
array('class'=>'CButtonColumn')
Nothing else than that! So, yes – I’m pretty sure that the problem is inside CButtonColumn’s code and repeat that this seems to be some kind of bug! :]
If you want to extend the CButton Column I have done it so:
/**
* EButtonColumn modifies the button urls so you can use it with CSqlDataProvider
*
*/
class EButtonColumn extends CButtonColumn
{
public $controllerPath='';
public $viewButtonUrl='Yii::app()->createUrl("$this->controllerPath/view",array("id"=>$data["id"]))';
public $updateButtonUrl='Yii::app()->createUrl("$this->controllerPath/update",array("id"=>$data["id"]))';
public $deleteButtonUrl='Yii::app()->createUrl("$this->controllerPath/delete",array("id"=>$data["id"]))';
}
klaus66, sorry for very late reply and thank you for sharing your solution.
But then again, it is not the way (in my opinion) how such things should be solved here. If there is a bug in base framework code and no one is able to prove that it is in user code, then it has to be fixed in base code, not in user code.