CakePHP saveAll saves one HABTM, but not another - cakephp

I have a Territory that habtm both a User and a School:
public $hasAndBelongsToMany = array(
'School' => array(
'className' => 'School',
'joinTable' => 'schools_territories',
'foreignKey' => 'territory_id',
'associationForeignKey' => 'school_id',
'order' => 'School.name',
),
'User' => array(
'className' => 'User',
'joinTable' => 'territories_users',
'foreignKey' => 'territory_id',
'associationForeignKey' => 'user_id',
'order' => array( 'User.last_name', 'User.first_name' ),
),
);
When I add a Territory, the form collects School data and sets the User data based on the currently authenticated user. A call to $this->Territory->saveAll( $this->data ), though, saves the Territory and School data, but nothing for the User association.
This is one of those moments where I know I've done something really, really stupid, but just can't see it. Here's what the data looks like immediately before saving:
Array
(
[Territory] => Array
(
[id] =>
[advisor_first_name] => Oompa
[advisor_last_name] => Loompa
[usrp_number] => 3908
[school_search] => sylv
)
[School] => Array
(
[School] => Array
(
[0] => 4ee0bbde-3714-419d-88a5-6d78147402e8
[1] => 4ee0bbde-33d8-49ce-8e71-6d78147402e8
[2] => 4ee0bbde-e190-4894-a522-6d78147402e8
[3] => 4ee0bbde-7734-47ef-a2c0-6d78147402e8
[4] => 4ee0bbde-be54-4087-8437-6d78147402e8
[5] => 4ee0bbde-de00-473e-b6ae-6d78147402e8
[6] => 4ee0bbde-7fc0-4683-a957-6d78147402e8
[7] => 4ee0bbde-0434-4b78-9295-6d78147402e8
[8] => 4ee0bbde-defc-43cb-8c8e-6d78147402e8
[9] => 4ee0bbde-de18-45da-8e23-6d78147402e8
[10] => 4ee0bbde-fff8-4fc0-bcd7-6d78147402e8
[11] => 4ee0bbde-e1c8-42f5-9663-6d78147402e8
)
)
[User] => Array
(
[User] => Array
(
[0] => e71fcad2-2a71-11e1-837a-66730eed7998
)
)
)
One interesting note, perhaps, is that when I dump $this->Territory from the TerritoriesController, I see a School property (containing the School model definition, of course), but no User property. On the other hand, both models are correctly represented in the hasAndBelongsToMany property array.
If anyone can see what I'm missing, I'd be eternally grateful. Seriously. My eyes are bleeding from the combination of looking and weeping.
Thanks.

Related

HABTM with join Cakephp

hello I have a table name University_Industry. It has uni_id and ind_id as foriegn keys of University table and Industry table. So meaning university will have many industries and industries will have many universities. I am getting the result successfully by running this query
public function getAllUniv(){
return $this->find('all', array(
'order' => 'uni_id',
));
}
This query returns me universities and their industries.Result look like this
Array
(
[0] => Array
(
[University] => Array
(
[uni_id] => 1
[uni_name] => NYC
)
[Industry] => Array
(
[0] => Array
(
[ind_id] => 1
[ind_name] => Finance
[UniversityIndustry] => Array
(
[id] => 1
[uni_id] => 1
[ind_id] => 1
)
)
[1] => Array
(
[ind_id] => 2
[ind_name] => Accounting
[UniversityIndustry] => Array
(
[id] => 2
[uni_id] => 1
[ind_id] => 2
)
)
)
)
Now I have another table which called users which has uni_id also as a foreign key. So I am looking for a query which can fetch only those universities with the industries which are found in users table. So It should fetch only those universities whose uni_id is exist in users table
I have no idea how can I join users table in my query and get the results.
This is how my University Modal look like at the moment
class University extends AppModel
{
public $useTable = 'university';
public $primaryKey = 'uni_id';
public $hasAndBelongsToMany = array(
'Industry' =>
array(
'className' => 'Industry',
'joinTable' => 'university_industry',
'foreignKey' => 'uni_id',
'associationForeignKey' => 'ind_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
),
);
public function getAllUniv(){
return $this->find('all', array(
'order' => 'uni_id',
));
}
}
You can join data in your find query.
$this->find('all', array(
'order' => 'uni_id',
'joins' => array(
array(
'table' => 'users ',
'alias' => 'UserJoin',
'type' => 'inner',
'conditions' => array(
'UserJoin.uni_id = University.id'
)
),
));
Here is official doc. Hope it will help you

CakePHP: HABTM relationship returns join table as an object - how do I get rid of it?

I have a 2 CakePHP models, Articles and Categories, attached using a hasAndBelongsToMany relationship, like so:
$category = new Category();
$category->bindModel(array('hasAndBelongsToMany' => array(
'Article' => array(
'className' => 'Article',
'joinTable' => 'articles_categories',
'foreignKey' => 'category_id',
'associationForeignKey' => 'article_id',
'fields' => 'id,name'
))));
$this->set('categories', $category->find('all',
array(
'fields' => 'id,name'
)));
...but then, when I print out $categories, I get the following:
Array
(
[0] => Array
(
[Category] => Array
(
[id] => 31
[name] => category name
[article_count] => 1
)
[Article] => Array
(
[0] => Array
(
[id] => 1445
[name] => article name
[teaser] =>
[author_id] => 3
[ArticlesCategory] => Array
(
[id] => 6634
[article_id] => 1445
[category_id] => 31
[author_id] => 3
[created] => 2014-03-10 12:27:26
[modified] => 2014-03-10 12:27:26
)
)
)
)
I really don't need the [ArticlesCategory] member of [Article]. This just leads me back to information I already have. I tried limiting recursion but that didn't help.
How would I get rid of this?
You have two options:
[1] Reduce recursive value to 0 (CakePHP default: 1)
$category->recursive = 0;
$category->find('all',
array(
'fields' => 'id,name'
))
[2] Start using ContainableBehaviour (my own preference), which gives you more control over model data retrieved
Add following to AppModel for App-wide coverage:
public $actsAs = array('Containable');
The find method becomes:
$category->find('all',
array(
'fields' => 'id, name',
'contain' => 'Article'
))
That should do it!

CakePHP HABTM relationship saving new associations

Im having some problems with saving my HABTM relationship for some code I'm working with. I have Podcasts and Categories in a HABTM relationship. Code and problem explanation below
Model/Podcast.php
class Podcast extends AppModel{
public $hasMany = 'Episode';
public $hasAndBelongsToMany = array(
'Category' =>
array(
'className' => 'Category',
'joinTable' => 'categories_podcasts',
'foreignKey' => 'podcast_id',
'associationForeignKey' => 'category_id',
'unique' => false
)
);
}
Model/Category.php
class Category extends AppModel{
public $hasAndBelongsToMany = array(
'Podcast' =>
array(
'className' => 'Podcast',
'joinTable' => 'categories_podcasts',
'foreignKey' => 'category_id',
'associationForeignKey' => 'podcast_id',
'unique' => false
)
);
}
This is what the array I'm passing into saveAll() looks like
Array
(
[Podcast] => Array
(
[name] => Mohr Stories - FakeMustache.com
[description] => Join your host Jay Mohr and a rotating cast of his hilarious friends as they regale you with tales and conversation covering every topic imaginable. You haven't heard the half of it until you've heard... Mohr Stories.
[website] => http://www.FakeMustache.com
)
[Category] => Array
(
[0] => 1
[1] => 2
)
)
Which works fine - the podcast is added and the join table is updated, but I was under the impression that my Category array could look like the following:
[Category] => Array
(
[0] => Array
(
[name] => Test
)
[1] => Array
(
[name] => Test2
)
)
Which would save the new categories "Test" and "Test2" and then update the join table with their newly added ids. Is this not possible, or I am missing some of the picture here?
You do are able to do that, but with another data format in which each record contains the associated data as on the following code that allows to save 2 records :
array{
array{
'Podcast' => array {
'id' => 25
},
'Category' => array {
'name' => 'Test'
}
}
array{
'Podcast' => array {
'id' => 25
},
'Category' => array {
'name' => 'Test2'
}
}
}
Pass this array to the saveAll method on one of your Podcast/Category Models.
$this->Podcast->saveAll($myData);
But take care, as you don't provide any id for the categories, they will be created, even if there are existing Category with the same name.
And if a Podcast with the id 25 doesn't exists, it will be created.

CakePHP 2.4.2: Contain Not Working as Expected

I have a model, Comment, that belongsto Module, Photo, Review, Article, and User. In turn, each of those models havemany Comment.
User belongsto Avatar and Avatar hasmany User
In the code below I successfully retrieve the latest 5 comments regardless of what model they come from (Photo, Article, Review) and I display some information about the User (username, online status).
$recentComments = $this->find('all',
array(
'limit' => '5',
'order' => array('Comment.id' => 'desc'),
'conditions' => array(
'Comment.page_id' => $page_id
),
'contain' => array(
'Photo' => array(
'fields' => array('Photo.id'),
'conditions' => array(
'Comment.module_id' => 8
),
),
'Review' => array(
'fields' => array('Review.title', 'Review.id'),
'conditions' => array(
'Comment.module_id' => 2
),
),
'Article' => array(
'fields' => array('Article.title', 'Article.id'),
'conditions' => array(
'Comment.module_id' => 3
),
),
'Module' => array(
'fields' => array('Module.model', 'Module.post_title'),
),
'User' => array(
'fields' => array('User.username', 'User.online'),
),
)
));
Unfortunately there are some issues with this. As you can see above I don't contain User->Avatar and all expected data is retrieved successfully.
$recentComments = Array
(
[0] => Array
(
[Comment] => Array
(
[id] => 179
[page_id] => 2
[module_id] => 3
[page] =>
[user_id] => 29
[post_id] => 9
[title] => test comment for article 9
[content] => here is the content for the comment
[created] => 2013-04-24 00:00:00
[redeemed] => 0
[status] => 0
)
[User] => Array
(
[username] => bowlerae
[online] => 0
)
[Module] => Array
(
[model] => Article
[post_title] => title
)
[Article] => Array
(
[title] => Test title for article 9
[id] => 9
[likes] => 0
[dislikes] => 0
[comments] => 2
)
[Photo] => Array
(
[id] =>
)
[Review] => Array
(
[title] =>
[id] =>
)
)
However, if I DO contain User->Avatar like this...
'User' => array(
'fields' => array('User.username', 'User.online'),
'Avatar' => array(
'fields' => array('Avatar.file')
)
),
Then the recursion basically becomes unlimited, ALL models related to Comment, User, Module, Article, Photo and Review are also retrieved which is A LOT.
Can anyone explain why this is happening? I will be happy to submit more code from some of the models if needed but I don't see any issues there.
Take a look at another example that works successfully...Below I am retrieving the 5 most recent articles. All information including the user Avatar is successfully retreived.
$this->set('articles_sidebar', ClassRegistry::init('Article')->find('all',
array(
'limit' => '5',
'order' => array('Article.id' => 'desc'),
'conditions' => array('
Article.page_id' => $page_id
),
'contain' => array(
'User' => array(
'fields' => array('User.username', 'User.online'),
'Avatar' => array(
'fields' => array('Avatar.file')
)
),
)
)));
Please note these two finds are performed in the AppController in the beforeRender given that $page_id > 0. $page_id is set in whatever the current controller is. I know people would probably ask about it but that's not what the issue is as I mentioned that example 2 retrieving the recent articles currently works.
EDIT: I discovered that it has something to do with my afterfind callback in the Article model. Is there a way I can tweak the queries in the afterfind and/or the $recentComments query so that they still work without breaking my contain? I don't need the likes, dislikes or comments virtual fields in my $recentComments query which is why they are not listed as one of the contained fields.
function afterFind($results, $primary = false) {
parent::afterFind($results, $primary);
foreach($results as $key => $val){
if (isset($val['Article']['id'])){
$results[$key]['Article']['likes'] = $this->Like->find('count', array('conditions' => array('Like.post_id' => $results[$key]['Article']['id'], 'Like.module_id' => 3, 'Like.status' => 0)));
$results[$key]['Article']['dislikes'] = $this->Like->find('count', array('conditions' => array('Like.post_id' => $results[$key]['Article']['id'], 'Like.module_id' => 3, 'Like.status' => 1)));
$results[$key]['Article']['comments'] = $this->Comment->find('count', array('conditions' => array('Comment.post_id' => $results[$key]['Article']['id'], 'Comment.module_id' => 3, 'Comment.status < 2')));
}
} // end for each
return $results;
} // end afterfind
My guess is your problem is this:
When using ‘fields’ and ‘contain’ options - be careful to include all
foreign keys that your query directly or indirectly requires.
Make sure you're including whatever field(s) are used to join User and Avatar models.
(read here)
For now I changed (in the Article model afterFind)
if (isset($val['Article']['id']))
to
if (isset($val['Article']['id']) && $val == "article")
Seems to be working in current controller but need further testing. Is there a better solution?

CakePHP 1.3 HABTM - Add works fine, Edit will not save HABTMs

I am having a problem with my HABTM updates in CakePHP 1.3.
First off, here is the relationship declaration as it appears in the Alert model:
var $hasAndBelongsToMany = array(
'Region' => array(
'className' => 'Region',
'joinTable' => 'alerts_regions',
'foreignKey' => 'alert_id',
'associationForeignKey' => 'region_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
),
'ForecastZone' => array(
'className' => 'ForecastZone',
'joinTable' => 'alerts_forecast_zones',
'foreignKey' => 'alert_id',
'associationForeignKey' => 'forecast_zone_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
When I submit my form to create a record for the model everything works fine and the associated models (HABTM tables) get the proper records created. $this->data looks like this:
Array
(
[Alert] => Array
(
[title] => Test Alert
[overview] => This is a test alert overview
[user_id] => 3
[timeframe] => Tonight
[effective] => 2012-07-05 18:00:00
[effective_time] => 6:00
[effective_ampm] => pm
[expires] => 2012-07-05 21:00:00
[expires_time] => 9:00
[expires_ampm] => pm
)
[Region] => Array
(
[Region] => Array
(
[0] => 6
[1] => 5
)
)
[ForecastZone] => Array
(
[ForecastZone] => Array
(
[0] => 1598
[1] => 1594
[2] => 2685
[3] => 1565
[4] => 1613
[5] => 1595
)
)
)
Now, because of the complex associations with this Model (Alert, ForecastZone, and Region) I have it set up so there is a separate interface on the Alerts/Edit page to update JUST the forecast zones. The Alerts controller figures out what Regions the Forecast Zones belong to, and the controller populates the [Region][Region] value on it's own. The ForecastZones are selected from an interface where the user clicks to select which zones they want.
When I submit a form to the /alerts/edit action, I can save the Alert data (title, overview, etc) no problem. However, it is IMPOSSIBLE for me to get the related HABTM models to update. I am at the verge of just doing it all with $this->query() but I am going to try one last time to figure out what is going wrong.
Here is what the form looks like when I submit to /alerts/edit:
Array
(
[Alert] => Array
(
[id] => 37
[fcz_update] => true
)
[Region] => Array
(
[Region] => Array
(
[0] => 6
[1] => 5
)
)
[ForecastZone] => Array
(
[ForecastZone] => Array
(
[0] => 1595
[1] => 1613
[2] => 1565
[3] => 2685
[4] => 1594
[5] => 1598
[6] => 2989
[7] => 3723
[8] => 1650
[9] => 1626
[10] => 1653
[11] => 1678
[12] => 3447
[13] => 1649
[14] => 1654
[15] => 1675
[16] => 3448
[17] => 1668
[18] => 1664
[19] => 1635
[20] => 1667
[21] => 1628
)
)
)
I have tried $this->Alert->save($this->data) as well as $this->Alert->saveAll($this->data) and neither of these work. Both save() functions return a 1 (true) so I assume they are successful... yet when I return to my recently edited records... nothing has changed. I tried adding all of the other Alert data into the form as hidden fields, so I could save the whole alert data at once... this does not work either.
Edit:
Here is the code from my alerts_controller.php edit function/action that performs the save operations:
if (!empty($this->data)) {
//If we are updating forecast zones
if(array_key_exists('fcz_update',$this->data['Alert'])){
$fcz_str = $this->data['Alert']['AlertForecastZone'];
$forecast_zones = explode(',',$fcz_str);
$fcz_regions = $this->ForecastZone->query('SELECT DISTINCT region_id FROM forecast_zones WHERE id IN (' . $fcz_str . ') AND region_id != 0;');
if(!empty($fcz_regions)){
foreach($fcz_regions as $fczr){ $alert_regions[] = $fczr['forecast_zones']['region_id']; }
$this->data['Region']['Region'] = $alert_regions;
}
$this->data['ForecastZone']['ForecastZone'] = $forecast_zones;
unset($this->data['Alert']['AlertForecastZone']);
//debug($this->data);
$this->Alert->save($this->data);
//Go back to alert edit to review changes
$this->redirect($this->referer());
}
}
The best bugs are the ones that throw no errors or have any way for you to figure out what's going wrong. So angry and frustrated... please help.
Play with the keying of your data.
Try [ForecastZone][0] => 1595
or
[ForecastZone][0][id] => 1595
or even
[ForecastZone][0][ForecastZone] => 1595
I always got to fight to remember what the correct keying order is for the HABTM
To save associated and related data you need to use the saveAll function $this->Alert->saveAll($this->data)

Resources