This walk-through will show you how to implement a DataMapper paginated search in CodeIgniter using an HTTP POST request. Why would we do this? The ridiculous restrictions introduced by CI’s configuration variable permitted_uri_chars that narrows the field of characters allowed in a URI.

CodeIgniter Routing is Opinionated

For web page requests, CodeIgniter supports SEO-friendly URI segments where controller and view names compose the full path. e.g., http://yourdomain.com/products/search/ will route to the Products controller class and its action method search(). Parameter values too can be used on the URI, such as http://yourdomain.com/products/view/1/ with 1 passed into the action method. e.g., public function view($product_id). Unfortunately for user-generated values, you’ll run into the below configuration line:

config/config.php

With segments enabled, CodeIgniter makes the URI even more restrictive than RFC 2396. Using a segment value not matching the above regex pattern, CodeIgniter will instead prompt users with error message:

The URI you submitted has disallowed characters.

This has implications when implementing search functionality for your web site. Using a HTTP GET request, the CodeIgniter URL helper replaces spaces with an underscore and you’ll have to use JavaScript to strip out characters outside the range of permitted_uri_chars. But what if you want to support additional characters in an input? For example, with the above regex configured, you cannot search for email addresses since the ‘@’ symbol isn’t allowed.

SEO-friendly DataMapper Pagination

As an EllisLab forum post permitted_uri_chars and $_GET points out, you can’t bookmark search results generated by a HTTP POST request. If this is a concern and you wish to still keep SEO-friendly URI segments for other parts of your web site, you may want to research how to enable HTTP GET query strings for only controller actions involving search. The below configuration will ensure CodeIgniter’s core won’t reset the PHP $_GET variable, however it affects all controllers.

config/config.php

With this setup, you’ll have to create your own routing solution. The reason to use a lightweight framework like CodeIgniter is for rapid development with minimal scaffolding. When a customized paginated search results on a public-facing web site, you may want to have a think about using Symfony 2 or a more modern framework that support more advanced routing without superficial limitations.

The POST Workaround

Instead, pagination links will trigger JavaScript that forms a POST request to request a different offset in the search results. This isn’t an ideal solution but if it’s an internal admin tool you’re developing, SEO is not a concern. It’s also against the grain with the HTTP standard since POST is considered a destructive event (i.e., data is modified) however this is a read-only request more appropriate for GET. However when you’re working with a legacy codebase infiltrated with technical debt, sometimes you just have to shut up and get on with it. That’s the attitude!

For an introduction to CodeIgniter’s pagination library, have a look at this very simple CodeIgniter and DataMapper pagination example (this blog is no longer online, however the Wayback Machine came to the rescue.) Page links are generated by invoking a method call on the pagination library’s instance, as long as you populate its properties correctly after calling DataMapper’s get_paged() method.

application/models/Item.sql

application/models/Item.php

The above model describes the items we will search on and paginate through. It’s a simple OR search that returns any partial match with no consideration for weighing results. You want to look into running an Apache Solr service if more advanced functionality is required.

For this bare-bones search engine implementation, below is the MVC structure and additional JavaScript required:

application/controllers/Site.php

application/views/site.php

application/views/search_results.php

/assets/js/search_results.js

DataMapper Pagination with AJAX

Want to use AJAX to refresh paginated results inline? Instead of page links triggering a vanilla POST form request, they’ll invoke AJAX through POST. For the sake of brievity, I’ll make the AJAX request return HTML rather than dealing with JSON and client-side templating. Keep in mind DataMapper does offer easy to_json() and all_to_json() methods that could be used on the $items result. And Apache Solr I mentioned earlier? It also returns JSON.

In our controller’s search() method, we’ll modify variables passed to the view template to include a boolean on whether the current request is by AJAX. This flag allows pagination links to behave differently.

application/controllers/Site.php

application/views/site.php

application/views/search_results.php

/assets/js/ItemSearch.js

/assets/js/search_results_ajax.js

I rate the above solution fairly high (or low?) as the hackiest solutions I’ve ever jerry-rigged into a bloated codebase built upon the wrong technology. (Multi-site e-commerce in CodeIgniter without a service-orientated architecture? Wat.) Truth is, if you’re implementing search on your own then get Apache Solr involved as many people are much smarter and better resourced than you. True story.