We are using the new cakePHP ORM and I have some issues understanding the way the new ORM works when dealing with hasMany associations.
E.g. We have a Table called "Users" which has a hasMany relation to another Table "UsersAbsences". The definition and handling itself works fine. We have defined the Table-Objects and the Entities.
$this->addAssociations([
'hasMany' => [
'UsersAbsences' => [
'className' => 'UsersAbsences',
'propertyName' => 'absences',
'foreignKey' => 'user_id'
]
]);
Within our UsersTable we've created a custom method which retrieves details to one User, using the QueryBuilders ->contain Method to fetch the hasMany-associated "UsersAbsences" defined by property "absence". We're setting the view-variable $user to work with the Entity in the template: Works fine.
...
$users->contain(['UsersAbsences',...]);
...
Now the part that puzzles me:
The Object-type of $user is "UserEntity". When I now iterate through the $user->absences... i always get CakeEntity objects, not (as I would expect) AbsenceEntity-objects.
Problem: I would like to implement a custom method in the UserAbsence-Entity which I would like to call in the template when iterating through each Absence of the user.
When using the ->contain Method for associations attached with "belongsTo", I get the correct Entity-Class.
Someone an idea how to deal with that?
Mind your terminology, entity classes do not have a Entity suffix, this makes your question a little confusing, as one might think that you've named all your classes incorrectly! Please use actual, ideally fully qualified classnames in your questions!
That being said, by default the name of the corresponding entity is figured from the table class name, that is, the singular name without Table suffix. So, for UsersTable it's User, and for UsersAbsencesTable it would be UsersAbsence, not just Absence.
So, renaming your Absence entity file/class to UsersAbsence should fix the problem. Alternatively you can manually define the entity class a table should use by specifying it via Table::entityClass().
See also
Cookbook > ... CakePHP Conventions > File and Class Name Conventions
Cookbook > ... ORM > Table Objects > Customizing the Entity Class a Table Uses
Related
Cakephp 2.10 ?
$text = $this->PrivacyPolicy->find('all', array(
'fields' => array('id', 'title', 'description')
));
How PrivacyPolicy is becoming a model role as there is no Model class in the files or relation with the database ?
Magic and unicorn dust... otherwise known as naming conventions and auto-models.
If no concrete model class can be found, CakePHP will create an instance of the AppModel base class instead, which will lookup the database table based on the the model name that you've used. So in your case the AppModel instance will look up the data in privacy_policies (lowercased, underscored, plural variant of the model name).
Quote from the docs:
CakePHP will dynamically create a model object for you if it cannot find a corresponding file in /app/Model. This also means that if your model file isn’t named correctly (for instance, if it is named ingredient.php or Ingredients.php rather than Ingredient.php), CakePHP will use an instance of AppModel rather than your model file (which CakePHP assumes is missing). If you’re trying to use a method you’ve defined in your model, or a behavior attached to your model, and you’re getting SQL errors that are the name of the method you’re calling, it’s a sure sign that CakePHP can’t find your model and you need to check the file names, your application cache, or both.
https://book.cakephp.org/2/en/models.html#understanding-models
I have 3 models.
Webcast and Tag are associated witha HABTMA association.
Webcast and Host are associated with a hasMany relationship (Webcast has many Host).
When I do a Tag->find I get Tag and Webcast models, however I want to get all 3. How can I go about that?
If your query is using $this->webcast->find then you'll get everything your looking for, except you can't search 'TAG' without joining the tables before the query. If you want to search 'TAG' which I recommend in this situation then your need to go into your Tag model and create relationships there too.
Tag HABTM Webcast
should do it. If you're not getting host then try 'recursive' => 2 in your query.
$this->Tag->find('all');
OR
$this->Tag->find('all', array('recursive' => 2));
From docs
The recursive property defines how deep CakePHP should go to fetch associated model data via find(), and read() methods.
http://book.cakephp.org/2.0/en/models/model-attributes.html#recursive
Scenario
I have Provider and Package.
A provider can have a number of featured packages.
So we need a HABTM between Provider and Package
I want to save all the providers featured packages in one shot using the Provider::edit() method
Setup
I have three models.
Provider
FeaturedPackage
Package
Models
These are setup using the 'HABTM Through', which should not be confused with HABTM. So the relationships are as follows.
Provider hasMany FeaturedPackage
FeaturedPackage belongsTo Provider
Package hasMany FeaturedPackage
FeaturedPackage belongsTo Package
Controller
public function admin_edit($id) {
if ($this->request->is('post') || $this->request->is('put')) {
if ($this->Provider->saveAll($this->request->data)) {
// snip
View
echo $this->Form->input('FeaturedPackages', array('type' => 'select', 'multiple' => true, 'options' => $packages));
The issue
I'm not sure how to save multiple variable numbers of hasMany records from one side of the relationship. I know from reading the book that the expected data array should be numerically indexed beneath the model.
However I'm not sure what to name my field to get the data formatted correctly.
I am unable to bake the view as it seems to ignore the hasMany and not even add a field for it.
Field names I've tried
FeaturedPackage
FeaturedPackage.FeaturedPackage
FeaturedPackage.package_id
FeaturedPackage..package_id
FeaturedPackage.[].package_id
FeaturedPackage.package_id][
I've solved this using a data massage method in my model, which is called from the controller.
This goes through and updates the array submitted from the form to match what is expected by the save.
I blogged my solution, http://jedistirfry.co.uk/blog/2013-08/how-to-create-hasmanythrough-multi-selects/
I was planning my database and since CakePHP hasn't named its classes with its own prefixes, there are many classes that may collison with model classes created according to naming conventions.
So my question is in three parts:
Is there a list of database
table names that are reserved or a
simple way to check if it is? It
would be a pain if I plan database
with 100 tables and notice some of
the tables and their connections would
have to be renamed...
Is there a simple way around it without breaking any CakePHP magic? Is it adviced? Would naming the tables in other way by adding own prefix 'my_' or similar at the beginning of table name the best way?
Is namespaces or something similar coming to
CakePHP version 2 that would allow
use of all kinds of table names?
No, there aren't. Cake doesn't care what you name your tables as long as you adhere to Cake's naming conventions. It generates the schemas it uses for magic model methods the first time a model/s is loaded by a controller; you don't have to lift a finger. See http://book.cakephp.org/view/903/Model-and-Database-Conventions
Best advice: don't fight Cake on this. If you really cannot adhere to to Cake's conventions, you might as well not use Cake; it's stupidly difficult, confusing and succeeding just means you've lost most of Cake's heavy lifting abilities. Pluralizing your table names isn't THAT bad, and Cake will be happy.
This functionality is already available in 1.3 - name your tables anything that pleases you (as long as they're plural words.)
-- You'd probably be well-served to check out baking apps in the console so you can get familiar with what Cake wants to see and how it works on different table layouts.
Edit after clarification:
Your models, controllers, and view directories all share a common name, like so:
// in /app/models/rate.php
class Rate extends AppModel {
var $name = 'Rate';
// in /app/controllers/rates_controller.php -- note the underscore
class RatesController extends AppController {
// controllers are capitalized + plural
var $name = 'Rates';
// in /app/views/rates/*.ctp - Cake's magic autoRender expects to find a view file with
// the same name as the action rendering it - the view for Rates::index() is index.ctp
All of your models extend cake's AppModel class (which extends cake's Model class), no prefix needed. All controllers will extend Cake's AppController class - the class name is suffixed with Controller, and the file name is suffixed with _Controller.
You'll fine AppModel and AppController in /app, and they exist expressly for whatever app-wide custom methods / properties you may have. Since all of your models / controllers extend them, inheritance automatically disperses whatever properties / methods you place in them - for example, Auth. ^_^
But you can still name a table Models, or Controllers, or Views, or whatever, I guess. The $name property is an alias; you can create multiple instances of the same table in the same model by aliasing it with a different name. You can create models without tables, and you can switch between multiple tables - or databases, or servers - in a single model. You can also create non-database-type data objects (such as flat xml files) for your models. Dynamically named classes / methods ($$Model::save(), etc) are what's running under the hood anyway. I've done something similar in iterations for the sake of DRYing up my app and I didn't have a problem. (Although I personally doubt actually pulling off a local model named Model would be worth the effort you'd put into the experiment...)
And on that note, Cake's API spells out all it's classes their methods, etc. (generates off the comments in the codebase):
http://api13.cakephp.org/classes
HTH. :D
I know from experience you can't use table names like 'files' and 'models' because they create classes that are already used by Cake for other things such as File and Model.
I haven't come across any other problems like this but I'm sure they are there to be found.
I would suggest avoiding the use of any name used by cake core classes.
I know this is a bit of an old thread but just came across it searching for something else. I think this shoud help answer question #2. In database.php you can add your db table prefix in the DATABASE_CONFIG class /app/config/database.php. See the last key in the config array below:
var $default = array(
'driver' => 'mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'user',
'password' => 'password',
'database' => 'database_name',
'prefix' => '',
);
I have a few models, all with appropriately named model files.
$this->Property->PropertyImage->Image->read();
All linked accordingly. Problem is, the recursive model is not able to attach to all the relationships and for some reason is returning AppModel data type when i do a var_dump on $this->PropertyImage. When i do a var_dump($this->Property); i get data type: Property.
What is going on here, what would cause this to happen? Also how can I fix this problem?
Do you hava a PropertyImage model in your application or is it a HABTM association? If you're having a Property which hasAndBelongsToMany Image, you need a pivot table (properties_images) in the database, but to access Image model from the PropertiesController, you'd do $this->Property->Image without anything in between.
Building on what Marko said, if you have a HABTM relationship your best bet is to use a join table, properties_images.
Then rather than doing a Property->PropertyImage->Image, you would just do a Property->Image->read/find().
What I failed to understand about the HABTM relationship is how to filter based on criteria in the related model. E.g., you cannot do this:
$this->Property->Image->find( 'all', array( 'conditions' => array( 'Image.id' => 7 ) ) );
Instead, you have to add the Containable behavior to the Property model, as described in the manual at http://book.cakephp.org/view/474/Containable.