I have a string being passed via an URL to a Codeigniter driven site. The string is urlencoded before being passed, then urldecoded by the controller before being passed to a function in the model that searches for the string in a database table. I've read several other posts/articles on this, but none offer a viable (for me) solution to the issue. Here are some of the things I have read:
URL having parentheses not working in codeigniter with datamapper ORM
Parenthesis issue in codeigniter url
php rawurldecode doesn't produce same string passed to rawurlencode
http://qubitlogs.com/PHP/2013/01/24/parentheses-in-urls-not-working-codeigniter-datamapper-orm/#.U0MtAce7mxa
And here is what is happening:
An url encoded string gets passed via an url, like so:
http://www.awebsite.com/controllername/functionname/test%28string%29
The controller handles this like so:
public function functionname($string) {
$this->load->model("codeigniter_model");
$dstring = urldecode($string);
$validString = $this->codeigniter_model->valid_string($dstring);
if (!$validString) {
$thiserror = "<br>Error: Invalid String. (".$dstring.")";
echo $thiserror;
exit;
}
}
And the model:
function valid_string($string)
{
$sql = "select id from dbtable where dbfield = ?";
$query = $this->db->query($sql, array($string));
//Added this in to see what the query actually end up being
echo $this->db->last_query();
if ($query->num_rows() > 0) :
return TRUE;
else:
return FALSE;
endif;
}
The echoed query is correct: select id from dbtable where dbfield = 'test(string)' and when run in Navicat returns the proper id. However - Codeigniter returns FALSE on the query.
I have also checked that the string in the query getting echoed does NOT contain HTML entities.
I have no control over the string being passed and really need Codeigniter to accept ( and ) as part of the string.
Thoughts, anyone?
EDIT: When the same string is passed to the Codeigniter controller via a post from a form, it works properly.
EDIT #2: Just tried creating the query using the Active Record method as suggested here: CAN'T ADD ANOTHER LINK SO : ellislab.com forums viewthread 162036
This:
$this->db->select('id');
$this->db->where('dbfield', "'".$string."'", FALSE);
$query = $this->db->get('dbfield');
does not work either.
EDIT #3: Thanks to Kyslik for suggesting using profiler. The string is apparently being html encoded after all: select id from users where string = 'test(string)' which, of course WOULD return false because of the HTML entities.
Unfortunately, adding code to remove those entities using both a regular query and an active record query:
$sql = "select id from dbtable where dbfield = ?";
$query = $this->db->query($sql, array(htmlspecialchars_decode($string)));
AND
$this->db->select('id');
$this->db->where('dbfield', "'".htmlspecialchars_decode ($string)."'", FALSE);
$query = $this->db->get('dbtable');
still do not work.
The answer is that htmlspecialchars_decode() does NOT decode the entities for ( and ). If I add this code to the controller before passing the string (and AFTER url decoding):
$dstring = str_replace("(", "(", $dstring);
$dstring = str_replace(")", ")", $dstring);
Then everything works as it should.
Related
I have tried to use pagination with laravel and everything works fine
until I navigate to another page, then the request doesn't preserve the previous query string and, as a result, the query doesn't work anymore.
This is the query in the controller:
$inputDate =$request->input('date') ;
$endDate = date('Y-m-d', strtotime($inputDate. ' + 7 days'));
$shifts = DB::table('driver_shifts')
->join('drivers','drivers.PermitNumber','=','driver_shifts.driver_badge_id')
->where ('driver_shifts.shift_start_time','>',$inputDate)
->where ('driver_shifts.shift_start_time','<',$endDate)
->orderby("vehicle_id")
->orderby("shift_start_time")
->paginate(20);
return view('reports.WeeklyTaxiShifts.WeeklyTaxiShifts', compact('shifts','inputDate'));
You can try to use:
$shifts = DB::table('driver_shifts')
->join('drivers','drivers.PermitNumber','=','driver_shifts.driver_badge_id')
->where ('driver_shifts.shift_start_time','>',$inputDate)
->where ('driver_shifts.shift_start_time','<',$endDate)
->orderby("vehicle_id")
->orderby("shift_start_time")
->paginate(20)
// this line will put back the query string parameters on the generated links
->appends(request()->query());
That is also explained with a blade usage example in the documentation
I've checked the Q&A about this and can't find anything, so thought i'd ask.
I have a very simple Laravel controller returning all results from a table as below via the 'Name model'. There is then also a further call to my controller, via the model to count the rows and all works and sends to the result set fine...
// All results from my 'Name' model:
$results = $this->name->getAllResults(); // All works fine.
// I then use my controller again, count the rows via the model and add them to $results:
$results['count'] = $this->countNames(); // Again fine
BUT, when i try to add a string to the $results array before i pass it off to th view, as in:
$results['test'] = 'Test'; // This fails in the view
$results['test'] = 124; // But this passes in the view and renders.
It only seems to allow me to add an INT to my result set array. as $results['test'] = 124 also fails.
I then finally, have this sending to my view via:
return view('names', compact('results')); // And that works fine.
Can anyone see what it is I am missing and why integer added to $results works and not a string?. Many thanks in advance.
You are updating collection data. The following line will give collection of models.
$results = $this->name->getAllResults(); // This may give collection of the model
And below, you are updating the collection object.
$results['count'] = $this->countNames();
You can do the following to safely send data to view, without modifying any.
$results = $this->name->getAllResults();
$count = $this->countNames();
$test = 'Test';
$test2 = 124;
return view('names', compact('results','count','test','test2'));
I am writing an update methoda for my api. I neede to update requested fields.
So ı am trying to get only requested fields and update them. However, the code below returns me null even thoug I cast $fill to string.
foreach ($fillableFields as $fill){
$customerId->$fill = $request->get("$fill") ;
edit- 1
My code is like below ;
$fillableFields = array('lastname','firstname');
when I dd($fill) it returns me null .
You forgot to call the save() method to update your object :
foreach ($fillableFields as $fill){
$customerId->$fill = $request->get($fill);
$customerId->save();
}
Your method should be.
public function yourMethod(Request $request)
{
try {
foreach ($fillableFields as $fill){
$customerId->$fill = $request->get($fill) ;
}
}
} catch (\Exception $ex) {
}
}
Best way to get request parameters is provided at laravel's official documentation as below.
You can get request parameter based on your specific need.
By using below statment you can get only those perameter you have specified in only attribute as array eg : username, password.
$input = $request->only(['username', 'password']);
If you wantto get all fields excluding particular field than you can define as below with except parameter. eg: by using below it will allow to get all the fields except credit_card.
$input = $request->except('credit_card');
For more information on request parameter please refer official doc url : https://laravel.com/docs/5.4/requests
I am trying to load a page dynamically based on the database results however I have no idea how to implement this into codeigniter.
I have got a controller:
function history()
{
//here is code that gets all rows in database where uid = myid
}
Now in the view for this controller I would like to have a link for each of these rows that will open say website.com/page/history?fid=myuniquestring however where I am getting is stuck is how exactly I can load up this page and have the controller get the string. And then do a database query and load a different view if the string exsists, and also retrieve that string.
So something like:
function history$somestring()
{
if($somestring){
//I will load a different view and pass $somestring into it
} else {
//here is code that gets all rows in database where uid = myid
}
}
What I don't understand is how I can detect if $somestring is at the end of the url for this controller and then be able to work with it if it exists.
Any help/advice greatly appreciated.
For example, if your url is :
http://base_url/controller/history/1
Say, 1 be the id, then you retrieve the id as follows:
function history(){
if( $this->uri->segment(3) ){ #if you get an id in the third segment of the url
// load your page here
$id = $this->uri->segment(3); #get the id from the url and load the page
}else{
//here is code that gets all rows in database where uid = myid and load the listing view
}
}
You should generate urls like website.com/page/history/myuniquestring and then declare controller action as:
function history($somestring)
{
if($somestring){
//I will load a different view and pass $somestring into it
} else {
//here is code that gets all rows in database where uid = myid
}
}
There are a lot of ways you can just expect this from your URI segments, I'm going to give a very generic example. Below, we have a controller function that takes two optional arguments from the given URI, a string, and an ID:
public function history($string = NULL, $uid = NULL)
{
$viewData = array('uid' => NULL, 'string' => NULL);
$viewName = 'default';
if ($string !== NULL) {
$vieData['string'] = $string;
$viewName = 'test_one';
}
if ($uid !== NULL) {
$viewData['uid'] = $uid;
}
$this->load->view($viewName, $viewData);
}
The actual URL would be something like:
example.com/history/somestring/123
You then know clearly both in your controller and view which, if any were set (perhaps you need to load a model and do a query if a string is passed, etc.
You could also do this in an if / else if / else block if that made more sense, I couldn't quite tell what you were trying to put together from your example. Just be careful to deal with none, one or both values being passed.
The more efficient version of that function is:
public function history($string = NULL, $uid = NULL)
{
if ($string !== NULL):
$viewName = 'test_one';
// load a model? do a query?
else:
$viewName = 'default';
endif;
// Make sure to also deal with neither being set - this is just example code
$this->load->view($viewName, array('string' => $string, 'uid' => $uid));
}
The expanded version just does a simpler job at illustrating how segments work. You can also examine the given URI directly using the CI URI Class (segment() being the most common method). Using that to see if a given segment was passed, you don't have to set default arguments in the controller method.
As I said, a bunch of ways of going about it :)
I'm working with the latest codeIgniter released, and i'm also working with jquery datatables from datatables.net
I've written this function: https://gist.github.com/4478424 which, as is works fine. Except when I filter by using the text box typing something in. The filter itself happens, but my count is completely off.
I tried to add in $res = $this->db->count_all_results() before my get, and it stops the get from working at all. What I need to accomplish, if ($data['sSearch'] != '') then to utilize the entire query without the limit to see how many total rows with the search filter exists.
If you need to see any other code other than whats in my gist, just ask and I will go ahead and post it.
$this->db->count_all_results() replaces $this->db->get() in a database call.
I.E. you can call either count_all_results() or get(), but not both.
You need to do two seperate active record calls. One to assign the results #, and one to get the actual results.
Something like this for the count:
$this->db->select('id');
$this->db->from('table');
$this->db->where($your_conditions);
$num_results = $this->db->count_all_results();
And for the actual query (which you should already have):
$this->db->select($your_columns);
$this->db->from('table');
$this->db->where($your_conditions);
$this->db->limit($limit);
$query = $this->db->get();
Have you read up on https://www.codeigniter.com/userguide2/database/active_record.html#caching ?
I see you are trying to do some pagination where you need the "real" total results and at the same time limiting.
This is my practice in most of my codes I do in CI.
$this->db->start_cache();
// All your conditions without limit
$this->db->from();
$this->db->where(); // and etc...
$this->db->stop_cache();
$total_rows = $this->db->count_all_results(); // This will get the real total rows
// Limit the rows now so to return per page result
$this->db->limit($per_page, $offset);
$result = $this->db->get();
return array(
'total_rows' => $total_rows,
'result' => $result,
); // Return this back to the controller.
I typed the codes above without testing but it should work something like this. I do this in all of my projects.
You dont actually have to have the from either, you can include the table name in the count_all_results like so.
$this->db->count_all_results('table_name');
Count first with no_reset_flag.
$this->db->count_all_results('', FALSE);
$rows = $this->db->get()->result_array();
system/database/DB_query_builder.php
public function count_all_results($table = '', $reset = TRUE) { ... }
The
$this->db->count_all_results();
actually replaces the:
$this->db->get();
So you can't actually have both.
If you want to do have both get and to calculate the num rows at the same query you can easily do this:
$this->db->from(....);
$this->db->where(....);
$db_results = $this->get();
$results = $db_results->result();
$num_rows = $db_results->num_rows();
Try this
/**
* #param $column_name : Use In Choosing Column name
* #param $where : Use In Condition Statement
* #param $table_name : Name of Database Table
* Description : Count all results
*/
function count_all_results($column_name = array(),$where=array(), $table_name = array())
{
$this->db->select($column_name);
// If Where is not NULL
if(!empty($where) && count($where) > 0 )
{
$this->db->where($where);
}
// Return Count Column
return $this->db->count_all_results($table_name[0]);//table_name array sub 0
}
Then Simple Call the Method
Like this
$this->my_model->count_all_results(['column_name'],['where'],['table name']);
If your queries contain a group by, using count_all_results fails. I wrote a simple method to work around this. The key to preventing writing your queries twice is to put them all inside a private method that can be called twice. Here is some sample code:
class Report extends CI_Model {
...
public function get($page=0){
$this->_complex_query();
$this->db->limit($this->results_per_page, $page*$this->results_per_page);
$sales = $this->db->get()->result(); //no table needed in get()
$this->_complex_query();
$num_results = $this->_count_results();
$num_pages = ceil($num_results/$this->results_per_page);
//return data to your controller
}
private function _complex_query(){
$this->db->where('a', $value);
$this->db->join('(subquery) as s', 's.id = table.s_id');
$this->db->group_by('table.column_a');
$this->db->from('table'); //crucial - we specify all tables here
}
private function _count_results(){
$query = $this->db->get_compiled_select();
$count_query = "SELECT count(*) as num_rows FROM (".$query.") count_wrap";
$r = $this->db->query($count_query)->row();
return $r->num_rows;
}
}