I've been developing a site over the past few weeks using CodeIgniter as the framework. I've been thinking of the best way to accomplish something, which in a lot of other frameworks in other languages is relatively simple: sortable tables. CodeIgniter switches off query strings by default, because your URLs contain method parameters. So a URL might look like:
/controller/method/param1/param2
You might think that you could just add in sortBy and sortOrder as two additional parameters to the controller method. I don't particularly want to do that, mainly because I want to have a re-usable controller. When you use query string parameters, PHP can easily tell you whether there is a parameter called sortBy. However, when you're using URL based parameters, it will vary with each controller.
I was wondering what my options were. As far as I can see they are something like:
Pass in my sortBy and sortOrder parameters, just suck it up, and develop some less-than-reusable component for it.
Have an additional controller, which will store the sortBy and sortOrder in the session (although it would have to know where you came from, and send you back to the original page).
Have some kind of AJAX function, which would call the controller above; then reload the page.
Hack CodeIgniter to turn query strings back on. Actually, if this is the only option, any links to how to do this would be appreciated.
I just can't quite believe such a simple task would present such a problem! Am I missing something? Does anyone have any recommendations?
While I love jQuery, and I'm already using it on the site, so TableSorter is a good option. However, I would like to do server-side sorting as there are some pages with potentially large numbers of results, including pagination.
If you're OK with sorting on the client side, the Tablesorter plugin for jQuery is pretty nice.
I ran into this with a fairly complex table. The hard part was that the table could grow/shrink depending on certain variables!! Big pain :(
Here's how I handled it..
Adjusted system/application/config/config.php to allow the comma character in the URI:
$config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-,';
Adjust my controller with a sorting function:
function sorter() {
//get the sort params
$sort = explode(",",$this->uri->segment(3)); //the 3rd segment is the column/order
//pass the params to the model
$data = $this->model_name->get_the_data($sort[0],$sort[1]);
$this->_show($data);
}
function _show($data) {
//all the code for displaying your table
}
I've oversimplified, but you get the idea. The purpose is to have a url like this:
/controller/sorter/columnname,sortorder
The sorter function calls another internal function to deal with the display/template/view logic - it's job is to deal with the sorting call and get the appropriate data from the model.
Of course, this could be reduced to just your current function:
function showGrid() {
$sort = $this->uri->segment(3);
if ($sort) {
//get the data sorted
} else {
//get the data the default way
}
//rest of your view logic
}
That way, you don't even need a separate function - and can use the third segment to define your sorting.
I recently added this Table sorter (which uses Prototype) to a bunch of my pages. It's fast and pretty easy to implement.
Related
is it possible in php to create a single url from multiple values across multiple tables?
For example, I want to generate a url that looks like the following:
http://www.domain.foo/joseph&soap&engineer&english, with the first name and surname is read from the NAMES table and occupation is read from the JOBS table and language is read from the SPEAKS table..?
Not sure if this is yet the correct way to solve my task, but thought I'd ask the question before I spend more time on it.
That isn't the way url parameters work. They should be name value pairs. You are also missing the query string. So what a normal url would look like would be this:
http://www.domain.foo/index.php?firstname=joseph&surname=soap&job=engineer&language=english
This is how you should be thinking about and approaching the use of the GET method and the passing of url parameters. Often this gets confused with URL rewriting, which converts url's that omit script names and parameters but you should start with the basic functionality and worry about layering on url rewriting once your code is working.
The other thing you might be missing is the "routing" that comes with pretty much every framework that implements the MVC pattern.
For example, consider route to be an additional parameter that gets passed as route=section_of_site. In your example, it seems that this function is going to look up information on a user or person, so perhaps it would be route=person
So now your full url would be:
http://www.domain.foo/index.php?route=person&firstname=joseph&surname=soap&job=engineer&language=english
Even without a framework or controller class, you now have a route variable which you could use to include a script, or to execute a function or class function. You have all the variables you need in $_GET, and all that is left is to do whatever data persistence work you require and return your result.
I am using CodeIgniter but this question applies in a general sense too.
I have a table of transactions with columns
item_name | type | date | price | document
I want to do the following in two completely independent cases.
1) Get a list of transactions within a certain date range.
2) Get the total price of each transaction.type within a certain date range.
The former can be achieved by simply using a select statement with > datetimestamp
The latter can be achieved by selecting the SUM, and grouping by the type whilst like implementing any required where conditionals e.g with > datetimestamp
Although a simple case, to achieve this I need to have two methods however the bulk of both of these methods (namely the WHERE clauses) are duplicated across both methods.
In terms of speed etc it does not matter but it seems like pointless code reproduction.
A second example is as follows.
I previously had a method get_data($ID) which would get a row from a table based on the ID passed in.
As such in a separate method I would get my 100 items for example.. return an array, loop through them and call get_data for each.
This setup meant that many different methods could get different lists from different sources and then still use the same get_data function and a loop to get the required data.
This minimized code duplication but was incredibly ineffiecient as it meant looping through loads of data items and hundreds of db queries.
In my current setup i just join the data table in each of my methods - code duplication but clear improved efficiency.
A final example is as follows
In codeigniter I can have a function such as the following:
get_thing($ID)
{
$this->load->database();
$this->db->where('ID',$ID);
$this->db->get('table');
}
BUT in alternate situations i might want to only get items in a specific folder.. as such making the function more generic works better.. e.g.
get_thing($array)
{
$this->load->database();
$this->db->where($array);
$this->db->get('table');
}
but then I might want to use this function in two different contexts e.g a user page and an admin page whereby admins can see all items, even unverified ones. My code now becomes:
get_thing($array,$show_unverified = false)
{
$this->load->database();
$this->db->where($array);
if($show_unverified == false)
{
$this->db->where('verified','YES');
}
$this->db->get('table');
}
As you can probably see this can quickly get out of hand and methods can become overly complex, confusing and full of conditionals.
My question is as follows - What are best practices for minimizing code duplication, and how could they be applied to the above situations? I spent hours and hours trying to make my code more efficient yet I'm getting nowhere because I cant workout what I should really be trying to achieve.
Cheers
My idea on code duplication in database access functions is that it is often better to keep it separate.
My rule here is especially that a function should not return different kinds of data depending on the parameter, for example it should not return a single user sometimes and other times an array of users. It may return error codes (false) though.
It is ok though if the function implements different access levels, which are shared across several pages.
This basically always comes back to common sense. You should try to minimize duplicate code and try to reduce complexity within single function. Keep them small and simple.
So basically everytime you try to generalize a function like this you would have to ask if the problem of duplicate code is bigger than the problem of overly complex functions.
In this case i would stop at your second point and next you could create some wrappers for the most common tasks (but be carefull not to make a maze of wrappers)
//you generic function
function get_thing($array)
{
$this->load->database();
$this->db->where($array);
$this->db->get('table');
}
// a nice and friendly wrapper
function get_thing_by_id($id)
{
get_thing(array('id' => $id));
}
// this is just getting silly. don't go crazy with wrappers, only for very often used things.
// and yes the function name is purposely crazy ;)
function get_thing_verified_by_name_and_city_and_some_more($name, $city, $somethingElse)
{
get_thing(array('name' => $name, 'city' => $city, 'somethingelse' => $somethingElse));
}
This answers the first part of your question. Assuming you're using mysql_fetch_assoc or similar. As you're iterating over the result sets you could store count values in a variable in the loop for the total price of each transaction type.
The second part as long as you're not repeating code ad infinitum which would cause you issues down the line when maintaining the code base, it's OK. For your function you could always test the type of variable being passed to the function and set conditional behaviours accordingly.
Have a look at the factory pattern or strategy pattern relating to software design patterns for further insight.
This is a long running question that gets me every time I am developing.
I suppose it is not specific to CodeIgniter, but as I am using it, I will consider it in my example.
Firstly, which is better:
function add_entry($data_array)
{
//code to add entry
}
function edit_entry($data_array)
{
//code to update entry
}
OR
function save_changes($what,$data_array)
{
//if what == update update
//otherwise insert
}
Both produce the same action, but does it really matter which one you use?
Getting onto more complicated things.
I have a page where I need to get ONE entry from the database.
I also have a page where I need to get all the entries from the same database ordered by a user specified column.
My resultant method is a function similar to
function($data_array,$order_by='',$limit='')
{
//get where $data_array
//if order_by!='' add order by
//if limit !='' add limit
}
As I develop my application and realise new places where I need 'similar' database functionality I am what feels like hacking previous methods so they work with all my case scenarios. The methods end up containing lots of conditional statements, and getting quite complex with in some cases 4 or 5 input parameters.
Have I missed the point? I don't want duplicate code, and when for the most part the functions are very similar I feel like this 'hacking' methodology works best.
Could someone advise?
Finally my admin functionality is part of the same application in an admin controller. I have an admin model which contains specific methods for admin db interaction. I however use some model functionality from 'user' models.
FOr example if on an admin page I need to get details of a db entry I may load the user model to access this function. There is nothing wrong/insecure about this..? right?
In addition to that within my admin model itself I need to get data about a user database entry so I call my user model directly from my admin model. This is strictly OK, but why? If i need data and there is already a method in my user model which gets it.. it seems a little pointless to rewrite the code in the admin model BUT each time that function is called does it load the whole user model again?
Thanks a lot all.
In order, add edit in the model vs save. Personally I have a save built in MY_Model that chooses whether it is a save or an edit depending on the existence of a primary key in the data being passed, so obviously I prefer that method it means a lot less duplication of code since I can use the save for any table without having functions in the model at all.
As to the second question I think it depends on situation. I also have a number of functions that have a ton of conditionals on them depending on where they're used and for what. In some cases I'm finding this makes the legibility of the code a little rough. If you're running them all through if statements it also could be impacting performance. DRY is a concept, not a rule and like other design concepts there are times when they just don't make sense, it's like database normalization, it's my personal opinion it's VERY easy to over normalize a database and destroy performance and usability.
Finally, using user functions in the admin code. I don't see an issue here at all, the reverse probably isn't true, but rewriting a function just because it's an "admin" function, when it's identical to a user function is utterly pointless. So you're correct there, it's a waste of time and space.
Is there a codeigniter plugin that allows me to quickly create find by functions without writing code on every field in a db table?
I find myself writing a lot of functions for tables such as findbyid findbyfirstname findbyemail and so on, any libraries already written to speedup my dev time? i tried googling but i havent come across any.
If you mean you have to write multiple methods in your model to find rows in a table by a specific field, you could just pass an associative array containing the fields and values you want to search to a generic function - something like:
function search_mytable($search=array()) {
$this->db->select('mytable.*');
$this->db->from('mytable');
if(!empty($search)
$this->db->where($search);
}
There's more information about what you can pass the CI active record where method here http://codeigniter.com/user_guide/database/active_record.html#select
If it's just simple data retrieval, you can just do something like this:
function find($column, $value)
{
$this->db->where($column, $value);
//etc
}
for simple queries. As BrynJ suggests, the Active Record class is rather flexible when it comes to taking parameters.
With simple queries I'm able to produce pages with nice pagination links. However, if the query is generated through some sort of search filter, I'm not sure how to pass the $_POST data from page to page.
Usually I can do this ... www.domain.com/search.php?id=200&type=host&rack=3&os=redhat%5
However, with CI's URI library, I'm using "pretty urls," hence my URL is more like www.domain.com/search/page/1 Appending the rest of the variables doesn't make sense nor will I think it will work. Any ideas on how to tackle this issue?
Why don't you just store the variables you need in a user session? Here is some information about how to use a session to store information about a user with codeigniter:
http://codeigniter.com/user_guide/libraries/sessions.html
Format your URI like so:
index.php/controller/method/id/200/type/host/rack/3/os=redhat%5/page/2
You can then use $this->uri->uri_to_assoc(n) to turn that URI into key-value pairs. See:
http://codeigniter.com/user_guide/libraries/uri.html
Please have a look the link below. This article show you have to pass parameters on pagination without turn on enable query string settings.
http://pujanpiya.com.np/?q=node/37
Hope this help!
If your using the built in pagination class, just make a separate search method on your controller and provide it with the pagination info...
FYI, there is a great jQuery Plugin that takes care of alot of table related work for you. Datatables.net
I just wrote a library that handles search, pagination, sorting and limiting.. Check it out here.
http://datatables.dyndns-web.com/