I am new to laravel, can someone explain to me the parameters of morphMany:
$this->morphMany(Photo::class, 'imageable');
The MorphMany relationship has the following function signature:
public function morphMany($related, $name, $type = null, $id = null, $localKey = null)
{
//
}
Where:
$related (required): refers to the related model. e.g: User::class.
$name (required): the name of the polymorphic relation, like commentable.
$type (optional): customize the {relation}_type field to look up when doing a query.
$id (optional): customize the {relation}_id field to look up when doing a query.
$localKey (optional): customize the local key (by default id) to search when doing a query.
So -using the example shown in the Laravel documentation- if you want to use a different table structure for the comments table from this:
posts
id - integer
title - string
body - text
videos
id - integer
title - string
url - string
comments
id - integer
body - text
commentable_id - integer
commentable_type - string
to this:
posts
id - integer
title - string
body - text
videos
id - integer
title - string
url - string
comments
id - integer
body - text
foo - integer // the index to look
bar - string // the type to match
You'd need to define your relationships like this:
Post.php
public function comments()
{
return $this->morphMany(Comment::class, 'commentable', 'foo', 'bar');
}
Video.php
public function comments()
{
return $this->morphMany(Comment::class, 'commentable', 'foo', 'bar');
}
Comment.php
public function commentable()
{
return $this->morphTo('commentable');
}
Check this other answer.
Polymorphic Relations
Table Structure
Polymorphic relations allow a model to belong to more than one other model on a single association. For example, imagine users of your application can "comment" on both posts and videos. Using polymorphic relationships, you can use a single comments table for both of these scenarios. First, let's examine the table structure required to build this relationship:
posts
id - integer
title - string
body - text
videos
id - integer
title - string
url - string
comments
id - integer
body - text
commentable_id - integer
commentable_type - string
Two important columns to note are the commentable_id and commentable_type columns on the comments table. The commentable_id column will contain the ID value of the post or video, while the commentable_type column will contain the class name of the owning model. The commentable_type column is how the ORM determines which "type" of owning model to return when accessing the commentable relation.
Model Structure
Next, let's examine the model definitions needed to build this relationship:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
/**
* Get all of the owning commentable models.
*/
public function commentable()
{
return $this->morphTo();
}
}
class Post extends Model
{
/**
* Get all of the post's comments.
*/
public function comments()
{
return $this->morphMany('App\Comment', 'commentable');
}
}
class Video extends Model
{
/**
* Get all of the video's comments.
*/
public function comments()
{
return $this->morphMany('App\Comment', 'commentable');
}
}
Retrieving Polymorphic Relations
Once your database table and models are defined, you may access the relationships via your models. For example, to access all of the comments for a post, we can use the comments dynamic property:
$post = App\Post::find(1);
foreach ($post->comments as $comment) {
//
}
You may also retrieve the owner of a polymorphic relation from the polymorphic model by accessing the name of the method that performs the call to morphTo. In our case, that is the commentable method on the Comment model. So, we will access that method as a dynamic property:
$comment = App\Comment::find(1);
$commentable = $comment->commentable;
The commentable relation on the Comment model will return either a Post or Video instance, depending on which type of model owns the comment.
See this link: polymorphic-relations:
You can entry like that:
+---------+----------------+-------------------+
| user_id | commentable_id | commentable_type |
+---------+----------------+-------------------+
| 1 | 1 | App\Post |
| 1 | 2 | App\Post |
| 1 | 3 | App\Post |
| 1 | 1 | App\Video |
| 1 | 2 | App\Video |
| 1 | 3 | App\Video |
+---------+----------------+-------------------+
Related
Consider the following scenario:
There are couple of entities in my Laravel application like the following:
Post
Page
Image
Video
All the above entities can have CustomFieldValues, which is another entity. The structure of the custom_field_values table is as follows:
ID
entity_id
custom_field_definition_id
value
[Timestamp fields]
All the CustomFieldValues belong to a single CustomFieldDefinition entity. Its table custom_field_definitions looks like following:
ID
parent_entity_name
definition_name
[Timestamp fields]
Following are some sample data from the custom_field_definitions table:
| ID | parent_entity_name | definition_name |
|----|--------------------|-------------------|
| 1 | Post | AuthorTwitterUrl |
| 2 | Page | SeoTitle |
| 3 | Image | OriginalSourceUrl |
| 4 | Video | MpaaRating |
As you can see, CustomFieldDefinitions are definitions of extra data, that we can store about each type of entity.
Following are some sampel data from the custom_field_values table:
| ID | entity_id | custom_field_definition_id | value |
|----|-----------|----------------------------|-----------------------------------|
| 1 | 1 | 1 | https://twitter.com/StackOverflow |
| 2 | 1 | 2 | My Page's SEO Title |
| 3 | 1 | 3 | http://example.com/image.jpg |
| 4 | 1 | 4 | G – General Audiences |
A little description about the data contained in the custom_field_values table:
CustomFieldValue:1: The value for CustomFieldDefinition:1 and its entity 1 (Post:1, in this case, because CustomFieldDefinition:1 is related to Post.) is "https://twitter.com/StackOverflow".
CustomFieldValue:2: The value for CustomFieldDefinition:2 and its entity 1 (Page:1, in this case, because CustomFieldDefinition:2 is related to Page.) is "My Page's SEO Title".
CustomFieldValue:3: The value for CustomFieldDefinition:3 and its entity 1 (Image:1, in this case, because CustomFieldDefinition:3 is related to Image.) is "http://example.com/image.jpg".
CustomFieldValue:4: The value for CustomFieldDefinition:4 and its entity 1 (Video:1, in this case, because CustomFieldDefinition:4 is related to Video.) is "G – General Audiences".
custom_field_values table's entity_id can refer to any entity class, therefore it is not a foreign key in the DB level. Only in combination with custom_field_definition_id we can find to which entity it actually refers to.
Now, all is well and good, until I need to add a relationship called customFieldDefinitions to any of the entities (Say Post.).
class Post extends Model {
public function customFieldDefinitions(){
$this -> hasMany ('CustomFieldDefinition');
}
}
The above does not work, because the datapoint that indicates the CustomFieldDefinition's relationship is not a foreign key field in the custom_field_definitions table, named post_id. We have to somehow build the relationship based on the fact that some records in the custom_field_definitions table has "Post" as the value of the field parent_entity_name.
CustomFieldDefinition::where('parent_entity_name', '=', 'Post');
The above snippet fetches the CustomFieldDefinitions that are related to the Post, however, it is not possible to do something like the following with the relationship:
class Post extends Model {
public function customFieldDefinitions(){
$this
-> hasMany ('CustomFieldDefinition')
-> where ('parent_entity_name', '=', 'Post')
;
}
}
The where constraint works. But Laravel also injects the ID of the current Post object into the set of constraints.
So, what I want to do is, not consider the current object's ID at all, and build a "Class Leavel Relationship", and not an "Object Level Relationship".
Is this possible under Laravel?
There might be a workaround but I'm not pretty sure about it.
What you could try doing is to define a mutated attribute and set it as the local key of the relationship:
class Post extends Model
{
public function getEntityNameAttribute()
{
return 'Post';
}
public function customFieldDefinitions()
{
return $this->hasMany(
'CustomFieldDefinition',
'parent_entity_name',
'entity_name'
);
}
}
You could also go further and define a trait which could be used by all your models which have customFieldDefinitions. It could look like:
trait HasCustomFieldDefinitionsTrait
{
public function getEntityNameAttribute()
{
return (new ReflectionClass($this))->getShortName();
}
public function customFieldDefinitions()
{
return $this->hasMany(
'CustomFieldDefinition',
'parent_entity_name',
'entity_name'
);
}
}
Then you can use it wherever needed:
class Post extends Model
{
use HasCustomFieldDefinitionsTrait;
}
class Video extends Model
{
use HasCustomFieldDefinitionsTrait;
}
class Page extends Model
{
use HasCustomFieldDefinitionsTrait;
}
class Image extends Model
{
use HasCustomFieldDefinitionsTrait;
}
Instead of hasMany(), you can create One To Many (Polymorphic) relationship between Post, Page, Image, Video and CustomFieldDefinition.
More about polymorphic relationships here.
Laravel 5.7. I have an Eloquent model Theme, with two fields, id and name.
Another model, Filter, has three fields: id, name, theme_id.
Filter:
class Filter extends Model
{
public function theme()
{
return $this->belongsTo('App\Theme');
}
}
I can attach these filters to an arbitrary number of other models, e.g. the model Foo has two fields, id and name. I attach the filter to it using a Filterables table:
Filterables:
id | filter_id | filterable_id | filterable_type
------------------------------------------------
1 | 1 | 3 | App\Foo
The above example shows that the Filter of id 1 is attached to the Foo of id 3.
Now to get all the filters of Foo, I have this relationship in my Foo model:
Foo:
class Foo extends Model
{
public function filters()
{
return $this->morphToMany('App\Filter', 'filterable');
}
}
This all works fine. But now I want to get the Foo model, and only the filters where the filter's theme_id is (for example) 1.
How can I add this condition to the filters relationship?
Just add a where clause directly to your relationship
class Foo extends Model
{
public function filters()
{
return $this->morphToMany('App\Filter', 'filterable')->where('theme_id', 1);
}
}
How to make a relationship between 3 tables of Laravel
+---------------+ +---------------+ +-------------------+
| venue_images | | transactions | | image_transaction |
+---------------+ +---------------+ +-------------------+
| id | | id | | venue_image_id |
| user_id | | user_id | | transaction_id |
+---------------+ +---------------+ +-------------------+
Here's my code but it's not working.
class Transaction extends Model
{
public function images(){
return $this->hasManyThrough(ImageTransaction::class ,VenueImage::class, 'transaction_id', 'id');
}
}
I want to join the three tables using transaction id and venue image id
on my controller
$transactions = transaction::where('user_id', Auth::id())->with('images')->get();
If I don't misunderstood your question you have a pivot table image_transaction which is many-to-many relationship. Eloquent automatically arrange pivot table's name alphabetically like transactions_venue_image so you need to pass your custom name image_transaction in the parameter. [docs]
In your VenueImage Model
public function transactions()
{
return $this->belongsToMany('App\Transaction','image_transaction');
}
In your Transaction Model
public function venue_images()
{
return $this->belongsToMany('App\VenueImage','image_transaction');
}
There are also 3rd and 4th parameters which are foreign keys of the related model which are explained better in the docs.
In your Controller
$transactions = transaction::where('user_id', Auth::id())->with('venue_images')->get();
The order of the parameters to the hasManyThrough function looks wrong. According to the Laravel documentation
The first argument passed to the hasManyThrough method is the name of the final model we wish to access, while the second argument is the name of the intermediate model.
So in your case it should be like this (without defining the foreign keys)
class Transaction extends Model
{
public function images() {
return $this->hasManyThrough(VenueImage::class, ImageTransaction::class);
}
}
If this does not work try to include the keys as described in the documentation:
Typical Eloquent foreign key conventions will be used when performing the relationship's queries. If you would like to customize the keys of the relationship, you may pass them as the third and fourth arguments to the hasManyThrough method. The third argument is the name of the foreign key on the intermediate model. The fourth argument is the name of the foreign key on the final model. The fifth argument is the local key, while the sixth argument is the local key of the intermediate model.
You should try this:
class Transaction extends Model
{
public function images(){
return $this->belongsToMany(ImageTransaction::class ,VenueImage::class, 'transaction_id', 'id');
}
}
I have a table as follows in my database:
table area_blocks;
id
owner_type
owner_id
created_at
updated_at
in the owner_type field it has the Eloquent Model name and the owner_id is the id of that model in the database. Example:
db: area_blocks
id | owner_type | owner_id
1 | App\Models\Title | 3
2 | App\Models\Title | 4
3 | App\Models\Textarea | 1
So I'm expecting when I fetch all of these to also eager load the relevant field from the eloquent model stored in owner_type.
Is there an eloquent relationship that can bring back that record from the owner_type field using eager loading? I've tried $this->morphTo(), e.g.
public function block()
{
return $this->morphTo();
}
but that is returned as null. Any ideas how this can be done?
Example code;
<?php
namespace App\Models;
use Cviebrock\EloquentSluggable\Sluggable;
use Illuminate\Database\Eloquent\Model;
class AreaBlocks extends Model
{
protected $with = ['block'];
public function block()
{
return $this->morphTo();
}
}
Route::get('/', function(){
return App\Models\AreaBlocks::all();
});
You have to specify the column name/prefix:
public function block()
{
return $this->morphTo('owner');
}
Or rename the relationship:
public function owner()
{
return $this->morphTo();
}
In Controller, I would like to pass the only one variable with specifies column from parent in it. Now,I'm using
View::make('store.product')->with('products', Product::find($id)
->join('design','product.design_id','=','design.id')
->join('user','design.user_id','=','user.id')
->select('user.name','design.title','product.price')
->get();
My question is
1.Is there a better way to do this by using Belongsto?
2.If can do, Is it work the same with Hasmany?
This is my table structure.
User
id | name
1 | 'John'
Design
id | user_id | title
1 | 1 | 'Chill'
2 | 1 | 'Mad'
Product
id | design_id | price
1 | 1 | 20
2 | 1 | 30
And Model be like this
Product belongsto Design ,
Design belongsto User
Add a method to your Users like so for your designs that the user has;
public function designs(){
$this->hasMany('Design');
}
For the designs model add the following methods;
public function user(){
$this->belongsTo('User');
}
public function products(){
$this->hasMany('Product');
}
For your products model
public function design(){
$this->belongsTo('Design');
}
These will set up the relationship allowing you to eager load the data on your models.
This can be done like so;
$variable = Product::with('designs')->find(1); //This will load the product with the related designs
If you want all the designs and the users that belong to the designs do the following;
$variable = Product::with('designs', 'design.user')->find(1); //This will load the designs that relate to the Product and include the user that that design belongs to on the Design model.
To access the properties use the following;
$variable->designs //This will return a collection of all the designs.
$variable->designs->first()->user //This will return the user model that the design belongs to.
An example of displaying the information;
#foreach ($variable->designs as $design)
{{ $design->user->username }}
#endforeach
Please note: i have not tested this code.