Laravel ordering a model belongsTo - laravel

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

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

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

Relation between 2 tables with a link table

I have this tables :
topics
-------
id | title
------+----------
1 | Sport
2 | Social
posts_topics
------------
id | post_id | topic_id
------+--------------+------------
1 | 1 | 1
2 | 1 | 2
posts
------
id | title
-----+----------
1 | A Test Post
I store topics in topics table and use posts_topics to link between my posts table and topics
now I want select title of topics when selecting posts,
After some searching in StackOverflow and Google, I've written this models:
Posts.php
public function post_topics()
{
return $this->hasMany('App\PostTopics');
}
PostTopics.php
public function topics()
{
return $this->hasManyThrough('App\Posts', 'App\Topics', 'id', 'topic_id');
}
Topics.php
protected $table = 'topics';
and in my controller I trying to fetch:
$post = Posts::with('post_topics')->find($post_id);
dd($post);
Now this code will work but cannot return title of topics.
Change the code to a Many to Many relationship in Posts.php:
public function post_topics()
{
return $this->belongsToMany('App\Topics', 'posts_topics', 'post_id', 'topic_id');
}
And then call it:
$post = Posts::with('post_topics')->find($post_id);
Try this and check if it works for you.

Resources