people..
My problem is:
I have a form, with a Collection, so, I setted a CollectionInputFilter to this Collection..
If i send two block of fields (collections) to validate, and one field of one of this block is validated, the same field in the another one block is automatically validated too, even if is wrong or not filled..
I don't know what to do.. I tried a lot of tips.. but, none worked..
Someone can help me? Or faced the same problem?
Here my code:
class ClienteEnderecoFilter extends CollectionInputFilter
{
public function __construct()
{
$this->add(array(
'name' => 'fieldOne',
'required' => true,
));
}
}
If i send two fieldOne and one is filled, the another one is validated too!
I think you need to set the input filter for each collection item on to the CollectionInputFilter instead of extending it. Like this:
use Zend\InputFilter\InputFilter;
class ClienteEnderecoFilter extends InputFilter
{
public function __construct()
{
$this->add(array(
'name' => 'fieldOne',
'required' => true,
));
}
}
$collectionInputFilter = new CollectionInputFilter();
$collectionInputFilter->setInputFilter(new ClienteEnderecoFilter());
Related
I'm a beginner in Yii Framework, and I have a problem I can't fix. I have this in my class controller :
public function actionIndex()
{
$dataProvider=new CActiveDataProvider('Absence');
$this->render('index',array(
'dataProvider'=>$dataProvider,
));
}
That gives me a list of all 'absence'.
In my case, 'erty' is logged in and sees a list of every absence. But, with his role, stored in my user's table, I want him to see only a list of absences with his 'Collaborateur alias'.
Can someone helps me with it ?
Just create
$criteria=new CDbCriteria;
$criteria->compare('role', $user->role /* replace this with required role*/, true);
And attach it into DataProvider
$dataProvider = new CActiveDataProvider('Absence', array( 'criteria'=>$criteria));
Better still, and as it's a business rule, it should go in the Absence data model.
So you can add a scope in your Absence data model:
'mine'=>array(
'order'=>'a_sort_column DESC',
'condition'=>'role=:role',
'params'=>array(
'owner'=>Yii::app()->user->getState('roles'),
),
),
and then in your code use
$dataProvider=new CActiveDataProvider(Absence::model()->mine())
If its relevant you can always use a default scope if this filter is always applied.
If this filter is always applied except in an admin context, think about using another class that extends the Absence model and applies the default scope like
class myAbsence extends Absence
{
public function defaultScope() {
return array(
'order'=>'a_sort_column DESC',
'condition'=>'role=:role',
'params'=>array(
'owner'=>Yii::app()->user->getState('roles'),
),
);
}
and then in your non-Admin controllers you would use
$dataProvider=new CActiveDataProvider('myAbsence')
I want to create a form select field which looks like this:
<select>
<option value="product.product_id">product_details.detail_name</option>
etc...
</select>
The value is not the problem, the problem is with the label.
I have a product entity and a productDetails entity which contains translated data about a product.
So, in my form type class, in the buildForm method, I have this:
$builder->add('product', 'entity', array(
'class' => 'MyBundle:Product',
'property' => 'details.detail_name',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('p')
->select('p, pd')
->join('p.details', 'pd')
->where('pd.language_id = :lang')
->setParameter('lang', 'en');
}));
I want the property to be the details.detail_name.
I tried different values for this property value. Like 'details.detail_name', 'pd.detail_name' and 'p.details.detail_name'.
But it seems to be impossible to get the property to display the detail name.
When I use to above mentioned code, I get this error:
Neither property "detail_name" nor method "getDetailName()" nor method "isDetailName()" exists in class "Doctrine\ORM\PersistentCollection"
This getDetailName() method does exist in the ProductDetails entity, and I have checked the entities and they all seem to be okay. Also, they work just fine when I use these entities outside the form.
I also tried to execute the resulting query directly on my database, and it gives me the expected results. The detail_name are in the right language.
So, can somebody help me on how to make the select choice list I want, with a joined query?
I finally managed to get this working. Below, I'm showing how I'm doing this, in case someone else has the same problem.
I am now using a custom form type.
And in the setDefaultOptions, I am calling a repository method, which returns an array with "product_id" => "detail_name".
class ProductChoiceType extends AbstractType
{
private $repository;
public function __construct(EntityRepository $repository)
{
$this->repository = $repository;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'choices' => $this->repository->findAllProductsForForm('nl', true)
));
}
public function getParent()
{
return 'choice';
}
public function getName()
{
return 'product_choice';
}
}
In the $this->repository->findAllProductsForForm method, I am using a query and a foreach loop to make the array suitable for the choice list.
Then, I had to register the repository and this type in my services.xml file:
<service id="mybundle.repository.product"
factory-service="doctrine.orm.default_entity_manager"
factory-method="getRepository"
class="MyBundle\Repository\ProductRepository" >
<argument>MyBundle:Product</argument> <!-- not sure why I need this, but it wouldn't work without it -->
</service>
<service id="mybundle.xxx.form.product_choice_type" class="Mybundle\Form\Type\ProductChoiceType">
<argument type="service" id="mybundle.repository.product" />
<tag name="form.type" alias="product_choice" />
</service>
And then, in the root form type (I think it's called that) I use the 'product_choice' as a form type.
I'm not sure if this the best way to do this, but at least it works.
Now I only need to figure out how to pass on the current language of the user on to the repository, but that's a problem for later.
From what I see your Method Product::getDetails returns Doctrine\ORM\PersistentCollection not `ProductDetails' entity (so collection not single object). It mean that product is related with details using one-to-many/many-to-many association.
You can try to do it from product details side then:
$builder->add(
'product',
'entity',
array(
'class' => 'MyBundle:ProductDetails',
'property' => 'detail_name',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('pd')
->select('pd, p')
->join('pd.product', 'p')
->where('pd.language_id = :lang')
->setParameter('lang', 'en');
}
)
);
In model:
public function getOptionsGender()
{
array(0=>'Any', 1=>Male', 2=>'Female');
}
In view (edit):
echo $form->dropDownList($model, 'gender', $model->optionsGender);
but I have a CDetailView with "raw" attributes, and it displays numbers instead of genders.
$attributes = array(
...
'gender',
)
What is appropriate way to convert these numbers back to genders? Should I do it in a model, replacing fields such as $this->gender = getOptionsGender($this->gender)? Any github examples will be very appreciated.
I had to choose gender, age, city, country etc. in a few views that are not related to this one. Where should I place my getOptionsGender function definitions?
Thank for your help, the problem is solved.
In model:
public function getGenderOptions() { ... }
public function genderText($key)
{
$options = $this->getGenderOptions();
return $options[$key];
}
In view:
$attributes = array(
array (
'name'=>'gender',
'type'=>'raw',
'value'=>$model->genderText($model->gender), //or $this->genderText(...)
),
);
$this->widget('zii.widgets.CDetailView', array(
'data'=>$model,
'attributes'=>$attributes,
));
The working example can be found here:
https://github.com/cdcchen/e23passport/blob/c64f50f9395185001d8dd60285b0798098049720/protected/controllers/UserController.php
In Jeffery Winsett's book "Agile Web Application Development with Yii 1.1", he deals with the issue using class constants in the model you are using. In your case:
class Model extends CActiveRecord
{
const GENDER_ANY=0;
const GENDER_MALE=1;
const GENDER_FEMALE=2;
public function getGenderOptions(){
return array(
self::GENDER_ANY=>'Any',
self::GENDER_MALE=>'Male',
self::GENDER_FEMALE=>'Female',
);
}
public function getGenderText(){
$genderOptions=$this->genderOptions();
return isset($genderOptions[$this->gender]) ? $genderOptions[$this->gender] : "unkown gender({$this->gender})";
}
}
Then in your CDetailView you would have to alter it from gender to:
array(
'name'=>'gender',
'value'=>CHtml::encode($model->genderText()),
),
If several models have the same data, you may want to create a base model that extends CActiveRecord and then extend the new model instead of CActiveRecord. If this model is the only one with that data (ie User model only has gender), but other views use that model to display data, then I would leave it just in the single model class. Also keep in mind that if you place getGenderOptions in the extended class, and you extend ALL your models, they will all have that option available, but may not have the attributes needed and will throw an error if you aren't checking for it.
All this being said, I still think it is a matter or preference. You can handle it however you want, wherever you want. This is just one example from a book I have specifically on Yii.
I've a standard Gii created admin view, which use a CGridView, and it's showing my user table data.
the problem is that user with name 'root' must NOT BE VISIBLE.
Is there a way to add a static where condition " ... and username !='root' " ?
admin.php [view]
'columns'=>array(
'id',
'username',
'password',
'realname',
'email',
.....
user.php [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);
$criteria->compare('username',$this->username,true);
$criteria->compare('password',$this->password,true);
$criteria->compare('realname',$this->realname,true);
$criteria->compare('email',$this->email,true);
......
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
You can use CDbCriteria's addCondition like this:
$criteria->addCondition("username !='root'");
Your best option would be to use Yii scopes which are essentially a saved where clause (or other modification of your existing criteria) that you can apply all over your app and only need to change in one place if your criteria ends up changing later.
What makes them even cooler is that you can string them together with other scopes / criteria changes (from users in grids for instance) without having to keep track of what criteria clause is getting changed by what.
A few examples that might apply to your situation. In your controller you probably have something like this:
$users = User::model()->search()->findAll();
Asgaroth's answer answers what you were asking on the surface. But there is so much more you can do (and do easily) using scopes.
If you add the below to your user model:
class User extends CActiveRecord
{
......
public function scopes()
{
return array(
'active'=>array(
'condition'=>'active=1',
),
'isAdmin'=>array(
'condition'=>'isAdmin=1',
),
);
}
}
then you can retrieve active users (with your users' filters still applied) like this in your controller:
$users = User::model()->active()->search()->findAll();
Or you can retrieve all active admin users (without being filtered by your gridview criteria) like this:
$users = User::model()->active()->isAdmin()->findAll();
Default scopes are just an extension of the same idea:
class User extends CActiveRecord
{
public function defaultScope()
{
return array(
'condition'=>"username != 'root'",
);
}
}
If before your isAdmin scope would return the root user, applying the default scope will eliminate the root user from the models returned, as it applies to every User::model() query you make.
So, I'm building a small form with a choice. However, I want to pre-select some of those options, as if I was applying selected="selected". I could not find how can this be done in the docs. Help? :D
To set default values for a form those values need to be set in the underlying data class for the form. Assuming the underlying data class is an Entity, the values can be defaulted in that Entity on construction. If you are not using entity annotations and don't want to alter your generated entity classes you can set default values into a new instance of the entity class and use it as the data for your form.
For example, for a User entity which has an array of roles and a method setRoles(array $roles) the roles can be defaulted in the constructor of the User entity like this (hardcoded strings used for clarity):
public function __construct()
{
$this->setRoles(array('ROLE_USER', 'ROLE_READER', 'ROLE_EDITOR');
}
Alternatively, the roles can be defaulted in the controller just before the form is displayed like this (simplistic example with no form class and hardcoded strings):
$allRoles = array('ROLE_USER', 'ROLE_READER', 'ROLE_EDITOR', 'ROLE_ADMIN', 'ROLE_SUPER_ADMIN');
$user = new User();
$user->setRoles(array('ROLE_USER', 'ROLE_READER', 'ROLE_EDITOR');
$form = $this->createFormBuilder($user)
->add('username', 'text')
->add('roles', 'choice', array('choices' => array_combine($allRoles, $allRoles),
'multiple' => true)
->getForm();
return $this->render('AcmeTaskBundle:Default:new.html.twig', array(
'form' => $form->createView(),
));
Ended up being simpler than i thought:
$form['form[selectionMenu]']->select(1);