I have a little problem. My code is working, but I think I'm not doing it the proper way.
In my GradeController i have this code:
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
if(auth()->user()->hasRole('Student')) {
$subjects = Subject::all();
return view('grades.student.index', compact('subjects'));
}
}
And in my view I'm getting Grades which belong to a specified user this way:
#foreach($subject->grades->where('student_id', '=', auth()->user()->id) as $grade)
<span class="badge badge-primary">
{{ $grade->value }}
</span>#endforeach
Is here, I mean Laravel, any better way to do this? Because I think that getting all Grades which belong to a Subject and then look for ID is not very "effective".
Have a good day.
You can use the with() eager loading helper, with a closure which will filter the subject's 'grades` based on the grade belonging to the logged in user:
$subjects = Subject::with(['grades' => function($query) {
$query->where('student_id', auth()->user()->id);
}])->get();
Note the removal of , '=', in the ->where() clause. It does not need this argument if checking if equal to.
In your controller index() you can make a middleware instead of doing this check auth()->user()->hasRole('Student') many times, buy if you will check only once here it will be fine what you did.
public function index()
{
// refactoring
return view('grades.student.index', ['subject' => Subject::all()]);
}
Also its not good to make a query in your blade file, so you can pass grades directly from you controller
public function index()
{
return view('grades.student.index', [
'grades' => Grade::where('student_id', auth()->id())->get()
]);
}
In index blade file you can now use:
#foreach($grades as $grade)
<span class="badge badge-primary">
{{ $grade->value }}
</span>
// you can get the subject from $grade->subject
#endforeach
Related
Users in my app can create their own groups. A Group is displayed on the groups/show.blade.php and other users can join said group(s).
I want to show a list of all people who have joined each group.
On the groups/show.blade.php page I have the following
#if ($joinedUsers)
#foreach($joinedUsers as $joinedUser)
{{$user->name}}
#endforeach
#else
#endif
#endsection
The error I get is:
Undefined variable: group_joined_user
which is strange because in my Group model I have
* Get the users that joined the group.
*/
public function joinedUsers()
{
return $this->belongsToMany(User::class, 'group_joined_user', 'group_id', 'user_id')
->withTimestamps();
}
And my GroupController.php
public function show($id)
{
$group = Group::with('joinedUsers')->where('id', $id)->first();
return view('groups.show', compact('group'));
}
I have this in my Routes
Route::resource('groups', 'GroupsController');
I'm fairly new to Laravel so I might be missing something obvious here?
you have defined $group variable only. joinedUsersis the relation and you never defined it as a variable anywhere. you have to do it like
#foreach($group->joinedUsers as $user)
{{$user->name}}
#endforeach
I recently asked a question about defining many to many relationships (using belongsToMany) and it was a huge help. So now in my models I have:
Users model
public function subjects()
{
return $this->belongsToMany('App\Subject', 'users_subjects', 'user_id', 'subjects_id');
}
Subjects model
public function users()
{
return $this->belongsToMany('App\User', 'users_subjects', 'subject_id', 'user_id');
}
This way I establish a relationship between users and subjects via the users_subjects table. My next step was to create a controller, SubjectsController, which ended up looking like this:
class SubjectsController extends Controller
{
// returns the view where subjects will be displayed
public function index()
{
return view('profiles.professor.prof_didactic_subjects');
}
// get users with subjects
public function getSubjects()
{
$subjects = User::with('subjects')->get();
}
// get a single user with a subject
public function getSubject($id)
{
$materia = User::where('id', '=', $id)->with('subjects')->first();
}
}
I'm not very sure about the code in the controller though.
The final step is where it gets tricky for me, even after reading the docs: I want to pass each result to the view, so I can have multiple tiles, each populated with data from subjects the user is associated with:
#foreach ($subjects as $subject)
<div class="tile is-parent">
<article class="tile is-child box">
<p class="title">{{ $subject['name'] }}</p>
<div class="content">
<p>{{ $subject['description'] }}</p>
</div>
</article>
</div>
#endforeach
I tried many different route configurations, but kept getting either the undefined variable error or trying to access non-object error.
What's the proper course of action here? I feel I'm missing something very basic. Thanks in advance for any help.
The answer
The solution provided below by #Sohel0415 worked perfectly. My index() method on the controller now looks like this:
public function index()
{
// temporary value while I figure out how to get the id of the current user
$user_id = 6;
$subjects = Subject::whereHas('users', function($q) use ($user_id){
$q->where('user_id', $user_id);
})->get();
return view('profiles.professor.prof_didactic_subjects')->with('subjects', $subjects);
}
My route looks like this:
Route::get('/professor', 'SubjectsController#index');
I was pretty lost, so this absolutely saved me, thanks again :)
You need to pass $subjects to your view. You can use compact() method for that like -
public function index()
{
$subjects = Subject::with('users')->get();
return view('profiles.professor.prof_didactic_subjects', compact('subjects'));
}
Or using with() method like -
public function index()
{
$subjects = Subject::with('users')->get();
return view('profiles.professor.prof_didactic_subjects')->with('subjects', $subjects);
}
If you want to get Subject for a particular user_id, use whereHas() -
$subjects = Subject::whereHas('users', function($q) use ($user_id){
$q->where('user_id', $user_id);
})->get();
I have a table of "rosters" that's pretty much strictly foreign keys. It acceses the "schools" table, "courses" table, and "students" table. So essentially, a 'student' takes a 'course' at a 'school'. My RostersController has this
public function show($id)
{
$roster = Roster::where('course_id', $id);
return view('roster')->with('roster', $roster);
}
My Roster Model is:
public function students()
{
return $this->hasMany('App\Student');
}
My student Model is:
public function roster()
{
return $this->belongsTo('App\Roster');
}
my view is this:
#foreach ($roster as $child)
<p>{{$child->id}}</p>
<p>{{$child->students->first_name}}</p>
#endforeach
The rosters table just saves the student_id rather than all of the student's data that is already in the 'students' table. So i'm trying to access the students table from this relation but when i run this, it tells me that anything related to the students table is 'not on this collection'. I know that I could do things this way if i was working with a hasOne relationship, but how can i accomplish this with a hasMany to output the students table value in each row?
You should try this
public function show($id)
{
$roster = Roster::with('students')->where('course_id', $id);
return view('roster')->with('roster', $roster);
}
Try this
Roster.php
public function show($id)
{
$roster = Roster::with('students')->where('course_id', $id)->get(); // load the students
return view('roster')->with('roster', $roster);
}
On the view
#foreach ($roster as $child)
<p>{{$child->id}}</p>
<p>
<!-- Loop through the stundents since this returns an collection of students and not just one -->
#foreach($child->students as $student)
{{$student->first_name}} <br>
#endforeach
</p>
#endforeach
Check this for more information on eager loading
The $child->students is a collection. So, you need to use another foreach loop inside the first one.
Your code should look like this:
#foreach ($roster as $child)
<p>{{$child->id}}</p>
#foreach($child->students as $student)
<p>{{$student->first_name}}</p>
<p>{{$student->age}}</p>
<p>{{$sutdent->another_column_in_students_table}}</p>
#endforeach
<p>{{$child->any_other_column_in_roster_table_if_needed}}</p>
#endforeach
So, your first issue is, that
$roster = Roster::where('course_id', $id);
will just return a QueryBuilder instance, you have to use
$roster = Roster::where('course_id', $id)->get();
then for sure, students is a collection, you have to iterate over it like this:
#foreach ($child->students as $student)
{{ $student->yourProperty}} <br>
#endforeach
Update:
I saw you know already about that when to use get() in query laravel 5
I'm trying to access a relations table from a collection of data passed in from the controller. I am able to iterate the collection in my view but I am unable to access the relationship data.
There are 2 tables:
stocks (default model)
stock_datas (has a foreign key stock_id which is already setup)
Controller:
public function getstock() {
return view('vehicles.getstock', ['stock' => \App\Stock::all()]);
}
Model (App\Stock) and then (App\StockData)
// From stock model:
public function stockdata() {
return $this->hasOne('App\StockData');
}
// Stock Data model:
public function stock() {
return $this->belongsTo('App\Stock');
}
View (loop):
#foreach ($stock as $k => $v)
{{ print_r($v->stockdata()->get())->year }}
#endforeach
When I try the query below, I get a
Undefined property: Illuminate\Database\Eloquent\Collection::$year (View: F:\websites\tempsite\resources\views\vehicles\getstock.blade.php)
However, year is a column in the stock_datas table.
I am also able to print_r data from the \App\StockData() table so the reference to the table is correct as doing print_r(\App\StockData::all()) from the controller does return all the rows as expected.
What am I doing wrong?
Since it's one to one relation, you should do it like this:
#foreach ($stock as $v)
{{ $v->stockdata->year }}
#endforeach
First one You have to change {{ print_r($v->stockdata()->get())->year }} this line, remove print_r. Next one in foreach loop you can do something like this
#foreach($stock as $one)
{{ $one->stockadata()->first()->year }}
#endforeach
For better solution you should check if isset $one->stockadata()->first()
and after that call ->year. Finally code should be like this
#foreach($stock as $one)
{{ isset($one->stockadata()->first()) : $one->stockadata()->first()->year : 'Default' }}
#endforeach
When calling get() method on any relationship You will always receive collection, no matter what relationship You have.
There are at least two (2) ways to solve Your problem:
1. $v->stockdata->year
2. $v->stockdata()->first()->year
I would suggest You to use first one, because Your stockdata has 1:1 relationship.
Good luck!
For example:
Stock.php model
class Stock extends Model
{
protected $primaryKey = 'id';
function stockdata() {
return $this->hasOne('App\StockDatas', 'id', 'stock_id');
}
public function getStock(){
return Stock::with('stockdata')->get();
}
}
In contriller
public function getstock(Stock $stock) {
return view('vehicles.getstock', ['stock' => $stock->getStock]);
}
view
#foreach ($stock as $k => $v)
{{ $v->stockdata->year }}
#endforeach
I have 3 models in my app Users, Sales and Plans, when I render sales for each customer (due to storing) I only get id's for other users and models related to that sale (like account manager, owner, plan), now I'm trying to use those ID's inside blade to get names or other rows based on ID and model. Here is the show function:
public function show($id) {
$user = User::find($id);
$sales = Sale::where('customer_id', '=', $id)->get();
return view('profiles.customer', ['user' => $user, 'sales' => $sales]);
}
And in blade I get all those sales like:
#foreach ($sales as $sale)
<li>
<i class="fa fa-home bg-blue"></i>
<div class="timeline-item">
<span class="time"><i class="fa fa-clock-o"></i> {{$sale->created_at->format('g:ia, M jS Y')}}</span>
<h3 class="timeline-header">{{$user->name}} became a customer</h3>
<div class="timeline-body">
<p>Date: {{$sale->sold_date}}</p>
<p>Price: {{$sale->sale_price}}</p>
</div>
</div>
</li>
#endforeach
So inside each record I have like "account_manager_id", "agent_id", "owner_id", "plan_id".
Currently I have this solved by adding public static function (this is for users, have same function for Plan model as well) in Sale model class:
public static function getUser($id) {
return User::where('id', $id)->first();
}
And I'm using it like this in Blade:
Account manager: {{$sale->getUser($sale->account_mgr_id)->name}}
Is this the safest and best way to do it? Or there is something I'm overlooking here?
You need to add relationships in your Sales Model.
class Sales extends Eloquent {
.....
.....
public function accountManager() {
return $this->hasMany('App\User', 'account_manager_id');
}
public function agents() {
return $this->hasMany('App\User', 'agent_id');
}
public function owner() {
return $this->hasOne('App\User', 'owner_id');
}
}
Now $sales->agents will give you a user with agent_id as id in User table.
Update your hasOne, hasMany relationships as your need. Laravel Documentation.
From your blade template, your access your AccountManager as
#foreach($sales->accountManager as $manager)
Name: {{ $manager->name}}
#endforeach
I think you could use Eloquent relationships. Taking your example, you should define relationship in your User model:
<?php
class User extends Eloquent {
public function sales() {
return $this->hasMany(Sale::class, 'customer_id');
}
}
Then, whenever you need to get sales of that user (entries, that relate via customer_id column), just simply do
<?php
$user = User::find($id);
$sales = $user->sales;
This is very fun when when you have to print out list of users that have sales, for example
<?php
public function showUsersWithSales() {
$users = User::with('sales')->get();
return view('users-with-sales', compact('users'));
}
users-with-sales.blade.php example:
#foreach ($users as $user)
User: {{ $user->name }}<br>
Sales:<br>
#foreach ($user->sales as $sale)
{{ $sale->amount }} {{ $sale->currency }} # {{ $sale->created_at }}<br>
#endforeach
<hr>
#endforeach
It would print all users with their sale amount and currency, followed by date when it was created.
This sample takes into account, that your User model has name attribute and your Sale model has amount, currency, created_at and customer_id fields in your database.
To reverse the action, say you have a sale and want to know who made it, just simply define a relationship!
<?php
class Sale extends Eloquent {
public function customer() {
return $this->belongsTo(User::class, 'customer_id');
}
}
Hope that helps!
Eloquent Relationship is your friend, https://laravel.com/docs/5.2/eloquent-relationships and you can solve your problem easily.
Suggestion is to remove all those function access and control from view and put it somewhere else. This will be good habit for you so you can avoid the infamous fat view.