Yii join two models - php

I've got two mysql tables:
1. prices
2. details
Table Prices
ID | DATE | CODE | PRICE
1 |2014-01-01 | AAA1 | 90.123
2 |2014-01-01 | AAB1 | 50.113
3 |2014-01-01 | AAC1 | 48.621
4 |2014-01-02 | AAA1 | 91.123
5 |2014-01-02 | AAB1 | 51.113
6 |2014-01-02 | AAC1 | 41.621
Table Details
CODE | NAME | DESCRIPTION
AAA1 | andria | A very good...
AAB1 | anasta | A very good...
AAC1 | simple | A very good...
Models:
Prices
public function relations(){
return array(
'code' => array(self::BELONGS_TO, 'details', 'code'),
);
}
Details
public function relations(){
return array(
'code' => array(self::HAS_MANY, 'prices', 'code'),
);
}
Here's the SQL code I want to execute:
SELECT * FROM prices a
JOIN (SELECT * FROM details) b
WHERE a.DATE=(SELECT MAX(DATE) FROM prices) AND a.code = b.code
My controllers:
$prices=new CActiveDataProvider('prices', array(
'criteria'=>new CDbCriteria (array(
'select'=>'code,date,close',
'condition'=>'date=(SELECT MAX(date) FROM prices)'
)),
));
$this->render('index',array(
'prices'=>$prices
));
index.php:
<?php
$this->widget('bootstrap.widgets.TbGridView', array(
'dataProvider'=>$prices,
'template'=>"{items}",
'enablePagination' => false,
'columns'=>array(
array('name'=>'code', 'header'=>'Code'),
array('name'=>'name', 'header'=>'Name'),
array('name'=>'close', 'header'=>'Close'),
),
)); ?>
With this controller commands, I can get the data from table prices, but I can't get the data from table details.

Take a look at the with part of the criteria: http://www.yiiframework.com/doc/api/1.1/CDbCriteria#with-detail
I think your code should look something like this (untested):
$prices=new CActiveDataProvider('prices', array(
'criteria'=>new CDbCriteria (array(
'select'=>'code,date,close',
'with' => 'details',
'condition'=>'date=(SELECT MAX(date) FROM prices)'
)),
));

you already have reliation in Model. So do query like this in your controller
$prices=new CActiveDataProvider('prices', array(
'criteria'=>new CDbCriteria (array(
'condition'=>'date=(SELECT MAX(date) FROM prices)'
)),
));
In View just use
$data->code->name
$data->code->description

Here's what I change:
index.php
<?php
$this->widget('bootstrap.widgets.TbGridView', array(
'dataProvider'=>$prices,
'template'=>"{items}",
'enablePagination' => false,
'columns'=>array(
array('name'=>'code', 'header'=>'Code'),
array('name'=>'name', 'header'=>'Name', 'value'=>'$data->details->name'),
array('name'=>'close', 'header'=>'Close'),
),
)); ?>
Models Prices
public function relations(){
return array(
'details' => array(self::BELONGS_TO, 'Stocks_details', 'code'),
);
}

Related

CakePHP contain() method sees belongsToMany over belongsTo of same table

So I have the following (simplified) model
+------------------+
| project_statuses |
+------------------+
+---------------------+ +----| id |
| projects | | | name |
+---------------------+ | +------------------+
| id | | +---------+
| name | | | clients |
| project_statuses_id |----+ +---------+
| client_id |---------| id |
+---------------------+ | name |
| +---------+
+------------------+ |
| clients_projects |------+
+------------------+
| id |
| client_id |
| project_id |
+------------------+
where a project belongs to many clients and a client can have many projects, but only one client (Projects.client_id) can take the responsibility for a project. The project status is here just for comparision.
So the associations in my ProjectsTable.php, ClientsTabe.php and ProjectStatusesTable.php look like this
// In ProjectsTable.php
$this->belongsTo('ProjectStatuses', [
'foreignKey' => 'project_status_id',
'joinType' => 'INNER'
]);
$this->belongsTo('Clients', [
'foreignKey' => 'client_id',
'joinType' => 'INNER'
]);
$this->belongsToMany('Clients', [
'foreignKey' => 'project_id',
'targetForeignKey' => 'client_id',
'joinTable' => 'clients_projects'
]);
// In ClientsTabe.php
$this->hasMany('Projects', [
'foreignKey' => 'client_id'
]);
$this->belongsToMany('Projects', [
'foreignKey' => 'client_id',
'targetForeignKey' => 'project_id',
'joinTable' => 'clients_projects'
]);
// In ProjectStatusesTable.php
$this->hasMany('Projects', [
'foreignKey' => 'project_status_id'
]);
Now in my projects/index page I'd like to have a table showing the project id, name, status and responsible. So I thought in something like this
// In ProjectsController.php
$this->Projects->find()->select(['Projects.id','Projects.name'])
->contain(['ProjectStatuses' => [
'fields' => [
'ProjectStatuses.name',
]]])
->contain(['Clients' => [
'fields' => [
'Clients.name',
]]]);
But only ProjectStatuses.name is fetched, for Clients.name it throws the error You are required to select the "ClientsProjects.project_id" field(s) telling me that is looking through the belongsToMany association rather than the belongsTo one.
In fact, if I just write ->contain('Clients') instead of specifying the Clients.name field it send the following queries
SELECT
Projects.id AS `Projects__id`,
Projects.name AS `Projects__name`,
ProjectStatuses.name AS `ProjectStatuses__name`
FROM
projects Projects
INNER JOIN project_statuses ProjectStatuses ON ProjectStatuses.id = (Projects.project_status_id)
SELECT
ClientsProjects.id AS `Clients_CJoin__id`,
ClientsProjects.client_id AS `Clients_CJoin__client_id`,
ClientsProjects.project_id AS `Clients_CJoin__project_id`,
Clients.id AS `Clients__id`,
Clients.name AS `Clients__name`,
FROM
clients Clients
INNER JOIN clients_projects ClientsProjects ON Clients.id = (ClientsProjects.client_id)
WHERE
ClientsProjects.project_id in (4)
How can I tell the query object to get the Clients.name through Projects.client_id just as I get ProjectStatuses.name through Projects.project_statuses_id instead of passing by the ClientProjectstable?
In your Projects table, you have created two associations for Clients, and vice versa. That doesn't work (as The Highlander said, "There can be only one"), and is likely the source of all your problems.
You'll need to change one of those associations in each table. Maybe a Project should belongsToMany Clients but only belongTo one ResponsibleClient, and a Client should belongsToMany Projects and hasMany ReponsibleProjects?

cakephp 3 paginate sort custom field

i try to sort "hasMany" associate
this is my code :
Advisor Modal
$this->hasMany('AdvisorNotes', [
'foreignKey' => 'id',
'sort' => [
'created' => 'DESC',
],
'joinType' => 'LEFT',
]);
Advisor Controller
public function index() {
$this->paginate = [
'contain' => [
'Branch' => [ 'BranchCity', 'Bank' ],
'AdvisorLevel',
'AdvisorRelation',
'UsersAgent',
'AdvisorNotes'
],
'sortWhitelist' => [
'BranchCity.city_name',
'Branch.name',
'Advisor.name',
'AdvisorRelation.relation',
'AdvisorLevel.level',
'Advisor.telephone',
'UsersAgent.name',
'AdvisorNotes.created',
'Bank.name',
]
];
$advisors = $this->paginate( $this->Advisor );
AdvisorNotes table
id | advisor_id | agent_id | type_id | note | created
Advisor table
id | name | image | date_of_birth | important_date | branch_id | telephone | fax | cellphone | email | description | level_id | relation_id | agent_id | agent_admin_id | created
i try to make field in the Advisor index that show the last AdvisorNote and be able to sort it.
as you can see AdvisorNotes as advisor_id in table but Advisor dont have any id of the AdvisorNotes.
thanks.

Use a dropdown to filter results within my dataProvider in GridView widget (Yii 1.x)

Within my Yii 1.x application I am using the GridView widgets to display tabular data. I'd like to have a simple dropdown that will allow me to filter the results.
In this instance I have a set of 'Reward' data - each reward will either be 'Positive' OR 'Negative'. And I'd like to have a dropdown that will filter between the two.
Here is my code:
<?php $this->widget('bootstrap.widgets.TbGridView', array(
'dataProvider' => $model->search(true, $drill, isset($drillVal) ? $drillVal : false, isset($drillField) ? $drillField : false),
'filter' => $model,
'id' => get_class($model),
'type' => 'striped bordered condensed',
'template' => $model->getGridTemplate(),
'pagerCssClass' => 'pull-right pagination pagination-sm',
'columns' => array(
array(
'name' => 'reward_name',
'filter' => false
),
array(
'name' => 'reward_type',
'header' => 'Reward Type',
'filter' => false // need some code here for filtering
),
array(
'name' => 'totalPoints',
'header' => 'Total Points',
'filter' => false
),
),
));
For example this will look like the following....
+-----------------------------------------+
| Reward Name | Reward Type | Total Points|
+-----------------------------------------+
| Good Work | Positive | 500 |
| Great Effort| Positive | 150 |
| Naughty | Negative | -75 |
+-----------------------------------------+
I'd like the the filtering to allow me to show ONLY 'negatives' OR only 'positives'
Can anyone suggest how I best go about doing this?
Note - in my search() within the criteria element there is an IF statement as follows:
'IF(t.type="1","Deduction","Achievement") AS reward_type',
First, you need to have a dropdown list inside your view:
CHtml::dropDownList('rewardType', $rewardType, array(0 =>"-", 1=>"Positive", 2=>"Negative"), array(
'onchange' =>
"$.fn.yiiGridView.update('yourGridID(without # sign)',{ data:{rewardType: $(this).val() }})",
));
You can also use manual dropdown list(without yii) and send ajax request on onchange event and update gridView on success event of ajax request.
Now you need to add this code inside your $model->search() method(after initialize $criteria):
if(isset($_POST['rewardType']) && ($_POST['rewardType'] != 0))
{
Yii::app()->session['rewardType'] = $_POST['rewardType'];
$criteria->addCondition("t.reward_type = ".$_POST['rewardType']);
}
And the final work is to adding one line at the beginning of your view file:
<?php $rewardType= (isset(Yii::app()->session['rewardType'])) ? Yii::app()->session['rewardType'] : 0; ?>
I have implemented similar situation recently. I had a dropdown list for changing page size inside CGridView and use this solution. Hope my solution can help you too.

CakePHP - One HABTM With Two Foreign Keys

I've been trying to save two foreign keys to the same row of a join table with no success. My Client model has two HABTM association with both User and Group models, a user can have many clients and create clients under many different groups.
The UserClient join table looks like this:
+----+---------+----------+-----------+
| id | user_id | group_id | client_id |
+----+---------+----------+-----------+
| 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 2 |
| 3 | 3 | 4 | 3 |
+----+---------+---------+------------+
Client model:
public $hasAndBelongsToMany = array(
'User' => array(
'className' => 'User',
'joinTable' => 'UserClient',
'foreignKey' => 'client_id',
'associationForeignKey' => 'user_id'
),
'Group' => array(
'className' => 'Group',
'joinTable' => 'UserClient',
'foreignKey' => 'client_id',
'associationForeignKey' => 'group_id'
)
);
Client view:
<?php echo $this->Form->create('Client'); ?>
<fieldset>
<legend><?php echo __('Add Client'); ?></legend>
<?php
echo $this->Form->input('first_name');
echo $this->Form->input('last_name');
echo $this->Form->input('email');
echo $this->Form->input('phone');
echo $this->Form->input('Client', array('multiple'=> 'false', 'options' => $group_ids));
echo $this->Form->hidden('User', array('value' => $user_id));
?>
</fieldset>
<?php echo $this->Form->end(__('Submit')); ?>
This works to a point, however instead of saving user_id, group_id and client_id to the same row in UserClient table, it create two separate rows for each HABTM, user_id, client_id get saved to one row and group_id, client_id get saved to another.
Is there any way to define multiple associationForeignKey within the same HABTM association so user_id, group_id and client_id get saved to the same row when creating a new client?
When you have to store additional fields in a HABTM relation, it is sometimes easier to use a join model. In your case this would mean creating a model for your UserClient table and use hasMany relations instead of HABTM.
Then of course you'll have to manage the save manually, but you'll have much more control of what must be done. You can find a few words about this in the Cake documentation.

Yii Sortable Attributes

I have the following talbes but when I define firstname as sortable it is not working (not showing firstname as link where I can click and sort the List View). Despite that if I user username is working just fine.
| User
| - userid
| - username
| Profile
| - userid
| - firstname
| - lastname
I have in controller:
$criteria->with=array(
'profile',
);
$criteria->addCondition('status = 1 or status = 2 or status = 3');
if($search)
$criteria->addCondition("firstname = '{$search}'");
$dataProvider=new CActiveDataProvider('YumUser', array(
'criteria' => $criteria,
'pagination'=>array(
'pageSize'=>50,
)));
In view:
$this->widget('zii.widgets.CListView', array(
'dataProvider'=>$dataProvider,
'itemView'=>'_view',
'template' => '{summary} {sorter} {items} <div style="clear:both;"></div> {pager}',
'sortableAttributes'=>array(
'firstname',
),
));
This only works if the sort property of the dataProvider is set explicitly (in these cases, where you're sorting by an attribute that is in another model)
Try this
$dataProvider = new CActiveDataProvider('YumUser', array(
'criteria' => $criteria,
'sort'=>array(
'attributes'=>array(
'firstname'=>array(
'asc'=>'firstname',
'desc'=>'firstname DESC',
),
),
),
'pagination'=>array(
'pageSize'=>50,
)));

Categories