Yii - CGridView - add own attribute - php

$this->widget('zii.widgets.grid.CGridView', array(
'dataProvider'=>$dataProvider,
'columns'=>array(
'title', // display the 'title' attribute
'category.name', // display the 'name' attribute of the 'category' relation
'content:html', // display the 'content' attribute as purified HTML
array( // display 'create_time' using an expression
'name'=>'create_time',
'value'=>'date("M j, Y", $data->create_time)',
),
array( // display 'author.username' using an expression
'name'=>'authorName',
'value'=>'$data->author->username',
//HERE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
'htmlOptions'=>array('class'=>'$data->author->username', 'secondAttribute' => $data->author->id),
//HERE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
),
array( // display a column with "view", "update" and "delete" buttons
'class'=>'CButtonColumn',
),
),
));
In option value i can add variable from PHP, but for option htmlOptions this is not possible. Why?How can i make attribute with PHP variable?

When you add arrays in the columns collection without specifying a class property, the type of column being created is CDataColumn. The property CDataColumn::value is explicitly documented to be
a PHP expression that will be evaluated for every data cell and whose
result will be rendered as the content of the data cells.
Therefore, value has the special property that it gets eval'ed for each row and that's why you can set it "dynamically". This is an exception however, and almost nothing else supports the same functionality.
However, you are in luck because the property cssClassExpression is another special exception that covers exactly this usage case. So you can do it like this:
array(
'name'=>'authorName',
'value'=>'$data->author->username',
'cssClassExpression' => '$data->author->username',
),
Edit: I made a mistake while copy/pasting from your example and did not notice that you were trying to do the same thing for additional attributes inside htmlOptions (I have now deleted the relevant part of the code).
If you need to add more options for dynamic values you have no choice but to subclass CDataColumn and override the renderDataCell method (stock implementation is here).

Don't know if this still applies or not (given that there is an accepted answer), but there is a slightly better solution in the form of "rowHtmlOptionsExpression". This specifies an expression that will be evaluated for every row. If the result of the eval() call is an array, it will be used as the htmlOptions for the <tr> tag. So you can basically now use something like this:
$this->widget('zii.widgets.grid.CGridView', array
(
...
'rowHtmlOptionsExpression' => 'array("id" => $data->id)',
...
All your tags will have an id attribute with the records' PK.
Just modify the jQuery slightly to obtain the id from that tag instead of an extra column and you should be set.

Extend the class CDataColumn
Under protected/components/ create the file DataColumn.php with the following content:
/**
* DataColumn class file.
* Extends {#link CDataColumn}
*/
class DataColumn extends CDataColumn
{
/**
* #var boolean whether the htmlOptions values should be evaluated.
*/
public $evaluateHtmlOptions = false;
/**
* Renders a data cell.
* #param integer $row the row number (zero-based)
* Overrides the method 'renderDataCell()' of the abstract class CGridColumn
*/
public function renderDataCell($row)
{
$data=$this->grid->dataProvider->data[$row];
if($this->evaluateHtmlOptions) {
foreach($this->htmlOptions as $key=>$value) {
$options[$key] = $this->evaluateExpression($value,array('row'=>$row,'data'=>$data));
}
}
else $options=$this->htmlOptions;
if($this->cssClassExpression!==null)
{
$class=$this->evaluateExpression($this->cssClassExpression,array('row'=>$row,'data'=>$data));
if(isset($options['class']))
$options['class'].=' '.$class;
else
$options['class']=$class;
}
echo CHtml::openTag('td',$options);
$this->renderDataCellContent($row,$data);
echo '</td>';
}
}
We can use this new class like this:
$this->widget('zii.widgets.grid.CGridView', array(
'id' => 'article-grid',
'dataProvider' => $model->search(),
'filter' => $model,
'columns' => array(
'id',
'title',
array(
'name' => 'author',
'value' => '$data->author->username'
),
array(
'class' => 'DataColumn',
'name' => 'sortOrder',
'evaluateHtmlOptions' => true,
'htmlOptions' => array('id' => '"ordering_{$data->id}"'),
),
array(
'class' => 'CButtonColumn',
),
),
));

Related

yii filter in two cgrdiview with same model

I have two cgridview that using same model.
When I use filters, the textfield names are the same (model class).
It's possible modify this name?
example:
$model1 = new ClassName;
$model2 = new ClassName;
if(isset($_GET['cgrdiview_1'])) {
$model1->attributes = $_GET['cgrdiview_1'];
}
if(isset($_GET['cgrdiview_2'])) {
$model2->attributes = $_GET['cgrdiview_2'];
}
Instead I have:
$_GET['model_class']
for both cgridview
Yes, It is possible by adding an id field.
The id attribute specifies a unique id for an HTML element (the value must be unique within the HTML document).
The id attribute is most used to point to a style in a style sheet, and by JavaScript (via the HTML DOM) to manipulate the element with the specific id.
If you check the Yii API Documentation for CGridView You can see that you can set an id field for the CGridview.
In your view, change the CGridview code to contain the following:
'id' => 'cgridview_1'
This code needs to be added at the top of your gridview. For example:
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'cgridview_1',
'dataProvider'=>$dataProvider,
'columns'=>array(
'title', // display the 'title' attribute
'category.name', // display the 'name' attribute of the 'category' relation
'content:html', // display the 'content' attribute as purified HTML
array( // display 'create_time' using an expression
'name'=>'create_time',
'value'=>'date("M j, Y", $data->create_time)',
),
array( // display 'author.username' using an expression
'name'=>'authorName',
'value'=>'$data->author->username',
),
array( // display a column with "view", "update" and "delete" buttons
'class'=>'CButtonColumn',
),
),
));
I solved my problem with custom filter in each column:
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'cgridview_2',
'dataProvider' => $data,
'filter' => $filter,
'columns'=>array(
array(
'name' => 'column1',
'filter' => CHtml::textField("cgridview2[column1]", $filter->column1),
),
array(
'name' => 'column2',
'filter' => CHtml::textField("cgridview2[column2]", $object->column2),
),

Style columns in CGridView based on data in yii

Is it possible to style CGridView based on data?
For example these two cases:
Or I have to use other options?
Yes, you can specify css class by data. Use cssClassExpression option.
$this->widget('zii.widgets.grid.CGridView', array(
'dataProvider'=>$dataProvider,
'columns'=>array(
// ...
array(
'name'=>'Delta',
'value' => $data->value,
'cssClassExpression' => '$data->delta > 0 ? "up" : "down"',
),
// ...
),
));
as clear from other answers that cssClassExpression can be used for this purpose. I think there would be many if statements so that you cant use inline if statements for cssClassExpression.
I would do it in this way.
1. First define a method in your model like
public function checkColour()
{
if('value'<10)
{
return 'green quarter'
}
if('value'>10)
{
return 'yellow half'
}
}
Basically this method is returning class names you have created and want to use for specific condition.
2. Then i would use it like
'columns'=>array(
array(
'name'=>'nozare',
'cssClassExpression' => '$data->checkColour()'
),
),
You can do that by using cssClassExpression
array(
'name' => 'status',
'value' => '$data->status0->title',
'cssClassExpression' => call a function or do by expression,
),
By that you can set different class for different row and write corresponding css for that

ZF2 Form: Customizing order of elements

I'm creating a form for a logged-in user to change their password, so I created a subclass of an existing password-reset form I have available. The forms will be identical except with an additional field for existing password. It's worked so far, except I can't figure out a way to manually set the order the new field; the only place I've gotten it to appear is at the end of the form. It seems that ZF2 requires you to add() form elements in the order that you want them rendered. I would do so, except the subclass form's constructor must the parent form's constructor before it can add new fields, by which point the parent form has already added its fields.
I have already tried setting the property order of my new field, but it did not work; I've tried several different combinations (I can't find the documentation for this feature anywhere, after lots of searching).
Subclass constructor snippet:
class ChangePassword extends ResetPassword implements InputFilterProviderInterface {
public function __construct() {
parent::__construct();
$this->add(array(
'type' => 'Zend\Form\Element\Password',
'name' => 'existingPassword',
'order' => 0,
'options' => array(
'label' => 'Existing Password',
'order' => 0,
),
'attributes' => array(
'required' => 'required',
'order' => 0,
)
));
}
Parent constructor snippet:
class ResetPassword extends Form implements InputFilterProviderInterface {
public function __construct() {
parent::__construct('reset-password');
$this->add(array(
'type' => 'Zend\Form\Element\Password',
'name' => 'password',
...
The key you're looking for which affects element order is named priority.
The form add() method accepts a second array containing $flags, and it's in this array that you must add the priority key/value pair.
Your constructor should end up looking something like this ...
class ChangePassword extends ResetPassword implements InputFilterProviderInterface {
public function __construct() {
parent::__construct();
$this->add(array(
'type' => 'Zend\Form\Element\Password',
'name' => 'existingPassword',
'options' => array(
'label' => 'Existing Password',
),
'attributes' => array(
'required' => 'required',
)
), // add flags array containing priority key/value
array(
'priority' => 1000, // Increase value to move to top of form
));
}
}
I have encouter this issue today, Crisp's answer helped but I think it would be nice to precise this :
In the view we have a lot of options to show our form :
<?= $this->form($form)?>
<?= $form->get('{element}') ?>
loop over $form->getIterator()
loop over $form->getElements()
etc...
I have to say i used a lot this structure in all of my projects :
<?php foreach ($form->get('fieldset')->getElements() as $elementName => $element): ?>
<?= $this->partial('partial/formElement', ['element' => $element])?>
<?php endforeach ?>
The problem is : getElements does not use priority, so its just give the element in order of when it was instanciated.
In the view we have to use the iteration method ($form->getIterator()) to get back this flag priority.

yii cgridview button label or title change

this is the code for a button, i want to change the label or in other words the title tag content.
'assignParent' => array(
'label' => 'Assign Parent',
'url' => '$data->parentId ? Yii::app()->controller->createUrl("updateParent", array("id" => $data->parentId)): Yii::app()->controller->createUrl("assignParent", array("imei" => $data->imei))',
'imageUrl' => Yii::app()->baseUrl . '/media/images/parent-btn.png',
'visible' => 'Yii::app()->user->checkAccess("oDeviceDeviceAssignParent") ? true : false',
'options' => array('style' => 'padding: 0px 3%'),
),
this is the view source code,
<td class="button-column"><i class="icon-eye-open"></i><i class="icon-pencil"></i><i class="icon-trash"></i><img alt="Assign Parent" src="/qelasysecurity_12/media/images/parent-btn.png"><img alt="Assign Student" src="/qelasysecurity_12/media/images/student-btn.png"></td>
this is how that exact part comes,
<a href="#" rel="tooltip" title="$data->parentId ? Assign Student : Update Student" style="padding: 0px 3%">
i want to make this kind of a logic to change the label name,
'options' => array('style' => 'padding: 0px 3%', 'title'=>'$data->parentId ? Assign Student : Update Student'),
$data->parentId ? Assign Student : Update Student'
any suggestion to accomplish this?
Here is the solution.
With help from http://www.yiiframework.com/wiki/714/yii-1-1-cgridview-use-special-variable-data-in-the-options-of-a-button-i-e-evaluate-options-attribute/
in /protected/components create a new extension "ButtonColumn.php"
<?php
/**
* ButtonColumn class file.
* Extends {#link CButtonColumn}
*/
class ButtonColumn extends CButtonColumn
{
/**
* Renders a link button.
* #param string $id the ID of the button
* #param array $button the button configuration which may contain 'label', 'url', 'imageUrl' and 'options' elements.
* See {#link buttons} for more details.
* #param integer $row the row number (zero-based)
* #param mixed $data the data object associated with the row
*/
protected function renderButton($id,$button,$row,$data)
{
if (isset($button['visible']) && !$this->evaluateExpression($button['visible'],array('row'=>$row,'data'=>$data)))
return;
$label=isset($button['label']) ? $button['label'] : $id;
$url=isset($button['url']) ? $this->evaluateExpression($button['url'],array('data'=>$data,'row'=>$row)) : '#';
$options=isset($button['options']) ? $button['options'] : array();
if(!isset($options['title']))
$options['title']=$label;
// Start of modification
if( isset ( $button['evaluateLabel'] ) )
{
$label = $this->evaluateExpression($label,array('data'=>$data,'row'=>$row));
$label = $button['evaluateLabel'][$label];
unset($options['evaluateLabel']);
}
// END of modifications
if(isset($button['imageUrl']) && is_string($button['imageUrl']))
echo CHtml::link(CHtml::image($button['imageUrl'],$label),$url,$options);
else
echo CHtml::link($label,$url,$options);
}
}
Note the modifications line. This checks for a new variable 'evaluateLabel'. If it exits it will parse the 'label' variable through the as an array key to find the desired new value.
My new CGridView code looks as follows
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'users-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'id',
'username',
'email',
...
array(
'class' => 'ButtonColumn',
'template' => '{update} {delete} {switch}',
'buttons'=>array (
'delete'=>array(
'label'=>'$data->custom_variable',
'imageUrl'=>false,
'options'=>array( 'class'=>'cbutton delete' ),
'evaluateLabel' => array(0=>"Suspend",1=>"Reactivate"),
),
So in my case '$data->custom_variable is a variable tied to the model that will always be 0 or 1. So the text loaded will be as paired in the evaluateLabel key.
Also note the class has changed from CButtonColumn to ButtonColumn
i think you cant do it directly but you can do it using property cssClassExpression
In it you can write a valid PHP expression that will be calculated. Make two css classes one with content "assign student" and other with "update Student". You can use it like,
1. Write a function in your model named checkStudent like
public function checkStudent()
{
if($this->id)
{
return "class name"
}
else
{
return "class name"}
}
2.Now you can use it like
"cssClassExpression"=>'$data->checkStudent()'
Now question arises that how can u right content using Css class. Here is good answer to that question.
You can use my code
'link'=>array(
'header'=>Yii::t('main', 'login'),
'type'=>'raw',
'value'=> 'CHtml::button($data->islogin==1?"可":"不可",array("onclick"=>"document.location.href=\'".Yii::app()->controller->createUrl("user/changelogin",array("id"=>$data->id))."\'", "class"=>"btn-link"))',
),

Yii: TbEditableColumn how to pass $data->id

I am trying to use the TbEditableColumn and the type select to have a pulldown menu.
The pulldown menu I need is filled by a function that I call.
That is working for basic cases. But for a another column, the pulldown values are dependent from the row in which it is (grid-view).
So for example the function I want to call to fill the pulldown and pass the id of the current data is:
$model->getPulldownValues($data->id)
But that throws an error that the variable $data is not defined. The funny part is that outside the editable array, I can use $data as expected.
See example below:
Any ideas?
$this->widget('bootstrap.widgets.TbExtendedGridView', array(
'type' => 'striped bordered',
'id'=>'order-image-grid',
'dataProvider'=>$model->search(),
'ajaxUpdate'=>true,
'template' => "{items}\n{extendedSummary}",
'rowCssClassExpression'=>'"FMDBGridColumn".$data->order_error',
'columns'=>array(
array(
'class' => 'bootstrap.widgets.TbEditableColumn',
'name' => 'streets',
'htmlOptions'=>array('width'=>'150'),
'value' => 'CHtml::value($data, "street")',
'editable' => array(
'type' => 'select',
'source' => CHtml::listData($model->availableStreets($data->id), 'id', 'street'),
'url' => $this->createUrl('cities/editable'),
'placement' => 'right',
)
),
),
));
Try to change method "availableStreets" into getter, e.g.:
public function getAvailableStreets()
{
// we don't need to send id as parameter of method,
// we can get it directly from model
// e.g.: $id = $this->id;
//
// put your code here
}
then, in widget, use property
$model->availableStreets
instead of method
$model->availableStreets($id)
Also you can put
CHtml::listData() into your getter and use
'source' => 'availableStreets',
instead of
'source' => CHtml::listData($model->availableStreets($data->id), 'id', 'street'),
you have to consider that $data in 'value' => 'CHtml::value($data, "street")', is referring to a model object which $model->search() is providing, but $data outside of the grid is different(You haven't shared what that is).

Categories