Saving data with 2 HABTM and one hasMany association - cakephp

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
)
)

Related

cakephp model chaining and retrieving results from multiple models

I am in process of chaining multiple models. The goal is to retrieve the category for the keyword when I pass a keyword.
Models
Keyword
columns:
id
keyword
KeywordCategory
columns:
keyword_id
category_id
Category
columns:
id
category
The following are the association definitions that I made.
Keyword
var $hasMany = array(
'KeywordCategory' => array(
'className' => 'KeywordCategory',
'foreignKey' => 'keyword_id',
'dependent' => true
)
);
KeywordCategory
var $belongsTo = array(
'Keyword' => array(
'className' => 'Keyword',
'foreignKey' => 'keyword_id',
'dependent' => true
),
'Category' => array(
'className' => 'Category',
'foreignKey' => 'category_id',
'dependent' => true
)
);
Category:
var $hasMany = array(
'KeywordCategory' => array(
'className' => 'KeywordCategory',
'foreignKey' => 'category_id',
'dependent' => true
)
);
My question is when I currently retrieve the Keyword category currently it will return something like following:
Array
(
[Keyword] => Array
(
[id] => 2
[keyword] => rc
[modified] =>
[created] =>
)
[KeywordCategory] => Array
(
[0] => Array
(
[keyword_id] => 2
[category_id] => 2
)
[1] => Array
(
[keyword_id] => 2
[category_id] => 1
)
)
)
I want to also return the Categories signified by category_id in the result. Can anyone suggest the change that I need to make?
Thank you

CakePHP saveAssociated not saving HasMany Through Model Data

I'm trying to get my associated models in CakePHP 2.3 to save properly, but I'm having issues. I'm storing posts, and I want to know what links are in those posts. For each of those links, I'd like to store anchor text if it is available. My database is set up as in the following diagram.
(source: derekperkins.com)
Anchor Model
class Anchor extends AppModel {
public $hasMany = array(
'PostsUrl' => array(
'className' => 'PostsUrl',
'foreignKey' => 'anchor_id',
'dependent' => false
)
);
public function save($data = NULL, $validate = true, $fieldList = array()) {
$id = Anchor::find('first', array(
'fields' => array('id'),
'recursive' => -1,
'conditions' => array('anchor' => $data['anchor'])
));
if( $id )
$data['id'] = $id['Anchor']['id'];
return parent::save($data, $validate, $fieldList);
}
}
URL Model
class Url extends AppModel {
public $hasMany = array(
'PostsUrl' => array(
'className' => 'PostsUrl',
'foreignKey' => 'url_id',
'dependent' => false
)
);
public function save($data = NULL, $validate = true, $fieldList = array()) {
$id = Url::find('first', array(
'fields' => array('id'),
'recursive' => -1,
'conditions' => array('url' => $data['url'])
));
if( $id )
$data['id'] = $id['Url']['id'];
return parent::save($data, $validate, $fieldList);
}
}
PostsUrl Model
class PostsUrl extends AppModel {
public $belongsTo = array(
'Post' => array(
'className' => 'Post',
'foreignKey' => 'post_id'
),
'Url' => array(
'className' => 'Url',
'foreignKey' => 'url_id'
'Anchor' => array(
'className' => 'Url',
'foreignKey' => 'anchor_id'
)*/
);
}
Post Model
class Post extends AppModel {
public $hasMany = array(
'PostsUrl' => array(
'className' => 'PostsUrl',
'foreignKey' => 'post_id',
'dependent' => false
)
);
public function save($data = NULL, $validate = true, $fieldList = array()) {
$id = Post::find('first', array(
'fields' => array('id'),
'recursive' => -1,
'conditions' => array('external_post_id' => $data['external_post_id'])
));
if( $id )
$data['id'] = $id['Post']['id'];
return parent::save($data, $validate, $fieldList);
}
}
Submitting Data
I've created a form to test my model. This is the code I'm using to save the array created by the form. I am getting a message saying that things saved successfully, but only the post saves. Nothing is entered into the other three tables. I'm also using DebugKit and no SQL calls reference any of that data.
$this->Post->saveAssociated($this->request->data, array('deep' => true))
Array
(
[Post] => Array
(
[external_post_id] => 12345
[sentiment_score] => 3.3
)
[URL] => Array
(
[url] => http://test.com
)
[Anchor] => Array
(
[anchor] => Test Anchor
)
)
I've also tried formatting my arrays to have the URL and Anchor underneath PostsUrl as a subarray, but that didn't work either.
My Model::save functions are there to keep me from duplicating data, and they work properly in other models I have used in the past (though I'm open to suggestions on a better way to do this, as this uses a database call for each check). I've also tried commenting them out, and it doesn't affect my code. How should I structure this to save properly?
First of all about your Model::save functions, for example:
public function save($data = NULL, $validate = true, $fieldList = array()) {
$id = Post::find('first', array(
'fields' => array('id'),
'recursive' => -1,
'conditions' => array('external_post_id' => $data['external_post_id'])
));
if( $id )
$data['id'] = $id['Post']['id'];
pr($data);
return parent::save($data, $validate, $fieldList);
}
it prints $data in this way :
Array
(
[id] => 3 //for example 3
[Post] => Array
(
[external_post_id] => 12345
[sentiment_score] => 3.3
)
[URL] => Array
(
[url] => http://test.com
)
[Anchor] => Array
(
[anchor] => Test Anchor
)
)
this $data is incorrect, the correct data is:
Array
(
[Post] => Array
(
[id] => 3 //for example 3
[external_post_id] => 12345
[sentiment_score] => 3.3
)
[URL] => Array
(
[url] => http://test.com
)
[Anchor] => Array
(
[anchor] => Test Anchor
)
)
You must change your Model::save function this way:
public function save($data = NULL, $validate = true, $fieldList = array()) {
$id = Post::find('first', array(
'fields' => array('id'),
'recursive' => -1,
'conditions' => array('external_post_id' => $data['external_post_id'])
));
if( $id )
$data[$this->name]['id'] = $id['Post']['id'];
return parent::save($data, $validate, $fieldList);
}
second , you can't save this data with single save, you should save your data this way:
$postData = array
(
'Post' => array
(
'external_post_id' => 12345
'sentiment_score' => 3.3
)
);
$this->Post->save($data);
$postUrlId = $this->PostsUrl->find('first', array(
'conditions' => array(
'post_id' => $this->Post->id
),
'fields' => array('id')
));
$urlAnchorData = array(
'URL' => array
(
'url' => 'http://test.com'
),
'Anchor' => array
(
'anchor' => 'Test Anchor'
),
'PostsUrl' => array(
'id' => $postUrlId['PostsUrl']['id']
)
);
$this->PostsUrl->saveAll('$urlAnchorData');

CakePHP multilevel models

I've been trying to figure out how to save multi-level models in CakePHP for some time now, but can't seem to find a solution.
I have three models. Survey, Question, and Choice.
Survey hasMany Question - so Question belongsTo Survey
Question hasMany Choice - so Choice belongsTo Question
The Question is linked to the Survey through its survey_id key.
The Choice, on the otherhand, is linked to the Question through its question_id key. (not directly linked to Survey though)
The form is created through $this->Form->create('Survey').
I want the application to save a survey with its corresponding questions, AND each question having their corresponding choice(s).
The problem is, only the Survey and Question models are saved. Choice gets discarded.
I'm using $this->saveAssociated($this->request->data, array( 'deep' => true) )
I will be updating my post to show the $_POST data.
Thanks,
XTN
Before saving data, you have to ensure that data should be in this format
in your controller:
$data = array('Survey' => array('id' => 1,'name' => 'test'),
'Question' => array(
array('id' => 1,'question' => 'test1','survey_id' => 1,
'Choice' => array(
array('id' => 1,'question_id' => 1,'choice' => 1),
array('id' => 2,'question_id' => 1,'choice' => 2)
)
),
array('id' => 2,'question' => 'question2','survey_id' => 1,
'Choice' => array(
array('id' => 3,'question_id' => 2,'choice' => 'sd'),
array('id' => 4,'question_id' => 2,'choice' => 'we')
)
)
)
);
$this->Survey->create();
$this->Survey->saveAssociated($data,array('deep'=>true));
Survey Model :
public $hasMany = array(
'Question' => array(
'className' => 'Question',
'foreignKey' => 'survey_id',
'dependent' => false,
)
);
Question Model :
public $belongsTo = array(
'Survey' => array(
'className' => 'Survey',
'foreignKey' => 'survey_id',
)
);
public $hasMany = array(
'Choice' => array(
'className' => 'Choice',
'foreignKey' => 'question_id',
'dependent' => false,
)
);
Choice Model :
public $belongsTo = array(
'Question' => array(
'className' => 'Question',
'foreignKey' => 'question_id',
)
);
I think it will work if found any problem please notify
Your models should looks like:
Survey Model: Survey.php
class Survey extends AppModel {
public $name = 'Survey';
public $hasMany = array('Questions' => array(
'className' => 'Question',
'foreignKey' => 'survey_id'
)
);
}
Question Model: Question.php
class Question extends AppModel{
public $name = 'Question';
public $belongsTo = array(
'Survey' => array('className' => 'Survey', 'foreignKey' => 'survey_id'),
'Answer' => array('className' => 'Choice', 'foreignKey' => 'answer_id'));
public $hasMany = array('Choices' => array('className' => 'Choice',
'foreignKey' => 'question_id'));
}
Choice Model: Choice.php
class Choice extends AppModel
{
public $name = 'Choice';
public $belongsTo = array('Question' => array('className' => 'Question',
'foreignKey' => 'question_id'));
}
Kindly ask if it not worked for you.

cannot fetch 2nd level model with cakephp

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 !

correct data array to save with this kind of relationship

class Post extends AppModel {
var $name = 'Post';
var $hasMany = array(
'CategoryPost' => array(
'className' => 'CategoryPost'
)
);
var $belongsTo = array(
'Page' => array(
'className' => 'Page'
)
);
class Category extends AppModel {
var $name = 'Category';
var $hasMany = array(
'CategoryPost' => array(
'className' => 'CategoryPost'
)
);
class CategoryPost extends AppModel {
var $name = 'CategoryPost';
var $validate = array(
'category_id' => array(
'rule' => array('multiple', array('in' => array(1, 2, 3, 4))),
'required' => FALSE,
'message' => 'Please select one, two or three options'
)
);
var $belongsTo = array(
'Post' => array(
'className' => 'Post'
),
'Category' => array(
'className' => 'Category'
)
);
Will this be the correct format of array that is needed to save this with saveAll? This doesn't save the CategoryPost model. If it isn't what should be the format of the array?
Array
(
[Post] => Array
(
[title] => query
[body] =>
query
[page_id] => 122
[modified] => 2010-12-30 23:33:47
[created] => 2010-12-30 23:33:47
[uri] => query-9
)
[CategoryPost] => Array
(
[0] => Array
(
[category_id] => 1
)
[1] => Array
(
[category_id] => 2
)
)
[Page] => Array
(
[meta_keywords] => query
[meta_description] => query
[title] => query
[layout] => index
)
)
Well, everthing seems fine, and the CategoryPost data should be saved.
Maybe comment out the validation from the CategoryPost model and try then.

Resources