Laravel orderby with relationship - php

i'm trying to sort the student list by each level with using relationship method with OrderBy function but unfortunately i can't make it work any idea whats missing on my code?
Note:
every-time i remove the orderby my code will work but students level are not arrange accordingly
Controller:
$students=Student::with('level')->where(['status' => 'ENROLLED'])->get()->orderBy('level_name','asc');
View
<table>
<tr>
<th>Name</th>
<th>Level</th>
</tr>
#foreach($students as $std)
<tr>
<td>
{{$std->student_name}}
</td>
<td>
#foreach($std->level as $lv)
{{$lv->level_name}}
#endforeach
</td>
</tr>
#endforeach
</table>

You can't order by a relationship because under the hood laravel makes two seperate queries under the hood.
You can instead use a join, something like this (beware I guessed your table names, so you may have to update them).
$users = Student::join('levels', 'students.level_id', '=', 'levels.id')
->orderBy('levels. level_name', 'asc')->select('students.*')->paginate(10);

Try this:
Controller:
$students = Student::with(['level' => function (Builder $query) {
$query->orderBy('level_name', 'asc');
}])->where(['status' => 'ENROLLED'])->get();
In addition you can add orderBy() to relation method.
Student Model:
public function level()
{
return $this->relationMethod(Level::class)->orderBy('level_name', 'asc');
}

Try this
$students=Student::with('level')->where(['status' => 'ENROLLED'])->orderBy('level_name','asc')->get();

Related

Displaying total answer and correct answer of each user

I have a db structure like this:
Tables:
users(id, email, password, ...) //default laravel users table
examinees(id, user_id, ...)
exam_quizzes(id, title, explanation)
exam_quiz_answers(id, title, exam_quiz_id, is_correct_ans)
submitted_answers(id, user_id, exam_quiz_id, exam_quiz_answer_id)
I already have the respective models and relationship methods set up.
Models:
User, Examinee, ExamQuiz, ExamQuizAnswer, SubmittedAnswer
Relationships:
// User -> hasOne() -> Examinee
$user->examinee
// ExamQuiz -> hasMany() -> ExamQuizAnswer
$examQuiz->examQuizAnswers
// SubmittedAnswer -> hasMany() -> ExamQuiz
$submittedAnswer->examQuizzes
// SubmittedAnswer -> hasMany() -> ExamQuizAnswer
$submittedAnswer->examQuizAnswers
// User -> hasMany() -> SubmittedAnswer
$user->submittedAnswers
In my view, how can I display the Name, Total Answered and Total Correct for every user who is also an examinee, in a table like this:
<tr>
<th>Name</th>
<th>Answered</th>
<th>Correct</th>
</tr>
#foreach()
{{-- I have no idea what to do here --}}
<tr>
<td></td>
<td></td>
<td></td>
</tr>
#endforeach
In your controller, u get the users an pass it to view.
$users = User:get();
and the make a foreach loop to get the answer and correct answers:
<tr>
<th>Name</th>
<th>Answered</th>
<th>Correct</th>
</tr>
#foreach($users as $user)
<tr>
<td>{{$user->name}}</td>
<td>{{count($user->submittedAnswers()->get())}}</td>
#php
foreach($user->submittedAnswers()->get() as $answer){
foreach($answer->examQuizAnswers->get() as $quiz){
$count = $quiz->where('is_correct_answer',1)->count()
}
}
#endphp
<td>{{$count}}</td>
</tr>
#endforeach
But of course you can write a method in a model to retrieve the correct answers. and just call that method instead.
you can write a method like this in User model:
public function get_correct_answers($user_id){
$user = User::whereId($user_id)->first();
foreach($user->submittedAnswers()->get() as $answer){
foreach($answer->examQuizAnswers->get() as $quiz){
$count = $quiz->where('is_correct_answer',1)->count()
}
}
return $count;
}
And then in the view u just call that method like this:
<tr>
<th>Name</th>
<th>Answered</th>
<th>Correct</th>
</tr>
#foreach($users as $user)
<tr>
<td>{{$user->name}}</td>
<td>{{count($user->submittedAnswers()->get())}}</td>
<td>{{$user->get_correct_answers($user->id)}}</td>
</tr>
#endforeach
Loop your $users and echo the 3 fields you want. Something roughly like this:
{{ $user->name }}
{{ $user->submittedAnswers->examQuizAnswers()->where('is_correct_answer', 1)->get()->count() }}
{{ $user->submittedAnswers->examQuizAnswers->count() }}
But please don't actually query in view files for the sake of the children.
Eager load related models in your controller:
User::with(['submitted_answers', 'submitted_answers.exam_quiz_answers'])->get()

How to get all records in laravel Models

I'm not using eloquent, my models are like this.
class drivers extends Model
{
}
I want to display records of all drivers ( one record in each row )
My driver table has field (driver_id,name,tyre_id)
My tyre table has field (id, title)
My bank table has field (id, driver_id, bank)
I want my record to be like this...
Driver Id, Name, Bank Details, Tyre Title
100000111, Ayer, Available, GO
.. so on
For bank details if driver_id has a record in bank table, it should display available otherwise N/A.
$drivers= Drivers::get();
$myarray = ();
foreach ($drivers as $d){
$bank = Bank::where('driver_id',$d->driver_id)->first();
$tyre = Tyre::where('id',$d->tyre_id)->first();
$myarray[] = $d->driver_id;
$myarray[] = $d->name;
$myarray[] = isset($bank) ? "available" ; '';
$myarray[] = $tyre->title;
}
This is what i have tried, I'm to new to laravel, how can i achieve this in laravel or using query like DB Table?
Laravel offers two very useful tools for performing database operations eloquent and query builder. It is advisable to work as much as possible with eloquent and their relationships as it facilitates much of the common operations that we normally need to perform.
Now, if you want to make more complex queries you can use query builder, also, an example of your case using query builder would be something like this:
In your controller you make the query and pass the data to view:
$data = DB::table('driver')
->leftJoin('bank', 'bank.driver_id','=', 'driver.driver_id')
->join('tyre', 'tyre.id','=', 'bank.tyre_id')
->select('driver.driver_id as id',
'driver.name',
'bank.id as bank_id',
'tyre.title')
->groupBy('driver.driver_id')
->get()
And in your view you can use a foreach loop to display the data (and you can use a conditional to display the bank field):
<table>
<thead>
<tr>
<th>Driver ID</th>
<th>Name</th>
<th>Bank Detail</th>
<th>Tyre Title</th>
</tr>
</thead>
<tbody>
#foreach($data as $item)
<tr>
<td>{{ $item->id }}</td>
<td>{{ $item->name }}</td>
<td>{{ isset($item->bank_id) ? "availible":"N/A" }}</td>
<td>{{ $item->title }}</td>
</tr>
#endforeach
</tbody>
</table>
likewise I recommend you read the documentation of eloquent and try to use it.
https://laravel.com/docs/5.5/queries
https://laravel.com/docs/5.5/eloquent
The Good way to Solve this is laravel relations
here the link
laravel documentation
Select your driver table as base table and use relations to get the other table fields;
array_push() function to push values to array
Another way is using DB Facade with joins: Like this:
$users = DB::table('users')
->join('contacts', 'users.id', '=', 'contacts.user_id')
->join('orders', 'users.id', '=', 'orders.user_id')
->select('users.*', 'contacts.phone', 'orders.price')
->get();

Laravel - aesthetic code, database query in controller?

I'm doing forum now.
I wrote code in controller, but I'm wondering or this is a good way to do this (or this is pure code, I mean or my code is solid etc). I was thinking that, maybe my questions to DB should be in MODELS? And do specific methods to this, or this can be here?
I'm not sure, or this database query can be in foreach?
On the main site I want to have in table : forum topics, how many posts, how many answers and last post:
Here's code:
class ForumController extends Controller
{
public function mainSite()
{
$mainData = [];
$topics = Topic::all();
$lastPost = [];
foreach ($topics as $topic) {
$allPosts = Posts::where('topic_id', $topic->id)->count();
$allComments = Comments::where('topic_id', $topic->id)->count();
$post = Posts::select('added_at', 'user_id', 'subject')->where('topic_id', $topic->id)->orderBy('added_at', 'DESC')->first();
$user = ForumUsers::select('name')->where('id', $post['user_id'])->first();
$lastPost[$topic->name]=[$post['added_at'], $post['subject'], $user['name']];
$mainData[] = ['topic' => $topic->name, 'posts' => $allPosts, 'comments' => $allComments];
}
return View('forum', ['mainData' => $mainData, 'lastPost' => $lastPost]);
}
}
And here is my View:
<table>
<tr>
<th>Forum</th>
<th>How many posts</th>
<th>How many answers</th>
<th>Last post</th>
</tr>
#foreach($mainData as $topic)
<tr>
<td>
{{$topic['topic']}}
</td>
<td>
{{$topic['posts']}}
</td>
<td>
{{$topic['comments']}}
</td>
<td>
#foreach($lastPost[$topic['topic']] as $post)
{{$post}}
#endforeach
</td>
</tr>
#endforeach
</table>
I'm a beginner and please tell me tips how to do good code, or how to have good habits in programming. Thanks for all answers!
Eloquent have some "magic" stuff hard to see for a beginner, like Model::whereMyField. For instance, you can transform your
Posts::where('topic_id', $topic->id)
to
Posts::whereTopicId($topic->id)
Plus, you can avoid all your Model::where by setting relations in your Eloquent model. Here's some reading: https://laravel.com/docs/5.4/eloquent-relationships
Don't forget //comments in your code. It's a good practice tu put some comments & PHP doc.
Bonus: you have a cool IDE helper to help you to discover all the "magic" methods of your models: https://github.com/barryvdh/laravel-ide-helper

How to order my foreach in laravel blade

I am using a hasMany in my Model class to retrive the clients notes, how ever i want to order these notes by the latest date created in laravel blade template.
My code is below and im getting an error on this.
Please advice me..
#foreach($clients->notes->orderBy('created_at', 'desc') as $note)
<table class="table table-bordered">
<tr>
<td class="col-xs-2 col-md-2"><b>Created On:</b> {{ date('d/m/y', strtotime($note->created_at)) }} <b>#</b> {{ date('g:i A', strtotime($note->created_at)) }} </td>
<td class="col-xs-14 col-md-12">{{ $note->notes }}</td>
</tr>
</table>
#endforeach
Guessing, because you haven't told us what error you're getting, but:
$clients->notes is an already-fetched collection of results. $clients->notes() is a query builder that you can apply further logic like ordering or additional criteria to.
You likely want:
$clients->notes()->orderBy('created_at', 'desc')->get()
but you should do that in the controller and pass it to the view instead of having the query directly in the Blade template.
(You can alternatively use Laravel's collection functions on $clients->notes, including the sortBy() function).
Data must be ordered within controller or models. If you have used hasMany validation in model you can do as mentioned below
In model write association
public function notes()
{
return $this->hasMany('Note')->orderBy('created_at', 'desc');
}
In your controller function associate client with notes like this
$clients = Client::with('notes')->get();
Hope you get your answer

Get the amount of the bill in the class method

Sorry for my English.
I want to make a record that would be deduced me the sum of all my orders, that is, folded string of orders and drew grouped by orders.
I have created a model "Sale", which comprises method AmountOrder
public function AmountOrder()
{
$AmountOrder = DB::table('goods')
->join('sale_lines', 'sale_lines.good_id', '=', 'goods.id')
->where('sale_id', $this->id)
->select(DB::raw('SUM(price*quantity) as total_sales'))
->value('total_sales');
return $AmountOrder;
}
and to deduce the code like this
#foreach ($sales as $sale)
<tr>
<td class="table-text"><div>{{ $sale->id }}</div></td>
<td>
{{ $sale->client->name }}
</td>
<td>
{{$sale->date}}
</td>
<td>
{{$sale->AmountOrder($sale)}}
</td>
<td>
{{$sale->debt($sale)}}
</td>
<td>
{{$sale->date_of_issue}}
</td>
</tr>
#endforeach
But the problem is that the query is performed on each line. I'm new to Laravel, but thought maybe you can solve this problem somehow more beautiful?
Thank you very much in advance!
You are probably talking about the Eager Loading.
From the docs:
When accessing Eloquent relationships as properties, the relationship data is "lazy loaded". This means the relationship data is not actually loaded until you first access the property. However, Eloquent can "eager load" relationships at the time you query the parent model. Eager loading alleviates the N + 1 query problem.
However, you will be not able to use the Eager Loading now, with this code in the AmountOrder method.
A simple google search, also, led me to this example of Eager Loading with aggregate functions/relationships.
It will be probably a good start to think and implement your solution.
you have wrong in your select :
$AmountOrder = DB::table('goods')
->join('sale_lines', 'sale_lines.good_id', '=', 'goods.id')
->where('sale_id', $this->id)
->select(DB::raw('SUM(sale_lines.price*sale_lines.quantity) as total_sales'))
->value('total_sales');
My relationship
class Sale extends Model
{
//Получаем товар в этой продаже
public function good()
{
return $this->belongsTo('App\Good');
}
}
class Good extends Model
{
//В каких закупках был этот товар
public function purchases()
{
return $this->hasMany('App\Purchase');
}
//Продажи с этим товаром
public function sales()
{
return $this->hasMany('App\Sale');
}
}
Is it correct?
In my model i create method
public function AmountOrderRelation()
{
return $this->belongsTo('App\Good')
->selectRaw('sum(price) as aggregate, id')
->groupBy('id');
}
In controller
$new_sales = Sale::with('AmountOrderRelation')->get();
#foreach ($new_sales as $sale)
<tr>
<td class="table-text"><div>{{ $sale->id }}</div></td>
<td>
{{ $sale->AmountOrderRelation }}
</td>
</tr>
#endforeach
But my relations is null. What's my mistake?
I did it!
public function AmountOrder()
{
return $this->HasOne('App\SaleLines')
->join('goods', 'sale_lines.good_id', '=', 'goods.id')
->selectRaw(DB::raw('SUM(price*quantity) as aggregate, sale_id'))
->groupBy('sale_id');
}
public function getAmountOrderAttribute()
{
// if relation is not loaded already, let's do it first
if ( ! array_key_exists('AmountOrder', $this->relations))
$this->load('AmountOrder');
$related = $this->getRelation('AmountOrder');
// then return the count directly
return ($related) ? (int) $related->aggregate : 0;
}
And in controller
$sales = Sale::with('AmountOrder')->get();

Categories