laravel eager loading for self relation - php

I have the model that has relation to itself like this
public function children() {
return $this->hasMany(AppointmentPart::class,'parent_id');
}
I want to eager loading for queries for this model but because every row can have child with() method doesn't work for me
$parts = AppointmentPart::whereParentId(0)->with('children')->get();
my blade:
<ul id="treeData">
#foreach($parts as $part)
<li id="{{ $part->id }}"
#if(!empty($part->children)) class="folder" #endif>
{{ $part->title }}
#if(!empty($part->children))
#include('appointment::admin.appointment.layout._part_child', array('parts' => $part->children))
#endif
</li>
#endforeach
</ul>
content of _part_child :
<ul>
#foreach($parts as $part)
<li id="{{ $part->id }}" #if(!empty($part->children)) class="folder" #endif>
{{ $part->title }}
#include('appointment::admin.appointment.layout._part_child',['parts'=>$part->children])
</li>
#endforeach
</ul>
how can I do this?

You should use a whereHas() function. Have a look at the official documentation.
If you need even more power, you may use the whereHas and orWhereHas methods to put "where" conditions on your has queries. These methods allow you to add customized constraints to a relationship constraint [...]
So in your case you have to change your method as follows:
$parts = AppointmentPart::with('children')->whereHas('children', function($query) use ($parent_id) {
$query->where('parent_id', '=', $parent_id);
})->get();
you can also create a local scope in order to create a reusable query statement.

This is an example for you. This might help to solve your problem. I have Structure Model which contain organizational structure of a company.
In my StructureController.php
public function index()
{
$parentStructures = Structure::where('parent_id', 0)->get();
return view('structure.structureTreeview', compact('parentStructures'));
}
In my Structure.php, I add with() at the relationship for eager loading.
public function children()
{
return $this->hasMany(Structure::class, 'parent_id')->with('children');
}
And, if you you check with this package here , it won't show any warning.
Reference : check this website here

Related

create counter for blog categories in laravel

I´m traying to create one counter for my blog´s categories. This should appear to the right name side of my category . i´m using my model with variable appends, that after i will use in my blade for show my result in one span. But i don´t know very well how i can use count in my Model. I´m doing this in my model Blog.
my variable appends contain:
protected $appends = [
'custom_fields',
'has_media',
'restaurant',
'blog_category',
'viewer',
'postCounter',
];
i´m traying this:
return $this->blogs()->count();
i have a relation between blog and blog_category with:
public function blogCategory()
{
return $this->belongsTo(\App\Models\BlogCategory::class, 'blog_category_id', 'id');
}
i want to do in my view appear for example:
innovation (2)
in my view i´m doing this:
#foreach($categories as $category)
<li>{{ trans('web.blog_category_'.$category->name) }}<span>{{$category->postCounter}}</span></li>
#endforeach
but always returned me 0, and i have post with categories
updated
With laravel relationship withCount you can do this easily. If you want to count the number of results from a relationship without actually loading them you may use the withCount method, which will place a {relation}_count column on your resulting models.
add withCount method to your query
$categories = Category::withCount('blogCategory')->get();
You can access the count in your foreach loop
// $category->blogCategory_count
#foreach($categories as $category)
<li>
<a href="{{ url('blogs/'.$category->name) }}">
{{trans('web.blog_category_'.$category->name) }}
</a>
<span>{{$category->blogCategory_count}}</span>
</li>
#endforeach

how to show multiple category name from checkbox value in eloquent relationship laravel

firstly, i'm a newbie in laravel.
i insert a array data as a string in server.
column name "category_id" & value like "1,2,3,4".
Now i just want to show these id's category name with eloquent relationship.
Using laravel latest version.
view page
{{$category_var = explode(',',$post->category_id)}}
#foreach($category_var as $category)
#if($category)
<span class="badge mb-3">{{$post->category->category}} </span>
#endif
#endforeach
class page
public function category(){
return $this-> belongsTo(Category::class);
}
Controller page
public function index()
{
$posts = post::paginate(9);
return view('pages/home',compact('posts'));
}
anything else need just ask me.
Thanks
just use the benefits of models and relation like below:
#foreach($posts as $post)
#foreach($post->category as $category)
<span class="badge mb-3">
{{ $category->(what ever you want from category model) }}
</span>
#endforeach
#endforeach

Laravel - getting relation in loop - best practices

Just an example:
let's say I have Post model, and the Comment model. Post, of course, have Comments, one-to-many relation.
I have to display list of posts with comments below it.
I'll get my posts in the controller:
$posts = Post::get(), I'll pass it to the blade view and then I'll loop through it
#foreach($posts as $post)
{{ $post->title }}
{{ $post->comments }}
#endforeach
where $post->comments is some relation
public function comments()
{
return $this->hasMany(Comment::class);
}
As we know, that query will be executed many times.
Now my question: how we should optimize it?
Return Cache::remember in the getter?
Get (somehow?) those comments, when getting the posts in one query? Something like join query? I know that I can write that kind of query, but I'm talking about Eloquent's query builder. And then how get the comments within the loop? Wouldn't {{ $post->comments }} call the relation again instead of getting stored data?
Different solution?
You can do $posts = Post::with('comments')->get() to eager load the comments with the post. Read more about it in the documentation: https://laravel.com/docs/5.7/eloquent-relationships#eager-loading
Also, to display the comments you would want to add another foreach loop. It would look something like this:
#foreach($posts as $post)
{{ $post->title }}
#foreach($post->comments as $comment)
{{ $comment->title }}
#endforeach
#endforeach
You’ve probably cached some model data in the controller before, but I am going to show you a Laravel model caching technique that’s a little more granular using Active Record models
Note that we could also use the Cache::rememberForever() method and rely on our caching mechanism’s garbage collection to remove stale keys. I’ve set a timer so that the cache will be hit most of the time, with a fresh cache every fifteen minutes.
The cacheKey() method needs to make the model unique, and invalidate the cache when the model is updated. Here’s my cacheKey implementation:
public function cacheKey()
{
return sprintf(
"%s/%s-%s",
$this->getTable(),
$this->getKey(),
$this->updated_at->timestamp
);
}
public function comments()
{
return $this->hasMany(Comment::class);
}
public function getCachedCommentsCountAttribute()
{
return Cache::remember($this->cacheKey() . ':comments_count', 15, function () {
return $this->comments->count();
});
}
yes u can do like that in controller
$minutes = 60;
$posts = Cache::remember('posts', $minutes, function () {
return Post::with('comments')->get()
});
in blade u can get like that
#foreach($posts as $post)
{{ $post->title }}
#foreach($post->comments as $comment)
{{ $comment->title }}
#endforeach
#endforeach
for more information read this article

What is Laravel's similar method to PDO::FETCH_ASSOC?

I am updating my php PDO website using Laravel Framework 5.3. I am finding Laravel very easy to use but there are few functions that I don't know the syntax to achieve results.
I have a Laravel controller
public function index(){
$posts = Post::latest()->get();
return view('/home', compact('posts'));
}
which I use in the blade to fetch data using a foreach loop
#foreach ($posts as $row)
#endforeach
which is similar to fetchAll() method in PDO
$row = $posts->fetchAll();
now I want the logic to get fetch(PDO::FETCH_ASSOC) like result, so that I can assign
$row = $posts->fetch(PDO::FETCH_ASSOC);
I have to do this because I want to use for loop in the blade to show the data, if I use foreach loop then it creates duplication for iterated result. Following is the PDO code that I have to update with Laravel
for($i=0; $i<$count_photos; $i++){
$row_gallery = $get_photos->fetch(PDO::FETCH_ASSOC);
$pst_id = $row_gallery['post_id'];
$img = $row_gallery['img_name'];
echo '<div class="img_gallery"><div class="gallery'.$i.'"> <img class="img-responsive" src="uploads/'. $img . '"></div></div>';
}
The models will have already been fetched when you use get() so you'll be able to access the properties on the model straight away e.g.
#foreach ($posts as $post)
{{ $post->title }} //assuming you have a "title" column in the database
#endforeach
You might find this helpful: https://laracasts.com/series/laravel-from-scratch-2017/episodes/7
Edit
If you have the photos relationship set up in your Post model then you can eager load the relationship. Also, since you only want to load one post you can use first() instead of get() so you would end up with the following:
Controller:
public function index()
{
$post = Post::with('photos')->latest()->first();
return view('/home', compact('post'));
}
Blade file
#foreach($post->photos as $k => $photo)
<div class="img_gallery">
<div class="gallery{{ $k }}">
<a href="{{ url('uploads/'.$photo->img_name) }}" data-lightbox="{{ $post->id }}">
<img class="img-responsive" src="{{ url('uploads/'.$photo->img_name) }}">
</a>
</div>
</div>
#endforeach

Laravel - Eager load a non-relationship method (N+1 issue)

With laravel's eager loading, I can do something like
$forums = Forum::with('posts', 'threads')->get();
To get the threads as its posts and forum without doing many queries.
However, I have a method that gets the last post of the forum.
This is my method in Forum.php
public function lastThread()
{
$forums = $this->arrayOfIDs(); // get array of all ids, even subforum's subforums.
return Thread::whereIn('forum_id', $forums)->orderBy('updated_at', 'desc')->first();
}
This is my partial view to get the last thread:
#if ($subforum->lastThread())
<a href="{{ viewThread($subforum->lastThread()) }}">
{{ $subforum->lastThread()->title }}
</a>
<br>
<a href="{{ viewPost($subforum->lastThread()->lastPost) }}"> {{ trans('forum/post.last_post') }}
:</a>
{{ $subforum->lastThread()->lastPost->user->info() }}
<br>
{{ $subforum->lastThread()->lastPost->created_at }}
#endif
As you can see, if I am doing $subforum->lastThread() a lot. I can store it in a variable like $lastThread = $subforum->lastThread() and then do something like $lastThread->created_at to reduce the number of queries but I figured it would be much easier if just include it with the original query to get all forums.
Something like:$forums = Forum::with('posts', 'threads', 'lastThread')->get(); perhaps? I have tried doing a hasOne relation for this method but that does not work because the last thread does not necessarily belong to that specific subforum.
Anyway, how can I best fix this N+1 problem?
Thank you.
you have endless options,
if the Cache class is right for you than I will go with this path, you can also do this:
protectd $lastThread;
public function lastThread()
{
if ($this->lastThread) return $this->lastThread;
return $this->fetchLastThread();
}
public function fetchLastThread() {
$forums = $this->arrayOfIDs(); // get array of all ids, even subforum's subforums.
$this->lastThread = Thread::whereIn('forum_id', $forums)->orderBy('updated_at', 'desc')->first();
return $this->lastThread;
}

Categories