Laravel Eloquent: Inverse of Many To Many (Polymorphic) - php

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'*/);
}
}

Related

Is there a way to get around using custom route key to load eloquent relationships in laravel

I have two models Product and Images. I changed the route key name on the product model to use the slug field and i'm now unable to load the hasMany relationship with the Image Model
Here is the Product Model
class Product extends Model
{
protected array $with = ['images'];
public function getKeyName()
{
return 'slug';
}
protected array $guarded = [];
public function images() : HasMany
{
return $this->hasMany(Image::class, 'product_id');
}
}
and the Image model
class Image extends Model
{
protected array $guarded = [];
public function image() : BelongsTo
{
return $this->belongsTo(Product::class);
}
}
so when I try
Product::first()->images
it just returns an empty collection
but without overriding the getKeyName() method, everything works fine
getKeyName() will get the primary key for the model. it supports to return id, after you change it to slug, it will return slug
And hasManyHere's the source code ;
The third parameter LocalKey will use getKeyName() when it's empty.
If you still want to use hasMany, you need to pass the third parameter like this:
public function images()
{
return $this->hasMany(Image::class, 'product_id', 'id');
}
This will convert the Eloquent query to database query, which will take the right local key products.id.

Is it possible to relate a table on a pivot one with eloquent ORM?

These are my tables many-to-many:
products and suppliers, however I need to relate the pivot(product_supplier) to a table called payment_supplier.
Product model
public function suppliers(){
return $this->belongsToMany('App\Supplier');
}
Supplier model
public function products(){
return $this->belongsToMany('App\Product');
}
but I need to relate pivot product_supplier to payment_supplier table just like described on the diagram
In this case, you could use a pivot model.
# Product Model
public function suppliers() {
return $this->belongsToMany(Supplier::class)->using(ProductSupplier::class);
}
# Supplier Model
public function products(){
return $this->belongsToMany(Product::class)->using(ProductSupplier::class);
}
# ProductSupplier Pivot Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class ProductSupplier extends Pivot
{
public function payment_supplier()
{
return $this->hasMany(PaymentSupplier::class);
}
}
However, doing it like this has a big problem: You CANNOT eager load a pivot's relationships. Not without an override (or a package).
The other way to go about it is using hasManyThrough
# Product Model
public function suppliers()
{
return $this->belongsToMany(Supplier::class)->using(ProductSupplier::class);
}
public function payment_suppliers()
{
return $this->hasManyThrough(PaymentSupplier::class, ProductSupplier::class);
}
# Supplier Model
public function products()
{
return $this->belongsToMany(Product::class)->using(ProductSupplier::class);
}
public function payment_suppliers()
{
return $this->hasManyThrough(PaymentSupplier::class, ProductSupplier::class);
}
This gives you every PaymentSupplier for a single Supplier/Product, so you'll need to apply some kind of filtering.

How To Get Article's Users and Comments With Eloquent

There are three database tables users, articles and a joining table article_users_comments, which holds the comment, the user id commented the article and the commented article id.
I can achieve the following thing with pure SQL join, but I want to do it with Eloquent, I thought that it would be quite easy, but I am kind of confused right now.
I have been trying different things, but it still doesn't work.
// User
class User extends Authenticatable implements MustVerifyEmail,CanResetPassword{
public function comments()
{
return $this->hasMany('App\ArticleComments');
}
}
// Article
class Article extends Model{
public function getArticles(){
$articles = Article::paginate(3);
return $articles;
}
public function getSingleArticle($title){
$article = Article::where('title','=',$title)->get();
return $article;
}
public function articleComments()
{
return $this->hasMany('App\ArticleComments');
}
}
// ArticleComments
class ArticleComments extends Model{
protected $table = 'article_users_comments';
public $timestamps = false;
public function article()
{
return $this->belongsTo('App\Article');
}
public function user()
{
$this->belongsTo('App\User');
}
}
// ArticleController(showing only the show method), which passes the data to the certain view
instantiating the Article Model
class ArticleController extends Controller{
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show($title)
{
$removeDashesFromUrl = str_replace('-',' ',$title);
$am = new Article();
$data = $am->getSingleArticle($removeDashesFromUrl);
return view('article',['article'=>$data]);
}
}
I want to get the comments and the users(which have commented the article) for a certain certain article.
You should set the foreign key in your articleComments and article relations:
Eloquent determines the default foreign key name by examining the name of the relationship method and suffixing the method name with _id. However, you may pass a custom key name as the second argument to the belongsTo method:
Article Model
public function articleComments()
{
return $this->hasMany('App\ArticleComments','commented_article_id');
}
ArticleComments Model
public function article()
{
return $this->belongsTo('App\Article','commented_article_id');
}
You can get the comments from a article using the relation:
$article = Article::find($id);
$article->articleComments; // This will return all comments for the given article
You could use a foreach loop and access each attribute from each comment:
foreach($article->articleComments as $comment)
{
echo $comment->id;
echo $comment->user->id;
echo $comment->user->username;
.
.
.
}
You can access the user and any of his attributes just calling the relation in your comment like i did above.
For more info: click here.
Note: i strongly recommend you changing your model name to Comment, we don't use model names in the plural, always in singular.

Multiple Column Pivot Eloquent Relationship

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.

`Trying to get property of non-object` using `hasManyThrough` relationship laravel 5

I am trying to develop a blog using Laravel 5 in which i have to show comment along with user on post.
Here is my database table schema.
User
id
name
Posts
id
post_content
user_id
Comments
id
comment
user_id
post_id
Here is my User Model
public function posts()
{
return $this->hasMany('App\Models\Posts');
}
public function comments(){
return $this->hasManyThrough('App\Models\Comments','App\Models\Posts');
}
Here is my Posts Model
public function user()
{
return $this->belongsTo('App\Models\User');
}
public function comments(){
return $this->hasMany('Ap\Models\Comments');
}
Here is my Comment Model
public function posts()
{
return $this->belongsTo('App\Models\Posts');
}
public function user(){
return $this->posts->name;
}
Here is my code how i am accessing user name
$comments = Comments::find(1);
$comment['comment'] = $comments->comment;
$comment['user_name'] = $comments->name;
$comment['post_id'] = $comments->posts->id;
may be i am not getting in right direction? if i am doing right then why it is not working.
In laravel 5 you do not call the model as you are doing. since the models are stored in the app folder just call like. Plus I think you need to define the relationship
class User extends Model {
public function phone()
{
return $this->hasOne('App\Phone');
}
}
class Phone extends Model {
public function user()
{
return $this->belongsTo('App\User');
}
}
$phone = Phone::find(1);
For the case of foreign keys and more regarding the Eloquent relationships in laravel 5 just follow the documentation on the laravel website. Make sure to look at dynamic properties of that are allowed by eloquent
http://laravel.com/docs/5.0/eloquent

Categories