Skip to content

Commit 0f0ca57

Browse files
authored
Merge pull request #494 from Noneatme/master
Added methods for importing data (CSV, XML) and table locking
2 parents adbede9 + dbc5199 commit 0f0ca57

File tree

7 files changed

+467
-9
lines changed

7 files changed

+467
-9
lines changed

.gitignore

Lines changed: 0 additions & 5 deletions
This file was deleted.

MysqliDb.php

Lines changed: 270 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,19 @@ class MysqliDb
8888
* @var array
8989
*/
9090
protected $_groupBy = array();
91-
91+
92+
/**
93+
* Dynamic type list for tempromary locking tables.
94+
* @var array
95+
*/
96+
protected $_tableLocks = array();
97+
98+
/**
99+
* Variable which holds the current table lock method.
100+
* @var string
101+
*/
102+
protected $_tableLockMethod = "READ";
103+
92104
/**
93105
* Dynamic array that holds a combination of where condition/table data value types and parameter references
94106
* @var array
@@ -381,6 +393,28 @@ public function setPrefix($prefix = '')
381393
return $this;
382394
}
383395

396+
/**
397+
* Pushes a unprepared statement to the mysqli stack.
398+
* WARNING: Use with caution.
399+
* This method does not escape strings by default so make sure you'll never use it in production.
400+
*
401+
* @author Jonas Barascu
402+
* @param [[Type]] $query [[Description]]
403+
*/
404+
private function queryUnprepared($query)
405+
{
406+
// Execute query
407+
$stmt = $this->mysqli()->query($query);
408+
409+
// Failed?
410+
if(!$stmt){
411+
throw new Exception("Unprepared Query Failed, ERRNO: ".$this->mysqli()->errno." (".$this->mysqli()->error.")");
412+
};
413+
414+
// return stmt for future use
415+
return $stmt;
416+
}
417+
384418
/**
385419
* Execute raw SQL query.
386420
*
@@ -864,6 +898,123 @@ public function join($joinTable, $joinCondition, $joinType = '')
864898

865899
return $this;
866900
}
901+
902+
903+
/**
904+
* This is a basic method which allows you to import raw .CSV data into a table
905+
* Please check out http://dev.mysql.com/doc/refman/5.7/en/load-data.html for a valid .csv file.
906+
907+
* @author Jonas Barascu (Noneatme)
908+
* @param string $importTable The database table where the data will be imported into.
909+
* @param string $importFile The file to be imported. Please use double backslashes \\ and make sure you
910+
* @param string $importSettings An Array defining the import settings as described in the README.md
911+
* @return boolean
912+
*/
913+
public function loadData($importTable, $importFile, $importSettings = null)
914+
{
915+
// We have to check if the file exists
916+
if(!file_exists($importFile)) {
917+
// Throw an exception
918+
throw new Exception("importCSV -> importFile ".$importFile." does not exists!");
919+
return;
920+
}
921+
922+
// Define the default values
923+
// We will merge it later
924+
$settings = Array("fieldChar" => ';', "lineChar" => PHP_EOL, "linesToIgnore" => 1);
925+
926+
// Check the import settings
927+
if(gettype($importSettings) == "array") {
928+
// Merge the default array with the custom one
929+
$settings = array_merge($settings, $importSettings);
930+
}
931+
932+
// Add the prefix to the import table
933+
$table = self::$prefix . $importTable;
934+
935+
// Add 1 more slash to every slash so maria will interpret it as a path
936+
$importFile = str_replace("\\", "\\\\", $importFile);
937+
938+
// Build SQL Syntax
939+
$sqlSyntax = sprintf('LOAD DATA INFILE \'%s\' INTO TABLE %s',
940+
$importFile, $table);
941+
942+
// FIELDS
943+
$sqlSyntax .= sprintf(' FIELDS TERMINATED BY \'%s\'', $settings["fieldChar"]);
944+
if(isset($settings["fieldEnclosure"])) {
945+
$sqlSyntax .= sprintf(' ENCLOSED BY \'%s\'', $settings["fieldEnclosure"]);
946+
}
947+
948+
// LINES
949+
$sqlSyntax .= sprintf(' LINES TERMINATED BY \'%s\'', $settings["lineChar"]);
950+
if(isset($settings["lineStarting"])) {
951+
$sqlSyntax .= sprintf(' STARTING BY \'%s\'', $settings["lineStarting"]);
952+
}
953+
954+
// IGNORE LINES
955+
$sqlSyntax .= sprintf(' IGNORE %d LINES', $settings["linesToIgnore"]);
956+
957+
// Exceute the query unprepared because LOAD DATA only works with unprepared statements.
958+
$result = $this->queryUnprepared($sqlSyntax);
959+
960+
// Are there rows modified?
961+
// Let the user know if the import failed / succeeded
962+
return (bool) $result;
963+
}
964+
965+
/**
966+
* This method is usefull for importing XML files into a specific table.
967+
* Check out the LOAD XML syntax for your MySQL server.
968+
*
969+
* @author Jonas Barascu
970+
* @param string $importTable The table in which the data will be imported to.
971+
* @param string $importFile The file which contains the .XML data.
972+
* @param string $importSettings An Array defining the import settings as described in the README.md
973+
*
974+
* @return boolean Returns true if the import succeeded, false if it failed.
975+
*/
976+
public function loadXml($importTable, $importFile, $importSettings = null)
977+
{
978+
// We have to check if the file exists
979+
if(!file_exists($importFile)) {
980+
// Does not exists
981+
throw new Exception("loadXml: Import file does not exists");
982+
return;
983+
}
984+
985+
// Create default values
986+
$settings = Array("linesToIgnore" => 0);
987+
988+
// Check the import settings
989+
if(gettype($importSettings) == "array") {
990+
$settings = array_merge($settings, $importSettings);
991+
}
992+
993+
// Add the prefix to the import table
994+
$table = self::$prefix . $importTable;
995+
996+
// Add 1 more slash to every slash so maria will interpret it as a path
997+
$importFile = str_replace("\\", "\\\\", $importFile);
998+
999+
// Build SQL Syntax
1000+
$sqlSyntax = sprintf('LOAD XML INFILE \'%s\' INTO TABLE %s',
1001+
$importFile, $table);
1002+
1003+
// FIELDS
1004+
if(isset($settings["rowTag"])) {
1005+
$sqlSyntax .= sprintf(' ROWS IDENTIFIED BY \'%s\'', $settings["rowTag"]);
1006+
}
1007+
1008+
// IGNORE LINES
1009+
$sqlSyntax .= sprintf(' IGNORE %d LINES', $settings["linesToIgnore"]);
1010+
1011+
// Exceute the query unprepared because LOAD XML only works with unprepared statements.
1012+
$result = $this->queryUnprepared($sqlSyntax);
1013+
1014+
// Are there rows modified?
1015+
// Let the user know if the import failed / succeeded
1016+
return (bool) $result;
1017+
}
8671018

8681019
/**
8691020
* This method allows you to specify multiple (method chaining optional) ORDER BY statements for SQL queries.
@@ -921,7 +1072,123 @@ public function groupBy($groupByField)
9211072
$this->_groupBy[] = $groupByField;
9221073
return $this;
9231074
}
924-
1075+
1076+
1077+
/**
1078+
* This method sets the current table lock method.
1079+
*
1080+
* @author Jonas Barascu
1081+
* @param string $method The table lock method. Can be READ or WRITE.
1082+
*
1083+
* @throws Exception
1084+
* @return MysqliDb
1085+
*/
1086+
public function setLockMethod($method)
1087+
{
1088+
// Switch the uppercase string
1089+
switch(strtoupper($method)) {
1090+
// Is it READ or WRITE?
1091+
case "READ" || "WRITE":
1092+
// Succeed
1093+
$this->_tableLockMethod = $method;
1094+
break;
1095+
default:
1096+
// Else throw an exception
1097+
throw new Exception("Bad lock type: Can be either READ or WRITE");
1098+
break;
1099+
}
1100+
return $this;
1101+
}
1102+
1103+
/**
1104+
* Locks a table for R/W action.
1105+
*
1106+
* @author Jonas Barascu
1107+
* @param string $table The table to be locked. Can be a table or a view.
1108+
*
1109+
* @throws Exception
1110+
* @return MysqliDb if succeeeded;
1111+
*/
1112+
public function lock($table)
1113+
{
1114+
// Main Query
1115+
$this->_query = "LOCK TABLES";
1116+
1117+
// Is the table an array?
1118+
if(gettype($table) == "array") {
1119+
// Loop trough it and attach it to the query
1120+
foreach($table as $key => $value) {
1121+
if(gettype($value) == "string") {
1122+
if($key > 0) {
1123+
$this->_query .= ",";
1124+
}
1125+
$this->_query .= " ".self::$prefix.$value." ".$this->_tableLockMethod;
1126+
}
1127+
}
1128+
}
1129+
else{
1130+
// Build the table prefix
1131+
$table = self::$prefix . $table;
1132+
1133+
// Build the query
1134+
$this->_query = "LOCK TABLES ".$table." ".$this->_tableLockMethod;
1135+
}
1136+
1137+
// Exceute the query unprepared because LOCK only works with unprepared statements.
1138+
$result = $this->queryUnprepared($this->_query);
1139+
1140+
// Reset the query
1141+
$this->reset();
1142+
1143+
// Are there rows modified?
1144+
if($result) {
1145+
// Return true
1146+
// We can't return ourself because if one table gets locked, all other ones get unlocked!
1147+
return true;
1148+
}
1149+
// Something went wrong
1150+
else {
1151+
throw new Exception("Locking of table ".$table." failed");
1152+
}
1153+
1154+
// Return the success value
1155+
return false;
1156+
}
1157+
1158+
/**
1159+
* Unlocks all tables in a database.
1160+
* Also commits transactions.
1161+
*
1162+
* @author Jonas Barascu
1163+
* @return MysqliDb
1164+
*/
1165+
public function unlock()
1166+
{
1167+
// Build the query
1168+
$this->_query = "UNLOCK TABLES";
1169+
1170+
// Exceute the query unprepared because UNLOCK and LOCK only works with unprepared statements.
1171+
$result = $this->queryUnprepared($this->_query);
1172+
1173+
// Reset the query
1174+
$this->reset();
1175+
1176+
// Are there rows modified?
1177+
if($result) {
1178+
// return self
1179+
return $this;
1180+
}
1181+
// Something went wrong
1182+
else {
1183+
throw new Exception("Unlocking of tables failed");
1184+
}
1185+
1186+
1187+
// Return self
1188+
return $this;
1189+
}
1190+
1191+
9251192
/**
9261193
* This methods returns the ID of the last inserted item
9271194
*
@@ -2017,4 +2284,4 @@ private function conditionToSql($operator, $val) {
20172284
}
20182285
}
20192286

2020-
// END class
2287+
// END class

0 commit comments

Comments
 (0)