CakePHP associations with 3 tables - php

I have 3 tables: Users, Profiles and Posts.
A User may have one Profile, and a Profile must have a User, a Post must have a User, and User may have many Posts. I have set this up in my Models and it all seems to work fine.
However when viewing a list of posts I am trying to show the Author firstname which is stored in the Profile table. But it's the User and Post table that are linked, and then the User table is linked to the Profile table. So their is no direct relation between the Post and the Profile.
How do I get this information?
Currently I'm trying to show this in the view with: <?php echo $post['Profile']['firstname']; ?> but because their is no direct association between the post and profile it does not work and I get the error: Undefined index: Profile
I have tried to do:
public function index()
{
$this->Post->recursive = 2;
$this->set('posts', $this->paginate('Post'));
}
but still no luck on getting the rest of the associations to come through!
Can anyone help? Thanks

There are two ways to do this. One way to accomplish this is to join tables on the fly. The other way to do this is with the containable behaviour.
To use the containable behaviour you must add var $actsAs = array('Containable'); to all the models involved (or AppModel if you use it a lot). Then you can something like this in the controller:
$post = $this->Post->find('first', array(
'conditions' => array('id' => $id),
'contain' => array('User' => 'Profile')
));
From the view you could use <?php echo $post['User']['Profile']['firstname']; ?>

Related

Laravel - Using post method in controller

I'm new to laravel and I'm still trying to learn.
This my code in my controller:
public function index(User $user){
$posts = $user->posts()->with(['user', 'likes']);
return view('users.posts.index', [
'user' => $user,
'posts' => $posts,
]);
}
And this is the code on blade.php
{{$user->name}}
I've also tried checking if the code:
dd($posts = $user->posts()->with(['user', 'likes']));
and it seems fine since it's returning data from the database. With these codes it was supposed to show the User's Post on a different page when you click on the User's name. The problem is that it only shows the user's name but not the user's post. I'm only following a tutorial but the result was different with the tutorial and the one that I'm doing. Can someone please help?
I suppose you are trying to lazy eager load the posts relationship on this $user and load the user and likes relationship for those posts. You can try to use load to load these relationships on the Collection:
$posts = $user->posts->load('user', 'likes');
Or load on the User instance:
$user->load('posts.user', 'posts.likes');
$posts = $user->posts;
If you are not trying to have these relationships loaded on the $user but you just want the posts with their relationships you can eager load them off of the relationship itself:
$posts = $user->posts()->with('user', 'likes')->get();

Cakephp - Multiple Models for one Table

I have a User model that is used to store data on users of a dental examination system.
Typically, three types of users will exist: Admininistrator, Location Manager and Examiner.
It seems to have become necessary to treat these three roles as seperate models in my application (imagine how I'd have a different view for each role with different options etc... It's a nightmare).
How would I go about setting up the relationships in each Model.
My first thought is:
//Administrator Model
class Administrator extends User {
$name = 'Administrator';
$table = 'User';
$belongsTo = array(
'User' => array(
'className' => 'User',
'conditions' => array('User.role' => 'administrator'),
)
);
}
And then the User model will reference this one using a hasMany? In terms of CakePHP convention, how would one actually model this accurately?
Also, would this model extend the User model or the AppModel?
Of course you can create different models using the same table name. To do so, link each model with specific table with $useTable property (not $table):
class Administrator extends AppModel {
public $useTable = 'User';
}
But I don't know any CakePHP model property which will allow you to filter data returned when fetching results... You can only write your own conditions when linking model with another one, for example:
class SomeOtherModel extends AppModel {
public $hasMany = array(
'Administrator' => array(
'className' => 'Administrator',
'conditions' => array('Administrator.role' => 'administrator')
)
);
}
Which is not a good solution, because it will work only when executing queries for SomeOtherModel model.
You can also try applying an afterFind() callback to your Administrator / Examiner / ... models which will delete users of another role than needed, but of course it is not an efficient solution.
Another solution is just creating different database views which will contain only users of selected role (more on views - here). Then you can point each view to your CakePHP model, just as it was a normal database table.
But this solution is also not perfect. If you will have to add a new user role or change your table schema in the future, you will also have to modify your database views.

Yii - CGridView

I am writting my first application with Yii (v1.1.12), and the learning curve is a bit steep for me, so I need some help.
Imagine the following tables (with their relations):
detail (n:1) document
document (n:1) user
user (n:1) department
document (n:1) category
user is the table that holds the information about the users that can login and use the application.
I have managed to put together (using Gii and hacking about) a view that lists all the documents, and have also managed to display the category name instead of the category ID in the grid.
One of the features I want to implement is allow the user switch the view so (a) only the documents relating to the logged in user are listed, or (b) only the documents relating to his/her department.
I looked around a bit with no luck. Can anyone help?
Cheers,
George
UPDATE: Currenlty I display the list of documents using zii.widgets.grid.CGridView.
UPDATE 2:
Following Omar's reference to CDbCriteria I found this URL with a bit more detail on the subject.
I came up with the following model code, that works fine:
public function searchByUser($user_id)
{
$criteria=new CDbCriteria;
$criteria->condition = " user_id = ".$user_id;
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
public function searchByDepartment($user_id)
{
$criteria=new CDbCriteria;
$criteria->alias="p";
$criteria->join = "JOIN (SELECT u.id
FROM user u
INNER JOIN user uu
ON u.department_id = uu.department_id
WHERE uu.id = ".$user_id.") uu
ON p.user_id = uu.id";
return new CActiveDataProvider($this, array('criteria'=>$criteria,));
}
While the above works as expected, I was hoping for a solution that would not require me to write chopped SQL code at all. Not due to lazyness, but just to leverage more of the functionality of the framework.
I just have the feeling that this approach doesn't follow best practices (?).
Try to create your own CDBCriteria and define whatever conditions inside it and pass it as data provider to your grid view.
If you allowed the search inside the grid view, pass the criteria to the search function, and inside it, merge the passed criteria with criteria inside the search.
You could use relations to achieve what you're after. For example, to view all the documents and departments of a certain user you first need to set up the relations for that user, in your case you could set your users model up like so;
public function relations()
{
return array(
'documents' => array(self::HAS_MANY, 'Document', 'user_id'),
'department' => array(self::HAS_ONE, 'Department', 'department_id'),
);
}
You can then pull all the documents for the current user like so:
$user = User::model()->findByPk($userId);
$documents = $user->documents;
$documents will then be an array of active models for all that users documents.
To obtain all the documents of that users department, there's a couple of options. You could use relations again, adding to the Department model the following:
public function relations()
{
return array(
'users' => array(self::HAS_MANY, 'User', 'department_id'),
'documents' => array(self::HAS_MANY, 'Document', 'document_id', 'through'=>'users'),
);
}
Which should give you the ability to pull all of a departments documents like so;
$department = Department::model()->findByPk($departmentId);
$documents = $department->documents;
Which in turn would mean you could grab the users department documents like so:
$user = User::model()->findByPk($userId);
$documents = $user->department->documents;
There may well be a more efficient way to grab those by using a relation in the Users model, but it's too late for me to work that our right now ;)
Once you have an array of active record models, you can always pass them to a data provider by using CArrayDataProvider like so;
$dataprovider = new CArrayDataProvider($documents);
I've not tested any of those by the way, so they may need some editing!
You need to modify the search function on the appropriate model (I'm going to guess at documents). You'll already be able to see code in there you can use.
Add some parameters to the search function itself, which can be passed in from the controller. Then use these to determine which compare calls to make.

CakePHP: controller uses another model instead of its own

The situations looks like this: I have two models with controllers and everything, WrittenTest and WrittenTestAnswer. The problem is that whenever I try to access model WrittenTestAnswer, both from WrittenTestsController (using $this->loadModel() before) and from its own WrittenTestAnswersController, it somehow accesses WrittenTest instead. I noticed it when data wasn't saved to WrittenTestAnswer, $this->WrittenTestAnswer->find() also returned data from written_tests table. I have no idea what's going on. I checked names and stuff so many times already. I am using CakePHP 1.3. Thanks for any help.
EDIT:
code from WrittenTestAnswer model:
class WrittenTestAnswer extends AppModel {
public $name = 'WrittenTestAnswer';
public $displayField = 'written_test_answer';
public $belongsTo = array(
'WrittenTest' => array(
'className' => 'WrittenTest',
'foreignKey' => 'written_test_id',
),
);
}
EDIT so for example this (in WrittenTestAnswersController)
debug($this->WrittenTestAnswer->name);
outputs WrittenTest. And
$this->WrittenTestAnswer->find('first');
returns first row from written_tests. Any idea what's going on?
How are you accessing the model? If the relationships are setup correctly, you shouldn't need to use $this->loadModel() at all. You can just access the related model through its parent like so:
$this->WrittenTest->WrittenTestAnswer->find('all');
This might not be the cause of your problems but we cannot tell until you actually post the code that's not working.

Data Filtering in Cakephp

Please forgive me, I am still fairly new to cakephp and in the process of reviving my redundant PhP skills.
I have searched all over the "interweb" for the answer, sadly to no avail.
Here is the scenario:
I have 2 tables - 1 being Users the other Groups ( for permissions).
Users table - pretty standard plus some additional profile info.
Groups table - holds Administrators, Super Users, Auth Users and Unauth Users.
The Users table is being used for authentication and profiles (other users can browse user profiles). Now with this in mind, I cannot work out how to filter out which users get rendered into the index view based on which group the users (not the the currently logged user) belong.
For example, The admin and Super user accounts profiles are being rendered and belongs to the "Administrators" and "Super users" groups respectively along with all the other Users. However all I want end users (Auth Users) to be able to see are other user profiles which are part of the Auth Users group.
So any and all help would be appreciated for both saving the remainder of my hair and finding a resolve to this problem I have.
Ok so here is what I did which is working like a charm. Configured the paginate variable to look like this:
var $paginate = array(
'fields' => array(
'User.img_file', 'User.given_name', 'User.family_name', 'Group.name', 'User.username', 'User.created'),
'limit' => 10,
'order' => array(
'User.given_name' => 'asc'
)
);
Then for the in the index function (which I wanted) I added the following:
function index() {
$this->User->recursive = 0;
$this->paginate = array(
'conditions' => array(
'group_id' => 3
));
$users = $this->paginate('User');
$this->set(compact('users'));
}
It's working like I want it to, but if anything does look malformed, or this can be extended, please do post comments.
First off, take a look at this for information on using the built in authentication features of cakephp.
" I cannot work out how to filter out which users get rendered into the index view based on which group the users (not the the currently logged user) belong."
not 100% on what you mean by this, but if you mean you only want to see other users that are admins (and assuming you know that the admin_group_id == 1) then, what you kind of want is the following.
$administrator_group_id = 1;
$conditions = array(
'group_id' => "{$administrator_group_id}"
);
$this->data = $this->User->findAll($conditions);
// note. automatically makes the result data available in the VIEW layer
pr($this->data); // VARDUMP utility
(ps. check this out for how to paginate this result data)
"...all I want end users (Auth Users) to be able to see are other user profiles which are part of the Auth Users group"
(assuming the auth stuff didn't help you.)
(if there are only 3 types of group types, and I can control them completely, then I would consider not using a group table, and using an enumeration. here are notes on how to do this in MySQL
http://dev.mysql.com/doc/refman/5.0/en/enum.html
or, you can just sort of hack it in PHP.
eg.
define('USER', 1);
define('S_USER', 10);
define('ADMIN', 100);
$user = $this->User->findById($my_user_id); // or get from auth
$conditions = array(
"group_id <= " => "{$user['User']['group_id']}"
);
$this->data = $this->User->findAll($conditions);
pr($this->data); // VARDUMP
( what I did was that you get the logged in user, and I made sure that the ADMIN had the highest level ID. SuperUsers had the 2nd, and Users the lowest. This query will return all users that are on the same level, or lower than their own level. group_id <= User.group_id)
I hope I haven't confused you too much. Just keep on experimenting with cake. It's worth it!

Categories