I want to use aliases in cakephp(2.5.3) models which names are different from the name of the related model. The problem occures when I want o save data with saveAssociated method. My models have hasMany and belongsTo relations. When I want to save data it says there is na error. I dig the problem and think that this could be a problem with validateAssociated method which can change $data value or my knowledge of CakePHP is still on newbe level... Can anyone explain me what may I do wrong ?.
My Model A:
public $hasMany = array(
'alias1' => array(
'className' => 'AB',
'foreignKey' => 'a_id'
),
);
My Model AB:
public $belongsTo = array(
'alias2' => array(
'className' => 'A',
'foreignKey' => 'a_id'
),
'alias3' => array(
'className' => 'B',
'foreignKey' => 'b_id'
),
);
I try to save data with:
$this->alias2->alias1->saveAssocated($this->request->data)
Any idea ?
Related
I have problem with HABTM models. When I try to fetch any related model f.e. like this:
$this->Tagi->find('first');
I dont get any results for associated model. Result looks like this:
array(
'Tagi' => array(
'id' => '1',
'nazwa' => 'sth'
),
'Instytucje' => array()
)
I am sure that there should be result, I've double checked it, even
$this->Tagi->getDataSource()->getLog(false, false)
shows correct query, that fetches right results.
If you have any idea whats wrong plz give me a hint.
Tagi model:
public $hasAndBelongsToMany = array(
'Instytucje' =>
array(
'className' => 'Instytucje.Instytucje',
'joinTable' => 'instytucje-tagi',
'foreignKey' => 'tag_id',
'associationForeignKey' => 'instytucja_id',
'unique'=> true
)
);
Instytucje model:
public $hasAndBelongsToMany = array(
'Tagi' =>
array(
'className' => 'Instytucje.Tagi',
'joinTable' => 'instytucje-tagi',
'foreignKey' => 'instytucja_id',
'associationForeignKey' => 'tag_id',
'unique'=> true
)
);
EDIT:
Main problem is that HABTM refers to AppModel causing error:
Error: Table app_models for model AppModel was not found in datasource prod.
Which can be bypassed by adding $useTable in AppModel, which results in prior problem.
SOLVED
When using naming convention far beyond this Cake use, you have use third model with $useTable pointed on reference table.
Moreover its important to correctly point Cake to classes inside plugins.
I think you're problem relates to the fact that you are not using CakePHP's naming conventions. From the look of the query being generated Cake doesn't know how to correctly alias the join table, hence getting an AppModel alias in your query. I suspect that this is causing issues when Cake tries to build the results array from the query.
This might not work, but try creating a model for the joins table called InstytucjeTagi; then update your associations to use this using the with key:-
Tagi model:
public $hasAndBelongsToMany = array(
'Instytucje' =>
array(
'className' => 'Instytucje.Instytucje',
'joinTable' => 'instytucje-tagi',
'with' => 'InstytucjeTagi',
'foreignKey' => 'tag_id',
'associationForeignKey' => 'instytucja_id',
'unique'=> true
)
);
Instytucje model:
public $hasAndBelongsToMany = array(
'Tagi' =>
array(
'className' => 'Instytucje.Tagi',
'joinTable' => 'instytucje-tagi',
'with' => 'InstytucjeTagi',
'foreignKey' => 'instytucja_id',
'associationForeignKey' => 'tag_id',
'unique'=> true
)
);
If this doesn't fix it try using the afterFind() callback in the Tagi model to check what Cake is returning:-
afterFind($results, $primary = false) {
parent::afterFind($results, $primary);
// Print out the $results to check what Cake is returning.
debug($results);
return $results;
}
I have a big question, i have 3 days now trying to find the solution. I explain
I have 3 models: Comprobantes, Receptores and Puestos.
Comprobantes have a field called receptor_id which gives you the receptor and
Receptor have a field call puesto_id which gives you the puesto, something like
Comprobantes access to Receptores and Receptores access to Puestos, the thing now is that I had made a relation to show the field puesto from Puestos of my Receptor in the Comprobantes Model the only problem i had is that only the id is shown and i want to show the field "puesto" from Puesto not the "id" from Puesto.
edit: This is the code but Comprobantes dont have a direct relation with Puestos
class Puesto
{
var $hasMany = array(
'Receptor' => array(
'className' => 'Receptor',
'foreignKey' => 'puesto_id'
)
);
}
class Receptor
{
var $belongsTo = array(
'Emisor' => array(
'className' => 'Emisor',
'foreignKey' => 'emisor_id'
),
'Puesto' => array(
'className' => 'Puesto',
'foreignKey' => 'puesto_id'
)
);
}
how to get name of (UserTransactionType.name) with Transaction.who_pay_fee_1,2,3 fields.
'user_transaction_type_id' works well but how to get the rest of fields work :(
//Transaction Model
public $belongsTo = array(
'UserTransactionType' => array(
'className' => 'UserTransactionType',
'foreignKey' => 'user_transaction_type_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
//UserTransactionType Model
public $hasMany = array(
'Transaction' => array(
'className' => 'Transaction',
'foreignKey' => 'user_transaction_type_id',
'dependent' => false,
))
This is the sample code for your controller:
$this->UserTransactionType->find('all',array(
'fields' => array('name'),
'contain' => array('Transaction')
)
);
If Models are associated you can specify in 'contain' which of them you want to get in the result.
If you want to have only some fields of related Model, you can determine them after 'Transaction' in 'contain' just like in the regular find() query:
'contain' => array('Transaction' => array('fields' => array('field_1',
'field_2') ))
But in your case, you don't need to specify fields, because by default you get all fields.
So no matter if you define or not fields "who_pay_fee_1,2,3" because if you use 'contain' by default you will get foreing_key - user_transaction_type_id.
I hope it's helpful
For people they like CakePhp :)
in Controller ->
get the list of 'UserTransactionType'
in View ->
after looping trough all the transactions; in Transaction Status column simply load the 'UserTransactionType'array and assign the number of array to $userTransactionTypes.
$userTransactionTypes[$transaction['Who_pay_fee_1']];
To be honest it was straight forward but needed a bit concentration :)
I have 3 models (User, Message and Tag) with the following relations:
User hasMany Message
Message belongsto User
Message HABTM Tag
Tag HABTM Message
If a User is logged in he might want to see all Message tagged with something.
$messages = $this->Message->find('all', array(
'conditions' => array("Message.user_id" => $this->uid),
'contain' => array(
'Tag' => array(
'conditions' => array(
'Tag.id' => $activetag['Tag']['id']
)
)
));
However, this find will return ALL messages of that user. (Containable behaviour is included in both models)
Containable on child (tag) does not perform filter on the parent (message), that's why all the messages are returned. The containable only place condition on the tag itself, in your case, messages not matching $activeTag would still get returned but with empty tag array attached, while messages matching would return with an array containing only one tag, the $activeTag, but all messages would get returned.
For your purpose CakepHP recommend using join function for filtering with HABTM, it joins hasOne or belongsTo for you automatically but when it comes to HABTM you may need to perform the join yourself if needed.
assuming tables are named conventionally:
$this->Message->recursive = -1;
$options['joins'] = array(
array('table' => 'messages_tags',
'alias' => 'MessageTag',
'type' => 'INNER',
'conditions' => array(
'Message.id = MessageTag.message_id',
)
) );
$options['conditions'] = array(
'MessageTag.tag_id' => $activetag['Tag']['id'],
'Message.user_id' => $this->uid );
$message = $this->Message->find('all', $options);
more info here:
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#joining-tables
In your Model Message add
**
* #see Model::$actsAs
*/
public $actsAs = array(
'Containable',
);
/**
* #see Model::$belongsTo
*/
public $belongsTo = array(
'Message' => array(
'className' => 'Message',
'foreignKey' => 'message_id',
),
'Tags' => array(
'className' => 'Tag',
'foreignKey' => 'tag_id',
),
);
in your controller :
// $tagsId = tags ids
$message = $this->MessageTag->find('all', array('conditions' => array('MessageTag.tag_id' => $tagsId),'contain' => array('Message')));
also is better follow cake naming convention, if you have tags(plural), message_tags(first singular second plural),messages(plural) tables you must have Tag,MessageTag,Message Models.
In my CakePHP app I have models for Matches and Teams. Each Match has a home_team_id and an away_team_id, both of which reference a different Team.
In my team.php file, I am able to form the relationship for a Team's home matches:
var $hasMany = array(
'HomeMatch' => array('className' => 'Match', 'foreignKey' => 'home_team_id'),
'AwayMatch' => array('className' => 'Match', 'foreignKey' => 'away_team_id')
);
My problem is that I cannot automatically retrieve a Team's home and away Matches in a single array. That is, the retrieved Matches are returned in separate HomeMatch and AwayMatch arrays, which causes sorting difficulties.
I have tried the following:
var $hasMany = array(
'Match' => array('foreignKey' => array('home_team_id', 'away_team_id'))
);
...with no luck.
Any ideas on how to combine these two foreign keys into a single relationship?
Thanks, Ben
A custom finderQuery should do the trick:
public $hasMany = array(
'Match' => array(
'className' => 'Match',
'foreignKey' => false,
'finderQuery' => 'SELECT *
FROM `matches` as `Match`
WHERE `Match`.`home_team_id` = {$__cakeID__$}
OR `Match`.`away_team_id` = {$__cakeID__$}'
)
);
I was having a similar issue and instead of creating a finderQuery I used the conditions operator and it worked great!
public $hasMany = array(
'Match' => array(
'className' => 'Match',
'foreignKey' => false,
'conditions' => array(
'OR' => array(
array('Match.home_team_id' => '{$__cakeID__$}'),
array('Match.away_team_id' => '{$__cakeID__$}')
)
),
)
);
They are returned in seperate array's because the sort of represent different models (in this particular case the model is the same).
You should probably build a helper method to go over the retrieved data (in the model object or in a separate helper class) and "flatten" it. then you'd be able to sort it.
Ken.