Laravel Polymorphic Relation using full model name - laravel-5

I have a polymorphic relationship set up in Laravel 5.4. The relationship is working but the query is using the fully qualified model name.
select * from `contact_info`
where `contact_info`.`entity_id` = '25'
and `contact_info`.`entity_id` is not null
and `contact_info`.`entity_type` = 'App\Modules\User\Model\User'
limit 1
The relationship is set on the User model:
/**
* #description Method handles polymorphic contact relationship.
* #return \Illuminate\Database\Eloquent\Relations\MorphOne
*/
public function contact()
{
return $this->morphOne('App\Modules\Common\ContactInfo', 'entity');
}
and the ContactInfo model:
/**
* #description Method establishes polymorphic relationship (tenant/user).
* #return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function entity()
{
return $this->morphTo();
}
The actual table has the values set to 'user', 'tenant' and 'referrer' instead of the model names/namespaces. I found a little bit of information about relationship maps but don't know if that'll solve my issue here.
Basically I need to find out how to tell the code that 'App\Modules\User\Model\User' should be 'user' in the database.

MorphMaps were the way to go. I added this to my AppServiceProvider boot method and it started pulling the data as expected:
use App\Modules\User;
use App\Modules\Tenant
Relation::morphMap([
'user' => User::class,
'tenant' => Tenant::class
]);
Query is now:
select * from `contact_info`
where `contact_info`.`entity_id` = '25'
and `contact_info`.`entity_id` is not null
and `contact_info`.`entity_type` = 'user' limit 1
This article helped me out tremendously.

Related

Using Eloquent Relationships

Laravel 8
I have a couple models using eloquent relationships.
A User which can have many blog posts
public function blogPosts() {
return $this->hasMany(BlogPost::class);
}
And a BlogPost which belongs to the author
public function author() {
return $this->belongsTo(User::class);
}
My issue is that when I attempt to use one of these relationships, I am returned an empty object.
We can focus on the belongsTo() relationship.
I am attempting to use this relationship here:
class BlogPostResourceCollection extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return $this->collection->map(function($post) {
return [
'id' => $post->id,
'author' => $post->author(),
'title' => $post->title,
];
});
}
}
Why am I getting an empty object for author?
Edit: the blog post table does have a foreign key user id column.
All the problem is laravel by default get the relation from the method name and you add custom names so to fix it you need to add the second argument for each relation like this
User Model
public function blogPosts() {
return $this->hasMany(BlogPost::class, 'user_id');
}
BlogPost
public function author() {
return $this->belongsTo(User::class, 'user_id');
}
At the end in your BlogPostResourceCollection you need to access the author data all you need is to write ->author not ->author()
for more info, you could check the One-To-Many relation here

Laravel getAttribute() on eloquent?

so i just wondered, if something like this is possible, since my code does not work.
protected $appends = ['position_name'];
public function getPositionNameAttribute()
{
return $this->belongsTo('App\EmployeePosition', 'employee_position_id')->name;
}
Can I append the name of Eloquen relationship model?
edit: so far, i am using this:
foreach ($employees as $e) {
$e->position_name = $e->position->name;
}
So, I needed to use the relation defined before.
protected $appends = ['position_name'];
public function position()
{
return $this->belongsTo('App\EmployeePosition', 'employee_position_id');
}
public function getPositionNameAttribute()
{
return $this->position->name;
}
Based on your comments i'd suggest to use the laravel default solution for your problems API resrouces
eg
class EmployeeResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'position_name' => $this->position->name,
];
}
}
note: using the with as other people suggested to preload information can increase performance by reducing the amount of queries, if you are returning a collection of employees.
Creating an accessor that looks up a value in another model and appending this field by using $appends is bad practice and will lead to n+1 queries whenever you fetch your Employee model. You should avoid doing this and just use $employee->position->name.
You should also make sure to use Employee::with('position') when you need to show the position name, so that the position model is fetched in a single query.
If the position name is something that you need in all your Employee queries, then you can set the Employee to always eager load the position by defining the following inside your Employee model:
/**
* The relationships that should always be loaded.
*
* #var array
*/
protected $with = ['position'];
I think you can just create a model with position names and reference it to the position id in the other mode by using eloquent relationships.

Get data from different tables using inner joins

I have column in results as 'user_id','test','subject' and in datatable i want to get the 'test_name' which is saved in 'tests' table, Student name 'name' saved in 'users' and 'subject name' in table 'subjects' in column 'subjects' now tell me what is the best way to get this data.
So far i had tried this but getting null while dumping.
$result = DB::table('results')
->where([
['results.subject',$request->subject],
['test',$request->test],
['user_id',$request->name]
])
->join('users','results.user_id','=','users.name')
->join('tests','tests.id','=','results.test')
->join('subjects','subjects.id','=','results.subject')
->select('results.*','users.name','tests.test_name','subjects.subjects As s_subject')
->first();
dd($result);
anyone who can guide me the best possible solution.
You should use the power of the Eloquent model of Laravel. You have to create Results model class as follow(and obviously need to create User, Test and Subject model class)
Results.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Carbon\Carbon;
class Results extends Model
{
/**
* The attributes that aren't mass assignable.
*
* #var array
*/
protected $guarded = ['id'];
/**
* Get the user profile associated with the results.
*/
public function user()
{
return $this->hasOne('App\User', 'user_id');
}
/**
* Get the test details associated with the results.
*/
public function test()
{
return $this->hasOne('App\Test', 'test');
}
/**
* Get the subject associated with the results.
*/
public function subject()
{
return $this->hasOne('App\Subject', 'subject');
}
}
Controller.php
$results = App\Results::where(['subject' => $request->subject, 'test' => $request->test, 'user_id' => $request->name])->first();
Now you will get the Result object with the test, subject, and test property.
Note that:- You should provide the output when you are asking any question.

Polymorphic relationship with pivot data

I would like to solve the following issue:
I got multiple models like:
Product
Customer
Each model should be able to have one or more Fields with pivot data.
Field:
id
title
type
required
Example:
Product has a field called video_url, type should be string containing the pivot value http://youtube.com/....
Customer has a field called external_id, type should be integer containing the pivot value 242.
The fields should be added dynamically by the user. The user should be able to decide, whether the field is morphing to Product or Customer (or even more later).
Maybe this helps to understand:
What I am doing right now
At the moment I created a new Model for each, product and customer
For customers:
class CustomerField extends Model
{
/**
* #return \Illuminate\Database\Eloquent\Relations\belongsToMany
*/
public function customers()
{
return $this->belongsToMany(Customer::class)->withPivot('value');
}
}
For products:
class ProductField extends Model
{
/**
* #return \Illuminate\Database\Eloquent\Relations\belongsToMany
*/
public function products()
{
return $this->belongsToMany(Product::class)->withPivot('value');
}
}
At the moment this works out, but of course, it's not the most elegant way to solve it.
My question
Is there a possibility to morph a field dynamically to Product or Customer with an additional pivot?
I think this is what you want Polymorphic:Many-to-Many
You don't need to add ProductField and CustomerField models,
you just need to add Product, Customer and Field model.
The fields will dynamically belongs to product or customer by fieldable_type. Even you have more models, it will store the model name to this fieldable_type.
And the tables you need to be created like this below:
fieldables table has fieldable_id and fieldable_type;
fieldable_type will set your model name automatically, like App\Product, and you can custom that by yourself in AppServiceProvider:
Relation::morphMap([
'products' => 'App\Product',
'customers' => 'App\Customer',
]);
In Product Model:
class Product extends Model
{
/**
* Get all of the fields for the product.
*/
public function fields()
{
return $this->morphToMany('App\Field', 'fieldable')->withPivot('value');
}
}
In Customer Model:
class Customer extends Model
{
/**
* Get all of the fields for the customer.
*/
public function fields()
{
return $this->morphToMany('App\Field', 'fieldable')->withPivot('value');
}
}
In Field Model:
class Field extends Model
{
/**
* Get all of the products that are assigned this field.
*/
public function products()
{
return $this->morphedByMany('App\Product', 'fieldable');
}
/**
* Get all of the customers that are assigned this field.
*/
public function customers()
{
return $this->morphedByMany('App\Customer', 'fieldable');
}
}
CRUD with Pivot Value:
After that, you can easily create, get, update, delete pivot value like:
Field::first()->products; # return the products with pivot value
Field::first()->customers; # return the customers with pivot value
Customer::first()->fields;
$field = Field::first();
# create new relationship with pivot value between customer and fields:
Customer::first()->fields()->attach($field, ['value' => 'customer new value field']);
# update pivot with value:
Customer::first()->fields()->sync([$field->id => ['value' => 'update customer value field']]);
# Delete pivot
Customer::first()->fields()->detach($field->id);
The best practice is to use a separate table to hold meta information so that you can easily add/remove "columns" as needed
For example, you could set your meta table up like this:
create table `ProductField` (
products_id int(11),
column_name varchar(255),
value varchar(255),
)
Then in your products model, add functionality to get, insert, check if exists, etc.
public function getMeta($column) {
$meta = DB::table('ProductField ')
->select('column_name', 'value')
->where('products_id', '=', $this->id)
->where('column_name', '=', $column)
->get();
if (!$meta->isEmpty()) {
return $meta;
} else {
return null;
}
}
public function addMeta($column, $value) {
DB::table('ProductField ')->insert(
[
'products_id' => $this->id,
'column_name' => $column,
'value' => $value,
]
);
}
The same way you can achieve dynamic nature for Customers too.
You can also use an array to store the feilds and then dynamically add them to the model
foreach ($request->input('cost') as $key=>$cost) {
Price::create([
'product_id' => $request->product_id[$key],
'date' => Carbon::now(),
'cost' => $cost,
'trend' => 0
]);
}
If you know that there will only be certain dynamic fields ahead of time, you could opt to create accessor methods for them

Laravel Relation Query

I have two tables course and module where each course belongs to a single module.
What I need is to eager load the module with course
my codes is :
$courses = Course::all();
$module = Coursemdule::all();
You need to create relation between both the table
In the course model
/**
* Get the user that owns the phone.
*/
public function module()
{
return $this->belongsTo('App\Coursemdule');
}
In the Coursemdule model
/**
* Get the Course record associated with the Coursemdule.
*/
public function course()
{
return $this->hasOne('App\Course');
}
To fetch this value
$phone = Course::find(1)->module;
OR
$phone = Course::with('module')->all();
I hope this will help you.

Resources