Laravel Factories - How to save nested relationships - laravel-5

I was working with laravel factories and stuck with nested relation ships.
For example a user may have many posts and a post has many comments.
i was able to save posts with user by each() method but was not able to save comments with posts
$users = factory(App\User::class, 3)->create()
->each(function ($user) {
$user->posts()->saveMany(factory(App\Post::class, 5)->make());
});
as save() method accepts array so i must have to use make() method with posts but now i can't attach comments with the posts.
after hour of searching i could not fond the solution but now i have solved it.
I am posting it in answers for the fellows who are looking for the solution and please update me if there have more decent solution to this.
Thanks

Here is how i solve this:
$users = factory(App\User::class, 3)->create()
->each(function ($user) {
$user->posts()->saveMany(factory(App\Post::class, 5)->make());
});
foreach ($users as $user){
foreach ($user->posts as $post){
$post->comments()->saveMany(factory(App\Comment::class, 5)->make());
}
}

Related

Consultation through relationships in laravel

I searched endlessly for a question that answered my question here and I didn't find it. My question is as follows, I have 3 models: User, Post and Comments. Where user has a relationship with one to many posting, and Post has a relationship with one to many comments as well. How can I get all of the user's comments on all posts?
Currently my solution looks like this:
Models Users:
public function comments(){
$comments = array();
foreach ($this->posts()->get() as $el) {
foreach ($el->posts()->get() as $nEl) {
array_push($comments, $nEl);
}
}
return collect($comments);
}
I would like a less expensive and native solution for laravel, if any.
On your User model, something like:
public function getComments()
{
return Comment::whereHas('posts', function ($query) {
$query->where('user_id', $this->id);
})->get();
}
Also see:
https://laravel.com/docs/6.x/eloquent-relationships#has-many-through

Laravel, eloquent and foreach in controller

I'm new in Laravel and I'm curious about one thing. I have 3 database tables: posts, comments, replies. I want to make a simple delete from each. But obviously post has many comments and comments has many replies. Whole thing is about these replies. Seems like I can't reach them.
I have properly working relations between tables.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Post;
use App\Comment;
use App\Reply;
use App\Traffic;
use Image;
use Illuminate\Support\Facades\Storage;
class PostsController extends Controller
{
//few others things here...
public function destroy($id) //$id is an id of post
{
// Select Post and comments of post
$post = Post::find($id);
$comments = Comment::where('post_id', $id);
//remove image (working fine)
Storage::delete('public/img/' . $post->image);
// delete all replies of post comments <<< Here is the issue. Can I even do like this?
foreach ($comments as $comment) {
$post_comment_reply = Reply::where('comment_id', $comment->id);
$post_comment_reply->delete();
}
// delete post record (working fine)
$post->delete();
//delete comments of post (working fine)
$comments->delete();
// return to user profile (working fine)
return redirect('/home')->with('success', 'Post has been deleted');
}
There is an even easier way to do so.. if you just add a database constraint to the foreign key in the replies table to the comment..
$table->unsignedInteger('comment_id');
$table->foreign('comment_id')->references('id')->on('comments')
->onDelete('cascade');
The last part: onDelete('cascade') ensures that all the replies will be deleted once a comment has been deleted :) so you don't have to manually do that in the application layer.
Let me know if it makes sense :)
Instead of deleting the replies in a loop, you can delete them all at once:
$comments = Comment::where('post_id', $id);
$comment_ids = $comments->pluck('id');
Reply::whereIn('comment_id', $comment_ids)->delete();
What's wrong in your code is that you create a db query but does not execute it:
// You forgot the ->get() following the where statement
foreach ($comments as $comment)
$post_comment_reply = Reply::where('comment_id', $comment->id)->get();
$post_comment_reply->delete();
}
However the code altogether is not quite optimal, you could make it directly on database level with onDelete('cascade'), or simply create a request to delete the replies without retrieving them and reducing the number of query to the db, like such:
foreach ($comments as $comment)
Reply::where('comment_id', $comment->id)->delete();
}
One step further reducing db queries like suggested above:
Reply::whereIn('comment_id', $comments->pluck('id'))->delete();
If you want to delete the relations via Laravel, you have to override the boot function.
Override the boot() on your Comment model like
protected static function boot()
{
static::deleting(function (Comment $model) {
$model->replies()->delete();
});
parent::boot();
}
This will delete all the replies associated to a comment when that comment is deleted via eloquent.

Laravel Eloquent: Get current id from inner function

I know, we can do this in the controller:
User::with('post')->get();
It will get every user's post from the database, based on users.id.
But the problem is, I want to do this:
User::with(['post' => function($query) {
# Throw users.id here...
}])->get();
How to do that?
You should get the users first, and then load related posts with a separate query and merge them manually.
$users = User::get();
$posts = Post::whereIn('user_id', $users->pluck('id'))->get(); // Get your additional data in this query
$users->each(function ($user) use ($posts)
{
$user->posts = $posts->where('user_id', $user->id);
});
Note: I did not test the code above. It's just an example to show you how to accomplish what you are trying to do.

Laravel > nested eager load with restriction

Assumed we've got users, friends and restaurants. Don't want to go to deep into the Model and relationship setup.
While me as a user is logged in: How can I get all friends who are "customers" of the restaurant?
I've got this and it's already working:
$friends = array_dot(Auth::user()->friends()->select('users.id')->get());
$customers = Restaurant::with(['users' => function($query) use($friends) {
$query->whereIn('users.id', $friends);
}])->find(restaurant_id);
But is this even possible with a single query?
It sounds like you want to find all of your friends that have a relationship to the restaurant. If so, you're looking for whereHas(). General idea:
$restaurantId = 1;
$user = Auth::user();
$friendCustomers = $user->friends()->whereHas('restaurant', function ($query) use ($restaurantId) {
$query->where('id', $restaurant_id);
})->get();
You can read more about querying relations, and whereHas, here.

Laravel Relationships Conditions - 3 tables

I've got a situation where I've got Posts, Users and Comments.
Each comment stores a post_id and a user_id. What I want to do is get all of a user's comments on a particular post, so that I can do a call like this:
$comments = Auth::User()->comments(post_id=x)->text
(where I know what x is)
I have:
User->HasMany(comments)
Comments->HasOne(User)
Comments->HasOne(Project)
Project->HasMany(comments)
I feel like there needs to be a where or a has or a wherehas or something thrown in.. the best I can manage is that I pull Auth::User()->comments into an array and then search through the array until I find the matching post ID.. that seems wasteful.
with doesn't apply any join, so you can't reference other table.
You can use this:
// User model
public function comments()
{
return $this->hasMany('Comment');
}
// Comment model
public function scopeForPost($query, $postId)
{
$query->where('post_id', $postId);
}
// then you can do this:
Auth::user()->comments()->forPost($postId)->get();
Alternatively you can eager load comments with constraint:
User::with(['comments' => function ($q) use ($postId) {
$q->where('post_id', $postId);
}])->find($someUserId);
// or exactly the same as above, but for already fetched user:
// $user .. or
Auth::user()->load(['comments' => function ($q) use ($postId) {
$q->where('post_id', $postId);
}]);
// then you can access comments for $postId just like this:
Auth::user()->comments; // collection
When you need to filter your relations, you just have to do it in your Eloquent query:
$data = User::with('posts', 'comments')
->where('users.id', Auth::User()->id)
->where('posts.id', $postID)
->get();
Then you can
foreach($data->comments as $comment)
{
echo $comment->text;
}
Your Comments table would have foreign keys Post_Id and User_ID
To Access all the comments of a particular post from a particular user , can you try this way?
Comment::select('comments.*')
->where('comments.user_id', Auth::user()->id)
->leftJoin('posts','posts.id','=','comments.post_id')
->leftJoin('users','users.id','=','comments.user_id')
->get();
Am sure there is better way to achieve it, but this should give you desired results.
Note use aliases if you have conflicting column names
Let me know if this worked.

Resources