Yii Framework: Different view (on actionIndex) depending on user's role - php

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')

Related

Filtering for Relational Model Fields in Admin - CGridView

I am having difficulty getting the filtering to work in CGridView for relational fields from another model.
URL Reference: Yii 1.1: Searching and sorting by related model in CGridView
I follow the codes and it seems to be returning me the inputs from the other model. Everything looks fine but unfortunately, the filter is not working.
On Search, it will display a quick load icon but failed to filter accordingly. Upon checking further, I noticed the input is wrong. I am using Google Inspect Element and noticed the following:
<input name="User[full_name]" type="text">
I am using User model, relational to Biodata. Shouldn't it be Biodata[full_name]? If this is, where should I be looking at the codes?
Thank you. :D
Filtering and sorting in CGridView widget can be done in few steps:
1. Add virtual field to your User model. It will be used to create column in CGridView; it also needed for proper filtering and sorting:
class User extends CActiveRecord
{
public $bioFullName;
//...
}
2. Modify search() function in your User model. You need to add array parameter to this function and add to $criteria object in this method connection with related model. To add sorting of related attribute, you also need to modify returned CActiveDataProvider. See what happens below:
public function search($params = array()) // <-- new parameter that handling params for searching
{
$criteria = new CDbCriteria($param);
$criteria->with = array('biodata'); //add relation with Biodata to $criteria object
// ... existing $criteria conditions
$criteria->addSearchCondition('biodata.full_name', $this->bioFullName, true, 'AND'); // add comparison of biodata.full_name
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
// ...
'sort'=>array(
'attributes'=>array(
'bioFullName'=>array( // <-- sorting of related field
'asc'=>'biodata.full_name ASC',
'desc'=>'biodata.full_name DESC',
),
'*',
),
),
));
}
3. Adjust CGridView widget in your view to show related column. In columns array of widget configuration add column named like virtual field from model (bioFullName):
<?php
$this->widget('zii.widgets.grid.CGridView', array(
// ... other widget configuration options
'columns'=>array(
// ... other columns
'bioFullName'=>array(
'name'=>'bioFullName', // <-- name of virtual model field
'value'=>'$data->biodata->full_name', // <-- getting field value from relation
'header'=>'Full name', // <-- CGridView column header
),
// ... other columns
),
));
?>

fetch photos of an album in Yii (relation case)

I have two tables , one album and one photo. the photo table has a FK (album_id) which refer to id in album table.
now I want to show photos in CListview but I don't know how. :(
As I see it is uses dataProvider and I don't know how to combine relation with it.
would you help me?
Thank you masters.
It's simple
$dataProvider=new CActiveDataProvider('Photo', array(
'criteria'=>array(
'condition'=>'album_id=:album_id',
'params'=>['album_id'=>$album_id]
)
));
Then in view use this dataProvider:
$this->widget('zii.widgets.CListView', array(
'dataProvider'=>$dataProvider,
'itemView'=>'_photo'
));
First you should create the relation in you models.
In Album.php add sth like this:
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'photos'=>array(self::HAS_MANY, 'Photo',array('album_id'=>'id')),
);
}
Similar in Photo.php:
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'album'=>array(self::BELONGS_TO, 'Album',array('id'=>'album_id')),
);
}
Now you can use it in your code:
$dataProvider=new CActiveDataProvider('Photo', array(
'criteria'=>array(
'condition'=>'album_id='.$yourAlbumId,
)
));
$this->widget('zii.widgets.CListView', array(
'dataProvider'=>$dataProvider,
...
));
If you need to refer to the Album in the CListview (let's say you have a field called "title" in the Album model), you can refer to it as $data->album->title in your items template.

cake php accessing a table

I just started cakephp following there tutorials
I'm able to grab the posts table in my post controller and spew it onto my index.ctp
In my view for the post controller i also want to list the User name that posted the article. My post table has a user_id, so i need to match it to my user table and pass it along
class PostsController extends AppController {
public function index() {
//passes values to the view
$this->set('posts', $this->Post->find('all'));
//is "Post" a post method? or is it the name of the table? i'm unsure of the syntax
$this->set('users', $this->Users->find('all')); //this does not work
}
}
thank you for your help with this basic question
You must use 'recursive'
$this->Post->find('all', array(
'recursive' => 2,
// ...
));
Of course, you first need to link models together
I assume that you have already set a belongsTo association (Post belongsTo User) and/or a hasMany association (User hasMany Post). If so, cake will automaticly brings the associated models (unless you put $recursive = -1 on your model).
Thus you'll have access to the users related to each post on the view: posts[i]['User']
You can also use this on your view to see the view variables:
debug($this->viewVars)
put this on your Post model if you don't:
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
)
);
Make sure that you load models corretly (in case you want to load the User model inside PostsController).
So simply add this attribute inside your class controller.
public $uses = array('Post','User');
to link models together . u need to add the association inside your Post model .
public $belongsTo = array(
'User'=>array(
'className'=> 'User',
'foreignKey'=>'user_id'
)
);
and i you want to retrieve data from database you have to set your recursivity and there is two ways
first one :
$posts = $this->Post->find('all',array('recursive'=>2));
// or
$this->Post->recursive = 2;
$posts = $this->Post->find('all');
second one : use the Containable behavior
set the recursivity to -1 in the AppModel and include the behavior
public $recursive = -1;
public $actsAs = array('Containable');
so simply u can retieve posts with any other linked models like that
$posts = $this->Post->find('all',array(
'contain'=>array('User'),
// ...
)));

There are <select> populated with (key->value), and field "key" in a model. How I can display it's "value" in a view, instead of "key"?

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.

Yii CGridView: how to add a static WHERE condtion?

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.

Categories