How to foreach a hasMany relationship in Laravel 6 - laravel

I'm using Laravel 6 and I want to foreach a hasMany relationship between Layanan Model and Objek Model
Layanan Model
class Layanan extends Model
{
protected $fillable = ['nama_layanan'];
protected $primaryKey = 'layanans_id';
public function objeks()
{
return $this->hasMany(Objek::class);
}
}
Objek Model
class Objek extends Model
{
protected $fillable = ['layanan_id', 'tipe_objeks'];
protected $primaryKey = 'objeks_id';
public function layanan()
{
return $this->belongsTo(Layanan::class);
}
}
Controller.php
class ObjekController extends Controller
{
public function object()
{
$objek = \App\Objek::all();
return view('pages.alat.object.object')->with('data', $objek);
}
}
View
#foreach ($data as $item)
#foreach ($item->layanan as $layanan)
<tr>
<td>{!! $item->objeks_id !!}</td>
<td>{!! $layanan->layanan_id !!},</td>
<td>{!! $item->tipe_objeks !!}</td>
<td>{!! $item->created_at->format('d/m/Y') !!}</td>
<td>{!! $item->updated_at->format('d/m/Y') !!}</td>
</tr>
#endforeach
#endforeach
I get the error
Invalid argument supplied for foreach()
What did I do wrong? Sometimes, it says that 'layanan' is non-object.

class Objek extends Model
{
protected $fillable=[
'layanan_id', 'tipe_objeks'
];
protected $primaryKey = 'objeks_id';
public function layanan() {
return $this->hasMany(Layanan::class,'layanan_id','layanan_id');
}
}
if you put Objek hasMany layanan
then only u can loop over 1 row of Objek
like $data = Objek::find(1(=);
foreach($data as $layanan)

Just update your view as below to add if condition to check wheather relationship values exist for each record.
#foreach ($data as $item)
#if($item->layanan->count())
#foreach ($item->layanan as $layanan)
<tr>
<td>{!! $item->objeks_id !!}</td>
<td>{!! $layanan->layanan_id !!},</td>
<td>{!! $item->tipe_objeks !!}</td>
<td>{!! $item->created_at->format('d/m/Y') !!}</td>
<td>{!! $item->updated_at->format('d/m/Y') !!}</td>
</tr>
#endforeach
#endif
#endforeach

My problem was exactly what you got. I can't loop collection from hasMany relationship.
I'd solved by converting data into object instead of collection.
public function convertToObject($data) {
return json_decode(json_encode($data), FALSE);
}
public function object() {
$objek = \App\Objek::all();
$objek = $this->convertToObject($objek);
return view('pages.alat.object.object')->with('data', $objek);
}

when you define relationship it will take id as PK so good practice define FK and PK like below
class Layanan extends Model
{
protected $fillable=[
'nama_layanan'
];
protected $primaryKey = 'layanans_id';
public function objeks() {
return $this->hasMany('App\Objek', 'layanan_id', 'layanans_id');
}
}
then you need to use IF()
#foreach ($data as $item)
#if(isset($item->layanan)) //// #if($item->layanan && is_array($item->layanan)) or #if(count($item->layanan) > 0)
#foreach ($item->layanan as $layanan)
#endforeach
#endif
#endforeach
UPDATE
as per your relationship if you take data from Objek then use like this
#foreach ($data as $item)
<tr>
<td>{!! $item->objeks_id !!}</td>
<td>{!! $item->layanan->layanans_id !!},</td>
<td>{!! $item->tipe_objeks !!}</td>
<td>{!! $item->created_at->format('d/m/Y') !!}</td>
<td>{!! $item->updated_at->format('d/m/Y') !!}</td>
</tr>
#endforeach

Related

Error when trying to make relation in laravel

I'm trying to make a relationship in Laravel but always get the error:
Trying to get property 'nama_guru' of non-object (View: D:\xampp\htdocs\supervisi_digital\resources\views\ManagementSupervisi\index.blade.php).
Here is my code:
Dokumen Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Dokumen extends Model
{
protected $table = "dokumen";
protected $primarikey = "id";
protected $fillable =
[
'id', 'nama_guru', 'mapel', 'file', 'keterangan'
];
public function supervisi()
{
return $this->hasMany(Supervisi::class);
}
}
Supervisi Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Supervisi extends Model
{
protected $table = "supervisi";
protected $primarikey = "id";
protected $fillable =
[
'id', 'id_guru', 'id_mapel', 'id_keterangan', 'penilaian'
];
public function dokumen()
{
return $this->belongsTo(Dokumen::class);
}
}
Blade View
<table>
<thead>
<tr>
<th>#</th>
<th>NAMA GURU</th>
<th>MAPEL</th>
<th>KETERANGAN</th>
<th>PENILAIAN</th>
<th>ACTION</th>
</tr>
</thead>
<tbody>
#foreach ($dtSupervisi as $item)
<tr>
<td>{{ $loop->iteration }}</td>
<td>{{ $item->dokumen->nama_guru }}</td>
<td></td>
<td></td>
<td></td>
</td>
</tr>
#endforeach
</tbody>
</table>
Controller
public function index()
{
$dtSupervisi = Supervisi::with('dokumen')->paginate(10);
return view('ManagementSupervisi.index', compact('dtSupervisi'));
}
I don't know what the problem is.
You need to define the foreign key and local key in your relationship.
public function dokumen()
{
return $this->belongsTo(Dokumen::class,'foreign_key','localkey');
}
so
public function dokumen()
{
return $this->belongsTo(Dokumen::class,'id_guru','id');
}
protected $fillable =
[
'id', 'id_guru', 'id_mapel', 'id_keterangan', 'penilaian', 'dokumen_id'
];
Use dokumen_id in Supervisi model and supervisi table after migrate database may be it's work

How to Generate Leave Balance Table for a particular employee using Laravel

In my Laravel-5.8 project, when an employee logs in I want to display a table as shown below that shows his Leave Balance
I have 3 tables that are applicable
class LeaveCategory extends Model
{
protected $table = 'leave_categories';
protected $fillable = [
'leave_category_name',
];
public function leavecategorydetail()
{
return $this->hasMany('App\Models\LeaveCategoryDetail');
}
}
class LeaveCategoryDetail extends Model
{
protected $table = 'leave_category_details';
protected $fillable = [
'id',
'leave_category_id',
'employment_type_id',
'no_of_days',
];
public function leavecategory()
{
return $this->belongsTo('App\Models\LeaveCategory', 'leave_category_id', 'id');
}
public function employmenttype()
{
return $this->belongsTo('App\Models\EmploymentType', 'employment_type_id', 'id' );
}
}
class LeaveRequest extends Model
{
protected $table = 'leave_requests';
protected $fillable = [
'id',
'employee_id',
'leave_category_id',
'leave_status',
'approved_days',
];
public function employee()
{
return $this->belongsTo('App\Models\Employee','employee_id');
}
public function leavetype()
{
return $this->belongsTo('App\Models\LeaveCategory','leave_category_id');
}
}
As earlier said, the expected result is to have 4 columns (Leave Category
, Applicable Leave, Approved Leave
, Available
)
Controller
public function leave_balance()
{
$userId = Auth::user()->id;
$userCompany = Auth::user()->company_id;
$employmentcategory = Employee::select('employeement_category_id')->where('employee_id', $userId)->where('is_active', 1)->first();
//Leave Category
$leavecategories = LeaveCategory::select('leave_category_name')->where('company_id', $userCompany)->get();
//Applicable Leave
$applicableleaves = DB::table('leave_categories')
->join('leave_category_details', 'leave_category_details.leave_category_id', '=', 'leave_categories.id')
->select('leave_category_details.no_of_days')
->where('leave_categories.company_id', $userCompany)
->where('leave_categories.employment_category_id',$employmentcategory)
->get();
//Approved Leave
$approvedleaves = DB::table('leave_requests')
->select('employee_id','leave_category_id', DB::raw('SUM(approved_days) AS approvedLeave'))
->where('employee_id', $userId)
->where('leave_category_id', $employmentcategory)
->where('leave_status',4)
->groupBy('employee_id', 'leave_category_id')
->get();
//Available
$availableleaves = $applicableleaves - $approvedleaves
$leavebalances = ...
return view('leave-balances')
->with('leavebalances', $leavebalances)
}
How do I combine the four queries in my controller ($leavecategories, $applicableleaves, $approvedleaves, $availableleaves) into $leavebalances and also get a view like
See the leave balance image
<thead>
<tr>
<th width="55%">
Leave Category
</th>
<th width="15%">
Applicable Leave
</th>
<th width="15%">
Approved Leave
</th>
<th width="15%">
leavebalances
</th>
</tr>
</thead>
<tbody>
<!--start foreach-->
<td>
</td>
<td>
</td>
<td>
</td>
<td>
</td>
<!--end foreach-->
If there is no field/value for $approvedleaves, it should initialize with 0
Thank you.
bro you can try make the two querys in one like this is not perfect but you get the idea
$all = DB::table('leave_category_details')
->leftJoin('leave_categories', function($join)
{
$join->on('leave_categories.id', '=', 'leave_category_details.leave_category_id');
$join->on('leave_categories.employment_category_id',$employmentcategory);
$join->on('leave_categories.company_id','=', $userCompany);
})
->leftJoin('leave_requests', function($join)
{
$join->on('leave_requests.employee_id', '=', $userId);
$join->on('leave_requests.leave_category_id', $employmentcategory);
$join->on('leave_status',4);
})
->groupBy('leave_requests.employee_id', 'leave_requests.leave_category_id')
->select('leave_category_details.no_of_days','leave_requests.employee_id','leave_requests.leave_category_id', DB::raw('SUM(leave_requests.approved_days) AS approvedLeave'),DB::raw('SUM(leave_requests.approved_days) - leave_category_details.no_of_days AS availableleaves')
->get();
Why get each column in a separate query when you can get them all in one.
I adjusted your controller like so:
public function leave_balance()
{
$userId = Auth::user()->id;
$userCompany = Auth::user()->company_id;
$employmentCategoryId = Employee::where('employee_id', $userId)->where('is_active', 1)->value('employeement_category_id');
//Leave Table
$leaveTable = DB::table('leave_categories as lc')
->join('leave_category_details as lcd', 'lcd.leave_category_id', '=', 'lc.id')
->join('leave_requests as lr', 'lr.leave_category_id', '=', 'lc.id')
->select('lcd.no_of_days as applicableLeaves ','lc.leave_category_name as leaveCategory',DB::raw('SUM(lr.approved_days) AS approvedLeaves'))
->where([
['lc.company_id', $userCompany],
['lc.employment_category_id',$employmentCategoryId],
['lr.employee_id', $userId],
['lr.leave_status',4]
])
->groupBy('leaveCategory', 'applicableLeaves')
->get();
return view('leave-balances')
->with('leaveTable', $leaveTable)
}
$availableleaves can be calculated in the view no need to send it.
Now in your view you display them like this:
<thead>
<tr>
<th width="55%">
Leave Category
</th>
<th width="15%">
Applicable Leave
</th>
<th width="15%">
Approved Leave
</th>
<th width="15%">
leavebalances
</th>
</tr>
</thead>
<tbody>
#foreach ($leaveTable as $tableRow)
<tr>
<td>
{{$tableRow->leaveCategory}}
</td>
<td>
{{$tableRow->applicableLeaves}}
</td>
<td>
#if(!$tabelRow->approvedLeaves->isEmpty()){{$tabelRow->approvedLeaves}}#else 0 #endif
</td>
<td>
{{($tableRow->applicableLeaves - $tabelRow->approvedLeaves)}}
</td>
</tr>
#endforeach
</tbody>
HTML is not my strongest field so your table might not show as wanted but i think its all good.

Laravel : get data for user using relationships

Get user's answer , question in page by user id , i want to display table for each user contains his answers with questions
I tried to create show page and add show function in UserController
Controller :
public function show($id)
{
$user = User::find($id);
$user_id = $user->id;
$survey = \App\Survey::pluck('title', 'id')->toArray();
$answers = \App\Answer::where('user_id','=',$user_id)->get();
return view('users.show', compact('user','survey','answers'));
}
view:
<table class="table">
<thead class="thead-light">
<tr>
<th>{{ __('Question') }}</th>
<th>{{ __('Answers') }}</th>
<th>{{ __('Creation Date') }}</th>
</tr>
</thead>
<tbody>
#foreach($answers as $t)
<tr>
<td> {{ optional($t->survey)->title }} </td>
<td> {{ $t->answer }} </td>
<td> {{$t->created_at}} </td>
</tr>
#endforeach
</tbody>
</table>
Answer model :
class Answer extends Model
{
protected $fillable = ['answer','commentaire','user_id','survey_id','last_ip'];
protected $table = 'answer';
public function survey()
{
return $this->belongsTo('App\Survey', 'survey_id');
}
public function question()
{
return $this->belongsTo(Question::class);
}
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
}
user model :
public function questions()
{
return $this->hasMany(Question::class);
}
public function answers()
{
return $this->hasMany(Answer::class);
}
the tables :
answer :
Survey :
I got empty part for question row
You are performing some queries that you actually don't need.
First get rid of this to lines:
$user_id = $user->id; $survey = \App\Survey::pluck('title', 'id')->toArray();
Then change your query to get your answers:
$answers = \App\Answer::where('user_id','=',$user->id)->with(['survey'])->get();
return view('users.show', compact('user','answers'));
Now in your view you could just do this:
<td> {{ $t->survey->title }} </td>
<td> {{ $t->answer }} </td>
<td> {{$t->created_at}} </td>

Soft delete on table

I just started using soft delete and I am not really sure how to do it, I am just following someone example and is still unsure how to do a proper soft delete. All I want is to delete the personal_info table but I keep on getting a no error message which make me at lost since I don't know what I am doing wrong, can someone help me? Thanks a lot
home.blade.php
<table class="table table-bordered">
<tr>
<th><strong><big>Name: </big></strong></th>
<th><strong><big>Action </big></strong></th>
</tr>
<td>
<tr>
#foreach($data as $value)
<tr>
<th>{{$value->Name}}</th>
<th><form action="{{ url('/home/'.$value->id.'/delete') }}" method="post">
<button>Delete</button>
</form></th>
</tr>
#endforeach
</tr>
</tr>
</table>
Controller:
public function delete($id = 0){
if ($id > 0){
personal_info::destroy($id);
}
return redirect("/home");
}
or should I do it this way?
public function delete($id){
$data = personal_info::find($id)->delete();
return redirect("/home");
}
personal_info model:
use Illuminate\Database\Eloquent\Model;
use Eloquent;
use SoftDeletes;
class personal_info extends Eloquent
{
protected $fillable = array('Name');
protected $table = 'personal_infos';
protected $primaryKey = 'id';
protected $dates = ['deleted_at'];
public function user_info1s() {
return $this->hasMany('App\user_info1','user_id');
}
Route: (not sure should I use DELETE instead)
Route::get('/home/{id}/delete', 'HomeController#delete');
There is no need to use delete on the method. try to change your function to this
public function delete($id){
personal_info::findOrFail($id)->delete();
return back();
}
Edit
The Route method and Form method must be the same.
Change
Route::get('/home/{id}/delete', 'HomeController#delete');
To
Route::post('/home/{id}/delete', 'HomeController#delete');
Controller Method
use Carbon\Carbon;
public function delete($id)
{
personal_info::find($id)->update([
'deleted_at' => Carbon::now()
]);
return redirect()->back();
}
home.blade.php
<table class="table table-bordered">
<tr>
<th><strong><big>Name: </big></strong></th>
<th><strong><big>Action </big></strong></th>
</tr>
<td>
<tr>
// Example of adjusting your query to support soft deletes
#php
$data = Some\Model::whereNotNull('deleted_at')->get();
#endphp
#foreach($data as $value)
<tr>
<th>{{$value->Name}}</th>
<th><form action="{{ url('/home/'.$value->id.'/delete') }}" method="post">
<button>Delete</button>
</form></th>
</tr>
#endforeach
</tr>
</tr>
</table>

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

Resources