Laravel self based polymorphic relationship - laravel

I have 3 tables:
categories
admins
users
Category can create users and admins. In categories table I have morphs field:
$table->nullableMorphs('userable');
And relations in Category model:
public function userable()
{
return $this->morphTo();
}
public function user()
{
return $this->morphOne(self::class, 'userable');
}
But when I tried do like this:
$category = Category::first();
$user = User::first();
$category->user()->save($user);
Get error with message:
Illuminate\Database\QueryException with message 'SQLSTATE[42S22]:
Column not found: 1054 Unknown column 'userable_id' in 'field list'
(SQL: update users set userable_id = 1, userable_type =
App\Models\Category, users.updated_at = 2021-04-27 12:13:22 where
id = 1)'
How I can correctly create self morph relationship in laravel?

So I think that what you should be doing is adding to the User model and the Admin model the relation user you've added to the category, those models that have this relationship can be morphed into the category
class User {
...
public function user() {
return $this->morphOne(Category::class, 'userable');
}
...
}
class Admin {
...
public function user() {
return $this->morphOne(Category::class, 'userable');
}
...
}
so you can do your logic this way
$category = Category::first();
$user = User::first();
$user->user()->save($category);
and it will update you category record with userable_type beeing App\Models\User and userable_id beeing 1 or whatever the user id is
id
name
userable_type
userable_id
1
Category Test
App\Models\User
1
to make things easier you can create a trait and add that relationship to the trait
trait IsUserable {
public function user() {
return $this->morphOne(Category::class, 'userable');
}
}
and instead of adding the relationship to User and Admin model, you just use the trait instead
class User {
use IsUserable;
}
class Admin {
use IsUserable;
}

Related

How to define this hasManyThrough relation?

I have the following models. Event belongs to a Casefile. Casefile and User are many-to-many.
class Casefile extends Model
{
public function users()
{
return $this->belongsToMany(User::class)->withTimestamps();
}
public function events()
{
return $this->morphMany('App\Event', 'casefile');
}
}
class User extends Authenticatable
{
public function casefiles()
{
return $this->belongsToMany(Casefile::class)->withTimestamps();
}
}
class Event extends Model
{
public function casefile()
{
return $this->belongsTo('App\Casefile');
}
public function users()
{
return $this->hasManyThrough('App\User', 'App\Casefile');
}
}
When I try to:
App\Event::find(526)->users()->get();
It gives:
Illuminate/Database/QueryException with message 'SQLSTATE[42S22]:
Column not found: 1054 Unknown column 'casefiles.event_id' in 'field
list' (SQL: select users.*, casefiles.event_id from users
inner join casefiles on casefiles.id = users.casefile_id
where casefiles.event_id = 526)'
How can I define the "Event has many Users" relation through Casefile?
It's not possible to use HasManyThrough here without a pivot model for the casefile_user table.
You can define a BelongsToMany relationship instead:
public function users()
{
return $this->belongsToMany('App\User', 'casefile_user', 'casefile_id', null, 'casefile_id');
}
If you not followed laravel naming convention for column name. Then you can specify your column name like this
public function users()
{
return $this->hasManyThrough(
'App\User',
'App\Casefile',
'event_id', // Foreign key on casefiles table...
'user_id', // Foreign key on users table...
);
}
For more Laravel HasManyThrough Relationship

Retrieve related models using hasManyThrough on a pivot table - Laravel 5.7

I'm trying to retrieve related models of the same type on from a pivot table.
I have 2 models, App\Models\User and App\Models\Group and a pivot model App\Pivots\GroupUser
My tables are have the following structure
users
id
groups
id
group_user
id
user_id
group_id
I have currently defined relationships as
In app/Models/User.php
public function groups()
{
return $this->belongsToMany(Group::class)->using(GroupUser::class);
}
In app/Models/Group.php
public function users()
{
return $this->belongsToMany(User::class)->using(GroupUser::class);
}
In app/Pivots/GroupUser.php
public function user()
{
return $this->belongsTo(User::class);
}
public function group()
{
return $this->belongsTo(Group::class);
}
I'm trying to define a relationship in my User class to access all other users that are related by being in the same group. Calling it friends. So far I've tried this:
app/Models/User.php
public function friends()
{
return $this->hasManyThrough(
User::class,
GroupUser::class,
'user_id',
'id'
);
}
But it just ends up returning a collection with only the user I called the relationship from. (same as running collect($this);
I have a solution that does work but is not ideal.
app/Models/User.php
public function friends()
{
$friends = collect();
foreach($this->groups as $group) {
foreach($group->users as $user) {
if($friends->where('id', $user->id)->count() === 0) {
$friends->push($user);
}
}
}
return $friends;
}
Is there a way I can accomplish this using hasManyThrough or some other Eloquent function?
Thanks.
You can't do that using hasManyThrough because there is no foreign key on the users table to relate it to the id of the group_user table. You could try going from the user to their groups to their friends using the existing belongsToMany relations:
app/Models/User.php:
// create a custom attribute accessor
public function getFriendsAttribute()
{
$friends = $this->groups() // query to groups
->with(['users' => function($query) { // eager-load users from groups
$query->where('users.id', '!=', $this->id); // filter out current user, specify users.id to prevent ambiguity
}])->get()
->pluck('users')->flatten(); // massage the collection to get just the users
return $friends;
}
Then when you call $user->friends you will get the collection of users who are in the same groups as the current user.

Laravel | Using Eloquent hasManyThrough

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);

Laravel Nested Relationship Where 5.5 Return All Records

I'm having some trouble when trying to fetch records from database. Here's the table schema:
users
--------------
id
username
password
email
divisions
--------------
id
name
employee
--------------
name
birth_date
status
class
division_id
user_id
projects
--------------
id
title
body
user_id
So, for the relationship explanations:
relationship
Okay, i'm trying to fetch project based on division_id on table employee with the following code:
# query code
$division_id = 10;
$items = Project::with(['user.employee.division' => function($query) use ($division_id) {
$query->where('id', $division_id);
}])->get();
I've added the required belongsTo, hasMany or hasOne to the models.
# User.php
class User extends Authenticatable
{
public function employee()
{
return $this->hasOne('App\Employee', 'user_id');
}
public function projects()
{
return $this->hasMany('App\Project', 'user_id');
}
}
# Division.php
class Division extends Model
{
public function employee()
{
return $this->hasMany('App\Employee', 'division_id');
}
}
# Employee
class Employee extends Model
{
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
public function division()
{
return $this->belongsTo('App\Division', 'division_id');
}
}
# Project.php
class Project extends Model
{
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
}
So, what's the problem?
Here's the thing, when i run the query code i'm getting all the records and the division object on employe relationship returning null.
If anyone thinks my code is wrong, please enlighten me.
Thanks.
So after some digging, i found an answer. The query code should change to whereHas:
# query code
$division_id = 10;
$items = App\Project::whereHas('user.employee.division',
function($query) use ($division_id) {
$query->where('divisions.id', $division_id);
})
->with(['user.employee.division']) // N+1 problem
->get();
reference: Laravel 5.3 Constraining Eager Loads not working

Laravel 5 nested relationship

I have 3 tables, with:
User: id | name
Products: id | name | user_id
Tracks: user_id | product_id
Relationships between these models (tables) are:
//User.php*************************************************
public function products() {
return $this->hasMany('App\Product');
}
public function track() {
return $this->hasMany('App\Track');
}
//Product.php*************************************************
public function user() {
return $this->belongsTo('App\User');
}
public function track() {
return $this->belongsTo('App\Track');
}
//Track.php*************************************************
public function products() {
return $this->hasMany('App\Product');
}
public function user() {
return $this->belongsTo('App\User');
}
Track table is used if user want to track the product. Some kind of bookmark.
So, when I'am on product page, $product = App\Product::find(1), if I want to echo the user name, I do $product->user()->name, but if I want to know if the user already tracking the current product, when I do $product->user()->track()->get() I receive this error:
Call to undefined method Illuminate\Database\Query\Builder::track()
If I do $product->user()->with('track')->get(), I don't get an error, but I get User Object which it do not contain info if the user already tracking the product.
How can I know if user is already tracking the product?
First, some of your relationship should be like these
Product.php
public function track() {
return $this->hasMany('App\Track');
}
Track.php
public function product() {
return $this->belongsTo('App\Product');
}
now to check if a user is tracking a product
$product = App\Product::find($id);
$product->user->track()->where('product_id', $product->id)->first();
another way
$user = App\User::find($id);
$user->track()->where('product_id', $product->id)->first();
And to check if logged in user is tracking the product, you can get the logged in user by Auth::user() , so
$user = Auth::user();
$user->track()->where('product_id', $product->id)->first();

Resources