Laravel 4 - Access to Table through Relational Models - php

Edit: typo
I have two Models: Project and Task. Both are relational to each other:
Project.php
class Project extends Eloquent {
public function tasks()
{
return $this->hasMany('Task');
}
Task.php
class Task extends Eloquent {
protected $guarded = [];
public function project()
{
return $this->belongsTo('Project');
}
Through my ProjectsController I pass the necessary Variables to my Projects View, like this:
ProjectsController:
public function index()
{
$projects = Project::with('tasks')->get();
return View::make('projects.index')
->with('projects', $projects);
}
And in my View I loop through every Project to show everything on a table:
Projects List
<table>
<tr>
<th>Id</th>
<th>Titel</th>
<th>Description</th>
<th>Tasks</th>
</tr>
#foreach($projects as $project)
<tr>
<td>{{$project->id}}</td>
<td>{{$project->title}}</td>
<td>{{$project->description}}</td>
<td>{{$project->task}}</td>
</tr>
#endforeach
</table>
As you can see, the last td-Tag, should access data from the tasks table.
I know that the above View does not work. But I want to know in general, how I would go about it, if I want to output the number of tasks, each project has.
Or anything else, that explains, how I can access different tables, through relational Models, in this particular situation.

since you defined the project model with $this->hasMany('Task'); and fetched models with their tasks, you can simply do
{{ $project->task()->count() }}
be sure to call the tasks as if it were a function, instead of a property.

Related

I can't acces the MODEL field on my database relationship

I can't access my MODEL field in my database
This is the code that I execute:
#foreach($clients as client)
{{$client->dependents->fname}}
{{$client->dependents->mname}}
{{$client->dependents->lname}}
#endforeach
This code returns an error : Property [fname] does not exist on this collection instance.
When I do this code:
#foreach($clients as client)
{{$client->dependents}}
#endforeach
This code returns a successful array of data.
This is my Client Model:
class Client extends Model
{
protected $guarded = [];
public function dependents() {
return $this->hasMany(Dependent::class);
}
}
And this is my Dependent Model:
class Code extends Model
{
protected $guarded = [];
public function client()
{
return $this->hasOne(Client::class);
}
}
How can I retrieve each field using the eloquent model method?
In order to access the fields on dependents model, you need to make a foreach, because the client model hasMany dependents and when you try to access simply $client->dependents->fname doesn't know which one to access.
So, you need to do the following:
#foreach($clients as $client)
#foreach($client->dependents as $dependent)
{{$dependent->fname}}
{{$dependent->mname}}
{{$dependent->lname}}
#endforeach
#endforeach
It is HasMany relation AND It returns multiple departments so that fname is undefined. Because it has an object of departments, not the department and you are missing $ from #foreach($clients as client).
#foreach($clients as $client)
#foreach($clients->dependents as $department)
{{$dependent->fname}}
{{$dependent->mname}}
{{$dependent->lname}}
#endforeach
#endforeach
Try this, It will work for you. Enjoy!

Eloquent Relationship in Loop

I'm building a Laravel frontend to an existing database (an ERP system named Epicor) with a view to extending that functionality in a separate (new) database. At the moment I am trying to display pieces of "equipment" that have a status of being shipped to a customer, and include information from the Part table. The DB relationships are all there and I can get all the information I need using SSMS - so I believe I must be going wrong in my use of Eloquent. I have the following models:
Equipment - this is a serial number in the system, so in effect an instance of a part:
<?php
class Equipment extends Model
{
protected $table = 'ERP.SerialNo';
public $timestamps = false;
protected $primaryKey = 'SerialNumber';
protected $keyType = 'string';
protected $fillable = [
'SerialNumber',
'SNStatus',
'PartNum',
'TerritoryID',
'JobNum',
'PackNum',
'PackLine',
'RMANum',
'CustNum',
'SNStatus'
];
public function Part()
{
return $this->belongsTo(Part::class,'PartNum','PartNum');
}
public function Customer()
{
return $this->belongsTo(Customer::class,'CustNum', 'CustNum');
}
}
Part
class Part extends Model
{
protected $table = 'ERP.Part';
public $timestamps = false;
protected $primaryKey = 'PartNum';
protected $keyType = 'string';
protected $fillable = [
'PartNum',
'SearchWord',
'Innactive',
'PartDescription',
'ClassID',
'CommodityCode',
'NetWeight'
];
public function ShipmentLine()
{
return $this->hasMany(Shipment::class, 'PartNum', 'PartNum');
}
public function Equipment()
{
return $this->hasMany(Equipment::class,'PartNum', 'PartNum');
}
}
Customer Controller
public function show($CustID)
{
$Customer = Customer::find($CustID);
$Shipments = $Customer->Shipment->where('Voided', '0');
$Equipments = $Customer->Equipment->where('SNStatus', 'SHIPPED');
return view('Customer.show', compact('Equipments', 'Customer','Shipments', 'Parts'));
}
show.blade.php (under Customer)
<?php
#foreach($Equipments as $Equipment)
<tr>
<td>ClassID</td>
<td>{{$Equipment->PartNum}}</td>
<td>{{$Equipment->SerialNumber}}</td>
<td>PartDescription is sometimes really really really long.....even longer than this!</td>
</tr>
#endforeach
Which all works fine and I get a list of all of the Equipment that has a status of being shipped to that customer. What I'd like to do now is, in the list of equipment, including fields from the Part table that relate (ClassID and PartDescription).
I've tried a few things, but feel I'm clutching at straws and all of my attempts fail. I have managed to display on Equipment show.blade.php Part information, so I believe the models are set up OK.
Thanks in advance,
Richard
First of all, the relations methods inside the Part model (as well as inside the Customer model) must be written at plural, since you are matching multiple entities:
public function ShipmentLines()
{
return $this->hasMany(Shipment::class, 'PartNum', 'PartNum');
}
public function Equipments()
{
return $this->hasMany(Equipment::class,'PartNum', 'PartNum');
}
Second, you can use the relation to load the equipments in the controller, instead of using lazy loading:
public function show($CustID)
{
$Customer = Customer::find($CustID);
$Shipments = $Customer->ShipmentLines()
->where('Voided', '0')
->get();
$Equipments = $Customer->Equipments()
->with('Part') // load the Part too in a single query
->where('SNStatus', 'SHIPPED')
->get();
return view('Customer.show', compact('Equipments', 'Customer', 'Shipments'));
}
Finally, in the blade template, you can use the Part of the equipment very easy:
#foreach ($Equipments as $Equipment)
<tr>
<td>{{$Equipment->Part->ClassID}}</td>
<td>{{$Equipment->PartNum}}</td>
<td>{{$Equipment->SerialNumber}}</td>
<td>PartDescription is sometimes really really really long.....even longer than this!</td>
</tr>
#endforeach
Also, I would recommend using #forelse instead of #foreach to cover those situations when no equipments exists:
#forelse ($Equipments as $Equipment)
<tr>
<td>{{$Equipment->Part->ClassID}}</td>
<td>{{$Equipment->PartNum}}</td>
<td>{{$Equipment->SerialNumber}}</td>
<td>PartDescription is sometimes really really really long.....even longer than this!</td>
</tr>
#empty
<tr>
<td colspan="4">There is no existing equipment!</td>
</tr>
#endforelse
I think what you're looking for is with().
Before I get to that though, you actually have a bigger problem there than it seems. Matei Mihai actually touched on this.
When you have something like $Customer->Equipment, you're actually making use of Eloquent's "dynamic properties". What this means is, there's a magic __get() in there somewhere that says if the desired property doesn't exist on the target model, check to see if it has a relation method by that name. And if so, lazy-load it if it hasn't already been eager-loaded via with() or load().
So when you do $Customer->Equipment, it's basically a shortcut for $Customer->Equipment()->get().
Next thing to consider is that the result of get() is an Eloquent\Collection, which is a child-class to Support\Collections. And Support\Collections have their own version of the where() method.
All that to say, $Customer->Equipment->where('SNStatus', 'SHIPPED') does not result in running a query that looks like:
SELECT * FROM Equipment WHERE customerID = ? AND SNStatus = 'SHIPPED'
What you're doing is running this instead:
SELECT * FROM Equipment WHERE customerID = ?
And then asking the Collection class to filter the resulting set by SNStatus='SHIPPED' afterwards. This can be a huge performance hit and even max out your servers RAM depending on how big those tables are. I think what you're really looking for there is this:
$Customer->Equipment()->where('SNStatus', 'SHIPPED')->get()
By calling on the actual Equipment() method rather than the dynamic property, you're telling Eloquent that you're not quite ready for it to execute the query yet, because you're still appending conditions to it.
(Also just as a side-note, your naming-convention hurts my OCD a little bit, methods should always be "camelCased". Only class names have their first letter capitalized.)
So... back to the question you actually asked, and including an understanding of the difference between Model::where() and Collection::where(), what we have is something like this:
$resutls = $Customer->Equipment()->with(['Part'])->where('SNStatus', 'SHIPPED')->get();
Since you wanted to specify a couple fields within the Parts table that you actually care about, you can use a constrained eager-load
$resutls = $Customer->Equipment()->with(['Part' => function (Illuminate\Database\Eloquent\Builder $query) {
$query->select([
'PartNum', //Per Equipment::Part(), This needs to be there for the relation to be mated with its parent
'ClassID',
'PartDescription'
]);
// Since PHP always handles objects by-reference, you don't actually need to return $query after having altered it here.
}])->where('SNStatus', 'SHIPPED')->get();
This will give you a nested Part object with just the fields you care about on each Equipment model element within the Eloquent\Collection results.
As for how to handle these results within your blade file, I'll differ to Matei Mihai on that, I think that answer is pretty good.

Issue querying laravel eloquent relationship

This is with reference to this question :
Laravel Eloquent One to Many relationship
I tried the suggested way, but couldn't resolve. Please help. Below is the changes i have done :
Earlier :
//Route for Restaurants Page
Route::get('/home/restaurants',function(){
$restaurants = DB::table('restaurants')->simplepaginate(3);
return view('restaurants',['restaurants_data'=>$restaurants]);
});
Changed as per suggestion :
Route::get('/home/restaurants',function(){
// $restaurants = DB::table('restaurants')->simplepaginate(3);
$restaurants = \App\Restaurant::simplePaginate(3);
return view('restaurants',['restaurants_data'=>$restaurants]);
});
In Restaurant model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Restaurant extends Model
{
public function offer(){
return $this->hasMany('Offer');
}
}
In view, now I am trying to access it by dumping the values.
<?php
var_dump($restaurants_data->offer);
?>
Error :
After doing dd()
Firstly, I would suggest changing your Offer Relationship to:
public function offers()
{
return $this->hasMany(Offer::class, 'restaurant_ID', 'id');
}
The above assumes that the Offer class and Restaurant class are in the same namespace. If they're not please add the correct namespace or import the Offer model in to the class.
Secondly, because you're paginating the results you will end up with a collection of Restaurant models (even if there is only one), so you will need to loop through them to get access to the offers for each model. I would also suggest eager loading the results e.g.
Route:
Route::get('/home/restaurants', function () {
$restaurants = \App\Restaurant::with('offers')->simplePaginate(3);
return view('restaurants', compact('restaurants'));
});
in your view:
#foreach($restaurants as $restaurant)
#foreach($restaurant->offers as $offer)
{!! dump($offer) !!}
#endforeach
#endforeach
{{ $restaurants->links() }}
Can you replace
$restaurants = \App\Restaurant::paginate(3); and amend the blade code to say
<?php
foreach($restraunts_data as $resturant) {
if(count($restaurant->offer) {
print_r($restaurant->offer);
}
}
?>
You are using the models incorrectly. You run no queries and you attempt to run a static method on the Restaurant class without selecting any restaurants. As far as I know is this not supported by Eloquent. If you look at the error message it complains that there are no property $offer.
Try to run some query, and the select the related Offer. This should work as expected.
For example:
$offers = \App\Restaurant::find(1)->offer;
This will return the many Offer relations for the Restaurant with ID 1.

Laravel 5.4 relationship beLongsToMany

Hi i need fix returning view. I made relation and its returning array. How i can change this and i am not sure i made good function to relationship. I just trying lerning it i saw many tutorials and i know in Laravel is so kind magic tips to returning.
My function must showing events what user was joining. I think i make it but its returning array and when i try do something like that
#foreach($zapisevents as $zapisevent)
<table class="table">
<th>{{$zapisevent->eventsave->name}}</th>
</table>
#endforeach
i got error:
Property [name] does not exist on this collection instance. (View: /home/mariusz/Pulpit/www/szpital/resources/views/profil/profil.blade.php)
but when i use <th>{{$zapisevent->eventsave}}</th> its returning array.
There is function for joining to event
public function index()
{
$userid = Auth::user();
$zapisevents = User::with('eventsave')->where('id',(Auth::user()->id))->get();
return view('profil.profil', ['userid' => $userid], ['zapisevents' => $zapisevents]);
}
Model User:
public function eventsave()
{
return $this->belongsToMany(HomeModel::class,'save_events','users_id','events_id')->withTimestamps();
}
Model HomeModel <<<
public function usersave()
{
return $this->belongsToMany(User::class,'save_events','events_id','users_id');
}
Its returning:
[{"id":5,"name":"asdasdsa","title":"Wydzial 1","start":"2017-04-04
03:00:00","end":"2017-04-04 07:59:00","created_at":"2017-04-01
18:50:40","updated_at":"2017-04-01
18:50:40","pivot":{"users_id":3,"events_id":5,"created_at":"2017-04-01
18:50:58","updated_at":"2017-04-01
18:50:58"}},{"id":7,"name":"kkkkkkkkkkkkkkkkkkkkkkkk","title":"Wydzial
4","start":"2017-04-01 00:00:00","end":"2017-04-01
23:59:59","created_at":"2017-04-01 19:54:24","updated_at":"2017-04-01
19:54:24","pivot":{"users_id":3,"events_id":7,"created_at":"2017-04-01
19:55:41","updated_at":"2017-04-01 19:55:41"}}]
the
#foreach($zapisevents as $zapisevent)
<table class="table">
<th>{{$zapisevent->eventsave->name}}</th>
</table>
#endforeach
Should ne
#foreach($zapisevents as $zapisevent)
<table class="table">
#foreach($zapisevent->eventsave as $eventSave)
<th>{{$eventsave->name}}</th>
#endForeach
</table>
#endforeach
in you code the name property is being called in a collection of HomeModel but it needs to be called in a model itself
When using arrays, you need to access their properties via their index like this:
$zapisevent->eventsave['name']
as opposed to:
$zapisevent->eventsave->name

How to display a hasMany() relationship through a table in a view.

I'm trying to display a table in my view with contents that consists of information from my Location table (Addr, City, etc...) and Resource table (Name, Descr.), they're linked by ID's through a ResourceLocation Table. Here's what I have so far:
Resource Controller
....
public function resource()
{
$resources = Resource::all();
return view('pages.resource', compact('resources'));
}
public function location()
{
$locations = Location::all
return view (compact('locations'));
}
public function resourcelocation()
{
$resourcelocations = ResourceLocation::all();
return view (compact ('resourcelocations'));
}
...
ResourceLocation Model
/**
* Class ResourceLocation
*/
class ResourceLocation extends Model
{
...
public function resource ()
{
return $this->belongsTo('App\Models\Resource');
}
public function location ()
{
return $this->belongsTo('App\Models\Location');
}
}
Resource Model
/**
* Class Resource
*/
class Resource extends Model
{
...
/** A resource can have many locations */
public function location()
{
return $this->hasMany('App\Models\ResourceLocation');
}
}
Location Model
/**
* Class Location
*/
class Location extends Model
{
...
public function resource()
{
return $this->hasMany('App\ResourceLocation');
}
}
resource.blade.php
<table>
<tr>
<th>Resource Name</th>
<th>Description</th>
<th>Address</th>
<th>City</th>
<th>Zip Code</th>
<th>County</th>
</tr>
#foreach ($resourcelocations as $resourcelocation)
<tr>
<td> {{ $resourcelocation->id }}</td>
<td>
#foreach ($resourcelocation->resources as $resource)
{{ $resource->Name }},
#endforeach
</td>
<td>
#foreach($resourcelocations->locations as $location)
{{ $location->City }}
#endforeach
</td>
</tr>
#endforeach
</table>
I just wanted to add a column or two to see if it was working, but I keep getting an undefined variable on resourcelocations, still trying to wrap my head around laravel and how the relationships work, so maybe my logic is messed up. Any help would be great!
Thanks.
There seems to be some confusion in your relationships. I think it could work with the way you have it but you are making it much more confusing than it needs to be.
First, start with removing the ResourceLocation model. You can relate resources and locations directly without need of the intermediary model (you will still need that table though). This is what's called a belongs-to-many relationship.
I also recommend when you have a relationship which can return many records (for example if a resource can have many locations) you should name the method which relates these locations rather than location.
With all that in mind, your Resource model gets...
public function locations()
{
return $this->belongsToMany('App\Models\Location', 'ResourceLocation');
}
Where I put 'ResourceLocation', that should be whatever you named your pivot table.
Same for your Location model which receives.
public function resources()
{
return $this->belongsToMany('App\Models\Resource', 'ResourceLocation');
}
Now that should be greatly simplified, we just replaced 4 relationship methods and 3 models for 2 relationship methods and 2 models.
Now for your controller, it's much more efficient to use eager loading to grab all your resource locations.
$resources = Resource::with('locations')->get();
And in your view...
#foreach ($resources as $resource)
#foreach ($resource->locations as $location)
<tr>
<td>{{ $resource->id }}</td>
<td>{{ $location->name }}</td>
</tr>
#endforeach
#endforeach
In your Resource Controller you are not passing any resourcelocation variable as I can see only resources has been called. try declaring the variable in array in this line return view('pages.resource', compact('resources'));
To get the first element in hasMany you can try below:
$resource->location()->first();
you can use dd(variable_name) before sending the data to the view to get a better idea of how your data is managed

Categories