How can I use math function in laravel eloquent query? - laravel

I want to do sum math calculation in my eloquent query .I have a variable which must be multiple to some of my DB filed and if more than 3000 then the parent item must be in my result.
1)Jewel model:
class Jewel extends Model
{
use HasFactory;
public function jewelsItems(){
return $this->hasMany('App\Models\JewelsItem');
}
}
2)JewelsItem Model:
class JewelsItem extends Model
{
//has weight,fee field
use HasFactory;
protected $table = 'jewel_items';
public function jewel(){
return $this->belongsTo('App\Models\Jewel');
}
}
the query must get me all jewel that have at least a jewelsItem with (weight*($varibale+fee)) > 5000.
note1: $varibale is not a DB field.
note2: I don't want to use Raw Expressions cause of theirs vulnerabilities.

You might want to try this:
return Jewel::whereHas('jewelsItems', function ($query) use ($variable) {
$query->whereRaw("(weight*({$varibale}+fee)) > 5000");
})->get();

Related

How to sort parent data based on child column detial in laravel 5.4?

I have 3 way relationship firstl i have get code like this in a controller
Trial::with('subjects')->where('source_id', $sourceId)->get()->toArray()
Now I want to sort subject.reactions on desc order of subject.reactions.accounts.nb_followers column. I tried to use orderby on relationship but it does not work because it sorting account indsted on reactions. I want to sort reaction based on value of "nb_followes" column present inside account table.
Trail Model
class Trial extends Model
{
use HasFactory;
public $table = 'trials';
public function subjects()
{
return $this->hasMany(Subject::class, 'trial_id')->with(['reactions', 'news'])->withCount('reactions');
}
}
Subject Model
class Subject extends Model
{
use HasFactory;
public $table = 'subjects';
public function reactions()
{
return $this->hasMany(Reaction::class, 'subject_id', 'id')->with(['accounts' => function ($q) {$q->orderBy('nb_followers', 'DESC');}])->where('twitter_error', 0)->where('active', 1)->orderby('key_reaction', 'DESC');
}
public function news()
{
return $this->hasone(News::class, 'id', 'news_item_id');
}
Reaction Model
class Reaction extends Model
{
use HasFactory;
public $table = 'reactions';
public function accounts()
{
return $this->belongsTo(Account::class, 'account_id', 'id')->where('name', '!=', '');
}
Thank you in Advance.
I want to sort reactions based on account table's column yes i cant use simple eloquent query because it will not create a structure that i want so that's why i created these relationships.
first you need to loop over trails using map method
second you need to use transform method to transform your subject in
sorted manner
as show in Subject model you need sort reaction by key_reaction
feild
$trails = ViewTrial::with('subjects')->where('source_id', $sourceId)->get();
$trails->map(function($trails){
return $trails->subjects = $trails->subjects->transform(function (&$subject) {
return [
"column" => $subject->column,
"reactions" => $subject->reactions->sortByDesc(function ($reactions) {
return $reactions['accounts']['nb_followers'];
})->sortByDesc('key_reaction')->values()->all()
]
})->values();
});
return $trails->toArray();

Laravel Eloquent Get Method Does Not Work

Well, I'm trying to get some records from db with models. My model is like this:
class People extends Model
{
protected $connection = 'test-connection';
protected $table = 't_people';
}
And I'm trying to get them like this:
public function searchPopularPlayers() {
return People::where('popularity', '<=', 10)->get();
}
When I use first() instead of get(), I get the first record. But I need them all. I get null when I use get(). Any help??

How can you use Eloquent to find related values two tables away and in a different database?

I am developing a system that extends an existing ERP system, and so is accessing two databases (both on the same MS SQL Server). I am trying to access items on the "Equipment" model (this is a table in the ERP database) through the "EquipmentInstance" model from the "EquipmentType" model (these two are in the new database). They are related as per this diagram:
The three models are as follows:
EquipmentType
namespace App;
use Illuminate\Database\Eloquent\Model;
class EquipmentType extends Model
{
protected $table = 'dbo.EquipmentType';
protected $connection = 'sqlsrv';
protected $primaryKey = 'EquipmentTypeID';
protected $fillable = [
'TypeName',
'ProductManager'
];
public function EquipmentInstance()
{
return $this->hasMany(EquipmentInstance::class,'EquipmentTypeID', 'EquipmentTypeID');
}
public function Equipment()
{
return $this->hasManyThrough(
Equipment::class,
EquipmentInstance::class,
'TypeID',
'PartNum',
'TypeID',
'PartNum'
);
}
}
EquipmentInstance
namespace App;
use Illuminate\Database\Eloquent\Model;
class EquipmentInstance extends Model
{
protected $table = 'dbo.EquipmentInstance';
protected $primaryKey = 'EquipmentID';
protected $keyType = 'string';
protected $connection = 'sqlsrv';
protected $fillable = [
'EquipmentID',
'EquipmentTypeID',
'PartNum'
];
public function Part()
{
return $this->belongsTo(Part::class,'PartNum','PartNum');
}
public function Equipment()
{
return $this->hasMany(Equipment::class,'PartNum', 'PartNum');
}
public function EquipmentType()
{
return $this->belongsTo(EquipmentType::class); /*,'EquipmentTypeID', 'EquipmentTypeID'*/
}
/* public function Attribute()
{
return $this->belongsTo(Equipment::class,'SerialNumber', 'JobNum');
}
public function TechNote()
{
return $this->belongsTo(Equipment::class,'SerialNumber', 'JobNum');
}*/
}
Equipment
namespace App;
use Illuminate\Database\Eloquent\Model;
class Equipment extends Model
{
protected $table = 'ERP.SerialNo';
public $timestamps = false;
protected $primaryKey = 'SerialNumber';
protected $keyType = 'string';
protected $connection = 'epicor';
public function Part()
{
return $this->belongsTo(Part::class,'PartNum','PartNum');
}
public function Customer()
{
return $this->belongsTo(Customer::class,'CustNum', 'CustNum');
}
public function Equipment()
{
return $this->belongsTo(Equipment::class,'SerialNumber', 'JobNum');
}
public function EquipmentInstance()
{
return $this->belongsTo(EquipmentInstance::class,'PartNum', 'PartNum');
}
}
On the EquipmentType Controller I am trying to get all of the Equipment through the EquipmentInstance so for each EquipmentInstance I can display all of the Equipments.
EquipmentType Controller
public function show(EquipmentType $EquipmentType)
{
$EquipmentInstance = $EquipmentType->EquipmentInstance()
->get();
$Equipments = $EquipmentType->EquipmentInstance()->Equipment()
->get();
return view('EquipmentType.show', compact('EquipmentType', 'EquipmentInstance', 'Equipments'));
}
The error message I get is
"BadMethodCallException
Call to undefined method Illuminate\Database\Eloquent\Relations\HasMany::Equipment()"
I believe the issue is that (my understanding is rocky) that Eloquent is trying to write one query to access both databases, which is failing. However I am not sure how to proceed.
Any help would be greatly received.
Richard
Update
I have implemented what gbalduzzi suggested in his answer, which almost worked, and I am sure the issue is with my blade implemtention. I have nested two forloops:
#foreach($EquipmentType->EquipmentInstance as $EquipmentInstance)
#foreach($Equipments as $Equipment)
<tr>
<td>{{$EquipmentInstance->PartNum}} - {{$EquipmentInstance->Part->PartDescription}}</td>
<td>{{$Equipment->SerialNumber}}</td>
<td>{{$Equipment->SNStatus}}</td>
<td>{{--{{$Equipment->Customer->LegalName}}--}}</td>
</tr>
#endforeach
#endforeach
Which is displaying the serial numbers (from the Equipment model) for the first EquipmentInstance only and repeating them for all EquipmentInstanced.
Update 2
I have proven that the issue is with first() in the suggested answer, as if I change this to last() the results change as you would expect (see update 1). So my question now is:
Is there an equivelant of first(), last() which is all() or every()?
The problem is not in your database configuration, but in the way you are calling the relationship. Instead of:
$Equipments = $EquipmentType->EquipmentInstance()->Equipment()
->get();
use:
$Equipments = $EquipmentType->EquipmentInstance->first()->Equipment()
->get();
Long answer
In Eloquent, you can use a relationship in 2 ways:
as a magic field (i.e. $EquipmentType->EquipmentInstance). In this case you get as a result an instance of the model EquipmentInstance (also, if you already queried it, it directly returns the value without executing a new query)
as an eloquent query (i.e. $EquipmentType->EquipmentInstance()). Using it as a function, you don't get the model but a RelationShip instance, that is basically an eloquent query and can be chained with other eloquent methods, such as where, orderBy, ecc
So, if you call $EquipmentType->EquipmentInstance()->Equipment() it throws an error because the eloquent query does NOT have the relationship Equipment().
On the other hand, $EquipmentType->EquipmentInstance->Equipment works because it calls Equipment on the actual model instance, that has the Equipment relationship properly defined.

Eloquent Relationships Optimize Query

I have the following models: CardBoard, User, UserPricingPlans, PricingPlanLimits
Note: Don't mind if there is something wrong with the models code.They are working fine.
CardBoard
class CardBoard extends Model{
public function user(){
return $this->belongsTo('Models\User','id_user');
}
}
User
class User extends Model{
public function pricingPlans(){
return $this->hasMany('Models\UserPricingPlan','id_user');
}
}
PricingPlan
class PricingPlan extends Model{
public function limits(){
return $this->hasOne('Models\PricingPlanLimits','id_pricing_plan','id_pricing_plan');
}
}
PricingPlanLimits
I'll not describe that Model, its not necessary for the problem. But keep in mind that there is an attribute called maxBoards.
The problem is that I only have the CardBoard Model Instance to work on and I want to get the maxBoard attribute from PricingPlanLImits. So I did it like this:
Note: I Already have the CardBoard Model Instance here!
$maxBoard = $cardBoard->user->pricingPlans->last()->limits->maxBoard;
return $maxBoard;
The code above runs great, but the number of queries generated by this operation is an overhead to me. Eloquent do an SELECT for every Relationship called and I don't want all these data and operations.
{
"query": "select * from `users` where `users`.`id_user` = ? limit 1",
"bindings": [
],
"time": 0.96
}
{
"query": "select * from `users_princing_plan` where `users_princing_plan`.`id_user` = ? and `users_princing_plan`.`id_user` is not null",
"bindings": [
],
"time": 0.8
}
{
"query": "select * from `pricing_plan_limits` where `pricing_plan_limits`.`id_pricing_plan` = ? and `pricing_plan_limits`.`id_pricing_plan` is not null limit 1",
"bindings": [
],
"time": 0.88
}
Isn't there an way to optmize this and run fewer queries in a Eloquent-Way ?
you can get a data in one query if you use with() method.
for example: CardBoard::with('user.pricingPlans')->get();
so can optimize your query using with method.
Previous comments were not too relevant to this solution...
example
$cardboard->user()->whereHas('pricingPlans', function ($plans) {
$plans->selectRaw('price_plan_limits.id, MAX(price_plan_limits.maxBoard) as MB'))
->from('price_plan_limits')
->where('price_plan_limits.id', 'price_plan.id')
->orderBy('MB', 'DESC')
})->get();
I usually go in reverse order:
$maxBoard = PricingPlanLimits::whereHas(function($q)use($cardBoard){
$q->whereHas('PricingPlan', function($q1)use($cardBoard){
$q1->whereHas('User', function($q2)use($cardBoard){
$q2->whereHas('CardBoard', function($q3)use($cardBoard){
$q3->where('id', $cardBoard['id']);
});
});
// Probably you have to improve this logic
// It is meant to select select the last occurrence
$q1->orderBy('created_at', 'desc');
$q1->limit(1);
});
})->first()['maxBoard'];
Totally untested, but this should be the correct approach to achieve your goal in one query.
You probably could reduce the number of calls by using hasManyThrough relation (See:https://laravel.com/docs/5.4/eloquent-relationships#has-many-through).
In that case you'd have something like
class CardBoard extends Model{
public function userPricingPlans(){
return $this->hasManyThrough('Models\UserPricingPlan', 'Models\User', 'id_user', 'id_user');
}
}
And then you could call it like this:
$maxBoard = $cardBoard->userPricingPlans->last()->limits->maxBoard;
To have it all in a single query you'd need fluent and real SQL joins, can't be done with eloquent (but then you'll miss all of ORM fun)
Typically it is totally fine to achieve the result with 3 queries, a query like that normally take 10ms. But each of your queries is taking nearly 1 second, which is way too long. I don't know the reason why.
You are also able to achieve the same result with a single query though.
Your naming is a bit unconventional. I use a more popular naming convention, hopefully you can apply to your case.
class CardBoard extends Model
{
protected $table = 'card_boards';
public function user()
{
return $this->belongsTo(User::class,'user_id');
}
}
class User extends Model
{
protected $table = 'users';
public function pricingPlans()
{
return $this->hasMany(UserPricingPlan::class,'user_id');
}
}
class PricingPlan extends Model
{
protected $table = 'pricing_plans';
// This is a one to one relationship so I use a singular form.
public function limit()
{
return $this->hasOne(PricingPlanLimit::class,'pricing_plan_id');
}
}
class PricingPlanLimit extends Model
{
protected $table = "pricing_plan_limits";
}
The query to get the result:
$carboardId = 100;
$latestPricingPlanSubQuery = PricingPlan::select('pricing_plans.*', DB::raw('MAX(created_at) as last_post_created_at'))
->groupBy('user_id');
$carboard = Carboard::select('card_boards.*', 'pricing_plan_limits.max_board')
->join('users', 'cardboards.user_id', '=', 'users.id')
->joinSub($latestPricingPlans, 'latest_pricing_plans', function ($join){
$join->on('users.id', '=', 'latest_pricing_plans.user_id');
})
->join('pricing_plan_limits', 'latest_pricing_plans.id', '=', 'pricing_plan_limits.pricing_plan_id')
->find($cardboardId);
The key thing is to have a sub query getting only the latest pricing plans for each user.

Laravel 4 - How to use where conditions for relation's column

This is what I want, I have two tables. one is 'Restaurants' and other is 'Facilities'.
The tables are simple.. and One-To-One relations. like there is a restaurant table with id, name, slug, etc and another table called facilities with id, restaurant_id, wifi, parking, etc
Here are my models:
class Restaurant extends Eloquent {
protected $table = 'restaurants';
public function facilities() {
return $this->hasOne('Facilities');
}
}
class Facilities extends Eloquent {
protected $table = 'facilities';
public function restaurant() {
return $this->belongsTo('Restaurant');
}
}
I want do like this Select * from restaurants r left join facilities rf on r.id=rf.restaurant_id where r.name = 'bbq' and rf.wifi != '1'.
How to use Eloquent to do that?
ps. sorry for modify from https://stackoverflow.com/questions/14621943/laravel-how-to-use-where-conditions-for-relations-column#= , but I have the similar problem.
You can use where and other sql-based methods on the relationship objects.
That means you can either create a custom method in your model:
class Restaurant extends Eloquent {
protected $table = 'restaurants';
public function facilities($wifi) {
return $this->belongsTo('Facility')->where('wifi', '=', $wifi);
}
}
Or you can try to use query scopes:
class Restaurant extends Eloquent {
protected $table = 'restaurants';
public function facility() {
return $this->belongsTo('Restaurant');
}
public function scopeFiltered($query, $wifi)
{
return $query->where('wifi', '>', 100);
}
}
Then:
$wifi = 1;
$restaurants = Restaurant::facilities()->filtered($wifi)->get();
This isn't exactly what you need likely, but query scopes is likely what you want to use to get what you're attempting.
THe key point is to know that relationship classes can be used like query builders - for example:
$this->belongsTo('Facility')->where('wifi', '=', $wifi)->orderBy('whatever', 'asc')->get();
There are some ways to filter both, this is using QueryBuilder:
Restaurant::join('facilities','facilities.restaurant_id','=','restaurants.id')
->where('name','bbq')
->where('facilities.wifi','!=', 1)
->get();

Resources