cakephp - has many through not saving - cakephp

I have the following has many through relationship
Alert AlertsElement Search
id id id
name alert_id link
search_id
ordering
Here are my models
class Alert extends AppModel {
var $name = 'Alert';
var $hasMany = array('AlertsElement');
class AlertsElement extends AppModel {
var $name = 'AlertsElement';
var $belongsTo = array('Alert','Search');
class Search extends AppModel {
var $name = 'Search';
var $hasMany = array('AlertsElement');
Here is my test code I am using to do a save -
$d = array();
$d['Alert']['id'] = 1976;
$d['Search']['id'] = 107;
$d['AlertsElement']['ordering'] = 2;
$this->Alert->create();
$this->Alert->saveAll($d);
I get this error -
Cannot use a scalar value as an array [CORE/cake/libs/model/model.php, line 1696]
and a row entry in alertselement with the order but the foreign keys set to 0
Any ideas ?
Thanks, Alex

In order to save a hasMany relationship with saveAll, the hasMany-associated model data (in this case, $d['AlertsElement']) needs to be an indexed array of associative arrays, thus:
$d = array(
'Alert' => array(
'id' => 1976
),
'AlertsElements' => array(
0 => array(
'ordering' => 2
)
)
);
$this->Alert->saveAll($d);
Note that the associative array, consisting of the 'ordering' key and its value, has been shifted down into an indexed array. This is the required format for saving hasMany-associated data in a single saveAll operation, because this format expands gracefully whether you want to save one or ten associated records.
Refer to the Cake manual on saving related model data for more info.

I got here looking for a solution to this question. Not to contradict Daniel, the book says that when you use a hasMany through relationship (which is what Alex is using), you can save all three records with a single saveAll call, but you do it from the joining table's model like this:
$data = array(
[Student] => Array
(
[first_name] => Joe
[last_name] => Bloggs
)
[Course] => Array
(
[name] => Cake
)
[CourseMembership] => Array
(
[days_attended] => 5
[grade] => A
)
);
$this->CourseMembership->saveAll($data);
Unless I've misread the book, that example should create the Student, Course, and joining CourseMembership records. That said, I have yet to be able to get it to work as documented. I'm passing in an id for the Course as I'm using an existing Course, but it doesn't create the Student or the CourseMembership record.
Any more feedback appreciated.

Related

Update except row of null in CakePHP

I have problem,when I tried to update except row of null in CakePHP.
Model -> Spot
column -> name,address
[MySQL]
id / name / address
1 / Shibuya Bakery / Shibuya street 123
For example,there is database like the one above.
Then CakePHP got a name-value(Shibuya Cake Shop) from Android,and address-value is null.
Therefore I wanna update just name-column.
id = 1(post from Android)
public function update()
{
if( isset($this->request->data["id"]) )
{
$id = intval($this->request->data["id"]);
$fields = array( 'Spot.id' => $id );
$data = array( 'Spot.name' => "'".$this->request->data["name"]."'",
"Spot.address" => "'".$this->request->data["address"]."'");
$this->Spot->updateAll( $data,$fields );
}
}
If you just want to update just one field, just use saveField.
$this->Spot->id = $this->request->data['id'];
$this->Spot->saveField('name', $this->request->data['name']);
Don't use updateAll, as that is meant for updating multiple records, though technically you could add conditions to prevent it from doing so. You're also passing incorrect arguments into updateAll. (See http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-updateall-array-fields-mixed-conditions for the correct way.) If you want to update multiple fields at once on a single record, use save:
public function update()
{
if( isset($this->request->data["id"]) )
{
$fields = array('name');
if(!empty($this->request->data['address'])) array_push($fields, 'address');
$this->Spot->save($this->request->data, true, $fields);
}
}
Read more on saving: http://book.cakephp.org/2.0/en/models/saving-your-data.html#saving-your-data

Automatically adding additional Records into HABTM table in CakePhp

Iam confused about a problem. i will describe it
I am using HABTM first time in cakephp,also i am not too much familiar with cakephp 2.4.6
I have
MediaOrg- model, media_orgs - table name
table fields- id,name
public $hasAndBelongsToMany = array(
'className' => 'ContactPerson',
'joinTable' => 'contact_people_media_orgs',
'foreignKey' => 'media_org_id',
'associationForeignKey' => 'contact_person_id'
);
ContactPerson -model contact_person- table name
table_fields - id,name,designation,contact_number,ladline
public $hasAndBelongsToMany = 'MediaOrg'
ContactPeopleMediaOrg -model , table name-contat_people_media_orgs
table_fields - contact_person_id ,media_org_id
//now in controller saving values for media_org table
$this->MediaOrg->save($this->request->data)
$media_org_id=$this->MediaOrg->id;
//next in controller saving values for contact_person table
$this->ContactPerson->save($this->request->data)
$mcontact_person_id=$this->ContactPerson->id;
//next saving id's into many-to-many table
$contact_person_mediaorg_table=array('contact_person_id'=>$contact_person_id,
'media_org_id'=>$media_org_id );
$this->ContactPeopleMediaOrg->save($contact_person_mediaorg_table);
everything is working fine. i dont know what happends in the common table
contact_person_media_org , the data is adding 3 tomes.
first time it adding correct id's and next each time it addiing the mobile number and land number of contact person with media_org_id
when i debug it using getDataSource(), i can find that some param is passing to that common table and adding to it. i dont know how it happening
(int) 6 => array('query' => 'INSERT INTO `go4ad`.`contact_people_media_orgs`
(`media_org_id`, `contact_person_id`) VALUES (?,?)',
'params' => array(
(int) 0 => '30',
(int) 1 => '55555555'
),
'affected' => (int) 0,
'numRows' => (int) 0,
'took' => (float) 1
Also i can find that some BEGIN and COMMIT keywords are there.
Actually What is happening..if anyone can help me..pls pls help me. iam stucking
You should use saveAssociated(). Overall, your process should generate something like this:
$this->request->data = array(
'MediaOrg' => array(
'id' => $media_org_id,
),
'ContactPerson' => array(
0 => $first_person_id,
1 => $second_person_id,
2 => $thurd_person_id,
),
);
$this->MediaOrg->saveAssociated($this->request->data);
And it will add rows to your HABTM relation table (media_organization_contact_persons). Now, this works when you have the rows already added to the database, and you want to add the connections. If you want to add data to the tables in the same time, or whatever that this answer doesn't cover for you, you can read this article.

cakephp -- getting appropriate model data in complex relationships

OK. Trying to articulate my issue as concisely as possible. I've got this massive project, for which the main purpose is generating a configuration file from Model data. I'm at a loss on how to get the appropriate information in an efficient way when gleaning the db for config info. The Models: District, Department, User, Carrier.
District
hasMany Department
| id | name |
Department
hasMany User through DepartmentPosition
| id | name | district_id | tone_name | tone_length |
User
hasMany Department through DepartmentPosition
belongsTo Carrier
| id | name | phone | email | carrier_id | notify_method (tiny int) |
Carrier
hasMany User
| id | name | mms_gateway |
DepartmentPosition
belongsTo User, Department
| id | user_id | department_id | role |
A configuration file is to generated for each district, using fields associated with all models above. For each department in a district the following is to be added to a string which, when finished, will be saved to the configuration file:
[tone_name] //comes from department name
tone = 123 //from department tone
tone_length = 4 //from department tone_length
mp3_emails = person#gmail.com, person2#gmail.com ... //comes from user email
amr_emails = 5551239999#vzwipix.com //concat of user phone and carrier mms_gateway
Basically, the lists for a department's mp3_emails and amr_emails should be generated based on the users in that department, based on the notify_method User field. I've been able get everything but these email fields into the config file with the following:
//districts controller
public function generate_config(){
$districts = $this->District->find('All');
$this->set(compact('districts'));
}
Now, for the view file:
<?php
$output = '';
$br = " \r\n";
$unrelated = array('id', 'name', 'district_id', 'tone_name');
foreach($districts as $district){
$name = $district['District']['name'];
$output .= "#{$name} configuration".$br;
$departments = $district['Department'];
foreach($departments as $department){
$output .= "[".$department['tone_name']."]".$br;
foreach($department as $key => $value){
if(!empty($value)&&(!in_array($key, $unrelated))){
$output .= $key." = ".$value.$br;
}
}
$output .= $br;
}
}
echo nl2br($output);
?>
That outputs something like this
#District 1 config
[Department1]
tone = 123
tone_length = 4
[Department2]
tone = 24.7
tone_length = 2
What I need to do is generate the email lists for each department, but I can't figure out a sensible way to do this. I feel it would be much easier if the departments and users used a HABTM relationship, rather than the hasMany through, which i felt i had to use instead because i'm saving additional data for the relationship. Any suggestions?? Don't hesitate to ask if i should be clearer about anything, but i've tried to give all relevant information while minimizing overwhelming details. THANK YOU!!!
EDIT
Thanks to #Drewdiddy611, my sanity has been spared. Containable behavior is perfect for this issue. So, hopefully someone will avoid a complete project overhaul -- something which I was moments away from doing -- and use the containable behavior.
Simply adding the following to my models:
public $actsAs = array('Containable');
I was able to then run the find query, implementing the containable behavior just as suggested below, i was able to add greater depth to my iteration over each District/Department. It was extremely easy because the returned array was just as written in the answer chosen below!!! thanks!
I think you were right to use the 'hasMany through' relationship based on your tables/data. In cakephp 2.1, they added a 'unique' key for HABTM relationships which you can set to 'keepExisting' and it's supposed to circumvent lost data.
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasmany-through - Look at the "Changed in 2.1" section below the main header.
Maybe give that a try?
You could also look at containable behavior.
Then you could do the following:
<?php
//model
var $actsAs = array('Containable');
//controller
$this->District->contain(array(
'Department' => array(
'DepartmentPosition' => array(
'User' => array(
'Carrier'
)
)
)
));
$districts = $this->District->find('all');
$this->set(compact('districts'));
// I believe this will be the outcome after the find.
///////////////////////////////////////////////////////
// array(
// [0] => array(
// 'District' => array(...),
// 'Department' => array(
// [0] => array(
// '...' => '...', // repeated...
// 'DepartmentPosition' => array(
// [0] => array(
// '...' => '...', // repeated...
// 'User' => array(
// '...' => '...', // repeated...
// 'Carrier' => array(
// '...' => '...', // repeated...
// )
// )
// )
// )
// )
// )
// )
// )
?>
-Andrew

cakephp 2.2 tree behaviour - Count the products in the given category and all subchildren recursively

CakePHP 2.2
I've built a nested vertical menu for my categories using the tree behaviour. In these categories there are products. What I would like to achieve is next to the category name having the containing numbers of products in the specified category. Here is an example:
Automobile (100) > SUV (35)
> Pickup (25)
> Berline (40)
Next to the category, we can see the numbers of products within it.
I checked online and find this tutorial : http://mrphp.com.au/blog/fun-tree-behaviour-cakephp
There is a line in the "finding your data" last part that shows how to achieve that:
// count the posts in the given category and all subchildren recursively
$id = 123;
$postCount = $this->Category->postCount($id);
debug($postCount);
The problem in this code is that it uses postCount() but in my case my categories don't have posts but products, in other words, I use a Product model and a ProductsController so it cannot work. I can I accomplish that?
[EDIT]
I found a solution. First i've created a new field in my categories table and named it product_count (singular name of the associated table). Then i added a belongsTo in my ProductsController.php. as followed:
public $belongsTo = array(
'Category' => array(
'counterCache' => true
)
);
Finally in the foreach of my vertical menu i simply added this next to the category name :
$v['Category']['product_count']
Now it's working!
Replace the postCount function with:
function productCount($id, $direct=false){
if (!$direct) {
$ids = array($id);
$children = $this->children($id);
foreach ($children as $child) {
$ids[] = $child['Category']['id'];
}
$id = $ids;
}
$this->Product->recursive = -1;
return $this->Product->findCount(array('category_id'=>$id));
}

Cakephp get longest unmodified entries

I have a model A that
$hasMany = array('B' => array('order' => 'B.created DESC'),
'C' => array('order' => 'C.created ASC')
);
So now i want to fetch the 5 most desolated entries of A(B,C) from my database that did not get modified the longest time(including B and C 'modified'-field).
How do i do that?
/EDIT: To explain it in other words:
Assume i have a model User that has information like name, adress, email etc.
A User also has many model Post on his page. And he has many model Other on his page too.
So we have a relationship of 1:n in User:Post and User:Other
Now i want to fetch those 5 User of my User, whose highest 'modified' value (User.modified, Post.modified or Other.modified) is one of they five lowest of all Users.
In other words: The 5 User who did not update their profile for the longest time.
I foresee some data inconsistencies in the future with this technique. I would do it this way.
Add field to user table called lastactivity
In AppController.php add beforeSave function
public function beforeSave() {
$save_data = $this->data;
if(isset($save_data['User']['modified']) || isset($save_data['Post']['modified'] || isset($save_data['Other']['modified']))){
$data = array();
$data['User']['id'] = $this->Auth->user('id');
$data['User']['lastactivity'] = date("Y-m-d H:i:s");
$this->User->save($data);
}
}

Resources