create morph relation in Laravel with different foreign key - php

I want to use morph relation in Laravel to access another model and also I didn't want use id as a foreign key to connect them, actually I want use uuid column on option table as foreign key in morph relation.
the table that I want to use morphrTo: (report table):
$table->id();
$table->unsignedBigInteger('exam_id')->nullable();
$table->foreign('exam_id')->references('id')->on('exams')->cascadeOnDelete();
$table->string('optionable_uuid');
$table->string('optionable_type');
$table->integer('spend_time')->default(0);
$table->timestamps();
and related morhMany table is: (option table)
$table->id();
$table->string('uuid');
$table->string('title');
$table->timestamps();
and my models same as below
// ReportModel
// OptionModelpublic function optionable()
{
return $this->morphTo();
}
// OptionModel
public function report()
{
return $this->morphMany(ExamReport::class,'optionable','optionable_type','optionable_uuid','uuid');
}
but output data in morph relation return null witch part is wrong and how can I fix it?

I must change morhTo model as same as below
public function optionable()
{
return $this->morphTo('optionable','optionable_type','optionable_uuid','uuid');
}

you can add diffrent primayKey in relation
public function report()
{
$this->primaryKey = "uuid";
return $this->morphMany(ExamReport::class,'optionable');
}

Related

Laravel Eloquent where statement returns no attributes

So from my previous post, I was advised to start using Eloquent models, which I did.
My end goal, is to print out specific gifts, that belongs to that specific box.
Migrations:
gift_items:
public function up()
{
Schema::create('gift_items', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->float('unit_price');
$table->integer('units_owned');
});
}
gift_campaigns:
public function up()
{
Schema::create('gift_campaigns', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->foreignId('user_foreignK')->constrained('users');
$table->integer('gift_item_count')->nullable();
$table->string('status');
$table->date('dispatch_date');
$table->date('delivery_date');
});
}
Pivot table:
public function up()
{
Schema::create('campaigns_gifts', function (Blueprint $table) {
$table->foreignId('gift_id')->constrained('gift_items');
$table->foreignId('campaign_id')->constrained('gift_campaigns');
});
}
Controller:
function box($id){
$data = Campaign::with('gifts')->where('id', $id)->get();
return view('DBqueries.boxView', ['data'=>$data]);
}
Error that I receive using this way:
Seems like the updated version is trying to call the gift_campaigns table id, instead of the pivots table campaign_id.
Once again, I need that Request $id would match the pivots table campaign_id, and print out all of the gifts that this specific id holds
First of all as I sense the campaigns_gifts is a pivot table for campaigns and gifts having a Many-to-Many relation. You are doing it completely against the conventions of Laravel.
You generally do not use a Model for pivot table.
You generally do not fetch data from the pivot table directly. Instead use the relation on one of the related Models.
Note: Laravel does allow a Model for a Pivot, and you can query the pivot table directly, just check the documentation.
The correct way:
Pivot
Make a pivot table (that you already have) with column gift_id and campaign_id. i.e., the convention for naming keys as [table_name_singular]_[primary_key_on_table]
Model
One each model, define relationship for the other data as:
Gift.php Model:
public function campaign() {
return $this->belongsToMany(Campaign::class, 'campaign_gift');
}
Campaign.php Model:
public function gifts() {
return $this->belongsToMany(Gift::class,'campaign_gift');
}
since gift have a hasMany relation, the gifts table must contain a foreign key to campaigns table named campaign_id (same as the one on pivot).
Controller
Now in your controller:
function box($id){
$data = Campaign::where('id',$id)->with('gifts')->get();
return view('DBqueries.boxView', ['data'=>$data]);
}
You don't need to tell Laravel which columns, tables etc are you referring to, as long as you follow the conventions, Laravel will magically do things that otherwise would have been much more painful.

Update parent model with the HasOne child relationship

I have a League model and a Season model their respective migrations and relationships.
League migration and relations
Schema::create('leagues', function (Blueprint $table) {
$table->unsignedBigInteger("id")->primary();
$table->boolean("active");
$table->string("name");
$table->unsignedBigInteger("current_season_id")->nullable();
$table->timestamps();
});
public function current_season()
{
return $this->hasOne(Season::class);
}
Season migration and relations
Schema::create('seasons', function (Blueprint $table) {
$table->unsignedBigInteger("id")->primary();
$table->string("name");
$table->unsignedBigInteger("league_id");
$table->boolean("is_current_season");
$table->timestamps();
});
public function league()
{
return $this->belongsTo(League::class);
}
I have two vars with my models:
$league = League::find(1);
$season = Season::find(10);
With this line, I know automatically league_id in the Season model is filled with the $league->id
$season->league()->associate($league)->save();
I want do the inverse, and fill the current_season_id without doing:
$league->current_season_id = $season->id;
$league->save();
Is it possible?
Following the comments from #M Khalid Junaid, I think it´s better this way:
Remove current_season_id from League model.
Rewrite the current_season relation to this way:
public function current_season()
{
return $this->hasOne(Season::class)->where("is_current_season", true);
}
Now, in this way, I can access the current season of the league in the form: $league->current_season
Thank you.
You do not need $table->unsignedBigInteger("current_season_id")->nullable(); in leagues table, if you are using hasOne relationship, otherwise you need another type of relationship.
I'd strong recommend in seasons table, to use a foreign key declaration in your migration
$table->unsignedBigInteger("league_id");
$table->foreign( 'league_id' )->references( 'id' )->on( 'leagues' );

Laravel 5.7 Multiple foreign key referencing to same table primary key

I have a table Task where it has creatorID and assignedTo. Both of them are foreign keys to my User table's primary key u_id. I'm trying to display the names of the creatorID and assignedTo but it is not working, it gave an error Trying to get property 'name' of non-object. Anyone able to spot whats wrong with my code? I'm opened to any suggestions or modifications to my models/tables thank you.
Task Table(Migration)
Schema::create('tasks', function (Blueprint $table) {
$table->increments('t_id');
$table->string('t_name');
$table->date('start_date');
$table->date('end_date');
$table->string('status');
$table->unsignedInteger('creatorID');
$table->unsignedInteger('assignedTo')->nullable();
$table->unsignedInteger('p_id');
$table->foreign('creatorID')->references('u_id')->on('users');
$table->foreign('assignedTo')->references('u_id')->on('users');
$table->foreign('p_id')->references('p_id')->on('projects');
$table->string('priority');
$table->timestamps();
});
User Table(Migration)
Schema::create('users', function (Blueprint $table) {
$table->increments('u_id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->rememberToken();
$table->string('status');
$table->string('picture');
$table->timestamps();
});
Task Model
public function users(){
return $this->belongsTo('App\User','u_id');
}
public function assignedTo(){
return $this->belongsTo('App\User','assignedTo');
}
User Model
public function tasks(){
return $this->hasMany('App\Task','u_id');
}
public function assignedTo(){
return $this->hasMany('App\Task','assignedTo');
}
Query
$ActiveTasks = Task::where('p_id',$projectID)->where('status','active')->get();
Blade File
#foreach($ActiveTasks as $task)
<p>{{$task->assignedTo->name}}</p> <<< NOT WORKING
#endforeach
You may need to update the assignedTo relationship in your Task model so that it knows to reference u_id instead of id:
public function assignedTo()
{
return $this->belongsTo(\App\User::class, 'assignedTo', 'u_id');
}
According to the docs:
If your parent model does not use id as its primary key, or you wish
to join the child model to a different column, you may pass a third
argument to the belongsTo method specifying your parent table's custom
key
https://laravel.com/docs/5.7/eloquent-relationships#defining-relationships
You should also check the tasks table to see if the assignedTo column is null for any of the saved records. Since you have the nullable attribute set, any records that had a null value in assignedTo would also produce the Trying to get property 'name' of non-object error when the relationship was accessed. If that's the case, you can define the relationship using the withDefault() method:
The belongsTo relationship allows you to define a default model that
will be returned if the given relationship is null. This pattern is
often referred to as the Null Object pattern and can help remove
conditional checks in your code
https://laravel.com/docs/5.7/eloquent-relationships#inserting-and-updating-related-models

Seeding Relationship one to many in Laravel

I need to seed a relationship in Laravel, where each user has many devices
The User model
public function devices()
{
return $this->hasMany(Device::class);
}
The Device model
public function users()
{
return $this->belongsTo(User::class);
}
}
The device_user table
Schema::create('device_user', function (Blueprint $table) {
$table->increments('id');
$table->integer('device_id')->unsigned()->index();
$table->foreign('device_id')->references('id')->on('devices')->onDelete('cascade');
$table->integer('user_id')->unsigned()->index();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->timestamps();
});
The seeder
factory(App\Device::class, 20)->create()->each(function(App\Device $device) {
$device->users()->attach([
rand(1,5),
rand(6,15),
rand(16,20),
]);
});
But, when I run the migration with seeder, I get this message
Call to undefined method Illuminate\Database\Query\Builder::attach()
Please, help
attach for many to many relationships, you don't need device_user table for one to many relationship, in one to many you should create a column with name user_id in device table and just it. after that you can insert data in device table with user_id. and get user relationship with
Device::user()->get();

Laravel relationship with single column

I have a table called "fields":
Schema::create('fields', function (Blueprint $table) {
$table->increments('id');
$table->string("label");
$table->string("name")->unique();
$table->text("options")->nullable();
$table->timestamps();
});
I want to have another table which simply stores the ids of some of the fields. I will call this default_fields.
I basically want a relationship or logic that allows me to grab these default_fields like I would with any other relation:
Schema::create('default_fields', function (Blueprint $table) {
$table->increments('id');
$table->integer("field_id");
});
How can I create a relationship that grabs all the fields whose id's are present in this table? I also want to be able to sync().
Would I just make a model for DefaultField and then do something like Field::whereIn('id', DefaultField::get()->pluck('id'))?
And then code my own sync() logic? Is there a super easy way to do this that I'm missing? I'd also like to be able to sort this like I would any other relation.
You can have a model Field that has this relationship:
public function defaultFields()
{
return $this->hasMany('App\DefaultField');
}
In you controller, you can fetch the Field with his related DefaultFields like:
$fields = Field::with('defaultFields')->get();
You can have a similar method field in your DefaultField model:
public function field()
{
return $this->belongsTo('App\Field');
}
In you controller, you can fetch the DefaultField with his parent Field:
$defaultFields = DefaultField::with('field')->get();
In your case, more productive will be 'is_default' boolean property in the fields table.

Categories