Cakephp HABTM: Model not accessible in controller - cakephp

I am trying to work with HABTM association between Profiles and Qualifications tables.
Model: Profile.php
App::uses('AppModel', 'Model');
class Profile extends AppModel {
public $hasAndBelongsToMany = array(
'Qualifications' => array(
'className' => 'Qualification',
'joinTable' => 'profile_qualifications',
'foreignKey' => 'profile_id',
'associationForeignKey' => 'qualification_id',
'unique' => 'keepExisting'
)
);
}
Model: Qualification.php
App::uses('AppModel', 'Model');
class Qualification extends AppModel {
public $hasAndBelongsToMany = array(
'Profile' => array(
'className' => 'Profile',
'joinTable' => 'profile_qualifications',
'foreignKey' => 'qualification_id',
'associationForeignKey' => 'profile_id',
'unique' => 'keepExisting',
)
);
}
Controller: ProfilesController.php
App::uses('AppController', 'Controller');
class ProfilesController extends AppController {
public function add() {
$qualifications = $this->Qualification->find('list'); /* Attempt 1 */
$qualifications = $this->Profile->Qualification->find('list'); /* Attempt 2 */
$qualifications = $this->Profile->ProfileQualification->Qualification->find('list'); /* Attempt 3 */
}
}
All three attempts mentioned as comment have given me an error saying:
Error: Call to a member function find() on a non-object
File: ~/app/Controller/ProfilesController.php
Line: xxx
I want to know how can I generate a list of all entries in Qualifications table ?
Moreover, what is the mistake in my code right now ?

In your Profile Model, the alias of your HABTM relation with Qualification is "Qualifications", so inside the controller you have to use : $qualifications = $this->Profile->Qualifications->find('list'); or remove the plural from the model.
To prevent these kind of mistakes and save your time, it's really useful to use automatic code generation with the cake bake console or an online CakePHP baking tool.

Related

How can I do an arbitrary level of recursion in CakePHP

I have a blog post model:
App::uses('AppModel', 'Model');
class BlogPost extends AppModel {
public $primaryKey = "ID";
public $useDbConfig = 'dev_blog';
public $useTable = 'posts';
public $hasMany = array(
'BlogComment' => array(
'foreignKey' => 'comment_post_ID',
'className' => 'BlogComment',
'order' => 'BlogComment.comment_date DESC',
'conditions' => array(
'BlogComment.comment_approved' => '1',
'BlogComment.comment_parent' => 0
)
)
);
}
And a blog comment model:
App::uses('AppModel', 'Model');
class BlogComment extends AppModel {
public $primaryKey = "comment_ID";
public $useDbConfig = 'dev_blog';
public $useTable = 'comments';
public $hasMany = array(
'Children' => array(
'className' => 'BlogComment',
'foreignKey' => 'comment_parent',
'order' => 'Children.comment_date DESC',
'conditions' => array(
'Children.comment_approved' => 1
)
)
);
}
In the controller, I set recursion on BlogPost equal to 2 and did a find:
$options = array(
'conditions' => array(
'BlogPost.ID' => $postID,
'BlogPost.post_status' => 'publish'
),
);
$post = $this->BlogPost->find('first', $options);
What this does is get the content of a particular blog post, all root comments and one level of replies to the root comments. It doesn't get a reply to a reply, though. How can I retrieve an arbitrary depth of replies?
Just don't use recursive at all. Ever.
Set it to -1 in the AppModel:
public $recursive = -1;
Then forget you even heard of it, and use CakePHP's awesome Containable Behavior to get the data you need.
Note: See the MANY other answers related to using CakePHP's Containable.

Limit to how deep a Many-To-Many will go with CakePHP

Is there a limit to how deep a select all will go for a many-to-may relationship? I have a design like this:
Table Survey has many Table Survey Sections. Table Survey Sections have many Questions. Table Questions have many Table Possible Responses.
If I do this for the Survey Table:
$this->loadModel('Survey');
$this->Survey->recursive = 2;
$getSurvey = $this->Survey->find('all');
It will only go as far as the Questions table but bring back the Possible Responses Table. But, if I query the Survey Sections table, it brings everything back. Is this an issue with my code or a limit to how deep a search will go?
Here is my Models:
<?php
App::uses('AppModel', 'Model');
class Survey extends AppModel {
public $validate = array(
);
public $belongsTo = array(
);
public $hasMany = array(
'surveySection' => array(
'className' => 'SurveySection',
'foreignKey' => 'surveys_id',
),
);
}
<?php
App::uses('AppModel', 'Model');
class SurveySection extends AppModel {
public $validate = array(
);
public $belongsTo = array(
);
public $hasMany = array(
'Question' => array(
'className' => 'Question',
'foreignKey' => 'survey_section_id',
),
);
}
<?php
App::uses('AppModel', 'Model');
class Question extends AppModel {
public $validate = array(
);
public $belongsTo = array(
);
public $hasMany = array(
'PossibleResponse' => array(
'className' => 'PossibleResponse',
'foreignKey' => 'questions_id',
),
);
}
<?php
App::uses('AppModel', 'Model');
class PossibleResponse extends AppModel {
public $validate = array(
);
public $belongsTo = array(
);
}
You should use $this->Survey->recursive = 3;.
As I said in the comment, the example at http://book.cakephp.org/2.0/en/models/model-attributes.html#recursive is a bit misleading because it only show an example for a specific case. In fact you can go as far as you want when fetching data.

cakephp ACL creates ARO but lft and rght values are null

Using CakePHP 2.2.3
I'm nearly finished with my project and now going back through to setup authorization.
I'm implementing ACL, truncated both the users and groups tables for a fresh start, ran the command to recreate the aco/aro/aros_acos tables and have followed the tutorial.
When I create a group, it creates a corresponding ARO entry but the lft, and rght fields are NULL. I commented out all of my other code in the users/groups models and controllers to try to narrow it down, but it doesn't seem to help.
I will post my code below, with comments and validations removed for the sake of space.
group model:
App::uses('AppModel', 'Model');
class Group extends AppModel {
public $actsAs = array('Acl' => array('type' => 'requester'));
public function parentNode() {
return null;
}
public $hasMany = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'group_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
}
User model:
App::uses('AppModel', 'Model');
App::uses('AuthComponent', 'Controller/Component');
class User extends AppModel {
//setup ACL settings and function
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));
}
} // end parentNode()
public function beforeSave($options = array()) {
if (isset($this->data[$this->alias]['password'])) {
$this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']);
}
return true;
}
AppController:
App::uses('Controller', 'Controller');
class AppController extends Controller {
public $components = array(
//'Security',
'Acl',
'Auth' => array(
'authorize' => array(
'Actions' => array('actionPath' => 'controllers')
)/*,
'authenticate' => array(
'Form' => array(
'scope' => array('User.activated' => 1 )
)
) */
),
'Session'
);
public $helpers = array(
'Html',
'Text',
'Session',
'Form'
);
/* public function isAuthorized($user = null) {
return true;
} */
public function beforeFilter(){
$this->Auth->loginRedirect = array('controller' => 'products', 'action' => 'index' );
$this->Auth->logoutRedirect = array('controller' => 'products', 'action' => 'index');
$this->Auth->authError = 'You are not allowed to see that.';
}
I even did an ACL implementation on a fresh install of cakephp 2.4.6, and everything works great. I have the projects side by side for comparison but can't find a difference in my ACL setup
Why aren't my lft and rght fields being set in my ARO table?
Short Answer: Remove MVC files associated with ACL tables.
Less Short Answer:
I setup ACL on a fresh install of cake 2.2.3, and everything worked great. Overwrote my code from my user and group models and controllers as well as AppController, and still no go.
I've seen a similar situation when I forget to add $actsAs = array('Tree'); to a model.
I realized I baked controllers/models/views for all ACL tables. DOH! (look for aroscontroller, acoscontroller, etc.)
I removed all the MVC files for these tables and it works great now.
This isn't a typical issue since normally one would add ACL schema after baking, but I started with a database I used on another project and forgot to remove the tables.
I really hope my stupidity helps someone else in this situation.

CakePHP: Call to a member function on a non-object error with aliases

I keep getting a "Call to a member function on a non-object" error in the index function of one of my controllers. Here is the code:
class ModulesController extends AppController {
public $uses = array('Module', 'User');
public $scaffold;
function index() {
if ($this->request->is('get')) { //Technically unecessary but good practice
$email = AuthComponent::user('email');
$user = $this->Module->User->findByEmail($email);
//Code omitted
}
}
And this is my Module model:
class Module extends AppModel {
public $name = 'Module';
public $hasMany = array(
'Slide' => array(
'className' => 'Slide',
'foreignKey' => 'module_id',
'order' => 'Slide.position ASC' //Let's make sure that name is correct
));
public $belongsTo = array(
'Creator' => array(
'className' => 'User'));
public $hasAndBelongsToMany = array(
'OUser' => array(
'className' => 'User',
'joinTable' => 'modules_users',
'foreignKey' => 'module_id',
'associationForeignKey' => 'user_id',
'unique' => 'keepExisting',
));
} //Unnecessary code omitted
And this is the code for my user model:
class User extends AppModel {
public $name = 'User';
public $uses = 'users';
public $hasMany = array(
'OModule' => array( //Owner of Modules
'className' => 'Module',
'foreignKey' => 'user_id',
'order' => 'Module.created DESC'
));
public $hasAndBelongsToMany = array(
'Module' => array(
'className' => 'Module',
'joinTable' => 'modules_users',
'foreignKey' => 'user_id',
'associationForeignKey' => 'module_id',
'unique' => 'keepExisting',
));
} //Unnecessary code omitted
It is worth mentioning that User and Module have both a belongsTo and a HABTM relationship, so User has the aliases Creator and OUser respectively. In the code above, the findByEmail is trying to use the Creator relationship, but that gives me the same error.
My question is, how do I call the findByEmail() method without error?
Change:
$user = $this->Module->User->findByEmail($email);
To:
$user = $this->User->findByEmail($email);
Voila!
Reason:
Somehow, there is either 1) no relationship between Module and User, 2) a mistake in the relationship, or 3) your using an alias for either or both of them so that using Module->User wouldn't be correct.
You're loading the 'User' model anyway though, so there's no reason to go through the Module model to access it - just access it directly (like example above).

Models with two hasOne relations to same table

I'm building an MMA (mixed martial arts) website with CakePHP. I've got a fights table in my database that has three columns at its simplest: id, fighter_a, and fighter_b.
I'm having trouble getting my head around what type of relation my Fight model would have with my Fighter module. Am I right in thinking fighter_a and fighter_b would be two hasOne relations?
I tried this with the following in my Fight model:
<?php
class Fight extends AppModel {
public $name = 'Fight';
public $hasOne = array(
'FighterA' => array(
'className' => 'Fighter',
'foreignKey' => 'fighter_a'
),
'FighterB' => array(
'className' => 'Fighter',
'foreignKey' => 'fighter_b'
)
);
}
And then this in my Fighter model:
<?php
class Fighter extends AppModel {
public $name = 'Fighter';
public $hasMany = array(
'Fight'
);
}
But this threw an error in my CakePHP application when calling $this->Fight->findById($id) (where $id was the ID of a fighter):
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Fight.fighter_id' in 'field list'
How can I link my models so that I can call all fights a fighter has been in?
Distilled from conversation under the question, the solution would be this:
Rewrite the $hasMany in your FighterModel to look like:
public $hasMany = array(
'Fight' => array(
'className' => 'Fight',
'finderQuery' => 'SELECT * FROM fights AS Fight WHERE Fight.fighter_a_id = {$__cakeID__$} OR Fight.fighter_b_id = {$__cakeID__$};'
)
);

Resources