I have a relationship that is almost 5 levels deep.
In my blades I use this all time without a problem. For example:
{{ $user->team->department->region->company->name ?? ''}}
How would I go about querying all users that belong to a company?
$users = User::where($user->team->department->region->company->id, '=', 1)->get(); ?????
Company Relationships:
class Company extends Model
{
public function region() {
return $this->hasMany('App\Region', 'company_id', 'id');
}
}
Region Relationships:
class Region extends Model
{
public function company() {
return $this->hasOne('App\Company', 'id', 'company_id');
}
public function department() {
return $this->hasMany('App\Department', 'region_id', 'id');
}
}
Department Relationships:
class Department extends Model
{
public function region() {
return $this->hasOne('App\Region', 'id', 'region_id');
}
public function team() {
return $this->hasMany('App\Team', 'department_id', 'id');
}
}
Team Relationships:
class Team extends Model
{
public function department() {
return $this->hasOne('App\Department', 'id', 'department_id');
}
public function user() {
return $this->hasMany('App\User', 'team_id', 'id');
}
}
User Relationships:
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function team() {
return $this->hasOne('App\Team', 'id', 'team_id');
}
}
I know there must be some easy way to query through nested relationships that Laravel provides a way for or a some facility for, but I have yet to find any decent examples from Googling around.
You can use this package for your need.
https://github.com/staudenmeir/eloquent-has-many-deep
read the instructions, depth of the relationship is not limited.
Related
User Model:
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* #var string[]
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* #var array
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
public function venues()
{
return $this->hasMany(Venue::class);
}
public function reviews()
{
return $this->hasMany(Review::class);
}
public function profile()
{
return $this->hasOne(Profile::class);
}
public function approvedVenues()
{
return $this->hasMany(Venue::class)->where('is_approved', '=', 1);
}
public function unapprovedVenues()
{
return $this->hasMany(Venue::class)->where('is_approved', false);
}
public function ownVenuesReviews()
{
return $this->reviews()->whereIn('user_id', function($query) {
$query->select('id')
->from('venues')
->whereRaw('venues.user_id = users.id');
})->get();
}
}
Venue Model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Venue extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'city_id',
'category_id',
'title',
'address',
'phone',
'email',
'website',
'facebook',
'instagram',
'content_bg',
'content_en',
'cover_image',
'lat',
'lng'
];
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function category()
{
return $this->belongsTo(Category::class, 'category_id');
}
public function city()
{
return $this->belongsTo(City::class, 'city_id');
}
public function features()
{
return $this->belongsToMany(Feature::class, 'venue_feature');
}
public function images()
{
return $this->hasMany(VenueImage::class);
}
public function reviews()
{
return $this->hasMany(Review::class);
}
}
Review Model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Review extends Model
{
use HasFactory;
protected $fillable = ['rating', 'content', 'venue_id', 'user_id'];
public function venue()
{
return $this->belongsTo(Venue::class);
}
public function images()
{
return $this->hasMany(ReviewImage::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
}
So Users have many Venues, Venues have many reviews.
I want to get reviews on own Venues for example (If I own venues with id 100, 101 - I want to get all reviews for these two venues)
Raw query is this:
SELECT * FROM `reviews` WHERE reviews.venue_id IN (SELECT venues.id FROM venues WHERE venues.user_id = 1)
What I tried in Laravel in User model (doesn't work), I'm also curious if there is a way, without raw part:
public function ownVenuesReviews()
{
return $this->reviews()->whereIn('user_id', function($query) {
$query->select('id')
->from('venues')
->whereRaw('venues.user_id = users.id');
})->get();
}
A HasManyThrough relationship should work, if I'm understanding your model relationships properly:
public function ownVenueReviews(): HasManyThrough
{
return $this->hasManyThrough(Review::class, Venue::class);
}
The raw part is only needed because you have to include the foreign key in the select portion of the sub query. Even though you may not want the user_id in the query result it must still be selected for Laravel to be able to make the relationship match work.
public function ownVenuesReviews()
{
return $this->reviews()->whereIn('user_id', function($query) {
$query->select('id', 'user_id')
->from('venues');
})->get();
}
I did it like this, but I'm not quite sure, that this is the best way, I'm open to suggestions:
public function ownVenuesReviews()
{
return Review::whereIn('venue_id', function($query) {
$query->select('id')
->from('venues')
->where('user_id', $this->id);
})->get();
}
I'm trying to display orders to sellers but also buyers can be able to see their orders. I have created all the necessary relationships and the data is on the database. I'm stuck in trying to query this data from database so i can show it. I have these tables orders table, order_product table. Here is how they look like https://imgur.com/a/Ud9e2Hh
I have tried below functions but still no luck in solving the problem.
If you need more information, just comment and i will provide.
Here are my functions
// Seller Orders
public function viewOrders(User $user)
{
// all sells
$sells = $user->allOrderFromSellers();
dd($sells);// this returns empty array
return view('orders')->with(compact('sells'));
}
//Buyer Orders
public function myOrders(User $user)
{
return view('myOrders', compact('user','orders'));
dd($orders);
}
And here are the models.
order_product.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class OrderProduct extends Model
{
protected $table = 'order_product';
protected $fillable = ['order_id', 'buyer_id', 'seller_id','product_id', 'quantity'];
public function products()
{
return $this->belongsTo('App\Products_model');
}
public function buyer()
{
return $this->belongsTo(User::class, 'id', 'buyer_id');
}
public function seller()
{
return $this->belongsTo(User::class, 'id', 'seller_id');
}
public function order()
{
return $this->belongsTo(Order::class);
}
}
and this is User.php
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password', 'Seller'
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
//public function isSeller() {
// return $this->seller;
//}
public function products()
{
return $this->hasMany(Products_model::class);
}
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
public function orders()
{
return $this->hasManyThrough(Order::class, Products_model::class, 'buyer_id', 'seller_id', 'product_id');
}
public function orderFromBuyers()
{
return $this->hasManyThrough(OrderProduct::class, Products_model::class, 'buyer_id', 'product_id');
}
public function orderFromSellers()
{
return $this->hasManyThrough(OrderProduct::class, Products_model::class, 'seller_id', 'product_id');
}
public function allOrderFromBuyers()
{
return $this->hasMany(OrderProduct::class, 'buyer_id');
}
public function allOrderFromSellers()
{
return $this->hasMany(OrderProduct::class, 'seller_id');
}
}
the above functions give me this collection as result:
"Collection {#281 ▼
#items: []
}"
please help me on this, i have spent so much time but nothing is working.
The relationship is not correct
You need to use many to many and has many through relations, as order has many products and product has many orders ....
https://laravel.com/docs/5.8/eloquent-relationships#many-to-many
https://laravel.com/docs/5.8/eloquent-relationships#has-many-through
Don't create model and relations for pivot table like OrderProduct
That is not correct
In this case order_product is a pivot table
So this is a correct Models
For Order model
public function products()
{
return $this->belongsToMany('App\Product', 'order_product');
}
For Product model
public function orders()
{
return $this->belongsToMany('App\Order', 'order_product');
}
After that you can handle rest of things
$order = Order::with('products')->findOrFail($id);
$products = $order->products
In my laravel project, I have tables that i want to insert a many to many relationship between 2 of them. I want to bind an User(that must be a cleaner kind) to one House of many from current Host user authenticated. To do so, I'm implementing the following function in Controller:
public function hireCleanerToHouse (Request $request)
{
$house_id = $request->houseAssign;
$email = $request->email;
$house = House::find($house_id);
$cleanerUser = User::where('email', $email)->first();
if ($cleanerUser && $house){
$cleanerUser->houses()->attach($house);
}
return response()->json('success', 200);
}
May I am missing a detail of logic that cant let me insert any data. Im pretty new using laravel and the Eloquent ORM.
to help understand better, here are the Models from project. The functions that take care of a separates tables (CRUD) are all working fine.
If there are some other tip to improve legibity or if I'm ignoring some best pratice, I will gladly accept it.
User:
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password'
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function host()
{
return $this->hasOne(Host::class);
}
public function cleaner()
{
return $this->hasOne(Cleaner::class);
}
}
House:
class House extends Model
{
protected $fillable = ['name', 'address', 'host_id'];
protected $dates = ['created_at', 'updated_at'];
protected $appends = ['next_cleaning'];
public function host()
{
return $this->belongsTo(Host::class);
}
public function cleaners()
{
return $this->belongsToMany(
Cleaner::class,
'cleaners_houses',
'house_id',
'cleaner_id'
);
}
public function cleanings()
{
return $this->hasMany(CleaningProject::class);
}
public function getNextCleaningAttribute()
{
return $this->cleanings()->orderBy('created_at', 'desc')->first();
}
}
Cleaner:
class Cleaner extends Model
{
protected $dates = ['created_at', 'updated_at'];
public function houses()
{
return $this->belongsToMany(
House::class,
'cleaners_houses',
'cleaner_id',
'house_id'
);
}
public function hosts()
{
return $this->belongsToMany(
Host::class,
'cleaners_hosts',
'cleaner_id',
'host_id'
);
}
public function user()
{
return $this->belongsTo(User::class);
}
public function cleanings()
{
return $this->hasMany(CleaningProject::class);
}
public function getNameAttribute()
{
return $this->user->name;
}
}
Host
class Host extends Model
{
protected $dates = ['created_at', 'updated_at'];
protected $appends = ['name'];
public function houses()
{
return $this->hasMany(House::class);
}
public function cleaners()
{
return $this->belongsToMany(
Cleaner::class,
'cleaners_hosts',
'host_id',
'cleaner_id'
);
}
public function user()
{
return $this->belongsTo(User::class);
}
public function getNameAttribute()
{
return $this->user->name;
}
}
And also the migration that bind many Cleaners to many House is already created:
Migration
class CreateCleanersHousesTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('cleaners_houses', function (Blueprint $table) {
$table->increments('id');
$table->integer('cleaner_id')->references('id')->on('cleaners');
$table->integer('house_id')->references('id')->on('houses');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('cleaners_houses');
}
}
here's the solution that I found:
public function hireCleanerToHouse (Request $request)
{
$email = $request->email;
$houseId = $request->idHouse;
$idUserEmail = User::where('email', $email)->first();
$cleaner = Cleaner::where('user_id', $idUserEmail->id)->first();
$house = House::find($houseId);
$cleaner->houses()->attach($house->id);
return response()->json([$cleaner, $house], 200);
}
As you may see the problemn was because the model Cleaner only contains 'id' and 'user_id', so i had to get first the user.id and find the cleaner where user_id = user.id.
Also I don't passed the $house->id in the attach() to match the relationship. Its now working fine. hope it helps someone else.
I'm getting problems calling relationships in laravel, I have 4 tables: users, tickets, stores, statuses. and I have a #OneToMany relationship in Users to Tickets (1 user have many tickets). the other relationships are #OneToOne (1 ticket have 1 store and status).
Now, I have 6 tickets in my table and 4 users, I can print the relationship to the 4 first tickets, but when I want to call the ticket 5 or 6, the relationship dissapears.
The same thing with store and status, I can print the relationship while the id of the ticket is not greater than the number of items I have in my table.
these are my models:
User Model:
class User extends Authenticatable
{
use Notifiable, HasRoles;
protected $fillable = [
'id','name', 'email', 'password',
];
protected $hidden = [
'password', 'remember_token',
];
public function tickets()
{
return $this->hasMany(Ticket::class, 'usuario_id');
}
}
Ticket Model:
class Ticket extends Model
{
protected $fillable = [
'comentarios', 'falla', 'modelo', 'no_serie', 'monto',
'usuario_id', 'status_id', 'store_id'
];
public function user()
{
return $this->belongsTo(User::class, 'id');
}
public function store()
{
return $this->hasOne(Store::class, 'id');
}
public function status()
{
return $this->hasOne(Status::class, 'id');
}
}
Store Model:
class Store extends Model
{
protected $table = "stores";
protected $primaryKey = 'id';
public $timestamps = false;
protected $fillable = [
'sucursal', 'dirección'
];
public function ticket()
{
return $this->belongsTo(Ticket::class, 'store_id');
}
}
Status Model:
class Status extends Model
{
public $timestamps = false;
protected $fillable = [
'status'
];
public function ticket()
{
return $this->belongsTo(Ticket::class, 'status_id');
}
}
Controller:
$tickets = Ticket::all();
return view('Admin.index', compact('tickets'));
View
#foreach($tickets as $ticket)
<li>{{ $ticket->user }}</li>
#endforeach
result:
Result of the foreach
In the view, I'm calling all the tickets ($tickets), and for each ticket, I print the relationship (user function). But in the result, you can see that just print first 4 relationships, (the number of my users), if I add 1 user, the relationship in the 5th ticket appears.
The Same problem with the other tables (store and status).
Replace user() relationship in Ticket moedel to:
public function user()
{
return $this->belongsTo(User::class, 'usuario_id', 'id');
}
in Ticket model you must change function to
public function user()
{
return $this->belongsTo(User::class, 'usuario_id', 'id');
}
or just public function user()
{
return $this->belongsTo(User::class);
}
Summary
I am receiving the following error when trying to call the relationship:
Object of class Illuminate\Database\Eloquent\Relations\BelongsToMany
could not be converted to string
My setup is very basic, and consists of two models, User and Role.
User Model [User.php]
<?php
use Illuminate\Auth\UserInterface;
class User extends Eloquent implements UserInterface {
protected $table = 'users';
protected $hidden = array('password');
protected $fillable = array('id', 'username', 'password');
public function getAuthIdentifier() {
return $this->getKey();
}
public function getAuthPassword() {
return $this->password;
}
}
Role Model [Role.php]
<?php
class Role extends Eloquent {
protected $table = "roles";
protected $fillable = array(
'id',
'code',
'name'
);
public function foo() {
return $this->belongsToMany('User', 'map_role_user', 'role_id', 'user_id');
}
}
And finally I'm calling the method foo in the routes file, example:
Route::get('role', function() {
return Role::find(1)->foo();
});
From
https://laravel.com/docs/5.3/eloquent-relationships or https://laravel.com/docs/4.2/eloquent#relationships
If a collection is cast to a string, it will be returned as JSON:
<?php
$roles = (string) User::find(1)->roles;
If you dont want to add further constraints to the query then you have to use dynamic properties concept. So,
$user = App\User::find(1);
foreach ($user->posts as $post) {
//
}
If you want to add more constraints then do this
App\User::find(1)->posts()->where('title', 'LIKE', '%Best%')->get()