getting joined tables id instead main table in blade - php

Do I need to specify main table's name in order to get that tables id instead joined table's id , My codes are (from controller ) :
public function RegistrationEditView(Request $r,$id){
$data=DB::table('bootcamp_users')->leftJoin('countries_detailed', function($join) {
$join->on('countries_detailed.id', '=', 'bootcamp_users.country_id');
})->where('bootcamp_users.id',$id)->first();
$course_lists=DB::table('course_languages')->get();
$bootcamps=DB::table('bootcamp_users')->get();
return view('admin.registeredBootcamp.edit',compact('data','course_lists','bootcamps'));
}

That's because the default SELECT behaviour of Laravel's query builder is SELECT * so any column sharing the same name in a joined table will overwrite the original selected table's columns: that's why you see countries_detailed.id instead of bootcamp_users.id.
I suggest specifying a custom select statement to only select needed columns from both tables, for example:
$data = DB::table('bootcamp_users')
->select(['bootcamp_users.id', /* Add all needed columns here */])
->leftJoin('countries_detailed', function($join) {
$join->on('countries_detailed.id', '=', 'bootcamp_users.country_id');
})->where('bootcamp_users.id',$id)->first();

Related

Laravel filtering - get the last created entry from the DB which has a specific ID

For this data (from a table called status_student):
I want to do some filtering. This table is a pivot table and I want to return a record from a base table only if the latest created object has a specific status_id.
For example, if I filter for status_id = 1, I shouldn't get any object for those two rows.
Here's my query for filtering:
$query = Student::whereHas('statusuri', function($q) use ($status) {
$q->orderBy('status_student.data_sfarsit', 'desc')->where('status_id', '=', $status);
});
statusuri From Student:
public function statusuri()
{
return $this->belongsToMany(Status::class, 'status_student')
->withPivot('id', 'data_inceput', 'data_sfarsit', 'document', 'status_id', 'stare_stagiu_id')
->withTimestamps();
}
My query works, but it returns an object if I filter for a status_id of 1 and of 6 too. I only want to return an object for filtering with status_id = 6 (because that is the latest status).
I tried modifying my query like this:
$query = Student::whereHas('statusuri', function($q) use ($status) {
$q->orderBy('status_student.data_sfarsit', 'desc')->first()->where('status_id', '=', $status);
});
but then it didn't work, and I think I know the reason.
first() returns an instance of another type (after an inner join, basically), which doesn't have a status_id. So I shouldn't use it this way. Also, it returns an object, not a Query Builder instance anymore.
The error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'students.id' in 'where clause' (SQL: select * from `statusuri` inner join `status_student` on `statusuri`.`id` = `status_student`.`status_id` where `students`.`id` = `status_student`.`student_id` order by `status_student`.`data_sfarsit` desc limit 1)
So, how can I make that query filter my latest created DB row with a specific status_id?
Thanks.
//Example:
For student_id = 1 as in the picture:
if status_id = 1:
query returns nothing
if status_id = 6:
query returns the student
Explanation:
$status_id is provided through a HTML form, and I want to return that specific student if the last status known is equal to this $status_id.
My approach was filtering with latest(id) so I get the latest entry on the first row, then getting the first entry, then doing a where clause on the status_id. But it doesn't work because first() returns an object, not a QueryBuilder anymore.

Laravel OrderBy by related table column

I have a table (A) that has a One to Many relation with another table (B).
I want to query Table A and eager load Table B with the Table A results - but I also want to sort Table A by a value in Table B.
I have tried using OrderBy in the query and also trying SortBy on the resultant collection but cannot get the Table A data to be sorted by the value found in Table B.
Example of what I have tried:
$query = ModelA::with("ModelB"])->get()->sortByDesc('ModelB.sortValue');
Keep in mind, I am only interested in the LATEST record from Table B. So I need to query Table A and sort by a value in the LATEST records of Table B.
How can I achieve this?
EDIT:
The below (as suggested by #ljubadr) works pretty close, but the issue is that there are many record in Table B which means that it doesn't reliably sort as it doesn't seem to sortby the latest records in Table B. Can I have the join return ONLY the latest record for each ID?
$query = ModelA::select('TableA.*')
->join('TableB', 'TableA.id', '=', 'TableB.col_id')
->groupBy('TableA.id')->orderBy('TableB.sortCol', 'desc')
->with(['x'])
->get();
EDIT 2:
#Neku80 answer has gotten me closest but it seems to not sort the column with the greatest accuracy.. I'm sorting a Decimal column and for the most part it is in order but in some places the items are out of order..
$latestTableB = ModelB::select(['TableA_id', 'sortByColumnName'], DB::raw('MAX(created_at) as created_at'))
->groupBy('TableA_id');
$query = ModelA::select('TableA.*')
->joinSub($latestTableB, 'latest_TableB', function ($join) {
$join->on('TableA.id', '=', 'latest_TableB.TableA_id');
})
->orderBy('latest_TableB.sortByColumnName')
->get();
For example, the ordering is like:
0.0437
0.0389
0.0247 <-- -1
0.025 <-- +1
0.0127
When I delete all rows except for the 'latest' rows, then it orders correctly, so it still must be ordering with old data...
I have found a solution:
ModelA::select('TableA.*', 'TableB.sortByCol as sortByCol')
->leftJoin('TableB', function ($query) {
$query->on('TableB.TableA_id', '=', 'TableA.id')
->whereRaw('TableB.id IN (select MAX(a2.id) from TableB as a2 join TableA as u2 on u2.id = a2.TableA_id group by u2.id)');
})
->orderBy('TableB.sortByCol')
->get();
Another alternative to order is like this:
$users = User::orderBy(
Company::select('name')
->whereColumn('companies.user_id', 'users.id'),
'asc'
)->get();
Here we are ordering in asc order by company name field.
In this article it is explained in detail.
You can simply execute a left join query:
ModelA::query()->leftJoin('model_b_table', 'model_a_table.primary_key', '=', 'model_b_table.foreign_key')->orderBy('model_a_table.target_column')->get();
This should work if you only need TableB's ID and created_at columns:
$latestTableB = ModelB::select('TableA_id', DB::raw('MAX(created_at) as created_at'))
->groupBy('TableA_id');
$query = ModelA::select('TableA.*')
->joinSub($latestTableB, 'latest_TableB', function ($join) {
$join->on('TableA.id', '=', 'latest_TableB.TableA_id');
})
->orderBy('latest_TableB.created_at')
->get();

laravel query builder: join dependent on primary content

I'm using luman and Database Query Builder to fetch full user info from database.
First, Please Take a lock at my database structure:
I have a table called users and a series of other tables that are related to user groups (Ex: secretaries, patients, doctors and admins) which stores additional information about the users.
Also To determine user access, I have a level column on user table which can have one of this value as enum: 'admin', 'doctor', 'secretary', 'patient'.
So, I want to get this information using one query by join and select.
My training code is something like this:
$userInfo = User::where("userID", $userID)
->limit(1)
->join('[GROUP_TABLE_NAME]', function ($join) {
$join->on('user.userID', '=', '[GROUP_TABLE_NAME]' .'.'.
'[GROUP_NAME]' . 'ID');
})
->get();
The GROUP_NAME comes from level column on user table and the GROUP_TABLE_NAME can be built based on the GROUP_NAME value(Ex: ['secretary' => 'secretaries' , 'patient' => 'patients' , ...]).
Any idea to handle the join structure using laravel query builder?
First you should be aware of the fact that this code architecture is not convenient and not easy to understand for other developers.
SQL
You can achieve your goal by using union and join.
Just convert this query for laravel builder or use it directly with DB::statement: select users.*, infos.info from users left join (( select secretaries.* from secretaries ) UNION (select doctors.* from doctors)) infos ON users.id = infos.user_id where users.id=?.
But
The easiest way to do it is to fetch info in two queries, both indexed and fast: user from users by primary key and then info by indexed field user_id in it. Create Doctorinfo, Admininfo models and correspondent migrations. So user class can be smth like this:
public function getInfo() {
switch($this->level) {
'doctor':
return $this->doctorinfo;
...
}
}
private function doctorinfo() {
$this->hasOne('App\Doctorinfo');
}
Builder
You can use left join to join all sub tables as well. The following builder selects info column.
User::where("userID", $userID)->limit(1)
->leftJoin('patients', 'users.id', '=', 'patients.user_id')
->leftJoin('doctors', 'users.id', '=', 'doctors.user_id')
->leftJoin('admins', 'users.id', '=', 'admins.user_id')
->select('users.*', DB::raw('IF(users.level="admin", admins.info, (IF users.level="doctors", doctors.info, patients.info))'))

How to get all column with some having same name when using laravel eloquent?

Let say I have 2 tables with column like this:
table_a: (with a corresponding model TableA)
id | (20+ other field) | deleted_at | created_at | updated_at
table_b: (with a corresponding model TableB)
id | a_id | ( 30+ other field ) | deleted_at | created_at | updated_at
Now I am using laravel and join on this two table
$result = TableA::join('table_b', 'table_a.id', '=', 'table_b.a_id')
->where('table_b.created_at', '>=', date('Y-m-d'))
->get();
The problem is that I am not able to some field of table_b when the column name are the same as table_a. (i.e. id, deleted_at, created_at and updated_at)
I have done some search, and from this question on stackoverflow, I may do:
$result = TableA::join('table_b', 'table_a.id', '=', 'table_b.a_id')
->where('table_b.created_at', '>=', date('Y-m-d'))
->select('table_b.id as b_id', 'table_b.created_at as b_created_at')
->get();
However, when using this method, I need to add all the column name into the select statement, and it is painful to do that on 50+ fields. It is an exporting function so all fields are necessary. Is there a way I can make the necessary rename without listing all other field that don't need a rename to retrieve all the rows? Or is there a way I can retrieve all the row without a rename?
P.S. I am using laravel 4. This is an old project and updating is even more painful than listing all fields.
Unfortunately Eloquent queries do not alias the fields from joined tables so the columns with the same name overwrite each other.
The obvious solution would be to rename the columns which is not always a viable option.
Another solution would be getting rid of joins and use whereHas method of a query builder, that allows filtering data based on data in related models. As it uses subselects instead of joins, no columns get overwritten.
All you'd need to do is defining a relation table_b in your TableA model with:
class TableA extends Model {
public function table_b() {
return $this->hasOne(TableB::class); //or hasMany, depending on your data model
}
}
and replacing:
$result = TableA::join('table_b', 'table_a.id', '=', 'table_b.a_id')
->where('table_b.created_at', '>=', date('Y-m-d'))
->get();
with
$result = TableA::whereHas('table_b', function($query) {
$query->where('created_at', '>=', date('Y-m-d');
});
That would give you all TableA records that have a related TableB record that matches given criteria.
// duplicates
$products = Product::orderBy('name')->get();
$ids = collect();
foreach ($products as $product) {
$count = Product::where('name', $product->name)->count();
if($count > 1) {
$ids->add($product->id);
}
}
$duplicates = Product::whereIn('id', $ids)->orderBy('name')->orderBy('id')->get();

Laravel 5.1 whereNotNull with join not working (returning all data)

I am trying to Select all non empty columns in 'user' table where column name is 'review'.
$applications = DB::table('users')
->join('applications', 'user.update_id', '=', 'applications.id')
->whereNotNull('users.review')
->select('user.id', 'user.rating', 'user.appid', 'user.review', 'applications.title', 'applications.price', 'applications.icon_uri')
->orderBy('user.id','asc')
->paginate(20);
$applications->setPath('');
return $applications;
But return data includes all information of both 'user.review' empty and not empty as well.
I feel there is no effect of whereNotNull() and i found no error in the statement.
I tried moving ->whereNotNull('user.review') this line top and bottom result is same.
I tried even by removing select and orderBy but returns same data.
$applications = DB::table('users')
->join('applications', 'user.update_id', '=', 'applications.id')
->whereNotNull('users.review')
->paginate(20);
$applications->setPath('');
return $applications;
Is there any way to make it work?
if your table is users you are missing an s in the table name, you should write
->whereNotNull('users.review')
same case in the join with the field update_id, otherwise you have to change the table name in the table method

Categories