How do i count for a specific data in a column in Laravel? - 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

Related

Eloquent 3 tables relationship in Vue component

I have got this relationships:
CUSTOMER
id
name
ADDRESS
id
name
customer_id
country_id
COUNTRY
id
name
My Customer model:
class Customer extends Model
{
use SoftDeletes;
public function addresses()
{
return $this->hasMany(Address::class);
}
My address model:
class Address extends Model
{
use SoftDeletes;
public function country()
{
return $this->hasOne(Country::class);
}
And right now doing:
Customer::with('addresses')->orderBy('id', 'DESC')
I can get all customer with his addresses. But How Can I get the name of country fro customer?
I know getting in blade, if I do 2 loops I can create $address variable and get country names:
loop customers
loop adddresses
$address->name
$address->country (name of country)
But if I try in Vue:
<tr v-for="(customer, index) in customers" :key="customer.id">
<td>{{ customer.id}}</td>
<td>{{ customer.firstname}}</td>
<td>{{ customer.lastname}}</td>
<td v-if="customer.addresses">
<tr v-for="(address, index) in customer.addresses" :key="address.id">
<td>{{ address.name }}</td>
<td>{{ address.country }}</td>
</tr>
</td>
I dont get any value in addres.country. I mean, how Can I relate CUSTOMERS & COUNTRIES for usign in Vuejs?
Thans in advance
I found a solution!
I updated Address model like this:
class Address extends Model
{
public function country()
{
return $this->belongsTo(Country::class);
}
protected $with = ['country'];
}
then I can solve country name like this:
<tr v-for="(customer, index) in customers" :key="customer.id">
<td>{{ customer.id}}</td>
<td>{{ customer.name}}</td>
<td v-if="customer.addresses">
<tr v-for="(address, index) in customer.addresses" :key="address.id">
<td>{{ address.name }}</td>
<td>{{ address.country.name }}</td>
</tr>
</td>

How to handle missing records of a Product's attribute value in laravel 7

Background Information:
I'm learning laravel 7 by creating an inventory system.
I have several models: Product, Stock, and Attribute.
The Product is like a group that holds all of the stock. Because I can have a Product like a speaker. But I can have many quantities of that same product that have different attribute values. This is when the Stock becomes important.
The Stock is where I have all of the items in my inventory.
For example: If I have 20 speakers, there will be one record in the products table and 20 records in the stocks table.
The Attribute is because I want to track different attributes depending on the Product. But the value of those attributes doesn't belong to the product; It belongs to the Stock of the product.
This is how my models relate:
A Product hasMany Stock
A Stock belongsTo a Product
A Product hasMany Attribute
An Attribute belongsTo a Product
An Attribute belongsToMany Stock
A Stock belongsToMany Attribute (I'm not very confident about this one.)
Goal:
The Product is L-Acoustics LA12X
The product's Attributes are Serial Number and Firmware
The product's Stock is the card that says "Stock Level".
For testing and simplicity, I copied the same values of Serial Number and Firmware to all of the Product's Stock; but in real life they should all have different serial numbers and firmware versions.
Problem:
I only get the output from the picture above when there is a value for each attribute. For example: if I delete the attribute value of the Serial Number from the first stock I get this.
Do you see how it shifted the Firmware value to the Serial Numbers column?
In the case that the record is deleted or the value hasn't been created I would like to put an empty cell in that table so that it looks like this. (I created this image by editing the HTML of the page, I still don't know how to get this output)
What I have tried:
I quickly thought of creating empty cells in my database belonging to the attribute so that when I loop through my records I get a perfect table. But this will quickly become a problem when my database increases size.
You could think of why would there be records that don't exist? Well one example is that my inventory is so old that there are some Serial Numbers that I can't retrieve. Therefore, there will be no records in the table.
Showing some code:
This is my database design:
This is my Product Model:
class Product extends Model {
protected $guarded = [];
protected $casts = [
'brand_id' => 'integer',
'category_id' => 'integer',
'price' => 'decimal:2',
'rental_price' => 'decimal:2',
'active' => 'boolean'
];
public function setNameAttribute($value)
{
$this->attributes['name'] = $value;
$this->attributes['slug'] = Str::slug($value);
}
public function brand()
{
return $this->belongsTo(Brand::class);
}
public function category()
{
return $this->belongsTo(Category::class);
}
public function stocks()
{
return $this->hasMany(Stock::class)->orderBy('barcode', 'asc');
}
public function attributes()
{
return $this->hasMany(Attribute::class);
}
}
This is my Stock Model:
class Stock extends Model {
protected $guarded = [];
protected $casts = [
'quantity' => 'integer',
'product_id' => 'integer',
];
public function product()
{
return $this->belongsTo(Product::class);
}
public function attributes()
{
return $this->belongsToMany(Attribute::class)->withPivot('value');
}
}
This is my Attribute Model:
class Attribute extends Model {
protected $guarded = [];
protected $casts = ['product_id' => 'integer'];
public function product()
{
return $this->belongsTo(Product::class);
}
public function stocks()
{
return $this->belongsToMany(Stock::class);
}
public function values()
{
// This method is empty because I don't know how to relate the attribute to its values.
}
I know I have dumped a lot of information but I really need help. I have an open mind and I'm open to new implementations.
UPDATE
Here is the code of my view:
<div class="block-content block-content-full">
<div class="table-responsive">
<table class="table table-borderless table-striped table-vcenter font-size-sm">
<thead>
<tr>
<th class="text-left">Barcode</th>
#foreach($product->attributes as $attribute)
<th class="text-center">{{ $attribute->name }}</th>
#endforeach
<th class="text-right">Condition</th>
<th class="text-right">Updated</th>
</tr>
</thead>
<tbody>
#forelse($product->stocks as $stock)
<tr>
<td class="text-left">
<strong>{{ $stock->barcode }}</strong>
</td>
#foreach($stock->attributes as $attribute)
<td class="text-center">{{ $attribute->pivot->value }}</td>
#endforeach
<td class="text-right">
#switch($stock->condition)
#case('Great')
<span class="badge badge-success"><i class="fa fa-check-circle mr-1"></i>Great</span>
#break
#case('Damaged')
<span class="badge badge-warning"><i class="fa fa-exclamation-circle mr-1"></i>Damage</span>
#break
#case('Broken')
<span class="badge badge-danger"><i class="fa fa-times-circle mr-1"></i>Broken</span>
#break
#endswitch
</td>
<td class="text-right">{{ $stock->updated_at->format('M d, Y') }}</td>
</tr>
#empty
<tr><td colspan="100" class="text-center">No Stock Available</td></tr>
#endforelse
</tbody>
</table>
</div>
</div>
Here is the show method in my controller:
public function show(Product $product)
{
return view('products.show', compact('product'));
}
UPDATE 2:
I updated my Attribute model to this:
class Attribute extends Model {
protected $guarded = [];
protected $casts = ['product_id' => 'integer'];
public function getValueByBarcode($stock)
{
return $this->values()->where('barcode', $stock)->first()->value ?? '';
}
public function product()
{
return $this->belongsTo(Product::class);
}
public function stocks()
{
return $this->belongsToMany(Stock::class);
}
public function values()
{
return $this->belongsToMany(Stock::class)->select('value');
}
}
and in my blade I got the values like this:
<table class="table table-borderless table-striped table-vcenter font-size-sm">
<thead>
<tr>
<th class="text-left">Barcode</th>
#foreach($product->attributes as $attribute)
<th class="text-center">{{ $attribute->name }}</th>
#endforeach
<th class="text-right">Condition</th>
<th class="text-right">Updated</th>
</tr>
</thead>
<tbody>
#forelse($product->stocks as $stock)
<tr>
<td class="text-left">
<strong>{{ $stock->barcode }}</strong>
</td>
#foreach($product->attributes as $attribute)
<td class="text-center">{{ $attribute->getValueByBarcode($stock->barcode) }}</td>
#endforeach
<td class="text-right">
#switch($stock->condition)
#case('Great')
<span class="badge badge-success"><i class="fa fa-check-circle mr-1"></i>Great</span>
#break
#case('Damaged')
<span class="badge badge-warning"><i class="fa fa-exclamation-circle mr-1"></i>Damage</span>
#break
#case('Broken')
<span class="badge badge-danger"><i class="fa fa-times-circle mr-1"></i>Broken</span>
#break
#endswitch
</td>
<td class="text-right">{{ $stock->updated_at->format('M d, Y') }}</td>
</tr>
#empty
<tr><td colspan="100" class="text-center">No Stock Available</td></tr>
#endforelse
</tbody>
</table>
This is the result of that change
I finally managed to get what I wanted but is there a cleaner way of doing the same thing?
I would use keyBy on your collection of stock attributes (Untested)
#php($s_attributes = $stock->attributes->keyBy('attribute_id'))
#foreach($product->attributes as $p_attribute)
<td class="text-center">{{ isset($s_attributes[$p_attribute->id]) ? $s_attributes[$p_attribute->id]->pivot->value : ''}}</td>
#endforeach

Trying to get property 'name' of non-object. Model relationship not working

I am getting an error message relating to my model relationship and have an identical section of my other app that works fine. I've looked through everything many times and cannot see any reason my model relationship should not work. When I try to access the model relationship in the view, I get "Trying to get property 'name' of non-object"
I have the following in my Controller:
public function edit($id)
{
//
$plansubmission = PlanSubmission::find($id);
$hradmins = PSPlanToHRAdminMapping::where('plan_submission_plan_id', $id)->get();
return view('planbuilder.add-hr-administrators', compact('plansubmission', 'hradmins'));
I have the following in my View:
<h3 class="py-3">Current HR Administrators with Online Access</h3>
<table class="table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Email Address</th>
</tr>
</thead>
<tbody>
#foreach($hradmins as $hradmin)
<tr>
<td> {{$hradmin->hradmin->name }}</td>
<td> {{$hradmin->hradmin->email }}</td>
#endforeach
</tr>
</tbody>
</table>
I have the following in my Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class PSPlanToHRAdminMapping extends Model
{
//
protected $guarded = [];
public function hradmin()
{
return $this->belongsTo('App\HRAdmin');
}
}
I have the following Migration:
{
Schema::create('p_s_plan_to_h_r_admin_mappings', function (Blueprint $table) {
$table->unsignedBigInteger('plan_submission_plan_id');
$table->unsignedBigInteger('h_r_admin_id');
$table->timestamps();
$table->foreign('plan_submission_plan_id')->references('id')->on('plan_submissions');
$table->foreign('h_r_admin_id')->references('id')->on('h_r_admins');
});
}
It's possible that for some records you don't have any record for hradmin relationship so you can try adding optional helper like so:
<td> {{optional($hradmin->hradmin)->name }}</td>
<td> {{optional($hradmin->hradmin)->email }}</td>
for example to avoid error

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

Accessing and displaying pivot table data

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();.

Resources