Cakephp left outer join from query builder is not giving correct sql format - cakephp

I want to make sql query like this. Which is working fine. but if I am trying with using cakephp query builder I could not find any path. I am also posting what I have done in cakephp query builder. Please check...
$query = '
select
schemes.name,
most_recent_export_product.itc_hs_code,
sum(epinvoicevalueusd) from oc
join (
select
oc_id, itc_hs_code
from
e_products
where
itc_hs_code is not null
group
by oc_id
order
by oc_id, created desc
)
as most_recent_export_product
on oc.id = most_recent_export_product.oc_id
join
schemes on schemes.id = oc.scheme_id
where
application_status_id = 8 AND
certificate_issue_date >= 2022-09-28
group by
scheme_id, most_recent_export_product.itc_hs_code
order by
scheme_id, most_recent_export_product.itc_hs_code';
$connection = ConnectionManager::get('default');
$ocData = $connection->execute($query)->fetchAll('assoc');
In cake php I am using query builder to do the same like
$exdata = $this->EProducts
->find()
->select(['oc_id', 'itc_hs_code'])
->group('oc_id')
->order(['created' => 'DESC']);
$ocData = $this->oc
->find()
->select([
'scheme_name' => 'Schemes.name',
'itc_hs_code' => 'ExportProducts.itc_hs_code',
'epinvoicevalueusd' => 'sum(oc.epinvoicevalueusd)'
])
->join([
'exp' => [
'type' => 'LEFT',
'conditions' => 'exp.oc_id = oc.id',
'group' => 'exp.oc_id'
]
])
->contain('Schemes')
->where([
'application_status_id' => 8,
'certificate_issue_date >=' => '2022-09-28'
])
->group(['scheme_id'])
->order(['scheme_id']);
I dont how to make it with using query builder in cakephp. Please help anyone. :)

Related

How to create a join that uses a subquery?

How do you convert the following SQL Query to a CakePhp Find Query?
SELECT
a.id, a.rev, a.contents
FROM
YourTable a
INNER JOIN (
SELECT
id, MAX(rev) rev
FROM
YourTable
GROUP BY
id
) b ON a.id = b.id AND a.rev = b.rev
I have tried the code below:
return $model->find('all', [
'fields' => $fields,
'joins' => [
[
'table' => $model->useTable,
'fields' => ['id','MAX(rev) as rev'],
'alias' => 'max_rev_table',
'type' => 'INNER',
'group' => ['id'],
'conditions' => [
$model->name.'.id= max_rev_table.id',
$model->name.'.rev = max_rev_table.rev'
]
]
],
'conditions' => [
$model->name.'.emp_id' => $empId
]
]);
But it seems that in the generated SQL, the fields under the joins is not included. So I don't get the max(rev) which I need to get only the rows with max(rev).
I have tried rearranging the items inside joins but still produces same auto-generated SQL.
Can you please help me?
There are no fields or group options for joins, the only options are table, alias, type, and conditions.
If you want to join a subquery, then you need to explicitly generate one:
$ds = $model->getDataSource();
$subquery = $ds->buildStatement(
array(
'fields' => array('MaxRev.id', 'MAX(rev) as rev'),
'table' => $ds->fullTableName($model),
'alias' => 'MaxRev',
'group' => array('MaxRev.id')
),
$model
);
and pass it to the joins table option:
'table' => "($subquery)"
See also
Cookbook > Models > Associations: Linking Models Together > Joining tables
Cookbook > Models > Retrieving Your Data > Sub-queries

How to create a `IN` clause in CakePHP query?

How do you make it where in clause in new CakePHP? I'm trying:
$restaurants->find()->where(['id' => $r_ids])->all();
Where $r_ids is array of ids I need in my query, but this doesn't work as expected.
With CakePHP 3.x it's now necessary to either indicate the data type, which for an array of values need to have [] appended to the type:
$query = $articles
->find()
->where(['id' => $ids], ['id' => 'integer[]']);
or to explicitly make use of the IN keyword:
$query = $articles
->find()
->where(['id IN' => $ids]);
See also
Cookbook > Database Access & ORM > Query Builder > Automatically Creating IN Clauses
Try this one for CakePHP 3.x
$query = $restaurants
->find()
->where(['id IN' => $r_ids]);
$query->all();
You can also use the short syntax:
$result = $restaurants->find('all',
['conditions' => ['id IN' =>$r_ids]]
)->all();
In Cakephp 3.x, If you have multiple where conditions then your query will be as below:
return $this
->find()
->contain([
'XYZ.Articles',
'YYY.Managers',
'ZZZ.Readers',
])
->where([
'ID' => $ids,
'READER.STATUS' => $xyz,
],[
'ID' => 'integer[]'
])
;

Rewriting a complex query using CakePHP

I quickly wrote a quite complex (in terms of structure) SQL query manually in CakePHP initially, but now I am trying to rewrite it to run withing the CakePHP find method.
$sql = "SELECT
`users`.`username`,
(SELECT ROUND(SUM(`divisions`.`amount`), 2)
FROM `purchases`
INNER JOIN `divisions`
ON `purchases`.`id` = `divisions`.`purchase_id`
WHERE `purchases`.`user_id` = `users`.`id`
AND `divisions`.`user_id` = `users`.`id`
AND `purchases`.`group_id` = " . $group_id . "
) AS `owed_to`
FROM `users`
INNER JOIN `memberships` ON `users`.`id` = `memberships`.`user_id`
INNER JOIN `groups` ON `memberships`.`group_id` = `groups`.`id`
WHERE `memberships`.`group_id` = " . $group_id . " AND
`users`.`id` != " . $user_id . ";";
Because SQL allows you to apply the WHERE filter across the whole query it becomes very simple. In Cake you cannot just go:
$results = $this->User->find('all', array(
'conditions' => array(
'Membership.group_id =' => $id
),...
I have tried setting joins:
$joins = array(
array('table'=>'memberships',
'alias' => 'Membership',
'type'=>'inner',
'conditions'=> array(
'Membership.user_id = User.id', 'Membership.group_id' => $id)
),
Which works OK for a single layer of recursion, but then models related to membership (such as group) are not subject to the filter.
I can only imagine I am doing something completely wrong.
Basically I am confused, any help would be appreciated.
Futher information
User => HasMany => Purchase, Membership, Division
Membership => BelongsTo => Group, User
Group => HasMany => Membership
Purchase => HasMany => Division
Purchase => BelongsTo => User, Group
Division => BelongsTo => Purchase, User
You can write it like this:
$results = $this->User->find('all', array(
'conditions' => array(
'Membership.group_id' => $id
),...
If User has many Membership and Membership belongs to User, and $this->User->recursive = 1, then it should work

CakePHP complex find condition using 'and' , 'or' clause

My find query is this;
//here $u_id is id of the users table retrieved using session variable
$this->User->Request->find("all",array("conditions" =>array("request.status" => "friends" ,
"OR" => array("request.friend_id" => "$u_id","request.user_id" => "$u_id"))));
the equivalent SQL query is:-
SELECT `Request`.`id`, `Request`.`user_id`, `Request`.`friend_id`, `Request`.`status`, `User`.`id`, `User`.`name`, `User`.`email`, `User`.`password`,
FROM
`myblog`.`requests` AS `Request` JOIN `myblog`.`users` AS `User`
ON
(`Request`.`friend_id` = `User`.`id`)
WHERE
((`request`.`friend_id` = 3) OR (`request`.`user_id` = 3)) AND `request`.`status` = 'friends'
Whereas I want the following line after 'ON' in the above SQL query to get the desired result:
`Request`.`user_id` = `User`.`id` OR `Request`.`friend_id` = `User`.`id`
What changes should I make in the find() method,
or should I change my model?
My tables are:
users(id, name, password, email)
requests(id, user_id(id of the users table),
friend_id(id of the users table), status)
You can specify the condition in your model.
public $belongsTo = array('User' => array('className' => 'Request',
'foreignKey' => 'user_id',
'conditions' => array('OR' => array('Request.user_id' => 'User.id', 'Request.friend_id' => 'User.id')
))
);

Problem with CakePHP model joins, missing fields from joined table.

Im probably missing something painfully obvious here. Im trying to join multiple tables together in Cake, but Im only getting the fields from the first table. Consider the following code..
$joins = array();
$joins[] = array(
'table' => 'products',
'alias' => 'Product',
'type' => 'LEFT',
'conditions' => array(
'Device.id = Product.device_id'
)
);
$joins[] = array(
'table' => 'pricepoints',
'alias' => 'Pricepoints',
'type' => 'LEFT',
'conditions' => array(
'Pricepoints.product_id = Product.id'
)
);
$all_products = $this->Device->find('all', array("joins" => $joins);
And this returns the following SQL
SELECT `Device`.`id`, `Device`.`manufacturer_id`, `Device`.`name`, `Device`.`type_id`, `Manufacturer`.`id`, `Manufacturer`.`name` FROM `devices` AS `Device` LEFT JOIN products AS `Product` ON (`Device`.`id` = `Product`.`device_id`) LEFT JOIN pricepoints AS `Pricepoints` ON (`Pricepoints`.`product_id` = `Product`.`id`) LEFT JOIN `manufacturers` AS `Manufacturer` ON (`Device`.`manufacturer_id` = `Manufacturer`.`id`)
ie. it only returns the fields from the parent Model ie. Device. How do I get it to select ALL fields from the join? Im presuming its to do with the way I have my Model relationships set up, but I think I have these set up correctly.
Anyone any advice?
you can specify the fields in the find query:
$all_products = $this->Device->find('all', array("fields" => array('Device.*','Product.*','Pricepoints.*')
"joins" => $joins
);
Hope this helps
Do your join by calling bindModel() instead of manually specifying the join. See the cookbook for the details on creating and destroying associations on the fly

Resources