How to implement pagination using multiple searcing criteria in codeigniter - php

Im trying to implement pagination using multiple searching criteria.
Supposed I Have student table. I also use pagination when the list of student displayed.
The pagination link is. site_url . '/student/page/'; so I use $config['uri_segment'] = 1;
so the pagination link will be
1
2
and son.
After that I wanna search student data using 3 searching criteria implemented using textfield.
id name address.
user can search by id or name or address or combination of the three criteria.
the url become
http://mysite/index.php/student/page/0
href=http://mysite/index.php/student/page/1
and son.
but I use get method for searching. and while trying to search using the search criteria field the url become
href="http://mysite/index.php/student/page/1?id=1&name=a&address=b
the problem occurred when I try create pagination based on criteria. because the pagination link have contain query string
i don't know how to create become
href="http://mysite/index.php/student/page/0?id=1&name=a&address=b
href="http://mysite/index.php/student/page/1?id=1&name=a&address=b
or do you have a best practice to solve this problem ?
To solve that problem, I try using $this->uri->uri_to_assoc(). First I create array asocc for pagination link.
$array = array('id' => '001', 'name' => 'a', 'address' => 'canada');
the url become
id/001/name/a/address/canada. I use $this->uri->uri_to_assoc() function to get key and value of the segment.
array (
id => 001,
name=>a,
address=>canada
)
but while there some searching criteria that not included while searching. let say, the user only search by name and address. the array become
$array = array('id' => '', 'name' => 'a', 'address' => 'canada'); and the url id/name/a/address/canada
the assoc array become
array (
id => name,
a=>address,
canada=>
)
the assoc array is not disorganized again. so I can't get the right value of the assoc array.
I think i will set the identifier to the searching criteria if not included. supposed i put #.
if isset($_GET['id']) then
$id = '#'
else
$id = $_GET['id']
$array = array('id' => $id, 'name' => 'a', 'address' => 'canada');
How about that ... ? or if there are another best practice ?
Thanks

I've always found it somewhat a pain to deal with uri's in ci.
Is there a way you can set a default of some kind for your values if the user doesn't include that as part of their search? or even not include the key? so it would return something like
id/10/name/false/address/canada
or
id/10/address/canada
then you can
$uri = $this->uri->uri_to_assoc();
$id = array_key_exists("id", $uri) ? $uri['id'] : false;
$id = $id == 'false' ? false : $id;
$query .= $id ? "AND id = $id" : "";
etc...

When I use uri_to_assoc, I always have a default array, so in my application, I can always get the required parameter, even if it missing from the uri
$param_default = array('cat','page');
$param_array = $this->uri->ruri_to_assoc(3, $param_default);
Now I can safely access $param_array['cat'] and $param_array['page'] even when uri doesn't contain that parameter.
I always user ruri_to_assoc and ruri_segment, so the extra parameter always start in 3rd uri segment.

Related

Find the position inside this Yii 2 query

I have this following Yii 2 query
$find = People::find()->where(['c_id' => $c_id])->orderBy('totals DESC, id DESC')->all();
So imagine this query was an array. Everything found by this query has an "id" attribute.
Since it's sorted by "totals", I essentially want to return the position in the array where I can find this specific id.
Currently, I'm using this code.
foreach ($find as $t) {
$arr[] = $t->id;
if ($t->id == $id) {
break;
}
}
$key = count($arr);
return $key;
However, this code is vany wayow on a 100k+ result query.
Is there anyway to speed this up?
You could get the result as an array (instead of object) as
$find = People::find()->where(['c_id' => $c_id])
->orderBy('totals DESC, id DESC')
->asArray()
->all();
then you could find your value using array_search()
$my_index = array_search($id,$find);
but for 100k+ you should find using a direct select in db...instead tha looping on php or load all in php and scan with array_search()
To get array from query in YII, you can use queryAll();
$find = People::find()->where(['c_id' => $c_id])->orderBy('totals DESC, id DESC')->queryAll();
OR, another way to convert the object into an array is:
$find = json_decode(json_encode($find), true); // to convert all data into array.
And once you get results in array, you can implement the actual code for your requirement as given below.
You can use array_search() function to get index of your value.
$a=array("a"=>"red","b"=>"green","c"=>"blue");
echo array_search("red",$a);
The array_search() function search an array for a value and returns the key.
Maybe I didn't understand you correctly but I assume that you are trying to detect the index or key for your desired id inside an array returned from an SQL query that is sorted by some other column like total.
So let us fetch records from the database with your query with a little change asArray() like this
$find = People::find()
->where(['c_id' => $c_id])
->orderBy('totals DESC, id DESC')
->asArray()
->all();
in the result, let us assume the People table returns you an array with the following dataset ordered by columns total and id DESC.
[
0 => [
'id' => 2 ,
'c_id'=>2,
'name' => 'John' ,
'age'=>18,
'totals'=>100,
],
1=>[
'id'=>1,
'c_id'=>55,
'name'=>'Bob',
'age'=>20,
'totals'=>80,
],
2=>[
'id'=>3,
'c_id'=>85,
'name'=>'Peter',
'age'=>15,
'totals'=>75,
]
];
Now if you look into \yii\helpers\ArrayHelper you will find ArrayHelper::getColumn().
Let us use this on the array we received from the query, I assume that you are searching $id inside the column id so we will first filter out the id column like below.
$idsArray = ArrayHelper::getColumn($find, 'id');
this will give us the ids in the following sequence which is in the same order as the initial result set.
[2,1,3]
then lets use the built-in php function array_search()
$key=array_search($yourId,$idsArray);
Hope this is what you are looking for.

How to call PHP case from another PHP case

I have been using a formula to uniquely secure id's gathered from database before they are presented to the client.
However, as my code grown complex, I've fallen into this pitfall: I have two separate cases returning json that need to use the same id. Because my securing function produces a unique hash and key at each perform, hashed ID gathered from one case cannot be encrypted in the other one that needs to use it. Therefore as a solution, I thought that sending hashed id gathered from first case back to first case again, decrypt it there and then somehow pass it to the other case without client never having chance to catch the real id.
All the codes work fine, my problem is matching the id drawn from first case that is to be used in the second case that also sends data back before case break, which is simply a client-triggered loop. I am providing codes in case you would ask it. The problem is simply matching the same id with different unique hash in two separate php cases. Sorry if I made this more complicated than it should have.
This is the first case I am using for filling a dropdown select.
case "tutorRefresh":
$tutorSelectSql = "SELECT id, tname, tsurname FROM tutors";
$tutorSelectQry = pg_query($tutorSelectSql);
while($row = pg_fetch_array($tutorSelectQry)){
$id = lockPandoraBox($row['id']);//encrypt the id
$response[] = array("id" => $id,
"tname" => $row['tname'],
"tsurname" => $row['tsurname']);
};
if(isset($response)){
echo json_encode($response);
} else {
$response = array("val" => 0);
echo json_encode($response);
}
break;
This is the function used by the second case that updates the table data, since it is too long and complex for a single issue to post it all here, I only shared relevant part of the code. I have to match the id encrypted in above code with the one encrypted here since this code fills in the table while the code above just fills in the dropdown select.
$crypted = lockPandoraBox($row["appid"]);
$tutorID = lockPandoraBox($row["tutorid"]);//encrypting id
$clientID = lockPandoraBox($row["clientid"]);//same method for another id, ignore this.
$fApp["hours"][] = array("recId" => $crypted,
"hour" => $row["hour"],
"tutor" => $tutorArr["tname"]." ".$tutorArr["tsurname"],
"tutorId" => $tutorID,// id that I need to use
"client" => $clientArr["cname"]." ".$clientArr["csurname"],
"clientId" => $clientID,
"department" => $dept,
"degree" => $deg,
"purpose" => $purposeArr["pname"],
"purposeId" => $row["purpose"],
"contact" => $clientArr["cgsm"],
"email" => $clientArr["cemail"],
"tutorAbsCheck" => $tutorAbsArray["id"],
"tutorAbsReason" => $tutorAbsArray["reason"],
"clientAbsCheck" => $clientAbsArray["id"],
"clientAbsReason" => $clientAbsArray["reason"]
);
/* */
}
return json_encode($fApp);
}
Lastly, this is the code in my main page which works in click event function that triggers the event I need. It simply changes select box's selection for the matching clicked record. It picks the id from table and tries to match it with the id in select box. Thanks in advance.
$("#tutorEdit").val(dayData["hours"][$(el.currentTarget).attr("key")].tutorId).trigger("change");
I think it will be better to change the structure a bit to combine both cases in order to achieve my goal. I wanted to know if I could get around it.

How to KeyBy where multiple items have the same key

I am using Laravel Collections methods and am trying to key my query results (which are a collection) by the id. The problem is I have multiple entries with the same id, but point to different countries and I want to have all of the values, not just the last one.
Here is my code that i am using so far:
$allCountries = new Collection($allCountries);
$offerCountries = $allCountries->keyBy('id');
dd($offerCountries);
foreach ($offer as $o) {
$o->countries = $allCountries->get($o->id);
}
To explain, my query puts the results in $allCountries which contains ids and countries and those results looks something like this
id=>225, country=>US
id=>225, country=>IT
id=>3304, country=>NZ
Just to give you a quick idea. I want to key this by the id which results in $offerCountries. I then loop thru a previous Collection that contains offers which have a certain ID that relates to the country result by id. So for the offer 225, the countries it contains are US and IT. I loop thru each offer and set the countries object equal to all the $allCountries id that it equals. The problem I have here is keyBy overwrites the value and only takes the last one. I am hoping to get some results like this:
[
225 => countries: {'id' => 225, 'country' => 'US'}, {'id' =>
'225', 'country' => 'IT'}
3304 => ['id' => 3304, 'country' => 'NZ'],
]
Is there a laravel method to do this, or do I need to write my own keyBy so it does not overwrite. If so, how can I get started to write this method?
Thanks
Instead of using keyBy, use groupBy:
$countriesById = collect($allCountries)->groupBy('id');
You could use filter and create a custom filter
$filtered = $allCountries->filter(function ($item) use ($id) {
return $item->id == $id;
});
$filtered->all();

CakePHP: How to use Find method + AJAX request with possibly empty search parameters

I'm working with CakePHP v2.3.x and on an edit page I need to dynamically update the page with search results...
I'm making an AJAX call from one of my Views/Tests/admin_edit.php view page to a specific action in my QuestionsController.php.
Here's the action (so far) that handles the request:
public function admin_search() {
if ($this->request->is('post')) {
$searchdata = $this->request->data;
$r = $this->Question->find('all', array('conditions' => array('Question.id' => $searchdata['id'])));
echo json_encode($r);
exit;
}
}
It currently only returns questions whose IDs match the one entered by the user, but the finished version will search several different fields. I know how to do this by adding additional key/value pairs to the conditions array. However, I don't know how to make those fields optional. What if the user enters the question name, but NOT the id, or visa versa? Is there a configuration so that CakePHP will ignore any empty field conditions?
Similarly, is there a way to set the operator so that, for example, I could match substrings or integer ranges? Update: I found this in the docs.
I would just remove any empty entries yourself first.
So let's say you have a $searchdata array with three optional fields, one of which is blank. First build your conditions array:
$searchdata = array("id" => 1, "name" => "", "type" => "foo");
$conditions = array('Question.id' => $searchdata['id'], 'Question.name' => $searchdata['name'], "Question.type" => $searchdata['type']);
(Or if you want to get fancy)
foreach($searchdata AS $key => $value) $conditions['Question.' . $key] = $value;
Now clean up $conditions, get rid of empty values:
$conditions = array_filter($conditions);
Tada:
$r = $this->Question->find('all', array('conditions' => $conditions));
See http://3v4l.org/JN6PA

How to match numbers in an array in PHP

I am working on the routing or uri's in my PHP app. Currently I have an array with a regex => url map like this...
<?php
$uri_routes = array(
//users/account like http://mysite.com/users/324 (any digit)
'users/friends/page-(\d+)' => 'modules/users/friends/page-$1',
'users/friends/' => 'modules/users/friends/',
'users/online' => 'modules/users/online/' ,
'users/online/page-(\d+)' => 'modules/users/online/page-$1',
'users/create' => 'modules/users/create',
'users/settings' => 'modules/users/settings',
'users/logout(\d+)' => 'modules/users/logout',
'users/login' => 'modules/users/login',
'users/home' => 'modules/users/home',
//forums
'forums/' => 'modules/forums/index',
'forums/viewthread/(\d+)' => 'modules/forums/viewthread/$1',
'forums/viewforum/(\d+)' => 'modules/forums/viewforum/$1',
'forums/viewthread/(\d+)/page-(\d+)' => 'modules/forums/viewthread/$1/page-$2',
'forums/viewforum/(\d+)/page-(\d+)' => 'modules/forums/viewforum/$1/page-$2'
//blog routes coming soon
//mail message routes coming soon
//various other routes coming soon
);
?>
I can then cycle through my $uri_routes map array and match a uri with preg_match() like this...
<?php
//get url from URL
$uri = isset($_GET['uri']) ? $_GET['uri'] : null;
//runs our function and returns an array
// $uri['module'] this will be the class/module/section
// $uri['method'] this will be the page in that section or method in that class
// $uri['urifragments'] this will either page a user ID, or an item ID or a page number for paging
$uri = get_route($_GET['uri'],$uri_routes);
function get_route($uri,$uri_routes)
{
foreach($uri_routes as $rUri => $rRoute)
{
if(preg_match("#^{$rUri}$#Ui",$uri))
{
$uri = preg_replace("#^{$rUri}$#Ui",$rRoute,$uri);
break;
}
}
$uri = explode('/',$uri);
$return['module'] = $uri['1'];
$return['method'] = $uri['2'];
$return['urifragments'] = $uri['3'];
$return['urifragments2'] = $uri['4'];
return $return;
}
I am open to an suggestion to improve this in any way. Right now I am stuck as there is 4 possible array key/values returned. If array key 3 or key 4 contains the word "page-" followed by a number, I would like to assign it to a $page variable. But if key 3 or key 4 contains just a number with no "page-" word, then I can assume it is a user ID, blog ID, forum ID, etc and assign it to an $id variable.
If you know a good approach to this, please help.
UPDATE
to simplify things, in addition to having "page-" in front of page numbers, I could have "id-" in front of id numbers
Instead of using $1 and $2 to match our routes try using named captures.
5.2.2 Named subpatterns now accept the syntax (?) and (?'name') as
well as (?P). Previous versions
accepted only (?P).
Source : preg_match
Also when you are doing a preg_replace you use \[0-99] where \0 is the whole string and \1 through \99 are the matches.
But if you are going to be using named captures you can assign an array to the $replacement parameter with the name capture (e.g. if you capture ?P<page> then you would pass an array('page'=>"new value of page")).
Hope that helps.

Categories