A modest collection of PHP libraries used at SparkFun.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

470 lines
11 KiB

  1. <?php
  2. namespace SparkLib;
  3. use \MongoDBI as Mongo;
  4. use \SparkLib\Iterator;
  5. /**
  6. * Class that creates a nice interface for accessing Mongo
  7. */
  8. class MongoFinder extends Iterator {
  9. protected $_mongo;
  10. protected $_mongoCursor;
  11. protected $_collection;
  12. protected $_class;
  13. protected $_query;
  14. protected $_where = [];
  15. protected $_sort = [];
  16. protected $_row;
  17. protected $_whereWaiting = false;
  18. protected $_sortWaiting = false;
  19. protected $_paramsChanged = false;
  20. protected $_resultCount;
  21. /**
  22. * Create new instance and store the collection name to be used in find()
  23. *
  24. * @param string $collection - mongo collection in which search happens
  25. * @param string $class - class in which to wrap mongo instance
  26. *
  27. * @return MongoFinder
  28. */
  29. public function __construct($collection, $class) {
  30. $this->_mongo = Mongo::getInstance();
  31. $this->_collection = $collection;
  32. $this->_class = $class;
  33. return $this;
  34. }
  35. /**
  36. * checks to see if you called either sortBy('something') or where('something')
  37. * without calling asc()/desc() or eq()/is() respectively
  38. *
  39. * @return void
  40. */
  41. protected function _checkWaiting() {
  42. if ($this->_whereWaiting !== false)
  43. throw new \Exception('Mongo WHERE waiting completion');
  44. if ($this->_sortWaiting !== false)
  45. throw new \Exception('Mongo SORT waiting completion');
  46. }
  47. /**
  48. * adds parameter to where array that will be used in find()
  49. *
  50. * @param string $key - the key in the mongodb for which you want to search
  51. *
  52. * @return MongoFinder
  53. */
  54. public function where($key) {
  55. $this->_checkWaiting();
  56. $this->_where[$key] = true;
  57. $this->_whereWaiting = $key;
  58. $this->_paramsChanged = true;
  59. return $this;
  60. }
  61. /**
  62. * adds parameter to where array that will be used in find()
  63. *
  64. * @param mixed $value - the value of the $key previously set in where()
  65. * @param string $comparison (optional) comparison to make between $key and $value
  66. *
  67. * @return MongoFinder
  68. */
  69. public function is($value, $comparison = false) {
  70. if (! $this->_where[$this->_whereWaiting])
  71. throw new \Exception('Mongo WHERE not called');
  72. if (! $comparison) {
  73. $comparison = '=';
  74. }
  75. switch($comparison) {
  76. case '=':
  77. $this->_where[$this->_whereWaiting] = $value;
  78. break;
  79. case '>':
  80. $this->_where[$this->_whereWaiting] = [ '$gt' => $value ];
  81. break;
  82. case '>=':
  83. $this->_where[$this->_whereWaiting] = [ '$gte' => $value ];
  84. break;
  85. case '<':
  86. $this->_where[$this->_whereWaiting] = [ '$lt' => $value ];
  87. break;
  88. case '<=':
  89. $this->_where[$this->_whereWaiting] = [ '$lte' => $value ];
  90. break;
  91. case 'in':
  92. $this->_where[$this->_whereWaiting] = [ '$in' => $value ];
  93. break;
  94. }
  95. $this->_paramsChanged = true;
  96. $this->_whereWaiting = false;
  97. return $this;
  98. }
  99. /**
  100. * adds parameter to where array that will be used in find()
  101. *
  102. * @param string $regex - the regex to match the against $key previously set in where()
  103. * @param string $flags - optional, regex search flags
  104. *
  105. * @return MongoFinder
  106. */
  107. public function isLike($regex, $flags = '') {
  108. if (! $this->_where[$this->_whereWaiting])
  109. throw new \Exception('Mongo WHERE not called');
  110. $this->_where[$this->_whereWaiting] = [ '$regex' => "$regex", '$options' => "$flags" ];
  111. $this->_paramsChanged = true;
  112. $this->_whereWaiting = false;
  113. return $this;
  114. }
  115. /**
  116. * adds parameter to where array that will be used in find() use for values
  117. * you DO NOT want to be true
  118. *
  119. * example:
  120. * where('cat_attitude')->not('angry'); // cat_attitude != angry
  121. * where('cat_weight')->not(20, '>'); // ! cat_weight > 20
  122. *
  123. * @param mixed $value - the value of the $key previously set in where()
  124. * @param string $comparison (optional) comparison to make between $key and $value
  125. *
  126. * @return MongoFinder
  127. */
  128. public function isNot($value, $comparison = false) {
  129. if (! $this->_where[$this->_whereWaiting])
  130. throw new \Exception('Mongo WHERE not called');
  131. if(! $comparison) {
  132. $comparison = '=';
  133. }
  134. switch($comparison) {
  135. case '=':
  136. if (is_array($value))
  137. $this->_where[$this->_whereWaiting] = [ '$not' => $value ];
  138. else
  139. $this->_where[$this->_whereWaiting] = [ '$not' => new MongoRegex('/^' . $value . '$/') ];
  140. break;
  141. case '>':
  142. $this->_where[$this->_whereWaiting] = [ '$not' => [ '$gt' => $value ] ];
  143. break;
  144. case '>=':
  145. $this->_where[$this->_whereWaiting] = [ '$not' => [ '$gte' => $value ] ];
  146. break;
  147. case '<':
  148. $this->_where[$this->_whereWaiting] = [ '$not' => [ '$lt' => $value ] ];
  149. break;
  150. case '<=':
  151. $this->_where[$this->_whereWaiting] = [ '$not' => [ '$lte' => $value ] ];
  152. break;
  153. }
  154. $this->_paramsChanged = true;
  155. $this->_whereWaiting = false;
  156. return $this;
  157. }
  158. /**
  159. * alias for is($value, '=')
  160. *
  161. * @param mixed $value value you're checking against $key set in where()
  162. *
  163. * @return MongoFinder
  164. */
  165. public function eq($value) {
  166. $this->_paramsChanged = true;
  167. return $this->is($value, '=');
  168. }
  169. /**
  170. * alias for is($value, 'in')
  171. *
  172. * @param mixed $value value you're checking against $key set in where()
  173. *
  174. * @return MongoFinder
  175. */
  176. public function in($value) {
  177. if(! is_array($value))
  178. return $this;
  179. $this->_paramsChanged = true;
  180. return $this->is($value, 'in');
  181. }
  182. /**
  183. * alias for is($value, [ '$exists' => false ])
  184. *
  185. * @return MongoFinder
  186. */
  187. public function isNull() {
  188. $this->_paramsChanged = true;
  189. return $this->is([ '$exists' => false ], '=');
  190. }
  191. /**
  192. * alias for is($value, [ '$exists' => true ])
  193. *
  194. * @return MongoFinder
  195. */
  196. public function isNotNull() {
  197. $this->_paramsChanged = true;
  198. return $this->is([ '$exists' => true ], '=');
  199. }
  200. /**
  201. * how to sort results of search
  202. *
  203. * @param mixed $order - if it's an array format: [ $sortKey => 1 ] if it
  204. * is a string then format: sortkey
  205. *
  206. * @return MongoFinder
  207. */
  208. public function orderBy($order) {
  209. $this->_checkWaiting();
  210. if (is_array($order)) {
  211. foreach($order as $k => $v) {
  212. switch($v) {
  213. case 1:
  214. case -1:
  215. $this->_sort[$k] = $v;
  216. break;
  217. case 'ASC':
  218. case 'asc':
  219. $this->_sort[$k] = -1;
  220. break;
  221. case 'DESC':
  222. case 'desc':
  223. $this->_sort[$k] = 1;
  224. break;
  225. }
  226. }
  227. } else {
  228. $this->_sort[$order] = true;
  229. $this->_sortWaiting = $order;
  230. }
  231. $this->_paramsChanged = true;
  232. return $this;
  233. }
  234. /**
  235. * sort by asc, should be proceeded with a call to sort()
  236. *
  237. * @return MongoFinder
  238. */
  239. public function asc() {
  240. if (! $this->_sort[$this->_sortWaiting])
  241. throw new \Exception('Mongo SORT not called');
  242. $this->_sort[$this->_sortWaiting] = -1;
  243. $this->_sortWaiting = false;
  244. $this->_paramsChanged = true;
  245. return $this;
  246. }
  247. /**
  248. * sort by desc, should be proceeded with a call to sort()
  249. *
  250. * @return MongoFinder
  251. */
  252. public function desc() {
  253. if (! $this->_sort[$this->_sortWaiting])
  254. throw new \Exception('Mongo SORT not called');
  255. $this->_sort[$this->_sortWaiting] = 1;
  256. $this->_sortWaiting = false;
  257. $this->_paramsChanged = true;
  258. return $this;
  259. }
  260. /**
  261. * Moves internal pointer forward once
  262. *
  263. * @return MongoFinder
  264. */
  265. public function next() {
  266. $this->_checkWaiting();
  267. if (! isset($this->_mongoCursor) || $this->_paramsChanged)
  268. $this->find();
  269. $this->_mongoCursor->next();
  270. return $this;
  271. }
  272. /**
  273. * Moves internal pointer forward by $int
  274. *
  275. * @param int $int - number of times to skip the internal pointer
  276. *
  277. * @return MongoFinder
  278. */
  279. public function skip($int) {
  280. $this->_checkWaiting();
  281. if (! isset($this->_mongoCursor) || $this->_paramsChanged)
  282. $this->find();
  283. $this->_mongoCursor->skip($int);
  284. return $this;
  285. }
  286. /**
  287. * Moves internal pointer to beginning
  288. *
  289. * @return MongoFinder
  290. */
  291. public function rewind() {
  292. $this->_checkWaiting();
  293. if (! isset($this->_mongoCursor) || $this->_paramsChanged)
  294. $this->find();
  295. $this->_mongoCursor->rewind();
  296. return $this;
  297. }
  298. /**
  299. * alias for getNext
  300. *
  301. * @return \Spark\$collection-wrapped Mongo thing
  302. */
  303. public function getOne() {
  304. return $this->getNext();
  305. }
  306. /**
  307. * Returns the current result and iterates the pointer
  308. *
  309. * @return \Spark\$collection-wrapped mongo-thing
  310. */
  311. public function getNext () {
  312. $this->_checkWaiting();
  313. if (! isset($this->_mongoCursor) || $this->_paramsChanged)
  314. $this->find();
  315. $result = $this->current();
  316. if (false !== $result)
  317. $this->next();
  318. return $result;
  319. }
  320. /**
  321. * Returns the current result at pointer
  322. *
  323. * @return \Spark\$collection-wrapped mongo-thing
  324. */
  325. public function current() {
  326. $this->_checkWaiting();
  327. if (! isset($this->_mongoCursor) || $this->_paramsChanged)
  328. $this->find();
  329. if (! $this->_mongoCursor->current())
  330. return false;
  331. return new $this->_class($this->_mongoCursor->current());
  332. }
  333. /**
  334. * get a count of the result ste
  335. *
  336. * @return int
  337. */
  338. public function count() {
  339. if (! isset($this->_mongoCursor) || $this->_paramsChanged) {
  340. $this->find();
  341. $this->_resultCount = null;
  342. }
  343. if (! isset($this->_resultCount))
  344. $this->_resultCount = $this->_mongoCursor->count();
  345. return $this->_resultCount;
  346. }
  347. /**
  348. * Actually does the search on mongo and iterates the pointer so it's on a result
  349. *
  350. * @return MongoFinder
  351. */
  352. public function find() {
  353. $this->_checkWaiting();
  354. $collection = $this->_collection;
  355. $this->_mongoCursor = $this->_mongo->$collection->find($this->_where)->sort($this->_sort);
  356. $this->_mongoCursor->next();
  357. // Reset params so no new find runs
  358. $this->_paramsChanged = false;
  359. return $this;
  360. }
  361. /**
  362. * Resets all query info
  363. */
  364. public function reset() {
  365. $this->_where = [];
  366. $this->_sort = [];
  367. $this->_whereWaiting = false;
  368. $this->_sortWaiting = false;
  369. $this->_paramsChanged = false;
  370. $this->_resultCount = null;
  371. $this->_row = null;
  372. $this->_query = null;
  373. $this->_mongoCursor = null;
  374. }
  375. /**
  376. * Dumps mongo query as string
  377. *
  378. * @return string
  379. */
  380. public function getQuery() {
  381. return 'db.' . $this->_collection . '.find(' . json_encode($this->_where) . ').sort(' . json_encode($this->_sort) . ').pretty()';
  382. }
  383. /**
  384. * Makes sure the current result is an instance of the class passed in
  385. *
  386. * @return boolean
  387. */
  388. public function valid () {
  389. return $this->_mongoCursor->current() instanceof $this->_class;
  390. }
  391. /**
  392. * Returns the mongo id of the mongo entity currently under the pointer
  393. *
  394. * @return string
  395. */
  396. public function key () {
  397. if (! isset($this->_mongoCursor) || $this->_paramsChanged)
  398. $this->find();
  399. return (string) $this->current()->id();
  400. }
  401. }