I would like to query pivot model relation using Eloquent.
I've my User model :
class User extends Authenticatable
{
public function preferences(): BelongsToMany
{
return $this->belongsToMany(Preference::class, 'user_preference')
->using(UserNotificationPreference::class) //Custom Pivot model
->withPivot([enabled, channel_id]);
}
}
Here is the custom pivot model :
class UserNotificationPreference extends Pivot
{
/**
* The attributes that should be casted to native types.
*
* #var array
*/
protected $casts = [
'enabled' => 'boolean'
];
/**
* Channel relation.
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function channel(): BelongsTo
{
return $this->belongsTo(Channel::class);
}
}
And the preference model :
class Preference extends Model
{
// protected $connection = "apodis";
/**
* The users that belong to the preference.
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function users(): BelongsToMany
{
return $this->belongsToMany(Preference::class, 'user_channel_notification_preference')
->using(UserNotificationPreference::class) //custom pivot
->withPivot(['preference_id', 'user_id', 'enabled', 'channel_id']);
}
}
From a User model, i would like to retrieve Preferences after querying custom pivot table relationship (Channel::class) ,
something like :
$user->preferences()
->wherePivot('enabled', true)
->whereHasPivot('channel', function(Builder $query) {
//doesn't exists
})->get()
There is a way to achieve this ?
(Products Model Laravel) public function products() {
return $this->belongsToMany('App\Product');
} (Shop Model Laravel)public function shops(){ return $this->belongsToMany('App\Shop');} you can specify the actual field names of that pivot table public function products(){ return $this->belongsToMany('App\Product','products_shops', 'shops_id', 'products_id');}possibility to get those values in our loops foreach ($shop-products as $product){echo $product->pivot->price;}
Related
In my Laravel project I'm having some trouble getting my hasManyThrough relationship to work, these are my models:
Pingtree
BuyerTier
PingtreeEntry
I want to get all of my BuyerTier models through my PingtreeEntry model.
This is my current relationship on my Pingtree model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Pingtree extends Model
{
use HasFactory, SoftDeletes;
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'pingtrees';
/**
* The attributes that should be cast.
*
* #var array<string, string>
*/
protected $casts = [
'is_enabled' => 'boolean',
];
/**
* The accessors to append to the model's array form.
*
* #var array
*/
protected $appends = [
'is_deleting',
];
/**
* Determine if we're editing the model
*
* #return bool
*/
public function getIsDeletingAttribute()
{
return false;
}
/**
* Get the company that owns the model.
*/
public function tiers()
{
return $this->hasManyThrough(
BuyerTier::class, // final model we want to access
PingtreeEntry::class, // intermediate model
'buyer_tier_id', // foreign key on intermediate model
'id', // foreign key on final model
'id', // local key
'pingtree_id' // local key on intermediate model
)->orderBy('processing_order', 'asc');
}
/**
* Get the pingtree entry model
*/
public function pingtree_entry()
{
return $this->belongsTo(PingtreeEntry::class, 'id', 'pingtree_id');
}
/**
* Get the company that owns the model.
*/
public function company()
{
return $this->belongsTo(Company::class);
}
/**
* Get the user that owns the model.
*/
public function user()
{
return $this->belongsTo(User::class);
}
}
And then I query this in my controller:
$pingtree = Pingtree::where('company_id', $company_id)
->where('id', $id)
->with([
'tiers.buyer',
'tiers.pingtree_entry'
])
->first();
This is what my pingtree_entries table looks like:
Right now, for some reason, despite having multiple tiers on my pingtree ID 3, I'm only ever getting 1 result back in my query, and I should be seeing all 4 tiers on my pingtree, what am I missing?
I'm developing a role and permissions based on laravel framework.
I have 3 models :
Weblog
User
Permissions
This is pivot table
user_id , weblog_id , permission_id
Now, a user can have a weblog with permission id 1,2 and another weblog with permission 1,2,3,4
How can I deploy relationships? and how can I check user permissions when managing a weblog. (middleware and ...)
With the fact that Permission are specific to Weblog
Say the pivot table is called permission_user_weblog
class User extends Model
{
public function weblogs()
{
return $this->belongsToMany(Weblog::class, 'permission_user_weblog');
}
public function permissionsFor(int $weblogId)
{
$permissionIds = null;
$this->weblogs()
->where('id', $weblogId)
->with('permissions')
->get()
->each(function($weblog) use(&$permissionIds) {
$permissionIds = $weblog->permissions->pluck('id');
});
return $permissionIds;
}
}
class Weblog extends Model
{
public function users()
{
return $this->belongsToMany(User::class, 'permission_user_weblog');
}
public function permissions()
{
return $this->belongsToMany(Permission::class, 'permission_user_weblog');
}
}
class Permission extends Model
{
public function weblogs()
{
return $this->belongsToMany(Weblog::class, 'permission_user_weblog');
}
}
Then you can check anywhere for whether logged in user has specific permission for a specific weblog
public function update(Request $request, $weblogId)
{
$user = auth()->user();
$permissions = $user->permissionsFor($weblogId);
//Check whether the logged in user has permission identified by id 1 or 4 for weblog
$can = !! $permissions->intersect([1,4])->count();
//Do rest of processing
}
your Weblog,User,Permission has ManyToMany Relation, its a kind of odd but if you want to have this kind of relation its not a problem.
just consider each pair a ManyToMany. and every one of those can have a hasMany to Pivot (i named it Access) too (based on your needs).
User model:
class User extends Model{
/**
* retrive weblogs
*
* #return BelongsToMany weblogs
*/
public function weblogs()
{
return $this->belongsToMany(App\WebLog::class,'accesses_table')
->withPivot("permission_id")
->using(App\Access::class);
}
/**
* retrive permissions
*
* #return BelongsToMany permissions
*/
public function permissions()
{
return $this->belongsToMany(App\Permission::class,'accesses_table')
->withPivot("weblog_id")
->using(App\Access::class);
}
/**
* retrive access
*
* #return hasMany [description]
*/
public function accesses()
{
return $this->hasMany(App\Access::class, "user_id");
}
}
Weblog model:
class Weblog extends Model{
/**
* retrive users
*
* #return BelongsToMany users
*/
public function users()
{
return $this->belongsToMany(App\User::class,'accesses_table')
->withPivot("permission_id")
->using(App\Access::class);
}
/**
* retrive permissions
*
* #return BelongsToMany permissions
*/
public function permissions()
{
return $this->belongsToMany(App\Permission::class,'accesses_table')
->withPivot("user_id")
->using(App\Access::class);
}
/**
* retrive access
*
* #return hasMany [description]
*/
public function accesses()
{
return $this->hasMany(App\Access::class, "weblog_id");
}
}
Permission model:
class Permission extends Model{
/**
* retrieve users
*
* #return BelongsToMany users
*/
public function users()
{
return $this->belongsToMany(App\User::class,'accesses_table')
->withPivot("weblog_id")
->using(App\Access::class);
}
/**
* retrieve weblogs
*
* #return BelongsToMany weblogs
*/
public function weblogs()
{
return $this->belongsToMany(App\Weblog::class,'accesses_table')
->withPivot("user_id")
->using(App\Access::class);
}
/**
* retrive access
*
* #return hasMany [description]
*/
public function accesses()
{
return $this->hasMany(App\Access::class, "permission_id");
}
}
and you can have a model for your pivot, which i named it Access :
Illuminate\Database\Eloquent\Relations\Pivot;
class Access extends Pivot
{
public $incrementing = true;
public function user()
{
return $this->belongsTo(App\User::class);
}
public function weblog()
{
return $this->belongsTo(App\Weblog::class);
}
public function permission()
{
return $this->belongsTo(App\Permission::class);
}
}
I'm trying to figure out how to implement a many to many relationship with a custom intermediate model (pivot table). This is my model:
banners
- id
- title
- description
banner_regions (Pivot)
- id
- region_id
- banner_id
- active
regions
- id
- name
- slug
Eloquent Models code:
class Banner extends Model
{
/**
* Get all of the regions for the banner.
*/
public function regions()
{
return $this->belongsToMany('App\Region', 'banner_regions')
->withPivot('active')
->using(BannerRegion::class);
}
}
class BannerRegion extends Model
{
}
class Region extends Model
{
/**
* Get all of the banners for the region.
*/
public function banners()
{
return $this->belongsToMany('App\Banner', 'banner_regions')
->withPivot('active')
->using(BannerRegion::class);
}
}
Banner Controller code:
class BannerController extends Controller
{
protected $model;
public function __construct(Banner $model)
{
$this->model = $model;
}
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$region = $request->region; // region model instance
// ??
}
}
So, my question here is how to retrieve the banners for a particular region?
SOLUTION
I've changed my code and now it works as expected.
I changed BannerRegion pivot model to be Pivot instead of Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class BannerRegion extends Pivot
{
// It is just for casting int values into boolean values.
// Useful for JSON responses.
protected $casts = [
'active' => 'boolean',
'for_customers' => 'boolean',
];
}
Banner Model. Nothing to add here, but I've made some changes in order to improve the JSON response, like $appends.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Banner extends Model
{
protected $hidden = ['id', 'regions'];
// Add data from the pivot model
protected $appends = ['for_customers'];
public function getForCustomersAttribute()
{
// Get the attribute 'for_customers' from the pivot model
return $this->regions
->keyBy('pivot.banner_id')
->get($this->id)
->pivot
->for_customers;
}
/**
* Get all of the regions for the banner.
*
*/
public function regions()
{
return $this->belongsToMany('App\Region', 'banner_regions')
->withPivot('for_customers', 'active')
->using('App\BannerRegion');
}
/**
* Scope a query to only include active banners for a specific region.
*
* #param \Illuminate\Database\Eloquent\Builder $query
* #param App\Region $region
* #return \Illuminate\Database\Eloquent\Builder
*/
public function scopeFindbyRegionAndActive($query, Region $region)
{
return $query->whereHas('regions', function($query) use ($region) {
return $query->whereRegionId($region->id)->whereActive(true);
});
}
}
In my Banner controller, I just added:
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Region $region)
{
return $this->model->findbyRegionAndActive($region)->get();
}
Region param is resolved by Dependency Injection (laravel.com/docs/5.4/routing#route-model-binding).
Finally, my route:
Route::group(['prefix' => '/regions/{region}'], function()
{
// Banners
Route::resource('banners', 'BannerController', ['only' => 'index']);
});
The endpoint:
/regions/my-region/banners
The JSON response:
[
{
"title": "a title...",
"description": "a descritpion...",
"link": "http://localhost",
"for_customers": true
}
]
I have a test site where I've got a recipes, ingredients, and ingredient_uses tables. Each recipe "uses" ingredients in different quantities and prepared differently (eg. chopped, sliced, minced, grated), so the ingredient_uses table tracks the recipe_id and the ingredient_id with the other variable info.
The relationships look like this:
Recipe model:
public function ingredients()
{
return $this->hasManyThrough('App\Ingredient', 'App\IngredientUse');
}
public function ingredientUses()
{
return $this->hasMany('App\IngredientUse');
}
Ingredient_Use model:
public function ingredient()
{
return $this->hasOne('App\Ingredient');
}
public function recipe()
{
return $this->belongsTo('App\Recipe');
}
Ingredient model:
public function recipe()
{
return $this->belongsToMany('App\Ingredient');
}
I thought I wanted a hasManyThrough relationship from the recipe to the ingredient table, using the ingredient_uses as the middle table. But the sql is wrong:
SELECT `ingredients`.*, `ingredient_uses`.`recipe_id`
FROM `ingredients`
INNER JOIN `ingredient_uses`
ON `ingredient_uses`.`id` = `ingredients`.`ingredient_use_id`
WHERE `ingredient_uses`.`recipe_id` = 1
This is what I think I want:
SELECT `ingredients`.*, `ingredient_uses`.`recipe_id`
FROM `ingredients`
INNER JOIN `ingredient_uses`
ON `ingredient_uses`.`ingredient_id` = `ingredients`.`id`
WHERE `ingredient_uses`.`recipe_id` = 1
Is there a more appropriate relationship that I should be using?
First thing - you don't need Ingredient_Use model. Model examples:
class Ingredient extends Model
{
/**
* #var string
*/
protected $table = 'ingredients';
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function recipes()
{
return $this->belongsToMany(Recipe::class, 'recipes_ingredients', 'ingredient_id');
}
}
class Recipe extends Model
{
/**
* #var string
*/
protected $table = 'recipes';
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function ingredients()
{
return $this->belongsToMany(Ingredient::class, 'recipes_ingredients', 'recipe_id')->withPivot('additional', 'fields');
}
}
Access to additional fields example
$recipe->pivot->additional;
in Ingredient model
class Ingredient extends Model
{
public function recipe(){
return $this->belongsToMany(Recipe::class, 'ingredient_uses', 'ingredient_id', 'recipe_id');
}
}
other models will be empty thats mean there would not be any function inside Recipe model and Ingredient model .
u can see documentation about this
https://laravel.com/docs/5.3/eloquent-relationships#many-to-many
I have my database (=model) structure like that:
game:
lot (typeof Lot)
places (array type of Place)
place_id // just a number of a lot in some game
user_id
What should I do to call in everywhere like this:
User::find(1)->games() // returns Game collection where user has places
?
Models are:
class Place extends Model
{
protected $fillable = ['place_id', 'user_id', 'game_id'];
public function user() {
return $this->belongsTo(User::class);
}
public function game() {
return $this->belongsTo(Game::class);
}
}
User:
class User extends Model implements AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['name', 'email', 'steam_id', 'avatar'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = ['remember_token'];
/**
* Get all of the tasks for the user.
*/
public function items()
{
return $this->hasMany(SteamItem::class);
}
public function places() {
return $this->hasMany(Place::class);
}
}
The Game:
class Game extends Model
{
protected $fillable = ['lot_id'];
public function lot() {
return $this->belongsTo(Lot::class);
}
public function places() {
return $this->hasMany(Place::class);
}
}
Now I use this code in my User class:
public function games() {
return Game::with(['places' => function ($query) {
$query->where('user_id', $this->id);
}]);;
}
It doesn't work, because I need to make it as a relationship method, but with method returns a query builder.
In the finals I must call $user->games and it should return me all the games user linked to through place.
Okay. I think I understand now.
User has many Place. Place belongs to User.
Place belongs to Game. Game has many Place.
You can try this:
$user = User::with('places.game.lot')->find(1);
This will fetch the User and eager load all the relationships. Because Place belongsTo a Game, which in turn belongs to Lot, you can then do this:
#foreach ($user->places as $place)
<img src="{{$place->game->lot->imageUrl}}" />
#endforeach
Also, place is actually a pivot table, and you can take advantage of Eloquent's many-to-many relationship, which I would recommend reading about.