CakePHP: fetch related records in order? - cakephp

I have two models: Event and Match. An event can have many matches. My Match model has $order = array('match_order' => 'asc'). The match_order column in my matches database table is just a simple INT column to order matches.
I have a controller method that updates the order of matches. However, because I fetch matches through the event, they don't seem to come in ascending by match_order, but by the id column. Here is an example call:
<?php
class EventsController extends AppController {
public function view($id) {
$event = $this->Event->find('first', array(
'conditions' => array(
'Event.id' => $id
),
'contain' => array('Match')
));
}
}
All the related matches come back as expected, but just not in the right order. I've cleared my model caches, is there anything else I need to do to get the matches order by the value in the Match model?

Solved it. Forgot I could specify order in my model associations, so I've added the following to my Event model:
<?php
class Event extends AppModel {
public $hasMany = array(
'Match' => array(
'order' => array(
'match_order' => 'asc'
)
)
);
}

Related

CakePHP Join virtualField as displayField

I'm trying to do one of two things, use a virtual field from a model as the display field in my join model, or use the virtual field as the display in a find('list') on my join model.
Here's the current layout:
MODELS
<?php
class Participant extends AppModel {
public $hasMany = array('Registration');
public $virtualFields = array(
'full_name' => 'CONCAT(last_name, ", ", first_name)'
);
public $displayField = 'full_name';
}
-----
class Contest extends AppModel {
public $hasMany = array('Registration');
}
-----
class Registration extends AppModel {
public $belongsTo = array('Participant', 'Contest');
public $hasMany = array('Assignment');
}
?>
Tables are as follows:
participants contests registrations
------------ --------- --------------
id id id
first_name name contest_id
last_name participant_id
In my contests controller I'm trying to develop a list to be viewed as checkboxes in the view.
Here is the excerpt from my contests controller:
$this->loadModel('Registration');
$this->set('registrations', $this->Registration->find(
'list',
array(
'conditions' => array('contest_id' => $contestId),
'recursive' => 1
)
)
);
//$contestId is defined previously, and I have verified such.
This all actually runs fine as it is, and in the view will display a column of checkboxes with the registration_id as the label next to the checkbox.
I would like to get a full_name as is defined in the Participant model to be the displayField of the Registration model. I've been searching and can't quite seem to find a good way of doing that. I hope I have been descriptive enough, and please let me know if you have any questions and I'll try to explain better. Thank you.
edit: I'm using CakePHP 2.4.
try to add the 'fields' parameter
$this->loadModel('Registration');
$this->set('registrations', $this->Registration->find(
'list',
array(
'fields' => array('Registration.id', 'Participant.full_name'),
'conditions' => array('contest_id' => $contestId),
'recursive' => 1
)
));
edit: apparently cake does not place virtual fields of associated models when using find() with 'fields' options.
So you have to build your array by yourself, hoping your models use the containable behavior
$registrations = $this->Registration->find(
'all',
array(
'contain' => array(
'Participant' => array('full_name')
),
'conditions' => array('contest_id' => $contestId),
)
);
$registrations = Hash::combine($registrations, '{n}.Registration.id', '{n}.Participant.full_name');
$this->set('registrations', $registrations);

Cakephp: Associations not working

I want to do a simple cakephp association program but it's not working.
I have two database tables: users and sec_datas. When i run this program it just shows the result of first row of users table, not the result of both tables which have same sec_id value.
Controller code:
<?php
class UsersController extends AppController
{
public function index()
{
$this->autoRender = FALSE;
$this->loadModel('User');
$storeDivisions = $this->User->find();
echo "<pre>";
print_r($storeDivisions);
echo "</pre>";
}
}
Model code:
<?php
class User extends AppModel {
public $useTable='users';
public $hasOne = array(
'Sec_data' => array(
'ClassName' => 'Sec_data',
'Conditions' => array('User.sec_id=Sec_data.sec_id'),
'Dependent' => false
)
);
}
?>
If the primary key of User model is id then cake try to associate the foreign key in Sec_data with that key regardless the conditions you set.
First of all you should do domething like
public $hasOne = array(
'Sec_data' => array(
'ClassName' => 'Sec_data',
'ForeignKey' => 'sec_id',
'Dependent' => false
)
);
But it would work only if sec_id is related to User.id
if you want Sec_data.sec_id related to User.sec_id (and User.sec_id is different from User.id) then you have to join the tables manually
edit: see comments
There are a couple of things going wrong here. You should read up on CakePHP's conventions.
Your associated model class should be called SecData, then it will automatically map to the sec_datas table.
No need to use loadModel.
No need to specify conditions. Set the correct recursive level instead.
For hasOne, only one table has a foreign key. By default, the other Model has it ("User hasOne SecData" -> SecData has foreign key). So in your example, you should remove the column sec_id from User, and add a user_id column to your SecData Model.
See also: CakePHP Book on associations
Updated Controller code:
<?php
class UsersController extends AppController {
public $uses = array('User'); // Do this instead of loadModel
public function index() {
$this->autoRender = FALSE;
$this->User->recursive = 1; // Make User load associated records
$storeDivisions = $this->User->find('all');
pr($storeDivisions); // pr() is a Cake shorthand for print_r wrapped in <pre>
}
}
For your model:
<?php
class User extends AppModel {
// Unecessary. Convention is to use lowercase classname + 's', which
// already gives us 'users'
// public $useTable='users';
public $hasOne = array(
'SecData' => array( // Model class Sec_Data must be renamed accordingly
'dependent' => false
)
);
}
?>
Study model association in cake php . Here is very good explanation http://www.phpsupercoder.com/model-association-cake-php

CakePHP Access a different table my view

I have two tables Contact and Quote, this is a one to many relationship (i.e. one contact can have many quotes). Foreign keys are all setup correctly.
When I go to create a new quote I want to be able to select from a drop down list of contacts.
My code looks like this:
Contact Model:
class Contact extends AppModel {
public $hasMany = array('Quote' => array('className' => 'Quote', 'foreignKey' => 'contact_id'));
}
Quote Model
class Quote extends AppModel {
public $belongsTo = array('Contact' => array('className' => 'Contact', 'foreignKey' => 'contact_id'));
public $validate = array(
'name' => array(
'rule' => 'notEmpty'
),
'amount' => array(
'rule' => 'notEmpty'
)
);
}
Add method in QuotesController:
public function add() {
// TODO: Update this so the user can select the id from a drop down list.
$this->request->data['Quote']['contact_id'] = '1';
if ($this->request->is('post')) {
$this->Quote->create(); // This line writes the details to the database.
if ($this->Quote->save($this->request->data)) {
$this->Session->setFlash('Your quote has been saved.');
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash('Unable to add your quote.');
}
}
}
As you can see I'm currently just hard coding the user id as part of the add process.
I'm assuming you have read and are using this method in your view.
http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::select
Less easy to find (or easier to overlook) is this:
http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#find-list
So in your controller you want to do
$contacts = $this->Article->find('list', array('fields' => array('Contact.id', 'Contact.name'));
$this->set(compact('contacts'));
Then in the view:
echo $this->Form->select('contact_id', $contacts);
Modify the fields for the find to reflect what is actually in your model. And if you need fields combined, you can possibly do it with virtual fields: http://book.cakephp.org/2.0/en/models/virtual-fields.html. Otherwise you can select the fields that need to be composited and use a foreach loop to combine them into a id=>[displayed value] array to pass on to the view. Only the id is the important thing and has to correspond to an id in the Contacts table.

CakePHP HABTM relationship table joins

I have a 'patient_cases' table with a HABTM relationship with a 'procedures' table. This 'procedures' tables has a many to one relationship with 'consultants' table, which then has a many to one relationship with 'specialties' table.
The relationships are working fine and using cakephp debug tool bar i can see everything is coming through correctly when i look at 'patient_cases' index page, however i'm only getting consultant_id and specialty_id rather than all of the fields with that associated model.
which is what should be happening i believe -
In CakePHP some associations (belongsTo and hasOne) performs automatic
joins to retrieve data, so you can issue queries to retrieve models
based on data in the related one.
But this is not the case with hasMany and hasAndBelongsToMany
associations.
I want to be able to get the consultant name from 'consultants' table using procedures.consultant_id and get the specialty name from 'specialties' using consultant.specialty_id.
I have tried playing about with some table joins but with no success,
class PatientCase extends AppModel {
public $hasAndBelongsToMany = array(
'Procedure' => array(
'className' => 'Procedure',
'joinTable' => 'patient_cases_procedures',
'foreignKey' => 'patient_case_id',
'associationForeignKey' => 'procedure_id',
'unique' => true
)
);
}
class Procedure extends AppModel {
public $belongsTo = array(
'Consultant' => array(
'className' => 'Consultant'
)
);
}
class Consultant extends AppModel {
public $belongsTo = array(
'Specialty' => array(
'className' => 'Specialty',
'conditions' => array('Specialty.active' => 1)
)
);
public $hasMany = array(
'Procedure'
);
}
class Specialty extends AppModel {
public $hasOne = array(
'Consultant'
);
);
}
$this->set('patientCases', $this->PatientCase->find('all'));
Returns everything i need, each model array is returned (other relationships not mentioned here), however the model array 'Consultant' is not returned, which also means i can not get the 'Specialty' data which is linked to the 'Consultant' model
EDIT
I have managed to get the data i need by using this in my PatientCasesController
$this->set('patientCases', $this->PatientCase->find('all'));
$this->set('patientCaseProcedures', $this->PatientCase->Procedure->find('all'));
However, ideally i want to return everything in the one 'patientCases', is this possible?
EDIT
I have solved the problem using the recursive function
$this->PatientCase->recursive = 3;
$this->set('patientCases', $this->PatientCase->find('all'));

How can I use counterCache() for tracking view counts with CakePHP?

Every time a user views a submission, I track the count and tie it to a session:
SubmissionsController:
// Count view
if ($this->Session->check('viewed_submission_' . $submissionId) !== true) {
$clientIp = ip2long($this->request->clientIp());
$this->SubmissionsViews->countView($submissionId, $clientIp);
$this->Session->write('viewed_submission_' . $submissionId, true);
}
I'm keeping track of them in a SubmissionsViews table.
SubmissionsViews Model:
class SubmissionsViews extends AppModel {
var $name = 'SubmissionsViews';
var $belongsTo = array(
'Submission' => array(
'className' => 'Submission'
)
);
public function countView($submissionId, $clientIp) {
$this->set('submission_id', $submissionId);
$this->set('user_ip', $clientIp);
$this->save($this->data);
}
}
My SubmissionsView table submissions_views has the following fields:
id
submission_id
user_ip
created
I'm trying to set up counterCache to keep track of additions to that table, but not sure how to set it up. I'm currently adding the counterCache in my $belongsTo within my Submission model:
class Submission extends AppModel {
var $belongsTo = array(
'User' => array(
'className' => 'User'
),
'SubmissionsViews' => array(
'counterCache' => true
)
);
But it's telling me it can't find Submission.submissions_views_id. In the documentation, all it said was that I needed to add a field to my submissions table called submissions_views_count, so I'm confused how to get this working?
Model names should be singlular, so SubmissionViews should be SubmissionView.
Your association is incorrect. Submission hasMany SubmissionView, not belongsTo.
counterCache needs to be specified in the SubmissionView model inside the belongsTo config for Submission, not in the Submission model file. Please read the manual more carefully.

Resources