how to get name of (UserTransactionType.name) with Transaction.who_pay_fee_1,2,3 fields.
'user_transaction_type_id' works well but how to get the rest of fields work :(
//Transaction Model
public $belongsTo = array(
'UserTransactionType' => array(
'className' => 'UserTransactionType',
'foreignKey' => 'user_transaction_type_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
//UserTransactionType Model
public $hasMany = array(
'Transaction' => array(
'className' => 'Transaction',
'foreignKey' => 'user_transaction_type_id',
'dependent' => false,
))
This is the sample code for your controller:
$this->UserTransactionType->find('all',array(
'fields' => array('name'),
'contain' => array('Transaction')
)
);
If Models are associated you can specify in 'contain' which of them you want to get in the result.
If you want to have only some fields of related Model, you can determine them after 'Transaction' in 'contain' just like in the regular find() query:
'contain' => array('Transaction' => array('fields' => array('field_1',
'field_2') ))
But in your case, you don't need to specify fields, because by default you get all fields.
So no matter if you define or not fields "who_pay_fee_1,2,3" because if you use 'contain' by default you will get foreing_key - user_transaction_type_id.
I hope it's helpful
For people they like CakePhp :)
in Controller ->
get the list of 'UserTransactionType'
in View ->
after looping trough all the transactions; in Transaction Status column simply load the 'UserTransactionType'array and assign the number of array to $userTransactionTypes.
$userTransactionTypes[$transaction['Who_pay_fee_1']];
To be honest it was straight forward but needed a bit concentration :)
Related
I am calling a find on a model called Book which is associated with a model Page(book_id)
However Page is associated with a model called Asset(page_id). I would like to get the array with all three models
Book
Page1
Asset1
Asset2
Asset3
Page2
Asset1
Asset2
Asset3
The code I have at the moment only get me Book and Page
$options = array(
'conditions' => array('Book.' . $this->Book->primaryKey => $id),
'contain' => 'Page'
);
$books = $this->Book->find('first', $options);
Book hasMany Pages
Page hasMany Assets
You can contain deeper associations, like it says in the docs
Example from the docs
$this->User->find('all', array(
'contain' => array(
'Profile',
'Account' => array(
'AccountSummary'
),
'Post' => array(
'PostAttachment' => array(
'fields' => array('id', 'name'),
'PostAttachmentHistory' => array(
'HistoryNotes' => array(
'fields' => array('id', 'note')
)
)
),
'Tag' => array(
'conditions' => array('Tag.name LIKE' => '%happy%')
)
)
)
));
Same thing with your models...
$options = array(
'conditions' => array('Book.' . $this->Book->primaryKey => $id),
'contain' => array('Page' => array('Asset')))
);
$books = $this->Book->find('first', $options);
Should work if your associations are set correctly (and if all models implement containable behavior).
EDIT
(to address the confusion the OP had)
The nested contain options works for the model expanding the array. For example, if models are associated like this
Model-A -> Model-B -> Model-C & Model-D
-> Model-E -> Model-C
you could get the entire array with data like
Model-A
Model-B1
Model-C1
Model-C2
Model-D2
Model-B2
Model-C (null)
Model-D3
Model-E1
Model-C1
Model-C3
using something like
$this->ModelA->find('all'), array(
'contain' => array(
'Model-B' => array('Model-C', 'Model-D'),
'Model-E' => array('Model-C')
)
);
Also, you can add options to the containable array, including the ones used for searching, like 'conditions' (though be careful with this, it means that if the model doesn't match the condition it will return a null array, it does not mean the the whole "Model-A" will not be in the returned data since one of the nested conditions was not fulfilled).
I want to limit the fields returned by a deeper association using containable.
My associations:
Game hasMany Review
The paginate and containable code:
$this->paginate = array(
'conditions' => $conditions,
'fields' => array(
'Game.id', 'Game.name',
'Publisher.id', 'Publisher.name'
),
'contain' => array(
'Game' => array(
'Review' => array(
'fields' => array('Review.id', 'ROUND(AVG(Review.score),1)')
)
),
)
);
$games = $this->paginate('Game');
Currently, all of the fields in the Review table are returned. 'ROUND(AVG(Review.score),1)' is never returned. How can I specify what fields I want returned from the Review association?
SQL dumps for two search results using #theJetzah's answer. The first is a search with one game as a result and the second is a search returning three games.
SELECT `Review`.`id`, `Review`.`review_text`, `Review`.`score`, `Review`.`user_id`, `Review`.`game_id`, `Review`.`created`, `Review`.`platform_id`, (ROUND(AVG(`Review`.`score`),1)) AS `Review__average_score` FROM `videogamedb`.`reviews` AS `Review` WHERE `Review`.`game_id` = (55)
SELECT `Review`.`id`, `Review`.`review_text`, `Review`.`score`, `Review`.`user_id`, `Review`.`game_id`, `Review`.`created`, `Review`.`platform_id`, (ROUND(AVG(`Review`.`score`),1)) AS `Review__average_score` FROM `videogamedb`.`reviews` AS `Review` WHERE `Review`.`game_id` IN (55, 56, 57)
Not a full answer, but an attempt to get it working :)
Approach1 (UPDATE: Containable doesn't support 'group by')
First of all, try to add the 'Game' model to the $uses array of your Controller, if it is not included yet, and re-organise the pagination array (as previously suggested by Sam), so that you'll be pagination the Game model itself.
Then, It may help to create a virtual field for the calculated score, but the results of 'Review' need to be grouped, otherwise you'll not be able to calculate the average score.
I'm not able to test this, but it may worth trying
something like this;
public $uses = array(
'Game',
// other models
);
public function myfunction()
{
$this->Game->Review->virtualFields['average_score'] = 'ROUND(AVG(Review.score),1)';
$this->paginate = array(
'Game' => array(
'fields' => array(
'Game.id',
'Game.name',
'Publisher.id',
'Publisher.name'
),
'contain' => array(
'Review' => array(
'fields' => array(
'Review.game_id,
'Review.average_score',
),
'group' => array(
'Review.game_id,
),
)
)
)
);
// Conditions can be passed to paginate,
// that way you can specify 'paginate' at
// one place and don't have to modify it
// to include the conditions
$games = $this->paginate('Game', $conditions);
}
Alternative approach: Using joins and a database-view
Apparently, the Containable behavior doesn't like group-by clauses; See this ticket for more information: Containable behavior does not implement 'group' option
CakePHP allows you to manually specify a join: Joining Tables
To simplify things and to prevent having to add a 'group by' for all fields, create a simple database-view in your database;
CREATE VIEW review_scores AS
SELECT
game_id,
ROUND(AVG(score),1) AS average_score,
COUNT(id) AS total_reviews
FROM
reviews
GROUP BY
game_id;
If you're unfamiliar with this; a database 'view' is basically a 'stored query', which can be accessed as if it was a regular table. See Create View
Then, use a 'manual' join, using the newly created database-view as the source-table. In your case, this will look something like this;
$this->paginate = array(
'Game' => array(
'fields' => array(
'Game.id',
'Game.name',
'Publisher.id',
'Publisher.name',
'ReviewScore.average_score',
'ReviewScore.total_reviews',
),
'joins' => array(
array(
'table' => 'review_scores',
'alias' => 'ReviewScore',
'type' => 'LEFT',
'conditions' => array(
'ReviewScores.game_id = Game.id',
)
)
)
)
);
Hope this helps
I think your array is a configured a little wrong, try:
$this->paginate = array(
'Game' => array(
'conditions' => $conditions,
'fields' => array(
'Game.id', 'Game.name',
'Publisher.id', 'Publisher.name'
),
'contain' => array(
'Review' => array(
'fields' => array('Review.id', 'ROUND(AVG(Review.score),1)')
)
)
)
);
$games = $this->paginate('Game');
As an aside, from personal experience, specifying the fields in a query doesn't always speed it up (certainly for small number of fields), assuming this is the motive for doing so. It does reduce memory occupancy but this is only relative to original size of the record and the number of records returned.
i have a two tables namely; histories and users. i need to display data like:
id | Username | Lastest created Post | First created Post
the data of id and username is from users table and the last created and first created post data is from histories. i need to view all the users, their lastest created post and their first created post. please help me to make controller and view thanks
Try below.
<?php
$users = $this->User->find('all',array
(
'conditions' => array
(
//conditions goes here
),
'fields' => array
(
'User.id',
'User.username',
'History.Lastest created Post',
'History.First created Post'
)
));
?>
Assume that relation between 'User' and 'History' table is One-to-One and there's a 'user_id' column in History table, you may need to specify relation between them in History model, for example:
var $hasOne = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
Then, you need to perform joins to do this. For example, somewhere in your User model, try something like this:
class User extends AppModel {
....
function getAllUsersHistory{
$allHistories = $this->find('all', array(
'joins' => array(
'table' => 'history',
'alias' => 'HistoryJoin'
'type' => 'INNER',
'conditions' => array(
// your conditions, for example: 'History.user_id' => 'User.id'
)
),
'fields' => array(
'User.id',
'User.username',
'History.lastest_created_post',
'History.first_created_post'
)
));
return $allHistories;
}
.....
}
In my CakePHP app I have models for Matches and Teams. Each Match has a home_team_id and an away_team_id, both of which reference a different Team.
In my team.php file, I am able to form the relationship for a Team's home matches:
var $hasMany = array(
'HomeMatch' => array('className' => 'Match', 'foreignKey' => 'home_team_id'),
'AwayMatch' => array('className' => 'Match', 'foreignKey' => 'away_team_id')
);
My problem is that I cannot automatically retrieve a Team's home and away Matches in a single array. That is, the retrieved Matches are returned in separate HomeMatch and AwayMatch arrays, which causes sorting difficulties.
I have tried the following:
var $hasMany = array(
'Match' => array('foreignKey' => array('home_team_id', 'away_team_id'))
);
...with no luck.
Any ideas on how to combine these two foreign keys into a single relationship?
Thanks, Ben
A custom finderQuery should do the trick:
public $hasMany = array(
'Match' => array(
'className' => 'Match',
'foreignKey' => false,
'finderQuery' => 'SELECT *
FROM `matches` as `Match`
WHERE `Match`.`home_team_id` = {$__cakeID__$}
OR `Match`.`away_team_id` = {$__cakeID__$}'
)
);
I was having a similar issue and instead of creating a finderQuery I used the conditions operator and it worked great!
public $hasMany = array(
'Match' => array(
'className' => 'Match',
'foreignKey' => false,
'conditions' => array(
'OR' => array(
array('Match.home_team_id' => '{$__cakeID__$}'),
array('Match.away_team_id' => '{$__cakeID__$}')
)
),
)
);
They are returned in seperate array's because the sort of represent different models (in this particular case the model is the same).
You should probably build a helper method to go over the retrieved data (in the model object or in a separate helper class) and "flatten" it. then you'd be able to sort it.
Ken.
I'm trying to use on the fly associations to trim down the data I retrieve, but the model I'm using is associated to other models with a re-named field because I have 2 of the same models associated with it.
So, here's the model, say 'test', that has two 'user' fields, both related to the User model.
In the model:
var $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id'
),
'User_Watched' => array(
'className' => 'User',
'foreignKey' => 'user_id_watched'
)
);
When I retrieve data related to 'test', I want to only retrieve particular data linked to the 'User' and 'User_Watched' fields without any other nested information.
But when I do:
$this->User->unbindModel(array('hasMany' => array('something1', 'something2')), false);
then something1 and something2 data does not show up for the 'User' field of model 'test', but is still retrieved for the 'User_watched' field.
Can I not retrieve unwanted data for the 'User_watched' field?
Hope this makes sense... :)
KcYxA,
Containable behavior might help a lot in this case, as benjamin mentioned, your "find" queries would look like:
$this->User->find('first', array(
'conditions' => array('User.id' => $id),
'contain' => array('UserWatched')
));
In this case, you won't have to use unbindModel method. In this example, you'll get User and UserWatched data.
If you need only User data from "find", then tell Cake to "$this->User->contain();" so it won't go further then User model.
to use on the fly associations to trim
down the data I retrieve
Good idea.
'foreignKey' => 'user_id_watched'
should possibly be:
'foreignKey' => 'user_watched_id'.
Edit 1: At least this would make sense according to my current understanding. If user_id is a correct foreign key(FK), which cakephp uses to unbind the relations, but user_id_watched isn't, than your described behavior is explained.
Edit 2: The Containable behavior gives you another tool for controlling associated models.
Change $primaryKey in fly, run controller
Sample:
// Models
//....
class PreProductoDescripcion extends AppModel {
/**
* Primary key field
*
* #var string
*/
public $primaryKey = 'id_producto_descripcion';
//....
//....
}
class SenasaPedidosDetalles extends AppModel {
/**
* Display field
*
* #var string
*/
public $displayField = 'cod_tango';
public $belongsTo = array(
'SenasaPedidos' => array(
'className' => 'SenasaPedidos',
'foreignKey' => 'senasa_pedidos_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'PreProductoDescripcion' => array(
'className' => 'PreProductoDescripcion',
'foreignKey' => 'cod_tango',
//'conditions' => array('SenasaPedidosDetalles.cod_tango' => 'PreProductoDescripcion.codigo'),
'fields' => '',
'order' => ''
)
);
//....
#
// Controller Fly
//...
$this->SenasaPedidos->Behaviors->load('Containable');
$this->SenasaPedidos->SenasaPedidosDetalles->PreProductoDescripcion->primaryKey = 'codigo';
$datos = $this->SenasaPedidos->find(
'first', array(
'fields' => array( 'SenasaPedidos.*' ),
'conditions' => array( 'SenasaPedidos.id' => $id ),
'contain' => array(
'Usuarios' => array(
'fields' => array( 'Usuarios.apellido_nombre' )
),
'Clientes' => array(
'fields' => array( 'Clientes.razon_social' )
),
'Provincias' => array(
'fields' => array( 'Provincias.nombre' )
),
'Transportes' => array(
'fields' => array( 'Transportes.razon_social' )
),
'SenasaPedidosDetalles' => array(
'fields' => array( 'SenasaPedidosDetalles.*' ),
'PreProductoDescripcion' => array(
'fields' => array(
'PreProductoDescripcion.id_producto_descripcion',
'PreProductoDescripcion.descripcion'
)
)
),
)
));
//...