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');
Related
Controller
public function index(){
$this->Store->unbindModel(
array('belongsTo' => array('Employee')), true
);
$options=array(
'joins' =>
array(
array(
'table' => 'Employee',
'alias' => 'Employee',
'foreignKey' => true,
'conditions'=> array('Employee.employee_store = Store.store_name')
)
));
$coupons = $this->Store->find('all', $options);
}
Model
class Store extends AppModel {
var $useTable = 'store'; }
Sql :
SELECT `Store`.`id`,
`Store`.`store_name`,
`Store`.`store_address`,
`Store`.`store_phone`,
`Store`.`store_email`,
`Store`.`store_website`,
`Store`.`date_enter`,
`Store`.`store_shortcode`
FROM `billing`.`store` AS `Store`
JOIN `billing`.`Employee` AS `Employee` ON (`Employee`.`employee_store` = `Store`.`store_name`)
WHERE 1 = 1
I need to display Both Employee and Store table columns. (employee_name,employee_mail etc from employee table, Store_name,store_add from store table )
$options = array(
array(
'table' => 'Employee',
'alias' => 'Employee',
'foreignKey' => true,
'conditions'=> array('Employee.employee_store = Store.store_name')
)
);
$coupons = $this->Store->find('all',array(
"fields"=>array("Employee.*","Store.*"),
"joins"=>$options
));
$this->Store->Behaviors->load('Containable');
$options = array(
'contain' => array(
'Employee' => array(
'conditions' => array(
'Employee.employee_store' => 'Store.store_name'
)
)
)
);
$coupons = $this->Store->find('all', $options);
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 have the following relations:
class Platformuser extends AppModel {
public $hasAndBelongsToMany = array(
'Service'
);
}
class Service extends AppModel {
public $hasAndBelongsToMany = array(
'Platformuser'
);
}
I am doing a action on the PlatformusersController to get the services associated with this user with the following query:
$this->Platformuser->find('all', array(
'conditions' => array('Platformuser.id' => $userId),
));
It returns everything about Platformuser/Service and I ONLY want the data of the service:
array(
(int) => array(
[Platformuser] => array(
[id] => [1]
[name] => [Domingo]
),
[Service] => array(
(int) 0 => array(
[id] => [1]
[name] => [dropbox],
[PlatformusersService] => array(
[id] => [1],
[platformuser_id] => [1],
[service_id] => [1],
[modified] => [2013-10-10 00:00:00],
[created] => [2013-10-10 00:00:00];
)
)
)
)
I want something like:
array(
[Service] => array(
(int) 0 => array(
[id] => [1]
[name] => [dropbox]
)
)
Any ideas?.
You should use the containable behavior. It's easy to use and you'll able to fetch data according to your need like.
$plateFormuser = $this->Platformuser->find('first', array(
'conditions' => array('Platformuser.id' => $userId),
'contain' => array(
'Service' => array(
'fields' => array('id', 'name')
)
)
));
$services = Set::merge(
Set::classicExtract($plateFormuser, 'Service.{n}.{id}'),
Set::classicExtract($plateFormuser, 'Semester.{n}.{name}')
);
Now you can json encode the $services to get result as you've mentioned [{"id":"1", "name":"dropbox"}].
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.