CakePHP multiple "has many" links - cakephp

am running CakePHP 2.4.6 and have two tables/models with multiple foreign key relationships. In simplified terms, I have an StockGroup model which is linked to an Account model by two foreign keys - sale_account_id and purchase_account_id. The documentation tells me to set up a $hasMany structure in the Account model like this :
public $hasMany = array(
"StockGroupSaleAccount" => array(
"className" => "StockGroup",
"foreignKey" => "sale_account_id"
),
"StockGroupPurchaseAccount" => array(
"className" => "StockGroup",
"foreignKey" => "purchase_account_id"
)
);
When I try to open a view, I get the message
"Error: StockGroupSaleAccounts controller not found" (if I use the alias "StockGroup", the same as the class name then there is no problem, but that stops me specifying the multiple links).

False alarm, I'm afraid! Was extracting data belonging to the linked tables, and was using the keys from $hasMany to get the model, and thence the relevant controller name - which was obviously incorrect in this situation - have now corrected it to look at the class name. Thanks for your help and forbearance!

Related

CakePHP conditions clause on associations when using Model::find()

I just confused because of a find() result. This is my configurations:
First, users can have different User.role values: student, admin, and some others.
// Book
public $belongsTo = array(
'Student' => array(
'className' => 'User',
'foreignKey' => 'student_id'
'conditions' => array('User.role' => 'student')
);
);
When I chain Models like $this->Book->Student->find('list'); I was expecting to get only users whose role are 'student', but instead, it gets all users. What is going on here, what is conditions for on association definition, where can it and cannot be used. Any lead would help, thanks.
PS: I am aware that I could put conditions on find(), that's not the issue
There is a difference between associated data and accessing an associated model object. If you access $this->Book->Student you're accessing the Student model and work in it's scope. The conditions in the defined associations work only in the context of the accessed object.
So if you do a find on the Book and list the students for that book:
$this->Book->find('first', array('contain' => array('Student'));
Your code will work correctly. It will find the book plus the first user who has the role stundent. BUT your association is wrong then: It should be hasMany. because why would you filter a book by role if the book just belongsTo one student?
If you want to filter users by their role you can implement a query param that is checked in beforeFind(), pseudocode: if isset roleFilter then add contention to filter by that role from roleFilter.
Or, if you don't need to paginate just create a getStudents() method in the user model that will return a find('list') that has the conditions.
Or Student extends User and put the filter in the beforeFind() and use that model instead of the User model in your Book association.
If you want to filter on model level or per model I think the last one is a good option. Don't forget to set $useTable and $name or the inherited model will cause problems.
you have miss , inside your model.
try this:
public $belongsTo = array(
'Student' => array(
'className' => 'User',
'foreignKey' => 'student_id', //<------ miss
'conditions' => array('User.role' => 'student')
);
);
Yoi can debug your query to check what is the real query that you make.
Personally I have never use this approach, I prefer to use foreign key with another table for examples Rolesand User.role_id.
Is better for me to use this approach to have more flexibility inside your app.
After I prefer to use a conditions where inside controller to check well the query, because in your way every query you search always for student role not for the other and can be a problem for the rest of role, because inside controller you see a find without conditions but it doesn't take right value because in your model there is a particular conditions.
For me the good way is to create a new table, use foreign key and where conditions inside action of the controller to view well what are you doing.
For default all relations are "left join", you must set the parameter "type" with "inner" value
// Book
public $belongsTo = array(
'Student' => array(
'className' => 'User',
'foreignKey' => 'student_id'
'conditions' => array('Student.role' => 'student'), // <-- Fix That (field name)
'type' => 'inner', // <-- add that
);
);

CakePHP - Retrieving a list of options from another table, to use with form helper/ before Instering

Here is Yet another cakePHP question! I have table called blood_groups which has blood_group_id and group fields.. Then I have another table called donors, which has several fields such as name, surname etc. Another field included inside this table is the foreign key 'blood_group_id' which will need to map to the blood_group table on retrieval. in the donor registration view, i want to be able to retrieve the values from the blood_groups table, and display them using the formHelper (with their respective id's).
I have gone through CAKE doc, and I understand that I would need to create the association between my models, but I am struggling to figure this one out. Should I create $hasOne association inside the Donor Model (considering that the Donor table has the fk of the other table). And how would I go about retrieving the options of blood_groups from the blood_groups Model?
Should It work like this?(and are any other prerequisites involved?) :
In my DonorController -
$this->set('blood_groups', $this->Donor->Blood_Group->find('all'));
in Views/Donor/add.ctp
echo $this->Form->input('blood_group_id');
Accessing data through associations is fine. But for radios or checkboxes you want to do a find('list). Your model and variable name does not match the CakePHP convention, there should be no underscore.
Properly named this should be already enough to populate the input.
// controller
$this->set('bloodGroups', $this->Donor->BloodGroup->find('list'));
// view
echo $this->Form->input('blood_group_id');
If you don't follow the conventions for some reason:
echo $this->Form->input('blood_group_id', array(
'options' => $bloodGroups
));
See:
Linking Models Together
The Form Helper
Create one function in BloodGroup Model
function getDonors(){
$options = array(
// 'conditions' => array('Donor.blood_group_id'=>$id),
'joins' => array(
array(
'alias' => 'Donor',
'table' => 'donors',
'type' => 'LEFT',
'conditions' => array(
'Donor.blood_group_id = BloodGroup.blood_group_id',
),
)
),
'fields' => array('Donor.name','Donor.surname','Donor.blood_group_id',
'BloodGroup.blood_group_id')
);
$returnData = $this->find('all',$options);
return $returnData;
}
Now from controller call this function
App::import('model','BloodGroup');
$BloodGroup = new BloodGroup;
$donorList = $BloodGroup->getDonors();
$this->set('donorList',$donorList);
In view file you will get list of donors in $donorList.

how to use cakedc/search plugin for searching across 3 different tables with 1 search bar?

I am using CakePHP2.4 and the search plugin https://github.com/CakeDC/search
I have the following
Employee hasOne EmployeeProfile
Employee hasMany Qualification
So I have a single search bar.
the search bar will search using LIKE through the following fields
Employee.name
EmployeeProfile.email
Qualification.title
how do I configure the model Employee->filterArgs for this search?
This is a cross-posting of the original issue here
The documentation includes an example.
'username' => array('type' => 'like', 'field' => array('User.username', 'UserInfo.first_name')),
You just have to make sure the models you're calling are available in your find() call. In the example the find will do a like on the users table username and on the user_infos first_name field at the same time.
I'd like to expand on this as I've been trying to setup a search on a hasMany relationship for a few hours and couldn't find anything. Mark mentionned "custom bindModel (as hasOne) for hasMany relationship (Qualification)". Here's how it's done :
$this->Employee->bindModel(array(
'hasOne' => array(
'Qualification' => array(
'foreignKey' => false,
'conditions' => array('Employee.id = Qualification.employee_id')
)
)
), false);
Just bind it before your paginate and add Qualification.title in your field list in your filterArgs

Can a virtualfield be based upon linked data in cakephp?

Is it possible for a virtualFields var to be the sum of a field from a linked table?
For example, in, say, an Invoice model, could you have
public $virtualFields = array(
'invoiceNett' => 'SUM(InvoiceLine.nett)'
);
but obviously only SUMming the lines that belong to that invoice?
Thanks.
== Using CakePHP 2.0
You could use the afterFind callback to get the sum. This avoids storing the calculated values​​, which should be avoided when possible.
function afterFind($results)
{
foreach($results as &$result)
{
/*
Use something like:
$this->InvoiceLine->find('all', array('fields' => array('SUM(InvoiceLine.nett) as total'),
'conditions' => array('invoice_id' => $result['Invoice']['id'])));
*/
}
unset($result);
}
As far as I know, the best way to do that would be to have an actual total field, and update it anytime the data is saved (likely with a afterSave callback method).
So - anytime an InvoiceLine is saved, you run some code to update it's associated Invoice with a new total.
//InvoiceLine model
public function beforeSave() {
//code to update Invoice's "total" field
}
Theoretically, yes, if that linked table is an associate that is joined (belongsTo and hasOne).
However this would be a poor idea because if you decide not to include that table you would generate a SQL error.
You'd be better off having a separate function grab the data or creating a virtual field that was a nested SQL query.
Defining your virtual field in the respective Model would make more sense.
If not done so, you will be breaking the MVC pattern.
You can use that virtual field from other related models.
If you don't want using them in all related models, you can always use the field
attribute whilst defining relations.
public $hasMany = array(
'IwantVirtualField' => array(
'className' => 'MyModel',
...
)
);
In a model where you don't want virtual field
public $belongsTo = array(
'IwantVirtualField' => array(
'className' => 'MyModel1',
'fields' => array('MyModel1.id', 'MyModel1.name')
...
)
);

Cakephp Multiple Relations to the Same Model

I'm working on a real estate application, where one Home can be listed by either one or two Realtors. As such, I'm trying to follow the example given in section 3.7.6.7 of the Cake Cookbook. Here's what's going on in my Realtors model:
class Realtor extends AppModel {
var $primaryKey = $num;
var $name = 'Realtor';
var $hasMany = array('Homes' =>
array('className' => 'Home',
'foreignKey' => 'realtor_num',
...),
'CoListedHomes' =>
array('className' => 'Home',
'foreignKey' => 'realtor2_num',
...)
);
This completely breaks the application, with the error message "Database table co_listed_homes for model CoListedHomes was not found."
Referring back to the Cookbook 3.7.6.7, second example, it seems clear that they shouldn't have or need separate "messages_received" and "messages_sent" tables in their database, so what is it that I'm doing wrong?
ETA: Weirdly (to me) I think I got the relationships to work by swapping around the order of the arrays: the first array, on foreignKey = realtor2_num, is called 'ListedHomes', the second array, on foreignKey = realtor_num is called 'Home'. I suppose my question then is, why should the order matter, and why should Cake started talking about unfound database tables in some circumstances?
Just in case, I'd have built this as a categories setup. Then created a join table between Category and Home.
Then you can have a hasAndBelongsToMany beween both categories and homes
"Database table co_listed_homes for model CoListedHomes was not found" probably refers to you not having a model defined for CoListedHomes that points to the homes table.
I would, however, code this as a n:m otherwise known as hasAndBelongsToMany, as stated by DavidYell.

Resources