Skip to content

Instantly share code, notes, and snippets.

@dsmeringe
Last active December 14, 2015 19:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dsmeringe/5134955 to your computer and use it in GitHub Desktop.
Save dsmeringe/5134955 to your computer and use it in GitHub Desktop.
This is actually an adapter version of a type-ahead-find solution I did for a client once.. See https://gist.github.com/dsmeringe/4952481 for usage of this class.
<?php
/**
* query class that performs ..queries!! :)
*
* This is actually an adapter version of a type-ahead-find solution I did
* for a client once..
*
* See https://gist.github.com/dsmeringe/4952481 for usage of this class
*
* This class IRL is used to replicate another DB and be used to perform type
* ahead find searches with a jquery autocomplete-textfield.
*
* remember to call initializeDB before searching and make sure that
* $this::externalDBSettingsFile is set correctly to include a php file with
* settings (pi1/connect.php) for external DB, otherwise import will fail.
*
* @author David Smeringe <david@merea.se>
*/
class queryClass {
/**
* Relative path to where to store sqlite db file(s). Preferably refering
* to somewhere outside doc root
* @var string
*/
public $dbPath = 'sqlitedb';
/**
* name of database file. Default is a "hidded" db file.
* @var string
*/
public $dbName = '.our_querydb.sqlite';
/**
* Name of internal table
* @var string
*/
public $dbTable= 'a_cool_table';
/**
* Name of external thing database connection credentials filename
* @var string
*/
public $externalDBSettingsFile = 'connect.php';
/**
* Limit search and import to this language. If -1 then no limit.
* @var int
*/
public $limitToLanguage = -1;
/**
* Max amount of hits to return
* @var int
*/
public $hitsLimit = 10;
//group search results in any of the following ways..
/**
* don't group search results. Use as third param in $this->search()
*/
const GROUPSEARCH_NONE = 0;
/**
* group search results on searchkey (term). Use as third param in $this->search()
*/
const GROUPSEARCH_SEARCHKEY = 1;
/**
* group search results on products_id. Use as third param in $this->search()
*/
const GROUPSEARCH_PRODUCTID = 2;
/**
* group search results on EAN (artno). Use as third param in $this->search()
*/
const GROUPSEARCH_EAN = 3;
private $_externalDBHandle;
private $_dbHandle;
private $_dbInitialized = false;
/**
* Default constructor.
*
* Remember to call initializeDB before searching
*/
function __construct() {
//create db dir if it doesn't exist
if (!is_dir($this->dbPath))
mkdir (dirname( __FILE__ ).'/'.$this->dbPath, 0775);
}
/**
* Search DB for products matching value
*
* @param string $searchValue
* @param int $lang language key to search on. Set to null to ignore. null is Ignored if $this->limitToLanguage is set
* @param boolean $returnDistinct set to any static GROUPSEARCH_*
* @return array
*/
public function search ($searchValue, $lang='3', $returnDistinct=0) {
if ($this->limitToLanguage>=0 && is_null($lang))
$lang = $this->limitToLanguage;
if (!$this->_dbInitialized) throw new Exception('DB not initialized. Please call initializeDB() before searching.');
$searchValue = str_replace(" ", " OR ", $searchValue);
$searchValue = explode(" ", $searchValue);
$searchV = '';
foreach ($searchValue as $k=>$v) {
$searchV .= "OR searchkey like '%".trim($v)."%'" ;
}
$groupBy = '';
switch ($returnDistinct) {
case self::GROUPSEARCH_EAN:
$groupBy = 'GROUP BY artno'; break;
case self::GROUPSEARCH_PRODUCTID:
$groupBy = 'GROUP BY products_id'; break;
case self::GROUPSEARCH_SEARCHKEY:
$groupBy = 'GROUP BY searchkey'; break;
default:
$groupBy = ''; break;
}
//assemble SQL query
$q = "SELECT searchkey, artno, prodtitle, products_id
FROM {$this->dbTable}
WHERE artno like '%{$searchValue}%' $searchV
AND language_id=$lang
$groupBy
ORDER BY searchkey, artno
LIMIT ".$this->hitsLimit;
//search
$res = sqlite_query($this->_dbHandle, $q);
//iterate hits, nah.. return all :)
$results = sqlite_fetch_all($res, SQLITE_ASSOC);
//return
return !is_null($results)? $results: array();
}
/**
* initialize the DB replica DB by setting up neccessary database file
* and tables as well as initially populating it with search data and
* setting a correct indexing.
*
* imports data if database didn't exist before..
*
* throws exception on failure
*/
public function initializeDB () {
//check/create db
$this->_dbHandle = sqlite_open($this->dbPath.'/'.$this->dbName, 0666, $error);
if (!$this->_dbHandle) die ($error);
//check if table exists..
$stm = "SELECT count(*) FROM sqlite_master WHERE type='table' AND tbl_name='{$this->dbTable}'";
$q = sqlite_query ($this->_dbHandle, $stm);
$tableExists = sqlite_fetch_single( $q);
//didn't exist?
if (is_null($tableExists) || !$tableExists || $tableExists<1) {
//create table
$stm = "CREATE TABLE {$this->dbTable}(" . /*IF NOT EXISTS - not supported in earlier versions of sqlite*/
" artno text NOT NULL,
searchkey text NOT NULL,
prodtitle text NOT NULL,
products_id integer NOT NULL,
language_id integer NOT NULL
)";
$ok = sqlite_exec($this->_dbHandle, $stm, $error);
if (!$ok)
throw new Exception('failed to create db schema');
//add indexes to db
//add index for title (artno is already indexed since it's the PK..
$ok = sqlite_exec($this->_dbHandle, "CREATE INDEX q_search_stdSearch ON {$this->dbTable} (artno, searchkey, language_id)");
$ok = sqlite_exec($this->_dbHandle, "CREATE INDEX q_search_searchkey ON {$this->dbTable} (searchkey)");
$ok = sqlite_exec($this->_dbHandle, "CREATE INDEX q_search_artno ON {$this->dbTable} (artno)");
$ok = sqlite_exec($this->_dbHandle, "CREATE INDEX q_search_language ON {$this->dbTable} (language_id)");
$ok = sqlite_exec($this->_dbHandle, "CREATE INDEX q_search_products_id ON {$this->dbTable} (products_id)");
} //eo table not existing
$eq = sqlite_query( $this->_dbHandle, "SELECT count(*) from ".$this->dbTable);
if (!$eq) throw new Exception('Failed check state on storage table');
$exists = sqlite_fetch_single( $eq);
$exists = $exists>0? true:false;
if (!$exists) {
//import initial data
try {
$this->importData();
} catch (Exception $e) {
throw new Exception($e->getMessage);
}
}
//more?
$this->_dbInitialized = true;
}
/**
* connects to a MySQL db representing the DB data we want
*
* throws Exception
*/
private function connectExternalDB() {
include_once ($this->externalDBSettingsFile);
$this->_externalDBHandle = mysql_connect($host, $user, $pass);
if (!$this->_externalDBHandle) throw new Exception('Failed to connect to external db.');
mysql_select_db($db/*, $this->_externalDBHandle*/);
}
/**
* import data from template db
*
* throws exception on error.
*/
private function importData() {
//init external db
if (!$this->_externalDBHandle)
$this->connectExternalDB();
//make sure our table is empty before we import
sqlite_exec($this->_dbHandle, "DELETE FROM ".$this->dbTable);
//go forth!
$andWhere = '';
if (is_int($this->limitToLanguage) && $this->limitToLanguage>=0)
$andWhere .= ' AND language_id='.$this->limitToLanguage;
//fetch data
$data = mysql_query(
"SELECT
products_erp_item_id as ean, products_name as prodname, p.products_id as prodid, language_id,
products_keywords, products_model, pd.products_category_level1 as cat1,
pd.products_category_level2 as cat2, pd.products_category_level2 as cat2,
pd.products_category_level3 as cat3, pd.products_category_level4 as cat4
FROM
products p, products_description pd
WHERE
p.products_status=1 AND p.parent_id=0
AND pd.products_id = p.products_id $andWhere
LIMIT 6000"/*,
$this->_externalDBHandle*/
);
if (!$data) throw new Exception('Failed to query external source');
//import
$error = '';
while ($row = mysql_fetch_assoc( $data)) {
$sql = "INSERT INTO {$this->dbTable}
(artno, searchkey, products_id, language_id, prodtitle)
VALUES ( '{$row['ean']}', '{$row['prodname']}', '{$row['prodid']}', {$row['language_id']}, '{$row['prodname']}')";
$k = sqlite_exec($this->_dbHandle, $sql, $error);
//add extra search keys
$keys = array ('ean', 'products_keywords', 'products_model', 'cat1', 'cat2', 'cat3', 'cat4');
foreach ($keys as $k=>$key) {
if (strlen($row[$key])>0) {
$sql = "INSERT INTO {$this->dbTable}
(artno, searchkey, products_id, language_id, prodtitle)
VALUES ( '{$row['ean']}', '{$row[$key]}', '{$row['prodid']}', {$row['language_id']}, '{$row['prodname']}')";
$k = sqlite_exec($this->_dbHandle, $sql, $error);
}
}
}
//reset index? (REINDEX)
}
public function closeDB() {
sqlite_close($this->_dbHandle);
if (!is_null($this->_externalDBHandle) && $this->_externalDBHandle!==false)
mysql_close($this->_externalDBHandle);
}
function __destruct() {
//make sure db connections are closed..
$this->closeDB();
}
/**
* use with care. removes all data AND the table itself :)
*
* @return boolean success state
*/
public function dropStorageTable() {
$k = sqlite_exec($this->_dbHandle, "DELETE FROM ".$this->dbTable);
$k = sqlite_exec($this->_dbHandle, "DROP TABLE ".$this->dbTable);
return !$k? false:true;
}
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment