Multiple level relationships on Laravel - php

I have this problem. In Laravel, I have 3 Models, ie:
class Department{
public function coordinations(){
return $this->has_many('Coordination');
}
}
class Coordination{
public function sites(){
return $this->has_many('Site');
}
}
class Site{
public function assets(){
return $this->has_many('FAssets');
}
}
class FAsset{}
I want to display all the asset grouped by Departments. I tryied to do something like:
#foreach($dependencias as $dep)
<tr>
<td colspan="8" style="width:100%">Dependencia: {{ $dep->code }} {{ $dep->description }}</td>
</tr>
#foreach($dep->coordinations()->sites()->assets()->get() as $asset)
...
But I got the "Method [sites] is not defined on the Query class" error. I was looking for the eager loading way but I dont see how it could work with an extra level on the relationships.
Also, I was considering to querying all the assets and filtering the assets given the actual coordination, but I think that this is going to take a lot of time to load...
Thanks in advance

class Deparment{
public function department_assets(){
$assets = array();
foreach($this->has_many('Coordination')->get() as $coordination){
foreach($coordination->sites()->get() as $site){
foreach($site->assets() as $asset)
{
$assets[] = $asset;
}
}
}
return $assets;
}
}
Now we can...
#foreach($dep->department_assets() as $asset)

Related

Display one image from database in laravel

I have two tables in Laravel
jalans
jalan_images
In one id_jalan can be many images, My relations like this
Model Jalan
// Model Jalan
public function jalanImage()
{
// return $this->hasMany('App\JalanImage', 'id_jalan');
return $this->hasMany(JalanImage::class, 'id_jalan');
}
public function firstImage()
{
// return $this->hasOne('App\JalanImage', 'id_jalan');
return $this->hasOne(JalanImage::class, 'id_jalan');
}
Model JalanImage
// Model JalanImage
public function jalan()
{
// return $this->belongsTo('App\Jalan', 'id_jalan', 'id');
return $this->belongsTo(Jalan::class, 'id_jalan', 'id');
}
I want to display data like title, created_at, and the first image in each id_jalan, in my Controller for display data like this
JalanController
// JalanController
public function jalan()
{
$jalan = Jalan::with('firstImage')->orderBy('created_at', 'DESC')->get();
return view('jalan.all', ['jalan' => $jalan]);
// dd($jalan->toArray());
}
In my view, first image from each id_jalan not displayed, my view like this
Blade View
#foreach($jalan as $row)
<div class="post-item clearfix">
<table border="0">
<tr>
<td width="170px">
<a href="#">
<img src="{{ asset('uploads/jalan/' .$row->first_image) }}" alt="" class="img-fluid" width="150px"></td>
</a>
<td>
<h4>{{ $row->title }}</h4>
<i class="icofont-wall-clock"></i> <time>{{ date('d F Y', strtotime($row->created_at)) }}</time>
<p style="color: #F3591F">{{ $row->category }}</p>
</td>
</tr>
</table>
</div>
#endforeach
When I display it with dd($jalan->toArray()); in my controller, the first_image displayed.
How to fix it?
Thank you
You should follow Laravel's (especially Eloquent's) advices and best practices regarding naming convention. That means your gallery_images table should have gallery_id field instead what you have there. Believe or not, that way you'll skip lot of unforced errors and questions. I will leave you answer of how I would do it in your place:
// \App\Gallery::class
class Gallery extends Model
{
public function galleryImages()
{
return $this->hasMany(GalleryImage::class);
}
public function firstGalleryImage()
{
return $this->hasOne(GalleryImage::class);
}
}
// \App\GalleryImage::class
class GalleryImage extends Model
{
public function gallery()
{
return $this->belongsTo(Gallery::class);
}
}
Basically you can set as many as you need methods in model that returns specific relation. In Gallery::class here, you can see I have set one more relation that would return just first image of that gallery. In controller you'd call it with
public function someControllerMethod()
{
$gallery = Gallery::with(['firstGalleryImage'])->first();
// or
$galleries = Gallery::with(['firstGalleryImage'])->get();
}
Now returned object $gallery or collection of $galleries would carry only first image related. In your case (and if you don't want to change names of fields) you need to follow documentation of how to set another model's keys as method's arguments.
you can use Laravel's Eloquent ORM ( Eager Loading ),
here is an example of a single Image(image) and multiple images (media) relationship.
public function media()
{
return $this->hasMany(Media::class, 'foreign_key', 'local_key');
}
public function defaultMedia()
{
return $this->hasOne(Media::class, 'foreign_key', 'local_key');
public function image()
{
return $this->defaultMedia();
}

Showing Empty after using eloquent un laravel

I have MyRoom.php and TotalCity.php in my project in which each room is categorizes inside a city
so that I did this in MyRoom.php
public function location()
{
return $this->belongsTo(TotalCity::class, 'location_id')->withTrashed();
}
and I did this in TotalCity.php
public function location()
{
return $this->hasMany(MyRoom::class , 'total_city_id')->withTrashed();
}
I have passed id with routes and controller like this
home.blade.php
id)}}">{{ $row->name }}
web.php
Route::get('/city/{id}/rooms/','SiteController#room')->name('room');
SiteController.php
public function room($id) {
$room = TotalCity::find($id)->location;
return view('frontend.pages.rooms', compact('room'));
}
rooms.blade.php
#foreach($room as $row)
<div class="room">
<img src="{{ asset(env('UPLOAD_PATH').'/' . $row['photoi1']) }}"/>
</div>
#endforeach
But this is not showing any rooms in any city while i have stored cities and rooms which comes under particular city in my database.
In the relationship method, you should mention foreign key and owner key as well,
for example in MyRoom.php:
public function location()
{
return $this->belongsTo(TotalCity::class, 'location_id', 'total_city_primary_key')->withTrashed();
}
you can read laravel documentioation for more detail.
foreignKey in TotalCity model is wrong :
public function location(){
return $this->hasMany(MyRoom::class, 'location_id')->withTrashed();
}
I strongly recommend you to change your model's names to City and Room. If you dont want to, make sure in both models you're connecting them to the correct table, you can achieve that using the $table variable in both models, like this:
$table = 'your_table_name';
This will make sure the table is connected right.
Also, you should make a few changes:
In your TotalCity model do this:
public function rooms()
{
return $this->hasMany(MyRoom::class , 'total_city_id')->withTrashed();
}
In your MyRoom model do this:
public function city()
{
return $this->belongsTo(TotalCity::class)->withTrashed();
}
In your SiteController do this:
public function room($id) {
$rooms = TotalCity::find($id)->rooms;
return view('frontend.pages.rooms', compact('rooms'));
}
In your View
#foreach($rooms as $room)
<h2>{{$room->name}}</h2>
<div class="room">
<img src="{{ asset(env('UPLOAD_PATH').'/' . $room['photoi1']) }}"/>
</div>
#endforeach

How could I get Laravel 5.2 Pagination for a collection being sent to a view?

I am using Laravel 5.2 and using a polymorphic relations table for a feeds page. A feed has pictures, articles, and links that have their own respective models. The controller method that I am using for the feed looks like this:
public function index()
{
$allActivity = Activity::get();
$activity = collect();
foreach($allActivity as $act)
{
$modelString = $act->actable_type;
$class = new $modelString();
$model = $class->find($act->actable_id);
$activity->push($model);
}
return view('feed', compact('activity'));
}
and here is the feed.blade.php view
#foreach($activity as $class)
// Gives me the model name so the correct partial view could be referenced
<?php
$split = explode("\\", get_class($class));
$model = lcfirst($split[1]);
?>
#include("partials.{$model}", [$model => $class])
#endforeach
Because of this setup, I can't get pagination using the method outlined in the Laravel documentation. How could I correctly implement pagination using this setup? Any help would be greatly appreciated.
Access your relation using the actable() relation you should have on your Activity model. It will also help you avoid using find() in the loop like you are which will give you an N+1 issue.
In your activity model you should have an actable method:
class Activity
{
public function actable()
{
return $this->morphTo();
}
}
Then in your view you can lazy load all polymorphic actable relations and pass to the view. You can even keep your view clean and resolve the model name in the map() function:
public function index()
{
$activity = Activity::with('actable')->get()->map(function($activity) {
$activity->actable->className = lcfirst(class_basename($activity->actable));
return $activity->actable;
});
return view('feed', compact('activity'));
}
Then in your view:
#foreach($activity as $model)
#include("partials.{$model->className}", [$model->className => $class])
#endforeach
To run this with pagination it would be:
Controller:
public function index()
{
$activities = Activity::with('actable')->paginate(25);
return view('feed', compact('activities'));
}
View:
#foreach($activities as $activity)
#include('partials.'.lcfirst(class_basename($activity->actable)), [lcfirst(class_basename($activity->actable)) => $activity])
#endforeach

Show data from multiple tables in view laravel

I asked question whose link is:
Link
Now my problem is that I want to show "CategoryName' of in food details.For that I add function in Categories.php model as:
public function food()
{
return $this->hasMany('App\Food','food_categories','Category_id','Food_id');
}
and in food.php
public function restaurant()
{
return $this->belongsToMany('App\Restaurant','food_restaurant','Food_id','Res_id');
}
public function categories()
{
return $this->belongsTo('App\Categories','food_categories');
}
Then in show.blade.php I add:
#foreach ($food->restaurant as $restaurant)
<h3><p>RestaurantName:</h3><h4>{{$restaurant->ResName}}</p></h4>
<h3><p>Contact #:</h3><h4>{{$restaurant->Contact}}</p></h4>
<h3><p>Location:</h3><h4>{{$restaurant->Address_Loc}}</p></h4>
#endforeach
#foreach ($food->categories as $categories)
<h3><p>CategoryName:</h3><h4>{{$categories->CategoryName}}</p></h4>
#endforeach
And I changed controller to :
public function show($Food_id)
{
$food = Food::with('restaurant.categories')->findOrFail($Food_id);
return view('show', compact('food'));
}
But it does not shows me categoryname.Plz help me where is the problem?
With Eager loading, "dot" notation loads nested relations.
In your controller you do
Food::with('restaurant.categories')
..this queries the restaurant() relation on the Food model, and that Restaurant's categories() relation.
I think you might need to call
Food::with('restaurant', 'categories')
as this will query both relations on the Food model.

Laravel resource::all with values from another table

I'm learning Laravel right now and i have following tables and resources (models, controllers, ect.):
tickets
- id
- title
- projectID
- statusID
projects
- id
- title
status
- id
- title
I have to make a list of my Tickets on the Startpage. Not nessesary to say that i need the Project- and Statustiltles and not the IDs. Currently i do:
Route::get('/', function()
{
$tickets = Ticket::all();
return View::make('layout')->with('tickets', $tickets);
});
My current output is:
tickets->id, tickets->title, tickets->projectID, tickets->statusID
The output i want is
tickets->id, tickets->title, tickets->projects->title, tickets->status->title
So i hope anyone can understand what i'm trying to ask here and maybe provide me some help. Thank you!
Resolution: I had to set the foreign_keys first in my DB. Then i used the relationships mentioned in the answers and it works fine.
My Model:
class Ticket extends \Eloquent {
protected $fillable = [];
public function project()
{
return $this->hasOne('Project', 'id', 'projectID');
}
public function status()
{
return $this->hasOne('Status', 'id', 'statusID');
}
}
My View:
#foreach($tickets as $key => $value)
...
<td>{{ $value->project->title }}</td>
<td>{{ $value->status->title }}</td>
...
#endforeach
If you configure you relationships correctly you can do that without problems using the Laravel Eager Loading feature, for example:
Eager Loading (Laravel docs)
Eager loading exists to alleviate the N + 1 query problem...
class Ticket extends Eloquent {
public function project()
{
return $this->belongsTo('Project', 'projectID', 'id');
}
public function status()
{
return $this->belongsTo('Status', 'statusID', 'id');
}
}
Now, just call the fields you want, for example:
foreach (Ticket::all() as $ticket)
{
echo $ticket->project->title;
echo $ticket->status->title;
}
Obs.: In your return object/array you can't see the relationships fields unless you do manual joins, etc. So, just configure your relationships and call the fields you want.
Sorry for my english
Define relationships specifying custom foreign keys (defaults would be status_id and project_id for your models):
// Ticket model
public function project()
{
return $this->belongsTo('Project', 'projectID');
}
public function status()
{
return $this->belongsTo('Status', 'statusID');
}
Then eager load related models:
$tickets = Ticket::with('project','status')->get();
// accessing:
foreach ($tickets as $ticket)
{
$ticket->status; // Status model with all its properties
$ticket->project; // Project model
}

Categories