helper vs controller performace in laravel 5.* - php

Lets assume I have a helper called engine.
if( ! function_exists('engine') )
{
function engine($user_id_1, $user_id_2, $league, $log = true)
{
/*
* variables
*/
$questionsLevel = 1;
$user1 = \App\User::where('id', $user_id_1)->first();
$user2 = \App\User::where('id', $user_id_2)->first();
$league = \App\Models\League::where('id', $league)->first();
$users = $league->all_users;
/*
* check if users joined to league correctly
*/
$user1_ok = $user2_ok = false;
foreach($users as $user)
{
if( $user->id == $user_id_1 )
{
$user1_ok = true;
}
if( $user->id == $user_id_2)
{
$user2_ok = true;
}
$check_users = [
$user1_ok,
$user2_ok
];
}
if( in_array(false, $check_users) )
{
return [
'errcode' => 404,
'errmessage' => 'one ro both user/s did not joined to league'
];
}
//DO SOME STUFF
}//function engine
}
As you know, I can write a controller to do same.
Does anyone know Which is faster based on Laravel architecture? and how can I test performance in both cases?
I'm using laravel 5.7.*

Fastest would be a solution that does not load unnecessary data:
if(! function_exists('engine'))
{
function engine(int $user1, int $user2, int $league)
{
return League::query()
->where('id', $league)
->whereHas('users', function($query) use ($user1) {
$query->where('id', $user1);
})
->whereHas('users', function($query) use ($user2) {
$query->where('id', $user2);
})
->exists();
}
}
In general, I don't think such a function should return complex results like an error message though. For this particular job, you should use a controller action. The helper method should only return true or false in my opinion. You could also place the same logic in a service class though (probably the best solution).

About the performance, I do not think there will be any changes as same query will be executed as well as same number of operation will be done, for example the foreach() loop it is O(n) in both the cases, so what will be difference, but you may like to change your code to something like below or may use ternary operator also. The difference will be in organization of codes. In controller the testing will be easier.
Note: But how id will be both $user1 and $user2, so let's make it OR in stead of AND
foreach($users as $user)
{
if( $user->id == $user_id_1 )
{
$user1_ok = true;
}elseif( $user->id == $user_id_2)
{
$user2_ok = true;
}
$check_users = [
$user1_ok,
$user2_ok
];
}
to
foreach($users as $user)
{
if( in_array($user->id, [$user_id_1, $user_id_2])
{
$user1_ok = true;
}
$check_users = [
$user1_ok,
$user2_ok
];
}
or
foreach($users as $user)
{
if( $user->id == $user_id_1 )
{
$check_users['user1_ok'] = $user->id == $user_id_1 ? true : false;
}
if( $user->id == $user_id_2)
{
$check_users['user2_ok'] = true;
}
}
If you want to get enginees with user1_ok, user2_ok, you may like to run a query as #Namoshek has suggested.
or you may like to change the query to something like
League::query()->where('id', $league)
->whereHas('users', function($query) use ($user1, $user2) {
$query->where('id', $user1)->where('id', $user2);
})->exists();
but, how id will be both $user1 and $user2?so, if it is OR instead of AND.
League::query()->where('id', $league)
->whereHas('users', function($query) use ($user1, $user2) {
$query->whereIn('id', [$user1, $user2]);
})->exists();
or even $users = [$user1, $user2]; and
League::query()->where('id', $league)
->whereHas('users', function($query) use ($users) {
$query->whereIn('id', $users);
})->exists();
By the way, if you want this using these query, you just may put it in Engine model and user or reuse it when ever required. Also for helper, it can be reusable where as in controller it is not.

Related

OctoberCMS Pagination

I use octobercms and User Extended plugin(Clacke). I try to render a pagination because for now i have a lot of registered users and they display on one page.
I use random users function from \classes\UserManager.php
public static function getRandomUserSet($limit = 7)
{
$returner = new Collection;
$userCount = User::all()->count();
if(!isset($userCount) || empty($userCount) || $userCount == 0)
return [];
if($userCount < $limit)
$limit = $userCount;
$users = User::all(); //paginate(5)
if(empty($users))
return $returner;
$users->random($limit);
$friends = FriendsManager::getAllFriends();
foreach($users as $user)
{
$userAdd = true;
if(!$friends->isEmpty())
{
foreach($friends as $friend)
{
if($user->id == $friend->id)
{
$userAdd = false;
break;
}
}
}
if($user->id == UserUtil::getLoggedInUser()->id)
$userAdd = false;
if($userAdd)
{
$returner->push($user);
}
}
return $returner->shuffle();
}
try to do this with changing return $returner->paginate(25); and $users = User::paginate(25); but throws me an error
An exception has been thrown during the rendering of a template
("Method paginate does not exist.").
After that i try to change directly in \components\User.php
public function randomUsers()
{
return UserManager::getRandomUserSet($this->property('maxItems'))->paginate(12);
}
But again the same error.
Tryed and with this code and render in default.htm {{ tests.render|raw }}
public function randomUsers()
{
$test = UserManager::getRandomUserSet($this->property('maxItems'));
return $test->paginate(10);
}
Again with no success. Could anyoune give me some navigation and help to fix this?
If you are using random users function from \classes\UserManager.php
I checked the code and found that its using Illuminate\Support\Collection Object. So, for that Collection Object pagination works differently
You need to use forPage method.
On the other hands paginate is method of Illuminate\Database\Eloquent\Collection <- so both collection are not same
Use forpage
// OLD return UserManager::getRandomUserSet($this->property('maxItems'))
// ->paginate(12);
TO
return UserManager::getRandomUserSet($this->property('maxItems'))
->forPage(1, 12);
forPage method works like forPage(<<PAGE_NO>>, <<NO_OF_ITEM_PER_PAGE>>);
so if you use forPage it will work fine.
if any doubt please comment.

Efficient way to populate drop-down dynamically in laravel

I have multiple database tables which each one of them populates a dropdown, The point is each dropdown effects next dropdown.
I mean if I select an item from the first dropdown, the next dropdown values change on the selected item, which means they are related and they populate dynamically on each dropdown item change.
I know this is not efficient and need refactoring so I'd be glad to point me out to the right way of populating those dropdowns.
This is the controller code:
<?php
use App\Http\Controllers\Controller;
use App\Models\County;
use App\Models\OutDoorMedia;
use App\Models\Province;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
class FiltersController extends Controller
{
protected static $StatusCode = 0;
protected static $Msg = '';
protected static $Flag = false;
public function index(Request $request)
{
try {
$validator = Validator::make($request->all(), [
'province_id' => 'exists:province,id',
'county_id' => 'exists:county,id',
'media_type' => Rule::in([MEDIA_TYPE_BILLBOARD, MEDIA_TYPE_OUTDOOR_MONITOR, MEDIA_TYPE_INDOOR_MONITOR, MEDIA_TYPE_STAND, MEDIA_TYPE_STRABOARD, MEDIA_TYPE_BRAND_BOARD]),
'media_status' => Rule::in([MEDIA_STATUS_AVAILABLE, MEDIA_STATUS_NEGOTIATING, MEDIA_STATUS_ASSIGNED, MEDIA_STATUS_ARCHIVE]),
'category_id' => 'exists:category_list,id',
]);
if ($validator->fails()) {
abort(400, $validator->errors()->first());
} else {
//############################# Input Filters ####################################
$province_id = $request->has('province_id') ? $request->province_id : null;
$county_id = $request->has('county_id') ? $request->county_id : null;
$media_type = $request->has('media_type') ? $request->media_type : null;
$location = $request->has('location') ? $request->location : null;
$media_status = $request->has('media_status') ? $request->media_status : null;
$category_id = $request->has('category_id') ? $request->category_id : null;
//this flag is for detecting if user is requesting from "advertiser my order" to ignore some concitions
$advertiser_my_orders = ($request->has('my_orders') && $request->my_orders == 'true') ? true : false;
$province_ids = [];
$county_ids = [];
//############################# Media Owner Filters ####################################
//offline section filters
if (!is_null($province_id) && Province::whereId($request->province_id)->exists()) {
//check correction of county id
if (!is_null($county_id) && County::whereId($county_id)->whereProvinceId($province_id)->exists()) {
$media_owner_ODM_Provinces = Province::whereHas('media')->get()->toArray();
$media_owner_ODM_Provinces_Counties = County::whereProvinceId($province_id)
->whereHas('county_media')->get()->toArray();
foreach ($media_owner_ODM_Provinces as $key => $province) {
if ($province['id'] == $province_id) {
$media_owner_ODM_Provinces[$key]['county'] = $media_owner_ODM_Provinces_Counties;
}
}
$media_owner_ODM_locations = OutDoorMedia::whereProvinceId($province_id)->whereCountyId($county_id)->groupBy('location')->pluck('location')->toArray();
$media_owner_ODM_media_types = OutDoorMedia::whereProvinceId($province_id)->whereCountyId($county_id)->whereIn('location', $media_owner_ODM_locations)->groupBy('media_type')->pluck('media_type')->toArray();
$media_owner_ODM_media_Status = OutDoorMedia::whereProvinceId($province_id)->whereCountyId($county_id)->whereIn('media_type', $media_owner_ODM_media_types)->groupBy('status')->pluck('status')->toArray();
} else {
$media_owner_ODM_Provinces = Province::whereHas('media')->with(['county' => function ($query) {
$query->whereHas('county_media');
}])->get()->toArray();
$media_owner_ODM_locations = OutDoorMedia::whereProvinceId($province_id)->groupBy('location')->pluck('location')->toArray();
$media_owner_ODM_media_types = OutDoorMedia::whereProvinceId($province_id)->whereIn('location', $media_owner_ODM_locations)->groupBy('media_type')->pluck('media_type')->toArray();
$media_owner_ODM_media_Status = OutDoorMedia::whereProvinceId($province_id)->whereIn('media_type', $media_owner_ODM_media_types)->groupBy('status')->pluck('status')->toArray();
}
} else {
$media_owner_ODM_Provinces = Province::whereHas('media')->with(['county' => function ($query) {
$query->whereHas('county_media');
}])->get()->toArray();
foreach ($media_owner_ODM_Provinces as $province) {
$province_ids[] = $province['id'];
foreach ($province['county'] as $county) {
$county_ids[] = $county['id'];
}
}
$media_owner_ODM_locations = OutDoorMedia::whereIn('province_id', $province_ids)->whereIn('county_id', $county_ids)->groupBy('location')->pluck('location')->toArray();
$media_owner_ODM_media_types = OutDoorMedia::whereIn('province_id', $province_ids)->whereIn('county_id', $county_ids)->whereIn('location', $media_owner_ODM_locations)->groupBy('media_type')->pluck('media_type')->toArray();
$media_owner_ODM_media_Status = OutDoorMedia::whereIn('province_id', $province_ids)->whereIn('county_id', $county_ids)->whereIn('media_type', $media_owner_ODM_media_types)->groupBy('status')->pluck('status')->toArray();
}
$media_owner_offline = [
'provinces' => $media_owner_ODM_Provinces,
'media_status' => $media_owner_ODM_media_Status,
'location' => $media_owner_ODM_locations,
'media_type' => $media_owner_ODM_media_types,
];
$filters['media_owner']['offline'] = $media_owner_offline;
self::$StatusCode = 200;
self::$Msg = $filters;
self::$Flag = true;
}
} catch (\Exception $e) {
//=========== Get Error Exception Message ============
self::$StatusCode = 400;
self::$Msg = $e->getMessage();
self::$Flag = false;
return $this->CustomeJsonResponse(self::$Flag, self::$StatusCode, self::$Msg);
//=========== Get Error Exception Message ============
} finally {
return $this->CustomeJsonResponse(self::$Flag, self::$StatusCode, self::$Msg);
}
}
}
FYI: I'm using laravel 5.3 framework.
Here are something that you should be aware of them.
first of all, if you validate province_id, so there is no need to double check it in your code. so you should remove
Province::whereId($request->province_id)->exists()
Second one is, Laravel has ->when eloquent method that helps you reduce if else statements for null values, if we have a null value for given parameter, it will not effect the query.
https://laravel.com/docs/5.8/queries#conditional-clauses
Third one, I suggest you to use Laravel Resources in order to transform your fetched data from database in API.
https://laravel.com/docs/5.8/eloquent-resources
This is better version of small portion of your code, I think with suggested tips and this code, you can refactor it:
class TestController extends Controller
{
const DEFAULT_COUNTRY_ID = '10';
public $request;
public function something(Request $request)
{
// Put Validations ...
$this->request = $request;
OutDoorMedia::when('province_id', function ($query) {
return $query->where('province_id', $this->request->province_id);
})
->when('country_id', function ($query) {
// if country_id exists
return $query->where('country_id', $this->request->country_id);
}, function ($query) {
// else of above if (country_id is null ...)
return $query->where('country_id', self::DEFAULT_COUNTRY_ID);
})
->get();
}
}
This is just a sample, You can use this way to refactor your code base.

Laravel 5.5 Eloquent - Optionally chain where statements

I want the following to happen - search by fields if fields are different from "all". Something like this:
// If $request['field'] != 'all' add query
if(isset($request['types'])) {
$query = Offer::whereHas('types', function ($query) use ($request) {
$typeArray = $request->get('types');
$query->whereIn('type', $typeArray);
});
}
if ($request['country'] != 'all') {
query->where('country_id', $country);
}
At the end I want to order and paginate the results like so:
$offers = $query->orderBy('created_at', 'desc')->paginate(9);
Is there any way to achieve this? If my question isn't clear enough tell me and I will edit it and try to explain better.
You can use eloquent when method to check conditions and append query. in when method you can check your conditions.
Offer::when(isset($request['types']), function($query) {
$query->whereHas('types', function ($query) {
$query->whereIn('type', request()->get('types'));
});
})
->when(request()->country != 'all', function($query) {
$query->where('country_id', request()->country);
})
->orderBy('created_at', 'desc')->paginate(9);
Can you try this:
<?php
$query = Offer::where(function($q) use ($request){
if ( !empty($request['country']) && is_numeric($request['country']) ) {
$query->where('country_id', $request['country']);
}
if( !empty($request['types']) && is_array($request['types']) ) {
$q->whereHas('types', function ($q) use ($request) {
$typeArray = $request->get('types');
$q->whereIn('type', $typeArray);
});
}
});
$offers = $query->orderBy('created_at', 'desc')->paginate(9);

How can I do a self-join in Laravel?

Here is my code:
$role_id = Auth::user()->role_id;
$related = Page::where('path', $request->path())->where('method', $_SERVER["REQUEST_METHOD"])->first()->related;
$pages = Page::where('related', $related)->get();
foreach ($pages as $page){
$accessGiven = page_role::where('role_id', $role_id)->where('page_id', $page->id)->first();
if ( sizeof($accessGiven) > 0 ) {
return $next($request);
}
}
return redirect('/');
It works well logically, but it is a little slow for huge dataset. You know, it's actually a middleware and will be executed before most of requests.
Anyway, I guess I can combine line 2 and line 3 and make one query of them. Any idea how can I do that?
Try this:
In your Page model:
public function relatedPages(){
return $this->hasMany(self::class, 'related', 'related');
}
then
$pages = Page::where('path', $request->path())->where('method', $_SERVER["REQUEST_METHOD"])->first()->relatedPages
Look in this example (I didn't test it):
$requestPath = $request->path();
$serverMethod = $_SERVER["REQUEST_METHOD"];
$pages = Page::where('related', function($query) use($requestPath,$serverMethod) {
$query->where('path', $requestPath )->where('method',
$serverMethod)->first()->related; }
)->get()

Building dynamic queries in Laravel - how to do a multiple option search

So I am trying to set up a search page and it has multiple get options But I am curious as to how to set this up correctly, I know this is far from correct as I am doing if statements inside of setting a variable, But I am so lost right now.
Any help would be greatly appreciated.
public function index()
{
$queryUsername = Request::get('u');
$queryPostcode = Request::get('p');
$queryOrderbyPhotos = Request::get('o1');
$queryOrderbyOnline = Request::get('o2');
$queryOrderbyTypes = Request::get('o3');
$users = User::rightJoin('user_profiles','users.id', '=', 'user_profiles.user_id')
if ($queryUsername)
{
->where('users.username', '=', "$queryUsername")
}
if ($queryPostcode) {
->where('user_profiles.postcode', '=', "$queryPostcode")
}
if ($queryOrderbyPhotos) {
->whereNotNull('user_profile.avatar')
}
if ($queryOrderbyOnline) {
->orderBy('users.last_online', 'DESC')
}
if ($queryOrderbyType) {
->orderBy('users.type', 'DESC')
}
->get();
return view('view', compact('users'));
}
This is how I'll approach the problem. I'll create a variable holding the query builder and then call all the additional query methods on it.
With Eloquent and actually with any class that allows Method Chaining you can do this:
$query = User::select(...)->join(..);
$query->where(...);
$query->get(...);
So in your case I'll be trying to achieve what you want in this manner:
public function index()
{
$input = Request::all();
$query = User::rightJoin('user_profiles', 'users.id', '=', 'user_profiles.user_id');
if (isset($input['u']) && $input['u'])
$query->where('users.username', '=', $input['u']);
if (isset($input['p']) && $input['p'])
$query->where('user_profiles.postcode', '=', $input ['p']);
if (isset($input['o1']) && $input['o1'])
$query->whereNotNull('user_profile.avatar');
if (isset($input['o2']) && $input['o2'])
$query->orderBy('users.last_online', 'DESC');
if (isset($input ['o3']) && $input['o3'])
$query->orderBy('users.type', 'DESC');
$users = $query->get();
return view('view', compact('users'));
}
Of course it will be a good idea that you have an additional check for valid input on each input parameter. But this can be achieved in many ways. You can read more about Laravel Controller Validation or Laravel Form Request Validation
By the way I'll suggest to move all this code in your model or in separate class as I prefer keeping controllers slim.
You can try :
$users_query = new User;
$users_query->rightJoin(....);
if ($queryUsername)
{
$users_query->where('users.username', '=', "$queryUsername")
}
// Your other conditions .....
....
$users = $users_query->get();
multiple option search
This is a trait that can be used by any models
This function will remove code repetitions into your project
public function scopeSearch($query, $keyword, $columns = [], $relativeTables = [])
{
if (empty($columns)) {
$columns = array_except(
Schema::getColumnListing($this->table), $this->guarded
);
}
$query->where(function ($query) use ($keyword, $columns) {
foreach ($columns as $key => $column) {
$clause = $key == 0 ? 'where' : 'orWhere';
$query->$clause($column, "LIKE", "%$keyword%");
if (!empty($relativeTables)) {
$this->filterByRelationship($query, $keyword, $relativeTables);
}
}
});
return $query;
}
Filter into relationship also
private function filterByRelationship($query, $keyword, $relativeTables)
{
foreach ($relativeTables as $relationship => $relativeColumns) {
$query->orWhereHas($relationship, function($relationQuery) use ($keyword, $relativeColumns) {
foreach ($relativeColumns as $key => $column) {
$clause = $key == 0 ? 'where' : 'orWhere';
$relationQuery->$clause($column, "LIKE", "%$keyword%");
}
});
}
return $query;
}

Categories