Accessing and displaying pivot table data - laravel

Theory:
Users can attend many events and many events can be attended by many users. Therefore, I have two many-to-many relationships within my models, linking to a pivot table (event_user). On attending each event, I want to be able to access the pivot table data (event_user) to see if they're already attending.
event_user:
--id
--event_id
--user_id
For example, my seed data is:
user 1 attending both event 2 and 3. I want to be able to show this within a view.
The closest I have got is (logically):
public function index()
{
$id = Auth::user()->id;
$attending = myApp\Event::find($id)->event;
var_dump($attending); **But this var_dump returns NULL, but $id returns the correct ID.**
$events = myApp\Event::all();
$this->layout->content = View::make('events.index')->with('events', $events);
}
My aim is to disable the 'attend' button, on any event where they are already attending, only leaving the attend-able events available!
Any help would be hugely appreciated. Thank you in advance.
Any additional code which you may find necessary:
Events Model:
<?php
namespace myApp;
use Eloquent;
class Event extends Eloquent {
public function consultant()
{
return $this->belongsToMany('myApp\Consultant');
}
public function location()
{
return $this->belongsToMany('myApp\Location');
}
public function user()
{
return $this->belongsToMany('myApp\User');
}
}
User Model:
<?php
namespace myApp;
use Eloquent;
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
class User extends Eloquent implements UserInterface, RemindableInterface {
public function event()
{
return $this->belongsToMany('Event');
}
public function practice()
{
return $this->belongToMany('Practice');
}
index.blade.php (showing the event list)
<div class="panel panel-success">
<!-- Default panel contents -->
<div class="panel-heading"><strong>Events</strong></div>
<!-- <div class="panel-body">
</div> -->
<!-- Table -->
<table class="table">
<thead>
<tr>
<th>Title</th>
<th>Date</th>
<th>Presenter</th>
<th>Location</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach($events as $event)
<tr>
<td>{{ $event->title }}</td>
<td>{{ date("j F Y", strtotime($event->date)) }}</td>
<td>{{ $event->consultant()->first()->title }} {{ $event->consultant()->first()->surname }}</td>
<td>{{ $event->location()->first()->address_1 }}</td>
<td><button type="button" class="btn btn-info">Attend</button></td>
</tr>
#endforeach
</tbody>
</table>
</div>
</table>

I think you're going about this the wrong way, that or I have misunderstood.
Firstly, you're trying to find an event with the same primary key as the currently authenticated user, which isn't correct, although it's an easy hole to fall down.
$id = Auth::user()->id;
$attending = myApp\Event::find($id)->event;
// equivalent of: SELECT * FROM `events` WHERE `id` = ?
Instead you'll want to do this
$id = Auth::user()->id;
$attending = myApp\Event::where('user_id', $id)->get();
// equivalent of: SELECT * FROM `events` WHERE `user_id` = ? (user_id insted of events.id)
That being said, surely the user events can be accessed by just calling the event property on the auth user?
$user = Auth::user();
$attending = $user->event;
To take this one step further, and make it so that you can check inside the foreach loop, you could advanced the above code to look like the following
$user = Auth::user();
$attending = $user->event->lists('id');
This will make an array of ids from the returned events that you need to assign to the view
$this->layout->content = View::make('events.index', array('events' => $events, 'attending' => $attending));
Now you can freely access it in your foreach
#foreach($events as $event)
<tr>
<td>{{ $event->title }}</td>
<td>{{ date("j F Y", strtotime($event->date)) }}</td>
<td>{{ $event->consultant()->first()->title }} {{ $event->consultant()->first()->surname }}</td>
<td>{{ $event->location()->first()->address_1 }}</td>
<td>
#if (!in_array($event->id, $attending))
<button type="button" class="btn btn-info">Attend</button>
#endif
</td>
</tr>
#endforeach
Also, seeing as Event is a reserved Alias (unless you've modified the config, which I wouldn't recommend), you'll want to specify the namespace in the relationship declaration within User
public function event()
{
return $this->belongsToMany('myApp\Event');
}
As a final point, it's not an issue as such, but in my own code I try to name relationships that have the potential to return multiple objects in the plural, so it would be public function events();.

Related

How do i count for a specific data in a column in Laravel?

I have two models, One is Employees and other one is Skill. One Employee can have many skills. Now I'm trying to show number of employees that possesses each consecutive skill in skills view page.
My Skills View Page:
<tbody>
#foreach ($skills as $skill)
<tr>
<th scope="row">{{ $loop->index+1 }}</th>
<td>{{ $skill->skill_name }}</td>
<td>{{ $skill->totalEmp($skill->id) }}</td>
<td style="width: 25%;">
<button class="btn btn-outline-danger" type="button" title="Delete Skill">Delete Skill</button>
</td>
</tr>
#endforeach
</tbody>
Skills Model:
class Skill extends Model
{
use HasFactory;
protected $fillable = [
'skill_name'
];
public function Employee()
{
return $this->hasMany(Employees::class, 'skill_id', 'id');
}
public function totalEmp($id){
return Employees::where('status', 1)->where('skill_id','=',$id)->count();
}
}
Employees Model:
class Employees extends Model
{
use HasFactory;
protected $guarded = [];
protected $table = 'employees';
public function Skills(){
return $this->hasMany(Skill::class);
}
}
Employee table has->skill_id, other irreverent fields || Skill Model has->id and skill_name field.
You can use withCount method for this.
https://laravel.com/docs/9.x/eloquent-relationships#counting-related-models
On your Skill model you have Employee relationship defined.
So you will be able to do something like this:
$skills = Skill::withCount(['Employee'])->get();
// Prints out number of employees that posses first skill
echo $skills[0]->employee_count;
Of course you can iterate through $skills with for loop, or foreach and similar, or even use collection methods.
<tbody>
#foreach ($skills as $skill)
<tr>
<th scope="row">{{ $loop->index+1 }}</th>
<td>{{ $skill->skill_name }}</td>
<td>{{ $skill->employee_count }}</td>
<td style="width: 25%;">
<button class="btn btn-outline-danger" type="button" title="Delete Skill">Delete Skill</button>
</td>
</tr>
#endforeach
</tbody>
If you still want to use your method totalEmp you can modify it like this:
public function totalEmp(){
return $this->employee()->count();
}
One other thing that I strongly recommend is to make sure you name your models and relationships properly.
Models should be named like: Skill and Employee
Relationships should be named like: employees() and skills()
Please see example here: https://laravel.com/docs/9.x/eloquent-relationships#many-to-many

Laravel need data from two different collections in for each

I have a for each loop to iterate over the collection of records. It has the user_id, but not the other user credentials. I created another collection of user. How do I include this in my iteration?
Controller
public function index(Request $request, $id)
{
$thoughts = Thought::where('id', $id)->orderBy('created_at', 'desc')->paginate(100)->get();
$user = User::where('id', $thoughts->user_id);
return view('backend.pages.thought_list', compact('thoughts, user'));
}
View
#foreach ($thoughts as $thought)
<tr>
<td class="col-md-1">{{ $user->username}} <span class="user_id_thought">ID - {{ $user->user_id}}</span></td>
<td class="col-md-1">{{ $thought->response }}</td>
<td>{{ $thought->thought_type }}</td>
<td>{{ $thought->id }}</td>
</tr>
#endforeach
How do I display my users variable in the loop. Or is there a better way of doing this.
Eager Loading is the perfect use case for your requirement in my opinion.
e.g.
$thoughts = Thought::with('user')->get();
foreach ($thoughts as $thought) {
echo $thought->user->name;
}
For more details see: https://laravel.com/docs/5.6/eloquent-relationships#eager-loading

Laravel blade one-to-one relationship in #foreach loop

I have a problem with show one to one relationship in Laravel.
I have that tables in DB:
manufacturers with defaul Laravel id column and cashregisters with manufacturer_id column releated to id from manufacturers table.
In models I do that:
Cashregister
class Cashregister extends Model
{
protected $table = "cashregisters";
public function manufacturer(){
return $this->hasOne('App\Manufacturer','id');
}
}
Manufacturer
class Manufacturer extends Model
{
protected $table = "manufacturers";
public function cashregisters(){
return $this->belongsTo('App\Cashregister');
}
}
I think this is ok. Next I do in CashregisterController that in index():
$cashregisters = Cashregister::all();
return view('cashregisters.index')->withCashregisters($cashregisters);
In cashregisters index View i do that loop:
<tbody>
#foreach ($cashregisters as $cashregister)
<tr>
<th>{{ $cashregister->id }}</th>
<td>{{ $cashregister->manufacturer_id }}</td>
......
<td>{{ $cashregister->DataNastPrzegUrzFisk }}</td>
#endforeach
</tr>
</tbody>
And it works great. Show everything from cashregisters table.
I want to display Manufacturer name instead of manufacturer_id, so I try do this that:
<td>{{ $cashregister->manufacturer->name }}</td>
Bu I got an error.
Next I searchover internet and try do that thing
#foreach ($cashregisters as $cashregister)
<tr>
<th>{{ $cashregister->id }}</th>
#foreach ($cashregister->manufacturer() as $item)
<td>{{ $item->name}}</td>
#endforeach
......
<td>{{ $cashregister->DataNastPrzegUrzFisk }}</td>
#endforeach
</tr>
</tbody>
But this piece of code show nothing :( Can anyone help me to fix that. When I after all table do that code:
<td>{{ $cashregister->manufacturer->name}}</td>
it show some information, so I think relationship is ok.
You need to set local key for manufacturer, like this:
class Cashregister extends Model
{
protected $table = "cashregisters";
public function manufacturer(){
return $this->hasOne('App\Manufacturer','id', 'manufacturer_id');
}
}
Use with() to load the relation:
$cashregisters = Cashregister::with('manufacturer')->all();
Then this will show the manufacturer:
$cashregister->manufacturer->name

Laravel view won't loop model even though it is populated

I'm creating a web based interface for my dovecot database.
I can get a list of all the virtual domains in the database and the number of emails and aliases easily enough.
But when I try to load a page to list the emails under a specific domain, it goes weird.
Three simple models:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class VirtualDomain extends Model
{
public function emails()
{
return $this->hasMany('App\VirtualUser', 'domain_id');
}
public function aliases()
{
return $this->hasMany('App\VirtualAlias', 'domain_id');
}
}
class VirtualUser extends Model
{
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password',
];
}
class VirtualAlias extends Model
{
//
}
My default controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\VirtualDomain;
use App\VirtualUser;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* #return \Illuminate\Http\Response
*/
public function index()
{
return view('home', [
'domains' => VirtualDomain::all(),
]);
}
public function domain($name)
{
$domain = VirtualDomain::where('name', $name)->first();
return view('domain', [
'domain' => $domain,
'emails' => VirtualUser::where('domain_id', $domain->id),
]);
}
}
and a couple of simple blades
home.blade.php
<p>
{{ $domains->count() }} domains
</p>
<table class="table-summary">
<thead>
<tr>
<th>name</th>
<th>emails</th>
<th>aliases</th>
<th class="table-summary-options">options</th>
</tr>
</thead>
<tbody>
#foreach ($domains as $domain)
<tr>
<td><a title="view emails for this domain" href="domain/{{ $domain->name }}">{{ $domain->name }}</a></td>
<td>{{ $domain->emails->count() }}</td>
<td>{{ $domain->aliases->count() }}</td>
<td class="table-summary-options"><a class="ui-action" title="remove this domain" href=""><img src="/img/ui/remove.png" alt="remove"></a></td>
</tr>
#endforeach
</tbody>
</table>
and domain.blade.php
<p>
<< - {{ $domain->name }} - {{ $emails->count() }} emails
</p>
<table class="table-summary">
<thead>
<tr>
<th>email</th>
<th class="table-summary-options">options</th>
</tr>
</thead>
<tbody>
#foreach ($emails as $email)
<tr>
<td><a title="view aliases for this domain" href="email/{{ $email->email }}">{{ $email->email }}</a></td>
<td class="table-summary-options"><a class="ui-action" title="remove this email" href=""><img src="/img/ui/remove.png" alt="remove"></a></td>
</tr>
#endforeach
</tbody>
</table>
The view outputs the correct number of emails under the domain with {{ $emails->count() }} - but the#foreach ($emails as $email)` does not loop.
When I modify the blade to simple use the emails from the domain variable ({{ $domain->emails->count() }} and #foreach ($domain->emails as $email)), I get the right count and the list is populated correctly.
What's making it go wrong when using the emails variable?
You have to make a small change for it to work
public function domain($name)
{
$domain = VirtualDomain::where('name', $name)->first();
return view('domain', [
'domain' => $domain,
'emails' => VirtualUser::where('domain_id', $domain->id)->get(),
]);
}
Without ->get() you will get a query builder instance while with get() will return a collection. In the foreach loop a collection can be iterated while a query builder instance can't be.
Hope this helps

Many to Many form binding

How to binding a input with many to many relationship data?
My relationship is: a Model has many Damages, and a Damage has many Models. in pivot table exists a price field.
I need populate a input with price data.
{{ Form::input('number', "prices[{$model->id}][{$damage->id}]") }}
My Model:
class Model extends \BaseModel {
public function damages()
{
return $this->belongsToMany('Damage', 'prices_damages', 'model_id', 'damage_id')
->withPivot('price')
->withTimestamps();
}
}
Pivot table
Schema::create('prices_damages', function(Blueprint $table)
{
$table->increments('id');
$table->integer('model_id')->unsigned();
$table->integer('damage_id')->unsigned();
$table->float('price')->nullable();
$table->timestamps();
});
Controller
/**
* Display a index dashboard page.
*
* #return \Illuminate\Http\Response
*/
public function getDamages()
{
$models = \Model::orderBy('order')->get();
$damages = \Damage::orderBy('order')->get();
return $this->render('Avarias', 'prices.damages', compact('models', 'damages'));
}
View:
<table class="table-striped table-header-rotated">
<thead>
<tr>
<th></th>
#foreach ($damages as $damage)
<th class="vertical"><div><span>{{ $damage->name }}</span></div></th>
#endforeach
</tr>
</thead>
<tbody>
#foreach ($models as $model)
<tr>
<td>{{ $model->fullname }}</td>
#foreach ($damages as $damage)
<td>
{{ Form::input('number', "prices[{$model->id}][{$damage->id}]", null, ['min' => 0, 'step' => 0.01]) }}
</td>
#endforeach
</tr>
#endforeach
</tbody>
</table>
You can't bind a collection (in the sense of laravel form model binding), so you can do this:
#foreach ($model->damages as $damage)
{{ Form::input('number', "damages[{$damage->id}][price]", $damage->pivot->price) }}
#endforeach

Resources