The groupBy method isn't working correctly. I am not sure what is the problem.
UPDATED
I made made a hasMany() <-> belongsTo() relations between tables.
Place model
protected $table = "places";
protected $guarded = [];
public $with = ["plans"];
public function plans()
{
return $this->hasMany("App\Models\Plan");
}
Plan Model
protected $guarded = [];
public function place()
{
return $this->belongsTo("App\Models\Place");
}
In controller when I return json data I can see the relation.
$place = Place::with(["plans"])->get();
return $place;
The result is fine with this. check the image: https://imgur.com/a/0CHXPhQ
But, when I try to use groupBy() on place_name column. It doesn't group the their plans in one place...
Place::with(["plans"])->groupBy("place_name")->get();
and the result: https://imgur.com/a/qPxMU42
As you see second place's plan doesn't group with first place... it's gone unknown... Expected result plan object should grouped too because their place_name grouped...
Any idea what's causing this problem? How can I fix it?
Try this
$data = Place::groupBy(['name','address'])->paginate(20);
If you are facing a problem that sometimes two rows are grouping due to group by, then you can do one thing. You can use group by concat both fields.
$data = Place::groupBy(\DB::raw('CONCAT(name, ' ', address)'))->paginate();
This is the logic you can put and get desire result.
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.
There are three tables in my system:
Students
Articles
categories
A student can write many articles and an article belongs to just one student. And an article can have only one category.
Article Model
class Articles extends Model
{
protected $fillable = ['id','title', 'body', 'students_id', 'created_at', 'updated_at'];
protected $table = 'articles';
public function students(){
return $this->belongsTo('App\Students');
}
public function categories(){
return $this->belongsTo('App\Categories');
}
}
I have created the above code, because I needed to get an articles list with who written by that article with the category name.
For that I used $article_list = Articles::get(); in the controller, and it works perfectly.
Then again I needed to get article list (this time I don't need the student name and category names; the output of the article table is more than enough).
But if I use $article_list = Articles::get(); it outputs the article table joining with the category and students table also.
Is there a way to get just the article table using Eloquent?
Relations within Eloquent are eager loaded so you are safe and it's no harm that categories are also being loaded. Quoted from the docs:
When accessing Eloquent relationships as properties, the relationship
data is "lazy loaded". This means the relationship data is not
actually loaded until you first access the property.
https://laravel.com/docs/5.4/eloquent-relationships#eager-loading
try :
class Articles extends Model
{
protected $fillable = ['id','title', 'body', 'students_id', 'created_at', 'updated_at'];
protected $table = 'articles';
public function students(){
return $this->belongsTo('App\Students');
}
public function categories(){
return $this->hasOne('App\Categories');
}
}
class Student extends Model
{
public function articles(){
return $this->hasMany('App\Articles');
}
}
you can try Has Many Through relationship type
official link: read more
#jjj's answer is the right one, but to explain in a bit more detail:
$articles = Articles::get();
will load the only articles. You can check it like this in your controller:
public function articles() {
$articles = Articles::get();
return $articles;
}
But $articles is a collection of models, and each model is "aware" of it's relationships. So if you try to access one of those relationships, Laravel will silently load it for you. So if you pass the same $articles above to your view (currently without categories), and then in your view do something like:
#foreach ($articles as $article)
{{ $article->categories->name }}
#endforeach
it will work, because Laravel is doing the SQL to find each article's category and then name. As #jjj explains, this is called Lazy loading and is described in the docs.
Incidentally lazy loading like this is usually inefficient, and it would be better to eager load, like you show in one of your comments above. It is described well in the docs.
I have calculated property 'price' in model Room.
protected $appends = ['price'];
public function getPriceAttribute()
{
if ($this->action) return $this->action_price_per_meter*$this->area;
return $this->price_per_meter*$this->area;
}
When i filter Rooms by this virtual field ...
$rooms = Room::where('price','<',100000)->get();
of course i got error - Column 'price' not found
How to solve this problem in Laravel way?
Eloquent has a whereRaw method which allows you to construct that segment of the query manually.
In your case you need the equivalent of the model's method in SQL:
->whereRaw('price_per_meter * area < 100000')
It'll be more complicated depending on what your action column is doing but that should serve as a starting point.
I've add accessors and mutators in my laravel model.
public function getAmountAttribute($amount)
{
return ($amount) / 100;
}
public function setAmountAttribute($amount)
{
$this->attributes['amount'] = $amount * 100;
}
These are working fine for me. At one place i am facing issue :
The issue is I want to apply aggregate function in my controller for amount field. But accessors is not working.
Model::with(['xxx'])->where('parent_id', $id)->sum('amount')
Value of $id is fine i checked. It's giving me sum of amount field where parent_id is equals to $id. It should give results after diving by 100.
Thanks in advance.
Accessors and mutators allow you to format Eloquent attributes when
retrieving them from a model or setting their value.
... not while querying the table.
Model::with(['xxx'])
->addSelect(DB::raw('SUM(`table`.amount/100) as sum_of_amounts'))
->where('parent_id', $id)
->get();
An accessor/mutator is called only when you access the property from the result but it doesn't work within the query so you can't do it but as an alternative, you may use Laravel's Collection::sum() method for example:
$collection = Model::with(['xxx'])->where('parent_id', $id)->get();
$someOfAmount = $collection->sum('amount');
Make sure that, you've created a protected $appends property in your Model in use where it has the following:
protected $appends = ['amount'];
Note: I'm not sure whether this $appends is required but you may give this a try.