mapper.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. <?php
  2. /*
  3. Copyright (c) 2009-2014 F3::Factory/Bong Cosca, All rights reserved.
  4. This file is part of the Fat-Free Framework (http://fatfree.sf.net).
  5. THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF
  6. ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  7. IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  8. PURPOSE.
  9. Please see the license.txt file for more information.
  10. */
  11. namespace DB\Mongo;
  12. //! MongoDB mapper
  13. class Mapper extends \DB\Cursor {
  14. protected
  15. //! MongoDB wrapper
  16. $db,
  17. //! Mongo collection
  18. $collection,
  19. //! Mongo document
  20. $document=array(),
  21. //! Mongo cursor
  22. $cursor;
  23. /**
  24. * Return database type
  25. * @return string
  26. **/
  27. function dbtype() {
  28. return 'Mongo';
  29. }
  30. /**
  31. * Return TRUE if field is defined
  32. * @return bool
  33. * @param $key string
  34. **/
  35. function exists($key) {
  36. return array_key_exists($key,$this->document);
  37. }
  38. /**
  39. * Assign value to field
  40. * @return scalar|FALSE
  41. * @param $key string
  42. * @param $val scalar
  43. **/
  44. function set($key,$val) {
  45. return $this->document[$key]=$val;
  46. }
  47. /**
  48. * Retrieve value of field
  49. * @return scalar|FALSE
  50. * @param $key string
  51. **/
  52. function get($key) {
  53. if ($this->exists($key))
  54. return $this->document[$key];
  55. user_error(sprintf(self::E_Field,$key));
  56. return FALSE;
  57. }
  58. /**
  59. * Delete field
  60. * @return NULL
  61. * @param $key string
  62. **/
  63. function clear($key) {
  64. unset($this->document[$key]);
  65. }
  66. /**
  67. * Convert array to mapper object
  68. * @return object
  69. * @param $row array
  70. **/
  71. protected function factory($row) {
  72. $mapper=clone($this);
  73. $mapper->reset();
  74. foreach ($row as $key=>$val)
  75. $mapper->document[$key]=$val;
  76. $mapper->query=array(clone($mapper));
  77. if (isset($mapper->trigger['load']))
  78. \Base::instance()->call($mapper->trigger['load'],$mapper);
  79. return $mapper;
  80. }
  81. /**
  82. * Return fields of mapper object as an associative array
  83. * @return array
  84. * @param $obj object
  85. **/
  86. function cast($obj=NULL) {
  87. if (!$obj)
  88. $obj=$this;
  89. return $obj->document;
  90. }
  91. /**
  92. * Build query and execute
  93. * @return array
  94. * @param $fields string
  95. * @param $filter array
  96. * @param $options array
  97. * @param $ttl int
  98. **/
  99. function select($fields=NULL,$filter=NULL,array $options=NULL,$ttl=0) {
  100. if (!$options)
  101. $options=array();
  102. $options+=array(
  103. 'group'=>NULL,
  104. 'order'=>NULL,
  105. 'limit'=>0,
  106. 'offset'=>0
  107. );
  108. $fw=\Base::instance();
  109. $cache=\Cache::instance();
  110. if (!($cached=$cache->exists($hash=$fw->hash($this->db->dsn().
  111. $fw->stringify(array($fields,$filter,$options))).'.mongo',
  112. $result)) || !$ttl || $cached[0]+$ttl<microtime(TRUE)) {
  113. if ($options['group']) {
  114. $grp=$this->collection->group(
  115. $options['group']['keys'],
  116. $options['group']['initial'],
  117. $options['group']['reduce'],
  118. array(
  119. 'condition'=>$filter,
  120. 'finalize'=>$options['group']['finalize']
  121. )
  122. );
  123. $tmp=$this->db->selectcollection(
  124. $fw->get('HOST').'.'.$fw->get('BASE').'.'.
  125. uniqid(NULL,TRUE).'.tmp'
  126. );
  127. $tmp->batchinsert($grp['retval'],array('safe'=>TRUE));
  128. $filter=array();
  129. $collection=$tmp;
  130. }
  131. else {
  132. $filter=$filter?:array();
  133. $collection=$this->collection;
  134. }
  135. $this->cursor=$collection->find($filter,$fields?:array());
  136. if ($options['order'])
  137. $this->cursor=$this->cursor->sort($options['order']);
  138. if ($options['limit'])
  139. $this->cursor=$this->cursor->limit($options['limit']);
  140. if ($options['offset'])
  141. $this->cursor=$this->cursor->skip($options['offset']);
  142. $result=array();
  143. while ($this->cursor->hasnext())
  144. $result[]=$this->cursor->getnext();
  145. if ($options['group'])
  146. $tmp->drop();
  147. if ($fw->get('CACHE') && $ttl)
  148. // Save to cache backend
  149. $cache->set($hash,$result,$ttl);
  150. }
  151. $out=array();
  152. foreach ($result as $doc)
  153. $out[]=$this->factory($doc);
  154. return $out;
  155. }
  156. /**
  157. * Return records that match criteria
  158. * @return array
  159. * @param $filter array
  160. * @param $options array
  161. * @param $ttl int
  162. **/
  163. function find($filter=NULL,array $options=NULL,$ttl=0) {
  164. if (!$options)
  165. $options=array();
  166. $options+=array(
  167. 'group'=>NULL,
  168. 'order'=>NULL,
  169. 'limit'=>0,
  170. 'offset'=>0
  171. );
  172. return $this->select(NULL,$filter,$options,$ttl);
  173. }
  174. /**
  175. * Count records that match criteria
  176. * @return int
  177. * @param $filter array
  178. * @param $ttl int
  179. **/
  180. function count($filter=NULL,$ttl=0) {
  181. $fw=\Base::instance();
  182. $cache=\Cache::instance();
  183. if (!($cached=$cache->exists($hash=$fw->hash($fw->stringify(
  184. array($filter))).'.mongo',$result)) || !$ttl ||
  185. $cached[0]+$ttl<microtime(TRUE)) {
  186. $result=$this->collection->count($filter);
  187. if ($fw->get('CACHE') && $ttl)
  188. // Save to cache backend
  189. $cache->set($hash,$result,$ttl);
  190. }
  191. return $result;
  192. }
  193. /**
  194. * Return record at specified offset using criteria of previous
  195. * load() call and make it active
  196. * @return array
  197. * @param $ofs int
  198. **/
  199. function skip($ofs=1) {
  200. $this->document=($out=parent::skip($ofs))?$out->document:array();
  201. if ($this->document && isset($this->trigger['load']))
  202. \Base::instance()->call($this->trigger['load'],$this);
  203. return $out;
  204. }
  205. /**
  206. * Insert new record
  207. * @return array
  208. **/
  209. function insert() {
  210. if (isset($this->document['_id']))
  211. return $this->update();
  212. if (isset($this->trigger['beforeinsert']))
  213. \Base::instance()->call($this->trigger['beforeinsert'],
  214. array($this,array('_id'=>$this->document['_id'])));
  215. $this->collection->insert($this->document);
  216. $pkey=array('_id'=>$this->document['_id']);
  217. if (isset($this->trigger['afterinsert']))
  218. \Base::instance()->call($this->trigger['afterinsert'],
  219. array($this,$pkey));
  220. $this->load($pkey);
  221. return $this->document;
  222. }
  223. /**
  224. * Update current record
  225. * @return array
  226. **/
  227. function update() {
  228. $pkey=array('_id'=>$this->document['_id']);
  229. if (isset($this->trigger['beforeupdate']))
  230. \Base::instance()->call($this->trigger['beforeupdate'],
  231. array($this,$pkey));
  232. $this->collection->update(
  233. $pkey,$this->document,array('upsert'=>TRUE));
  234. if (isset($this->trigger['afterupdate']))
  235. \Base::instance()->call($this->trigger['afterupdate'],
  236. array($this,$pkey));
  237. return $this->document;
  238. }
  239. /**
  240. * Delete current record
  241. * @return bool
  242. * @param $filter array
  243. **/
  244. function erase($filter=NULL) {
  245. if ($filter)
  246. return $this->collection->remove($filter);
  247. $pkey=array('_id'=>$this->document['_id']);
  248. if (isset($this->trigger['beforeerase']))
  249. \Base::instance()->call($this->trigger['beforeerase'],
  250. array($this,$pkey));
  251. $result=$this->collection->
  252. remove(array('_id'=>$this->document['_id']));
  253. parent::erase();
  254. $this->skip(0);
  255. if (isset($this->trigger['aftererase']))
  256. \Base::instance()->call($this->trigger['aftererase'],
  257. array($this,$pkey));
  258. return $result;
  259. }
  260. /**
  261. * Reset cursor
  262. * @return NULL
  263. **/
  264. function reset() {
  265. $this->document=array();
  266. parent::reset();
  267. }
  268. /**
  269. * Hydrate mapper object using hive array variable
  270. * @return NULL
  271. * @param $key string
  272. * @param $func callback
  273. **/
  274. function copyfrom($key,$func=NULL) {
  275. $var=\Base::instance()->get($key);
  276. if ($func)
  277. $var=call_user_func($func,$var);
  278. foreach ($var as $key=>$val)
  279. $this->document[$key]=$val;
  280. }
  281. /**
  282. * Populate hive array variable with mapper fields
  283. * @return NULL
  284. * @param $key string
  285. **/
  286. function copyto($key) {
  287. $var=&\Base::instance()->ref($key);
  288. foreach ($this->document as $key=>$field)
  289. $var[$key]=$field;
  290. }
  291. /**
  292. * Return field names
  293. * @return array
  294. **/
  295. function fields() {
  296. return array_keys($this->document);
  297. }
  298. /**
  299. * Return the cursor from last query
  300. * @return object|NULL
  301. **/
  302. function cursor() {
  303. return $this->cursor;
  304. }
  305. /**
  306. * Instantiate class
  307. * @return void
  308. * @param $db object
  309. * @param $collection string
  310. **/
  311. function __construct(\DB\Mongo $db,$collection) {
  312. $this->db=$db;
  313. $this->collection=$db->selectcollection($collection);
  314. $this->reset();
  315. }
  316. }