diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 52d75990..00000000 --- a/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -#Mac OS X files -.DS_Store - -#Vim trash -*.swp \ No newline at end of file diff --git a/MysqliDb.php b/MysqliDb.php index f3bc1ca7..6c6b5580 100644 --- a/MysqliDb.php +++ b/MysqliDb.php @@ -81,7 +81,19 @@ class MysqliDb * @var array */ protected $_groupBy = array(); - + + /** + * Dynamic type list for tempromary locking tables. + * @var array + */ + protected $_tableLocks = array(); + + /** + * Variable which holds the current table lock method. + * @var string + */ + protected $_tableLockMethod = "READ"; + /** * Dynamic array that holds a combination of where condition/table data value types and parameter references * @var array @@ -373,6 +385,28 @@ public function setPrefix($prefix = '') return $this; } + /** + * Pushes a unprepared statement to the mysqli stack. + * WARNING: Use with caution. + * This method does not escape strings by default so make sure you'll never use it in production. + * + * @author Jonas Barascu + * @param [[Type]] $query [[Description]] + */ + private function queryUnprepared($query) + { + // Execute query + $stmt = $this->mysqli()->query($query); + + // Failed? + if(!$stmt){ + throw new Exception("Unprepared Query Failed, ERRNO: ".$this->mysqli()->errno." (".$this->mysqli()->error.")"); + }; + + // return stmt for future use + return $stmt; + } + /** * Execute raw SQL query. * @@ -856,6 +890,123 @@ public function join($joinTable, $joinCondition, $joinType = '') return $this; } + + + /** + * This is a basic method which allows you to import raw .CSV data into a table + * Please check out http://dev.mysql.com/doc/refman/5.7/en/load-data.html for a valid .csv file. + + * @author Jonas Barascu (Noneatme) + * @param string $importTable The database table where the data will be imported into. + * @param string $importFile The file to be imported. Please use double backslashes \\ and make sure you + * @param string $importSettings An Array defining the import settings as described in the README.md + * @return boolean + */ + public function loadData($importTable, $importFile, $importSettings = null) + { + // We have to check if the file exists + if(!file_exists($importFile)) { + // Throw an exception + throw new Exception("importCSV -> importFile ".$importFile." does not exists!"); + return; + } + + // Define the default values + // We will merge it later + $settings = Array("fieldChar" => ';', "lineChar" => PHP_EOL, "linesToIgnore" => 1); + + // Check the import settings + if(gettype($importSettings) == "array") { + // Merge the default array with the custom one + $settings = array_merge($settings, $importSettings); + } + + // Add the prefix to the import table + $table = self::$prefix . $importTable; + + // Add 1 more slash to every slash so maria will interpret it as a path + $importFile = str_replace("\\", "\\\\", $importFile); + + // Build SQL Syntax + $sqlSyntax = sprintf('LOAD DATA INFILE \'%s\' INTO TABLE %s', + $importFile, $table); + + // FIELDS + $sqlSyntax .= sprintf(' FIELDS TERMINATED BY \'%s\'', $settings["fieldChar"]); + if(isset($settings["fieldEnclosure"])) { + $sqlSyntax .= sprintf(' ENCLOSED BY \'%s\'', $settings["fieldEnclosure"]); + } + + // LINES + $sqlSyntax .= sprintf(' LINES TERMINATED BY \'%s\'', $settings["lineChar"]); + if(isset($settings["lineStarting"])) { + $sqlSyntax .= sprintf(' STARTING BY \'%s\'', $settings["lineStarting"]); + } + + // IGNORE LINES + $sqlSyntax .= sprintf(' IGNORE %d LINES', $settings["linesToIgnore"]); + + // Exceute the query unprepared because LOAD DATA only works with unprepared statements. + $result = $this->queryUnprepared($sqlSyntax); + + // Are there rows modified? + // Let the user know if the import failed / succeeded + return (bool) $result; + } + + /** + * This method is usefull for importing XML files into a specific table. + * Check out the LOAD XML syntax for your MySQL server. + * + * @author Jonas Barascu + * @param string $importTable The table in which the data will be imported to. + * @param string $importFile The file which contains the .XML data. + * @param string $importSettings An Array defining the import settings as described in the README.md + * + * @return boolean Returns true if the import succeeded, false if it failed. + */ + public function loadXml($importTable, $importFile, $importSettings = null) + { + // We have to check if the file exists + if(!file_exists($importFile)) { + // Does not exists + throw new Exception("loadXml: Import file does not exists"); + return; + } + + // Create default values + $settings = Array("linesToIgnore" => 0); + + // Check the import settings + if(gettype($importSettings) == "array") { + $settings = array_merge($settings, $importSettings); + } + + // Add the prefix to the import table + $table = self::$prefix . $importTable; + + // Add 1 more slash to every slash so maria will interpret it as a path + $importFile = str_replace("\\", "\\\\", $importFile); + + // Build SQL Syntax + $sqlSyntax = sprintf('LOAD XML INFILE \'%s\' INTO TABLE %s', + $importFile, $table); + + // FIELDS + if(isset($settings["rowTag"])) { + $sqlSyntax .= sprintf(' ROWS IDENTIFIED BY \'%s\'', $settings["rowTag"]); + } + + // IGNORE LINES + $sqlSyntax .= sprintf(' IGNORE %d LINES', $settings["linesToIgnore"]); + + // Exceute the query unprepared because LOAD XML only works with unprepared statements. + $result = $this->queryUnprepared($sqlSyntax); + + // Are there rows modified? + // Let the user know if the import failed / succeeded + return (bool) $result; + } /** * This method allows you to specify multiple (method chaining optional) ORDER BY statements for SQL queries. @@ -913,7 +1064,123 @@ public function groupBy($groupByField) $this->_groupBy[] = $groupByField; return $this; } - + + + /** + * This method sets the current table lock method. + * + * @author Jonas Barascu + * @param string $method The table lock method. Can be READ or WRITE. + * + * @throws Exception + * @return MysqliDb + */ + public function setLockMethod($method) + { + // Switch the uppercase string + switch(strtoupper($method)) { + // Is it READ or WRITE? + case "READ" || "WRITE": + // Succeed + $this->_tableLockMethod = $method; + break; + default: + // Else throw an exception + throw new Exception("Bad lock type: Can be either READ or WRITE"); + break; + } + return $this; + } + + /** + * Locks a table for R/W action. + * + * @author Jonas Barascu + * @param string $table The table to be locked. Can be a table or a view. + * + * @throws Exception + * @return MysqliDb if succeeeded; + */ + public function lock($table) + { + // Main Query + $this->_query = "LOCK TABLES"; + + // Is the table an array? + if(gettype($table) == "array") { + // Loop trough it and attach it to the query + foreach($table as $key => $value) { + if(gettype($value) == "string") { + if($key > 0) { + $this->_query .= ","; + } + $this->_query .= " ".self::$prefix.$value." ".$this->_tableLockMethod; + } + } + } + else{ + // Build the table prefix + $table = self::$prefix . $table; + + // Build the query + $this->_query = "LOCK TABLES ".$table." ".$this->_tableLockMethod; + } + + // Exceute the query unprepared because LOCK only works with unprepared statements. + $result = $this->queryUnprepared($this->_query); + + // Reset the query + $this->reset(); + + // Are there rows modified? + if($result) { + // Return true + // We can't return ourself because if one table gets locked, all other ones get unlocked! + return true; + } + // Something went wrong + else { + throw new Exception("Locking of table ".$table." failed"); + } + + // Return the success value + return false; + } + + /** + * Unlocks all tables in a database. + * Also commits transactions. + * + * @author Jonas Barascu + * @return MysqliDb + */ + public function unlock() + { + // Build the query + $this->_query = "UNLOCK TABLES"; + + // Exceute the query unprepared because UNLOCK and LOCK only works with unprepared statements. + $result = $this->queryUnprepared($this->_query); + + // Reset the query + $this->reset(); + + // Are there rows modified? + if($result) { + // return self + return $this; + } + // Something went wrong + else { + throw new Exception("Unlocking of tables failed"); + } + + + // Return self + return $this; + } + + /** * This methods returns the ID of the last inserted item * @@ -1908,4 +2175,4 @@ public function paginate ($table, $page, $fields = null) { } } -// END class +// END class \ No newline at end of file diff --git a/readme.md b/readme.md index 8cc97059..4c7c7666 100644 --- a/readme.md +++ b/readme.md @@ -1,12 +1,15 @@ MysqliDb -- Simple MySQLi wrapper and object mapper with prepared statements
### Table of Contents + **[Initialization](#initialization)** **[Objects mapping](#objects-mapping)** **[Insert Query](#insert-query)** **[Update Query](#update-query)** **[Select Query](#select-query)** **[Delete Query](#delete-query)** +**[Insert Data](#insert-data)** +**[Insert XML](#insert-xml)** **[Running raw SQL queries](#running-raw-sql-queries)** **[Query Keywords](#query-keywords)** **[Where Conditions](#where--having-methods)** @@ -19,7 +22,8 @@ MysqliDb -- Simple MySQLi wrapper and object mapper with prepared statements **[Has method](#has-method)** **[Helper Methods](#helper-methods)** **[Transaction Helpers](#transaction-helpers)** -**[Error Helpers](#error-helpers)** +**[Error Helpers](#error-helpers)** +**[Table Locking](#table-locking)** ## Support Me @@ -212,6 +216,55 @@ foreach ($logins as $login) echo $login; ``` +###Insert Data +You can also load .CSV or .XML data into a specific table. +To insert .csv data, use the following syntax: +```php +$path_to_file = "/home/john/file.csv"; +$db->loadData("users", $path_to_file); +``` +This will load a .csv file called **file.csv** in the folder **/home/john/** (john's home directory.) +You can also attach an optional array of options. +Valid options are: + +```php +Array( + "fieldChar" => ';', // Char which separates the data + "lineChar" => '\r\n', // Char which separates the lines + "linesToIgnore" => 1 // Amount of lines to ignore at the beginning of the import +); +``` + +Attach them using +```php +$options = Array("fieldChar" => ';', "lineChar" => '\r\n', "linesToIgnore" => 1); +$db->loadData("users", "/home/john/file.csv", $options); +``` + +###Insert XML +To load XML data into a table, you can use the method **loadXML**. +The syntax is smillar to the loadData syntax. +```php +$path_to_file = "/home/john/file.xml"; +$db->loadXML("users", $path_to_file); +``` + +You can also add optional parameters. +Valid parameters: +```php +Array( + "linesToIgnore" => 0, // Amount of lines / rows to ignore at the beginning of the import + "rowTag" => "" // The tag which marks the beginning of an entry +) +``` + +Usage: +```php +$options = Array("linesToIgnore" => 0, "rowTag" => ""): +$path_to_file = "/home/john/file.xml"; +$db->loadXML("users", $path_to_file, $options); +``` + ###Pagination Use paginate() instead of get() to fetch paginated result ```php @@ -630,6 +683,7 @@ if (!$db->insert ('myTable', $insertData)) { } ``` + ### Error helpers After you executed a query you have options to check if there was an error. You can get the MySQL error string or the error code for the last executed query. ```php @@ -668,3 +722,25 @@ print_r ($db->trace); ) ``` + +##Table Locking +To lock tables, you can use the **lock** method together with **setLockMethod**. +The following example will lock the table **users** for **write** access. +```php +$db->setLockMethod("WRITE")->lock("users"); +``` + +Calling another **->lock()** will remove the first lock. +You can also use +```php +$db->unlock(); +``` +to unlock the previous locked tables. +To lock multiple tables, you can use an array. +Example: +```php +$db->setLockMethod("READ")->lock(array("users", "log")); +``` +This will lock the tables **users** and **log** for **READ** access only. +Make sure you use **unlock()* afterwards or your tables will remain locked! + diff --git a/tests/dataimport/data.csv b/tests/dataimport/data.csv new file mode 100644 index 00000000..5c0c627e --- /dev/null +++ b/tests/dataimport/data.csv @@ -0,0 +1,6 @@ +id;username;name +3;simon;Simon Jarred +4;martin;Martin Fuel +5;example;Example Name 1 +6;example2;Example Name 2 +7;example3;Example Name 4 \ No newline at end of file diff --git a/tests/dataimport/data.xml b/tests/dataimport/data.xml new file mode 100644 index 00000000..3cb5669c --- /dev/null +++ b/tests/dataimport/data.xml @@ -0,0 +1,8 @@ + + + + + Likame + Datataa + + diff --git a/tests/dataimport/test.import.php b/tests/dataimport/test.import.php new file mode 100644 index 00000000..f637bdd8 --- /dev/null +++ b/tests/dataimport/test.import.php @@ -0,0 +1,61 @@ +loadData("users", "datanew.csv"); +} +catch(Exception $e) +{ + echo "Test 1 Succeeded!", PHP_EOL; + // goto newtest + goto test_import1; +} +test_import1: +{ + try + { + // Import the CSV + $db->loadData("users", // Table + "D:\\DEV\\git\\PHP-MySQLi-Database-Class\\tests\\dataimport\\data.csv", + Array("fieldEnclosure" => '', "lineStarting" => '')); + echo "Test 2 Succeeded!", PHP_EOL; + + goto test_import2; + } + catch(Exception $e) + { + echo($e); + } +} +test_import2: +{ + try + { + $db->setLockMethod("WRITE")->lock(array("users", "log")); + + $db->loadXML("users", + "D:\\DEV\\git\\PHP-MySQLi-Database-Class\\tests\\dataimport\\data.xml"); + echo "Test 3 Succeeded!", PHP_EOL; + + $db->unlock(); + + } + catch(Exception $e) + { + echo($e); + } +} \ No newline at end of file diff --git a/tests/dataimport/users.sql b/tests/dataimport/users.sql new file mode 100644 index 00000000..04b48195 --- /dev/null +++ b/tests/dataimport/users.sql @@ -0,0 +1,45 @@ +-- phpMyAdmin SQL Dump +-- version 4.5.1 +-- http://www.phpmyadmin.net +-- +-- Host: 127.0.0.1 +-- Erstellungszeit: 27. Jun 2016 um 14:21 +-- Server-Version: 10.1.13-MariaDB +-- PHP-Version: 5.6.20 + +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; + +-- +-- Datenbank: `db_test` +-- + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `users` +-- + +CREATE TABLE `users` ( + `id` int(11) NOT NULL, + `username` varchar(32) NOT NULL, + `name` varchar(32) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- +-- Daten für Tabelle `users` +-- + +INSERT INTO `users` (`id`, `username`, `name`) VALUES +(1, 'test_1', 'John Doe'), +(2, 'test_2', 'Test User'); + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;