I'm working on a view with lots of invoices.
Users can filter them by 'Customer', 'Date' and also by 'Referent'.
An invoice is linked to a customer, and a customer can have a 'referent' or not.
So in my 'referent' select list, the default value is 'All' to not filter by 'referent', and the rest is the list of all referents got by QueryBuilder.
Now, I need help to know how can I insert an option 'No Referent' in the select list to get all invoices for which the customer has no referent.
Here is my referent field in my 'InvoiceSearchType':
->add('referent', 'genemu_jqueryselect2_entity', array(
'label' => 'Referent',
'class' => 'GeocalUserBundle:User',
'query_builder' => function (UserRepository $ur) {
return $ur->getEmployesQueryBuilder();
},
'empty_value' => '',
'configs' => array(
'placeholder' => 'All',
'width' => '100%',
'allowClear' => true,
),
'required' => false,
))
Here, my QueryBuilder:
public function getEmployesQueryBuilder()
{
$queryBuilder = $this->createQueryBuilder('u')
->leftJoin('u.groups', 'g')
->where('u.enabled = 1')
->andWhere('g.id NOT IN(1)')
->orderBy('u.nom', 'ASC')
;
return $queryBuilder;
}
And I just display the field like that:
<td class="label">Chargé d'affaire</td>
<td colspan="2">{{ form_widget(form.referent) }}</td>
Thanks in advance ! :)
[SOLVED]
First I added a method which get the result (array) of the query, add another referent and return it:
public function getReferentWithNull()
{
// Get the list of referents
$referents = $this->doctrine->getRepository('GeocalUserBundle:User')->getEmployesQueryBuilder()->getQuery()->getResult();
// Create a new instance
$nobody = new User();
$nobody->setName("No Referent");
// Put it in the array result with the key -1
$referents[-1] = $nobody;
return $referents;
}
Then, I modified my form field type to 'choice' type and call my previous function:
->add('referent', 'genemu_jqueryselect2_choice', array(
'label' => 'Referent',
'choices' => $this->getReferentWithNull(),
'empty_value' => '',
'configs' => array(
'placeholder' => 'All',
'width' => '100%',
'allowClear' => true,
),
'required' => false,
))
Finally, I have my last option 'No Referent' with a key of -1.
Hope that it helps someone :)
Related
I have simple Yii cgridview code with pagination. Pagination is working fine but in the last page I have faced one issue.
For example, If I have 13 records in DB table, and set pagination for 10 pages per page then for first page It will show "1 - 10 of 13 results" but when I clicked on 2nd page link then It will show "4 - 13 of 13" instead of "11 - 13 of 13".
Here is my code.
1) Controller :
function actiontransactionHistory(){
$creditTransactionObj = new CreditTransaction();
$this->render('history',array(
'creditTransactionObj'=>$creditTransactionObj,
));
}
2) Model :
public function search()
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria=new CDbCriteria;
$criteria->compare('id',$this->id,true);
$criteria->compare('fkasmtGroupId',$this->fkasmtGroupId);
$criteria->compare('fkgroupSurveyUserId',$this->fkgroupSurveyUserId,true);
$criteria->compare('fkasmtId',$this->fkasmtId);
$criteria->compare('transaction_type',$this->transaction_type);
$criteria->compare('credit_type',$this->credit_type,true);
$criteria->compare('credit_qty',$this->credit_qty);
$criteria->compare('transaction_date',$this->transaction_date,true);
$criteria->compare('isDelete',$this->isDelete);
$criteria->compare('status',$this->status);
$criteria->compare('created_at',$this->created_at,true);
$criteria->compare('modified_at',$this->modified_at,true);
$sort = array(
"defaultOrder" => "transaction_date DESC",
);
return new CActiveDataProvider($this, array(
"criteria"=>$criteria,
"sort" => $sort,
));
}
3) View:
<?php
$this->widget('zii.widgets.grid.CGridView', array(
'id' => 'history-grid',
'dataProvider' => $creditTransactionObj->search(),
'loadingCssClass' => '',
'enableSorting' => true,
'itemsCssClass' => 'my-teams',
'summaryText' => "Displaying {start} - {end} of {count} results.",
"emptyText" => "There is no transaction history available.",
'columns' => array(
array('name' => 'transaction_date', 'header' => 'Date', 'type' => 'raw', 'value' => 'date("d-M-Y",strtotime($data->transaction_date))', 'htmlOptions' => array('class' => '')),
array('name' => 'credit_qty', 'header' => '# of Credits', 'sortable'=>false, 'type' => 'raw', 'value' => '($data->transaction_type == 1) ? - $data->credit_qty : $data->credit_qty', 'htmlOptions' => array('class' => '')),
array('name' => 'credit_type', 'header' => 'Type', 'type' => 'raw', 'value' => '$data->credit_type', 'htmlOptions' => array('class' => '')),
array('name' => 'transaction_type', 'header' => 'Activity', 'type' => 'raw', 'value' => '($data->transaction_type == 0) ? "Purchased" : (($data->transaction_type == 1) ? "Spent" : "Refunded")', 'htmlOptions' => array('class' => '')),
array('name' => 'fkasmtGroupId', 'header' => 'Group Name', 'type' => 'raw', 'value' => array($this,'getGroupName'), 'htmlOptions' => array('width' => '35%')),
)
));
?>
I have also attached both pages screenshot.
Any help will be appreciate. Thanks in advance !
After spending lots of hours finally I found the solution for this issue.
Actually the problem was in fetchData() function which is used in
yii/framework/web/CActiveDataProvider.php framework class file.
In fetchData() method, limit was not calculated properly for the last page pagination. so I have made changes to calculate correct limit.
Old Code:
protected function fetchData()
{
$criteria=clone $this->getCriteria();
if(($pagination=$this->getPagination())!==false)
{
$pagination->setItemCount($this->getTotalItemCount());
$pagination->applyLimit($criteria);
}
$baseCriteria=$this->model->getDbCriteria(false);
if(($sort=$this->getSort())!==false)
{
// set model criteria so that CSort can use its table alias setting
if($baseCriteria!==null)
{
$c=clone $baseCriteria;
$c->mergeWith($criteria);
$this->model->setDbCriteria($c);
}
else
$this->model->setDbCriteria($criteria);
$sort->applyOrder($criteria);
}
$this->model->setDbCriteria($baseCriteria!==null ? clone $baseCriteria : null);
$data=$this->model->findAll($criteria);
$this->model->setDbCriteria($baseCriteria); // restore original criteria
return $data;
}
New Code:
protected function fetchData()
{
$criteria=clone $this->getCriteria();
if(($pagination=$this->getPagination())!==false)
{
$pagination->setItemCount($this->getTotalItemCount());
$pagination->applyLimit($criteria);
// update limit to the correct value for the last page
$limit=$pagination->getLimit();
$offset=$pagination->getOffset();
if ( $offset+$limit > $pagination->getItemCount() )
$criteria->limit = $pagination->getItemCount() - $offset;
}
$baseCriteria=$this->model->getDbCriteria(false);
if(($sort=$this->getSort())!==false)
{
// set model criteria so that CSort can use its table alias setting
if($baseCriteria!==null)
{
$c=clone $baseCriteria;
$c->mergeWith($criteria);
$this->model->setDbCriteria($c);
}
else
$this->model->setDbCriteria($criteria);
$sort->applyOrder($criteria);
}
$this->model->setDbCriteria($baseCriteria!==null ? clone $baseCriteria : null);
$data=$this->model->findAll($criteria);
$this->model->setDbCriteria($baseCriteria); // restore original criteria
return $data;
}
But remember, never update a core class file of framework. so I just extend this method in my Model file and write below code.
Final Code In My Model file without change in framework file:
class CustomActiveDataProvider extends CActiveDataProvider
{
/**
* Fetches the data from the persistent data storage.
* #return array list of data items
*/
protected function fetchData()
{
$criteria=clone $this->getCriteria();
if(($pagination=$this->getPagination())!==false)
{
$pagination->setItemCount($this->getTotalItemCount());
$pagination->applyLimit($criteria);
// update limit to the correct value for the last page
$limit=$pagination->getLimit();
$offset=$pagination->getOffset();
if ( $offset+$limit > $pagination->getItemCount() )
$criteria->limit = $pagination->getItemCount() - $offset;
}
$baseCriteria=$this->model->getDbCriteria(false);
if(($sort=$this->getSort())!==false)
{
// set model criteria so that CSort can use its table alias setting
if($baseCriteria!==null)
{
$c=clone $baseCriteria;
$c->mergeWith($criteria);
$this->model->setDbCriteria($c);
}
else
$this->model->setDbCriteria($criteria);
$sort->applyOrder($criteria);
}
$this->model->setDbCriteria($baseCriteria!==null ? clone $baseCriteria : null);
$data=$this->model->findAll($criteria);
$this->model->setDbCriteria($baseCriteria); // restore original criteria
return $data;
}
}
// Used this custome active data provider as shown in below.
public function search()
{
$criteria=new CDbCriteria;
$criteria->compare('id',$this->id,true);
$criteria->compare('isDelete',$this->isDelete);
$criteria->compare('status',$this->status);
$criteria->compare('created_at',$this->created_at,true);
$criteria->compare('modified_at',$this->modified_at,true);
$sort = array(
"defaultOrder" => "transaction_date DESC",
);
return new CustomActiveDataProvider($this, array(
"criteria"=>$criteria,
"sort" => $sort,
"pagination" => array('pageSize' => (isset($_REQUEST['pageSize'])?$_REQUEST['pageSize']:10))
));
}
After this change, Last page pagination works completely fine.
Thanks!
I have three tables like this :
objects (id , name , tags , ...)
tags (id , name , ...)
object_tags(id , objectID , tagID)
I used krajee treeView input and in my "objects" form I have :
$form->field($model, 'tags')->
widget(\kartik\tree\TreeViewInput::className(),[
'name' => 'kvTreeInput',
'value' => 'false', // preselected values
'query' => Tags::find()->addOrderBy('root, lft')->name,
'headingOptions' => ['label' => 'تگ'],
'rootOptions' => ['label'=>'<i class="fa fa-building"></i>'],
'fontAwesome' => true,
'asDropdown' => true,
'multiple' => true,
'options' => ['disabled' => false]
]);
But I have no idea how should I write the codes in my controller or in my model!!!
In your action you should iterate over the post result
This is just a brief suggestion you must add the code related to your specific need
public function actionYourAction()
{
$post = Yii::$app->request->post();
if (!empty($post)){
$postTags = $post['tags'];
foreach ($postASCCId as $key => $value) {
$modelNew = new YourModel();
$modelNew->tag = $value;
$modelNew->save();
}
}
.....
}
I have simple CGridView, which is not showing me any data at all.
I've logged query built by CDbCriteria and there was some LIKE conditions which uses mysql default field values, so MySQL searches for entries with default values in required fields.
Since I have none entries matching this condidion it returns 0 rows. But the thing is, I don't need these LIKEs. Is there a way to disable it?
Here's the view:
$this->widget('zii.widgets.grid.CGridView', array(
'dataProvider' => $model->search(),
'filter' => $model,
'columns' => array(
array(
'name' => 'name',
'type' => 'raw',
'value' => 'CHtml::encode($data->name)'
),
array(
'name' => 'email',
'type' => 'raw',
'value' => 'CHtml::link(CHtml::encode($data->email), "mailto:".CHtml::encode($data->email))',
),
),
));
Controller:
public function actionUsers() {
$model = new Users();
$this->renderPartial('users',array(
'model' => $model,
));
}
Function Search of model Users:
return new CActiveDataProvider(get_class($this), array(
'criteria'=>$criteria,
'sort'=>array(
'defaultOrder'=>'user ASC',
),
'pagination'=>array(
'pageSize'=>5
),
));
And Logged Query:
SELECT * FROM `users` `t` WHERE (((((email LIKE :ycp0) AND (`group` LIKE :ycp1)) AND (gender LIKE :ycp2)) AND (city LIKE :ycp3)) AND (name LIKE :ycp4)) AND (av_url LIKE :ycp5) ORDER BY `t`.`name` LIMIT 5. Bound with :ycp0='%NotSet%', :ycp1='%3%', :ycp2='%Secret%', :ycp3='%NotSet%', :ycp4='%NotSet%', :ycp5='%noav.jpg%'
I am new to Stackoverflow and don't have sufficient reputation to post a comment, otherwise I would have asked that you share more information on how you configure your CDbCriteria object in your $model->search() function.
But, I suspect that you are using partial matches in the compare method, which can be disabled by passing false in the $partialMatch parameter as follows:
$criteria = new CDbCriteria;
$criteria->compare('email', $someValue, false);
Or, since false is the default value, you can simple write:
$criteria->compare('email', $someValue);
Good morning,
In Symfony 1.4,
I tried to do what is explained here : Customizing layout to sfWidgetFormDoctrineChoice
But it doesn't work. Instead of adding a thumbnail, I just want to hide the <li> before the input, and in some condition disable/hide the checkbox input but show the label anyway.
When I add the renderer without argument, I get this error :
sfWidgetFormMySelectCheckbox requires the following options: 'choices'.
Here is my formatter code :
class sfWidgetFormMySelectCheckbox extends sfWidgetFormSelectCheckbox
{
public function configure($options = array(), $arguments = array())
{
parent::configure($options, $arguments);
}
protected function formatChoices($name, $value, $choices, $attributes)
{
.....
// new
$inputs[$id] = array(
'input' => sprintf('| test | %s',
$this->renderTag('input', array_merge($baseAttributes, $attributes))
),
'label' => $this->renderContentTag('label', self::escapeOnce($option), array('for' => $id)),
);
}
return call_user_func($this->getOption('formatter'), $this, $inputs);
}
}
And now the form where I call it :
$this->setWidget('aaa', new sfWidgetFormDoctrineChoice(array(
'model' => 'Aaa',
'expanded' => true,
'multiple' => true,
'add_empty' => false,
'query' => $query,
'renderer' => new sfWidgetFormMySelectCheckbox()
)));
Thanks for your help !
According to the docs you have to pass the choices option to the renderer object. Try something like this:
$this->setWidget('aaa', new sfWidgetFormDoctrineChoice(array(
'model' => 'Aaa',
'expanded' => true,
'multiple' => true,
'add_empty' => false,
'query' => $query,
)));
$this->widgetSchema['aaa']->setOption('renderer', new sfWidgetFormMySelectCheckbox(array(
'choices' => new sfCallable(array($this->widgetSchema['aaa'], 'getChoices'))
)));
So basically you want the renderer object get the choices from the parent widget. To do that you have to pass a sfCallable object which takes an array as the first argument in which you pass the instance of your parent widget and the name of the function getChoices.
Remember also that the expanded option is not used when you override the renderer.
I have to modify a Zend form where some of the fields can contain decimals. Currently you can only enter your decimals using a point: 34.75)
What I'd like the users to be able is to write their decimals both with a comma or a point. The fields can contain either numbers like 34.75 and 34,75 (In this case, both have the same value). I don't want to modify any configuration on the server, so I need to do this in the code.
Right now the value of some fields is calculated in function of other fields; so when you enter a comma, it messes up the calculations. It's done in javascript, and I'll need to fix those calculations - but for now, I want to fix this issue in the php code when I retrieve the form.
I tried to find a solution on the Zend website, but I didn't find anything I've already read elsewhere with more examples. As you'll see in the code, I need to add either a filter or a validator to a zend_form_element_text. I cannot use a str_replace, as the element is a zend_form_element_text.
I have found this other question for reference.
Here is my resulting code:
$tabBilanFrais = array( 'txtFraisSecretariat' => array( 'nom' => 'Frais secrétariat', 'disabled' => true, "class"=>"calcul"),
'txtFraisRegion' => array( 'nom' => 'Frais région', 'disabled' => false),
'txtFraisSalle' => array( 'nom' => 'Salle', 'disabled' => false, "class"=>"calcul"),
'txtFraisPause' => array( 'nom' => 'Pauses', 'disabled' => false, "class"=>"calcul"),
'txtDivers' => array( 'nom' => 'Divers', 'disabled' => false, "class"=>"calcul"),
'txtTotalRegion' => array( 'nom' => 'Total région', 'disabled' => true, "class"=>"total"),
'txtIndemnisationAdherent' => array( 'nom' => 'Comm. ADH', 'disabled' => true, "class"=>"calcul"),
'txtIndemnisationPAP' => array( 'nom' => 'Comm. PAP', 'disabled' => true, "class"=>"calcul"),
'txtIndemnisationForNext' => array( 'nom' => 'Comm. ForNext', 'disabled' => true, "class"=>"calcul"),
'txtIndemnisationPROStages' => array( 'nom' => 'Comm. PROStages', 'disabled' => true, "class"=>"calcul"),
'txtRecettes' => array( 'nom' => 'Recettes', 'disabled' => true, "class"=>"totalMontant"),
'txtDepenses' => array( 'nom' => 'Dépenses', 'disabled' => true, "class"=>"totalMontant"),
'txtRecettesH' => array( 'nom' => 'Recettes', 'disabled' => false, "class"=>"hiddenTxt"),
'txtDepensesH' => array( 'nom' => 'Dépenses', 'disabled' => false, "class"=>"hiddenTxt")
);
$tabFormulaire = array() ;
foreach($tabBilanFrais as $id => $tabElement)
{
if($tabElement['nom'] == 'Frais region' )
$element = new Zend_Form_Element_Hidden($id, array("label" => $tabElement['nom'], "required" => false, 'decorators' => array("ViewHelper", "Errors", "Label"))) ;
else{
$element = new Zend_Form_Element_Text($id, array("label" => $tabElement['nom'], "required" => false, 'decorators' => array("ViewHelper", "Errors", "Label"))) ;
//$element->addFilter('pregReplace', array('match' => '/,/', 'replace' => '.'));
$element->addFilter('LocalizedToNormalized');
$element->addValidator('float', true, array('locale' => 'fr_FR'));
if(isset($tabElement['class']) && $tabElement['class']){
$element->setAttrib('class', $tabElement['class']);
}
}
if( $tabElement['disabled'])
$element->setAttrib('disabled', 'disabled');
$tabFormulaire[] = $element ;
}
The pregReplace isn't working. The validator is (comma becomes a .). I get an error message about the number not being a float.
You can always write your own validator. In case of float I faced the same problem like you:
class Yourlib_Validate_Float extends Zend_Validate_Abstract
{
const INVALID = 'floatInvalid';
const NOT_FLOAT = 'notFloat';
/**
* #var array
*/
protected $_messageTemplates = array(
self::INVALID => "Invalid type given. String, integer or float expected",
self::NOT_FLOAT => "'%value%' does not appear to be a float",
);
public function isValid($value)
{
$this->_setValue($value);
$value = str_replace(',', '.', $value);
if (!is_string($value) && !is_int($value) && !is_numeric($value)) {
$this->_error(self::INVALID);
return false;
}
if (is_numeric($value)) {
return true;
}
$this->_error(self::NOT_FLOAT);
return false;
}
}
And to add the validator:
$element->addValidator(new Yourlib_Validate_Float());
Please rename Yourlib to whatever suits you. And you need to register your "namespace" in the application.ini like this:
autoloadernamespaces.Yourlib = "Yourlib_"
Strictly speaking this validator is a numeric validator. It accepts all numeric values like ints and floats thru the check with is_numeric. Feel free to modify that.
Alright, here is the modified part of the code:
foreach($tabBilanFrais as $id => $tabElement)
{
if($tabElement['nom'] == 'Frais region' )
$element = new Zend_Form_Element_Hidden($id, array("label" => $tabElement['nom'], "required" => false, 'decorators' => array("ViewHelper", "Errors", "Label"))) ;
else{
$element = new Zend_Form_Element_Text($id, array("label" => $tabElement['nom'], "required" => false, 'decorators' => array("ViewHelper", "Errors", "Label"))) ;
$element->addFilter('pregReplace', array('match' => '/,/', 'replace' => '.'));
$element->addFilter('LocalizedToNormalized');
$element->addValidator(new Anper_Validate_Float(), true, array('locale' => 'fr_FR'));
if(isset($tabElement['class']) && $tabElement['class']){
$element->setAttrib('class', $tabElement['class']);
}
}
if( $tabElement['disabled'])
$element->setAttrib('disabled', 'disabled');
$tabFormulaire[] = $element ;
}
But now I need the values of the fields in $element to have the comma replaced by a point before the element is added to $tabFormulaire. Currently numbers with a comma are truncated (124,5 becomes 124) when I validate the form and display the updated values.
The pregreplace doesn't seem to work.
Edit: it seems I don't need the pregReplace. I used two echo in my isValid function for the custom validator: one before the str_replace and one after. When I write in one of the field a value with a comma, both the echo displays the number with a point. I assume it's the result of the filter LocalizedToNormalized.
What I don't understand is why once the values are saved and displayed those with a comma are truncated despite the findings I just made.
Edit2: If I write for example 124 8, and use a pregReplace to do like there was no blank, the 8 isn't kept on save; despite the pregReplace working (tried with my previous echo).
Although #bitWorking's answer is 100% OK, from the view point of semantics, it's better to use a filter (since it's more of filtering rather than validating)
either NumberFormat filter or writing your own.