Laravel | Polymorphic relationship from morphed model - laravel

I am creating a polymorphic relationship which defaults to null, as it doesn't have an association originally.
I then create the association and I want to be able to update the polymorphic table to have a link between the original and the morphed class.
post_images:
| id | post_id | width | height | created_at | updated_at
files:
| id | fileable_type | fileable_id | file_disk_id | created_at | updated_at
So basically, inside of the polymorphic table the type and the id they default to null as it has to be processed first and converted to a standard format etc.
I then create the associated row inside of the morphed table, however I am unsure how to update the polymorphic to have the type and the id from the morphed table.
I have tried to do something like $post->images()->create(...); and then try and do something like $file->where('id', $fileId)->firstOrFail()->associate($post); but this just seems to return null.
So the current process is:
Upload the file -> creates a row in the files table which contains the polymorphic relationships but columns are defaulted to null.
Standardize the file -> the file then gets converted and compressed into a standard format that is used for the site.
Create post_images row -> The site then creates a row for post_images table which then needs to link the update the files row to contain the new polymorphic relation.
Eloquent relationships:
File.php
public function fileable()
{
return $this->morphTo();
}
PostImage.php
public function file()
{
return $this->morphMany(File::class, 'fileable');
}
Post.php
public function images()
{
return $this->hasMany(PostImage::class);
}

I think you should just be able to call the save method on the relationship. For example:
$post->file()->save($file)

Related

Load model one to many relation eloquent way without primary key but on multiple overlapping fields

I'm working on an older project that I've been tasked to speed up certain parts of while we work on a complete re-write since the code is just badly maintained, poorly written and outdated for what it's suppose to do.
I stumbled into an issue to the core of the project and because of this I can't change it without breaking almost everything else. So I need to load a "relation" the eloquent way (using Planning:with('availability') but there isn't a real foreign ID, it rather laps with multiple fields.
Would there be a way to load it all in one query with the overlapping fields rather than have it load separately creating an n+1 problem?
+--------------+-----------------+
| Planning | Availability |
+--------------+-----------------+
| planning_id | availability_id |
| date | date |
| startHour | startHour |
| stopHour | stopHour |
| candidate_id | candidate_id |
| section_id | section_id |
+--------------+-----------------+
From the above example you can see the overlapping fields are date, startHour, stopHour, candidate_id and section_id.
I tried get...attribute but that still loads with n+1, I tried including it with ->with(['availabilities']) but that doesn't work since I ask for the
model and not the relation:
Edit for more clarity:
Planning Model:
public function availabilities()
{
return Availability::where('section_id', $this->section_id)
->where('candidate_id', $this->candidate_id)
->where('planningDate', $this->planningDate)
->where('startHour', $this->startHour)
->where('stopHour', $this->stopHour)
->get();
}
public function availabilities2()
{
return $this->hasMany('App\Models\Availability', 'candidate_id', 'candidate_id')
}
Controller:
$plannings = Planning::with(['availabilities'])->get();
$plannings = Planning::with(['availabilities2' => function ($query) {
// $this is suppose to be Planning model but doesn't work
$query->where('section_id', $this->section_id)
->where('planningDate', $this->planningDate)
->where('startHour', $this->startHour)
->where('stopHour', $this->stopHour);
// ---- OR ---- //
// Don't have access to planning table here
$query->where('section_id', 'planning.section_id')
->where('planningDate', 'planning.planningDate')
->where('startHour', 'planning.startHour')
->where('stopHour', 'planning.stopHour');
}])->get();
First of all to be able to load my relation I took one of the keys that matched and took the one which had the least matches which in my case was section_id.
So on my Planning model I have a function:
public function availabilities()
{
return $this->hasMany('App\Models\Availability', 'section_id', 'section_id');
}
This way I can load the data when needed with: Planning:with('availability').
However since I had a few other keys that needed to match I found a way to limit this relation by adding a subquery to it:
$planning = Planning::with([
'availabilities' => function ($query) {
$query->where('candidate_id', $this->candidate_id)
->where('startHour', $this->startHour)
->where('stopHour', $this->stopHour);
},
// Any other relations could be added here
])
->get();
It's not the best way but it is the only way I found it not getting too much extra data, while also respecting my relationship
When you want to use multiple fields in where() method you most insert a array in the where() method:
This document can help you
change your code to this:
return Availability::where([
['section_id', $this->section_id],
['candidate_id', $this->candidate_id],
['planningDate', $this->planningDate],
['startHour', $this->startHour],
['stopHour', $this->stopHour]
])->firstOrFail();

how retrieve relation between two model that other models have polymorphic relation with both of them

I have these two models: Tag and Translate, I have these tables: tags, translates, translatables, Tag model uses translatable.
my table columns is like this:
translates: translatables: tags:
id name lang | translate_id translatable_id translatable_type | id
-- ---- ---- | ------------- --------------- ----------------- | --
| |
I want to write a function to get current translation of tag based on app locale which means I should query on translates tables based on translatables table data and using translateble_id and translatable_type find id in the translate table and get the name based on lang.
do you know how i can do this?
this is translatable trait:
trait Translatable{
public function translates(){
return $this->morphToMany(Translate::class,'translatable','translatables','translatable_id','translate_id');
}
}
and this is how I get locale:
$locale=app()->getLocale();
I get tags translates with querybuilder like:
$locale=app()->getLocale();
$tags=DB::table('translates as T')
->where('lang','=',$locale)
->join('translatables as R','R.translate_id','=','T.id')
->where('R.translatable_type','=','App\Models\Tag')
->join('tags as M','R.translatable_id','=','M.id')
->get();
but i want to use a function in model and eloquent.

Retrieving User's Name from Query

I have an Employee evaluation form which stores the supervisor (FTO) and employee (FTE) in the database by their ID number (EID).
I can retrieve the data and display the EID of both the supervisor and the employee, but how can I setup it up to pull their names from the user table and display those?
My User Table
ID | EID | first name | last name | email | password
Evaluation Table
ID | ftoEid | fteEid | date | rating1 | etc...
Currently using in the model:
$dor = Dor::find($id);
return view('dor.show', compact('dor'));
You need to set up relationships.
class Evaluation extends Model {
public function user() {
return $this->belongsTo("App\User", 'fteEid');
}
}
Dor::find(1)->user->first_name;
Your field names are confusing so I am not sure if they are correct; hopefully you get the idea.

Laravel pivot sync() attaches wrong values

I've been dealing with this issue on almost every sync() to the pivot tables where, in Laravel 5.6, when I perform the following it adds one row that is non existent in the request:
$firm_data = request()->all(); // get the request data
$firm = Firm::where('id', request('firm_id')); // get the Firm model
$firm->update($firm_data); // update the model
// add the firm address with a belongsToMany() relationship set on Models\Firm and on Models\Address
$address = $firm->with('addresses')->first();
$addresses = request('addresses'); // get the request data for address
$address->update($addresses); // all ok till here
// deal with the pivot table
// here's the issue...
$address->cities()->sync([request('firm_id'), $addresses['city_id']]);
The firm data and the address data state is saved correctly in the DB, although, the pivot table is filled with two columns instead of one.
I have, in the whole request, one only city_id = 3034856 but to the pivot table it is added the first row firm_id = 1, city_id = 1, "which comes from nowhere":
The pivot table - firm_city
+---------+---------+
| firm_id | city_id |
+---------+---------+
| 1 | 1 |
| 1 | 3034856 |
+---------+---------+
Any thoughts on why this might be happening?
Just to make sure it's right, here the methods for each model Firm and Address:
// On the Firm model
public function cities()
{
return $this->belongsToMany(City::class, 'firm_city');
}
// on the City model
public function firms()
{
$this->belongsToMany(Firm::class, 'firm_city');
}
Thanks in advance for any insight on why this might be happening,
Remove the firm_id:
$address->cities()->sync([$addresses['city_id']]);
Eloquent gets the firm_id from $address, you don't have to specify it.
If you specify
it, Eloquent assumes that you want to sync a City with id=1.

Laravel 4: one to many by on multiple columns?

I'm making a table that essentially maps rows in a table to rows in another table where the structures are as follows:
|--- Words --| |- Synonyms -|
|------------| |------------|
| id | | id |
| en | | word_id |
| ko | | synonym_id |
| created_at | | created_at |
| updated_at | | updated_at |
|------------| |------------|
Now then, I know I can essentially have the words model have many Synonyms through a function like:
public function synonyms()
{
return $this->hasMany('Synonym');
}
No problem, but this method always gets it by the the word_id, and I would like to get it from word_id OR synonym_id that way I don't have to make multiple entries in the DB.
Is there anyway I can do this?
Check laravel docs Eloquent relationships. It would only get word_id because that's the only foreign key I believe.
Also why do you have synonym_id in your Synonyms table?
I believe you are looking for polymorphic relationship.
http://laravel.com/docs/eloquent#polymorphic-relations
I think your best bet is to create a many-to-many relationship with words on itself using the synonyms table as your pivot table.
Add this to your Word model.
public function synonyms()
{
return $this->belongsToMany('Word', 'synonyms', 'user_id', 'synonym_id');
}
Using it:
$word = Word::where('en', '=', 'someword')->first();
foreach($word->synonyms as $synonym) {
// This method would probably return the same word as a synonym of itself so we can skip that iteration.
if($synonym->en == $word->en) {
continue;
}
// Echo the synonym.
echo $synonym->en;
}
I'm a bit confused on you wanting to be able to find synonyms by the word_id or synonym_id but I think if you are using the many-to-many, it won't matter because if you know the synonym, it's still technically just a word, and you'd do the exact same thing.

Resources