CActiveDataProvider using relations accessing foreign key data from another table - php

I have a relatively simple setup using Yii application but I'm struggling to access data across tables in the following scenario:
Two tables:
payments_info (id, data): where id is the primary auto increment key, model class name PaymentInfo
payments (id, payment_id, other_columns): here id is again a primary key and payment_id is foreign key pointing to one of the records in the payment_info table, model class name Payment
In the relations array inside the Payment model class I have the following:
'payment_id'=>array(self::BELONGS_TO, 'PaymentInfo', 'id'),
I haven't added anything in the PaymentInfo model's relations as technically it doesn't know anything about the other table.
I have a controller creating a CActiveDataProvider from the Payments model and showing all the records. In it, I'd like to have a field which would be showing the 'data' column from the PaymentInfo model but I have no idea how to get there.
In the cdbcriteria in the controller, used to create the data provider I tried using:
$criteria->with = array('payment_id');
And then in the view, inside the columns variable of the CGridView which displays the data provider I added:
array(
'name'=>'payment_id',
'visible'=>true,
'value'=> $data->payment_id->data,
),
I also tried different combinations of adding ' or " around the $data variable but with no success so far. I manage to get the correct payment_id displayed but I can't figure out how to display the 'data' value from the other table. Any tips appreciated!

you can use below method to Get Data From Another Relational Table in YII
Payment Model In Relation Function
public function relations()
{
return array(
'pinfos' => array(self::BELONGS_TO, 'PaymentInfo', 'payment_id'),
);
}
PaymentInfo Model In Relation Function
public function relations()
{
return array(
'payments' => array(self::HAS_MANY, 'Payment', 'payment_id'),
);
}
And In zii.widgets.grid.CGridView put this
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'post-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'pinfos.data',
/* other fields goes here*/
array(
'class'=>'CButtonColumn',
),
),
)); ?>
No need to do anything else.

Related

Display attribute using via() or viaTable() when there is multiple relations- YII2

I have for models say, supplier-machine, supplier, supplier-to-part and parts.
This is how these tables are related to each other.
In Supplier model the relation is defined as below to retrieve part_name of Part table keeping supplier-to-part as junction table.
public function getSupplierToParts()
{
return $this->hasMany(SupplierToPart::className(), ['supplier_id' => 'id']);
}
public function getParts()
{
return $this->hasMany(Part::className(), ['id' => 'part_id'])->viaTable('supplier_to_part', ['supplier_id' => 'id']);
}
In Detail view I use implode to display part_name
[
'attribute'=>'Nature of business',
'value' => implode(\yii\helpers\ArrayHelper::map($model->parts, 'id', 'part_name')),
],
My question is how do I display part_name in supplier-machine model instead of supplier model?? In this case I think supplier and supplier-to-part becomes like a two junction tables. How do I solve this?
You can access the parts through the supplier relation via $model->supplier->parts, assuming your relation to Supplier in your SupplierMachine model is supplier. You still have to account for the multiple parts though:
'value' => implode(",",
\yii\helpers\ArrayHelper::map($model->supplier->parts, 'id', 'part_name')
),

Yii Get Related Data into a GridView

My view lets the user see a person's details, including the families they are in. This is done in the DB with a Person table, Family table, and a familyMembership link table between the two.
The relationship in the CActiveRecord model for Person is as such:
'families' => array(self::MANY_MANY, 'Family', 'familymembership(Person, Family)'),
In my person controller, I am wanting to pass a variable into the view that has the related data in a way that TbGridView (CGridView but Bootstrap) will accept (a data provider). The controller calls the person model's getFamilies() function:
public function getFamilies() {
// returns an array of Family objects related to this Person model
$familiesArray = $this->getRelated('families');
// puts this array into a CArrayDataProvider
$families = new CArrayDataProvider($familiesArray);
return $families;
}
The return value goes back to the controller, which is then handed through in the renderPartial() call. The view has a TbGridView widget initialisation like this:
$this->widget('bootstrap.widgets.TbGridView',
array(
//the CArrayDataProvider from the model function
'dataProvider' => $families,
'columns' => array(
array(
'name' => 'Family Name',
// Example field from Family model
'value' => '$data->familyName'
)
)
));
However, in doing this I am getting the following error:
'Property "Family.id" is not defined. (D:\wamp\www\yii\framework\base\CComponent.php:130)'
The Family model does not have an id property, and I don't understand why the widget is looking for such.
What's going wrong here?
Thanks.
Quoting the doc from Yii Class Reference, you have to provide a keyField:
Elements in the raw data array may be either objects (e.g. model objects) or associative arrays (e.g. query results of DAO). Make sure to set the keyField property to the name of the field that uniquely identifies a data record or false if you do not have such a field.
By default keyField will be set to "id", so you need to overwrite it with your Family model primary key :
<?php
$familyDataProvider = new CArrayDataProvider($rawData, array(
'keyField' => 'yourPkNameHere',
));

Change yii crud generated page

I have 2 models: taxiDrivers and taxiOrders. In taxiOrders I have a field driver_id, but instead of just an id, I want to output driver's name (which is located in taxiDrivers). Both models are generated via gii, and crud tools are also generated. The page which is needed to be changed is taxiOrders/admin (view: admin.php, models: TaxiOrders.php, TaxiDrivers.php and respective controllers)
2 DCoder: Thanks dude! but one more query I have and hope you can clearify: I have a standart generated admin.php view page with following code:
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'taxi-orders-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'id',
'uid',
'did',
'type',
'notes',
//'or_lat',
//'or_lng',
//'des_lat',
//'des_lng',
'cost',
'rating',
'date',
'time',
'status',
array(
'class'=>'CButtonColumn',
),
),
)); ?>
and below code is for controller:public function actionAdmin()
{
$model=new TaxiOrders('search');
$model->unsetAttributes(); // clear any default values
if(isset($_GET['TaxiOrders']))
$model->attributes=$_GET['TaxiOrders'];
$this->render('admin',array(
'model'=>$model,
));
}
So can u please show me where I could put your manipulations.
The Yii way would be to define a Relation between the two model classes (and enforce it with a Foreign Key). Then Yii would know how the two classes are related and you would have an easier time loading the related data.
In the TaxiOrders model class, a relation would look like this:
/**
* #property TaxiDrivers $Driver
*/
class TaxiOrders extends CActiveRecord {
// ...
public function relations() {
return array(
'Driver' => array(self::BELONGS_TO, 'TaxiDrivers', 'driver_id'),
);
}
// ...
}
In the controller, when you load the order data, you can prefetch the associated driver data like this:
public function actionOrderInfo($orderID) {
$order = TaxiOrders::model()->with('Driver')->findByPk($orderID);
// render it
}
with('Driver') will make sure that each returned order has its driver info already loaded, no matter if you need to find one record or a lot of records. It is a lot more efficient than trying to load that related data yourself.
In the view, you can output the driver info like this:
echo CHtml::encode($order->Driver->Name);
Unless you have a foreign key to ensure data integrity, it is possible that the Driver has been deleted without clearing his existing orders... In that case $order->Driver will be NULL and the line above will cause an error. Figuring out how to avoid it should be obvious.
In TaxiOrders admin use this to display driver name
TaxiDrivers::getName($data->driver_id);
Now in the TaxiDrivers Model write a function with sql query to get the driver name like this...
public function getName($getid) {
$sql = "SELECT name from `taxi_drivers` where `driver_id`='$getid'";
$command=yii::app()->db->createCommand($sql);
$rs = $command->queryScalar();
return $rs;
}
I Hope it will help you..
I always prefer Query Builder approach instead of Active record approach in joined scenarios. Like that
First Get Data through Query Builder
public function getTaxtOrder($id) {
$select = Yii::app()->db->createCommand()
->select('to.*, td.name as driver_name')
->from('TaxiOrders to')
->join('TaxiDriver td', 'td.id = to.driver_id')
->where('to.id = :id', array(':id' => $id));
return $select->queryRow();
}
Then Pass through controller
$data = TaxiOrders::model()->getTaxtOrder($id);
$this->render('view',array(
'data' => $data
));
Last use this into views
$this->widget('zii.widgets.CDetailView', array(
'data'=>$data,
'attributes'=>array(
array('label' => 'Order No', 'value' =>$model['order_no']),
array('label' => 'Driver Name', 'value' =>$model['driver_name']),
array('label' => 'Date', 'value' =>$model['order_date']),
),
));
This approach easily and flexibly work with multiple joined tables than Active Record Approach.

Relational Databases in Yii

So I've tried this: http://www.yiiframework.com/wiki/285/accessing-data-in-a-join-table-with-the-related-models
Basically I have a table called User which relates to ToolAccess; related via a primary key on User and a field for userID on ToolAccess. Now tool access relates to the table Tool which contains a ToolID. Now this doesn't work in Yii, I can't seem to get the toolName field off of the tool table using Yii. Any ideas on how to do this on a Active Record?
I'm using giix if that matters.
Relations code:
public function relations() {
return array(
'usergalleries' => array(self::HAS_MANY, 'Usergallery', 'userid'),
'userinroles' => array(self::HAS_MANY, 'Userinroles', 'userid'),
'userfailedlogin' => array(self::HAS_MANY, 'Userfailedlogin','userid'),
// table name, relation, class name, relation key
'toolaccess' =>array(self::HAS_MANY, 'Toolaccess','userid'),
'tool' =>array(self::HAS_MANY, 'Tool','toolid')
);
}
I'm assuming your schema looks something like this:
User table tool_access table Tool table
id | other columns userid | toolid id | name | other columns
In this case, the User model should have a relation like this (note that the tools will be ordered by name in this case):
public function relations() {
return array(
// other relations here...
'tools' =>array(self::MANY_MANY, 'Tool', 'tool_access(userid,toolid)',
'order' => 'tools.name',
),
);
}
and the code to read the tools should look like this:
$user = User::model()->with('tools')->findByPk($id);
foreach($user->tools as $tool) {
echo $tool->name;
}
I used eager loading of the tools here mostly because of personal preference, using lazy loading should work just as well in this case. But eager loading should be preferred whenever you're processing multiple User records at once.
So if I have understood it properly, user and tool are related in a many-to-many relationship by their primary keys.
So you should define this relationship in the User model like:
'tools' => array(self::MANY_MANY, 'Tool', 'tool_access(userid, toolid)', 'index' => 'id'),
This way you can access the name of the tool after getting the user model
$user = User::model->findByPk($id);
$tools = $user->tools;
foreach ($tools as $tool)
{
echo $tool->name;
}
I hope it works for you.

Yii framework: Using data from related Active Record models for searching

Yii 1.1 application development Cookbook explain a method for using data from related Active Record Models for searching the related models as well. This method is explained in page number 193 and 194. i have tried to integrate this method in to my application but it does not work. could anybody explain me whether this feature is still available in Yii framework version 1.1.8
At this location also i could find comments for searching data form related active record models. But it also does not work.
http://www.yiiframework.com/doc/api/1.1/CDbCriteria
I have order table and user table
Order table and User table has One to many Relation.
User has many orders and order has exactly one user.
So , i am editing following CDbCriterial to include user tables name and email field in to Order tables search entries.
Order table has following relations
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(
'comments' => array(self::HAS_MANY, 'Comment', 'order_id'),
'user' => array(self::BELONGS_TO, 'User', 'user_id'),
'orderstatus' => array(self::BELONGS_TO, 'Orderstatus', 'orderstatus_id'),
'commentCount' => array(self::STAT, 'Comment' , 'order_id')
);
}
This is the search/filter conditions with user table's name filed included
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('order_create_date',$this->order_create_date,true);
$criteria->compare('price',$this->price,true);
$criteria->compare('bank_account_number',$this->bank_account_number,true);
$criteria->compare('hardwaredetail_Id',$this->hardwaredetail_Id);
$criteria->compare('user_id',$this->user_id);
$criteria->compare('order_update_date',$this->order_update_date,true);
$criteria->compare('is_received',$this->is_received);
$criteria->compare('order_received_date',$this->order_received_date,true);
$criteria->compare('is_notify_by_email',$this->is_notify_by_email);
$criteria->compare('warehouse_offered_price',$this->warehouse_offered_price,true);
$criteria->compare('warehouse_offered_price_date',$this->warehouse_offered_price_date,true);
$criteria->compare('orderstatus_id',$this->orderstatus_id);
$criteria->together = true;
$criteria->with = array('user');
$criteria->compare('user.name',$this->user,true);
//$criteria->compare('user.name',$this->user->name);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
and Order admin page is edited to display the name filed as follows
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'order-grid',
'dataProvider'=>$model->search(),
//'filter'=>$model,
'columns'=>array(
'id',
'order_create_date',
'price',
'bank_account_number',
array(
'name'=>'user',
'value'=>'$data->user->name'
),
),
));
Error message returned
After solving the id column ambiguity problem by applying the solution that thaddeusmt gave i have faced with the following error message.
Thanks in advance for any help
I could find the answer. This is all i did.
Following are the two tables i have. Order and User
I have Order/Admin page, Default generated code provide searching order table data only. i want to relate user tables name field in the search criteria.
This is the initial look of the search page.
I want to integrated user name filed from other table in to this search. then final look will be as follows.
so these are the steps i did.
first in the Order model i have following relations
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(
'user' => array(self::BELONGS_TO, 'User', 'user_id'),
);
}
This is generated by Yii framework , i did nothing :)
Then , i changed the search method as follows
public function search()
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria=new CDbCriteria;
$criteria->compare('t.order_create_date',$this->order_create_date,true);
$criteria->compare('t.price',$this->price,true);
$criteria->compare('t.bank_account_number',$this->bank_account_number,true);
$criteria->compare('t.hardwaredetail_Id',$this->hardwaredetail_Id);
//$criteria->compare('user_id',$this->user_id);
$criteria->compare('t.order_update_date',$this->order_update_date,true);
$criteria->compare('t.is_received',$this->is_received);
$criteria->compare('t.order_received_date',$this->order_received_date,true);
$criteria->compare('t.is_notify_by_email',$this->is_notify_by_email);
$criteria->compare('t.warehouse_offered_price',$this->warehouse_offered_price,true);
$criteria->compare('t.warehouse_offered_price_date',$this->warehouse_offered_price_date,true);
$criteria->compare('t.orderstatus_id',$this->orderstatus_id);
$criteria->together = true;
$criteria->compare('t.id',$this->id,true);
$criteria->with = array('user');
$criteria->compare('name',$this->user,true,"OR");
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
it is important to put t in-front of the t if your Order table primary key field if both have save name. in my case it is id and id, so i had to put t.
Other thing is the order of the elements
$criterial->togeter = true; should come before the relational elements.
then u updated to rules method in Order table. i added user filed and name filed to safe attributes.
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
//array(' orderstatus_id', 'required'),
array('hardwaredetail_Id, user_id, is_received, is_notify_by_email, orderstatus_id', 'numerical', 'integerOnly'=>true),
array('price, warehouse_offered_price', 'length', 'max'=>10),
array('bank_account_number', 'length', 'max'=>100),
array('order_create_date, order_update_date, order_received_date, warehouse_offered_price_date, user,name', 'safe'),
// The following rule is used by search().
// Please remove those attributes that should not be searched.
array('id, order_create_date, price, bank_account_number, hardwaredetail_Id, user_id, order_update_date, is_received, order_received_date, is_notify_by_email, warehouse_offered_price, warehouse_offered_price_date, orderstatus_id', 'safe', 'on'=>'search'),
);
}
Finally update your UI code.
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'order-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'id',
'order_create_date',
'price',
'bank_account_number',
array(
'name'=>'user',
'value'=>'$data->user->name'
)
)); ?>
i updated the order admin with the
array(
'name'=>'user',
'value'=>'$data->user->name'
)
That is what i did and it worked for me. ask me if you need any help. Thanks every one looking in to this issue.
It looks like both the user and order have columns named id. And your criteria uses them both in the WHERE clause, which is giving the "ambiguous" mySql error message.
When using with criteria (which does a SQL JOIN of the tables), if two of your tables have columns with the same name you need to use the mySql "dot" prefix on conditions, like so:
$criteria->compare('t.id',$this->id); // the default table prefix in Yii is "t"
When I JOIN a table using $criteria->with I prefix all of the column names (in my compare and condition criteria, etc)., like so:
$criteria->compare('t.id',$this->id); // t
$criteria->compare('t.order_create_date',$this->order_create_date,true); // t
$criteria->with = array('user');
$criteria->compare('user.name',$this->user_id,true); // user (the filter will set user_id)
Your gridview will need to look like this:
array(
'name'=>'user_id',
'header'=>'User',
'sortable'=>false,
'value'=>'$data->user->name'
),
Also, I think there is a larger problem, as you point out with your edit:
Your search function is set up like this: user.name',$this->user - but $this->user is going to return the current user object via the relation, not the search criteria. The column filter will set the user_id, property.
EDIT: Nope, you can $this->user as your column name so long as you set it as a safe attribute.
The way I am getting around this is shown in more detail here:
Search in BELONGS_TO model column with CGridView, Yii
Sorting will not work with that though - just the filtering.
Here is good post in the Yii forum that might give you more clues, too:
http://www.yiiframework.com/forum/index.php?/topic/9083-search-filter-of-a-relations-field-through-cgridview/
Sadly, sorting and filter CGridViews on relations is not really default functionality. YOu can easily display related info with a column name like user.name, but it won't sort or filter. Good luck!

Categories