Yii CSqlDataProvider with relations - php

I have an complex query with joins and conditions
and I get data with CSqlDataProvider
But I also need to join relational table records.
Lets say, we have table A (products) and table B (product_modifications)
I need to list products along with their modifications..
I get data from table A, and i need also to get some records from table B
for each record in table A, query should get an array from table B
Basic code:
class Product extends CActiveRecord
{
//some code
public function relations()
{
return array(
'modifications' => array(self::HAS_MANY, 'Modification', 'modification_product_id'),
);
}
//some code
}
my query
$sql = Yii::app()->db->createCommand();
...//different joins and conditions
$this->dataProvider = new CSqlDataProvider($sql->text, array(
'keyField' => 'product_id',
'pagination' => array('pageSize' => 20),
));
How can i join records from table B (product_modifications)?
In CActiveDataProvider its like:
$this->dataProvider = new CActiveDataProvider ('products', array(
'pagination' => array('pageSize' => 20),
'criteria' => array(
'with' => array(
'modifications' => array('condition' => 'some condition',),
),
),
));
But i dont know how to do this with CSqlDataProvider
UPD:
solved by converting query to corresponding CActiveDataProvider query

You can't use AR-relation with sql commands, because it is absolutely different tools for work with Db. In ActiveRecord you are using relation, in Sql commands you are using sql-joins. That's mean you should add you condition there with join:
$sql = Yii::app()->db->createCommand();
...//different joins and conditions + your condition

Related

How to join 2 model with relation when using Yii CActiveDataProvider

I want to export data to excel using 2 models. HasilPutusan model and DetilSaksi model.
When I used the CActiveDataProvider with Model HasilPutusan, I managed to get all the data together with the data relationships that are interrelated with this HasilPutusan models.
And how do I combine the data both by using Model DetilSaksi?
My controller :
public function actionCetakjalanperistiwa()
{
if(isset($_POST['PrintJaper'])){
$data = $_POST['id_kecelakaan'];
if($data==''){
return false;
}else{
$dataProvider=new CActiveDataProvider('HasilPutusan', array(
'criteria'=>array(
'condition'=>'id_kecelakaan=:id',
'params'=>array(':id'=>$data),
),
));
}
$this->widget('ext.EExcelView', array(
'grid_mode'=>'export',
'title' => 'Data Jalan Peristiwa',
'dataProvider' => $dataProvider,
'exportType'=>'Excel2007',
'columns'=>array(
'id_hasil_putusan',
'id_detil',
'id_kapal',
'idKapal.nama_kapal',
'idKapal.tahun_pembuatan',
'idKapal.konstruksi',
'idKapal.isi_kotor',
'idKapal.tenaga_penggerak_utama',
'idKapal.pemilik',
'idKapal.nakhoda',
'idKapal.awak_kapal',
'idKapal.surat_kapal',
),
));
}else{
$this->render('_japer');
}
}
In my tbl_hasil_putusan table, i have a id_detil field which I will use to re-do the query to the second table using the DetilSaksi model.
My database :
How do I combine the two tables that can be joined so that I can call related (blue arrow) data in the second table?
Thanks
You can use join in the criteria array. Something like this:
$dataProvider=new CActiveDataProvider('HasilPutusan', array(
'criteria'=>array(
'condition'=>'id_kecelakaan=:id',
'select' => 't.*, ds.*'
'join' => 'LEFT JOIN tbl_detil_saksi ds on ds.id_detil = t.id_detil',
'params'=>array(
':id'=>$data),
),
));

CakePHP 2.x retrieve/query using data in HABTM join table

I have 2 tables joined by a HABTM relationship. portfolios and assets. The join table is portfolios_assets.
I wish to have an extra field in the join table, rebalance_date such that I can query the assets in a portfolio for a given date. How would I construct a find such that I can determine the most recent date and only return the assets for that date.
So, in my Portfolio model I might have:
$params = array(
'conditions' => array(
'Portfolio.id' => 5,
'?.rebalance_date' => '2013-11-01' //no model name for this field as it's in the join table
),
'order' => array(...)
);
$result = $this->find('all', $params);
In the above example, I just keyed in a date. I'm not sure how I would retrieve the latest date without writing a raw query in Cake. (I could do SELECT rebalance_date FROM portfolios_assets ORDER BY rebalance_date DESC LIMIT 1; but this is not following Cake's convention)
You need to use the hasMany through model: http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasmany-through-the-join-model
You would need to create another model eg PortfolioAssests and the table would need to be portfolio_assets not portfolios_assets.
Then you should be able to use:
$assets = $this->Assets->find('all', array(
'conditions' => array(
'Portfolio.id' => 5,
'PortfolioAsset.rebalance_date' => '2013-11-01'
)
));

cakephp - getting joins correct

I'm a bit of a newbie and I'm having trouble getting my cakephp query correct.
I have a users table and users hasmany rotas. The rotas table looks like
monday_am
monday_pm
tuesday_am
etc
and if the user is due to be in on monday_pm for example, then that field will have 1. Otherwise 0.
I want to get all the users who belong to a particular room, have deregistered=0 and - and this is where I'm having the problem - who have a rota where (for example) tuesday_am = 1
So I have the code:
$options['conditions'] = array('User.room_id'=>$room_id, 'User.deregistered'=>0, 'User.request'=>0);
$options['joins'] = array(
array('table' => 'rotas',
'alias' => 'rota',
'type' => 'INNER',
'conditions' => array('rota.'.$todaysDay.'_'.$amPm => 1)
)
);
$usersToday = $this->User->find('all', $options);
If I don't try the join then I get a nice small list of users like I expect. If I try the join I get 100s of results which are mostly duplicates!
How can I get just one record of a user who has the required conditions in the Users table as well as the Rotas table, and how come doing the join as I have causes so many results?!?
thanks
Assuming you have a column in your rota table called user_id, you can make your Rota model like this:
class Rota extends Model
{
public $belongsTo = array('User');
}
Then in your controller, you can query the Rotas and get the user from it.
$users = $this->Rota->find('all', array(
'fields' => array('DISTINCT (User.id)' /* add more fields for selection */ ),
'conditions' => array(
'User.room_id'=>$room_id,
'User.deregistered'=>0,
'User.request'=>0,
'Rota.'.$todaysDay.'_'.$amPm => 1
)
));

how to set a relation with empty value?

I have 2 tables namely Equipment and Supply.
I have used array_merge in yii php to serve as union for two different tables for the purpose of diplaying them in a single grid.
Everything works fine with the fields that is common with the two tables. The problem is when I try to display a field that is only existing in one of these two tables. It says Property "Supply.equipType" is not defined" because only equipment has the equipType relation.
in my gridView:
array(
'name'=>'equipment_type',
'value'=>'$data->equipType->name',
),
in my controller where I did the merging:
$prov1 = new CActiveDataProvider('BaseEiEquipItem', array(
'criteria' => array(
'condition' => 'id>0'
)));
$prov2 = new CActiveDataProvider('BaseSiReceivedItem', array(
'criteria' => array(
'condition' => 'id>0'
)));
$records=array_merge($prov1->data , $prov2->data);
$provAll = new CArrayDataProvider($records,
array(
'sort' => array( //optional and sortring
'keyField'=>false,
'attributes' => array(
'id', 'description',),
),
'pagination' => array('pageSize' => 10) //optional add a pagination
)
);
$this->render('create',array(
'model'=>$model,
'searchModel'=>$searchModel,
'modelGrid'=>$modelGrid,
'provAll' => $provAll,
));
in my equipment model :
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(
'equipType' => array(self::BELONGS_TO, 'BaseRefEquipmentType', 'equipment_type'),
);
}
Any idea on how to solve this? Is there any way to fake a relation or something?
thanks in advance
Just add condition to check if equipType is there:
'value'=>'isset($data->equipType) ? $data->equipType->name : ""'
Using sql to merge results would be better, though.
Can't you just use plain SQL union?
If Equipment has 2 fields (a, b) and Supply has 2 fields (b, c), you can do union like this:
SELECT a, b, null FROM Equipment
UNION ALL
SELECT null, b, c FROM Supply
This problem shows, that it's probably not a good idea to mix different model types in one gridview. If you have a model that has no equipType then it's pretty obvious, that you can't show this column in a gridview. So what would you expect?
As a (dirty) workaround you can add all missing columns as pseudo attributes to the models where they are missing:
public $equipType;

Getting dependent records in $dataProvider variable

i have two tables tbl_entries and tbl_votings
tbl_entries -> id, othercolums
tbl_votings -> id, entry_id, othercolumns
i want to show data in zii.widgets.CListView from the tbl_entries if users have voted for the entries.
i am able to run below sql query successfully.
select * from tbl_entries where id in (select tbl_entries.id from tbl_votings where entry_id = tbl_entries.id )
how can i do in YII style so that i can show result in CListView?
in Entries model add relation:
...
'votes' => array(self::HAS_MANY, 'Votings', 'entry_id'),
...
Then search via AR:
$records = Entries::model()->with('votes')->findAll();
Hope this helps.
Updated
Ok, i missed word "if" in your post, and thought you need entries ONLY if users voted for it. Read update below for right code. I leave you the first part just in case you need it in the future. INNER JOIN will take entries ONLY if that entries have at least 1 vote.
------------------ FIRST PART (wrong) --------------------
Declare a relation inside your Entries model:
public function relations() {
return array(
'votes' => array(self::HAS_MANY, 'Votings', 'entry_id'),
);
}
And when you create your data provider do it like this:
$dataProvider = new CActiveDataProvider('Entries',
array(
'criteria' => array(
'with' => array(
'votes'=>array(
'joinType' => 'INNER JOIN'
)
)
)
)
);
This will not create you the SQL you have written above, but this will do it the right way.
The SQL will look similar to this:
select * from tbl_entries t INNER JOIN tbl_votings v ON t.id = v.entry_id
----------------- SECOND PART (right) ------------------
Update
Ok, so if you simply need to get entries and votes for them you do the same relation declaration from first part:
public function relations() {
return array(
'votes' => array(self::HAS_MANY, 'Votings', 'entry_id'),
);
}
And you create your data provider like this:
$dataProvider = new CActiveDataProvider('Entries',
array(
'criteria' => array(
'with' => array('votes')
)
)
);

Categories