Laravel Eloquent polymorphic relationships to model hierarchy/inheritance - laravel-5

I am modelling a report which depending on its type (missing person, wanted, etc) could contain different fields but each type shares a number of fields. This I think is what differentiates this question from say: this question where the fields are common across all types.
At the moment I have created a report model with an integer field indicating its type which references a type table. The advantage is that this is an easy model to understand and implement. It means that I can also easily search for objects. On the other hand, this means that some fields do not make sense for some models and thus are always null. As I add fields unique to the individual types I end up with many columns in the table of which many are null.
I would like some advice on using Laravel's (5.2) polymorphic relationships. The way I envision this is that I will have a base report model that contains all the shared fields and have the MorphTo fields; then have the different types separate containing their unique fields in a one-to-one relationship with the reports model. Is this possible and does this even make sense? The problems I see are that I lose the ability to search across all "reports" regardless of type. Or is there a way around this like say Java where inheritance allows for sub-classes to be considered members of the superclass?
A rough diagram of such relationships is shown below. Reports holds the shared fields while Wanted and Missing have unique fields.

From the diagram representing the relations in your question above, I think it is a straight forward case of parent - child.
Here, your BaseReport or Report is the parent model so to say and the WantedReport and MissingReport are the child - so to say.
Only thing which needs to be changed in your diagram is that you need to define the foreign key of report_id in your child tables to point to the parent record in the base reports table.
Table-reports
id // primary key
date
description
Table - wanted_reports
id //primary key
report_id //foreign key linking to the reports table
status_at
is_captured //maybe a boolean field
charge
Table - missing_reports
id //primary key
report_id //foreign key linking to the reports table
status_at
is_found
last_seen_date
last_seen_at
The you can define the corresponding models as - Report
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Report extends Model
{
protected $table = 'reports';
protected $fillable = ['report_date', 'description'];
public function wanted_reports()
{
return $this->hasMany(WantedReport::class);
}
public function missing_reports()
{
return $this->hasMany(MissingReport::class);
}
}
WantedReport
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class WantedReport extends Model
{
protected $table = 'wanted_reports';
protected $fillable = ['report_id', 'status_at', 'is_captured', 'charge'];
public function report()
{
return $this->belongsTo(Report::class);
}
}
MissingReport
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class MissingReport extends Model
{
protected $table = 'missing_reports';
protected $fillable = ['report_id', 'status_at', 'is_found', 'last_seen_date', 'last_seen_at];
public function report()
{
return $this->belongsTo(Report::class);
}
}
Then anywhere in your application you can access the relations like you normally do, for example
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ReportsController
{
public function store(Request $$request)
{
$report = $this->model->create([
'report_date' => Carbon:::now(),
'description' => $request->get('description')
]);
if($report)
{
$wantedReport = $report->wanted_reports()->create([
'status_at' => $request->get('status'),
...
]);
$missingReport = $report->missing_reports()->create([
'status_at' => $request->get('status'),
...
]);
}
}
}
Hope this helps

Related

laravel eloquent migrations

In laravel eloquent relationship, is it still necessary to make migration even though there's an existing database? beginners here.
I create a one-to-one eloquent relationship inside my model to get the specific column from another table's record and fetch to the datatable, but it did not work.
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Directorystatus extends Model
{
use HasFactory;
protected $table = 'user_status';
protected $fillable = ['status_id' , 'status_xtitle'];
public function userbasic() {
return $this->belongsTo(directorybasic::class,'user_xusern','status_xuser');
}
}
class Directoryuser extends Model
{
use HasFactory;
protected $table = 'user_basic';
protected $primaryKey = 'user_id';
protected $fillable = ['user_id' , 'user_xusern' , 'user_xfirtname' ,'user_xmiddlename','user_xlastname'];
public function userstatus() {
return $this->hasOne(directorystatus::class,'user_xusern','status_xuser');
}
}
No. Migrations are not necessary. Defining relationships on both sides is also not necessary, if you don't need them both. (You can have only belongsTo, without having hasOne or hasMany in the opposite model.)
First, make sure you are using the right object (Directorystatus::class / Directoryuser:class - I see they're not capitalized in your code). The next param is the foreign key, meaning the column which points to a model's primary key. The third param is optional and is used only if the primary key is not id.
For example if you have a column status_xuser in the table user_status, which contains a user_id from user_basic table, you should define it like this:
public function userbasic() {
return $this->belongsTo(Directoryuser::class,'status_xuser','user_id');
}
And in order to use this relationship, when you retrieve a model from the db, for example, you should call on it the same way your relationship function is named.
$status = Directorystatus::find(1);
$user = $status->userbasic();
I would also suggest you name your classes in camelCase, because it's the accepted practice (in Laravel especially).

Understanding Eloquent One-to-many Relationships

I'm trying to understand Eloquent relationships but it seems I'm missing something in understanding it. I have:
A Meeting produces many Documents.
A specific Document can be produced for one Meeting.
Thus a one to many relationship. I am trying to display the 'meeting_name' in the Document details table but get this error:
Trying to get property 'meeting_name' of non-object (View: C:\wamp64\www\yajra_contact_system\resources\views\documents\index.blade.php)
Here is my code.
Please please explain with code solution:
app\Document.php File:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Document extends Model
{
protected $fillable = [
'document_name',
'document_desc',
];
public function meeting(){
return $this->belongsTo(Meeting::class);
}
}
app\Meeting.php File:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Meeting extends Model
{
protected $fillable = [
'document_id',
'meeting_name',
'meeting_desc',
'room_no',
'no_of_invitees',
'comments'
];
public function documents(){
return $this->hasMany(Document::class);
}
}
app\Http\Controllers\DocumentsController.php File:
namespace App\Http\Controllers;
use App\Document;
use App\Meeting;
use Illuminate\Http\Request;
class DocumentsController extends Controller
{
public function index()
{
$documents = Document::all();
$meetings = Meeting::all();
return view('documents.index', compact('documents', 'meetings'));
}
}
resources\views\documents\index.blade.php File:
#foreach($documents as $document)
<tr>
<td>{{$document->id}}</td>
<td>{{$document->document_name}}</td>
<td>{{$document->document_desc}}</td>
<td>{{$document->meetings->meeting_name}}</td> <!-- ERROR IS HERE -->
</tr>
#endforeach
you have many problems in your code:
first: in order to use the relation ... you have to load it first ...
loading relation done by using with('relationName') method ...
in index:
$documents = Document::with('meeting')->all();
second:
<td>{{$document->meetings->meeting_name}}</td> <!-- ERROR IS HERE -->
the relation is named meeting without s .. not meetings ...
third:
it's pest practices to provide foreign key in relation:
in Meeting model
public function documents(){
return $this->hasMany(Document::class,'meeting_id');
}
in Document model:
public function meeting(){
return $this->belongsTo(Meeting::class,'meeting_id');
}
please make sure you have a column meeting_id in your documents table referenced as foreign key to id in meetings table
more details about loading relation in:
https://laravel.com/docs/7.x/eloquent-relationships#eager-loading
So you have your tables mixed up.
You have a single record per meeting yet you have a document_id in your meetings table which you would need to duplicate meeting record for each document.
Remove the document_id from your meetings table
Add a meeting_id to your documents table
remember to update your models fallible array else you wont get the new columns in the collection.
This should fix your problem as your relationships are correct.

Laravel - Illegal offset type when accessing property on OneToMany relationship

I'm learning how to use laravel and relationships. I'm having trouble accessing data from a hasMany relationship, I understand this might be a silly question. This might be a duplicate question, but I didn't find any specific answer like this.
I have two models, a salesman table and a prices table table. One salesman has many prices tables, so it's like this:
On Salesman model
<?php
namespace App\Pedidos;
use Illuminate\Database\Eloquent\Model;
class Vendedores extends Model
{
protected $connection = 'Pedidos';
protected $primaryKey = 'CD_VENDEDOR';
public function TabelaDePreco() {
return $this->hasMany('\App\Pedidos\TabelaDePreco', 'CD_VENDEDOR', 'CD_VENDEDOR');
}
}
On the prices table model
<?php
namespace App\Pedidos;
use Illuminate\Database\Eloquent\Model;
class TabelaDePreco extends Model
{
protected $connection = 'Pedidos';
protected $primaryKey = ['CD_VENDEDOR', 'CD_PRODUTO', 'CD_ESTADO'];
public function Vendedores() {
return $this->belongsTo('\App\Pedidos\Vendedores', 'CD_VENDEDOR', 'CD_VENDEDOR');
}
}
On the Controller
public function index()
{
$vendedores = Vendedores::all();
return view('pedidos.tabeladepreco.index')
->with('title', 'Tabela de preços')
->with('vendedores', $vendedores);
}
On the view, this will return TabelaDePreco model
#foreach($vendedores as $vendedor)
#foreach ($vendedor->TabelaDePreco as $tabela)
{{ dd($tabela) }}
#endforeach
Here is a print from the code above:
TabelaDePreco model
As you can see, data is loaded on the $tabela variable.
If I try to print, on the view, {{ $tabela->NR_LIMITE1 }}, I get the illegal offset type error. How do I access this attribute since data is loaded when using dd()? I've tried $tabela['NR_LIMITE1'] but with the same error.
What am I doing wrong?
Best regards.
EDIT:
As pointed by Jonas on the comments, Laravel won't support relationships when one of the tables has composite keys. Back to migrations.
You need to eager load your relation:
$vendedores = Vendedores::with('TabelaDePreco')->all();
return view('pedidos.tabeladepreco.index')
->with('title', 'Tabela de preços')
->with('vendedores', $vendedores);
It may also happen that $vendedor->TabelaDePreco is not defined if there is not TabelaDePreco associated with the $vendedor. Try adding a isset(). Also thing about pagination if you have many entries.

Hasmany with child of child in laravel 5.5

I have 3 tables.
shops
shop_foods
foods
I need to fetch foods table data when I create hasmany relation with shops_food and store.
$this->hasMany('App\Diet\ShopFood', 'shop_id', 'id');
Please, show your code so we can know what are you trying to do.
But what I can see here is that you have a wrong relationship.
Why are you assigning hasMany in a Many to Many relationship?
In your Shop Model you can make a foods relationship with:
$this->belongsToMany('App\Diet\Food);
Then you can retreive you food when calling
$shop->foods
And the shop_foods with the Pivot property
If i understand correctly you want to get foods when you call shops->shop_foods. if is that
//first you call your shops as you want.
Shop::with(['shops_food' => function($query){
//the 'shops_food' relationship should be called within an array
//this way you could query the relationship as the eloquent model.
//that way you could call the 'foods' relationship inside the shops_food relationship.
$query->with('foods')
}])
...
Note that you must have the relationship declared in shop and shop_foods models
lets your models are like these
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Shop extends Model
{
//
public function shops_food()
{
//shop_id is the foreing key inside your shop_foods table
return $this->hasMany('App\ShopFood','shop_id');
}
....
}
then ShopFood Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class ShopFood extends Model
{
//
public function foods()
{
//shop_food_id is the foreing key inside your foods table
return $this->hasMany('App\Food','shop_food_id');
}
....
}
This reads as if you want
public function foods() {
$this->hasMany('App\Diet\Food');
}
in your ShopFood model and in your Shop model
public function shopfoods() {
$this->hasMany('App\Diet\ShopFood')->with('foods');
}
You can also make 2 separate relations in the Shop model:
public function shopfoods() {
$this->hasMany('App\Diet\ShopFood');
}
public function shopfoodsWithFoods() {
$this->hasMany('App\Diet\ShopFood')->with('foods');
}
So that way you can use whatever you need at that moment.
But the whole thing is really not clear...
I am not even sure how the 3 table are connected, so the hasMany are just guesses.
Nevertheless you can just go with the "with" function.
PS
There is also the possibility to just declare
protected $with = ['foods'];
in your ShopFood model, if you ALWAYS want those 2 connected. It's all in the documentation.

Laravel 5.4 : get parent object from the child object using Model Inheritance

I am using Laravel 5.4.
The issue I am facing is regarding model inheritance.
Following is the code snippet of the 2 classes:
class Company extends Model{
protected $table = 'companies';
}
class Vendor extends Company{
protected $table = 'companies';
}
NOTE : Here the Company is the parent class and Vendor is the child class. Also, both the models refer to the same table.
I am having an object of the Vendor class (i.e. child class).
For example:
$vendor = Vendor::find(1);
How can I get the Company object from the existing Vendor object?
As both refer to the same record in the database.
You can solve your problem by specifying field/column names in your relationship method. You can specify the exact keys when you declare the relationship method so Laravel will not use it's convention for mapping the model/table, for example, you can use the following syntax in your Company model for it's many-to-many relationship method:
class Company extends Model
{
public function someMethod()
{
return $this->belongsToMany(
'App\OtherModel', // name of the other model
'pivot_table_name', // name of the pivot table
'company_id' // this model foreign key
'other_model_foreign_key' // other model foreign key
);
}
}
In thus case, the foreign keys should match with the field/column names used in your pivot table. Now you can call your relationship method (someMethod) from instance of Vendor objects and it'll work just fine because compoany_id is specified so Laravel will use that column name instead of vendor_id. Check more about many-to-many relationship.

Resources