CakePHP: Using bindModel to trace backwards by 3 models? - mysql

I have the following models:
-> = 1:many, <-> = many:many
User<->Product->MessageList<->Message
My goal with this query is to find all messages corresponding to the logged in . I also filter out messages that have been read (I find the messages ids in the prior find and store them in $messageIdsReadByPerson).
Here is my attempt:
// only grab messages belonging to this user
$this->Message->unbindModel(array('hasAndBelongsToMany'=>array('MessageListsMessage')));
$this->Message->bindModel( array(
'hasOne' => array(
'MessageListsMessage' => array(
'className' => 'MessageListsMessage',
'foreignKey' => false,
'type' => 'INNER',
'conditions' => array(
'MessageListsMessage.message_id = Message.id'
)
),
'MessageList' => array(
'className' => 'MessageList',
'foreignKey' => false,
'type' => 'INNER',
'conditions' => array(
'MessageList.id = MessageListsMessage.message_list_id'
)
),
'Product' => array(
'className' => 'Product',
'foreignKey' => false,
'type' => 'INNER',
'conditions' => array(
'Product.id = MessageList.product_id'
)
),
'ProductsUser' => array(
'className' => 'ProductsUser',
'foreignKey' => false,
'type' => 'INNER',
'conditions' => array(
'ProductsUser.product_id = Product.id'
)
),
'User' => array(
'className' => 'User',
'foreignKey' => false,
'conditions' => array(
'User.id = ProductsUser.user_id',
'User.id' => $this->Auth->user('id')
)
)
)
));
$messages = $this->Message->find('all', array(
'recursive' => -1
));
$this->set('messages', $messages);
But that only gives this query:
SELECT `Message`.`id`, `Message`.`text`, `Message`.`url` FROM `messages` AS `Message` WHERE 1 = 1
How can this be done properly using bindModel (or some easier method that I'm unaware of, although joins is apparently not the way to go).
EDIT:
this doesnt work either:
function index() {
// only grab messages belonging to this user
$messages = $this->Message->find('all', array(
'contain' => array(
'MessageList' => array(
'Product' => array(
'User.id = ' . $this->Auth->user('id')
)
)
)
));
$this->set('messages', $messages);
}

I think the same answer applies as I just gave here:
How to get only products with images in CakePhp
containable or recursive higher

Related

Cakephp Multiple Table search in container behaviour

hello I want to get results from multiple tables using the keyword which user has typed. The problem I am having is I don't know how can I do search on that table which is getting through contain
Here's my query
public function getGigPostBasedOnSearch($keyword){
$this->Behaviors->attach('Containable');
return $this->find('all', array(
'contain' => array(
'User','UserInfo', 'GigPostAndCategory.Category','Location','GigPostAndCalender'
),
'conditions' => array(
'AND' => array(
'GigPost.active' => 1,
'OR' => array(
array('GigPost.title LIKE' => '%'.$keyword.'%'),
array('GigPost.description LIKE' => '%'.$keyword.'%'),
array('Location.location_string LIKE' => '%'.$keyword.'%'),
)
)
//'OrderGigPost.request' => 0
),
'order' => 'GigPost.gig_post_id DESC',
'recursive' => 0
));
}
This query works fine. I want to search keyword in category table also.I want to do something like this
array('Category.cat_name LIKE' => '%'.$keyword.'%')
Please help me how can I search in Category table as well
Thank you
Model GigPost.php
<?php
class GigPost extends AppModel
{
public $useTable = 'gig_post';
public $primaryKey = 'gig_post_id';
public $hasAndBelongsToMany = array(
'Category' =>
array(
'className' => 'Category',
'joinTable' => 'gigpost_category',
'foreignKey' => 'gig_post_id',
'associationForeignKey' => 'cat_id',
'unique' => true,
)
);
public $hasOne = array(
'Location' => array(
'className' => 'Location',
'foreignKey' => 'gig_post_id',
'conditions' => array('GigPost.gig_post_id = Location.gig_post_id')
));
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'fields' => array('User.user_id','User.email','User.active')
),
'UserInfo' => array(
'className' => 'UserInfo',
'foreignKey' => 'user_id',
)
);
public function getGigPostBasedOnSearch($keyword){
$this->Behaviors->attach('Containable');
return $this->find('all', array(
'contain' => array(
'User','UserInfo', 'GigPostAndCategory.Category'=>array(
'conditions'=>array(
'OR' => array(
array('Category.cat_name' => '%'.$keyword.'%'),
),
)
),'Location','GigPostAndCalender'
),
'conditions' => array(
'AND' => array(
'GigPost.active' => 1,
'OR' => array(
array('GigPost.title LIKE' => '%'.$keyword.'%'),
array('GigPost.description LIKE' => '%'.$keyword.'%'),
array('Location.location_string LIKE' => '%'.$keyword.'%'),
// array('Category.cat_name LIKE' => '%'.$keyword.'%'),
)
)
//'OrderGigPost.request' => 0
),
'order' => 'GigPost.gig_post_id DESC',
'recursive' => 0
));
}
}
Your issue is that the relationship between GigPost and Category is HABTM, so when you use contain Cake has to do a separate query to retrieve the related categories as there is not a one-on-one association. To get round this you need to manually perform a join rather than contain the categories:-
$this->find('all', array(
'contain' => array(
'User', 'UserInfo', 'GigPostAndCategory.Category', 'Location', 'GigPostAndCalender'
),
'joins' => array(
array(
'table' => 'gigpost_category',
'alias' => 'GigPostCategory',
'type' => 'INNER',
'conditions' => 'GigPost.id = GigPostCategory.gig_post_id'
),
array(
'table' => 'categories',
'alias' => 'Category',
'type' => 'INNER',
'conditions' => 'Category.id = GigPostCategory.cat_id'
)
),
'conditions' => array(
'AND' => array(
'GigPost.active' => 1,
'OR' => array(
array('GigPost.title LIKE' => '%'.$keyword.'%'),
array('GigPost.description LIKE' => '%'.$keyword.'%'),
array('Location.location_string LIKE' => '%'.$keyword.'%'),
array('Category.cat_name LIKE' => '%'.$keyword.'%'),
)
)
),
'order' => 'GigPost.gig_post_id DESC',
'group' => 'GigPost.id',
'recursive' => 0
));
This ensures that the categories will be included with the data returned by the primary query.
You will probably need to modify the joins array a bit as you don't appear to be using Cake naming conventions for your table so I am not sure what you have named things. However, this should put you on the right track.

Finding all with a JOIN query giving a single result instead of multiple

I have the following CakePHP code; I want it to return all rows (two in this case)
public function orderdetails($orderId)
{
$Productsaleslist = $this->Productsales->find('all', array(
'joins' => array(
array(
'table' => 'product',
'alias' => 'prod',
'type' => 'INNER',
'conditions' => array(
'prod.id = Productsales.product_id'
)
),
array(
'table' => 'sales_order_address',
'alias' => 'sod',
'type' => 'INNER',
'conditions' => array(
'sod.id = Productsales.sales_order_address_id'
)
),
),
'fields' => array(
'Productsales.*',
'prod.name',
'prod.image_url',
'sod.*'
),
'conditions' => array(
'Productsales.product_sales_slno' => $orderId
)
)
);
$this->set('Productsaleslist', $Productsaleslist);
}
$orderId comes from a URL parameter, and it contains the value of product_sales_slno (RAJ201701211485025418). This should retrieve two integers, but I'm only getting one as shown below.
How can I fix this?

How to include from another table in query Cakephp

I need to know the cakeish way of adding the from Features_properties. so when I do
where FeaturesProperty.feature_id = 3
it won't give an error
The below will generate a MySQL query
$this->Paginator->settings = array('conditions' => array($conditionID, $categoryID, $localityID, $serviceTypeID,
'Property.price BETWEEN ' . $params['priceFrom'] . ' AND ' . params['priceTo'],
$bedrooms,$bathrooms,'FeaturesProperty.feature_id = 3','Property.viewable = 1'),
'contain' => array('Image' => array('conditions' => array('main_image' => true)),
'ServiceType','Category','FeaturesProperty', 'Condition', 'Locality',),
'limit' => 15);
Which can be seen hereunder
SELECT `Property`.`id`, `Property`.`address1`, `Property`.`address2`,
`Property`.`area`, `Property`.`category_id`, `Property`.`condition_id`,
`Property`.`contact_detail_id`, `Property`.`country_id`, `Property`.`description`,
`Property`.`length`, `Property`.`main_image_id`, `Property`.`price`,
`Property`.`service_type_id`, `Property`.`locality_id`, `Property`.`viewable`,
`Property`.`width`, `Property`.`bathroom_qty`, `Property`.`bedroom_qty`,
`Category`.`id`, `Category`.`category`, `Condition`.`id`, `Condition`.`condition`,
`ServiceType`.`id`, `ServiceType`.`service_type`, `Locality`.`id`,
`Locality`.`locality`, `Locality`.`region_id`, (SELECT concat(`Locality`.`locality`,"
- ", region) FROM regions where id = `Locality`.`region_id`) AS `Locality__fullname`
FROM `realestate`.`properties` AS `Property` LEFT JOIN `realestate`.`categories` AS
`Category` ON (`Property`.`category_id` = `Category`.`id`) LEFT JOIN
`realestate`.`conditions` AS `Condition` ON (`Property`.`condition_id` =
`Condition`.`id`) LEFT JOIN `realestate`.`service_types` AS `ServiceType` ON
(`Property`.`service_type_id` = `ServiceType`.`id`) LEFT JOIN
`realestate`.`localities` AS `Locality` ON (`Property`.`locality_id` =
`Locality`.`id`)
WHERE `Property`.`price` BETWEEN 0 AND 99999999 AND
`FeaturesProperty`.`feature_id` = 3 AND `Property`.`viewable` = 1 LIMIT 15
What I need is
SELECT `Property`.`id`, `Property`.`address1`, `Property`.`address2`,
`Property`.`area`, `Property`.`category_id`, `Property`.`condition_id`,
`Property`.`contact_detail_id`, `Property`.`country_id`, `Property`.`description`,
`Property`.`length`, `Property`.`main_image_id`, `Property`.`price`,
`Property`.`service_type_id`, `Property`.`locality_id`, `Property`.`viewable`,
`Property`.`width`, `Property`.`bathroom_qty`, `Property`.`bedroom_qty`,
`Category`.`id`, `Category`.`category`, `Condition`.`id`, `Condition`.`condition`,
`ServiceType`.`id`, `ServiceType`.`service_type`, `Locality`.`id`,
`Locality`.`locality`, `Locality`.`region_id`, (SELECT concat(`Locality`.`locality`,"
- ", region) FROM regions where id = `Locality`.`region_id`) AS `Locality__fullname`
FROM `realestate`.`properties` AS `Property` LEFT JOIN `realestate`.`categories` AS
`Category` ON (`Property`.`category_id` = `Category`.`id`) LEFT JOIN
`realestate`.`conditions` AS `Condition` ON (`Property`.`condition_id` =
`Condition`.`id`) LEFT JOIN `realestate`.`service_types` AS `ServiceType` ON
(`Property`.`service_type_id` = `ServiceType`.`id`) LEFT JOIN
`realestate`.`localities` AS `Locality` ON (`Property`.`locality_id` =
`Locality`.`id`) left outer join `realestate`.`features_properties` AS `FeaturesProperty` ON
(`Property`.`id` = `FeaturesProperty`.`property_id`)
WHERE `Property`.`price` BETWEEN 0 AND 99999999 AND
`FeaturesProperty`.`feature_id` = 3 AND `Property`.`viewable` = 1 LIMIT 15
The most important part is
left outer join realestate.features_properties AS FeaturesProperty ON
(Property.id = FeaturesProperty.property_id)
Also since it might help hereunder are the relations from property model
/**
* belongsTo associations
*/
public $belongsTo = array(
'Category' => array(
'className' => 'Category',
'foreignKey' => 'category_id'
),
'Condition' => array(
'className' => 'Condition',
'foreignKey' => 'condition_id'
),
'ContactDetail' => array(
'className' => 'ContactDetail',
'foreignKey' => 'contact_detail_id'
),
'ServiceType' => array(
'className' => 'ServiceType',
'foreignKey' => 'service_type_id'
),
'Country' => array(
'className' => 'Country',
'foreignKey' => 'country_id'
),
'Locality' => array(
'className' => 'Locality',
'foreignKey' => 'locality_id'
)
);
/**
* hasMany associations
*/
public $hasMany = array(
'Image' => array(
'className' => 'Image',
'foreignKey' => 'property_id',
'dependent' => false,
),
'DetailsProperty' => array(
'className' => 'DetailsProperty',
'foreignKey' => 'property_id'
),
'FeaturesProperty' => array(
'className' => 'FeaturesProperty',
'foreignKey' => 'property_id'
)
);
public $hasAndBelongsToMany = array(
'Detail' =>
array(
'className' => 'Detail',
'joinTable' => 'details_properties',
'foreignKey' => 'property_id',
'associationForeignKey' => 'detail_id',
),
'Feature' =>
array(
'className' => 'Feature',
'joinTable' => 'features_properties',
'foreignKey' => 'property_id',
'associationForeignKey' => 'feature_id',
)
);
I found the answer although it might have been a bit dumb I didn't know how to handle joins in cakephp
so now my code is
$this->Paginator->settings = array('conditions' => array($conditionID, $categoryID, $localityID, $serviceTypeID,
'Property.price BETWEEN ' . $params['priceFrom'] . ' AND ' . $params['priceTo'], $bedrooms, $bathrooms, 'FeaturesProperty.feature_id = 2', 'Property.viewable = 1'),
'contain' => array('Image' => array('conditions' => array('main_image' => true)),
'ServiceType', 'Category', 'FeaturesProperty', 'Condition', 'Locality',),
'joins' => array(
array(
'table' => 'features_properties',
'type' => 'left outer',
'alias' => 'FeaturesProperty',
'conditions' => array('property.id = FeaturesProperty.property_id')
)
),
'limit' => 15);
the code for the join being
Although this leaves me with multiple rows rather than a distinct 1 row
I have changed my idea of using a join to using an exists as was suggested by AD7six this has solved my problem with multiple rows.
so my code now is
$this->Paginator->settings = array('conditions' => array($conditionID, $categoryID, $localityID, $serviceTypeID,
'Property.price BETWEEN ' . $params['priceFrom'] . ' AND ' . $params['priceTo'], $bedrooms, $bathrooms, '(exists (select 1 from features_properties where features_properties.feature_id = 2))', 'Property.viewable = 1'),
'contain' => array('Image' => array('conditions' => array('main_image' => true)),
'ServiceType', 'Category', 'FeaturesProperty', 'Condition', 'Locality',),
'joins' => array(
array(
'table' => 'features_properties',
'type' => 'left outer',
'alias' => 'FeaturesProperty',
'conditions' => array('property.id = FeaturesProperty.property_id')
)
),
'limit' => 15);

CakePHP 2.x: Join on belongsto

in my model 'mesn' i have belongsto relations:
public $belongsTo = array(
'Order' => array(
'className' => 'Order', 'foreignKey' => 'order_id', 'conditions' => '', 'fields' => '', 'order' => ''
), 'SaleOrder' => array(
'className' => 'SaleOrder', 'foreignKey' => 'sale_order_id', 'conditions' => '', 'fields' => '',
'order' => ''
), 'ShippedBox' => array(
'className' => 'Box', 'foreignKey' => 'shipped_box_id', 'conditions' => '', 'fields' => '',
'order' => ''
)
); /* * * hasMany associations** #var array
In one of my model-functions i want to join another table on the "belongsto" "ShippedBox" (table: boxes). But doesn't matter how i try to write the join i get an error message of unknown column:
$arr_s_result = $this->find('all', array(
'joins' => array(
array('table' => 'shipments',
'alias' => 'MyShipments',
'type' => 'INNER',
'conditions' => array(
'MyShipments.id = ShippedBox.shipment_id'
)
)),
'conditions' => array('Mesn.name' => $arr_search), 'recursive' => 0
));
I have tried:
'MyShipments.id = ShippedBox.shipment_id'
and
'MyShipments.id = box.shipment_id'
and even
'MyShipments.id = Boxes.shipment_id'
where the table "boxes" with the field "shipment_id" exists.
How can i get this join working?
'MyShipments.id = box.shipment_id'
to:
'MyShipments.id = Box.shipment_id'
Also what about this:
$arr_s_result = $this->find('all', array(
'conditions' => array('Mesn.name' => $arr_search), 'recursive' => 0
));
The belongsTo join is done automatically.
I think you'll need to do something like use subqueries to get what you want -
$arr_s_result = $this->find('all', array(
'joins' => array(
array('table' => '(SELECT boxes.id, [enter other fields you need here] FROM shipments JOIN boxes ON boxes.shipment_id = shipments .id)',
'alias' => 'MyShipments',
'type' => 'INNER',
'conditions' => array(
'Mens.shipped_box_id = MyShipments.id'
)
)),
'conditions' => array('Mesn.name' => $arr_search), 'recursive' => 0
));

cakephp lot of queries inside a query

I have a site developed in cakephp 2.x
I have a query with contain with a very lot of table relationed. I know that the most efficient way to do this big query is to make the join query manually. But I wanted to try with contain. I have selected only the field that I need but at the end I print how much queries inside are created: 5000 queries!! The database take 6-10 second to elaborate this query and I want to optimize time. Has cakephp a method to optimize? I don't want to use join now because there are a lot of join to create. Is possible? I know that is a crazy query with a lot of data but is the main page of the product with a lot of data.
This is the query:
$this->set('product',$this->Product->find('all', array(
'contain' => array(
'Origin' => array(
'fields'=>array('id','diet_id'),
'OriginAlias' => array(
'fields'=>array('id','origin_id','language','alias'),
'conditions' => array('OriginAlias.language' => $lang)
),
'Diet' => array(
'fields'=>array('id'),
'DietAlias' => array(
'fields'=>array('id','diet_id','language','alias'),
'conditions' => array('DietAlias.language' => $lang)
),
)
),
'ProductAlias' => array(
'conditions' => array('ProductAlias.alias' => $alias),
'User' => array(
'fields'=>array('id','username'),
'Profile' => array(
'fields'=>array('id','user_id','country','sex','diet','show_diet'),
),
'UserOptions' => array ('fields' => array ('avatar_type')),
'Avatar' => array ('fields' => array ('filename'))
),
'User2' => array(
'fields'=>array('id','username'),
'Profile' => array(
'fields'=>array('id','user_id','country','sex','diet','show_diet'),
),
'UserOptions' => array ('fields' => array ('avatar_type')),
'Avatar' => array ('fields' => array ('filename'))
)
),
'ProductImage'=> array(
'limit' => 1,
'ProductImageVote'=> array(
'order' => 'ProductImageVote.vote desc'
),
'User' => array(
'fields'=>array('id','username'),
'Profile' => array(
'fields'=>array('id','user_id','country','sex','diet','show_diet'),
),
)
),
'ProductGroupProduct' => array(
'fields'=>array('id','user_id','product_id','product_group_id'),
'ProductGroup'=> array(
'fields'=>array('id','text','language'),
),
),
'ProductIngredientVersion' => array(
'conditions' => $cond_version,
'limit' => 1,
'ProductIngredient' => array(
/*'Product' => array(
'ProductAlias' => array()
),*/
'Ingredient' => array(
'fields'=>array('id','user_id','origin_id','active'),
'IngredientAlias' => array(
'fields'=>array('id','user_id','ingredient_id','edit_user_id','alias'),
),
),
),
),
'ProductVersion' => array(
'conditions' => array('ProductVersion.active' => '1', 'ProductVersion.product_id' => $id),
'limit' => 1,
'ProductProperty' => array(
'Property' => array(
'fields'=>array('id','user_id','group_id','unit_id'),
'PropertyAlias' => array(
'fields'=>array('id','user_id','property_id','alias','language'),
'conditions' => array('PropertyAlias.language' => $lang)
),
'Unit' => array(
'fields'=>array('id','user_id','symbol','active'),
),
'PropertyGroup' => array(
'fields'=>array('id','user_id'),
'PropertyGroupAlias' => array(
'fields'=>array('id','user_id','group_id','alias'),
'conditions' => array('PropertyGroupAlias.language' => $lang)
)
)
),
),
),
'Manufacturer' => array(
'fields'=>array('id','text'),
),
'Brand' => array(
'fields'=>array('id','text'),
),
'UserProductLike' => array(
'conditions' => array('UserProductLike.product_id' => $id),
),
'UserProductHate' => array(
'conditions' => array('UserProductHate.product_id' => $id),
),
),
'conditions' => array('Product.id' => $id)
)));