diff --git a/README.md b/README.md index 70a0001..0ac5e82 100644 --- a/README.md +++ b/README.md @@ -1953,8 +1953,18 @@ 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) + + + + +* 2.9.4 2021-03-22 + * **ORM:** **_BasePdoOneRepo** is updated to BINARYVERSION=4. If you are using the ORM, then you should rebuild all ORM classes. + * **CORE:** Query chains creates a new instance of **PdoOneQuery** instead of a instance of **PdoOne**, so it is possible to define + multiples chains without interfering each others. + * **ORM:** Fixed a problem with the base class. Now the Base class contains the constant COMPILEDVERSION + * **ORM**: Fixed a problem with the model and the method toArray() * 2.9.3 2021-02-22 - * fix a bug with useCache() when we use with where() + * fix a bug with **useCache()** when we use with where() * 2.9.2 2021-02-18 * fixed a bug with createSequence() * 2.9.1 2021-02-16 @@ -2032,7 +2042,6 @@ echo $this->internalCacheCounter; ``` * The internal cache is tested with runRawQuery (if returns an array), toList(), meta() and first() - * 2.0.1 2020-08-12 * Fixed a bug with the generated code with linked relation manytoone and onetonone. * 2.0 2020-08-11 diff --git a/composer.json b/composer.json index dc31d31..f67c45e 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,6 @@ { "name": "eftec/pdoone", "description": "Procedural PDO Data access class in a single Class", - "version": "2.9.3", "type": "library", "keywords": [ "dao", diff --git a/examples/repomysql/generated/AbstractTableCategoryRepo.php b/examples/repomysql/generated/AbstractTableCategoryRepo.php deleted file mode 100644 index 61b393d..0000000 --- a/examples/repomysql/generated/AbstractTableCategoryRepo.php +++ /dev/null @@ -1,403 +0,0 @@ - - * $code=$pdoOne->generateCodeClass('TableCategory','repomysql',array(),array('TableParent'=>'TableParentRepo','TableChild'=>'TableChildRepo','TableGrandChild'=>'TableGrandChildRepo','TableGrandChildTag'=>'TableGrandChildTagRepo','TableParentxCategory'=>'TableParentxCategoryRepo','TableCategory'=>'TableCategoryRepo','TableParentExt'=>'TableParentExtRepo',),array(),'','','TestDb','mysql\repomodel\TableCategoryModel',array(),array()); - * - */ -abstract class AbstractTableCategoryRepo extends TestDb -{ - const TABLE = 'TableCategory'; - const COMPILEDVERSION=3; - const IDENTITY = NULL; - const PK = [ - 'IdTableCategoryPK' - ]; - const ME=__CLASS__; - const EXTRACOLS=''; - - /** - * It returns the definitions of the columns
- * Example:
- *
-     * self::getDef(); // ['colName'=>[php type,php conversion type,type,size,nullable,extra,sql],'colName2'=>..]
-     * self::getDef('sql'); // ['colName'=>'sql','colname2'=>'sql2']
-     * self::getDef('identity',true); // it returns the columns that are identities ['col1','col2']
-     * 
- * PHP Types: binary, date, datetime, decimal/float,int, string,time, timestamp
- * PHP Conversions: datetime (datetime class), datetime2 (iso),datetime3 (human string) - * , datetime4 (sql no conversion!), timestamp (int), bool, int, float
- * Param Types: PDO::PARAM_LOB, PDO::PARAM_STR, PDO::PARAM_INT
- * - * @param string|null $column =['phptype','conversion','type','size','null','identity','sql'][$i] - * if not null then it only returns the column specified. - * @param string|null $filter If filter is not null, then it uses the column to filter the result. - * - * @return array|array[] - */ - public static function getDef($column=null,$filter=null) { - $r = [ - 'IdTableCategoryPK' => [ - 'phptype' => 'int', - 'conversion' => 'int', - 'type' => 'int', - 'size' => NULL, - 'null' => FALSE, - 'identity' => FALSE, - 'sql' => 'int not null' - ], - 'Name' => [ - 'phptype' => 'string', - 'conversion' => NULL, - 'type' => 'varchar', - 'size' => '200', - 'null' => TRUE, - 'identity' => FALSE, - 'sql' => 'varchar(200)' - ] - ]; - if($column!==null) { - if($filter===null) { - foreach($r as $k=>$v) { - $r[$k]=$v[$column]; - } - } else { - $new=[]; - foreach($r as $k=>$v) { - if($v[$column]===$filter) { - $new[]=$k; - } - } - return $new; - } - } - return $r; - } - - /** - * It converts a row returned from the database.
- * If the column is missing then it sets the field as null. - * - * @param array $row [ref] - */ - public static function convertOutputVal(&$row) { - if($row===false || $row===null) { - return; - } - $row['IdTableCategoryPK']=isset($row['IdTableCategoryPK']) ? (int)$row['IdTableCategoryPK'] : null; - !isset($row['Name']) and $row['Name']=null; // varchar - - } - - /** - * It converts a row to be inserted or updated into the database.
- * If the column is missing then it is ignored and not converted. - * - * @param array $row [ref] - */ - public static function convertInputVal(&$row) { - isset($row['IdTableCategoryPK']) and $row['IdTableCategoryPK']=(int)$row['IdTableCategoryPK']; - } - - - /** - * It gets all the name of the columns. - * - * @return string[] - */ - public static function getDefName() { - return [ - 'IdTableCategoryPK', - 'Name' - ]; - } - - /** - * It returns an associative array (colname=>key type) with all the keys/indexes (if any) - * - * @return string[] - */ - public static function getDefKey() { - return [ - 'IdTableCategoryPK' => 'PRIMARY KEY' - ]; - } - - /** - * It returns a string array with the name of the columns that are skipped when insert - * @return string[] - */ - public static function getDefNoInsert() { - return [ - - ]; - } - - /** - * It returns a string array with the name of the columns that are skipped when update - * @return string[] - */ - public static function getDefNoUpdate() { - return [ - 'IdTableCategoryPK' - ]; - } - - /** - * It adds a where to the query pipeline. It could be stacked with many where() - * Example:
- *
-     * self::where(['col'=>'value'])::toList();
-     * self::where(['col']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
-     * self::where(['col=?']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
-     * 
- * - * @param array|string $sql =self::factory() - * @param null|array|int $param - * - * @return TableCategoryRepo - */ - public static function where($sql, $param = PdoOne::NULL) - { - self::getPdoOne()->where($sql, $param,false,TableCategoryRepo::TABLE); - return TableCategoryRepo::class; - } - - public static function getDefFK($structure=false) { - if ($structure) { - return [ - - ]; - } - /* key,refcol,reftable,extra */ - return [ - '_TableParentxCategory' => [ - 'key' => 'ONETOMANY', - 'col' => 'IdTableCategoryPK', - 'reftable' => 'TableParentxCategory', - 'refcol' => '_idcategoryPKFK' - ] - ]; - } - - /** - * It returns all the relational fields by type. '*' returns all types.
- * It doesn't return normal columns. - * - * @param string $type=['*','MANYTOONE','ONETOMANY','ONETOONE','MANYTOMANY'][$i] - * - * @return string[] - * @noinspection SlowArrayOperationsInLoopInspection - */ - public static function getRelations($type='all') { - $r= [ - 'ONETOMANY' => [ - '_TableParentxCategory' - ] - ]; - if($type==='*') { - $result=[]; - foreach($r as $arr) { - $result = array_merge($result,$arr); - } - return $result; - } - return isset($r[$type]) ? $r[$type] : []; - - } - - public static function toList($filter=PdoOne::NULL,$filterValue=null) { - if(self::$useModel) { - return TableCategoryModel::fromArrayMultiple( self::_toList($filter, $filterValue)); - } - return self::_toList($filter, $filterValue); - } - - /** - * It sets the recursivity. By default, if we query or modify a value, it operates with the fields of the entity. - * With recursivity, we could use the recursivity of the fields, for example, loading a MANYTOONE relation
- * Example:
- *
-     * self::setRecursive([]); // (default) no use recursivity.
-     * self::setRecursive('*'); // recursive every MANYTOONE,ONETOONE,MANYTOONE and ONETOONE relations (first level) 
-     * self::setRecursive('MANYTOONE'); // recursive all relations of the type MANYTOONE (first level)
-     * self::setRecursive(['_relation1','_relation2']); // recursive only the relations of the first level 
-     * self::setRecursive(['_relation1','_relation1/_subrelation1']); // recursive the relations (first and second level)
-     * 
- * If array then it uses the values to set the recursivity.
- * If string then the values allowed are '*', 'MANYTOONE','ONETOMANY','MANYTOMANY','ONETOONE' (first level only)
- * - * @param string|array $recursive=self::factory(); - * - * @return TableCategoryRepo - */ - public static function setRecursive($recursive=[]) - { - if(is_string($recursive)) { - $recursive=TableCategoryRepo::getRelations($recursive); - } - return parent::_setRecursive($recursive); - } - - public static function limit($sql) - { - self::getPdoOne()->limit($sql); - return TableCategoryRepo::class; - } - - /** - * It returns the first row of a query. - * @param array|mixed|null $pk [optional] Specify the value of the primary key. - * - * @return array|bool It returns false if not file is found. - * @throws Exception - */ - public static function first($pk = PdoOne::NULL) { - if(self::$useModel) { - return TableCategoryModel::fromArray(self::_first($pk)); - } - return self::_first($pk); - } - - /** - * It returns true if the entity exists, otherwise false.
- * Example:
- *
-     *  $this->exist(['id'=>'a1','name'=>'name']); // using an array
-     *  $this->exist('a1'); // using the primary key. The table needs a pks and it only works with the first pk.
-     *  
- * - * @param array|mixed $entity =self::factory() - * - * @return bool true if the pks exists - * @throws Exception - */ - public static function exist($entity) { - return self::_exist($entity); - } - - /** - * It inserts a new entity(row) into the database
- * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function insert(&$entity,$transactional=true) { - return self::_insert($entity,$transactional); - } - - /** - * It merge a new entity(row) into the database. If the entity exists then it is updated, otherwise the entity is - * inserted
- * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function merge(&$entity,$transactional=true) { - return self::_merge($entity,$transactional); - } - - /** - * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function update($entity,$transactional=true) { - return self::_update($entity,$transactional); - } - - /** - * It deletes an entity by the primary key - * - * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return mixed - * @throws Exception - */ - public static function delete($entity,$transactional=true) { - return self::_delete($entity,$transactional); - } - - /** - * It deletes an entity by the primary key. - * - * @param array $pk =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return mixed - * @throws Exception - */ - public static function deleteById($pk,$transactional=true) { - return self::_deleteById($pk,$transactional); - } - - /** - * Returns an array with the default values (0 for numbers, empty for string, and array|null if recursive) - * - * @param array|null $values =self::factory() - * @param string $recursivePrefix It is the prefix of the recursivity. - * - * @return array - */ - public static function factory($values = null, $recursivePrefix = '') { - $recursive=static::getRecursive(); - static::setRecursive(); // reset the recursivity. - $row= [ - 'IdTableCategoryPK'=>0, - '_TableParentxCategory'=>(in_array($recursivePrefix.'_TableParentxCategory',$recursive,true)) - ? [] - : null, /* ONETOMANY! */ - 'Name'=>'' - ]; - - if ($values !== null) { - $row = array_merge($row, $values); - } - return $row; - } - - /** - * It returns an empty array with null values and no recursivity. - * @param array|null $values=self::factoryNull() - * - * @return array - */ - public static function factoryNull($values=null) { - $row= [ - 'IdTableCategoryPK'=>null, - '_TableParentxCategory'=>null, /* ONETOMANY! */ - 'Name'=>null - ]; - if ($values !== null) { - $row = array_merge($row, $values); - } - return $row; - } - -} \ No newline at end of file diff --git a/examples/repomysql/generated/AbstractTableChildRepo.php b/examples/repomysql/generated/AbstractTableChildRepo.php deleted file mode 100644 index 540b3f2..0000000 --- a/examples/repomysql/generated/AbstractTableChildRepo.php +++ /dev/null @@ -1,443 +0,0 @@ - - * $code=$pdoOne->generateCodeClass('TableChild','repomysql',array(),array('TableParent'=>'TableParentRepo','TableChild'=>'TableChildRepo','TableGrandChild'=>'TableGrandChildRepo','TableGrandChildTag'=>'TableGrandChildTagRepo','TableParentxCategory'=>'TableParentxCategoryRepo','TableCategory'=>'TableCategoryRepo','TableParentExt'=>'TableParentExtRepo',),array(),'','','TestDb','mysql\repomodel\TableChildModel',array(),array()); - * - */ -abstract class AbstractTableChildRepo extends TestDb -{ - const TABLE = 'TableChild'; - const COMPILEDVERSION=3; - const IDENTITY = 'idtablachildPK'; - const PK = [ - 'idtablachildPK' - ]; - const ME=__CLASS__; - const EXTRACOLS=''; - - /** - * It returns the definitions of the columns
- * Example:
- *
-     * self::getDef(); // ['colName'=>[php type,php conversion type,type,size,nullable,extra,sql],'colName2'=>..]
-     * self::getDef('sql'); // ['colName'=>'sql','colname2'=>'sql2']
-     * self::getDef('identity',true); // it returns the columns that are identities ['col1','col2']
-     * 
- * PHP Types: binary, date, datetime, decimal/float,int, string,time, timestamp
- * PHP Conversions: datetime (datetime class), datetime2 (iso),datetime3 (human string) - * , datetime4 (sql no conversion!), timestamp (int), bool, int, float
- * Param Types: PDO::PARAM_LOB, PDO::PARAM_STR, PDO::PARAM_INT
- * - * @param string|null $column =['phptype','conversion','type','size','null','identity','sql'][$i] - * if not null then it only returns the column specified. - * @param string|null $filter If filter is not null, then it uses the column to filter the result. - * - * @return array|array[] - */ - public static function getDef($column=null,$filter=null) { - $r = [ - 'idtablachildPK' => [ - 'phptype' => 'int', - 'conversion' => 'int', - 'type' => 'int', - 'size' => NULL, - 'null' => FALSE, - 'identity' => TRUE, - 'sql' => 'int not null auto_increment' - ], - 'NameChild' => [ - 'phptype' => 'string', - 'conversion' => NULL, - 'type' => 'varchar', - 'size' => '50', - 'null' => TRUE, - 'identity' => FALSE, - 'sql' => 'varchar(50)' - ], - 'idgrandchildFK' => [ - 'phptype' => 'int', - 'conversion' => 'int', - 'type' => 'int', - 'size' => NULL, - 'null' => TRUE, - 'identity' => FALSE, - 'sql' => 'int' - ] - ]; - if($column!==null) { - if($filter===null) { - foreach($r as $k=>$v) { - $r[$k]=$v[$column]; - } - } else { - $new=[]; - foreach($r as $k=>$v) { - if($v[$column]===$filter) { - $new[]=$k; - } - } - return $new; - } - } - return $r; - } - - /** - * It converts a row returned from the database.
- * If the column is missing then it sets the field as null. - * - * @param array $row [ref] - */ - public static function convertOutputVal(&$row) { - if($row===false || $row===null) { - return; - } - $row['idtablachildPK']=isset($row['idtablachildPK']) ? (int)$row['idtablachildPK'] : null; - !isset($row['NameChild']) and $row['NameChild']=null; // varchar - $row['idgrandchildFK']=isset($row['idgrandchildFK']) ? (int)$row['idgrandchildFK'] : null; - isset($row['_idgrandchildFK']) - and $row['_idgrandchildFK']['idgrandchildPK']=&$row['idgrandchildFK']; // linked MANYTOONE - - } - - /** - * It converts a row to be inserted or updated into the database.
- * If the column is missing then it is ignored and not converted. - * - * @param array $row [ref] - */ - public static function convertInputVal(&$row) { - isset($row['idtablachildPK']) and $row['idtablachildPK']=(int)$row['idtablachildPK']; - isset($row['idgrandchildFK']) and $row['idgrandchildFK']=(int)$row['idgrandchildFK']; - } - - - /** - * It gets all the name of the columns. - * - * @return string[] - */ - public static function getDefName() { - return [ - 'idtablachildPK', - 'NameChild', - 'idgrandchildFK' - ]; - } - - /** - * It returns an associative array (colname=>key type) with all the keys/indexes (if any) - * - * @return string[] - */ - public static function getDefKey() { - return [ - 'idtablachildPK' => 'PRIMARY KEY', - 'idgrandchildFK' => 'KEY' - ]; - } - - /** - * It returns a string array with the name of the columns that are skipped when insert - * @return string[] - */ - public static function getDefNoInsert() { - return [ - 'idtablachildPK' - ]; - } - - /** - * It returns a string array with the name of the columns that are skipped when update - * @return string[] - */ - public static function getDefNoUpdate() { - return [ - 'idtablachildPK' - ]; - } - - /** - * It adds a where to the query pipeline. It could be stacked with many where() - * Example:
- *
-     * self::where(['col'=>'value'])::toList();
-     * self::where(['col']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
-     * self::where(['col=?']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
-     * 
- * - * @param array|string $sql =self::factory() - * @param null|array|int $param - * - * @return TableChildRepo - */ - public static function where($sql, $param = PdoOne::NULL) - { - self::getPdoOne()->where($sql, $param,false,TableChildRepo::TABLE); - return TableChildRepo::class; - } - - public static function getDefFK($structure=false) { - if ($structure) { - return [ - 'idgrandchildFK' => 'FOREIGN KEY REFERENCES`TableGrandChild`(`idgrandchildPK`)' - ]; - } - /* key,refcol,reftable,extra */ - return [ - 'idgrandchildFK' => [ - 'key' => 'FOREIGN KEY', - 'refcol' => 'idgrandchildPK', - 'reftable' => 'TableGrandChild', - 'extra' => '', - 'name' => 'tablachild_fk1' - ], - '_idgrandchildFK' => [ - 'key' => 'MANYTOONE', - 'refcol' => 'idgrandchildPK', - 'reftable' => 'TableGrandChild', - 'extra' => '', - 'name' => 'tablachild_fk1' - ], - '_TableParent' => [ - 'key' => 'ONETOMANY', - 'col' => 'idtablachildPK', - 'reftable' => 'TableParent', - 'refcol' => '_idchild2FK' - ] - ]; - } - - /** - * It returns all the relational fields by type. '*' returns all types.
- * It doesn't return normal columns. - * - * @param string $type=['*','MANYTOONE','ONETOMANY','ONETOONE','MANYTOMANY'][$i] - * - * @return string[] - * @noinspection SlowArrayOperationsInLoopInspection - */ - public static function getRelations($type='all') { - $r= [ - 'MANYTOONE' => [ - '_idgrandchildFK' - ], - 'ONETOMANY' => [ - '_TableParent' - ] - ]; - if($type==='*') { - $result=[]; - foreach($r as $arr) { - $result = array_merge($result,$arr); - } - return $result; - } - return isset($r[$type]) ? $r[$type] : []; - - } - - public static function toList($filter=PdoOne::NULL,$filterValue=null) { - if(self::$useModel) { - return TableChildModel::fromArrayMultiple( self::_toList($filter, $filterValue)); - } - return self::_toList($filter, $filterValue); - } - - /** - * It sets the recursivity. By default, if we query or modify a value, it operates with the fields of the entity. - * With recursivity, we could use the recursivity of the fields, for example, loading a MANYTOONE relation
- * Example:
- *
-     * self::setRecursive([]); // (default) no use recursivity.
-     * self::setRecursive('*'); // recursive every MANYTOONE,ONETOONE,MANYTOONE and ONETOONE relations (first level) 
-     * self::setRecursive('MANYTOONE'); // recursive all relations of the type MANYTOONE (first level)
-     * self::setRecursive(['_relation1','_relation2']); // recursive only the relations of the first level 
-     * self::setRecursive(['_relation1','_relation1/_subrelation1']); // recursive the relations (first and second level)
-     * 
- * If array then it uses the values to set the recursivity.
- * If string then the values allowed are '*', 'MANYTOONE','ONETOMANY','MANYTOMANY','ONETOONE' (first level only)
- * - * @param string|array $recursive=self::factory(); - * - * @return TableChildRepo - */ - public static function setRecursive($recursive=[]) - { - if(is_string($recursive)) { - $recursive=TableChildRepo::getRelations($recursive); - } - return parent::_setRecursive($recursive); - } - - public static function limit($sql) - { - self::getPdoOne()->limit($sql); - return TableChildRepo::class; - } - - /** - * It returns the first row of a query. - * @param array|mixed|null $pk [optional] Specify the value of the primary key. - * - * @return array|bool It returns false if not file is found. - * @throws Exception - */ - public static function first($pk = PdoOne::NULL) { - if(self::$useModel) { - return TableChildModel::fromArray(self::_first($pk)); - } - return self::_first($pk); - } - - /** - * It returns true if the entity exists, otherwise false.
- * Example:
- *
-     *  $this->exist(['id'=>'a1','name'=>'name']); // using an array
-     *  $this->exist('a1'); // using the primary key. The table needs a pks and it only works with the first pk.
-     *  
- * - * @param array|mixed $entity =self::factory() - * - * @return bool true if the pks exists - * @throws Exception - */ - public static function exist($entity) { - return self::_exist($entity); - } - - /** - * It inserts a new entity(row) into the database
- * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function insert(&$entity,$transactional=true) { - return self::_insert($entity,$transactional); - } - - /** - * It merge a new entity(row) into the database. If the entity exists then it is updated, otherwise the entity is - * inserted
- * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function merge(&$entity,$transactional=true) { - return self::_merge($entity,$transactional); - } - - /** - * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function update($entity,$transactional=true) { - return self::_update($entity,$transactional); - } - - /** - * It deletes an entity by the primary key - * - * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return mixed - * @throws Exception - */ - public static function delete($entity,$transactional=true) { - return self::_delete($entity,$transactional); - } - - /** - * It deletes an entity by the primary key. - * - * @param array $pk =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return mixed - * @throws Exception - */ - public static function deleteById($pk,$transactional=true) { - return self::_deleteById($pk,$transactional); - } - - /** - * Returns an array with the default values (0 for numbers, empty for string, and array|null if recursive) - * - * @param array|null $values =self::factory() - * @param string $recursivePrefix It is the prefix of the recursivity. - * - * @return array - */ - public static function factory($values = null, $recursivePrefix = '') { - $recursive=static::getRecursive(); - static::setRecursive(); // reset the recursivity. - $row= [ - 'idtablachildPK'=>0, - '_TableParent'=>(in_array($recursivePrefix.'_TableParent',$recursive,true)) - ? [] - : null, /* ONETOMANY! */ - 'NameChild'=>'', - 'idgrandchildFK'=>0, - '_idgrandchildFK'=>(in_array($recursivePrefix.'_idgrandchildFK',$recursive,true)) - ? TableGrandChildRepo::factory(null,$recursivePrefix.'_idgrandchildFK') - : null, /* MANYTOONE!! */ - ]; - isset($row['_idgrandchildFK']) - and $row['_idgrandchildFK']['idgrandchildPK']=&$row['idgrandchildFK']; // linked MANYTOONE - - if ($values !== null) { - $row = array_merge($row, $values); - } - return $row; - } - - /** - * It returns an empty array with null values and no recursivity. - * @param array|null $values=self::factoryNull() - * - * @return array - */ - public static function factoryNull($values=null) { - $row= [ - 'idtablachildPK'=>null, - '_TableParent'=>null, /* ONETOMANY! */ - 'NameChild'=>null, - 'idgrandchildFK'=>null, - '_idgrandchildFK'=>null, /* MANYTOONE!! */ - ]; - if ($values !== null) { - $row = array_merge($row, $values); - } - return $row; - } - -} \ No newline at end of file diff --git a/examples/repomysql/generated/AbstractTableGrandChildRepo.php b/examples/repomysql/generated/AbstractTableGrandChildRepo.php deleted file mode 100644 index 69a2cd6..0000000 --- a/examples/repomysql/generated/AbstractTableGrandChildRepo.php +++ /dev/null @@ -1,414 +0,0 @@ - - * $code=$pdoOne->generateCodeClass('TableGrandChild','repomysql',array(),array('TableParent'=>'TableParentRepo','TableChild'=>'TableChildRepo','TableGrandChild'=>'TableGrandChildRepo','TableGrandChildTag'=>'TableGrandChildTagRepo','TableParentxCategory'=>'TableParentxCategoryRepo','TableCategory'=>'TableCategoryRepo','TableParentExt'=>'TableParentExtRepo',),array(),'','','TestDb','mysql\repomodel\TableGrandChildModel',array(),array()); - * - */ -abstract class AbstractTableGrandChildRepo extends TestDb -{ - const TABLE = 'TableGrandChild'; - const COMPILEDVERSION=3; - const IDENTITY = 'idgrandchildPK'; - const PK = [ - 'idgrandchildPK' - ]; - const ME=__CLASS__; - const EXTRACOLS=''; - - /** - * It returns the definitions of the columns
- * Example:
- *
-     * self::getDef(); // ['colName'=>[php type,php conversion type,type,size,nullable,extra,sql],'colName2'=>..]
-     * self::getDef('sql'); // ['colName'=>'sql','colname2'=>'sql2']
-     * self::getDef('identity',true); // it returns the columns that are identities ['col1','col2']
-     * 
- * PHP Types: binary, date, datetime, decimal/float,int, string,time, timestamp
- * PHP Conversions: datetime (datetime class), datetime2 (iso),datetime3 (human string) - * , datetime4 (sql no conversion!), timestamp (int), bool, int, float
- * Param Types: PDO::PARAM_LOB, PDO::PARAM_STR, PDO::PARAM_INT
- * - * @param string|null $column =['phptype','conversion','type','size','null','identity','sql'][$i] - * if not null then it only returns the column specified. - * @param string|null $filter If filter is not null, then it uses the column to filter the result. - * - * @return array|array[] - */ - public static function getDef($column=null,$filter=null) { - $r = [ - 'idgrandchildPK' => [ - 'phptype' => 'int', - 'conversion' => 'int', - 'type' => 'int', - 'size' => NULL, - 'null' => FALSE, - 'identity' => TRUE, - 'sql' => 'int not null auto_increment' - ], - 'NameGrandChild' => [ - 'phptype' => 'string', - 'conversion' => NULL, - 'type' => 'varchar', - 'size' => '50', - 'null' => TRUE, - 'identity' => FALSE, - 'sql' => 'varchar(50)' - ] - ]; - if($column!==null) { - if($filter===null) { - foreach($r as $k=>$v) { - $r[$k]=$v[$column]; - } - } else { - $new=[]; - foreach($r as $k=>$v) { - if($v[$column]===$filter) { - $new[]=$k; - } - } - return $new; - } - } - return $r; - } - - /** - * It converts a row returned from the database.
- * If the column is missing then it sets the field as null. - * - * @param array $row [ref] - */ - public static function convertOutputVal(&$row) { - if($row===false || $row===null) { - return; - } - $row['idgrandchildPK']=isset($row['idgrandchildPK']) ? (int)$row['idgrandchildPK'] : null; - !isset($row['NameGrandChild']) and $row['NameGrandChild']=null; // varchar - - } - - /** - * It converts a row to be inserted or updated into the database.
- * If the column is missing then it is ignored and not converted. - * - * @param array $row [ref] - */ - public static function convertInputVal(&$row) { - isset($row['idgrandchildPK']) and $row['idgrandchildPK']=(int)$row['idgrandchildPK']; - } - - - /** - * It gets all the name of the columns. - * - * @return string[] - */ - public static function getDefName() { - return [ - 'idgrandchildPK', - 'NameGrandChild' - ]; - } - - /** - * It returns an associative array (colname=>key type) with all the keys/indexes (if any) - * - * @return string[] - */ - public static function getDefKey() { - return [ - 'idgrandchildPK' => 'PRIMARY KEY' - ]; - } - - /** - * It returns a string array with the name of the columns that are skipped when insert - * @return string[] - */ - public static function getDefNoInsert() { - return [ - 'idgrandchildPK' - ]; - } - - /** - * It returns a string array with the name of the columns that are skipped when update - * @return string[] - */ - public static function getDefNoUpdate() { - return [ - 'idgrandchildPK' - ]; - } - - /** - * It adds a where to the query pipeline. It could be stacked with many where() - * Example:
- *
-     * self::where(['col'=>'value'])::toList();
-     * self::where(['col']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
-     * self::where(['col=?']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
-     * 
- * - * @param array|string $sql =self::factory() - * @param null|array|int $param - * - * @return TableGrandChildRepo - */ - public static function where($sql, $param = PdoOne::NULL) - { - self::getPdoOne()->where($sql, $param,false,TableGrandChildRepo::TABLE); - return TableGrandChildRepo::class; - } - - public static function getDefFK($structure=false) { - if ($structure) { - return [ - - ]; - } - /* key,refcol,reftable,extra */ - return [ - '_TableChild' => [ - 'key' => 'ONETOMANY', - 'col' => 'idgrandchildPK', - 'reftable' => 'TableChild', - 'refcol' => '_idgrandchildFK' - ], - '_TableGrandChildTag' => [ - 'key' => 'ONETOMANY', - 'col' => 'idgrandchildPK', - 'reftable' => 'TableGrandChildTag', - 'refcol' => '_IdgrandchildFK' - ] - ]; - } - - /** - * It returns all the relational fields by type. '*' returns all types.
- * It doesn't return normal columns. - * - * @param string $type=['*','MANYTOONE','ONETOMANY','ONETOONE','MANYTOMANY'][$i] - * - * @return string[] - * @noinspection SlowArrayOperationsInLoopInspection - */ - public static function getRelations($type='all') { - $r= [ - 'ONETOMANY' => [ - '_TableChild', - '_TableGrandChildTag' - ] - ]; - if($type==='*') { - $result=[]; - foreach($r as $arr) { - $result = array_merge($result,$arr); - } - return $result; - } - return isset($r[$type]) ? $r[$type] : []; - - } - - public static function toList($filter=PdoOne::NULL,$filterValue=null) { - if(self::$useModel) { - return TableGrandChildModel::fromArrayMultiple( self::_toList($filter, $filterValue)); - } - return self::_toList($filter, $filterValue); - } - - /** - * It sets the recursivity. By default, if we query or modify a value, it operates with the fields of the entity. - * With recursivity, we could use the recursivity of the fields, for example, loading a MANYTOONE relation
- * Example:
- *
-     * self::setRecursive([]); // (default) no use recursivity.
-     * self::setRecursive('*'); // recursive every MANYTOONE,ONETOONE,MANYTOONE and ONETOONE relations (first level) 
-     * self::setRecursive('MANYTOONE'); // recursive all relations of the type MANYTOONE (first level)
-     * self::setRecursive(['_relation1','_relation2']); // recursive only the relations of the first level 
-     * self::setRecursive(['_relation1','_relation1/_subrelation1']); // recursive the relations (first and second level)
-     * 
- * If array then it uses the values to set the recursivity.
- * If string then the values allowed are '*', 'MANYTOONE','ONETOMANY','MANYTOMANY','ONETOONE' (first level only)
- * - * @param string|array $recursive=self::factory(); - * - * @return TableGrandChildRepo - */ - public static function setRecursive($recursive=[]) - { - if(is_string($recursive)) { - $recursive=TableGrandChildRepo::getRelations($recursive); - } - return parent::_setRecursive($recursive); - } - - public static function limit($sql) - { - self::getPdoOne()->limit($sql); - return TableGrandChildRepo::class; - } - - /** - * It returns the first row of a query. - * @param array|mixed|null $pk [optional] Specify the value of the primary key. - * - * @return array|bool It returns false if not file is found. - * @throws Exception - */ - public static function first($pk = PdoOne::NULL) { - if(self::$useModel) { - return TableGrandChildModel::fromArray(self::_first($pk)); - } - return self::_first($pk); - } - - /** - * It returns true if the entity exists, otherwise false.
- * Example:
- *
-     *  $this->exist(['id'=>'a1','name'=>'name']); // using an array
-     *  $this->exist('a1'); // using the primary key. The table needs a pks and it only works with the first pk.
-     *  
- * - * @param array|mixed $entity =self::factory() - * - * @return bool true if the pks exists - * @throws Exception - */ - public static function exist($entity) { - return self::_exist($entity); - } - - /** - * It inserts a new entity(row) into the database
- * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function insert(&$entity,$transactional=true) { - return self::_insert($entity,$transactional); - } - - /** - * It merge a new entity(row) into the database. If the entity exists then it is updated, otherwise the entity is - * inserted
- * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function merge(&$entity,$transactional=true) { - return self::_merge($entity,$transactional); - } - - /** - * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function update($entity,$transactional=true) { - return self::_update($entity,$transactional); - } - - /** - * It deletes an entity by the primary key - * - * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return mixed - * @throws Exception - */ - public static function delete($entity,$transactional=true) { - return self::_delete($entity,$transactional); - } - - /** - * It deletes an entity by the primary key. - * - * @param array $pk =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return mixed - * @throws Exception - */ - public static function deleteById($pk,$transactional=true) { - return self::_deleteById($pk,$transactional); - } - - /** - * Returns an array with the default values (0 for numbers, empty for string, and array|null if recursive) - * - * @param array|null $values =self::factory() - * @param string $recursivePrefix It is the prefix of the recursivity. - * - * @return array - */ - public static function factory($values = null, $recursivePrefix = '') { - $recursive=static::getRecursive(); - static::setRecursive(); // reset the recursivity. - $row= [ - 'idgrandchildPK'=>0, - '_TableChild'=>(in_array($recursivePrefix.'_TableChild',$recursive,true)) - ? [] - : null, /* ONETOMANY! */ - '_TableGrandChildTag'=>(in_array($recursivePrefix.'_TableGrandChildTag',$recursive,true)) - ? [] - : null, /* ONETOMANY! */ - 'NameGrandChild'=>'' - ]; - - if ($values !== null) { - $row = array_merge($row, $values); - } - return $row; - } - - /** - * It returns an empty array with null values and no recursivity. - * @param array|null $values=self::factoryNull() - * - * @return array - */ - public static function factoryNull($values=null) { - $row= [ - 'idgrandchildPK'=>null, - '_TableChild'=>null, /* ONETOMANY! */ - '_TableGrandChildTag'=>null, /* ONETOMANY! */ - 'NameGrandChild'=>null - ]; - if ($values !== null) { - $row = array_merge($row, $values); - } - return $row; - } - -} \ No newline at end of file diff --git a/examples/repomysql/generated/AbstractTableGrandChildTagRepo.php b/examples/repomysql/generated/AbstractTableGrandChildTagRepo.php deleted file mode 100644 index dcfb48b..0000000 --- a/examples/repomysql/generated/AbstractTableGrandChildTagRepo.php +++ /dev/null @@ -1,430 +0,0 @@ - - * $code=$pdoOne->generateCodeClass('TableGrandChildTag','repomysql',array(),array('TableParent'=>'TableParentRepo','TableChild'=>'TableChildRepo','TableGrandChild'=>'TableGrandChildRepo','TableGrandChildTag'=>'TableGrandChildTagRepo','TableParentxCategory'=>'TableParentxCategoryRepo','TableCategory'=>'TableCategoryRepo','TableParentExt'=>'TableParentExtRepo',),array(),'','','TestDb','mysql\repomodel\TableGrandChildTagModel',array(),array()); - * - */ -abstract class AbstractTableGrandChildTagRepo extends TestDb -{ - const TABLE = 'TableGrandChildTag'; - const COMPILEDVERSION=3; - const IDENTITY = 'IdTablaGrandChildTagPK'; - const PK = [ - 'IdTablaGrandChildTagPK' - ]; - const ME=__CLASS__; - const EXTRACOLS=''; - - /** - * It returns the definitions of the columns
- * Example:
- *
-     * self::getDef(); // ['colName'=>[php type,php conversion type,type,size,nullable,extra,sql],'colName2'=>..]
-     * self::getDef('sql'); // ['colName'=>'sql','colname2'=>'sql2']
-     * self::getDef('identity',true); // it returns the columns that are identities ['col1','col2']
-     * 
- * PHP Types: binary, date, datetime, decimal/float,int, string,time, timestamp
- * PHP Conversions: datetime (datetime class), datetime2 (iso),datetime3 (human string) - * , datetime4 (sql no conversion!), timestamp (int), bool, int, float
- * Param Types: PDO::PARAM_LOB, PDO::PARAM_STR, PDO::PARAM_INT
- * - * @param string|null $column =['phptype','conversion','type','size','null','identity','sql'][$i] - * if not null then it only returns the column specified. - * @param string|null $filter If filter is not null, then it uses the column to filter the result. - * - * @return array|array[] - */ - public static function getDef($column=null,$filter=null) { - $r = [ - 'IdTablaGrandChildTagPK' => [ - 'phptype' => 'int', - 'conversion' => 'int', - 'type' => 'int', - 'size' => NULL, - 'null' => FALSE, - 'identity' => TRUE, - 'sql' => 'int not null auto_increment' - ], - 'Name' => [ - 'phptype' => 'string', - 'conversion' => NULL, - 'type' => 'varchar', - 'size' => '50', - 'null' => TRUE, - 'identity' => FALSE, - 'sql' => 'varchar(50)' - ], - 'IdgrandchildFK' => [ - 'phptype' => 'int', - 'conversion' => 'int', - 'type' => 'int', - 'size' => NULL, - 'null' => TRUE, - 'identity' => FALSE, - 'sql' => 'int' - ] - ]; - if($column!==null) { - if($filter===null) { - foreach($r as $k=>$v) { - $r[$k]=$v[$column]; - } - } else { - $new=[]; - foreach($r as $k=>$v) { - if($v[$column]===$filter) { - $new[]=$k; - } - } - return $new; - } - } - return $r; - } - - /** - * It converts a row returned from the database.
- * If the column is missing then it sets the field as null. - * - * @param array $row [ref] - */ - public static function convertOutputVal(&$row) { - if($row===false || $row===null) { - return; - } - $row['IdTablaGrandChildTagPK']=isset($row['IdTablaGrandChildTagPK']) ? (int)$row['IdTablaGrandChildTagPK'] : null; - !isset($row['Name']) and $row['Name']=null; // varchar - $row['IdgrandchildFK']=isset($row['IdgrandchildFK']) ? (int)$row['IdgrandchildFK'] : null; - isset($row['_IdgrandchildFK']) - and $row['_IdgrandchildFK']['idgrandchildPK']=&$row['IdgrandchildFK']; // linked MANYTOONE - - } - - /** - * It converts a row to be inserted or updated into the database.
- * If the column is missing then it is ignored and not converted. - * - * @param array $row [ref] - */ - public static function convertInputVal(&$row) { - isset($row['IdTablaGrandChildTagPK']) and $row['IdTablaGrandChildTagPK']=(int)$row['IdTablaGrandChildTagPK']; - isset($row['IdgrandchildFK']) and $row['IdgrandchildFK']=(int)$row['IdgrandchildFK']; - } - - - /** - * It gets all the name of the columns. - * - * @return string[] - */ - public static function getDefName() { - return [ - 'IdTablaGrandChildTagPK', - 'Name', - 'IdgrandchildFK' - ]; - } - - /** - * It returns an associative array (colname=>key type) with all the keys/indexes (if any) - * - * @return string[] - */ - public static function getDefKey() { - return [ - 'IdTablaGrandChildTagPK' => 'PRIMARY KEY', - 'IdgrandchildFK' => 'KEY' - ]; - } - - /** - * It returns a string array with the name of the columns that are skipped when insert - * @return string[] - */ - public static function getDefNoInsert() { - return [ - 'IdTablaGrandChildTagPK' - ]; - } - - /** - * It returns a string array with the name of the columns that are skipped when update - * @return string[] - */ - public static function getDefNoUpdate() { - return [ - 'IdTablaGrandChildTagPK' - ]; - } - - /** - * It adds a where to the query pipeline. It could be stacked with many where() - * Example:
- *
-     * self::where(['col'=>'value'])::toList();
-     * self::where(['col']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
-     * self::where(['col=?']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
-     * 
- * - * @param array|string $sql =self::factory() - * @param null|array|int $param - * - * @return TableGrandChildTagRepo - */ - public static function where($sql, $param = PdoOne::NULL) - { - self::getPdoOne()->where($sql, $param,false,TableGrandChildTagRepo::TABLE); - return TableGrandChildTagRepo::class; - } - - public static function getDefFK($structure=false) { - if ($structure) { - return [ - 'IdgrandchildFK' => 'FOREIGN KEY REFERENCES`TableGrandChild`(`idgrandchildPK`)' - ]; - } - /* key,refcol,reftable,extra */ - return [ - 'IdgrandchildFK' => [ - 'key' => 'FOREIGN KEY', - 'refcol' => 'idgrandchildPK', - 'reftable' => 'TableGrandChild', - 'extra' => '', - 'name' => 'FK_tablagrandchildcat_tablagrandchild' - ], - '_IdgrandchildFK' => [ - 'key' => 'MANYTOONE', - 'refcol' => 'idgrandchildPK', - 'reftable' => 'TableGrandChild', - 'extra' => '', - 'name' => 'FK_tablagrandchildcat_tablagrandchild' - ] - ]; - } - - /** - * It returns all the relational fields by type. '*' returns all types.
- * It doesn't return normal columns. - * - * @param string $type=['*','MANYTOONE','ONETOMANY','ONETOONE','MANYTOMANY'][$i] - * - * @return string[] - * @noinspection SlowArrayOperationsInLoopInspection - */ - public static function getRelations($type='all') { - $r= [ - 'MANYTOONE' => [ - '_IdgrandchildFK' - ] - ]; - if($type==='*') { - $result=[]; - foreach($r as $arr) { - $result = array_merge($result,$arr); - } - return $result; - } - return isset($r[$type]) ? $r[$type] : []; - - } - - public static function toList($filter=PdoOne::NULL,$filterValue=null) { - if(self::$useModel) { - return TableGrandChildTagModel::fromArrayMultiple( self::_toList($filter, $filterValue)); - } - return self::_toList($filter, $filterValue); - } - - /** - * It sets the recursivity. By default, if we query or modify a value, it operates with the fields of the entity. - * With recursivity, we could use the recursivity of the fields, for example, loading a MANYTOONE relation
- * Example:
- *
-     * self::setRecursive([]); // (default) no use recursivity.
-     * self::setRecursive('*'); // recursive every MANYTOONE,ONETOONE,MANYTOONE and ONETOONE relations (first level) 
-     * self::setRecursive('MANYTOONE'); // recursive all relations of the type MANYTOONE (first level)
-     * self::setRecursive(['_relation1','_relation2']); // recursive only the relations of the first level 
-     * self::setRecursive(['_relation1','_relation1/_subrelation1']); // recursive the relations (first and second level)
-     * 
- * If array then it uses the values to set the recursivity.
- * If string then the values allowed are '*', 'MANYTOONE','ONETOMANY','MANYTOMANY','ONETOONE' (first level only)
- * - * @param string|array $recursive=self::factory(); - * - * @return TableGrandChildTagRepo - */ - public static function setRecursive($recursive=[]) - { - if(is_string($recursive)) { - $recursive=TableGrandChildTagRepo::getRelations($recursive); - } - return parent::_setRecursive($recursive); - } - - public static function limit($sql) - { - self::getPdoOne()->limit($sql); - return TableGrandChildTagRepo::class; - } - - /** - * It returns the first row of a query. - * @param array|mixed|null $pk [optional] Specify the value of the primary key. - * - * @return array|bool It returns false if not file is found. - * @throws Exception - */ - public static function first($pk = PdoOne::NULL) { - if(self::$useModel) { - return TableGrandChildTagModel::fromArray(self::_first($pk)); - } - return self::_first($pk); - } - - /** - * It returns true if the entity exists, otherwise false.
- * Example:
- *
-     *  $this->exist(['id'=>'a1','name'=>'name']); // using an array
-     *  $this->exist('a1'); // using the primary key. The table needs a pks and it only works with the first pk.
-     *  
- * - * @param array|mixed $entity =self::factory() - * - * @return bool true if the pks exists - * @throws Exception - */ - public static function exist($entity) { - return self::_exist($entity); - } - - /** - * It inserts a new entity(row) into the database
- * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function insert(&$entity,$transactional=true) { - return self::_insert($entity,$transactional); - } - - /** - * It merge a new entity(row) into the database. If the entity exists then it is updated, otherwise the entity is - * inserted
- * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function merge(&$entity,$transactional=true) { - return self::_merge($entity,$transactional); - } - - /** - * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function update($entity,$transactional=true) { - return self::_update($entity,$transactional); - } - - /** - * It deletes an entity by the primary key - * - * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return mixed - * @throws Exception - */ - public static function delete($entity,$transactional=true) { - return self::_delete($entity,$transactional); - } - - /** - * It deletes an entity by the primary key. - * - * @param array $pk =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return mixed - * @throws Exception - */ - public static function deleteById($pk,$transactional=true) { - return self::_deleteById($pk,$transactional); - } - - /** - * Returns an array with the default values (0 for numbers, empty for string, and array|null if recursive) - * - * @param array|null $values =self::factory() - * @param string $recursivePrefix It is the prefix of the recursivity. - * - * @return array - */ - public static function factory($values = null, $recursivePrefix = '') { - $recursive=static::getRecursive(); - static::setRecursive(); // reset the recursivity. - $row= [ - 'IdTablaGrandChildTagPK'=>0, - 'Name'=>'', - 'IdgrandchildFK'=>0, - '_IdgrandchildFK'=>(in_array($recursivePrefix.'_IdgrandchildFK',$recursive,true)) - ? TableGrandChildRepo::factory(null,$recursivePrefix.'_IdgrandchildFK') - : null, /* MANYTOONE!! */ - ]; - isset($row['_IdgrandchildFK']) - and $row['_IdgrandchildFK']['idgrandchildPK']=&$row['IdgrandchildFK']; // linked MANYTOONE - - if ($values !== null) { - $row = array_merge($row, $values); - } - return $row; - } - - /** - * It returns an empty array with null values and no recursivity. - * @param array|null $values=self::factoryNull() - * - * @return array - */ - public static function factoryNull($values=null) { - $row= [ - 'IdTablaGrandChildTagPK'=>null, - 'Name'=>null, - 'IdgrandchildFK'=>null, - '_IdgrandchildFK'=>null, /* MANYTOONE!! */ - ]; - if ($values !== null) { - $row = array_merge($row, $values); - } - return $row; - } - -} \ No newline at end of file diff --git a/examples/repomysql/generated/AbstractTableParentExtRepo.php b/examples/repomysql/generated/AbstractTableParentExtRepo.php deleted file mode 100644 index e3e3025..0000000 --- a/examples/repomysql/generated/AbstractTableParentExtRepo.php +++ /dev/null @@ -1,406 +0,0 @@ - - * $code=$pdoOne->generateCodeClass('TableParentExt','repomysql',array('_idtablaparentExtPK'=>'PARENT',),array('TableParent'=>'TableParentRepo','TableChild'=>'TableChildRepo','TableGrandChild'=>'TableGrandChildRepo','TableGrandChildTag'=>'TableGrandChildTagRepo','TableParentxCategory'=>'TableParentxCategoryRepo','TableCategory'=>'TableCategoryRepo','TableParentExt'=>'TableParentExtRepo',),array(),'','','TestDb','mysql\repomodel\TableParentExtModel',array(),array()); - * - */ -abstract class AbstractTableParentExtRepo extends TestDb -{ - const TABLE = 'TableParentExt'; - const COMPILEDVERSION=3; - const IDENTITY = 'idtablaparentExtPK'; - const PK = [ - 'idtablaparentExtPK' - ]; - const ME=__CLASS__; - const EXTRACOLS=''; - - /** - * It returns the definitions of the columns
- * Example:
- *
-     * self::getDef(); // ['colName'=>[php type,php conversion type,type,size,nullable,extra,sql],'colName2'=>..]
-     * self::getDef('sql'); // ['colName'=>'sql','colname2'=>'sql2']
-     * self::getDef('identity',true); // it returns the columns that are identities ['col1','col2']
-     * 
- * PHP Types: binary, date, datetime, decimal/float,int, string,time, timestamp
- * PHP Conversions: datetime (datetime class), datetime2 (iso),datetime3 (human string) - * , datetime4 (sql no conversion!), timestamp (int), bool, int, float
- * Param Types: PDO::PARAM_LOB, PDO::PARAM_STR, PDO::PARAM_INT
- * - * @param string|null $column =['phptype','conversion','type','size','null','identity','sql'][$i] - * if not null then it only returns the column specified. - * @param string|null $filter If filter is not null, then it uses the column to filter the result. - * - * @return array|array[] - */ - public static function getDef($column=null,$filter=null) { - $r = [ - 'idtablaparentExtPK' => [ - 'phptype' => 'int', - 'conversion' => 'int', - 'type' => 'int', - 'size' => NULL, - 'null' => FALSE, - 'identity' => TRUE, - 'sql' => 'int not null auto_increment' - ], - 'fieldExt' => [ - 'phptype' => 'string', - 'conversion' => NULL, - 'type' => 'varchar', - 'size' => '50', - 'null' => TRUE, - 'identity' => FALSE, - 'sql' => 'varchar(50)' - ] - ]; - if($column!==null) { - if($filter===null) { - foreach($r as $k=>$v) { - $r[$k]=$v[$column]; - } - } else { - $new=[]; - foreach($r as $k=>$v) { - if($v[$column]===$filter) { - $new[]=$k; - } - } - return $new; - } - } - return $r; - } - - /** - * It converts a row returned from the database.
- * If the column is missing then it sets the field as null. - * - * @param array $row [ref] - */ - public static function convertOutputVal(&$row) { - if($row===false || $row===null) { - return; - } - $row['idtablaparentExtPK']=isset($row['idtablaparentExtPK']) ? (int)$row['idtablaparentExtPK'] : null; - !isset($row['fieldExt']) and $row['fieldExt']=null; // varchar - - } - - /** - * It converts a row to be inserted or updated into the database.
- * If the column is missing then it is ignored and not converted. - * - * @param array $row [ref] - */ - public static function convertInputVal(&$row) { - isset($row['idtablaparentExtPK']) and $row['idtablaparentExtPK']=(int)$row['idtablaparentExtPK']; - } - - - /** - * It gets all the name of the columns. - * - * @return string[] - */ - public static function getDefName() { - return [ - 'idtablaparentExtPK', - 'fieldExt' - ]; - } - - /** - * It returns an associative array (colname=>key type) with all the keys/indexes (if any) - * - * @return string[] - */ - public static function getDefKey() { - return [ - 'idtablaparentExtPK' => 'PRIMARY KEY' - ]; - } - - /** - * It returns a string array with the name of the columns that are skipped when insert - * @return string[] - */ - public static function getDefNoInsert() { - return [ - 'idtablaparentExtPK' - ]; - } - - /** - * It returns a string array with the name of the columns that are skipped when update - * @return string[] - */ - public static function getDefNoUpdate() { - return [ - 'idtablaparentExtPK' - ]; - } - - /** - * It adds a where to the query pipeline. It could be stacked with many where() - * Example:
- *
-     * self::where(['col'=>'value'])::toList();
-     * self::where(['col']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
-     * self::where(['col=?']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
-     * 
- * - * @param array|string $sql =self::factory() - * @param null|array|int $param - * - * @return TableParentExtRepo - */ - public static function where($sql, $param = PdoOne::NULL) - { - self::getPdoOne()->where($sql, $param,false,TableParentExtRepo::TABLE); - return TableParentExtRepo::class; - } - - public static function getDefFK($structure=false) { - if ($structure) { - return [ - 'idtablaparentExtPK' => 'FOREIGN KEY REFERENCES`TableParent`(`idtablaparentPK`)' - ]; - } - /* key,refcol,reftable,extra */ - return [ - 'idtablaparentExtPK' => [ - 'key' => 'FOREIGN KEY', - 'refcol' => 'idtablaparentPK', - 'reftable' => 'TableParent', - 'extra' => '', - 'name' => 'FK_tablaparent_ext_tablaparent' - ], - '_idtablaparentExtPK' => [ - 'key' => 'PARENT', - 'refcol' => 'idtablaparentPK', - 'reftable' => 'TableParent', - 'extra' => '', - 'name' => 'FK_tablaparent_ext_tablaparent', - 'col' => 'idtablaparentExtPK' - ] - ]; - } - - /** - * It returns all the relational fields by type. '*' returns all types.
- * It doesn't return normal columns. - * - * @param string $type=['*','MANYTOONE','ONETOMANY','ONETOONE','MANYTOMANY'][$i] - * - * @return string[] - * @noinspection SlowArrayOperationsInLoopInspection - */ - public static function getRelations($type='all') { - $r= [ - - ]; - if($type==='*') { - $result=[]; - foreach($r as $arr) { - $result = array_merge($result,$arr); - } - return $result; - } - return isset($r[$type]) ? $r[$type] : []; - - } - - public static function toList($filter=PdoOne::NULL,$filterValue=null) { - if(self::$useModel) { - return TableParentExtModel::fromArrayMultiple( self::_toList($filter, $filterValue)); - } - return self::_toList($filter, $filterValue); - } - - /** - * It sets the recursivity. By default, if we query or modify a value, it operates with the fields of the entity. - * With recursivity, we could use the recursivity of the fields, for example, loading a MANYTOONE relation
- * Example:
- *
-     * self::setRecursive([]); // (default) no use recursivity.
-     * self::setRecursive('*'); // recursive every MANYTOONE,ONETOONE,MANYTOONE and ONETOONE relations (first level) 
-     * self::setRecursive('MANYTOONE'); // recursive all relations of the type MANYTOONE (first level)
-     * self::setRecursive(['_relation1','_relation2']); // recursive only the relations of the first level 
-     * self::setRecursive(['_relation1','_relation1/_subrelation1']); // recursive the relations (first and second level)
-     * 
- * If array then it uses the values to set the recursivity.
- * If string then the values allowed are '*', 'MANYTOONE','ONETOMANY','MANYTOMANY','ONETOONE' (first level only)
- * - * @param string|array $recursive=self::factory(); - * - * @return TableParentExtRepo - */ - public static function setRecursive($recursive=[]) - { - if(is_string($recursive)) { - $recursive=TableParentExtRepo::getRelations($recursive); - } - return parent::_setRecursive($recursive); - } - - public static function limit($sql) - { - self::getPdoOne()->limit($sql); - return TableParentExtRepo::class; - } - - /** - * It returns the first row of a query. - * @param array|mixed|null $pk [optional] Specify the value of the primary key. - * - * @return array|bool It returns false if not file is found. - * @throws Exception - */ - public static function first($pk = PdoOne::NULL) { - if(self::$useModel) { - return TableParentExtModel::fromArray(self::_first($pk)); - } - return self::_first($pk); - } - - /** - * It returns true if the entity exists, otherwise false.
- * Example:
- *
-     *  $this->exist(['id'=>'a1','name'=>'name']); // using an array
-     *  $this->exist('a1'); // using the primary key. The table needs a pks and it only works with the first pk.
-     *  
- * - * @param array|mixed $entity =self::factory() - * - * @return bool true if the pks exists - * @throws Exception - */ - public static function exist($entity) { - return self::_exist($entity); - } - - /** - * It inserts a new entity(row) into the database
- * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function insert(&$entity,$transactional=true) { - return self::_insert($entity,$transactional); - } - - /** - * It merge a new entity(row) into the database. If the entity exists then it is updated, otherwise the entity is - * inserted
- * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function merge(&$entity,$transactional=true) { - return self::_merge($entity,$transactional); - } - - /** - * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function update($entity,$transactional=true) { - return self::_update($entity,$transactional); - } - - /** - * It deletes an entity by the primary key - * - * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return mixed - * @throws Exception - */ - public static function delete($entity,$transactional=true) { - return self::_delete($entity,$transactional); - } - - /** - * It deletes an entity by the primary key. - * - * @param array $pk =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return mixed - * @throws Exception - */ - public static function deleteById($pk,$transactional=true) { - return self::_deleteById($pk,$transactional); - } - - /** - * Returns an array with the default values (0 for numbers, empty for string, and array|null if recursive) - * - * @param array|null $values =self::factory() - * @param string $recursivePrefix It is the prefix of the recursivity. - * - * @return array - */ - public static function factory($values = null, $recursivePrefix = '') { - $recursive=static::getRecursive(); - static::setRecursive(); // reset the recursivity. - $row= [ - 'idtablaparentExtPK'=>0, - 'fieldExt'=>'' - ]; - - if ($values !== null) { - $row = array_merge($row, $values); - } - return $row; - } - - /** - * It returns an empty array with null values and no recursivity. - * @param array|null $values=self::factoryNull() - * - * @return array - */ - public static function factoryNull($values=null) { - $row= [ - 'idtablaparentExtPK'=>null, - 'fieldExt'=>null - ]; - if ($values !== null) { - $row = array_merge($row, $values); - } - return $row; - } - -} \ No newline at end of file diff --git a/examples/repomysql/generated/AbstractTableParentRepo.php b/examples/repomysql/generated/AbstractTableParentRepo.php deleted file mode 100644 index 931fe33..0000000 --- a/examples/repomysql/generated/AbstractTableParentRepo.php +++ /dev/null @@ -1,532 +0,0 @@ - - * $code=$pdoOne->generateCodeClass('TableParent','repomysql',array('_idchild2FK'=>'PARENT','_TableParentxCategory'=>'MANYTOMANY','fieldKey'=>array(0=>'encrypt',1=>NULL,),'extracol'=>'datetime3',),array('TableParent'=>'TableParentRepo','TableChild'=>'TableChildRepo','TableGrandChild'=>'TableGrandChildRepo','TableGrandChildTag'=>'TableGrandChildTagRepo','TableParentxCategory'=>'TableParentxCategoryRepo','TableCategory'=>'TableCategoryRepo','TableParentExt'=>'TableParentExtRepo',),array(),'','','TestDb','mysql\repomodel\TableParentModel',array('extracol'=>'CURRENT_TIMESTAMP','extracol2'=>'20',),array(0=>'idchild2FK',)); - * - */ -abstract class AbstractTableParentRepo extends TestDb -{ - const TABLE = 'TableParent'; - const COMPILEDVERSION=3; - const IDENTITY = 'idtablaparentPK'; - const PK = [ - 'idtablaparentPK' - ]; - const ME=__CLASS__; - const EXTRACOLS='CURRENT_TIMESTAMP as `extracol`,20 as `extracol2`'; - - /** - * It returns the definitions of the columns
- * Example:
- *
-     * self::getDef(); // ['colName'=>[php type,php conversion type,type,size,nullable,extra,sql],'colName2'=>..]
-     * self::getDef('sql'); // ['colName'=>'sql','colname2'=>'sql2']
-     * self::getDef('identity',true); // it returns the columns that are identities ['col1','col2']
-     * 
- * PHP Types: binary, date, datetime, decimal/float,int, string,time, timestamp
- * PHP Conversions: datetime (datetime class), datetime2 (iso),datetime3 (human string) - * , datetime4 (sql no conversion!), timestamp (int), bool, int, float
- * Param Types: PDO::PARAM_LOB, PDO::PARAM_STR, PDO::PARAM_INT
- * - * @param string|null $column =['phptype','conversion','type','size','null','identity','sql'][$i] - * if not null then it only returns the column specified. - * @param string|null $filter If filter is not null, then it uses the column to filter the result. - * - * @return array|array[] - */ - public static function getDef($column=null,$filter=null) { - $r = [ - 'idtablaparentPK' => [ - 'phptype' => 'int', - 'conversion' => 'int', - 'type' => 'int', - 'size' => NULL, - 'null' => FALSE, - 'identity' => TRUE, - 'sql' => 'int not null auto_increment' - ], - 'fieldVarchar' => [ - 'phptype' => 'string', - 'conversion' => NULL, - 'type' => 'varchar', - 'size' => '50', - 'null' => TRUE, - 'identity' => FALSE, - 'sql' => 'varchar(50)' - ], - 'idchildFK' => [ - 'phptype' => 'int', - 'conversion' => 'int', - 'type' => 'int', - 'size' => NULL, - 'null' => TRUE, - 'identity' => FALSE, - 'sql' => 'int unsigned' - ], - 'fieldInt' => [ - 'phptype' => 'int', - 'conversion' => 'int', - 'type' => 'int', - 'size' => NULL, - 'null' => TRUE, - 'identity' => FALSE, - 'sql' => 'int' - ], - 'fielDecimal' => [ - 'phptype' => 'float', - 'conversion' => 'decimal', - 'type' => 'decimal', - 'size' => '9,2', - 'null' => TRUE, - 'identity' => FALSE, - 'sql' => 'decimal(9,2)' - ], - 'fieldDateTime' => [ - 'phptype' => 'datetime', - 'conversion' => 'datetime', - 'type' => 'datetime', - 'size' => NULL, - 'null' => TRUE, - 'identity' => FALSE, - 'sql' => 'datetime' - ], - 'fieldUnique' => [ - 'phptype' => 'string', - 'conversion' => NULL, - 'type' => 'varchar', - 'size' => '20', - 'null' => TRUE, - 'identity' => FALSE, - 'sql' => 'varchar(20)' - ], - 'fieldKey' => [ - 'phptype' => 'string', - 'conversion' => [ - 'encrypt', - NULL - ], - 'type' => 'varchar', - 'size' => '100', - 'null' => TRUE, - 'identity' => FALSE, - 'sql' => 'varchar(100)' - ] - ]; - if($column!==null) { - if($filter===null) { - foreach($r as $k=>$v) { - $r[$k]=$v[$column]; - } - } else { - $new=[]; - foreach($r as $k=>$v) { - if($v[$column]===$filter) { - $new[]=$k; - } - } - return $new; - } - } - return $r; - } - - /** - * It converts a row returned from the database.
- * If the column is missing then it sets the field as null. - * - * @param array $row [ref] - */ - public static function convertOutputVal(&$row) { - if($row===false || $row===null) { - return; - } - $row['idtablaparentPK']=isset($row['idtablaparentPK']) ? (int)$row['idtablaparentPK'] : null; - !isset($row['fieldVarchar']) and $row['fieldVarchar']=null; // varchar - $row['idchildFK']=isset($row['idchildFK']) ? (int)$row['idchildFK'] : null; - $row['fieldInt']=isset($row['fieldInt']) ? (int)$row['fieldInt'] : null; - $row['fielDecimal']=isset($row['fielDecimal']) ? (float)$row['fielDecimal'] : null; - $row['fieldDateTime']=isset($row['fieldDateTime']) ? PdoOne::dateConvert($row['fieldDateTime'], 'sql', 'class') : null; - !isset($row['fieldUnique']) and $row['fieldUnique']=null; // varchar - !isset($row['fieldKey']) and $row['fieldKey']=null; // no conversion - $row['extracol']=isset($row['extracol']) ? PdoOne::dateConvert($row['extracol'], 'sql', 'human') : null; - !isset($row['extracol2']) and $row['extracol2']=null; // - isset($row['_TableParentExt']) - and $row['_TableParentExt']['idtablaparentExtPK']=&$row['idtablaparentPK']; // linked ONETOONE - - } - - /** - * It converts a row to be inserted or updated into the database.
- * If the column is missing then it is ignored and not converted. - * - * @param array $row [ref] - */ - public static function convertInputVal(&$row) { - isset($row['idtablaparentPK']) and $row['idtablaparentPK']=(int)$row['idtablaparentPK']; - isset($row['idchildFK']) and $row['idchildFK']=(int)$row['idchildFK']; - isset($row['fieldInt']) and $row['fieldInt']=(int)$row['fieldInt']; - isset($row['fielDecimal']) and $row['fielDecimal']=(float)$row['fielDecimal']; - isset($row['fieldDateTime']) and $row['fieldDateTime']=PdoOne::dateConvert($row['fieldDateTime'], 'class', 'sql'); - isset($row['fieldKey']) and $row['fieldKey']=self::getPdoOne()->encrypt($row['fieldKey']); - isset($row['extracol']) and $row['extracol']=PdoOne::dateConvert($row['extracol'], 'human', 'sql'); - } - - - /** - * It gets all the name of the columns. - * - * @return string[] - */ - public static function getDefName() { - return [ - 'idtablaparentPK', - 'fieldVarchar', - 'idchildFK', - 'fieldInt', - 'fielDecimal', - 'fieldDateTime', - 'fieldUnique', - 'fieldKey' - ]; - } - - /** - * It returns an associative array (colname=>key type) with all the keys/indexes (if any) - * - * @return string[] - */ - public static function getDefKey() { - return [ - 'idtablaparentPK' => 'PRIMARY KEY', - 'fieldUnique' => 'UNIQUE KEY', - 'idchildFK' => 'KEY', - 'fieldKey' => 'KEY', - 'idchild2FK' => 'KEY' - ]; - } - - /** - * It returns a string array with the name of the columns that are skipped when insert - * @return string[] - */ - public static function getDefNoInsert() { - return [ - 'idtablaparentPK' - ]; - } - - /** - * It returns a string array with the name of the columns that are skipped when update - * @return string[] - */ - public static function getDefNoUpdate() { - return [ - 'idtablaparentPK' - ]; - } - - /** - * It adds a where to the query pipeline. It could be stacked with many where() - * Example:
- *
-     * self::where(['col'=>'value'])::toList();
-     * self::where(['col']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
-     * self::where(['col=?']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
-     * 
- * - * @param array|string $sql =self::factory() - * @param null|array|int $param - * - * @return TableParentRepo - */ - public static function where($sql, $param = PdoOne::NULL) - { - self::getPdoOne()->where($sql, $param,false,TableParentRepo::TABLE); - return TableParentRepo::class; - } - - public static function getDefFK($structure=false) { - if ($structure) { - return [ - 'idchild2FK' => 'FOREIGN KEY REFERENCES`TableChild`(`idtablachildPK`)' - ]; - } - /* key,refcol,reftable,extra */ - return [ - 'idchild2FK' => [ - 'key' => 'FOREIGN KEY', - 'refcol' => 'idtablachildPK', - 'reftable' => 'TableChild', - 'extra' => '', - 'name' => 'TableParent_fk2' - ], - '_idchild2FK' => [ - 'key' => 'PARENT', - 'refcol' => 'idtablachildPK', - 'reftable' => 'TableChild', - 'extra' => '', - 'name' => 'TableParent_fk2' - ], - '_TableParentExt' => [ - 'key' => 'ONETOONE', - 'col' => 'idtablaparentPK', - 'reftable' => 'TableParentExt', - 'refcol' => 'idtablaparentExtPK' - ], - '_TableParentxCategory' => [ - 'key' => 'MANYTOMANY', - 'col' => 'idtablaparentPK', - 'reftable' => 'TableParentxCategory', - 'refcol' => '_idtablaparentPKFK', - 'refcol2' => '_idcategoryPKFK', - 'col2' => 'IdTableCategoryPK', - 'table2' => 'TableCategory' - ] - ]; - } - - /** - * It returns all the relational fields by type. '*' returns all types.
- * It doesn't return normal columns. - * - * @param string $type=['*','MANYTOONE','ONETOMANY','ONETOONE','MANYTOMANY'][$i] - * - * @return string[] - * @noinspection SlowArrayOperationsInLoopInspection - */ - public static function getRelations($type='all') { - $r= [ - 'ONETOONE' => [ - '_TableParentExt' - ], - 'MANYTOMANY' => [ - '_TableParentxCategory' - ] - ]; - if($type==='*') { - $result=[]; - foreach($r as $arr) { - $result = array_merge($result,$arr); - } - return $result; - } - return isset($r[$type]) ? $r[$type] : []; - - } - - public static function toList($filter=PdoOne::NULL,$filterValue=null) { - if(self::$useModel) { - return TableParentModel::fromArrayMultiple( self::_toList($filter, $filterValue)); - } - return self::_toList($filter, $filterValue); - } - - /** - * It sets the recursivity. By default, if we query or modify a value, it operates with the fields of the entity. - * With recursivity, we could use the recursivity of the fields, for example, loading a MANYTOONE relation
- * Example:
- *
-     * self::setRecursive([]); // (default) no use recursivity.
-     * self::setRecursive('*'); // recursive every MANYTOONE,ONETOONE,MANYTOONE and ONETOONE relations (first level) 
-     * self::setRecursive('MANYTOONE'); // recursive all relations of the type MANYTOONE (first level)
-     * self::setRecursive(['_relation1','_relation2']); // recursive only the relations of the first level 
-     * self::setRecursive(['_relation1','_relation1/_subrelation1']); // recursive the relations (first and second level)
-     * 
- * If array then it uses the values to set the recursivity.
- * If string then the values allowed are '*', 'MANYTOONE','ONETOMANY','MANYTOMANY','ONETOONE' (first level only)
- * - * @param string|array $recursive=self::factory(); - * - * @return TableParentRepo - */ - public static function setRecursive($recursive=[]) - { - if(is_string($recursive)) { - $recursive=TableParentRepo::getRelations($recursive); - } - return parent::_setRecursive($recursive); - } - - public static function limit($sql) - { - self::getPdoOne()->limit($sql); - return TableParentRepo::class; - } - - /** - * It returns the first row of a query. - * @param array|mixed|null $pk [optional] Specify the value of the primary key. - * - * @return array|bool It returns false if not file is found. - * @throws Exception - */ - public static function first($pk = PdoOne::NULL) { - if(self::$useModel) { - return TableParentModel::fromArray(self::_first($pk)); - } - return self::_first($pk); - } - - /** - * It returns true if the entity exists, otherwise false.
- * Example:
- *
-     *  $this->exist(['id'=>'a1','name'=>'name']); // using an array
-     *  $this->exist('a1'); // using the primary key. The table needs a pks and it only works with the first pk.
-     *  
- * - * @param array|mixed $entity =self::factory() - * - * @return bool true if the pks exists - * @throws Exception - */ - public static function exist($entity) { - return self::_exist($entity); - } - - /** - * It inserts a new entity(row) into the database
- * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function insert(&$entity,$transactional=true) { - return self::_insert($entity,$transactional); - } - - /** - * It merge a new entity(row) into the database. If the entity exists then it is updated, otherwise the entity is - * inserted
- * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function merge(&$entity,$transactional=true) { - return self::_merge($entity,$transactional); - } - - /** - * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function update($entity,$transactional=true) { - return self::_update($entity,$transactional); - } - - /** - * It deletes an entity by the primary key - * - * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return mixed - * @throws Exception - */ - public static function delete($entity,$transactional=true) { - return self::_delete($entity,$transactional); - } - - /** - * It deletes an entity by the primary key. - * - * @param array $pk =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return mixed - * @throws Exception - */ - public static function deleteById($pk,$transactional=true) { - return self::_deleteById($pk,$transactional); - } - - /** - * Returns an array with the default values (0 for numbers, empty for string, and array|null if recursive) - * - * @param array|null $values =self::factory() - * @param string $recursivePrefix It is the prefix of the recursivity. - * - * @return array - */ - public static function factory($values = null, $recursivePrefix = '') { - $recursive=static::getRecursive(); - static::setRecursive(); // reset the recursivity. - $row= [ - 'idtablaparentPK'=>0, - '_TableParentExt'=>(in_array($recursivePrefix.'_TableParentExt',$recursive,true)) - ? TableParentExt::factory(null,$recursivePrefix.'_TableParentExt') - : null, /* ONETOONE! */ - '_TableParentxCategory'=>(in_array($recursivePrefix.'_TableParentxCategory',$recursive,true)) - ? [] - : null, /* MANYTOMANY! */ - 'fieldVarchar'=>'', - 'idchildFK'=>0, - 'idchild2FK'=>0, - 'fieldInt'=>0, - 'fielDecimal'=>0.0, - 'fieldDateTime'=>'', - 'fieldUnique'=>'', - 'fieldKey'=>'' - ]; - isset($row['_TableParentExt']) - and $row['_TableParentExt']['idtablaparentExtPK']=&$row['idtablaparentPK']; // linked ONETOONE - - if ($values !== null) { - $row = array_merge($row, $values); - } - return $row; - } - - /** - * It returns an empty array with null values and no recursivity. - * @param array|null $values=self::factoryNull() - * - * @return array - */ - public static function factoryNull($values=null) { - $row= [ - 'idtablaparentPK'=>null, - '_TableParentExt'=>null, /* ONETOONE! */ - '_TableParentxCategory'=>null, /* MANYTOMANY! */ - 'fieldVarchar'=>null, - 'idchildFK'=>null, - 'idchild2FK'=>null, - 'fieldInt'=>null, - 'fielDecimal'=>null, - 'fieldDateTime'=>null, - 'fieldUnique'=>null, - 'fieldKey'=>null - ]; - if ($values !== null) { - $row = array_merge($row, $values); - } - return $row; - } - -} \ No newline at end of file diff --git a/examples/repomysql/generated/AbstractTableParentxCategoryRepo.php b/examples/repomysql/generated/AbstractTableParentxCategoryRepo.php deleted file mode 100644 index 69a266f..0000000 --- a/examples/repomysql/generated/AbstractTableParentxCategoryRepo.php +++ /dev/null @@ -1,446 +0,0 @@ - - * $code=$pdoOne->generateCodeClass('TableParentxCategory','repomysql',array(),array('TableParent'=>'TableParentRepo','TableChild'=>'TableChildRepo','TableGrandChild'=>'TableGrandChildRepo','TableGrandChildTag'=>'TableGrandChildTagRepo','TableParentxCategory'=>'TableParentxCategoryRepo','TableCategory'=>'TableCategoryRepo','TableParentExt'=>'TableParentExtRepo',),array(),'','','TestDb','mysql\repomodel\TableParentxCategoryModel',array(),array()); - * - */ -abstract class AbstractTableParentxCategoryRepo extends TestDb -{ - const TABLE = 'TableParentxCategory'; - const COMPILEDVERSION=3; - const IDENTITY = 'idtablaparentPKFK'; - const PK = [ - 'idtablaparentPKFK', - 'idcategoryPKFK' - ]; - const ME=__CLASS__; - const EXTRACOLS=''; - - /** - * It returns the definitions of the columns
- * Example:
- *
-     * self::getDef(); // ['colName'=>[php type,php conversion type,type,size,nullable,extra,sql],'colName2'=>..]
-     * self::getDef('sql'); // ['colName'=>'sql','colname2'=>'sql2']
-     * self::getDef('identity',true); // it returns the columns that are identities ['col1','col2']
-     * 
- * PHP Types: binary, date, datetime, decimal/float,int, string,time, timestamp
- * PHP Conversions: datetime (datetime class), datetime2 (iso),datetime3 (human string) - * , datetime4 (sql no conversion!), timestamp (int), bool, int, float
- * Param Types: PDO::PARAM_LOB, PDO::PARAM_STR, PDO::PARAM_INT
- * - * @param string|null $column =['phptype','conversion','type','size','null','identity','sql'][$i] - * if not null then it only returns the column specified. - * @param string|null $filter If filter is not null, then it uses the column to filter the result. - * - * @return array|array[] - */ - public static function getDef($column=null,$filter=null) { - $r = [ - 'idtablaparentPKFK' => [ - 'phptype' => 'int', - 'conversion' => 'int', - 'type' => 'int', - 'size' => NULL, - 'null' => FALSE, - 'identity' => TRUE, - 'sql' => 'int not null auto_increment' - ], - 'idcategoryPKFK' => [ - 'phptype' => 'int', - 'conversion' => 'int', - 'type' => 'int', - 'size' => NULL, - 'null' => FALSE, - 'identity' => FALSE, - 'sql' => 'int not null' - ] - ]; - if($column!==null) { - if($filter===null) { - foreach($r as $k=>$v) { - $r[$k]=$v[$column]; - } - } else { - $new=[]; - foreach($r as $k=>$v) { - if($v[$column]===$filter) { - $new[]=$k; - } - } - return $new; - } - } - return $r; - } - - /** - * It converts a row returned from the database.
- * If the column is missing then it sets the field as null. - * - * @param array $row [ref] - */ - public static function convertOutputVal(&$row) { - if($row===false || $row===null) { - return; - } - $row['idtablaparentPKFK']=isset($row['idtablaparentPKFK']) ? (int)$row['idtablaparentPKFK'] : null; - $row['idcategoryPKFK']=isset($row['idcategoryPKFK']) ? (int)$row['idcategoryPKFK'] : null; - isset($row['_idcategoryPKFK']) - and $row['_idcategoryPKFK']['IdTableCategoryPK']=&$row['idcategoryPKFK']; // linked MANYTOONE - isset($row['_idtablaparentPKFK']) - and $row['_idtablaparentPKFK']['idtablaparentPK']=&$row['idtablaparentPKFK']; // linked ONETOONE - - } - - /** - * It converts a row to be inserted or updated into the database.
- * If the column is missing then it is ignored and not converted. - * - * @param array $row [ref] - */ - public static function convertInputVal(&$row) { - isset($row['idtablaparentPKFK']) and $row['idtablaparentPKFK']=(int)$row['idtablaparentPKFK']; - isset($row['idcategoryPKFK']) and $row['idcategoryPKFK']=(int)$row['idcategoryPKFK']; - } - - - /** - * It gets all the name of the columns. - * - * @return string[] - */ - public static function getDefName() { - return [ - 'idtablaparentPKFK', - 'idcategoryPKFK' - ]; - } - - /** - * It returns an associative array (colname=>key type) with all the keys/indexes (if any) - * - * @return string[] - */ - public static function getDefKey() { - return [ - 'idtablaparentPKFK' => 'PRIMARY KEY', - 'idcategoryPKFK' => 'PRIMARY KEY' - ]; - } - - /** - * It returns a string array with the name of the columns that are skipped when insert - * @return string[] - */ - public static function getDefNoInsert() { - return [ - 'idtablaparentPKFK' - ]; - } - - /** - * It returns a string array with the name of the columns that are skipped when update - * @return string[] - */ - public static function getDefNoUpdate() { - return [ - 'idtablaparentPKFK', - 'idcategoryPKFK' - ]; - } - - /** - * It adds a where to the query pipeline. It could be stacked with many where() - * Example:
- *
-     * self::where(['col'=>'value'])::toList();
-     * self::where(['col']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
-     * self::where(['col=?']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
-     * 
- * - * @param array|string $sql =self::factory() - * @param null|array|int $param - * - * @return TableParentxCategoryRepo - */ - public static function where($sql, $param = PdoOne::NULL) - { - self::getPdoOne()->where($sql, $param,false,TableParentxCategoryRepo::TABLE); - return TableParentxCategoryRepo::class; - } - - public static function getDefFK($structure=false) { - if ($structure) { - return [ - 'idcategoryPKFK' => 'FOREIGN KEY REFERENCES`TableCategory`(`IdTableCategoryPK`)', - 'idtablaparentPKFK' => 'FOREIGN KEY REFERENCES`TableParent`(`idtablaparentPK`)' - ]; - } - /* key,refcol,reftable,extra */ - return [ - 'idcategoryPKFK' => [ - 'key' => 'FOREIGN KEY', - 'refcol' => 'IdTableCategoryPK', - 'reftable' => 'TableCategory', - 'extra' => '', - 'name' => 'tablaparentxcategory_fk2' - ], - '_idcategoryPKFK' => [ - 'key' => 'MANYTOONE', - 'refcol' => 'IdTableCategoryPK', - 'reftable' => 'TableCategory', - 'extra' => '', - 'name' => 'tablaparentxcategory_fk2' - ], - 'idtablaparentPKFK' => [ - 'key' => 'FOREIGN KEY', - 'refcol' => 'idtablaparentPK', - 'reftable' => 'TableParent', - 'extra' => '', - 'name' => 'FK_tablaparentxcategory_tablaparent' - ], - '_idtablaparentPKFK' => [ - 'key' => 'ONETOONE', - 'refcol' => 'idtablaparentPK', - 'reftable' => 'TableParent', - 'extra' => '', - 'name' => 'FK_tablaparentxcategory_tablaparent', - 'col' => 'idtablaparentPKFK' - ] - ]; - } - - /** - * It returns all the relational fields by type. '*' returns all types.
- * It doesn't return normal columns. - * - * @param string $type=['*','MANYTOONE','ONETOMANY','ONETOONE','MANYTOMANY'][$i] - * - * @return string[] - * @noinspection SlowArrayOperationsInLoopInspection - */ - public static function getRelations($type='all') { - $r= [ - 'MANYTOONE' => [ - '_idcategoryPKFK' - ], - 'ONETOONE' => [ - '_idtablaparentPKFK' - ] - ]; - if($type==='*') { - $result=[]; - foreach($r as $arr) { - $result = array_merge($result,$arr); - } - return $result; - } - return isset($r[$type]) ? $r[$type] : []; - - } - - public static function toList($filter=PdoOne::NULL,$filterValue=null) { - if(self::$useModel) { - return TableParentxCategoryModel::fromArrayMultiple( self::_toList($filter, $filterValue)); - } - return self::_toList($filter, $filterValue); - } - - /** - * It sets the recursivity. By default, if we query or modify a value, it operates with the fields of the entity. - * With recursivity, we could use the recursivity of the fields, for example, loading a MANYTOONE relation
- * Example:
- *
-     * self::setRecursive([]); // (default) no use recursivity.
-     * self::setRecursive('*'); // recursive every MANYTOONE,ONETOONE,MANYTOONE and ONETOONE relations (first level) 
-     * self::setRecursive('MANYTOONE'); // recursive all relations of the type MANYTOONE (first level)
-     * self::setRecursive(['_relation1','_relation2']); // recursive only the relations of the first level 
-     * self::setRecursive(['_relation1','_relation1/_subrelation1']); // recursive the relations (first and second level)
-     * 
- * If array then it uses the values to set the recursivity.
- * If string then the values allowed are '*', 'MANYTOONE','ONETOMANY','MANYTOMANY','ONETOONE' (first level only)
- * - * @param string|array $recursive=self::factory(); - * - * @return TableParentxCategoryRepo - */ - public static function setRecursive($recursive=[]) - { - if(is_string($recursive)) { - $recursive=TableParentxCategoryRepo::getRelations($recursive); - } - return parent::_setRecursive($recursive); - } - - public static function limit($sql) - { - self::getPdoOne()->limit($sql); - return TableParentxCategoryRepo::class; - } - - /** - * It returns the first row of a query. - * @param array|mixed|null $pk [optional] Specify the value of the primary key. - * - * @return array|bool It returns false if not file is found. - * @throws Exception - */ - public static function first($pk = PdoOne::NULL) { - if(self::$useModel) { - return TableParentxCategoryModel::fromArray(self::_first($pk)); - } - return self::_first($pk); - } - - /** - * It returns true if the entity exists, otherwise false.
- * Example:
- *
-     *  $this->exist(['id'=>'a1','name'=>'name']); // using an array
-     *  $this->exist('a1'); // using the primary key. The table needs a pks and it only works with the first pk.
-     *  
- * - * @param array|mixed $entity =self::factory() - * - * @return bool true if the pks exists - * @throws Exception - */ - public static function exist($entity) { - return self::_exist($entity); - } - - /** - * It inserts a new entity(row) into the database
- * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function insert(&$entity,$transactional=true) { - return self::_insert($entity,$transactional); - } - - /** - * It merge a new entity(row) into the database. If the entity exists then it is updated, otherwise the entity is - * inserted
- * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function merge(&$entity,$transactional=true) { - return self::_merge($entity,$transactional); - } - - /** - * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return array|false=self::factory() - * @throws Exception - */ - public static function update($entity,$transactional=true) { - return self::_update($entity,$transactional); - } - - /** - * It deletes an entity by the primary key - * - * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return mixed - * @throws Exception - */ - public static function delete($entity,$transactional=true) { - return self::_delete($entity,$transactional); - } - - /** - * It deletes an entity by the primary key. - * - * @param array $pk =self::factory() - * @param bool $transactional If true (default) then the operation is transactional - * - * @return mixed - * @throws Exception - */ - public static function deleteById($pk,$transactional=true) { - return self::_deleteById($pk,$transactional); - } - - /** - * Returns an array with the default values (0 for numbers, empty for string, and array|null if recursive) - * - * @param array|null $values =self::factory() - * @param string $recursivePrefix It is the prefix of the recursivity. - * - * @return array - */ - public static function factory($values = null, $recursivePrefix = '') { - $recursive=static::getRecursive(); - static::setRecursive(); // reset the recursivity. - $row= [ - 'idtablaparentPKFK'=>0, - '_idtablaparentPKFK'=>(in_array($recursivePrefix.'_idtablaparentPKFK',$recursive,true)) - ? TableParentRepo::factory(null,$recursivePrefix.'_idtablaparentPKFK') - : null, /* ONETOONE!! */ - 'idcategoryPKFK'=>0, - '_idcategoryPKFK'=>(in_array($recursivePrefix.'_idcategoryPKFK',$recursive,true)) - ? TableCategoryRepo::factory(null,$recursivePrefix.'_idcategoryPKFK') - : null, /* MANYTOONE!! */ - ]; - isset($row['_idcategoryPKFK']) - and $row['_idcategoryPKFK']['IdTableCategoryPK']=&$row['idcategoryPKFK']; // linked MANYTOONE - isset($row['_idtablaparentPKFK']) - and $row['_idtablaparentPKFK']['idtablaparentPK']=&$row['idtablaparentPKFK']; // linked ONETOONE - - if ($values !== null) { - $row = array_merge($row, $values); - } - return $row; - } - - /** - * It returns an empty array with null values and no recursivity. - * @param array|null $values=self::factoryNull() - * - * @return array - */ - public static function factoryNull($values=null) { - $row= [ - 'idtablaparentPKFK'=>null, - '_idtablaparentPKFK'=>null, /* ONETOONE!! */ - 'idcategoryPKFK'=>null, - '_idcategoryPKFK'=>null, /* MANYTOONE!! */ - ]; - if ($values !== null) { - $row = array_merge($row, $values); - } - return $row; - } - -} \ No newline at end of file diff --git a/examples/repomysql/generated/TableCategoryRepo.php b/examples/repomysql/generated/TableCategoryRepo.php deleted file mode 100644 index 4507bda..0000000 --- a/examples/repomysql/generated/TableCategoryRepo.php +++ /dev/null @@ -1,24 +0,0 @@ - - * $code=$pdoOne->generateCodeClassRepo(''TableCategory'',''repomysql'','array('TableParent'=>'TableParentRepo','TableChild'=>'TableChildRepo','TableGrandChild'=>'TableGrandChildRepo','TableGrandChildTag'=>'TableGrandChildTagRepo','TableParentxCategory'=>'TableParentxCategoryRepo','TableCategory'=>'TableCategoryRepo','TableParentExt'=>'TableParentExtRepo',)',''mysql\repomodel\TableCategoryModel''); - * - */ -class TableCategoryRepo extends AbstractTableCategoryRepo -{ - const ME=__CLASS__; - const MODEL= TableCategoryModel::class; - - -} \ No newline at end of file diff --git a/examples/repomysql/generated/TableChildRepo.php b/examples/repomysql/generated/TableChildRepo.php deleted file mode 100644 index 1a21c50..0000000 --- a/examples/repomysql/generated/TableChildRepo.php +++ /dev/null @@ -1,24 +0,0 @@ - - * $code=$pdoOne->generateCodeClassRepo(''TableChild'',''repomysql'','array('TableParent'=>'TableParentRepo','TableChild'=>'TableChildRepo','TableGrandChild'=>'TableGrandChildRepo','TableGrandChildTag'=>'TableGrandChildTagRepo','TableParentxCategory'=>'TableParentxCategoryRepo','TableCategory'=>'TableCategoryRepo','TableParentExt'=>'TableParentExtRepo',)',''mysql\repomodel\TableChildModel''); - * - */ -class TableChildRepo extends AbstractTableChildRepo -{ - const ME=__CLASS__; - const MODEL= TableChildModel::class; - - -} \ No newline at end of file diff --git a/examples/repomysql/generated/TableGrandChildRepo.php b/examples/repomysql/generated/TableGrandChildRepo.php deleted file mode 100644 index a830b20..0000000 --- a/examples/repomysql/generated/TableGrandChildRepo.php +++ /dev/null @@ -1,24 +0,0 @@ - - * $code=$pdoOne->generateCodeClassRepo(''TableGrandChild'',''repomysql'','array('TableParent'=>'TableParentRepo','TableChild'=>'TableChildRepo','TableGrandChild'=>'TableGrandChildRepo','TableGrandChildTag'=>'TableGrandChildTagRepo','TableParentxCategory'=>'TableParentxCategoryRepo','TableCategory'=>'TableCategoryRepo','TableParentExt'=>'TableParentExtRepo',)',''mysql\repomodel\TableGrandChildModel''); - * - */ -class TableGrandChildRepo extends AbstractTableGrandChildRepo -{ - const ME=__CLASS__; - const MODEL= TableGrandChildModel::class; - - -} \ No newline at end of file diff --git a/examples/repomysql/generated/TableGrandChildTagRepo.php b/examples/repomysql/generated/TableGrandChildTagRepo.php deleted file mode 100644 index 2516329..0000000 --- a/examples/repomysql/generated/TableGrandChildTagRepo.php +++ /dev/null @@ -1,24 +0,0 @@ - - * $code=$pdoOne->generateCodeClassRepo(''TableGrandChildTag'',''repomysql'','array('TableParent'=>'TableParentRepo','TableChild'=>'TableChildRepo','TableGrandChild'=>'TableGrandChildRepo','TableGrandChildTag'=>'TableGrandChildTagRepo','TableParentxCategory'=>'TableParentxCategoryRepo','TableCategory'=>'TableCategoryRepo','TableParentExt'=>'TableParentExtRepo',)',''mysql\repomodel\TableGrandChildTagModel''); - * - */ -class TableGrandChildTagRepo extends AbstractTableGrandChildTagRepo -{ - const ME=__CLASS__; - const MODEL= TableGrandChildTagModel::class; - - -} \ No newline at end of file diff --git a/examples/repomysql/generated/TableParentExtRepo.php b/examples/repomysql/generated/TableParentExtRepo.php deleted file mode 100644 index 3013e3b..0000000 --- a/examples/repomysql/generated/TableParentExtRepo.php +++ /dev/null @@ -1,24 +0,0 @@ - - * $code=$pdoOne->generateCodeClassRepo(''TableParentExt'',''repomysql'','array('TableParent'=>'TableParentRepo','TableChild'=>'TableChildRepo','TableGrandChild'=>'TableGrandChildRepo','TableGrandChildTag'=>'TableGrandChildTagRepo','TableParentxCategory'=>'TableParentxCategoryRepo','TableCategory'=>'TableCategoryRepo','TableParentExt'=>'TableParentExtRepo',)',''mysql\repomodel\TableParentExtModel''); - * - */ -class TableParentExtRepo extends AbstractTableParentExtRepo -{ - const ME=__CLASS__; - const MODEL= TableParentExtModel::class; - - -} \ No newline at end of file diff --git a/examples/repomysql/generated/TableParentRepo.php b/examples/repomysql/generated/TableParentRepo.php deleted file mode 100644 index a688bf1..0000000 --- a/examples/repomysql/generated/TableParentRepo.php +++ /dev/null @@ -1,24 +0,0 @@ - - * $code=$pdoOne->generateCodeClassRepo(''TableParent'',''repomysql'','array('TableParent'=>'TableParentRepo','TableChild'=>'TableChildRepo','TableGrandChild'=>'TableGrandChildRepo','TableGrandChildTag'=>'TableGrandChildTagRepo','TableParentxCategory'=>'TableParentxCategoryRepo','TableCategory'=>'TableCategoryRepo','TableParentExt'=>'TableParentExtRepo',)',''mysql\repomodel\TableParentModel''); - * - */ -class TableParentRepo extends AbstractTableParentRepo -{ - const ME=__CLASS__; - const MODEL= TableParentModel::class; - - -} \ No newline at end of file diff --git a/examples/repomysql/generated/TableParentxCategoryRepo.php b/examples/repomysql/generated/TableParentxCategoryRepo.php deleted file mode 100644 index 29828cf..0000000 --- a/examples/repomysql/generated/TableParentxCategoryRepo.php +++ /dev/null @@ -1,24 +0,0 @@ - - * $code=$pdoOne->generateCodeClassRepo(''TableParentxCategory'',''repomysql'','array('TableParent'=>'TableParentRepo','TableChild'=>'TableChildRepo','TableGrandChild'=>'TableGrandChildRepo','TableGrandChildTag'=>'TableGrandChildTagRepo','TableParentxCategory'=>'TableParentxCategoryRepo','TableCategory'=>'TableCategoryRepo','TableParentExt'=>'TableParentExtRepo',)',''mysql\repomodel\TableParentxCategoryModel''); - * - */ -class TableParentxCategoryRepo extends AbstractTableParentxCategoryRepo -{ - const ME=__CLASS__; - const MODEL= TableParentxCategoryModel::class; - - -} \ No newline at end of file diff --git a/examples/repomysql/generated/TestDb.php b/examples/repomysql/generated/TestDb.php deleted file mode 100644 index a5c5016..0000000 --- a/examples/repomysql/generated/TestDb.php +++ /dev/null @@ -1,54 +0,0 @@ - 'TableParentRepo', - 'TableChild' => 'TableChildRepo', - 'TableGrandChild' => 'TableGrandChildRepo', - 'TableGrandChildTag' => 'TableGrandChildTagRepo', - 'TableParentxCategory' => 'TableParentxCategoryRepo', - 'TableCategory' => 'TableCategoryRepo', - 'TableParentExt' => 'TableParentExtRepo' - ]; - /** - * With the name of the table, we get the class - * @param string $tableName - * - * @return string[] - */ - protected function tabletoClass($tableName) { - return static::RELATIONS[$tableName]; - } -} \ No newline at end of file diff --git a/examples/repomysql/generatedmodel/AbstractTableCategoryModel.php b/examples/repomysql/generatedmodel/AbstractTableCategoryModel.php deleted file mode 100644 index 0435ad4..0000000 --- a/examples/repomysql/generatedmodel/AbstractTableCategoryModel.php +++ /dev/null @@ -1,95 +0,0 @@ - - * $code=$pdoOne->generateAbstractModelClass({args}); - * - */ -abstract class AbstractTableCategoryModel -{ - /** @var int $IdTableCategoryPK */ - public $IdTableCategoryPK; - /** @var string $Name */ - public $Name; - - /** @var TableParentxCategoryModel[] $_TableParentxCategory onetomany */ - public $_TableParentxCategory; - - - /** - * AbstractTableCategoryModel constructor. - * - * @param array|null $array - */ - public function __construct($array=null) - { - if($array===null) { - return; - } - foreach($array as $k=>$v) { - $this->{$k}=$v; - } - } - - // - public static function fromArray($array) { - if($array===null) { - return null; - } - $obj=new TableCategoryModel(); - $obj->IdTableCategoryPK=isset($array['IdTableCategoryPK']) ? $array['IdTableCategoryPK'] : null; - $obj->Name=isset($array['Name']) ? $array['Name'] : null; - $obj->_TableParentxCategory=isset($array['_TableParentxCategory']) ? - $obj->_TableParentxCategory=TableParentxCategoryModel::fromArrayMultiple($array['_TableParentxCategory']) - : null; // onetomany - - return $obj; - } - - /** - * It converts the current object in an array - * - * @return mixed - */ - public function toArray() { - return static::objectToArray($this); - } - - /** - * It converts an array of arrays into an array of objects. - * - * @param array|null $array - * - * @return array|null - */ - public static function fromArrayMultiple($array) { - if($array===null) { - return null; - } - $objs=[]; - foreach($array as $v) { - $objs[]=self::fromArray($v); - } - return $objs; - } - // - -} // end class \ No newline at end of file diff --git a/examples/repomysql/generatedmodel/AbstractTableChildModel.php b/examples/repomysql/generatedmodel/AbstractTableChildModel.php deleted file mode 100644 index f938f27..0000000 --- a/examples/repomysql/generatedmodel/AbstractTableChildModel.php +++ /dev/null @@ -1,105 +0,0 @@ - - * $code=$pdoOne->generateAbstractModelClass({args}); - * - */ -abstract class AbstractTableChildModel -{ - /** @var int $idtablachildPK */ - public $idtablachildPK; - /** @var string $NameChild */ - public $NameChild; - /** @var int $idgrandchildFK */ - public $idgrandchildFK; - - /** @var TableGrandChildModel $_idgrandchildFK manytoone */ - public $_idgrandchildFK; - /** @var TableParentModel[] $_TableParent onetomany */ - public $_TableParent; - - - /** - * AbstractTableChildModel constructor. - * - * @param array|null $array - */ - public function __construct($array=null) - { - if($array===null) { - return; - } - foreach($array as $k=>$v) { - $this->{$k}=$v; - } - } - - // - public static function fromArray($array) { - if($array===null) { - return null; - } - $obj=new TableChildModel(); - $obj->idtablachildPK=isset($array['idtablachildPK']) ? $array['idtablachildPK'] : null; - $obj->NameChild=isset($array['NameChild']) ? $array['NameChild'] : null; - $obj->idgrandchildFK=isset($array['idgrandchildFK']) ? $array['idgrandchildFK'] : null; - $obj->_idgrandchildFK=isset($array['_idgrandchildFK']) ? - $obj->_idgrandchildFK=TableGrandChildModel::fromArray($array['_idgrandchildFK']) - : null; // manytoone - ($obj->_idgrandchildFK !== null) - and $obj->_idgrandchildFK->idgrandchildPK=&$obj->idgrandchildFK; // linked manytoone - $obj->_TableParent=isset($array['_TableParent']) ? - $obj->_TableParent=TableParentModel::fromArrayMultiple($array['_TableParent']) - : null; // onetomany - - return $obj; - } - - /** - * It converts the current object in an array - * - * @return mixed - */ - public function toArray() { - return static::objectToArray($this); - } - - /** - * It converts an array of arrays into an array of objects. - * - * @param array|null $array - * - * @return array|null - */ - public static function fromArrayMultiple($array) { - if($array===null) { - return null; - } - $objs=[]; - foreach($array as $v) { - $objs[]=self::fromArray($v); - } - return $objs; - } - // - -} // end class \ No newline at end of file diff --git a/examples/repomysql/generatedmodel/AbstractTableGrandChildModel.php b/examples/repomysql/generatedmodel/AbstractTableGrandChildModel.php deleted file mode 100644 index 03223c6..0000000 --- a/examples/repomysql/generatedmodel/AbstractTableGrandChildModel.php +++ /dev/null @@ -1,100 +0,0 @@ - - * $code=$pdoOne->generateAbstractModelClass({args}); - * - */ -abstract class AbstractTableGrandChildModel -{ - /** @var int $idgrandchildPK */ - public $idgrandchildPK; - /** @var string $NameGrandChild */ - public $NameGrandChild; - - /** @var TableChildModel[] $_TableChild onetomany */ - public $_TableChild; - /** @var TableGrandChildTagModel[] $_TableGrandChildTag onetomany */ - public $_TableGrandChildTag; - - - /** - * AbstractTableGrandChildModel constructor. - * - * @param array|null $array - */ - public function __construct($array=null) - { - if($array===null) { - return; - } - foreach($array as $k=>$v) { - $this->{$k}=$v; - } - } - - // - public static function fromArray($array) { - if($array===null) { - return null; - } - $obj=new TableGrandChildModel(); - $obj->idgrandchildPK=isset($array['idgrandchildPK']) ? $array['idgrandchildPK'] : null; - $obj->NameGrandChild=isset($array['NameGrandChild']) ? $array['NameGrandChild'] : null; - $obj->_TableChild=isset($array['_TableChild']) ? - $obj->_TableChild=TableChildModel::fromArrayMultiple($array['_TableChild']) - : null; // onetomany - $obj->_TableGrandChildTag=isset($array['_TableGrandChildTag']) ? - $obj->_TableGrandChildTag=TableGrandChildTagModel::fromArrayMultiple($array['_TableGrandChildTag']) - : null; // onetomany - - return $obj; - } - - /** - * It converts the current object in an array - * - * @return mixed - */ - public function toArray() { - return static::objectToArray($this); - } - - /** - * It converts an array of arrays into an array of objects. - * - * @param array|null $array - * - * @return array|null - */ - public static function fromArrayMultiple($array) { - if($array===null) { - return null; - } - $objs=[]; - foreach($array as $v) { - $objs[]=self::fromArray($v); - } - return $objs; - } - // - -} // end class \ No newline at end of file diff --git a/examples/repomysql/generatedmodel/AbstractTableGrandChildTagModel.php b/examples/repomysql/generatedmodel/AbstractTableGrandChildTagModel.php deleted file mode 100644 index 532e10e..0000000 --- a/examples/repomysql/generatedmodel/AbstractTableGrandChildTagModel.php +++ /dev/null @@ -1,100 +0,0 @@ - - * $code=$pdoOne->generateAbstractModelClass({args}); - * - */ -abstract class AbstractTableGrandChildTagModel -{ - /** @var int $IdTablaGrandChildTagPK */ - public $IdTablaGrandChildTagPK; - /** @var string $Name */ - public $Name; - /** @var int $IdgrandchildFK */ - public $IdgrandchildFK; - - /** @var TableGrandChildModel $_IdgrandchildFK manytoone */ - public $_IdgrandchildFK; - - - /** - * AbstractTableGrandChildTagModel constructor. - * - * @param array|null $array - */ - public function __construct($array=null) - { - if($array===null) { - return; - } - foreach($array as $k=>$v) { - $this->{$k}=$v; - } - } - - // - public static function fromArray($array) { - if($array===null) { - return null; - } - $obj=new TableGrandChildTagModel(); - $obj->IdTablaGrandChildTagPK=isset($array['IdTablaGrandChildTagPK']) ? $array['IdTablaGrandChildTagPK'] : null; - $obj->Name=isset($array['Name']) ? $array['Name'] : null; - $obj->IdgrandchildFK=isset($array['IdgrandchildFK']) ? $array['IdgrandchildFK'] : null; - $obj->_IdgrandchildFK=isset($array['_IdgrandchildFK']) ? - $obj->_IdgrandchildFK=TableGrandChildModel::fromArray($array['_IdgrandchildFK']) - : null; // manytoone - ($obj->_IdgrandchildFK !== null) - and $obj->_IdgrandchildFK->idgrandchildPK=&$obj->IdgrandchildFK; // linked manytoone - - return $obj; - } - - /** - * It converts the current object in an array - * - * @return mixed - */ - public function toArray() { - return static::objectToArray($this); - } - - /** - * It converts an array of arrays into an array of objects. - * - * @param array|null $array - * - * @return array|null - */ - public static function fromArrayMultiple($array) { - if($array===null) { - return null; - } - $objs=[]; - foreach($array as $v) { - $objs[]=self::fromArray($v); - } - return $objs; - } - // - -} // end class \ No newline at end of file diff --git a/examples/repomysql/generatedmodel/AbstractTableParentExtModel.php b/examples/repomysql/generatedmodel/AbstractTableParentExtModel.php deleted file mode 100644 index f40964c..0000000 --- a/examples/repomysql/generatedmodel/AbstractTableParentExtModel.php +++ /dev/null @@ -1,92 +0,0 @@ - - * $code=$pdoOne->generateAbstractModelClass({args}); - * - */ -abstract class AbstractTableParentExtModel -{ - /** @var int $idtablaparentExtPK */ - public $idtablaparentExtPK; - /** @var string $fieldExt */ - public $fieldExt; - - - - - /** - * AbstractTableParentExtModel constructor. - * - * @param array|null $array - */ - public function __construct($array=null) - { - if($array===null) { - return; - } - foreach($array as $k=>$v) { - $this->{$k}=$v; - } - } - - // - public static function fromArray($array) { - if($array===null) { - return null; - } - $obj=new TableParentExtModel(); - $obj->idtablaparentExtPK=isset($array['idtablaparentExtPK']) ? $array['idtablaparentExtPK'] : null; - $obj->fieldExt=isset($array['fieldExt']) ? $array['fieldExt'] : null; - - - return $obj; - } - - /** - * It converts the current object in an array - * - * @return mixed - */ - public function toArray() { - return static::objectToArray($this); - } - - /** - * It converts an array of arrays into an array of objects. - * - * @param array|null $array - * - * @return array|null - */ - public static function fromArrayMultiple($array) { - if($array===null) { - return null; - } - $objs=[]; - foreach($array as $v) { - $objs[]=self::fromArray($v); - } - return $objs; - } - // - -} // end class \ No newline at end of file diff --git a/examples/repomysql/generatedmodel/AbstractTableParentModel.php b/examples/repomysql/generatedmodel/AbstractTableParentModel.php deleted file mode 100644 index 8fb18f7..0000000 --- a/examples/repomysql/generatedmodel/AbstractTableParentModel.php +++ /dev/null @@ -1,126 +0,0 @@ - - * $code=$pdoOne->generateAbstractModelClass({args}); - * - */ -abstract class AbstractTableParentModel -{ - /** @var int $idtablaparentPK */ - public $idtablaparentPK; - /** @var string $fieldVarchar */ - public $fieldVarchar; - /** @var int $idchildFK */ - public $idchildFK; - /** @var int $fieldInt */ - public $fieldInt; - /** @var float $fielDecimal */ - public $fielDecimal; - /** @var datetime $fieldDateTime */ - public $fieldDateTime; - /** @var string $fieldUnique */ - public $fieldUnique; - /** @var string $fieldKey */ - public $fieldKey; - /** @var mixed $extracol extra column: CURRENT_TIMESTAMP */ - public $extracol; - /** @var mixed $extracol2 extra column: 20 */ - public $extracol2; - - /** @var TableParentExtModel $_TableParentExt onetoone */ - public $_TableParentExt; - /** @var TableParentxCategoryModel[] $_TableParentxCategory manytomany */ - public $_TableParentxCategory; - - - /** - * AbstractTableParentModel constructor. - * - * @param array|null $array - */ - public function __construct($array=null) - { - if($array===null) { - return; - } - foreach($array as $k=>$v) { - $this->{$k}=$v; - } - } - - // - public static function fromArray($array) { - if($array===null) { - return null; - } - $obj=new TableParentModel(); - $obj->idtablaparentPK=isset($array['idtablaparentPK']) ? $array['idtablaparentPK'] : null; - $obj->fieldVarchar=isset($array['fieldVarchar']) ? $array['fieldVarchar'] : null; - $obj->idchildFK=isset($array['idchildFK']) ? $array['idchildFK'] : null; - $obj->fieldInt=isset($array['fieldInt']) ? $array['fieldInt'] : null; - $obj->fielDecimal=isset($array['fielDecimal']) ? $array['fielDecimal'] : null; - $obj->fieldDateTime=isset($array['fieldDateTime']) ? $array['fieldDateTime'] : null; - $obj->fieldUnique=isset($array['fieldUnique']) ? $array['fieldUnique'] : null; - $obj->fieldKey=isset($array['fieldKey']) ? $array['fieldKey'] : null; - $obj->extracol=isset($array['extracol']) ? $array['extracol'] : null; - $obj->extracol2=isset($array['extracol2']) ? $array['extracol2'] : null; - $obj->_TableParentExt=isset($array['_TableParentExt']) ? - $obj->_TableParentExt=TableParentExtModel::fromArray($array['_TableParentExt']) - : null; // onetoone - ($obj->_TableParentExt !== null) - and $obj->_TableParentExt->idtablaparentExtPK=&$obj->xxx1; // linked onetoone - $obj->_TableParentxCategory=isset($array['_TableParentxCategory']) ? - $obj->_TableParentxCategory=TableParentxCategoryModel::fromArrayMultiple($array['_TableParentxCategory']) - : null; // manytomany - - return $obj; - } - - /** - * It converts the current object in an array - * - * @return mixed - */ - public function toArray() { - return static::objectToArray($this); - } - - /** - * It converts an array of arrays into an array of objects. - * - * @param array|null $array - * - * @return array|null - */ - public static function fromArrayMultiple($array) { - if($array===null) { - return null; - } - $objs=[]; - foreach($array as $v) { - $objs[]=self::fromArray($v); - } - return $objs; - } - // - -} // end class \ No newline at end of file diff --git a/examples/repomysql/generatedmodel/AbstractTableParentxCategoryModel.php b/examples/repomysql/generatedmodel/AbstractTableParentxCategoryModel.php deleted file mode 100644 index c21caa3..0000000 --- a/examples/repomysql/generatedmodel/AbstractTableParentxCategoryModel.php +++ /dev/null @@ -1,104 +0,0 @@ - - * $code=$pdoOne->generateAbstractModelClass({args}); - * - */ -abstract class AbstractTableParentxCategoryModel -{ - /** @var int $idtablaparentPKFK */ - public $idtablaparentPKFK; - /** @var int $idcategoryPKFK */ - public $idcategoryPKFK; - - /** @var TableCategoryModel $_idcategoryPKFK manytoone */ - public $_idcategoryPKFK; - /** @var TableParentModel $_idtablaparentPKFK onetoone */ - public $_idtablaparentPKFK; - - - /** - * AbstractTableParentxCategoryModel constructor. - * - * @param array|null $array - */ - public function __construct($array=null) - { - if($array===null) { - return; - } - foreach($array as $k=>$v) { - $this->{$k}=$v; - } - } - - // - public static function fromArray($array) { - if($array===null) { - return null; - } - $obj=new TableParentxCategoryModel(); - $obj->idtablaparentPKFK=isset($array['idtablaparentPKFK']) ? $array['idtablaparentPKFK'] : null; - $obj->idcategoryPKFK=isset($array['idcategoryPKFK']) ? $array['idcategoryPKFK'] : null; - $obj->_idcategoryPKFK=isset($array['_idcategoryPKFK']) ? - $obj->_idcategoryPKFK=TableCategoryModel::fromArray($array['_idcategoryPKFK']) - : null; // manytoone - ($obj->_idcategoryPKFK !== null) - and $obj->_idcategoryPKFK->IdTableCategoryPK=&$obj->idcategoryPKFK; // linked manytoone - $obj->_idtablaparentPKFK=isset($array['_idtablaparentPKFK']) ? - $obj->_idtablaparentPKFK=TableParentModel::fromArray($array['_idtablaparentPKFK']) - : null; // onetoone - ($obj->_idtablaparentPKFK !== null) - and $obj->_idtablaparentPKFK->idtablaparentPK=&$obj->xxx2; // linked onetoone - - return $obj; - } - - /** - * It converts the current object in an array - * - * @return mixed - */ - public function toArray() { - return static::objectToArray($this); - } - - /** - * It converts an array of arrays into an array of objects. - * - * @param array|null $array - * - * @return array|null - */ - public static function fromArrayMultiple($array) { - if($array===null) { - return null; - } - $objs=[]; - foreach($array as $v) { - $objs[]=self::fromArray($v); - } - return $objs; - } - // - -} // end class \ No newline at end of file diff --git a/examples/repomysql/generatedmodel/TableCategoryModel.php b/examples/repomysql/generatedmodel/TableCategoryModel.php deleted file mode 100644 index 419625e..0000000 --- a/examples/repomysql/generatedmodel/TableCategoryModel.php +++ /dev/null @@ -1,30 +0,0 @@ - - * $code=$pdoOne->generateCodeClass({args}); - * - */ -class TableCategoryModel extends AbstractTableCategoryModel -{ - - -} // end class \ No newline at end of file diff --git a/examples/repomysql/generatedmodel/TableChildModel.php b/examples/repomysql/generatedmodel/TableChildModel.php deleted file mode 100644 index fbb7bce..0000000 --- a/examples/repomysql/generatedmodel/TableChildModel.php +++ /dev/null @@ -1,30 +0,0 @@ - - * $code=$pdoOne->generateCodeClass({args}); - * - */ -class TableChildModel extends AbstractTableChildModel -{ - - -} // end class \ No newline at end of file diff --git a/examples/repomysql/generatedmodel/TableGrandChildModel.php b/examples/repomysql/generatedmodel/TableGrandChildModel.php deleted file mode 100644 index 680942a..0000000 --- a/examples/repomysql/generatedmodel/TableGrandChildModel.php +++ /dev/null @@ -1,30 +0,0 @@ - - * $code=$pdoOne->generateCodeClass({args}); - * - */ -class TableGrandChildModel extends AbstractTableGrandChildModel -{ - - -} // end class \ No newline at end of file diff --git a/examples/repomysql/generatedmodel/TableGrandChildTagModel.php b/examples/repomysql/generatedmodel/TableGrandChildTagModel.php deleted file mode 100644 index 8d0b31b..0000000 --- a/examples/repomysql/generatedmodel/TableGrandChildTagModel.php +++ /dev/null @@ -1,30 +0,0 @@ - - * $code=$pdoOne->generateCodeClass({args}); - * - */ -class TableGrandChildTagModel extends AbstractTableGrandChildTagModel -{ - - -} // end class \ No newline at end of file diff --git a/examples/repomysql/generatedmodel/TableParentExtModel.php b/examples/repomysql/generatedmodel/TableParentExtModel.php deleted file mode 100644 index 3fd7f2d..0000000 --- a/examples/repomysql/generatedmodel/TableParentExtModel.php +++ /dev/null @@ -1,30 +0,0 @@ - - * $code=$pdoOne->generateCodeClass({args}); - * - */ -class TableParentExtModel extends AbstractTableParentExtModel -{ - - -} // end class \ No newline at end of file diff --git a/examples/repomysql/generatedmodel/TableParentModel.php b/examples/repomysql/generatedmodel/TableParentModel.php deleted file mode 100644 index 7f2e16f..0000000 --- a/examples/repomysql/generatedmodel/TableParentModel.php +++ /dev/null @@ -1,30 +0,0 @@ - - * $code=$pdoOne->generateCodeClass({args}); - * - */ -class TableParentModel extends AbstractTableParentModel -{ - - -} // end class \ No newline at end of file diff --git a/examples/repomysql/generatedmodel/TableParentxCategoryModel.php b/examples/repomysql/generatedmodel/TableParentxCategoryModel.php deleted file mode 100644 index 0ad9524..0000000 --- a/examples/repomysql/generatedmodel/TableParentxCategoryModel.php +++ /dev/null @@ -1,30 +0,0 @@ - - * $code=$pdoOne->generateCodeClass({args}); - * - */ -class TableParentxCategoryModel extends AbstractTableParentxCategoryModel -{ - - -} // end class \ No newline at end of file diff --git a/examples/repomysql/test_select.php b/examples/repomysql/test_select.php index 48b6c3e..f83a1c0 100644 --- a/examples/repomysql/test_select.php +++ b/examples/repomysql/test_select.php @@ -21,6 +21,8 @@ ])::testRecursive(); + + //new dBug(TableParentRepo::base()->runRawQuery('select * from tableparent',[],true)); TableChildRepo::$useModel = false; $child = TableParentRepo::setRecursive([ diff --git a/examples/repomysql/test_select2.php b/examples/repomysql/test_select2.php index 00faac1..cdd5e32 100644 --- a/examples/repomysql/test_select2.php +++ b/examples/repomysql/test_select2.php @@ -12,6 +12,20 @@ new dBug(TableParentRepo::base()->runRawQuery('select * from tableparent',[],true)); +$random=''; +for($i=0;$i<10;$i++) { + $random.=chr(mt_rand(64,90)); +} + +$m=new TableParentModel(); +$m->fieldUnique=$random; +$m->fieldKey="key1"; +$m->fieldVarchar='varchar'; +$m->fieldDateTime=new DateTime(); + + +//TableParentRepo::insert($m); + $parent= (TableParentRepo::setRecursive( [ @@ -23,7 +37,7 @@ ,'_TableParentExt' ]))::first(1); new dBug($parent); -die(1); + diff --git a/lib/PdoOne.php b/lib/PdoOne.php index e621e7c..004cd4a 100644 --- a/lib/PdoOne.php +++ b/lib/PdoOne.php @@ -1,4 +1,5 @@ - - - // /** * @var array * @see \eftec\PdoOne::generateCodeClassConversions * @see \eftec\PdoOne::generateCodeClass */ - private $codeClassConversion = []; - private $lastBindParam = []; + public $codeClassConversion = []; + public $genError = true; /** @var int */ - private $affected_rows = 0; - private $select = ''; + public $affected_rows = 0; + + /** @var PdoOne_IExt */ + public $service; + /** @var string|array [optional] It is the family or group of the cache */ + public $cacheFamily = ''; + /** @var IPdoOneCache The service of cache [optional] */ + public $cacheService; /** * @var null|int $ttl If 0 then the cache never expires.
* If false then we don't use cache.
@@ -172,53 +182,10 @@ class PdoOne * cache * (in seconds) */ - private $useCache = false; - /** @var bool if true then builderReset will not reset (unless it is force), if false then it will reset */ - private $noReset = false; + public $useCache = false; /** @var null|array it stores the values obtained by $this->tableDependency() */ - private $tableDependencyArrayCol; - private $tableDependencyArray; - /** @var null|string the unique id generate by sha256or $hashtype and based in the query, arguments, type - * and methods - */ - private $uid; - /** @var string|array [optional] It is the family or group of the cache */ - private $cacheFamily = ''; - /** @var IPdoOneCache The service of cache [optional] */ - private $cacheService; - /** @var array */ - private $where = []; - - /** @var array parameters for the set. [paramvar,value,type,size] */ - private $setParamAssoc = []; - - - /** @var array parameters for the where. [paramvar,value,type,size] */ - private $whereParamAssoc = []; - /** @var array parameters for the having. [paramvar,value,type,size] */ - private $havingParamAssoc = []; - - private $whereCounter = 1; - - /** @var array */ - //private $whereParamValue = []; - - /** @var array */ - private $set = []; - - private $group = ''; - - private $recursive = []; - - private $genError = true; - - /** @var array */ - private $having = []; - - private $distinct = ''; - - /** @var PdoOne_IExt */ - private $service; + public $tableDependencyArrayCol; + public $tableDependencyArray; //
@@ -646,101 +613,6 @@ public function getDefTableExtended($table, $onlyDescription = false) return $this->service->getDefTableExtended($table, $onlyDescription); } - /** - * It adds an "order by" in a query.
- * Example:
- *
-     *      ->select("")->order("column")->toList();
-     *      ->select("")->order("col1,col2")->toList();
-     * 
- * - * @param string $sql Input SQL query - * - * @return PdoOne - * @test InstanceOf PdoOne::class,this('name desc') - */ - public function order($sql) - { - if ($sql === null) { - return $this; - } - $this->order = ($sql) ? ' order by ' . $sql : ''; - - return $this; - } - - /** - * Macro of join.
- * Example:
- *
-     *          innerjoin('tablejoin on t1.field=t2.field')
-     *          innerjoin('tablejoin tj on t1.field=t2.field')
-     *          innerjoin('tablejoin','t1.field=t2.field')
-     * 
- * - * @param string $sql - * @param string $condition - * - * @return PdoOne - * @see \eftec\PdoOne::join - */ - public function innerjoin($sql, $condition = '') - { - return $this->join($sql, $condition); - } - - /** - * It generates an inner join
- * Example:
- *
-     *          join('tablejoin on t1.field=t2.field')
- * join('tablejoin','t1.field=t2.field')
- *
- * - * @param string $sql Example "tablejoin on table1.field=tablejoin.field" - * @param string $condition - * - * @return PdoOne - * @test InstanceOf PdoOne::class,this('tablejoin on t1.field=t2.field') - */ - public function join($sql, $condition = '') - { - if ($condition !== '') { - $sql = "$sql on $condition"; - } - $this->from .= ($sql) ? " inner join $sql " : ''; - $this->tables[] = explode(' ', $sql)[0]; - - return $this; - } - - /** - * Adds a from for a query. It could be used by select,insert,update and - * delete.
- * Example:
- *
-     *      from('table')
-     *      from('table alias')
-     *      from('table1,table2')
-     *      from('table1 inner join table2 on table1.c=table2.c')
-     * 
- * - * @param string $sql Input SQL query - * - * @return PdoOne - * @test InstanceOf PdoOne::class,this('table t1') - */ - public function from($sql) - { - if ($sql === null) { - return $this; - } - $this->from = ($sql) ? $sql . $this->from : $this->from; - $this->tables[] = explode(' ', $sql)[0]; - - return $this; - } - /** * It executes the cli Engine. * @@ -839,7 +711,7 @@ protected static function removeTrailSlash($txt) * @return false|string * @throws Exception */ - protected function run( + public function run( $database, $server, $user, @@ -905,2970 +777,2993 @@ protected function run( return "Output $output not defined. Use csv/json/selectcode/arraycode/createcode/classcode"; } } - /** - * Connects to the database. - * - * @param bool $failIfConnected true=it throw an error if it's connected, - * otherwise it does nothing + * @param string $tableName * + * @return string * @throws Exception - * @test exception this(false) */ - public function connect($failIfConnected = true) + public function generateCodeCreate($tableName) { - if ($this->isOpen) { - if (!$failIfConnected) { - return; - } // it's already connected. - $this->throwError('Already connected', ''); - } - try { - if ($this->logLevel >= 2) { - $this->storeInfo("connecting to {$this->server} {$this->user}/*** {$this->db}"); - } - $cs = (!$this->charset) ? ';charset=' . $this->charset : ''; - $this->service->connect($cs, false); - if ($this->conn1 instanceof stdClass) { - $this->isOpen = true; - return; - } - $this->conn1->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - $this->conn1->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); - - //$this->conn1->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); It is not required. + $code = "\$pdo->createTable('" . $tableName . "',\n"; + $arr = $this->getDefTable($tableName); + $arrKey = $this->getDefTableKeys($tableName); + $arrFK = self::varExport($this->getDefTableFK($tableName)); + $keys = self::varExport($arrKey); + $code .= "\t" . self::varExport($arr); + $code .= ",$keys);\n"; + $code .= "\$pdo->createFk('" . $tableName . "',\n"; + $code .= "$arrFK);\n"; - $this->isOpen = true; - } catch (Exception $ex) { - $this->isOpen = false; - $this->throwError("Failed to connect to {$this->databaseType}", $ex->getMessage(), '', true, $ex); - } + return $code; } + public function generateBaseClass($baseClassName, $namespace, $classes, $modelUse = false) + { + $r = <<<'eot' +=2. It - * shows an extra message - * @param string|array $extraParam It's only used if $logLevel>=3 It - * shows parameters (if any) - * - * @param bool $throwError if true then it throw error (is enabled). Otherwise it store the error. - * - * @param null|RuntimeException $exception + * With the name of the table, we get the class + * @param string $tableName * - * @see \eftec\PdoOne::$logLevel + * @return string[] */ - public function throwError($txt, $txtExtra, $extraParam = '', $throwError = true, $exception = null) - { - if ($this->logLevel === 0) { - $txt = 'Error on database'; - } - if ($this->logLevel >= 2) { - $txt .= "\n
extra:[{$txtExtra}]"; - } - if ($this->logLevel >= 2) { - $txt .= "\n
last query:[{$this->lastQuery}]"; - } - if ($this->logLevel >= 3) { - $txt .= "\n
database:" . $this->server . ' - ' . $this->db; - if (is_array($extraParam)) { - foreach ($extraParam as $k => $v) { - if (is_array($v) || is_object($v)) { - $v = json_encode($v); - } - $txt .= "\n
$k:$v"; - } - } else { - $txt .= "\n
Params :[" . $extraParam . "]\n
"; - } - if ($exception !== null) { - $txt .= "\n
message :[" . str_replace("\n", "\n
", $exception->getMessage()) . "]"; - $txt .= "\n
trace :[" . str_replace("\n", "\n
", $exception->getTraceAsString()) . "]"; - $txt .= "\n
code :[" . str_replace("\n", "\n
", $exception->getCode()) . "]\n
"; + protected function tabletoClass($tableName) { + return static::RELATIONS[$tableName]; + } +} +eot; + /*foreach($classes as $id=>$entity) { + foreach($entity as $k=>$class) { + $classes[$id][$k] = $namespace . '\\' . $class; } } - if ($this->getMessages() === null) { - $this->debugFile($txt, 'ERROR'); - } else { - $this->getMessages()->addItem($this->db, $txt); - $this->debugFile($txt, 'ERROR'); - } - $this->errorText = $txt; - if ($throwError && $this->throwOnError && $this->genError) { - throw new RuntimeException($txt); - } - $this->builderReset(true); // it resets the chain if any. - } + */ + $namespace = trim($namespace, '\\'); - /** - * Injects a Message Container. - * - * @return MessageList|null - * @test equals null,this(),'this is not a message container' - */ - public function getMessages() - { - if (function_exists('messages')) { - return messages(); - } - - return null; - } - - public function debugFile($txt, $level = 'INFO') - { - if (!$this->logFile) { - return; // debug file is disabled. - } - $fz = @filesize($this->logFile); - - if (is_object($txt) || is_array($txt)) { - $txtW = print_r($txt, true); - } else { - $txtW = $txt; - } - if ($fz > 10000000) { - // mas de 10mb = reducirlo a cero. - $fp = @fopen($this->logFile, 'wb'); - } else { - $fp = @fopen($this->logFile, 'ab'); - } - if(!$fp) { - return; // unable to open the log file. - } - if ($this->logLevel === 2) { - $txtW .= ' param:' . json_encode($this->lastParam); - } - - $txtW = str_replace(array("\r\n", "\n"), ' ', $txtW); - try { - $now = new DateTime(); - @fwrite($fp, $now->format('c') . "\t" . $level . "\t" . $txtW . "\n"); - } catch (Exception $e) { - } - - @fclose($fp); + return str_replace([ + '{type}', + '{class}', + '{exception}', + '{namespace}', + '{namespace2}', + '{relations}', + '{modeluse}', + '{version}', + '{compiled}' + ], [ + $this->databaseType, + $baseClassName, + ($namespace) ? 'use Exception;' : '', // {exception} + ($namespace) ? "namespace $namespace;" : '', // {namespace} + ($namespace) ? "$namespace\\\\" : '', // {namespace2} + $this::varExport($classes), + $modelUse ? 'true' : 'false', // {modeluse} + self::VERSION . ' Date generated ' . date('r'), // {version} + _BasePdoOneRepo::BINARYVERSION, // {compiled} + ], $r); } /** - * @return array + * @param string $tableName + * @param string $namespace + * @param null $customRelation + * @param null $classRelations + * @param array $specialConversion + * @param null $defNoInsert + * @param null $defNoUpdate + * @param null $baseClass + * @param array $extraColumn + * @param array $columnRemove + * + * @return string|string[] + * @throws Exception */ - public function getSetParamAssoc() + public function generateAbstractModelClass( + $tableName, + $namespace = '', + $customRelation = null, + $classRelations = null, + $specialConversion = [], + $defNoInsert = null, + $defNoUpdate = null, + $baseClass = null, + $extraColumn = [], + $columnRemove = [] + ) { - return $this->setParamAssoc; - } + $r = <<<'eot' +whereParamAssoc; - } +/** + * Generated by PdoOne Version {version}. + * DO NOT EDIT THIS CODE. THIS CODE WILL SELF GENERATE. + * @copyright (c) Jorge Castro C. MIT License https://github.com/EFTEC/PdoOne + * Class {classname} + *
+ * $code=$pdoOne->generateAbstractModelClass({args});
+ * 
+ */ +abstract class Abstract{classname} +{ +{fields} + +{fieldsrel} - /** - * @return array - */ - public function getHavingParamAssoc() - { - return $this->havingParamAssoc; - } /** - * It reset the parameters used to Build Query. + * Abstract{classname} constructor. * - * @param bool $forced if true then calling this method resets the stacks of variables
- * if false then it only resets the stack if $this->noreset=false; (default is false) + * @param array|null $array */ - public function builderReset($forced = false) + public function __construct($array=null) { - if ($this->noReset && !$forced) { + if($array===null) { return; } - $this->select = ''; - $this->noReset = false; - $this->useCache = false; - $this->from = ''; - $this->tables = []; - $this->where = []; - - $this->whereParamAssoc = []; - $this->setParamAssoc = []; - $this->havingParamAssoc = []; - - $this->whereCounter = 1; - //$this->whereParamValue = []; - $this->set = []; - $this->group = ''; - $this->recursive = []; - $this->genError = true; - $this->having = []; - $this->limit = ''; - $this->distinct = ''; - $this->order = ''; + foreach($array as $k=>$v) { + $this->{$k}=$v; + } } - /** - * Write a log line for debug, clean the command chain then throw an error - * (if throwOnError==true) - * - * @param $txt - * - * @throws Exception - */ - public function storeInfo($txt) - { - if ($this->getMessages() === null) { - $this->debugFile($txt); - } else { - $this->getMessages()->addItem($this->db, $txt, 'info'); - $this->debugFile($txt); + // + public static function fromArray($array) { + if($array===null) { + return null; } - } + $obj=new {classname}(); +{fieldsfa} +{fieldsrelfa} + return $obj; + } + /** - * Returns the last error. - * - * @return string + * It converts the current object in an array + * + * @return mixed */ - public function lastError() - { - if (!$this->isOpen) { - return "It's not connected to the database"; - } - - return $this->conn1->errorInfo()[2]; + public function toArray() { + return static::objectToArray($this); } - + /** - * It adds a delimiter to a text based in the type of database (` for mysql - * and [] for sql server)
Example:
- * $pdoOne->addDelimiter('hello world'); // `hello` world
- * $pdoOne->addDelimiter('hello.world'); // `hello`.`world`
- * $pdoOne->addDelimiter('hello=value); // `hello`=value
- * - * @param $txt + * It converts an array of arrays into an array of objects. + * + * @param array|null $array * - * @return mixed|string + * @return array|null */ - public function addDelimiter($txt) - { - if (strpos($txt, $this->database_delimiter0) === false) { - $pos = $this->strposa($txt, [' ', '=']); - if ($pos === false) { - $quoted = $this->database_delimiter0 . $txt . $this->database_delimiter1; - $quoted = str_replace('.', $this->database_delimiter1 . '.' . $this->database_delimiter0, $quoted); - } else { - $arr = explode(substr($txt, $pos, 1), $txt, 2); - $quoted - = $this->database_delimiter0 . $arr[0] . $this->database_delimiter1 . substr($txt, $pos, 1) - . $arr[1]; - $quoted = str_replace('.', $this->database_delimiter1 . '.' . $this->database_delimiter0, $quoted); - } - - return $quoted; + public static function fromArrayMultiple($array) { + if($array===null) { + return null; } - // it has a delimiter, so we returned the same text. - return $txt; + $objs=[]; + foreach($array as $v) { + $objs[]=self::fromArray($v); + } + return $objs; } + //
+ +} // end class +eot; + //$lastns = explode('\\', $namespace); + //$baseClass = ($baseClass === null) ? end($lastns) : $baseClass; - private function strposa($haystack, $needles = [], $offset = 0) - { - $chr = []; - foreach ($needles as $needle) { - $res = strpos($haystack, $needle, $offset); - if ($res !== false) { - $chr[$needle] = $res; + $fa = func_get_args(); + foreach ($fa as $f => $k) { + if (is_array($k)) { + $fa[$f] = str_replace([' ', "\r\n", "\n"], ['', '', ''], var_export($k, true)); + } else { + $fa[$f] = "'$k'"; } } - if (empty($chr)) { - return false; + if ($classRelations === null || !isset($classRelations[$tableName])) { + $className = self::camelize($tableName); + } else { + $className = $classRelations[$tableName]; } - return min($chr); - } - - /** - * 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 $param [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
-     *
-     * @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($rawSql, $param = null, $returnArray = true)
-    {
-        if (!$this->isOpen) {
-            $this->throwError("It's not connected to the database", '');
+        $r = str_replace(array(
+            '{version}',
+            '{classname}',
+            '{exception}',
+            '{namespace}'
+        ), array(
+            self::VERSION . ' Date generated ' . date('r'), //{version}
+            $className, // {classname}
+            ($namespace) ? 'use Exception;' : '',
+            ($namespace) ? "namespace $namespace;" : ''
+        ), $r);
+        $pk = '??';
+        $pk = $this->service->getPK($tableName, $pk);
+        $pkFirst = (is_array($pk) && count($pk) > 0) ? $pk[0] : null;
 
-            return false;
-        }
-        if (!$rawSql) {
-            $this->throwError("Query empty", '');
-            return false;
+        try {
+            $relation = $this->getDefTableFK($tableName, false, true);
+        } catch (Exception $e) {
+            return 'Error: Unable read fk of table ' . $e->getMessage();
         }
-        $writeCommand = self::queryCommand($rawSql, true) !== 'dql';
-
-        /** @var bool|string $uid it stores the unique identifier of the query */
-        $uid = false;
 
-        if ($this->readonly && $writeCommand) {
-            // we aren't checking SQL-DLC queries. Also, "insert into" is stopped but "  insert into" not.
-            $this->throwError('Database is in READ ONLY MODE', '');
-            return false;
+        try {
+            $deps = $this->tableDependency(true);
+        } catch (Exception $e) {
+            return 'Error: Unable read table dependencies ' . $e->getMessage();
+        } //  ["city"]=> {["city_id"]=> "address"}
+        $after = @$deps[1][$tableName];
+        if ($after === null) {
+            $after = @$deps[1][strtolower($tableName)];
         }
-        if (!is_array($param) && $param !== null) {
-            $this->throwError('runRawQuery, param must be null or an array', '');
-            return false;
+        $before = @$deps[2][$tableName];
+        if ($before === null) {
+            $before = @$deps[2][strtolower($tableName)];
         }
-        if ($this->useInternalCache && $returnArray === true && !$writeCommand) {
-            // if we use internal cache and we returns an array and it is not a write command
-            $uid = hash($this->encryption->hashType, $rawSql . serialize($param));
-            if (isset($this->internalCache[$uid])) {
-                // we have an internal cache, so we will return it.
-                $this->internalCacheCounter++;
-                return $this->internalCache[$uid];
+        if (is_array($after) && is_array($before)) {
+            foreach ($before as $key => $rows) { // $value is [relcol,table]
+                foreach ($rows as $value) {
+                    $relation['' . self::$prefixBase . $value[1]] = [
+                        'key' => 'ONETOMANY',
+                        'col' => $key,
+                        'reftable' => $value[1],
+                        'refcol' => $value[0]
+                    ];
+                }
             }
         }
-
-        $this->lastParam = $param;
-        $this->lastQuery = $rawSql;
-        if ($this->logLevel >= 2) {
-            $this->storeInfo($rawSql);
-        }
-        if ($param === null) {
-            $rows = $this->runRawQueryParamLess($rawSql, $returnArray);
-            if ($uid !== false && $returnArray) {
-                $this->internalCache[$uid] = $rows;
+        // converts relations to ONETOONE
+        foreach ($relation as $k => $rel) {
+            if ($rel['key'] === 'ONETOMANY') {
+                $pkref = null;
+                $pkref = $this->service->getPK($rel['reftable'], $pkref);
+                if ('' . self::$prefixBase . $pkref[0] === $rel['refcol'] && count($pkref) === 1) {
+                    $relation[$k]['key'] = 'ONETOONE';
+                    $relation[$k]['col'] = 'xxx1';
+                    $relation[$k]['refcol'] = ltrim($relation[$k]['refcol'], self::$prefixBase);
+                }
             }
-            return $rows;
-        }
+            if ($rel['key'] === 'MANYTOONE') {
+                $pkref = null;
+                $pkref = $this->service->getPK($rel['reftable'], $pkref);
 
-        // the "where" has parameters.
-        $stmt = $this->prepare($rawSql);
-        if ($stmt === false) {
-            $this->throwError("Unable to prepare statement", $rawSql);
-            return false;
-        }
-        $counter = 0;
-        if ($this->isAssoc($param)) {
-            $this->lastBindParam = $param;
-            // [':name'=>value,':name2'=>value2];
-            foreach ($param as $k => $v) {
-                // note: the second field is & so we could not use $v
-                $stmt->bindParam($k, $param[$k], $this->getType($v));
-            }
-        } else {
-            // parameters numeric
-            $this->lastBindParam = [];
-            $f = reset($param);
-            if (is_array($f)) {
-                // arrays of arrays.
-                // [[name1,value1,type1,l1],[name2,value2,type2,l1]]
-                foreach ($param as $k => $v) {
-                    $this->lastBindParam[$counter] = $v[0];
-                    // note: the second field is & so we could not use $v
-                    $stmt->bindParam($v[0], $param[$k][1], $v[2], $v[3]);
-                }
-            } else {
-                // [value1,value2]
-                foreach ($param as $i => $iValue) {
-                    //$counter++;
-                    //$typeP = $this->stringToPdoParam($param[$i]);
-                    $this->lastBindParam[$i] = $param[$i];
-                    //$stmt->bindParam($counter, $param[$i + 1], $typeP);
-                    $stmt->bindParam($i + 1, $param[$i], $this->getType($param[$i]));
+                if ($pkref[0] === $rel['refcol'] && count($pkref) === 1
+                    && (strcasecmp($k, '' . self::$prefixBase . $pkFirst) === 0)
+                ) {
+                    // if they are linked by the pks and the pks are only 1.
+                    $relation[$k]['key'] = 'ONETOONE';
+                    $relation[$k]['col'] = 'xxx2';
+                    $relation[$k]['refcol'] = ltrim($relation[$k]['refcol'], self::$prefixBase);
                 }
             }
         }
+        if ($customRelation) {
+            foreach ($relation as $k => $rel) {
+                if (isset($customRelation[$k])) {
+                    // parent.
+                    if ($customRelation[$k] === 'PARENT') {
+                        $relation[$k]['key'] = 'PARENT';
+                    } elseif ($customRelation[$k] === 'MANYTOMANY') {
+                        // the table must has 2 primary keys.
+                        $pks = null;
+                        $pks = $this->service->getPK($relation[$k]['reftable'], $pks);
+                        /** @noinspection PhpParamsInspection */
+                        if ($pks !== false || count($pks) === 2) {
+                            $relation[$k]['key'] = 'MANYTOMANY';
+                            $refcol2 = ('' . self::$prefixBase . $pks[0] === $relation[$k]['refcol']) ? $pks[1]
+                                : $pks[0];
 
-        if ($this->useCache !== false && $returnArray) {
-            $this->uid = hash($this->encryption->hashType, $this->lastQuery . serialize($this->lastBindParam));
-            $result = $this->cacheService->getCache($this->uid, $this->cacheFamily);
-            if ($result !== false) {
-                // it's found in the cache.
-                if (is_array($result)) {
-                    $this->affected_rows = count($result);
-                } else {
-                    $this->affected_rows = 0;
-                }
-                if ($uid !== false) {
-                    $this->internalCache[$uid] = $result;
+                            try {
+                                $defsFK = $this->service->getDefTableFK($relation[$k]['reftable'], false);
+                            } catch (Exception $e) {
+                                return 'Error: Unable read table dependencies ' . $e->getMessage();
+                            }
+                            try {
+                                $keys2 = $this->service->getDefTableKeys($defsFK[$refcol2]['reftable'], true,
+                                    'PRIMARY KEY');
+                            } catch (Exception $e) {
+                                return 'Error: Unable read table dependencies' . $e->getMessage();
+                            }
+                            $relation[$k]['refcol2'] = '' . self::$prefixBase . $refcol2;
+                            if (is_array($keys2)) {
+                                $keys2 = array_keys($keys2);
+                                $relation[$k]['col2'] = $keys2[0];
+                            } else {
+                                $relation[$k]['col2'] = null;
+                            }
+                            $relation[$k]['table2'] = $defsFK[$refcol2]['reftable'];
+                        }
+                    }
+                    // manytomany
                 }
-                return $result;
             }
-        } else {
-            $this->uid = null;
         }
-        $this->runQuery($stmt);
+        //die(1);
 
-        if ($returnArray && $stmt instanceof PDOStatement) {
-            $rows = ($stmt->columnCount() > 0) ? $stmt->fetchAll(PDO::FETCH_ASSOC) : [];
-            $this->affected_rows = $stmt->rowCount();
-            $stmt = null;
-            if ($uid !== false) {
-                $this->internalCache[$uid] = $rows;
-            }
-            return $rows;
-        }
+        $gdf = $this->getDefTable($tableName, $specialConversion);
 
-        if ($stmt instanceof PDOStatement) {
-            $this->affected_rows = $stmt->rowCount();
-        } else {
-            $this->affected_rows = 0;
+        foreach ($columnRemove as $v) {
+            unset($gdf[$v]);
         }
 
-        return $stmt;
-    }
-
-    /**
-     * It returns the command (in lower case) or the type of command of a query
- * Example:
- *
-     * $this->queryCommand("select * from table") // returns "select"
-     * $this->queryCommand("select * from table",true) // returns "dql"
-     * 
- * - * @param string $sql - * @param false $returnType if true then it returns DML (insert/updat/delete/etc) or DQL (select/show/display) - * - * @return string - * - */ - public static function queryCommand($sql, $returnType = false) - { - if (!$sql) { - return $returnType ? 'dml' : 'dql'; - } - $command = strtolower((explode(' ', trim($sql)))[0]); - if ($returnType) { - if ($command === 'select' || $command === 'show' || $command === 'display') { - return 'dql'; + $fields = []; + $fieldsb = []; + foreach ($gdf as $varn => $field) { + switch ($field['phptype']) { //binary, date, datetime, decimal,int, string,time, timestamp + case 'binary': + case 'date': + case 'datetime': + case 'decimal': + case 'float': + case 'int': + case 'string': + case 'time': + case 'timestamp': + $fields[] = "\t/** @var " . $field['phptype'] . " \$$varn */\n\tpublic \$$varn;"; + $fieldsb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? \$array['$varn'] : null;"; + break; } - return 'dml'; } - return $command; - } + foreach ($extraColumn as $varn => $value) { + $fields[] = "\t/** @var mixed \$$varn extra column: $value */\n\tpublic \$$varn;"; + $fieldsb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? \$array['$varn'] : null;"; + } + $fieldsArr = implode("\n", $fields); + $fieldsbArr = implode("\n", $fieldsb); - // + $field2s = []; + $field2sb = []; + foreach ($relation as $varn => $field) { + //$varnclean = ltrim($varn, PdoOne::$prefixBase); + switch ($field['key']) { + case 'FOREIGN KEY': + break; + case 'MANYTOONE': + $class = $classRelations[$field['reftable']]; + $field2s[] = "\t/** @var $class \$$varn manytoone */ + public \$$varn;"; + $field2sb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? + \$obj->$varn=$class::fromArray(\$array['$varn']) + : null; // manytoone"; + $col = ltrim($varn, self::$prefixBase); + $rcol = $field['refcol']; + $field2sb[] = "\t\t(\$obj->$varn !== null) + and \$obj->{$varn}->{$rcol}=&\$obj->$col; // linked manytoone"; + break; + case 'MANYTOMANY': + $class = $classRelations[$field['reftable']]; + $field2s[] = "\t/** @var {$class}[] \$$varn manytomany */ + public \$$varn;"; + $field2sb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? + \$obj->$varn=$class::fromArrayMultiple(\$array['$varn']) + : null; // manytomany"; + break; + case 'ONETOMANY': + $class = $classRelations[$field['reftable']]; + $field2s[] = "\t/** @var {$class}[] \$$varn onetomany */ + public \$$varn;"; + $field2sb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? + \$obj->$varn=$class::fromArrayMultiple(\$array['$varn']) + : null; // onetomany"; + break; + case 'ONETOONE': + $class = $classRelations[$field['reftable']]; + $field2s[] = "\t/** @var $class \$$varn onetoone */ + public \$$varn;"; + $field2sb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? + \$obj->$varn=$class::fromArray(\$array['$varn']) + : null; // onetoone"; - /** - * It starts a transaction. If fails then it returns false, otherwise true. - * - * @return bool - * @test equals true,this() - * @posttest execution $this->pdoOne->commit(); - * @example examples/testdb.php 92,4 - */ - public function startTransaction() - { - if ($this->transactionOpen || !$this->isOpen) { - return false; + $col = isset($field['col']) ? $field['col'] : $pkFirst; + + $rcol = $field['refcol']; + + $field2sb[] = "\t\t(\$obj->$varn !== null) + and \$obj->{$varn}->{$rcol}=&\$obj->$col; // linked onetoone"; + break; + } } - $this->transactionOpen = true; - $this->conn1->beginTransaction(); - return true; - } + $fields2Arr = implode("\n", $field2s); + $fields2Arrb = implode("\n", $field2sb); - /** - * Commit and close a transaction. - * - * @param bool $throw if true and it fails then it throws an error. - * - * @return bool - * @throws Exception - * @test equals false,(false),'transaction is not open' - */ - public function commit($throw = true) - { - if (!$this->transactionOpen && $throw) { - $this->throwError('Transaction not open to commit()', ''); + $r = str_replace(['{fields}', '{fieldsrel}', '{fieldsfa}', '{fieldsrelfa}'], + [$fieldsArr, $fields2Arr, $fieldsbArr, $fields2Arrb], $r); + // return $r; + // die(1); - return false; + if (@count($this->codeClassConversion) > 0) { + // we forced the conversion but only if it is not specified explicit + foreach ($gdf as $k => $colDef) { + $type = $colDef['type']; + if (isset($this->codeClassConversion[$type]) && $gdf[$k]['conversion'] === null) { + $gdf[$k]['conversion'] = $this->codeClassConversion[$type]; + } + } } - if (!$this->isOpen) { - $this->throwError("It's not connected to the database", ''); - return false; + // discard columns + $identities = $this->getDefIdentities($tableName); + if ($defNoInsert !== null) { + $noInsert = array_merge($identities, $defNoInsert); + } else { + $noInsert = $identities; } - $this->transactionOpen = false; - - return @$this->conn1->commit(); - } - - /** - * Rollback and close a transaction - * - * @param bool $throw [optional] if true and it fails then it throws an error. - * - * @return bool - * @throws Exception - * @test equals false,(false),'transaction is not open' - */ - public function rollback($throw = true) - { - if (!$this->transactionOpen && $throw) { - $this->throwError('Transaction not open to rollback()', ''); + if ($defNoInsert !== null) { + $noUpdate = array_merge($identities, $defNoUpdate); + } else { + $noUpdate = $identities; } - if (!$this->isOpen) { - $this->throwError("It's not connected to the database", ''); - return false; + try { + $r = str_replace(array( + '{pk}', + '{def}', + '{defname}', + '{defkey}', + '{defnoinsert}', + '{defnoupdate}', + '{deffk}', + '{deffktype}', + '{array}', + '{array_null}' + ), array( + self::varExport($pk), + //str_replace(["\n\t\t ", "\n\t\t ],"], ['', '],'], PdoOne::varExport($gdf, "\t\t")), // {def} + self::varExport($gdf, "\t\t"), + self::varExport(array_keys($gdf), "\t\t"), // {defname} + self::varExport($this->getDefTableKeys($tableName), "\t\t"), // {defkey} + self::varExport($noInsert, "\t\t"), // {defnoinsert} + self::varExport($noUpdate, "\t\t"), // {defnoupdate} + self::varExport($this->getDefTableFK($tableName), "\t\t\t"), //{deffk} + self::varExport($relation, "\t\t"), //{deffktype} + str_replace("\n", "\n\t\t", + rtrim($this->generateCodeArray($tableName, null, false, false, true, $classRelations, $relation), + "\n")), + str_replace("\n", "\n\t\t", + rtrim($this->generateCodeArray($tableName, null, true, false, true, $classRelations, $relation), + "\n")) + ), $r); + } catch (Exception $e) { + return "Unable read definition of tables " . $e->getMessage(); } - $this->transactionOpen = false; - return @$this->conn1->rollback(); + return $r; } - // - /** - * Internal Use: It runs a raw query - * - * @param string $rawSql - * @param bool $returnArray + * @param string $tableName + * @param string $namespace + * @param null $customRelation + * @param null $classRelations + * @param array $specialConversion + * @param null $defNoInsert + * @param null $defNoUpdate + * @param null $baseClass * - * @return array|bool|false|PDOStatement + * @return string|string[] * @throws Exception - * @see \eftec\PdoOne::runRawQuery */ - private function runRawQueryParamLess($rawSql, $returnArray) + public function generateModelClass( + $tableName, + $namespace = '', + $customRelation = null, + $classRelations = null, + $specialConversion = [], + $defNoInsert = null, + $defNoUpdate = null, + $baseClass = null + ) { - // the "where" chain doesn't have parameters. - try { - $rows = $this->conn1->query($rawSql); - if ($rows === false) { - throw new RuntimeException('Unable to run raw runRawQueryParamLess', 9001); - } - } catch (Exception $ex) { - $rows = false; - $this->throwError('Exception in runRawQueryParamLess :', $rawSql, ['param' => $this->lastParam], true, $ex); - } - - if ($returnArray && $rows instanceof PDOStatement) { - if ($rows->columnCount() > 0) { - $result = @$rows->fetchAll(PDO::FETCH_ASSOC); - $this->affected_rows = $rows->rowCount(); + $r = <<<'eot' + + * $code=$pdoOne->generateCodeClass({args}); + *
+ */ +class {classname} extends Abstract{classname} +{ - $this->affected_rows = $rows->rowCount(); + +} // end class +eot; + //$lastns = explode('\\', $namespace); + //$baseClass = ($baseClass === null) ? end($lastns) : $baseClass; - return true; + $fa = func_get_args(); + foreach ($fa as $f => $k) { + if (is_array($k)) { + $fa[$f] = str_replace([' ', "\r\n", "\n"], ['', '', ''], var_export($k, true)); + } else { + $fa[$f] = "'$k'"; + } + } + if ($classRelations === null || !isset($classRelations[$tableName])) { + $className = self::camelize($tableName); + } else { + $className = $classRelations[$tableName]; } - $this->affected_rows = $rows->rowCount(); - - return $rows; - } - - /** - * Prepare a query. It returns a mysqli statement. - * - * @param string $statement A SQL statement. - * - * @return PDOStatement returns the statement if correct otherwise null - * @throws Exception - */ - public function prepare($statement) - { - if (!$this->isOpen) { - $this->throwError("It's not connected to the database", ''); + $r = str_replace(array( + '{version}', + '{classname}', + '{exception}', + '{namespace}' + ), array( + self::VERSION . ' Date generated ' . date('r'), //{version} + $className, // {classname} + ($namespace) ? 'use Exception;' : '', + ($namespace) ? "namespace $namespace;" : '' + ), $r); + $pk = '??'; + $pk = $this->service->getPK($tableName, $pk); + $pkFirst = (is_array($pk) && count($pk) > 0) ? $pk[0] : null; - return null; - } - $this->lastQuery = $statement; - if ($this->readonly) { - if (stripos($statement, 'insert ') === 0 || stripos($statement, 'update ') === 0 - || stripos($statement, 'delete ') === 0 - ) { - // we aren't checking SQL-DCL queries. - $this->throwError('Database is in READ ONLY MODE', ''); - } - } - if ($this->logLevel >= 2) { - $this->storeInfo($statement); + try { + $relation = $this->getDefTableFK($tableName, false, true); + } catch (Exception $e) { + return 'Error: Unable read fk of table ' . $e->getMessage(); } try { - $stmt = $this->conn1->prepare($statement); - } catch (Exception $ex) { - $stmt = false; - if ($this->errorText === '') { - $this->throwError('Failed to prepare', $ex->getMessage(), ['param' => $this->lastParam]); - } + $deps = $this->tableDependency(true); + } catch (Exception $e) { + return 'Error: Unable read table dependencies ' . $e->getMessage(); + } // ["city"]=> {["city_id"]=> "address"} + $after = @$deps[1][$tableName]; + if ($after === null) { + $after = @$deps[1][strtolower($tableName)]; } - if (($stmt === false) && $this->errorText === '') { - $this->throwError('Unable to prepare query', $this->lastQuery, ['param' => $this->lastParam]); + $before = @$deps[2][$tableName]; + if ($before === null) { + $before = @$deps[2][strtolower($tableName)]; } + if (is_array($after) && is_array($before)) { + foreach ($before as $key => $rows) { // $value is [relcol,table] + foreach ($rows as $value) { + $relation['' . self::$prefixBase . $value[1]] = [ + 'key' => 'ONETOMANY', + 'col' => $key, + 'reftable' => $value[1], + 'refcol' => $value[0] + ]; + } + } + } + // converts relations to ONETOONE + foreach ($relation as $k => $rel) { + if ($rel['key'] === 'ONETOMANY') { + $pkref = null; + $pkref = $this->service->getPK($rel['reftable'], $pkref); + if ('' . self::$prefixBase . $pkref[0] === $rel['refcol'] && count($pkref) === 1) { + $relation[$k]['key'] = 'ONETOONE'; + $relation[$k]['col'] = 'xxx3'; + $relation[$k]['refcol'] = ltrim($relation[$k]['refcol'], self::$prefixBase); + } + } + if ($rel['key'] === 'MANYTOONE') { + $pkref = null; + $pkref = $this->service->getPK($rel['reftable'], $pkref); - return $stmt; - } - - /** - * It returns true if the array is an associative array. False - * otherwise.
- * Example:
- * isAssoc(['a1'=>1,'a2'=>2]); // true
- * isAssoc(['a1','a2']); // false
- * isAssoc('aaa'); isAssoc(null); // false
- * - * @param mixed $array - * - * @return bool - */ - private function isAssoc($array) - { - if ($array === null) { - return false; + if ($pkref[0] === $rel['refcol'] && count($pkref) === 1 + && (strcasecmp($k, '' . self::$prefixBase . $pkFirst) === 0) + ) { + // if they are linked by the pks and the pks are only 1. + $relation[$k]['key'] = 'ONETOONE'; + $relation[$k]['col'] = 'xxx4'; + $relation[$k]['refcol'] = ltrim($relation[$k]['refcol'], self::$prefixBase); + } + } } - if (!is_array($array)) { - return false; + if ($customRelation) { + foreach ($relation as $k => $rel) { + if (isset($customRelation[$k])) { + // parent. + if ($customRelation[$k] === 'PARENT') { + $relation[$k]['key'] = 'PARENT'; + } elseif ($customRelation[$k] === 'MANYTOMANY') { + // the table must has 2 primary keys. + $pks = null; + $pks = $this->service->getPK($relation[$k]['reftable'], $pks); + /** @noinspection PhpParamsInspection */ + if ($pks !== false || count($pks) === 2) { + $relation[$k]['key'] = 'MANYTOMANY'; + $refcol2 = ('' . self::$prefixBase . $pks[0] === $relation[$k]['refcol']) ? $pks[1] + : $pks[0]; + + try { + $defsFK = $this->service->getDefTableFK($relation[$k]['reftable'], false); + } catch (Exception $e) { + return 'Error: Unable read table dependencies ' . $e->getMessage(); + } + try { + $keys2 = $this->service->getDefTableKeys($defsFK[$refcol2]['reftable'], true, + 'PRIMARY KEY'); + } catch (Exception $e) { + return 'Error: Unable read table dependencies' . $e->getMessage(); + } + $relation[$k]['refcol2'] = '' . self::$prefixBase . $refcol2; + if (is_array($keys2)) { + $keys2 = array_keys($keys2); + $relation[$k]['col2'] = $keys2[0]; + } else { + $relation[$k]['col2'] = null; + } + $relation[$k]['table2'] = $defsFK[$refcol2]['reftable']; + } + } + // manytomany + } + } } + //die(1); - return (array_values($array) !== $array); - } + $gdf = $this->getDefTable($tableName, $specialConversion); + $fields = []; + $fieldsb = []; + foreach ($gdf as $varn => $field) { + switch ($field['phptype']) { //binary, date, datetime, decimal,int, string,time, timestamp + case 'binary': + case 'date': + case 'datetime': + case 'decimal': + case 'float': + case 'int': + case 'string': + case 'time': + case 'timestamp': + $fields[] = "\t/** @var " . $field['phptype'] . " \$$varn */\n\tpublic \$$varn;"; + $fieldsb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? \$array['$varn'] : null;"; + break; + } + } + $fieldsArr = implode("\n", $fields); + $fieldsbArr = implode("\n", $fieldsb); + $field2s = []; + $field2sb = []; + foreach ($relation as $varn => $field) { + //$varnclean = ltrim($varn, PdoOne::$prefixBase); + switch ($field['key']) { + case 'FOREIGN KEY': + break; + case 'MANYTOONE': + $class = $classRelations[$field['reftable']]; + $field2s[] = "\t/** @var $class \$$varn manytoone */ + public \$$varn;"; + $field2sb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? + \$obj->$varn=$class::fromArray(\$array['$varn']) + : null; // manytoone"; + break; + case 'MANYTOMANY': + $class = $classRelations[$field['reftable']]; + $field2s[] = "\t/** @var {$class}[] \$$varn manytomany */ + public \$$varn;"; + $field2sb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? + \$obj->$varn=$class::fromArrayMultiple(\$array['$varn']) + : null; // manytomany"; + break; + case 'ONETOMANY': + $class = $classRelations[$field['reftable']]; + $field2s[] = "\t/** @var {$class}[] \$$varn onetomany */ + public \$$varn;"; + $field2sb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? + \$obj->$varn=$class::fromArrayMultiple(\$array['$varn']) + : null; // onetomany"; + break; + case 'ONETOONE': + $class = $classRelations[$field['reftable']]; + $field2s[] = "\t/** @var $class \$$varn onetoone */ + public \$$varn;"; + $field2sb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? + \$obj->$varn=$class::fromArray(\$array['$varn']) + : null; // onetoone"; + break; + } + } + $fields2Arr = implode("\n", $field2s); + $fields2Arrb = implode("\n", $field2sb); - // + $r = str_replace(['{fields}', '{fieldsrel}', '{fieldsfa}', '{fieldsrelfa}'], + [$fieldsArr, $fields2Arr, $fieldsbArr, $fields2Arrb], $r); + // return $r; + // die(1); - /** - * Convert date from unix timestamp -> ISO (database format). - *

Example: ::unixtime2Sql(1558656785); // returns 2019-05-24 00:13:05 - * - * @param integer $dateNum - * - * @return string - */ - public static function unixtime2Sql($dateNum) - { - // 31/01/2016 20:20:00 --> 2016-01-31 00:00 - if ($dateNum === null) { - return self::$dateEpoch; + if (@count($this->codeClassConversion) > 0) { + // we forced the conversion but only if it is not specified explicit + foreach ($gdf as $k => $colDef) { + $type = $colDef['type']; + if (isset($this->codeClassConversion[$type]) && $gdf[$k]['conversion'] === null) { + $gdf[$k]['conversion'] = $this->codeClassConversion[$type]; + } + } } - return date(self::$isoDateTimeMs, $dateNum); - } - - /** - * Convert date, from mysql date -> text (using a format pre-established) - * - * @param string $sqlField - * @param bool $hasTime if true then the date contains time. - * - * @return string Returns a text with the date formatted (human readable) - */ - public static function dateSql2Text($sqlField, $hasTime = false) - { - $tmpDate = self::dateTimeSql2PHP($sqlField, $hasTime); - if ($tmpDate === null) { - return null; + // discard columns + $identities = $this->getDefIdentities($tableName); + if ($defNoInsert !== null) { + $noInsert = array_merge($identities, $defNoInsert); + } else { + $noInsert = $identities; } - if ($hasTime) { - return $tmpDate->format((strpos($sqlField, '.') !== false) ? self::$dateTimeMicroHumanFormat - : self::$dateTimeHumanFormat); + if ($defNoInsert !== null) { + $noUpdate = array_merge($identities, $defNoUpdate); + } else { + $noUpdate = $identities; } - return $tmpDate->format(self::$dateHumanFormat); + try { + $r = str_replace(array( + '{pk}', + '{def}', + '{defname}', + '{defkey}', + '{defnoinsert}', + '{defnoupdate}', + '{deffk}', + '{deffktype}', + '{array}', + '{array_null}' + ), array( + self::varExport($pk), + //str_replace(["\n\t\t ", "\n\t\t ],"], ['', '],'], PdoOne::varExport($gdf, "\t\t")), // {def} + self::varExport($gdf, "\t\t"), + self::varExport(array_keys($gdf), "\t\t"), // {defname} + self::varExport($this->getDefTableKeys($tableName), "\t\t"), // {defkey} + self::varExport($noInsert, "\t\t"), // {defnoinsert} + self::varExport($noUpdate, "\t\t"), // {defnoupdate} + self::varExport($this->getDefTableFK($tableName), "\t\t\t"), //{deffk} + self::varExport($relation, "\t\t"), //{deffktype} + str_replace("\n", "\n\t\t", + rtrim($this->generateCodeArray($tableName, null, false, false, true, $classRelations, $relation), + "\n")), + str_replace("\n", "\n\t\t", + rtrim($this->generateCodeArray($tableName, null, true, false, true, $classRelations, $relation), + "\n")) + ), $r); + } catch (Exception $e) { + return "Unable read definition of tables " . $e->getMessage(); + } + + return $r; } - /** - * Convert date, from mysql -> php - * - * @param string $sqlField - * @param bool $hasTime - * - * @return bool|DateTime|null - */ - public static function dateTimeSql2PHP($sqlField, &$hasTime = false) + public function generateCodeClassRepo( + $tableClassName, + $namespace = '', + $classRelations = [], + $modelfullClass = '' + ) { - // 3 2016-01-31 00:00:00 -> 01/01/2016 00:00:00 - // mysql always returns the date/datetime/timestmamp in ansi format. - if ($sqlField === '' || $sqlField === null) { - if (self::$dateEpoch === null) { - return null; - } + $r = <<<'eot' + + * $code=$pdoOne->generateCodeClassRepo({args}); + * + */ +class {classname} extends Abstract{classname} +{ + const ME=__CLASS__; + {modelclass} + + +} +eot; - if (strpos($sqlField, '.')) { - // with date with time and microseconds - //2018-02-06 05:06:07.123 - // Y-m-d H:i:s.v - $hasTime = true; - //$x = DateTime::createFromFormat("Y-m-d H:i:s.u", "2018-02-06 05:06:07.1234"); - return DateTime::createFromFormat(self::$isoDateTimeMs, $sqlField); + $fa = func_get_args(); + foreach ($fa as $f => $k) { + if (is_array($k)) { + $fa[$f] = str_replace([' ', "\r\n", "\n"], ['', '', ''], var_export($k, true)); + } else { + $fa[$f] = "'$k'"; + } } - if (strpos($sqlField, ':')) { - // date with time - $hasTime = true; - return DateTime::createFromFormat(self::$isoDateTime, $sqlField); + if ($modelfullClass) { + $arr = explode('\\', $modelfullClass); + $modelClass = end($arr); + $modelUse = true; + } else { + $modelClass = false; + $modelUse = false; } - // only date - $hasTime = false; - return DateTime::createFromFormat(self::$isoDate, $sqlField); + $r = str_replace(array( + '{version}', + '{classname}', + '{exception}', + '{args}', + '{table}', + '{namespace}', + '{modelnamespace}', + '{modelclass}', + '{modeluse}' + ), array( + self::VERSION . ' Date generated ' . date('r'), // {version} + $classRelations[$tableClassName], // {class} + ($namespace) ? 'use Exception;' : '', + "'" . implode("','", $fa) . "'", // {args} + $tableClassName, //{table} + ($namespace) ? "namespace $namespace;" : '', // {namespace} + $modelfullClass ? "use $modelfullClass;" : '', // {modelnamespace} + $modelClass ? "const MODEL= $modelClass::class;" : '', // {modelclass} + $modelUse ? 'true' : 'false' // {modeluse} + ), $r); + return $r; } + - /** - * It converts a date (as string) into another format.
- * Example: - *

-     * $pdoOne->dateConvert('01/01/2019','human','sql'); // 2019-01-01
-     * 
- *
iso depends on the database. - * Example: Y-m-d H:i:s
- * human is based in d/m/Y H:i:s but it could be changed (self::dateHumanFormat)
- * sql depends on the database
- * class is a DateTime() object
- * - * @param string $sqlField The date to convert - * @param string $inputFormat =['iso','human','sql','class','timestamp'][$i] - * @param string $outputFormat =['iso','human','sql','class','timestamp'][$i] - * @param null|string $force =[null,'time','ms','none'][$i] It forces if the result gets time or - * microseconds
- * null = no force the result (it is calculated automatically)
- * time = returns with a precision of seconds
- * ms = returns with a precision of microseconds
- * none = it never returns any time
- * - * @return bool|DateTime - */ - public static function dateConvert($sqlField, $inputFormat, $outputFormat, $force = null) + public static function varExport($input, $indent = "\t") { - /** @var boolean $ms if true then the value has microseconds */ - $ms = false; - /** @var boolean $time if true then the value has time */ - $time = false; - $tmpDate=self::dateConvertInput($sqlField,$inputFormat,$ms,$time); - if (!$tmpDate) { - return false; - } - if ($force !== null) { - if ($force === 'ms') { - $ms = true; - } elseif ($force === 'time') { - $time = true; - $ms = false; - } elseif ($force === 'none') { - $time = false; - $ms = false; - } - } - switch ($outputFormat) { - case 'iso': - if ($ms) { - return $tmpDate->format(self::$dateTimeMicroFormat); - } - if ($time) { - return $tmpDate->format(self::$dateTimeFormat); - } - return $tmpDate->format(self::$dateFormat); - case 'human': - if ($ms) { - return $tmpDate->format(self::$dateTimeMicroHumanFormat); - } - if ($time) { - return $tmpDate->format(self::$dateTimeHumanFormat); - } - - return $tmpDate->format(self::$dateHumanFormat); - case 'sql': - if ($ms) { - return $tmpDate->format(self::$isoDateInputTimeMs); - } - if ($time) { - return $tmpDate->format(self::$isoDateInputTime); + switch (gettype($input)) { + case 'string': + $r = "'" . addcslashes($input, "\\\$\'\r\n\t\v\f") . "'"; + break; + case 'array': + $indexed = array_keys($input) === range(0, count($input) - 1); + $r = []; + foreach ($input as $key => $value) { + $r[] = "$indent " . ($indexed ? '' : self::varExport($key) . ' => ') . self::varExport($value, + "$indent "); } - return $tmpDate->format(self::$isoDateInput); - case 'class': - return $tmpDate; - case 'timestamp': - return $tmpDate->getTimestamp(); + $r = "[\n" . implode(",\n", $r) . "\n" . $indent . ']'; + break; + case 'boolean': + $r = $input ? 'TRUE' : 'FALSE'; + break; + default: + $r = var_export($input, true); + break; } - return false; + return $r; } /** - * It converts a date and time value (expressed in different means) into a DateTime object or false if the operation - * fails.
+ * It generates a class
* Example:
*
-     * $r=PdoOne::dateConvertInput('01/12/2020','human',$ms,$time); // it depends on the fields self::$date*HumanFormat
-     * $r=PdoOne::dateConvertInput('2020-12-01','iso',$ms,$time); // it depends on the fields self::$date*Format
-     * $r=PdoOne::dateConvertInput('2020-12-01','sql',$ms,$time); // it depends on the database
-     * $r=PdoOne::dateConvertInput(50000,'timestamp',$ms,$time); // a timestamp
-     * $r=PdoOne::dateConvertInput(new DateTime(),'class',$ms,$time); // a DateTime object (it keeps the same one)
+     * $class = $this->generateCodeClass('tablename', 'namespace\namespace2'
+     *          ,['_idchild2FK'=>'PARENT' // relation
+     *          ,'_tablaparentxcategory'=>'MANYTOMANY' // relation
+     *          ,'col'=>'datetime3' // conversion
+     *          ,'col2'=>'conversion(%s)' // custom conversion (identified by %s)
+     *          ,'col3'=>] // custom conversion (identified by %s)
+     *          ,'Repo');
+     * $class = $this->generateCodeClass(['ClassName'=>'tablename'], 'namespace\namespace2'
+     *          ,['/idchild2FK'=>'PARENT','/tablaparentxcategory'=>'MANYTOMANY']
+     *          ,'Repo');
      * 
* - * @param mixed $inputValue the input value. - * @param string $inputFormat=['iso','human','sql','class','timestamp'][$i] The input format - * @param boolean $ms [ref] It returns if it includes microseconds - * @param boolean $time [ref] It returns if it includes time - * @return DateTime|false false if the operation fails + * @param string|array $tableName The name of the table and the class. + * If the value is an array, then the key is the name of the table and + * the value is the name of the class + * @param string $namespace The Namespace of the generated class + * @param array|null $columnRelations An associative array to specific custom relations, such as PARENT
+ * The key is the name of the columns and the value is the type of + * relation
+ * @param null|string[] $classRelations The postfix of the class. Usually it is Repo or Dao. + * + * @param array $specialConversion An associative array to specify a custom conversion
+ * The key is the name of the columns and the value is the type of + * relation
+ * @param string[]|null $defNoInsert An array with the name of the columns to not to insert. The identity + * is added automatically to this list + * @param string[]|null $defNoUpdate An array with the name of the columns to not to update. The identity + * is added automatically to this list + * @param string|null $baseClass The name of the base class. If no name then it uses the last namespace + * @param string $modelfullClass (default:'') The full class of the model (with the namespace). If + * empty, then it doesn't use a model + * @param array $extraCols An associative array with extra columns where they key is the name of + * the column and the value is the value to return (it is evaluated in + * the query). It is used by toList() and first(), it's also added to + * the model. + * + * @param array $columnRemove + * + * @return string|string[] + * @throws Exception */ - public static function dateConvertInput($inputValue, $inputFormat, &$ms, &$time) { - switch ($inputFormat) { - case 'iso': - if (strpos($inputValue, '.') !== false) { - $ms = true; - $tmpDate = DateTime::createFromFormat(self::$dateTimeMicroFormat, $inputValue); - } elseif (strpos($inputValue, ':') !== false) { - $time = true; - $tmpDate = DateTime::createFromFormat(self::$dateTimeFormat, $inputValue); - } else { - $tmpDate = DateTime::createFromFormat(self::$dateFormat, $inputValue); - if ($tmpDate === false) { - return false; - } - $tmpDate->setTime(0, 0); - } - break; - case 'human': - if (strpos($inputValue, '.') !== false) { - $ms = true; - $tmpDate = DateTime::createFromFormat(self::$dateTimeMicroHumanFormat, $inputValue); - } elseif (strpos($inputValue, ':') !== false) { - $time = true; - $tmpDate = DateTime::createFromFormat(self::$dateTimeHumanFormat, $inputValue); - } else { - $tmpDate = DateTime::createFromFormat(self::$dateHumanFormat, $inputValue); + public function generateCodeClass( + $tableName, + $namespace = '', + $columnRelations = null, + $classRelations = null, + $specialConversion = [], + $defNoInsert = null, + $defNoUpdate = null, + $baseClass = null, + $modelfullClass = '', + $extraCols = [], + $columnRemove = [] + ) + { + $r = <<<'eot' +setTime(0, 0); - } - break; - case 'sql': - if (strpos($inputValue, '.') !== false) { - $ms = true; - $tmpDate = DateTime::createFromFormat(self::$isoDateTimeMs, $inputValue); - } elseif (strpos($inputValue, ':') !== false) { - $time = true; - $tmpDate = DateTime::createFromFormat(self::$isoDateTime, $inputValue); - } else { - $tmpDate = DateTime::createFromFormat(self::$isoDate, $inputValue); - $tmpDate->setTime(0, 0); - } - break; - case 'class': - /** @var DateTime $tmpDate */ - $tmpDate = $inputValue; - $time = $tmpDate->format('Gis') !== '000000'; - break; - case 'timestamp': - $tmpDate = new DateTime(); - $tmpDate->setTimestamp($inputValue); - $time = $tmpDate->format('Gis') !== '000000'; - $ms = fmod($inputValue, 1) !== 0.0; - break; - default: - $tmpDate = false; - trigger_error('PdoOne: dateConvert type not defined'); - } - return $tmpDate; - } +/** + * Generated by PdoOne Version {version}. + * DO NOT EDIT THIS CODE. Use instead the Repo Class. + * @copyright (c) Jorge Castro C. MIT License https://github.com/EFTEC/PdoOne + * Class Abstract{classname} + *
+ * $code=$pdoOne->generateCodeClass({args});
+ * 
+ */ +abstract class Abstract{classname} extends {baseclass} +{ + const TABLE = '{table}'; + const IDENTITY = {identity}; + const PK = {pk}; + const ME=__CLASS__; + const EXTRACOLS='{extracol}'; /** - * Convert date, from text -> mysql (using a format pre-established) + * It returns the definitions of the columns
+ * Example:
+ *
+     * self::getDef(); // ['colName'=>[php type,php conversion type,type,size,nullable,extra,sql],'colName2'=>..]
+     * self::getDef('sql'); // ['colName'=>'sql','colname2'=>'sql2']
+     * self::getDef('identity',true); // it returns the columns that are identities ['col1','col2']
+     * 
+ * PHP Types: binary, date, datetime, decimal/float,int, string,time, timestamp
+ * PHP Conversions: datetime (datetime class), datetime2 (iso),datetime3 (human string) + * , datetime4 (sql no conversion!), timestamp (int), bool, int, float
+ * Param Types: PDO::PARAM_LOB, PDO::PARAM_STR, PDO::PARAM_INT
* - * @param string $textDate Input date - * @param bool $hasTime If true then it works with date and time - * (instead of date) + * @param string|null $column =['phptype','conversion','type','size','null','identity','sql'][$i] + * if not null then it only returns the column specified. + * @param string|null $filter If filter is not null, then it uses the column to filter the result. * - * @return string + * @return array|array[] */ - public static function dateText2Sql($textDate, $hasTime = true) - { - if (($hasTime)) { - $tmpFormat = strpos($textDate, '.') === false ? self::$dateTimeFormat : self::$dateTimeMicroFormat; - } else { - $tmpFormat = self::$dateFormat; + public static function getDef($column=null,$filter=null) { + $r = {def}; + if($column!==null) { + if($filter===null) { + foreach($r as $k=>$v) { + $r[$k]=$v[$column]; + } + } else { + $new=[]; + foreach($r as $k=>$v) { + if($v[$column]===$filter) { + $new[]=$k; + } + } + return $new; + } } - - $tmpDate = DateTime::createFromFormat($tmpFormat, $textDate); - if (!$hasTime && $tmpDate) { - $tmpDate->setTime(0, 0); + return $r; + } + + /** + * It converts a row returned from the database.
+ * If the column is missing then it sets the field as null. + * + * @param array $row [ref] + */ + public static function convertOutputVal(&$row) { + if($row===false || $row===null) { + return; } +{convertoutput} +{linked} + } - return self::dateTimePHP2Sql($tmpDate); // it always returns a date with time. Mysql Ignores it. + /** + * It converts a row to be inserted or updated into the database.
+ * If the column is missing then it is ignored and not converted. + * + * @param array $row [ref] + */ + public static function convertInputVal(&$row) { +{convertinput} } + /** - * Conver date from php -> mysql - * It always returns a time (00:00:00 if time is empty). it could returns - * microseconds 2010-01-01 00:00:00.00000 - * - * @param DateTime $date + * It gets all the name of the columns. * - * @return string + * @return string[] */ - public static function dateTimePHP2Sql($date) - { - // 31/01/2016 20:20:00 --> 2016-01-31 00:00 - if ($date == null) { - return self::$dateEpoch; - } - if ($date->format('u') !== '000000') { - return $date->format(self::$isoDateTimeMs); - } - - return $date->format(self::$isoDateTime); + public static function getDefName() { + return {defname}; } /** - * Returns the current date(and time) in Text (human) format. Usually, it is d/m/Y H:i:s - * - * @param bool $hasTime - * @param bool $hasMicroseconds + * It returns an associative array (colname=>key type) with all the keys/indexes (if any) * - * @return string - * @throws Exception - * @see PdoOne::$dateTimeFormat + * @return string[] */ - public static function dateTextNow( - $hasTime = true, - $hasMicroseconds = false - ) - { - $tmpDate = new DateTime(); - if ($hasTime) { - return $tmpDate->format(($hasMicroseconds !== false) ? self::$dateTimeMicroHumanFormat - : self::$dateTimeHumanFormat); - } - return $tmpDate->format(self::$dateHumanFormat); + public static function getDefKey() { + return {defkey}; } /** - * Returns the current (PHP server) date and time in the regular format. (Y-m-d\TH:i:s\Z in long format) - * - * @param bool $hasTime - * @param bool $hasMicroseconds + * It returns a string array with the name of the columns that are skipped when insert + * @return string[] + */ + public static function getDefNoInsert() { + return {defnoinsert}; + } + + /** + * It returns a string array with the name of the columns that are skipped when update + * @return string[] + */ + public static function getDefNoUpdate() { + return {defnoupdate}; + } + + /** + * It adds a where to the query pipeline. It could be stacked with many where() + * Example:
+ *
+     * self::where(['col'=>'value'])::toList();
+     * self::where(['col']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
+     * self::where(['col=?']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
+     * 
+ * + * @param array|string $sql =self::factory() + * @param null|array|int $param * - * @return string - * @throws Exception - * @see PdoOne::$dateTimeFormat + * @return {classname} */ - public static function dateNow( - $hasTime = true, - $hasMicroseconds = false - ) + public static function where($sql, $param = PdoOne::NULL) { - $tmpDate = new DateTime(); - if ($hasTime) { - return $tmpDate->format(($hasMicroseconds !== false) ? self::$dateTimeMicroFormat : self::$dateTimeFormat); + self::getPdoOne()->where($sql, $param,false,{classname}::TABLE); + return {classname}::class; + } + + public static function getDefFK($structure=false) { + if ($structure) { + return {deffk}; } - return $tmpDate->format(self::$dateFormat); + /* key,refcol,reftable,extra */ + return {deffktype}; } /** - * Returns the current date(and time) in SQL/ISO format. It depends on the type of database. + * It returns all the relational fields by type. '*' returns all types.
+ * It doesn't return normal columns. + * + * @param string $type=['*','MANYTOONE','ONETOMANY','ONETOONE','MANYTOMANY'][$i] * - * @param bool $hasTime - * @param bool $hasMicroseconds + * @return string[] + * @noinspection SlowArrayOperationsInLoopInspection + */ + public static function getRelations($type='all') { + $r= {deffktype2}; + if($type==='*') { + $result=[]; + foreach($r as $arr) { + $result = array_merge($result,$arr); + } + return $result; + } + return isset($r[$type]) ? $r[$type] : []; + + } + + /** + * @param array|int $filter (optional) if we want to filter the results. + * @param array|null $filterValue (optional) the values of the filter + * @return array|bool|null + * @throws Exception + */ + public static function toList($filter=PdoOne::NULL,$filterValue=null) { + if(self::$useModel) { + return {classmodellist} + } + return self::_toList($filter, $filterValue); + } + + /** + * It sets the recursivity. By default, if we query or modify a value, it operates with the fields of the entity. + * With recursivity, we could use the recursivity of the fields, for example, loading a MANYTOONE relation
+ * Example:
+ *
+     * self::setRecursive([]); // (default) no use recursivity.
+     * self::setRecursive('*'); // recursive every MANYTOONE,ONETOONE,MANYTOONE and ONETOONE relations (first level) 
+     * self::setRecursive('MANYTOONE'); // recursive all relations of the type MANYTOONE (first level)
+     * self::setRecursive(['_relation1','_relation2']); // recursive only the relations of the first level 
+     * self::setRecursive(['_relation1','_relation1/_subrelation1']); // recursive the relations (first and second level)
+     * 
+ * If array then it uses the values to set the recursivity.
+ * If string then the values allowed are '*', 'MANYTOONE','ONETOMANY','MANYTOMANY','ONETOONE' (first level only)
* - * @return string + * @param string|array $recursive=self::factory(); + * + * @return {classname} */ - public static function dateSqlNow($hasTime = true, $hasMicroseconds = false) + public static function setRecursive($recursive=[]) { - $tmpDate = new DateTime(); - if ($hasTime) { - var_dump(($hasMicroseconds !== false) ? self::$isoDateTimeMs : self::$isoDateTime); - return $tmpDate->format(($hasMicroseconds !== false) ? self::$isoDateTimeMs : self::$isoDateTime); + if(is_string($recursive)) { + $recursive={classname}::getRelations($recursive); } - - return $tmpDate->format(self::$isoDate); + return parent::_setRecursive($recursive); } - public static function isCli() + public static function limit($sql) { - return !http_response_code(); + self::getPdoOne()->limit($sql); + return {classname}::class; } - //
- /** - * @param mixed $v Variable + * It returns the first row of a query. + * @param array|mixed|null $pk [optional] Specify the value of the primary key. * - * @return int=[PDO::PARAM_STR,PDO::PARAM_INT,PDO::PARAM_BOOL][$i] - * @test equals PDO::PARAM_STR,(20.3) - * @test equals PDO::PARAM_STR,('hello') + * @return array|bool It returns false if not file is found. + * @throws Exception */ - private function getType(&$v) - { - switch (1) { - case (is_float($v)): - case ($v === null): - $vt = PDO::PARAM_STR; - break; - case (is_numeric($v)): - $vt = PDO::PARAM_INT; - break; - case (is_bool($v)): - - $vt = PDO::PARAM_INT; - $v = ($v) ? 1 : 0; - break; - case (is_object($v) && $v instanceof DateTime): - $vt = PDO::PARAM_STR; - $v = self::dateTimePHP2Sql($v); - break; - default: - $vt = PDO::PARAM_STR; - } - - return $vt; + public static function first($pk = PdoOne::NULL) { + if(self::$useModel) { + return {classmodelfirst} + } + return self::_first($pk); } /** - * Run a prepared statement. - *
Example:
- * $con->runQuery($con->prepare('select * from table')); - * - * @param PDOStatement $stmt PDOStatement - * @param array|null $namedArgument (optional) + * It returns true if the entity exists, otherwise false.
+ * Example:
+ *
+     *  $this->exist(['id'=>'a1','name'=>'name']); // using an array
+     *  $this->exist('a1'); // using the primary key. The table needs a pks and it only works with the first pk.
+     *  
* - * @param bool $throwError (default true) if false, then it won't throw an error but it will store the error + * @param array|mixed $entity =self::factory() * - * @return bool returns true if the operation is correct, otherwise false + * @return bool true if the pks exists * @throws Exception - * @test equals true,$this->pdoOne->runQuery($this->pdoOne->prepare('select - * 1 from dual')) - * @test equals - * [1=>1],$this->pdoOne->select('1')->from('dual')->first(),'it - * must runs' */ - public function runQuery($stmt, $namedArgument = null, $throwError = true) - { - if (!$this->isOpen) { - $this->throwError("It's not connected to the database", '', $throwError); - return null; - } - try { - //$namedArgument = ($namedArgument === null) - // ? array_merge($this->setParamAssoc,$this->whereParamAssoc,$this->havingParamAssoc) : $namedArgument; - $r = $stmt->execute($namedArgument); - } catch (Exception $ex) { - $this->throwError($this->databaseType . ':Failed to run query', $this->lastQuery, - ['param' => $this->lastParam, 'error_last' => json_encode(error_get_last())], $throwError, $ex); - return false; - } - if ($r === false) { - $this->throwError('Exception query ', $this->lastQuery, ['param' => $this->lastParam], $throwError); - return false; - } - - return true; + public static function exist($entity) { + return self::_exist($entity); } - protected static function fixCsv($value) - { - if (is_numeric($value)) { - return $value; - } - $value = str_replace('"', '""', $value); - - return '"' . $value . '"'; + /** + * It inserts a new entity(row) into the database
+ * @param array|object $entity =self::factory() + * @param bool $transactional If true (default) then the operation is transactional + * + * @return array|false=self::factory() + * @throws Exception + */ + public static function insert(&$entity,$transactional=true) { + return self::_insert($entity,$transactional); + } + + /** + * It merge a new entity(row) into the database. If the entity exists then it is updated, otherwise the entity is + * inserted
+ * @param array|object $entity =self::factory() + * @param bool $transactional If true (default) then the operation is transactional + * + * @return array|false=self::factory() + * @throws Exception + */ + public static function merge(&$entity,$transactional=true) { + return self::_merge($entity,$transactional); } /** - * @param string $query + * @param array|object $entity =self::factory() + * @param bool $transactional If true (default) then the operation is transactional * - * @return string + * @return array|false=self::factory() * @throws Exception */ - public function generateCodeSelect($query) - { - $q = self::splitQuery($query); - $code = '/** @var array $result=array(' . $this->generateCodeArray($query, $query) . ') */' . "\n"; - - $code .= '$result=$pdo' . "\n"; - foreach ($q as $k => $v) { - if ($v !== null) { - $k2 = str_replace(' by', '', $k); // order by -> order - foreach ($v as $vitem) { - $code .= "\t->{$k2}(\"{$vitem}\")\n"; - } - } - } - $code .= "\t->toList();\n"; - - return $code; - } - - protected static function splitQuery($query) - { - $result = []; - $parts = [ - 'select', - 'from', - 'inner join', - 'inner join', - 'inner join', - 'inner join', - 'inner join', - 'inner join', - 'left join', - 'left join', - 'left join', - 'left join', - 'left join', - 'left join', - 'right join', - 'right join', - 'right join', - 'right join', - 'right join', - 'right join', - 'where', - 'group by', - 'having', - 'order by', - 'limit', - '*END*', - ]; - $partsRealIndex = [ - 'select', - 'from', - 'innerjoin', - 'innerjoin', - 'innerjoin', - 'innerjoin', - 'innerjoin', - 'innerjoin', - 'left', - 'left', - 'left', - 'left', - 'left', - 'left', - 'right', - 'right', - 'right', - 'right', - 'right', - 'right', - 'where', - 'group', - 'having', - 'order', - 'limit', - '*END*', - ]; - $query = str_replace(array("\r\n", "\n", "\t", ' ', ' '), ' ', - $query); // remove 3 or 2 space and put instead 1 space - $query = ' ' . trim($query, " \t\n\r\0\x0B;") . '*END*'; // we also trim the last ; (if any) - $pfin = 0; - foreach ($parts as $kp => $part) { - $ri = $partsRealIndex[$kp]; - if ($part !== '*END*') { - //$result[$ri] = null; - $pini = stripos($query, $part, $pfin); - if ($pini !== false) { - $pini += strlen($part); - $found = false; - $cp = count($parts); - for ($i = $kp + 1; $i < $cp; $i++) { - $pfin = stripos($query, $parts[$i], $pini); - if ($pfin !== false) { - $found = $pfin; - break; - } - } - if ($found !== false) { - $pfin = $found; - if (!isset($result[$ri])) { - $result[$ri] = []; - } - $result[$ri][] = trim(substr($query, $pini, $pfin - $pini)); - } - } - } - } - - return $result; - } + public static function update($entity,$transactional=true) { + return self::_update($entity,$transactional); + } /** - * @param string $table - * @param null|string $sql - * @param bool $defaultNull - * @param bool $inline - * @param bool $recursive - * @param null|array $classRelations [optional] The relation table=>classname - * @param array $relation [optional] An optional custom relation of columns + * It deletes an entity by the primary key * - * @return string + * @param array|object $entity =self::factory() + * @param bool $transactional If true (default) then the operation is transactional + * + * @return mixed * @throws Exception */ - public function generateCodeArray( - $table, - $sql = null, - $defaultNull = false, - $inline = true, - $recursive = false, - $classRelations = null, - $relation = [] - ) - { - if ($sql === null) { - $sql = 'select * from ' . $this->addDelimiter($table); - } - $r = $this->toMeta($sql); - - $ln = ($inline) ? '' : "\n"; - if ($recursive) { - /** @noinspection PhpUnusedLocalVariableInspection */ - list($tables, $after, $before) = $this->tableDependency(true); - } else { - $tables = null; - $after = null; - $before = null; - } - $result = '[' . $ln; - $used = []; - $norepeat = []; - foreach ($r as $row) { - $name = $row['name']; - if (!in_array($name, $used, true)) { - if ($defaultNull) { - $default = 'null'; - } else { - $default = $this->typeDict($row); - } - $result .= "'" . $name . "'=>" . $default . ',' . $ln; - if ($recursive) { - if (isset($before[$table][$name])) { - foreach ($before[$table][$name] as $k => $v3) { - if ($v3[1] - && $v3[0][0] !== self::$prefixBase - ) { // before is defined as [colremote,tableremote] - $colName = self::$prefixBase . $v3[1]; - if (!$defaultNull) { - $default = '(in_array($recursivePrefix.\'' . $colName . '\',$recursive,true)) - ? [] - : null'; - } else { - $default = 'null'; - } - if (!in_array($colName, $norepeat)) { - if (isset($relation[$colName])) { - $key = $relation[$colName]['key']; - - if ($key === 'PARENT') { - $default = 'null'; - } - if ($key === 'ONETOONE' && !$defaultNull) { - if ($classRelations === null - || !isset($classRelations[$relation[$colName]['reftable']]) - ) { - $className = self::camelize($relation[$colName]['reftable']) . 'Repo'; - } else { - $className = $relation[$colName]['reftable']; - } - $default = '(in_array($recursivePrefix.\'' . $colName . '\',$recursive,true)) - ? ' . $className . '::factory(null,$recursivePrefix.\'' . $colName . '\') - : null'; - } - $result .= "'" . $colName . "'=>" . $default . ', /* ' . $key . '! */' . $ln; - $norepeat[] = $colName; - } else { - $result .= "'" . $colName . "'=>" . $default . ', /* onetomany */' . $ln; - $norepeat[] = $colName; - } - } - } - } - } - if (@$after[$table][$name]) { - if (!$defaultNull) { - if ($classRelations === null || !isset($classRelations[$after[$table][$name]])) { - $className = self::camelize($after[$table][$name]) . 'Repo'; - } else { - $className = $classRelations[$after[$table][$name]]; - } - $default = '(in_array($recursivePrefix.\'' . self::$prefixBase . $name . '\',$recursive,true)) - ? ' . $className . '::factory(null,$recursivePrefix.\'' . self::$prefixBase . $name . '\') - : null'; - } - if (!in_array($name, $norepeat)) { - $namep = self::$prefixBase . $name; - if (isset($relation[$namep])) { - /*array(5) { - ["key"]=> - string(11) "FOREIGN KEY" - ["refcol"]=> - string(14) "idtablachildPK" - ["reftable"]=> - string(10) "TableChild" - ["extra"]=> - string(0) "" - ["name"]=> - string(26) "FK_TableParent_TableChild1" - }*/ - $key = $relation[$namep]['key']; - if ($key !== 'PARENT') { - // $default = 'null'; - $result .= "'" . $namep . "'=>" . $default . ', /* ' . $key . '!! */' . $ln; - $norepeat[] = $name; - } - } else { - $result .= "'" . $namep . "'=>" . $default . ', /* manytoone */' . $ln; - $norepeat[] = $name; - } - } - } - } - } - - $used[] = $name; - } - $result .= ']' . $ln; - $result = str_replace(",$ln]", "$ln]", $result); - return $result; + public static function delete($entity,$transactional=true) { + return self::_delete($entity,$transactional); } /** - * It returns an array with the metadata of each columns (i.e. name, type, - * size, etc.) or false if error. - * - * @param null|string $sql If null then it uses the generation of query - * (if any).
if string then get the - * statement of the query + * It deletes an entity by the primary key. * - * @param array $args + * @param array $pk =self::factory() + * @param bool $transactional If true (default) then the operation is transactional * - * @return array|bool + * @return mixed * @throws Exception */ - public function toMeta($sql = null, $args = []) - { - $uid = false; - if ($sql === null) { - $this->beginTry(); - /** @var PDOStatement $stmt */ - $stmt = $this->runGen(false, PDO::FETCH_ASSOC, 'tometa', $this->genError); - if ($this->endtry() === false) { - return false; - } + public static function deleteById($pk,$transactional=true) { + return self::_deleteById($pk,$transactional); + } + + /** + * Returns an array with the default values (0 for numbers, empty for string, and array|null if recursive) + * + * @param array|null $values =self::factory() + * @param string $recursivePrefix It is the prefix of the recursivity. + * + * @return array + */ + public static function factory($values = null, $recursivePrefix = '') { + $recursive=static::getRecursive(); + static::setRecursive(); // reset the recursivity. + $row= {array}; +{linked} + if ($values !== null) { + $row = array_merge($row, $values); + } + return $row; + } + + /** + * It returns an empty array with null values and no recursivity. + * @param array|null $values=self::factoryNull() + * + * @return array + */ + public static function factoryNull($values=null) { + $row= {array_null}; + if ($values !== null) { + $row = array_merge($row, $values); + } + return $row; + } + +} +eot; + $lastns = explode('\\', $namespace); + + if ($modelfullClass) { + $arr = explode('\\', $modelfullClass); + $modelClass = end($arr); + $modelUse = true; } else { - if ($this->useInternalCache) { - $uid = hash($this->encryption->hashType, 'meta:' . $sql . serialize($args)); - if (isset($this->internalCache[$uid])) { - // we have an internal cache, so we will return it. - $this->internalCacheCounter++; - return $this->internalCache[$uid]; - } - } - /** @var PDOStatement $stmt */ - $stmt = $this->runRawQuery($sql, $args, false); + $modelClass = false; + $modelUse = false; } - if ($stmt === null || $stmt instanceof PDOStatement === false) { - $stmt = null; - return false; - } - $numCol = $stmt->columnCount(); - $rows = []; - for ($i = 0; $i < $numCol; $i++) { - $rows[] = $stmt->getColumnMeta($i); + $baseClass = ($baseClass === null) ? end($lastns) : $baseClass; + + $fa = func_get_args(); + foreach ($fa as $f => $k) { + if (is_array($k)) { + $fa[$f] = str_replace([' ', "\r\n", "\n"], ['', '', ''], var_export($k, true)); + } else { + $fa[$f] = "'$k'"; + } } - $stmt = null; - if ($uid !== false) { - $this->internalCache[$uid] = $rows; + if ($classRelations === null || !isset($classRelations[$tableName])) { + $className = self::camelize($tableName); + } else { + $className = $classRelations[$tableName]; } - return $rows; - } - /** - * Begin a try block. It marks the erroText as empty and it store the value of genError - */ - private function beginTry() - { - $this->errorText = ''; - $this->isThrow = $this->genError; // this value is deleted when it trigger an error - $this->throwOnErrorB = $this->throwOnError; - $this->throwOnError = false; - } + $extraColArray = ''; + foreach ($extraCols as $k => $v) { + $extraColArray .= $v . ' as ' . $this->addQuote($k) . ','; + } + $extraColArray = rtrim($extraColArray, ','); - /** - * Run builder query and returns a PDOStatement. - * - * @param bool $returnArray true=return an array. False returns a - * PDOStatement - * @param int $extraMode PDO::FETCH_ASSOC,PDO::FETCH_BOTH,PDO::FETCH_NUM,etc. - * By default it returns - * $extraMode=PDO::FETCH_ASSOC - * - * @param string $extraIdCache [optional] if 'rungen' then cache is - * stored. If false the cache could be - * stored - * - * @param bool $throwError - * - * @return bool|PDOStatement|array - * @throws Exception - */ - public function runGen( - $returnArray = true, - $extraMode = PDO::FETCH_ASSOC, - $extraIdCache = 'rungen', - $throwError = true - ) - { - $this->errorText = ''; - $allparam = ''; - $uid = false; - $sql = $this->sqlGen(); - $isSelect = self::queryCommand($sql, true) === 'dql'; + $r = str_replace(array( + '{version}', + '{classname}', + '{exception}', + '{baseclass}', + '{args}', + '{table}', + '{namespace}', + '{modelnamespace}', + '{classmodellist}', + '{classmodelfirst}', + '{extracol}' + ), array( + self::VERSION . ' Date generated ' . date('r'), //{version} + $className, // {classname} + ($namespace) ? 'use Exception;' : '', + $baseClass, // {baseclass} + implode(",", $fa), + $tableName, // {table} + ($namespace) ? "namespace $namespace;" : '', //{namespace} + $modelUse ? "use $modelfullClass;" : '', // {modelnamespace} + $modelUse ? "$modelClass::fromArrayMultiple( self::_toList(\$filter, \$filterValue));" + : 'false; // no model set', // {classmodellist} + $modelUse ? "$modelClass::fromArray(self::_first(\$pk));" : 'false; // no model set' // {classmodelfirst} + , + $extraColArray // {extracol} + ), $r); + $pk = '??'; + $pk = $this->service->getPK($tableName, $pk); + $pkFirst = (is_array($pk) && count($pk) > 0) ? $pk[0] : null; try { - $allparam = array_merge($this->setParamAssoc, $this->whereParamAssoc, $this->havingParamAssoc); - - if ($isSelect && $this->useInternalCache && $returnArray) { - $uid = hash($this->encryption->hashType, $sql . $extraMode . serialize($allparam)); - if (isset($this->internalCache[$uid])) { - // we have an internal cache, so we will return it. - $this->internalCacheCounter++; - $this->builderReset(); - return $this->internalCache[$uid]; - } - } + $relation = $this->getDefTableFK($tableName, false, true); + } catch (Exception $e) { + return 'Error: Unable read fk of table ' . $e->getMessage(); + } - /** @var PDOStatement $stmt */ - $stmt = $this->prepare($sql); + // many to many + /*foreach ($relation as $rel) { + $tableMxM = $rel['reftable']; + $tableFK = $this->getDefTableFK($tableMxM, false, true); + } + */ + try { + $deps = $this->tableDependency(true); } catch (Exception $e) { - $this->throwError('Error in prepare runGen', $extraIdCache, ['values' => $allparam], $throwError, $e); - $this->builderReset(); - return false; + return 'Error: Unable read table dependencies ' . $e->getMessage(); + } // ["city"]=> {["city_id"]=> "address"} + $after = @$deps[1][$tableName]; + if ($after === null) { + $after = @$deps[1][strtolower($tableName)]; } - if ($stmt === null || $stmt === false) { - $this->builderReset(); - return false; + $before = @$deps[2][$tableName]; + if ($before === null) { + $before = @$deps[2][strtolower($tableName)]; } - $reval = true; - if ($allparam) { - try { - foreach ($allparam as $k => $v) { - $reval = $reval && $stmt->bindParam($v[0], $allparam[$k][1], $v[2]); + if (is_array($after) && is_array($before)) { + foreach ($before as $key => $rows) { // $value is [relcol,table] + foreach ($rows as $value) { + $relation[self::$prefixBase . $value[1]] = [ + 'key' => 'ONETOMANY', + 'col' => $key, + 'reftable' => $value[1], + 'refcol' => $value[0] //, ltrim( $value[0],self::$prefixBase) + ]; } - } catch(Exception $ex) { - if(is_object($allparam[$k][1])) { - $this->throwError("Error in bind. Parameter error." - , "Parameter {$v[0]} ($k) is an object of the class ".get_class($allparam[$k][1]) - , ['values' => $allparam], $throwError); - $this->builderReset(); - return false; + } + } + // converts relations to ONETOONE + foreach ($relation as $k => $rel) { + if ($rel['key'] === 'ONETOMANY') { + $pkref = null; + $pkref = $this->service->getPK($rel['reftable'], $pkref); + if (self::$prefixBase . $pkref[0] === $rel['refcol'] && count($pkref) === 1) { + $relation[$k]['key'] = 'ONETOONE'; + $relation[$k]['refcol'] = ltrim($relation[$k]['refcol'], self::$prefixBase); } - $this->throwError("Error in bind. Parameter error.", "Parameter {$v[0]} ($k)" - , ['values' => $allparam], $throwError); - $this->builderReset(); - return false; } - if (!$reval) { - $this->throwError('Error in bind', $extraIdCache, ['values' => $allparam], $throwError); - $this->builderReset(); - return false; + if ($rel['key'] === 'MANYTOONE') { + $pkref = null; + $pkref = $this->service->getPK($rel['reftable'], $pkref); + if ($pkref[0] === $rel['refcol'] && count($pkref) === 1 + && (strcasecmp($k, self::$prefixBase . $pkFirst) === 0) + ) { + // if they are linked by the pks and the pks are only 1. + $relation[$k]['key'] = 'ONETOONE'; + $relation[$k]['col'] = $pkFirst; + $relation[$k]['refcol'] = ltrim($relation[$k]['refcol'], self::$prefixBase); + } } } - $useCache = $this->useCache; // because builderReset cleans this value - if ($useCache !== false && $returnArray) { - $this->uid - = hash($this->encryption->hashType, - $this->lastQuery . $extraMode . serialize($allparam) . $extraIdCache); - $result = $this->cacheService->getCache($this->uid, $this->cacheFamily); - if ($result !== false) { - // it's found in the cache. - $this->builderReset(); - if ($uid !== false) { - $this->internalCache[$uid] = $result; + if ($columnRelations) { + foreach ($relation as $k => $rel) { + if (isset($columnRelations[$k])) { + // parent. + if ($columnRelations[$k] === 'PARENT') { + $relation[$k]['key'] = 'PARENT'; + } elseif ($columnRelations[$k] === 'MANYTOMANY') { + // the table must has 2 primary keys. + $pks = null; + $pks = $this->service->getPK($relation[$k]['reftable'], $pks); + /** @noinspection PhpParamsInspection */ + if ($pks !== false || count($pks) === 2) { + $relation[$k]['key'] = 'MANYTOMANY'; + $refcol2 = (self::$prefixBase . $pks[0] === $relation[$k]['refcol']) ? $pks[1] : $pks[0]; + + try { + $defsFK = $this->service->getDefTableFK($relation[$k]['reftable'], false); + } catch (Exception $e) { + return 'Error: Unable read table dependencies ' . $e->getMessage(); + } + try { + $keys2 = $this->service->getDefTableKeys($defsFK[$refcol2]['reftable'], true, + 'PRIMARY KEY'); + } catch (Exception $e) { + return 'Error: Unable read table dependencies' . $e->getMessage(); + } + $relation[$k]['refcol2'] = self::$prefixBase . $refcol2; + if (is_array($keys2)) { + $keys2 = array_keys($keys2); + $relation[$k]['col2'] = $keys2[0]; + } else { + $relation[$k]['col2'] = null; + } + $relation[$k]['table2'] = $defsFK[$refcol2]['reftable']; + } + } + // manytomany } - return $result; } - } elseif ($extraIdCache === 'rungen') { - $this->uid = null; } - $this->runQuery($stmt, null, false); - if ($returnArray && $stmt instanceof PDOStatement) { - $result = ($stmt->columnCount() > 0) ? $stmt->fetchAll($extraMode) : []; - $this->affected_rows = $stmt->rowCount(); - $stmt = null; // close - if ($extraIdCache === 'rungen' && $this->uid) { - // we store the information of the cache. - $this->setCache($this->uid, $this->cacheFamily, $result, $useCache); - } - $this->builderReset(); - if ($uid !== false) { - $this->internalCache[$uid] = $result; - } - return $result; + //die(1); + $convertOutput = ''; + $convertInput = ''; + $getDefTable = $this->getDefTable($tableName, $specialConversion); + + foreach ($columnRemove as $v) { + unset($getDefTable[$v]); } + //die(1); - $this->builderReset(); - return $stmt; - } + // we forced the conversion but only if it is not specified explicit - /** - * Generates the sql (script). It doesn't run or execute the query. - * - * @param bool $resetStack if true then it reset all the values of the - * stack, including parameters. - * - * @return string - */ - public function sqlGen($resetStack = false) - { - if (stripos($this->select, 'select ') === 0) { - // is it a full query? $this->select=select * ..." instead of $this->select=* - $words = preg_split('#\s+#', strtolower($this->select)); - } else { - $words = []; - } - if (!in_array('select', $words)) { - $sql = 'select ' . $this->distinct . $this->select; - } else { - $sql = $this->select; // the query already constains "select", so we don't want "select select * from". - } - if (!in_array('from', $words)) { - $sql .= ' from ' . $this->from; - } else { - $sql .= $this->from; - } - $where = $this->constructWhere(); - $having = $this->constructHaving(); + $allColumns = array_merge($getDefTable, $extraCols); // $extraColArray does not has type + + foreach ($allColumns as $kcol => $colDef) { + $type = isset($colDef['type']) ? $colDef['type'] : null; + $conversion = null; + if (isset($columnRelations[$kcol])) { + $conversion = $columnRelations[$kcol]; + if ($type !== null) { + $getDefTable[$kcol]['conversion'] = $conversion; + } else { + $type = 'new column'; + } + } elseif ($type !== null && isset($this->codeClassConversion[$type]) + && $getDefTable[$kcol]['conversion'] === null + ) { + $conversion = $this->codeClassConversion[$type]; + $getDefTable[$kcol]['conversion'] = $conversion; + } + + if ($conversion !== null) { + if (is_array($conversion)) { + list($input, $output) = $conversion; + } else { + $input = $conversion; + $output = $input; + } - $sql .= $where . $this->group . $having . $this->order . $this->limit; + switch ($input) { + case 'encrypt': + $tmp2 = "isset(%s) and %s=self::getPdoOne()->encrypt(%s);"; + break; + case 'decrypt': + $tmp2 = "isset(%s) and %s=self::getPdoOne()->decrypt(%s);"; + break; + case 'datetime3': + $tmp2 = "isset(%s) and %s=PdoOne::dateConvert(%s, 'human', 'sql');"; + break; + case 'datetime4': + $tmp2=''; + //$tmp2 = "isset(%s) and %s=PdoOne::dateConvert(%s, 'sql', 'sql');"; + break; + case 'datetime2': + $tmp2 = "isset(%s) and %s=PdoOne::dateConvert(%s, 'iso', 'sql');"; + break; + case 'datetime': + $tmp2 = "isset(%s) and %s=PdoOne::dateConvert(%s, 'class', 'sql');"; + break; + case 'timestamp': + $tmp2 = "isset(%s) and %s=PdoOne::dateConvert(%s, 'timestamp', 'sql')"; + break; + case 'bool': + $tmp2 = "isset(%s) and %s=(%s) ? 1 : 0;"; + break; + case 'int': + $tmp2 = "isset(%s) and %s=(int)%s;"; + break; + case 'float': + case 'decimal': + $tmp2 = "isset(%s) and %s=(float)%s;"; + break; + default: + if (strpos($input, '%s') !== false) { + $tmp2 = "%s=isset(%s) ? " . $input . " : null;"; + } else { + $tmp2 = '// type ' . $input . ' not defined'; + } + } + switch ($output) { + case 'encrypt': + $tmp = "%s=isset(%s) ? self::getPdoOne()->encrypt(%s) : null;"; + break; + case 'decrypt': + $tmp = "%s=isset(%s) ? self::getPdoOne()->decrypt(%s) : null;"; + break; + case 'datetime3': + $tmp = "%s=isset(%s) ? PdoOne::dateConvert(%s, 'sql', 'human') : null;"; + break; + case 'datetime4': + // sql->sql no conversion + $tmp = ''; + break; + case 'datetime2': + $tmp = "%s=isset(%s) ? PdoOne::dateConvert(%s, 'sql', 'iso') : null;"; + break; + case 'datetime': + $tmp = "%s=isset(%s) ? PdoOne::dateConvert(%s, 'sql', 'class') : null;"; + break; + case 'timestamp': + $tmp = "%s=isset(%s) ? PdoOne::dateConvert(%s, 'sql', 'timestamp') : null;"; + break; + case 'bool': + $tmp = "%s=isset(%s) ? (%s) ? true : false : null;"; + break; + case 'int': + $tmp = "%s=isset(%s) ? (int)%s : null;"; + break; + case 'float': + case 'decimal': + $tmp = "%s=isset(%s) ? (float)%s : null;"; + break; + case null: + $tmp = "!isset(%s) and %s=null; // no conversion"; + break; + default: + if (strpos($output, '%s') !== false) { + $tmp = "%s=isset(%s) ? " . $output . " : null;"; + } else { + $tmp = '// type ' . $output . ' not defined'; + } + } - if ($resetStack) { - $this->builderReset(); + if ($tmp !== '') { + $convertOutput .= "\t\t" . str_replace('%s', "\$row['$kcol']", $tmp) . "\n"; + $convertInput .= "\t\t" . str_replace('%s', "\$row['$kcol']", $tmp2) . "\n"; + } + } else { + $tmp = "!isset(%s) and %s=null; // $type"; + $convertOutput .= "\t\t" . str_replace('%s', "\$row['$kcol']", $tmp) . "\n"; + } } - return $sql; - } + $linked = ''; + foreach ($relation as $k => $v) { + $key = $v['key']; + if ($key === 'MANYTOONE') { + //$col = ltrim($v['refcol'], '_'); + $col = ltrim($k, '_'); + $linked .= str_replace(['{_col}', '{refcol}', '{col}'], [$k, $v['refcol'], $col], "\t\tisset(\$row['{_col}']) + and \$row['{_col}']['{refcol}']=&\$row['{col}']; // linked MANYTOONE\n"); + } + if ($key === 'ONETOONE') { + //$col = ltrim($v['refcol'], '_'); + //$col = ltrim($k, '_'); + $linked .= str_replace(['{_col}', '{refcol}', '{col}'], [$k, $v['refcol'], $v['col']], "\t\tisset(\$row['{_col}']) + and \$row['{_col}']['{refcol}']=&\$row['{col}']; // linked ONETOONE\n"); + } + } + //$convertOutput.=$linked; - /** - * @return string - */ - private function constructWhere() - { - return count($this->where) ? ' where ' . implode(' and ', $this->where) : ''; - } + $convertOutput = rtrim($convertOutput, "\n"); + $convertInput = rtrim($convertInput, "\n"); - // + // discard columns + //$identities=$this->getDefTableKeys($tableName,); + $identities = $this->getDefIdentities($tableName); + if (count($identities) > 0) { + $identity = $identities[0]; + } else { + $identity = null; + } + if ($defNoInsert !== null) { + $noInsert = array_merge($identities, $defNoInsert); + } else { + $noInsert = $identities; + } + if ($defNoUpdate !== null) { + $noUpdate = array_merge($identities, $defNoUpdate); + } else { + $noUpdate = array_merge($identities); + } + if ($pk) { + // we never update the primary key. + /** @noinspection AdditionOperationOnArraysInspection */ + $noUpdate += $pk; // it adds and replaces duplicates, indexes are ignored. + } - /** - * Returns a list of objects from the current schema/db
- * - * @param string $type =['table','function'][$i] The type of the - * object - * @param bool $onlyName If true then it only returns the name of the - * objects. - * - * @return bool|array - * @throws Exception - */ - public function objectList($type = 'table', $onlyName = false) - { - $query = $this->service->objectList($type, $onlyName); - if ($onlyName) { - return $this->select($query)->toListSimple(); + $relation2 = []; + foreach ($relation as $col => $arr) { + if ($arr['key'] !== 'FOREIGN KEY' && $arr['key'] !== 'PARENT' && $arr['key'] !== 'NONE') { + @$relation2[$arr['key']][] = $col; + } + //if($arr['key']==='MANYTOONE') { + // $relation2[]=$col; + // } } - return $this->runRawQuery($query, []); + try { + $r = str_replace(array( + '{pk}', + '{identity}', + '{def}', + '{convertoutput}', + '{convertinput}', + '{defname}', + '{defkey}', + '{defnoinsert}', + '{defnoupdate}', + '{deffk}', + '{deffktype}', + '{deffktype2}', + '{array}', + '{array_null}', + '{linked}' + ), array( + self::varExport($pk), + self::varExport($identity), // {identity} + //str_replace(["\n\t\t ", "\n\t\t ],"], ['', '],'], self::varExport($gdf, "\t\t")), // {def} + self::varExport($getDefTable, "\t\t"), // {def} + $convertOutput, // {convertoutput} + $convertInput, // {convertinput} + self::varExport(array_keys($getDefTable), "\t\t"), // {defname} + self::varExport($this->getDefTableKeys($tableName), "\t\t"), // {defkey} + self::varExport($noInsert, "\t\t"), // {defnoinsert} + self::varExport($noUpdate, "\t\t"), // {defnoupdate} + self::varExport($this->getDefTableFK($tableName), "\t\t\t"), //{deffk} + self::varExport($relation, "\t\t"), //{deffktype} + self::varExport($relation2, "\t\t"), //{deffktype2} + str_replace("\n", "\n\t\t", + rtrim($this->generateCodeArray($tableName, null, false, false, true, $classRelations, $relation), + "\n")), + str_replace("\n", "\n\t\t", + rtrim($this->generateCodeArray($tableName, null, true, false, true, $classRelations, $relation), + "\n")), + $linked // {linked} + ), $r); + } catch (Exception $e) { + return "Unable read definition of tables " . $e->getMessage(); + } + + return $r; } /** - * It returns an array of simple columns (not declarative). It uses the - * first column
- * Example:
+ * It returns an array with all the tables of the schema, also the foreign key and references of each table
+ * Example: *
-     * select('select id from table')->toListSimple() // ['1','2','3','4']
+     * $this->tableDependency();
+     * // ['table'=>['city','country'],
+     * //    'after'=>['city'=>['country'],'country=>[]],
+     * //    'before'=>['country'=>['city'],'city=>[]]
+     * //   ]
+     * $this->tableDependency(true);
+     * // ["tables" => ["city","country"]
+     * //    ,"after" => ["city" => ["countryfk" => "country"],"country" => []]
+     * //    ,"before" => ["city" => [],"country" => ["country_id" => "country_id","city"]]
+     * // ]
      * 
* - * @return array|bool + * @param bool $returnColumn If true then in "after" and "before", it returns the name of the columns + * @param bool $forceLowerCase if true then the names of the tables are stored as lowercase + * + * @return array * @throws Exception */ - public function toListSimple() + public function tableDependency($returnColumn = false, $forceLowerCase = false) { - $useCache = $this->useCache; // because builderReset cleans this value - $this->beginTry(); - $rows = $this->runGen(true, PDO::FETCH_COLUMN, 'tolistsimple', false); - if ($this->endtry() === false) { - return false; + if ($returnColumn) { + if ($this->tableDependencyArrayCol !== null) { + return $this->tableDependencyArrayCol; + } + } elseif ($this->tableDependencyArray !== null) { + return $this->tableDependencyArray; } - if ($this->uid && $useCache !== false) { - // we store the information of the cache. - $this->setCache($this->uid, $this->cacheFamily, $rows, $useCache); + $tables = $this->objectList('table', true); + $after = []; + $before = []; + foreach ($tables as $table) { + $before[$table] = []; } + foreach ($tables as $table) { + $arr = $this->getDefTableFK($table, false); + $deps = []; + foreach ($arr as $k => $v) { + $v['reftable'] = ($forceLowerCase) ? strtolower($v['reftable']) : $v['reftable']; + $k = ($forceLowerCase) ? strtolower($k) : $k; + if ($returnColumn) { + $deps[$k] = $v['reftable']; + if (!isset($before[$v['reftable']][$v['refcol']])) { + $before[$v['reftable']][$v['refcol']] = []; + } + $before[$v['reftable']][$v['refcol']][] = [$k, $table]; // remote column and remote table - return $rows; + } else { + $deps[] = $v['reftable']; + $before[$v['reftable']][] = $table; + } + } + $after[$table] = $deps; // ['city']=>['country','location'] + } + if ($returnColumn) { + $this->tableDependencyArrayCol = [$tables, $after, $before]; + return $this->tableDependencyArrayCol; + } + $this->tableDependencyArray = [$tables, $after, $before]; + return $this->tableDependencyArray; } + private function typeDict($row, $default = true) + { + return $this->service->typeDict($row, $default); + } /** - * It adds a select to the query builder. - *
Example:
- *
-     * ->select("\*")->from('table') = "select * from table"
- * ->select(['col1','col2'])->from('table') = "select col1,col2 from - * table"
- * ->select('col1,col2')->from('table') = "select col1,col2 from - * table"
- * ->select('select *')->from('table') = "select * from table"
- * ->select('select * from table') = "select * from table"
- * ->select('select * from table where id=1') = "select * from table - * where id=1"
- *
+ * Returns a list of objects from the current schema/db
* - * @param string|array $sql + * @param string $type =['table','function'][$i] The type of the + * object + * @param bool $onlyName If true then it only returns the name of the + * objects. * - * @return PdoOne - * @test InstanceOf PdoOne::class,this('select 1 from DUAL') + * @return bool|array + * @throws Exception */ - public function select($sql) + public function objectList($type = 'table', $onlyName = false) { - if (is_array($sql)) { - $this->select .= implode(', ', $sql); - } elseif ($this->select === '') { - $this->select = $sql; - } else { - $this->select .= ', ' . $sql; + $query = $this->service->objectList($type, $onlyName); + if ($onlyName) { + return $this->select($query)->toListSimple(); } - return $this; + return $this->runRawQuery($query, []); } /** - * It adds a having to the query builder. - *
Example:
- * select('*')->from('table')->group('col')->having('field=2') - * having( ['field'=>20] ) // associative array with automatic type - * having( ['field'=>[20]] ) // associative array with type defined - * having( ['field',20] ) // array automatic type - * having(['field',[20]] ) // array type defined - * having('field=20') // literal value - * having('field=?',[20]) // automatic type - * having('field',[20]) // automatic type (it's the same than - * where('field=?',[20]) having('field=?', [20] ) // type(i,d,s,b) - * defined having('field=?,field2=?', [20,'hello'] ) - * - * @param string|array $sql - * @param array|mixed $param - * - * @return PdoOne - * @see http://php.net/manual/en/mysqli-stmt.bind-param.php for types - * @test InstanceOf - * PdoOne::class,this('field1=?,field2=?',[20,'hello']) + * @param $sql + * @return PdoOneQuery */ - public function having($sql, $param = self::NULL) - { - if ($sql === null) { - return $this; - } - - return $this->where($sql, $param, true); + public function select($sql) { + $query=new PdoOneQuery($this); + return $query->select($sql); } /** + * It sets a value into the query (insert or update)
* Example:
- * where( ['field'=>20] ) // associative array with automatic type - * where( ['field'=>[20]] ) // associative array with type defined - * where( ['field',20] ) // array automatic type - * where (['field',[20]] ) // array type defined - * where('field=20') // literal value - * where('field=?',[20]) // automatic type - * where('field',[20]) // automatic type (it's the same than - * where('field=?',[20]) where('field=?', [20] ) // type(i,d,s,b) - * defined where('field=?,field2=?', [20,'hello'] ) - * where('field=:field,field2=:field2', - * ['field'=>'hello','field2'=>'world'] ) // associative array as value + * ->from("table")->set('field1=?',20),set('field2=?','hello')->insert()
+ * ->from("table")->set("type=?",[6])->where("i=1")->update()
+ * set("type=?",6) // automatic
* - * @param string|array $sql Input SQL query or associative/indexed - * array - * @param array|mixed $param Associative or indexed array with the - * conditions. - * @param bool $isHaving if true then it is a HAVING sql commando - * instead of a WHERE. + * @param string|array $sqlOrArray + * @param array|mixed $param * - * @param null|string $tablePrefix * - * @return PdoOne - * @see http://php.net/manual/en/mysqli-stmt.bind-param.php for types + * @return PdoOneQuery * @test InstanceOf * PdoOne::class,this('field1=?,field2=?',[20,'hello']) */ - public function where($sql, $param = self::NULL, $isHaving = false, $tablePrefix = null) + public function set($sqlOrArray, $param = PdoOne::NULL) { - if ($sql === null) { - return $this; - } - $this->constructParam2($sql, $param, $isHaving ? 'having' : 'where', false, $tablePrefix); - return $this; + $query=new PdoOneQuery($this); + return $query->set($sqlOrArray,$param); } - /** - * Returns true if the current query has a "having" or "where" - * - * @param bool $having true it return the number of where
- * false it returns the number of having - * - * @return bool - */ - public function hasWhere($having = false) - { - if ($having) { - return count($this->having) > 0; - } - return count($this->where) > 0; - } + public static function camelize($input, $separator = '_') + { + return str_replace($separator, '', ucwords($input, $separator)); + } /** - * It adds an "limit" in a query. It depends on the type of database
- * Example:
+ * It returns a field, column or table, the quotes defined by the current database type. It doesn't considers points + * or space
*
-     *      ->select("")->limit("10,20")->toList();
+     * $this->addQuote("aaa"); // [aaa] (sqlserver) `aaa` (mysql)
+     * $this->addQuote("[aaa]"); // [aaa] (sqlserver, unchanged)
      * 
* - * @param string $sql Input SQL query + * @param string $txt * - * @return PdoOne - * @throws Exception - * @test InstanceOf PdoOne::class,this('1,10') + * @return string + * @see \eftec\PdoOne::addDelimiter to considers points */ - public function limit($sql) + public function addQuote($txt) { - if ($sql === null) { - return $this; + if (strlen($txt) < 2) { + return $txt; } - $this->service->limit($sql); - - return $this; + if ($txt[0] === $this->database_delimiter0 && substr($txt, -1) === $this->database_delimiter1) { + // it is already quoted. + return $txt; + } + return $this->database_delimiter0 . $txt . $this->database_delimiter1; } + /** - * Adds a distinct to the query. The value is ignored if the select() is - * written complete.
- *
-     *      ->select("*")->distinct() // works
-     *      ->select("select *")->distinct() // distinct is ignored.
-     *
+ * Connects to the database. * - * @param string $sql Input SQL query + * @param bool $failIfConnected true=it throw an error if it's connected, + * otherwise it does nothing * - * @return PdoOne - * @test InstanceOf PdoOne::class,this() + * @throws Exception + * @test exception this(false) */ - public function distinct($sql = 'distinct') + public function connect($failIfConnected = true) { - if ($sql === null) { - return $this; + if ($this->isOpen) { + if (!$failIfConnected) { + return; + } // it's already connected. + $this->throwError('Already connected', ''); } - $this->distinct = ($sql) ? $sql . ' ' : ''; + try { + if ($this->logLevel >= 2) { + $this->storeInfo("connecting to {$this->server} {$this->user}/*** {$this->db}"); + } + $cs = (!$this->charset) ? ';charset=' . $this->charset : ''; + $this->service->connect($cs, false); + if ($this->conn1 instanceof stdClass) { + $this->isOpen = true; + return; + } + $this->conn1->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->conn1->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); - return $this; + //$this->conn1->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); It is not required. + + $this->isOpen = true; + } catch (Exception $ex) { + $this->isOpen = false; + $this->throwError("Failed to connect to {$this->databaseType}", $ex->getMessage(), '', true, $ex); + } } /** - * It returns an associative array where the first value is the key and the - * second is the value
If the second value does not exist then it uses - * the index as value (first value)
- * Example:
- *
-     * select('select cod,name from table')->toListKeyValue() //
-     * ['cod1'=>'name1','cod2'=>'name2'] select('select cod,name,ext from
-     * table')->toListKeyValue('|') //
-     * ['cod1'=>'name1|ext1','cod2'=>'name2|ext2']
-     * 
+ * Write a log line for debug, clean the command chain then throw an error + * (if throwOnError==true) * - * @param string|null $extraValueSeparator (optional) It allows to read a - * third value and returns it - * concatenated with the value. - * Example '|' + * @param string $txt The message to show. + * @param string $txtExtra It's only used if $logLevel>=2. It + * shows an extra message + * @param string|array $extraParam It's only used if $logLevel>=3 It + * shows parameters (if any) * - * @return array|bool|null - * @throws Exception + * @param bool $throwError if true then it throw error (is enabled). Otherwise it store the error. + * + * @param null|RuntimeException $exception + * + * @see \eftec\PdoOne::$logLevel */ - public function toListKeyValue($extraValueSeparator = null) + public function throwError($txt, $txtExtra, $extraParam = '', $throwError = true, $exception = null) { - $list = $this->toList(PDO::FETCH_NUM); - if (!is_array($list)) { - return null; + if ($this->logLevel === 0) { + $txt = 'Error on database'; } - $result = []; - foreach ($list as $item) { - if ($extraValueSeparator === null) { - $result[$item[0]] = isset($item[1]) ? $item[1] : $item[0]; + if ($this->logLevel >= 2) { + $txt .= "\n
extra:[{$txtExtra}]"; + } + if ($this->logLevel >= 2) { + $txt .= "\n
last query:[{$this->lastQuery}]"; + } + if ($this->logLevel >= 3) { + $txt .= "\n
database:" . $this->server . ' - ' . $this->db; + if (is_array($extraParam)) { + foreach ($extraParam as $k => $v) { + if (is_array($v) || is_object($v)) { + $v = json_encode($v); + } + $txt .= "\n
$k:$v"; + } } else { - $result[$item[0]] = (isset($item[1]) ? $item[1] : $item[0]) . $extraValueSeparator . @$item[2]; + $txt .= "\n
Params :[" . $extraParam . "]\n
"; + } + if ($exception !== null) { + $txt .= "\n
message :[" . str_replace("\n", "\n
", $exception->getMessage()) . "]"; + $txt .= "\n
trace :[" . str_replace("\n", "\n
", $exception->getTraceAsString()) . "]"; + $txt .= "\n
code :[" . str_replace("\n", "\n
", $exception->getCode()) . "]\n
"; } } - - return $result; + if ($this->getMessages() === null) { + $this->debugFile($txt, 'ERROR'); + } else { + $this->getMessages()->addItem($this->db, $txt); + $this->debugFile($txt, 'ERROR'); + } + $this->errorText = $txt; + if ($throwError && $this->throwOnError && $this->genError) { + throw new RuntimeException($txt); + } } /** - * It returns an declarative array of rows.
- * If not data is found, then it returns an empty array
- * This method is an end of the chain method, so it clears the method stack
- * Example:
- *
-     * $this->select('select id,name from table')->toList() // [['id'=>'1','name'='john'],['id'=>'2','name'=>'anna']]
-     * $this->select('id,name')
-     *      ->from('table')
-     *      ->where('condition=?',[20])
-     *      ->toList();
-     * 
- * - * @param int $pdoMode (optional) By default is PDO::FETCH_ASSOC + * Injects a Message Container. * - * @return array|bool - * @throws Exception + * @return MessageList|null + * @test equals null,this(),'this is not a message container' */ - public function toList($pdoMode = PDO::FETCH_ASSOC) + public function getMessages() { - $useCache = $this->useCache; // because builderReset cleans this value - $this->beginTry(); - $rows = $this->runGen(true, $pdoMode, 'tolist', false); - if ($this->endtry() === false) { - return false; - } - if ($this->uid && $useCache !== false) { - // we store the information of the cache. - $this->setCache($this->uid, $this->cacheFamily, $rows, $useCache); + if (function_exists('messages')) { + return messages(); } - return $rows; - } - /** - * It returns a PDOStatement.
- * Note: The result is not cached. - * - * @return PDOStatement - * @throws Exception - */ - public function toResult() - { - return $this->runGen(false); + return null; } - /** - * It returns the first row. If there is not row then it returns false.
- * This method is an end of the chain method, so it clears the method stack
- * Example:
- *
-     *      $con->select('*')->from('table')->first(); // select * from table
-     *      (first value)
-     * 
- * - * @return array|null|false - * @throws Exception - */ - public function first() + public function debugFile($txt, $level = 'INFO') { - $useCache = $this->useCache; // because builderReset cleans this value - $uid = false; - if ($useCache !== false) { - $sql = $this->sqlGen(); - $this->uid = hash($this->encryption->hashType, - $sql . PDO::FETCH_ASSOC . serialize($this->whereParamAssoc) . serialize($this->havingParamAssoc) - . 'firstscalar'); - $rows = $this->cacheService->getCache($this->uid, $this->cacheFamily); - if ($rows !== false) { - $this->builderReset(); - - return $rows; - } - } - if ($this->useInternalCache) { - $sql = (!isset($sql)) ? $this->sqlGen() : $sql; - $allparam = array_merge($this->setParamAssoc, $this->whereParamAssoc, $this->havingParamAssoc); - $uid = hash($this->encryption->hashType, 'first' . $sql . serialize($allparam)); - if (isset($this->internalCache[$uid])) { - // we have an internal cache, so we will return it. - $this->internalCacheCounter++; - $this->builderReset(); - return $this->internalCache[$uid]; - } + if (!$this->logFile) { + return; // debug file is disabled. } - $this->beginTry(); - /** @var PDOStatement $statement */ - $statement = $this->runGen(false, PDO::FETCH_ASSOC, 'first', false); - if ($this->endtry() === false) { - return null; + $fz = @filesize($this->logFile); + + if (is_object($txt) || is_array($txt)) { + $txtW = print_r($txt, true); + } else { + $txtW = $txt; } - $row = null; - if ($statement === false) { - $row = null; - } elseif (!$statement->columnCount()) { - $row = null; + if ($fz > 10000000) { + // mas de 10mb = reducirlo a cero. + $fp = @fopen($this->logFile, 'wb'); } else { - $row = $statement->fetch(PDO::FETCH_ASSOC); - @$statement->closeCursor(); - $statement = null; + $fp = @fopen($this->logFile, 'ab'); } - - if ($this->uid && $useCache !== false) { - // we store the information of the cache. - $this->setCache($this->uid, $this->cacheFamily, $row, $useCache); + if(!$fp) { + return; // unable to open the log file. } - if ($uid !== false) { - $this->internalCache[$uid] = $row; + if ($this->logLevel === 2) { + $txtW .= ' param:' . json_encode($this->lastParam); } - return $row; + $txtW = str_replace(array("\r\n", "\n"), ' ', $txtW); + try { + $now = new DateTime(); + @fwrite($fp, $now->format('c') . "\t" . $level . "\t" . $txtW . "\n"); + } catch (Exception $e) { + } + + @fclose($fp); } - //
- // /** - * @return string + * Write a log line for debug, clean the command chain then throw an error + * (if throwOnError==true) + * + * @param $txt + * + * @throws Exception */ - private function constructHaving() + public function storeInfo($txt) { - return count($this->having) ? ' having ' . implode(' and ', $this->having) : ''; + if ($this->getMessages() === null) { + $this->debugFile($txt); + } else { + $this->getMessages()->addItem($this->db, $txt, 'info'); + $this->debugFile($txt); + } } /** - * It ends a try block and throws the error (if any) + * Returns the last error. * - * @return bool - * @throws Exception + * @return string */ - private function endTry() + public function lastError() { - $this->throwOnError = $this->throwOnErrorB; - if ($this->errorText) { - $this->throwError('endtry:' . $this->errorText, '', '', $this->isThrow); - return false; + if (!$this->isOpen) { + return "It's not connected to the database"; } - return true; + + return $this->conn1->errorInfo()[2]; } /** - * It returns an array with all the tables of the schema, also the foreign key and references of each table
- * Example: - *
-     * $this->tableDependency();
-     * // ['table'=>['city','country'],
-     * //    'after'=>['city'=>['country'],'country=>[]],
-     * //    'before'=>['country'=>['city'],'city=>[]]
-     * //   ]
-     * $this->tableDependency(true);
-     * // ["tables" => ["city","country"]
-     * //    ,"after" => ["city" => ["countryfk" => "country"],"country" => []]
-     * //    ,"before" => ["city" => [],"country" => ["country_id" => "country_id","city"]]
-     * // ]
-     * 
+ * It adds a delimiter to a text based in the type of database (` for mysql + * and [] for sql server)
Example:
+ * $pdoOne->addDelimiter('hello world'); // `hello` world
+ * $pdoOne->addDelimiter('hello.world'); // `hello`.`world`
+ * $pdoOne->addDelimiter('hello=value); // `hello`=value
* - * @param bool $returnColumn If true then in "after" and "before", it returns the name of the columns - * @param bool $forceLowerCase if true then the names of the tables are stored as lowercase + * @param $txt * - * @return array - * @throws Exception + * @return mixed|string */ - public function tableDependency($returnColumn = false, $forceLowerCase = false) + public function addDelimiter($txt) { - if ($returnColumn) { - if ($this->tableDependencyArrayCol !== null) { - return $this->tableDependencyArrayCol; + if (strpos($txt, $this->database_delimiter0) === false) { + $pos = $this->strposa($txt, [' ', '=']); + if ($pos === false) { + $quoted = $this->database_delimiter0 . $txt . $this->database_delimiter1; + $quoted = str_replace('.', $this->database_delimiter1 . '.' . $this->database_delimiter0, $quoted); + } else { + $arr = explode(substr($txt, $pos, 1), $txt, 2); + $quoted + = $this->database_delimiter0 . $arr[0] . $this->database_delimiter1 . substr($txt, $pos, 1) + . $arr[1]; + $quoted = str_replace('.', $this->database_delimiter1 . '.' . $this->database_delimiter0, $quoted); } - } elseif ($this->tableDependencyArray !== null) { - return $this->tableDependencyArray; - } - $tables = $this->objectList('table', true); - $after = []; - $before = []; - foreach ($tables as $table) { - $before[$table] = []; - } - foreach ($tables as $table) { - $arr = $this->getDefTableFK($table, false); - $deps = []; - foreach ($arr as $k => $v) { - $v['reftable'] = ($forceLowerCase) ? strtolower($v['reftable']) : $v['reftable']; - $k = ($forceLowerCase) ? strtolower($k) : $k; - if ($returnColumn) { - $deps[$k] = $v['reftable']; - if (!isset($before[$v['reftable']][$v['refcol']])) { - $before[$v['reftable']][$v['refcol']] = []; - } - $before[$v['reftable']][$v['refcol']][] = [$k, $table]; // remote column and remote table - } else { - $deps[] = $v['reftable']; - $before[$v['reftable']][] = $table; - } - } - $after[$table] = $deps; // ['city']=>['country','location'] - } - if ($returnColumn) { - $this->tableDependencyArrayCol = [$tables, $after, $before]; - return $this->tableDependencyArrayCol; + return $quoted; } - $this->tableDependencyArray = [$tables, $after, $before]; - return $this->tableDependencyArray; + // it has a delimiter, so we returned the same text. + return $txt; } - private function typeDict($row, $default = true) + private function strposa($haystack, $needles = [], $offset = 0) { - return $this->service->typeDict($row, $default); - } + $chr = []; + foreach ($needles as $needle) { + $res = strpos($haystack, $needle, $offset); + if ($res !== false) { + $chr[$needle] = $res; + } + } + if (empty($chr)) { + return false; + } - public static function camelize($input, $separator = '_') - { - return str_replace($separator, '', ucwords($input, $separator)); + return min($chr); } /** - * @param string $tableName + * 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.
+     * 
      *
-     * @return string
+     * @param string $rawSql The query to execute
+     * @param array|null $param [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
+     *
+     * @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 generateCodeCreate($tableName)
+    public function runRawQuery($rawSql, $param = null, $returnArray = true)
     {
-        $code = "\$pdo->createTable('" . $tableName . "',\n";
-        $arr = $this->getDefTable($tableName);
-        $arrKey = $this->getDefTableKeys($tableName);
-        $arrFK = self::varExport($this->getDefTableFK($tableName));
-        $keys = self::varExport($arrKey);
-        $code .= "\t" . self::varExport($arr);
-        $code .= ",$keys);\n";
-        $code .= "\$pdo->createFk('" . $tableName . "',\n";
-        $code .= "$arrFK);\n";
+        if (!$this->isOpen) {
+            $this->throwError("It's not connected to the database", '');
 
-        return $code;
-    }
+            return false;
+        }
+        if (!$rawSql) {
+            $this->throwError("Query empty", '');
+            return false;
+        }
+        $writeCommand = self::queryCommand($rawSql, true) !== 'dql';
 
-    public static function varExport($input, $indent = "\t")
-    {
-        switch (gettype($input)) {
-            case 'string':
-                $r = "'" . addcslashes($input, "\\\$\'\r\n\t\v\f") . "'";
-                break;
-            case 'array':
-                $indexed = array_keys($input) === range(0, count($input) - 1);
-                $r = [];
-                foreach ($input as $key => $value) {
-                    $r[] = "$indent    " . ($indexed ? '' : self::varExport($key) . ' => ') . self::varExport($value,
-                            "$indent    ");
-                }
+        /** @var bool|string $uid it stores the unique identifier of the query */
+        $uid = false;
 
-                $r = "[\n" . implode(",\n", $r) . "\n" . $indent . ']';
-                break;
-            case 'boolean':
-                $r = $input ? 'TRUE' : 'FALSE';
-                break;
-            default:
-                $r = var_export($input, true);
-                break;
+        if ($this->readonly && $writeCommand) {
+            // we aren't checking SQL-DLC queries. Also, "insert into" is stopped but "  insert into" not.
+            $this->throwError('Database is in READ ONLY MODE', '');
+            return false;
+        }
+        if (!is_array($param) && $param !== null) {
+            $this->throwError('runRawQuery, param must be null or an array', '');
+            return false;
+        }
+        if ($this->useInternalCache && $returnArray === true && !$writeCommand) {
+            // if we use internal cache and we returns an array and it is not a write command
+            $uid = hash($this->encryption->hashType, $rawSql . serialize($param));
+            if (isset($this->internalCache[$uid])) {
+                // we have an internal cache, so we will return it.
+                $this->internalCacheCounter++;
+                return $this->internalCache[$uid];
+            }
         }
-        return $r;
-    }
 
-    /**
-     * It generates a class
- * Example:
- *
-     * $class = $this->generateCodeClass('tablename', 'namespace\namespace2'
-     *          ,['_idchild2FK'=>'PARENT' // relation
-     *          ,'_tablaparentxcategory'=>'MANYTOMANY' // relation
-     *          ,'col'=>'datetime3' // conversion
-     *          ,'col2'=>'conversion(%s)' // custom conversion (identified by %s)
-     *          ,'col3'=>] // custom conversion (identified by %s)
-     *          ,'Repo');
-     * $class = $this->generateCodeClass(['ClassName'=>'tablename'], 'namespace\namespace2'
-     *          ,['/idchild2FK'=>'PARENT','/tablaparentxcategory'=>'MANYTOMANY']
-     *          ,'Repo');
-     * 
- * - * @param string|array $tableName The name of the table and the class. - * If the value is an array, then the key is the name of the table and - * the value is the name of the class - * @param string $namespace The Namespace of the generated class - * @param array|null $columnRelations An associative array to specific custom relations, such as PARENT
- * The key is the name of the columns and the value is the type of - * relation
- * @param null|string[] $classRelations The postfix of the class. Usually it is Repo or Dao. - * - * @param array $specialConversion An associative array to specify a custom conversion
- * The key is the name of the columns and the value is the type of - * relation
- * @param string[]|null $defNoInsert An array with the name of the columns to not to insert. The identity - * is added automatically to this list - * @param string[]|null $defNoUpdate An array with the name of the columns to not to update. The identity - * is added automatically to this list - * @param string|null $baseClass The name of the base class. If no name then it uses the last namespace - * @param string $modelfullClass (default:'') The full class of the model (with the namespace). If - * empty, then it doesn't use a model - * @param array $extraCols An associative array with extra columns where they key is the name of - * the column and the value is the value to return (it is evaluated in - * the query). It is used by toList() and first(), it's also added to - * the model. - * - * @param array $columnRemove - * - * @return string|string[] - * @throws Exception - */ - public function generateCodeClass( - $tableName, - $namespace = '', - $columnRelations = null, - $classRelations = null, - $specialConversion = [], - $defNoInsert = null, - $defNoUpdate = null, - $baseClass = null, - $modelfullClass = '', - $extraCols = [], - $columnRemove = [] - ) - { - $r = <<<'eot' - - * $code=$pdoOne->generateCodeClass({args}); - *
- */ -abstract class Abstract{classname} extends {baseclass} -{ - const TABLE = '{table}'; - const COMPILEDVERSION={compiled}; - const IDENTITY = {identity}; - const PK = {pk}; - const ME=__CLASS__; - const EXTRACOLS='{extracol}'; + $this->lastParam = $param; + $this->lastQuery = $rawSql; + if ($this->logLevel >= 2) { + $this->storeInfo($rawSql); + } + if ($param === null) { + $rows = $this->runRawQueryParamLess($rawSql, $returnArray); + if ($uid !== false && $returnArray) { + $this->internalCache[$uid] = $rows; + } + return $rows; + } - /** - * It returns the definitions of the columns
- * Example:
- *
-     * self::getDef(); // ['colName'=>[php type,php conversion type,type,size,nullable,extra,sql],'colName2'=>..]
-     * self::getDef('sql'); // ['colName'=>'sql','colname2'=>'sql2']
-     * self::getDef('identity',true); // it returns the columns that are identities ['col1','col2']
-     * 
- * PHP Types: binary, date, datetime, decimal/float,int, string,time, timestamp
- * PHP Conversions: datetime (datetime class), datetime2 (iso),datetime3 (human string) - * , datetime4 (sql no conversion!), timestamp (int), bool, int, float
- * Param Types: PDO::PARAM_LOB, PDO::PARAM_STR, PDO::PARAM_INT
- * - * @param string|null $column =['phptype','conversion','type','size','null','identity','sql'][$i] - * if not null then it only returns the column specified. - * @param string|null $filter If filter is not null, then it uses the column to filter the result. - * - * @return array|array[] - */ - public static function getDef($column=null,$filter=null) { - $r = {def}; - if($column!==null) { - if($filter===null) { - foreach($r as $k=>$v) { - $r[$k]=$v[$column]; + // the "where" has parameters. + $stmt = $this->prepare($rawSql); + if ($stmt === false) { + $this->throwError("Unable to prepare statement", $rawSql); + return false; + } + $counter = 0; + if ($this->isAssoc($param)) { + $this->lastBindParam = $param; + // [':name'=>value,':name2'=>value2]; + foreach ($param as $k => $v) { + // note: the second field is & so we could not use $v + $stmt->bindParam($k, $param[$k], $this->getType($v)); + } + } else { + // parameters numeric + $this->lastBindParam = []; + $f = reset($param); + if (is_array($f)) { + // arrays of arrays. + // [[name1,value1,type1,l1],[name2,value2,type2,l1]] + foreach ($param as $k => $v) { + $this->lastBindParam[$counter] = $v[0]; + // note: the second field is & so we could not use $v + $stmt->bindParam($v[0], $param[$k][1], $v[2], $v[3]); } } else { - $new=[]; - foreach($r as $k=>$v) { - if($v[$column]===$filter) { - $new[]=$k; - } + // [value1,value2] + foreach ($param as $i => $iValue) { + //$counter++; + //$typeP = $this->stringToPdoParam($param[$i]); + $this->lastBindParam[$i] = $param[$i]; + //$stmt->bindParam($counter, $param[$i + 1], $typeP); + $stmt->bindParam($i + 1, $param[$i], $this->getType($param[$i])); } - return $new; } } - return $r; - } - - /** - * It converts a row returned from the database.
- * If the column is missing then it sets the field as null. - * - * @param array $row [ref] - */ - public static function convertOutputVal(&$row) { - if($row===false || $row===null) { - return; + + if ($this->useCache !== false && $returnArray) { + $this->uid = hash($this->encryption->hashType, $this->lastQuery . serialize($this->lastBindParam)); + $result = $this->cacheService->getCache($this->uid, $this->cacheFamily); + if ($result !== false) { + // it's found in the cache. + if (is_array($result)) { + $this->affected_rows = count($result); + } else { + $this->affected_rows = 0; + } + if ($uid !== false) { + $this->internalCache[$uid] = $result; + } + return $result; + } + } else { + $this->uid = null; } -{convertoutput} -{linked} - } + $this->runQuery($stmt); - /** - * It converts a row to be inserted or updated into the database.
- * If the column is missing then it is ignored and not converted. - * - * @param array $row [ref] - */ - public static function convertInputVal(&$row) { -{convertinput} - } + if ($returnArray && $stmt instanceof PDOStatement) { + $rows = ($stmt->columnCount() > 0) ? $stmt->fetchAll(PDO::FETCH_ASSOC) : []; + $this->affected_rows = $stmt->rowCount(); + $stmt = null; + if ($uid !== false) { + $this->internalCache[$uid] = $rows; + } + return $rows; + } + + if ($stmt instanceof PDOStatement) { + $this->affected_rows = $stmt->rowCount(); + } else { + $this->affected_rows = 0; + } + return $stmt; + } /** - * It gets all the name of the columns. + * It returns the command (in lower case) or the type of command of a query
+ * Example:
+ *
+     * $this->queryCommand("select * from table") // returns "select"
+     * $this->queryCommand("select * from table",true) // returns "dql"
+     * 
+ * + * @param string $sql + * @param false $returnType if true then it returns DML (insert/updat/delete/etc) or DQL (select/show/display) + * + * @return string * - * @return string[] */ - public static function getDefName() { - return {defname}; + public static function queryCommand($sql, $returnType = false) + { + if (!$sql) { + return $returnType ? 'dml' : 'dql'; + } + $command = strtolower((explode(' ', trim($sql)))[0]); + if ($returnType) { + if ($command === 'select' || $command === 'show' || $command === 'display') { + return 'dql'; + } + return 'dml'; + } + return $command; } + // + /** - * It returns an associative array (colname=>key type) with all the keys/indexes (if any) + * It starts a transaction. If fails then it returns false, otherwise true. * - * @return string[] + * @return bool + * @test equals true,this() + * @posttest execution $this->pdoOne->commit(); + * @example examples/testdb.php 92,4 */ - public static function getDefKey() { - return {defkey}; + public function startTransaction() + { + if ($this->transactionOpen || !$this->isOpen) { + return false; + } + $this->transactionOpen = true; + $this->conn1->beginTransaction(); + + return true; } /** - * It returns a string array with the name of the columns that are skipped when insert - * @return string[] + * Commit and close a transaction. + * + * @param bool $throw if true and it fails then it throws an error. + * + * @return bool + * @throws Exception + * @test equals false,(false),'transaction is not open' */ - public static function getDefNoInsert() { - return {defnoinsert}; + public function commit($throw = true) + { + if (!$this->transactionOpen && $throw) { + $this->throwError('Transaction not open to commit()', ''); + + return false; + } + if (!$this->isOpen) { + $this->throwError("It's not connected to the database", ''); + + return false; + } + $this->transactionOpen = false; + + return @$this->conn1->commit(); } /** - * It returns a string array with the name of the columns that are skipped when update - * @return string[] + * Rollback and close a transaction + * + * @param bool $throw [optional] if true and it fails then it throws an error. + * + * @return bool + * @throws Exception + * @test equals false,(false),'transaction is not open' */ - public static function getDefNoUpdate() { - return {defnoupdate}; + public function rollback($throw = true) + { + if (!$this->transactionOpen && $throw) { + $this->throwError('Transaction not open to rollback()', ''); + } + if (!$this->isOpen) { + $this->throwError("It's not connected to the database", ''); + + return false; + } + $this->transactionOpen = false; + + return @$this->conn1->rollback(); } + // + /** - * It adds a where to the query pipeline. It could be stacked with many where() - * Example:
- *
-     * self::where(['col'=>'value'])::toList();
-     * self::where(['col']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
-     * self::where(['col=?']=>['value'])::toList(); // s= string/double/date, i=integer, b=bool
-     * 
- * - * @param array|string $sql =self::factory() - * @param null|array|int $param + * Internal Use: It runs a raw query * - * @return {classname} + * @param string $rawSql + * @param bool $returnArray + * + * @return array|bool|false|PDOStatement + * @throws Exception + * @see \eftec\PdoOne::runRawQuery */ - public static function where($sql, $param = PdoOne::NULL) + private function runRawQueryParamLess($rawSql, $returnArray) { - self::getPdoOne()->where($sql, $param,false,{classname}::TABLE); - return {classname}::class; - } + // the "where" chain doesn't have parameters. + try { + $rows = $this->conn1->query($rawSql); + if ($rows === false) { + throw new RuntimeException('Unable to run raw runRawQueryParamLess', 9001); + } + } catch (Exception $ex) { + $rows = false; + $this->throwError('Exception in runRawQueryParamLess :', $rawSql, ['param' => $this->lastParam], true, $ex); + } - public static function getDefFK($structure=false) { - if ($structure) { - return {deffk}; + if ($returnArray && $rows instanceof PDOStatement) { + if ($rows->columnCount() > 0) { + $result = @$rows->fetchAll(PDO::FETCH_ASSOC); + $this->affected_rows = $rows->rowCount(); + + return $result; + } + + $this->affected_rows = $rows->rowCount(); + + return true; } - /* key,refcol,reftable,extra */ - return {deffktype}; + + $this->affected_rows = $rows->rowCount(); + + return $rows; } /** - * It returns all the relational fields by type. '*' returns all types.
- * It doesn't return normal columns. - * - * @param string $type=['*','MANYTOONE','ONETOMANY','ONETOONE','MANYTOMANY'][$i] + * Prepare a query. It returns a mysqli statement. * - * @return string[] - * @noinspection SlowArrayOperationsInLoopInspection - */ - public static function getRelations($type='all') { - $r= {deffktype2}; - if($type==='*') { - $result=[]; - foreach($r as $arr) { - $result = array_merge($result,$arr); + * @param string $statement A SQL statement. + * + * @return PDOStatement returns the statement if correct otherwise null + * @throws Exception + */ + public function prepare($statement) + { + if (!$this->isOpen) { + $this->throwError("It's not connected to the database", ''); + + return null; + } + $this->lastQuery = $statement; + if ($this->readonly) { + if (stripos($statement, 'insert ') === 0 || stripos($statement, 'update ') === 0 + || stripos($statement, 'delete ') === 0 + ) { + // we aren't checking SQL-DCL queries. + $this->throwError('Database is in READ ONLY MODE', ''); } - return $result; } - return isset($r[$type]) ? $r[$type] : []; - - } - - public static function toList($filter=PdoOne::NULL,$filterValue=null) { - if(self::$useModel) { - return {classmodellist} + if ($this->logLevel >= 2) { + $this->storeInfo($statement); } - return self::_toList($filter, $filterValue); + + try { + $stmt = $this->conn1->prepare($statement); + } catch (Exception $ex) { + $stmt = false; + if ($this->errorText === '') { + $this->throwError('Failed to prepare', $ex->getMessage(), ['param' => $this->lastParam]); + } + } + if (($stmt === false) && $this->errorText === '') { + $this->throwError('Unable to prepare query', $this->lastQuery, ['param' => $this->lastParam]); + } + + return $stmt; } - + /** - * It sets the recursivity. By default, if we query or modify a value, it operates with the fields of the entity. - * With recursivity, we could use the recursivity of the fields, for example, loading a MANYTOONE relation
+ * It returns true if the array is an associative array. False + * otherwise.
* Example:
- *
-     * self::setRecursive([]); // (default) no use recursivity.
-     * self::setRecursive('*'); // recursive every MANYTOONE,ONETOONE,MANYTOONE and ONETOONE relations (first level) 
-     * self::setRecursive('MANYTOONE'); // recursive all relations of the type MANYTOONE (first level)
-     * self::setRecursive(['_relation1','_relation2']); // recursive only the relations of the first level 
-     * self::setRecursive(['_relation1','_relation1/_subrelation1']); // recursive the relations (first and second level)
-     * 
- * If array then it uses the values to set the recursivity.
- * If string then the values allowed are '*', 'MANYTOONE','ONETOMANY','MANYTOMANY','ONETOONE' (first level only)
+ * isAssoc(['a1'=>1,'a2'=>2]); // true
+ * isAssoc(['a1','a2']); // false
+ * isAssoc('aaa'); isAssoc(null); // false
* - * @param string|array $recursive=self::factory(); + * @param mixed $array * - * @return {classname} + * @return bool */ - public static function setRecursive($recursive=[]) + private function isAssoc($array) { - if(is_string($recursive)) { - $recursive={classname}::getRelations($recursive); + if ($array === null) { + return false; + } + if (!is_array($array)) { + return false; } - return parent::_setRecursive($recursive); - } - public static function limit($sql) - { - self::getPdoOne()->limit($sql); - return {classname}::class; + return (array_values($array) !== $array); } + + + // + /** - * It returns the first row of a query. - * @param array|mixed|null $pk [optional] Specify the value of the primary key. + * Convert date from unix timestamp -> ISO (database format). + *

Example: ::unixtime2Sql(1558656785); // returns 2019-05-24 00:13:05 * - * @return array|bool It returns false if not file is found. - * @throws Exception + * @param integer $dateNum + * + * @return string */ - public static function first($pk = PdoOne::NULL) { - if(self::$useModel) { - return {classmodelfirst} - } - return self::_first($pk); + public static function unixtime2Sql($dateNum) + { + // 31/01/2016 20:20:00 --> 2016-01-31 00:00 + if ($dateNum === null) { + return self::$dateEpoch; + } + + return date(self::$isoDateTimeMs, $dateNum); } /** - * It returns true if the entity exists, otherwise false.
- * Example:
- *

-     *  $this->exist(['id'=>'a1','name'=>'name']); // using an array
-     *  $this->exist('a1'); // using the primary key. The table needs a pks and it only works with the first pk.
-     *  
+ * Convert date, from mysql date -> text (using a format pre-established) * - * @param array|mixed $entity =self::factory() + * @param string $sqlField + * @param bool $hasTime if true then the date contains time. * - * @return bool true if the pks exists - * @throws Exception + * @return string Returns a text with the date formatted (human readable) */ - public static function exist($entity) { - return self::_exist($entity); + public static function dateSql2Text($sqlField, $hasTime = false) + { + $tmpDate = self::dateTimeSql2PHP($sqlField, $hasTime); + if ($tmpDate === null) { + return null; + } + if ($hasTime) { + return $tmpDate->format((strpos($sqlField, '.') !== false) ? self::$dateTimeMicroHumanFormat + : self::$dateTimeHumanFormat); + } + + return $tmpDate->format(self::$dateHumanFormat); } /** - * It inserts a new entity(row) into the database
- * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional + * Convert date, from mysql -> php * - * @return array|false=self::factory() - * @throws Exception + * @param string $sqlField + * @param bool $hasTime + * + * @return bool|DateTime|null */ - public static function insert(&$entity,$transactional=true) { - return self::_insert($entity,$transactional); + public static function dateTimeSql2PHP($sqlField, &$hasTime = false) + { + // 3 2016-01-31 00:00:00 -> 01/01/2016 00:00:00 + // mysql always returns the date/datetime/timestmamp in ansi format. + if ($sqlField === '' || $sqlField === null) { + if (self::$dateEpoch === null) { + return null; + } + + return DateTime::createFromFormat(self::$isoDateTimeMs, self::$dateEpoch); + } + + if (strpos($sqlField, '.')) { + // with date with time and microseconds + //2018-02-06 05:06:07.123 + // Y-m-d H:i:s.v + $hasTime = true; + //$x = DateTime::createFromFormat("Y-m-d H:i:s.u", "2018-02-06 05:06:07.1234"); + return DateTime::createFromFormat(self::$isoDateTimeMs, $sqlField); + } + + if (strpos($sqlField, ':')) { + // date with time + $hasTime = true; + return DateTime::createFromFormat(self::$isoDateTime, $sqlField); + } + // only date + $hasTime = false; + + return DateTime::createFromFormat(self::$isoDate, $sqlField); } - + /** - * It merge a new entity(row) into the database. If the entity exists then it is updated, otherwise the entity is - * inserted
- * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional + * It converts a date (as string) into another format.
+ * Example: + *
+     * $pdoOne->dateConvert('01/01/2019','human','sql'); // 2019-01-01
+     * 
+ *
iso depends on the database. + * Example: Y-m-d H:i:s
+ * human is based in d/m/Y H:i:s but it could be changed (self::dateHumanFormat)
+ * sql depends on the database
+ * class is a DateTime() object
* - * @return array|false=self::factory() - * @throws Exception + * @param string $sqlField The date to convert + * @param string $inputFormat =['iso','human','sql','class','timestamp'][$i] + * @param string $outputFormat =['iso','human','sql','class','timestamp'][$i] + * @param null|string $force =[null,'time','ms','none'][$i] It forces if the result gets time or + * microseconds
+ * null = no force the result (it is calculated automatically)
+ * time = returns with a precision of seconds
+ * ms = returns with a precision of microseconds
+ * none = it never returns any time
+ * + * @return bool|DateTime */ - public static function merge(&$entity,$transactional=true) { - return self::_merge($entity,$transactional); + public static function dateConvert($sqlField, $inputFormat, $outputFormat, $force = null) + { + /** @var boolean $ms if true then the value has microseconds */ + $ms = false; + /** @var boolean $time if true then the value has time */ + $time = false; + $tmpDate=self::dateConvertInput($sqlField,$inputFormat,$ms,$time); + if (!$tmpDate) { + return false; + } + if ($force !== null) { + if ($force === 'ms') { + $ms = true; + } elseif ($force === 'time') { + $time = true; + $ms = false; + } elseif ($force === 'none') { + $time = false; + $ms = false; + } + } + switch ($outputFormat) { + case 'iso': + if ($ms) { + return $tmpDate->format(self::$dateTimeMicroFormat); + } + if ($time) { + return $tmpDate->format(self::$dateTimeFormat); + } + return $tmpDate->format(self::$dateFormat); + case 'human': + if ($ms) { + return $tmpDate->format(self::$dateTimeMicroHumanFormat); + } + if ($time) { + return $tmpDate->format(self::$dateTimeHumanFormat); + } + + return $tmpDate->format(self::$dateHumanFormat); + case 'sql': + if ($ms) { + return $tmpDate->format(self::$isoDateInputTimeMs); + } + if ($time) { + return $tmpDate->format(self::$isoDateInputTime); + } + + return $tmpDate->format(self::$isoDateInput); + case 'class': + return $tmpDate; + case 'timestamp': + return $tmpDate->getTimestamp(); + } + return false; } /** - * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional + * It converts a date and time value (expressed in different means) into a DateTime object or false if the operation + * fails.
+ * Example:
+ *
+     * $r=PdoOne::dateConvertInput('01/12/2020','human',$ms,$time); // it depends on the fields self::$date*HumanFormat
+     * $r=PdoOne::dateConvertInput('2020-12-01','iso',$ms,$time); // it depends on the fields self::$date*Format
+     * $r=PdoOne::dateConvertInput('2020-12-01','sql',$ms,$time); // it depends on the database
+     * $r=PdoOne::dateConvertInput(50000,'timestamp',$ms,$time); // a timestamp
+     * $r=PdoOne::dateConvertInput(new DateTime(),'class',$ms,$time); // a DateTime object (it keeps the same one)
+     * 
* - * @return array|false=self::factory() - * @throws Exception + * @param mixed $inputValue the input value. + * @param string $inputFormat =['iso','human','sql','class','timestamp'][$i] The input format + * @param boolean $ms [ref] It returns if it includes microseconds + * @param boolean $time [ref] It returns if it includes time + * @return DateTime|false false if the operation fails + * @noinspection PhpUnhandledExceptionInspection + * @noinspection PhpDocMissingThrowsInspection */ - public static function update($entity,$transactional=true) { - return self::_update($entity,$transactional); + public static function dateConvertInput($inputValue, $inputFormat, &$ms, &$time) { + switch ($inputFormat) { + case 'iso': + if (strpos($inputValue, '.') !== false) { + $ms = true; + $tmpDate = DateTime::createFromFormat(self::$dateTimeMicroFormat, $inputValue); + } elseif (strpos($inputValue, ':') !== false) { + $time = true; + $tmpDate = DateTime::createFromFormat(self::$dateTimeFormat, $inputValue); + } else { + $tmpDate = DateTime::createFromFormat(self::$dateFormat, $inputValue); + if ($tmpDate === false) { + return false; + } + $tmpDate->setTime(0, 0); + } + break; + case 'human': + if (strpos($inputValue, '.') !== false) { + $ms = true; + $tmpDate = DateTime::createFromFormat(self::$dateTimeMicroHumanFormat, $inputValue); + } elseif (strpos($inputValue, ':') !== false) { + $time = true; + $tmpDate = DateTime::createFromFormat(self::$dateTimeHumanFormat, $inputValue); + } else { + $tmpDate = DateTime::createFromFormat(self::$dateHumanFormat, $inputValue); + + if ($tmpDate === false) { + return false; + } + $tmpDate->setTime(0, 0); + } + break; + case 'sql': + if (strpos($inputValue, '.') !== false) { + $ms = true; + $tmpDate = DateTime::createFromFormat(self::$isoDateTimeMs, $inputValue); + } elseif (strpos($inputValue, ':') !== false) { + $time = true; + $tmpDate = DateTime::createFromFormat(self::$isoDateTime, $inputValue); + } else { + $tmpDate = DateTime::createFromFormat(self::$isoDate, $inputValue); + $tmpDate->setTime(0, 0); + } + break; + case 'class': + if(is_array($inputValue)) { + // sometimes we have a DateTime class but it is converted into an array. We fixed this problem. + $inputValue=new DateTime($inputValue['date']); + } + /** @var DateTime $tmpDate */ + $tmpDate = $inputValue; + $time = $tmpDate->format('Gis') !== '000000'; + break; + case 'timestamp': + $tmpDate = new DateTime(); + $tmpDate->setTimestamp($inputValue); + $time = $tmpDate->format('Gis') !== '000000'; + $ms = fmod($inputValue, 1) !== 0.0; + break; + default: + $tmpDate = false; + trigger_error('PdoOne: dateConvert type not defined'); + } + return $tmpDate; } /** - * It deletes an entity by the primary key + * Convert date, from text -> mysql (using a format pre-established) * - * @param array|object $entity =self::factory() - * @param bool $transactional If true (default) then the operation is transactional + * @param string $textDate Input date + * @param bool $hasTime If true then it works with date and time + * (instead of date) * - * @return mixed - * @throws Exception + * @return string */ - public static function delete($entity,$transactional=true) { - return self::_delete($entity,$transactional); + public static function dateText2Sql($textDate, $hasTime = true) + { + if (($hasTime)) { + $tmpFormat = strpos($textDate, '.') === false ? self::$dateTimeFormat : self::$dateTimeMicroFormat; + } else { + $tmpFormat = self::$dateFormat; + } + + $tmpDate = DateTime::createFromFormat($tmpFormat, $textDate); + if (!$hasTime && $tmpDate) { + $tmpDate->setTime(0, 0); + } + + return self::dateTimePHP2Sql($tmpDate); // it always returns a date with time. Mysql Ignores it. } /** - * It deletes an entity by the primary key. + * Conver date from php -> mysql + * It always returns a time (00:00:00 if time is empty). it could returns + * microseconds 2010-01-01 00:00:00.00000 * - * @param array $pk =self::factory() - * @param bool $transactional If true (default) then the operation is transactional + * @param DateTime $date * - * @return mixed - * @throws Exception + * @return string */ - public static function deleteById($pk,$transactional=true) { - return self::_deleteById($pk,$transactional); + public static function dateTimePHP2Sql($date) + { + // 31/01/2016 20:20:00 --> 2016-01-31 00:00 + if ($date == null) { + return self::$dateEpoch; + } + if ($date->format('u') !== '000000') { + return $date->format(self::$isoDateTimeMs); + } + + return $date->format(self::$isoDateTime); } - + /** - * Returns an array with the default values (0 for numbers, empty for string, and array|null if recursive) + * Returns the current date(and time) in Text (human) format. Usually, it is d/m/Y H:i:s * - * @param array|null $values =self::factory() - * @param string $recursivePrefix It is the prefix of the recursivity. + * @param bool $hasTime + * @param bool $hasMicroseconds * - * @return array + * @return string + * @throws Exception + * @see PdoOne::$dateTimeFormat */ - public static function factory($values = null, $recursivePrefix = '') { - $recursive=static::getRecursive(); - static::setRecursive(); // reset the recursivity. - $row= {array}; -{linked} - if ($values !== null) { - $row = array_merge($row, $values); - } - return $row; + public static function dateTextNow( + $hasTime = true, + $hasMicroseconds = false + ) + { + $tmpDate = new DateTime(); + if ($hasTime) { + return $tmpDate->format(($hasMicroseconds !== false) ? self::$dateTimeMicroHumanFormat + : self::$dateTimeHumanFormat); + } + return $tmpDate->format(self::$dateHumanFormat); } - + /** - * It returns an empty array with null values and no recursivity. - * @param array|null $values=self::factoryNull() - * - * @return array + * Returns the current (PHP server) date and time in the regular format. (Y-m-d\TH:i:s\Z in long format) + * + * @param bool $hasTime + * @param bool $hasMicroseconds + * + * @return string + * @throws Exception + * @see PdoOne::$dateTimeFormat */ - public static function factoryNull($values=null) { - $row= {array_null}; - if ($values !== null) { - $row = array_merge($row, $values); - } - return $row; + public static function dateNow( + $hasTime = true, + $hasMicroseconds = false + ) + { + $tmpDate = new DateTime(); + if ($hasTime) { + return $tmpDate->format(($hasMicroseconds !== false) ? self::$dateTimeMicroFormat : self::$dateTimeFormat); + } + return $tmpDate->format(self::$dateFormat); } -} -eot; - $lastns = explode('\\', $namespace); - - if ($modelfullClass) { - $arr = explode('\\', $modelfullClass); - $modelClass = end($arr); - $modelUse = true; - } else { - $modelClass = false; - $modelUse = false; + /** + * Returns the current date(and time) in SQL/ISO format. It depends on the type of database. + * + * @param bool $hasTime + * @param bool $hasMicroseconds + * + * @return string + */ + public static function dateSqlNow($hasTime = true, $hasMicroseconds = false) + { + $tmpDate = new DateTime(); + if ($hasTime) { + return $tmpDate->format(($hasMicroseconds !== false) ? self::$isoDateTimeMs : self::$isoDateTime); } - $baseClass = ($baseClass === null) ? end($lastns) : $baseClass; + return $tmpDate->format(self::$isoDate); + } - $fa = func_get_args(); - foreach ($fa as $f => $k) { - if (is_array($k)) { - $fa[$f] = str_replace([' ', "\r\n", "\n"], ['', '', ''], var_export($k, true)); - } else { - $fa[$f] = "'$k'"; - } - } - if ($classRelations === null || !isset($classRelations[$tableName])) { - $className = self::camelize($tableName); - } else { - $className = $classRelations[$tableName]; - } + public static function isCli() + { + return !http_response_code(); + } - $extraColArray = ''; - foreach ($extraCols as $k => $v) { - $extraColArray .= $v . ' as ' . $this->addQuote($k) . ','; - } - $extraColArray = rtrim($extraColArray, ','); + //
- $r = str_replace(array( - '{version}', - '{classname}', - '{exception}', - '{baseclass}', - '{args}', - '{table}', - '{compiled}', - '{namespace}', - '{modelnamespace}', - '{classmodellist}', - '{classmodelfirst}', - '{extracol}' - ), array( - self::VERSION . ' Date generated ' . date('r'), //{version} - $className, // {classname} - ($namespace) ? 'use Exception;' : '', - $baseClass, // {baseclass} - implode(",", $fa), - $tableName, // {table} - _BasePdoOneRepo::BINARYVERSION, // {compiled} - ($namespace) ? "namespace $namespace;" : '', //{namespace} - $modelUse ? "use $modelfullClass;" : '', // {modelnamespace} - $modelUse ? "$modelClass::fromArrayMultiple( self::_toList(\$filter, \$filterValue));" - : 'false; // no model set', // {classmodellist} - $modelUse ? "$modelClass::fromArray(self::_first(\$pk));" : 'false; // no model set' // {classmodelfirst} - , - $extraColArray // {extracol} - ), $r); - $pk = '??'; - $pk = $this->service->getPK($tableName, $pk); - $pkFirst = (is_array($pk) && count($pk) > 0) ? $pk[0] : null; + /** + * @param mixed $v Variable + * + * @return int=[PDO::PARAM_STR,PDO::PARAM_INT,PDO::PARAM_BOOL][$i] + * @test equals PDO::PARAM_STR,(20.3) + * @test equals PDO::PARAM_STR,('hello') + */ + public function getType(&$v) + { + switch (1) { + case (is_float($v)): + case ($v === null): + $vt = PDO::PARAM_STR; + break; + case (is_numeric($v)): + $vt = PDO::PARAM_INT; + break; + case (is_bool($v)): - try { - $relation = $this->getDefTableFK($tableName, false, true); - } catch (Exception $e) { - return 'Error: Unable read fk of table ' . $e->getMessage(); + $vt = PDO::PARAM_INT; + $v = ($v) ? 1 : 0; + break; + case (is_object($v) && $v instanceof DateTime): + $vt = PDO::PARAM_STR; + $v = self::dateTimePHP2Sql($v); + break; + default: + $vt = PDO::PARAM_STR; } - // many to many - /*foreach ($relation as $rel) { - $tableMxM = $rel['reftable']; - $tableFK = $this->getDefTableFK($tableMxM, false, true); + return $vt; + } + + /** + * Run a prepared statement. + *
Example:
+ * $con->runQuery($con->prepare('select * from table')); + * + * @param PDOStatement $stmt PDOStatement + * @param array|null $namedArgument (optional) + * + * @param bool $throwError (default true) if false, then it won't throw an error but it will store the error + * + * @return bool returns true if the operation is correct, otherwise false + * @throws Exception + * @test equals true,$this->pdoOne->runQuery($this->pdoOne->prepare('select + * 1 from dual')) + * @test equals + * [1=>1],$this->pdoOne->select('1')->from('dual')->first(),'it + * must runs' + */ + public function runQuery($stmt, $namedArgument = null, $throwError = true) + { + if (!$this->isOpen) { + $this->throwError("It's not connected to the database", '', $throwError); + return null; } - */ try { - $deps = $this->tableDependency(true); - } catch (Exception $e) { - return 'Error: Unable read table dependencies ' . $e->getMessage(); - } // ["city"]=> {["city_id"]=> "address"} - $after = @$deps[1][$tableName]; - if ($after === null) { - $after = @$deps[1][strtolower($tableName)]; - } - $before = @$deps[2][$tableName]; - if ($before === null) { - $before = @$deps[2][strtolower($tableName)]; + //$namedArgument = ($namedArgument === null) + // ? array_merge($this->setParamAssoc,$this->whereParamAssoc,$this->havingParamAssoc) : $namedArgument; + $r = $stmt->execute($namedArgument); + } catch (Exception $ex) { + $this->throwError($this->databaseType . ':Failed to run query', $this->lastQuery, + ['param' => $this->lastParam, 'error_last' => json_encode(error_get_last())], $throwError, $ex); + return false; } - if (is_array($after) && is_array($before)) { - foreach ($before as $key => $rows) { // $value is [relcol,table] - foreach ($rows as $value) { - $relation[self::$prefixBase . $value[1]] = [ - 'key' => 'ONETOMANY', - 'col' => $key, - 'reftable' => $value[1], - 'refcol' => $value[0] //, ltrim( $value[0],self::$prefixBase) - ]; - } - } + if ($r === false) { + $this->throwError('Exception query ', $this->lastQuery, ['param' => $this->lastParam], $throwError); + return false; } - // converts relations to ONETOONE - foreach ($relation as $k => $rel) { - if ($rel['key'] === 'ONETOMANY') { - $pkref = null; - $pkref = $this->service->getPK($rel['reftable'], $pkref); - if (self::$prefixBase . $pkref[0] === $rel['refcol'] && count($pkref) === 1) { - $relation[$k]['key'] = 'ONETOONE'; - $relation[$k]['refcol'] = ltrim($relation[$k]['refcol'], self::$prefixBase); - } - } - if ($rel['key'] === 'MANYTOONE') { - $pkref = null; - $pkref = $this->service->getPK($rel['reftable'], $pkref); - if ($pkref[0] === $rel['refcol'] && count($pkref) === 1 - && (strcasecmp($k, self::$prefixBase . $pkFirst) === 0) - ) { - // if they are linked by the pks and the pks are only 1. - $relation[$k]['key'] = 'ONETOONE'; - $relation[$k]['col'] = $pkFirst; - $relation[$k]['refcol'] = ltrim($relation[$k]['refcol'], self::$prefixBase); - } - } - } - if ($columnRelations) { - foreach ($relation as $k => $rel) { - if (isset($columnRelations[$k])) { - // parent. - if ($columnRelations[$k] === 'PARENT') { - $relation[$k]['key'] = 'PARENT'; - } elseif ($columnRelations[$k] === 'MANYTOMANY') { - // the table must has 2 primary keys. - $pks = null; - $pks = $this->service->getPK($relation[$k]['reftable'], $pks); - /** @noinspection PhpParamsInspection */ - if ($pks !== false || count($pks) === 2) { - $relation[$k]['key'] = 'MANYTOMANY'; - $refcol2 = (self::$prefixBase . $pks[0] === $relation[$k]['refcol']) ? $pks[1] : $pks[0]; - try { - $defsFK = $this->service->getDefTableFK($relation[$k]['reftable'], false); - } catch (Exception $e) { - return 'Error: Unable read table dependencies ' . $e->getMessage(); - } - try { - $keys2 = $this->service->getDefTableKeys($defsFK[$refcol2]['reftable'], true, - 'PRIMARY KEY'); - } catch (Exception $e) { - return 'Error: Unable read table dependencies' . $e->getMessage(); - } - $relation[$k]['refcol2'] = self::$prefixBase . $refcol2; - if (is_array($keys2)) { - $keys2 = array_keys($keys2); - $relation[$k]['col2'] = $keys2[0]; - } else { - $relation[$k]['col2'] = null; - } - $relation[$k]['table2'] = $defsFK[$refcol2]['reftable']; - } - } - // manytomany - } - } - } - //die(1); - $convertOutput = ''; - $convertInput = ''; - $getDefTable = $this->getDefTable($tableName, $specialConversion); + return true; + } - foreach ($columnRemove as $v) { - unset($getDefTable[$v]); + protected static function fixCsv($value) + { + if (is_numeric($value)) { + return $value; } - //die(1); - - // we forced the conversion but only if it is not specified explicit - - $allColumns = array_merge($getDefTable, $extraCols); // $extraColArray does not has type - - foreach ($allColumns as $kcol => $colDef) { - $type = isset($colDef['type']) ? $colDef['type'] : null; - $conversion = null; - if (isset($columnRelations[$kcol])) { - $conversion = $columnRelations[$kcol]; - if ($type !== null) { - $getDefTable[$kcol]['conversion'] = $conversion; - } else { - $type = 'new column'; - } - } elseif ($type !== null && isset($this->codeClassConversion[$type]) - && $getDefTable[$kcol]['conversion'] === null - ) { - $conversion = $this->codeClassConversion[$type]; - $getDefTable[$kcol]['conversion'] = $conversion; - } + $value = str_replace('"', '""', $value); - if ($conversion !== null) { - if (is_array($conversion)) { - list($input, $output) = $conversion; - } else { - $input = $conversion; - $output = $input; - } + return '"' . $value . '"'; + } - switch ($input) { - case 'encrypt': - $tmp2 = "isset(%s) and %s=self::getPdoOne()->encrypt(%s);"; - break; - case 'decrypt': - $tmp2 = "isset(%s) and %s=self::getPdoOne()->decrypt(%s);"; - break; - case 'datetime3': - $tmp2 = "isset(%s) and %s=PdoOne::dateConvert(%s, 'human', 'sql');"; - break; - case 'datetime4': - $tmp2=''; - //$tmp2 = "isset(%s) and %s=PdoOne::dateConvert(%s, 'sql', 'sql');"; - break; - case 'datetime2': - $tmp2 = "isset(%s) and %s=PdoOne::dateConvert(%s, 'iso', 'sql');"; - break; - case 'datetime': - $tmp2 = "isset(%s) and %s=PdoOne::dateConvert(%s, 'class', 'sql');"; - break; - case 'timestamp': - $tmp2 = "isset(%s) and %s=PdoOne::dateConvert(%s, 'timestamp', 'sql')"; - break; - case 'bool': - $tmp2 = "isset(%s) and %s=(%s) ? 1 : 0;"; - break; - case 'int': - $tmp2 = "isset(%s) and %s=(int)%s;"; - break; - case 'float': - case 'decimal': - $tmp2 = "isset(%s) and %s=(float)%s;"; - break; - default: - if (strpos($input, '%s') !== false) { - $tmp2 = "%s=isset(%s) ? " . $input . " : null;"; - } else { - $tmp2 = '// type ' . $input . ' not defined'; - } - } - switch ($output) { - case 'encrypt': - $tmp = "%s=isset(%s) ? self::getPdoOne()->encrypt(%s) : null;"; - break; - case 'decrypt': - $tmp = "%s=isset(%s) ? self::getPdoOne()->decrypt(%s) : null;"; - break; - case 'datetime3': - $tmp = "%s=isset(%s) ? PdoOne::dateConvert(%s, 'sql', 'human') : null;"; - break; - case 'datetime4': - // sql->sql no conversion - $tmp = ''; - break; - case 'datetime2': - $tmp = "%s=isset(%s) ? PdoOne::dateConvert(%s, 'sql', 'iso') : null;"; - break; - case 'datetime': - $tmp = "%s=isset(%s) ? PdoOne::dateConvert(%s, 'sql', 'class') : null;"; - break; - case 'timestamp': - $tmp = "%s=isset(%s) ? PdoOne::dateConvert(%s, 'sql', 'timestamp') : null;"; - break; - case 'bool': - $tmp = "%s=isset(%s) ? (%s) ? true : false : null;"; - break; - case 'int': - $tmp = "%s=isset(%s) ? (int)%s : null;"; - break; - case 'float': - case 'decimal': - $tmp = "%s=isset(%s) ? (float)%s : null;"; - break; - case null: - $tmp = "!isset(%s) and %s=null; // no conversion"; - break; - default: - if (strpos($output, '%s') !== false) { - $tmp = "%s=isset(%s) ? " . $output . " : null;"; - } else { - $tmp = '// type ' . $output . ' not defined'; - } - } + /** + * @param string $query + * + * @return string + * @throws Exception + */ + public function generateCodeSelect($query) + { + $q = self::splitQuery($query); + $code = '/** @var array $result=array(' . $this->generateCodeArray($query, $query) . ') */' . "\n"; - if ($tmp !== '') { - $convertOutput .= "\t\t" . str_replace('%s', "\$row['$kcol']", $tmp) . "\n"; - $convertInput .= "\t\t" . str_replace('%s', "\$row['$kcol']", $tmp2) . "\n"; + $code .= '$result=$pdo' . "\n"; + foreach ($q as $k => $v) { + if ($v !== null) { + $k2 = str_replace(' by', '', $k); // order by -> order + foreach ($v as $vitem) { + $code .= "\t->{$k2}(\"{$vitem}\")\n"; } - } else { - $tmp = "!isset(%s) and %s=null; // $type"; - $convertOutput .= "\t\t" . str_replace('%s', "\$row['$kcol']", $tmp) . "\n"; - } - } - - $linked = ''; - foreach ($relation as $k => $v) { - $key = $v['key']; - if ($key === 'MANYTOONE') { - //$col = ltrim($v['refcol'], '_'); - $col = ltrim($k, '_'); - $linked .= str_replace(['{_col}', '{refcol}', '{col}'], [$k, $v['refcol'], $col], "\t\tisset(\$row['{_col}']) - and \$row['{_col}']['{refcol}']=&\$row['{col}']; // linked MANYTOONE\n"); } - if ($key === 'ONETOONE') { - //$col = ltrim($v['refcol'], '_'); - //$col = ltrim($k, '_'); - $linked .= str_replace(['{_col}', '{refcol}', '{col}'], [$k, $v['refcol'], $v['col']], "\t\tisset(\$row['{_col}']) - and \$row['{_col}']['{refcol}']=&\$row['{col}']; // linked ONETOONE\n"); - } - } - //$convertOutput.=$linked; - - $convertOutput = rtrim($convertOutput, "\n"); - $convertInput = rtrim($convertInput, "\n"); - - // discard columns - //$identities=$this->getDefTableKeys($tableName,); - $identities = $this->getDefIdentities($tableName); - if (count($identities) > 0) { - $identity = $identities[0]; - } else { - $identity = null; - } - if ($defNoInsert !== null) { - $noInsert = array_merge($identities, $defNoInsert); - } else { - $noInsert = $identities; - } - if ($defNoUpdate !== null) { - $noUpdate = array_merge($identities, $defNoUpdate); - } else { - $noUpdate = array_merge($identities); - } - if ($pk) { - // we never update the primary key. - /** @noinspection AdditionOperationOnArraysInspection */ - $noUpdate += $pk; // it adds and replaces duplicates, indexes are ignored. } + $code .= "\t->toList();\n"; - $relation2 = []; - foreach ($relation as $col => $arr) { - if ($arr['key'] !== 'FOREIGN KEY' && $arr['key'] !== 'PARENT' && $arr['key'] !== 'NONE') { - @$relation2[$arr['key']][] = $col; - } - //if($arr['key']==='MANYTOONE') { - // $relation2[]=$col; - // } - } - - try { - $r = str_replace(array( - '{pk}', - '{identity}', - '{def}', - '{convertoutput}', - '{convertinput}', - '{defname}', - '{defkey}', - '{defnoinsert}', - '{defnoupdate}', - '{deffk}', - '{deffktype}', - '{deffktype2}', - '{array}', - '{array_null}', - '{linked}' - ), array( - self::varExport($pk), - self::varExport($identity), // {identity} - //str_replace(["\n\t\t ", "\n\t\t ],"], ['', '],'], self::varExport($gdf, "\t\t")), // {def} - self::varExport($getDefTable, "\t\t"), // {def} - $convertOutput, // {convertoutput} - $convertInput, // {convertinput} - self::varExport(array_keys($getDefTable), "\t\t"), // {defname} - self::varExport($this->getDefTableKeys($tableName), "\t\t"), // {defkey} - self::varExport($noInsert, "\t\t"), // {defnoinsert} - self::varExport($noUpdate, "\t\t"), // {defnoupdate} - self::varExport($this->getDefTableFK($tableName), "\t\t\t"), //{deffk} - self::varExport($relation, "\t\t"), //{deffktype} - self::varExport($relation2, "\t\t"), //{deffktype2} - str_replace("\n", "\n\t\t", - rtrim($this->generateCodeArray($tableName, null, false, false, true, $classRelations, $relation), - "\n")), - str_replace("\n", "\n\t\t", - rtrim($this->generateCodeArray($tableName, null, true, false, true, $classRelations, $relation), - "\n")), - $linked // {linked} - ), $r); - } catch (Exception $e) { - return "Unable read definition of tables " . $e->getMessage(); - } - - return $r; + return $code; } - /** - * It returns a field, column or table, the quotes defined by the current database type. It doesn't considers points - * or space
+ * It sets conversions depending of the type of data. This method is used together with generateCodeClassAll(). + * This value persists across calls
+ * For example, if we always want to convert tinyint into boolean, then we could use this function + * , instead of specify per each column.
+ * Example:
*
-     * $this->addQuote("aaa"); // [aaa] (sqlserver) `aaa` (mysql)
-     * $this->addQuote("[aaa]"); // [aaa] (sqlserver, unchanged)
+     * $this->parent->generateCodeClassConversions(
+     *      ['datetime'=>'datetime2'
+     *      ,'tinyint'=>'bool' // converts tinyint as boolean
+     *      ,'int'=['int',null] // converts input int as integer, and doesn't convert output int
+     *      ]);
+     * echo $this->parent->generateCodeClassAll('table');
+     * $this->parent->generateCodeClassConversions(); // reset.
      * 
+ * PHP Conversions: + * * - * @param string $txt - * - * @return string - * @see \eftec\PdoOne::addDelimiter to considers points - */ - public function addQuote($txt) - { - if (strlen($txt) < 2) { - return $txt; - } - if ($txt[0] === $this->database_delimiter0 && substr($txt, -1) === $this->database_delimiter1) { - // it is already quoted. - return $txt; - } - return $this->database_delimiter0 . $txt . $this->database_delimiter1; - } - - /** - * It returns a simple array with all the columns that has identities/sequence. - * - * @param string $table - * - * @return array - * @throws Exception - */ - public function getDefIdentities($table) - { - $r = $this->service->getDefTable($table); - $identities = []; - foreach ($r as $k => $v) { - if (stripos($v, $this->database_identityName) !== false) { - $identities[] = $k; - } - } - return $identities; - } - - /** - * If true, then on error, the code thrown an error.
> - * If false, then on error, the the code returns false and logs the errors ($this->errorText). - * - * @param bool $value - * - * @return $this - */ - public function setThrowOnError($value = false) - { - $this->throwOnError = $value; - return $this; - } - - /** - * Flush and disable the internal cache. By default, the internal cache is not used unless it is set. - * - * @param bool $useInternalCache if true then it enables the internal cache. + * @param array $conversion An associative array where the key is the type and the value is the conversion. * - * @see \eftec\PdoOne::setUseInternalCache + * @link https://github.com/EFTEC/PdoOne + * @see \eftec\PdoOne::generateCodeClass + * @see \eftec\PdoOne::setEncryption */ - public function flushInternalCache($useInternalCache = false) + public function generateCodeClassConversions($conversion = []) { - $this->internalCacheCounter = 0; - $this->internalCache = []; - $this->useInternalCache = $useInternalCache; + $this->codeClassConversion = $conversion; } /** @@ -4073,6 +3968,20 @@ public function generateAllClasses( $this->setUseInternalCache($internalCache); return $logs; } + /** + * Flush and disable the internal cache. By default, the internal cache is not used unless it is set. + * + * @param bool $useInternalCache if true then it enables the internal cache. + * + * @see \eftec\PdoOne::setUseInternalCache + */ + public function flushInternalCache($useInternalCache = false) + { + $this->internalCacheCounter = 0; + $this->internalCache = []; + $this->useInternalCache = $useInternalCache; + } + /** * If true then the library will use the internal cache that stores DQL commands.
@@ -4087,1674 +3996,751 @@ public function setUseInternalCache($useInternalCache = true) $this->useInternalCache = $useInternalCache; } - public function generateBaseClass($baseClassName, $namespace, $classes, $modelUse = false) + protected static function splitQuery($query) { - $r = <<<'eot' -$entity) { - foreach($entity as $k=>$class) { - $classes[$id][$k] = $namespace . '\\' . $class; - } - } - */ - $namespace = trim($namespace, '\\'); - - return str_replace([ - '{type}', - '{class}', - '{exception}', - '{namespace}', - '{namespace2}', - '{relations}', - '{modeluse}' - ], [ - $this->databaseType, - $baseClassName, - ($namespace) ? 'use Exception;' : '', // {exception} - ($namespace) ? "namespace $namespace;" : '', // {namespace} - ($namespace) ? "$namespace\\\\" : '', // {namespace2} - $this::varExport($classes), - $modelUse ? 'true' : 'false' // {modeluse} - ], $r); - } - - /** - * @param string $tableName - * @param string $namespace - * @param null $customRelation - * @param null $classRelations - * @param array $specialConversion - * @param null $defNoInsert - * @param null $defNoUpdate - * @param null $baseClass - * @param array $extraColumn - * @param array $columnRemove - * - * @return string|string[] - * @throws Exception - */ - public function generateAbstractModelClass( - $tableName, - $namespace = '', - $customRelation = null, - $classRelations = null, - $specialConversion = [], - $defNoInsert = null, - $defNoUpdate = null, - $baseClass = null, - $extraColumn = [], - $columnRemove = [] - ) - { - $r = <<<'eot' - - * $code=$pdoOne->generateAbstractModelClass({args}); - * - */ -abstract class Abstract{classname} -{ -{fields} - -{fieldsrel} - - - /** - * Abstract{classname} constructor. - * - * @param array|null $array - */ - public function __construct($array=null) - { - if($array===null) { - return; - } - foreach($array as $k=>$v) { - $this->{$k}=$v; - } - } - - // - public static function fromArray($array) { - if($array===null) { - return null; + $result = []; + $parts = [ + 'select', + 'from', + 'inner join', + 'inner join', + 'inner join', + 'inner join', + 'inner join', + 'inner join', + 'left join', + 'left join', + 'left join', + 'left join', + 'left join', + 'left join', + 'right join', + 'right join', + 'right join', + 'right join', + 'right join', + 'right join', + 'where', + 'group by', + 'having', + 'order by', + 'limit', + '*END*', + ]; + $partsRealIndex = [ + 'select', + 'from', + 'innerjoin', + 'innerjoin', + 'innerjoin', + 'innerjoin', + 'innerjoin', + 'innerjoin', + 'left', + 'left', + 'left', + 'left', + 'left', + 'left', + 'right', + 'right', + 'right', + 'right', + 'right', + 'right', + 'where', + 'group', + 'having', + 'order', + 'limit', + '*END*', + ]; + $query = str_replace(array("\r\n", "\n", "\t", ' ', ' '), ' ', + $query); // remove 3 or 2 space and put instead 1 space + $query = ' ' . trim($query, " \t\n\r\0\x0B;") . '*END*'; // we also trim the last ; (if any) + $pfin = 0; + foreach ($parts as $kp => $part) { + $ri = $partsRealIndex[$kp]; + if ($part !== '*END*') { + //$result[$ri] = null; + $pini = stripos($query, $part, $pfin); + if ($pini !== false) { + $pini += strlen($part); + $found = false; + $cp = count($parts); + for ($i = $kp + 1; $i < $cp; $i++) { + $pfin = stripos($query, $parts[$i], $pini); + if ($pfin !== false) { + $found = $pfin; + break; + } + } + if ($found !== false) { + $pfin = $found; + if (!isset($result[$ri])) { + $result[$ri] = []; + } + $result[$ri][] = trim(substr($query, $pini, $pfin - $pini)); + } + } + } } - $obj=new {classname}(); -{fieldsfa} -{fieldsrelfa} - return $obj; - } - - /** - * It converts the current object in an array - * - * @return mixed - */ - public function toArray() { - return static::objectToArray($this); + return $result; } - + /** - * It converts an array of arrays into an array of objects. - * - * @param array|null $array + * @param string $table + * @param null|string $sql + * @param bool $defaultNull + * @param bool $inline + * @param bool $recursive + * @param null|array $classRelations [optional] The relation table=>classname + * @param array $relation [optional] An optional custom relation of columns * - * @return array|null + * @return string + * @throws Exception */ - public static function fromArrayMultiple($array) { - if($array===null) { - return null; - } - $objs=[]; - foreach($array as $v) { - $objs[]=self::fromArray($v); + public function generateCodeArray( + $table, + $sql = null, + $defaultNull = false, + $inline = true, + $recursive = false, + $classRelations = null, + $relation = [] + ) + { + if ($sql === null) { + $sql = 'select * from ' . $this->addDelimiter($table); } - return $objs; - } - // - -} // end class -eot; - //$lastns = explode('\\', $namespace); - //$baseClass = ($baseClass === null) ? end($lastns) : $baseClass; + $query=new PdoOneQuery($this); + $r = $query->toMeta($sql); - $fa = func_get_args(); - foreach ($fa as $f => $k) { - if (is_array($k)) { - $fa[$f] = str_replace([' ', "\r\n", "\n"], ['', '', ''], var_export($k, true)); - } else { - $fa[$f] = "'$k'"; - } - } - if ($classRelations === null || !isset($classRelations[$tableName])) { - $className = self::camelize($tableName); + $ln = ($inline) ? '' : "\n"; + if ($recursive) { + /** @noinspection PhpUnusedLocalVariableInspection */ + list($tables, $after, $before) = $this->tableDependency(true); } else { - $className = $classRelations[$tableName]; + $tables = null; + $after = null; + $before = null; } + $result = '[' . $ln; + $used = []; + $norepeat = []; + foreach ($r as $row) { + $name = $row['name']; + if (!in_array($name, $used, true)) { + if ($defaultNull) { + $default = 'null'; + } else { + $default = $this->typeDict($row); + } + $result .= "'" . $name . "'=>" . $default . ',' . $ln; + if ($recursive) { + if (isset($before[$table][$name])) { + foreach ($before[$table][$name] as $k => $v3) { + if ($v3[1] + && $v3[0][0] !== self::$prefixBase + ) { // before is defined as [colremote,tableremote] + $colName = self::$prefixBase . $v3[1]; + if (!$defaultNull) { + $default = '(in_array($recursivePrefix.\'' . $colName . '\',$recursive,true)) + ? [] + : null'; + } else { + $default = 'null'; + } + if (!in_array($colName, $norepeat)) { + if (isset($relation[$colName])) { + $key = $relation[$colName]['key']; - $r = str_replace(array( - '{version}', - '{classname}', - '{exception}', - '{namespace}' - ), array( - self::VERSION . ' Date generated ' . date('r'), //{version} - $className, // {classname} - ($namespace) ? 'use Exception;' : '', - ($namespace) ? "namespace $namespace;" : '' - ), $r); - $pk = '??'; - $pk = $this->service->getPK($tableName, $pk); - $pkFirst = (is_array($pk) && count($pk) > 0) ? $pk[0] : null; - - try { - $relation = $this->getDefTableFK($tableName, false, true); - } catch (Exception $e) { - return 'Error: Unable read fk of table ' . $e->getMessage(); - } - - try { - $deps = $this->tableDependency(true); - } catch (Exception $e) { - return 'Error: Unable read table dependencies ' . $e->getMessage(); - } // ["city"]=> {["city_id"]=> "address"} - $after = @$deps[1][$tableName]; - if ($after === null) { - $after = @$deps[1][strtolower($tableName)]; - } - $before = @$deps[2][$tableName]; - if ($before === null) { - $before = @$deps[2][strtolower($tableName)]; - } - if (is_array($after) && is_array($before)) { - foreach ($before as $key => $rows) { // $value is [relcol,table] - foreach ($rows as $value) { - $relation['' . self::$prefixBase . $value[1]] = [ - 'key' => 'ONETOMANY', - 'col' => $key, - 'reftable' => $value[1], - 'refcol' => $value[0] - ]; - } - } - } - // converts relations to ONETOONE - foreach ($relation as $k => $rel) { - if ($rel['key'] === 'ONETOMANY') { - $pkref = null; - $pkref = $this->service->getPK($rel['reftable'], $pkref); - if ('' . self::$prefixBase . $pkref[0] === $rel['refcol'] && count($pkref) === 1) { - $relation[$k]['key'] = 'ONETOONE'; - $relation[$k]['col'] = 'xxx1'; - $relation[$k]['refcol'] = ltrim($relation[$k]['refcol'], self::$prefixBase); - } - } - if ($rel['key'] === 'MANYTOONE') { - $pkref = null; - $pkref = $this->service->getPK($rel['reftable'], $pkref); - - if ($pkref[0] === $rel['refcol'] && count($pkref) === 1 - && (strcasecmp($k, '' . self::$prefixBase . $pkFirst) === 0) - ) { - // if they are linked by the pks and the pks are only 1. - $relation[$k]['key'] = 'ONETOONE'; - $relation[$k]['col'] = 'xxx2'; - $relation[$k]['refcol'] = ltrim($relation[$k]['refcol'], self::$prefixBase); - } - } - } - if ($customRelation) { - foreach ($relation as $k => $rel) { - if (isset($customRelation[$k])) { - // parent. - if ($customRelation[$k] === 'PARENT') { - $relation[$k]['key'] = 'PARENT'; - } elseif ($customRelation[$k] === 'MANYTOMANY') { - // the table must has 2 primary keys. - $pks = null; - $pks = $this->service->getPK($relation[$k]['reftable'], $pks); - /** @noinspection PhpParamsInspection */ - if ($pks !== false || count($pks) === 2) { - $relation[$k]['key'] = 'MANYTOMANY'; - $refcol2 = ('' . self::$prefixBase . $pks[0] === $relation[$k]['refcol']) ? $pks[1] - : $pks[0]; - - try { - $defsFK = $this->service->getDefTableFK($relation[$k]['reftable'], false); - } catch (Exception $e) { - return 'Error: Unable read table dependencies ' . $e->getMessage(); + if ($key === 'PARENT') { + $default = 'null'; + } + if ($key === 'ONETOONE' && !$defaultNull) { + if ($classRelations === null + || !isset($classRelations[$relation[$colName]['reftable']]) + ) { + $className = self::camelize($relation[$colName]['reftable']) . 'Repo'; + } else { + $className = $relation[$colName]['reftable']; + } + $default = '(in_array($recursivePrefix.\'' . $colName . '\',$recursive,true)) + ? ' . $className . '::factory(null,$recursivePrefix.\'' . $colName . '\') + : null'; + } + $result .= "'" . $colName . "'=>" . $default . ', /* ' . $key . '! */' . $ln; + $norepeat[] = $colName; + } else { + $result .= "'" . $colName . "'=>" . $default . ', /* onetomany */' . $ln; + $norepeat[] = $colName; + } + } } - try { - $keys2 = $this->service->getDefTableKeys($defsFK[$refcol2]['reftable'], true, - 'PRIMARY KEY'); - } catch (Exception $e) { - return 'Error: Unable read table dependencies' . $e->getMessage(); + } + } + if (@$after[$table][$name]) { + if (!$defaultNull) { + if ($classRelations === null || !isset($classRelations[$after[$table][$name]])) { + $className = self::camelize($after[$table][$name]) . 'Repo'; + } else { + $className = $classRelations[$after[$table][$name]]; } - $relation[$k]['refcol2'] = '' . self::$prefixBase . $refcol2; - if (is_array($keys2)) { - $keys2 = array_keys($keys2); - $relation[$k]['col2'] = $keys2[0]; + $default = '(in_array($recursivePrefix.\'' . self::$prefixBase . $name . '\',$recursive,true)) + ? ' . $className . '::factory(null,$recursivePrefix.\'' . self::$prefixBase . $name . '\') + : null'; + } + if (!in_array($name, $norepeat)) { + $namep = self::$prefixBase . $name; + if (isset($relation[$namep])) { + /*array(5) { + ["key"]=> + string(11) "FOREIGN KEY" + ["refcol"]=> + string(14) "idtablachildPK" + ["reftable"]=> + string(10) "TableChild" + ["extra"]=> + string(0) "" + ["name"]=> + string(26) "FK_TableParent_TableChild1" + }*/ + $key = $relation[$namep]['key']; + if ($key !== 'PARENT') { + // $default = 'null'; + $result .= "'" . $namep . "'=>" . $default . ', /* ' . $key . '!! */' . $ln; + $norepeat[] = $name; + } } else { - $relation[$k]['col2'] = null; + $result .= "'" . $namep . "'=>" . $default . ', /* manytoone */' . $ln; + $norepeat[] = $name; } - $relation[$k]['table2'] = $defsFK[$refcol2]['reftable']; } } - // manytomany - } - } - } - //die(1); - - $gdf = $this->getDefTable($tableName, $specialConversion); - - foreach ($columnRemove as $v) { - unset($gdf[$v]); - } - - $fields = []; - $fieldsb = []; - foreach ($gdf as $varn => $field) { - switch ($field['phptype']) { //binary, date, datetime, decimal,int, string,time, timestamp - case 'binary': - case 'date': - case 'datetime': - case 'decimal': - case 'float': - case 'int': - case 'string': - case 'time': - case 'timestamp': - $fields[] = "\t/** @var " . $field['phptype'] . " \$$varn */\n\tpublic \$$varn;"; - $fieldsb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? \$array['$varn'] : null;"; - break; - } - } - foreach ($extraColumn as $varn => $value) { - $fields[] = "\t/** @var mixed \$$varn extra column: $value */\n\tpublic \$$varn;"; - $fieldsb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? \$array['$varn'] : null;"; - } - $fieldsArr = implode("\n", $fields); - $fieldsbArr = implode("\n", $fieldsb); - - $field2s = []; - $field2sb = []; - foreach ($relation as $varn => $field) { - //$varnclean = ltrim($varn, self::$prefixBase); - switch ($field['key']) { - case 'FOREIGN KEY': - break; - case 'MANYTOONE': - $class = $classRelations[$field['reftable']]; - $field2s[] = "\t/** @var $class \$$varn manytoone */ - public \$$varn;"; - $field2sb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? - \$obj->$varn=$class::fromArray(\$array['$varn']) - : null; // manytoone"; - $col = ltrim($varn, self::$prefixBase); - $rcol = $field['refcol']; - $field2sb[] = "\t\t(\$obj->$varn !== null) - and \$obj->{$varn}->{$rcol}=&\$obj->$col; // linked manytoone"; - break; - case 'MANYTOMANY': - $class = $classRelations[$field['reftable']]; - $field2s[] = "\t/** @var {$class}[] \$$varn manytomany */ - public \$$varn;"; - $field2sb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? - \$obj->$varn=$class::fromArrayMultiple(\$array['$varn']) - : null; // manytomany"; - break; - case 'ONETOMANY': - $class = $classRelations[$field['reftable']]; - $field2s[] = "\t/** @var {$class}[] \$$varn onetomany */ - public \$$varn;"; - $field2sb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? - \$obj->$varn=$class::fromArrayMultiple(\$array['$varn']) - : null; // onetomany"; - break; - case 'ONETOONE': - $class = $classRelations[$field['reftable']]; - $field2s[] = "\t/** @var $class \$$varn onetoone */ - public \$$varn;"; - $field2sb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? - \$obj->$varn=$class::fromArray(\$array['$varn']) - : null; // onetoone"; - - $col = isset($field['col']) ? $field['col'] : $pkFirst; - - $rcol = $field['refcol']; - - $field2sb[] = "\t\t(\$obj->$varn !== null) - and \$obj->{$varn}->{$rcol}=&\$obj->$col; // linked onetoone"; - break; - } - } - - $fields2Arr = implode("\n", $field2s); - $fields2Arrb = implode("\n", $field2sb); - - $r = str_replace(['{fields}', '{fieldsrel}', '{fieldsfa}', '{fieldsrelfa}'], - [$fieldsArr, $fields2Arr, $fieldsbArr, $fields2Arrb], $r); - // return $r; - // die(1); - - if (@count($this->codeClassConversion) > 0) { - // we forced the conversion but only if it is not specified explicit - foreach ($gdf as $k => $colDef) { - $type = $colDef['type']; - if (isset($this->codeClassConversion[$type]) && $gdf[$k]['conversion'] === null) { - $gdf[$k]['conversion'] = $this->codeClassConversion[$type]; } - } - } - - // discard columns - $identities = $this->getDefIdentities($tableName); - if ($defNoInsert !== null) { - $noInsert = array_merge($identities, $defNoInsert); - } else { - $noInsert = $identities; - } - if ($defNoInsert !== null) { - $noUpdate = array_merge($identities, $defNoUpdate); - } else { - $noUpdate = $identities; - } - - try { - $r = str_replace(array( - '{pk}', - '{def}', - '{defname}', - '{defkey}', - '{defnoinsert}', - '{defnoupdate}', - '{deffk}', - '{deffktype}', - '{array}', - '{array_null}' - ), array( - self::varExport($pk), - //str_replace(["\n\t\t ", "\n\t\t ],"], ['', '],'], self::varExport($gdf, "\t\t")), // {def} - self::varExport($gdf, "\t\t"), - self::varExport(array_keys($gdf), "\t\t"), // {defname} - self::varExport($this->getDefTableKeys($tableName), "\t\t"), // {defkey} - self::varExport($noInsert, "\t\t"), // {defnoinsert} - self::varExport($noUpdate, "\t\t"), // {defnoupdate} - self::varExport($this->getDefTableFK($tableName), "\t\t\t"), //{deffk} - self::varExport($relation, "\t\t"), //{deffktype} - str_replace("\n", "\n\t\t", - rtrim($this->generateCodeArray($tableName, null, false, false, true, $classRelations, $relation), - "\n")), - str_replace("\n", "\n\t\t", - rtrim($this->generateCodeArray($tableName, null, true, false, true, $classRelations, $relation), - "\n")) - ), $r); - } catch (Exception $e) { - return "Unable read definition of tables " . $e->getMessage(); - } - - return $r; - } - - /** - * @param string $tableName - * @param string $namespace - * @param null $customRelation - * @param null $classRelations - * @param array $specialConversion - * @param null $defNoInsert - * @param null $defNoUpdate - * @param null $baseClass - * - * @return string|string[] - * @throws Exception - */ - public function generateModelClass( - $tableName, - $namespace = '', - $customRelation = null, - $classRelations = null, - $specialConversion = [], - $defNoInsert = null, - $defNoUpdate = null, - $baseClass = null - ) - { - $r = <<<'eot' - - * $code=$pdoOne->generateCodeClass({args}); - * - */ -class {classname} extends Abstract{classname} -{ - - -} // end class -eot; - //$lastns = explode('\\', $namespace); - //$baseClass = ($baseClass === null) ? end($lastns) : $baseClass; - - $fa = func_get_args(); - foreach ($fa as $f => $k) { - if (is_array($k)) { - $fa[$f] = str_replace([' ', "\r\n", "\n"], ['', '', ''], var_export($k, true)); - } else { - $fa[$f] = "'$k'"; - } - } - if ($classRelations === null || !isset($classRelations[$tableName])) { - $className = self::camelize($tableName); - } else { - $className = $classRelations[$tableName]; - } - - $r = str_replace(array( - '{version}', - '{classname}', - '{exception}', - '{namespace}' - ), array( - self::VERSION . ' Date generated ' . date('r'), //{version} - $className, // {classname} - ($namespace) ? 'use Exception;' : '', - ($namespace) ? "namespace $namespace;" : '' - ), $r); - $pk = '??'; - $pk = $this->service->getPK($tableName, $pk); - $pkFirst = (is_array($pk) && count($pk) > 0) ? $pk[0] : null; - - try { - $relation = $this->getDefTableFK($tableName, false, true); - } catch (Exception $e) { - return 'Error: Unable read fk of table ' . $e->getMessage(); - } - - try { - $deps = $this->tableDependency(true); - } catch (Exception $e) { - return 'Error: Unable read table dependencies ' . $e->getMessage(); - } // ["city"]=> {["city_id"]=> "address"} - $after = @$deps[1][$tableName]; - if ($after === null) { - $after = @$deps[1][strtolower($tableName)]; - } - $before = @$deps[2][$tableName]; - if ($before === null) { - $before = @$deps[2][strtolower($tableName)]; - } - if (is_array($after) && is_array($before)) { - foreach ($before as $key => $rows) { // $value is [relcol,table] - foreach ($rows as $value) { - $relation['' . self::$prefixBase . $value[1]] = [ - 'key' => 'ONETOMANY', - 'col' => $key, - 'reftable' => $value[1], - 'refcol' => $value[0] - ]; - } - } - } - // converts relations to ONETOONE - foreach ($relation as $k => $rel) { - if ($rel['key'] === 'ONETOMANY') { - $pkref = null; - $pkref = $this->service->getPK($rel['reftable'], $pkref); - if ('' . self::$prefixBase . $pkref[0] === $rel['refcol'] && count($pkref) === 1) { - $relation[$k]['key'] = 'ONETOONE'; - $relation[$k]['col'] = 'xxx3'; - $relation[$k]['refcol'] = ltrim($relation[$k]['refcol'], self::$prefixBase); - } - } - if ($rel['key'] === 'MANYTOONE') { - $pkref = null; - $pkref = $this->service->getPK($rel['reftable'], $pkref); - - if ($pkref[0] === $rel['refcol'] && count($pkref) === 1 - && (strcasecmp($k, '' . self::$prefixBase . $pkFirst) === 0) - ) { - // if they are linked by the pks and the pks are only 1. - $relation[$k]['key'] = 'ONETOONE'; - $relation[$k]['col'] = 'xxx4'; - $relation[$k]['refcol'] = ltrim($relation[$k]['refcol'], self::$prefixBase); - } - } - } - if ($customRelation) { - foreach ($relation as $k => $rel) { - if (isset($customRelation[$k])) { - // parent. - if ($customRelation[$k] === 'PARENT') { - $relation[$k]['key'] = 'PARENT'; - } elseif ($customRelation[$k] === 'MANYTOMANY') { - // the table must has 2 primary keys. - $pks = null; - $pks = $this->service->getPK($relation[$k]['reftable'], $pks); - /** @noinspection PhpParamsInspection */ - if ($pks !== false || count($pks) === 2) { - $relation[$k]['key'] = 'MANYTOMANY'; - $refcol2 = ('' . self::$prefixBase . $pks[0] === $relation[$k]['refcol']) ? $pks[1] - : $pks[0]; - - try { - $defsFK = $this->service->getDefTableFK($relation[$k]['reftable'], false); - } catch (Exception $e) { - return 'Error: Unable read table dependencies ' . $e->getMessage(); - } - try { - $keys2 = $this->service->getDefTableKeys($defsFK[$refcol2]['reftable'], true, - 'PRIMARY KEY'); - } catch (Exception $e) { - return 'Error: Unable read table dependencies' . $e->getMessage(); - } - $relation[$k]['refcol2'] = '' . self::$prefixBase . $refcol2; - if (is_array($keys2)) { - $keys2 = array_keys($keys2); - $relation[$k]['col2'] = $keys2[0]; - } else { - $relation[$k]['col2'] = null; - } - $relation[$k]['table2'] = $defsFK[$refcol2]['reftable']; - } - } - // manytomany - } - } - } - //die(1); - - $gdf = $this->getDefTable($tableName, $specialConversion); - $fields = []; - $fieldsb = []; - foreach ($gdf as $varn => $field) { - switch ($field['phptype']) { //binary, date, datetime, decimal,int, string,time, timestamp - case 'binary': - case 'date': - case 'datetime': - case 'decimal': - case 'float': - case 'int': - case 'string': - case 'time': - case 'timestamp': - $fields[] = "\t/** @var " . $field['phptype'] . " \$$varn */\n\tpublic \$$varn;"; - $fieldsb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? \$array['$varn'] : null;"; - break; - } - } - $fieldsArr = implode("\n", $fields); - $fieldsbArr = implode("\n", $fieldsb); - - $field2s = []; - $field2sb = []; - foreach ($relation as $varn => $field) { - //$varnclean = ltrim($varn, self::$prefixBase); - switch ($field['key']) { - case 'FOREIGN KEY': - break; - case 'MANYTOONE': - $class = $classRelations[$field['reftable']]; - $field2s[] = "\t/** @var $class \$$varn manytoone */ - public \$$varn;"; - $field2sb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? - \$obj->$varn=$class::fromArray(\$array['$varn']) - : null; // manytoone"; - break; - case 'MANYTOMANY': - $class = $classRelations[$field['reftable']]; - $field2s[] = "\t/** @var {$class}[] \$$varn manytomany */ - public \$$varn;"; - $field2sb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? - \$obj->$varn=$class::fromArrayMultiple(\$array['$varn']) - : null; // manytomany"; - break; - case 'ONETOMANY': - $class = $classRelations[$field['reftable']]; - $field2s[] = "\t/** @var {$class}[] \$$varn onetomany */ - public \$$varn;"; - $field2sb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? - \$obj->$varn=$class::fromArrayMultiple(\$array['$varn']) - : null; // onetomany"; - break; - case 'ONETOONE': - $class = $classRelations[$field['reftable']]; - $field2s[] = "\t/** @var $class \$$varn onetoone */ - public \$$varn;"; - $field2sb[] = "\t\t\$obj->$varn=isset(\$array['$varn']) ? - \$obj->$varn=$class::fromArray(\$array['$varn']) - : null; // onetoone"; - break; - } - } - - $fields2Arr = implode("\n", $field2s); - $fields2Arrb = implode("\n", $field2sb); - - $r = str_replace(['{fields}', '{fieldsrel}', '{fieldsfa}', '{fieldsrelfa}'], - [$fieldsArr, $fields2Arr, $fieldsbArr, $fields2Arrb], $r); - // return $r; - // die(1); - - if (@count($this->codeClassConversion) > 0) { - // we forced the conversion but only if it is not specified explicit - foreach ($gdf as $k => $colDef) { - $type = $colDef['type']; - if (isset($this->codeClassConversion[$type]) && $gdf[$k]['conversion'] === null) { - $gdf[$k]['conversion'] = $this->codeClassConversion[$type]; - } - } - } - - // discard columns - $identities = $this->getDefIdentities($tableName); - if ($defNoInsert !== null) { - $noInsert = array_merge($identities, $defNoInsert); - } else { - $noInsert = $identities; - } - if ($defNoInsert !== null) { - $noUpdate = array_merge($identities, $defNoUpdate); - } else { - $noUpdate = $identities; - } - - try { - $r = str_replace(array( - '{pk}', - '{def}', - '{defname}', - '{defkey}', - '{defnoinsert}', - '{defnoupdate}', - '{deffk}', - '{deffktype}', - '{array}', - '{array_null}' - ), array( - self::varExport($pk), - //str_replace(["\n\t\t ", "\n\t\t ],"], ['', '],'], self::varExport($gdf, "\t\t")), // {def} - self::varExport($gdf, "\t\t"), - self::varExport(array_keys($gdf), "\t\t"), // {defname} - self::varExport($this->getDefTableKeys($tableName), "\t\t"), // {defkey} - self::varExport($noInsert, "\t\t"), // {defnoinsert} - self::varExport($noUpdate, "\t\t"), // {defnoupdate} - self::varExport($this->getDefTableFK($tableName), "\t\t\t"), //{deffk} - self::varExport($relation, "\t\t"), //{deffktype} - str_replace("\n", "\n\t\t", - rtrim($this->generateCodeArray($tableName, null, false, false, true, $classRelations, $relation), - "\n")), - str_replace("\n", "\n\t\t", - rtrim($this->generateCodeArray($tableName, null, true, false, true, $classRelations, $relation), - "\n")) - ), $r); - } catch (Exception $e) { - return "Unable read definition of tables " . $e->getMessage(); - } - - return $r; - } - - public function generateCodeClassRepo( - $tableClassName, - $namespace = '', - $classRelations = [], - $modelfullClass = '' - ) - { - $r = <<<'eot' - - * $code=$pdoOne->generateCodeClassRepo({args}); - * - */ -class {classname} extends Abstract{classname} -{ - const ME=__CLASS__; - {modelclass} - - -} -eot; - - $fa = func_get_args(); - foreach ($fa as $f => $k) { - if (is_array($k)) { - $fa[$f] = str_replace([' ', "\r\n", "\n"], ['', '', ''], var_export($k, true)); - } else { - $fa[$f] = "'$k'"; - } - } - - if ($modelfullClass) { - $arr = explode('\\', $modelfullClass); - $modelClass = end($arr); - $modelUse = true; - } else { - $modelClass = false; - $modelUse = false; - } - - $r = str_replace(array( - '{version}', - '{classname}', - '{exception}', - '{args}', - '{table}', - '{namespace}', - '{modelnamespace}', - '{modelclass}', - '{modeluse}' - ), array( - self::VERSION . ' Date generated ' . date('r'), // {version} - $classRelations[$tableClassName], // {class} - ($namespace) ? 'use Exception;' : '', - "'" . implode("','", $fa) . "'", // {args} - $tableClassName, //{table} - ($namespace) ? "namespace $namespace;" : '', // {namespace} - $modelfullClass ? "use $modelfullClass;" : '', // {modelnamespace} - $modelClass ? "const MODEL= $modelClass::class;" : '', // {modelclass} - $modelUse ? 'true' : 'false' // {modeluse} - ), $r); - return $r; - } - - /** - * If true then the stack/query builder will not reset the stack (but on error) when it is finished
- * Example:
- *
-     * $this->pdoOne->select('*')->from('missintable')->setNoReset(true)->toList();
-     * // we do something with the stack
-     * $this->pdoOne->builderReset(true); // reset the stack manually
-     * 
- * - * @param bool $noReset - * - * @return $this - */ - public function setNoReset($noReset = true) - { - $this->noReset = $noReset; - return $this; - } - - /** - * It returns an uniqued uid ('sha256' or the value defined in PdoOneEncryption::$hashType) based in all the - * parameters of the query (select,from,where,parameters,group,recursive,having,limit,distinct,order,etc.) and - * optionally in an extra value - * - * @param mixed|null $extra [optional] If we want to add an extra value to the UID generated - * @param string $prefix A prefix added to the UNID generated. - * - * @return string - * @see \eftec\PdoOneEncryption::$hashType - */ - public function buildUniqueID($extra = null, $prefix = '') - { - // set and setparam are not counted - $all = [ - $this->select, - $this->from, - $this->where, - $this->whereParamAssoc, - $this->havingParamAssoc, - // $this->setParamAssoc, - //$this->whereParamValue, - $this->group, - $this->recursive, - $this->having, - $this->limit, - $this->distinct, - $this->order, - $extra - ]; - return $prefix . hash($this->encryption->hashType, json_encode($all)); - } - - /** - * It sets conversions depending of the type of data. This method is used together with generateCodeClassAll(). - * This value persists across calls
- * For example, if we always want to convert tinyint into boolean, then we could use this function - * , instead of specify per each column.
- * Example:
- *
-     * $this->generateCodeClassConversions(
-     *      ['datetime'=>'datetime2'
-     *      ,'tinyint'=>'bool' // converts tinyint as boolean
-     *      ,'int'=['int',null] // converts input int as integer, and doesn't convert output int
-     *      ]);
-     * echo $this->generateCodeClassAll('table');
-     * $this->generateCodeClassConversions(); // reset.
-     * 
- * PHP Conversions: - * - * - * @param array $conversion An associative array where the key is the type and the value is the conversion. - * - * @link https://github.com/EFTEC/PdoOne - * @see \eftec\PdoOne::generateCodeClass - * @see \eftec\PdoOne::setEncryption - */ - public function generateCodeClassConversions($conversion = []) - { - $this->codeClassConversion = $conversion; - } - - public function render() - { - if ($this->logLevel) { - ob_clean(); - } - - if (!$this->logLevel) { - $web = <<<'LOGS' - - - - - - PdoOne Login Screen - - -LOGS; - $web .= $this->bootstrapcss(); - $web .= <<<'LOGS' - - - -
-
-
-
-
-
-
-

Login Screen

-
-
-
-
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
- -
-
-
-
-
-
-
-
-
- - - -LOGS; - echo $web; - } else { - $web = <<<'TEM1' - - - - - - PdoOne {{version}} - - - -TEM1; - $web .= $this->bootstrapcss(); - $web .= <<<'TEM1' - - - -
-
-
-
-
-
-
-

PdoOne {{version}}.

-
-
-
-
-
- -     - -
-
- -
-
- -
-
- - Examples: mysql, sqlsrv -
-
- - -
-
- -
-
- - Examples: 127.0.0.1 or (local)\sqlexpress -
-
- - -
-
- -
-
- - Examples: root, sa -
-
- - -
-
- -
-
- - Examples: abc.123, 12345 (note: the password is visible) -
-
- - -
-
- -
-
- - Examples: sakila, contoso, adventureworks -
-
- - -
-
- -
-
- - - Examples: select * from table , tablename -
-
- - -
-
- -
-
- - - Examples: classcode,selectcode,arraycode,csv,json -
-
- - -
-
- -
-
- - Examples: namespace1\namespace2 -
-
- - -
-
- -
-
- -
-
- - - -
-
- -     - -
-
- -
-
- -
-
-
-
-
- - - -TEM1; - - $database = @$_POST['database']; - $server = @$_POST['server']; - $user = @$_POST['user']; - $pwd = @$_POST['pwd']; - $db = @$_POST['db']; - $input = @$_POST['input']; - $output = @$_POST['output']; - $namespace = @$_POST['namespace']; - $button = @$_POST['button']; - $log = ''; - if ($button) { - try { - $log = $this->run($database, $server, $user, $pwd, $db, $input, $output, $namespace); - } catch (Exception $e) { - $log = $e->getMessage(); - } - } - - $web = str_replace('{{version}}', $this::VERSION, $web); - $valid = ['mysql', 'sqlsrv', 'oci']; + } - $web = str_replace(array('{{database}}', '{{server}}', '{{user}}', '{{pwd}}', '{{db}}', '{{input}}'), - array($this->runUtilCombo($valid, $database), $server, $user, $pwd, $db, $input), $web); - $valid = [ - 'classcode', - 'selectcode', - 'createcode', - 'arraycode', - 'csv', - 'json', - ]; - $web = str_replace(array('{{output}}', '{{namespace}}', '{{log}}'), - array($this->runUtilCombo($valid, $output), $namespace, $log), $web); + $used[] = $name; + } + $result .= ']' . $ln; + $result = str_replace(",$ln]", "$ln]", $result); + return $result; + } - $ms = 1; - $web = str_replace('{{ms}}', $ms, $web); - echo $web; + /** + * It returns a simple array with all the columns that has identities/sequence. + * + * @param string $table + * + * @return array + * @throws Exception + */ + public function getDefIdentities($table) + { + $r = $this->service->getDefTable($table); + $identities = []; + foreach ($r as $k => $v) { + if (stripos($v, $this->database_identityName) !== false) { + $identities[] = $k; + } } + return $identities; } + - public function bootstrapcss() + + /** + * Begin a try block. It marks the erroText as empty and it store the value of genError + */ + public function beginTry() { - return << -html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:transparent}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],.input-group-sm input[type=time],input[type=date].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm,input[type=time].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],.input-group-lg input[type=time],input[type=date].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg,input[type=time].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.in{opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;opacity:0;line-break:auto}.tooltip.in{opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} - -BOOTS; + $this->errorText = ''; + $this->isThrow = $this->genError; // this value is deleted when it trigger an error + $this->throwOnErrorB = $this->throwOnError; + $this->throwOnError = false; } + //
+ + // + + + // + // - private function runUtilCombo($array, $select) - { - $r = ''; - foreach ($array as $item) { - /** @noinspection TypeUnsafeComparisonInspection */ - $r .= ""; - } - return $r; - } /** - * It changes default database, schema or user. + * It sets the cache service (optional). * - * @param $dbName + * @param IPdoOneCache $cacheService Instance of an object that implements IPdoOneCache * - * @test void this('travisdb') + * @return $this */ - public function db($dbName) + public function setCacheService($cacheService) { - if (!$this->isOpen) { - return; - } - $this->db = $dbName; - $this->tableDependencyArray = null; - $this->tableDependencyArrayCol = null; - $this->conn1->exec('use ' . $dbName); + $this->cacheService = $cacheService; + return $this; } /** - * returns if the database is in read-only mode or not. + * It stores a cache. This method is used internally by PdoOne.
* - * @return bool - * @test equals false,this(),'the database is read only' + * @param string $uid The unique id. It is generate by sha256 based in the query, parameters, type of query + * and method. + * @param string|string[] $family [optional] It is the family or group of the cache. It could be used to invalidate + * the whole group. For example, to invalidate all the cache related with a table. + * @param mixed|null $data The data to store + * @param null|bool|int $ttl If null then the cache never expires.
+ * If false then we don't use cache.
+ * If int then it is the duration of the cache (in seconds) + * + * @return void. */ - public function readonly() + public function setCache($uid, $family = '', $data = null, $ttl = null) { - return $this->readonly; + if ($family === '*') { + $family = $this->tables; + } + $this->cacheService->setCache($uid, $family, $data, $ttl); } /** - * Alias of PdoOne::connect() + * Invalidate a single cache or a list of cache based in a single uid or in + * a family/group of cache. * - * @param bool $failIfConnected + * @param string|string[] $uid The unique id. It is generate by sha256 (or by $hashtype) + * based in the query, parameters, type + * of query and method. + * @param string|string[] $family [optional] It is the family or group + * of + * the cache. It could be used to + * invalidate the whole group. For + * example, to invalidate all the cache + * related with a table. * - * @throws Exception - * @test exception this(false) - * @see PdoOne::connect() + * @return $this + * @see \eftec\PdoOneEncryption::$hashType */ - public function open($failIfConnected = true) + public function invalidateCache($uid = '', $family = '') { - $this->connect($failIfConnected); + if ($this->cacheService !== null) { + if ($family === '*') { + $family = $this->tables; + } + $this->cacheService->invalidateCache($uid, $family); + } + return $this; } + + //
+ // + /** - * It closes the connection + * Returns the number of affected rows. * - * @test void this() + * @param PDOStatement|null|bool $stmt + * + * @return mixed */ - public function close() + public function affected_rows($stmt = null) { - $this->isOpen = false; - if ($this->conn1 === null) { - return; - } // its already close - - @$this->conn1 = null; - } - - public function getPK($table,$pkDefault=null) { - return $this->service->getPK($table,$pkDefault); + if ($stmt instanceof PDOStatement && !$this->isOpen) { + return $stmt->rowCount(); + } + return $this->affected_rows; // returns previous calculated information } /** - * It returns the next sequence. - * It gets a collision free number if we don't do more than one operation - * every 0.0001 seconds. - * But, if we do 2 or more operations per seconds then, it adds a sequence - * number from - * 0 to 4095 - * So, the limit of this function is 4096 operations per 0.0001 second. - * - * @see \eftec\PdoOne::getSequencePHP It's the same but it uses less - * resources but lacks of a sequence. + * Returns the last inserted identity. * - * @param bool $asFloat - * @param bool $unpredictable - * @param string $sequenceName (optional) the name of the sequence. If - * not then it uses $this->tableSequence + * @param null|string $sequenceName [optional] the name of the sequence * - * @return string . Example string(19) "3639032938181434317" - * @throws Exception + * @return mixed a number or 0 if it is not found */ - public function getSequence( - $asFloat = false, - $unpredictable = false, - $sequenceName = '' - ) + public function insert_id($sequenceName = null) { - $sql = $this->service->getSequence($sequenceName); - $r = $this->runRawQuery($sql); - if ($unpredictable) { - if (PHP_INT_SIZE === 4) { - return $this->encryption->encryptSimple($r[0]['id']); - } - -// $r is always a 32 bit number so it will fail in PHP 32bits - return $this->encryption->encryptInteger($r[0]['id']); - } - if ($asFloat) { - return (float)$r[0]['id']; + if (!$this->isOpen) { + return -1; } - return $r[0]['id']; + return $this->conn1->lastInsertId($sequenceName); } /** - *

This function returns an unique sequence

- * It ensures a collision free number only if we don't do more than one - * operation per 0.0001 second However,it also adds a pseudo random number - * (0-4095) so the chances of collision is 1/4095 (per two operations done - * every 0.0001 second).
It is based on Twitter's Snowflake number - * - * @param bool $unpredictable - * - * @return float - * @see \eftec\PdoOne::getSequence + * @return IPdoOneCache */ - public function getSequencePHP($unpredictable = false) + public function getCacheService() { - $ms = microtime(true); - //$ms=1000; - $timestamp = (double)round($ms * 1000); - $rand = (fmod($ms, 1) * 1000000) % 4096; // 4096= 2^12 It is the millionth of seconds - $calc = (($timestamp - 1459440000000) << 22) + ($this->nodeId << 12) + $rand; - usleep(1); - - if ($unpredictable) { - if (PHP_INT_SIZE === 4) { - return '' . $this->encryption->encryptSimple($calc); - } + return $this->cacheService; + } -// $r is always a 32 bit number so it will fail in PHP 32bits - return '' . $this->encryption->encryptInteger($calc); - } - return '' . $calc; - } + // + // /** - * It uses \eftec\PdoOne::$masks0 and \eftec\PdoOne::$masks1 to flip - * the number, so they are not as predictable. - * This function doesn't add entrophy. However, the generation of Snowflakes - * id - * (getSequence/getSequencePHP) generates its own entrophy. Also, - * both masks0[] and masks1[] adds an extra secrecy. - * - * @param $number + * @param string|int $password

Use a integer if the method is + * INTEGER

+ * @param string $salt

Salt is not used by SIMPLE or + * INTEGER

+ * @param string $encMethod

Example : AES-256-CTR See + * http://php.net/manual/en/function.openssl-get-cipher-methods.php + *

+ *

if SIMPLE then the encryption is + * simplified (generates a short + * result)

+ *

if INTEGER then the encryption is + * even simple (generates an integer)

* - * @return mixed + * @throws Exception + * @test void this('123','somesalt','AES-128-CTR') */ - public function getUnpredictable($number) + public function setEncryption($password, $salt, $encMethod) { - $string = '' . $number; - $maskSize = count($this->masks0); - - for ($i = 0; $i < $maskSize; $i++) { - $init = $this->masks0[$i]; - $end = $this->masks1[$i]; - $tmp = $string[$end]; - $string = substr_replace($string, $string[$init], $end, 1); - $string = substr_replace($string, $tmp, $init, 1); + if (!extension_loaded('openssl')) { + $this->encryption->encEnabled = false; + $this->throwError('OpenSSL not loaded, encryption disabled', ''); + } else { + $this->encryption->encEnabled = true; + $this->encryption->setEncryption($password, $salt, $encMethod); } - - return $string; } /** - * it is the inverse of \eftec\PdoOne::getUnpredictable + * Wrapper of PdoOneEncryption->encrypt * - * @param $number + * @param $data * - * @return mixed - * @see \eftec\PdoOne::$masks0 - * @see \eftec\PdoOne::$masks1 + * @return bool|string + * @see \eftec\PdoOneEncryption::encrypt */ - public function getUnpredictableInv($number) + + public function encrypt($data) { - $maskSize = count($this->masks0); - for ($i = $maskSize - 1; $i >= 0; $i--) { - $init = $this->masks1[$i]; - $end = $this->masks0[$i]; - $tmp = $number[$end]; - $number = substr_replace($number, $number[$init], $end, 1); - $number = substr_replace($number, $tmp, $init, 1); - } + return $this->encryption->encrypt($data); + } - return $number; + public function hash($data) + { + return $this->encryption->hash($data); } /** - * Returns true if the table exists. It uses the default schema ($this->db) + * Wrapper of PdoOneEncryption->decrypt * - * @param string $tableName The name of the table (without schema). + * @param $data * - * @return bool true if the table exist - * @throws Exception + * @return bool|string + * @see \eftec\PdoOneEncryption::decrypt */ - public function tableExist($tableName) + public function decrypt($data) { - return $this->objectExist($tableName); + return $this->encryption->decrypt($data); } - /** - * returns true if the object exists - * Currently only works with table - * - * @param string $objectName - * @param string $type =['table','function','sequence'][$i] The type of the object - * - * @return bool - * @throws Exception - */ - public function objectExist($objectName, $type = 'table') + public function render() { - $query = $this->service->objectExist($type); + if ($this->logLevel) { + ob_clean(); + } + + if (!$this->logLevel) { + $web = <<<'LOGS' + + + + + + PdoOne Login Screen + + +LOGS; + $web .= $this->bootstrapcss(); + $web .= <<<'LOGS' + + + +
+
+
+
+
+
+
+

Login Screen

+
+
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + +LOGS; + echo $web; + } else { + $web = <<<'TEM1' + + + + + + PdoOne {{version}} + + + +TEM1; + $web .= $this->bootstrapcss(); + $web .= <<<'TEM1' + + + +
+
+
+
+
+
+
+

PdoOne {{version}}.

+
+
+
+
+
+ +     + +
+
+ +
+
+ +
+
+ + Examples: mysql, sqlsrv +
+
+ + +
+
+ +
+
+ + Examples: 127.0.0.1 or (local)\sqlexpress +
+
+ + +
+
+ +
+
+ + Examples: root, sa +
+
+ + +
+
+ +
+
+ + Examples: abc.123, 12345 (note: the password is visible) +
+
+ + +
+
+ +
+
+ + Examples: sakila, contoso, adventureworks +
+
+ + +
+
+ +
+
+ + + Examples: select * from table , tablename +
+
+ + +
+
+ +
+
+ - if($this->databaseType==='oci') { - $arr = $this->runRawQuery($query, [$objectName,$this->db]); - } else { - $arr = $this->runRawQuery($query, [$objectName]); - } + Examples: classcode,selectcode,arraycode,csv,json +
+
+ + +
+
+ +
+
+ + Examples: namespace1\namespace2 +
+
+ + +
+
+ +
+
+ +
+
+ - return is_array($arr) && count($arr) > 0; - } - /** - * It returns a list of tables ordered by dependency (from no dependent to - * more dependent)
- * Note:: This operation is not foolproof because the tables could - * have circular reference. - * - * @param int $maxLoop The number of tests. If the sort is - * correct, then it ends as fast as it can. - * @param bool $returnProblems [false] if true then it returns all the - * tables with problem - * @param bool $debugTrace [false] if true then it shows the - * operations done. - * - * @return array List of table. - * @throws Exception - */ - public function tableSorted($maxLoop = 5, $returnProblems = false, $debugTrace = false) - { - list($tables, $after, $before) = $this->tableDependency(); - $tableSorted = []; - // initial load - foreach ($tables as $k => $table) { - $tableSorted[] = $table; - } - $problems = []; - for ($i = 0; $i < $maxLoop; $i++) { - if ($this->reSort($tables, $tableSorted, $after, $before, $problems, $debugTrace)) { - break; - } - } - if ($returnProblems) { - return $problems; - } +
+
+ +     + +
+
- return $tableSorted; - } +
+
+ +
+
+
+
+
+ - /** - * Resort the tableSorted list based in dependencies. - * - * @param array $tables An associative array with the name of the - * tables - * @param array $tableSorted (ref) An associative array with the name - * of the tables - * @param array $after $after[city]=[country,..] - * @param array $before $before[city]=[address] - * @param array $tableProblems (ref) an associative array whtn the name - * of the tables with problem. - * @param bool $debugTrace If true then it shows a debug per - * operation. - * - * @return bool true if the sort is finished and there is nothing wrong. - */ - protected function reSort( - $tables, - &$tableSorted, - $after, - $before, - &$tableProblems, - $debugTrace = false - ) - { - shuffle($tables); - $tableProblems = []; - $nothingWrong = true; - foreach ($tables as $k => $table) { - $pos = array_search($table, $tableSorted); - // search for after in the wrong position - $wrong = false; - $pairProblem = ''; - for ($i = 0; $i < $pos; $i++) { - if (in_array($tableSorted[$i], $before[$table])) { - $wrong = true; - $nothingWrong = false; - $pairProblem = $tableSorted[$i]; - if ($debugTrace) { - echo "reSort: [wrong position] $table ($pos) is after " . $tableSorted[$i] . " ($i)
"; - } - break; - } - } - if ($wrong) { - // the value is already in the list, we start removing it - $cts = count($tableSorted); - for ($i = $pos + 1; $i < $cts; $i++) { - $tableSorted[$i - 1] = $tableSorted[$i]; - } - unset($tableSorted[count($tableSorted) - 1]); // we removed the last element. - // We found the initial position to add. - $pInitial = 0; - foreach ($tableSorted as $k2 => $v2) { - if (in_array($v2, $after[$table])) { - $pInitial = $k2 + 1; - } - } - // we found the last position - $pEnd = count($tableSorted); - foreach ($tableSorted as $k2 => $v2) { - if (in_array($v2, $before[$table])) { - $pEnd = $k2 - 1; - } - } - if ($pEnd < $pInitial) { - $tableProblems[] = $table; - $tableProblems[] = $pairProblem; - if ($debugTrace) { - echo "reSort: $table There is a circular reference (From $pInitial to $pEnd)
"; - } - } - if (isset($tableSorted[$pInitial])) { - if ($debugTrace) { - echo "reSort: moving $table to $pInitial
"; - } - // the space is used, so we stack the values - for ($i = count($tableSorted) - 1; $i >= $pInitial; $i--) { - $tableSorted[$i + 1] = $tableSorted[$i]; - } - $tableSorted[$pInitial] = $table; - } else { - $tableSorted[$pInitial] = $table; + +TEM1; + + $database = @$_POST['database']; + $server = @$_POST['server']; + $user = @$_POST['user']; + $pwd = @$_POST['pwd']; + $db = @$_POST['db']; + $input = @$_POST['input']; + $output = @$_POST['output']; + $namespace = @$_POST['namespace']; + $button = @$_POST['button']; + $log = ''; + if ($button) { + try { + $log = $this->run($database, $server, $user, $pwd, $db, $input, $output, $namespace); + } catch (Exception $e) { + $log = $e->getMessage(); } } - } - return $nothingWrong; - } + $web = str_replace('{{version}}', $this::VERSION, $web); + $valid = ['mysql', 'sqlsrv', 'oci']; - /** - * It returns the statistics (minimum,maximum,average,sum and count) of a - * column of a table - * - * @param string $tableName Name of the table - * @param string $columnName The column name to analyze. - * - * @return array|bool Returns an array of the type - * ['min','max','avg','sum','count'] - * @throws Exception - */ - public function statValue($tableName, $columnName) - { - $query = "select min($columnName) min - ,max($columnName) max - ,avg($columnName) avg - ,sum($columnName) sum - ,count($columnName) count - from $tableName"; + $web = str_replace(array('{{database}}', '{{server}}', '{{user}}', '{{pwd}}', '{{db}}', '{{input}}'), + array($this->runUtilCombo($valid, $database), $server, $user, $pwd, $db, $input), $web); + $valid = [ + 'classcode', + 'selectcode', + 'createcode', + 'arraycode', + 'csv', + 'json', + ]; + $web = str_replace(array('{{output}}', '{{namespace}}', '{{log}}'), + array($this->runUtilCombo($valid, $output), $namespace, $log), $web); - return $this->runRawQuery($query); + $ms = 1; + + $web = str_replace('{{ms}}', $ms, $web); + echo $web; + } } - /** - * Returns the columns of a table - * - * @param string $tableName The name of the table. - * - * @return array|bool=['colname','coltype','colsize','colpres','colscale','iskey','isidentity','isnullable'] - * @throws Exception - */ - public function columnTable($tableName) + public function bootstrapcss() { - $query = $this->service->columnTable($tableName); - - return $this->runRawQuery($query); + return << +html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:transparent}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],.input-group-sm input[type=time],input[type=date].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm,input[type=time].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],.input-group-lg input[type=time],input[type=date].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg,input[type=time].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.in{opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;opacity:0;line-break:auto}.tooltip.in{opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} + +BOOTS; } - /** - * Returns all the foreign keys (and relation) of a table - * - * @param string $tableName The name of the table. - * - * @return array|bool - * @throws Exception - */ - public function foreignKeyTable($tableName) + private function runUtilCombo($array, $select) { - $query = $this->service->foreignKeyTable($tableName); + $r = ''; + foreach ($array as $item) { + /** @noinspection TypeUnsafeComparisonInspection */ + $r .= ""; + } - return $this->runRawQuery($query); + return $r; } + /** * It drops a table. It ises the method $this->drop(); * @@ -5972,505 +4958,495 @@ public function createFK($tableName, $definition) } /** - * Returns true if the sql starts with "select " or with "show ". + * It changes default database, schema or user. * - * @param string $sql The query + * @param $dbName * - * @return bool + * @test void this('travisdb') */ - public function isQuery($sql) + public function db($dbName) { - $sql = trim($sql); - - return (stripos($sql, 'select ') === 0 || stripos($sql, 'show ') === 0); + if (!$this->isOpen) { + return; + } + $this->db = $dbName; + $this->tableDependencyArray = null; + $this->tableDependencyArrayCol = null; + $this->conn1->exec('use ' . $dbName); } - /** @noinspection TypeUnsafeComparisonInspection */ - public function filterKey($condition, $columns, $returnSimple) + /** + * returns if the database is in read-only mode or not. + * + * @return bool + * @test equals false,this(),'the database is read only' + */ + public function readonly() { - if ($condition === null) { - // no filter. - return $columns; - } - $result = []; - foreach ($columns as $key => $col) { - if ($returnSimple) { - if ($col == $condition) { - $result[$key] = $col; - } - } elseif ($col['key'] == $condition) { - $result[$key] = $col; - } - } - - return $result; + return $this->readonly; } /** - * It generates a query for "count". It is a macro of select() - *
Example:
- *
-     * ->count('')->from('table')->firstScalar() // select count(*) from
-     * table
- * ->count('from table')->firstScalar() // select count(*) from table
- * ->count('from table where condition=1')->firstScalar() // select count(*) - * from table where condition=1
- * ->count('from table','col')->firstScalar() // select count(col) from - * table
- *
+ * Alias of PdoOne::connect() * - * @param string|null $sql [optional] - * @param string $arg [optional] + * @param bool $failIfConnected * - * @return PdoOne + * @throws Exception + * @test exception this(false) + * @see PdoOne::connect() */ - public function count($sql = '', $arg = '*') + public function open($failIfConnected = true) { - return $this->_aggFn('count', $sql, $arg); + $this->connect($failIfConnected); } - private function _aggFn($method, $sql = '', $arg = '') + /** + * It closes the connection + * + * @test void this() + */ + public function close() { - if ($arg === '') { - $arg = $sql; // if the argument is empty then it uses sql as argument - $sql = ''; // and it lefts sql as empty - } - if ($arg === '*' || $this->databaseType !== 'sqlsrv') { - return $this->select("select $method($arg) $sql"); - } + $this->isOpen = false; + if ($this->conn1 === null) { + return; + } // its already close + + @$this->conn1 = null; + } - return $this->select("select $method(cast($arg as decimal)) $sql"); + public function getPK($table,$pkDefault=null) { + return $this->service->getPK($table,$pkDefault); } /** - * It generates a query for "sum". It is a macro of select() - *
Example:
- * ->sum('from table','col')->firstScalar() // select sum(col) from - * table
- * ->sum('col')->from('table')->firstScalar() // select sum(col) from - * table
- * ->sum('','col')->from('table')->firstScalar() // select sum(col) from - * table
+ * It returns the next sequence. + * It gets a collision free number if we don't do more than one operation + * every 0.0001 seconds. + * But, if we do 2 or more operations per seconds then, it adds a sequence + * number from + * 0 to 4095 + * So, the limit of this function is 4096 operations per 0.0001 second. * - * @param string $sql [optional] it could be the name of column or part - * of the query ("from table..") - * @param string $arg [optiona] it could be the name of the column + * @see \eftec\PdoOne::getSequencePHP It's the same but it uses less + * resources but lacks of a sequence. * - * @return PdoOne + * @param bool $asFloat + * @param bool $unpredictable + * @param string $sequenceName (optional) the name of the sequence. If + * not then it uses $this->tableSequence + * + * @return string . Example string(19) "3639032938181434317" + * @throws Exception */ - public function sum($sql = '', $arg = '') + public function getSequence( + $asFloat = false, + $unpredictable = false, + $sequenceName = '' + ) { - return $this->_aggFn('sum', $sql, $arg); + $sql = $this->service->getSequence($sequenceName); + $r = $this->runRawQuery($sql); + if ($unpredictable) { + if (PHP_INT_SIZE === 4) { + return $this->encryption->encryptSimple($r[0]['id']); + } + +// $r is always a 32 bit number so it will fail in PHP 32bits + return $this->encryption->encryptInteger($r[0]['id']); + } + if ($asFloat) { + return (float)$r[0]['id']; + } + + return $r[0]['id']; } /** - * It generates a query for "min". It is a macro of select() - *
Example:
- * ->min('from table','col')->firstScalar() // select min(col) from - * table
- * ->min('col')->from('table')->firstScalar() // select min(col) from - * table
- * ->min('','col')->from('table')->firstScalar() // select min(col) from - * table
+ *

This function returns an unique sequence

+ * It ensures a collision free number only if we don't do more than one + * operation per 0.0001 second However,it also adds a pseudo random number + * (0-4095) so the chances of collision is 1/4095 (per two operations done + * every 0.0001 second).
It is based on Twitter's Snowflake number * - * @param string $sql - * @param string $arg + * @param bool $unpredictable * - * @return PdoOne + * @return float + * @see \eftec\PdoOne::getSequence */ - public function min($sql = '', $arg = '') + public function getSequencePHP($unpredictable = false) { - return $this->_aggFn('min', $sql, $arg); + $ms = microtime(true); + //$ms=1000; + $timestamp = (double)round($ms * 1000); + $rand = (fmod($ms, 1) * 1000000) % 4096; // 4096= 2^12 It is the millionth of seconds + $calc = (($timestamp - 1459440000000) << 22) + ($this->nodeId << 12) + $rand; + usleep(1); + + if ($unpredictable) { + if (PHP_INT_SIZE === 4) { + return '' . $this->encryption->encryptSimple($calc); + } + +// $r is always a 32 bit number so it will fail in PHP 32bits + return '' . $this->encryption->encryptInteger($calc); + } + + return '' . $calc; } /** - * It generates a query for "max". It is a macro of select() - *
Example:
- * ->max('from table','col')->firstScalar() // select max(col) from - * table
- * ->max('col')->from('table')->firstScalar() // select max(col) from - * table
- * ->max('','col')->from('table')->firstScalar() // select max(col) from - * table
+ * It uses \eftec\PdoOne::$masks0 and \eftec\PdoOne::$masks1 to flip + * the number, so they are not as predictable. + * This function doesn't add entrophy. However, the generation of Snowflakes + * id + * (getSequence/getSequencePHP) generates its own entrophy. Also, + * both masks0[] and masks1[] adds an extra secrecy. * - * @param string $sql - * @param string $arg + * @param $number * - * @return PdoOne + * @return mixed */ - public function max($sql = '', $arg = '') + public function getUnpredictable($number) { - return $this->_aggFn('max', $sql, $arg); + $string = '' . $number; + $maskSize = count($this->masks0); + + for ($i = 0; $i < $maskSize; $i++) { + $init = $this->masks0[$i]; + $end = $this->masks1[$i]; + $tmp = $string[$end]; + $string = substr_replace($string, $string[$init], $end, 1); + $string = substr_replace($string, $tmp, $init, 1); + } + + return $string; } /** - * It generates a query for "avg". It is a macro of select() - *
Example:
- * ->avg('from table','col')->firstScalar() // select avg(col) from - * table
- * ->avg('col')->from('table')->firstScalar() // select avg(col) from - * table
- * ->avg('','col')->from('table')->firstScalar() // select avg(col) from - * table
+ * it is the inverse of \eftec\PdoOne::getUnpredictable * - * @param string $sql - * @param string $arg + * @param $number * - * @return PdoOne + * @return mixed + * @see \eftec\PdoOne::$masks0 + * @see \eftec\PdoOne::$masks1 */ - public function avg($sql = '', $arg = '') + public function getUnpredictableInv($number) { - return $this->_aggFn('avg', $sql, $arg); + $maskSize = count($this->masks0); + for ($i = $maskSize - 1; $i >= 0; $i--) { + $init = $this->masks1[$i]; + $end = $this->masks0[$i]; + $tmp = $number[$end]; + $number = substr_replace($number, $number[$init], $end, 1); + $number = substr_replace($number, $tmp, $init, 1); + } + + return $number; } /** - * Adds a left join to the pipeline. It is possible to chain more than one - * join
- * Example:
- *

-     *      left('table on t1.c1=t2.c2')
-     *      left('table on table.c1=t2.c2').left('table2 on
-     * table1.c1=table2.c2')
-     * 
+ * Returns true if the table exists. It uses the default schema ($this->db) * - * @param string $sql Input SQL query + * @param string $tableName The name of the table (without schema). * - * @return PdoOne - * @test InstanceOf PdoOne::class,this('table2 on table1.t1=table2.t2') + * @return bool true if the table exist + * @throws Exception */ - public function left($sql) + public function tableExist($tableName) { - if ($sql === null) { - return $this; - } - $this->from .= ($sql) ? " left join $sql" : ''; - $this->tables[] = explode(' ', $sql)[0]; - return $this; + return $this->objectExist($tableName); } /** - * Adds a right join to the pipeline. It is possible to chain more than one - * join
- * Example:
- * right('table on t1.c1=t2.c2')
- * right('table on table.c1=t2.c2').right('table2 on - * table1.c1=table2.c2')
+ * returns true if the object exists + * Currently only works with table * - * @param string $sql Input SQL query + * @param string $objectName + * @param string $type =['table','function','sequence'][$i] The type of the object * - * @return PdoOne - * @test InstanceOf PdoOne::class,this('table2 on table1.t1=table2.t2') + * @return bool + * @throws Exception */ - public function right($sql) + public function objectExist($objectName, $type = 'table') { - if ($sql === null) { - return $this; + $query = $this->service->objectExist($type); + + if($this->databaseType==='oci') { + $arr = $this->runRawQuery($query, [$objectName,$this->db]); + } else { + $arr = $this->runRawQuery($query, [$objectName]); } - $this->from .= ($sql) ? " right join $sql" : ''; - $this->tables[] = explode(' ', $sql)[0]; - return $this; + + return is_array($arr) && count($arr) > 0; } /** - * It sets a value into the query (insert or update)
- * Example:
- * ->from("table")->set('field1=?',20),set('field2=?','hello')->insert()
- * ->from("table")->set("type=?",[6])->where("i=1")->update()
- * set("type=?",6) // automatic
- * - * @param string|array $sqlOrArray - * @param array|mixed $param + * It returns a list of tables ordered by dependency (from no dependent to + * more dependent)
+ * Note:: This operation is not foolproof because the tables could + * have circular reference. * + * @param int $maxLoop The number of tests. If the sort is + * correct, then it ends as fast as it can. + * @param bool $returnProblems [false] if true then it returns all the + * tables with problem + * @param bool $debugTrace [false] if true then it shows the + * operations done. * - * @return PdoOne - * @test InstanceOf - * PdoOne::class,this('field1=?,field2=?',[20,'hello']) + * @return array List of table. + * @throws Exception */ - public function set($sqlOrArray, $param = self::NULL) + public function tableSorted($maxLoop = 5, $returnProblems = false, $debugTrace = false) { - if ($sqlOrArray === null) { - return $this; + list($tables, $after, $before) = $this->tableDependency(); + $tableSorted = []; + // initial load + foreach ($tables as $k => $table) { + $tableSorted[] = $table; + } + $problems = []; + for ($i = 0; $i < $maxLoop; $i++) { + if ($this->reSort($tables, $tableSorted, $after, $before, $problems, $debugTrace)) { + break; + } } - if (count($this->where)) { - $this->throwError('method set() must be before where()', 'set'); - return $this; + if ($returnProblems) { + return $problems; } - $this->constructParam2($sqlOrArray, $param, 'set'); - return $this; + return $tableSorted; } /** - * Example:
- *
-     * where( ['field'=>20] ) // associative array (named)
-     * where( ['field=?'=>20] ) // associative array (numeric)
-     * where( ['field=:name'=>20] ) // associative array (named)
-     * where( ['field=:name and field2=:name'=>20] ) // IT DOESN'T WORK
-     * where( ['field'=>[20]] ) // associative array with type defined
-     * where( ['field',20] ) // indexed array automatic type
-     * where (['field',[20]] ) // indexed array type defined
-     * where('field=20') // literal value
-     * where('field=?',[20]) // automatic type
-     * where('field',[20]) // automatic type (it's the same than
-     * where('field=?',[20]) where('field=?', [20] ) // type(i,d,s,b)
-     *      defined where('field=?,field2=?', [20,'hello'] )
-     * where('field=:field,field2=:field2',
-     *      ['field'=>'hello','field2'=>'world'] ) // associative array as value
-     * 
+ * Resort the tableSorted list based in dependencies. * - * @param array|string $where - * @param string|array|int $params - * @param string $type - * @param bool $return - * @param null|string $tablePrefix + * @param array $tables An associative array with the name of the + * tables + * @param array $tableSorted (ref) An associative array with the name + * of the tables + * @param array $after $after[city]=[country,..] + * @param array $before $before[city]=[address] + * @param array $tableProblems (ref) an associative array whtn the name + * of the tables with problem. + * @param bool $debugTrace If true then it shows a debug per + * operation. * - * @return array|null + * @return bool true if the sort is finished and there is nothing wrong. */ - public function constructParam2( - $where, - $params = PdoOne::NULL, - $type = 'where', - $return = false, - $tablePrefix = null + protected function reSort( + $tables, + &$tableSorted, + $after, + $before, + &$tableProblems, + $debugTrace = false ) { - $queryEnd = []; - $named = []; - $pars = []; - - if ($params === self::NULL || $params === null) { - if (is_array($where)) { - $numeric = isset($where[0]); - if ($numeric) { - // numeric - $c = count($where) - 1; - for ($i = 0; $i < $c; $i += 2) { - $v = $where[$i + 1]; - // constructParam2(['field',20]]) - $param = [$this->whereCounter, $v, $this->getType($v), null]; - $queryEnd[] = $where[$i]; - $named[] = '?'; - $this->whereCounter++; - $pars[] = $param; - } - } else { - // named - foreach ($where as $k => $v) { - if (strpos($k, '?') === false) { - if (strpos($k, ':') !== false) { - // "aaa=:aaa" - - $parts = explode(':', $k, 2); - $paramName = ':' . str_replace('.', '_', $parts[1]); - $named[] = $paramName; - } else { - // "aaa" - - $paramName = ':' . str_replace('.', '_', $k); - $named[] = $paramName; - } - } else { - // "aa=?" - $paramName = $this->whereCounter; - $this->whereCounter++; - $named[] = '?'; - } - // constructParam2(['field'=>20]) - $param = [$paramName, $v, $this->getType($v), null]; - $pars[] = $param; - if ($tablePrefix !== null && strpos($k, '.') === false) { - $queryEnd[] = $tablePrefix . '.' . $k; - } else { - $queryEnd[] = $k; - } + shuffle($tables); + $tableProblems = []; + $nothingWrong = true; + foreach ($tables as $k => $table) { + $pos = array_search($table, $tableSorted); + // search for after in the wrong position + $wrong = false; + $pairProblem = ''; + for ($i = 0; $i < $pos; $i++) { + if (in_array($tableSorted[$i], $before[$table])) { + $wrong = true; + $nothingWrong = false; + $pairProblem = $tableSorted[$i]; + if ($debugTrace) { + echo "reSort: [wrong position] $table ($pos) is after " . $tableSorted[$i] . " ($i)
"; } + break; } - } else { - // constructParam2('query=xxx') - $named[] = ''; - $queryEnd[] = $where; } - } else { - // where and params are not empty - if (!is_array($params)) { - $params = [$params]; - } - if (!is_array($where)) { - $queryEnd[] = $where; - $numeric = isset($params[0]); - if ($numeric) { - foreach ($params as $k => $v) { - // constructParam2('name=? and type>?', ['Coca-Cola',12345]); - $named[] = '?'; - $pars[] = [ - $this->whereCounter, - $v, - $this->getType($v), - null - ]; - $this->whereCounter++; - } - } else { - foreach ($params as $k => $v) { - $named[] = $k; - // constructParam2('name=:name and type<:type', ['name'=>'Coca-Cola','type'=>987]);; - $pars[] = [$k, $v, $this->getType($v), null]; - //$paramEnd[]=$param; + if ($wrong) { + // the value is already in the list, we start removing it + $cts = count($tableSorted); + for ($i = $pos + 1; $i < $cts; $i++) { + $tableSorted[$i - 1] = $tableSorted[$i]; + } + unset($tableSorted[count($tableSorted) - 1]); // we removed the last element. + // We found the initial position to add. + $pInitial = 0; + foreach ($tableSorted as $k2 => $v2) { + if (in_array($v2, $after[$table])) { + $pInitial = $k2 + 1; } } - if (count($named) === 0) { - $named[] = '?'; // at least one argument. + // we found the last position + $pEnd = count($tableSorted); + foreach ($tableSorted as $k2 => $v2) { + if (in_array($v2, $before[$table])) { + $pEnd = $k2 - 1; + } } - } else { - // constructParam2([],..); - $numeric = isset($where[0]); - - if ($numeric) { - foreach ($where as $k => $v) { - //$named[] = '?'; - $queryEnd[] = $v; + if ($pEnd < $pInitial) { + $tableProblems[] = $table; + $tableProblems[] = $pairProblem; + if ($debugTrace) { + echo "reSort: $table There is a circular reference (From $pInitial to $pEnd)
"; } - } else { - trigger_error('parameteres not correctly defined'); - /*foreach ($where as $k => $v) { - $named[] = '?'; - $queryEnd[] = $k; - }*/ } - $numeric = isset($params[0]); - if ($numeric) { - foreach ($params as $k => $v) { - //$paramEnd[]=$param; - // constructParam2(['name','type'], ['Coca-Cola',123]); - $named[] = '?'; - $pars[] = [$this->whereCounter, $v, $this->getType($v), null]; - $this->whereCounter++; - //$paramEnd[]=$param; + if (isset($tableSorted[$pInitial])) { + if ($debugTrace) { + echo "reSort: moving $table to $pInitial
"; } - } else { - foreach ($params as $k => $v) { - $named[] = $k; - // constructParam2(['name=:name','type<:type'], ['name'=>'Coca-Cola','type'=>987]);; - $pars[] = [$k, $v, $this->getType($v), null]; - //$paramEnd[]=$param; + // the space is used, so we stack the values + for ($i = count($tableSorted) - 1; $i >= $pInitial; $i--) { + $tableSorted[$i + 1] = $tableSorted[$i]; } + $tableSorted[$pInitial] = $table; + } else { + $tableSorted[$pInitial] = $table; } } } - //echo "
where:"; - $i = -1; + return $nothingWrong; + } - foreach ($queryEnd as $k => $v) { - $i++; + /** + * It returns the statistics (minimum,maximum,average,sum and count) of a + * column of a table + * + * @param string $tableName Name of the table + * @param string $columnName The column name to analyze. + * + * @return array|bool Returns an array of the type + * ['min','max','avg','sum','count'] + * @throws Exception + */ + public function statValue($tableName, $columnName) + { + $query = "select min($columnName) min + ,max($columnName) max + ,avg($columnName) avg + ,sum($columnName) sum + ,count($columnName) count + from $tableName"; - if ($named[$i] !== '' && strpos($v, '?') === false && strpos($v, $named[$i]) === false) { - $v .= '=' . $named[$i]; - $queryEnd[$k] = $v; - } - switch ($type) { - case 'where': - $this->where[] = $v; - break; - case 'having': - $this->having[] = $v; - break; - case 'set': - $this->set[] = $v; - break; - } - } + return $this->runRawQuery($query); + } - switch ($type) { - case 'where': - $this->whereParamAssoc = array_merge($this->whereParamAssoc, $pars); - break; - case 'having': - $this->havingParamAssoc = array_merge($this->havingParamAssoc, $pars); - break; - case 'set': - $this->setParamAssoc = array_merge($this->setParamAssoc, $pars); - break; - } + /** + * Returns the columns of a table + * + * @param string $tableName The name of the table. + * + * @return array|bool=['colname','coltype','colsize','colpres','colscale','iskey','isidentity','isnullable'] + * @throws Exception + */ + public function columnTable($tableName) + { + $query = $this->service->columnTable($tableName); - if ($return) { - return [$queryEnd, $pars]; - } - return null; + return $this->runRawQuery($query); } /** - * It groups by a condition.
- * Example:
- * ->select('col1,count(*)')->from('table')->group('col1')->toList(); + * Returns all the foreign keys (and relation) of a table * - * @param string $sql Input SQL query + * @param string $tableName The name of the table. * - * @return PdoOne - * @test InstanceOf PdoOne::class,this('fieldgroup') + * @return array|bool + * @throws Exception */ - public function group($sql) + public function foreignKeyTable($tableName) { - if ($sql === null) { - return $this; - } - $this->group = ($sql) ? ' group by ' . $sql : ''; + $query = $this->service->foreignKeyTable($tableName); - return $this; + return $this->runRawQuery($query); } /** - * It sets a recursive array.
- * example::
- *
-     * $this->recursive(['field1','field2']);
-     * 
+ * Returns true if the sql starts with "select " or with "show ". * - * @param array|mixed $rec + * @param string $sql The query * - * @return $this + * @return bool */ - public function recursive($rec) + public function isQuery($sql) { - if (is_array($rec)) { - $this->recursive = $rec; - } else { - $this->recursive = [$rec]; + $sql = trim($sql); + + return (stripos($sql, 'select ') === 0 || stripos($sql, 'show ') === 0); + } + + /** @noinspection TypeUnsafeComparisonInspection */ + public function filterKey($condition, $columns, $returnSimple) + { + if ($condition === null) { + // no filter. + return $columns; } - return $this; + $result = []; + foreach ($columns as $key => $col) { + if ($returnSimple) { + if ($col == $condition) { + $result[$key] = $col; + } + } elseif ($col['key'] == $condition) { + $result[$key] = $col; + } + } + + return $result; } + + //
+ // /** - * It gets the recursive array. + * Generates and execute an insert command.
+ * Example:
+ *
+     * insert('table',['col1',10,'col2','hello world']); // simple array: name1,value1,name2,value2..
+     * insert('table',null,['col1'=>10,'col2'=>'hello world']); // definition is obtained from the values
+     * insert('table',['col1'=>10,'col2'=>'hello world']); // definition is obtained from the values
+     * insert('table',['col1','col2'],[10,'hello world']); // definition (binary) and value
+     * insert('table',['col1','col2'],['col1'=>10,'col2'=>'hello world']); // definition declarative array)
+     *      ->set(['col1',10,'col2','hello world'])
+     *      ->from('table')
+     *      ->insert();
+     *
* - * @return array + * @param null|string $tableName + * @param string[]|null $tableDef + * @param string[]|int|null $values + * + * @return mixed Returns the identity (if any) or false if the operation fails. + * @throws Exception */ - public function getRecursive() - { - return $this->recursive; + public function insert( + $tableName = null, + $tableDef = null, + $values = PdoOne::NULL + ) { + $query=new PdoOneQuery($this); + return $query->insert($tableName,$tableDef,$values); } - /** - * It returns true if recursive has some needle.
- * If $this->recursive is '*' then it always returns true. + * It returns an array with the metadata of each columns (i.e. name, type, + * size, etc.) or false if error. + * + * @param null|string $sql If null then it uses the generation of query + * (if any).
if string then get the + * statement of the query * - * @param string $needle - * @param null|array $recursiveArray If null then it uses the recursive array specified by $this->>recursive(); + * @param array $args * - * @return bool + * @return array|bool + * @throws Exception */ - public function hasRecursive($needle, $recursiveArray = null) + public function toMeta($sql = null, $args = []) { - if (count($this->recursive) === 1 && $this->recursive[0] === '*') { - return true; - } - if ($recursiveArray) { - return in_array($needle, $recursiveArray, true); - } - return in_array($needle, $this->recursive, true); + $query=new PdoOneQuery($this); + return $query->toMeta($sql,$args); } - /** * If false then it wont generate an error.
* If true (default), then on error, it behave normally
@@ -6479,252 +5455,141 @@ public function hasRecursive($needle, $recursiveArray = null) * * @param bool $error * - * @return PdoOne + * @return PdoOneQuery * @see \eftec\PdoOne::$errorText */ public function genError($error = false) { - $this->genError = $error; - return $this; + $query=new PdoOneQuery($this); + return $query->genError($error); } - - //
- - // - /** - * Executes the query, and returns the first column of the first row in the - * result set returned by the query. Additional columns or rows are ignored.
- * If value is not found then it returns null.
- * * This method is an end of the chain method, so it clears the method stack
- * Example:
- *
-     * $con->select('*')->from('table')->firstScalar(); // select * from table (first scalar value)
-     * 
+ * It generates a query for "min". It is a macro of select() + *
Example:
+ * ->min('from table','col')->firstScalar() // select min(col) from + * table
+ * ->min('col')->from('table')->firstScalar() // select min(col) from + * table
+ * ->min('','col')->from('table')->firstScalar() // select min(col) from + * table
* - * @param string|null $colName If it's null then it uses the first - * column. + * @param string $sql + * @param string $arg * - * @return mixed|null - * @throws Exception + * @return PdoOneQuery */ - public function firstScalar($colName = null) + public function min($sql = '', $arg = '') { - $rows = null; - $useCache = $this->useCache; // because builderReset cleans this value - if ($useCache !== false) { - $sql = $this->sqlGen(); - $this->uid = hash($this->encryption->hashType, - $sql . PDO::FETCH_ASSOC . serialize($this->whereParamAssoc) . serialize($this->havingParamAssoc) - . 'firstscalar'); - $rows = $this->cacheService->getCache($this->uid, $this->cacheFamily); - if ($rows !== false) { - $this->builderReset(); - - return $rows; - } - } - $this->beginTry(); - /** @var PDOStatement $statement */ - $statement = $this->runGen(false, PDO::FETCH_ASSOC, 'firstscalar', false); - if ($this->endtry() === false) { - return null; - } - $row = null; - if ($statement === false) { - $row = null; - } elseif (!$statement->columnCount()) { - $row = null; - } else { - $row = $statement->fetch(PDO::FETCH_ASSOC); - @$statement->closeCursor(); - $statement = null; - if ($row !== false) { - if ($colName === null) { - $row = reset($row); // first column of the first row - } else { - $row = $row[$colName]; - } - } else { - $row = null; - } - } - if ($this->uid && $useCache !== false) { - // we store the information of the cache. - $this->setCache($this->uid, $this->cacheFamily, $row, $useCache); - } - - return $row; + $query=new PdoOneQuery($this); + return $query->_aggFn('min', $sql, $arg); } - /** - * Returns the last row. It's not recommended. Use instead first() and change the order.
- * This method is an end of the chain method, so it clears the method stack
- * Note: This method could not be efficient because it reads all the values. - * If you can, then use the methods sort()::first()
- * Example:
+ * It generates a query for "count". It is a macro of select() + *
Example:
*
-     * $con->select('*')->from('table')->last(); // select * from table (last scalar value)
+     * ->count('')->from('table')->firstScalar() // select count(*) from
+     * table
+ * ->count('from table')->firstScalar() // select count(*) from table
+ * ->count('from table where condition=1')->firstScalar() // select count(*) + * from table where condition=1
+ * ->count('from table','col')->firstScalar() // select count(col) from + * table
*
* - * @return array|null - * @throws Exception - * @see \eftec\PdoOne::first + * @param string|null $sql [optional] + * @param string $arg [optional] + * + * @return PdoOneQuery */ - public function last() + public function count($sql = '', $arg = '*') { - $useCache = $this->useCache; // because builderReset cleans this value - if ($useCache !== false) { - $sql = $this->sqlGen(); - $this->uid = hash($this->encryption->hashType, - $sql . PDO::FETCH_ASSOC . serialize($this->whereParamAssoc) . serialize($this->havingParamAssoc) - . 'last'); - $rows = $this->cacheService->getCache($this->uid, $this->cacheFamily); - if ($rows !== false) { - $this->builderReset(); - - return $rows; - } - } - $this->beginTry(); - /** @var PDOStatement $statement */ - $statement = $this->runGen(false, PDO::FETCH_ASSOC, 'last', false); - if ($this->endtry() === false) { - return null; - } - $row = null; - if ($statement === false) { - $row = null; - } elseif (!$statement->columnCount()) { - $row = null; - } else { - while ($dummy = $statement->fetch(PDO::FETCH_ASSOC)) { - $row = $dummy; - } - @$statement->closeCursor(); - $statement = null; - } - - if ($this->uid && $useCache !== false) { - // we store the information of the cache. - $this->setCache($this->uid, $this->cacheFamily, $row, $useCache); - } - - return $row; + $query=new PdoOneQuery($this); + return $query->_aggFn('count', $sql, $arg); } - /** - * @return string + * It generates a query for "sum". It is a macro of select() + *
Example:
+ * ->sum('from table','col')->firstScalar() // select sum(col) from + * table
+ * ->sum('col')->from('table')->firstScalar() // select sum(col) from + * table
+ * ->sum('','col')->from('table')->firstScalar() // select sum(col) from + * table
+ * + * @param string $sql [optional] it could be the name of column or part + * of the query ("from table..") + * @param string $arg [optiona] it could be the name of the column + * + * @return PdoOneQuery */ - private function constructSet() + public function sum($sql = '', $arg = '') { - return count($this->set) ? ' set ' . implode(',', $this->set) : ''; + $query=new PdoOneQuery($this); + return $query->_aggFn('sum', $sql, $arg); } - //
- - // /** - * Generates and execute an insert command.
- * Example:
- *
-     * insert('table',['col1',10,'col2','hello world']); // simple array: name1,value1,name2,value2..
-     * insert('table',null,['col1'=>10,'col2'=>'hello world']); // definition is obtained from the values
-     * insert('table',['col1'=>10,'col2'=>'hello world']); // definition is obtained from the values
-     * insert('table',['col1','col2'],[10,'hello world']); // definition (binary) and value
-     * insert('table',['col1','col2'],['col1'=>10,'col2'=>'hello world']); // definition declarative array)
-     *      ->set(['col1',10,'col2','hello world'])
-     *      ->from('table')
-     *      ->insert();
-     *
+ * It generates a query for "max". It is a macro of select() + *
Example:
+ * ->max('from table','col')->firstScalar() // select max(col) from + * table
+ * ->max('col')->from('table')->firstScalar() // select max(col) from + * table
+ * ->max('','col')->from('table')->firstScalar() // select max(col) from + * table
* - * @param null|string $tableName - * @param string[]|null $tableDef - * @param string[]|int|null $values + * @param string $sql + * @param string $arg * - * @return mixed Returns the identity (if any) or false if the operation fails. - * @throws Exception + * @return PdoOneQuery */ - public function insert( - $tableName = null, - $tableDef = null, - $values = self::NULL - ) + public function max($sql = '', $arg = '') { - if ($tableName === null) { - $tableName = $this->from; - } else { - $this->tables[] = $tableName; - } - if ($this->useCache === true) { - $this->invalidateCache('', $this->cacheFamily); - } - if ($tableDef !== null) { - $this->constructParam2($tableDef, $values, 'set'); - } - // using builder. from()->set()->insert() - $errorCause = ''; - if (!$tableName) { - $errorCause = "you can't execute an empty insert() without a from()"; - } - if (count($this->set) === 0) { - $errorCause = "you can't execute an empty insert() without a set()"; - } - if ($errorCause) { - $this->throwError('Insert:' . $errorCause, 'insert'); - return false; - } - //$sql = 'insert into ' . $this->addDelimiter($tableName) . ' (' . implode(',', $col) . ') values(' - // . implode(',', $colT) . ')'; - $sql - = /** @lang text */ - 'insert into ' . $this->addDelimiter($tableName) . ' ' . $this->constructInsert(); - $param = $this->setParamAssoc; - $this->beginTry(); - $this->runRawQuery($sql, $param); - $this->builderReset(true); - if ($this->endtry() === false) { - return false; - } - - return $this->insert_id(); + $query=new PdoOneQuery($this); + return $query->_aggFn('max', $sql, $arg); } /** - * @return string + * It generates a query for "avg". It is a macro of select() + *
Example:
+ * ->avg('from table','col')->firstScalar() // select avg(col) from + * table
+ * ->avg('col')->from('table')->firstScalar() // select avg(col) from + * table
+ * ->avg('','col')->from('table')->firstScalar() // select avg(col) from + * table
+ * + * @param string $sql + * @param string $arg + * + * @return PdoOneQuery */ - private function constructInsert() - { - if (count($this->set)) { - $arr = []; - $val = []; - $first = $this->set[0]; - if (strpos($first, '=') !== false) { - // set([]) - foreach ($this->set as $v) { - $tmp = explode('=', $v); - $arr[] = $tmp[0]; - $val[] = $tmp[1]; - } - $set = '(' . implode(',', $arr) . ') values (' . implode(',', $val) . ')'; - } else { - // set('(a,b,c) values(?,?,?)',[]) - foreach ($this->setParamAssoc as $v) { - $vn = $v[0]; - if ($vn[0] !== ':') { - $vn = ':' . $vn; - } - $val[] = $vn; - } - $set = '(' . implode(',', $this->set) . ') values (' . implode(',', $val) . ')'; - } - } else { - $set = ''; - } - - return $set; + public function avg($sql = '', $arg = '') + { + $query=new PdoOneQuery($this); + return $query->_aggFn('avg', $sql, $arg); + } + /** + * Adds a from for a query. It could be used by select,insert,update and + * delete.
+ * Example:
+ *
+     *      from('table')
+     *      from('table alias')
+     *      from('table1,table2')
+     *      from('table1 inner join table2 on table1.c=table2.c')
+     * 
+ * + * @param string $sql Input SQL query + * + * @return PdoOneQuery + * @test InstanceOf PdoOne::class,this('table t1') + */ + public function from($sql) + { + $query=new PdoOneQuery($this); + return $query->from($sql); } - /** * It allows to insert a declarative array. It uses "s" (string) as * filetype. @@ -6741,28 +5606,9 @@ private function constructInsert() */ public function insertObject($tableName, &$object, $excludeColumn = []) { - $objectCopy = (array)$object; - foreach ($excludeColumn as $ex) { - unset($objectCopy[$ex]); - } - - $id = $this->insert($tableName, $objectCopy); - /** id could be 0,false or null (when it is not generated */ - if ($id) { - $pks = $this->service->getDefTableKeys($tableName, true, 'PRIMARY KEY'); - if ($pks > 0) { - // we update the object because it returned an identity. - $k = array_keys($pks)[0]; // first primary key - if (is_array($object)) { - $object[$k] = $id; - } else { - $object->$k = $id; - } - } - } - return $id; + $query=new PdoOneQuery($this); + return $query->insertObject($tableName, $object, $excludeColumn); } - /** * Delete a row(s) if they exists. * Example: @@ -6783,45 +5629,12 @@ public function insertObject($tableName, &$object, $excludeColumn = []) public function delete( $tableName = null, $tableDefWhere = null, - $valueWhere = self::NULL + $valueWhere = PdoOne::NULL ) { - if ($tableName === null) { - $tableName = $this->from; - } else { - $this->tables[] = $tableName; - } - if ($this->useCache === true) { - $this->invalidateCache('', $this->cacheFamily); - } - // using builder. from()->set()->where()->update() - $errorCause = ''; - if (!$tableName) { - $errorCause = "you can't execute an empty delete() without a from()"; - } - if ($errorCause) { - $this->throwError('Delete:' . $errorCause, ''); - return false; - } - - if ($tableDefWhere !== null) { - $this->constructParam2($tableDefWhere, $valueWhere); - } - - $sql = 'delete from ' . $this->addDelimiter($tableName); - $sql .= $this->constructWhere(); - $param = $this->whereParamAssoc; - - $this->beginTry(); - $stmt = $this->runRawQuery($sql, $param, false); - $this->builderReset(true); - if ($this->endtry() === false) { - return false; - } - - return $this->affected_rows($stmt); + $query=new PdoOneQuery($this); + return $query->delete($tableName, $tableDefWhere, $valueWhere); } - /** * Generate and run an update in the database. *
Example:
@@ -6842,276 +5655,231 @@ public function delete( * @param string[]|null $tableDefWhere * @param string[]|int|null $valueWhere * - * @return mixed - * @throws Exception - */ - public function update( - $tableName = null, - $tableDef = null, - $values = self::NULL, - $tableDefWhere = null, - $valueWhere = self::NULL - ) - { - if ($tableName === null) { - // using builder. from()->set()->where()->update() - $tableName = $this->from; - } else { - $this->tables[] = $tableName; - } - if ($this->useCache === true) { - $this->invalidateCache('', $this->cacheFamily); - } - - if ($tableDef !== null) { - $this->constructParam2($tableDef, $values, 'set'); - } - - if ($tableDefWhere !== null) { - $this->constructParam2($tableDefWhere, $valueWhere); - } - - $errorCause = ''; - - if (!$tableName) { - $errorCause = "you can't execute an empty update() without a from()"; - } - if (count($this->set) === 0) { - $errorCause = "you can't execute an empty update() without a set()"; - } - if ($errorCause) { - $this->throwError('Update:' . $errorCause, 'update'); - return false; - } - - $sql = 'update ' . $this->addDelimiter($tableName); - $sql .= $this->constructSet(); - $sql .= $this->constructWhere(); - $param = array_merge($this->setParamAssoc, $this->whereParamAssoc); // the order matters. - - // $this->builderReset(); - $this->beginTry(); - $stmt = $this->runRawQuery($sql, $param, false); - $this->builderReset(true); - if ($this->endtry() === false) { - return false; - } - return $this->affected_rows($stmt); + * @return mixed + * @throws Exception + */ + public function update( + $tableName = null, + $tableDef = null, + $values = PdoOne::NULL, + $tableDefWhere = null, + $valueWhere = PdoOne::NULL + ) + { + $query=new PdoOneQuery($this); + return $query->update($tableName, $tableDef, $values,$tableDefWhere,$valueWhere); } - - - //
- // - /** - * It sets to use cache for the current pipelines. It is disabled at the end of the pipeline
- * It only works if we set the cacheservice
- * Example
- *
-     * $this->setCacheService($instanceCache);
-     * $this->useCache()->select()..; // The cache never expires
-     * $this->useCache(60)->select()..; // The cache lasts 60 seconds.
-     * $this->useCache(60,'customers')
-     *        ->select()..; // cache associated with customers
-     *                      // it could be invalidated by invalidateCache()
-     * $this->useCache(60,['customers','invoices'])
-     *        ->select()..; // cache associated with customers
-     *                      // it could be invalidated by invalidateCache()
-     * $this->useCache(60,'*')->select('col')
-     *      ->from('table')->toList(); // '*' uses all the table assigned.
-     * 
+ * Adds a right join to the pipeline. It is possible to chain more than one + * join
+ * Example:
+ * right('table on t1.c1=t2.c2')
+ * right('table on table.c1=t2.c2').right('table2 on + * table1.c1=table2.c2')
* - * @param null|bool|int $ttl null then the cache never expires.
- * false then we don't use cache.
- * int then it is the duration of the cache (in seconds) - * @param string|array $family [optional] It is the family or group of the cache. It could be used to - * identify a group of cache to invalidate the whole group (for example - * ,invalidate all cache from a specific table).
- * * If "*" then it uses the tables assigned by from() and join() + * @param string $sql Input SQL query * - * @return $this - * @see \eftec\PdoOne::invalidateCache + * @return PdoOneQuery + * @test InstanceOf PdoOne::class,this('table2 on table1.t1=table2.t2') */ - public function useCache($ttl = 0, $family = '') + public function right($sql) { - if ($this->cacheService === null) { - $ttl = false; - } - $this->cacheFamily = $family; - $this->useCache = $ttl; - - return $this; + $query=new PdoOneQuery($this); + return $query->right($sql); } - /** - * It sets the cache service (optional). + * Adds a left join to the pipeline. It is possible to chain more than one + * join
+ * Example:
+ *
+     *      left('table on t1.c1=t2.c2')
+     *      left('table on table.c1=t2.c2').left('table2 on
+     * table1.c1=table2.c2')
+     * 
* - * @param IPdoOneCache $cacheService Instance of an object that implements IPdoOneCache + * @param string $sql Input SQL query * - * @return $this + * @return PdoOneQuery + * @test InstanceOf PdoOne::class,this('table2 on table1.t1=table2.t2') */ - public function setCacheService($cacheService) + public function left($sql) { - $this->cacheService = $cacheService; - - return $this; + $query=new PdoOneQuery($this); + return $query->left($sql); } - /** - * It stores a cache. This method is used internally by PdoOne.
+ * Example:
+ * where( ['field'=>20] ) // associative array with automatic type + * where( ['field'=>[20]] ) // associative array with type defined + * where( ['field',20] ) // array automatic type + * where (['field',[20]] ) // array type defined + * where('field=20') // literal value + * where('field=?',[20]) // automatic type + * where('field',[20]) // automatic type (it's the same than + * where('field=?',[20]) where('field=?', [20] ) // type(i,d,s,b) + * defined where('field=?,field2=?', [20,'hello'] ) + * where('field=:field,field2=:field2', + * ['field'=>'hello','field2'=>'world'] ) // associative array as value * - * @param string $uid The unique id. It is generate by sha256 based in the query, parameters, type of query - * and method. - * @param string|string[] $family [optional] It is the family or group of the cache. It could be used to invalidate - * the whole group. For example, to invalidate all the cache related with a table. - * @param mixed|null $data The data to store - * @param null|bool|int $ttl If null then the cache never expires.
- * If false then we don't use cache.
- * If int then it is the duration of the cache (in seconds) + * @param string|array $sql Input SQL query or associative/indexed + * array + * @param array|mixed $param Associative or indexed array with the + * conditions. + * @param bool $isHaving if true then it is a HAVING sql commando + * instead of a WHERE. * - * @return void. + * @param null|string $tablePrefix + * + * @return PdoOneQuery + * @see http://php.net/manual/en/mysqli-stmt.bind-param.php for types + * @test InstanceOf + * PdoOne::class,this('field1=?,field2=?',[20,'hello']) */ - public function setCache($uid, $family = '', $data = null, $ttl = null) + public function where($sql, $param = PdoOne::NULL, $isHaving = false, $tablePrefix = null) { - if ($family === '*') { - $family = $this->tables; - } - $this->cacheService->setCache($uid, $family, $data, $ttl); + $query=new PdoOneQuery($this); + return $query->where($sql, $param, $isHaving, $tablePrefix); } - /** - * Invalidate a single cache or a list of cache based in a single uid or in - * a family/group of cache. + * It adds a having to the query builder. + *
Example:
+ * select('*')->from('table')->group('col')->having('field=2') + * having( ['field'=>20] ) // associative array with automatic type + * having( ['field'=>[20]] ) // associative array with type defined + * having( ['field',20] ) // array automatic type + * having(['field',[20]] ) // array type defined + * having('field=20') // literal value + * having('field=?',[20]) // automatic type + * having('field',[20]) // automatic type (it's the same than + * where('field=?',[20]) having('field=?', [20] ) // type(i,d,s,b) + * defined having('field=?,field2=?', [20,'hello'] ) * - * @param string|string[] $uid The unique id. It is generate by sha256 (or by $hashtype) - * based in the query, parameters, type - * of query and method. - * @param string|string[] $family [optional] It is the family or group - * of - * the cache. It could be used to - * invalidate the whole group. For - * example, to invalidate all the cache - * related with a table. + * @param string|array $sql + * @param array|mixed $param * - * @return $this - * @see \eftec\PdoOneEncryption::$hashType + * @return PdoOneQuery + * @see http://php.net/manual/en/mysqli-stmt.bind-param.php for types + * @test InstanceOf + * PdoOne::class,this('field1=?,field2=?',[20,'hello']) */ - public function invalidateCache($uid = '', $family = '') + public function having($sql, $param = PdoOne::NULL) { - if ($this->cacheService !== null) { - if ($family === '*') { - $family = $this->tables; - } - $this->cacheService->invalidateCache($uid, $family); - } - return $this; + $query=new PdoOneQuery($this); + return $query->having($sql, $param); } - - - //
- // - /** - * Returns the number of affected rows. + * It generates an inner join
+ * Example:
+ *
+     *          join('tablejoin on t1.field=t2.field')
+ * join('tablejoin','t1.field=t2.field')
+ *
* - * @param PDOStatement|null|bool $stmt + * @param string $sql Example "tablejoin on table1.field=tablejoin.field" + * @param string $condition * - * @return mixed + * @return PdoOneQuery + * @test InstanceOf PdoOne::class,this('tablejoin on t1.field=t2.field') */ - public function affected_rows($stmt = null) + public function join($sql, $condition = '') { - if ($stmt instanceof PDOStatement && !$this->isOpen) { - return $stmt->rowCount(); - } - return $this->affected_rows; // returns previous calculated information + $query=new PdoOneQuery($this); + return $query->join($sql, $condition); } - /** - * Returns the last inserted identity. + * It groups by a condition.
+ * Example:
+ * ->select('col1,count(*)')->from('table')->group('col1')->toList(); * - * @param null|string $sequenceName [optional] the name of the sequence + * @param string $sql Input SQL query * - * @return mixed a number or 0 if it is not found + * @return PdoOneQuery + * @test InstanceOf PdoOne::class,this('fieldgroup') */ - public function insert_id($sequenceName = null) + public function group($sql) { - if (!$this->isOpen) { - return -1; - } - - return $this->conn1->lastInsertId($sequenceName); + $query=new PdoOneQuery($this); + return $query->group($sql); } - /** - * @return IPdoOneCache + * It adds an "order by" in a query.
+ * Example:
+ *
+     *      ->select("")->order("column")->toList();
+     *      ->select("")->order("col1,col2")->toList();
+     * 
+ * + * @param string $sql Input SQL query + * + * @return PdoOneQuery + * @test InstanceOf PdoOne::class,this('name desc') */ - public function getCacheService() + public function order($sql) { - return $this->cacheService; + $query=new PdoOneQuery($this); + return $query->order($sql); } - - - //
- // - /** - * @param string|int $password

Use a integer if the method is - * INTEGER

- * @param string $salt

Salt is not used by SIMPLE or - * INTEGER

- * @param string $encMethod

Example : AES-256-CTR See - * http://php.net/manual/en/function.openssl-get-cipher-methods.php - *

- *

if SIMPLE then the encryption is - * simplified (generates a short - * result)

- *

if INTEGER then the encryption is - * even simple (generates an integer)

+ * It adds an "limit" in a query. It depends on the type of database
+ * Example:
+ *
+     *      ->select("")->limit("10,20")->toList();
+     * 
+ * + * @param string $sql Input SQL query * + * @return PdoOneQuery * @throws Exception - * @test void this('123','somesalt','AES-128-CTR') + * @test InstanceOf PdoOne::class,this('1,10') */ - public function setEncryption($password, $salt, $encMethod) + public function limit($sql) { - if (!extension_loaded('openssl')) { - $this->encryption->encEnabled = false; - $this->throwError('OpenSSL not loaded, encryption disabled', ''); - } else { - $this->encryption->encEnabled = true; - $this->encryption->setEncryption($password, $salt, $encMethod); - } + $query=new PdoOneQuery($this); + return $query->limit($sql); } - /** - * Wrapper of PdoOneEncryption->encrypt + * Adds a distinct to the query. The value is ignored if the select() is + * written complete.
+ *
+     *      ->select("*")->distinct() // works
+     *      ->select("select *")->distinct() // distinct is ignored.
+     *
* - * @param $data + * @param string $sql Input SQL query * - * @return bool|string - * @see \eftec\PdoOneEncryption::encrypt + * @return PdoOneQuery + * @test InstanceOf PdoOne::class,this() */ - - public function encrypt($data) + public function distinct($sql = 'distinct') { - return $this->encryption->encrypt($data); + $query=new PdoOneQuery($this); + return $query->distinct($sql); } - - public function hash($data) + /** + * It sets a recursive array.
+ * example::
+ *
+     * $this->recursive(['field1','field2']);
+     * 
+ * + * @param array|mixed $rec The fields to load recursively. + * + * @return PdoOneQuery + */ + public function recursive($rec) { - return $this->encryption->hash($data); + $query=new PdoOneQuery($this); + return $query->recursive($rec); } - /** - * Wrapper of PdoOneEncryption->decrypt - * - * @param $data + * It gets the recursive array. * - * @return bool|string - * @see \eftec\PdoOneEncryption::decrypt + * @return array */ - public function decrypt($data) + public function getRecursive() { - return $this->encryption->decrypt($data); + $query=new PdoOneQuery($this); + return $query->getRecursive(); } //
diff --git a/lib/PdoOneQuery.php b/lib/PdoOneQuery.php new file mode 100644 index 0000000..d54254a --- /dev/null +++ b/lib/PdoOneQuery.php @@ -0,0 +1,1892 @@ + + + + + public $select = ''; + public $limit = ''; + public $order = ''; + + /** @var bool if true then builderReset will not reset (unless it is force), if false then it will reset */ + public $noReset = false; + public $uid; + + + + /** @var array */ + public $where = []; + + /** @var array parameters for the set. [paramvar,value,type,size] */ + public $setParamAssoc = []; + + + /** @var array parameters for the where. [paramvar,value,type,size] */ + public $whereParamAssoc = []; + /** @var array parameters for the having. [paramvar,value,type,size] */ + public $havingParamAssoc = []; + + public $whereCounter = 1; + + /** @var array */ + //private $whereParamValue = []; + + /** @var array */ + public $set = []; + public $from = ''; + + public $group = ''; + + public $recursive = []; + + + + /** @var array */ + public $having = []; + + public $distinct = ''; + + + // + + /** + * PdoOneQuery constructor. + * @param PdoOne $parent + */ + public function __construct(PdoOne $parent) + { + $this->parent = $parent; + } + /** + * It returns an array with the metadata of each columns (i.e. name, type, + * size, etc.) or false if error. + * + * @param null|string $sql If null then it uses the generation of query + * (if any).
if string then get the + * statement of the query + * + * @param array $args + * + * @return array|bool + * @throws Exception + */ + public function toMeta($sql = null, $args = []) + { + $uid = false; + if ($sql === null) { + $this->parent->beginTry(); + /** @var PDOStatement $stmt */ + $stmt = $this->runGen(false, PDO::FETCH_ASSOC, 'tometa', $this->parent->genError); + if ($this->endtry() === false) { + return false; + } + } else { + if ($this->parent->useInternalCache) { + $uid = hash($this->parent->encryption->hashType, 'meta:' . $sql . serialize($args)); + if (isset($this->internalCache[$uid])) { + // we have an internal cache, so we will return it. + $this->parent->internalCacheCounter++; + return $this->parent->internalCache[$uid]; + } + } + /** @var PDOStatement $stmt */ + $stmt = $this->parent->runRawQuery($sql, $args, false); + } + if ($stmt === null || $stmt instanceof PDOStatement === false) { + $stmt = null; + + return false; + } + $numCol = $stmt->columnCount(); + $rows = []; + for ($i = 0; $i < $numCol; $i++) { + $rows[] = $stmt->getColumnMeta($i); + } + $stmt = null; + if ($uid !== false) { + $this->parent->internalCache[$uid] = $rows; + } + return $rows; + } + + /** + * Executes the query, and returns the first column of the first row in the + * result set returned by the query. Additional columns or rows are ignored.
+ * If value is not found then it returns null.
+ * * This method is an end of the chain method, so it clears the method stack
+ * Example:
+ *
+     * $con->select('*')->from('table')->firstScalar(); // select * from table (first scalar value)
+     * 
+ * + * @param string|null $colName If it's null then it uses the first + * column. + * + * @return mixed|null + * @throws Exception + */ + public function firstScalar($colName = null) + { + $rows = null; + $useCache = $this->parent->useCache; // because builderReset cleans this value + if ($useCache !== false) { + $sql = $this->sqlGen(); + $this->uid = hash($this->parent->encryption->hashType, + $sql . PDO::FETCH_ASSOC . serialize($this->whereParamAssoc) . serialize($this->havingParamAssoc) + . 'firstscalar'); + $rows = $this->parent->cacheService->getCache($this->uid, $this->parent->cacheFamily); + if ($rows !== false) { + $this->builderReset(); + + return $rows; + } + } + $this->parent->beginTry(); + /** @var PDOStatement $statement */ + $statement = $this->runGen(false, PDO::FETCH_ASSOC, 'firstscalar', false); + if ($this->endtry() === false) { + return null; + } + $row = null; + if ($statement === false) { + $row = null; + } elseif (!$statement->columnCount()) { + $row = null; + } else { + $row = $statement->fetch(PDO::FETCH_ASSOC); + @$statement->closeCursor(); + $statement = null; + if ($row !== false) { + if ($colName === null) { + $row = reset($row); // first column of the first row + } else { + $row = $row[$colName]; + } + } else { + $row = null; + } + } + if ($this->uid && $useCache !== false) { + // we store the information of the cache. + $this->parent->setCache($this->uid, $this->parent->cacheFamily, $row, $useCache); + } + + return $row; + } + + /** + * Returns the last row. It's not recommended. Use instead first() and change the order.
+ * This method is an end of the chain method, so it clears the method stack
+ * Note: This method could not be efficient because it reads all the values. + * If you can, then use the methods sort()::first()
+ * Example:
+ *
+     * $con->select('*')->from('table')->last(); // select * from table (last scalar value)
+     * 
+ * + * @return array|null + * @throws Exception + * @see \eftec\PdoOne::first + */ + public function last() + { + $useCache = $this->parent->useCache; // because builderReset cleans this value + + if ($useCache !== false) { + $sql = $this->sqlGen(); + $this->uid = hash($this->parent->encryption->hashType, + $sql . PDO::FETCH_ASSOC . serialize($this->whereParamAssoc) . serialize($this->havingParamAssoc) + . 'last'); + $rows = $this->parent->cacheService->getCache($this->uid, $this->parent->cacheFamily); + if ($rows !== false) { + $this->builderReset(); + + return $rows; + } + } + $this->parent->beginTry(); + /** @var PDOStatement $statement */ + $statement = $this->runGen(false, PDO::FETCH_ASSOC, 'last', false); + if ($this->endtry() === false) { + return null; + } + $row = null; + if ($statement === false) { + $row = null; + } elseif (!$statement->columnCount()) { + $row = null; + } else { + while ($dummy = $statement->fetch(PDO::FETCH_ASSOC)) { + $row = $dummy; + } + @$statement->closeCursor(); + $statement = null; + } + + if ($this->uid && $useCache !== false) { + // we store the information of the cache. + $this->parent->setCache($this->uid, $this->parent->cacheFamily, $row, $useCache); + } + + return $row; + } + + + /** + * It returns an array of simple columns (not declarative). It uses the + * first column
+ * Example:
+ *
+     * select('select id from table')->toListSimple() // ['1','2','3','4']
+     * 
+ * + * @return array|bool + * @throws Exception + */ + public function toListSimple() + { + $useCache = $this->parent->useCache; // because builderReset cleans this value + $this->parent->beginTry(); + $rows = $this->runGen(true, PDO::FETCH_COLUMN, 'tolistsimple', false); + if ($this->endtry() === false) { + return false; + } + if ($this->uid && $useCache !== false) { + // we store the information of the cache. + $this->parent->setCache($this->uid, $this->parent->cacheFamily, $rows, $useCache); + } + + return $rows; + } + + /** + * It adds a select to the query builder. + *
Example:
+ *
+     * ->select("\*")->from('table') = "select * from table"
+ * ->select(['col1','col2'])->from('table') = "select col1,col2 from + * table"
+ * ->select('col1,col2')->from('table') = "select col1,col2 from + * table"
+ * ->select('select *')->from('table') = "select * from table"
+ * ->select('select * from table') = "select * from table"
+ * ->select('select * from table where id=1') = "select * from table + * where id=1"
+ *
+ * + * @param string|array $sql + * + * @return PdoOneQuery + * @test InstanceOf PdoOne::class,this('select 1 from DUAL') + */ + public function select($sql) + { + if (is_array($sql)) { + $this->select .= implode(', ', $sql); + } elseif ($this->select === '') { + $this->select = $sql; + } else { + $this->select .= ', ' . $sql; + } + + return $this; + } + + /** + * It adds a having to the query builder. + *
Example:
+ * select('*')->from('table')->group('col')->having('field=2') + * having( ['field'=>20] ) // associative array with automatic type + * having( ['field'=>[20]] ) // associative array with type defined + * having( ['field',20] ) // array automatic type + * having(['field',[20]] ) // array type defined + * having('field=20') // literal value + * having('field=?',[20]) // automatic type + * having('field',[20]) // automatic type (it's the same than + * where('field=?',[20]) having('field=?', [20] ) // type(i,d,s,b) + * defined having('field=?,field2=?', [20,'hello'] ) + * + * @param string|array $sql + * @param array|mixed $param + * + * @return PdoOneQuery + * @see http://php.net/manual/en/mysqli-stmt.bind-param.php for types + * @test InstanceOf + * PdoOne::class,this('field1=?,field2=?',[20,'hello']) + */ + public function having($sql, $param = PdoOne::NULL) + { + if ($sql === null) { + return $this; + } + + return $this->where($sql, $param, true); + } + + /** + * Example:
+ * where( ['field'=>20] ) // associative array with automatic type + * where( ['field'=>[20]] ) // associative array with type defined + * where( ['field',20] ) // array automatic type + * where (['field',[20]] ) // array type defined + * where('field=20') // literal value + * where('field=?',[20]) // automatic type + * where('field',[20]) // automatic type (it's the same than + * where('field=?',[20]) where('field=?', [20] ) // type(i,d,s,b) + * defined where('field=?,field2=?', [20,'hello'] ) + * where('field=:field,field2=:field2', + * ['field'=>'hello','field2'=>'world'] ) // associative array as value + * + * @param string|array $sql Input SQL query or associative/indexed + * array + * @param array|mixed $param Associative or indexed array with the + * conditions. + * @param bool $isHaving if true then it is a HAVING sql commando + * instead of a WHERE. + * + * @param null|string $tablePrefix + * + * @return PdoOneQuery + * @see http://php.net/manual/en/mysqli-stmt.bind-param.php for types + * @test InstanceOf + * PdoOne::class,this('field1=?,field2=?',[20,'hello']) + */ + public function where($sql, $param = PdoOne::NULL, $isHaving = false, $tablePrefix = null) + { + if ($sql === null) { + return $this; + } + $this->constructParam2($sql, $param, $isHaving ? 'having' : 'where', false, $tablePrefix); + return $this; + } + + /** + * Returns true if the current query has a "having" or "where" + * + * @param bool $having true it return the number of where
+ * false it returns the number of having + * + * @return bool + */ + public function hasWhere($having = false) + { + if ($having) { + return count($this->having) > 0; + } + + return count($this->where) > 0; + } + + /** + * It adds an "limit" in a query. It depends on the type of database
+ * Example:
+ *
+     *      ->select("")->limit("10,20")->toList();
+     * 
+ * + * @param string $sql Input SQL query + * + * @return PdoOneQuery + * @throws Exception + * @test InstanceOf PdoOne::class,this('1,10') + */ + public function limit($sql) + { + if ($sql === null) { + return $this; + } + $this->limit=$this->parent->service->limit($sql); + + return $this; + } + + /** + * Adds a distinct to the query. The value is ignored if the select() is + * written complete.
+ *
+     *      ->select("*")->distinct() // works
+     *      ->select("select *")->distinct() // distinct is ignored.
+     *
+ * + * @param string $sql Input SQL query + * + * @return PdoOneQuery + * @test InstanceOf PdoOne::class,this() + */ + public function distinct($sql = 'distinct') + { + if ($sql === null) { + return $this; + } + $this->distinct = ($sql) ? $sql . ' ' : ''; + + return $this; + } + + /** + * It returns an associative array where the first value is the key and the + * second is the value
If the second value does not exist then it uses + * the index as value (first value)
+ * Example:
+ *
+     * select('select cod,name from table')->toListKeyValue() //
+     * ['cod1'=>'name1','cod2'=>'name2'] select('select cod,name,ext from
+     * table')->toListKeyValue('|') //
+     * ['cod1'=>'name1|ext1','cod2'=>'name2|ext2']
+     * 
+ * + * @param string|null $extraValueSeparator (optional) It allows to read a + * third value and returns it + * concatenated with the value. + * Example '|' + * + * @return array|bool|null + * @throws Exception + */ + public function toListKeyValue($extraValueSeparator = null) + { + $list = $this->toList(PDO::FETCH_NUM); + if (!is_array($list)) { + return null; + } + $result = []; + foreach ($list as $item) { + if ($extraValueSeparator === null) { + $result[$item[0]] = isset($item[1]) ? $item[1] : $item[0]; + } else { + $result[$item[0]] = (isset($item[1]) ? $item[1] : $item[0]) . $extraValueSeparator . @$item[2]; + } + } + + return $result; + } + + /** + * It returns an declarative array of rows.
+ * If not data is found, then it returns an empty array
+ * This method is an end of the chain method, so it clears the method stack
+ * Example:
+ *
+     * $this->select('select id,name from table')->toList() // [['id'=>'1','name'='john'],['id'=>'2','name'=>'anna']]
+     * $this->select('id,name')
+     *      ->from('table')
+     *      ->where('condition=?',[20])
+     *      ->toList();
+     * 
+ * + * @param int $pdoMode (optional) By default is PDO::FETCH_ASSOC + * + * @return array|bool + * @throws Exception + */ + public function toList($pdoMode = PDO::FETCH_ASSOC) + { + $useCache = $this->parent->useCache; // because builderReset cleans this value + $this->parent->beginTry(); + $rows = $this->runGen(true, $pdoMode, 'tolist', false); + if ($this->endtry() === false) { + return false; + } + if ($this->uid && $useCache !== false) { + // we store the information of the cache. + $this->parent->setCache($this->uid, $this->parent->cacheFamily, $rows, $useCache); + } + return $rows; + } + + /** + * It returns a PDOStatement.
+ * Note: The result is not cached. + * + * @return PDOStatement + * @throws Exception + */ + public function toResult() + { + return $this->runGen(false); + } + + /** + * It returns the first row. If there is not row then it returns false.
+ * This method is an end of the chain method, so it clears the method stack
+ * Example:
+ *
+     *      $con->select('*')->from('table')->first(); // select * from table
+     *      (first value)
+     * 
+ * + * @return array|null|false + * @throws Exception + */ + public function first() + { + $useCache = $this->parent->useCache; // because builderReset cleans this value + $uid = false; + if ($useCache !== false) { + $sql = $this->sqlGen(); + $this->uid = hash($this->parent->encryption->hashType, + $sql . PDO::FETCH_ASSOC . serialize($this->whereParamAssoc) . serialize($this->havingParamAssoc) + . 'firstscalar'); + $rows = $this->parent->cacheService->getCache($this->uid, $this->parent->cacheFamily); + if ($rows !== false) { + $this->builderReset(); + + return $rows; + } + } + if ($this->parent->useInternalCache) { + $sql = (!isset($sql)) ? $this->sqlGen() : $sql; + $allparam = array_merge($this->setParamAssoc, $this->whereParamAssoc, $this->havingParamAssoc); + $uid = hash($this->parent->encryption->hashType, 'first' . $sql . serialize($allparam)); + if (isset($this->parent->internalCache[$uid])) { + // we have an internal cache, so we will return it. + $this->parent->internalCacheCounter++; + $this->builderReset(); + return $this->parent->internalCache[$uid]; + } + } + $this->parent->beginTry(); + /** @var PDOStatement $statement */ + $statement = $this->runGen(false, PDO::FETCH_ASSOC, 'first', false); + if ($this->endtry() === false) { + return null; + } + $row = null; + if ($statement === false) { + $row = null; + } elseif (!$statement->columnCount()) { + $row = null; + } else { + $row = $statement->fetch(PDO::FETCH_ASSOC); + @$statement->closeCursor(); + $statement = null; + } + + if ($this->uid && $useCache !== false) { + // we store the information of the cache. + $this->parent->setCache($this->uid, $this->parent->cacheFamily, $row, $useCache); + } + if ($uid !== false) { + $this->parent->internalCache[$uid] = $row; + } + + return $row; + } + + //
+ + // + + /** + * @return string + */ + private function constructHaving() + { + return count($this->having) ? ' having ' . implode(' and ', $this->having) : ''; + } + + /** + * It ends a try block and throws the error (if any) + * + * @return bool + * @throws Exception + */ + private function endTry() + { + $this->parent->throwOnError = $this->parent->throwOnErrorB; + if ($this->parent->errorText) { + $this->throwErrorChain('endtry:' . $this->parent->errorText, '', '', $this->parent->isThrow); + return false; + } + return true; + } + + + + /** + * If true, then on error, the code thrown an error.
> + * If false, then on error, the the code returns false and logs the errors ($this->parent->errorText). + * + * @param bool $value + * + * @return $this + */ + public function setThrowOnError($value = false) + { + $this->parent->throwOnError = $value; + return $this; + } + + + + /** + * If true then the stack/query builder will not reset the stack (but on error) when it is finished
+ * Example:
+ *
+     * $this->parent->pdoOne->select('*')->from('missintable')->setNoReset(true)->toList();
+     * // we do something with the stack
+     * $this->parent->pdoOne->builderReset(true); // reset the stack manually
+     * 
+ * + * @param bool $noReset + * + * @return $this + */ + public function setNoReset($noReset = true) + { + $this->noReset = $noReset; + return $this; + } + + /** + * It returns an uniqued uid ('sha256' or the value defined in PdoOneEncryption::$hashType) based in all the + * parameters of the query (select,from,where,parameters,group,recursive,having,limit,distinct,order,etc.) and + * optionally in an extra value + * + * @param mixed|null $extra [optional] If we want to add an extra value to the UID generated + * @param string $prefix A prefix added to the UNID generated. + * + * @return string + * @see \eftec\PdoOneEncryption::$hashType + */ + public function buildUniqueID($extra = null, $prefix = '') + { + // set and setparam are not counted + $all = [ + $this->select, + $this->from, + $this->where, + $this->whereParamAssoc, + $this->havingParamAssoc, + // $this->setParamAssoc, + //$this->whereParamValue, + $this->group, + $this->recursive, + $this->having, + $this->limit, + $this->distinct, + $this->order, + $extra + ]; + return $prefix . hash($this->parent->encryption->hashType, json_encode($all)); + } + + /** + * Write a log line for debug, clean the command chain then throw an error + * (if throwOnError==true) + * + * @param string $txt The message to show. + * @param string $txtExtra It's only used if $logLevel>=2. It + * shows an extra message + * @param string|array $extraParam It's only used if $logLevel>=3 It + * shows parameters (if any) + * + * @param bool $throwError if true then it throw error (is enabled). Otherwise it store the error. + * + * @param null|RuntimeException $exception + * + * @see \eftec\PdoOne::$logLevel + */ + public function throwErrorChain($txt, $txtExtra, $extraParam = '', $throwError = true, $exception = null) + { + if ($this->parent->logLevel === 0) { + $txt = 'Error on database'; + } + if ($this->parent->logLevel >= 2) { + $txt .= "\n
extra:[{$txtExtra}]"; + } + if ($this->parent->logLevel >= 2) { + $txt .= "\n
last query:[{$this->parent->lastQuery}]"; + } + if ($this->parent->logLevel >= 3) { + $txt .= "\n
database:" . $this->parent->server . ' - ' . $this->parent->db; + if (is_array($extraParam)) { + foreach ($extraParam as $k => $v) { + if (is_array($v) || is_object($v)) { + $v = json_encode($v); + } + $txt .= "\n
$k:$v"; + } + } else { + $txt .= "\n
Params :[" . $extraParam . "]\n
"; + } + if ($exception !== null) { + $txt .= "\n
message :[" . str_replace("\n", "\n
", $exception->getMessage()) . "]"; + $txt .= "\n
trace :[" . str_replace("\n", "\n
", $exception->getTraceAsString()) . "]"; + $txt .= "\n
code :[" . str_replace("\n", "\n
", $exception->getCode()) . "]\n
"; + } + } + if ($this->parent->getMessages() === null) { + $this->parent->debugFile($txt, 'ERROR'); + } else { + $this->parent->getMessages()->addItem($this->parent->db, $txt); + $this->parent->debugFile($txt, 'ERROR'); + } + $this->parent->errorText = $txt; + + if ($throwError && $this->parent->throwOnError && $this->parent->genError) { + throw new RuntimeException($txt); + } + $this->builderReset(true); // it resets the chain if any. + } + + + /** + * It generates a query for "count". It is a macro of select() + *
Example:
+ *
+     * ->count('')->from('table')->firstScalar() // select count(*) from
+     * table
+ * ->count('from table')->firstScalar() // select count(*) from table
+ * ->count('from table where condition=1')->firstScalar() // select count(*) + * from table where condition=1
+ * ->count('from table','col')->firstScalar() // select count(col) from + * table
+ *
+ * + * @param string|null $sql [optional] + * @param string $arg [optional] + * + * @return PdoOneQuery + */ + public function count($sql = '', $arg = '*') + { + return $this->_aggFn('count', $sql, $arg); + } + + public function _aggFn($method, $sql = '', $arg = '') + { + if ($arg === '') { + $arg = $sql; // if the argument is empty then it uses sql as argument + $sql = ''; // and it lefts sql as empty + } + if ($arg === '*' || $this->parent->databaseType !== 'sqlsrv') { + return $this->select("select $method($arg) $sql"); + } + + return $this->select("select $method(cast($arg as decimal)) $sql"); + } + + /** + * It generates a query for "sum". It is a macro of select() + *
Example:
+ * ->sum('from table','col')->firstScalar() // select sum(col) from + * table
+ * ->sum('col')->from('table')->firstScalar() // select sum(col) from + * table
+ * ->sum('','col')->from('table')->firstScalar() // select sum(col) from + * table
+ * + * @param string $sql [optional] it could be the name of column or part + * of the query ("from table..") + * @param string $arg [optiona] it could be the name of the column + * + * @return PdoOneQuery + */ + public function sum($sql = '', $arg = '') + { + return $this->_aggFn('sum', $sql, $arg); + } + + /** + * It generates a query for "min". It is a macro of select() + *
Example:
+ * ->min('from table','col')->firstScalar() // select min(col) from + * table
+ * ->min('col')->from('table')->firstScalar() // select min(col) from + * table
+ * ->min('','col')->from('table')->firstScalar() // select min(col) from + * table
+ * + * @param string $sql + * @param string $arg + * + * @return PdoOneQuery + */ + public function min($sql = '', $arg = '') + { + return $this->_aggFn('min', $sql, $arg); + } + + /** + * It generates a query for "max". It is a macro of select() + *
Example:
+ * ->max('from table','col')->firstScalar() // select max(col) from + * table
+ * ->max('col')->from('table')->firstScalar() // select max(col) from + * table
+ * ->max('','col')->from('table')->firstScalar() // select max(col) from + * table
+ * + * @param string $sql + * @param string $arg + * + * @return PdoOneQuery + */ + public function max($sql = '', $arg = '') + { + return $this->_aggFn('max', $sql, $arg); + } + + /** + * It generates a query for "avg". It is a macro of select() + *
Example:
+ * ->avg('from table','col')->firstScalar() // select avg(col) from + * table
+ * ->avg('col')->from('table')->firstScalar() // select avg(col) from + * table
+ * ->avg('','col')->from('table')->firstScalar() // select avg(col) from + * table
+ * + * @param string $sql + * @param string $arg + * + * @return PdoOneQuery + */ + public function avg($sql = '', $arg = '') + { + return $this->_aggFn('avg', $sql, $arg); + } + + /** + * Adds a left join to the pipeline. It is possible to chain more than one + * join
+ * Example:
+ *
+     *      left('table on t1.c1=t2.c2')
+     *      left('table on table.c1=t2.c2').left('table2 on
+     * table1.c1=table2.c2')
+     * 
+ * + * @param string $sql Input SQL query + * + * @return PdoOneQuery + * @test InstanceOf PdoOne::class,this('table2 on table1.t1=table2.t2') + */ + public function left($sql) + { + if ($sql === null) { + return $this; + } + $this->from .= ($sql) ? " left join $sql" : ''; + $this->parent->tables[] = explode(' ', $sql)[0]; + return $this; + } + + /** + * Adds a right join to the pipeline. It is possible to chain more than one + * join
+ * Example:
+ * right('table on t1.c1=t2.c2')
+ * right('table on table.c1=t2.c2').right('table2 on + * table1.c1=table2.c2')
+ * + * @param string $sql Input SQL query + * + * @return PdoOneQuery + * @test InstanceOf PdoOne::class,this('table2 on table1.t1=table2.t2') + */ + public function right($sql) + { + if ($sql === null) { + return $this; + } + $this->from .= ($sql) ? " right join $sql" : ''; + $this->parent->tables[] = explode(' ', $sql)[0]; + return $this; + } + + /** + * It sets a value into the query (insert or update)
+ * Example:
+ * ->from("table")->set('field1=?',20),set('field2=?','hello')->insert()
+ * ->from("table")->set("type=?",[6])->where("i=1")->update()
+ * set("type=?",6) // automatic
+ * + * @param string|array $sqlOrArray + * @param array|mixed $param + * + * + * @return PdoOneQuery + * @test InstanceOf + * PdoOne::class,this('field1=?,field2=?',[20,'hello']) + */ + public function set($sqlOrArray, $param = PdoOne::NULL) + { + if ($sqlOrArray === null) { + return $this; + } + if (count($this->where)) { + $this->throwErrorChain('method set() must be before where()', 'set'); + return $this; + } + + $this->constructParam2($sqlOrArray, $param, 'set'); + return $this; + } + + /** + * Example:
+ *
+     * where( ['field'=>20] ) // associative array (named)
+     * where( ['field=?'=>20] ) // associative array (numeric)
+     * where( ['field=:name'=>20] ) // associative array (named)
+     * where( ['field=:name and field2=:name'=>20] ) // IT DOESN'T WORK
+     * where( ['field'=>[20]] ) // associative array with type defined
+     * where( ['field',20] ) // indexed array automatic type
+     * where (['field',[20]] ) // indexed array type defined
+     * where('field=20') // literal value
+     * where('field=?',[20]) // automatic type
+     * where('field',[20]) // automatic type (it's the same than
+     * where('field=?',[20]) where('field=?', [20] ) // type(i,d,s,b)
+     *      defined where('field=?,field2=?', [20,'hello'] )
+     * where('field=:field,field2=:field2',
+     *      ['field'=>'hello','field2'=>'world'] ) // associative array as value
+     * 
+ * + * @param array|string $where + * @param string|array|int $params + * @param string $type + * @param bool $return + * @param null|string $tablePrefix + * + * @return array|null + */ + public function constructParam2( + $where, + $params = PdoOne::NULL, + $type = 'where', + $return = false, + $tablePrefix = null + ) + { + $queryEnd = []; + $named = []; + $pars = []; + + if ($params === PdoOne::NULL || $params === null) { + if (is_array($where)) { + $numeric = isset($where[0]); + if ($numeric) { + // numeric + $c = count($where) - 1; + for ($i = 0; $i < $c; $i += 2) { + $v = $where[$i + 1]; + // constructParam2(['field',20]]) + $param = [$this->whereCounter, $v, $this->parent->getType($v), null]; + $queryEnd[] = $where[$i]; + $named[] = '?'; + $this->whereCounter++; + $pars[] = $param; + } + } else { + // named + foreach ($where as $k => $v) { + if (strpos($k, '?') === false) { + if (strpos($k, ':') !== false) { + // "aaa=:aaa" + + $parts = explode(':', $k, 2); + $paramName = ':' . str_replace('.', '_', $parts[1]); + $named[] = $paramName; + } else { + // "aaa" + + $paramName = ':' . str_replace('.', '_', $k); + $named[] = $paramName; + } + } else { + // "aa=?" + $paramName = $this->whereCounter; + $this->whereCounter++; + $named[] = '?'; + } + // constructParam2(['field'=>20]) + $param = [$paramName, $v, $this->parent->getType($v), null]; + $pars[] = $param; + if ($tablePrefix !== null && strpos($k, '.') === false) { + $queryEnd[] = $tablePrefix . '.' . $k; + } else { + $queryEnd[] = $k; + } + } + } + } else { + // constructParam2('query=xxx') + $named[] = ''; + $queryEnd[] = $where; + } + } else { + // where and params are not empty + if (!is_array($params)) { + $params = [$params]; + } + if (!is_array($where)) { + $queryEnd[] = $where; + $numeric = isset($params[0]); + if ($numeric) { + foreach ($params as $k => $v) { + // constructParam2('name=? and type>?', ['Coca-Cola',12345]); + $named[] = '?'; + $pars[] = [ + $this->whereCounter, + $v, + $this->parent->getType($v), + null + ]; + $this->whereCounter++; + } + } else { + foreach ($params as $k => $v) { + $named[] = $k; + // constructParam2('name=:name and type<:type', ['name'=>'Coca-Cola','type'=>987]);; + $pars[] = [$k, $v, $this->parent->getType($v), null]; + //$paramEnd[]=$param; + } + } + if (count($named) === 0) { + $named[] = '?'; // at least one argument. + } + } else { + // constructParam2([],..); + $numeric = isset($where[0]); + + if ($numeric) { + foreach ($where as $k => $v) { + //$named[] = '?'; + $queryEnd[] = $v; + } + } else { + trigger_error('parameteres not correctly defined'); + /*foreach ($where as $k => $v) { + $named[] = '?'; + $queryEnd[] = $k; + }*/ + } + $numeric = isset($params[0]); + if ($numeric) { + foreach ($params as $k => $v) { + //$paramEnd[]=$param; + // constructParam2(['name','type'], ['Coca-Cola',123]); + $named[] = '?'; + $pars[] = [$this->whereCounter, $v, $this->parent->getType($v), null]; + $this->whereCounter++; + //$paramEnd[]=$param; + } + } else { + foreach ($params as $k => $v) { + $named[] = $k; + // constructParam2(['name=:name','type<:type'], ['name'=>'Coca-Cola','type'=>987]);; + $pars[] = [$k, $v, $this->parent->getType($v), null]; + //$paramEnd[]=$param; + } + } + } + } + //echo "
where:"; + + $i = -1; + + foreach ($queryEnd as $k => $v) { + $i++; + + if ($named[$i] !== '' && strpos($v, '?') === false && strpos($v, $named[$i]) === false) { + $v .= '=' . $named[$i]; + $queryEnd[$k] = $v; + } + switch ($type) { + case 'where': + $this->where[] = $v; + break; + case 'having': + $this->having[] = $v; + break; + case 'set': + $this->set[] = $v; + break; + } + } + + switch ($type) { + case 'where': + $this->whereParamAssoc = array_merge($this->whereParamAssoc, $pars); + break; + case 'having': + $this->havingParamAssoc = array_merge($this->havingParamAssoc, $pars); + break; + case 'set': + $this->setParamAssoc = array_merge($this->setParamAssoc, $pars); + break; + } + + if ($return) { + return [$queryEnd, $pars]; + } + return null; + } + + /** + * It groups by a condition.
+ * Example:
+ * ->select('col1,count(*)')->from('table')->group('col1')->toList(); + * + * @param string $sql Input SQL query + * + * @return PdoOneQuery + * @test InstanceOf PdoOne::class,this('fieldgroup') + */ + public function group($sql) + { + if ($sql === null) { + return $this; + } + $this->group = ($sql) ? ' group by ' . $sql : ''; + + return $this; + } + + /** + * It sets a recursive array.
+ * example::
+ *
+     * $this->recursive(['field1','field2']);
+     * 
+ * + * @param array|mixed $rec The fields to load recursively. + * + * @return $this + */ + public function recursive($rec) + { + if (is_array($rec)) { + $this->recursive = $rec; + } else { + $this->recursive = [$rec]; + } + return $this; + } + + /** + * Alias of recursive() + * + * @param array|mixed $fields The fields to load recursively. + * @return $this + * @see \eftec\PdoOne::recursive + */ + public function include($fields) { + return $this->recursive($fields); + } + + /** + * It gets the recursive array. + * + * @return array + */ + public function getRecursive() + { + return $this->recursive; + } + + /** + * It returns true if recursive has some needle.
+ * If $this->recursive is '*' then it always returns true. + * + * @param string $needle + * @param null|array $recursiveArray If null then it uses the recursive array specified by $this->parent->>recursive(); + * + * @return bool + */ + public function hasRecursive($needle, $recursiveArray = null) + { + if (count($this->recursive) === 1 && $this->recursive[0] === '*') { + return true; + } + if ($recursiveArray) { + return in_array($needle, $recursiveArray, true); + } + return in_array($needle, $this->recursive, true); + } + + /** + * If false then it wont generate an error.
+ * If true (default), then on error, it behave normally
+ * If false, then the error is captured and store in $this::$errorText
+ * This command is specific for generation of query and its reseted when the query is executed. + * + * @param bool $error + * + * @return PdoOneQuery + * @see \eftec\PdoOne::$errorText + */ + public function genError($error = false) + { + $this->parent->genError = $error; + return $this; + } + + //
+ + + /** + * Generates and execute an insert command.
+ * Example:
+ *
+     * insert('table',['col1',10,'col2','hello world']); // simple array: name1,value1,name2,value2..
+     * insert('table',null,['col1'=>10,'col2'=>'hello world']); // definition is obtained from the values
+     * insert('table',['col1'=>10,'col2'=>'hello world']); // definition is obtained from the values
+     * insert('table',['col1','col2'],[10,'hello world']); // definition (binary) and value
+     * insert('table',['col1','col2'],['col1'=>10,'col2'=>'hello world']); // definition declarative array)
+     *      ->set(['col1',10,'col2','hello world'])
+     *      ->from('table')
+     *      ->insert();
+     *
+ * + * @param null|string $tableName + * @param string[]|null $tableDef + * @param string[]|int|null $values + * + * @return mixed Returns the identity (if any) or false if the operation fails. + * @throws Exception + */ + public function insert( + $tableName = null, + $tableDef = null, + $values = PdoOne::NULL + ) + { + if ($tableName === null) { + $tableName = $this->from; + } else { + $this->parent->tables[] = $tableName; + } + if ($this->parent->useCache === true) { + $this->parent->invalidateCache('', $this->parent->cacheFamily); + } + if ($tableDef !== null) { + $this->constructParam2($tableDef, $values, 'set'); + } + // using builder. from()->set()->insert() + $errorCause = ''; + if (!$tableName) { + $errorCause = "you can't execute an empty insert() without a from()"; + } + if (count($this->set) === 0) { + $errorCause = "you can't execute an empty insert() without a set()"; + } + if ($errorCause) { + $this->throwErrorChain('Insert:' . $errorCause, 'insert'); + return false; + } + //$sql = 'insert into ' . $this->parent->addDelimiter($tableName) . ' (' . implode(',', $col) . ') values(' + // . implode(',', $colT) . ')'; + $sql + = /** @lang text */ + 'insert into ' . $this->parent->addDelimiter($tableName) . ' ' . $this->constructInsert(); + $param = $this->setParamAssoc; + $this->parent->beginTry(); + $this->parent->runRawQuery($sql, $param); + $this->builderReset(true); + if ($this->endtry() === false) { + return false; + } + + return $this->parent->insert_id(); + } + + /** + * @return string + */ + private function constructInsert() + { + if (count($this->set)) { + $arr = []; + $val = []; + $first = $this->set[0]; + if (strpos($first, '=') !== false) { + // set([]) + foreach ($this->set as $v) { + $tmp = explode('=', $v); + $arr[] = $tmp[0]; + $val[] = $tmp[1]; + } + $set = '(' . implode(',', $arr) . ') values (' . implode(',', $val) . ')'; + } else { + // set('(a,b,c) values(?,?,?)',[]) + foreach ($this->setParamAssoc as $v) { + $vn = $v[0]; + if ($vn[0] !== ':') { + $vn = ':' . $vn; + } + $val[] = $vn; + } + $set = '(' . implode(',', $this->set) . ') values (' . implode(',', $val) . ')'; + } + } else { + $set = ''; + } + + return $set; + } + + /** + * It allows to insert a declarative array. It uses "s" (string) as + * filetype. + *

Example: ->insertObject('table',['field1'=>1,'field2'=>'aaa']); + * + * @param string $tableName The name of the table. + * @param array|object $object associative array with the colums and + * values. If the insert returns an identity then it changes the value + * @param array $excludeColumn (optional) columns to exclude. Example + * ['col1','col2'] + * + * @return mixed + * @throws Exception + */ + public function insertObject($tableName, &$object, $excludeColumn = []) + { + $objectCopy = (array)$object; + foreach ($excludeColumn as $ex) { + unset($objectCopy[$ex]); + } + + $id = $this->insert($tableName, $objectCopy); + /** id could be 0,false or null (when it is not generated */ + if ($id) { + $pks = $this->parent->service->getDefTableKeys($tableName, true, 'PRIMARY KEY'); + if ($pks > 0) { + // we update the object because it returned an identity. + $k = array_keys($pks)[0]; // first primary key + if (is_array($object)) { + $object[$k] = $id; + } else { + $object->$k = $id; + } + } + } + return $id; + } + + /** + * Delete a row(s) if they exists. + * Example: + * delete('table',['col1',10,'col2','hello world']); + * delete('table',['col1','col2'],[10,'hello world']); + * $db->from('table') + * ->where('..') + * ->delete() // running on a chain + * delete('table where condition=1'); + * + * @param string|null $tableName + * @param string[]|null $tableDefWhere + * @param string[]|int $valueWhere + * + * @return mixed + * @throws Exception + */ + public function delete( + $tableName = null, + $tableDefWhere = null, + $valueWhere = PdoOne::NULL + ) + { + if ($tableName === null) { + $tableName = $this->from; + } else { + $this->parent->tables[] = $tableName; + } + if ($this->parent->useCache === true) { + $this->parent->invalidateCache('', $this->parent->cacheFamily); + } + // using builder. from()->set()->where()->update() + $errorCause = ''; + if (!$tableName) { + $errorCause = "you can't execute an empty delete() without a from()"; + } + if ($errorCause) { + $this->throwErrorChain('Delete:' . $errorCause, ''); + return false; + } + + if ($tableDefWhere !== null) { + $this->constructParam2($tableDefWhere, $valueWhere); + } + + /** @noinspection SqlWithoutWhere */ + $sql = 'delete from ' . $this->parent->addDelimiter($tableName); + $sql .= $this->constructWhere(); + $param = $this->whereParamAssoc; + + $this->parent->beginTry(); + $stmt = $this->parent->runRawQuery($sql, $param, false); + $this->builderReset(true); + if ($this->endtry() === false) { + return false; + } + + return $this->parent->affected_rows($stmt); + } + + /** + * Generate and run an update in the database. + *
Example:
+ *

+     *      update('table',['col1',10,'col2','hello world'],['wherecol',10]);
+     *      update('table',['col1','col2'],[10,'hello world'],['wherecol'],[10]);
+     *      $this->from("producttype")
+     *          ->set("name=?",['Captain-Crunch'])
+     *          ->where('idproducttype=?',[6])
+     *          ->update();
+     *      update('product_category set col1=10 where idproducttype=1')
+     * 
+ * + * @param string|null $tableName The name of the table or the whole + * query. + * @param string[]|null $tableDef + * @param string[]|int|null $values + * @param string[]|null $tableDefWhere + * @param string[]|int|null $valueWhere + * + * @return mixed + * @throws Exception + */ + public function update( + $tableName = null, + $tableDef = null, + $values = PdoOne::NULL, + $tableDefWhere = null, + $valueWhere = PdoOne::NULL + ) + { + if ($tableName === null) { + // using builder. from()->set()->where()->update() + $tableName = $this->from; + } else { + $this->parent->tables[] = $tableName; + } + if ($this->parent->useCache === true) { + $this->parent->invalidateCache('', $this->parent->cacheFamily); + } + + if ($tableDef !== null) { + $this->constructParam2($tableDef, $values, 'set'); + } + + if ($tableDefWhere !== null) { + $this->constructParam2($tableDefWhere, $valueWhere); + } + + $errorCause = ''; + + if (!$tableName) { + $errorCause = "you can't execute an empty update() without a from()"; + } + if (count($this->set) === 0) { + $errorCause = "you can't execute an empty update() without a set()"; + } + if ($errorCause) { + $this->throwErrorChain('Update:' . $errorCause, 'update'); + return false; + } + + $sql = 'update ' . $this->parent->addDelimiter($tableName); + $sql .= $this->constructSet(); + $sql .= $this->constructWhere(); + $param = array_merge($this->setParamAssoc, $this->whereParamAssoc); // the order matters. + + // $this->builderReset(); + $this->parent->beginTry(); + $stmt = $this->parent->runRawQuery($sql, $param, false); + $this->builderReset(true); + if ($this->endtry() === false) { + return false; + } + return $this->parent->affected_rows($stmt); + } + + + + + + + + /** + * Run builder query and returns a PDOStatement. + * + * @param bool $returnArray true=return an array. False returns a + * PDOStatement + * @param int $extraMode PDO::FETCH_ASSOC,PDO::FETCH_BOTH,PDO::FETCH_NUM,etc. + * By default it returns + * $extraMode=PDO::FETCH_ASSOC + * + * @param string $extraIdCache [optional] if 'rungen' then cache is + * stored. If false the cache could be + * stored + * + * @param bool $throwError + * + * @return bool|PDOStatement|array + * @throws Exception + */ + public function runGen( + $returnArray = true, + $extraMode = PDO::FETCH_ASSOC, + $extraIdCache = 'rungen', + $throwError = true + ) + { + $this->parent->errorText = ''; + $allparam = ''; + $uid = false; + $sql = $this->sqlGen(); + $isSelect = PdoOne::queryCommand($sql, true) === 'dql'; + + try { + $allparam = array_merge($this->setParamAssoc, $this->whereParamAssoc, $this->havingParamAssoc); + + if ($isSelect && $this->parent->useInternalCache && $returnArray) { + $uid = hash($this->parent->encryption->hashType, $sql . $extraMode . serialize($allparam)); + if (isset($this->internalCache[$uid])) { + // we have an internal cache, so we will return it. + $this->parent->internalCacheCounter++; + $this->builderReset(); + return $this->parent->internalCache[$uid]; + } + } + + /** @var PDOStatement $stmt */ + $stmt = $this->parent->prepare($sql); + } catch (Exception $e) { + $this->throwErrorChain('Error in prepare runGen', $extraIdCache, ['values' => $allparam], $throwError, $e); + $this->builderReset(); + return false; + } + if ($stmt === null || $stmt === false) { + $this->builderReset(); + return false; + } + $reval = true; + if ($allparam) { + try { + foreach ($allparam as $k => $v) { + $reval = $reval && $stmt->bindParam($v[0], $allparam[$k][1], $v[2]); + } + } catch(Exception $ex) { + if(is_object($allparam[$k][1])) { + $this->throwErrorChain("Error in bind. Parameter error." + , "Parameter {$v[0]} ($k) is an object of the class ".get_class($allparam[$k][1]) + , ['values' => $allparam], $throwError); + $this->builderReset(); + return false; + } + $this->throwErrorChain("Error in bind. Parameter error.", "Parameter {$v[0]} ($k)" + , ['values' => $allparam], $throwError); + $this->builderReset(); + return false; + } + if (!$reval) { + $this->throwErrorChain('Error in bind', $extraIdCache, ['values' => $allparam], $throwError); + $this->builderReset(); + return false; + } + } + $useCache = $this->parent->useCache; // because builderReset cleans this value + if ($useCache !== false && $returnArray) { + $this->uid + = hash($this->parent->encryption->hashType, + $this->parent->lastQuery . $extraMode . serialize($allparam) . $extraIdCache); + $result = $this->parent->cacheService->getCache($this->uid, $this->parent->cacheFamily); + if ($result !== false) { + // it's found in the cache. + $this->builderReset(); + if ($uid !== false) { + $this->parent->internalCache[$uid] = $result; + } + return $result; + } + } elseif ($extraIdCache === 'rungen') { + $this->uid = null; + } + $this->parent->runQuery($stmt, null, false); + if ($returnArray && $stmt instanceof PDOStatement) { + $result = ($stmt->columnCount() > 0) ? $stmt->fetchAll($extraMode) : []; + $this->parent->affected_rows = $stmt->rowCount(); + $stmt = null; // close + if ($extraIdCache === 'rungen' && $this->uid) { + // we store the information of the cache. + $this->parent->setCache($this->uid, $this->parent->cacheFamily, $result, $useCache); + } + $this->builderReset(); + if ($uid !== false) { + $this->parent->internalCache[$uid] = $result; + } + return $result; + } + + $this->builderReset(); + return $stmt; + } + + /** + * Generates the sql (script). It doesn't run or execute the query. + * + * @param bool $resetStack if true then it reset all the values of the + * stack, including parameters. + * + * @return string + */ + public function sqlGen($resetStack = false) + { + if (stripos($this->select, 'select ') === 0) { + // is it a full query? $this->select=select * ..." instead of $this->select=* + $words = preg_split('#\s+#', strtolower($this->select)); + } else { + $words = []; + } + if (!in_array('select', $words)) { + $sql = 'select ' . $this->distinct . $this->select; + } else { + $sql = $this->select; // the query already constains "select", so we don't want "select select * from". + } + if (!in_array('from', $words)) { + $sql .= ' from ' . $this->from; + } else { + $sql .= $this->from; + } + $where = $this->constructWhere(); + $having = $this->constructHaving(); + + $sql .= $where . $this->group . $having . $this->order . $this->limit; + + if ($resetStack) { + $this->builderReset(); + } + + return $sql; + } + + /** + * @return string + */ + private function constructWhere() + { + return count($this->where) ? ' where ' . implode(' and ', $this->where) : ''; + } + + + // + + /** + * @return string + */ + private function constructSet() + { + return count($this->set) ? ' set ' . implode(',', $this->set) : ''; + } + /** + * It reset the parameters used to Build Query. + * + * @param bool $forced if true then calling this method resets the stacks of variables
+ * if false then it only resets the stack if $this->noreset=false; (default is false) + */ + public function builderReset($forced = false) + { + if ($this->noReset && !$forced) { + return; + } + $this->select = ''; + $this->noReset = false; + $this->parent->useCache = false; + $this->from = ''; + $this->parent->tables = []; + $this->where = []; + + $this->whereParamAssoc = []; + $this->setParamAssoc = []; + $this->havingParamAssoc = []; + + $this->whereCounter = 1; + //$this->whereParamValue = []; + $this->set = []; + $this->group = ''; + $this->recursive = []; + $this->parent->genError = true; + $this->having = []; + $this->limit = ''; + $this->distinct = ''; + $this->order = ''; + } + /** + * @return array + */ + public function getSetParamAssoc() + { + return $this->setParamAssoc; + } + + /** + * @return array + */ + public function getWhereParamAssoc() + { + return $this->whereParamAssoc; + } + + /** + * @return array + */ + public function getHavingParamAssoc() + { + return $this->havingParamAssoc; + } + + + /** + * It adds an "order by" in a query.
+ * Example:
+ *
+     *      ->select("")->order("column")->toList();
+     *      ->select("")->order("col1,col2")->toList();
+     * 
+ * + * @param string $sql Input SQL query + * + * @return PdoOneQuery + * @test InstanceOf PdoOne::class,this('name desc') + */ + public function order($sql) + { + if ($sql === null) { + return $this; + } + $this->order = ($sql) ? ' order by ' . $sql : ''; + + return $this; + } + + /** + * Macro of join.
+ * Example:
+ *
+     *          innerjoin('tablejoin on t1.field=t2.field')
+     *          innerjoin('tablejoin tj on t1.field=t2.field')
+     *          innerjoin('tablejoin','t1.field=t2.field')
+     * 
+ * + * @param string $sql + * @param string $condition + * + * @return PdoOneQuery + * @see \eftec\PdoOne::join + */ + public function innerjoin($sql, $condition = '') + { + return $this->join($sql, $condition); + } + + /** + * It generates an inner join
+ * Example:
+ *
+     *          join('tablejoin on t1.field=t2.field')
+ * join('tablejoin','t1.field=t2.field')
+ *
+ * + * @param string $sql Example "tablejoin on table1.field=tablejoin.field" + * @param string $condition + * + * @return PdoOneQuery + * @test InstanceOf PdoOne::class,this('tablejoin on t1.field=t2.field') + */ + public function join($sql, $condition = '') + { + if ($condition !== '') { + $sql = "$sql on $condition"; + } + $this->from .= ($sql) ? " inner join $sql " : ''; + $this->parent->tables[] = explode(' ', $sql)[0]; + + return $this; + } + + /** + * Adds a from for a query. It could be used by select,insert,update and + * delete.
+ * Example:
+ *
+     *      from('table')
+     *      from('table alias')
+     *      from('table1,table2')
+     *      from('table1 inner join table2 on table1.c=table2.c')
+     * 
+ * + * @param string $sql Input SQL query + * + * @return PdoOneQuery + * @test InstanceOf PdoOne::class,this('table t1') + */ + public function from($sql) + { + if ($sql === null) { + return $this; + } + $this->from = ($sql) ? $sql . $this->from : $this->from; + $this->parent->tables[] = explode(' ', $sql)[0]; + + return $this; + } + /** + * It sets to use cache for the current pipelines. It is disabled at the end of the pipeline
+ * It only works if we set the cacheservice
+ * Example
+ *
+     * $this->setCacheService($instanceCache);
+     * $this->useCache()->select()..; // The cache never expires
+     * $this->useCache(60)->select()..; // The cache lasts 60 seconds.
+     * $this->useCache(60,'customers')
+     *        ->select()..; // cache associated with customers
+     *                      // it could be invalidated by invalidateCache()
+     * $this->useCache(60,['customers','invoices'])
+     *        ->select()..; // cache associated with customers
+     *                      // it could be invalidated by invalidateCache()
+     * $this->useCache(60,'*')->select('col')
+     *      ->from('table')->toList(); // '*' uses all the table assigned.
+     * 
+ * + * @param null|bool|int $ttl null then the cache never expires.
+ * false then we don't use cache.
+ * int then it is the duration of the cache (in seconds) + * @param string|array $family [optional] It is the family or group of the cache. It could be used to + * identify a group of cache to invalidate the whole group (for example + * ,invalidate all cache from a specific table).
+ * * If "*" then it uses the tables assigned by from() and join() + * + * @return $this + * @see \eftec\PdoOne::invalidateCache + */ + public function useCache($ttl = 0, $family = '') + { + if ($this->parent->cacheService === null) { + $ttl = false; + } + $this->parent->cacheFamily = $family; + $this->parent->useCache = $ttl; + + return $this; + } + + +} \ No newline at end of file diff --git a/lib/_BasePdoOneRepo.php b/lib/_BasePdoOneRepo.php index 8f7a0dc..9ed4a60 100644 --- a/lib/_BasePdoOneRepo.php +++ b/lib/_BasePdoOneRepo.php @@ -9,8 +9,6 @@ * @noinspection DisconnectedForeachInstructionInspection * @noinspection PhpUnused * @noinspection NullPointerExceptionInspection - * @noinspection PhpUndefinedMethodInspection - * @noinspection PhpUndefinedClassConstantInspection */ namespace eftec; @@ -23,7 +21,7 @@ /** * Class _BasePdoOneRepo * - * @version 4.14 2021-02-16 + * @version 4.15 2021-03-22 * @package eftec * @author Jorge Castro Castillo * @copyright (c) Jorge Castro C. MIT License https://github.com/EFTEC/PdoOne @@ -31,9 +29,11 @@ abstract class _BasePdoOneRepo { // it is used for compatibility. - const BINARYVERSION=3; + const BINARYVERSION = 4; /** @var PdoOne */ public static $pdoOne; + /** @var PdoOneQuery */ + public static $pdoOneQuery; /** @var array $gQuery =[['columns'=>[],'joins'=>[],'where'=>[]] */ public static $gQuery = []; public static $gQueryCounter = 0; @@ -111,8 +111,8 @@ protected static function getPdoOne() if (self::$pdoOne !== null) { return self::$pdoOne; } - if(self::BINARYVERSION!==static::COMPILEDVERSION) { - throw new RuntimeException(self::class.' requires a new version'); + if (self::BINARYVERSION !== static::COMPILEDVERSION) { + throw new RuntimeException(self::class . ' requires a new version'); } if (function_exists('PdoOne')) { return PdoOne(); @@ -123,6 +123,16 @@ protected static function getPdoOne() return null; } + /** + * @return PdoOneQuery + */ + protected static function getQuery() { + if(self::$pdoOneQuery===null) { + return new PdoOneQuery(self::getPdoOne()); + } + return self::$pdoOneQuery; + } + /** * It sets the field self::$pdoOne * @@ -147,7 +157,7 @@ protected static function reset($forcedPdoOne = false) self::$gQuery = []; self::$falseOnError = false; self::$lastException = ''; - self::getPdoOne()->builderReset($forcedPdoOne); + self::getQuery()->builderReset($forcedPdoOne); } /** @@ -184,11 +194,12 @@ public static function testRecursive($initClass = null, $recursiveInit = '') $local = static::NS . $initClass; } //$recursive=$local::getPdoOne()->getRecursive(); + /** @noinspection PhpUndefinedMethodInspection */ $relations = $local::getDefFK(); foreach ($relations as $nameCol => $r) { $key = $r['key']; $recursiveComplete = ltrim($recursiveInit . '/' . $nameCol, '/'); - if (self::getPdoOne()->hasRecursive($recursiveComplete)) { + if (self::getQuery()->hasRecursive($recursiveComplete)) { $used = ''; } else { $used = '// '; @@ -196,22 +207,22 @@ public static function testRecursive($initClass = null, $recursiveInit = '') switch ($key) { case 'PARENT': $class = static::RELATIONS[$r['reftable']]; - echo "// \$relation['".$recursiveComplete. "']; //".$local . '->' . $class . " ($key)
"; + echo "// \$relation['" . $recursiveComplete . "']; //" . $local . '->' . $class . " ($key)
"; break; case 'MANYTOONE': case 'ONETOONE': case 'ONETOMANY': $class = static::RELATIONS[$r['reftable']]; - echo $used."\$relation['".$recursiveComplete. "']; //".$local . '->' . $class . " ($key)
"; - if($used==='') { - self::testRecursive($class,$recursiveComplete); + echo $used . "\$relation['" . $recursiveComplete . "']; //" . $local . '->' . $class . " ($key)
"; + if ($used === '') { + self::testRecursive($class, $recursiveComplete); } break; case 'MANYTOMANY': $class = static::RELATIONS[$r['table2']]; - echo $used."\$relation['".$recursiveComplete. "']; //".$local . '->' . $class . " ($key)
"; - if($used!=='') { - self::testRecursive($class,$recursiveComplete); + echo $used . "\$relation['" . $recursiveComplete . "']; //" . $local . '->' . $class . " ($key)
"; + if ($used !== '') { + self::testRecursive($class, $recursiveComplete); } break; } @@ -265,10 +276,10 @@ public static function createForeignKeys() public static function query($sql, $param = null) { try { - $pdoOne = self::getPdoOne(); - if (self::$useCache && $pdoOne->getCacheService() !== null) { + $pdoOne = self::getQuery(); + if (self::$useCache && $pdoOne->parent->getCacheService() !== null) { self::$uid = $pdoOne->buildUniqueID([$sql, $param], 'query'); - $getCache = $pdoOne->getCacheService()->getCache(self::$uid, static::TABLE); + $getCache = $pdoOne->parent->getCacheService()->getCache(self::$uid, static::TABLE); if ($getCache !== false) { self::reset(); return $getCache; @@ -281,7 +292,7 @@ public static function query($sql, $param = null) } $rowc = self::getPdoOne()->runRawQuery($sql, $param); if ($rowc !== false && $usingCache) { - $pdoOne->getCacheService()->setCache(self::$uid, $recursiveClass, $rowc, self::$useCache); + $pdoOne->parent->getCacheService()->setCache(self::$uid, $recursiveClass, $rowc, self::$useCache); self::reset(); } } catch (Exception $exception) { @@ -329,6 +340,8 @@ public static function getRecursiveClass(&$final = null, $prefix = '') if (!in_array($className, $final, true)) { $final[] = $className; } + + /** @noinspection PhpUndefinedMethodInspection */ $class::getRecursiveClass($final, $keyr); if ($keyRels[$keyr]['key'] === 'MANYTOMANY') { $className @@ -337,6 +350,7 @@ public static function getRecursiveClass(&$final = null, $prefix = '') if (!in_array($className, $final, true)) { $final[] = $className; } + /** @noinspection PhpUndefinedMethodInspection */ $class::getRecursiveClass($final, $keyr); } } @@ -517,7 +531,7 @@ public static function getPostfix() */ public static function useCache($ttl = null, $family = '') { - self::getPdoOne()->useCache($ttl, $family); + self::getQuery()->useCache($ttl, $family); self::$useCache = $ttl; return static::ME; } @@ -570,7 +584,7 @@ public static function order($order) */ public static function innerjoin($sql, $condition = '') { - self::getPdoOne()->innerjoin($sql, $condition); + self::getQuery()->innerjoin($sql, $condition); return static::ME; } @@ -617,10 +631,10 @@ public static function group($sql) */ public static function count($where = null) { - $pdoOne = self::getPdoOne(); - if (self::$useCache && $pdoOne->getCacheService() !== null) { + $pdoOne = self::getQuery(); + if (self::$useCache && $pdoOne->parent->getCacheService() !== null) { self::$uid = $pdoOne->buildUniqueID([$where], static::TABLE . '::count'); - $getCache = $pdoOne->getCacheService()->getCache(self::$uid, static::TABLE); + $getCache = $pdoOne->parent->getCacheService()->getCache(self::$uid, static::TABLE); if ($getCache !== false) { self::reset(); return $getCache; @@ -640,7 +654,7 @@ public static function count($where = null) $from = (isset(self::$gQuery[0]['joins'])) ? self::$gQuery[0]['joins'] : []; $rowc = self::getPdoOne()->count()->from($from)->where($where)->firstScalar(); if ($rowc !== false && $usingCache) { - $pdoOne->getCacheService()->setCache(self::$uid, $recursiveClass, (int)$rowc, self::$useCache); + $pdoOne->parent->getCacheService()->setCache(self::$uid, $recursiveClass, (int)$rowc, self::$useCache); self::reset(); } return $rowc; @@ -679,7 +693,7 @@ protected static function generationRecursive( // $nameColClean = trim($nameCol, PdoOne::$prefixBase); $recursiveComplete = ltrim($recursiveInit . '/' . $nameCol, '/'); //echo "check recursive: $recursiveComplete
"; - if (self::getPdoOne()->hasRecursive($recursiveComplete)) { + if (self::getQuery()->hasRecursive($recursiveComplete)) { //echo "OK $type
"; // type='PARENT' is n switch ($type) { @@ -694,6 +708,7 @@ protected static function generationRecursive( $refCol = ltrim($keyRel['refcol'], PdoOne::$prefixBase); $newQuery['joins'] .= " left join {$keyRel['reftable']} as $tableRelAlias " . "on {$pt}{$col}=$tableRelAlias.$refCol \n"; // $recursiveInit$nameCol\n"; // adds a query to the current query + /** @noinspection PhpUndefinedMethodInspection */ $class::generationRecursive($newQuery, $tableRelAlias . '.', $colRelAlias . '.', $recursiveComplete, false); // $recursiveInit . $nameCol break; @@ -708,6 +723,7 @@ protected static function generationRecursive( $newQuery['joins'] .= " left join {$keyRel['reftable']} as $tableRelAlias " . "on {$pt}{$col}=$tableRelAlias.$refCol \n"; // $recursiveInit$nameCol\n"; // adds a query to the current query + /** @noinspection PhpUndefinedMethodInspection */ $class::generationRecursive($newQuery, $tableRelAlias . '.', $colRelAlias . '.', $recursiveComplete, false); // $recursiveInit . $nameCol break; @@ -727,6 +743,7 @@ protected static function generationRecursive( //self::$gQuery[]=$other; $class = $ns . static::RELATIONS[$keyRel['reftable']]; // $ns . PdoOne::camelize($keyRel['reftable']) . $postfix; + /** @noinspection PhpUndefinedMethodInspection */ $class::generationRecursive($other, '', '', $pColumn . $recursiveComplete, false); //$recursiveInit . $nameCol self::$gQuery[] = $other; @@ -750,6 +767,7 @@ protected static function generationRecursive( $other['data'] = $keyRel; $class = $ns . static::RELATIONS[$keyRel['reftable']]; // $ns . PdoOne::camelize($keyRel['reftable']) . $postfix; + /** @noinspection PhpUndefinedMethodInspection */ $class::generationRecursive($other, '', '', $pColumn . $recursiveComplete, false); //$recursiveInit . $nameCol // we reduce a level @@ -788,6 +806,109 @@ protected static function generationRecursive( } } + /** + * This method validates a model before it is inserted/updated into the database. + * + * @param object|array $model It could be one model or multiple models. + * @param boolean $multiple if true then it validates multiples models at once. + * @param array $recursive =self::factory() + * @return bool if true then the model is valid, otherwise its false. + */ + public static function validateModel($model, $multiple = false, $recursive = []) + { + if ($multiple) { + if ($model === null || count($model) === 0) { + return true; + } + $array = $model; + } else { + $array[0] = $model; + } + $defs = static::getDef(); + $fks = static::getDefFK(); + foreach ($array as $mod) { + if (is_object($mod)) { + $mod = (array)$mod; + } + foreach ($defs as $col => $def) { + $curCol = array_key_exists($col, $mod) ? $mod[$col] : null; + + // if null (or missing) and it is allowed = true + // if null (or missing) and not null and it is not identity = false (identities are generated) + if (($curCol === null) && !($def['null'] === false && $def['identity'] === false)) { + return false; + } + switch ($def['phptype']) { + case 'binary': + case 'string': + if (!is_string($curCol)) { + // not a string + return false; + } + break; + case 'float': + if (!is_float($curCol)) { + return false; + } + break; + case 'timestamp': + case 'int': + if (!is_int($curCol)) { + return false; + } + break; + case 'time': + case 'datetime': + case 'date': + $bool = false; + $time = false; + $r = false; + if ($def['conversion'] === 'datetime2') { + $r = PdoOne::dateConvertInput($curCol, 'iso', $bool, $time); + } elseif ($def['conversion'] === 'datetime3') { + $r = PdoOne::dateConvertInput($curCol, 'human', $bool, $time); + } elseif ($def['conversion'] === 'datetime') { + $r = PdoOne::dateConvertInput($curCol, 'class', $bool, $time); + } elseif ($def['conversion'] === 'datetime4') { + $r = PdoOne::dateConvertInput($curCol, 'sql', $bool, $time); + } + if ($r === false) { + return false; + } + } + } + if (count($recursive) > 0) { + $ns = self::getNamespace(); + foreach ($fks as $key => $fk) { + if (array_key_exists($key, $mod) && self::getQuery()->hasRecursive($key, $recursive)) { + $curFK = $fk['key']; + $class = $ns . static::RELATIONS[$fk['reftable']]; + switch ($curFK) { + case 'ONETOMANY': + case 'MANYTOMANY': + /** @noinspection PhpUndefinedMethodInspection */ + $r = $class::validateModel($mod[$key], true, $recursive); + break; + case 'MANYTOONE': + case 'ONETOONE': + /** @noinspection PhpUndefinedMethodInspection */ + $r = $class::validateModel($mod[$key], false, $recursive); + break; + default: + $r = true; + } + if ($r === false) { + return false; + } + } + } + } + } + + + return true; + } + protected static function _merge($entity, $transaction = true) { if (static::_exist($entity)) { @@ -831,6 +952,18 @@ protected static function _exist($entity) } } + protected static function objectToArray($obj) + { + if (is_object($obj) || is_array($obj)) { + $ret = (array)$obj; + foreach ($ret as &$item) { + $item = self::objectToArray($item); + } + return $ret; + } + return $obj; + } + /** * Update an registry * @@ -847,21 +980,21 @@ protected static function _update($entity, $transaction = true) if (is_object($entity)) { $entity = static::objectToArray($entity); } - $pdoOne = self::getPdoOne(); + $pdoOne = self::getQuery(); //$defTable = static::getDef('conversion'); (static::ME)::convertInputVal($entity); self::invalidateCache(); // only the fields that are defined are inserted $entityCopy = self::intersectArraysNotNull($entity, static::getDefName()); $entityCopy = self::diffArrays($entityCopy, array_merge(static::getDefKey(), static::getDefNoUpdate())); // columns discarded - if ($pdoOne->transactionOpen === true) { + if ($pdoOne->parent->transactionOpen === true) { // we disable transaction to avoid nested transactions. // mysql does not allows nested transactions // sql server allows nested transaction but afaik, it only counts the outer one. $transaction = false; } if ($transaction) { - $pdoOne->startTransaction(); + $pdoOne->parent->startTransaction(); } $recursiveBack = $pdoOne->getRecursive(); $r = $pdoOne->from(static::TABLE)->set($entityCopy)->where(static::intersectArrays($entity, static::PK)) @@ -889,6 +1022,7 @@ protected static function _update($entity, $transaction = true) //self::_setRecursive([$def['refcol2']]); self::_setRecursive([]); + /** @noinspection PhpUndefinedMethodInspection */ $oldRows = ($classRef::where($refcol, $entity[$col1]))::_toList(); $oldRowsKeys = []; foreach ($oldRows as $v) { @@ -900,12 +1034,15 @@ protected static function _update($entity, $transaction = true) foreach ($newRows as $item) { if (in_array($item[$refpk], $insertKeys, false)) { $item[$refcol] = $fatherPK; + /** @noinspection PhpUndefinedMethodInspection */ $classRef::insert($item, false); } elseif (!in_array($item[$refpk], $deleteKeys, false)) { + /** @noinspection PhpUndefinedMethodInspection */ $classRef::update($item, false); } } foreach ($deleteKeys as $key2) { + /** @noinspection PhpUndefinedMethodInspection */ $classRef::deleteById($key2, false); } } @@ -929,6 +1066,7 @@ protected static function _update($entity, $transaction = true) } //self::_setRecursive([$def['refcol2']]); self::_setRecursive([]); + /** @noinspection PhpUndefinedMethodInspection */ $oldRows = ($classRef::where($refcol, $entity[$col1]))::_toList(); $oldRowsKeys = []; foreach ($oldRows as $v) { @@ -940,14 +1078,18 @@ protected static function _update($entity, $transaction = true) foreach ($newRows as $item) { if (in_array($item[$col2], $insertKeys, false)) { $pk2 = $item[$def['col2']]; + /** @noinspection PhpUndefinedMethodInspection */ if ($class2::exist($item) === false && self::getPdoOne()->hasRecursive($key, $recursiveBack) ) { + /** @noinspection PhpUndefinedMethodInspection */ $pk2 = $class2::insert($item, false); } else { + /** @noinspection PhpUndefinedMethodInspection */ $class2::update($item, false); } $relationalObjInsert = [$refcol => $entity[$def['col']], $refcol2 => $pk2]; + /** @noinspection PhpUndefinedMethodInspection */ $classRef::insert($relationalObjInsert, false); } } @@ -955,10 +1097,12 @@ protected static function _update($entity, $transaction = true) foreach ($newRows as $item) { if (in_array($item[$col2], $deleteKeys)) { $pk2 = $item[$def['col2']]; - if (self::getPdoOne()->hasRecursive($key, $recursiveBack)) { + if ($pdoOne->hasRecursive($key, $recursiveBack)) { + /** @noinspection PhpUndefinedMethodInspection */ $class2::deleteById($item, $pk2); } $relationalObjDelete = [$refcol => $entity[$def['col']], $refcol2 => $pk2]; + /** @noinspection PhpUndefinedMethodInspection */ $classRef::deleteById($relationalObjDelete, false); } } @@ -982,7 +1126,6 @@ protected static function _update($entity, $transaction = true) } } - /** * It invalidates a family/group of cache
* Example @@ -1139,7 +1282,7 @@ protected static function _insert(&$entity, $transaction = true) { $returnObject = false; try { - $pdoOne = self::getPdoOne(); + $pdoOne = self::getQuery(); //$defTable = static::getDef('conversion'); //self::_convertInputValue($entity, $defTable); @@ -1159,14 +1302,14 @@ protected static function _insert(&$entity, $transaction = true) ' and case of the fields', $entity); return false; } - if ($pdoOne->transactionOpen === true) { + if ($pdoOne->parent->transactionOpen === true) { // we disable transaction to avoid nested transactions. // mysql does not allows nested transactions // sql server allows nested transaction but afaik, it only counts the outer one. $transaction = false; } if ($transaction) { - $pdoOne->startTransaction(); + $pdoOne->parent->startTransaction(); } $insert = $pdoOne->insertObject(static::TABLE, $entityCopy); $pks = static::IDENTITY; @@ -1193,6 +1336,7 @@ protected static function _insert(&$entity, $transaction = true) $refCol = ltrim($def['refcol'], PdoOne::$prefixBase); $item[$refCol] = $entityCopy[$def['col']]; // if the pk (of the original object) is identity. + /** @noinspection PhpUndefinedMethodInspection */ $classRef::insert($item, false); } } @@ -1201,8 +1345,10 @@ protected static function _insert(&$entity, $transaction = true) . static::RELATIONS[$def['table2']]; // $ns . PdoOne::camelize($def['table2']) . $postfix; foreach ($entity[$key] as $item) { $pk2 = $item[$def['col2']]; + /** @noinspection PhpUndefinedMethodInspection */ if ($pdoOne->hasRecursive($key, $recursiveBack) && $class2::exist($item) === false) { // we only update it if it has a recursive + /** @noinspection PhpUndefinedMethodInspection */ $pk2 = $class2::insert($item, false); } $classRel = $ns @@ -1210,6 +1356,7 @@ protected static function _insert(&$entity, $transaction = true) $refCol = ltrim($def['refcol'], PdoOne::$prefixBase); $refCol2 = ltrim($def['refcol2'], PdoOne::$prefixBase); $relationalObj = [$refCol => $entityCopy[$def['col']], $refCol2 => $pk2]; + /** @noinspection PhpUndefinedMethodInspection */ $classRel::insert($relationalObj, false); } } @@ -1242,19 +1389,6 @@ protected static function _insert(&$entity, $transaction = true) } } - - protected static function objectToArray($obj) - { - if (is_object($obj) || is_array($obj)) { - $ret = (array)$obj; - foreach ($ret as &$item) { - $item = self::objectToArray($item); - } - return $ret; - } - return $obj; - } - /** * @param mixed $filter * @param mixed $filterValue @@ -1287,11 +1421,10 @@ protected static function generationStart($type, $filter = PdoOne::NULL, $filter try { static::$gQuery = []; static::$gQueryCounter = 0; - /** @var PdoOne $pdoOne instance of PdoOne */ - $pdoOne = self::getPdoOne(); - if (self::$useCache && $pdoOne->getCacheService() !== null) { + $pdoOne = self::getQuery(); + if (self::$useCache && $pdoOne->parent->getCacheService() !== null) { self::$uid = $pdoOne->buildUniqueID([$filter, $filterValue], static::TABLE . '::' . $type); - $getCache = $pdoOne->getCacheService()->getCache(self::$uid, static::TABLE); + $getCache = $pdoOne->parent->getCacheService()->getCache(self::$uid, static::TABLE); if ($getCache !== false) { self::reset(); return $getCache; @@ -1358,7 +1491,7 @@ protected static function generationStart($type, $filter = PdoOne::NULL, $filter self::convertSQLValueInit($rowc, true); } if ($rowc !== false && $usingCache) { - $pdoOne->getCacheService()->setCache(self::$uid, $recursiveClass, $rowc, self::$useCache); + $pdoOne->parent->getCacheService()->setCache(self::$uid, $recursiveClass, $rowc, self::$useCache); } self::reset(); return $rowc; @@ -1442,14 +1575,17 @@ protected static function convertSQLValueInit(&$rows, $list = false) // PARENT not because parent is a fk but is used for a one way relation. case 'MANYTOONE': $class = $ns . static::RELATIONS[$v['reftable']]; + /** @noinspection PhpUndefinedMethodInspection */ $class::convertSQLValueInit($row[$k], false); break; case 'ONETOMANY': $class = $ns . static::RELATIONS[$v['reftable']]; + /** @noinspection PhpUndefinedMethodInspection */ $class::convertSQLValueInit($row[$k], true); break; case 'MANYTOMANY': $class = $ns . static::RELATIONS[$v['table2']]; + /** @noinspection PhpUndefinedMethodInspection */ $class::convertSQLValueInit($row[$k], true); break; } @@ -1461,107 +1597,6 @@ protected static function convertSQLValueInit(&$rows, $list = false) } } - /** - * This method validates a model before it is inserted/updated into the database. - * - * @param object|array $model It could be one model or multiple models. - * @param boolean $multiple if true then it validates multiples models at once. - * @param array $recursive =self::factory() - * @return bool if true then the model is valid, otherwise its false. - */ - public static function validateModel($model, $multiple = false, $recursive = []) - { - if ($multiple) { - if ($model === null || count($model) === 0) { - return true; - } - $array = $model; - } else { - $array[0] = $model; - } - $defs = static::getDef(); - $fks = static::getDefFK(); - foreach ($array as $mod) { - if (is_object($mod)) { - $mod = (array)$mod; - } - foreach ($defs as $col => $def) { - $curCol = array_key_exists($col, $mod) ? $mod[$col] : null; - - // if null (or missing) and it is allowed = true - // if null (or missing) and not null and it is not identity = false (identities are generated) - if (($curCol === null) && !($def['null'] === false && $def['identity'] === false)) { - return false; - } - switch ($def['phptype']) { - case 'binary': - case 'string': - if (!is_string($curCol)) { - // not a string - return false; - } - break; - case 'float': - if (!is_float($curCol)) { - return false; - } - break; - case 'timestamp': - case 'int': - if (!is_int($curCol)) { - return false; - } - break; - case 'time': - case 'datetime': - case 'date': - $bool = false; - $time = false; - $r = false; - if ($def['conversion'] === 'datetime2') { - $r = PdoOne::dateConvertInput($curCol, 'iso', $bool, $time); - } elseif ($def['conversion'] === 'datetime3') { - $r = PdoOne::dateConvertInput($curCol, 'human', $bool, $time); - } elseif ($def['conversion'] === 'datetime') { - $r = PdoOne::dateConvertInput($curCol, 'class', $bool, $time); - } elseif ($def['conversion'] === 'datetime4') { - $r = PdoOne::dateConvertInput($curCol, 'sql', $bool, $time); - } - if ($r === false) { - return false; - } - } - } - if (count($recursive) > 0) { - $ns = self::getNamespace(); - foreach ($fks as $key => $fk) { - if (array_key_exists($key, $mod) && self::getPdoOne()->hasRecursive($key, $recursive)) { - $curFK = $fk['key']; - $class = $ns . static::RELATIONS[$fk['reftable']]; - switch ($curFK) { - case 'ONETOMANY': - case 'MANYTOMANY': - $r = $class::validateModel($mod[$key], true, $recursive); - break; - case 'MANYTOONE': - case 'ONETOONE': - $r = $class::validateModel($mod[$key], false, $recursive); - break; - default: - $r=true; - } - if ($r === false) { - return false; - } - } - } - } - } - - - return true; - } - /** * It deletes a registry * @@ -1602,15 +1637,15 @@ protected static function _delete($entity, $transaction = true, $columns = null) } $entityCopy = self::intersectArraysNotNull($entity, $columns); self::invalidateCache(); - $pdoOne = self::getPdoOne(); - if ($pdoOne->transactionOpen === true) { + $pdoOne = self::getQuery(); + if ($pdoOne->parent->transactionOpen === true) { // we disable transaction to avoid nested transactions. // mysql does not allows nested transactions // sql server allows nested transaction but afaik, it only counts the outer one. $transaction = false; } if ($transaction) { - $pdoOne->startTransaction(); + $pdoOne->parent->startTransaction(); } $defs = static::getDefFK(); $ns = self::getNamespace(); @@ -1628,6 +1663,7 @@ protected static function _delete($entity, $transaction = true, $columns = null) //$refcol = ltrim($def['refcol'], PdoOne::$prefixBase); // it is how they are joined //$refpk = $classRef::PK[0]; foreach ($newRows as $item) { + /** @noinspection PhpUndefinedMethodInspection */ $classRef::deleteById($item, false); } } @@ -1649,11 +1685,13 @@ protected static function _delete($entity, $transaction = true, $columns = null) $cols2[] = $item[$col2]; } $relationalObjDelete = [$refcol => $entity[$col1]]; + /** @noinspection PhpUndefinedMethodInspection */ $classRef::delete($relationalObjDelete, false); - if (self::getPdoOne()->hasRecursive($key, $recursiveBackup)) { + if (self::getQuery()->hasRecursive($key, $recursiveBackup)) { foreach ($cols2 as $c2) { // $k = $v[$refcol2]; $object2Delete = [$col2 => $c2]; + /** @noinspection PhpUndefinedMethodInspection */ $class2::delete($object2Delete, false); } } @@ -1726,7 +1764,7 @@ protected static function _first($pk = PdoOne::NULL) * * @return self */ - public function having($sql, $param = self::NULL) + public function having($sql, $param = PdoOne::NULL) { self::getPdoOne()->having($sql, $param); return static::ME; diff --git a/lib/ext/PdoOne_IExt.php b/lib/ext/PdoOne_IExt.php index d475453..9ca716d 100644 --- a/lib/ext/PdoOne_IExt.php +++ b/lib/ext/PdoOne_IExt.php @@ -206,6 +206,7 @@ public function createFK($tableName,$foreignKey); * * * @param $sql + * @return string * * @throws Exception */ diff --git a/lib/ext/PdoOne_Mysql.php b/lib/ext/PdoOne_Mysql.php index 1f88632..dd0ba3e 100644 --- a/lib/ext/PdoOne_Mysql.php +++ b/lib/ext/PdoOne_Mysql.php @@ -1,4 +1,4 @@ -parent->limit = ($sql) ? ' limit ' . $sql : ''; + return ($sql) ? ' limit ' . $sql : ''; } diff --git a/lib/ext/PdoOne_Oci.php b/lib/ext/PdoOne_Oci.php index cadd325..43b2241 100644 --- a/lib/ext/PdoOne_Oci.php +++ b/lib/ext/PdoOne_Oci.php @@ -1,4 +1,7 @@ -'',"ColumnName"=>'',"is_unique"=>0,"is_primary_key"=>0,"TYPE"=>0]) */ - $pks=$this->getPK($table,null); + $pks=$this->getPK($table); $result = $this->parent->select('SELECT ALL_indexes.INDEX_NAME "IndexName",all_ind_columns.COLUMN_NAME "ColumnName", (CASE WHEN UNIQUENESS = \'UNIQUE\' THEN 1 ELSE 0 END) "is_unique",0 "is_primary_key",0 "TYPE"') @@ -434,10 +437,9 @@ public function foreignKeyTable($tableName) public function createSequence($tableSequence = null, $method = 'snowflake') { - $sql = "CREATE SEQUENCE {$tableSequence} + return "CREATE SEQUENCE {$tableSequence} START WITH 1 INCREMENT BY 1"; - return $sql; } public function getSequence($sequenceName) { $sequenceName = ($sequenceName == '') ? $this->parent->tableSequence : $sequenceName; @@ -526,15 +528,15 @@ public function createFK($tableName, $foreignKey) public function limit($sql) { //throw new RuntimeException("no yet implemented"); - if (!$this->parent->order) { - $this->parent->throwError('limit without a sort', ''); - } + //if (!$this->parent->order) { + // $this->parent->throwError('limit without a sort', ''); + //} if (strpos($sql, ',')) { $arr = explode(',', $sql); - $this->parent->limit = " OFFSET {$arr[0]} ROWS FETCH NEXT {$arr[1]} ROWS ONLY"; - } else { - $this->parent->limit = " OFFSET 0 ROWS FETCH NEXT $sql ROWS ONLY"; + return " OFFSET {$arr[0]} ROWS FETCH NEXT {$arr[1]} ROWS ONLY"; } + + return " OFFSET 0 ROWS FETCH NEXT $sql ROWS ONLY"; } public function getPK($query, $pk=null) diff --git a/lib/ext/PdoOne_Sqlsrv.php b/lib/ext/PdoOne_Sqlsrv.php index bbc0865..aae6827 100644 --- a/lib/ext/PdoOne_Sqlsrv.php +++ b/lib/ext/PdoOne_Sqlsrv.php @@ -1,4 +1,7 @@ -parent->order) { - $this->parent->throwError('limit without a sort', ''); - } + //if (!$this->parent->order) { + // $this->parent->throwError('limit without a sort', ''); + //} if (strpos($sql, ',')) { $arr = explode(',', $sql); - $this->parent->limit = " OFFSET {$arr[0]} ROWS FETCH NEXT {$arr[1]} ROWS ONLY"; - } else { - $this->parent->limit = " OFFSET 0 ROWS FETCH NEXT $sql ROWS ONLY"; + return " OFFSET {$arr[0]} ROWS FETCH NEXT {$arr[1]} ROWS ONLY"; } + + return " OFFSET 0 ROWS FETCH NEXT $sql ROWS ONLY"; } public function getPK($query, $pk=null) diff --git a/lib/ext/PdoOne_TestMockup.php b/lib/ext/PdoOne_TestMockup.php index 3244a09..e849957 100644 --- a/lib/ext/PdoOne_TestMockup.php +++ b/lib/ext/PdoOne_TestMockup.php @@ -1,4 +1,5 @@ -parent->order) { - $this->parent->throwError('limit without a sort', ''); - } + //if (!$this->parent->order) { + // $this->parent->throwError('limit without a sort', ''); + //} if (strpos($sql, ',')) { $arr = explode(',', $sql); - $this->parent->limit = " OFFSET {$arr[0]} ROWS FETCH NEXT {$arr[1]} ROWS ONLY"; - } else { - $this->parent->limit = " OFFSET 0 ROWS FETCH NEXT $sql ROWS ONLY"; + return " OFFSET {$arr[0]} ROWS FETCH NEXT {$arr[1]} ROWS ONLY"; } + + return " OFFSET 0 ROWS FETCH NEXT $sql ROWS ONLY"; } public function getPK($query, $pk=null) diff --git a/tests/PdoOne_mysql_Test.php b/tests/PdoOne_mysql_Test.php index 2a04edb..522da03 100644 --- a/tests/PdoOne_mysql_Test.php +++ b/tests/PdoOne_mysql_Test.php @@ -1,4 +1,7 @@ -cacheData[$uid])) { $this->cacheCounter++; return $this->cacheData[$uid]; @@ -56,7 +60,7 @@ public function setCache($uid, $family = '', $data = null, $ttl = null) if (!isset($this->cacheDataFamily[$fam])) { $this->cacheDataFamily[$fam] = []; } - + $this->cacheDataFamily[$fam][] = $uid; $this->cacheData[$uid] = $data; //var_dump($fam); @@ -84,11 +88,11 @@ public function invalidateCache($uid = '', $family = '') $family = [$family]; } foreach ($family as $fam) { - foreach($this->cacheDataFamily[$fam] as $id) { + foreach ($this->cacheDataFamily[$fam] as $id) { unset($this->cacheData[$id]); echo "deleting cache $id\n"; } - $this->cacheDataFamily[$fam]=[]; + $this->cacheDataFamily[$fam] = []; } } //unset($this->cacheData[$uid]); @@ -151,7 +155,7 @@ public function test_connect() $this->pdoOne->connect(); } - function test_chainresetErrorList() + public function test_chainresetErrorList() { $this->pdoOne->logLevel = 3; $rows = $this->pdoOne->genError(false)->select('select 123 field1 from dual222')->toList(); @@ -180,7 +184,7 @@ function test_chainresetErrorList() self::assertNotEmpty($this->pdoOne->errorText); // there is an error. } - function test_chainresetErrorListSimple() + public function test_chainresetErrorListSimple() { $this->pdoOne->logLevel = 3; $rows = $this->pdoOne->genError(false)->select('select 123 field1 from dual222')->toListSimple(); @@ -209,7 +213,7 @@ function test_chainresetErrorListSimple() self::assertNotEmpty($this->pdoOne->errorText); // there is an error. } - function test_genCode() + public function test_genCode() { if (!$this->pdoOne->tableExist('table1')) { $this->pdoOne->createTable('table1', ['id' => 'int']); @@ -221,7 +225,7 @@ function test_genCode() self::assertContains('$pdo->createTable(\'table1', $this->pdoOne->generateCodeCreate('table1')); } - function test_debug() + public function test_debug() { $file = __DIR__ . '/file.txt'; $this->pdoOne->logFile = $file; @@ -231,7 +235,7 @@ function test_debug() $this->pdoOne->logFile = ''; } - function test_chainresetErrorMeta() + public function test_chainresetErrorMeta() { $this->pdoOne->logLevel = 3; $rows = $this->pdoOne->genError(false)->select('select 123 field1 from dual222')->toMeta(); @@ -260,7 +264,7 @@ function test_chainresetErrorMeta() self::assertNotEmpty($this->pdoOne->errorText); // there is an error. } - function test_chainresetErrorFirst() + public function test_chainresetErrorFirst() { $this->pdoOne->logLevel = 3; $rows = $this->pdoOne->genError(false)->select('select 123 field1 from dual222')->first(); @@ -293,7 +297,7 @@ function test_chainresetErrorFirst() //$this->assertEquals([['field1'=>123]],$rows); } - function test_chainresetErrorLast() + public function test_chainresetErrorLast() { $this->pdoOne->logLevel = 3; $rows = $this->pdoOne->genError(false)->select('select 123 field1 from dual222')->last(); @@ -302,6 +306,7 @@ function test_chainresetErrorLast() $rows = $this->pdoOne->genError(true)->select('select 123 field1 from dual222')->last(); $rows = 'XXX'; } catch (Exception $exception) { + var_dump("this message must be visible"); $rows = false; } self::assertEquals(false, $rows); @@ -324,7 +329,7 @@ function test_chainresetErrorLast() } - function test_createtable() + public function test_createtable() { if ($this->pdoOne->tableExist('table5')) { $this->pdoOne->dropTable('table5'); @@ -334,23 +339,23 @@ function test_createtable() self::assertEquals(true, $r); self::assertEquals(array( - 'id' => [ - 'phptype' => 'int', + 'id' => [ + 'phptype' => 'int', 'conversion' => null, - 'type' => 'int', - 'size' => null, - 'null' => false, - 'identity' => false, - 'sql' => 'int not null' + 'type' => 'int', + 'size' => null, + 'null' => false, + 'identity' => false, + 'sql' => 'int not null' ], 'name' => [ - 'phptype' => 'string', + 'phptype' => 'string', 'conversion' => null, - 'type' => 'varchar', - 'size' => '50', - 'null' => true, - 'identity' => false, - 'sql' => 'varchar(50)' + 'type' => 'varchar', + 'size' => '50', + 'null' => true, + 'identity' => false, + 'sql' => 'varchar(50)' ] ), $this->pdoOne->getDefTable('table5')); @@ -359,16 +364,16 @@ function test_createtable() } - function test_chainreset() + public function test_chainreset() { $this->pdoOne->logLevel = 3; $rows = $this->pdoOne->select('select 123 field1 from dual'); - $this->pdoOne->builderReset(); + // $this->pdoOne->builderReset(); $rows = $this->pdoOne->select('select 123 field1 from dual')->toList(); self::assertEquals([['field1' => 123]], $rows); } - function test_cache() + public function test_cache() { $this->pdoOne->getCacheService()->cacheCounter = 0; @@ -381,8 +386,6 @@ function test_cache() self::assertEquals([['field1' => 123]], $rows); - - self::assertEquals(1, $this->pdoOne->getCacheService()->cacheCounter); // 1= cache used 1 time $this->pdoOne->invalidateCache(); $rows = $this->pdoOne->select('select 123 field1 from dual')->useCache() @@ -392,7 +395,7 @@ function test_cache() $this->pdoOne->getCacheService()->cacheCounter = 0; } - function test_cache_expire() + public function test_cache_expire() { $this->pdoOne->getCacheService()->cacheCounter = 0; $this->pdoOne->invalidateCache(); @@ -400,10 +403,10 @@ function test_cache_expire() sleep(2); // enough time to expire the cache. $rows = $this->pdoOne->select('select 123 field1 from dual')->useCache(1, 'dual')->toList(); // +1 cache self::assertEquals(1, $this->pdoOne->getCacheService()->cacheCounter); // 1= cache used 1 time - + } - function test_cache_family() + public function test_cache_family() { $this->pdoOne->getCacheService()->cacheCounter = 0; $this->pdoOne->invalidateCache(); @@ -415,7 +418,7 @@ function test_cache_family() self::assertEquals(2, $this->pdoOne->getCacheService()->cacheCounter); // 1= cache used 1 time } - function test_cache_multiple_family() + public function test_cache_multiple_family() { $this->pdoOne->getCacheService()->cacheCounter = 0; $this->pdoOne->invalidateCache(); @@ -433,7 +436,7 @@ function test_cache_multiple_family() } - function test_cache_noCache() + public function test_cache_noCache() { $this->pdoOne->setCacheService(null); @@ -575,16 +578,166 @@ public function test_open() self::assertEquals(8, $this->pdoOne->getCacheService()->cacheCounter); // 3= cache used 1 time - self::assertEquals([['id_category'=>3]], - $this->pdoOne->select('id_category')->from('product_category')->where('id_category',3)->useCache()->toList()); - self::assertEquals([['id_category'=>4]], - $this->pdoOne->select('id_category')->from('product_category')->where('id_category',4)->useCache()->toList()); - self::assertEquals(['id_category'=>123], + self::assertEquals([['id_category' => 3]], + $this->pdoOne->select('id_category')->from('product_category')->where('id_category', 3)->useCache()->toList()); + self::assertEquals([['id_category' => 4]], + $this->pdoOne->select('id_category')->from('product_category')->where('id_category', 4)->useCache()->toList()); + self::assertEquals(['id_category' => 123], $this->pdoOne->select('id_category')->from('product_category')->order('id_category desc')->useCache()->first()); - self::assertEquals(['id_category'=>2], + self::assertEquals(['id_category' => 2], $this->pdoOne->select('id_category')->from('product_category')->order('id_category')->useCache()->first()); } + public function test_select() + { + //$this->expectException(\Exception::class); + //$this->pdoOne->open(true); + try { + $r = $this->pdoOne->runRawQuery('drop table product_category'); + self::assertEquals(true, $r, 'Drop failed'); + } catch (Exception $e) { + $r = false; + // drops silently + } + + + $sqlT2 = 'CREATE TABLE `product_category` ( + `id_category` INT NOT NULL, + `catname` VARCHAR(45) NULL, + PRIMARY KEY (`id_category`));'; + + try { + $r = $this->pdoOne->runRawQuery($sqlT2); + } catch (Exception $e) { + echo $e->getMessage() . '
'; + } + self::assertEquals(true, $r, 'failed to create table'); + $this->pdoOne->getCacheService()->cacheCounter = 0; + + self::assertGreaterThan(1, count($this->pdoOne->objectList('table'))); + // we add some values + $this->pdoOne->set(['id_category' => 123, 'catname' => 'cheap'])->from('product_category')->insert(); + $this->pdoOne->insert('product_category', ['id_category', 'catname'], + ['id_category' => 2, 'catname' => 'cheap']); + + $this->pdoOne->insert('product_category', ['id_category' => 3, 'catname' => 'cheap']); + + $this->pdoOne->insert('product_category', ['id_category' => 4, 'catname' => 'cheap4']); + $this->pdoOne->insert('product_category', ['id_category', '5', 'catname', 'cheap']); + $query1=$this->pdoOne->select('*')->from('product_category')->where('1=1')->order('id_category'); + $r = $query1->toList(); + $query2=$this->pdoOne->select('*')->from('product_category')->where('catname=?',['cheap'])->order('id_category'); + $r2 = $query2->toList(); + + //var_dump(var_export($r)); + self::assertEquals([ + 0 => + [ + 'id_category' => 2, + 'catname' => 'cheap', + ], + 1 => + [ + 'id_category' => 3, + 'catname' => 'cheap', + ], + 2 => + [ + 'id_category' => 4, + 'catname' => 'cheap4', + ], + 3 => + [ + 'id_category' => 5, + 'catname' => 'cheap', + ], + 4 => + [ + 'id_category' => 123, + 'catname' => 'cheap', + ], + ], $r); + self::assertEquals([ + 0 => + [ + 'id_category' => 2, + 'catname' => 'cheap', + ], + 1 => + [ + 'id_category' => 3, + 'catname' => 'cheap', + ], + 2 => + [ + 'id_category' => 5, + 'catname' => 'cheap', + ], + 3 => + [ + 'id_category' => 123, + 'catname' => 'cheap', + ], + ], $r2); + + $query1=$this->pdoOne->select('*')->from('product_category')->where('1=1')->order('id_category')->useCache(10); + $r = $query1->toList(); + $query2=$this->pdoOne->select('*')->from('product_category')->where('catname=?',['cheap'])->order('id_category')->useCache(10); + $r2 = $query2->toList(); + + //var_dump(var_export($r)); + self::assertEquals([ + 0 => + [ + 'id_category' => 2, + 'catname' => 'cheap', + ], + 1 => + [ + 'id_category' => 3, + 'catname' => 'cheap', + ], + 2 => + [ + 'id_category' => 4, + 'catname' => 'cheap4', + ], + 3 => + [ + 'id_category' => 5, + 'catname' => 'cheap', + ], + 4 => + [ + 'id_category' => 123, + 'catname' => 'cheap', + ], + ], $r); + self::assertEquals([ + 0 => + [ + 'id_category' => 2, + 'catname' => 'cheap', + ], + 1 => + [ + 'id_category' => 3, + 'catname' => 'cheap', + ], + 2 => + [ + 'id_category' => 5, + 'catname' => 'cheap', + ], + 3 => + [ + 'id_category' => 123, + 'catname' => 'cheap', + ], + ], $r2); + } + + public function test_quota() { self::assertEquals('`hello` world', $this->pdoOne->addDelimiter('hello world')); @@ -657,6 +810,9 @@ public function test_emptyargs() public function test_time() { + PdoOne::$dateTimeFormat = 'Y-m-d\TH:i:s\Z'; + PdoOne::$dateTimeMicroFormat = 'Y-m-d\TH:i:s.u\Z'; + PdoOne::$dateFormat = 'Y-m-d'; self::assertEquals('2019-02-06 05:06:07', PdoOne::dateText2Sql('2019-02-06T05:06:07Z', true)); self::assertEquals('2019-02-06 00:00:00', PdoOne::dateText2Sql('2019-02-06', false)); @@ -735,9 +891,9 @@ public function test_rollback() } - public function test_select() + public function test_selectDual() { - self::assertInstanceOf(PdoOne::class, $this->pdoOne->select('select 1 from DUAL')); + self::assertInstanceOf(PdoOneQuery::class, $this->pdoOne->select('select 1 from DUAL')); } public function test_sqlGen() @@ -757,6 +913,7 @@ public function test_sqlGen() self::assertEquals('select 1, 2 from DUAL where field=:field', $this->pdoOne->select(['1', '2'])->from('DUAL')->where('field=:field', [':field' => 20])->sqlGen(true)); + /** @noinspection SqlAggregates */ self::assertEquals('select 1, 2 from DUAL where field=? group by 2 having field2=? order by 1', $this->pdoOne->select(['1', '2'])->from('DUAL')->where('field=?', [20])->order('1')->group('2') ->having('field2=?', [4])->sqlGen(true)); @@ -764,58 +921,58 @@ public function test_sqlGen() public function test_join() { - self::assertInstanceOf(PdoOne::class, $this->pdoOne->join('tablejoin on t1.field=t2.field')); + self::assertInstanceOf(PdoOneQuery::class, $this->pdoOne->join('tablejoin on t1.field=t2.field')); } public function test_from() { - self::assertInstanceOf(PdoOne::class, $this->pdoOne->from('table t1')); + self::assertInstanceOf(PdoOneQuery::class, $this->pdoOne->from('table t1')); } public function test_left() { - self::assertInstanceOf(PdoOne::class, $this->pdoOne->left('table2 on table1.t1=table2.t2')); + self::assertInstanceOf(PdoOneQuery::class, $this->pdoOne->left('table2 on table1.t1=table2.t2')); } public function test_right() { - self::assertInstanceOf(PdoOne::class, $this->pdoOne->right('table2 on table1.t1=table2.t2')); + self::assertInstanceOf(PdoOneQuery::class, $this->pdoOne->right('table2 on table1.t1=table2.t2')); } public function test_where() { - self::assertInstanceOf(PdoOne::class, $this->pdoOne->where('field1=?,field2=?', [20, 'hello'])); + self::assertInstanceOf(PdoOneQuery::class, $this->pdoOne->where('field1=?,field2=?', [20, 'hello'])); } public function test_set() { - self::assertInstanceOf(PdoOne::class, $this->pdoOne->set('field1=?,field2=?', [20, 'hello'])); + self::assertInstanceOf(PdoOneQuery::class, $this->pdoOne->set('field1=?,field2=?', [20, 'hello'])); } public function test_group() { - self::assertInstanceOf(PdoOne::class, $this->pdoOne->group('fieldgroup')); + self::assertInstanceOf(PdoOneQuery::class, $this->pdoOne->group('fieldgroup')); } public function test_having() { - self::assertInstanceOf(PdoOne::class, $this->pdoOne->having('field1=?,field2=?', [20, 'hello'])); + self::assertInstanceOf(PdoOneQuery::class, $this->pdoOne->having('field1=?,field2=?', [20, 'hello'])); } public function test_order() { - self::assertInstanceOf(PdoOne::class, $this->pdoOne->order('name desc')); + self::assertInstanceOf(PdoOneQuery::class, $this->pdoOne->order('name desc')); } public function test_limit() { - self::assertInstanceOf(PdoOne::class, $this->pdoOne->limit('1,10')); + self::assertInstanceOf(PdoOneQuery::class, $this->pdoOne->limit('1,10')); } public function test_distinct() { - self::assertInstanceOf(PdoOne::class, $this->pdoOne->distinct()); + self::assertInstanceOf(PdoOneQuery::class, $this->pdoOne->distinct()); }