CodeIgniter DataMapper - where_join_field Generating Incorrect Query - php

My program is using the WanWizard DataMapper ORM with CodeIgniter, and needs to perform a simple query based on a field in a join table. Here is my code:
$d = new Deal();
$deals = $d
->where_join_field('networks', 'status', 'active')
->get();
Fairly straightforward, as you can see. A many-to-many relationship between "deals" and "networks", with a "status" field in the join table. However, this is the query it generates:
SELECT * FROM (`deal`) WHERE `deal_network`.`status` = 'active'
Obviously, this doesn't work at all, because there in no JOIN in place to introduce the deal_network table. I tried using include_related, but it aliases the deal_network table as networks_deal_network, resulting in an incorrect table reference. How can I get DataMapper to JOIN that table into the query properly?

It appears that this is table name bug in _join_field in libraries/datamapper.php. I forked and submitted a pull-request with the fix.
https://bitbucket.org/jonahbron/datamapper

Related

How to write this self joined query with distinct based on the inner join relationship using the query builder?

I have an entity Place that can hold many different types of places, particularly in this case "cities" and "states", where each "city" can hold a reference to the same table pointing to a parent "state".
I have another entity Office with a many-to-one relationship to Place, but because of domain constraints, an office will only be linked to a "city", never to a "state".
I have to write a query to get all states where we have one or more offices, but only those.
In plain SQL, the query works out easyly enough:
SELECT DISTINCT states.*
FROM offices o
INNER JOIN places cities ON cities.id = o.place_id
INNER JOIN places states ON cities.parent_place_id = states.id
WHERE p.place_type = 'city'
This works, and we get exactly what we need.
But I need (I would prefer) to write the query using the query builder. This is "base" query, but we need to conditionally apply several more filters, which would make using the QB much cleaner and manageable.
Currently we are using a Native Query, but this means that we need to manipulate the SQL string before we call em::createNativeQuery(), which is cumbersome.
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('states')->distinct()
->from(PartnerOffice::class, 'o')
->innerJoin('o.place', 'p')
->innerJoin('p.parentPlace', 'states')
->where("p.placeType = 'city'");
But this gives an error about:
SELECT DISTINCT': Error: Cannot select entity through identification variables without choosing at least one root entity alias
How can we make this query work?
Thanks and regards.
It's hard to figure out the query you should build without seeing the entities (at least for me). Eg: do you have a mappedBy field? But I think your problem is that you are fetching no information from PartnerOffice while it is your root entry (from). Either change your ->select() for a ->addSelect but that will fetch the entire PartnerOffice entity for each record and you distinct might not work as expected, or use the Place entity as your root entry and flip the logic.
Let's assume your entities' relations are bidirectional. Something like this should do it (but you might need to change it for the real entities and fields names).
$this->getEntityManager()->createQueryBuilder()
->distinct()
->from(Place::class, 'p')
->join('p.cities', 'c')
->join(PartnerOffice::class, 'po', Join::WITH, 'po.place = p')
->andWhere('p.placeType = \'state\'');
I think you have posted an incomplete base sql query (WHERE AND) and you made an alias p.place_type which is not defined above in the query.
However, from your statement, I think you can try the query below,
$qb->selectRaw('DISTINCT s.states')
->from('offices as o')
->join('places as c', 'c.id', '=', 'o.place_id')
->join('places as s', 's.id', '=', 'c.parent_place_id')
->where('c.placeType', 'city')->get();
By default, ->join() refers to inner join in queryBuilder.
Source:
https://laravel.com/docs/8.x/queries#inner-join-clause

Using Laravel's query builder or Eloquent to inner join a table with a temporary one

I have a rather complex query that I'm going to simplify it for making it easier to read:
SELECT `temp_table`.field1, `temp_table`.field2 FROM
(A TEMPORARY TABLE MADE OUT OF A SELECTION QUERY) AS temp_table
INNER JOIN table ON temp_table.field1 = table.id
WHERE table.some_field = 'something'
I am currently using a RAW query for this, but now I want to rewrite it using either Query Builder or Eloquent ORM in Laravel. I am new to Laravel. So, any idea or help is appreciated.
try using fromSub
DB::query()->fromSub(function ($query) {
$query->selectRaw('your query to get the temp_table');
}, 'temp_table')->join('table','table.id','=','temp_table.field1')
->where('table.some_field','something');
the method 'fromSub' take a string as second parameter, it is 'as' in sql, so you can use it to name the temp table ....

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

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);

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

Yii - Query Manipulation for Custom CGridView with Advanced Search

So, I've extended CGridView to include an Advanced Search feature tailored to the needs of my organization.
Filter - lets you show/hide columns in the table, and you can also reorder columns by dragging the little drag icon to the left of each item.
Sort - Allows for the selection of multiple columns, specify Ascending or Descending.
Search - Select your column and insert search parameters. Operators tailored to data type of selected column.
Version 1 works, albeit slowly. Basically, I had my hands in the inner workings of CGridView, where I snatch the results from the DataProvider and do the searching and sorting in PHP before rendering the table contents.
Now writing Version 2, where I aim to focus on clever CDbCriteria creation, allowing MySQL to do the heavy lifting so it will run quicker. The implementation is trivial when dealing with a single database table. The difficulty arises when I'm dealing with 2 or more tables... For example, if the user intends to search on a field that is a STAT relation, I need that relation to be present in my query so that I may include comparisons.
Here's the question. How do I assure that Yii includes all with relations in my query so that I include comparisons? I've included all my relations with my criteria in the model's search function and I've tried CDbCriteria's together set to true ...
public function search() {
$criteria=new CDbCriteria;
$criteria->compare('id', $this->id);
$criteria->compare( ...
...
$criteria->with = array('relation0','relation1','relation3');
$criteria->together = true;
return new CActiveDataProvider(
get_class($this), array(
'criteria'=>$criteria,
'pagination' => array('pageSize' => 50)
));}
Then I'll snatch the criteria from the DataProvider and add a few conditions, for example, looking for dates > 1234567890. But I still get errors like this...
CDbCommand failed to execute the SQL statement:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 't.relation3' in 'where clause'.
The SQL statement executed was:
SELECT COUNT(DISTINCT `t`.`id`) FROM `table` `t`
LEFT OUTER JOIN `relation_table` `relation0` ON (`t`.`id`=`relation0`.`id`)
LEFT OUTER JOIN `relation_table` `relation1` ON (`t`.`id`=`relation1`.`id`)
WHERE (`t`.`relation3` > 1234567890)
Where relation0 and relation1 are BELONGS_TO relations, but any STAT relations, here depicted as relation3, are missing. Furthermore, why is the query a SELECT COUNT(DISTINCT 't'.'id') ?
Edit #DCoder Here's the specific relation I'm working with now. The main table is Call, which has a HAS_MANY relation to CallSegments, which keeps the times. So the startTime of the Call is the minimum start_time of all the related CallSegments. And startTime is the hypothetical relation3 in my anonymized query error.
'startTime' => array(self::STAT, 'CallSegments', 'call_id',
'select' => 'min(`start_time`)'),
Edit Other people have sent me to CDbCriteria's together property, but as you can see above, I am currently trying that to no avail.
Edit Looks like the issue has may have been reported: Yii and github tickets.
It is not a good idea to snatch the sql from a criteria and use it by yourself.
If you are using the "with" property then you could easily use comparisons like:
$criteria->compare("`relation1`.`id`", $yourVarHere);
Also Yii doesn't behave well with grouping.
My approach with STAT relations is using an subquery in the selects of Yii, followed by having:
$criteria->select = array("`t`.*", "(SELECT COUNT(*) FROM `relation3` WHERE `id` = `t`.id_relation3) AS `rel3`");
$criteria->having = "`rel3` > " . $yourValue;
The above method creates a bug in the gridview pagination because the count is done on a different query. A workaround will be to drop the "with" property and write the joins by yourself in the "join" property like:
$criteria->join = "LEFT OUTER JOIN `relation_table` `relation0` ON (`t`.`id`=`relation0`.`id`)
LEFT OUTER JOIN `relation_table` `relation1` ON (`t`.`id`=`relation1`.`id`)
LEFT OUTER JOIN `relation_table` `relation3` ON (`t`.`id`=`relation3`.`id`)";
If the bug is a little difficult to get working could you use the stat relation as a simple HAS_ONE with :
'select'=>'count(relation3.id)',
'joinType'=>'left join',
'group'=>'relation3.id',
'on'=>'t.id = relation3.id',
'together'=>true
to get the count value out along side everything else?
Not sure how well this would work for your case but it's been helpful for me from time to time.

Categories