i am just trying to override the class of CGridColumn but somehow its not overriding.
my code is below
<?php
Yii::import('zii.widgets.grid.CGridColumn');
class CGridColumnCustom extends CGridColumn
{
// new variable that will bind near header
public $headerFilter;
public function renderHeaderCell()
{
$this->headerHtmlOptions['id']=$this->id;
echo CHtml::openTag('th',$this->headerHtmlOptions);
$this->renderHeaderCellContent();
$this->renderFilterHeaderCellContent();
echo "</th>";
}
#------------------------------------------------------------------------------------
# custom function that will concat with renderHeaderCellContent at renderHeaderCell
#------------------------------------------------------------------------------------
protected function renderFilterHeaderCellContent()
{
echo trim($this->headerFilter)!=='' ? $this->headerFilter : $this->grid->blankDisplay;
}
}
?>
and i added this file(CGridColumnCustom.php) in components folder and i also imported this file in CustomerModule.php file like below
$this->setImport(array(
'customer.components.*',
));
but when i am trying to implement my custom function like below in view file
'columns'=>array(
array(
'name' => 'Name',
'value' => '$data->Name',
'type' => 'raw',
'headerFilter'=> '<span class="name-filter-head" onclick="alert(\'test\');"> CustomFilter</span>',
),
then it will give below error
Property "CDataColumn.headerFilter" is not defined.
But if i directly added those changes on Core file at CGridColumn.php at gii.widgets.grid then its working fine but i don't want to change in core file.
Try to do it following way:
First thing (actually not sure if it's relevant, but name your class CGridCustomColumn with word 'Column' on the end.
Second, view should be like this then:
'columns'=>array(
array(
'header' => 'CGridCustom',
'class' => 'CGridCustomColumn'
'value' => '$data->Name'
'type' => 'raw'
),
Related
This is the error:
Alias "custom.controllers.ExampleController.php" is invalid. Make sure
it points to an existing PHP file and the file is readable.
My code is given below
main.php=>
return
array(
'controllerMap' => array(
'product' => array(
'class' => 'custom.controllers.Product.php',
),
),
'import' => array(
'custom.mycompany.*',
),
'components' =>
array(
'widgetHandler' => array(
//Load a component
'class' => 'custom.mycompany.mywidget.mywidget',
),
)
);
Product.php=>
<?php
class Product extends Controller
{
public function actionIndex()
{
echo "this is the default index function";
}
public function actionTest()
{
echo "This is the test function";
}
}
I am using lightspeed cms.
the notation for Yii2 / php class is not dot based but slash based
'class' => 'custom\controllers\Product.php',
(And in your code there is not the ExampleController..)
see p https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
and https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md
https://github.com/yiisoft/yii2/blob/master/docs/internals/core-code-style.md
for PHP an Yii2 coding standard suggestion
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.
I would like to make navigation buttons in my view, for example index.phtml but it's not working. I did know how to do it in Zend1 but in Zend2 I have a problem. My code looks like this (file index.phtml):
$container = new \Zend\Navigation\Navigation($tableActions);
var_dump($container);
echo '<div class="table-column">';
echo $this->navigation($container)->menu();
echo '</div>';
Variable $tableAction looks like this:
public $tableActions = array(
array(
'label' => 'On/Off',
'module' => 'import',
'controller' => 'import',
'action' => 'setstatus',
'params' => array('id' => null),
),
);
I did not get any error, just whole site die on this line. var_dump returns object(Zend\Navigation\Navigation) so it's fine so far. Problem is, how to show it...
The navigation pages have dependencies which aren't being met by just creating a new container class in a view. The Mvc page needs a RouteStackInterface (Router) instance and a RouteMatch instance. Similarly Uri pages need the current Request instance.
You can see this clearly if you take a look at the Zend\Navigation\Service\AbstractNavigationFactory and its preparePages and injectComponents methods.
The view is not the right place to be instantiating menus, instead put the menu configuration spec in your module.config.php...
<?php
return array(
'navigation' => array(
'table_actions' => array(
array(
'label' => 'On/Off',
'module' => 'import',
'controller' => 'import',
'action' => 'setstatus',
'params' => array('id' => null),
),
),
),
);
Write a factory extending the AbstractNavigationFactory class and implement the getName() method which returns the name of your menu spec key (table_actions in this example)
<?php
namespace Application\Navigation\Service;
use Zend\Navigation\Service\AbstractNavigationFactory;
class TableActionsFactory extends AbstractNavigationFactory
{
/**
* #return string
*/
protected function getName()
{
return 'table_actions';
}
}
Map the factory to a service name in the service_manager spec of module.config.php ...
<?php
return array(
'navigation' => array(// as above ... )
'service_manager' => array(
'factories' => array(
'TableActionsMenu' => 'Application\Navigation\Service\TableActionsFactory',
),
),
);
Now you can call the view helper using the service name TableActionsMenu you just mapped
<div class="table-column">
<?php echo $this->navigation('TableActionsMenu')->menu(); ?>
</div>
Finally, if, as I suspect, you need to change an attribute of the page depending on the view, you can do that too, navigation containers have find* methods which can be accessed from the navigation helper and used to retrieve pages.
Here's an example looking for the page with a matching page label, then changing it before rendering (obviously not an ideal search param, but it gives you the idea)
$page = $this->navigation('TableActionsMenu')->findOneByLabel('On/Off');
$page->setLabel('Off/On');
// and then render ...
echo $this->navigation('TableActionsMenu')->menu();
I´m developing an application using Zend Framework 2 and I use FormRow helper to render a label, the input and errors (if present) in a Form.
//within the view
echo $this->formRow($form->get('Name'));
When a user submits the form without filling the required input text field FormRow render´s it with the following error message:
<label>
<span>Name: </span>
<input class="input-error" type="text" value="" placeholder="Insert Name Here" name="Name">
</label>
<ul>
<li>Value is required and can't be empty</li>
</ul>
How can I set a class for the li tag to style it afterwards?
I know that I can echo the formElementErrors with the desired class attribute via..
<?php echo $this->formElementErrors($form->get("Name"), array('class' => "valuerequired", 'message' => "errortestmessage")); ?>
..but FormRow will still render the error message without the class.
Just for reference I´m setting the entity this way:
public function getInputFilter()
{
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$factory = new InputFactory();
$inputFilter->add($factory->createInput(array(
'name' => 'Name',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 1,
'max' => 100,
),
),
),
)));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
See the code of formElementErrors
Basically you could do something like:
$this->formElementErrors($elem)
->setMessageOpenFormat('<ul%s><li class="some-class">')
->setMessageSeparatorString('</li><li class="some-class">');
But that is quite unhandy...
The better solution would be to extend the Zend\Form\View\Helper\FormElementErrors by your own class and then register the view-helper formElementErrors to your class. So basically you'd have something like this:
namespace Mymodule\Form\View\Helper;
use Zend\Form\View\Helper\FormElementErrors as OriginalFormElementErrors;
class FormElementErrors extends OriginalFormElementErrors
{
protected $messageCloseString = '</li></ul>';
protected $messageOpenFormat = '<ul%s><li class="some-class">';
protected $messageSeparatorString = '</li><li class="some-class">';
}
Last thing then would be to register the view helper with this new Class. For this you provide the following code inside your Modules Module.php
public function getViewHelperConfig()
{
return array(
'invokables' => array(
'formelementerrors' => 'Mymodule\Form\View\Helper\FormElementErrors'
),
);
}
displaimer: This code isn't tested, let me know if there are some errors, but i think this should work out quite well.
Ok, the solution to my own problem was right in front of me, instead of using:
//within the view
echo $this->formRow($form->get('Name'));
I called each element of the form individually:
//within the view
echo $this->formLabel($form->get('Name'));
echo $this->formInput($form->get('Name'));
echo $this->formElementErrors($form->get("Name"), array('class' => "some_class", 'message' => "errormessage"));
Don´t know if it´s the most efficient way of doing it, plz drop a line if you think otherwise.
FormRow check if "form_element_errors" plugin registered. And if so use it as default to display error messages.
So Sam's example work. You should redefine standard plugin and inform mvc about it.
I'v done it in module.config.php
'view_helpers' => array(
'invokables' => array(
'formElementErrors'=> 'MyModule\View\Helper\FormElementErrors',
and FormRow start display errors as I wish :)
As your problem, please try
Change
//within the view
echo $this->formRow($form->get('Name'));
to
//within the view
echo $this->formRow($form->get('Name'),null,false);
// Note: add more 2 last parameters, false- for $renderErrors => will NOT render Errors Message.
//Look original function in helper/formrow.php: function __invoke(ElementInterface $element = null, $labelPosition = null, $renderErrors = null, $partial = null)
Render Errors Message as your funciton
echo $this->formElementErrors($form->get('name'), array('class' => 'your-class-here'));
From the documentation of ZF2. Here's the link: http://framework.zend.com/manual/2.0/en/modules/zend.form.view.helpers.html#formelementerrors
echo $this->formElementErrors($element, array('class' => 'help-inline'));
// <ul class="help-inline"><li>Value is required and can't be empty</li></ul>
I use echo $this->formElementErrors($form, array('class' => "error-messages")); to show all error messages in one place:
echo $this->formElementErrors($form, array('class' => "error-messages"));// Print all error messagess
echo $this->formLabel($form->get('Name'));
echo $this->formInput($form->get('Name'));
echo $this->formLabel($form->get('Name2'));
echo $this->formInput($form->get('Name2'));
$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',
),
),
));