I have four tables:
gallery_categories table:
id (int);
title (varchar);
gallery_pictures table:
id (int);
title (varchar);
gallery_categories_gallery_pictures table to joint above tables:
gallery_category_id (int);
gallery_picture_id (int);
and the i18n table:
id (int)
locale (varchar)
model (varchar)
foreign_key (int)
field (varchar)
content (text)
I tried:
$this->GalleryCategory->bindModel(array(
'hasOne' => array(
'GalleryCategoriesGalleryPicture',
'GalleryPicture' => array(
'className' => 'GalleryPicture',
'foreignKey' => false,
'conditions' => array('GalleryPicture.id = GalleryCategoriesGalleryPicture.gallery_picture_id')
))));
$galleryCategories = $this->GalleryCategory->find('all', array(
'fields' => array('GalleryCategory.*','GalleryPicture.*')
));
But all time it returns with the right Category table fields and wrong Pictures table fields...
The Category fields contain the right translated words.
The Pictrures fields contain the wrong translated words.
The Models:
class GalleryCategory extends AppModel
{
public $tablePrefix = 'ef_';
var $name = 'GalleryCategory';
public $actsAs = array('Translate' => array(
'title' => 'titleTranslation'
)
);
}
class GalleryPicture extends AppModel
{
public $tablePrefix = 'ef_';
var $name = 'GalleryPicture';
public $actsAs = array('Translate' => array(
'title' => 'titleTranslation'
)
);
}
How could I get the right translated words from both table?
ps: The translation with "i18n Database Tables" works right with only one table (exp: only GalleryCategory or only GalleryPicture).
Translation doesn't work with associated models, see the last couple of lines here:
Note that only fields of the model you are directly doing find on will be translated. Models attached via associations won’t be translated because triggering callbacks on associated models is currently not supported.
Update
It might be tricky, as you have a HABTM relationship - previously I have worked around this by adding a second data retrieval to the afterFind callback of a model, but this was with a simpler association. Hope this helps.
Related
I'm working on a customer database. For larger business customers the relationships get a little complicated. It's almost working but one ID field is not being automatically filled.
I have 3 models, Customer, CustomerAddress and CustomerContact. A customer can have many addresses, and many contacts, but the contacts are also based at one address so an address has many contacts.
Database:
customer has id
customer_addresses has customer_id
customer_contacts has customer_id, customer_address_id
Models:
class Customer extends AppModel {
public $hasMany = array(
'CustomerContact',
'CustomerAddress',
);
....
}
class CustomerAddress extends AppModel {
public $belongsTo = array(
'Customer',
);
public $hasMany = array(
'CustomerContact',
);
....
}
class CustomerContact extends AppModel {
public $belongsTo = array(
'Customer',
'CustomerAddress',
);
....
}
I have one form that adds fields for all 3 models, and I'm calling '$this->Customer-saveAll' in the controller. It saves everything just fine, but doesn't fill in the 'customer_address_id' field in the 'customer_contacts' table.
I've tried changing the relationship in contacts to belongs to address, I've tried renaming the field to customer_addresses_id (made it pleural) just incase it was a quirk with Cake's internal naming, but it still doesn't fill the address ID field.
My workaround is to update the contact after the saveAll command in the controller, since the address has it's id set at this point:
$this->CustomerContact->saveField( 'customer_address_id', $this->CustomerAddress->id);
This is my first project with Cake and it seems really good at doing this stuff by itself, so I'm wondering if I've got something wrong somewhere.
TIA!
EDIT:
After going through the associations again, I changed the association on CustomerContact from hasOne to belongsTo CustomerAddress because I realised I got it wrong.
I tried 2 different ways of submitting the data:
'Customer' => .... data ....
'CustomerAddress' => array(
(int) 0 => array(
'CustomerContact' => array(
(int) 0 => array(
.... data ....
)
),
.... data ....
)
)
This sets the customer_address_id field on the contact, but not customer_id.
'Customer' => .... data ....
'CustomerAddress' => array(
(int) 0 => array(
.... data ....
)
),
'CustomerContact' => array(
(int) 0 => array(
.... data ....
)
)
This sets the customer_id but not the customer_address_id.
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);
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'));
I'm new to an existing Cake project where we are trying to use a virtualField within a model to alias another model field. In Context:
class Product extends AppModel {
var $name = 'Product';
var $hasOne = array('ProductPrice');
var $virtualFields = array(
'price' => 'ProductPrice.current_price'
);
// Typical fields in the database for Product. id, name, etc.
}
class ProductPrice extends AppModel {
var $name = 'ProductPrice';
var $belongsTo = array('Product');
// Fields are product_id, current_price
}
The ProductPrice model is for a view in the database that contains different price tiers with a current_price column allowing retrieval of the current price of the product. When accessing the Product model via something like:
$this->Product->find('all' ....);
I don't have any problems with getting the price field. The problem is if the query to Product is done indirectly through something like
$this->Inventory->find('all');
We get:
SQL Error: 1054: Unknown column 'ProductPrice.current_price' in 'field list' [CORE/cake/libs/model/datasources/dbo_source.php, line 681]
I know that the issue is that the SQL generated by the Inventory query does not attempt to join the ProductPrice view. I assumed that this would happen automagically via the Product model as it knows it "hasOne" ProductPrice.
I've tried setting "recursive" on the Inventory model to 2,1, etc. with no success.
What am I missing?
TLDR:
You cannot use fields from a different model in a VirtualField.
Other options:
If you're doing a query like:
$this->Inventory->find('all');
You can use something like CakePHP's Containable behavior to make sure you're getting the data you want:
//controller code
$inv = $this->Inventory->getInventory();
//model code
class Inventory extends AppModel {
public $actsAs = array('Containable');
public function getInventory() {
return $this->find('all', array(
'contain' => array(
'Product' => array(
'ProductPrice'
)
)
));
}
}
Using containable like in the above code example should return the data in a format something like this:
[0] => Array
(
[Inventory] => Array
(
[id] => 12345
)
[Product] => Array
(
[0] => Array
(
[id] => 54321
[title] => product A
)
[ProductPrice] => Array
(
[id] => 6789
[current_price] => 24.99
)
)
//...
When you get the data like that, it should be easy to access the Product's current price.
You could also just do it in the controller, but it's better practice to keep your queries in the model to stay within the "Fat Model, Skinny Controller" mantra. If you really want to keep it in your controller, you can just do this:
$inv = $this->find('all', array(
'contain' => array(
'Product' => array(
'ProductPrice'
)
)
);
(BUT - you still have to specify that a model $actsAs Containable (per first code example).
I am working on a plugin for our CakePHP CMS that will handle blogs. When getting to the tags I needed to set the HABTM relationship to unique = false to be able add tags to a post without having to reset them all.
The BlogPost model looks like this
class BlogPost extends AppModel {
var $name = 'BlogPost';
var $actsAs = array('Core.WhoDidIt', 'Containable');
var $hasMany = array('Blog.BlogPostComment');
var $hasAndBelongsToMany = array('Blog.BlogTag' => array('unique' => false), 'Blog.BlogCategory');
}
The BlogTag model looks like this
class BlogTag extends AppModel {
var $name = 'BlogTag';
var $actsAs = array('Containable');
var $hasAndBelongsToMany = array('Blog.BlogPost');
}
The SQL error I am getting when I have the unique => true setting in the HABTM relationship between the BlogPost and BlogTag is
Query: SELECT `Blog`.`BlogTag`.`id`, `Blog`.`BlogTag`.`name`, `Blog`.`BlogTag`.`slug`, `Blog`.`BlogTag`.`created_by`, `Blog`.`BlogTag`.`modified_by`, `Blog`.`BlogTag`.`created`, `Blog`.`BlogTag`.`modified`, `BlogPostsBlogTag`.`blog_post_id`, `BlogPostsBlogTag`.`blog_tag_id` FROM `blog_tags` AS `Blog`.`BlogTag` JOIN `blog_posts_blog_tags` AS `BlogPostsBlogTag` ON (`BlogPostsBlogTag`.`blog_post_id` = 4 AND `BlogPostsBlogTag`.`blog_tag_id` = `Blog`.`BlogTag`.`id`)
As you can see it is trying to set the blog_tags table to 'Blog'.'BlogTag. which isn't a valid MySQL name.
When I remove the unique => true from the relationship it all works find and I can save one tag but when adding another it just erases the first one and puts the new one in its place.
Does anyone have any ideas? is it a bug or am I just missing something?
Cheers,
Dean
Dean
So do you have the tables blog_posts_blog_tags?
To quote the CakePHP literature on HABTM relationships
We'll need to set up an extra table in the database to handle HABTM associations. This new join table's name needs to include the names of both models involved, in alphabetical order, and separated with an underscore ( _ ).
So ( dropping the Blog bit for readability !) you need your posts tables, your tags table and your posts_tags table and then the HABTM definition is
class Post extends AppModel {
var $name = 'Post';
var $hasAndBelongsToMany = array(
'Tag' =>
array(
'className' => 'Tag',
'joinTable' => 'posts_tags',
'foreignKey' => 'post_id',
'associationForeignKey' => 'tag_id',
'unique' => true,
)
);
}