Phalcon - How do i do a SELECT IN Subquery with Phalcon models? - php

I need to know how to do i do a subquery type selection with phalcon models?
for example i want to select all the users who viewed me, they are stored in the UserView table with columns 'id','user_from','user_to' (mapped by User table user_id to either user_from or user_to)
so i want to select all the users who has a user_to as with the current user, and group by user_to make sure i only get one recorded, I wrote below function to do this but there is fundamental two problems
1. Is how to do sub-query using phalcon models
2. Is my logic correctly applied on the back-end of the DB (as i cant see real executed query)
public function getUserWithViewedMe($limit=1000000){
return User::query()
->rightJoin("XYZ\Models\UsersView")
->andWhere(" XYZ\Models\UsersView.user_from IN :user_id: ",
array('user_id' => $this->user->user_id) )
->group('user_id')
->order("XYZ\Models\UsersView.id DESC ")
->limit($limit)
->execute();
}
This returns empty set...

So far it is not possible to model subqueries in Phalcon. There is also topic according to standard implementation issues.
To query params according to other table, here is an answer.
To query IN you can use queryBuilder
$this->modelsManager->createBuilder()
// ...
->inWhere('column', $array);

Related

Select rows with equal values

How to do in Laravel?
1) get from table rows where user_id has the equal values?
2) return sum of some_amount values from this selected rows
Table:
- id;
- user_id;
- some_amount;
Table has bunch of certificates with some amount of money that belongs to different users. I need to find all certificates that belongs to one user (one user can have few certificates) and count how much money he have from all his certificates
Given you aren't looking for a solution to query this for an individual user, it sounds like you want to group by the user and sum the result of certificates.
The answer from #PhilCross is pretty close, you'd just need to modify it to add the group clause and remove the where condition. Something like this:
ModelName::groupBy('user_id')->sum('some_amount');
or
\DB::table('table_name')->groupBy('user_id')->sum('some_amount');
Generally eloquent or the query builder will have a method that relates to how you would do this in raw SQL.
I find it helpful to write out or think about how I would write the raw SQL and then slowly fill the eloquent or query builder in from that.
If you use a Model:
ModelName::where('user_id', 1)->sum('some_amount')
If you're using the query builder:
\DB::table('table_name')->where('user_id', 1)->sum('some_amount');
This is the documentation for the query builder:
https://laravel.com/docs/5.6/queries
Models: https://laravel.com/docs/5.6/eloquent
Model Relationships: https://laravel.com/docs/5.6/eloquent-relationships
Collections: https://laravel.com/docs/5.6/collections

Select rows using query builder in CakePHP based on the same end association but different relation to it

What I want to do is to get all rows related with user_id but in a different way.
First condition is to get all Books that are related with the User via Resources table where user_id is stored (in other words - Books owned by the User). Second condition is to get all Books that are related with the User through the Cities model again which is stored in the Resources table as well (Books that belong to Cities owned by the User).
I tried really a lot of things and I simply cannot make this two conditions work because I use JOIN (tried different combinations of innerJoinWith and leftJoinWith) on the same "end" model (User).
What I've done so far:
$userBooks = $this->Books->find()
->leftJoinWith("Resources.Users")
->leftJoinWith("Cities.Resources.Users")
->where(["Resources.Users" => 1])
->orWhere(["Cities.Resources.Users" => 1])
->all();
This of course does not work, but I hope you get the point about what I'm trying to achieve. The best what I was able to get with trying different approaches is the result of only one JOIN statement what is logical.
Basically, this can be separated into 2 parts which gives expected result (but I do not prefer it because I want it done with one query of course):
$userBooks = $this->Books->find()
->innerJoinWith("Resources.Users", function($q) {
return $q->where(["Users.id" => 1]);
})
->all();
$userBooks2 = $this->Books->find()
->innerJoinWith("Cities.Resources.Users", function($q) {
return $q->where(["Users.id" => 1]);
})
->all();
Also, before this I created an SQL script which works well and result is like expected:
SELECT books.id FROM books, cities, users_resources WHERE
(users_resources.resource_id = books.resource_id AND users_resources.user_id = 1)
OR
(users_resources.resource_id = cities.resource_id AND books.city_id = cities.id AND users_resources.user_id = 1)
This query works and I want to transfer it into ORM styled query in CakePHP to get both Books that are owned by the user and the ones that are connected with the User via Cities. I want somehow to separate these joins to individually filter data like I did in the SQL query.
EDIT
I've tried #ndm solution but the result is the same as where there is only 1 association (User) - I was still able to get data based on only one join statement (second one was ignored). Due to the fact I had to move on, I ended up with
$userBooks = $this->Books->find()
->innerJoinWith("Cities.Resources.Users"‌​)
->where(["Users.id" => $userId])
->union($this->Books->find()
->innerJoinWith("Resour‌​ces.Users")
->where([‌​"Users.id" => $userId])
)
->all();
which outputs correct result but not in very effective way (by union of 2 queries). I would really like to know the best way to approach this as this is a very common case (filtering by related model (user) that is associated with other models).
The ORM (specifically the eager loader) doesn't allow joining the same alias multiple times.
This can be worked around in various ways, the most simple one probaly being creating a separate association with a unique alias. For example in your ResourcesTable, create another association to Users with a different alias, say Users2, like:
$this->belongsToMany('Users2', [
'className' => 'Users'
]);
Then you can use that association in the second leftJoinWith(), and apply the conditions accordingly:
$this->Books
->find()
->leftJoinWith('Resources.Users')
->leftJoinWith('Cities.Resources.Users2')
->where(['Users.id' => 1])
->orWhere(['Users2.id' => 1])
->group('Books.id')
->all();
And don't forget to group your books to avoid duplicate results.
You could also create the joins manually using leftJoin() or join() instead, where you can define the aliases on your own (or don't use any at all) so that there are no conflicts, for more complex queries that can be a tedious task though.
You could also use your two separate queries as subqueries for conditions on Books, or even create a union query from them, which however might perform worse...
See also
Cookbook > Database Access & ORM > Query Builder > Adding Joins
CakePHP Issues > Improve association data fetching

Cakephp - get database data from not associated models

I wanted to ask, how can i get data from one table and use this in other find.
For example, i have films table.
I want to get highest rated 3 films. Result should return 3 ID's.
Now, i want to create other query from not associated table, and pass this 3 ID's as "conditions" to find data in other table.
I dont want to use associations, because, data is stored in many databases, and this is problematic.
Thank You.
Once you've got your film IDs you can use in to filter the results from your other Model:-
$filmIds = ['32','55','75'];
$query = TableRegistry::get('Model')->find()
->where(function ($exp, $q) use ($filmIds) {
return $exp->in('film_id', $filmIds);
});
// WHERE film_id IN ('32','55','75')
Check out the docs section on advanced conditions.
If you need to get your film IDs into the correct format (i.e. that shown in the example code) you can use Hash::extract() on the results from your previous query.
if your cakephp version 3.x you can use subqueries in fairly intuitive way
$films = TableRegistry::get('Films')->find('highestRated')
->select(['id'])
->limlt(3);
$query = $related->find()
->where(['id' => $films]);
Subqueries are accepted anywhere a query expression can be used. For example, in the select() and join() methods. http://book.cakephp.org/3.0/en/orm/query-builder.html#subqueries

Id Column Overwritten by Join

Using Laravel 4.2 & MySQL.
I have an applications table with an id and a fit_score_id column, and a fit_scores table with an id column. It's a basic "belongs to" relationship.
The following code:
$query = Application::join('fit_scores', 'applications.fit_score_id', '=', 'fit_scores.id');
$collection = $query->get();
...produces a collection of Application models with the id property set to the value of the fit_score_id. What am I doing to cause this?
I should note that it is necessary to do this join rather than simply using eloquent relations, because I'm going to want to order the results by a column on the fit_scores table. I don't believe this is possible using Eloquent without an explicit join.
The best way to solve this is by chaining the join method to a select method as following:
Application::select('*', \DB::raw("applications.id as appid"))
->join('fit_scores', 'applications.fit_score_id', '=', 'fit_scores.id')
->get();
Explained: The solution simply suggest that instead of thinking to prevent the behavior of overwriting the first id with the joined id, we can hook into the primary selection query (before joining) and change the label of the id column into something else (in this case 'appid'). By doing so, we end up with both the id of the parent table being labeled 'appid' and the id of the joined table being labeled 'id' again while they lives together on the final result.
I was able to find a possible solution using this answer:
Laravel 4 - JOIN - Same column name
Basically, since Laravel does not automatically prefix column names with table_name. for joined tables, we need to manually work around it by aliasing any conflicting column names in joins. Adding this select statement to my query did it:
->select(DB::raw("applications.*, fit_scores.*, applications.id as id"))
It depends on what you need but probably you can achieve it using eager loading. In case you need to mix joins and eager loading check this out. http://www.jmilan.net/posts/eager-loading-joins-in-laravel

Kohana ORM (custom query) vs MySQL ORDER BY & GROUP BY

I have a table with messages. I want to group these messages into conversations, checking if there is unread messages and previewing the latest message.
My current ORM looks like this:
$text = new Model_Text;
$text->select(DB::expr('min(`text`.`read`) AS `read_all`'))
->where('time', '>', $time_offset);
->order_by('read', 'DESC')->order_by('time', 'ASC')
->group_by('contact')->with('user')->find_all();
But as you know MySQL groups before it orders, meaning I don't get the latest message, while the min('text'.'read') trick makes sure I always know if the conversation has unread messages.
To get the latest message in each conversation, I would do a MySQL query like this:
SELECT min(q1.`read`) as read_all, q2.* FROM texts q1
INNER JOIN (
SELECT * FROM texts ORDER BY time DESC
) q2 ON(q1.contact = q2.contact)
GROUP BY q1.contact
ORDER BY q2.`time` DESC, q2.`read` ASC
But I am completely lost as for how to optimally implement this query with ORM.
My best guess would be to execute the above query directly with Kohana's DB class and load an ORM object for each row returned. But, this will cause one database hit for each and every conversation loaded into ORM, for data that I already retrieved - stupid!
Let me know your thoughts!
Thanks.
There is not a good way to do this with the ORM class, even if you could select the data properly, you wouldn't be able to read it back, as your Model_Text won't have the read_all property.
I think the best way to do this is with two queries, The first would be to get the conversations that you are interested in, select contact, min(read) as read_all ... where time > $time_offset and group by contact. Once you have the contact and read_all, you can make another query the get just the latest text for each conversation and load that into your Model_Text as suggested by Carl: DB::query(DATABASE::SELECT, $sql)->as_object('Model_Text')->execute()

Categories