How to get all the details from multiple tables in Cakephp? - php

I have two different tables as Post and Post_Info.
I want to get all the details using post_id in cake php from both the tables. The Post_Info table having post_id as foreign key and other related details.
I am new to Cakephp and I don't know how to get data from multiple tables in Cakephp.
I can get data from Post table using following query in Cakephp.
Following code is getting data from Post table.
$userdata = $this->Post->find('first', array(
'conditions' => array('Post.post_id' => $post_id)
));

To achieve this you have to do two things.
Make a relation of belongsTo in PostInfoTable.php in your model folder. you can read more about it here.
Once you have made the relationship all you have to do is use "contain" in your query along with the Model name. Your query will be modified as below
$userdata = $this->Post->find('first', array(
'conditions' => array('Post.post_id' => $post_id),
'contain' => ['PostInfo']
));
And you are good to go.
Moreover, I will recommend you to go through the documentation at least once it will be a great help to you.

Related

CakePHP Paginate Sort on Contain Data

It's a simple application for tracking user hobbies. A user can have many hobbies, which are organized into different hobby groups.
So I have 4 related tables: users, users_hobbies, hobbies, hobby_groups.
users is a has-and-belongs-to-many relation to hobbies, via the users_hobbies join table.
hobbies belongs-to a hobby_group, hobby_group has-many hobbies
Makes sense, pretty simple so far.
In UsersHobbiesController, I have:
$paginate = [
'contain' => [
'Hobby' => [
'fields' => ['id', 'name'],
'HobbyGroup' => [
'fields' => ['id', 'name']
]
]
]
];
Also, in my index() function, I build the pagination.
$usersHobbies = $this->paginate('UsersHobby');
$this->set(compact('usersHobbies));
When I access the $usersHobbies variable in my index.ctp View file, it has the data I want for building an output table. It looks like:
array(
(int) 0 => array(
'UsersHobby' => array(...),
'Hobby' => array(
'id' => 'abc-def-ghi',
'name' => 'Jet Skiing',
'HobbyGroup' => array(
'id' => 'oiuy-trew-qldf',
'name' => 'Watersports'
)
)
)
....
)
To sort the output table, I've added some sorting columns:
<th><?php echo $this->Paginator->sort('Hobby.name', 'Hobby'); ?></th>
<th><?php echo $this->Paginator->sort('Hobby.HobbyGroup.id', 'Hobby'); ?></th>
The first header works for sorting on the Hobby name. But I can't get the second header to sort for the HobbyGroup. Is there a simple way to do this?
I've searched for a few hours on StackOverflow, but can't seem to find an answer that works for me. Thanks.
This will not work as you expect because of how CakePHP fetches data.
Depending how you setup your relationships what will happen is that Cake will first select the Hobbies and then do another query for Hobby Groups.
So any sorting you apply to the second query will be generally useless and lost when that data is mapped to the list of Hobbies.
So you have
SELECT hobbies....
Then another select will be issued
SELECT hooby_groups WHERE hobby_id IN [list of ids ] ORDER BY hobby_groups_id
The data from the second select will be mapped to the first one, so your order by did not do much!
Depending on what CakePHP version you are using you can do a couple of things:
Define the relationship to use INNER JOIN strategy so will avoid creating two SELECT statements. This way the order by cause will sort all the data as you expect. The caveat is that with INNER JOIN Hobbies that do not have any group associated will never be selected by your pagination.
If you are using CakePHP 2.x you could have a look at Custom Query Pagination and build the query as you need it to be for it to work.
If you're using CakePHP 3.x you have a query builder there and the Paginator can paginate any query, so you can do you inner joins there.
To help you out you can use the DebugKit from CakePHP to see what actual queries are being run and how tweaking this and that parameter affects how the query is being generated. You will see where the order by is applied and why it may not work.
In CakePHP 2.x you can use type to control how the joining is done. You need to use INNER

Optimize query in Laravel Backpack n-n relationships

I am building a backend panel for a website with Laravel Backpack. It is really nice, but I have noticed that relationship queries are very expensive.
I have two models: Product and Center with a many to many relationship between them. In my CenterCrudController I have defined a field this way:
$this->crud->addColumns([
// More fields...
[
'label' => 'Products',
'type' => 'select2_multiple',
'name' => 'products', // the method that defines the relationship in your Model
'entity' => 'products', // the method that defines the relationship in your Model
'attribute' => 'name', // foreign key attribute that is shown to user
'model' => 'App\Models\Product', // foreign key model
'pivot' => true, // on create&update, do you need to add/delete pivot table entries?
],
// More fields...
]);
It works fine, showing a select multiple field with related models. But the query used is SELECT * FROM products, which is highly expensive (table products have thousands of records with about 25 columns).
In this example I only need id and name fields. I am looking for something like Query Builder select() method.
Is there a way for optimizing this type of query?
Thanks in advance!
Not sure if this is actually an answer, but I'll post it anyway.
The best solution (as pointed by #tabacitu) was using select2_from_ajax field. It doesn't slow page load and make an ajax request for retrieving data only when user clicks on the select field.

cakephp find query conditions on associated model

I have a model User and a model Role in a CakePHP application. The association between the two models is the following:
User $belongsTo Role
Role $hasMany User
I want to make a query on the User model to find all users with a specific role (let's say the role Supervisor). I did my query like this:
$supervisors = $this->User->find('all', array(
'contain' => array(
'Role' => array(
'conditions' => array(
'Role.name' => 'Supervisor'
)
)
)
));
But the above query returns me all the users in my users table. It does not return only the users with role Supervisor. I know that if I do two queries, one on the Role model to find the id of the role type 'Supervisor' and then do another query on the User model and pass the id of the supervisor role record in the conditions on my User model like this:
$supervisor_role_id = $this->Role->field('id', array('Role.name' => 'Supervisor'));
$supervisors = $this->User->find('all', array(
'conditions' => array(
'User.role_id' => $supervisor_role_id
)
));
The above queries will give me the desired result. But I don't wanna do 2 queries to do this. Why doesn't the first approach work. Any idea please?
Thank you
The reason your attempt didn't work
CakePHP's Containble Behavior creates separate queries for each model. So - what you did was basically described like this: "Find all Users. Also find any Roles with the name of 'Supervisor". As you can see, there is no condition that crosses between the two.
So, you can do one of the following:
1) [easy way] Query the other way around
Query from the Role model and contain it's user(s). This pulls the role you want (based on your provided conditions) then contains any/all of it's users.
Note - if you've already loaded the 'User' model (or it's been loaded by default because you're in the UsersController), you can run your find like this: $this->User->Role->find(..... - so you don't have to load the Role model separately.
2) Use JOINs (see CakePHP Book on Joining Tables)
This allows you to limit the result of a parent model based on it's associated data.

Save a HABTM record to database CakePHP

I am working on a followers / following system with CakePHP 2.
I have setup my database with a users table, and a user_users table. The users table is the main table containing every user on the system, whilst the user_users table contains the records of followers.
I then have a UsersController, User model and Follower model.
I can successful output a button to either say Follow or Following dependent on whether the currently logged in user is following the user of which the profile they are viewing belongs to, however what I am unable to understand how to do, is create new following relationships in the table. In other words, I do not know how to create records in the user_users table.
I am not sure where the logic for this should go, and thus what my "Follow" button should point to.
This is probably a very simple question, but I am totally stumped. I have tried adding a "follow" action to the UsersController but I cannot get that to work.
Any help much appreciated,
Duncan
HATBM isn't a good fit in this situation. From the cookbook:
HABTM data is treated like a complete set, each time a new data
association is added the complete set of associated rows in database
is dropped and created again so you will always need to pass the whole
data set for saving. For an alternative to using HABTM see hasMany
through (The Join Model)
For this reason, HABTM is mainly good for pretty 'dumb' relationships. I've used it in cases such as where a User has to select many Interests - and they just get a list of checkboxes, where they can click multiple Interests, and save them all in one hit.
In your case, it'll be easier to have a separate table with it's own model. I'd call it Relationships or something similar. It would have an id, followed_by_id, following_id, and any other fields you may need.
I've dug up some code from an old cake 1.3 app, but it should help you out. Your Relationships model would look something like this:
<?php
class Relationship extends AppModel {
var $name = 'Relationship';
var $belongsTo = array(
'FollowedBy' => array(
'className' => 'User',
'foreignKey' => 'followed_by_id'
),
'Following' => array(
'className' => 'User',
'foreignKey' => 'following_id'
)
);
}
?>
Your User's model would have to have relationships like this:
var $hasMany = array(
'Followers' => array(
'className' => 'Relationship',
'foreignKey' => 'following_id',
'dependent'=> true
),
'FollowingUsers' => array(
'className' => 'Relationship',
'foreignKey' => 'followed_by_id',
'dependent'=> true
),
);
Then in your relationships controller, you'd have methods something like this:
function add($following_id = null) {
$this->Relationship->create();
$this->Relationship->set('followed_by_id',$this->Auth->User('id'));
$this->Relationship->set('following_id',$following_id);
$this->Relationship->save();
$this->redirect($this->referer());
}
function delete($id = null) {
$this->Relationship->delete($id);
$this->redirect($this->referer());
}
Note that in that code, I'm modifying the database with a GET request - which I really shouldn't be doing (it's old code, from years ago). You'll want to enforce a POST request for both the add and delete methods, since they're modifying the database.
But still, that code should set you on the right track.

Building conditional statement across multiple models (CakePHP 1.2.5)

Let's say I have 3 models: User, Region, Country.
User belongsTo Region
Region belongsTo Country
Each of these models is using the Containable behavior. I'm attempting to find users from the country with code 'US'. Here's what I'm attempting:
$users = $this->User->find('all', array(
'conditions' => array('Country.code' => 'US'),
'contain' => array('Region.Country'),
));
CakePHP is separating this into 2 queries:
First, it is SELECTing the ID's for all countries with code 'US'.
Then it is using those ID's to SELECT all users JOINing regions where region.country_id is in that list of country ID's previously retrieved.
As a result, I end up with SQL errors in my app since my conditions array contains a reference to Country.code, and the second query that Cake builds doesn't JOIN countries.
The best Cake solution I see is to build a sub-query as described in the Complex Find Conditions portion of the manual. However, this seems very convoluted, and is more of a hack than I would like to implement. Is there an easier way that I'm overlooking?
Nate Abele (former lead dev of CakePHP) wrote an article about doing ad hoc joins which might help.
Put the conditions in the contain as well. E.g:
$users = $this->User->find('all', array('contain' => array(
'Region' => array(
'Country' => array(
'conditions' => array('Country.code' => 'US'),
),
),
)));
See the second-to-last example in the Containable manual

Categories