I'm using CakePHP 1.3 and am having some trouble converting a SQL query to CakePHP. The concept behind the query is to retrieve all products associated with a specified account and belonging to a certain product category or the specified category's immediate child.
There are three tables involved here, products, product_categories and category_types. product_categories is a link table that joins products to category_types. The category_types table is a tree structure that can join to itself.
The SQL for the query should end up looking something like this if the category's id was 1 and the account's id was 1:
SELECT Product.id, Product.name, Product.account_id
FROM products AS Product
INNER JOIN product_categories AS pc ON (Product.id = pc.product_id)
INNER JOIN category_types AS ct ON (ct.id = pc.category_type_id)
WHERE (
((Product.account_id IS NULL) OR (Product.account_id = '1'))
AND ((ct.id = '1') OR (ct.parent_id = '1'))
)
Here is the code I'm using to try and do this, which is located in a function in my model:
$this->find('all', array(
'joins' => array(
array(
'table' => 'product_categories',
'alias' => 'pc',
'type' => 'INNER',
'conditions' => array(
'Product.id = pc.product_id'
)
),
// get category type
array(
'table' => 'category_types',
'alias' => 'ct',
'type' => 'INNER',
'conditions' => array(
'pc.category_type_id = ct.id'
)
)
)
, 'conditions'=> array(
'OR' => array(
'Product.account_id IS NULL'
, 'Product.account_id' => $account_id
)
, 'OR' => array(
'ct.id' => $categoryTypeId
, 'ct.parent_id' => $categoryTypeId
)
)
));
The problem is that the query is ignoring the account_id OR conditions resulting in SQL like this:
SELECT Product.id, Product.name, Product.account_id
FROM products AS Product
INNER JOIN product_categories AS pc ON (Product.id = pc.product_id)
INNER JOIN category_types AS ct ON (ct.id = pc.category_type_id)
WHERE ((ct.id = '1') OR (ct.parent_id = '1'))
How can I alter my php code to get the desired results?
Thanks to user2076809 for pointing me in the right direction. The solution is to wrap the two OR conditions each in their own array.
$this->find('all', array(
'joins' => array(
array(
'table' => 'product_categories',
'alias' => 'pc',
'type' => 'INNER',
'conditions' => array(
'Product.id = pc.product_id'
)
),
// get category type
array(
'table' => 'category_types',
'alias' => 'ct',
'type' => 'INNER',
'conditions' => array(
'pc.category_type_id = ct.id'
)
)
),
'conditions'=> array(
array(
'OR' => array(
'Product.account_id IS NULL'
, 'Product.account_id' => $account_id
)
),
array(
'OR' => array(
'ct.id' => $categoryTypeId
, 'ct.parent_id' => $categoryTypeId
)
)
)
));
I think, you should wrap the both of the array in only one OR condition like:-
$this->find('all', array(
'joins' => array(
array(
'table' => 'product_categories',
'alias' => 'pc',
'type' => 'INNER',
'conditions' => array(
'Product.id = pc.product_id'
)
),
// get category type
array(
'table' => 'category_types',
'alias' => 'ct',
'type' => 'INNER',
'conditions' => array(
'pc.category_type_id = ct.id'
)
)
),
'conditions'=> array(
'OR' => array(
array(
'Product.account_id IS NULL'
, 'Product.account_id' => $account_id
),
array(
'ct.id' => $categoryTypeId
, 'ct.parent_id' => $categoryTypeId
)
)
)
));
Related
i am using cakephp 1.3 version.
i try to display data with pagination.but my code getting wrong query.
plz help to correct them.
below is my code...
$this->paginate = array(
'fields' => array('u_data.lane_id AS LaneId'),
'joins' => array(array(
'table' => 'countries',
'alias' => 'origin_country',
'type' => 'INNER',
'conditions' => array('origin_country.id'=>'CustomerRoute.origin_country')
),
array(
'table' => 'countries',
'alias' => 'dest_country',
'type' => 'INNER',
'conditions' => array('dest_country.id'=>'CustomerRoute.dest_country')
),
array(
'table' => 'states',
'alias' => 'origin_state',
'type' => 'INNER',
'conditions' => array('origin_state.id'=>'CustomerRoute.origin_state')
),
array(
'table' => 'states',
'alias' => 'dest_state',
'type' => 'INNER',
'conditions' => array('dest_state.id'=>'CustomerRoute.dest_state')
),
array(
'table' => 'postalcodes',
'alias' => 'origin_city',
'type' => 'INNER',
'conditions' => array('origin_city.id'=>'CustomerRoute.origin_city')
),
array(
'table' => 'postalcodes',
'alias' => 'dest_city',
'type' => 'INNER',
'conditions' => array('dest_city.id'=>'CustomerRoute.dest_city')
),
array(
'table' => 'portfolios',
'alias' => 'p',
'type' => 'INNER',
'conditions' => array('p.id'=>'CustomerRoute.portfolio_id')
),
array(
'table' => 'supplier_uquotes_data',
'alias' => 'u_data',
'type' => 'LEFT',
'conditions' => array('CustomerRoute.id'=>'u_data.lane_id')
)),
'conditions' => array('CustomerRoute.portfolio_id' =>'"'.$_SESSION["portfolioid"].'"', 'u_data.supplier_id IN' => '"'.implode(',',$_SESSION['supplierid']).'"'),
'group' => array('CustomerRoute.id'),
'limit' => 10);
$content = $this->paginate('CustomerRoute');
print_r($content);
exit;
the above code getting below query.....
SELECT `u_data`.`lane_id` AS `LaneId` FROM `customers` AS `Customer` INNER JOIN countries AS `origin_country` ON (`origin_country`.`id` = 'CustomerRoute.origin_country') INNER JOIN countries AS `dest_country` ON (`dest_country`.`id` = 'CustomerRoute.dest_country') INNER JOIN states AS `origin_state` ON (`origin_state`.`id` = 'CustomerRoute.origin_state') INNER JOIN states AS `dest_state` ON (`dest_state`.`id` = 'CustomerRoute.dest_state') INNER JOIN postalcodes AS `origin_city` ON (`origin_city`.`id` = 'CustomerRoute.origin_city') INNER JOIN postalcodes AS `dest_city` ON (`dest_city`.`id` = 'CustomerRoute.dest_city') INNER JOIN portfolios AS `p` ON (`p`.`id` = 'CustomerRoute.portfolio_id') LEFT JOIN supplier_uquotes_data AS `u_data` ON (`CustomerRoute`.`id` = 'u_data.lane_id') LEFT JOIN `jobtitles` AS `Jobtitle` ON (`Customer`.`job_title` = `Jobtitle`.`id`) LEFT JOIN `countries` AS `Country` ON (`Customer`.`country` = `Country`.`id`) LEFT JOIN `states` AS `State` ON (`Customer`.`state` = `State`.`id`) LEFT JOIN `market_areas` AS `MarketArea` ON (`Customer`.`market_area` = `MarketArea`.`id`) WHERE `CustomerRoute`.`portfolio_id` = '\"69\"' AND `u_data`.`supplier_id` IN '\"37\"' GROUP BY `CustomerRoute`.`id` LIMIT 10
but i need the result like below mentioned query....the below mentioned query is my correct query.......
plz suggest whats the problem in my code..
SELECT u_data.lane_id AS LaneId,
origin_city.pcode AS origin_pcode,
dest_city.pcode AS dest_pcode
FROM customer_routes AS CustomerRoute
INNER JOIN countries AS origin_country ON ( origin_country.id = CustomerRoute.origin_country )
INNER JOIN countries AS dest_country ON ( dest_country.id = CustomerRoute.dest_country )
INNER JOIN states AS origin_state ON ( origin_state.id = CustomerRoute.origin_state )
INNER JOIN states AS dest_state ON ( dest_state.id = CustomerRoute.dest_state )
INNER JOIN postalcodes AS origin_city ON ( origin_city.id = CustomerRoute.origin_city )
INNER JOIN postalcodes AS dest_city ON ( dest_city.id = CustomerRoute.dest_city )
INNER JOIN portfolios AS p ON ( p.id = CustomerRoute.portfolio_id )
LEFT JOIN supplier_uquotes_data AS u_data ON ( CustomerRoute.id = u_data.lane_id AND u_data.mode = '".$mode."')
WHERE CustomerRoute.portfolio_id = '".$_SESSION["portfolioid"]."' AND u_data.supplier_id
IN (".implode(',',$_SESSION['supplierid']).") GROUP BY CustomerRoute.id
You should define remaining fields also and pass the Model name like this : $this->Prg->commonProcess('CustomerRoute');
This is your code
$this->Prg->commonProcess('CustomerRoute');
$this->paginate = array(
'fields' => array('u_data.lane_id AS LaneId','origin_city.pcode AS origin_pcode','dest_city.pcode AS dest_pcode'),
'joins' => array(array(
'table' => 'countries',
'alias' => 'origin_country',
'type' => 'INNER',
'conditions' => array('origin_country.id'=>'CustomerRoute.origin_country')
),
array(
'table' => 'countries',
'alias' => 'dest_country',
'type' => 'INNER',
'conditions' => array('dest_country.id'=>'CustomerRoute.dest_country')
),
array(
'table' => 'states',
'alias' => 'origin_state',
'type' => 'INNER',
'conditions' => array('origin_state.id'=>'CustomerRoute.origin_state')
),
array(
'table' => 'states',
'alias' => 'dest_state',
'type' => 'INNER',
'conditions' => array('dest_state.id'=>'CustomerRoute.dest_state')
),
array(
'table' => 'postalcodes',
'alias' => 'origin_city',
'type' => 'INNER',
'conditions' => array('origin_city.id'=>'CustomerRoute.origin_city')
),
array(
'table' => 'postalcodes',
'alias' => 'dest_city',
'type' => 'INNER',
'conditions' => array('dest_city.id'=>'CustomerRoute.dest_city')
),
array(
'table' => 'portfolios',
'alias' => 'p',
'type' => 'INNER',
'conditions' => array('p.id'=>'CustomerRoute.portfolio_id')
),
array(
'table' => 'supplier_uquotes_data',
'alias' => 'u_data',
'type' => 'LEFT',
'conditions' => array('CustomerRoute.id'=>'u_data.lane_id')
)),
'conditions' => array('CustomerRoute.portfolio_id' =>'"'.$_SESSION["portfolioid"].'"', 'u_data.supplier_id IN' => '"'.implode(',',$_SESSION['supplierid']).'"'),
'group' => array('CustomerRoute.id'),
'limit' => 10);
$content = $this->paginate('CustomerRoute');
I have 2 tables :
ORDERS
------
id
type
LOGS
----
id
order_id
time
I want to query with cakephp so I have :
array(
'Order' => array(
'id' => '38',
'type' => 'online',
),
'Log' => array(
'time' => '2014-09-24 21:17:14'
)
)
The problem is that I want only the last order, not all orders with all logs.
I did something like this :
$ordersList = $this->Order->find('all', array(
'fields' => array(
'Order.*',
'Log.time'
),
'joins' => array(
array(
'table' => 'logs',
'alias' => 'Log',
'type' => 'right',
'conditions' => array(
'Log.order_id = Order.id'
),
)
),
);
To have only the last one, you can order the result by log.time and then take only the first record (with the param 'first' or just by fetching the first record of the recordset).
For example :
$order = $this->Order->find('first', array(
'order' => array('Log.time' => 'desc')
));
in your case :
$ordersList = $this->Order->find('first', array(
'fields' => array(
'Order.*',
'Log.time'
),
'joins' => array(
array(
'table' => 'logs',
'alias' => 'Log',
'type' => 'right',
'conditions' => array(
'Log.order_id = Order.id'
),
'order' => array('Log.time' => 'desc')
)
),
);
As information, if you want to have a simple method to join the models, take a look at the containable behavior. With this behavior, the link are setted in the model then you only have to declare which associated model you want to retrieve with your current model.
=> http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html
This is driving me crazy. This is not throwing any errors but it is also not performing the joins. I'm hoping that this is one where I've spent too long looking at it and the answer is obvious to someone else...
$lines = $this->RevenueLine->find('all', array(
'conditions' => array(
'RevenueLine.is_triggered' => 1,
'RevenueLine.date_triggered >=' => $sqldate1,
'RevenueLine.date_triggered <=' => $sqldate2,
),
'joins' => array(
array(
'table' => 'projects',
'alias' => 'Project',
'type' => 'INNER',
'conditions' => array(
'RevenueLine.project_id = Project.id'
)
),
array(
'table' => 'clients',
'alias' => 'Client',
'type' => 'INNER',
'conditions' => array(
'Project.client_id = Client.id'
)
),
array(
'table' => 'classifications',
'alias' => 'Classification',
'type' => 'INNER',
'conditions' => array(
'Project.classification_id = Classification.id'
)
)
),
'order' => array(
'Client.client_number ASC',
'Project.pn_counter ASC'
)
)
);
You need to select the fields from the joined tables:
'fields' => array(
'JoinTable1.*',
'JoinTable2.*',
'JoinTable3.*',
'JoinTable4.*'
)
as a parameter of your find.
I have the sql code and i want to do in cake php but i dont know how and i don't want to do it like that $obj->query($sql);
This is my query:
"SELECT dbfiles.amount, MONTHNAME(cases.enquiry_date) as Month FROM cases
INNER JOIN services ON cases.id = services.case_id
INNER JOIN dbfiles ON services.id = dbfiles.service_id
WHERE cases.status = 2 AND YEAR(cases.enquiry_date) = '2012' AND dbfiles.type = 'INV'
AND cases.currency = 'EUR' GROUP BY dbfiles.invoice_num ORDER BY Month DESC; "
Assuming $obj is of class Case
$obj->find('all', array(
'fields' => array('dbfiles.amount', 'MONTHNAME(cases.enquiry_date) as MONTH',
'joins' => array(
array(
'table' => 'services',
'type' => 'INNER',
'conditions' => array('cases.id = services.case_id'),
'foreignKey' => false
),
array(
'table' => 'dbfiles',
'type' => 'INNER',
'conditions' => array('services.id = dbfiles.service_id'),
'foreignKey' => false
)
),
'conditions' => array('cases.status' => 2, ... the rest of your WHERE clauses)
)
I have these tables in my database: 'products' belongs to a 'company', and 'company' has many 'company_addresses'. And I am building a search action in my controller which will return the products based on user's inputted company address. How can I get something like the followin?
SELECT *
FROM products
LEFT JOIN companies ON products.company_id = companies.id
LEFT JOIN company_addresses ON companies.id = company_addresses.company_id
WHERE company_addresses.address1 LIKE '%'.$this->data['Product']['keyword'].'%'
OR company_addresses.address2 LIKE '%'.$this->data['Product']['keyword'].'%'
This doesn't seem to work:
$results = $this->Product->find('all', array(
'conditions' => array(
'OR' => array(
array('CompanyAddress.address1 LIKE' => '%'.$this->data['Product']['keyword'].'%'),
array('CompanyAddress.address2 LIKE' => '%'.$this->data['Product']['keyword'].'%')
)
),
'order' => array(
'Product.id' => 'ASC'
),
'contain' => array(
'Company' => array(
'CompanyAddress' => array(
'State',
'City'
)
)
)
));
Nor this:
$results = $this->Product->find('all', array(
'order' => array(
'Product.id' => 'ASC'
),
'contain' => array(
'Company' => array(
'CompanyAddress' => array(
'conditions' => array(
'OR' => array(
array('CompanyAddress.address1 LIKE' => '%'.$this->data['Product']['keyword'].'%'),
array('CompanyAddress.address2 LIKE' => '%'.$this->data['Product']['keyword'].'%')
)
),
'State',
'City'
)
)
)
));
I think the best for you is to use the join of CakePHP.
Something like this:
$this->Product->find('all', array(
'joins' => array(
array(
'table' => 'companies',
'alias' => 'Company',
'type' => 'left',
'conditions' => array(
'Company.id = Product.company_id'
),
),
array(
'table' => 'companies_addresses',
'alias' => 'CompaniesAddress',
'type' => 'left',
'conditions' => array(
'CompaniesAdress.company_id = Company.id',
'OR' => array(
'CompanyAdress.address1 LIKE' => '%' . $this->data['Product']['keyword'] . '%',
'CompanyAdress.address2 LIKE' => '%' . $this->data['Product']['keyword'] . '%',
),
),
),
),
));