AbstractMongo.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. <?php
  2. /**
  3. * Questa classe fa da interfaccia verso MongoDb
  4. *
  5. * @author Paolo Libertini <paolo.libertini@thinkopen.it>
  6. * @method getCollection()
  7. * @method sendAsArray()
  8. * @method getCollectionData()
  9. * @method loadByAttribute()
  10. * @method load()
  11. * @method addFieldToFilter($_attributeName, $_value)
  12. * @method addFieldToSelect($_attributeName)
  13. */
  14. class Mooses_AbstractMongo extends Mooses_Mongodb_Mongo_Document {
  15. const ASC = "ASC";
  16. protected static $_db;
  17. protected static $_collection;
  18. protected static $_instance;
  19. protected $calledClass;
  20. protected $_classMethods = array();
  21. protected static $_order = array();
  22. protected $_queryConditions = array();
  23. protected $_fields = array();
  24. protected $_sorterArray = array();
  25. protected $_sorterAttribute = "";
  26. protected $_sorterDate = false;
  27. protected $_sorterOrder = self::ASC;
  28. protected $_forceNoCachePaginatedCollection = false;
  29. public $loadKey = array();
  30. protected $_dataUpdate = array();
  31. protected $_sendAsArray = false;
  32. protected $_pageLimit = false;
  33. protected $_page = false;
  34. public function __construct($_data = []) {
  35. $this->_setCollectionAndDatabase();
  36. parent::__construct($_data, array());
  37. $this->calledClass = get_called_class();
  38. self::$_instance = $this;
  39. return $this;
  40. }
  41. private function _setCollectionAndDatabase(){
  42. $_mongoInformations = $this->_retrieveTableName();
  43. self::$_db = $_mongoInformations['db'];
  44. static::$_db = $_mongoInformations['db'];
  45. self::$_collection = $_mongoInformations['collection'];
  46. static::$_collection = $_mongoInformations['collection'];
  47. $this->_classMethods = get_class_methods($this);
  48. }
  49. public static function getInstance() {
  50. if (self::$_instance === NULL || self::$_instance->calledClass !== get_called_class()) {
  51. $_className = get_called_class();
  52. self::$_instance = new $_className();
  53. }
  54. return self::$_instance;
  55. }
  56. protected static function ___retrieveTableName(){
  57. $_return = array();
  58. $annotations = new ReflectionClass(get_called_class());
  59. $_tableSuffix = NULL;
  60. $_tablePrefix = "re_";
  61. $_databaseAnnotated = self::_parseDocComment($annotations->getDocComment(), "@Db");
  62. $_collectionAnnotated = self::_parseDocComment($annotations->getDocComment(), "@Collection");
  63. $_return['collection'] = (stristr($_collectionAnnotated, $_tableSuffix)) ? $_collectionAnnotated : $_tablePrefix . $_collectionAnnotated . $_tableSuffix;
  64. $_return['db'] = $_databaseAnnotated;
  65. return $_return;
  66. }
  67. protected function _retrieveTableName() {
  68. return self::___retrieveTableName();
  69. }
  70. private static function _parseDocComment($str, $tag = ''){
  71. if (empty($tag)) {
  72. return $str;
  73. }
  74. $matches = array();
  75. preg_match("/" . $tag . ":(.*)(\\r\\n|\\r|\\n)/U", $str, $matches);
  76. if (isset($matches[1])) {
  77. return trim($matches[1]);
  78. }
  79. return '';
  80. }
  81. protected function ___sendAsArray(){
  82. $this->_sendAsArray = true;
  83. return $this;
  84. }
  85. protected function _convertMongoCursor($_mongoCursor, $_forceArray = false, $_order = false){
  86. $_calledClass = $_mongoCursor->getDocumentClass();
  87. if($_mongoCursor->count() == 0){
  88. return (($_forceArray) ? array() : false);
  89. } elseif($_mongoCursor->count() == 1){
  90. $_object = new $_calledClass($_mongoCursor->next());
  91. $_object->loadKey = $this->loadKey;
  92. $_object = (($_forceArray) ? array($_object) : $_object);
  93. return $_object;
  94. } else {
  95. $_arrayResults = array();
  96. while($_result = $_mongoCursor->next()){
  97. $_object = new $_calledClass($_result);
  98. $_object->loadKey = $this->_loadKey;
  99. array_push($_arrayResults, (($this->_sendAsArray && $_order === false) ? $_object->getData() : $_object));
  100. }
  101. if($_order !== false && is_array($_order)){
  102. $this->_sorterAttribute = $_order[0];
  103. $this->_sorterOrder = $_order[1];
  104. $this->_sorterDate = (($_order[2] === true) ? true : false);
  105. $_orderer = function($a, $b){
  106. if($this->_sorterDate === false) {
  107. if (strcasecmp((string)$a->getData($this->_sorterAttribute), (string)$b->getData($this->_sorterAttribute)) > 0) {
  108. return (($this->_sorterOrder == self::ASC) ? 1 : -1);
  109. } else {
  110. return (($this->_sorterOrder == self::ASC) ? -1 : 1);
  111. }
  112. } else {
  113. if(is_a($a, "Default_Model_Mapper_Utenti")) {
  114. $_datiA = $a->getAllData();
  115. } else {
  116. $_datiA = $a->getData();
  117. }
  118. if(is_array($_datiA[$this->_sorterAttribute]) && isset($_datiA[$this->_sorterAttribute]['date'])) {
  119. $_dataA = DateTime::createFromFormat("Y-m-d H:i:s", substr($_datiA[$this->_sorterAttribute]['date'], 0, 19));
  120. } else {
  121. $_dataA = DateTime::createFromFormat("Y-m-d H:i:s", substr($_datiA[$this->_sorterAttribute], 0, 19));
  122. }
  123. if(is_a($b, "Default_Model_Mapper_Utenti")) {
  124. $_datiB = $b->getAllData();
  125. } else {
  126. $_datiB = $b->getData();
  127. }
  128. if(is_array($_datiB[$this->_sorterAttribute]) && isset($_datiB[$this->_sorterAttribute]['date'])) {
  129. $_dataB = DateTime::createFromFormat("Y-m-d H:i:s", substr($_datiB[$this->_sorterAttribute]['date'], 0, 19));
  130. } else {
  131. $_dataB = DateTime::createFromFormat("Y-m-d H:i:s", substr($_datiB[$this->_sorterAttribute], 0, 19));
  132. }
  133. if($_dataA > $_dataB){
  134. return (($this->_sorterOrder == self::ASC) ? 1 : -1);
  135. } else {
  136. return (($this->_sorterOrder == self::ASC) ? -1 : 1);
  137. }
  138. }
  139. };
  140. usort($_arrayResults, $_orderer);
  141. }
  142. return $_arrayResults;
  143. }
  144. }
  145. protected function ___setPageSize($_limit){
  146. $this->_pageLimit = $_limit;
  147. return $this;
  148. }
  149. protected function ___setPage($_page){
  150. $this->_page = $_page;
  151. return $this;
  152. }
  153. public static function __callStatic($name, $arguments)
  154. {
  155. if(get_called_class() != get_class(self::$_instance)){
  156. $_mongoInformations = self::_retrieveTableName();
  157. self::$_db = $_mongoInformations['db'];
  158. static::$_db = $_mongoInformations['db'];
  159. self::$_collection = $_mongoInformations['collection'];
  160. static::$_collection = $_mongoInformations['collection'];
  161. }
  162. }
  163. public function __call($methodName, $params = null) {
  164. if(get_called_class() != get_class(self::$_instance)){
  165. $this->_setCollectionAndDatabase();
  166. }
  167. $_methodsAllowed = array("setData", "setProperty", "getData", "getProperty");
  168. $methodPrefix = substr($methodName, 0, 3);
  169. $key = strtolower(substr($methodName, 3));
  170. $_isUppercase = ctype_upper(substr($methodName, 3, 1));
  171. if (in_array("___".$methodName, $_methodsAllowed) || in_array("___".$methodName, $this->_classMethods)) {
  172. return call_user_func_array(array($this, "___".$methodName), $params);
  173. } elseif ($methodPrefix == 'set' && count($params) == 1 && $_isUppercase) {
  174. $value = ((is_string($params[0])) ? htmlspecialchars($params[0],ENT_QUOTES,"UTF-8") : $params[0]);
  175. return parent::setProperty($key, $value);
  176. } elseif ($methodPrefix == 'get') {
  177. return htmlspecialchars(parent::getProperty($key),ENT_QUOTES);
  178. } elseif (!in_array($methodName, array_flip(get_class_methods($this)))) {
  179. throw new Exception("Method \"" . $methodName . "\" doesn't exist in " . get_called_class(), 500);
  180. }
  181. }
  182. public static function addMongoRegexp(&$_value){
  183. if(!is_array($_value) && stristr($_value, "/") != FALSE){
  184. $_value = new MongoRegex($_value);
  185. }
  186. }
  187. protected function ___getCollection(){
  188. $this->_queryConditions = array();
  189. return $this;
  190. }
  191. protected function ___addFieldToFilter($_attributeName, $_value){
  192. if(!is_array($_value) && stristr($_value, "/") != FALSE){
  193. $_value = new MongoRegex($_value);
  194. }
  195. if(is_array($_value)){
  196. array_walk_recursive($_value, array(get_called_class(), "addMongoRegexp"));
  197. $_condition = array();
  198. foreach ($_value as $_operators => $_realValue) {
  199. $_condition[$_operators] = $_realValue;
  200. }
  201. } else {
  202. $_condition = $_value;
  203. }
  204. if(isset($this->_queryConditions[$_attributeName])){
  205. $_alreadyExistingConditions = $this->_queryConditions[$_attributeName];
  206. $_condition = array_merge($_alreadyExistingConditions, $_condition);
  207. }
  208. $this->_queryConditions[$_attributeName] = $_condition;
  209. return $this;
  210. }
  211. protected function ___setFieldToSelect($_fields = array()){
  212. $_truesFiller = function(&$_value){$_value = true;};
  213. $_keysFields = array_flip($_fields);
  214. array_walk_recursive($_keysFields, $_truesFiller);
  215. $this->_fields = $_keysFields;
  216. return $this;
  217. }
  218. /**
  219. * @param $_pageLimit
  220. * @param $_pageSize
  221. * @return Mooses_Mongodb_Mongo_Iterator_Cursor
  222. * @throws MongoCursorException
  223. * @throws Mooses_Mongodb_Mongo_Exception
  224. */
  225. protected static function fetchAllWithLimits($query, array $fields, $_pageLimit, $_pageSize){
  226. $inheritance = static::getCollectionInheritance();
  227. if (count($inheritance) > 1) {
  228. $query['_type'] = $inheritance[0];
  229. }
  230. // If we are selecting specific fields make sure _type is always there
  231. if (!empty($fields) && !isset($fields['_type'])) {
  232. $fields['_type'] = 1;
  233. }
  234. $_skipParameter = $_pageSize * $_pageLimit;
  235. $cursor = static::getMongoCollection(false)->find($query, $fields)->limit($_pageLimit)->skip($_skipParameter);
  236. $config = array();
  237. $config['connectionGroup'] = static::getConnectionGroupName();
  238. $config['db'] = static::getDbName();
  239. $config['collection'] = static::getCollectionName();
  240. $config['documentClass'] = static::getDocumentClass();
  241. $config['documentSetClass'] = static::getDocumentSetClass();
  242. return new Mooses_Mongodb_Mongo_Iterator_Cursor($cursor, $config);
  243. }
  244. /**
  245. * @param $_pageLimit
  246. * @param $_pageSize
  247. * @return int
  248. * @throws Mooses_Mongodb_Mongo_Exception
  249. */
  250. protected static function countResults($query){
  251. $inheritance = static::getCollectionInheritance();
  252. if (count($inheritance) > 1) {
  253. $query['_type'] = $inheritance[0];
  254. }
  255. // If we are selecting specific fields make sure _type is always there
  256. if (!empty($fields) && !isset($fields['_type'])) {
  257. $fields['_type'] = 1;
  258. }
  259. return static::getMongoCollection(false)->count($query);
  260. }
  261. protected function ___countCollection(){
  262. return self::countResults($this->_queryConditions);
  263. }
  264. protected function ___getCollectionData($_forceArray = true){
  265. if($this->_pageLimit !== false && $this->_page !== false) {
  266. $_result = self::fetchAllWithLimits($this->_queryConditions, $this->_fields, $this->_pageLimit, $this->_page);
  267. } else {
  268. $_result = $this->fetchAll($this->_queryConditions, $this->_fields);
  269. }
  270. if (count($this->_sorterArray) == 0) {
  271. return $this->_convertMongoCursor($_result, $_forceArray);
  272. } else {
  273. return $this->_convertMongoCursor($_result, $_forceArray, $this->_sorterArray);
  274. }
  275. }
  276. protected function ___getPaginatedCollectionData($_page = 1, $_itemsPerPage = 15){
  277. // $_cacher = Mooses_Cacher::getInstance(strtolower("p_".str_replace("Default_Model_Mapper", "", get_called_class())), 3600);
  278. // $_idCache = preg_replace("/[^a-zA-Z0-9_]+/", "", "query_" . json_encode($this->_queryConditions));
  279. // if($this->_forceNoCachePaginatedCollection === true || !$_arrayResults = $_cacher->load($_idCache)) {
  280. $_result = $this->fetchAll($this->_queryConditions, $this->_fields);
  281. if (count($this->_sorterArray) == 0) {
  282. $_arrayResults = $this->_convertMongoCursor($_result, true);
  283. } else {
  284. $_arrayResults = $this->_convertMongoCursor($_result, true, $this->_sorterArray);
  285. }
  286. // $_cacher->save($_arrayResults, $_idCache);
  287. // }
  288. if(count($_arrayResults) > 0) {
  289. $_paginatorAdapter = new Zend_Paginator_Adapter_Array($_arrayResults);
  290. $_paginator = new Zend_Paginator($_paginatorAdapter);
  291. $_paginator->setItemCountPerPage($_itemsPerPage)->setCurrentPageNumber($_page);
  292. return $_paginator;
  293. } else {
  294. return false;
  295. }
  296. }
  297. protected function ___setCollectionOrder($_sorterArray){
  298. $this->_sorterArray = $_sorterArray;
  299. return $this;
  300. }
  301. protected function ___useCache($_useCache = true){
  302. $this->_forceNoCachePaginatedCollection = !$_useCache;
  303. return $this;
  304. }
  305. protected function ___getData($_key = NULL, $_default = NULL, $_forceDateExtendedFormat = false){
  306. if(is_null($_key)) {
  307. return parent::getAllData();
  308. } else {
  309. if(parent::hasProperty($_key)) {
  310. $_value = parent::getProperty($_key);
  311. if (is_object($_value)) {
  312. if(isset($_value->date)) {
  313. $_valueDatetime = DateTime::createFromFormat("Y-m-d H:i:s", $_value->date);
  314. if(is_object($_valueDatetime) && $_forceDateExtendedFormat){
  315. $_value = $_valueDatetime->format("d/m/Y H:i:s");
  316. } elseif(is_object($_valueDatetime)) {
  317. $_value = $_valueDatetime->format("d/m/Y");
  318. } else {
  319. $_value = $_value->date;
  320. }
  321. } else {
  322. $_value = $_value->getAllData();
  323. }
  324. }
  325. return $_value;
  326. } else {
  327. return (($_default != NULL) ? $_default : NULL);
  328. }
  329. }
  330. }
  331. protected function ___setData($_key, $_value = NULL, $_forceCleanData = false){
  332. if(is_array($_key)){
  333. foreach($_key as $_chiave => $_value){
  334. parent::setProperty($_chiave, $_value);
  335. $this->_dataUpdate[$_chiave] = $_value;
  336. }
  337. } else {
  338. if($_key == "birthdate" || is_a($_value, "DateTime")){
  339. $_oldValue = $_value;
  340. $_value = new stdClass();
  341. $_value->date = $_oldValue->format("Y-m-d H:i:s");
  342. $_value->timezone_type = 3;
  343. $_value->timezone = "Europe/Rome";
  344. }
  345. parent::setProperty($_key, $_value, $_forceCleanData);
  346. }
  347. $this->_dataUpdate[$_key] = $_value;
  348. return $this;
  349. }
  350. protected function ___load($_value, $_attributeName = NULL){
  351. if(is_null($_attributeName)){
  352. $this->loadKey = ['_id' => new MongoId($_value)];
  353. return parent::find($_value);
  354. } else {
  355. $this->loadKey = array($_attributeName => $_value);
  356. return parent::fetchOne(array($_attributeName => $_value));
  357. }
  358. }
  359. protected function ___loadByAttribute($_attributeName = "", $_value = "", $_forceArray = false){
  360. if(stristr($_value, "/") !== FALSE){
  361. $_value = new MongoRegex($_value);
  362. }
  363. $this->loadKey = array($_attributeName => $_value);
  364. $_results = parent::fetchAll($this->loadKey);
  365. return $this->_convertMongoCursor($_results, $_forceArray);
  366. }
  367. public function ___save($entierDocument = false, $safe = true){
  368. return parent::save($entierDocument, $safe);
  369. }
  370. public function deleteData($_safe = true, $onlyOne = true){
  371. $mongoCollection = $this->_getMongoCollection(true);
  372. $this->preDelete();
  373. if (!$this->isRootDocument()) {
  374. $result = $mongoCollection->update($this->loadKey, array('$unset' => array($this->getPathToDocument() => 1)), array('safe' => $_safe));
  375. }
  376. else {
  377. $result = $mongoCollection->remove($this->loadKey, array('justOne' => $onlyOne, 'safe' => $_safe));
  378. }
  379. return $result;
  380. }
  381. public function updateData($_criteria = false){
  382. $_criteria = $_criteria ?: $this->loadKey;
  383. $_return = $this->update($_criteria, array('$set' => $this->_dataUpdate));
  384. return $_return;
  385. }
  386. public function setUpdateCriteria($_array){
  387. $this->loadKey = $_array;
  388. return $this;
  389. }
  390. public function flatten($array, $prefix = '') {
  391. $result = array();
  392. foreach($array as $key=>$value) {
  393. if(is_array($value)) {
  394. $result = $result + $this->flatten($value, strtolower($prefix . $key . '_'));
  395. }
  396. else {
  397. $result[strtolower($prefix . $key)] = $value;
  398. }
  399. }
  400. return $result;
  401. }
  402. }