Seeding a table that has multiple relationships? - laravel

I have a comments table that belongsTo both a user table and an article table.
How can the table be seeded so that both the user and article foreign key is complete?
After looking through various examples, I found that you could seed the user then attach/save other models...
factory(User::class, 100)->create()->each(function ($user) {
$articles = factory(Article::class, 5)->make();
$user->article()->saveMany($articles);
But how can the same be done for comments with two foreign keys?

Not tested, but it should work, or at least give you the idea.
In the case of comments, you can create a factory like this one, where you will assign the foreign keys taking a random User and Article:
$factory->define(Comment::class, function (Generator $faker) {
return [
'content' => $faker->paragraph,
'user_id' => User::inRandomOrder()->select('id')->first()->id,
'article_id' => Article::inRandomOrder()->select('id')->first()->id
];
});
Note that both User and Article records must exist for you to assign their ids to foreign keys. So to use the factory in the seeder, you will have call it after the users and articles be inserted:
// do the inserts of users and articles, I left this as you show it in the question
factory(User::class, 100)->create()->each(function ($user) {
$articles = factory(Article::class, 5)->make();
$user->article()->saveMany($articles);
});
factory(Comment::class, 50)->create();

Related

Get records from two relations (one is pass through accessor) with paginate

Laravel 7.x
I need to get posts from two relations. Look:
User has Posts;
User has Friends (accessor);
Friends has Posts;
How can I get all own (User) posts and all posts by each Friend, and paginated?
Which the best way to do that?
Only to pass the idea that I want to say:
$user = User::find(1);
$posts = $user->with('posts','friends.posts')->paginate(15); // I wanna get only the Posts collection
I would suggest query your Post model and then apply filter for user and related friends filter.
$user = User::with('friends')->find(1);
$userIds = $user->friends->pluck('id')->toArray();
array_push($userIds, $user->id);
$posts = Post::whereIn('user_id', $userIds)->paginate(15);
You can use hasManyThrough, this relationship is a bit complicated to understand a provide shortcut way to access data of another mode relation.
In your User model make relationship like this way :
public function posts()
{
return $this->hasManyThrough(
Post::class,
Friend::class,
'user_id', // Foreign key on friends table...
'friend_id', // Foreign key on posts table...
'id', // Local key on users table...
'id' // Local key on friends table...
);
}
Now you can get all posts from User model like this way :
$user = User::find(1);
$posts = $user->with('posts')->paginate(15);

Column Shows 'id' Number Instead of Foreign Key Attribute

When using the select2 field/column type in Laravel Backpack, the list view displays the 'id' of the foreign entity instead of the foreign key required (in this case the 'name' of the Session).
Laravel 5.8.4, Backpack 3.4. I asked in GitHub and the response was that my relationships were incorrect in my models. I don't think that's the problem as the name loads in the edit view.
GradeCrudController
$this->crud->addColumn([
'label' => "Session",
'type' => 'select2',
'name' => 'session_id', // the db column for the foreign key
'entity' => 'session', // the method that defines the relationship in your Model
'attribute' => 'name', // foreign key attribute that is shown to user
'model' => "App\Models\Session" // foreign key model
]);
Grade (Model)
public function session()
{
return $this->belongsTo('App\Models\Session');
}
Session (Model)
public function grades()
{
return $this->hasMany('App\Models\Grade');
}
As it's been a few days and nobody has responded, I thought I'd post the answer I came up with. Note that I highly doubt that this is the correct solution, but for my project it will do.
I added a Laravel Observer for the Grade Model. Once a user adds a new record, the observer visits the session table, pulls the name of the session using the key and adds it as a column to the Grades table.
Then in backpack I just display the 'name' column.
There has to be a better way than this... But for now it will do.
I see you're using a "select2" column type. That's not something Backpack provides by default - it only has a "select" column.
Most likely what happened is that Backpack loaded the "text" column, since it couldn't find a "select2" column. Hence, the ID.
Try changing "select2" to "select". It should work for you without any observers/anything else.
I was having a similar issue. I could not get the foreign key attribute to show up no matter what. I finally got it working by doing to following.
Add the foreign key to the belongsTo method. It should be the name of the column in that model that has the ID that is associated with in the belongsTo model.
public function session()
{
return $this->belongsTo('App\Models\Session','name');
}
One other item that I suggest is to make sure all columns that have foreign keys are set to the same data types in the database.

Laravel polymorphic relation load model by morph name and foreign key

I am trying to create a comment to a commentable model (which is created by using polymorphic relations.) using the morphname key.
For example; i would like create a comment giving the morph name (blog) and foreign_key (1). But the morph name could change. So we don't know which key can be with the request. I don't want to create actions for each resource.
AppServiceProvider:
Relation::morphMap([
'blog' => \App\Models\Blog\BlogPost::class,
'product' => \App\Models\Commerce\Product::class,
]);
Conroller -> action:
$this->validate($request, [
'commentable_model' => 'required|string',
'commentable_id' => 'required|integer|min:1',
'comment' => 'required|string'
]);
// i am trying to load data using morph name and foreign key here; but it's not working.
$model = $request->commentable_model::findOrFail($request->commentable_id);
$comment = new Comment;
$comment->comment = $request->comment;
$comment->commentable()->associate($model);
$comment->user()->associate(auth('api')->user());
$comment->save();
Commentable relation:
Schema::create('comments', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->text('comment');
$table->integer('parent_id')->unsigned()->nullable();
$table->foreign('parent_id')->references('id')->on('comments')->onDelete('cascade');
$table->morphs('commentable');
$table->timestamps();
});
Data:
commentable_model => 'blog',
commentable_id => 1
I will send these data via api endpoints. So, i would like to use just morhpnames, not full class names.
I could not get it work.
Any ideas?
I found the solution and it was in front of my eyes all the time actually...
We are making the mapping using the Relation class.
I have checked if can we access the relations with any method or variables and the answer is yes.
The short answer is;
Relation::$morphMap
returns all the mapping as an array.
And my solution for the question is;
$model = Relation::$morphMap[$request->commentable_model]::findOrFail($request->commentable_id);
Accessing that array value using our given morph name (which is blog).

How to show all books not associated with a user. many-to-many relationship

I have 3 tables in my database. users, books and book_user.
book_user is the pivot table that manages the many-to-many relationship between users and books.
I have the Laravel relationship correctly setup and i am able to display all the books associated to a user using the following in Laravel.
$user = Auth::user();
return response()->json($user->books()->get());
Now i am trying to display all the books that are NOT associated to a particular user. I have been struggling for quite some time and cant seem to find anything that works. Any tips would be greatly appreciated.
If the book is not related to the user then you should not try to retrieve it via the user. Instead use the book model:
Book::whereDoesntHave('users', function ($q) {
$q->where('id', \Auth::id());
})->get();
If you want all books not belonging to any user at all, instead of all books not belonging to a particular user, you could do something like :
$books = Book::whereDoesntExists(function($query) {
$query->select('book_user.id_book')
->from('book_user')
->where('book_user.id_book', '=', 'books.id');
// Assuming that the columns on the pivot table are 'id_book' and 'id_user' and the primary key is 'id' in the 'books' table.
});
Here you fetch all books that haven't their id in the book_user pivot table's id_book column. So basically, all books not belonging to any user.

Laravel and pivot table to relate different model

I'm wondering how, but it's bit confusing.
I have fine belongs to many relation between users and groups tables as well as appropriate models for all of that.
But i also have table students, where not all users are student so i students table i maintain user_id field.
My question would be: Can i use pivot table "group_user" for relations between student and group model, in students table i have "user_id" field? and how?
I tried something like
public function students()
{
return $this->belongsToMany('Student','group_user','group_id','user_id');
{
but i don't see the way how to tell eloquent not to take students.id but to take students.user_id???
Assuming these relations:
Subject belongsTo Group
Group belongsToMany User
User hasOne Student
you can easily do this:
$subject = Subject::find($someId);
// of course for multiple subject use eager loading:
// $subjects = Subject::with('group.users.student')->get();
$users = $subject->group->users; // related users
foreach ($users as $user)
{
$user->student; // null|student model
}

Resources