How do i access data using two BelongsTo? - laravel

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?

Related

Laravel Create multiple records in Pivot table

I'm trying to create a function in our Laravel 5.8 app that would add multiple records to a pivot table. At present we have the following setup;
Users
Training Courses
Users Training Courses (pivot table for the above relationships, with a few extra fields)
I want to be able to show all users in the database, then check their name, pick a training course and hit "Add" and it'll create a record in the pivot table for each user that was selected.
I can't figure out where to start with this - it seems like I need to have a "for each user selected, run the store function" loop in the controller, but I have no idea where to start.
I wasn't sure if there was an easy way to do this in eloquent or not. Is there a simple way to do this?
Eloquent does this automatically if you set up the relationships correctly and you don't have to worry about pivot tables.
class Users
{
public function trainingCourses()
{
return $this->hasMany(TrainingCourses::class);
}
}
class TrainingCourses
{
public function user()
{
return $this->belongsTo(User::class);
}
}
Then you can use the save() method to create the relationship. But I find it better to wrap this function inside a helper method that you can use throughout your code:
class Users
{
...
public function assignTrainingCourse(TrainingCourse $trainingCourse)
{
return $this->trainingCourses()->save($trainingCourse);
}
}
In your code, you could then do something as simple as this:
$user = User::find(1);
$trainingCourse = TrainingCourse::find(1);
$user->assignTrainingCourse($trainingCourse);
Building on this, suppose you have the following route to assign a training course, where it expects a trainingcourse_id in the request:
Route::post('/users/{user}/trainingcourses', 'UserTrainingCoursesController#store');
Thanks to route model binding, Laravel can inference the parent model (user) from the URL, and your controller might look like this:
// UserTrainingCoursesController.php
public function store(User $user)
{
$trainingCourse = TrainingCourse::find(request()->input('trainingcourse_id'));
$user->assignTrainingCourse($trainingCourse);
return back();
}
Of course, you'll want to put some validation in here, but this should get you started.

Laravel Eloquent Relation belongsTo update

I am trying to update/delete/create in belongsTo relations.
Company has many sports
sports is belonging to Company
Here is two models.
class CompanySports
{
public function company()
{
return $this->belongsTo(Company::class, "company_id","id");
}
class Company
public function sports()
{
return $this->hasMany(CompanySports::class,"company_id","id");
}
}
at controller, when sports is added or modified or remove, what is the best practice to update?
i know that many to many, sync can be used. In this, what is the best solution? Should i compare everytime after loading all from database which is not good practice i believe.
From your code, I would first recommend putting your models in separate files, and ensuring they are singular. If you use the artisan make:model command to generate the stubs, it should do this for you.
// app/CompanySport.php // <-- NOTE singular
class CompanySport // <-- NOTE singular
{
public function company()
{
return $this->belongsTo(Company::class, "company_id","id");
}
}
// app/Company.php
class Company {
public function sports()
{
return $this->hasMany(CompanySport::class,"company_id","id"); // singular
}
}
From there, I find it helpful to build helper methods in the various classes so that the grammar sounds natural and more importantly, belongs to the model. For example:
// app/Company.php
class Company
{
...
public function addSport(CompanySport $sport)
{
$this->sports()->save($sport);
}
public function removeSport(CompanySport $sport)
{
$this->sports()->find($sport->id)->delete();
}
}
These helper functions can then be easily called from anywhere, e.g. controller:
// CompanySportsController.php
public function store(Company $company, CompanySport $sport)
{
$company->addSport($sport);
return redirect('/company/' . $company->id);
}
If you are using these helpers, there is no comparing or sync to be done since you are only using a one to many relationship. Eloquent does everything for you.
Also, I've found this cheatsheet particularly helpful when building out the initial relationships and scaffolding of a new app.
While adding new record of Company Model, you need not to do anything as there is no child for it yet.
While updating an instance of a Company model, again you need not to update anything on its children. As relationship are based on id(primary key) which I believe you don't change while updating.
And now for deleting there are some questions. Do you want to delete the children when the parent is deleting? If so, you can use ON DELETE CASCADE which you can set up in migration like
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
in your spors table.
Well you can make your own function too like answered in here
Well if you don't want to delete the children, you can use softdelete on your Model. set up the relations then like
CompanySports
public function company()
{
return $this->belongsTo(Company::class, "company_id","id")->withTrashed();
}
This way you can get the parent of a children without any error though the parent is deleted.

Laravel hasMany relationship detach all

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);

A model with polymolymorphic ony to many and simple one to many relationship

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!

How to eloquent relationship with multiple results

Problem
I have two classes, Users & Posts. A user "hasMany" posts and a post "belongTo" a user. But when I call "User::all()" it doesn't automatically pull the users posts for obvious reasons, because if my user had relations to 100 different tables pulling all users would start to become pretty chunky.
Question
Is there a way to pull all users and all user->posts in one or few lines of code without going through a foreach loop?
I know i can use a mutator but the problem I have is my field is called user_id and i have tested it with this code:
public function getUserIdAttribute($id)
{
return User::find($id);
}
But it will replace "user_id" field value with a user object, Id rather have it set to its own "temporary user" field within the result. I'm trying to find best practice!
Thank you in advance.
What you're looking for is called Eager Loading
Inside your post model :
class Post extends Model
{
protected $table='posts';
public $primaryKey='id';
public function user(){
return $this->belongsTo('App\User','user_id');
}
}
now you want to get post with user use below code :
$posts=Post::with('user')->get();
inside your user model :
class User extends Model
{
public function posts(){
return $this->hasMany('App\Model\Post');
}
}
now you want to get a user with all posts :
$user=User::where('id',$id)->first();
$user_posts=$user->posts;

Resources