Laravel QueryBuilder to Eloquent Relationship - laravel

Updated:
I'm almost getting my result, I'd just like to filter the "current" student I'm looking for
My updated model:
// Student.php
public function enrolled()
{
return $this->belongsToMany('App\Classroom', 'inscribed_students')
->with('enrolled_steps.student_process')
->whereRaw('start_at >= curdate()');
}
// Classroom.php
public function enrolled_steps()
{
return $this->hasMany('App\InscribedStudent');
}
// InscribedStudent.php
public function student_process()
{
return $this->hasMany('App\StudentSelectionProcess');
}
My output json:
{
"id": 1,
"name": "This is a Student",
"email": "dborer#example.org",
"enrolled": [
{
"id": 31,
"name": "This is a Classroom",
"shift": "Morning",
"enrolled_steps: [
{
"id": 1,
"student_id": 1,
"classroom_id": 1,
"student_process": [
{
"id": 1,
"status": "Approved"
}
]
},
{
"id": 2,
"student_id": 2,
"classroom_id": 1,
"student_process": [
{
"id": 2,
"status": "Approved"
}
]
},
]
}
]
}
The current problem is the enrolled_steps are return a array, but im filter one student, how can I fix it to get only my current student?
My expected output:
{
"id": 1,
"name": "This is a Student",
"email": "dborer#example.org",
"enrolled": [
{
"id": 31,
"name": "This is a Classroom",
"shift": "Morning",
"enrolled_steps: {
"id": 1,
"student_id": 1,
"classroom_id": 1,
"student_process": [
{
"id": 1,
"status": "Approved"
}
]
}
}
]
}
Question:
My problem is I have to do multiple/hard relationship to show info about student.
+--------------------+
| Student |
+--------------------+
| id |
| name |
| email |
+--------------------+
+--------------------+
| Classroom |
+--------------------+
| id |
| name |
| shift |
+--------------------+
+--------------------+
| InscribedStudent |
+--------------------+
| id |
| student_id | << Foreign key
| classroom_id | << Foreign key
+--------------------+
+--------------------+
| SelectionProcess |
+--------------------+
| id |
| classroom_id | << Foreign key
| enabled |
+--------------------+
+-------------------------+
| StudentSelectionProcess |
+-------------------------+
| id |
| inscribed_student_id | << Foreign key
| selection_process_id | << Foreign key
| status |
+-------------------------+
My QueryBuilder
$student = DB::table('students')
->join('inscribed_students', 'inscribed_students.student_id', '=', 'students.id')
->join('classrooms', 'classrooms.id', '=', 'inscribed_students.classroom_id')
->join('selection_processes', 'selection_processes.classroom_id', '=', 'classrooms.id')
// If exists show, else null
->leftjoin('student_selection_processes', 'student_selection_processes.selection_process_id', '=', 'selection_processes.id')
->select('students.*', 'classrooms.*', 'student_selection_processes.*')
->where([
['selection_processes.enabled', 1], // Very important
['students.id', $id]
])
->first();
But I consider the way very messy and still need to rearrange for the resource, so I want know if is possible to convert this Query to Eloquent relationship.
My expected eloquent json result
{
"id": 1,
"name": "This is a Student",
"email": "dborer#example.org",
"enrolled": [
{
"id": 31,
"name": "This is a Classroom",
"shift": "Morning",
"process: {
"id": 5,
"status": "Approved"
}
}
]
}
I can get at the classrooms but I don't know how can I get the process
// StudentController.php - Student controller
$student = Student::with(['enrolled'])
->find($id);
// Student.php - Student Model
public function enrolled()
{
return $this->belongsToMany('App\Classroom', 'inscribed_students');
}
If possible, if student_selection_processes is null don't show the related classroom

I am not fully understanding what you are after, however, something like this might help:
Student::with(['classroom', 'classroom.selectionProcess', 'classroom.selectionProcess.StudentSelectionProcess'])
->having(['classroom.selectionProcess.StudentSelectionProcess'])
->find($id);
Have a look at nested eager loading in Laravel Docs: https://laravel.com/docs/5.8/eloquent-relationships#eager-loading

Model Student(table name)
//relation beetween student and classroom
public function StudentClassroom(){
return $this->belongsToMany('App\Classroom','inscribed_student','student_id','classroom_id')->withTimestamps();
}
//relation beetween Student and InscribedStudent
public function enrolled(){
return $this->hasMany('App\InscribedStudent','student_id','id');
}
Model Classroom (table name)
//relation beetween classroom and student
public function classroomstudents(){
return $this->belongsToMany('App\Student','inscribed_student','classroom_id','student_id')->withTimestamps();
}
//relation beetween classroom and SelectionProcess
public function selectionclass(){
return $this->hasMany('App\SelectionProcess','classroom_id','id');
}
Model SelectionProcess(table name)
//relation beetween SelectionProcess and InscribedStudent
public function SelectionProcesInscribedStudent(){
return $this->belongsToMany('App\InscribedStudent','student_selection_process ','inscribed_student_id','selection_process_id')->withTimestamps();
}
Model InscribedStudent(table name)
//relation beetween InscribedStudent and SelectionProcess
public function InscribedStudentSelectionProces(){
return $this->belongsToMany('App\SelectionProcess','student_selection_process ','selection_process_id','inscribed_student_id')->withTimestamps();
}
//relation beetween InscribedStudent and Student
public function student()
{
return $this->belongsTo('App\Student','student_id','id');
}
//relation beetween InscribedStudent and classroom
public function classroom()
{
return $this->belongsTo('App\classroom','classroom_id','id');
}
Model StudentSelectionProcess(table name)
//relation beetween StudentSelectionProcess and InscribedStudent
public function inscribed_student()
{
return $this->belongsTo('App\InscribedStudent','inscribed_student_id','id');
}
//relation beetween StudentSelectionProcess and SelectionProcess
public function selection_process()
{
return $this->belongsTo('App\SelectionProcess','selection_process_id','id');
}
Now do in your controller
Student::with(['StudentClassroom','enrolled.InscribedStudentSelectionProces']);

Take a look on this : https://laravel.com/docs/5.8/eloquent-relationships
It's how I would do this. Rename the relationship to the one you will create in your model.
$student = Student::with(['manyToMany_classrooms_students.hasMany_selectionProcess' => function($query1) use($id) {
$query1->where('enabled', 1);
$query1->with(['hasMany_studentSelectionProcess' => function($query2) use($id) {
$query2->where('student_id', $id);
}])
}])->find($id);
I would also change your table for this
+--------------------+
| InscribedStudent |
+--------------------+
| //id | << Remove id -> you don't need this for that situation
| student_id |
| classroom_id |
+--------------------+
+-------------------------+
| StudentSelectionProcess |
+-------------------------+
| id |
| student_id | << Refer directly to your student -> InscribedStudent is
| selection_process_id | not a model, it's a pivot table
| status |
+-------------------------+
Using this, with correct relationship, you can find a user with StudentSelectionProcess. Join the SelectionProcess then the classroom.
Student -> hasMany -> StudentSelectionProcess -> belongsTo -> SelectionProcess -> belongsTo -> Classroom
Taking time to create relationships correctly will make your life easier.

Related

Laravel with() eager loading returning empty data

I have a one-to-many relationship in my model. Basically a Category and a Product. A product can only have one category but a category can have many products. The code below works:
return Category::select('id', 'name')->whereIn('id', $categories)->with('products')->get();
It returns with a product key and within that the product columns in the database, but when I use eager loading it just returns an empty set:
return Category::select('id', 'name')->whereIn('id', $categories)->with(['products' => function($query){
$query->limit(5);
}])->get();
I've also tried adding the return keyword like this return $query->limit(5); but still no luck.
I have also tried specifying columns like this:
return Category::select('id', 'name')->whereIn('id', $categories)->with('products:id,name')->get();
But it still returns an empty dataset.
Since I'm building an API, this is what the JSON data looks like:
[
{
"id": 161,
"name": "Health & Personal Care",
"products": []
},
{
"id": 256,
"name": "Makeup & Fragrances",
"products": []
},
]
My table structure:
categories (there's no product_id column, since it's one to many)
+----+------+
| id | name |
+----+------+
| | |
+----+------+
| | |
+----+------+
| | |
+----+------+
product
+----+------+-------+-------------+
| id | name | price | category_id |
+----+------+-------+-------------+
| | | | |
+----+------+-------+-------------+
| | | | |
+----+------+-------+-------------+
| | | | |
+----+------+-------+-------------+
My category model is declared like this:
public function products()
{
return $this->hasMany(Product::class);
}
and the product model is:
public function category()
{
return $this->belongsTo(Category::class);
}
you are trying to limit the loaded relation, not the query, you can do this using eloquent-eager-limit
install it:
composer require staudenmeir/eloquent-eager-limit:"^1.0"
then in Category Model:
class Category extends Model
{
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
.....
public function products()
{
return $this->hasMany(Product::class, 'product_id');
}
public function lastFiveProducts()
{
return $this->hasMany(Product::class, 'product_id')
->latest()->limit(5);
}
}
and in Product:
class Product extends Model
{
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
......
}
now this query will get the expected results:
return Category::select('id', 'name')->whereIn('id', $categories)->with(['products' => function($query){
$query->limit(5);
}])->get();
or using the new relation:
return Category::select('id', 'name')->whereIn('id', $categories)->with(['lastFiveProducts'])->get();
also note when you use ->with('products:id,name') loading a relation with specific columns, you always should load the foreing key ->with('products:id,name,category_id')

conditional relationship for "with()"

I have a table called transactions and another table called cars with the below structure:
transactions
| id | date | amount | status | user_added |
| --- | ---- | ------ | ------ | ---------- |
cars
| id | plate | specs | buy_transaction | sell_transaction |
| --- | ----- | ----- | ---------------- | ----------------- |
A car has always a buy_transaction but not always a sell_transaction, situation is that I am trying to get all transactions (that might be car-related or not car-related) and include the CAR related to that transaction weather it is sold or bought, so I need to make the relationship conditional but i couldn't achieve that.
$journal = Transaction::with(
['user'=> function($query) {
$query->select('id', 'name');
},
'income',
'outcome',
'car'
])->where('date', '>=', $fromDate)->where('date', '<=', $toDate);
This is the modal class:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Transaction extends Model
{
public function income()
{
//.....
}
public function outcome()
{
//.....
}
public function user()
{
return $this->belongsTo('App\User', 'user_added', 'id');
}
// *** problem starts here ***
public function car()
{
if (transaction status == 1) {
return $this->belongsTo('App\Car', 'id', 'sell_transaction');
}
else if (transaction status == 2) {
return $this->belongsTo('App\Car', 'id', 'buy_transaction');
}
}
}
I need to stick to that query structure because the query command is longer and I am joining and including other tables, I was hoping I could make the car() belongsTo relation conditional somehow.
I followed some similar situations like this but it didn't work for me.
Thank you.
The link you post has the answer
public function soldCar()
{
return $this->belongsTo('App\Car', 'id', 'sell_transaction');
}
public function boughtCar()
{
return $this->belongsTo('App\Car', 'id', 'buy_transaction');
}
public function scopeCar($query)
{
return $query->when($this->type === '1',function($q){
return $q->with('soldCar');
})
->when($this->type === '2',function($q){
return $q->with('boughtCar');
});
}
Edit: Also, this looks like a prime example for polymorfic relations as documented here https://laravel.com/docs/5.5/eloquent-relationships#polymorphic-relations

Advanced Filters in laravel

In my case I'm working on search training institutes based on institute name,location and course
I have 3 tables
institutes Table
id | institute_name | phone_number
----------------------------------------------
1 | vepsun | 85214542462
----------------------------------------------
2 | infocampus | 52466475544
Locations table
id | institute_id(fk) | location_name
------------------------------------------------
1 | 1 | Banglore
------------------------------------------------
2 | 1 | delhi
courses table
id | institute_id(fk) | course_name
------------------------------------------------
1 | 1 | php
------------------------------------------------
2 | 1 | delhi
I have created relations between 3 tables tables
Institute Model :
public function locations()
{
return $this->hasMany(Location::class);
}
public function courses()
{
return $this->hasMany(Course::class);
}
Course Model:
public function institute()
{
return $this->belongsTo(Institute::class);
}
Location model:
public function institute()
{
return $this->belongsTo(Institute::class);
}
So I have tried below code
public function filter(Request $request)
{
$institute = (new Institute)->newQuery();
// Search for a user based on their institute.
if ($request->has('institute_name')) {
$institute->where('institute_name', $request->input('institute_name'));
}
// Search for a user based on their course_name.
if ($request->has('course_name')) {
$institute->whereHas('courses', function ($query) use ($request) {
$query->where('courses.course_name', $request->input('course_name'));
});
}
// Search for a user based on their course_name.
if ($request->has('location_name')) {
$institute->whereHas('locations', function ($query) use ($request) {
$query->where('locations.location_name', $request->input('location_name'));
});
}
return response()->json($institute->get());
}
From above code i'm able to filter the data but it show only institution table data like below.
[
{
"id": 2,
"institute_name": "qspider",
"institute_contact_number": "9903456789",
"institute_email": "qspider#gmail.com",
"status": "1",
}
]
but what I need is when I do seach with course_name or instute_name I need to fetch data from institues table,courses and locations table. Can anyone help on this, please?
Eager load the relations in your if statements.
// Search for a user based on their course_name.
if ($request->has('course_name')) {
$institute->whereHas('courses', function ($query) use ($request) {
$query->where('courses.course_name', $request->input('course_name'));
})
->with('courses); // <<<<<< add this line
}
// Search for a user based on their course_name.
if ($request->has('location_name')) {
$institute->whereHas('locations', function ($query) use ($request) {
$query->where('locations.location_name', $request->input('location_name'));
})
->with('locations'); // <<<<<<< add this line
}
This will fetch related courses and locations as well.

advanced filters in laravel

In my case, I have to filter the data by city or institute
I have two tables like below
Institutes table
id | institute_name | phoneNumber
-------------------------------------
1 infocampus 9999999999
-------------------------------------
2 jspider 2348234982
courses table
id | institute_id | course_name
------------------------------------------
1 1 java
2 1 php
Relations that I have created
Institue model
public function courses()
{
return $this->hasMany(Course::class);
}
Course model
public function institute()
{
return $this->belongsTo(Institute::class);
}
I have tried with below code
public function filter(Request $request)
{
$institute = (new Institute)->newQuery();
// Search for a user based on their institute.
if ($request->has('institute_name')) {
$institute->where('institute_name', $request->input('institute_name'));
}
// Search for a user based on their course_name.
if ($request->has('course_name')) {
$institute->whereHas('courses', function ($query) use ($request) {
$query->where('courses.course_name', $request->input('course_name'));
});
}
return response()->json($institute->get());
}
From above i able to filter the data but it show only institution table data like below
[
{
"id": 2,
"institute_name": "qspider",
"institute_contact_number": "9903456789",
"institute_email": "qspider#gmail.com",
"status": "1",
"created_at": null,
"updated_at": null
}
]
but what I need is when I do seach with course_name or instute_name I need to fetch data from institue table as well as courses table data.
Can anyone help on this, please?
Try the following it should return the institutes with the courses.
$institute->with(['courses' => function ($query) use ($request) {
$query->where('courses.course_name', $request->input('course_name'));
}])

Laravel ordering a model belongsTo

This is my models structure:
class Family extends Model
{
public function members()
{
return $this->hasMany('App\Person');
}
}
class Person extends Model
{
public function family()
{
return $this->belongsTo('App\Family');
}
public function school()
{
return $this->belongsTo('App\School');
}
}
class School extends Model
{
public function students()
{
return $this->hasMany('App\Person');
}
}
In short, a Person belongs to many Schools, but only one Family.
Dummy data:
families_table
id | name
---------
1 | Smith
2 | Doe
people_table
id | family_id | school_id | name
----------------------------------
1 | 1 | 1 | Betty
2 | 2 | 1 | John
school_table
id | name
-------------
1 | MIT
So we have to users: Smith, Betty and Doe, John. Now, if I do this:
$school = School::find(1);
foreach ($school->students AS $student) {
echo $student->family->name . ', ' . $student->name
}
I will get:
Smith, Betty
Doe, John
What I want to see is:
Doe, John
Smith, Betty
How do I get the School and list the members sorted by the name field in from the Family table?
The easiest way is by sorting collection:
$school->students->load('family');
$school->students->sortBy(function ($item) {
return $item->family->name;
});
You can also sort results in query using join like:
$students = $school->students()
->with('family')
->select('students.*')
->join('families', 'families.id' , '=', 'students.family_id')
->orderBy('families.name')
->get();
$school->setRelation('students', $students);

Resources