Is there a simple solution to detach (without deleting) all related models through a hasMany Relation on a model?
For example, I have two tables:
College (id, name)
Student (id, name, college_id(nullable) )
In the College model I define this relationship:
class College extends Model
{
public function students()
{
return $this->hasMany('App\Student','college_id','id');
}
}
What is the best way to detach all current College students from the College (i.e, get all the students of the college and set their college_id to null)?
Is there an easy way to detach all students from the College model using eloquent?
Something like
class College extends Model
{
...
public function detachAllStudents()
{
...
}
}
P.S. already have read this question Laravel hasMany detach, but get errors when I try to apply it to my application
Yes, you can detach all the college students as below. Actually, we already have the college_id in your students table and we have to make this college_id null somehow. This is not called detach in Eloquent. The word detach come when you have many to many relationships. So, let's just update the students and see if it works or not.
$college->students()->update(['college_id' => null);
So, you method can be completed as below:
public function detachAllStudents()
{
$college->students()->update(['college_id' => null]);
}
That's it!
Directly from the documentation https://laravel.com/docs/5.8/eloquent-relationships:
Toggling Associations
The many-to-many relationship also provides a toggle method which "toggles" the attachment status of the given IDs. If the given ID is currently attached, it will be detached. Likewise, if it is currently detached, it will be attached:
$user->roles()->toggle([1, 2, 3]);
Despite Imran answer perfectly fits this scenario I would add a more generic approach. Let's say $college respects an interface instead of an implementation, you wouldn't know the foreign key at run time.
<?php
use Illuminate\Database\Eloquent\Relations\HasMany;
interface HasStudents {
public function students(): HasMany;
}
class College extends Model implements HasStudents
{
public function students(): HasMany;
{
return $this->hasMany(Student::class, 'college_id', 'id');
}
}
function detachStudents(HasStudents $model): void {
$studentsRelationship = $model->students();
$studentsRelationship->update([
$studentsRelationship->getQualifiedForeignKeyName() => null
]);
}
detachStudents($college);
Related
In a project, let's say that we have Customers, and each customer can have one Voucher. The voucher, though, may be for a different thing for different customers - maybe for a Hote, a Car or a Flight.
We have a table of flight voucher codes, a table of hotel voucher codes and a table of car voucher codes.
When a customer is allocated a voucher, therefore, we allocated them the next code for the relevant thing that they're getting a voucher for. But rather than have multiple tables (customer_car_voucher, customer_hotel_voucher, and so on) I would rather have a Voucher table which is, in turn, linked to the relevant voucher type.
What I want to be able to do is just go $customer->voucher->code to get the relevant code, whether that be a flight, a hotel or a car. Other vouchers may be added at a later date, you see, for different things.
I think I can do this using morphable relationships - the voucher morphsTo car, hotel and flight, so within the the voucher table there is a "voucherable_type" and a "voucherable_id". But damned if I can get it to work.
Any help, please? Am I going about it wrong?
you arte right. and for a hint use:
public function customer()
{
return $this->belongsTo(Customer::class):
}
public function voucherable()
{
return $this->morphTo();
}
in voucher model.
and for each flight,car,hotel include:
public function voucher(){
return $this->morphOne(Voucher::class,'voucherable');
}
you can see Laravel morph relationship too for more help.
In Laravel's Eloquent ORM is used for morphable relationships.
First, create two Models AirVoucher and Voucher.
First, the AirVoucher model uses the following relationship.
public function voucher()
{
return $this->morphOne(Voucher::class, 'voucherable');
}
Second, the Voucher model uses the following relationship.
public function voucherable()
{
return $this->morphTo();
}
You can use the following Laravel official relationship document for more help.
Laravel Morph Relationships.
you must use laravel Polymorphic Relationships.
in Voucher model set this model as polymorph model(function name = modelname+"able"):
public function voucherable() \Illuminate\Database\Eloquent\Relations\MorphTo
{
return $this->morphTo();
}
then in Car model (or hotel/fight) set realation(function name= polymorph name):
if each car has one voucher, use morphOne:
public function files(): \Illuminate\Database\Eloquent\Relations\MorphOne
{
return $this->morphOne(Voucher::class, 'voucherable');
}
if each car has many voucher, use morphMany:
public function files(): \Illuminate\Database\Eloquent\Relations\MorphMany
{
return $this->morphMany(Voucher::class, 'voucherable');
}
Retrieving The Relationship
$car = Car::find(1);
$vocher = $car->voucher;
laravel docs
I am using laravel 5.7. I have now a situation where i have a Fight model with structure user_id, fightable_id
i have two other tables users and monsters. so users_id refers to users (a user can have many fights) and fightable_id can refer to either a user or a monster (monsters table). so I have to define the functions for the relation ship
for User model i have to do
1.for polymorphic one to many relationship
public function fights()
{
return $this->morphMany('App\Fight', 'fightable');
}
2.for simple one to many relationship
public function fights()
{
return $this->hasMany('App\Fight');
}
I am confused now. ofcourse the only way is to change the functions name. but i will be doing the correct thing by changing the function names right (as both the functions have same name). or is there anything I am doing wrong?
I'm not sure I understand your question completely but I'll try my best!
Also this post will really help you understand the problem you are facing I think,
Laravel morph relationship
Using the code from the post I linked but taking your tables would produce models defined like this.
User Model
class User extends Model
{
public function fights()
{
return $this->hasMany('App\Fight');
}
}
Fight Model
class Fight extends Model
{
public function user()
{
return $this->belongsTo('App\User');
}
public function fightable()
{
return $this->morphTo();
}
}
Monster Model
class Monster extends Model
{
public function fight()
{
return $this->morphOne('App\Fight', 'fightable');
}
}
If you still feel this has not answered your question or need some more help just let me know!
I'm learning Laravel and Laravel eloquent at the moment and now I try to solve a problem using relations in Laravel.
This is what I want to archive:
The database holds many sport clubs. A sport club has a lot of teams. Each team has games. The teams table has a column named club_id. Now I want to create Eloquent relations to get all games of a club.
Here is what I got so far:
Club model
id => PRIMARY
public function games()
{
return $this->hasMany('App\Models\Games')->whereHas('homeTeam')->orWhereHas('guestTeam');
}
Game model
home_id => FOREIGN KEY of team ; guest_id => FOREIGN KEY of team
public function homeTeam()
{
return $this->belongsTo('App\Models\Team','home_id')->where('club_id','=', $club_id);
}
public function guestTeam()
{
return $this->belongsTo('App\Models\Team','guest_id')->where('club_id','=', $club_id);
}
Team model
id => PRIMARY ; club_id => FOREIGN
In my controller all I want to do is Club::findOrFail($id)->games()
Executing the code above returns a SQL error that the games table does not have a column named club_id.
What is the correct way to create this kind of relation?
Thanks!
EDIT
Thanks to Nikola Gavric I've found a way to get all Games - but only where club teams are the home or away team.
Here is the relation:
public function games()
{
return $this->hasManyThrough('App\Models\Game','App\Models\Team','club_id','home_id');
}
How is it possible to get the games where the home_id OR the guest_id matches a team of the club? The last parameter in this function does not allow an array.
There is method to retrieve a "distant relationship with an intermediary" and it is called Has Many Through.
There is also a concrete example on how to use it which includes Post, Country and User, but I think it will be sufficient to give you a hint on how to create games relationship inside of a Club model. Here is a link, but when you open it, search for hasManyThrough keyword and you will see an example.
P.S: With right keys naming you could achieve it with:
public function games()
{
return $this->hasManyThrough('App\Models\Games', 'App\Models\Teams');
}
EDIT #01
Since you have 2 types of teams, you can create 2 different relationships where each relationship will get you one of the type you need. Like this:
public function gamesAsHome()
{
return $this
->hasManyThrough('App\Models\Games', 'App\Models\Teams', 'club_id', 'home_id');
}
public function gamesAsGuests()
{
return $this
->hasManyThrough('App\Models\Games', 'App\Models\Teams', 'club_id', 'guest_id');
}
EDIT #02
Merging Relationships: To merge these 2 relationships, you can use merge() method on the Collection instance, what it will do is, it will append all the records from second collection into the first one.
$gamesHome = $model->gamesAsHome;
$gamesGuests = $model->gamesAsGuests;
$games = $gamesHome->merge($gamesGuests);
return $games->unique()->all();
Thanks to #HCK for pointing out that you might have duplicates after the merge and that unique() is required to get the unique games after the merge.
EDIT #03
sortBy also offers a callable instead of a attribute name in cases where Collection contains numerical indexing. You can sort your Collection like this:
$merged->sortBy(function($game, $key) {
return $game->created_at;
});
When you define that Club hasMany games you are indicating that game has a foreign key called club_id pointing to Club. belongsTo is the same but in the other way. These need to be coherent with what you have on your database, that means that you need to have defined those keys as foreign keys on your tables.
Try this...
Club model
public function games()
{
return $this->hasMany('App\Models\Games');
}
Game model
public function homeTeam()
{
return $this->belongsTo('App\Models\Team','home_id');
}
public function guestTeam()
{
return $this->belongsTo('App\Models\Team','guest_id');
}
Your Query like
Club::where('id',$id)->has('games.guestTeam')->get();
i have a situation where i have entity, base, which can contain ingredients, drinks and bases itself. I know how to make relations to ingredients and drinks, but do not know how to do it when it comes to bases within base. Any help would be appreciated. I can not use solution with parent_id, because one base can belong to several other bases, and do not want duplicates in the bases table. I need some solution with pivot table.
Assuming you have parent_id in bases table
class Base extends Model
{
public function children()
{
return $this->hasMany(Base::class,'parent_id','id');
}
public function parent()
{
return $this->belongsTo(Base::class,'id','parent_id');
}
}
And then you can easily access bases of a base like this
foreach($base->children() as $childBase){
$childBase->ingredients();
$childBase->drinks();
}
Finally i found solution, self referencing entity can be done with itself with Many To Many relation.
Model relation look like this :
class BaseDrink extends Model {
public function drinkbase(){
return $this
->belongsToMany('App\BaseDrink', 'basedrink_basedrink','basedrink_id', 'parent_basedrink_id')
->withPivot('created_by')
->withPivot('last_modified_by')
->withPivot('id')
->withTimestamps();
}
}
I have three tables - "courses", "lessons" and "tasks". Each lesson belongsTo a course, and each task BelongsTo a lesson. I want to output a task, showing the task name, the lesson name, and the course name. How do I access the course table data? To get the lesson information linked to a course, I have used the following in my Task model:
$lessonName = $this->lessons->lesson_name;
To get the course name associated to that lesson, I have tried the following with no success, but I am really guessing here:
$courseName = $this->lessons->courses->course_name;
My model relationships are as follows:
Course.php
public function lessons()
{
return $this->hasMany('App\Lesson');
}
Lesson.php
public function tasks()
{
return $this->belongsTo('App\Task', 'task_id', 'id');
}
Task.php
public function lessons()
{
return $this->belongsTo('App\Lesson', 'lesson_id', 'id');
}
Where am I going wrong? Thanks
there is another way you can do this by using accessors.
on your Task model do the following:
public function getLessonAttribute(){
return Lesson::where('id', $this->attributes[*foreign_key_field*])->first();
}
Here you receive all the data regarding the lesson that the task belongs to, and can use them as any other attribute (field) of the model.
on your Lesson model get the course that it belongs to.
public function getCourseAttribute(){
return Course::where('id', $this->attributes[*course_foreign_key_field*])->first();
}
and then assuming that $task is your collection, you can access the lesson and the course like the following in blade:
$task->lesson->lesson_name and $task->lesson->course->course_name
In your lesson.php model doesn't exist relationship courses so there are your issue. Use answer what is told you #jeroenF
So you want the inverse of hasManyThrough?
The hasManyThrough feature of Laravel (see their site) facilitates connecting your Courses to Task directly, without having the intermediate connection in a separate relationship.
You are looking for the inverse?