laravel 5.1 using Lazy Eager Loading usage - php

I'm working on a support ticket tool.
Table design at the moment:
tickets: |id|supp_id|title|user_id|...
ticket_replies: |id|ticket_id|user_id|text
files: |id|ticket_replie_id|name
model of ticket
public function ticket_replie()
{
return $this->hasMany('App\ticket_replie', 'ticket_id', 'id');
}
model of ticket_replie
public function file()
{
return $this->hasOne('App\File', 'ticket_replie_id', 'id');
}
controller
$ticket = Auth::user()->tickets()->where('id', $id)->firstOrFail();
return view('protected.ticketDetail', compact('ticket'));
view
ID: {{$ticket->id}}
title: {{ $ticket->title}}<br>
status: {{ returnStatus($ticket->status) }}<br>
Ticket created: {{ $ticket->created_at }}<br>
#if (!$ticket->supporter)
supporter:-<br></br></br>
#else
supporter {{ $ticket->supporter->username }}<br></br>
#endif
#foreach($ticket->ticket_replie as $reply)
#if ($reply->file == null)
reply text: {{ $reply->text }}</br>
#else
reply text: {{ $reply->text }}</br>
file: Download file<br>
#endif
reply created at: {{$reply->created_at}}</br></br>
#endforeach
current screen of querys:
Each ticket_replie can contain exact one "file", which stands for an attached file. As you may see in the querys this generates much load. Is there an way to use (laravel Lazy Eager Loading) to minimize amount of querys?
Ordering the ticket_replies by:
$ticket = Auth::user()->tickets()->where('id', $id)->with(['ticket_replie.file'])->firstOrFail();
$ticket->sortBy('ticket_replie.created_at');
$ticket->values();

In the controller something like this should do the job...
$ticket = Auth::user()->tickets()->where('id', $id)->with(['ticket_replie.file'])->firstOrFail();

Related

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

Fetch all users that meets the method in laravel

I'm trying to get all the users where a given method in User model meets. Please see my code below:
User.php
public function isPicker(){
return ($this->where('isPicker', 1)) ? true : false;
}
Now, I can use User::all();, but it returns all the users. What I want is to only return the users that meets the isPicker() method. What I'm trying to do in view is:
#foreach($users as $user)
#if($user->isPicker())
{{ $user->first_name }}
#endif
#endforeach
This is also working fine, but it is not that efficient to use. What if there's a lot of method to check? Any idea for this?
Just do:
$users = User::where('isPicker', 1)->get();
Or create a scope:
public function scopeIsPicker($query)
{
return $query->where('isPicker', 1);
}
// usage
$users = User::isPicker()->get();
Well you could change you code up a little to look like this.
#foreach($users->where('isPicker', 1)->all() as $user)
{{ $user->first_name }}
#endforeach
But this will only work if the users var is a collection.
Other wise just change you query on how your getting the users to something like this.
User::where('isPicker', 1)->get()
Instead of checking in model file, you can directly query in your controller try below code
if you want to display only isPicker=1 users.
Controller Code :
$users = User::where('isPicker', 1)->get();
Blade code :
#foreach($users as $user)
{{ $user->first_name }}
#endforeach
OR
if you want to display all users including isPicker 0&1.
Controller Code :
$users = User::all();
Blade code :
#foreach($users as $user)
#if($user->isPicker == 1)
{{ $user->first_name }}
#else
<p>Picker is 0</p>
#endif
#endforeach
Note : Remove your isPicker function from user model file because its unuse.

Laravel 5.4: Avoid html in the Controllers and DB queries in the blade

I have a problem I can not solve. I have a foreach that prints me an HTML every time it finds value in the database, and it all works.
However, I would like to avoid putting html in the controller.php file.
At the moment I did:
$html_console='';
if($article->id_game > '0'){
$prel_console = \DB::table('info_game')
->where('id_game', '=', $article->id_game)
->get();
foreach($prel_console as $name_console)
{
$name_console_game = \DB::table('console')
->where('id', '=', $name_console->id_console)
->first();
$html_console.='<span class="label">'. $name_console_game->abb_cat.'</span>' ;
}
}
While in the blade:
{!! $html_console !!}
I tried to do this in the blade:
#foreach ($prel_console as $name_console)
<span class="label margin-top-5 font-size-10">{{ $name_console_game->abb_cat }}</span>
#endforeach
If I put the foreach in the blade, how do I deal with the query "name_console_game"
If you have a one to many relation between info_game table which should have a InfoGame model and console table with Console model then your could do something like this:
controller:
public function someMethod()
{
// assuming that you already have an $article object
$infoGame = InfoGame::where('id_game', $article->id_game)->get();
return view('some.view', compact('infoGame'));
}
view location views/some/view/blade.php
#foreach($infoGame->console as $name_console_game)
<span>{{ $name_console_game->abb_cat }}</span>
#endforeach

Laravel many to many relationship access value from Controller in blade

I have two database table
1. patient
--id
--name
2. report
--id
--description
and pivot talbe
patient_report
--id
--report_id
--patient_id
My Patient Model
class Patient extends Model
{
public function reports()
{
return $this->belongsToMany('App\Report' , 'patient_reports');
}
}
My Report Model
class Report extends Model
{
public function patients()
{
return $this->belongsToMany('App\Patient' , 'patient_reports');
}
}
My ReportControlller
public function viewList($reportFloor = null)
{
$report = Report::orderBy('created_at' , 'desc')->paginate(50);
return view('admin.report_list' , ['reports' => $report]);
}
Database : patients table have report_id column and reports table have patient_id column
N.B : I want to find the patient name who has a report. And i am using the laravel dynamic properties like that ---
And finally my blade
#foreach ($reports as $report)
{{ $report->patients->name }}
#endforeach
But it provides an error like that
Try this:
#foreach ($reports->patients as $patient)
{{ $patient->name }}
#endforeach
As the error implies, you're attempting to access the name attribute on a collection. To fix you should change:
#foreach ($reports as $report)
{{ $report->patients->name }}
#endforeach
to:
#foreach ($reports as $report)
#foreach ($report->patients as $patient)
{{ $patient->name }}
#endforeach
#endforeach
In your controller, i would first find the patients and afterwards get their reports.
$patients = Patient::with('reports')->get();
And then in the view i would do:
//This will first get all the patients
#foreach ($patients as $patient)
// This will get each of patients relational reports
#foreach ($patient->reports as $report)
{{ $report->name }}
#endforeach
#endforeach

How to show related items with pagination in Eloquent and Blade?

AdminsController
public function index(){
$someMessages=$this->blog->paginate(5);
$users=User::all();
// return 'Welcome Admin';
return View::make('admin.admin',['someMessages'=>$someMessages,'users'=>$users]);
}
admin.blade.php
Here need to display username from 'users' variable based on uid stored in 'someMessages' object.
#extends('layout.default')
#section('title')
<title>Welcome Admin</title>
#stop
#section('content')
#foreach($someMessages as $message)
<blockquote>{{$message['blog']}}
<small>
<cite>
</cite>
</small>
</blockquote>
#endforeach
{{ $someMessages->links()}}
#stop
If you have a Eloquent relation between blog messages and users defined correctly you don't need to query the users separately. What you want to do is just go with the eager loading:
//in case you call it author in your "relation definition"
$someMessages = $this->blog->with('author')->paginate(5);
And then in your Blade template:
#foreach($someMessages as $message)
<div>
<div>{{ $message->blog }}</div>
<div>{{ $message->author->name }}</div>
</div>
#endforeach
If you haven't declared the relationship yet - it's easy. Open up your user eloquent model class and add a method:
public function blogs()
{
return $this->hasMany('Blog', 'uid');
}
In the blog eloquent model class you'd add:
public function author()
{
return $this->belongsTo('User', 'uid');
}
More about eloquent and relationships can be found here.
If that's not the case you can still filter your users collection to get the one you want:
#foreach($someMessages as $message)
<?php
$user = $users->filter(function($user) use ($message)
{
return $user->id == $message->uid;
})->first();
?>
<div>
<div>{{ $message->blog }}</div>
<div>{{ $user->name }}</div>
</div>
#foreach
What happens here is you filter your users collection based on $message->uid value and take the first one from it (it should always be just one or none since user IDs are unique).
Although you need to understand that in this case you will get all the users from database and filter through them for every blog message you're outputting. Eager loading is a much better idea here and I'd stick to it if possible.

Categories