Laravel with() eager loading returning empty data - laravel

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

Related

Laravel QueryBuilder to Eloquent Relationship

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.

How to query a table based on criteria of another table in Laravel?

In my laravel project I have an inventory and a medicine table which the formats are as the following:
Inventory Table
id | medicine_id | expiry_date
-------|-----------------|-------------
1 | 1 | 13-11-2021
2 | 2 | 01-01-2020
3 | 2 | 23-05-2024
Medicine Table
id | name | category
-------|-----------------|-------------
1 | Hemophine | Syringe
2 | andrenalin | Tablet
3 | aspirin | Capsule
The models are set as below:
Inventory Model
class Inventory extends Model
{
public $incrementing = false;
public function medicine(){
return $this->belongsTo('App\Medicine', 'medicine_id');
}
}
Medicine Model
class Medicine extends Model
{
public $incrementing = false;
public function inventory(){
return $this->hasMany('App\Inventory', 'medicine_id');
}
}
For retrieving all the inventories with a specific category such as Syringe, I tried eager loading, but couldn't figure out the flaws. Here is what I did to get all the inventories with a Syringe category, but it didn't work.
public function index()
{
$medicines = Inventory::where('medicine_id'->category, "Syringe")->get();
foreach($medicines as $medicine){
echo $medicine->medicine->name ." ".$medicine->expiry_date;
echo "<br>";
}
}
Now, Is there anyway to get all the inventory items based on their categories they are in? in this case in "Syringe" category?
Your syntax is a bit wrong, in order to load the medicine for each inventory you need to use with and the where function is where('column', value)
So change it to this:
$medicines = Inventory::with([
'medicine' => function($query) {
$query->where('category', "Syringe");
}
])->get();
or even better the other way around:
$medicines = Medicine::with('inventory')->where('category', 'Syringe')->get();

How to combine models and order by model two data in Laravel 5.4

I would like to be able to output leaders in a section by their position in a group. I'm thinking of combining these tables through a pivot table ordered by position from the groups table.
These are the three class I have relationships established
class Leader extends Model
{
public function groups()
{
return $this->belongsToMany('App\Group');
}
}
class Group extends Model
{
public function leaders()
{
return $this->belongsToMany('App\Leader');
}
}
class GroupLeader extends Model
{
// Maybe some function here?
}
I would like to be able to get the groups in the controller and pass them individually kinda like this.
$group1 = Leader::where('group', '=', 'group1_name')->orderBy('position', 'asc');
$group2 = Leader::where('group', '=', 'group2_name')->orderBy('position', 'asc');
return view('page.leadership')->with([
'group1' => $group1,
'group2' => $group2,
]);
Then I would pass the variables to include files which would loop through them in order.
Here is my table structure
leaders
--------
| id
| first_name
| last_name
| deceased
----------
groups
----------
| id
| group
| title
| position
-----------
group_leader
-------------
| id
| group_id
| leader_id
------------
Try it
$groups = Leader::whereIn('group', ['group1_name', 'group2_name'])
->orderBy('position', 'asc')
->groupBy('group', 'id')
->get(['group', '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.

Resources