mysql cakephp query to join multiple tables to get sum of columns - mysql

I want to run a query which calculate sum for a particular columns. for this i need to join two table that will map the matched records and give the results. Below i paste my query please correct me where i am wrong in my query..
it may be mapping twice so it shows wrong result.
$this->Inventory->find('all',array('joins'=>array(
array('table' => 'items',
'alias' => 'item',
'type' => 'left',
'conditions' => array(
'Inventory.item_id = item.id')
),
array('table' => 'material_owners',
'alias' => 'owner',
'type' => 'left',
'conditions' => array(
'Inventory.material_owner_id = owner.id')
),
array('table' => 'projects',
'alias' => 'project',
'type' => 'left',
'conditions' => array(
'Inventory.project_id = project.id')
),
array('table' => 'material_payments',
'alias' => 'mp',
'type' => 'left',
'conditions' => array(
'Inventory.material_owner_id=mp.material_owner_id'),
),
),
'conditions'=>array('Inventory.project_id'=>$project_id),
'fields' =>array('sum(Inventory.total_amount) as total_amount','sum(mp.paid_amount) as paid_amount','item.name','item.id','owner.id','owner.first_name','owner.last_name','project.id','project.name'),
'group'=> array('item.name','item.id','owner.id','owner.first_name','owner.last_name','project.name','project.id')
)
);
This is the result query generated by cakephp.
SELECT sum("Inventory"."total_amount") as total_amount, sum("mp"."paid_amount") as paid_amount, "item"."name" AS "item__name", "item"."id" AS "item__id", "owner"."id" AS "owner__id", "owner"."first_name" AS "owner__first_name", "owner"."last_name" AS "owner__last_name", "project"."id" AS "project__id", "project"."name" AS "project__name" FROM "inventories" AS "Inventory" left JOIN "items" AS "item" ON ("Inventory"."item_id" = "item"."id") left JOIN "material_owners" AS "owner" ON ("Inventory"."material_owner_id" = "owner"."id") left JOIN "projects" AS "project" ON ("Inventory"."project_id" = "project"."id") left JOIN "material_payments" AS "mp" ON ("Inventory"."material_owner_id" = "mp"."material_owner_id") LEFT JOIN "items" AS "Item" ON ("Inventory"."item_id" = "Item"."id") LEFT JOIN "units" AS "Unit" ON ("Inventory"."unit_id" = "Unit"."id") LEFT JOIN "projects" AS "Project" ON ("Inventory"."project_id" = "Project"."id") LEFT JOIN "material_owners" AS "MaterialOwner" ON ("Inventory"."project_id" = "MaterialOwner"."id") WHERE "Inventory"."project_id" = '4' GROUP BY "item"."name", "item"."id", "owner"."id", "owner"."first_name", "owner"."last_name", "project"."name", "project"."id"

I'm not sure what you're trying to achive here. Does your cake query work at all?
What errors do you get or how do you want the output to be?
For a sollution, I'd take a look at virtualfields.
http://book.cakephp.org/2.0/en/models/virtual-fields.html
They can be added to each model prior to your query with the joins.

Related

cakephp 3 is there something like autoFields for joins to select all fields of a join?

I have a query which selects some computed field. In order to get all other fields of the main model and associated models, I can use autoFields(true). I also need to join some tables on certain conditions.
So far the query looks like this:
$functionsBuilder = $this->MainModel->find()->func();
$documents = $this->MainModel->find('all', [
'conditions' => […],
'contain' => […],
'join' => [
'table' => 'tablename',
'alias' => 'AliasName',
'type' => 'LEFT',
'foreignKey' => false,
'conditions' => […],
],
])
->autoFields(true)
->select([
'computed_field_name' => $functionsBuilder->group_concat(['OtherModel.name' => 'literal']),
]);
The fields of the joined table are not included in the result. Usually when you want to include all fields of an associated table, you do a select call with the table class as a parameter, e.g. ->select($this->MainModel->AssociatedModel).
The table used in the join might not be any (deeper) association with the main model. Even if it also is an association, the alias defined in the join might be a different one.
What I came up with so far is to load the table class for the table referenced in the join, then change its alias, and then use that in a select. Yielding:
$this->loadModel('AssociatedModel');
$this->AssociatedModel->alias('AliasName');
$functionsBuilder = $this->MainModel->find()->func();
$documents = $this->MainModel->find('all', [
'conditions' => […],
'contain' => […],
'join' => [
'table' => 'tablename',
'alias' => 'AliasName',
'type' => 'LEFT',
'foreignKey' => false,
'conditions' => […],
],
])
->autoFields(true)
->select($this->AssociatedModel)
->select([
'computed_field_name' => $functionsBuilder->group_concat(['OtherModel.name' => 'literal']),
]);
The result then contains all fields from the join with the correct AliasName.
I was just wondering whether there is a better way to do this. If I have a lot of joins I need to load a lot of table classes and alias them correctly. It is doable, but is there maybe a more elegant solution?

how to use straight_join in cakephp?

I write codes as seen below when joining two and more tables in cakephp:
$options['joins'] = array(
array(
'table' => 'table2',
'alias' => 'tbl2',
'type' => 'inner',
'conditions' => array(
'tbl1.customer_id = tbl2.customer_id' ,
"tbl2.del_flag = 'N'" ,
)
)
);
That continues with more tables included in the join.
How do I write the code if I wanna use 'straight_join' instead of 'inner join'?
Changing the 'type' above into 'straight' doesn't do anything.
Is it possible in cakephp?
Thank you for the help.

how use multiple conditions to get data from 2 HABTM tables in Cakephp 2.x

I'm trying to get only those products which have specific brand and category.
in Product model i used this query
$products = $this->find('all', array(
'joins' => array(
array('table' => 'brands_products',
'alias' => 'BrandsProducts',
'type' => 'INNER',
'conditions' => array(
'BrandsProducts.brand_id' => $brandId,
'BrandsProducts.product_id = Product.id'
)
)
),
'group' => 'Product.id'
));
By using above query i can get only those products which has specific brand id.
Exactly i don't know how to use multiple conditions to get data from 2 HABTM tables.
in my example product HABTM relation with Category and Brands.
$products = $this->find('all', array(
'joins' => array(
array('table' => 'brands_products',
'alias' => 'BrandsProducts',
'type' => 'INNER',
'conditions' => array(
'BrandsProducts.brand_id' => $brandId,
'BrandsProducts.product_id = Product.id'
)
),
array('table' => 'categories_products',
'alias' => 'CategoriesProducts',
'type' => 'INNER',
'conditions' => array(
'CategoriesProducts.category_id' => $categoriesId,
'CategoriesProducts.product_id = Product.id'
)
)
),
'group' => 'Product.id'
));
By using this query i got error in db. SO i'm looking how to write proper query to get only those products where brand and category match.

Fetch random row from group by query (mysql) in Cakephp

I have a table 'activities' and the columns are grouped by three of its attributes. Now I want to fetch Activity ID that is randomly selected from all the set of groups. The query is as follows:
$act = $this->find('all', array('conditions' => $cond,
'limit' => $limit,
'fields' => array('count(*) as count, Activity.id as id'),
'page' => $page,
'group' => $group,
));
These are the things I tried and none of them worked.
1. Create a self join which should have random records
$this->bindModel(array(
'belongsTo' => array(
'Random' => array(
'className' => 'Activity',
'order' => 'rand()',
'foreignKey' => 'id',
'fields' => 'Random.id'
)
)
));
This failed because the rand() order is appended in the end which simply randomizes all fetched activities.
Added a join option
'joins' => array(
array(
'table' => 'activities',
'alias' => 'Random',
'type' => 'LEFT',
'conditions' => array(
'Activity.id = Random.id'
),
'fields' => 'Random.id',
'order' => 'rand()'
)
This also didn't work as order value is not parsed by Cake
Tried to add condition
'Activity.id >= floor(rand() * (max(Activity.id) - min(Activity.id)) - min(Activity.id))'
Gave a mysql error
Please help me out
Assuming that your query is working:
$act = $this->find('all', array('conditions' => $cond,
'limit' => $limit,
'fields' => array('count(*) as count, Activity.id as id'),
'page' => $page,
'group' => $group,
'order' => 'rand()',
'limit' => '1' // if you only want one random activity ID
));
Ok finally figured it out. We need to hack the way cake php works. In place of table name, I wrote a query to fetch randomly ordered records. Also notice the join type is 'right'. Left join wont produce randomly sorted list
$activities = $this->find('all', array('conditions' => $cond,
'page' => $page,
'limit' => $limit,
'fields' => array('count(*) as count, Random.id as id, max(Activity.modified) as Modified'),
'group' => $group,
'order' => 'Modified desc',
'joins' => array(
array(
'table' => '(select * from activities order by rand())',
'alias' => 'Random',
'type' => 'RIGHT',
'conditions' => array(
'Activity.id = Random.id'
),
)
)
));

ISNULL not working in order in CAKEPHP

I am using LEFT join and as a result getting null values for is_read column in Messages table. I want to keep the nulls at bottom when ordering. I'm using this in paginator. Following is the my code for doing the same:
$this->Paginator->settings = array(
'fields' => array('User.*'),
'joins' => array(
array('table' => 'messages',
'alias' => 'Message',
'type' => 'LEFT',
'conditions' => array(
'User.id = Message.user_from_id'
)
),
),
'limit' => 20,
'group' => array('User.id'),
'order' => array('ISNULL(Message.is_read)' => 'asc','Message.is_read' => 'asc', 'Message.created' => 'asc'),
);
The query Cakephp generates for this is as follows:
SELECT `User`.*, (CONCAT(`User`.`first_name`, ' ', `User`.`last_name`)) AS `User__full_name` FROM `srs_development`.`users` AS `User` LEFT JOIN `srs_development`.`messages` AS `Message` ON (`User`.`id` = `Message`.`user_from_id`) WHERE 1 = 1 GROUP BY `User`.`id` ORDER BY `Message`.`is_read` asc, `Message`.`created` asc LIMIT 20
ISNULL function is getting omitted in the final query.
Also please suggest a way to accomplish this without using custom pagination() if possible.
Aggregate functions didn't work in the order clause when using Pagination component. I tried declaring a virtual field in Message model as:
public $virtualFields = array(
'sortme' => "ISNULL(Message.is_read)",
);
So finally, declaring it as virtual field in the Message model did the job.
Thank you everyone.