I want to share a recent work request that made me have to think for a bit. My customer would like to upload a text file containing one integer per line. The PHP script that reads the file should then use the integers as lookup keys to a SQLite database. The order in which they appear is how the customer wants to see the recordset displayed on the webpage.

Ok, so my boilerplate PHP class that I like to use looks like this


class MyList {
  private $dbh;
  function __construct() {
     this->dbh=PDO('sqlite:/path/to/my/sqlite.db');
  }
  // and its one useful method
  function get($file) {
     // read the raw file
     $aFileData = file($file);
     // now screen for lines that just contain integers
     $aFilteredData = array(); 
     foreach ($aFileData as $line) {
        if (preg_match('/^\s*\d+\s*$/,$line)) {
           $rec_key = preg_replace('/^\s*(\d+)\s*$/,'$1',$line);
           array_push($aFilteredData,$rec_key);
        }
      }
  ...
  }
}

This got me an array of the lookup keys.

From here, I wanted to query the SQLite database with a single call rather than making one call per key. I did that by making an WHERE key IN clause using my filtered array.


   $key_csv = join(",", $aFilteredData);
   $query = 'SELECT key,name,description,value,timestamp FROM mytable WHERE key IN ('.$key_csv.')';
   $sth = $this->dbh->prepare($query);
   $sth->execute();
   ...

Here is where I had to think about this. What mode should I use to fetch my recordsets? Ideally, I wanted an associative array that has ‘key’ as the index whose value is an associative array that has the field names as the keys.

Let’s say I get that, how would I tie it back to my original list while preserving the order? I would need to map the key to its ordering index to do that. But what if there is more than one instance of the key in the original list? Eh, I will cross that bridge if the customer needs it and jot it down under caveats.

For now I’ll perform a quick mapping using array_flip:


   $aKeyToIndex = array_flip($aFilteredData);  // thus if $aFilteredData[3] = 123 then  $aKeyToIndex[123] = 3

Back to the fetch mode selection. Originally I thought fetchAll with a mode of FETCH_COLUMN|FETCH_GROUP would be the right choice to create the array of associative arrays. Unfortunately, that mode only returns one other column as the value of each associative array — the second column, in my case ‘name’. So my result set just looked like
$aRecords[key]=name
instead of
$aRecords[key]=('name'=>name,'description'=>description, etc)

So instead I decided to with FETCH_CLASS and create another class object that would encase my return values:


      $aDRResults=$sth->fetchAll(PDO::FETCH_CLASS,"MyRec");
      foreach ($aDBResults as $rec) {
         // create the return array of records in proper order
         $aReturn[$aKeyToIndex[$rec->key]]=$rec;
      }
      return $aReturn;
   }
} // end of class MyList
class MyRec {
   public $key;
   public $name;
   public $description;
   public $value;
   public $timestamp;
}

I suppose I could have also used FETCH_OBJ to be even lazier to return an array of anonymous objects, but this way I now have the foundation to perform post query processing if needed.


Next post: Note to self on SQLite

Previous post: Cool <<= 2