Multiple Column Pivot Eloquent Relationship - php

Background: The application in question allows users to apply tags from a list of available tags. One article can have many tags and each tag may belong to many articles. The relationship between those is fine, but the complication comes in that a user should only see the tags which they have applied to the article. For instance, if Alice applies ['Apple', 'Banana', 'Cherry'] to article #1, Alice should not see Bob's article #1 tags of ['Grape', 'Orange', 'Kiwi'].
Ideal: An attach would work where the Auth'd user accesses the tags and applies it to an article by creating records in the intermediate pivot table. Additionally, if a user has applied a tag that does not exist yet, they should be able to insert new tags in the same action.
This action would be similar to how tags are applied to a StackOverflow post, actually.
The code I currently works, but just barely, so I wanted to see how others might organize the relationships between these. I'm also open to using a package if one exists that can handle this logic.
Relationships:
class User extends Authenticatable
{
public function articles()
{
return $this->hasMany('\App\Article');
}
public function articles_tags()
{
return $this->belongsToMany('\App\Article_Tag', 'article_tag_user', 'article_tag_id','user_id');
}
}
class Article extends Model
{
public function tags()
{
return $this->belongsToMany('\App\Tag', 'article_tag');
}
public function user()
{
return $this->belongsTo('\App\User', 'user_id');
}
public function article_tag_user()
{
return $this->hasManyThrough('\App\Tag', '\App\Article_Tag_User', 'article_id', 'id', 'article_id', 'tag_id');
}
}
class Tag extends Model
{
protected $fillable = [
'name'
];
public function user()
{
return $this->belongsToMany('\App\User', 'article_tag_user', 'id', 'article_tag_id');
}
public function articles()
{
return $this->belongsToMany('\App\Article', 'article_tag');
}
}
class Article_Tag extends Model
{
protected $table = 'article_tag';
public function user()
{
return $this->belongsToMany('\App\User', 'article_tag_user', 'user_id', 'article_tag_id');
}
public function tags()
{
return $this->belongsTo('\App\Tag');
}
}
class Article_Tag_User extends Model
{
protected $table = 'article_tag_user';
public function tags()
{
return $this->hasManyThrough('\App\Tag', '\App\Article_Tag');
}
}
Table Schema
Tag Table
|id|name|
Article_Tag Table
|id|article_id|tag_id|
Article_Tag_User
|id|user_id|article_tag_id|

You only need one pivot table (it also doesn't need an id):
article_tag_user: article_id | tag_id | user_id
Then you have BelongsToMany relationships between all combinations of Article, Tag, User.

Related

How to define the relationship on this scenrio?

I have a newsletter table with this structure:
newsletter table:
id, title, title_color_id, background_color_id, description
And this table will have only one record.
I have a Nova resource to allow to create this initial record:
class Newsletter extends Resource{
public function fields(Request $request)
{
return [
ID::make(__('ID'), 'id')->sortable(),
BelongsTo::make('Bg Color', 'Color', \App\Nova\Color::class),
BelongsTo::make('Text Color', 'Color', \App\Nova\Color::class),
Text::make('Title')->sortable(),
Text::make('Description')->sortable(),
];
}
}
My doub is how to proprly set the relationships on the Newsletter model, because I have two fields that should have an id of a colors table record, but I cannot of course have 2 method colors on the model with those 2 different ccolumns like below. Do you know how to handle this scenario?
The Newsletter model:
class Newsletter extends Model
{
use HasFactory;
protected $table = 'newsletter';
public function color()
{
return $this->belongsTo(Color::class, 'title_color_id');
}
public function color()
{
return $this->belongsTo(Color::class, 'background_color_id');
}
}
This code will not work, you can not have 2 different methods in same class with same name.You need to rename at least one of them.
Relations should look like:
class Newsletter extends Model
{
use HasFactory;
protected $table = 'newsletter';
public function titleColor()
{
return $this->belongsTo(Color::class, 'title_color_id');
}
public function backgroundColor()
{
return $this->belongsTo(Color::class, 'background_color_id');
}
}

Laravel Eloquent: Inverse of Many To Many (Polymorphic)

Example copied from official Laravel Docs:
For example, a Post model and Video model could share a polymorphic relation to a Tag model. Using a many-to-many polymorphic relation in this situation would allow your application to have a single table of unique tags that may be associated with posts or videos. First, let's examine the table structure required to build this relationship:
posts
id - integer
name - string
videos
id - integer
name - string
tags
id - integer
name - string
taggables
tag_id - integer
taggable_id - integer
taggable_type - string
From a tag object I wanted to get all the videos and posts, to which that subjected tag belongs (in case of morphOne an morphMany I can do that by morphTo() method)
Laravel says, I need to define both the videos and posts methods in Tag model in order to define an inverse but I want a relation like taggables which will return the respected parent (whether it's Post or Video)
Reference
I need a similar thing like imageable (but it is polymorphic one to one and I need this kind of thing in many to many)
You can just use MorphOne/MorphMany in your pivot model.
https://laravel.com/docs/8.x/eloquent-relationships#defining-custom-intermediate-table-models
class Video extends Model
{
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable')->using(Taggable::class);
}
public function taggables()
{
return $this->morphMany(Taggable::class, 'taggable');
}
}
class Post extends Model
{
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable')->using(Taggable::class);
}
public function taggables()
{
return $this->morphMany(Taggable::class, 'taggable');
}
}
class Tag extends Model
{
public function posts()
{
return $this->morphedByMany(Post::class, 'taggable')->using(Taggable::class);
}
public function videos()
{
return $this->morphedByMany(Video::class, 'taggable')->using(Taggable::class);
}
public function taggables()
{
return $this->hasMany(Taggable::class/*, 'tag_id'*/)
}
}
use Illuminate\Database\Eloquent\Relations\MorphPivot;
class Taggable extends MorphPivot
{
public $incrementing = false; // this is the default value. Change if you need to.
public $guarded = []; // this is the default value. Change if you need to.
protected $table = 'taggables';
public function taggable()
{
return $this->morphTo();
}
public function tag()
{
return $this->belongsTo(Tag::class/*, 'tag_id'*/);
}
}

No query results for model [App\Subreddit] when adding moderators to a forum category

I am trying to assign users as moderators to a forum category. At the moment, I am only trying to display route where a user can add moderators subreddit/{id}/moderators and display the subreddit name.
For that, I am getting No query results for model [App\Subreddit]
I must've messed up the relations between the tables somewhere.
My tables
#users: id, name, email...
#subreddits: id, name, user_id...
#moderators: id, user_id, subreddit_id
Routes.php
Route::get('subreddit/{id}/moderators', [
'as' => 'moderators',
'uses' => 'ModeratorsController#create'
]);
Moderator.php Model
class Moderator extends Model
{
protected $table = 'moderators';
protected $fillable = ['user_id', 'subreddit_id'];
public function subreddit() {
return $this->belongsToMany('App\Subreddit');
}
public function user() {
return $this->belongsTo('App\User');
}
}
In User Model, I have a hasManyThrough
public function moderators() {
return $this->hasManyThrough('App\Moderator', 'App\Subreddit');
}
In Subreddit Model
public function moderators() {
return $this->hasMany('App\Moderator');
}
And then in ModeratorsController I have the following create() method
public function create(Moderator $moderator, Subreddit $subreddit, User $user)
{
$subreddit = Subreddit::with('user')->findOrFail($subreddit->id);
return view('user/moderators')->with('subreddit', $subreddit)->with('user', $user)->with('moderator', $moderator);
}
If I change findOrFail to firstOrFail it will get me the first subreddit in the database, but I don't want the first, I want the exact one I'm trying to add moderators to.
class User extends Model{
public function canModerate(){ //name this as you wish
return $this->belongsToMany('App\Subreddit','moderators');
}
public function subreddits(){
return $this->hasMany('App\Subreddit');
}
}
class Subreddit extends Model{
public function moderators(){ // name this as you wish
return $this->belongsToMany('App\User','moderators');
}
public function creator(){
return $this->belongsTo('App\User');
}
}
remove id from moderators table and you wont need the Moderator class.
Additional documentation can be found here.
Edit RouteServiceProvider::boot method and add the following line:
$router->model('subreddit', 'App\Subreddit');
Documentation can be found here.
Your route should then look like:
Route::resource('subreddit.moderator','ModeratorsController');
The URL should look like http://localhost/subreddit/{subreddit}/moderator/create
And finally the controller method should be:
public function create(Subreddit $subreddit)
{
$user = $subreddit->creator;
$moderators = $subreddit->moderators()->get();
return view('user/moderators')->with(compact('subreddit','user','moderators'));
}

Eloquent has() in Many-To-Many

I'm having a simple blog system, with a posts, tags and posts_tags table. The posts and tags table both have an id and a content field, whereas the posts_tags table has the fields post_id and tag_id. My eloquent models are:
class Post extends \Illuminate\Database\Eloquent\Model
{
public function tags()
{
return $this->belongsToMany('Tag', 'posts_tags', 'post_id', 'tag_id');
}
}
and
class Tag extends \Illuminate\Database\Eloquent\Model
{
public function posts()
{
return $this->belongsToMany('Post', 'posts_tags', 'tag_id', 'post_id');
}
}
Now, as far as I understood, if I call Post::has('tags')->get() I should get the posts which have at least one tag (which all do). But all I get is an empty array (which I get when calling Tag::has('posts')->get(), too). What am I doing wrong?
Ok, found the problem in this question: Laravel Eloquent::Find() returning NULL with an existing ID
Problem was, I was using soft deletes
class Post extends \Illuminate\Database\Eloquent\Model
{
use SoftDeletingTrait;
protected $dates = ['deleted_at'];
public function tags()
{
return $this->belongsToMany('Tag', 'posts_tags', 'post_id', 'tag_id');
}
}
But my deleted_at column wasn't nullable.

Laravel selection from pivot table

I have three tables: users, items and user_items. A user has many items and a item belongs to many users.
**Users**
id
username
password
**Items**
id
name
**User_items**
id
user_id
item_id
Models:
class User extends Eloquent {
public function items()
{
return $this->belongsToMany('Item', 'user_items', 'item_id', 'user_id');
}
}
class Item extends Eloquent {
public function users()
{
return $this->belongsToMany('User', 'user_items', 'user_id', 'item_id');
}
}
I need to select all items table, print it and highlight rows which belongs to specific user id=1.
Selection highlighted output:
What is the right way to do it (in laravel style)?
You can use it like this
public function user_items()
{
return $this->belongsToMany('User', 'user_items', 'user_id', 'item_id')->withPivot('id');
}
Like this you can access values of third table.
Some useful links-
http://www.developed.be/2013/08/30/laravel-4-pivot-table-example-attach-and-detach/
http://vegibit.com/many-to-many-relationships-in-laravel/
http://laravel.com/docs/4.2/eloquent
You can do it like this way...
class User extends Eloquent {
public function items()
{
return $this->belongsToMany('Item', 'user_items', 'item_id', 'user_id')->withPivot('id');
}
}
class Item extends Eloquent {
public function users()
{
return $this->belongsToMany('User', 'user_items', 'user_id', 'item_id')->withPivot('id');
}
}
From controller..
$user_id = 2;
Item::with(['users'=>function($q) use ($user_id){$q->where('user_id',$user_id);}])->get();
In view at the time of listing a row you can highlight the row just use a condition as each item->users is blank or not.

Categories