I have a problem with an HABTM relation... I have two models: Tablets habtm Terms. When I query for a term, I can get the tablets but not the terms related to the tablets. My model file seems fine, the table in the database also, the query is ok, where shold I look for the problem ?!
Term model :
class Term extends AppModel {
var $name = 'Term';
var $displayField = 'term';
var $actsAs = array('Searchable','ExtendAssociations','Tree', 'Containable');
var $validate = array(
'term' => array(
'rule' => 'notEmpty'
)
);
var $belongsTo = array(
'WordType' => array(
'className' => 'WordType',
'foreignKey' => 'word_type_id'
),
'ParentTerm' =>
array( 'className' => 'Term',
'foreignKey' => 'parent_id'
)
);
var $hasAndBelongsToMany = array(
'Tablet' =>
array(
'className' => 'Tablet',
'joinTable' => 'tablets_terms',
'foreignKey' => 'term_id',
'associationForeignKey' => 'tablet_id',
'unique' => true,
)
);
}
Tablet model :
class Tablet extends AppModel {
var $name = 'Tablet';
var $displayField = 'no_museum';
var $actsAs = array ('Searchable','ExtendAssociations', 'Containable');
var $belongsTo = array( 'DateDay','DateMonth','Collection', 'Period', 'ArchLoc', 'ArchSite', 'Genre','Month', 'Year','Project', 'Collection','Ruler', 'MainVerb', 'Official','MainAction');
var $hasMany = array('TabletFile');
var $hasAndBelongsToMany = array(
'Tag' =>
array(
'className' => 'Tag',
'joinTable' => 'tablets_tags',
'foreignKey' => 'tablet_id',
'associationForeignKey' => 'tag_id',
'unique' => true,
),
'FromPerson' =>
array(
'className' => 'FromPerson',
'joinTable' => 'from_people_tablets',
'foreignKey' => 'tablet_id',
'associationForeignKey' => 'term_id',
'unique' => true,
),
'ToPerson' =>
array(
'className' => 'ToPerson',
'joinTable' => 'tablets_to_people',
'foreignKey' => 'tablet_id',
'associationForeignKey' => 'term_id',
'unique' => true,
),
'FromLocation' =>
array(
'className' => 'FromLocation',
'joinTable' => 'from_locations_tablets',
'foreignKey' => 'tablet_id',
'associationForeignKey' => 'term_id',
'unique' => true,
),
'ToLocation' =>
array(
'className' => 'ToLocation',
'joinTable' => 'tablets_to_locations',
'foreignKey' => 'tablet_id',
'associationForeignKey' => 'term_id',
'unique' => true,
),
'Language' =>
array(
'className' => 'Language',
'joinTable' => 'languages_tablets',
'foreignKey' => 'tablet_id',
'associationForeignKey' => 'language_id',
'unique' => true,
),
'Group' =>
array(
'className' => 'Group',
'joinTable' => 'groups_tablets',
'foreignKey' => 'tablet_id',
'associationForeignKey' => 'group_id',
'unique' => true,
),
'Term' =>
array(
'className' => 'Term',
'joinTable' => 'tablets_terms',
'foreignKey' => 'tablet_id',
'associationForeignKey' => 'term_id',
'unique' => true,
),
'Keyword' =>
array(
'className' => 'Keyword',
'joinTable' => 'keywords_tablets',
'foreignKey' => 'tablet_id',
'associationForeignKey' => 'keyword_id',
'unique' => true,
)
);
function beforeSave() {
$termIds = $this->_getTermIds();
if(!empty($termIds)) {
if(!isset($this->data['Term']['Term'])) {
$this->data['Term']['Term'] = $termIds;
} else {
foreach($termIds as $termId) {
$this->data['Term']['Term'][] = $termId;
}
}
}
//debug($this);
return true;
}
function _getTermIds() {
$terms=$this->data['Tablet']['translit'];
$terms= str_replace(array('\r\n', '\r', '\n','\n\r','\t'), ' ', $terms);
$terms = trim($terms, chr(173));
mb_internal_encoding("UTF-8");
mb_regex_encoding("UTF-8");
$terms = mb_ereg_replace('\s+', ' ', $terms);
$terms = explode(" ", $terms);
$terms=array_map('trim', $terms);
$anti_terms = array('#tablet','1.','2.','3.','4.','5.','6.','7.','7.','9.','10.','11.','12.','13.','14.','15.','16.','17.','18.','19.','20.','1\'.','2\'.','3\'.','4\'.','5\'.','Rev.',
'Obv.','#tablet','#obverse','#reverse','S1','S2','C1','C2','C3','C4','C5','C6','C7','C8','C9', '\r', '\n','\r\n', '\t',''. ' ', null, chr(173), 'x', '[x]','[...]' );
foreach($terms as $key => $term) {
if(in_array($term, $anti_terms) || is_numeric($term)) {
unset($terms[$key]);
}
}
if(Set::filter($terms)) {
foreach($terms as $term) {
$this->Tablet->Term = $this->Term;
$existingTerm = $this->Tablet->Term->find('first', array('conditions' => array('Term.term' => $term)));
if(!$existingTerm) {
$this->Term->create();
$this->Term->saveField('term', $term);
$termIds[] = $this->Term->id;
}
else {
$termIds[] = $existingTerm['Term']['id'];
}
}
return array_unique($termIds);
}
return false;
}
}
Term controller
function view($id = null) {
if (!$id) {
$this->Session->setFlash(__('Invalid term', true));
$this->redirect(array('action' => 'index'));
}
$term=$this->Term->find( 'first', array( 'conditions' => array('Term.id' => $id),/* 'recursive'=> 2 */ 'contain'=>array( 'WordType', 'Tablet' => array('Term') )));
$this->set('term', $term);
}
In the view :
<?php debug($term);?>
result :
Array
(
[Term] => Array
(
[id] => 626
[word_type_id] => 7
[term] => [ubi.]ku6
[sumerograms] =>
[translation] =>
[akkadian] =>
[comments] =>
[bibliography] =>
[parent_id] => 0
[lft] => 446
[rght] => 447
)
[WordType] => Array
(
[id] => 7
[word_type] => unknown
[parent_id] => 0
)
[Tablet] => Array
(
[0] => Array
(
[id] => 97
[arch_loc_id] =>
[arch_site_id] => 3
[period_id] => 1
[genre_id] => 2
[month_id] => 1
[year_id] => 3
[collection_id] => 14
[no_arch] =>
[no_museum] => AO
[no_cdli] => P220929
[no_etcsl] =>
[no_perso] => DP 279
[size] =>
[date_day] => 0
[date_month] => 0
[subject] => livraison de poissons par de nombreux pêcheurs pour Baranamtara
[translit] => Obv.
C1
1. 3 u tar.ku6
2. 2 geš’u 6 geš2 [ubi.]ku6
3. [1] [geš’u] [gir.ku6] [mun]
4. [1] [geš2] [3] [u] [ba]
5. [sila3] [i3-ku6]
etc etc bla bla long text.
[translation] =>
[abstract] =>
[epi_notes] =>
[comments] => Pour un festival !
[publications] => DP 279, Allotte de la Fuÿe, François M., 1912
[project_id] => 2
[ruler_id] => 61
[main_verb_id] => 345
[official_id] => 165
[main_action_id] => 9
[date_day_id] =>
[date_month_id] =>
[TabletsTerm] => Array
(
[id] => 19482
[tablet_id] => 97
[term_id] => 626
)
)
)
)
notice that cakephp created the model TabletsTerm and not TabletsTerms, is that a cue ?
Anyways I just do not know where to look anymore. Any suggestions ?
Thank you !
Related
I'm trying to do this with last 2.x CakePHP version
http://i.stack.imgur.com/IylLu.png
Without education_id in projects_students table works perfectly. But doesn't work with education_id.
this is my implementation with education_id:
ProjectsStudent.php
class ProjectsStudent extends AppModel {
public $belongsTo = array(
'Project' => array(
'className' => 'Project',
'foreignKey' => 'project_id'
),
'Student' => array(
'className' => 'Student',
'foreignKey' => 'student_id'
)
);
Student.php
public $hasMany = array('ProjectsStudent');
public $hasAndBelongsToMany = array(
'Education' => array(
'className' => 'Student',
'joinTable' => 'projects_students',
'foreignKey' => 'student_id',
'associationForeignKey' => 'project_id',
),
);
Project.php
public $belongsTo = 'Status';
public $hasMany = array(
'Doc' => array(
'className' => 'Doc',
'joinTable' => 'docs',
'foreignKey' => 'project_id',
),
'History' => array(
'className' => 'History',
'joinTable' => 'history',
'foreignKey' => 'project_id',
),
'ProjectsStudent'
);
public $hasAndBelongsToMany = array(
'Student' => array(
'className' => 'Student',
'joinTable' => 'projects_students',
'foreignKey' => 'project_id',
'associationForeignKey' => 'student_id',
),
'Professor' => array(
'className' => 'Professor',
'joinTable' => 'projects_professors',
'foreignKey' => 'project_id',
'associationForeignKey' => 'professor_id',
),
'Education' => array(
'className' => 'Education',
'joinTable' => 'projects_educations',
'foreignKey' => 'project_id',
'associationForeignKey' => 'education_id',
),
);
ProjectsController.php
$data = array(
'Project' => array(
'id' => $id,
'status_id' => 5,
'Student' => array($this->Auth->user('id'))),
'History' => array(
array(
'action' => 'update -> status 5',
'student_id' => $this->Auth->user('id'),
)
),
'ProjectsStudent' => array(array(
'education_id' => "5"))
);
$this->Project->saveAll($data, array('deep' => true))
Output error:
Error: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '109' for key 'PRIMARY'
SQL Query: INSERT INTO tfg.projects_students (project_id, education_id) VALUES (109, 5)
I think CakePHP is trying 2 inserts, with (project_id, student_id) and with (project_id, education_id)
I want just one input like INSERT INTO tfg.projects_students (project_id,student_id,education_id) VALUES (109, 1, 5)
Thanks in advance, sorry for my english.
---SOLUTION---
$data = array(
'Project' => array(
'id' => $id,
'status_id' => 5,
'Student' => array($this->Auth->user('id'))),
'History' => array(
array(
'action' => 'update -> status 5',
'student_id' => $this->Auth->user('id'),
)
),
);
$this->Project->saveAll($data, array('deep' => true));
$this->Project->ProjectsStudent->updateAll(
array('education_id' => $this->Auth->user('education')),
array('project_id' => $this->Project->id));
You appear to have setup the projects_students table to use project_id as the primary key. The primary key needs to be unique whereas the project_id foreign key is likely to be duplicated (as appears to be happening for you). The join table should be at least:-
id (int)
project_id (int)
student_id (int)
Where id is the primary key (with auto increment enabled).
I am trying to save data with multiple HABTM & hasMany relation. Even if I use
$this->Project->save();
$this->Project->saveMany();
$this->Project->saveAssociated();
$this->Project->saveAlll();
It does not save associated model, I can't figure out what I am doing wrong.
Models Reations
Project hasAndBelongsToMany Skill
Project hasMany Milestone
Project hasAndBelongsToMany Attachment (because Milestone's will also have attachement so can not bind project & attachment in hasMany Relation)
Project Model
class Project extends AppModel{
public $belongsTo = array('Profile');
public $hasMany = array('Milestone');
public $hasAndBelongsToMany = array(
'Skill' =>
array(
'className' => 'Skill',
'joinTable' => 'projects_skills',
'foreignKey' => 'project_id',
'associationForeignKey' => 'skill_id',
'dependent' => true,
//'conditions' => array('Advertisement.advertisement_is_active' => '1')
),
'Attachment' =>
array(
'className' => 'Attachment',
'joinTable' => 'attachments_projects',
'foreignKey' => 'project_id',
'associationForeignKey' => 'attachment_id',
)
);
public function beforeSave($options = array()) {
parent::beforeSave($options = array());
foreach (array_keys($this->hasAndBelongsToMany) as $model):
if(isset($this->data[$this->alias][$model])):
$this->data[$model][$model] = $this->data[$this->alias][$model];
unset($this->data[$this->alias][$model]);
endif;
endforeach;
return true;
}
}
Skill Model
class Skill extends AppModel {
public $name = 'Skill';
public $hasOne = array('Exam');
public $hasAndBelongsToMany = array(
'Category' =>
array(
'className' => 'Category',
'joinTable' => 'categories_skills',
'foreignKey' => 'skill_id',
'associationForeignKey' => 'category_id',
'dependent' => true
),
'Profile' =>
array(
'className' => 'Profile',
'joinTable' => 'profiles_skills',
'foreignKey' => 'skill_id',
'associationForeignKey' => 'profile_id',
'dependent' => true
),
'Project' =>
array(
'className' => 'Project',
'joinTable' => 'projects_skills',
'foreignKey' => 'skill_id',
'associationForeignKey' => 'project_id',
//'dependent' => true
)
);
}
Attachment Model
class Attachment extends AppModel{
public $name = 'Attachment';
public $hasAndBelongsToMany = array(
'Project' =>
array(
'className' => 'Project',
'joinTable' => 'attachments_projects',
'foreignKey' => 'attachment_id',
'associationForeignKey' => 'project_id',
'dependent' => true,
)
);
}
Data
Array
(
[Project] => Array
(
[project_name] => Name Of Project
[project_detail] => Dummy short description
[project_category_id] => 3
[project_budget] => 45
[Skill] => Array
(
[0] => Array
(
[skill_id] => 14
)
[1] => Array
(
[skill_id] => 16
)
)
[project_status] => 1
[project_created] => 2014-08-04 15:31:00
[creator_id] => 1
)
[Milestone] => Array
(
[milestone_name] => Milestone 1
[milestone_budget] => 45
[milestone_created_on] => 2014-08-04 15:31:00
[project_id] => 8
)
[Attachment] => Array
(
[attachment_path] => 11615297103.zip
[attachment_term] => Project Attachment
[attachment_type] => zip
[project_id] => 8
)
)
Pass Skill id not the field name of relation model
[Skill] => Array
(
[0] => Array
(
[id] => 14
)
[1] => Array
(
[id] => 16
)
)
I'm going through the following links to use Acl component in my application
http://book.cakephp.org/2.0/en/core-libraries/components/access-control-lists.html
http://code.tutsplus.com/tutorials/how-to-use-cakephps-access-control-lists--net-1345
in usersController i have a function to install aros and acos
public function install(){
$aro = $this->Acl->Aro;
$aco = $this->Acl->Aco;
$aro_groups = array(
0 => array(
'alias' => 'admin'
),
1 => array(
'alias' => 'operator'
),
2 => array(
'alias' => 'user'
),
);
$aco_groups = array(
0 => array(
'alias' => 'User'
),
1 => array(
'alias' => 'Supplier'
),
2 => array(
'alias' => 'Inventory'
),
3 => array(
'alias' => 'Invoice'
),
4 => array(
'alias' => 'Incentive'
),
5 => array(
'alias' => 'Promotion'
),
6 => array(
'alias' => 'Feedback'
),
7 => array(
'alias' => 'Message'
),
8 => array(
'alias' => 'History'
),
);
foreach($aro_groups as $data):
$aro->create();
$aro->save($data);
endforeach;
foreach($aco_groups as $data):
$aco->create();
$aco->save($data);
endforeach;
foreach($aco_groups as $data):
$this->Acl->allow('admin',$data);
$this->Acl->allow('operator',$data);
endforeach;
}
my user model is as follows:
public $belongsTo = array(
'Role' => array(
'className' => 'Role',
'foreignKey' => 'role_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
public function beforeSave($options = array()){
if(isset($this->data['User']['password']))
{
$this->data['User']['password']= AuthComponent::password($this->data['User']['password']);
}
}
public $actsAs = array('Acl' => array('type' => 'requester'));
public function parentNode() {
if (!$this->id && empty($this->data)) {
return null;
}
if (isset($this->data['User']['group_id'])) {
$groupId = $this->data['User']['group_id'];
} else {
$groupId = $this->field('group_id');
}
if (!$groupId) {
return null;
} else {
return array('Group' => array('id' => $groupId));
}
}
public function bindNode($user) {
return array('model' => 'Role', 'foreign_key' => $user['User']['role_id']);
}
role model:
public $hasMany = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'role_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
public $actsAs = array('Acl' => array('type' => 'requester'));
public function parentNode() {
return null;
}
now, when I try to access /users/install just to check that all relations are successfully created or not, i'm getting this error
AclNode::node() - Couldn't find Aco node identified by "Array ( [Aco0.model] => model [Aco0.foreign_key] => U ) "
Warning (2): Illegal string offset 'id' [CORE\Cake\Model\AclNode.php, line 140]
In the parentNode function in your User model, you have group_id instead of role_id.
Replace all occurrences of group_id to role_id in your User model and you should be ok and also Replace Group with Role
public function parentNode() {
if (!$this->id && empty($this->data)) {
return null;
}
if (isset($this->data['User']['role_id'])) {
$roleId = $this->data['User']['role_id'];
} else {
$roleId = $this->field('role_id');
}
if (!$roleId) {
return null;
} else {
return array('Role' => array('id' => $roleId));
}
}
I'm using Cakephp and try to make my model to ActAs 'Translation'. But i have a problem.
My Client model relations are:
public $belongsTo = array("User");
public $hasAndBelongsToMany = array(
'Category' =>
array(
'className' => 'Category',
'joinTable' => 'categories_clients',
'foreignKey' => 'client_id',
'associationForeignKey' => 'category_id',
'unique' => true,
),
);
In Category Model:
public $name = 'Category';
public $hasMany = array(
'Product'
);
public $displayField = 'title';
public $recursive = 1;
public $actsAs = array(
'Tree',
'Translate' => array(
'title',
'alias',
'description',
),
'Containable' => array('CategoryTranslations'),
);
public $translateModel = 'CategoryTranslations';
public $translateTable = 'category_translations';
When i query from Client Controller to get all Client and relative Category, i can't get field "title", "alias" and "description" of Category. Here is my code:
if(isset($this->passedArgs['language_id'])){
$language_id = $this->passedArgs['language_id'];
}
else{
$language_id = Configure::read('Config.language_site');
}
$this->Client->Category->locale = $language_id;
$this->Client->recursive = 1;
$this->paginate = array('limit' => 20,'order' => array('User.id' => 'desc'));
$Clients = $this->paginate();
Here is result:
array(
'Client' => array(
'id' => '1',
'user_id' => '1',
'client_type' => 'tenderer',
'image' => null,
'image_list' => null,
'code' => 'SG0593',
'name' => 'Oorts',
'address' => '1000 Huynh Tan Phat',
'telephone' => '0987654321',
'fax' => '0983213434',
'email' => 'nguyentuandat.vn#gmail.com',
'proxy' => 'Dat Nguyen',
'position' => 'C.E.O',
'mobile' => '0987654321',
'referred_by' => 'noone',
'order' => null,
'status' => true,
'created' => '2014-03-27 00:00:00',
'modified' => '2014-03-27 00:00:00'
),
'User' => array(
'password' => '*****',
'id' => '1',
'username' => 'admin',
'group_id' => '1',
'gender' => 'Male',
'first_name' => 'Nguyễn',
'middle_name' => 'Tuấn',
'last_name' => 'Đạt',
'email' => 'nguyentuandat.vn#gmail.com',
'phone_number' => '(+84) 947235313',
'image' => '/uploads/images/255443_102754699884371_1177788671_n.jpg',
'status' => true,
'created' => '2014-01-16 09:26:09',
'modified' => '2014-01-22 06:47:25',
'full_name' => 'Nguyễn Tuấn Đạt'
),
'Category' => array(
(int) 0 => array(
'id' => '1',
'parent_id' => '0',
'type' => 'product',
'image' => '',
'order' => '0',
'status' => true,
'lft' => '1',
'rght' => '16',
'created' => '2014-01-25 00:00:00',
'modified' => '2014-01-25 00:00:00',
'CategoriesClient' => array(
'id' => '1',
'category_id' => '1',
'client_id' => '1'
)
)
)
)
Can you help me? Thank you!
Restructure the code
Unfortunately because of how behaviors work - that's none-trivial to solve exactly as asked.
You can however do something like this:
$Clients = $this->paginate();
// extract category ids in the results
$categoryIds = array_unique(Hash::extract($Clients, '{n}.Category.{n}.id'));
$categories = $this->Client->Category->find('all', array(
'conditions' => array(
'Category.id' => $categoryIds
)
));
// generate a category-id indexed array of categories
$categories = Hash::combine($categories, '{n}.Category.id', '{n}.Category');
// replace the untranslated category entries with translated category data
foreach($Clients as &$client) {
foreach($client['Category'] as &$cat) {
$id = $cat['id'];
$cat = $categories[$id];
}
}
This example is shown as pseudo controller code - you could also consider putting it in your Client afterFind method or a custom finder.
Thank for help.
I found a solution:
I added bellow code into AppModel->afterFind and it work fine
public function afterFind($results, $primary = false) {
if(!empty($this->hasAndBelongsToMany)) {
foreach($this->hasAndBelongsToMany as $model => $settings) {
if(isset($this->{$model}->actsAs['Translate'])) {
if(!empty($results[0][$model])) {
foreach($results as $k => $v){
foreach($results[$k][$model] as $row => $result) {
$supplement = $this->{$model}->find('first', array(
'conditions' => array(
$model .'.id' => $result['id']),
'fields' => $this->{$model}->actsAs['Translate'],
'recursive' => -1));
if(!empty($supplement)) {
$results[$k][$model][$row] = array_merge($results[$k][$model][$row], array_diff($supplement[$model], $result));
}
}
}// end foreach k=>v
}
}
}
}
return $results;
}
I'm having problems with HABTM + cakephp.
In my app i have this models (with relation definition):
Cliente:
public $hasAndBelongsToMany = array(
'TiposComercio' => array(
'className' => 'TiposComercio',
'joinTable' => 'tipos_comercio_cliente',
'foreignKey' => 'cliente',
'associationForeignKey' => 'tipo_comercio'
)
);
TiposComercio:
public $hasAndBelongsToMany = array(
'Cliente' => array(
'className' => 'Cliente',
'joinTable' => 'tipos_comercio_cliente',
'foreignKey' => 'tipo_comercio',
'associationForeignKey' => 'cliente'
)
);
When the form is posted, i have in array $this->request->data this:
array(
'Cliente' => array(
'password' => '*****',
'razao_social' => 'Teste',
'responsavel' => 'responsavel',
'cidade' => '1',
'cep' => '13560201',
'logradouro' => 'Log test',
'numero' => '',
'bairro' => '',
'complemento' => '',
'atividades' => '',
'username' => 'username11',
'TiposComercio' => array(
(int) 0 => '1',
(int) 1 => '4'
)
)
)
When i execute $this->Cliente->saveAll($this->request->data); in ClientesController I have the following problem:
Just Cliente data is saved. Nothing of TiposComercio is saved.
What's wrong?
Thanks!
The structure of your data array seems to be wrong. Have a look at the manual for saving HABTM data: http://book.cakephp.org/2.0/en/models/saving-your-data.html#saving-related-model-data-habtm
Based on the example, your data array should be formed like this:
array(
'Cliente' => array(
'password' => '*****',
'razao_social' => 'Teste',
'responsavel' => 'responsavel',
'cidade' => '1',
'cep' => '13560201',
'logradouro' => 'Log test',
'numero' => '',
'bairro' => '',
'complemento' => '',
'atividades' => '',
'username' => 'username11',
),
'TiposComecio' => array(
(int) 0 => '1',
(int) 1 => '4'
)
)