Mailtrap vs Mandrill: sending emails in development

After a long time using Gmail SMTP as my main service for sending email in development environment. I switched to Mailtrap and Mandrill for some recent projects. This post will introduce my experiment when working with these two awesome mailing services.

Mailtrap.io

Mailtrap is a fake SMTP server for development teams to test, view and share emails sent from the development and staging environments without spamming real customers.

As its slogan, Mailtrap focuses to non-production environment. It “traps” all email sending services from your application and display these email in a virtual inbox. You can preview all your sent emails without caring about the recipient addresses exist or not. If you use real email addresses (in staging server for example), there will be no real email sent to them, too, which means there is no spam to your customers.

Mailtrap virtual inbox with a spam analysis
Mailtrap virtual inbox with a spam analysis

You can create many inbox  and use different inbox in different enviroments. However, the free plan of Mailtrap support only one, and a maximum of 50 emails inside the inbox. When this inbox is full, you can continue trapping your emails by clear all old ones.

You can see many useful information about each email inside the inbox via detail panel:

  • The address of sender and recipient as well as the sending time
  • The rendered HTML email, HTML source
  • The text email if available
  • The spam analysis to make sure your email is not recognized at spam by other machines
  • The result of email HTML validation in many email clients

Mandrill

Mandrill is a reliable, scalable, and secure delivery API for transactional emails from websites and applications. It’s ideal for sending data-driven transactional emails, including targeted e-commerce and personalized one-to-one messages.

Different from Mailtrap, Mandrill is a service allowing sending real email even in production environment. To prevent your customer from receiving testing email when develop your application, you can switch to test mode of your Mandrill account. In this mode, Mandrill works like Mailtrap: not sending any emails, just show them in its reports with the email content.

Mandrill outbound report in test mode
Mandrill outbound report in test mode

You can see basic information about your email inside the Outbound report of Mandrill:

  • Email sender, recipient and delivering time
  • Email subject
  • Email content: HTML and text (via another popup window)

Comparision

Feature Mailtrap.io Mandrill
Use in production No Yes
Free tier Yes Yes
Trapping emails Yes (default) Yes (After turn test mode on)
Email information
  • Sender
  • Recipient
  • “Delivery” time
  • Subject
  • email HTML
  • email text
  • Sender
  • Recipient
  • “Delivery” time
  • Subject
  • email HTML
  • email text
Extra information
  • HTML validation in many email clients
  • Span analysis report
  • Using email templates in the account
  • Statistic about email opening and clicking
Support SMTP Yes Yes (real mode only)
Support REST api Yes Yes (required when using test mode)
Separate emails in environments By sending email to different virtual inbox By adding tags into STMP headers or REST payload.
Refresh time Instant With a little delay

My favorite one

I cannot tell you which service is better. Each one has its own pros and cons. In my personal project, I use Mandrill for production environment because it awesome delivery feature. In development servers, I use Mailtrap to analyze my emails and preview the result quickly. I hope that after reading my reviews, you can have another inspiration for sending emails in your application.

 

Solve the Akismet problem with CloudFlare SSL

Askimet over SSL

Today, I’ve spent time on diving in the solve the problem when using Akismet anti-spam plugin over the HTTPS with the setup using CloudFlare flexible SSL.

Problems

Akismet is one of most common anti spam plugin used in WordPress. I’ve used this plugin for years and really been satisfied with it. The problem occurs when I put this blog after the CloudFlare CND services and enable its flexible SSL support.

The first thing I’ve tried is install the CloudFlare flexible SSL plugin for WordPress. After that, most of problems was resolved but one. My Firefox still gives me a notification that their are some mixed content on my site, which means some of my resources were loaded from unsecured  protocol. I opened the web console and realized that the Akismet js file is still hosted over non-SSL domain.

[blocked] The page at ‘https://www.hieule.info/’ was loaded over HTTPS, but ran insecure content from ‘http://blog-static.hieule.info/wp-content/plugins/akismet/_inc/form.js’: this content should also be loaded over HTTPS.

I tried to install many HTTPS forcing plugins from WordPress repository but nothing helps.

The reason

The reason why the CloudFlare flexible SSL plugin works for other resources but the Akismet ones is the order WordPress loads its plugins. The CMS sort active plugins by their names before saving into the database and load the plugin using that order. Therefore, the Akismet was loaded before the CloudFlare flexible SSL, and all of Akismet resources was still using the non-SSL domain.

My working solution

The most easy way is install a WordPress plugin to customize the loading order of the CMS active plugins. I can find this plugin and confirmed that it works correctly. However, I do not want to install too much plugin into my WordPress installation. I tried to change the order of active plugin right before they are saved to the database. Fortunately, WordPress support the active_plugins hook called right there. I add these lines of code into my functions.php in my theme:

function pureclean_sort_plugins() {
    $active_plugins = get_option('active_plugins');
    $this_plugin = 'cloudflare-flexible-ssl';
    $this_plugin_key = array_search($this_plugin, $active_plugins);
    if ($this_plugin_key) { // if it's 0 it's the first plugin already, no need to continue
        array_splice($active_plugins, $this_plugin_key, 1);
        array_unshift($active_plugins, $this_plugin);
        update_option('active_plugins', $active_plugins);
    }
}
add_action("activated_plugin", "pureclean_sort_plugins");

Finally, deactivate a plugin and reactive it to make these code work (don’t forget this step). The problem was resolved.

References:

Laravel 4 life cycle: Request, Route and Router object

Today, I’ll share you about one of my experiment when working with Laravel 4 application: distinguish Request, Route, and Router object of the application instance. All these object play a part in the routing phase, the step that decide which method of which controller will be call on each time the Laravel application run.

The Request object

A Request object is mapped from the raw HTTP request that received from web server. There is no information about “controller” or “method” stored in it. This object is created in two ways in Laravel:

  • Created directly from PHP superglobal when the application is booted by web server. This is most common situation when a user reach your application from an URL.
  • Created manually when runnin a unit test

In this article, I’ll talk about the Request object that created by first method. This object contains information about:

  • All parameters from $_GET
  • All parameters from $_POST
  • All cookie information from $_COOKIE
  • All uploaded file information from $_FILES
  • All server information from $_SERVER

The Request object is the first object that created in the application life cycle among these three. It is created inside the constructor of the Application  class.

 The Route object

A route is a 3-tuple of these information:

  • Which HTTP method is currently handled (GET, POST, PUT or DELETE, …)
  • Which URI is currently served (/foo, /bar/1, or /foo/1/bar, …)
  • Which action will be executed. An action is whether a closure object or a method of some controller, both will return a response in the end (an HTML page or a JSON content, …)

A route may contains a name defined in the routes.php  file (and any files that included by that file). Without this object, Laravel cannot know how to continue serving the current request because it cannot decide which is the correct action to do (the C part in MVC definition). The Route object is the last created object among these three

The Router object

As a result, a question is asked: “How is the route object created?”. Well, the answer is in this section: the Router object. It is created independently with the Request object via the RoutingServiceProvider which is called after the application is created (and after the Request object is created). This object keep the reference to the current Request object and the matched Route object that matched with defined rules.

When created, the Router object:

  • creates a collection of registered routes
  • allows new route registered inside routes.php
  • look up it registered route collection to choose the correct route with the current request

So, we can think this object as a link between the HTTP request and the Laravel controller.

Conclusion

Each of three objects keep some information that may be useful in different situation. In my experience:

  • When you want to known information about the HTTP request (server, cookie, get and post params, … ), use the Request object
  • When you want to known information about the current action (controller, method, closures, …), use the Route object
  • If you does not decide what information will be get, use the Router object which can access both of above. One of my package can be used as an example for this situation.

Happy coding!

 

HTML Checkbox enhancement: iCheck and Shift-Click

iCheck plug-in

When other HTML form elements can be formatting so easily, checkboxes and radio buttons need some javascript to be more beautiful. iCheck is a jQuery plug-in that:

Highly customizable checkboxes and radio buttons for jQuery and Zepto.
Damir Sultanov – iCheck author

You can see how checkboxes and radio buttons are formatted by iCheck in the image below

Checkboex and radio buttons when formatted with iChekc plug in
Checkboxes and radio buttons when formatted with iChekc plug in

Selecting multiples checkboxes in a range with Shift-Click

When the number of checkboxes increases, the time it consumes to check or uncheck these checkboxes grows. Users will feel unhappy if he must click 10 times just to check 10 consecutive checkboxes. In many desktop applications, this task is not a problem because users can Shift-Click and just touch the first checkboxes and the last one. In the end of this post, you will know how I implement this behavior in Web screens. The final result will something looks like this JS Bin:

Working with javascript

You can choose with tab to view in the previous demo frame by click the corresponding buttons in the top of the frame

Init the iCheck plugin

First, include iCheck javascript and CSS files after loading jQuery.

<head>
    <link rel="stylesheet" href="https://www.hieule.info/source/iCheck-1.0.2/skins/minimal/_all.css" />
    <script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
    <script src="https://www.hieule.info/source/iCheck-1.0.2/icheck.min.js" type="text/javascript"></script>
</head>

And call this init code to format all of your checkboxes

$(document).ready(function(){
  // Init checkboxes with iCheck
  $('input').iCheck({
    checkboxClass: 'icheckbox_minimal-blue',
    radioClass: 'iradio_minimal'
  });
  // Next step code here
});

After this step, you must see all of your checkboxes appear in a different style than the default one.

Handle the click  event

Instead of handling this event on the input element itself, we binding your handler on the outter div that created by iCheck

/* This code is inside the $(document).ready() */
  // Binding `click` event into iCheck inputs
  $('#checkboxes .iCheck-helper').click(function(evt) {
    var $this = $(this);
    var element = $this.prev('input[type="checkbox"]');
    var parent = $this.parents('.control');
    if (element.length)
    {
      // Next step code here
    }
  });

I create variables to hold the reference to two objects: the real input element the the container element that repeated in the form (in this examples, you can see the div.control element is the container of each input). Moreover, when handling the click event, javascript allow us to know whether the Shift key is press at the same time with the click by supporting the shiftKey property.

Manipulate the click event

Now, my code works as follows:

  1. Detect whether this is a normal click or a Shift-Click
  2. If this is a normal click, record the current zero-based index of the clicked checkboxes in the checkboxes array
  3. If this is a Shift-Click, find the previous clicked checkboxes by the saved index. If nothing found, treat this event as normal. Otherwise, copy the state of the previous clicked one to all checkboxes between this current target and the previous one (including these both)
if (evt.shiftKey)
{
  if (lastCheckedCheckboxIndex >= 0)
  {
    var isChecked = $('#checkboxes input[type="checkbox"]:nth(' + lastCheckedCheckboxIndex + ')')[0].checked ? 'check' : 'uncheck';
    $('#checkboxes input[type="checkbox"]').each(function(i, e) {
      if ((i - pIndex) * (i - lastCheckedCheckboxIndex) <= 0)
      {
        // this checkbox is in the range
        $(e).iCheck(isChecked);
      }
    });
  }
}
else
{
  lastCheckedCheckboxIndex = pIndex;
}

 In the end…

If you are not using the iCheck plug-in, the click handler can be registered directly on the input element it self. It’s rumored that iCheck author will support this event on the 2.0 release. I think I will refactor this code when this happens. You can preview the code of this tutorial by the frame at the second section or go to this lik: https://jsbin.com/lobowe/6/edit

 

PHP – convert Vietnamese character between VNI and Unicode

Why I need it?

VNI here stands for VNI Encoding, a way to store Vietnamese characters without using Unicode in 1990s ans 2000s. In fact, many Vietnamese old documents used it instead of Unicode. When working with a new web project, a client requires me to build their website with Unicode (of course!) but the articles is imported from a bundle of MS Word documents which are used VNI Encoding. Therefore, I need to find a function to convert these character into Unicode standard before save them to the database.

The code

Yep, first of all, this is my code:

/**
 * Return unicode char by its code
 */
function unichr($u) {
    return mb_convert_encoding('&#' . intval($u) . ';', 'UTF-8', 'HTML-ENTITIES');
}

/**
 * Convert from VNI to Unicode
 */
function vni2uni($vniInput)
{

	$unicode = array(7845, 7847, 7849, 7851, 7853, 226, 7843, 227, 7841, 7855, 7857, 7859,	                         	
                                7861, 7863, 259, 250, 249, 7911, 361, 7909, 7913, 7915, 7917, 7919, 7921, 432, 
			        7871, 7873, 7875, 7877, 7879, 234, 233, 232, 7867, 7869, 7865, 7889, 7891, 7893, 
                                7895, 7897, 7887, 245, 7885, 7899, 7901, 7903, 7905, 7907, 417, 
                                237, 236, 7881, 297, 7883, 253, 7923, 7927, 7929, 7925, 273, 7844, 7846, 7848,
                                7850, 7852, 194, 7842, 195, 7840, 7854, 7856, 7858, 7860, 7862, 258, 
                                218, 217, 7910, 360, 7908, 7912, 7914, 7916, 7918, 7920, 431, 7870, 7872, 7874,
                                7876, 7878, 202, 201, 200, 7866, 7868, 7864, 7888, 7890, 7892, 7894, 7896,
                                7886, 213, 7884, 7898, 7900, 7902, 7904, 7906, 416, 205, 204, 7880, 296,
                                7882, 221, 7922, 7926, 7928, 7924, 272, 225, 224, 244, 243, 242, 193, 192, 212, 211, 210);
	$vniChars = array("aá", "aà", "aå", "aã", "aä", "aâ", "aû", "aõ", "aï", "aé", "aè",
                            "aú", "aü", "aë", "aê", "uù", "uø", "uû", "uõ", "uï", "öù", "öø", "öû", "öõ",
                            "öï", "ö", "eá", "eà", "eå", "eã", "eä", "eâ", "eù", "eø", "eû", "eõ", "eï",
                            "oá", "oà", "oå", "oã", "oä", "oû", "oõ", "oï", "ôù", "ôø",
                            "ôû", "ôõ", "ôï", "ô", "í", "ì", "æ", "ó", "ò", "yù", "yø", "yû", "yõ", "î",
                            "ñ", "AÁ", "AÀ", "AÅ", "AÃ", "AÄ", "AÂ", "AÛ", "AÕ", 
                            "AÏ", "AÉ", "AÈ", "AÚ", "AÜ", "AË", "AÊ", "UÙ", "UØ", "UÛ", "UÕ",
                            "UÏ", "ÖÙ", "ÖØ", "ÖÛ", "ÖÕ", "ÖÏ", "Ö", "EÁ", "EÀ", "EÅ", 
                            "EÃ", "EÄ", "EÂ", "EÙ", "EØ", "EÛ", "EÕ", "EÏ", "OÁ", "OÀ", "OÅ", 
                            "OÃ", "OÄ", "OÛ", "OÕ", "OÏ", "ÔÙ", "ÔØ", "ÔÛ",
                            "ÔÕ", "ÔÏ", "Ô", "Í", "Ì", "Æ", "Ó", "Ò", "YÙ", "YØ", "YÛ", "YÕ", 
                            "Î", "Ñ", "aù", "aø", "oâ", "où", "oø", "AÙ", "AØ", "OÂ", "OÙ", "OØ");

	return str_replace($vniChars, array_map('unichr', $unicode), $vniInput);

}

The first function, unichr is just the Unicode version for the chr function, which return an Unicode character from it code. In the conversion routine, all you need here is two array: one contains VNI character, another contains the corresponding Unicode character. For the conversion from Unicode to VNI, exchange the position of these arrays.

Setup a small local web server for your office with WampServer 2

WampServer is a useful and convenient tool to setup a web development environment on Windows. It’s so easy to use WampServer for a private purpose – the way that you only access your projects via localhost. However, if you have no experience, you will find it difficult to use WampServer for your local office. What below is how I setup the server for our web applications in my company and allow just my colleagues access the server, hope that it will help you in some way.

A first general look

First, let take a look at the general steps to know what you will do:

  • Give your server a static IP
  • Determine which computers can access your server

Step 1: A static IP for your server

In this post, I’ll setup a local web server, which means that there will be no internet connection from the server. If you want to setup a real web server which allows people connect from internet, consider looking at another article.

First, you have to know the Physical Address (or MAC Address) of the server. Open the Connection properties of the current network connection of the server, press details button and look for the Physical address line in the new opened window, you will find the Physical Address of the server as a string with 8 pair of characters separated by 5 hyphens. Copy or note this string in order to use it later.

Now, keep the Network Connection Details windows open and look at the line named as DHCP Enabled. If its value is No, write the IP of the server (which can be found at the line IPv4 Address) to another location to use in the next steps. Otherwise, you have to setup your router to get a static IP for the server.

To set the static IP for the server, open the browser and navigate to the address of the router (it may be 192.169.1.1 or 192.168.0.1). If you don’t know this, ask your network administrator to do this step. After logging in the router configuration page, find the page allow you to bind an IP to a client (computer) by the MAC address. Use the previous saved MAC address (Physical address) of the server to bind an static local IP. This step may vary in different routers.

In the end of this step, you have to know the IP address of the server. Review what I wrote before if you don’t.

Step 2: Allow computers allowed

In this step, you have to be sure all client computer can access to server. Open the Command Prompt and enter ipconfig /all command. If the result look like the follow figure, it is fine. Otherwise, consider another setting for your network.

C:\Users\TrungHieu>ping 192.168.0.101

Pinging 192.168.0.101 with 32 bytes of data:
Reply from 192.168.0.101: bytes=32 time<1ms TTL=64
Reply from 192.168.0.101: bytes=32 time<1ms TTL=64
Reply from 192.168.0.101: bytes=32 time<1ms TTL=64
Reply from 192.168.0.101: bytes=32 time<1ms TTL=64

Ping statistics for 192.168.0.101:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms
The result of a successful “ping”

With WampServer running, click to its icon in the system tray, point to Apache, click httpd.conf to edit the httpd.conf file in your text editor. Find a line as # onlineoffline tag – don’t remove and be sure that the next lines are the same as below to allow access only from localhost:

#   onlineoffline tag - don't remove
    Order Deny,Allow
    Deny from all
    Allow from 127.0.0.1
    Allow from ::1
httpd.conf config to allow access only from localhost

To allow connections from more computer, insert Allow from <computer IP address> for each computer (with <computer IP address> is its address) after the Allow from ::1 line. You can also use IP address with subnet mask here for shorting your configuration.In some cases, you just want to allow your colleagues to access some web sites, not the whole server, do not insert more Allow statements. Go to the end of the httpd.conf file and insert this code:

<Directory "absolute/path/to/the/website/folder">

</Directory>
Insert Allow from <computer IP address> lines between the and tag.

Final step

Everything will work after you restart your Apache service by clicking to the Wamp system tray icon, point to Apache, Services and click Restart services. You can consider allowing Wamp to auto start and go online with the Windows or setup more secure method with the Basic Authentication. However, you will have a simple web server for your office with just above steps.

See my video to more specific

Solve the “Denied access” problem when using Wamp server 2 on Windows 8

After installing the Windows 8 Consumer Preview (I’ll post a article about its UX soon), I cannot access my localhost as well as all of my local web folders. For more specific, I met these error notice

Access denied on localhost when using Wamp server 2 on Windows 8
Access denied on localhost when using Wamp server 2 on Windows 8

Fortunately, I finally found the way to solve this problem (although I still don’t know why it happens now).

Quick fix on Apache httpd.conf and alias configuration files

First, open the httpd.conf file of your apache version by a text editor such as Notepad or Notepad++. It’s usually located at \wamp\bin\apache\<your apache version>\conf\httpd.conf or you can use the quick menu of Wamp manager to open it. Find these snippet before modify it:

# First, we configure the "default" to be a very restrictive set of 
# features.  
#
<Directory />
    Options FollowSymLinks
    AllowOverride None
    Order deny,allow
    Deny from all
</Directory>

Replace the Deny from all with Allow from all and save the file.We have not finished yet. Find another snippet:

#   onlineoffline tag - don't remove
    Order Deny,Allow
    Deny from all
    Allow from 127.0.0.1

Replace Deny from all with Allow from all again. The localhost runs correctly now.After do all above thing, I believe that you can locate to localhost and your local web project with your browser. However, if you navigate to https://localhost/phpmyadmin, you will continue getting the Forbidden error. The reason is you did not modify the alias configuration file to allow access from your localhost. Let open this file by using Wamp manager or finding it in \wamp\alias\phpmyadmin.conf, find this snippet

<Directory "c:/wamp/apps/phpmyadmin3.4.10.1/">
    Options Indexes FollowSymLinks MultiViews
    AllowOverride all
        Order Deny,Allow
    Deny from all
    Allow from 127.0.0.1
</Directory>

Change Allow from 127.0.0.1 to Allow from ::1 or you can do as above, change Deny from all into Allow from all. Now, that’s all.

See how I did it with a video

You can see this video on youtube to see how I solved this problems more clearly.

Custom string error in CodeIginter where clause

What is the trouble?

Base on CodeIgniter Database class user guide, you can pass a custom string directly to the where clause by the $this->db->where() method. Let see the flowing example, suppose that you have successfully initiated your database.

...
$val = 1;
$this->db->where("id='".$val."'");
$this->db->get('mytable');
...

You are sure that your table has a field named id and expects that query returns a record whose id equals to 1. But, ironically, your get an ugly error message!

A Database Error Occurred Error Number: 1054 Unknown column `id=1` in `where clause` SELECT * FROM (`mytable`) WHERE `id=1`

Understand the error

Notes: This section explain how this error arises. If you just want to have a solution, skip to next section.The evil thing happens when the function $this->_protect_identifiers() is call from _where method of CI_DB_active_record class in ./system/database/DB_active_rec.php file. Actually, the goal of the _protect_identifiers() function is to escape the table field name with ‘`’ (backticks) character, which is very helpful when executing some queries with particular field name. Read the source of this function carefully with the notice that our parameters now is $item = ‘id=1’, $prefix_single = FALSE, $protect_identifiers = NULL, $field_exists = TRUE you can find that the hidden culprit is these line:

$alias = '';
if (strpos($item, ' ') !== FALSE)
    {
        $alias = strstr($item, " ");            
                $item = substr($item, 0, - strlen($alias));
    }

The snippet find the first space in $item and then split $item into two sub string. For example, if $item = ‘my_name AS f1′, after executing, $item=’myname’ and $alias = ‘AS f1’. Next, CI “wrap” the $item by a pair of backticks, as a result, the whole of our custom string, which does not have any space, will be “wrapped” and be considered as a field name instead of a valid where clause.

The correct solution

Till the time I wrote this post, only two things you can do to overcome this obstacle: modify your custom string or use the optional parameters of $this->db->where() method.

  • Modify your custom string: remember to leave a space character (” ” – ACSII code is 32) between the field name and the operators. To find out why I suggest this solution, read the explanation section above.
  • Use the optional parameters: instead of $this->db->where($custom_string) , you can use $this->db->where($custom_string, NULL, FALSE) to avoid this error. But, do queries in this way can cause critical errors because it cancel the escaping process, which ensure your input queries is safe to be executed.

How to use Codeigniter pagination

The pagination and the CodeIgniter framework

When you want to present a long list of data, the pagination becomes a useful way to give your users the best interactivity. This makes pagination supported as a class in CodeIgniter framework. Unfortunately, despite of what developers said on the user guide, using this class is not easily to understand for many people. In this post, I will introduce the basic of how to use CodeIgniter pagination class via an example. You can see my demo at the demo page

Our data source

Create a table in your database as below:

CREATE TABLE 'demo_page' (
'id' INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
'value_code' CHAR( 6 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
'quantity' INT NOT NULL
)

Our table has three field: id – the id of each record; value_code – the identifying string of each item, in this case, it contains six capital Latin letters, and quantity – the number of each item in the storehouse.

The Model

In this demo, our Model involves two method: getting the total number of record in the data source and getting an certain of number of records based on passed argument. Create a file called demo_page.php in your ./application/models/ folder and make its content as these line:

<?php
class Demo_page extends CI_Model {
    function __construct() {
        parent::__construct();
        $this->load->library('database');
    }

    function count_items(){
        return $this->db->count_all('demo_page');
    }

    function get_items($limit, $offset){
        $data = array();
        $this->db->limit($limit, $offset);
        $Q = $this->db->get('demo_page');
        if($Q->num_rows() > 0){
            foreach ($Q->result_array() as $row){
                $data[] = $row;
            }
        }
        $Q->free_result();
        return $data;
    }
}

The Controller

What we do in the Controller is loading libraries and models, configuring the pagination class, getting data from database and calling the view. In your ./application/controllers/ folder, create a new php file as below and name it demo.php

<?php
class demo extends CI_Controller {

    function __construct() {
        parent::__construct();
    }

    function index() {
        // return to previous page
        redirect(base_url());
    }

    function ci_pagination() {
        $this->load->library('pagination');
        $this->load->library('table');
        $this->load->model('demo/Demo_page');

        $result_per_page = 5;  // the number of result per page

        $config['base_url'] = base_url() . '/demo/ci_pagination/';
        $config['total_rows'] = $this->Demo_page->count_items();
        $config['per_page'] = $result_per_page;
        $this->pagination->initialize($config);

        $datatable = $this->Demo_page->get_items($result_per_page, $this->uri->segment(3));

        $this->load->view('demo/ci_pagination', array(
            'datatable' => $datatable,
            'result_per_page' => $result_per_page
        ));
    }
}

I will explain these lines after we finish our View

The View

I use this View to present our data in the tabular format:

<!doctype html>
<html>
    <head>
        <title>Demo for CodeIgniter Pagination :: Demo cho việc phân trang trong CodeIgniter</title>
        <meta charset="utf-8" />
        <style>
            td{
                text-align: center;
            }
            td{
                border-top: 1px solid #ccc;
            }
            table{
                margin: 1em;
            }
        </style>
    </head>
    <body>
        <h1>Demo for CodeIgniter Pagination</h1>
        <?php

        // generate the table
        $this->table->set_heading('ID', 'ITEM CODE', 'QUANTITY');
        echo $this->table->generate($datatable);

        // generate the page navigation

        echo $this->pagination->create_links();
        ?>
    </body>
</html>

Now, we can test our demo with by typing this URL into your browser https://Your_CI_App_folder_URL/demo/ci_pagination

CodeIgniter pagination configuration

Now, when our demo runs perfectly, let look at our Controller again to learn what those lines do.

The first three lines loads the necessary modules. In this demo, we needs the pagination library (of course) to generate the navigation links, the table library to quickly generate a table based on what we got from our source and the demo_page model to communicate with the database.

The, we define a variable contains how many item will be display in a page. After that, in the next four lines, we configure the pagination class before initiate it:

  • $config[‘base_url’]: the full URL to the controller class/function contains the pagination.
  • $config[‘total_rows’]: the total number of records you want to display. This our demo, we will display all record in the table and get it by count_items method of the Model.
  • $config[‘per_page’]: the number of records you want to display in each page. You had better use a constant or a variable to make it easily to be modified later.

After initiate the pagination, we just simply echo the $this->pagination->create_links(); result in our View.

Conclusion

After reading this post, I hope you can understand how to use the useful pagination class of CodeIgniter. Remember, this utility does not play a roll as a Model, its objective is generate the navigation links and write the URL. You can learn more about it in the CodeIgniter user guide.

Convert date time data between php servers and user browsers

You need this because:

  • Your web server is in a fixed location when your users can be in any country all over the world,
  • Our world doesn’t use the same clock but it’s divided in to 24 parts called timezones
  • You need to get, manipulate or display current date time between your web server and your user’s browser

Timezone, UTC time and local time

In general, we have this formula to calculate local time

local time = UTC time + your timezone (in hour)

For example, I’m in Vietnam, my timezone is UTC+7, so when the UTC time is 00:00, my clock will display current time as 07:00. It is so easily to reason the following lines out:

(A) local time = (B) local time – (B) timezone + (A) timezone

PHP function

To implement what we found in PHP, I use this code snippet:

function convert_from_another_time($source, $source_timezone, $dest_timezone){
    $offset = $dest_timezone - $source_timezone;
    if($offset == 0)
        return $source;
    $target = new DateTime($source->format(Y-m-d H:i:s));
    $target->modify($offset+' hours');
    return $target
}

In above function:

  • $source: a DateTime object contains our source data
  • $source_timezone: an integer refers to the timezone of our source data. For example, if the timezone of our source data is ‘Asia/Ho_Chi_Minh’ (UTC+7), $source_timezone is 7.
  • $dest_timezone: an integer refers to the timezone of our target data

As you can see, in my function, I create a new DateTime object and “clone” the data of $source to it. This ensure our old value will not be changed after executing.

Get the timezone of your php web server

Use this code:

$d = new DateTime();
$dtz = ($d->getOffset())/3600;
// we can also use the this procedure style
// $dtz = date_offset_get($d);

Notice that the getOffset method return the difference between your local web server time and UTC time in second, you have to change this value to hours before pass to our convert function.

Get the timezone of your user

The only way to do this is use a client script language like javascript. You can consider using my code:

var d = new Date();
var dtz = -(d.getTimezoneOffset())/60;

Why,i use the negative operation before the result? Unlike php, javascript getTimezoneOffset function return the difference between the UTC time and the client time in minute, therefore, you need to convert that value like above.

Let you server know the user timezone

This is what i used:

  • PHP: check if there is a timezone data in the cookie.
  • JS: if there is no timezone data, calculate like previous session and make an ajax call with post data contains the result.
  • PHP: set the client cookie.

The last thing for WAMP Server 2 users

If you use WAMP Server 2, It’s highly recommended to review your date.timezone value in the php.ini file. In my experience, WAMP makes your php timezone as UTC when keep the timezone of your MySQL the same as your local timezone by default. This may cause further troubles when working with DATETIME, DATE, TIME or TIMESTAMP fields.