I would like to return selected values to the CGridView (similar to IN query) using the search function that is generated by Yii.To elaborate further let me use the example below:
Here I return values of the based on the value '34455' (fk_recordid)
public function search()
{
$fk_recordid = '34455';
$criteria=new CDbCriteria;
$criteria->compare('id',$this->id,true);
$criteria->compare('fk_recordid',$fk_recordid,true);
$criteria->compare('babypid',$this->babypid);
$criteria->compare('babysbn',$this->babysbn);
return new CActiveDataProvider(get_class($this), array(
'criteria'=>$criteria,
));
}
How would I change this code to widen the fk_recordid criteria i.e return records based on several values such as '34455','47859','78956' .....
Instead of compare() like you have:
$fk_recordid = '34455';
$criteria->compare('fk_recordid',$fk_recordid,true);
You could use addInCondition() like so:
$myRecordIds = array('34455','47859','78956');
$criteria->addInCondition('fk_recordid',$myRecordIds);
I don't know how you are passing in all of these record ids from the CGridView to the search() function, but once you have them addInCondition() will work. I hope this helps!
Related
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
),
));
?>
I use a search symptoms form and I pass off the symptomCode to my diseaseController
both symptoms and diseases have model classes, but the table connecting the 2 does not.
I use this query to find all the diseases that have the specific symptom
$diseaseCodes = Yii::app()->db->createCommand()
->select ('ICD10')
->from('tbl_disease')
->join('tbl_symptom_disease', 'tbl_disease.ICD10=tbl_symptom_disease.diseaseCode')
->where('symptomCode=:symptomCode',
array(':symptomCode'=>$_GET['symptomCode']))
->queryAll();
Now I want to know how I can use this to populate a dataprovider to populate a gridview
One idea I had was to create a custom model function
public function queryResultSearch($diseaseArray)
{
$criteria=new CDbCriteria;
$criteria->compare('ICD10',$diseaseArray,true);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
And use this to render yii's admin action (for disease models), but I can't get it to work because probably my entire way at going at this is wrong.
Can someone help me please? How to use a mysql query result to populate an activedataprovider object.
Thank you for your time
In your controller action you can try this:
$model=new ModelName('search');
$model->unsetAttributes();
if(isset($_GET['ModelName']))
$model->attributes=$_GET['ModelName'];
$diseaseCodes = Yii::app()->db->createCommand()
->select ('ICD10')
->from('tbl_disease')
->join('tbl_symptom_disease', 'tbl_disease.ICD10=tbl_symptom_disease.diseaseCode')
->where('symptomCode=:symptomCode',
array(':symptomCode'=>$_GET['symptomCode']))
->queryAll();
$diseaseArray=array();
foreach ($diseaseCodes as $dc) {
$diseaseArray[]=$dc['ICD10'];
}
$criteria=new CDbCriteria;
$criteria->compare('ICD10',$diseaseArray,true);
$dataProvider = new CActiveDataProvider(get_class($model),array('criteria'=>$criteria));
$dataProvider->criteria->mergeWith($model->search()->criteria);
$this->render('view',array(
'model'=>$this->loadModel($id),
'dataProvider'=> $dataProvider,
));
You are going to want to use CArrayDataProvider. This is less than ideal.. and you will have to implement all your sorting and pagination on your own
You are much better off using a model and implementing similar to
public function queryResultSearch($diseaseArray)
{
$criteria=new CDbCriteria;
$criteria->compare('ICD10',$diseaseArray,true);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
To make your first model I would personally use Gii.
I have some sharded tables in a MySQL environment. I use Yii so I wanted to add some support for the sharding. I have managed to make a custom CActiveRecord class that is mother to all the sharded models:
class ShardedActiveRecord extends CActiveRecord{
private $_shard;
public function tableName(){
return get_class($this) . $this->getShard();
}
public function setShard($shard) {
$this->_shard = $shard;
call_user_func(array(get_class($this), 'model'))->_shard = $shard;
$this->refreshMetaData();
return $this;
}
public function getShard() {
if($this->_shard === null){
$this->setShard("");
}
return $this->_shard;
}
public function createShardedTable($shard){
$connection=Yii::app()->db;
$command = $connection->createCommand("CREATE TABLE IF NOT EXISTS ".get_class($this).$shard." LIKE ".get_class($this));
$command->execute();
}
}
Everything works fine on insert, but when I need to retrieve some data, I don't know how to proceed. I would like in my model to have a parameter that would be the sharded tables to unite. Here is an example of what I would like to be possible:
MyModel::model()->shard("01,"02",03")->findAll();
And eventually that to return the last 3 tables:
$data = new CActiveDataProvider("MyModel")
The sql for retrieving the data shoold look like this if I want to select the first 3 tables:
SELECT * FROM stats01
UNION SELECT * FROM stats02
UNION SELECT * FROM stats03
I have tried to redefine the fetchData() function but it didn't work, the redefined function was not altering the dataset... I need to have all the datas as one big chunk as if it was a single table.
Thank you!
Update:
I found a solution to the problem. I created a function in my ShardedActiveRecord class that generates a custom CActiveRecord and returns it. Here is how it looks:
public function getMergedDataProvider($back){
$modelArray = array();
$model = array();
$now = date("Ym");
for($i=0; $i<$back; $i++){
array_push($modelArray, $this->model()->setShard(date("Ym", strtotime("-".$i." month", strtotime($now))))->findAll());
}
foreach($modelArray as $ma){
$model = array_merge($model, $ma);
}
$dataProvider = new CActiveDataProvider(get_class($this), array(
'data'=>$model,
));
return $dataProvider;
}
I added a parameter that will allow me to go get the last 3 table shards (they are relative to a date).
In an action that requires the use of a sharded model, I can call the function as so, and use it as a normal CActiveDataProvider:
$dataProvider = MyModel::model()->getMergedDataProvider(3);
I hope this will help someone!
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.
I am having very difficult time to select usernames of all posts in the blog demo given in Yii..
author is relation of post class with user...
$criteria = new CDbCriteria;
$criteria->with='author';
$criteria->select='author.username';
$dataProvider=new CActiveDataProvider('Post', array(
'criteria' => $criteria,
));
var_dump($dataProvider->getData());
Error:
Active record "Post" is trying to select an invalid column "author.username". Note, the column must exist in the table or be an expression with alias.
what with does is eager loading..that means the relation's data
will also loaded from database alongwith, and when when u'll call the
relation, there won't be a actual query..
What select does is it selects it from database and maps it to model
variable..
Now in your case what is happening is you're trying to write some relation's column in select, which will be there in select even without writing it, but as there is no corresponding variable to map this value yii is throwing an error..
So first thing if you need to username of auther in response, you can get it by relation calling, which won't be a database call, and u dont need to write a select..
And if u want to call the username as a part of the post model only u have got to declare it as a property in model, and then specify alias in select..
$criteria = new CDbCriteria;
$criteria->with='author';
$criteria->select='author.username as auther_username';
$dataProvider=new CActiveDataProvider('Post', array(
'criteria' => $criteria,
));
var_dump($dataProvider->getData());
and in your Post model declare..
public $auther_username;
Now it won't throw error, and you can access the username by both ways..$post->auther_username, and $post->auther->username
Try this:
$criteria = new CDbCriteria;
$criteria->with=array('author'=>array('select'=>'username'));
// you can still select Post table columns here
$criteria->select='post_content';
$dataProvider=new CActiveDataProvider('Post', array(
'criteria' => $criteria,
));
var_dump($dataProvider->getData());
Try this:
$criteria = new CDbCriteria;
$criteria->with=array('author'=>array('select'=>'username'));
$dataProvider=new CActiveDataProvider('Post', array(
'criteria' => $criteria,
));
var_dump($dataProvider->getData());
Source: CActiveRecord.with
In order to customize the options on the fly, we should pass an array
parameter to the with() method. The array keys are relation names, and
the array values are the corresponding query options.