Eloquent get user with highest relation table points - php

I'm trying to get the user with the highest evaluation points based on charisma and persuasion
What i have wrote so far after this i couldn't figure out how to make it work
$user = User::where('commision_id', $data['commision'])->whereHas('role', function ($q) {
$q->where('level', 2);
})->with(['evaluations' => function ($q) {
}]);
The evaluations relation migration
Schema::create('evaluations', function (Blueprint $table) {
$table->id();
$table->boolean('charisma')->default(false);
$table->boolean('persuasion')->default(false);
$table->boolean('politics')->default(false);
$table->boolean('heat')->default(false);
$table->string('title');
$table->integer('hl')->nullable();
$table->integer('exp')->nullable();
$table->unsignedBigInteger('user_id');
$table->timestamps();
$table->index('user_id');
});
So basically i have to sum the exp points based on persuasion and the exp points based on charisma then sum both of those sum values to get the highest ranking user.

You can add up those 2 properties on the fly and simply get the one with the highest attribute.
$user = User::where('commision_id', $data['commision'])->whereHas('role', function ($q) {
$q->where('level', 2);
})->with(['evaluations'])
->get()
->each(function ($u) {
$u->score = $u->evaluations->charisma + $u->evaluations->persuasion
});
$topUser = $user->orderBy('score')->first();
In case one user can have many evaluations, you would do the similar thing:
->each(function ($u) {
$u->score = null;
$charisma = $u->evaluations->pluck('charisma')->sum();
$persuasion = = $u->evaluations->pluck('persuasion')->sum();
$u->score += $charisma;
$u->score += $persuasion ;
});

What made it work is using filter function to filter the boolean values of Charisma and Persuasion, thanks to N L which pointed me to the right direction, i accepted his answer.
$user = User::where('commision_id', $data['commision'])->whereHas('role', function ($q) {
$q->where('level', 2);
})->with(['evaluations'])
->get()
->each(function ($u) {
$u->score = null;
$charisma = $u->evaluations->filter(function($value) {
return $value->charisma == true;
})->sum('exp');
$persuasion = $u->evaluations->filter(function($value) {
return $value->persuasion == true;
})->sum('exp');
$u->score += $charisma;
$u->score += $persuasion;
})->sortByDesc('score')->first();

Related

Helping querying relations in eloquent

I have this query in my codebase,
$listings = Tag::has('listings')->with(['listings' => function ($query) use ($request) {
$query->where('moderated', 1)
->where('active', 1);
if($request->query('free') == true) {
$query->where('cost', '0.00');
}
if($request->query('type') != "") {
$query->with(['types' => function($q) use ($request) {
$q->whereIn('id', explode(",", $request->query('type')));
}]);
}
$query->with('primaryImage');
}])
->paginate(3);
What I am trying to do is add parts of the query based on what is in the GET request (this bit works), what isnt working is the query on a relation.
Here I am querying Tags that can have many listings, each listing can have many types and I want to only return tags that have listing that match the filter parameters, i.e only show listings that cost "0.00" and then only tags that have listings that match the types in the get request.
So if a user sends type=1,2,3 in the GET request I want to return tags that have listings where the types relationship contains 1 of those IDs, is this possible?
The types relationship on a listing looks like this,
public function types() {
return $this->belongsToMany('App\Type');
}
and the relation from type to listing looks like this,
public function listings() {
return $this->belongsToMany('App\Listing');
}
Instead of using has and with maybe you can use whereHas
$listings = Tag::whereHas('listings', function($query){
$query->where('moderated', 1)
->where('active', 1);
if($request->query('free') == true) {
$query->where('cost', '0.00');
}
if($request->query('type') != "") {
$query->whereHas('types', function($q) use($request){
$q->whereIn('id', explode(",", $request->query('type')));
});
}
$query->with('primaryImage');
});
As you said, "each listing can have many types"
Change this in your Listing::class
public function types() {
return $this->hasMany('App\Type');
}

Get whereBetween on the lowest relationship table column

I've got some product with deals. Every product has several deals. I managed to get the whereBetween, but only on all associated deals. I would like to only return products where the lowest deal matches the whereBetween.
So get products where the lowest associated deal price is between the min & max given price.
Here is my code so far:
$products = Product::when($request->filled('brand'), function ($query)use($request) {
$brandRequest = $request->input('brand');
$brandReqArray = explode(',', $brandRequest);
$query->whereHas('brands', function($q) use ($brandReqArray) {
$q->whereIn('slug', $brandReqArray);
});
})->when($request->filled('gender'), function ($query)use($request) {
$genderRequest = $request->input('gender');
$genderReqArray = explode(',', $genderRequest);
$query->whereHas('genders', function($q) use ($genderReqArray) {
$q->whereIn('slug', $genderReqArray);
});
})->when($request->filled('price'), function ($query)use($request) {
$priceRequest = $request->input('price');
$priceReqArray = explode(';', $priceRequest);
$query->whereHas('deals', function($q) use ($priceReqArray) {
$q->whereBetween('price', $priceReqArray);
});
})->with(['deals' => function ($query) {
$query->orderBy('price','asc');
}])->paginate(24);
$input = $request->only(['brands', 'gender', 'price']);
$products->appends($input);
Try this and let me know, is that what you wanted?
$products = Product::when($request->filled('brand'), function ($query)use($request) {
$brandRequest = $request->input('brand');
$brandReqArray = explode(',', $brandRequest);
$query->whereHas('brands', function($q) use ($brandReqArray) {
$q->whereIn('slug', $brandReqArray);
});
})->when($request->filled('gender'), function ($query)use($request) {
$genderRequest = $request->input('gender');
$genderReqArray = explode(',', $genderRequest);
$query->whereHas('genders', function($q) use ($genderReqArray) {
$q->whereIn('slug', $genderReqArray);
});
})->when($request->filled('price'), function ($query)use($request) {
$priceRequest = $request->input('price');
$priceReqArray = explode(';', $priceRequest);
$query->whereHas('deals', function($q) use ($priceReqArray) {
$q->whereBetween('price', $priceReqArray)
->whereNotExists(function ($q)use($priceReqArray) {
$q->select(DB::raw(1))
->from('deals')
//->whereNull('deals.deleted_at')
->whereNotBetween('price', $priceReqArray)
->whereRaw('product.id = deals.productId');
})
->orderBy('price','asc')->limit(1);
})
})->paginate(24);

laravel retrieving data from database takes too much time

I am working with laravel project which uses a mysql database.It has some tables which has over 5 million data.it takes too much time to get these data to frontend. following is the function i use to get data. i have some filters used such as date range (from,to) search by name($seach) pagination amount(records_number) etc. but when i try to get all records it takes too much time. is there any solutions/optimizations for this matter?
Thanks.
public function transactionListBetween($from, $to, $sort, $search, $records_number, $filter, $previous_sort) {
$query = $this->accountTransactions
->with('transactionType', 'giver', 'recipient')
->leftJoin('tbdb_users as recipient', 'recipient.id', '=', 'tbdb_account_transaction.recipient_id')
->leftJoin('tbdb_users as giver', 'giver.id', '=', 'tbdb_account_transaction.giver_id')
->leftJoin('tbdb_account_transaction_type', 'tbdb_account_transaction.account_transaction_type_id', '=', 'tbdb_account_transaction_type.id')
->where('recipient.name', 'like', "%$search%")
->select('tbdb_account_transaction.*');
if($filter) {
if($filter == -1) {
$transactionTypeModel = \App::make('App\Models\AccountTransactionTypeModel');
$depositTypeIds = $transactionTypeModel->whereIn('name', ['PayPal Deposit', 'Eway Deposit', 'Bank Deposit', 'BPay Deposit', 'Poli Deposit'])->lists('id');
$query = $query->whereIn('tbdb_account_transaction.account_transaction_type_id', $depositTypeIds);
} else {
$query = $query->where('tbdb_account_transaction.account_transaction_type_id', $filter);
}
}
if($from) {
$query = $query->where('tbdb_account_transaction.created_date', '>=', $from);
}
if($to) {
$query = $query->where('tbdb_account_transaction.created_date', '<=', $to->endOfDay());
}
if($records_number == 'no_paginate') {
return $query->orderBy($sort, $previous_sort)
->get();
} else {
$totalAmount = $query->sum('amount');
$collection = $query->orderBy($sort, $previous_sort)->paginate($records_number);
$collection->totalAmount = number_format($totalAmount / 100, 2);
return $collection;
}
}
You seem to be doing 3 left joins and getting the same data as you did with the with.
You can drop them:
public function transactionListBetween($from, $to, $sort, $search, $records_number, $filter, $previous_sort) {
$query = $this->accountTransactions->->with('transactionType', 'giver', 'recipient')
->whereHas('recipient', function ($query) use ($search) { //Query the related model
$query->where('name', 'LIKE', "%$search%"); //Note this will not use an index
});
if ($filter) {
if ($filter == -1) {
$query->whereHas('transactionType', function ($query) {
$query->whereIn('id', \DB::raw("(SELECT id FROM tbdb_account_transaction_type WHERE name IN ('PayPal Deposit', 'Eway Deposit', 'Bank Deposit', 'BPay Deposit', 'Poli Deposit')");
});
} else {
$query->whereHas('transactionType', function ($query) use ($filter) {
$query->where('id', $filter);
});
}
}
if($from) {
$query = $query->where('created_date', '>=', $from);
}
if($to) {
$query = $query->where('created_date', '<=', $to->endOfDay());
}
if($records_number == 'no_paginate') {
return $query->orderBy($sort, $previous_sort)
->get();
} else {
$totalAmount = $query->sum('amount');
$collection = $query->orderBy($sort, $previous_sort)->paginate($records_number);
$collection->totalAmount = number_format($totalAmount / 100, 2);
return $collection;
}
}
This will reduce the main result set greatly making it easier to work with.
In my case i have also having this problem when using mysql database. These much records in table with relationship takes too much time. You have to use raw query to get result. You have to use pagination to show the data. because fetching all the data will slow down.
But i highly recommended you to normalize your table structure. In my case i did normalize of my tables by split it with yearly.
Found out why this is happening. it is because of pagination. it takes lot of time to paginate. there are more than 50000 pages so it takes time. i used simplePaginate() instead of paginate(). then it loads faster than usual

How to show Nearby Places with Laravel API

I'm creating REST API with Laravel 5.6 (I have to say I'm new because I might have used the wrong terms. I'm sorry about that,I'm improving myself. I need to hear my faults :) )
I have one function for find nearby places in my controller
public function index(\Illuminate\Http\Request $request) {
if($request->has('party_category')){
$parties = Parties::where('party_category', $request->party_category)->get();//your new query here
}
else if($request->has('lat') && $request->has('long')){
$parties = Parties::whereRaw("ACOS(SIN(RADIANS('latitude'))*SIN(RADIANS($request->lat))+COS(RADIANS('latitude'))*COS(RADIANS($request->lat))*COS(RADIANS('longitude')-RADIANS($request->long)))*6380 < 10");
}else {
$parties = Parties::all();
}
return Fractal::includes('places')->collection($parties,new PartyTransformer);
}
and I'm using this url for send current location but when I giving them , laravel showing to me all parties not nearby.I want to show nearby places
http://127.0.0.1:8000/api/parties?lat=37.043237&long=27.392445
but when I sending my parameter to url it showing
{"data":[]}
I can't show any nearby places
also in my database I'm keeping lat and long like this :
public function up()
{
Schema::create('parties', function (Blueprint $table) {
$table->increments('id');
$table->integer('places_id')->unsigned();
$table->string('slug');
$table->string('party_title');
$table->string('party_category');
$table->string('rating')->nullable();
$table->date('party_date');
$table->string("latitude")->nullable();
$table->string("longitude")->nullable();
$table->integer('fav_count')->nullable();
$table->longText('image_path');
$table->longText('party_desp');
$table->timestamps();
});
}
How can I show the nearby ones ?
I fixed , I hope it will help somebody
class PartiesController extends Controller
{
public function index(\Illuminate\Http\Request $request) {
if($request->has('lat') && $request->has('long')){
$lat = $request->lat;
$long = $request->long;
$parties=DB::select(DB::raw("SELECT *,111.045*DEGREES(ACOS(COS(RADIANS(':lat'))*COS(RADIANS(`latitude`))*COS(RADIANS(`longitude`) - RADIANS(':long'))+SIN(RADIANS(':lat'))*SIN(RADIANS(`latitude`)))) AS distance_in_km FROM parties ORDER BY distance_in_km asc LIMIT 0,5"), array(
'lat' => $lat,
'long' => $long
));
$hidacik = Parties::hydrate($parties);
return Fractal::includes('places')->collection($hidacik,new PartyTransformer);
}
else {
$parties = Parties::all();
}
return Fractal::includes('places')->collection($parties,new PartyTransformer);
}
}
In $parties = Parties::whereRaw("ACOS(SIN(RADIANS('latitude'))*SIN(RADIANS($request->lat))+COS(RADIANS('latitude'))*COS(RADIANS($request->lat))*COS(RADIANS('longitude')-RADIANS($request->long)))*6380 < 10");
you are missing ->get(). you need to add get() in order to return a collection which you can then work with
//this returns a collection now since we added get()
$parties = Parties::whereRaw("ACOS(SIN(RADIANS('latitude'))*SIN(RADIANS($request->lat))+COS(RADIANS('latitude'))*COS(RADIANS($request->lat))*COS(RADIANS('longitude')-RADIANS($request->long)))*6380 < 10")->get();

How to use Laravel's attach() on filtered query result?

I would like to use attach() on a filtered Builder result:
$users = User::whereIn('type', array(1, 2))->get();
$usersType_1 = $users->filter(function($item) {
return $item->type == 1;
});
$usersType_2 = $users->filter(function($item) {
return $item->type == 2;
});
$usersType_1->role()->attach(3);
$usersType_2->role()->attach(4);
So, I need to attach the role based on the user type. The role() method is specified on the User model
The attach() part from the code above throws the following error: Method role() doesn't exist - which I assume happens because filter() returns a Collection.
Is there a working way to attach pivot entries on filtered Builder result? Or do I need to run 2 separate queries and run attach() on them respectively?
You couldn't use role method no collection instead of User model. Try by:
$users = User::whereIn('type', array(1, 2))->get();
$usersType_1 = $users->filter(function($item) {
if($item->type == 1) {
return $item->role()->attach(3);
}
})
->all();
$usersType_2 = $users->filter(function($item) {
if($item->type == 2) {
return $item->role()->attach(4);
}
})
->all();
Alternative :
$users = User::whereIn('type', array(1, 2))->get();
$usersType_1 = $users->filter(function($item) {
$item->type == 1;
})
->all();
$usersType_2 = $users->filter(function($item) {
return $item->type == 2;
})
->all();
$role1 = Role::find(3);
$role2 = Role::find(4);
$role1->users()->attach($usersType_1->puck('id'));
$role2->users()->attach($usersType_2->puck('id'));
Why not just do:
$usersType_1 = $users->where('type', 1); // or whereType(1)
$usersType_1->role()->attach(3);
$usersType_2 = $users->where('type', 2); // or whereType(1)
$usersType_2->role()->attach(4);
I think the "Method role() doesn't exist" exception occurs because you're filtering an Eloquent Collection.
EDIT: I see why this doesn't work. It's because you're trying to attach an entire collection to a role. It would work if you used find() and then attached a role.
So you should loop over all the users with type 1 and attach the role
foreach ($usersType_1 as $user) {
$user->role()->attach(3);
}

Categories