link between model cake php - cakephp

I try to use CakePhp but i don't find the solution. Here is my problem :
I have 2 models ant i try to link it.
The First is Player (which mysql table is players)
The second is Toto (which mysql table is totos)
I have a field un players called toto_id
And in my model Player i wrote :
<?php
class Player extends AppModel {
public $validate = array(
'name' => array(
'rule' => 'notEmpty'
)
);
public $hasOne = 'Toto';
}
And in my controller PlayersController
class PlayersController extends AppController {
public $helpers = array('Html', 'Form', 'Session');
public $components = array('Session');
public function index() {
$params = array(
'order' => array("Player.firstname")
);
$a = $this->Player->find('all', $params);
print_r($a);
$this->set('liste', $a);
}
}
And my print_r displays
Array
(
[0] => Array
(
[Player] => Array
(
[id] => 2
[name] => DESMAISON
[firstname] => Christophe
[is_intern] => 1
[score] => 663
[created] => 2014-01-05
[modified] => 0000-00-00 00:00:00
[toto_id] => 2
)
)
[1] => Array
(
[Player] => Array
(
[id] => 10
[name] => CHARLERY
[firstname] => Daphné
[is_intern] => 1
[score] => 572
[created] => 2014-01-04
[modified] => 0000-00-00 00:00:00
[toto_id] => 0
)
)
I don't understand why in my array i haven't a reference to Toto. Someone can help me ?
Thank you for your help
Ps : i use cakephp 2.4.4 PHP 5.4.7

I think this is BelongsTo Association .
Instead of HasOne association try to use BelongsTo,
class Invoice extends AppModel {
public $belongsTo = array(
'Toto' => array(
'className' => 'Toto',
'foreignKey' => 'toto_id'
),
);
if you want to retrieve data from database you have to set your recursivity and there is two ways first one :
$players = $this->Player->find('all',array('recursive'=>2));
// or
$this->Player->recursive = 2;
$player = $this->Player->find('all');

Related

CakePHP HABTM won't save relationships

I've been digging through similar questions but couldn't get my app to save HABMT relationsships.
I want to have "Test" HABMT "Browser".
The Tables:
cake_browsers (id, name, ...)
cake_tests (id, name, ...)
cake_browsers_tests(browser_id, test_id)
The Relationsships that are already in the middle-table can be read.
The result of $this->Test->findById($id) is:
Array(
[Test] => Array
(
[id] => 94
[name] => NM Test 2
[description] =>
[created] => 2015-03-22 18:54:41
[modified] => 2015-03-22 19:53:28
)
[Browser] => Array
(
[0] => Array
(
[id] => 2
[name] => Firefox
[height] => 768
[width] => 1366
[active] => 1
)
)
)
The Model:
class Test extends AppModel {
public $actsAs = array('Containable');
public $hasAndBelongsToMany = array(
'Browser' => array(
'className' => 'Browser',
)
); ...
The controller-function:
public function add() {
$this->set('testDomains', $this->Test->DomainSchedule->TestDomain->find('all'));
$this->set('browsers', $this->Test->Browser->find('list'));
if ($this->request->is('post')) {
$this->Test->create();
$newTest = $this->request->data;
print "<pre>"; print_r($newTest); print "</pre>";
if ($this->Test->saveAll($newTest)) {
if($this->scheduleAutoTestRun($this->Test->id)){
$this->Session->setFlash(__('Your test has been saved. Additionally it has been added to the queue.'));
}else{
$this->Session->setFlash(__('Your test has been saved.'));
}
//return $this->redirect(array('action' => 'index'));
}else{
$this->Session->setFlash(__('Unable to add your test.'));
}
}
}
The View:
...
<?php echo $this->Form->input('Browser',array('multiple'=>'checkbox'));?>
...
The Content of $this->request->data
Array(
[Test] => Array
(
[name] => NM Test
[description] =>
)
[Browser] => Array
(
[Browser] => Array
(
[0] => 2
)
)
So, as far as I've been reading, I understand that this is the way it should look like. Unfortunately it is not saving the relationship that Browser with id=2 is connected to the new created Test.
Could anybody give me a hint what I am doing wrong?
Thanks!
I believe this needs the param deep as in $this->Test->saveAll( $data, array('deep' => true) )

Cake php multimodel form post parameters

I'm a cakephp newbie, and I was ordered to use the 1.3 version.
I can't understand (and both guides and api docs don't tell it) how I could create an HABTM association in a POST request.
I'm trying to create a wine, that could be made of many vines. For example I'm creating a "soave" whine, that is made of "garganega" and "chardonnay" vines.
How should the POST params should be?
Given theses models
class Wine extends AppModel{
var $hasAndBelongsToMany = array(
'Vine' => array(
'className' => 'Vine',
'joinTable' => 'wine_vines',
'foreignKey' => 'wine_id',
'associationForeignKey' => 'vine_id',
'with' => 'WineVine',
),
);
}
class Vine extends AppModel{
var $hasAndBelongsToMany = array(
'Wine' => array(
'className' => 'Wine',
'joinTable' => 'wine_vines',
'foreignKey' => 'vine_id',
'associationForeignKey' => 'wine_id',
'with' => 'WineVine',
),
);
}
class WineVine extends AppModel{
var $name = "WineVine";
public $belongsTo = array("Wine", "Vine");
}
I tried a POST like this:
Array
(
[Wine] => Array
(
[denomination] => Soave DOP
[fantasy_name] =>
[kind] => White
)
[Vine] => Array
(
[0] => Array
(
[name] => garganega
)
[2] => Array
(
[name] => chardonnay
)
)
)
but it does not perform any inserts in vine table, only in wine.
Here's the log:
2 INSERT INTO `wines` (`denomination`, `fantasy_name`, `kind`, `modified`, `created`) VALUES ('', '', '', '2013-10-25 17:27:14', '2013-10-25 17:27:14') 1 55
3 SELECT LAST_INSERT_ID() AS insertID 1 1 1
4 SELECT `WineVine`.`vine_id` FROM `wine_vines` AS `WineVine` WHERE `WineVine`.`wine_id` = 2 0 0 1
5 SELECT `Vine`.`id`, `Vine`.`name`, `Vine`.`created`, `Vine`.`modified` FROM `vines` AS `Vine` WHERE 1 = 1 5 5 0
6 SELECT `Wine`.`id`, `Wine`.`denomination`, `Wine`.`fantasy_name`, `Wine`.`kind`, `Wine`.`created`, `Wine`.`modified`, `Wine`.`producer_id`, `WineVine`.`id`, `WineVine`.`wine_id`, `WineVine`.`vine_id`, `WineVine`.`created`, `WineVine`.`modified` FROM `wines` AS `Wine` JOIN `wine_vines` AS `WineVine` ON (`WineVine`.`vine_id` IN (1, 2, 3, 4, 5) AND `WineVine`.`wine_id` = `Wine`.`id`)
after saving the wine, try injecting its id into the data array
$this->data['Wine']['id'] = $this->Wine->id;
and then call an overloaded Model::saveAssociated() that will save all vines and update the join table by itself.
this overloaded method is described at:
http://bakery.cakephp.org/articles/ccadere/2013/04/19/save_habtm_data_in_a_single_simple_format
edit: sorry, that's for cake 2.x
i just realized 1.3 has no saveAssociated method
edit 2: but it does work in cake 1.3 if you change the last line of the saveAssociated method to
return parent::saveAll($data, $options);

CakePHP 2.3 Model probleme getting data

sorry for my english and i'm new using CAKEPHP 2.3
i get a problem using model association where i can't get "joined data" ( i understand that cakephp get automatically joined data if models are set good , obviously i make it wrong )
I have 2 tables in my database:
departements (with the following columns)
-id
-name
-region_id ( region_id is a foreign key on regions(id) )
regions (with the following columns)
-id
-name
i make two models in cakePhp:
Region.php
<?php
class Region extends AppModel {
var $name = 'Region';
var $actsAs = array( 'Containable' );
public $hasMany = array(
'Departement' => array(
'className' => 'Departement',
'foreignKey' => 'region_id',
'dependent' => false
)
);
}
?>
Departement.php
<?php
class Departement extends AppModel {
var $name = 'Departement';
var $actsAs = array( 'Containable' );
public $belongsTo=array(
'Region' => array(
'className' => 'Region',
'foreignKey' => 'region_id',
'dependent' => false
)
);
}
?>
in a another controller where i need departements and regions, i tryed many thinks but after yours i need your help please !!!
i make this :
first attempt:
<?php
class SignalementsController extends AppController {
public $helpers = array('Html', 'Form');
var $name = 'Signalements';
var $uses = array('Signalements','Regions','Departements','Communes');
public $recursive = 4;
public function index() {
$departements = $this->Departements->find('all');
$this->set('departements', $departements);
}
}
who output in the index view:
array(
(int) 0 => array(
'Departements' => array(
'id' => '1',
'name' => 'AIN',
'region_id' => '82'
)
),
(int) 1 => array(
'Departements' => array(
'id' => '10',
'name' => 'AUBE',
'region_id' => '21'
)
)
...
)
but there is no region joined automatically
second attempt: i change the find models from
$regions = $this->Regions->find('all');
$this->set('regions', $regions);
who output :
array(
(int) 0 => array(
'Regions' => array(
'id' => '11',
'name' => 'ILE-DE-FRANCE'
)
), ... );
but there is no departements joined automatically
last
$fields=array('Region.id','Region.nom','Departement.id','Departement.nom');
$all= $this->Regions->Departements->find('all',$fields);
/* or all this not working as execpted
$all= $this->Departements->Regions->find('all',$fields);
$all= $this->Departements->find('all',$fields);
$all= $this->Regions->find('all',$fields);
*/
$this->set('all', $all);
i try to get the regions data inside each departemts for exemple like this
array(
(int) 0 => array(
'Departements' => array(
'id' => '1',
'nom' => 'AIN',
'region' => array( 'id' => '82', 'nom' => 'POITOU-CHARENTES')
)
),
but i get only this :
array(
(int) 0 => array(
'Departements' => array(
'id' => '1',
'nom' => 'AIN',
'region_id' => '82'
)
),
If you have any idea please leave it
thank you very much
You are using Containable behaviour in your model, so try the find with this:
$departements = $this->Departement->find('all', array('contain' => 'Region'));
Also the maximum level for recursive is 2, setting it to 4 (or 1000) has the effect of 2.
Your ID is of type varchar and should be int.
Also, here is a stripped down version of your code
Region.php
<?php
class Region extends AppModel {
var $actsAs = array( 'Containable' );
public $hasMany = array( 'Departement' );
}
?>
Departement.php
<?php
class Departement extends AppModel {
var $actsAs = array( 'Containable' );
public $belongsTo=array( 'Region' );
}
?>
<?php
class SignalementsController extends AppController {
public $helpers = array('Html', 'Form');
var $uses = array('Signalements','Regions','Departements','Communes');
public function index() {
$this->Departement->recursive = 1;
$departements = $this->Departements->find('all');
$this->set('departements', $departements);
}
}
index.ctp
<?php print_r($departements); ?>
What does it give you?

join belongsTo in find method

Score belongs to Player:
class Score extends AppModel {
public $name = 'Answer';
public $belongsTo = array('Player');
}
In PlayersController, I want to get player scores, with his details.
Question #1: How to include belongsTo model in find method result? (join it)
Question #2: How to get sum of all scores distance (Score.distance) which belongs to that player? (I mean SUM(Score.distance), group by Score.player_id)
Note for Q1: Because each player has a lot scores, I don't like to join scores in each find method I use in that controller. I want to get them in just 1 action)
Cakephp automagic not actually join tables as you can see in sql_dump in your layout at bottom.
You will see below queries.
SELECT `Player`.`id`, `Player`.`name`, `Player`.`created` FROM `players` AS `Player` WHERE `Player`.`id` = 10 GROUP BY `Player`.`id`
SELECT `Score`.`id`, `Score`.`player_id`, `Score`.`scores` FROM `scores` AS `Score` WHERE `Score`.`player_id` = (10)
Quite clear that its not actually join table so you need to do it following way.
And following will help you because i have already tested it.
In Player Controller you need to unbind score model first and then custom code to join score table as below.
In player model create one virtual field as below.
<?php
class Player extends AppModel
{
var $name = 'Player';
var $displayField = 'name';
var $virtualFields = array
(
'Distance' => 'SUM(Score.scores)'
);
var $hasMany = array
(
'Score' => array
(
'className' => 'Score',
'foreignKey' => 'player_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
}
?>
For testing i manually added conditions to find player number 10 scores
<?php
class PlayersController extends AppController {
var $name = 'Players';
function index()
{
$this->Player->unbindModel(array
(
'hasMany' => array
(
'Score'
)
));
$this->Player->bindModel(array
(
'belongsTo' => array
(
'Score' => array
(
'foreignKey' => false,
'conditions' => array
(
'Player.id = Score.player_id'
)
)
)
));
$order = "Player.id";
$direction = "asc";
if(isset($this->passedArgs['sort']) && $this->passedArgs['sort']=="Distance")
{
$order = $this->passedArgs['sort'];
$direction = $this->passedArgs['direction'];
unset($this->passedArgs['sort']);
unset($this->passedArgs['direction']);
}
$this->pagination = array();
$this->pagination = array
(
'conditions' => array
(
'Score.player_id' => 10
),
'fields' => array
(
'Player.*',
'SUM(Score.scores) AS Distance'
),
'group' => array
(
'Score.player_id'
),
'limit' => 15,
'order'=>$order." ".$direction
);
$playersScore = $this->paginate('Player');
}
}
?>
And the resulting array would be looks like below.
Array
(
[0] => Array
(
[Player] => Array
(
[id] => 10
[name] => Dexter Velasquez
[created] => 2012-08-02 12:03:07
[Distance] => 18
)
)
)
For testing i have used Generate Mysql Data.
for sorting link
<?php $direction = (isset($this->passedArgs['direction']) && isset($this->passedArgs['sort']) && $this->passedArgs['sort'] == "Distance" && $this->passedArgs['direction'] == "desc")?"asc":"desc";?>
and display above link as below.
<?php echo $this->Paginator->sort('Distance','Distance',array('direction'=>$direction));?>
So, Player has many Scores...
class Player extends AppModel {
public $name = 'Player';
public $hasMany = array('Score');
}
... which means your find() in PlayersController can be something like...
$players = $this->Player->find(
'all',
array(
'contain' => array('Score'), // <-- If you're not using Containable Behavior, remove.
'conditions' => array('Player.id' => $id), // <-- Change or remove as necessary.
'fields' => array(
'Player.id',
'Player.name',
'Sum(Score.distance) as sum_dist',
),
'group' => array(
'Player.id',
'Player.name',
),
'order' => array(
'Player.name',
),
)
);
Hopefully you get the idea.

Second Level Expansion?

First of all, sorry if this sounds too simple. I'm new to cakePHP by the way.
My objective is to expand the team_id in season controller where it linked thru standings table.
My current model rule are:
Each season has many standings
Each standings denotes a team.
Basically is a many to many relation between season and team = standings.
Here's my SeasonController:
class SeasonsController extends AppController
{
public $helpers = array('Html', 'Form');
public function view($id)
{
$this->Season->id = $id;
$this->set('season', $this->Season->read());
}
}
When I browse a single season thru /cakephp/seasons/view/1, the $season array shows:
Array
(
[Season] => Array
(
[id] => 1
[name] => English Premier League 2013/2014
[start] => 1350379014
[end] => 0
)
[Standing] => Array
(
[0] => Array
(
[id] => 1
[team_id] => 1
[win] => 1
[lose] => 0
[draw] => 1
[GF] => 5
[GA] => 4
[season_id] => 1
[rank] => 1
)
[1] => Array
(
[id] => 2
[team_id] => 2
[win] => 0
[lose] => 1
[draw] => 1
[GF] => 4
[GA] => 5
[season_id] => 1
[rank] => 2
)
)
)
The result only shows team_id, but how can I get further expansion for team_id?
I have put below in my Team's model, but still doesn't auto linked. Below are all my models:
class Team extends AppModel
{
public $name = 'Team';
public $hasMany = array(
'Standing' => array(
'className' => 'Standing'
));
}
class Season extends AppModel
{
public $name = 'Season';
public $hasMany = array(
'Standing' => array(
'className' => 'Standing'
));
}
class Standing extends AppModel
{
public $name = 'Standing';
public $belongsTo = array(
'Team' => array(
'className' => 'Team',
'foreignKey' => 'team_id',
),
'Season' => array(
'className' => 'Season',
'foreignKey' => 'season_id',
),
);
}
TLDR:
Use CakePHP's [Containable] behavior.
Explanation & Code example:
"Containable" lets you "contain" any associated data you'd like. Instead of "recursive", which blindly pulls entire levels of data (and can often cause memory errors), "Containable" allows you to specify EXACTLY what data you want to retrieve even down to the field(s) if you'd like.
Basically, you set your model to public $actsAs = array('Containable'); (I suggest doing that in your AppModel so Containable is available for all models). Then, you pass a "contain" array/nested array of the associated models you want to retrieve (see below example).
After reading about it and following the instructions, your code should look similar to this:
public function view($id = null)
{
if(empty($id)) $this->redirect('/');
$season = $this->Season->find('first', array(
'conditions' => array(
'Season.id' => $id
),
'contain' => array(
'Standing' => array(
'Team'
)
)
);
$this->set(compact('season'));
}
Once you get more comfortable with CakePHP, you should really move functions like this into the model instead of having them in the controller (search for "CakePHP Fat Models Skinny Controllers"), but for now, just get it working. :)
Use :
$this->Season->id = $id;
$this->Season->recursive = 2;
$this->set('season', $this->Season->read());

Resources