TextExpander: Smarten/Straighten Quotes

I’ve been a long time user of TextExpander, though I haven’t tapped into its full potential until recently.

I often find myself converting between "straight" quotes and “curly” quotes. Some applications can perform this conversion internally, while others use plug-ins. Mac OS X 10.9 (Mavericks) can even “smarten” your quotes and dashes automatically. (System Preferences > Keyboard > Text > Use smart quotes and dashes.) I do a fair amount of text editing where “smart” quotes aren’t desired. TextExpander to the rescue!

Here are two snippets that will transform your clipboard contents into the desired quote format. (Be sure to change the content type to: Shell Script.)

Smarten Quotes:
https://gist.github.com/benrothe/7281205

#!/usr/bin/env php
<?php
$str = `pbpaste`;
$str = preg_replace('/(^|[-\xe2\x80\x94\/(\[{"\s])\'/', "$1\xe2\x80\x98", $str);
$str = preg_replace('/\'/', "\xe2\x80\x99", $str);
$str = preg_replace('/(^|[-\xe2\x80\x94\/(\[{\xe2\x80\x98\s])"/', "$1\xe2\x80\x9c", $str);
$str = preg_replace('/"/', "\xe2\x80\x9d", $str);
$str = preg_replace('/--/', "\xe2\x80\x94", $str);
$str = preg_replace('/\.\.\./', "\xe2\x80\xa6", $str);
echo $str;

Straighten Quotes:
https://gist.github.com/benrothe/7281227

#!/usr/bin/env php
<?php
$str = `pbpaste`;
$find = array("\xe2\x80\x98", "\xe2\x80\x99", "\xe2\x80\x9c", "\xe2\x80\x9d", "\xe2\x80\x93", "\xe2\x80\x94", "\xe2\x80\xa6");
$replace = array("'", "'", '"', '"', '-', '--', '...');
echo str_replace($find, $replace, $str);

Enjoy!

PHP tip: Use error_log() to email debugging info

Have you ever come across a situation where you can't echo debugging information on-screen because the problem code is live, or the display_errors configuration directive is off? PHP's error_log() is a handy utility function that can be used to email instead of echo'ing on-screen. I like to combine it with print_r() for formatting. For example, let's say you need to see all of the data in the $_SESSION superglobal:

error_log(print_r($_SESSION, TRUE), 1, 'benrothe@gmail.com');

The second parameter to error_log(), 1 in this case, sends the contents of the first parameter to the email address specified in the third parameter. I like to use TRUE as the second parameter to print_r() to return it's output instead of printing it (which is does by default). The result is anicely formatted email in your inbox like so:

From: Apache <apache@some.domain.com>
Sent: Wednesday, December 24, 2008 10:20 AM
To: benrothe@gmail.com
Subject: PHP error_log message

Array
   (
      [member_count] => 3567120
      [member_id] => 123456
      [last_login] => 2008-12-24 10:05:25
      [first_name] => Ben
      [last_name] => Rothe
      [opt_out] => 0
   )

MySQL tip: Output query results vertically

Have you ever pulled your hair out trying to read excessively long query results that wrap onto multiple lines in the mysql command-line program? This happens to me about once a week, so today I went hunting for a better way. Turns out it's super easy and incredibly useful!

Here's an example of what this might look like on your screen:

 mysql> SELECT * FROM customers;
 +-------------+------------+---------------------+-------+----
 --------+-----------+------------------------+-----------+----
 ----------+-------+----------+-------------+---------------+--
 ------------+ | customer_id | address_id | stamp
 | title | first_name | last_name | address                |
 address_2 | city         | state | province | postal_code |
 country       | phone_number |
 +-------------+------------+---------------------+-------+----
 --------+-----------+------------------------+-----------+----
 ----------+-------+----------+-------------+---------------+--
 ------------+ |           1 |          1 | 2008-12-04 12:00:00
 | Mr.   | Ben        | Rothe     | 8555 Cedar Place Drive |
 Suite 114 | Indianapolis | IN    |          | 46240       |
 United States | 317-251-6744 |
 +-------------+------------+---------------------+-------+----
 --------+-----------+------------------------+-----------+----
 ----------+-------+----------+-------------+---------------+--
 ------------+
 1 row in set (0.00 sec)
 

To output this result set vertically, just end your query with \G instead of a ; character.

 mysql> SELECT * FROM customers\G
 *************************** 1. row ***************************
 customer_id: 1
 address_id: 1
 stamp: 2008-11-24 15:41:25
 title: Mr.
 first_name: Ben
 last_name: Rothe
 address: 8555 Cedar Place Drive
 address_2: Suite 114
 city: Indianapolis
 state: IN
 province:
 postal_code: 46240
 country: United States
 phone_number: 317-251-6744
 1 row in set (0.00 sec)
 

How cool is that?!?

CI How-to: Using the pagination class with SQL WHERE conditions

A recent issue I've run into is trying to use CodeIgniter's pagination class with queries that contain WHERE conditions. Specifically, I need to set the paginator's $config['total_rows'] to the number of rows in the full result set when one or more WHERE conditions exist in conjuction with a LIMIT clause. And ideally, I don't want to run the same query twice. It just feels dirty.

Let's take a quick look at the problem. Typical usage of the CI's pagination class looks something like this in the controller. I'll use a theoretical blog application for the code examples.

function index() {

    // configure the paginator
    $this->load->library('pagination');
    $config['base_url'] = base_url() . 'blog/index/';
    $config['per_page'] = 5;
    $config['total_rows'] = $this->db->count_all('entries');
    $this->pagination->initialize($config);

    // load the model and get the query results
    $this->load->model('Blog_model', 'blog');
    $data['entries'] = $this->blog->select_entries($config['per_page'], $this->uri->segment(3));

    // load the view
    $this->load->view('entry_view', $data);
}

This works well enough when you don't have any WHERE conditions to apply. But if you do have WHERE conditions, you are going to have to execute two similar queries, one with a LIMIT clause, and one without. There is a more elegant solution. MySQL has functionality to calculate the number of rows in the full result set without running the query again. Let's apply this to the blog model's select_entries() function.

function select_entries($conditions = NULL, $limit = NULL, $offset = NULL) {
    $this->db->select("SQL_CALC_FOUND_ROWS *");

    if(isset($conditions)) {
        $this->db->where($conditions, NULL, FALSE);
    }

    $query = $this->db->get('entries', $limit, $offset);

    if($query->num_rows() > 0) {

        // let's see how many rows would have been returned without the limit
        $count_query = $this->db->query('SELECT FOUND_ROWS() AS row_count');
        $found_rows = $count_query->row();

        // load all of the returned results into a single array ($rows).
        // this is handy if you need to execute other SQL statements or bring
        // in additional model data that might be useful to have in this array.
        // alternatively, you could return $query object if you prefer that.
        $rows = array();
        foreach($query->result() as $row) {

            // to build on my comment above about returning an array instead of
            // the raw $query object, as an example, this would be a good spot
            // to retrieve the comment count for each entry and append that to
            // the current row before we push the row data into the $rows array.
            $row->comment_count = $this->_comment_count($row->entry_id);

            array_push($rows, $row);
        }

        // after the foreach loop above, we should now have all of the combined
        // entry data in a single array. let's return a two-element array: the
        // first element contains the result set in array form, and the second
        // element is the number of rows in the full result set without the limit
        return array('rows' => $rows, 'found_rows' => (int) $found_rows->row_count);
    } else {
        return FALSE;
    }
}

Now let's revise the controller.

function index() {

    // configure the paginator
    $this->load->library('pagination');
    $config['base_url'] = base_url() . 'blog/index/';
    $config['per_page'] = 5;

    // load the model and get results
    $this->load->model('Blog_model', 'blog');
    $data['entries'] = $this->blog->select_entries("is_visible = 1", $config['per_page'], $this->uri->segment(3));

    // set the total_rows value and initialize the paginator
    $config['total_rows'] = (isset($data['entries']['found_rows'])) ? $data['entries']['found_rows'] : 0;
    $this->pagination->initialize($config);

    // load the view
    $this->load->view('entry_view', $data);
}

Finally, iterate through the entries array in the view.

<div id="entries">
    <?php foreach($entries['rows'] as $entry) { ?>
    <div class="entry">
        <h1 class="title"><?php echo $entry->title; ?></h1>
        <div class="body"><?php echo $entry->body; ?></div>
    </div>
    <?php } ?>
</div>
</pre>
<?php echo $this->pagination->create_links(); ?>

That's it! Being somewhat new to CI, maybe there is a better way to do this? In fact, there probably is! I guess that is one of the things I like about CI so far... there is often more than one way to do things.

PHP tip: Use a regular expression in a switch statement

I came across this nugget while trying to rapidly prototype some new functionality for one of my clients. The client wanted specific artwork to show up for a subset of related items. The prototype has worked out so well, that I haven't yet had to go back add a flag field to the underlying database table. One of the things I had going for me was that the SKUs I needed to identify followed a rigid naming convention. I didn't want to update the switch statement each time a new SKU is added, so instead of this:

switch($sku) {
    case '11012E':
    case '11345E':
    case '11678E':
    case '11901E':
        // ... do stuff
        break;
    case '08012E':
    case '08345E':
        // ... do different stuff
        break;
}

You can use a function in a switch statement to return the result of a regular expression. Even better.

switch($sku) {
    case (preg_match("/^11[0-9]{3}E$/i", $sku) ? $sku : !$sku):
        // ... do stuff
        break;
    case (preg_match("/^08[0-9]{3}E$/i", $sku) ? $sku : !$sku):
        // ... do different stuff
        break;
}