CakePHP paginate results with a condition on another table linked with HABTM - php

I've done some searching but I can't find anything relevant enough/working for my scenario. I've got:
Jobs <--> HABTM (Users_jobs table) <--> Users
I would like to do a paginate() from my Job controller with a condition on the User.id, as I do need to fetch -and paginate- all the jobs from the current user.
If you do provide a link to another topic/site, please provide an explanation with it and how you would apply it to my case.
Cheers,
Nicolas.

Make sure your HABTM associations are setup correctly (cake bake would expect a join table of jobs_users rather than users_jobs (see: Cakephp-HABTM) If they are, change the references to JobsUser in the example below to UsersJob to match the layout you described.
//In an action in jobs_controller.php
//Fake a HasOne association to the JobsUser model, setting $reset to false
$this->Job->bindModel(array(
'hasOne' => array(
'JobsUser'
)
), false);
//Setup the paginate conditions, grouping on job.id
//Set $user_id to the user to filter results by (can also be an array of users)
$options = array(
'group' => 'Job.id',
'conditions' => array(
'JobsUser.user.id' => $user_id
)
);
$this->paginate = $options;
$users = $this->paginate();

Related

GroceryCrud set a n-n relation with where clause

I have three tables (simplified) which are:
And I have to display all houses for each user.
In my controller I have a function like this:
public function create_houses_table($usr_id)
{
$crud = new grocery_CRUD();
$crud->set_language("italian");
$crud->set_theme('datatables');
$crud->set_subject('Casette');
$crud->set_table('tbl_houses');
$crud->set_relation_n_n('Casette',
'tbl_users_houses',
'tbl_users',
'house_id',
'user_id',
'usr_name',
NULL,
array('user_id' => $usr_id));
...
}
and what I get is this:
Every time I select a user from the combo I need to refresh my list filtering on usr_id...but I get always all the houses.
What I'm wrong?
This is not the intended usage for set_relation_n_n (it will show all the user houses in one field inside the user row).
What you want can be better done listing from tbl_users_houses, filtering by client with $crud->where() and linking with the other tables with two simple relations.
If I understand correctly you are trying to fetch only the records for the logged in User... and u have multiple users per house, hence the n-n relation.
I also faced this problem and here's what I did.
$myprojects = $this->admin_model->get_employee_projects($this->user_id);
$myprojectids = array_column($myprojects, 'id');
//get only one column from the multi-dimensional array
$crud->where("`projects`.id IN", "(" . implode(",", $myprojectids) . ")", false);
// the false disables escaping
$crud->set_relation_n_n('assigned_employees', 'project_employees', 'employees', 'project', 'employee', 'name');
//Only so it also still shows the name of Users assigned
So basically projects here is like houses, and I am using the WHERE IN clause to filter the records based on the projects I get from my model method...

unable to fetch records from multiple tables using join

I am using Yii framework. i am wonder how i can get records from multiple tables i did research but couldn't find any usefull link i am using following code for this please let me know where i am missing
my model Task.php
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'prj_user' => array(self::BELONGS_TO, 'User', 'id'),
);
}
model User.php
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
array('task', self::HAS_MANY, 'Task','project_id')
);
}
and this is my main controller
$criteria = new CDbCriteria;
$criteria->compare('t.id', 1);
$criteria->with = array( 'prj_user' => array('select' => 'username,title,roles', 'joinType'=>'inner join'));
$rows = Task::model()->findAll( $criteria );
but still i am getting columns only from task table but i need more three columns from users table please help me
Let Yii worry about joining your tables. Your relations looks fine so you should be able to access them directly
For example, what does this return?
foreach ($rows as $task)
{
if ( isset($task->prj_user) )
echo $task->prj_user->username . "<br>";
}
Or this?
this->widget('zii.widgets.grid.CGridView', array(
'dataProvider'=>new CActiveDataProvider('Task'),
'columns'=>array(
'id',
'prj_user.username',
'prj_user.title',
'prj_user.roles',
)
));
->with() is used for eager loading, so at this point you probably don't need it. In fact, unless I misread you completely, you can remove your criteria all together.

Relational Databases in Yii

So I've tried this: http://www.yiiframework.com/wiki/285/accessing-data-in-a-join-table-with-the-related-models
Basically I have a table called User which relates to ToolAccess; related via a primary key on User and a field for userID on ToolAccess. Now tool access relates to the table Tool which contains a ToolID. Now this doesn't work in Yii, I can't seem to get the toolName field off of the tool table using Yii. Any ideas on how to do this on a Active Record?
I'm using giix if that matters.
Relations code:
public function relations() {
return array(
'usergalleries' => array(self::HAS_MANY, 'Usergallery', 'userid'),
'userinroles' => array(self::HAS_MANY, 'Userinroles', 'userid'),
'userfailedlogin' => array(self::HAS_MANY, 'Userfailedlogin','userid'),
// table name, relation, class name, relation key
'toolaccess' =>array(self::HAS_MANY, 'Toolaccess','userid'),
'tool' =>array(self::HAS_MANY, 'Tool','toolid')
);
}
I'm assuming your schema looks something like this:
User table tool_access table Tool table
id | other columns userid | toolid id | name | other columns
In this case, the User model should have a relation like this (note that the tools will be ordered by name in this case):
public function relations() {
return array(
// other relations here...
'tools' =>array(self::MANY_MANY, 'Tool', 'tool_access(userid,toolid)',
'order' => 'tools.name',
),
);
}
and the code to read the tools should look like this:
$user = User::model()->with('tools')->findByPk($id);
foreach($user->tools as $tool) {
echo $tool->name;
}
I used eager loading of the tools here mostly because of personal preference, using lazy loading should work just as well in this case. But eager loading should be preferred whenever you're processing multiple User records at once.
So if I have understood it properly, user and tool are related in a many-to-many relationship by their primary keys.
So you should define this relationship in the User model like:
'tools' => array(self::MANY_MANY, 'Tool', 'tool_access(userid, toolid)', 'index' => 'id'),
This way you can access the name of the tool after getting the user model
$user = User::model->findByPk($id);
$tools = $user->tools;
foreach ($tools as $tool)
{
echo $tool->name;
}
I hope it works for you.

Retrieving models without getting associated models - CakePHP

I use the find('all') function to retrieve the post records from my database, but this will also return all the User information that is associated with the Post model with a belongsTo - hasMany relationship.
The downside of this is that the user model contains password and other important information. Is this considered a security issue? I am nowhere echo-ing the information on the view.
Thanks
EDIT:
I modified my code but I am still getting the associated models.
$this->set('posts_list',$this->Post->find('all',array('contain' => false, 'order' => array('Post.price ASC'))));
Any ideas?
You have several options. You can set the recursive property on a model:
$this->Post->recursive = -1;
$posts = $this->Post->find('all');
Alterantively, you can specify recursive as an option to your search:
$posts = $this->Post->find('all', array(
'recursive' => -1,
'conditions' => ...
);
You can also use the Containable behaviour in your Post model. In that case you can specify an empty set:
class Post extends AppModel {
var $actsAs = array('Containable');
}
$this->Post->contain();
$posts = $this->Post->find('all');
Or, specified in the query:
$posts = $this->Post->find('all', array(
'contain' => false,
);
The upside for the Containable behaviour is when you later on associate other models with your post. Suppose that you implement a Tag model. Now you want to find a post with it's tags, but not the use model:
$posts = $this->Post->find('all', array(
'contain' => array('Tag'),
);
Not necessarily.
But you are retrieving information when you don't need it. It's not a problem now, but keep in mind this becomes a huge problem when you have a lot of associated data
Consider setting your recursive attribute to -1 (or 0 if needed)
$this->Model->recursive = -1;
This will pull data only from the selected model
Or for more fine tuned selection, you can use the Containable behavior : http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html
This allows you to select which associations to keep when retrieving data.
just so you know
$this->Model->recursive = -1 will remove all associations
$this->Model->recursive = 0 will remove only hasMany assosiation (so it keeps belongsTo)
Do u use this:
$this->Post->find('all')// If u access it from Post controller
OR,
$this->User->Post->find('all')//If u access it from User controller

How to include associations in find() with fieldlist

I want to receive model data by find(all), but the user should get only a restricted set of table fields. That's easy:
$ret = $this->find('all',array('fields'=>array(
'Employee.id','Employee.address_id'
)));
But this model (Employees model) also has a belongsTo association:
var $belongsTo = array(
'Address' => array(
'className' => 'Address',
'foreignKey' => 'address_id',
'fields' => array('Address.full_name')
)
);
I want the Address.full_name field to appear in my fetched data too. But it doesn't work with the find() call above, and it throws an error (SQL Error: 1054: Unknown column 'Address.full_name' in 'field list') when trying this:
'fields'=>array('Employee.id','Employee.address_id','Address.full_name')
Anyone knows how to solve this?
EDIT: I totally forgot that Address.full_name is a virtual field. Looking at the Cakephp-produced SQL, it's obvious why it doesn't work:
SELECT
`Employee`.`id`, `Employee`.`address_id`, `Address`.`full_name`
FROM
`employees` AS `Employee`
LEFT JOIN `addresses` AS `Address`
ON (`Employee`.`address_id` = `Address`.`id`)
WHERE 1 = 1
In the address model, full_name is defined like this:
var $virtualFields = array(
'full_name' => 'CONCAT_WS(" ", Address.firstname, Address.surname)'
);
So then, the question is: Is it a CakePHP bug that it's not able to include (foreign model's) virtual fields within a fieldlist supplied to find()?
Unfortunately, you cannot use virtual fields the way you wish to. From Limitations of Virtual Fields in the Cake documentation:
The implementation of virtualFields in 1.3 has a few limitations. First you cannot use virtualFields on associated models for conditions, order, or fields arrays. Doing so will generally result in an SQL error as the fields are not replaced by the ORM. This is because it difficult to estimate the depth at which an associated model might be found.
It looks like you'll have to use the Containable behaviour.
I would use the Containable behavior in this case.
Make sure you have the Containable behavior loaded in your Employee model first:
var $actsAs = array('Containable');
Then, when you're trying to get your data, do it like this:
$params = array('fields' => array('Employee.id', 'Employee.address_id'),
'contain' => array('Address' => array('fields' => array('Address.full_name')));
$ret = $this->find('all', $params);
More on the containable behavior here: http://book.cakephp.org/view/1323/Containable
SQL Error: 1054: Unknown column
'Address.full_name' in 'field list')
This error gives you a clue that something is amiss with either your column name call (could it be fullname rather than full_name) or, more likely your Model definition. Employee belongsTo an Address but does the Address haveOne or haveMany Employees?

Categories