Entity.class.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. <?php
  2. require_once __DIR__.'/../constant.php';
  3. require_once(__ROOT__.'class'.SLASH.'Database.class.php');
  4. /**
  5. @version 2
  6. **/
  7. class Entity
  8. {
  9. public $debug = false,$pdo = null;
  10. public $created,$updated,$creator,$updater,$joins;
  11. public static $lastError = '';
  12. public static $lastResult = '';
  13. public static $lastQuery = '';
  14. const ACTIVE = 'published';
  15. const INACTIVE = 'deleted';
  16. public function __construct()
  17. {
  18. if (!isset($this->TABLE_NAME)) {
  19. $this->TABLE_NAME = strtolower(get_called_class());
  20. }
  21. $this->connect();
  22. $this->fields['created'] = 'datetime';
  23. $this->fields['updated'] = 'datetime';
  24. $this->fields['updater'] = 'string';
  25. $this->fields['creator'] = 'string';
  26. $this->joins = array();
  27. $this->created = time();
  28. global $myUser;
  29. if(is_object($myUser) && $myUser->login!='') $this->creator = $myUser->login;
  30. }
  31. public function connect()
  32. {
  33. $this->pdo = Database::instance();
  34. }
  35. public function __toString()
  36. {
  37. foreach ($this->toArray() as $key => $value) {
  38. echo $key.' : '.$value.','.PHP_EOL;
  39. }
  40. }
  41. public static function debug()
  42. {
  43. return array(self::$lastQuery, self::$lastError, self::$lastResult);
  44. }
  45. public function __sleep()
  46. {
  47. return array_keys($this->toArray());
  48. }
  49. public function __wakeup()
  50. {
  51. $this->connect();
  52. }
  53. //Comparaison de deux instances d'une même entité, retourne les champs ayant changés uniquement
  54. public static function compare($obj1,$obj2){
  55. $class = get_called_class();
  56. $instance = new $class();
  57. $compare = array();
  58. foreach ($obj1->fields as $field => $type) {
  59. if($field == 'updated' || $field == 'updater') continue;
  60. if($obj1->$field != $obj2->$field){
  61. if($type=='int' && (($obj1->$field==0 && $obj2->$field =='') || ($obj2->$field=='' && $obj1->$field ==0)) ) continue;
  62. $compare[] = array('field'=>$field,'value1'=>$obj1->$field,'value2'=>$obj2->$field);
  63. }
  64. }
  65. return $compare;
  66. }
  67. public function toArray($decoded=false) {
  68. $fields = array();
  69. foreach ($this->fields as $field => $type) {
  70. $fields[$field] = $decoded ? html_entity_decode($this->$field) : $this->$field;
  71. }
  72. return $fields;
  73. }
  74. public function toText() {
  75. $text = array();
  76. foreach ($this->fields as $field => $type) {
  77. $value = is_object($this->$field) ? '[object]' : $this->$field;
  78. $text[]= $field.' = '.$value;
  79. }
  80. return implode(', ',$text);
  81. }
  82. public function fromArray($array) {
  83. foreach ($array as $field => $value) {
  84. $this->$field = $value;
  85. }
  86. }
  87. public function sgbdType($type) {
  88. $sgbd = BASE_SGBD;
  89. $types = $sgbd::types();
  90. return isset($types[$type]) ? $types[$type] : $types['default'];
  91. }
  92. public function closeDatabase() {
  93. // $this->close();
  94. }
  95. //Génère un slug unique pour l'element de liste utilisable dans le code
  96. // $label => string du label à slugifier
  97. // $column => la colonne en base où checker les slugs existants
  98. public static function generateSlug($label, $column){
  99. $slug = slugify($label);
  100. $class = get_called_class();
  101. $item = new $class();
  102. if(!array_key_exists($column, $item->fields)) return;
  103. $params = array_key_exists('state', $item->fields) ? array('state'=>$class::ACTIVE) : array();
  104. $i='';
  105. while($class::rowCount(array_merge(array($column=>$slug.$i), $params)) > 0 ) $i++;
  106. return $i==''?$slug:$slug.'-'.$i;
  107. }
  108. public static function tableName()
  109. {
  110. $class = get_called_class();
  111. $instance = new $class();
  112. return ENTITY_PREFIX.$instance->TABLE_NAME;
  113. }
  114. // GESTION SQL
  115. /**
  116. * Verifie l'existence de la table en base de donnée.
  117. *
  118. * @author Valentin CARRUESCO
  119. *
  120. * @category manipulation SQL
  121. *
  122. * @param <String> créé la table si elle n'existe pas
  123. *
  124. * @return true si la table existe, false dans le cas contraire
  125. */
  126. public static function checkTable($autocreate = false)
  127. {
  128. $class = get_called_class();
  129. $instance = new $class();
  130. $query = 'SELECT count(*) as numRows FROM sqlite_master WHERE type="table" AND name=?';
  131. $statement = $instance->customQuery($query, array($instance->tableName()));
  132. if ($statement != false) {
  133. $statement = $statement->fetchArray();
  134. if ($statement['numRows'] == 1) {
  135. $return = true;
  136. }
  137. }
  138. if ($autocreate && !$return) self::create();
  139. return $return;
  140. }
  141. public static function install($classDirectory)
  142. {
  143. foreach (glob($classDirectory.SLASH.'*.class.php') as $file) {
  144. $infos = explode('.', basename($file));
  145. $class = array_shift($infos);
  146. require_once($classDirectory.SLASH.$class.'.class.php');
  147. $reflection = new ReflectionClass($class);
  148. if (!class_exists($class) || !method_exists($class, 'create') || $class == get_class() || $reflection->isAbstract()) {
  149. continue;
  150. }
  151. $class::create();
  152. }
  153. }
  154. public static function uninstall($classDirectory)
  155. {
  156. foreach (glob($classDirectory.SLASH.'*.class.php') as $file) {
  157. $infos = explode('.', basename($file));
  158. $class = array_shift($infos);
  159. require_once($classDirectory.SLASH.$class.'.class.php');
  160. $reflection = new ReflectionClass($class);
  161. if (!class_exists($class) || !method_exists($class, 'drop') || $class == get_class() || $reflection->isAbstract()) continue;
  162. $class::drop();
  163. }
  164. }
  165. /**
  166. * Methode de vidage de l'entité.
  167. *
  168. * @author Valentin CARRUESCO
  169. * @category manipulation SQL
  170. * @return Aucun retour
  171. */
  172. public static function truncate()
  173. {
  174. $class = get_called_class();
  175. $instance = new $class();
  176. $sgbd = BASE_SGBD;
  177. $sql = $sgbd::truncate();
  178. $query = Entity::render($sql,array(
  179. 'table' => $instance->tableName()
  180. ));
  181. $instance->customExecute($query);
  182. }
  183. /**
  184. * Methode de creation de l'entité.
  185. *
  186. * @author Valentin CARRUESCO
  187. * @category manipulation SQL
  188. *
  189. * @return Aucun retour
  190. */
  191. public static function create()
  192. {
  193. $class = get_called_class();
  194. $instance = new $class();
  195. $fields = array();
  196. foreach ($instance->fields as $field => $type)
  197. $fields[$field] = $instance->sgbdType($type);
  198. $sgbd = BASE_SGBD;
  199. $sql = $sgbd::create();
  200. $query = Entity::render($sql,array(
  201. 'table' => $instance->tableName(),
  202. 'fields' => $fields
  203. ));
  204. $instance->customExecute($query);
  205. if(isset($instance->indexes)) $instance->index($instance->indexes);
  206. }
  207. public static function drop()
  208. {
  209. $class = get_called_class();
  210. $instance = new $class();
  211. $sgbd = BASE_SGBD;
  212. $sql = $sgbd::drop();
  213. $query = Entity::render($sql,array(
  214. 'table' => $instance->tableName()
  215. ));
  216. $instance->customExecute($query);
  217. if(isset($instance->indexes)) $instance->index($instance->indexes,false);
  218. }
  219. /**
  220. * Methode d'insertion ou de modifications d'elements de l'entité.
  221. *
  222. * @author Valentin CARRUESCO
  223. *
  224. * @category manipulation SQL
  225. *
  226. * @param Aucun
  227. *
  228. * @return Aucun retour
  229. */
  230. public function save()
  231. {
  232. $this->updated = time();
  233. global $myUser;
  234. if(is_object($myUser) && $myUser->login!='') $this->updater = $myUser->login;
  235. $data = array();
  236. $numericType = array('object', 'timestamp', 'datetime', 'date', 'int', 'float', 'decimal');
  237. $stringType = array('string', 'longstring', 'default');
  238. if (isset($this->id) && $this->id > 0) {
  239. $fields = array();
  240. $i = 0;
  241. foreach ($this->fields as $field => $type) {
  242. if ($type == 'key') continue;
  243. $data[':'.$i] = $this->{$field};
  244. if($type=='boolean') $data[':'.$i] = $data[':'.$i] ? 1:0;
  245. //if(in_array($type, $numericType) && !$data[':'.$i]) $data[':'.$i] = 0;
  246. //if(in_array($type, $stringType) && !$data[':'.$i]) $data[':'.$i] = '';
  247. $fields[$field] = ':'.$i;
  248. $i++;
  249. }
  250. $data[':id'] = $this->id;
  251. $sgbd = BASE_SGBD;
  252. $sql = $sgbd::update();
  253. $query = self::render($sql,array(
  254. 'table' => $this->tableName(),
  255. 'fields' => $fields,
  256. 'filters' => array('id'=>':id'),
  257. ));
  258. } else {
  259. $fields = array();
  260. $i = 0;
  261. foreach ($this->fields as $field => $type) {
  262. if ($type == 'key') continue;
  263. $data[':'.$i] = $this->{$field};
  264. if($type=='boolean') $data[':'.$i] = $data[':'.$i] ? 1:0;
  265. //if(in_array($type, $numericType) && !$data[':'.$i]) $data[':'.$i] = 0;
  266. //if(in_array($type, $stringType) && !$data[':'.$i]) $data[':'.$i] = '';
  267. $fields[$field] = ':'.$i;
  268. $i++;
  269. }
  270. $sgbd = BASE_SGBD;
  271. $sql = $sgbd::insert();
  272. $query = self::render($sql,array(
  273. 'table' => $this->tableName(),
  274. 'fields' => $fields
  275. ));
  276. }
  277. $this->customExecute($query, $data);
  278. $this->id = !isset($this->id) || !is_numeric($this->id) ? $this->pdo->lastInsertId() : $this->id;
  279. }
  280. /**
  281. * Méthode de modification d'éléments de l'entité.
  282. *
  283. * @author Valentin CARRUESCO
  284. *
  285. * @category manipulation SQL
  286. *
  287. * @param <Array> $colonnes=>$valeurs
  288. * @param <Array> $colonnes (WHERE) =>$valeurs (WHERE)
  289. * @param <String> $operation="=" definis le type d'operateur pour la requete select
  290. *
  291. * @return Aucun retour
  292. */
  293. public static function change($columns, $columns2 = array(), $operation = '=')
  294. {
  295. $class = get_called_class();
  296. $instance = new $class();
  297. $fields = array();
  298. $i = 0;
  299. foreach ($columns as $field => $value) {
  300. $data[':'.$i] = $value;
  301. $fields[$field] = ':'.$i;
  302. $i++;
  303. }
  304. $filters = array();
  305. $i = 0;
  306. foreach ($columns2 as $field => $value) {
  307. $data[':_'.$i] = $value;
  308. $filters[$field] = ':_'.$i;
  309. $i++;
  310. }
  311. $sgbd = BASE_SGBD;
  312. $sql = $sgbd::update();
  313. $query = Entity::render($sql,array(
  314. 'table' => $instance->tableName(),
  315. 'fields' => $fields,
  316. 'filters' => $filters,
  317. ));
  318. $instance->customExecute($query, $data);
  319. }
  320. /**
  321. * Méthode de selection de tous les elements de l'entité.
  322. *
  323. * @author Valentin CARRUESCO
  324. *
  325. * @category manipulation SQL
  326. *
  327. * @param <String> $ordre=null
  328. * @param <String> $limite=null
  329. *
  330. * @return <Array<Entity>> $Entity
  331. */
  332. public static function populate($order = null, $limit = null,$selColumn = array('*'),$joins = 0)
  333. {
  334. $results = self::loadAll(array(), $order, $limit,$selColumn,$joins);
  335. return $results;
  336. }
  337. /**
  338. * Méthode de selection multiple d'elements de l'entité.
  339. *
  340. * @author Valentin CARRUESCO
  341. *
  342. * @category manipulation SQL
  343. *
  344. * @param <Array> $colonnes (WHERE)
  345. * @param <Array> $valeurs (WHERE)
  346. * @param <String> $ordre=null
  347. * @param <String> $limite=null
  348. * @param <String> $operation="=" definis le type d'operateur pour la requete select
  349. *
  350. * @return <Array<Entity>> $Entity
  351. */
  352. public static function loadAll($columns = array(), $order = null, $limit = null, $selColumn = array('*'),$joins = 0)
  353. {
  354. $values = array();
  355. $i=0;
  356. foreach($columns as $key=>$value){
  357. //Gestion du IN
  358. if(strlen($key)>=3 && substr($key,-3) == ':IN'){
  359. $columns[$key] = array();
  360. foreach (explode(',',$value) as $v2) {
  361. $tag = ':'.$i;
  362. $columns[$key][]= $tag;
  363. $values[$tag] = $v2;
  364. $i++;
  365. }
  366. $columns[$key] = implode(',',$columns[$key]);
  367. //Gestion des opérateurs classiques
  368. } else {
  369. $tag = ':'.$i;
  370. $columns[$key] = $tag;
  371. $values[$tag] = $value;
  372. $i++;
  373. }
  374. }
  375. $class = get_called_class();
  376. $instance = new $class();
  377. $data = array(
  378. 'table' => $instance->tableName(),
  379. 'selected' => $selColumn,
  380. 'limit' => !isset($limit) || count($limit) == 0 ? null: $limit,
  381. 'orderby' => !isset($order) || count($order) == 0 ? null: $order,
  382. 'filter' => !isset($columns) || count($columns) == 0 ? null: $columns
  383. );
  384. $data['joins'] = array();
  385. if($joins!=0){
  386. foreach ($data['selected'] as $k=>$column) {
  387. $data['selected'][$k] = $instance->tableName().'.'.$column;
  388. }
  389. $data = self::recursiveJoining($instance,$data,$joins);
  390. }
  391. $sgbd = BASE_SGBD;
  392. $sql = $sgbd::select();
  393. $sql = Entity::render($sql,$data);
  394. return $instance->customQuery($sql, $values, true,$joins);
  395. }
  396. /**
  397. * Méthode privée de gestion du join récursif sur les objets liés
  398. *
  399. * @author Valentin CARRUESCO
  400. *
  401. * @category manipulation SQL
  402. *
  403. * @param <Object> $instance $instance de départ
  404. * @param <Array> $data Tableau de construction de la requete via render()
  405. * @param <Int> $iterations Nombre d'iteration réecurive maximum
  406. *
  407. * @return <Array> $data
  408. */
  409. private static function recursiveJoining($instance,$data,$iterations){
  410. if($iterations==0) return $data;
  411. $iterations--;
  412. if(isset($instance->links)){
  413. foreach ($instance->links as $field => $className) {
  414. $field2 = 'id';
  415. $classField = explode('.',$className);
  416. if(isset($classField[1]))
  417. list($className,$field2) = $classField;
  418. $joinInstance = new $className();
  419. foreach ($joinInstance->fields as $key=>$type) {
  420. $data['selected'][] = $className::tableName().'.'.$key.' as '.$className::tableName().'_join_'.$key;
  421. }
  422. $data['joins'][] = array(
  423. 'jointable1' => $instance::tableName(),
  424. 'jointable2' => $className::tableName(),
  425. 'field1' => $field,
  426. 'field2' => $field2
  427. );
  428. $data = self::recursiveJoining($joinInstance,$data,$iterations);
  429. }
  430. }
  431. return $data;
  432. }
  433. /**
  434. * Methode de comptage des éléments de l'entité.
  435. *
  436. * @author Valentin CARRUESCO
  437. *
  438. * @category manipulation SQL
  439. * @return<Integer> nombre de ligne dans l'entité'
  440. */
  441. public static function rowCount($columns = null) {
  442. $values = array();
  443. $i=0;
  444. foreach($columns as $key=>$value){
  445. $tag = ':'.$i;
  446. $columns[$key] = $tag;
  447. $values[$tag] = $value;
  448. $i++;
  449. }
  450. $class = get_called_class();
  451. $instance = new $class();
  452. $data = array(
  453. 'table' => $class::tableName(),
  454. 'selected' => 'id' ,
  455. 'filter' => count($columns) == 0 ? null: $columns
  456. );
  457. $sgbd = BASE_SGBD;
  458. $sql = $sgbd::count();
  459. $execQuery = $instance->customQuery(Entity::render($sql,$data), $values);
  460. $row = $execQuery->fetch();
  461. return $row['number'];
  462. }
  463. public static function loadAllOnlyColumn($selColumn, $columns, $order = null, $limit = null)
  464. {
  465. $objects = self::loadAll($columns, $order, $limit, $operation, $selColumn);
  466. if (count($objects) == 0) {
  467. $objects = array();
  468. }
  469. return $objects;
  470. }
  471. /**
  472. * Méthode de selection unique d'élements de l'entité.
  473. *
  474. * @author Valentin CARRUESCO
  475. *
  476. * @category manipulation SQL
  477. *
  478. * @param <Array> $colonnes (WHERE)
  479. * @param <Array> $valeurs (WHERE)
  480. * @param <String> $operation="=" definis le type d'operateur pour la requete select
  481. *
  482. * @return <Entity> $Entity ou false si aucun objet n'est trouvé en base
  483. */
  484. public static function load($columns,$joins =0) {
  485. $objects = self::loadAll($columns, null, array('1'),array('*'),$joins);
  486. if (!isset($objects[0])) $objects[0] = false;
  487. return $objects[0];
  488. }
  489. /**
  490. * Méthode de selection unique d'élements de l'entité.
  491. *
  492. * @author Valentin CARRUESCO
  493. *
  494. * @category manipulation SQL
  495. *
  496. * @param <Array> $colonnes (WHERE)
  497. * @param <Array> $valeurs (WHERE)
  498. * @param <String> $operation="=" definis le type d'operateur pour la requete select
  499. *
  500. * @return <Entity> $Entity ou false si aucun objet n'est trouvé en base
  501. */
  502. public static function getById($id,$joins =0 ) {
  503. return self::load(array('id' => $id),$joins);
  504. }
  505. public static function render($sql,$data=array()) {
  506. //loop
  507. $sql = preg_replace_callback('/{{\:([^\/\:\?}]*)}}(.*?){{\/\:[^\/\:\?}]*}}/',function($matches) use ($data) {
  508. $tag = $matches[1];
  509. $sqlTpl = $matches[2];
  510. $sql = '';
  511. if(isset($data[$tag])){
  512. $i = 0;
  513. $values = $data[$tag];
  514. if($tag =='joins'){
  515. //joins
  516. foreach($values as $join){
  517. $occurence = $sqlTpl;
  518. foreach($join as $key=>$value){
  519. $occurence = str_replace(array('{{'.$key.'}}'),array($value),$occurence);
  520. }
  521. $sql.= $occurence;
  522. }
  523. } else {
  524. //filters
  525. foreach($values as $key=>$value){
  526. $i++;
  527. $last = $i == count($values);
  528. $operator = isset($data['operator']) ? $data['operator'][0] : '=';
  529. $postoperator = isset($data['postoperator']) ? $data['postoperator'][0] : '';
  530. if(strpos($key,':')!==false){
  531. $infos = explode(':',$key);
  532. $key = $infos[0];
  533. $operator = $infos[1];
  534. if($operator=='IN'){
  535. $operator = 'IN(';
  536. $postoperator = ')';
  537. }
  538. }
  539. $occurence = str_replace(array('{{key}}','{{value}}','{{operator}}','{{postoperator}}'),array($key,$value,$operator,$postoperator),$sqlTpl);
  540. $occurence = preg_replace_callback('/{{\;}}(.*?){{\/\;}}/',function($matches) use ($last){
  541. return $last? '': $matches[1];
  542. },$occurence);
  543. $sql.= $occurence;
  544. }
  545. }
  546. return $sql;
  547. }
  548. return '';
  549. },$sql);
  550. //conditions
  551. $sql = preg_replace_callback('/{{\?([^\/\:\?}]*)}}(.*?){{\/\?[^\/\:\?}]*}}/',function($matches) use ($data) {
  552. $key = $matches[1];
  553. $sql = $matches[2];
  554. return !isset($data[$key]) || (is_array($data[$key]) && count($data[$key])==0) ?'':$sql;
  555. },$sql);
  556. //simple vars
  557. $sql = preg_replace_callback('/{{([^\/\:\;\?}]*)}}/',function($matches) use ($data) {
  558. $key = $matches[1];
  559. return isset($data[$key])?$data[$key]:'';
  560. },$sql);
  561. return $sql;
  562. }
  563. /**
  564. * Methode de définition de l'éxistence d'un moins un des éléments spécifiés en base.
  565. *
  566. * @author Valentin CARRUESCO
  567. *
  568. * @category manipulation SQL
  569. * @return<boolean> existe (true) ou non (false)
  570. */
  571. public static function exist($columns = null) {
  572. $result = self::rowCount($columns);
  573. return $result != 0;
  574. }
  575. public static function deleteById($id) {
  576. self::delete(array('id' => $id));
  577. }
  578. /**
  579. * Méthode de Suppression d'elements de l'entité.
  580. *
  581. * @author Valentin CARRUESCO
  582. *
  583. * @category manipulation SQL
  584. *
  585. * @param <Array> $colonnes (WHERE)
  586. * @param <Array> $valeurs (WHERE)
  587. * @param <String> $operation="=" definis le type d'operateur pour la requete select
  588. *
  589. * @return Aucun retour
  590. */
  591. public static function delete($columns, $limit = array()) {
  592. $values = array();
  593. $i=0;
  594. foreach($columns as $key=>$value){
  595. $tag = ':'.$i;
  596. $columns[$key] = $tag;
  597. $values[$tag] = $value;
  598. $i++;
  599. }
  600. $class = get_called_class();
  601. $instance = new $class();
  602. $data = array(
  603. 'table' => $class::tableName(),
  604. 'limit' => count($limit) == 0 ? null: $limit,
  605. 'filter' => count($columns) == 0 ? null: $columns
  606. );
  607. $sgbd = BASE_SGBD;
  608. $sql = $sgbd::delete();
  609. return $instance->customExecute(Entity::render($sql,$data), $values);
  610. }
  611. /**
  612. * Méthode d'indexation de la ou les colonnes ciblées
  613. * nb : il est possible d'appeller automatiquement cette méthode sur les classes entity lors du create
  614. * si la classe contient l'attribut $this->indexes = array(...);
  615. * @author Valentin CARRUESCO
  616. *
  617. * @category manipulation SQL
  618. *
  619. * @param <Array> | <String> $colonne(s)
  620. * @param <Boolean> Mode (true : ajout, false : suppression)
  621. *
  622. * @return Aucun retour
  623. */
  624. public static function index($columns,$mode = true){
  625. if(!is_array($columns)) $columns = array($columns);
  626. $columns = array_filter($columns);
  627. $sgbd = BASE_SGBD;
  628. $class = get_called_class();
  629. foreach($columns as $column){
  630. if(!is_array($column)) $column = array($column);
  631. $data = array(
  632. 'table' => $class::tableName(),
  633. 'column' => '`'.implode('`,`',$column).'`',
  634. 'index_name' => $class::tableName().'_'.implode('_',$column),
  635. );
  636. $results = $class::staticQuery(Entity::render($sgbd::count_index(),$data));
  637. $exists = $results->fetch();
  638. if($mode){
  639. if($exists['exists'] != 1) $class::staticQuery(Entity::render($sgbd::create_index(),$data));
  640. }else{
  641. if($exists['exists'] > 0) $class::staticQuery(Entity::render($sgbd::drop_index(),$data));
  642. }
  643. }
  644. }
  645. public static function paginate($itemPerPage,$currentPage,&$query,$data){
  646. global $_;
  647. $class = get_called_class();
  648. $obj = new $class();
  649. $keys = array_keys($obj->fields, 'key');
  650. $key = count($keys) == 1 ? $keys[0] : 'id';
  651. $queryNumber = $query;
  652. $queryNumber = preg_replace("/(SELECT.+[\n|\t]*)FROM[\s\t\r\n]/iU", 'SELECT DISTINCT '.$obj->tableName().'.'.$key.' FROM ',$queryNumber);
  653. $queryNumber = $class::staticQuery('SELECT COUNT(*) FROM ('.$queryNumber.') number',$data)->fetch();
  654. $number = $queryNumber[0];
  655. $pageNumber = $number / $itemPerPage;
  656. if($currentPage > $pageNumber) $currentPage = 0;
  657. $limit = ' LIMIT '.($currentPage*$itemPerPage).','.$itemPerPage;
  658. $query .= $limit;
  659. return array(
  660. 'pages' => $pageNumber,
  661. 'current' => $currentPage,
  662. 'total' => $number
  663. );
  664. }
  665. public function customExecute($query, $data = array()) {
  666. self::$lastQuery = $query;
  667. try {
  668. if(BASE_DEBUG) self::logFile($query.' :: '.json_encode($data, JSON_UNESCAPED_UNICODE));
  669. $stm = $this->pdo->prepare($query);
  670. $stm->execute($data);
  671. } catch (Exception $e) {
  672. self::$lastError = $this->pdo->errorInfo();
  673. //Log l'erreur uniquement si elle ne proviens pas elle même de Log (evite les mysql gone away)
  674. if(get_class($this) !='Log')
  675. Log::put("[SQL ERROR] - ".$e->getMessage().' - '.$e->getLine().' - Requete : '.$query.' - Données : '.json_encode($data,JSON_UNESCAPED_UNICODE));
  676. if(BASE_DEBUG) self::logFile( "Erreur : ".$e->getMessage());
  677. throw new Exception($e->getMessage().' - '.$e->getLine().' : '.$query.' - '.json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
  678. }
  679. }
  680. public static function provide($parameter = 'id',$join=0){
  681. global $_;
  682. $class = get_called_class();
  683. return !empty($_[$parameter]) ? $class::getById($_[$parameter],$join) : new $class() ;
  684. }
  685. public static function staticQuery($query, $data = array(), $fill = false,$joins = 0) {
  686. $class = get_called_class();
  687. $instance = new $class();
  688. return $instance->customQuery($query, $data, $fill,$joins);
  689. }
  690. public function customQuery($query, $data = array(), $fill = false,$joins = 0) {
  691. $query = str_replace('{{table}}', '`'.$this->tableName().'`', $query);
  692. self::$lastQuery = $query;
  693. try{
  694. if(BASE_DEBUG) self::logFile($query.' :: '.json_encode($data, JSON_UNESCAPED_UNICODE));
  695. $results = $this->pdo->prepare($query);
  696. $results->execute($data);
  697. if (!$results) throw new Exception(json_encode($this->pdo->errorInfo()));
  698. }catch(Exception $e){
  699. self::$lastError = $e->getMessage();
  700. //echo "[SQL ERROR] - Erreur : ".$e->getMessage().' - Requete : '.$query.' - Données : '.json_encode($data);
  701. Log::put("[SQL ERROR] - Erreur : ".$e->getMessage().' - Requete : '.$query.' - Données : '.json_encode($data, JSON_UNESCAPED_UNICODE));
  702. if(BASE_DEBUG) self::logFile( "Erreur : ".$e->getMessage());
  703. throw $e;
  704. }
  705. if (!$fill) return $results;
  706. $class = get_class($this);
  707. $objects = array();
  708. $results = $results->fetchAll();
  709. self::$lastResult = $results;
  710. foreach ($results as $queryReturn) {
  711. $object = new $class();
  712. foreach ($this->fields as $field => $type) {
  713. if (isset($queryReturn[$field])) {
  714. $object->{$field} = $queryReturn[$field];
  715. }
  716. }
  717. if($joins>0) $object = self::recursiveJoiningFill($object,$queryReturn,$joins);
  718. $objects[] = $object;
  719. unset($object);
  720. }
  721. return $objects == null ? array() : $objects;
  722. }
  723. private static function recursiveJoiningFill($object,$queryReturn,$iterations){
  724. if($iterations == 0) return $object;
  725. $iterations--;
  726. if(isset($object->links)){
  727. foreach ($object->links as $link=>$classLink) {
  728. $classField = explode('.',$classLink);
  729. if(isset($classField[1]))
  730. $classLink = $classField[0];
  731. $instanceLink = new $classLink();
  732. foreach ($instanceLink->fields as $field => $type) {
  733. if (isset($queryReturn[$classLink::tableName().'_join_'.$field]))
  734. $instanceLink->{$field} = $queryReturn[$classLink::tableName().'_join_'.$field];
  735. }
  736. $instanceLink = self::recursiveJoiningFill($instanceLink,$queryReturn,$iterations);
  737. $object->joins[$link] = $instanceLink;
  738. }
  739. }
  740. return $object;
  741. }
  742. /**
  743. * Récupere l'objet join ex : $contact->join("adress")->street; --> récupere l'attribut street de la class Adress dont l'id est spécifié dans la colonne adress de la class Contact
  744. * Nb : cette méthode ne fonctionne que si vous avez placé le parametre joins > 0 dans la méthode LoadALl
  745. * Nb : cette méthode ne fonctionne que si vous avez précisé le lien entre Contact et Adress dans la classe Contact via :
  746. protected $links = array(
  747. 'address' => 'Address'
  748. );
  749. *
  750. * @author Valentin CARRUESCO
  751. * @category manipulation SQL
  752. *
  753. * @param <Array> $colonnes (WHERE)
  754. * @param <Array> $valeurs (WHERE)
  755. * @param <String> $operation="=" definis le type d'operateur pour la requete select
  756. *
  757. * @return Aucun retour
  758. */
  759. public function join($field){
  760. return isset($this->joins[$field])?$this->joins[$field]:'';
  761. }
  762. public static function logFile($msg){
  763. file_put_contents(__DIR__.SLASH.'..'.SLASH.'sql.debug.sql', date('H:i:s').' | '.$msg.PHP_EOL,FILE_APPEND);
  764. }
  765. /*
  766. public function __get($name) {
  767. $pos = strpos($name, '_object');
  768. if ($pos !== false) {
  769. $field = strtolower(substr($name, 0, $pos));
  770. if (array_key_exists($field, $this->fields)) {
  771. $class = ucfirst($field);
  772. return $class::getById($this->{$field});
  773. }
  774. }
  775. throw new Exception('Attribut '.get_class($this)."->$name non existant");
  776. }
  777. */
  778. public static function log_executed_query($string, $data) {
  779. $indexed=$data==array_values($data);
  780. foreach($data as $k=>$v) {
  781. if(is_string($v)) $v="'$v'";
  782. if($indexed) $string=preg_replace('/\?/',$v,$string,1);
  783. else $string=str_replace(":$k",$v,$string);
  784. }
  785. self::logFile($string);
  786. }
  787. }