Join on Eloquent breaks Relationships? - laravel

I have the following relation:
I have a many-to-many relation between Course and Users, and then again a has-many relation between Users and Certificates.
Now here is way how to get all users from one course, who have a certificate in the same course:
$user = $course->users()
->whereHas('certificates', function ($query) use($course){
$query->where('course_certificates.course_id', '=', $course->id);
})->first();
This will return a user with certificates $user->certificates.
This is the query.
SELECT *
FROM "course_users"
WHERE "course_users"."course_id" = ? AND
"course_users"."course_id" IS NOT NULL AND
EXISTS (
SELECT *
FROM "course_certificates"
WHERE "course_users"."id" = "course_certificates"."user_id" AND
"course_certificates"."course_id" = ?
)
If I use a join instead:
$user = $course->users()
->join('course_certificates', 'course_certificates.user_id', '=', 'course_users.id')
->where('course_certificates.course_id', '=', $course->id)
->first();
Then I get the same user, however the relationship $user->certificates is empty?
SELECT *
FROM "course_users"
INNER JOIN "course_certificates"
ON "course_certificates"."user_id" = "course_users"."id"
WHERE "course_users"."course_id" = ? AND
"course_users"."course_id" IS NOT NULL AND
"course_certificates"."course_id" = ?
Why is that?

I am not 100% sure, but if you use join I think laravel do not understand whatever table you are joining it with that it is a relation, it is just a join, it could mean anything, but the first query you are telling it what the relation means (whereHas some data for X relation).
Looking at the source code, I can confirm what I am saying: join and whereHas (and has).
getRelated() does the magic (adds the data to your relation). See that join does not do anything of this, it literally joins queries, nothing else related to relations. Also join comes from Query/Builder and any other method about relations comes from Eloquent/Builder.

Related

Laravel JOIN with JSON string

I have two tables in Laravel, one is the comment table, and the second is the users table. In the comment table, I have this type of data.
For this comment table, I want to match the tags column's userid in JSON, so how can we join that userid with the user's table? here is what I tried, but that is not working as expected.
$messages = TopicComment::where('user_id', $currentUserId)
->join("users", "users.id", "=", "users.id")
->(function ($query) {
$query->whereJsonContains('tags.userid', users.id);
})
->
->get()->toArray();
with this package you can create a relation via a json field
https://github.com/staudenmeir/eloquent-json-relations
First, there seem to be a number of errors in your code.
Judging from the DB schema, there is no user_id column in your comments table and so, ::where('user_id', $currentUserId) will not work.
A similar issue occurs in your join statement. You're joining on "users.id","=","users.id" which is the same column in the same table.
There's no method called in the line with function($query). Ideally, should be a where clause.
Correct usage of whereJsonContains would be:
$query->whereJsonContains('tags', ['userid' => $currentUserId]);
Rouge arrow after that line.
And so, your final result after correcting the changes should look like:
use Illuminate\Support\Facades\DB;
...
$messages = TopicComment::join('users', 'users.id', DB::Raw("CAST(comments.tags->'$.userid' AS UNSIGNED)"))
->where(function ($query) use ($currentUserId) {
$query->whereJsonContains('tags', ['userid' => $currentUserId]);
})
->get()
->toArray();
I think the only way to extract is to use json tables.
DB::select(DB::raw("
SELECT document_types.*, jst.name
FROM document_types,
JSON_TABLE(settings, '$[*]' COLUMNS (
`userid` int(11) PATH '$.userid',
`name` varchar(255) PATH '$.name'
)) jst
inner join users on jst.userid = users.id WHERE users.id = :id"
,['id' => $currentUserId]))->get()
credit: https://dba.stackexchange.com/questions/306938/extract-json-object-inside-of-an-json-array-then-search-base-on-id
Unfortunately I don't have json functions on my mysql so the code will probably fail, maybe at least it'll help get you on the right track.
You can try this way...
eg:
<?php
$comments = DB::table('topic_comments')
->join('users', 'users.id', '=', 'topic_comments.tags->userid')
->select('topic_comments.*', 'users.name')
->get();

How to convert query with many joins and inner select to Laravel ORM

Hi guys please help solve this problem.
Need to convert this query to Laravel ORM.
$user = Auth::user();
$datas = DB::select('select * from kvits k
left join (select o.kvit_id, sum(o.amount * o.price) as sum from operations o
group by o.kvit_id) s on k.id = s.kvit_id
left join users u on k.user_id = u.id
left join users h on k.hamkor_id = h.id
left join stores s2 on k.store_id = s2.id
where k.user_id = ' $user->id ');
Main problem is how to convert this query to Larave ORM.
Basically this part
left join (select o.kvit_id, sum(o.amount * o.price) as sum from operations o
group by o.kvit_id) s on k.id = s.kvit_id
Second problem is:
Why Laravel getting the last join id in the column without taking all the ids (e.g. k.id, ****, u.id, ***, s.id on this query). And does not display data from all other joined columns.
How to get them.
I want to get data all joined tables also, like k.id, k.date, ... u.id, u.name, .... s.id, s.name, s.notes . Like belowed table.
First, Why Laravel getting the last join id in the column without taking all the ids (e.g. k.id, ****, u.id, ***, s.id on this query)., Laravel uses PDO to fetch data from the database, and PDO fetches the data by the column name and not by the table_name.column_name index.
Basically what you have to do is create your Model (the model that will contain all this data together) like that (it is a model example):
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class MyUserModel extends Model {
public function addresses() {
// query all addresses of this user and returns it
}
public function phone () {
// query User's phone from database and returns it
}
// ...
}
and then when you want to use some value, for example, you want to get the User's phone ID, you just use $user->phone->id and Laravel will run the phone() method from MyUserModel class. To learn more about go to Laravel Eloquent Relationships Queries Documentation
if you want to always load these data together whenever a query with this model is made, you can use the protected $with model variable to can fetch all data. see more on Laravel Documentation.
CAUTION: If you want to customize the method query from outside the method you must call the method as a Function, for example $user->phone()->where('active', 1), and not as a Dynamic Property like $user->phone->where('active', 1).

Laravel 5.6 Custom Query Build showing empty result

No errors, just an empty result. I am trying to work out why this query within a model is showing an empty collection.
Mysql Workbench query:
select
u.`name`, u.email, ual.admin, a.account_name
from
users as u
join users_account_link as ual on u.id = ual.user_id and u.account_id_in_use = ual.account_id
join accounts a on ual.account_id = a.id
where
u.sub = 'ABCDE';
Spits one row containing the desired result set.
Recreating this in Laravel query builder:
$settings = DB::table('users as u')
->join('users_account_link as ual', function ($join) {
$join->on('u.id', '=', 'ual.user_id')
->where('u.account_id_in_use', '=', 'ual.account_id');
})
->join('accounts as a', 'ual.account_id', '=', 'a.id')
->select('u.name as user_name', 'u.email as user_email', 'ual.admin as admin_check', 'a.account_name')
->where('u.sub',auth()->user()->sub)
->get();
dd($settings);
Provides an empty collection. I have done many custom queries which work well however the problem I have narrowed down the result set is the additional condition for join users_account_link as ual on u.id = ual.user_id and u.account_id_in_use = ual.account_id and have tried to move this condition to a where clause which still provides an empty result.
'u.account_id_in_use', '=', 'ual.account_id' are both integers however replacing u.account_id_in_use with a hardcoded integer e.g. 2 would return a result. Therefore, Laravel seems to have an issue with this field and replaced the where-> with a whereRaw now returns the desired result.
For anyone having a similar issue, try replacing fields with hardcode values to isolate the issue and look into using raw when possible to overcome the issue.
Hope this helps anyone in need.

How to Join same table in laravel

I wan to wirte a join query to connect same table, and without ON, but when i write it in laravel without on it is showing error
$key = DB::table('api_keys as ak')
->join('api_keys as bk','')
->where('ak.api_key', $api_key)->where('ak.user_id',0)
->pluck('api_key');
want to build the below query,
SELECT * FROM `api_keys` as ak
JOIN `api_keys` as bk
WHERE ak.`api_key`=$akey
and ak.`user_id`=$auser
and bk.`user_id`=$bsuer
and bk.`api_key`=$bkey
You must provide an ON clause for the join. More about where ON clauses are required can be found in this answer.
You can view the generated query using toSql() on a QueryBuilder object:
echo $key = DB::table('api_keys as ak')
->join('api_keys as bk','')
->where('ak.api_key', $api_key)->where('ak.user_id',0)
->toSql();
Which in your case returns:
select * from `api_keys` as `ak` inner join `api_keys` as `bk`
on `` `` where `ak`.`api_key` = ? and `ak`.`user_id` = ?
In your case it isn't totally clear what you are trying to achieve, but you might consider joining on api_key or the primary key of the api_keys table, if that is different:
$key = DB::table('api_keys as ak')
->join('api_keys as bk','ak.api_key', '=', bk.api_key)
->where('ak.api_key', $api_key)->where('ak.user_id',0)
->pluck('api_key');
DB::table('registerusers as a')
->join('registerusers as b', 'a.id', 'b.refer_id')
->where('a.username', 'b.username')
->where('b.id', 'a.refer_id')
->value('b.id');
without using on clause in laravel query builder you can use following
$key = DB::table(DB::raw('api_keys as ak, api_keys as bk'))
->where('ak.api_key', '=', $api_key)
->where('ak.user_id','=',0)
->where('ak.PK','=','bk.PK')
->pluck('ak.api_key')
where PK references to your table's primary key.
result will in your case.
select * from api_keys as ak, api_keys as bk where ak.api_key= 'api_key_value' and ak.user_id = 0 and ak.PK = bk.PK
I solved this by creating my own class and starting out with a base query which I modify to apply the join (using Laravel's joinSub function) as follows:
public function __construct()
{
$this->query = DB::table('question_responses as BASE');
}
public function applyFilter($questionId, $questionValue) {
$filterTableStr = 'filter_table_'.$questionId;
$filterIdStr = 'filter_id_'.$questionId;
$filterQuery = DB::table('question_responses AS '.$filterTableStr)
->select('survey_response_id AS '.$filterIdStr)
->where($filterTableStr.'.question_short_name', $questionId)
->where($filterTableStr.'.value', $questionValue);
$resultTableStr = 'result_table_'.$questionId;
$this->query = $this->query
->joinSub($filterQuery, $resultTableStr, function($join) use ($resultTableStr, $filterIdStr) {
$join->on('BASE.survey_response_id', '=', $resultTableStr.'.'.$filterIdStr);
});
}
After applying my required filters I can just call $this->query->get() as normal to obtain the result.
The important part was to make sure that each resulting table and join fields has unique names.
With this method I can apply unlimited filters to my base query.

Doctrine query only returning one row?

I'm new to Doctrine but somewhat familiar with SQL. I have a very simple schema with Users and Challenges. Each Challenge has a "challenger id" and a "opponent id" which are foreign keys into the User table. I want to print a list of all challenges, with the output being the names from the User table. Here is my Doctrine query;
$q = Doctrine_Query::create()
->select('u1.name challenger, u2.name opponent')
->from('Challenge c')
->leftJoin('c.Challenger u1')
->leftJoin('c.Opponent u2');
The problem is that this only returns one row. I've used the getSqlQuery() command to look at the generated SQL which ends up being:
SELECT u.name AS u__0, u2.name AS u2__1 FROM challenge c
LEFT JOIN user u ON c.challenger_id = u.id
LEFT JOIN user u2 ON c.opponent_id = u2.id
When run in a 3rd party SQL client this query retrieves all of the rows as expected. Any idea how I can get all of the rows from Doctrine? I'm using $q->execute() which I understand should work for multiple rows.
Thanks.
For me it worked by chaning the hydration mode:
$result = $query->execute(array(), Doctrine_Core::HYDRATE_SCALAR);
Set result set then returns an array instead of objects.
I just ran into this issue and in my case the problem was that my query didn't select any field from the FROM table. Example:
$query = Doctrine_Query::create()
->select(
'ghl.id as id,
ghl.patbase_id as patbase_id,
ghl.publication_no as publication_no,
ghl.priority_no as priority_no
'
)
->from('GridHitListContents ghlc')
->leftJoin('ghlc.GridHitList ghl')
As you can see there is no selected field from the GridHitListContents table.
with a $query->count() I got 2000ish results, but with $query->fetchArray() only the first one.
When I added
$query = Doctrine_Query::create()
->select(
'ghlc.id,
ghl.id as id,
...
'
)
->from('GridHitListContents ghlc')
->leftJoin('ghlc.GridHitList ghl')
I got back all my results.
$query->fetchOne() work fine for me.
Use this $result = $q->execute(array(), Doctrine_Core::HYDRATE_ARRAY)

Resources