diff --git a/README.md b/README.md index 5783dc2..ac3c37b 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ ProductRepo // this class was generated with echo $pdoOne()->generateCodeClass([ * [2. Running a native PDO statement](#2-running-a-native-pdo-statement) * [3. Running using the query builder](#3-running-using-the-query-builder) * [4. Running using an ORM](#4-running-using-an-orm) + * [5. Run a query with a different mode](#5-run-a-query-with-a-different-mode) * [How to work with Date values?](#how-to-work-with-date-values) * [How to run a transaction?](#how-to-run-a-transaction) * [Custom Queries](#custom-queries) @@ -246,12 +247,13 @@ to the PHP folder and Apache Folder. With the method **RunRawQuery()**, we could execute a command directly to PDO with or without parameters. And it could return a **PdoStatement** or an **array**. It is useful when we want speed. -> **RunRawQuery($rawSql,$param,$returnArray)** +> **RunRawQuery($rawSql,$param,$returnArray,$fetchMode,$fetchArgument)** > > string **$rawSql** The query to execute > array|null **$param** [type1,value1,type2,value2] or [name1=>value,name2=value2] > bool **$returnArray** if true (default) then it returns an array. If false then it returns a **PDOStatement** - +> int **$fetchMode** Indicates the mode to fetch. Example: PDO::FETCH_ASSOC +> null **$fetchArgument** The argument of the fetchMode. ```php $sql='select * from table where id=1'; $pdoStatement=$pdoOne->runRawQuery($sql,[],false); // [] are the parameters @@ -314,7 +316,7 @@ $pdoOne->set(['name'=>'cocacola']) ### 4. Running using an ORM -This library also allows to create an **[orm]**(#orm) of your tables. If you are generated an ORM, then you can use the +The library eftec\PdoOneORM allows to create an **[orm]**(#orm) of your tables. If you are generated an ORM, then you can use the next code ```php @@ -323,6 +325,18 @@ ProductRepo::toList(['category'=>'drink']); Where **ProductRepo** is a service class generated by using the ORM. +### 5. Run a query with a different mode +By default, PdoOne executes the queries in the mode PDO::FETCH_ASSOC +You can change by running the queries as: + +```php +$pdo->setFechMode(PDO::FETCH_CLASS,'stdClass')->runRawQuery($query); +// or you can run as +$pdo->runRawQuery($query,null,true,false,null,PDO::FETCH_CLASS,'stdClass') +``` + + + ## How to work with Date values? PdoOne allows 5 types of dates. @@ -1862,15 +1876,20 @@ In a nutshell: > Every minor version means that it adds a new functionality i.e. 1.5 -> 1.6 (new methods) > > Every decimal version means that it patches/fixes/refactoring a previous functionality i.e. 1.5.0 -> 1.5.1 (fix) + +* 4.8 2024-07-06 + * [upd] added fetchMode for runRawQuery() + * [new] added setFetchMode() + * [new] PdoOneQuery added runRawQuery() which is a wrapper of PdoOne::runRawQuery() * 4.7.1 2024-06-07 - * fixed a phpdoc in PdoOneQuery::where() + * [fix] ixed a phpdoc in PdoOneQuery::where() * 4.7 2024-06-07 - * Update phpdoc using markdown without "php" because PHPStorm is not compatible with it.]() + * [upd] Update phpdoc using markdown without "php" because PHPStorm is not compatible with it. * 4.6.2 2024-03-02 * [fix]PdoOne::$cacheService is now mixed. * 4.6.1 2024-03-02 * [fix]PdoOne::$instance is null by default (instead of undefined) - * updated CliOne dependency. + * [upd] updated CliOne dependency. * 4.6 2024-03-02 * Updating dependency to PHP 7.4. The extended support of PHP 7.2 ended 3 years ago. * Added more type hinting in the code. diff --git a/lib/PdoOne.php b/lib/PdoOne.php index bab41b1..0ec60d2 100644 --- a/lib/PdoOne.php +++ b/lib/PdoOne.php @@ -29,11 +29,11 @@ * @package eftec * @author Jorge Castro Castillo * @copyright (c) Jorge Castro C. Dual Licence: MIT and Commercial License https://github.com/EFTEC/PdoOne - * @version 4.7 + * @version 4.8 */ class PdoOne { - public const VERSION = '4.7'; + public const VERSION = '4.8'; /** @var int We need this value because null and false could be a valid value. */ public const NULL = PHP_INT_MAX; /** @var string Prefix of the related columns. It is used for ORM */ @@ -107,14 +107,14 @@ class PdoOne /** @var string It is generated and set automatically by the type of database */ public string $database_identityName = 'identity'; /** @var string server ip. Ex. 127.0.0.1 127.0.0.1:3306 */ - public string $server=''; - public ?string $user=null; + public string $server = ''; + public ?string $user = null; /** @var null|string the unique id generate by sha256or $hashtype and based in the query, arguments, type * and methods */ - public ?string $uid=null; + public ?string $uid = null; public array $lastBindParam = []; - public ?string $pwd=null; + public ?string $pwd = null; /** @var string The name of the database/schema */ public string $db; /** @var string the name of the locker */ @@ -134,9 +134,9 @@ class PdoOne public array $traceBlackList = []; //['PdoOne.php', 'PdoOneQuery.php', 'PdoOne_Mysql.php', 'PdoOne.Sqlsrv.php', 'PdoOne.Oci.php' //, 'PdoOneTestMockup.php', '_BasePdoOneRepo.php']; /** @var PDO|null */ - public ?PDO $conn1=null; + public ?PDO $conn1 = null; /** @var bool|null True if the transaction is open */ - public ?bool $transactionOpen=null; + public ?bool $transactionOpen = null; /** @var bool if the database is in READ ONLY mode or not. If true then we must avoid to write in the database. */ public bool $readonly = false; /** @var boolean if true then it logs the file using the php log file (if enabled) */ @@ -154,7 +154,7 @@ class PdoOne */ public int $logLevel = 0; /** @var string|null last query executed */ - public ?string $lastQuery=null; + public ?string $lastQuery = null; public ?array $lastParam = []; /** @var array the tables used in the queries and added by the methods from() and join() */ public array $tables = []; @@ -174,14 +174,14 @@ class PdoOne /** @var mixed The service of cache [optional] */ public $cacheService; /** @var null|array it stores the values obtained by $this->tableDependency() */ - public ?array $tableDependencyArrayCol=null; - public ?array $tableDependencyArray=null; + public ?array $tableDependencyArrayCol = null; + public ?array $tableDependencyArray = null; /** @var null|array $partition is an associative array [column=>value] with a fixed and pre-established conditions */ - public ?array $partition=null; + public ?array $partition = null; /** @var MessageContainer|null it stores the messages. */ private ?MessageContainer $messageContainer; /** @var PdoOne|null */ - protected static ?PdoOne $instance=null; + protected static ?PdoOne $instance = null; protected string $tableKV = ''; protected string $defaultTableKV = ''; @@ -1636,18 +1636,20 @@ private function strposa($haystack, $needles = []) ** $values=$con->runRawQuery('select * from table where id=?,[[1,20,PDO::PARAM_INT]]',true); // a full parameter. * * - * @param string $rawSql The query to execute - * @param array|null $params [type1,value1,type2,value2] or [name1=>value,name2=value2] - * @param bool $returnArray if true then it returns an array. If false then it returns a - * PDOStatement - * @param bool $useCache if true then it uses cache (only if the service is available). - * @param null|string|string[] $cacheFamily if cache is used, then it is used to set the family or group of the - * cache. + * @param string $rawSql The query to execute + * @param array|null $params [type1,value1,type2,value2] or [name1=>value,name2=value2] + * @param bool $returnArray if true then it returns an array. If false then it returns a + * PDOStatement + * @param bool $useCache if true then it uses cache (only if the service is available). + * @param null|string|string[] $cacheFamily if cache is used, then it is used to set the family or group of the + * cache. + * @param int $fetchMode The mode to fetch + * @param null $fetchArgument The argument of the fetch model * @return bool|PDOStatement|array an array of associative or a pdo statement. False is the operation fails - * @throws Exception + * @throws JsonException * @test equals [0=>[1=>1]],this('select 1',null,true) */ - public function runRawQuery(string $rawSql, ?array $params = null, ?bool $returnArray = true, bool $useCache = false, $cacheFamily = null) + public function runRawQuery(string $rawSql, ?array $params = null, ?bool $returnArray = true, bool $useCache = false, $cacheFamily = null, int $fetchMode = PDO::FETCH_ASSOC, $fetchArgument = null) { $this->beginTry(); if (!$this->isOpen) { @@ -1687,7 +1689,7 @@ public function runRawQuery(string $rawSql, ?array $params = null, ?bool $return $this->lastQuery = $rawSql; $this->storeInfo("[INFO]\t$rawSql"); if ($params === null) { - $rows = $this->runRawQueryParamLess($rawSql, $returnArray); + $rows = $this->runRawQueryParamLess($rawSql, $returnArray, $fetchMode, $fetchArgument); if ($uid !== false && $returnArray) { $this->internalCache[$uid] = $rows; } @@ -1762,7 +1764,11 @@ public function runRawQuery(string $rawSql, ?array $params = null, ?bool $return return false; } if ($returnArray && $stmt instanceof PDOStatement) { - $rows = ($stmt->columnCount() > 0) ? $stmt->fetchAll(PDO::FETCH_ASSOC) : []; + if ($fetchMode === PDO::FETCH_COLUMN || $fetchMode === PDO::FETCH_CLASS || $fetchMode === PDO::FETCH_FUNC) { + $rows = ($stmt->columnCount() > 0) ? $stmt->fetchAll($fetchMode, $fetchArgument) : []; + } else { + $rows = ($stmt->columnCount() > 0) ? $stmt->fetchAll($fetchMode) : []; + } $this->affected_rows = $stmt->rowCount(); $stmt = null; if ($uid !== false) { @@ -1814,14 +1820,15 @@ public static function queryCommand(string $sql, bool $returnType = false): stri /** * Internal Use: It runs a raw query * - * @param string $rawSql - * @param bool $returnArray - * + * @param string $rawSql The raw sql query to execute. + * @param bool $returnArray If true then it returns an array (or a value determined by fetchMode) + * @param int $fetchMode Indicates the mode to fetch. Example: PDO::FETCH_ASSOC + * @param null $fetchArgument The argument of the fetchMode. * @return array|bool|PDOStatement - * @throws Exception + * @throws JsonException * @see PdoOne::runRawQuery */ - private function runRawQueryParamLess(string $rawSql, bool $returnArray) + private function runRawQueryParamLess(string $rawSql, bool $returnArray, int $fetchMode = PDO::FETCH_ASSOC, $fetchArgument = null) { $this->beginTry(); // the "where" chain doesn't have parameters. @@ -1836,7 +1843,11 @@ private function runRawQueryParamLess(string $rawSql, bool $returnArray) } if ($returnArray && $rows instanceof PDOStatement) { if ($rows->columnCount() > 0) { - $result = @$rows->fetchAll(PDO::FETCH_ASSOC); + if ($fetchMode === PDO::FETCH_COLUMN || $fetchMode === PDO::FETCH_CLASS || $fetchMode === PDO::FETCH_FUNC) { + $result = @$rows->fetchAll($fetchMode, $fetchArgument); + } else { + $result = @$rows->fetchAll($fetchMode); + } $this->affected_rows = $rows->rowCount(); $this->endTry(); return $result; @@ -2268,6 +2279,37 @@ public function generateCodeArray( return str_replace(",$ln]", "$ln]", $result); } + /** + * @param int $mode + * Controls the contents of the returned array as documented in + * PDOStatement::fetch. + * Defaults to value of PDO::ATTR_DEFAULT_FETCH_MODE + * (which defaults to PDO::FETCH_BOTH) + *

+ *

+ * To return an array consisting of all values of a single column from + * the result set, specify PDO::FETCH_COLUMN. You + * can specify which column you want with the + * column-index parameter. + *

+ *

+ * To fetch only the unique values of a single column from the result set, + * bitwise-OR PDO::FETCH_COLUMN with + * PDO::FETCH_UNIQUE. + *

+ *

+ * To return an associative array grouped by the values of a specified + * column, bitwise-OR PDO::FETCH_COLUMN with + * PDO::FETCH_GROUP. + *

+ * @param null $fetchArgument The argument of th fetch mode + * @return PdoOneQuery + */ + public function setFechMode(int $mode = PDO::FETCH_ASSOC, $fetchArgument = null): PdoOneQuery + { + return (new PdoOneQuery($this))->setFechMode($mode, $fetchArgument); + } + /** * This function is used to generate a list of recursive fields. * @param array $getDefTable @@ -2463,7 +2505,7 @@ public function now(): ?string { try { return (new PdoOneQuery($this))->now(); - } catch(Exception $e) { + } catch (Exception $e) { return null; } } @@ -3705,7 +3747,7 @@ public function createFK(string $tableName, array $definitions): bool */ protected function isArrayItems(array $items): bool { - return(isset($items[0]) && is_array($items[0])); + return (isset($items[0]) && is_array($items[0])); } /** @@ -4143,7 +4185,7 @@ public function columnTable(string $tableName) $tmpArr = $r; $r = []; foreach ($tmpArr as $v) { - $row=[]; + $row = []; $row['colname'] = $v['cid']; $row['coltype'] = $v['type']; $row['colsize'] = null; @@ -4152,7 +4194,7 @@ public function columnTable(string $tableName) $row['iskey'] = $v['pk']; $row['isidentity'] = null; $row['isnullable'] = $v['notnull']; - $r[]=$row; + $r[] = $row; } } $this->endTry(); @@ -4177,11 +4219,11 @@ public function foreignKeyTable(string $tableName) $r = []; foreach ($tmpR as $v) { $row = []; - $row['collocal']=$v['from']; - $row['tablerem']=$v['table']; - $row['colrem']=$v['to']; - $row['fk_name']=$row['tablerem'].'_'.$row['colrem']; - $r[]=$row; + $row['collocal'] = $v['from']; + $row['tablerem'] = $v['table']; + $row['colrem'] = $v['to']; + $row['fk_name'] = $row['tablerem'] . '_' . $row['colrem']; + $r[] = $row; } } $this->endTry(); diff --git a/lib/PdoOneQuery.php b/lib/PdoOneQuery.php index 6dc7c5d..bf60e3b 100644 --- a/lib/PdoOneQuery.php +++ b/lib/PdoOneQuery.php @@ -16,13 +16,14 @@ /** * Class PdoOneQuery * - * @version 4.2 + * @version 4.8 * @package eftec * @author Jorge Castro Castillo * @copyright (c) Jorge Castro C. Dual Licence: MIT and Commercial License https://github.com/EFTEC/PdoOne */ class PdoOneQuery { + public const VERSION = '4.8'; // /** @var PdoOne */ public PdoOne $parent; @@ -30,6 +31,9 @@ class PdoOneQuery public $ormClass; /** @var array parameters for the having. [paramvar,value,type,size] */ public array $havingParamAssoc = []; + public int $fetchMode = PDO::FETCH_ASSOC; + /** @var mixed|null */ + public $fetchArgument; public int $whereCounter = 1; /** * @var null|int $ttl If 0 then the cache never expires.
@@ -49,7 +53,7 @@ class PdoOneQuery protected string $order = ''; /** @var bool if true then builderReset will not reset (unless it is force), if false then it will reset */ protected bool $noReset = false; - protected ?string $uid=null; + protected ?string $uid = null; /** @var array */ protected array $where = []; /** @var array parameters for the set. [paramvar,value,type,size] */ @@ -117,7 +121,7 @@ public function toMeta(?string $sql = null, array $args = []) } } /** @var PDOStatement $stmt */ - $stmt = $this->parent->runRawQuery($sql, $args, false, $this->useCache !== false, $this->cacheFamily); + $stmt = $this->runRawQuery($sql, $args, false, $this->useCache !== false, $this->cacheFamily, $this->fetchMode, $this->fetchArgument); } if ($stmt instanceof PDOStatement === false) { $stmt = null; @@ -1066,9 +1070,9 @@ public function fetchLoop(callable $callable, int $pdoMode = PDO::FETCH_ASSOC): if ($stmt === null) { return null; } - $numRow=0; + $numRow = 0; while ($row = $stmt->fetch($pdoMode)) { - $result[] = $callable($row,$numRow); + $result[] = $callable($row, $numRow); $numRow++; } $stmt = null; @@ -1868,7 +1872,7 @@ protected function _insert($tableName = null, $param[] = [':ID_' . $identityColumn, '0', 1, null]; $sql .= ' returning ' . $identityColumn . ' into :ID_' . $identityColumn; } - $this->parent->runRawQuery($sql, $param, true, $this->useCache !== false, $this->cacheFamily); + $this->runRawQuery($sql, $param, true, $this->useCache !== false, $this->cacheFamily, $this->fetchMode, $this->fetchArgument); $this->builderReset(true); if ($this->endtry() === false) { return false; @@ -1883,6 +1887,50 @@ protected function _insert($tableName = null, return $this->parent->insert_id(); } + /** + * It sets the mode to fetch the values. By default it uses PDO::FETCH_ASSOC + * @param int $fetchMode Example: PDO::FETCH_BOTH + * @param mixed|null $fetchArgument the argument of the fetch_mode + * @return PdoOneQuery + * @see https://www.php.net/manual/en/pdostatement.fetch.php + */ + public function setFechMode(int $fetchMode, $fetchArgument = null): PdoOneQuery + { + $this->fetchMode = $fetchMode; + $this->fetchArgument = $fetchArgument; + return $this; + } + + /** + * It runs a raw query + * **Example:** + * ``` + * $values=$con->runRawQuery('select * from table where id=?',[20],true); // with parameter + * $values=$con->runRawQuery('select * from table where id=:name',['name'=>20],true); // with named parameter + * $values=$con->runRawQuery('select * from table,[]',true); // without parameter. + * $values=$con->runRawQuery('select * from table where id=?,[[1,20,PDO::PARAM_INT]]',true); // a full parameter. + * ``` + * + * @param string $rawSql The query to execute + * @param array|null $params [type1,value1,type2,value2] or [name1=>value,name2=value2] + * @param bool $returnArray if true then it returns an array. If false then it returns a + * PDOStatement + * @param bool $useCache if true then it uses cache (only if the service is available). + * @param null|string|string[] $cacheFamily if cache is used, then it is used to set the family or group of the + * cache. + * @param int|null $fetchMode The mode to fetch. By default is PDO::FETCH_ASSOC + * @param null $fetchArgument The argument of the fetch mode + * @return bool|PDOStatement|array an array of associative or a pdo statement. False is the operation fails + * @throws Exception + * @test equals [0=>[1=>1]],this('select 1',null,true) + */ + public function runRawQuery(string $rawSql, ?array $params = null, ?bool $returnArray = true, + bool $useCache = false, $cacheFamily = null, ?int $fetchMode = null, $fetchArgument = null) + { + return $this->parent->runRawQuery($rawSql, $params, $returnArray, $useCache, $cacheFamily, + $fetchMode ?? $this->fetchMode, $fetchArgument ?? $this->fetchArgument); + } + /** * @return string */ @@ -2030,7 +2078,7 @@ public function delete( $sql .= $this->constructWhere(); $param = $this->whereParamAssoc; $this->beginTry(); - $stmt = $this->parent->runRawQuery($sql, $param, false, $this->useCache !== false, $this->cacheFamily); + $stmt = $this->runRawQuery($sql, $param, false, $this->useCache !== false, $this->cacheFamily, $this->fetchMode, $this->fetchArgument); $this->builderReset(true); if ($this->endtry() === false) { return false; @@ -2082,7 +2130,7 @@ public function now(): ?string { $sql = $this->parent->service->now(); try { - $r = $this->parent->runRawQuery($sql); + $r = $this->runRawQuery($sql, null, true, false, null, $this->fetchMode, $this->fetchArgument); } catch (Exception $e) { $this->parent->throwError('Unable to read now() ' . $e->getMessage(), $sql); } @@ -2178,7 +2226,7 @@ public function update( $param = array_merge($this->setParamAssoc, $this->whereParamAssoc); // the order matters. // $this->builderReset(); $this->beginTry(); - $stmt = $this->parent->runRawQuery($sql, $param, false, $this->useCache !== false, $this->cacheFamily); + $stmt = $this->runRawQuery($sql, $param, false, $this->useCache !== false, $this->cacheFamily, $this->fetchMode, $this->fetchArgument); $this->builderReset(true); if ($this->endtry() === false) { return false; diff --git a/tests/PdoOne_mysql_Test.php b/tests/PdoOne_mysql_Test.php index cfeca3a..ce46418 100644 --- a/tests/PdoOne_mysql_Test.php +++ b/tests/PdoOne_mysql_Test.php @@ -19,7 +19,11 @@ use eftec\PdoOne; use eftec\PdoOneQuery; use Exception; +use PDO; use PHPUnit\Framework\TestCase; +class SomeClass { + public $abc; +} /** * Class CacheServicesmysql @@ -108,7 +112,7 @@ class PdoOne_mysql_Test extends TestCase public function setUp(): void { - $this->pdoOne = new PdoOne('mysql', '127.0.0.1', 'travis', '', 'travisdb'); + $this->pdoOne = new PdoOne('mysql', '127.0.0.1', 'travis', 'travis', 'travisdb'); $this->pdoOne->connect(); $this->pdoOne->logLevel = 3; $cache = new CacheServicesmysql(); @@ -134,7 +138,7 @@ public function test_pdo():void //$this->pdoOne->conn1->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,false); $result=$this->pdoOne ->select('select * from table_city') - ->fetchLoop(static function($row) {return($row);},\PDO::FETCH_ASSOC); + ->fetchLoop(static function($row) {return($row);}, PDO::FETCH_ASSOC); // var_dump($result); $x2=memory_get_usage(); var_dump($x2); @@ -1087,6 +1091,14 @@ public function test_runQuery(): void public function test_runRawQuery(): void { self::assertEquals([0 => [1 => 1]], $this->pdoOne->runRawQuery('select 1')); + self::assertIsObject($this->pdoOne->setFechMode(PDO::FETCH_CLASS,'stdClass')->runRawQuery('select 1')[0]); + self::assertIsObject($this->pdoOne->runRawQuery('select 1',null,true,false,null,PDO::FETCH_CLASS,'stdClass')[0]); + //$x=$this->pdoOne->setFechMode(PDO::FETCH_CLASS,'SomeClass')->runRawQuery('select ?',['1'])[0]; + //var_dump($x); + $x=$this->pdoOne->setFechMode(PDO::FETCH_CLASS,SomeClass::class)->runRawQuery('select ? as abc',['1'])[0]; + self::assertInstanceOf(SomeClass::class,$x); + self::assertEquals("1",$x->abc); + } /**