Laravel Eloquent - sync() on a Morph relationship - laravel

It seems like there is no sync() function for morph tables on Laravel.
I have two tables avails and questions. questions is a morphMany table. I want to use the sync command, and this is what I did:
Avail::find($id)->questions()->sync($some_ids);
This gives me the following error:
Call to undefined method Illuminate\Database\Query\Builder::sync()
So is there a way to get sync to work or am I just not doing this right?

morphMany is a 1-to-many, not many-to-many relation, so there is no sync method.
Use saveMany/save instead, and associate for the other way around.
to mimic sync behaviour for this kind of relation you can do this:
$questionsOld = $avail->questions()->get();
$questionsOld->each(function ($question) {
// appropriate fields here:
// 1*
$question->FOREIGN_ID = null;
$question->FOREIGN_TYPE = null;
$question->save();
});
$questionsNew = Question::whereIn('id', $someIds)->get();
// *2
$avail->questions()->saveMany($questionsNew->getDictionary());
Now:
*1 you can't use dissociate and must explicitly set relation_id and relation_type to null, since morphTo doesn't override this method, so it wouldn't work as expected.
*2 getDictionary() returns simple array of models, instead of collection. It's required becase saveMany typehints its parameter as array.

Related

Can I use with() instead Load() Laravel eloquent

$school=$school->teachers->load('TeacherCourses');
This will return all teachers with their courses in my laravel project..
can I use with() function like below?? if I cant then how ?
$school=$school->teachers->with('TeacherCourses');
Yes, but the correct syntax would be different. load() is used after you've retrieved the model from the Database, but with() is used prior to that.
$school = School::first();
$school->teachers->load('teacherCourses');
̶T̶h̶i̶s̶ ̶w̶o̶n̶'̶t̶ ̶w̶o̶r̶k̶,̶ ̶a̶s̶ ̶̶t̶e̶a̶c̶h̶e̶r̶s̶̶ ̶s̶u̶g̶g̶e̶s̶t̶s̶ ̶a̶ ̶C̶o̶l̶l̶e̶c̶t̶i̶o̶n̶,̶ ̶w̶h̶i̶c̶h̶ ̶d̶o̶e̶s̶n̶'̶t̶ ̶h̶a̶v̶e̶ ̶a̶ ̶̶l̶o̶a̶d̶(̶)̶̶ ̶m̶e̶t̶h̶o̶d̶.̶ Corrected by #patricus; Collections do have a load() method, so ->teachers->load('teacherCourses') will work. Alternative syntax would be:
$school->load('teachers.teacherCourses');
The more efficient way to write this is:
$school = School::with('teachers.teacherCourses')->first();
This will eager-load both the teachers and teacherCourses nested relationships, in a minimal number of database calls.
Edit
Route Model Binding will do this behind-the-scenes:
$school = School::findOrFail($schoolId);
Since it's only loading a single Model, using load() is perfectly acceptable. with() loads the relationships via a single query, for a total of 1 + (# of relationships loaded) queries (3 in your case, school, teachers and teacherCourses). load() will do the same thing, so there's no need to use with() over load() in this case.

How to dissociate elements from a HasMany relation?

There's the save and saveMany methods on the HasMany relation class, but where are the dissociate(Many)/detach(Many) methods? There's also no built-in way to get the inverse relationship method, so what's the best way to dissociate an array of id's/models from a HasMany relationship object.
Currently I'm using:
$hasMany = $parent->theRelationship(); // Get the relationship object.
$child = $hasMany->getRelated(); // Get an empty related model.
$key = $hasMany->getForeignKeyName(); // Get the name of the column on the child to set to NULL.
$child->findMany($IDs)->each(function($model) use ($key) {
$model->$key = NULL;
$model->save();
});
This could be alot shorter with something like:
$hasMany = $parent->theRelationship();
$hasMany->dissociate($IDs);
Bonus points if you have any official answers from Taylor as to why he hasn't implemented this, I've seen him close feature requests of this kind on GitHub.
I am not sure why there isn't a function, but to be more performant than your example, you could use the DB class like:
\DB::table('child_table')->where('parent_id', $parent->id)->update(['parent_id' => null]);
You could use detach like so;
$parent->theRelationship()->detach([1,2,3])
Where you pass an array of IDs.
From Laravel documentation:
"For convenience, attach and detach also accept arrays of IDs as input"
The performatic way (1 db update):
$partent->theRelationship()->update(['parent_id' => null]);
The readable way (multiple db updates):
$parent->theRelationship->each->parentRelationship()->dissociate();

In Laravel Eloquent, how to check for multiple relationship existence in one "has"?

So, in order to check the existence of a relationship on a model, we use the has function on the relationship like model1->has('relationship1').
While it is possible to supply the model1->with() function with an array of relations to eager load them all, both has and whereHas functions do not accept arrays as parameters. How to check for the existence of multiple relationships?
Right now, I am running multiple has functions on the same model (The relations are not nested):
model1->has('relationship1')
->has('relationship2')
->has('relationship3')
But that is tedious and error-prone. Solution anyone?
There unfortunately isn't a way to pass an array of relationships to has() or whereHas(), but you can use a QueryScope instead. On your Model, define the following:
public function scopeCheckRelationships($query){
return $query->has("relationship1")->has("relationship2")->has("relationship3");
}
Then, when querying your Model in a Controller, simply run:
$result = Model::checkRelationships()->get();
The function name to use a Scope is the name of the function, minus the word scope, so scopeCheckRelationships() is used as checkRelationships().
Also, it's actually possible to pass the relationships you want to query as a param:
public function scopeCheckRelationships($query, $relationships = []){
foreach($relationships AS $relationship){
$query->has($relationship);
// Might need to be `$query = $query->has(...);`, but I don't think so.
}
return $query;
}
...
$result = Model::checkRelationships(["relationship1", "relationship2", "relationship3"])->get();
In case you need this to be dynamic.
Here's the documentation for Query Scopes if you need more info: https://laravel.com/docs/5.8/eloquent#query-scopes

Laravel 5 many to many attach two columns not working

in my Laravel 5 application i have many to many relationship between two models.I'm using a pivot table to keep track of them. In my both models i have defined belongsToMany method with relevant pivot table name as parameter.Then i'm going to add values to the pivot table in controller. It works fine for only one column. For the other one it's not inserting any values. In the Controller i'm calling like this,
$this->mymodel->addToPivotTable($values);
Should i pass two parameters there?
I could able to solve this. I needed to call the method after saving my data set to the table.It's like this,
public function add(Request $request){
$post = $request->all();
$arr = array(1,4,5);
$result = $this->mymodel->create($post);
$result->classifications()->attach($arr);
}

Doctrine toarray does not convert relations

I followed doctrine documnetation to get started. Here is the documentation.
My code is
$User = Doctrine_Core::getTable("User")->find(1);
when I access relations by $User->Phonenumbers, it works. When I convert User object to array by using toArray() method, it does not convert relations to array. It simply display $User data.
Am I missing something?
By using the find method you've only retrieved the User data which is why the return of toArray is limited to that data. You need to specify the additional data to load, and the best place to do this is usually in the original query. From the example you linked to, add the select portion:
$q = Doctrine_Query::create()
->select('u.*, e.*, p.*') // Example only, select what you need, not *
->from('User u')
->leftJoin('u.Email e')
->leftJoin('u.Phonenumbers p')
->where('u.id = ?', 1);
Then when toArray'ing the results from that, you should see the associated email and phonenumber data as well.
I also noticed an anomaly with this where if you call the relationship first then call the ToArray, the relationship somehow gets included. what i mean is that, taking your own eg,
$User = Doctrine_Core::getTable("User")->find(1);
$num= $User->Phonenumbers->office; // assumed a field 'office' in your phone num table
$userArray = $user->toArray(true);
In the above case, $userArray somehow contains the whole relationship. if we remove the $num assignment it doesn't.
am guessing this is due to doctrine only fetching the one record first, and it's only when you try to access foreign key values that it fetches the other related tables

Resources