Laravel 5. Using the USING operator - php

I tried to find it for a long time, and I can't believe that Laravel doesn't have this functionality.
So, I can write:
select * from a join b where a.id = b.id
or more beautiful:
select * from a join b using(id)
First case is simple for Laravel:
$query->leftJoin('b', 'a.id', '=', 'b.id')
But how to write second case? I expect that it should be simple and short, like:
$query->joinUsing('b', 'id')
But thereis no such method and I can't find it.
PS: it's possible that the answer is very simple, it's just hard to find by word "using", because it's everywhere.
UPDATE
I'm going deeper to source, trying to make scope or pass a function to join, but even inside of this function I can't to anything with this $query. Example:
public function scopeJoinUsing($query, $table, $field) {
sql($query->join(\DB::raw("USING(`{$field}`)")));
// return
// inner join `b` on USING(`id`) ``
// yes, with "on" and with empty quotes
sql($query->addSelect(\DB::raw("USING(`{$field}`)")));
// return
// inner join `b` USING(`id`)
// but in fields place, before "FROM", which is very logic :)
}
So even if forget about scope , I can't do this in DB::raw() , so it's impossible... First time I see that something impossible in Laravel.

So, the answer is - it's impossible.
Laravel doesn't support this functionality, which is really sad.
I fix it in Laravel source code, my PULL REQUEST here - https://github.com/laravel/framework/pull/12773

Nothing is impossible in Laravel.
Until support for join using is added to Laravel core, you can add support for it by installing this Laravel package: https://github.com/nihilsen/laravel-join-using.
It works like this:
use Illuminate\Support\Facades\DB;
use Nihilsen\LaravelJoinUsing\JoinUsingClause;
DB::table('users')->leftJoin(
'clients',
fn (JoinUsingClause $join) => $join->using('email', 'name')
);
// select * from `users` left join `clients` using (`email`, `name`)

Related

Issue in Laravel 5.1 Inner Join Query

Below is my Query in Laravel 5.1
\App\Models\Project\Bids\ProjectBid_Model
::selectRaw('B.*')
->join('tblproject P','B.projectid','=','P.projectid')
->where('P.WhoCreatedTheProject',14)
->first()
and below is the equivalant query
select B.* from `tblprojectbid`
inner join `tblproject P` on `B`.`projectid` = `P`.`projectid`
where `P`.`WhoCreatedTheProject` = 14 limit 1
What's the problem ?
Please check the line 1 in Query: select B.* from tblprojectbid.
What's the question ?
How can I change
select B.* from tblprojectbid
to
select B.* from tblprojectbid B
If you want to use Eloquent I'm afraid there is no easy way to do it.
I use in this case full table name for model for instance
\App\Models\Project\Bids\ProjectBid_Model
::selectRaw('bid_table.*')
->join('tblproject AS P','bid_table.projectid','=','P.projectid')
->where('P.WhoCreatedTheProject',14)
->first()
However it's also possible that you set alias in ProjectBid_Model:
protected $table = 'bid_table AS B';
The con is you will have this table always aliased with B, so in case you have 2 models with same alias (in this case B), you won't be able to change it later just for one table, so I think the better is 1st approach (without using alias)
Here is the final solution.
\App\Models\Project\Bids\ProjectBid_Model
::selectRaw('B.*')
->from('tblprojectbid as B')
->join('tblproject as P','B.projectid','=','P.projectid')
->where('P.WhoCreatedTheProject',14)
->first()
try this.
\DB::table('tblprojectbid as B')
->select()
->join('tblproject as P','B.projectid','=','P.projectid')
->where('P.WhoCreatedTheProject',14)
->first()

Problems With My Database Class

I'm new to OO PHP and I have created a Database Class, And I have a problem when I build My SELECT Query With the JOIN method.
If I do the Querys in Functions.
<?php
require_once("class.Database.php");
function test1($db) {
$test1 = $db->Select("*")
->From("Table1")
->Join("Table2","table_2_id = table_1_id")
->Join("Table3","table_3_id = table_2_id")
->BuildSelect();
return $test1;
}
function test2($db) {
$test2 = $db->Select("*")
->From("Table4")
->Join("Table5","table_5_id = table_4_id")
->Join("Table6","table_6_id = table_5_id")
->BuildSelect();
return $test2;
}
echo test1($db);
echo "<br>";
echo test2($db);
?>
The problem is that the First function - test1 will print out -
SELECT * FROM Table1 LEFT JOIN Table2 ON table_2_id = table_1_id LEFT JOIN Table3 ON table_3_id = table_2_id - Which is good
But then the second function test2 will print out -
SELECT * FROM Table4 LEFT JOIN Table2 ON table_2_id = table_1_id LEFT JOIN Table3 ON table_3_id = table_2_id LEFT JOIN Table5 ON table_5_id = table_4_id LEFT JOIN Table6 ON table_6_id = table_5_id
The test2 function seems to be printing out the Values from the test1 function's JOIN method as well as its own Values from the JOIN method.
Can someone please help.
Your addition of the Reset() method is what you needed.
All of your member variables except your join array were being reset on each call. For example:
$this->where = trim($where);
Which will replace your where member every time you call your Where() method. In contrast, you are doing this in your Join() method:
$this->join[] = ...
Which adds a value to the end of the member array rather than rewriting what is already in the array. It has the same affect as using array_push() (see here)
One last suggestion I was going to make was to call Reset() at the end of BuildSelect() so that you wouldn't have to remember to do so in your queries, but it looks like you did that. My recommendation, however, would be to change this:
self::Reset();
To this:
$this->Reset();
In reality, either will work and PHP makes them functionally the same for what you are trying to accomplish, but other languages aren't as forgiving. :: is designed to be used to call static members, but earlier versions of PHP do the "automagic" thing of realizing that you are calling from an instantiated class and treating self:: the same as $this->
My understanding is that later versions of PHP (5.3+) refer to what is contained in scope at the point of definition, not the point of execution when you use self::. In most cases, you are going to want to reference the scope of the class at execution time. The two notes I have about that are:
I have never allowed the use of self:: when $this-> is
appropriate, so what I am saying here is only hearsay (I have
never tested it to be true)
Given the fact that your Reset() method doesn't do any form of
logic on instantiated variables, it won't have any adverse affect on
the specific code you are using. But it is best to start with good
coding habits. Some time down the road, this practice WILL bite you if you
continue coding in PHP.
PHP can be great when you are first learning, but the things that make it great for learning often come back to haunt you when you get to making any project of long-lasting consequence if you are not coding deliberately.

Symfony 2: INNER JOIN on non related table with doctrine query builder

I'm trying to build a query with the doctrine query builder which joins a non related table like this:
$query = $this->createQueryBuilder('gpr')
->select('gpr, p')
->innerJoin('TPost', 'p')
->where('gpr.contentId = p.contentId')
But this doesn't work. I still get an error:
Error: Identification Variable TPost used in join path expression but was not defined before.
I searched for this error message and everybody answered to use the table alias + attribute like p.someAttribute. But the table I want to join isn't related in the table I start my select from.
As a normal mysql query i would write it like this:
SELECT * FROM t_group_publication_rel gpr
INNER JOIN t_post p
WHERE gpr.content_id = p.content_id
Any ideas what i'm doing wrong?
Today I was working on similar task and remembered that I opened this issue. I don't know since which doctrine version it's working but right now you can easily join the child classes in inheritance mapping. So a query like this is working without any problem:
$query = $this->createQueryBuilder('c')
->select('c')
->leftJoin('MyBundleName:ChildOne', 'co', 'WITH', 'co.id = c.id')
->leftJoin('MyBundleName:ChildTwo', 'ct', 'WITH', 'ct.id = c.id')
->orderBy('c.createdAt', 'DESC')
->where('co.group = :group OR ct.group = :group')
->setParameter('group', $group)
->setMaxResults(20);
I start the query in my parent class which is using inheritance mapping. In my previous post it was a different starting point but the same issue if I remember right.
Because it was a big problem when I started this issue I think it could be also interesting for other people which don't know about it.
Joins between entities without associations were not possible until version 2.4, where you can generate an arbitrary join with the following syntax:
$query = $em->createQuery('SELECT u FROM User u JOIN Blacklist b WITH u.email = b.email');
Reference: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html
Want to improve this post? Add citations from reputable sources by editing the post. Posts with unsourced content may be edited or deleted.
$dql = "SELECT
a, md.fisrtName , md.LastName, mj
FROM MembersBundle:Memberdata md
INNER JOIN MembersBundle:Address a WITH md = a.empID
INNER JOIN MembersBundle:Memberjob mj WITH md = mj.memberData
...
WHERE
a.dateOfChange IS NULL
AND WHERE
md.someField = 'SomeValue'";
return $em->createQuery( $dql )->getResult();
A DQL join only works if an association is defined in your mapping. In your case, I'd say it's much easier to do a native query and use ResultSetMapping to populate your objects.

CakePHP: add variables to query in controller

I have a controller action that uses a sql query:
$tag = $this->params['tag'];
$this->set('projects', $this->Project->query('SELECT * FROM projects INNER JOIN projects_tags ON projects.id = projects_tags.project_id INNER JOIN tags on projects_tags.tag_id = tags.id WHERE tags.tag LIKE $tag'));
As you can see at the end I want to use a where clause with the $tag variable but I'm not sure how the syntax would go. As I'm getting the error
Unknown column '$tag' in 'where clause'
Can someone steer me in the right direction?
Ta,
Jonesy
I would strongly advise you to use the Cake ORM instead of raw queries, especially if you're going to plug URL parameters into it. Conditions on HABTM tables can be tricky, but you can build your joins using Cake's ORM syntax as well!
Read the manual, section 3.7.6.9 Joining tables.
Should you want to use Cake's ORM, the following code should provide results equivalent to your raw SQL query:
$this->loadModel('ProjectsTag'); // Load the joining table as pseudo-model
// Define temporary belongsTo relationships between the pseudo-model and the two real models
$this->ProjectsTag->bindModel(array(
'belongsTo' => array('Project','Tag')
));
// Retrieve all the join-table records with matching Tag.tag values
$result_set = $this->ProjectsTag->find('all',array(
'conditions' => array('Tag.tag LIKE' => "%{$tag}%")
));
// Extract the associated Project records from the result-set
$projects = Set::extract('/Project', $result_set);
// Make the set of Project records available to the view
$this->set(compact('projects'));
in php there's a difference between single and double quotes... basically, single quotes dont evaluate the variables... use double quotes instead
And i think that LIKE will need also single quotes.. i'm not really sure
"SELECT * FROM projects INNER JOIN projects_tags ON projects.id = projects_tags.project_id INNER JOIN tags on projects_tags.tag_id = tags.id WHERE tags.tag LIKE '$tag'"
i know.. i know.. people will start talkin' about sql injection.. and the need to scape the caracters... that's another question =)
good luck!
I would at least consider using the cakephp sanitize functions on your tag strings if they are user sourced. See http://book.cakephp.org/view/1183/Data-Sanitization or if using mysql as the db at least consider using http://www.php.net/manual/en/function.mysql-escape-string.php or do something to filter your user input. But the best thing is to make use of the CakePHP orm stuff.
Modify your query as:
$this->set('projects',
$this->Project->query("SELECT * FROM projects
INNER JOIN projects_tags
ON projects.id = projects_tags.project_id
INNER JOIN tags ON projects_tags.tag_id = tags.id
WHERE tags.tag LIKE '" . $tag . "'") //problem was here
);
and it will work.

Getting Doctrine to use MySQL "FORCE INDEX"

I have a query in Doctrine's DQL that needs to be able to use MySQL's "FORCE INDEX" functionality in order to massively reduce the query time. Below is what the query basically looks like in plain SQL:
SELECT id FROM items FORCE INDEX (best_selling_idx)
WHERE price = ... (etc)
LIMIT 200;
I assume I have to extend some Doctrine component to be able to do this with DQL (or is there a way to inject arbitrary SQL into one of Doctrin's queries?). Anyone have any ideas?
I've found very few helpful Doctrine_RawSql examples online, so here's what I ended up doing to create my query.
$q = new Doctrine_RawSql();
$q->select('{b.id}, {b.description}, {c.description}')
->from('table1 b FORCE INDEX(best_selling_idx) INNER JOIN table2 c ON b.c_id = c.id')
->addComponent('b', 'Table1 b')
->addComponent('c', 'b.Table2 c');
If you don't like native SQL, you can use the following patch. https://gist.github.com/arnaud-lb/2704404
This patch suggests to create only one custom Walker and set it up as follows
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'UseIndexWalker');
$query->setHint(UseIndexWalker::HINT_USE_INDEX, 'some_index_name');
I created a Doctrine SqlWalker extension to apply USE INDEX and FORCE INDEX hints using DQL on top of MySql. Works with both createQuery and createQueryBuilder. You can set different index hints per DQL table aliases.
use Ggergo\SqlIndexHintBundle\SqlIndexWalker;
...
$query = ...;
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, SqlIndexWalker::class);
$query->setHint(SqlIndexWalker::HINT_INDEX, [
'your_dql_table_alias' => 'FORCE INDEX FOR JOIN (your_composite_index) FORCE INDEX FOR ORDER BY (PRIMARY)',
'your_another_dql_table_alias' => 'FORCE INDEX (PRIMARY)',
...
]);
https://github.com/ggergo/SqlIndexHintBundle

Categories