I have some table like the follows:
id
name
parent_id
...
The parent_id is the id from the same table itself as it is something simple. For example:
id: 1
name: abc
parent_id: 0
id: 2
name: abc_parent
parent_id: 1
id: 3
name: another
parent_id: 1
How can I get the "name" inside some function in that model? Something like...
public function parentName()
{
$pid = this->parent_id;
/// return self::find($pid)->name; ### this does not actually work
}
Should I create a separate function and pass the parent_id to it? or is there any simpler way?
In your model, define a parent relationship.
class Model extends Eloquent {
public function parent() {
return $this->hasOne('Model', 'parent_id');
}
}
This will allow you to use the relationship like $item->parent->name.
If you want something like $item->parentName, you can make an attribute accessor as an alias to the relationship.
public function getParentNameAttribute() {
return $this->parent->name;
}
You'd then be able to do $item->parentName.
Related
I have setup a User model to be able to be linked with a polymorphic many to many to 3 models, let's call them A, B and C.
I have setup the models as follows:
// User.php
public function a(): MorphToMany
{
return $this->morphedByMany(A::class, 'shareable')->withPivot('view_mode');
}
public function b(): MorphToMany
{
return $this->morphedByMany(B::class, 'shareable')->withPivot('view_mode');
}
public function c(): MorphToMany
{
return $this->morphedByMany(C::class, 'shareable')->withPivot('view_mode');
}
// A, B and C.php
public function users(): MorphToMany
{
return $this->morphToMany(User::class, 'shareable');
}
Of course I created a pivot table to store the relations.
I am now trying to find a way to find ALL relations linked to a given user based on the pivot table. Let's say this is currently my data:
user_id
view_mode
shareable_type
shareable_id
1
RW
App\Models\A
1
1
R
App\Models\A
2
1
W
App\Models\B
5
2
R
App\Models\A
2
1
W
App\Models\C
8
How can I retrieve all the related content for User with id 1?
My goal would be for it to look like this:
relations: [
a: [
- A entity with id 1
- A entity with id 2
],
b: [
- B entity with id 5
],
c: [
- C entity with id 8
]
]
I tried by created a model named Shareable, and adding the following:
protected $table = 'shareables';
public function shareable()
{
return $this->morphTo();
}
public function a()
{
return $this->morphedByMany(A::class, 'shareable');
}
I then added the following to the User model:
public function shared()
{
return $this->hasMany(Shareable::class)->with('a');
}
But when I call it by doing
$user->shared
I don't get my a instances, only an empty collection.
What am I doing wrong?
I have three tables,
1. user (id, name)
2. state (id, stateName, code)
3. user_relations(id, user_id, state_id)
3 models,
1. User
2. State
3. UserRelation
By logic, User has one UserRelation and UserRelation has one State
I can get user state by
User::first()->relation->state
What I want is call this by User::with('state')->first().
If you would like to keep the current db schema like it is, you may use Laravel accessors
Accessors and mutators allow you to format Eloquent attribute values
when you retrieve or set them on model instances.
class User
public function getStateAttribute($value) {
return $this->relation->state;
}
Usage
User::findOrFail($id)->state;
Edit
User Model
class User extends Authenticatable
{
//...
public function state() {
return $this->relation()->with('state');
}
public function relation() {
return $this->hasOne('App\UserRelation', 'user_id');
}
}
UserRelation Model
class UserRelation extends Model
{
protected $table = 'user_relations';
public function state() {
return $this->belongsTo('App\State'); // must be belongsTo instead of hasOne
}
}
State Model
class State extends Model
{
protected $table = 'states';
}
Controller
App\User::with('state')->find(1);
{
"id": 1,
"name": "Test",
"email": "test#test.com",
"email_verified_at": "2020-02-13 00:00:00",
"created_at": "2020-02-20 00:00:00",
"updated_at": null,
"state": {
"id": 2,
"state_id": 2,
"user_id": 1,
"state": {
"id": 2,
"stateName": "state 2",
"code": "code 2"
}
}
}
If user can only have one state,
add a state_id in the user table and get rid of the third table user_relations
class User extends Model
{
public function state()
{
return $this->hasOne('App\State');
}
}
class State extends Model
{
public function user()
{
return $this->belongsTo('App\User');
}
}
In this way, you can
$state = User::find(1)->state;
$user = State::find(1)->user;
First way:
You have to define state_id in users table for one to one relation.
class User extends Model
{
public function state()
{
return $this->hasOne('App\State');
}
}
Or if you need same database structure, then
Second Way:
Basically it's Many to Many relations between User and State.
So better way is to define many to many relation function in Models of User and State considering UserRelation a middle(pivot) table.
class User extends Model
{
public function states()
{
return $this->belongsToMany('App\State', 'user_relations');
}
}
class State extends Model
{
public function users()
{
return $this->belongsToMany('App\User', 'user_relations');
}
}
and then to access states of user, you can call:
$user->states->first();
It's ok that you need one to one relation, you can call first() method to fetch first record od state.
Simillery to access user of state
$state->users->first();
And if you need to access user_relations (one to one) only, you have to define methods in both user and state model:
class User extends Model
{
public function user_relation()
{
return $this->hasOne('App\UserRelation');
}
}
class State extends Model
{
public function user_relation()
{
return $this->hasOne('App\UserRelation');
}
}
Since user and state are many to many, you can user belongsToMany function inside User model.
public function states(){
return $this->belongsToMany(State::class, 'user_relations', 'user_id', 'state_id');
}
You can get Eager Loading by retrieving both models related to the user like:
$user = User::with('states')->first();
And now to retrieve states, you can loop through the states like:
foreach($user->states as $state){
//Retrieved state model variable
....
}
If you are doing one-to-one relationship you won't need a pivot table, in your case the user_relations table. Pivot tables are used to express many-to-many relations.
And for the solution you want. You will have to store the user_id in the state table.
so your state table should look like the following:
state (id, user_id, stateName, code)
Relations should be like:
User model:
public function state()
{
return $this->hasOne('App\State');
}
State model:
public function user()
{
return $this->belongsTo('App\User');
}
I have the following models in a many-to-many relationship:
class Event extends Model
{
public function positions() {
return $this->belongsToMany(Position::class, 'position_events');
}
}
class Position extends Model
{
public function events() {
return $this->belongsToMany(Event::class, 'position_events');
}
}
class PositionEvent extends Model
{
public function position() {
return $this->hasOne(Position::class, 'id', 'position_id');
}
public function event() {
return $this->hasOne(Event::class, 'id', 'event_id');
}
}
The position_events table looks like:
id | event_id | position_id
If $event is an instance of Event, I can get the related positions as:
$event->positions;
This gives me something like the following for each related Position:
{"id":4,"name":"Striker","created_at":"2019-04-02 16:19:57","updated_at":"2019-04-02 16:19:57","pivot":{"event_id":27,"position_id":4}}
Notice the pivot element. It only has event_id and position_id as properties, these are columns from the position_events table. How do I get it to have the id column from that table as well?
Have you tried using withPivot(), for example:
$this->belongsToMany(Position::class, 'position_events')->withPivot('id');
I have a table called invoiceDetails that has item_id as foreign key from another table called items which has category_id as foreign key from table called categories.
I want to do that following using eloquent:
$result = InvoiceDetail::groupBy('item_id')
->selectRaw('sum(qty) as qty, item_id')->with('item', 'category')->get();
but I am getting error:
Call to undefined relationship [category] on model [App\InvoiceDetail].
Here's my relation inside Category model:
public function invoiceDetail() {
return $this->hasManyThrough('App\InvoiceDetail', 'App\Item', 'category_id', 'item_id');
}
Any suggestions?
Not sure you would even need a hasManyThrough relation here, unless you want to fetch all InvoiceDatail objects belonging to all items which in turn belong to the Category. That part is not clear from your question.
But in your example you are fetching items with their category from distinct item_id.
The reason this is not working is because you are trying to fetch the category relation from the InvoiceDetail object, which does not exist.
->with('item', 'category')
You want to load the Category based on the item relation, not based on the InvoiceDetail, try the dot notation (given that you did define the other relations)
->with('item.category')
Relations should be like this:
class InvoiceDetail extends Model
{
public function item()
{
return $this->belongsTo(\App\Item::class);
}
}
class Item extends Model
{
public function invoiceDetails()
{
return $this->hasMany(\App\InvoiceDetail::class);
}
public function category()
{
return $this->belongsTo(\App\Category::class);
}
}
class Category extends Model
{
public function items()
{
return $this->hasMany(\App\Item::class);
}
public function invoiceDetails()
{
return $this->hasManyThrough(\App\InvoiceDetail::class, \App\Item::class, 'category_id', 'item_id');
}
}
You would want to use the hasManyThrough if, for example, you have a Category and you want to load all the InvoiceDetails directly.
dd($category->invoiceDetails);
I'm trying to figure out how to set up my relationship using a pivot table with three different relationships.
users
- id
- username
products
- id
- name
tags
- id
- user_id
- name
user_products
- user_id
- tag_id
- product_id
When using the "Product" model, I would like to be able to pull all the associated tags. I'm currently able to pull the "user_products" object, but I would like to know how to pull the actual tag object in stead.
Models:
class User {
public function tags()
{
return $this->hasMany('UserTag');
}
}
class Product {
public function user()
{
return $this->belongsTo('User','user_products','product_id','user_id');
}
public function tags()
{
????
}
}
return $this->belongsTo('User','user_products','product_id','user_id')
->withPivot('tag_id')
->join('tags', 'tags.id', '=', 'user_products.tag_id')
->select(...);
Something like this should work fine.