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
}
]
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 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;}
a beginner question here, how can i get the building_name column on my buildings table which has a relationship with information, i wanted to access it inside the show function and display in on the show.views? And i also wanted to know how can i call it inside the show view Please help.
<?php
namespace App\Http\Controllers;
use App\Information;
use App\Building;
use Illuminate\Http\Request;
use Session;
class InformationController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
$buildings = new Building();
$buildings::all();
return view('create', compact('buildings'));
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->validate($request, array(
'building_information' => 'required',
'building_id' => 'required'
));
//store in the db
$information = new Information;
$information->building_information = $request->building_information;
$information->building_id = $request->building_id;
$information->save();
Session::flash('success', 'The information was successfully saved!');
//redirect to other page
return redirect()->route('information.show', $information->id);
}
/**
* Display the specified resource.
*
* #param \App\Information $information
* #return \Illuminate\Http\Response
*/
public function show($id)
{
$information = Information::find($id);
return view('show')->with('information', $infomation);
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Building extends Model
{
public function information()
{
return $this->hasMany('App\Information');
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Information extends Model
{
public function building()
{
return $this->belongsTo('App\Building');
}
}
Load the relationship too when getting the Information object:
$information = Information::with('building')->find($id);
If there are multiple buildings associated, you can loop through them in your blade view:
#foreach( $information->building as $building)
<li>{{ $building->building_name }}</li>
#endforeach
Just add relationship and pass it to the view
public function show($id)
{
$information = Information::with('building')->findOrFail($id);
return view('show')->with('information', $infomation);
}
In your view:
{{$information->building->building_name}}
I have two models as Project and Collaborator this is My tables
Project column
id
project_name
project_note
user_id
Collaborator Model
id
project_id
collaborator_id
I need join this models to get project_name instead project_id of Collaborator Model.
how can I do this. I read www.laravel.com documents but difficult to understand. help me in code...
project Model
<?php
namespace App;
use Auth;
use Illuminate\Database\Eloquent\Model;
class Project extends Model
{
/*
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['project_name', 'project_notes', 'project_status', 'due_date'];
public function scopePersonal($query)
{
return $query->where('user_id', Auth::user()->id);
}
//
}
collaborator Model
<?php
namespace App;
use Auth;
use Illuminate\Database\Eloquent\Model;
class Collaboration extends Model
{
protected $table = 'project_collaborator';
/*
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['project_id', 'collaborator_id'];
/*
* Get the user that is a collaborator on another project
* #return collection
*/
public function user()
{
return $this->belongsTo(User::class, 'collaborator_id');
}
/*
* Query scope to return information about the current project
* #param $query
* #param int $id
* #return query
*/
public function scopeProject($query, $id)
{
return $query->where('project_id', $id);
}
public function scopeColabo($query)
{
return $query->where('collaborator_id',Auth::user()->id);
}
}
Based on your comment, you need to add a relationship hasOne to your collaboration model:
class Collaboration extends Model
{
.....
public function project()
{
return $this->hasOne('App\Project');
}
.....
}
The method project() will define your relationship with your project model. And then you'll be able to get the collaboration project name like this:
$collaborations = Collaboration::with('project')->get();
foreach ( $collaborations as $collaboration ) {
echo $collaboration->project->project_name;
}
You can read more in the documentation.
In your Collaboration class add the followoing relation:
public function project()
{
return $this->belongsTo('App\Project');
}
And in your User class define a relation as:
public function collaborations()
{
return $this->hasMany('App\Collaboration', 'collaborator_id');
}
Then you can get all the collaborations of logged in user by:
$collaborations = auth()->user()->collaborations()->with('project')->get()
or
To get all collaborations you can so as:
$collaborations = Collaboration::with('project')->get();
In your view file:
#foreach ($collaborations as $collaboration)
{{ $collaboration->project->project_name }}
#endforeach
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.