Last active
December 14, 2015 19:09
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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