Convert the MySql query to CakePhp 2.0 - mysql

How do I convert this MySql query to CakePhp's find.
And please tell me how can i practice writing Find queries in cakephp
select distinct trips.fk_userid from spots, trips
where spots.fk_tripid = trips.id
and trips.isapproved = 1
and spots.id in (".$row[$first_index]['spot_list'].")

The model can be Trip and you can query like this
$this->Trip->query("select distinct trips.fk_userid from spots, trips where spots.fk_tripid = trips.id and trips.isapproved = 1 and spots.id in (".$row[$first_index]['spot_list'].")");
or
You should create Trip and Spot model and in Trip model, you have to have this in Spot model:
public $belongsTo = array(
'Trip' => array(
'className' => 'Trip',
'foreignKey' => 'fk_tripid'
)
);
and query it like this:
$this->Spot->find('all', array(
'fields' => array("distinct Trip.fk_userid"),
'conditions' => array(
'Trip.isapproved' => 1,
'Spot.id' => $row[$first_index]['spot_list']
)
));

Related

How to use conditions in self association without joins?

I have Product model [id, parent_id, x].
I want to append on each Product, single ProductParent, only when Product.x = 0, and Product.parent_id = ProductParent.id.
What i try is:
public $hasOne = array(
'ProductParent' => array(
'className' => 'Product',
'foreignKey' => false,
'conditions' => array(
'ProductParent.id = Product.parent_id',
'Product.x' => 0
)
)
);
And get output Product.parent_id is unknown.
You can try using Tree behavior. Check this: http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html

Retrieving info from multiple tables using joins in sql with cakephp

My table structure is below
recipies
categories
ingredients(contain category_id)
recipies_categories
recipies_ingredients
Using cakephp I want to retrieve ingredients of a specified category of a specified recipe. For example I have-
recipies:recp1,recp2,recp3
categories:cat1,cat2,cat3
ingredeints:ing1,ing2,ing3
recipies_categories:recp1-cat1,recp2-cat2,recp2-cat3,recp3-cat3
ingredients_categories:ing1-cat1,ing2-cat2
Given recipe=recp2, category=cat1 or cat3 I should get no ingredients
Given recipe=recep2, category=cat2 I should get ing2
I have tried various ways using joins but couldn't get the result. Anybody can help?
$settings = array();
$settings['recursive'] = 1;
if (!empty($selected_categories)) {
$this->Ingredient->Behaviors->load('Containable');
$settings['conditions'] = array(
'Ingredient.category_id' => $selected_categories //array containing selected catagory ids
);
$settings['contain'] = array(
'Category' => array('id', 'name')
);
}
$settings['joins'] = array(
array('table' => 'recipes_ingredients',
'alias' => 'RecipesIngredient',
'type' => 'INNER',
'conditions' => array(
'RecipesIngredient.ingredient_id = Ingredient.id',
'RecipesIngredient.recipe_id' => $recipe_id //selected recipe
)
)
);
$this->Paginator->settings = $settings;
$ingredients = $this->Paginator->paginate($this->Ingredient);

CakePHP counterCache joining irrelevant tables to update counter

I have a User model and a Message model.
The Message model is linked to the User model twice like this:
public $belongsTo = array(
'UserSender' => array(
'className' => 'User',
'foreignKey' => 'sender_id',
'counterCache' => array(
'messages_sent_count' => array(
'is_deleted' => FALSE
)
)
),
'UserRecipient' => array(
'className' => 'User',
'foreignKey' => 'recipient_id',
'counterCache' => array(
'messages_received_count' => array(
'is_deleted' => FALSE
),
'messages_unread_count' => array(
'is_deleted' => FALSE,
'is_read' => FALSE
)
)
),
'Operator' => array(
'className' => 'Operator',
'foreignKey' => 'operator_id'
)
);
Besides the User model, the Message model also $belongsTo the Operator model. The Operator model is irrelevant to the message count for the users, but its table is still being joined in the count query, as debug shows:
'query' => 'SELECT COUNT(*) AS `count` FROM `database`.`messages` AS `Message` LEFT JOIN `database`.`operators` AS `Operator` ON (`Message`.`operator_id` = `Operator`.`id`) LEFT JOIN `database`.`users` AS `UserSender` ON (`Message`.`sender_id` = `UserSender`.`id`) LEFT JOIN `database`.`users` AS `UserRecipient` ON (`Message`.`recipient_id` = `UserRecipient`.`id`) WHERE `Message`.`is_deleted` = '0' AND `Message`.`sender_id` = 389',
'params' => array(),
'affected' => (int) 1,
'numRows' => (int) 1,
'took' => (float) 394
For the sake of simplicity I've actually excluded one more model that the Message model $belongsTo, but the above query shows the problem.
The counterCache function does a quite expensive query just to update the counter. Is there a way to maybe override or adjust the counterCache method to not join irrelevant tables in the query?
I can't test it right now, but since the recursive setting used by Model::updateCounterCache() is hard-coded based on whether conditions are defined for the counter cache field, the only way to change this (besides completely reimplementing Model::updateCounterCache()) is probably to modify the count query in Model::_findCount() or Model::beforeFind() of your Message model.
public function beforeFind($query) {
// ... figure whether this is the count query for updateCounterCache,
// maybe even try to analyze whether the passed conditions require
// joins or not.
if(/* ... */) {
$query['recursive'] = -1;
}
return $query;
}
Depending on how much control you'll actually need the containable behavior might do the trick too, it sets recursive to -1 in case no containments are being passed
$Message->contain(); // === recursive is being set to -1 in before find callback
$Message->delete(123);

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.

CakePHP model associations - Random order on retrieve of associated model for HABTM

I am retrieving data:
$mydata = $this->ProductList->find('all', array('order' => 'rand()', 'conditions' => array('name' => 'we love')));
I have set up a HABTM relationship to the Product model. As you can see, I am fetching all products in the 'we love'-list. Now, I want those Products I am retrieving to be randomised. But they are not, instead the MySQL is randomised on the ProductList model as you can see in the SQL. Why is that? How can I get the random fetch on the Products instead?
Resulting MySQL query:
SELECT `ProductList`.`id`, `ProductList`.`name` FROM `database`.`product_lists` AS `ProductList` WHERE `name` = 'we love' ORDER BY rand() ASC
SELECT `Product`.`id`, `Product`.`category_id`, `Product`.`name`, `Product`.`price`, `Product`.`description`, `ProductListsProduct`.`product_list_id`, `ProductListsProduct`.`product_id` FROM `database`.`products` AS `Product` JOIN `database`.`product_lists_products` AS `ProductListsProduct` ON (`ProductListsProduct`.`product_list_id` = 3 AND `ProductListsProduct`.`product_id` = `Product`.`id`)
EDIT:
There are so many different ways to approach this; to get a random product from a user's product list. You could do it with PHP - just find all of the products and then use rand() to pick on from the returned array. You could set a Model query condition. The list goes on...
I would probably create an alias to the Product model in ProductList called RandomProduct. You could set the query for the retrieved product when you set the relationship:
public $hasMany = array(
'RandomProduct' => array(
'className' => 'Product',
'foreignKey' => 'product_list_id',
'order' => 'Rand()',
'limit' => '1',
'dependent' => true
)
);
You can then use the containable behavior so that this model is only retrieved when you need it. (You wouldn't need to do this if recursive finds are greater than -1, but I usually do that as best practice so that my models only query for the data that they need.) The following would return any ProductList called 'we love' and a "random" product associated with that list.
$mydata = $this->ProductList->find(
'all',
array(
'conditions' => array(
'name' => 'we love'
)
),
'contain' => array(
'RandomProduct'
)
);