skip parameter value of a function - php

Let suppose I have a function:
public function __getalldata($tablename,$tablefield=array(),$orderbyfield = 'id',$ascdesc = 'desc',$limit = 200,$type='result')
{
//Query generation according to above parameters
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->limit($limit)
->get();
return $data->$type();
}
This function is been used to many pages. Now, I want to skip the parameter $limit From function. That means I don't want to give $limit value but I need all data from database.
So what happens is if I have data like 600 and I don't know how many data I have, I don't give any parameter value to function (so by default it will take 500 data). But here I need all data so how can I manage this situation?

From https://www.codeigniter.com/user_guide/database/query_builder.html passing in NULL to the limit method as the first parameter will cause the limit statement to not limit anything - hence returning all of the rows (see on github):
public function limit($value, $offset = 0)
{
is_null($value) OR $this->qb_limit = (int) $value;
empty($offset) OR $this->qb_offset = (int) $offset;
return $this;
}
So in short if you want to skip the $limit parameter try setting its value to NULL. Like so...
public function __getalldata($tablename, $tablefield=array(), $orderbyfield = 'id', $ascdesc = 'desc', $limit = NULL, $type='result')
{
// Query generation according to above parameters
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->limit($limit)
->get();
return $data->$type();
}

I assume that the default value of $limit = 200 is already used in a number of places in the application. So the goal is to bypass the default when you want all the matching records. My suggestion is to simply pass the value 0 (zero) when you want them all. Then the only a small code change is required in the class method.
public function __getalldata($tablename, $tablefield=array(), $orderbyfield = 'id',
$ascdesc = 'desc',$limit = 200,$type='result')
{
if($limit > 0)
{
$this->db->limit($limit);
}
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->get();
return $data->$type();
}

public function __getalldata($tablename,$tablefield=array(),$orderbyfield = 'id',$ascdesc = 'desc',$limit = '',$type='result')
{
//Query generation according to above parameters
if($limit == '') {
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->get();
} else {
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->limit($limit)
->get();
}
return $data->$type();
}
Did you tried this?

As per MySQL documentation:
You can used bigint max values 18446744073709551615 to retrieve all data from MySQL.
So your function should look like this
public function __getalldata($tablename,$tablefield=array(),$orderbyfield = 'id',$ascdesc = 'desc',$limit = 18446744073709551615,$type='result')
{
//Query generation according to above parameters
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->limit($limit)
->get();
return $data->$type();
}

Note my main answer is at the bottom (IF you can't edit this function) but I have put in alternative answers as am unclear from OP as to if the function can/should be edited.
Edit this function
Your declared function has:
public function __getalldata($tablename,...,$limit = 200 ...){ ... }
Where the reference given in various answers here appearing to be adjusting this default value - the simplest solution is to NOT edit the default but set a special case and explicitly check for if $limit is exactly (and only) null.
To clarify:
PHP will use an explicitly set NULL value over any default value (in this case, 200).
So;
// within the function
if($limit !== null) {
//use limit in your PDO
}
This does not convert if $limit == 0 or other type-juggling cases, common to PHP
This above means that if no $limit value is given then default of 200 is used. If an explicit NULL value is given, that will be used instead.
Code
Please note this code is for example only and can probably be simplified further to better follow DRY patterns, but I don't know enough about CodeIgniter to do this here and now.
public function __getalldata($tablename,$tablefield=array(),$orderbyfield = 'id',$ascdesc = 'desc',$limit = 200,$type='result')
{
//Query generation according to above parameters
if($limit !== null){
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->limit($limit)
->get();
}
else {
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->get();
}
return $data->$type();
}
If you can't edit the function
I'm unclear on if you can (or want) to edit the function itself, if you're using Codeigniter, so instead simply refer an automatic value to the function arguments to return every valud row, no matter what (in effect removing the case for limit.
To do this use the PHP Constant PHP_INT_SIZE or PHP_INT_MAX which will return exactly that value. Reading this question/answer tells you more about this, but these constants should be self explanatory, as the maximum int values.
If a MySQL LIMIT exceeds the rows returned, this has no effect, so by setting this to a maximal value, it will always exceed the rows returned and/or the rows usable by the resulting page output.
Therefore:
$var = __getalldata("table",[0=>'columnname'],'id','desc',PHP_INT_MAX,'result');
Will return you every row within the PHP Integer limit of [2,147,483,647] or [9,223,372,036,854,775,807], depending on your system (32bit or 64bit).
And let's face it -- if 2 billion is not enough of a limit, you need to face you have some serious problems with your SQL query ;-)

That is easy with the use of an if statement. Just check to see if a condition is true for your limit, if not resolve to else. Like so:
public function __getalldata($tablename,$tablefield=array(),$orderbyfield = 'id',$ascdesc = 'desc',$limit = 200,$type='result')
{
if () // check $limit condition here
{
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->limit($limit)
->get();
return $data->$type();
}
else {
$data = $this->db
->select($tablefield)
->from($tablename)
->order_by($orderbyfield, $ascdesc)
->get();
return $data->$type();
}
}
EDIT: I am not sure what your conditions for the $limit variable are, so just enter them in the if statement's arguments.

Related

Laravel conditional clause when for filtering value

I have a event table with status o or 1.
I have to filter the event with status 1,0 or ALL.
I tried with laravel when conditional clause,its not working with value zero,other conditionals are working.
$status = Input::get('status');
$events = DB::table('events')
->select('events.*')
->when($status, function ($query) use ($status) {
return $query->where("events.status",$status);
})
->get();
in_array function used in when method try this one
because 0(zero) means(false) by default understand so try to use inner function in when method
if didn't pass status then set default value as 2 or any number but not 0,1 and null
$status = Input::has('status') ? Input::get('status') : 2;
$events = DB::table('events')->select('events.*')
->when(in_array($status,[0,1]), function ($query) use ($status) {
return $query->where("events.status",$status);
})->get();
second way create a new function
function checkStatus($status,$array) {
if(isset($status)) {
return in_array($status,$array);
}
return false;
}
$events = DB::table('events')->select('events.*')
->when(checkStatus($status,[0,1]), function ($query) use ($status) {
return $query->where("events.status",$status);
})->get();
For any future reader using 0 and 1
we can use when in the following way
->when($request->has('status'),
fn ($query) => $query->where("status", $request->status)
)->get();
we are getting the user requested status using has method in Laravel to deal with it.

Laravel modify Collection data

I wonder if Laravel have any helper to modify a collection.
What I need to do is to make a query with paginate() then check if the logged in users ID match the sender or receiver and based on that add a new value to the output:
$userId = Auth::guard('api')->user()->user_id;
$allMessages = Conversation::join('users as sender', 'conversations.sender_id', '=', 'sender.user_id')
->join('users as reciver', 'conversations.recipient_id', '=', 'reciver.user_id')
->where('sender_id',$userId)->orWhere('recipient_id',$userId)
->orderBy('last_updated', 'desc')
->select('subject','sender_id','recipient_id', 'sender_unread', 'recipient_unread', 'last_updated', 'reciver.username as receivername', 'sender.username as sendername')
->paginate(20);
Now I want to do something like:
if ($allMessages->sender_id == $userId) {
// add new value to output
newField = $allMessages->sendername
} else {
// add new value to output
newField = $allMessages->receivername
}
Then send the data with the new value added
return response()->json(['messages' => $allMessages], 200);
Is this possible?
You're better off using the Collection class's built-in functions for this. For example, the map function would be perfect.
https://laravel.com/docs/5.3/collections#method-map
$allMessages = $allMessages->map(function ($message, $key) use($userId) {
if ($message->sender_id == $userId) {
$message->display_name = $message->receivername;
} else {
$message->display_name = $message->sendername;
}
return $message;
});
Solved by adding:
foreach ($allMessages as $message) {
if ($message->sender_id == $userId) {
$message->display_name = $message->receivername;
} else {
$message->display_name = $message->sendername;
}
}
You can surely use the laravel's LengthAwarePaginator.
Along with total count of collection you also need to pass the slice of collection's data that needs to be displayed on each page.
$total_count = $allMessages->count();
$per_page = 2;
$current_page = request()->get('page') ?? 1;
$options = [
'path' => request()->url(),
'query' => request()->query(),
];
Suppose you want 2 results per page then calculate the offset first
$offset = ($current_page - 1) * $per_page;
Now slice the collection to get per page data
$per_page_data = $collection->slice($offset, $per_page);
$paginated_data = new LengthAwarePaginator($per_page_data, $total_count, $per_page, $current_page, $options);
$paginated_data will have only limited number of items declared by $per_page variable.
If you want next two slice of data then pass api_request?page="2" as your url.
As I don't know which Laravel version you're using, taking Laravel 5.2 let me give you a smarter way to deal with this (if I get your problem correctly).
You can use Laravel's LengthAwarePaginatior(API Docs).
Don't use paginate method when you are bulding your query, instead of that use simple get method to get simple collection.
$userId = Auth::guard('api')->user()->user_id;
$allMessages = Conversation::join('users as sender', 'conversations.sender_id', '=', 'sender.user_id')
->join('users as reciver', 'conversations.recipient_id', '=', 'reciver.user_id')
->where('sender_id',$userId)->orWhere('recipient_id',$userId)
->orderBy('last_updated', 'desc')
->select('subject','sender_id','recipient_id','sender_unread','recipient_unread','last_updated','reciver.username as receivername','sender.username as sendername')
->get();
Now you can populate extra items into that collection based on your certain conditions like this.
if ($allMessages->sender_id == $userId ) {
// add new value to collection
} else {
// add new value to collection
}
Now use LengthAwarePaginator, to convert that populated collection into a paginated collection.
$total_count = $allMessages->count();
$limit = 20;
$current_page = request()->get('page');
$options = [
'path' => request()->url(),
'query' => request()->query(),
];
$paginated_collection = new LengthAwarePaginator($allMessages, $total_count, $limit, $current_page, $options);
The variable $paginated_collection now can be used to be sent in response. Hope this helps you to deal with your problem.

laravel gives me object when it shouldn't

So I have the following scope on an Investor Model:
public function scopeCountUsersInRegionForCompany(Builder $query, $companyName, $regionCode)
{
return $query->select('users.*')
->join('user_profile', 'user_profile.user_id', '=', 'users.id')
->join('investments', 'users.id', '=', 'investments.user_id')
->join('offerings', 'investments.offering_id', '=', 'offerings.id')
->join('companies', 'offerings.company_id', '=', 'companies.id')
->where('companies.slug', '=', $companyName)
->where('user_profile.region', '=', $regionCode)
->count();
}
Its used in the following context:
public function regionCountForCompany($slug)
{
$regions = [];
$totalUsersForCompany = Investor::countUsersForCompany($slug);
if ($totalUsersForCompany === 0) {
return [];
}
foreach ($this->getAllRegions() as $regionCode => $regionName) {
$regions[$regionName] = (Investor::countUsersInRegionForCompany($slug, $regionCode) / Investor::countUsersForCompany($slug)) * 100;
}
if (max($regions) > 0) {
return $regions;
} else {
return [];
}
}
Note the for each loop:
foreach ($this->getAllRegions() as $regionCode => $regionName) {
$regions[$regionName] = (Investor::countUsersInRegionForCompany($slug, $regionCode) / Investor::countUsersForCompany($slug)) * 100;
}
the following call:
Investor::countUsersInRegionForCompany($slug, $regionCode)
will return 1 and then every other time after that it returns: Object of class Illuminate\Database\Eloquent\Builder could not be converted to int If I do: var_dump(is_object(Investor::countUsersInRegionForCompany($slug, $regionCode))) Its false once, then true for every other time its called.
This is the only scope that causes this issue. How ever if I do a var dump inside the scope to get the variable $query instead of returning it like you see me doing, it comes back as an int.
If I hit the end point in the browser that calls this particular set of code, all of this works, no errors about objects or any of that. The test is as follows:
public function it_should_return_region_count_for_investors() {
$this->createInvestment();
$investor = factory(User::class)->create([
'role_id' => 4
]);
$response = $this->actingAs($investor)
->call('GET', 'api/v1/investors-signedup-by-jurisdiction/');
$this->assertNotEmpty(json_decode($response->getContent()));
}
The response comes back as: Object of class Illuminate\Database\Eloquent\Builder could not be converted to int (It actually renders a html laravel error page with this message)
What is going on?
It's because of a little bit stupid check of Laravel. When it calls scope it checks what the function must return and add fallback for chaining :
return call_user_func_array([$this->model, $scope], $parameters) ?: $this;
so when your function returns 1 - ternary operator equals to true and result returns, when it return 0 - ternary operator fails, and you get this instead.
I advice you to not return result from scope. It's done to modify query, not to return results. Put count() outside the scope like
Investor::usersInRegionForCompany($slug, $regionCode)->count()

PHP Error: Cannot use a scalar value as an array... However

I'm currently working with the medoo.php framework, and although I would normally use their ticket area on github, it appears that no one actually uses that... so...
At any rate, when I'm running one of my files which uses "require" to call the framework, I get the following error:
Warning: Cannot use a scalar value as an array in /home/..../public_html/projects/friendcodes/medoo.min.php on line 759
However, when I inspect the code (the below is lines 752 to 764), I see that it is in fact supposed to check if $where is not set, and if it isn't, make it an array - however this php error begs to differ.
I'm guessing that $where is being set as a variable somewhere else, that's not an array, but there are over 100 occurrences of the variable in the framework, and 830 lines of code, which you probably don't want to see. (Let me know in a comment and I'll add it - again, this is directly from medoo's most two recent updates/releases.)
public function get($table, $columns, $where = null)
{
if (!isset($where))
{
$where = array();
}
$where['LIMIT'] = 1;
$data = $this->select($table, $columns, $where);
return isset($data[0]) ? $data[0] : false;
}
My main question is - How do I rectify this problem without breaking something in this framework which is extremely complex (for my level, at any rate)
Update: How silly of me! I found the problem. Just as people suggested, I was calling $where wrong.
I was calling it with:
$accountinfo = $database->get('xf_user_field_value', ['field_value'], 1);
Instead of
$accountinfo = $database->get('xf_user_field_value', ['field_value'], ["user_id"=>1]);
(Where the third arg is $where) Thanks for the help guys!
Right, first things first, we need to find out what is calling get that shouldn't be. WHICH IS THE ENTIRE PROBLEM. The problem isn't the function itself, the problem is something is calling it using an argument for $where which isn't an array. Changing a library to fix one faulty call is ridiculous.
Step 1: Temporarily edit the get function to include a print_r of the $where variable.
public function get($table, $columns, $where = null)
{
if(isset($where)) print_r($where);
if (!isset($where))
{
$where = array();
}
$where['LIMIT'] = 1;
$data = $this->select($table, $columns, $where);
return isset($data[0]) ? $data[0] : false;
}
This will show us before the error prints the value of $where, which will help you find the malformed get call.
If this fails, try using PHP's built-in backtrace to try to find the issue:
public function get($table, $columns, $where = null)
{
if(isset($where)) print_r(debug_backtrace());
if (!isset($where))
{
$where = array();
}
$where['LIMIT'] = 1;
$data = $this->select($table, $columns, $where);
return isset($data[0]) ? $data[0] : false;
}
The ->get() method is not called properly.
Cannot use a scalar value as an array
That warning is shown if $where is either true, a numeric value or a resource. Valid method calls include:
->get('table', '*')
->get('table', '*', array('WHERE' => 'foo = "bar"'))
Check the manual and fix your code.
EDIT 3: try moving $where['LIMIT'] = 1; inside of the isset statement, since you wouldn't want to pass LIMIT 1 to the query constructor if $where is passed by reference.
DISCLAIMER I have no knowledge of the medoo framework.
public function get($table, $columns, $where = null)
{
if (is_null($where))
{
$where = array('LIMIT'=>1);
}
$data = $this->select($table, $columns, $where);
return isset($data[0]) ? $data[0] : false;
}

CakePHP 1.3 paginate with custom query

I've managed to over-ride the default methods for a custom query in my model as suggested elsewhere,
function paginate($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array())
and
function paginateCount($conditions = null, $recursive = 0, $extra = array())
Unfortunately this approach over-rides all pagination for this model, and affects other pagination elsewhere. I found some code which may help where I could select whether I wanted the custom pagination used based on a variable e.g.
In my model
var $useCustom = false;
function paginateCount($conditions = null, $recursive = 0, $extra = array())
{
if(!$this->useCustom)
return parent::paginateCount($conditions, $recursive);
// code to handle custom paginate count here
}
I have found that using this method gives me an error,
Fatal error: Call to undefined method
AppModel::paginateCount() in....
What am I doing wrong? I assume that I would also need similar code in the paginate function as well? Am I also correct in thinking that I can set this variable in my controller i.e. $this->useCustom = 'true';
After a bit of delving into the code I found that the methods of paginateCount and paginate do not exist in the Model, or anywhere else for that matter, which is why I could not call them. The solution was copy the code from the main controller, which tests for the existence of the over-ride
For those that would like a similar solution use the following in paginateCount
if(!$this->useCustom)
{
$parameters = compact('conditions');
if ($recursive != $this->recursive) {
$parameters['recursive'] = $recursive;
}
$count = $this->find('count', array_merge($parameters, $extra));
return $count;
} else {
custom method...
}
and in paginate use
if(!$this->useCustom)
{
$parameters = compact('conditions', 'fields', 'order', 'limit', 'page');
if ($recursive != $this->recursive) {
$parameters['recursive'] = $recursive;
}
$results = $this->find('all', array_merge($parameters, $extra));
return $results;
} else {
custom method...
}
Hope this helps someone else.
I think you need the public keyword before your function to use the scope resolution operator in this way.
i.e. public function paginateCount(....

Categories