Cakephp condition on belongs to many table - cakephp

I have 3 tables
users
bases
bases_users
In usersTable I have created a belongsToMany relation like
$this->belongsToMany('Bases', [
'foreignKey' => 'user_id',
'targetForeignKey' => 'base_id',
'joinTable' => 'bases_users',
]);
Now I am trying to fetch all users where user is under this login user bases.
Query like below
SELECT users.name FROM users
INNER JOIN bases_users on bases_users.user_id = users.id
WHERE bases_users.base_id in (1,2) //login user 1 has tow bases (1,2)
GROUP BY Users.id
I'm trying to get accepted data using below query with content
$this->Users->find()
->contain([
'UserAuthorities',
'Bases' => function($q) use($userId){
return $q->where(['Bases.users.id' => $userId]);
}
])
How can I match the data where BasesUsers.user_id = $userId ?
I have tried the code after #ndm comment like below
$queryUsers = $this->Users->find()
->contain([
'UserAuthorities',
'Bases'
])
->innerJoinWith('BasesUsers', function(\Cake\ORM\Query $q) {
return $q->where(['BasesUsers.user_id' => 10]);
})
->where($conditions)
->order($order)
;
Query is generating :
SELECT
...
FROM
users Users
INNER JOIN bases_users BasesUsers ON (
BasesUsers.user_id = 10
AND Users.id = BasesUsers.user_id
)
INNER JOIN user_authorities UserAuthorities ON UserAuthorities.id = Users.user_authority_id
WHERE
(
Users.company_id = 1
AND Users.is_deleted = FALSE
)
ORDER BY
Users.created DESC
LIMIT
20 OFFSET 0
Here My expecting query is
INNER JOIN bases_users BasesUsers ON (
BasesUsers.user_id = 10
)
Sample data
Users table :
id, name,
1 A
2 B
3 C
4 D
Bases table :
id Name
1 Base 1
2 Base 2
3 Base 3
BasesUsers:
id user_id base_id
1 1 1
2 1 2
3 2 1
4 3 1
5 4 3
6 3 2
Expected result for user id 1 logged in
name bases
A Base1,Base2
B Base1
C Base1,Base2
How I will remove AND Users.id = BasesUsers.user_id from innerJoin with ?

I have the same problem, not exactly for compare two id's but for check if a column match a condition, i solve this using matching()
Read this of CakePHP documentation:
https://book.cakephp.org/4/en/orm/retrieving-data-and-resultsets.html#filtering-by-associated-data-via-matching-and-joins
I use those examples and work perfect. Hope this can help you.

Related

Array structure of raw SQL query results in CakePHP 3.8 / CakePHP 4 vs CakePHP 2

After CakePHP 4.0 has been released, we are considering to migrate our CakePHP 2.x application to 3.8 or 4.0. Currently, we are stuck with this issue:
Our application uses raw SQL statements sometimes using the Model::query() method.
For example, this CakePHP 2 code:
$sql = "SELECT u.id, u.firstname FROM users u, contacts c WHERE u.id = 2 and c.id = u.contact_id";
$u = $this->User->query($sql); // Or on any other model...
$this->log($u);
returns
Array
(
[0] => Array
(
[u] => Array
(
[id] => 2
[firstname] => MyFirstName
)
[c] => Array
(
[zip] => 12345
)
)
)
When we try to do the same thing with CakePHP 3.8
$connection = ConnectionManager::get('default');
$sql = "SELECT u.id, u.firstname FROM users u, contacts c WHERE u.id = 1 and c.id = u.contact_id";
$u = $connection->execute($sql)->fetchAll();
$this->log($u);
the result is
Array
(
[0] => Array
(
[0] => 2
[1] => MyFirstName
[2] => 12345
)
)
In order to migrate safely: Is there a way to make CakePHP 3.8 / 4 return query results using the same array structure as in CakePHP 2?
Rewriting the statements to use ORM is not an option. The example above is not a real code - the real queries are more complex.
Per the documentation, you should be using ->fetchAll('assoc'). The output of this isn't quite identical to what you had in Cake 2, but at least the field names are present.

Linq Query Condition for multiple row

I tried to solve one query from last 2 days but didn't.
It looks easy to understand but can't made.
There are two column in Table for example:
ResourceId || MappingId
1 2
1 3
2 2
2 4
3 2
3 4
4 2
4 5
5 2
5 4
This is one table which have two fields ResourceId and MappingId.
Now I want resourceId which have Mappingid {2,4}
Means answer must be ResourceId {2,3,5}
How can I get this answer in Linq Query?
Use Contains of collection. This method can be translated by Entity Framework into SQL IN operator:
int[] mappingIds = { 2, 4 };
var resources = from t in table
where mappingIds.Contains(t.MappingId)
select t.ResourceId;
Lambda syntax:
var resources = table.Where(t => mappingIds.Contains(t.MappingId))
.Select(t => t.ResourceId);
Generated SQL will look like:
SELECT [Extent1].[ResourceId]
FROM [dbo].[TableName] AS [Extent1]
WHERE [Extent1].[MappingId] IN (2,4)
UPDATE: If you want to get resources which have ALL provided mapping ids, then
var resources = from t in table
group t by t.ResourceId into g
where mappingIds.All(id => g.Any(t => t.Id == id))
select g.Key;
Entity Framework is able to translate this query into SQL, but it will not be that beautiful as query above.
IQueryable<int> resourceIds = table
// groups items by ResourceId
.GroupBy(item => item.ResourceId)
// consider only group where: an item has 2 as MappingId value
.Where(group => group.Select(item => item.MappingId).Contains(2))
// AND where: an item has 4 as MappingId value
.Where(group => group.Select(item => item.MappingId).Contains(4))
// select Key (= ResourceId) of filtered groups
.Select(group => group.Key);

way for using DATE_SUB(CURDATE(),INTERVAL 2 DAY) condition in cakephp

i am using cakephp and creating this condition:
$conditions = array("Userword.levelid"=>0, "Userword.userid"=>$this->Auth->user('UserId'),"date(Userword.date)"=>"DATE_SUB(CURDATE(),INTERVAL 0 DAY)");
$Userword = $this->paginate(null, $conditions);
cake using this select :
`Userword`.`userwordid`, `Userword`.`userid`, `Userword`.`wordid`, `Userword`.`date`, `Userword`.`levelid`, `Word`.`wordid`, `Word`.`word`, `Word`.`meaning`, `User`.`UserId`, `User`.`Email`, `User`.`password`, `User`.`username`, `User`.`cell`, `User`.`web` FROM `userwords` AS `Userword` LEFT JOIN `words` AS `Word` ON (`Userword`.`wordid` = `Word`.`wordid`) LEFT JOIN `users` AS `User` ON (`Userword`.`userid` = `User`.`UserId`) WHERE date(`Userword`.`date`) = 'DATE_SUB(CURDATE(),INTERVAL 2 DAY)'
but by this select mysql can't return record because cakephp in this select use this character (') in last passage.
when i remove this character and test this code in phpmyadmin this code return rows. two images in below show this :
Just don't separate it out as key => value pair:
$conditions = array(
...
'DATE(Userword.date) = DATE_SUB(CURDATE(), INTERVAL 0 DAY)'
);

CAKEPHP Group by problem in paginate

CAKEPHP Group by problem in paginate..
This is my table structure
Friends Table
`id` int(11) NOT NULL AUTO_INCREMENT,
`user1_id` int(11) NOT NULL DEFAULT '0',--> UserFrom
`user2_id` int(11) NOT NULL DEFAULT '0',---> UserTo
I need to get all the unique records of one user and his user1_id = 100
There are lot of duplicate values in user2_id. I need to get the unique values
While i trying this code it returns only first 12 values(according to limit).
If i commented the group by line then all records are displaying (including duplicate values)
$this->paginate = array(
'conditions' => $conditions,
'contain'=>array(
'UserFrom'=>array(
'fields'=>array(
'UserFrom.id',
'UserFrom.first_name',
'UserFrom.last_name',
),
),
'UserTo'=>array(
'fields'=>array(
'UserTo.id',
'UserTo.first_name',
'UserTo.last_name',
)
)
),'limit' => 12,
'order' => array('Friend.id' => 'desc'),
'recursive'=>0,
'fields'=>array('DISTINCT Friend.user2_id','Friend.*'),
'group'=>array('UserTo.id'),
);
This is my sql query on that page
SELECT COUNT(*) AS count FROM friends AS Friend LEFT JOIN users AS UserTo ON (Friend.user2_id = UserTo.id) LEFT JOIN users AS UserFrom ON (Friend.user1_id = UserFrom.id) WHERE UserFrom.nick_name = 'shyam' AND Friend.status = 'friend' GROUP BY UserTo.id
SELECT DISTINCT Friend.user2_id, Friend.*, UserTo.id, UserTo.first_name, UserTo.last_name, UserTo.nick_name, UserTo.name, UserTo.icon_medium, UserTo.icon_big, UserFrom.id, UserFrom.first_name, UserFrom.last_name, UserFrom.nick_name, UserFrom.name FROM friends AS Friend LEFT JOIN users AS UserTo ON (Friend.user2_id = UserTo.id) LEFT JOIN users AS UserFrom ON (Friend.user1_id = UserFrom.id) WHERE UserFrom.nick_name = 'shyam' AND Friend.status = 'friend' GROUP BY UserTo.id ORDER BY Friend.id desc LIMIT 12
1.First count query returning count properly
2.Second query if i put limit it not working properly. i.e there are 144 records in my table i need to display 12 per page.. only first page coming if i use group by and limit
You should have either DISTINCT or GROUP BY, not both - they're duplicating each other and probably causing problems with your query. Remove one of them and retry.
Note: If you want to manually request pages other than 1st with $this->paginate(), add 'page' parameter with page number to your $this->paginate array, like this:
$this->paginate = array_merge($this->paginate, array('page' => $pageNumber);
However, normally you wouldn't want to do this, as paginator does this automatically (passing page variable via URL parameter to your action) as long as you use pagination controls in your view (see http://book.cakephp.org/view/1233/Pagination-in-Views).

CakePHP 3 find() with beforeFind() callback SQL issue

I'm having a problem with data integrity when using find() in my controller in conjunction with beforeFind() in a behavior callback. The WHERE Submissions.site_id is not being added in the WHERE clause like it should be. I get different result sets depending on where the WHERE clause is set.
in my SubmissionsController:
public function index()
{
$query = $this->Submissions->find('all')
->where(['user_id' => $this->Auth->user('id')])
->contain(['Users', 'Categories']);
$this->set('submissions', $this->paginate($query));
}
In my beforeFind() Model callback (attached as a 'TenantBehavior' to
$query->where([$this->_table->alias().'.'.'site_id' => 3]);
The problem is that with the above, the SQL generated puts the "WHERE" clause as an AND on the JOIN condition like so, and NOT on the actual WHERE:
...
FROM
submissions Submissions
INNER JOIN users Users ON (
Users.id = (Submissions.user_id)
AND Users.site_id = 3
)
INNER JOIN categories Categories ON (
Categories.id = (Submissions.category_id)
AND Categories.site_id = 3
)
WHERE
user_id = 315
If I remove the beforeFind() ->where and instead place it on the controller ->where I get the expected SQL and result set like so:
...
FROM
submissions Submissions
INNER JOIN users Users ON (
Users.id = (Submissions.user_id)
AND Users.site_id = 3
)
INNER JOIN categories Categories ON (
Categories.id = (Submissions.category_id)
AND Categories.site_id = 3
)
WHERE
(
user_id = 315
AND Submissions.site_id = 3
)
Thoughts? Suggestions?
EDIT
As #ndm's suggestion, I began to update and provide much more context. In doing so I discovered (like an idiot) that I was missing the $this->addBehavior('Tenant'); on my 'SubmissionsTable' model. Adding this of course solved the issue.
$query = $this->Submissions->find('all', [
'conditions' => [
'user_id' => $this->Auth->user('id')
],
'contain' => [
'Users',
'Categories',
]
]);
Passing conditions as inline array will solve your issue and append beforeFind() where conditions properly.

Resources