Laravel 8 hasManyThrough not returning data - laravel

I have 3 tables that are all Eloquent Models. I'm attempting to use a hasManyThrough, or a pivot, or such to get a relationship from origin table to the last table. I have a hasManyThrough setup now, but Im not getting any results. The query from a dd() looks to be correct as far as the query is built-out, but I don't see any data. There are no errors being thrown.
Tables and ids:
users:
id
apps:
id
user_id
app_id
appversions:
id
app_id
user_id
A user can have many apps. An app can have many versions. I would like to get all versions for a particular user from all of their apps they own. The only one difference is the versions table is called 'appversions' and the model is called version.
I am setting the table name as such on the version model.
class Version extends Model
{
use HasFactory;
protected $table = 'appversions';
}
On my User model, I am attempting this hasManyThrough
public function versions()
{
return $this->hasManyThrough(
'App\Models\Version',
'App\Models\App',
'user_id',
'app_id',
'id',
'app_id'
);
}
And then calling it from my controller like so:
$userVersions = $user->version();

I figured it out and seeing it now.
I changed this:
$userVersions = $user->version();
To this:
$userVersions = $user->version;

Related

Laravel eloquent for four tables

I'm new to Laravel. I am developing a project. and in this project I have 4 tables related to each other
-Users
-Orders
-OrderParcels
-Situations
When listing the parcels of an order, I want to get the information of that order only once, the user information of that order once again, and list the parcels as a table under it. so far everything ok. but I also want to display the status of the parcels listed in the table as names. I couldn't add the 4th table to the query. do you have a suggestion? I'm putting pictures that explain the structure below.
My current working code is
$orderParcels = Orders::whereId($id)
->with('parcels')
->with('users:id,name')
->first();
and my 'orders' model has method
public function parcels(){
return $this->hasMany(OrderParcels::class);
}
public function users(){
return $this->hasOne(User::class,'id','affixer_id');
}
Note[edit]: I already know how to connect like this
$orderParcels = DB::table('order_parcels as op')
->leftjoin('orders as o','op.orders_id','o.id')
->leftjoin('users as u','o.affixer_id','u.id')
->leftjoin('situations as s','op.status','s.id')
->select('op.*','o.*','u.name','s.situations_name')
->where('op.orders_id',$id)->get();
but this is not working for me, for each parcels record it returns me orders and user info. I want once orders info and once user info.
Laravel provides an elegant way to manage relations between models. In your situation, the first step is to create all relations described in your schema :
1. Model Order
class User extends Model {
public function parcels()
{
return $this->hasMany(OrderParcels::class);
}
public function users()
{
return $this->hasOne(User::class,'id','affixer_id');
}
}
2. Model Parcel
class Parcel extends Model {
public function situations()
{
return $this->hasOne(Situation::class, ...);
}
}
Then, you can retrieve all desired informations simply like this :
// Retrieve all users of an order
$users = $order->users; // You get a Collection of User instances
// Retrieve all parcels of an order
$parcels = $order->parcels; // You get a Collection of User instances
// Retrieve the situation for a parcel
$situations = $parcel->situations // You get Situation instance
How it works ?
When you add a relation on your model, you can retrieve the result of this relation by using the property with the same name of the method. Laravel will automatically provide you those properties ! (e.g: parcels() method in your Order Model will generate $order->parcels property.
To finish, in this situation where you have nested relations (as describe in your schema), you should use with() method of your model to eager load all the nested relation of order model like this :
$orders = Orders::with(['users', 'parcels', 'parcels.situations'])->find($id)
I encourage you to read those stubs of Laravel documentation :
Define model relations
Eager loading
Laravel Collection
Good luck !
Use join to make a perfect relations between tables.
$output = Orders::join('users', 'users.id', '=', 'orders.user_id')
->join('order_parcels', 'order_parcels.id', '=', 'orders.parcel_id')
->join('situations', 'situation.id', '=', 'order_parcels.situation_id')
->select([
'orders.id AS order_id',
'users.id AS user_id',
'order.parcels.id AS parcel_id',
'and so on'
])
->where('some row', '=', 'some row or variable')->get();

Invalid argument supplied for foreach() in laravel when use Eloquent: Relationships

I wanna get the class name of a user via the user ID. When I input the ID of a user so I will wanna get the class name. I have three tables such as users table, classes table, and class_users table. The class_users table is born from two users table and classes table.
A users table has an id, name, email, password.
A classes table has an id, class_code, class_name.
A class_users table has an id, class_id, user_id
And this problem relates to Eloquent Relationships.
Thank you for help.
My Route:
Route::get('/find_classes/{id}','StudentController#find_classes');
My Controller function:
public function find_classes($id)
{
$users = User::find($id);
foreach($users->classes as $class)
{
echo $class->name . '<br';
dd($class);
}
}
My User Model:
public function classes()
{
return $this->belongsTo('App\Classes','class_users','user_id','class_id');
}
Looks like you might have the wrong relationship set up on your User model. You have a one to many set up, but your DB is setup to handle a many to many. I suggest you change your User model relationship:
public function classes()
{
return $this->belongsToMany('App\Classes');
}
Note, you may need to name the FK on that relation, as I see you have class_id on the table, but your actual class is named 'Classes'. Check through your relationships to ensure this is explicit on the FK where it doesn't follow Laravel convention exactly.
With this relationship, your foreach loop should work. It would be a good idea for efficiency, as mare96 noted, to eager load the classes on the $users collection when you query:
$users = User::with('classes')->find($id);

Eloquent hasManyThrough also get middle table information

I have same table structure as mentioned in laravel document for HasManyThrough relationship hasManyThrough
countries
id - integer
name - string
users
id - integer
country_id - integer
name - string
posts
id - integer
user_id - integer
title - string
and define a relationship like same as in doc.
public function posts()
{
return $this->hasManyThrough(
'App\Post', 'App\User',
'country_id', 'user_id', 'id'
);
}
Now when I List posts of specific country. I need the information of user of the post too. I mean information from pivot table(users)
$posts = Country::find(2)->posts();
The above returns post data only..
What you need is to eager load the users alongside the posts, can be achieved via the with() method on the Builder:
$posts = Country::find(2)->posts()->with('user')->get();
If you're loading huge amounts of data and don't want the whole User instance loaded, you can even specify which fields to only be retrieved from the users table:
$posts = Country::find(2)->posts()->with('user:id,name')->get();
Then you can simply use $post->user->name or whatever you need when iterating your collection.
Refer to the documentation for more information.
Try this one:
$posts = Country::find(2)->posts()->with('user')->get();

Complicated Eloquent relationship using `Model::query`

I have a complicated relationship I'm trying to establish between two models.
The goal is to use $supplier->supply_orders to access the orders where the user supplies an item.
This throws: LogicException: Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation.
With the code I've got I can use $supplier->supply_orders()->get(), however, when I try to use it as a relationship it throws. Since this is a relationship I should be able to wrap it in a relationship, but how would I go about doing that?
Supplier Model:
class Supplier extends Model {
public function supply_orders() {
return Order::query()
->select('order.*')
->join('item_order', 'order.id', '=', 'item_order.order_id')
->join('item', 'item_order.item_id', '=', 'item.id')
->where('item.supplier_id', '=', $this->id);
}
}
~~~ A whole lot of back info that I don't think you need but might ~~~
sql tables:
supplier
- id
items:
- id
- supplier_id
item_order:
- id
- order_id
- item_id
orders:
- id
The other Eloquent Models:
class Item extends Model {
public function orders() {
return $this->belongsToMany('Order');
}
}
class Order extends Model {}
Example of how this should work:
$supplier = factory(Supplier::class)->create();
$item = factory(Item::class)->create([
'supplier_id' => $supplier->id,
]);
$order = factory(Order::class)->create();
$order->items()->attach($item);
$orders = $supplier->supply_orders // Throws LogicException
This throws: LogicException: Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
Sounds like a hasManyThrough with a many to many relationship. Laravel has no inbuilt support for this but you can always go ahead and write your own relationship like this: https://laravel.io/forum/03-04-2014-hasmanythrough-with-many-to-many
If you dont want relationships you can always do something like:
Order::whereHas('items.supplier', function($query) use($supplier) {
$query->where('id', $supplier->id);
});
For this to work, you need to have a relationship function items in your Order model and a relationship function supplier in your item model
I believe the reason it throws a relationship error is that you haven't created an Eloquent relation for
$supplier->supply_orders.
Instead, Laravel looks at your supply_orders() as a method in the class, and thus can't figure out which table to use as the pivot. To get the base relationship to work within Eloquent, you'd need to create a new pivot table for the relationship between suppliers and orders something like:
suppliers
-id
orders
-id
order_supplier
-id
-order_id
-supplier_id
From here, Laravel will accept a simple many to many relationship between the two (this would not cause a failure):
Supplier Class:
/**
* Get all orders associated with this supplier via order_supplier table
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function orders(){
return $this->belongsToMany("\App\Order");
}
Now that the relationship is solid both between the suppliers and orders, as well as the orders and items, you can eager load the relationship in all directions. Where it gets complicated for your particular need with the current DB setup is that you have a 3rd parameter from the items table that is not a direct pivot. Without having to re-structure the DB, I think the easiest would be to load your suppliers and the relationships like normal:
$suppliers = Supplier::with('orders', function($query) {
$query->with('items');
});
From here you've got all the relationships loaded and can draw down the ones with the right item->ids in a follow-up to the $suppliers collection. There are quite a few ways to skin the cat (even including all in one query) now that you have the Eloquent relationship... but I tend to keep it a little more simple by breaking it into a few readable bits.
Hope this helps.

Laravel orm Order by custom attribute

In my user model I have added custom attribute get number of post user have created.
I can successfully get that but when I have try to order by it give error.
1054 Unknown column 'posts' in 'order clause'
Can you let us know what is the Eloquent query you're running? Also make sure that the name of the table you're using is correct.
EDIT:
Does the posts column exist in the users table? Or are you trying to get all the posts from the Post Model and order the posts by their name in descending order?
If that is what you want to do then you would need to go to your User Model and set up your relationship with posts.
In User Model create this method:
public function posts() {
return $this->hasMany('Post'); // Post would be the name of the Posts Model
}
In Post model do this:
public function user() {
return $this->belongsTo('User'); // Where User is the name of the Users Model
}
Then according to the Laravel documentation you could do someonething like this with the query:
$users = User::with(array('posts' => function($query)
{
$query->orderBy('name', 'desc'); // assuming that 'name' would be the column in the posts table you want to sort by
}))->take(5)->get();
I hope I am correct and this helps and that solves your issue.

Resources