PageRenderTime 55ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Shanty/Mongo/Collection.php

https://bitbucket.org/ilyabazhenov/speakplace
PHP | 521 lines | 258 code | 74 blank | 189 comment | 34 complexity | 2bf0f381687c3e8e998cb65e0a637ee3 MD5 | raw file
  1. <?php
  2. require_once 'Shanty/Mongo/Document.php';
  3. require_once 'Shanty/Mongo/Exception.php';
  4. require_once 'Shanty/Mongo/Iterator/Cursor.php';
  5. /**
  6. * @category Shanty
  7. * @package Shanty_Mongo
  8. * @copyright Shanty Tech Pty Ltd
  9. * @license New BSD License
  10. * @author Coen Hyde
  11. */
  12. abstract class Shanty_Mongo_Collection
  13. {
  14. protected static $_connectionGroup = 'default';
  15. protected static $_db = null;
  16. protected static $_collection = null;
  17. protected static $_requirements = array();
  18. protected static $_cachedCollectionInheritance = array();
  19. protected static $_cachedCollectionRequirements = array();
  20. protected static $_documentSetClass = 'Shanty_Mongo_DocumentSet';
  21. /**
  22. * Get the name of the mongo db
  23. *
  24. * @return string
  25. */
  26. public static function getDbName()
  27. {
  28. $db = static::$_db;
  29. if (is_null($db)) {
  30. $db = static::getConnection()->getDatabase();
  31. }
  32. return $db;
  33. }
  34. /**
  35. * Get the name of the mongo collection
  36. *
  37. * @return string
  38. */
  39. public static function getCollectionName()
  40. {
  41. return static::$_collection;
  42. }
  43. /**
  44. * Get the name of the connection group
  45. *
  46. * @return string
  47. */
  48. public static function getConnectionGroupName()
  49. {
  50. return static::$_connectionGroup;
  51. }
  52. /**
  53. * Determine if this collection has a database name set
  54. *
  55. * @return boolean
  56. */
  57. public static function hasDbName()
  58. {
  59. return !is_null(static::getDbName());
  60. }
  61. /**
  62. * Determine if this collection has a collection name set
  63. *
  64. * @return boolean
  65. */
  66. public static function hasCollectionName()
  67. {
  68. return !is_null(static::getCollectionName());
  69. }
  70. /**
  71. * Is this class a document class
  72. *
  73. * @return boolean
  74. */
  75. public static function isDocumentClass()
  76. {
  77. return is_subclass_of(get_called_class(), 'Shanty_Mongo_Document');
  78. }
  79. /**
  80. * Get the name of the document class
  81. *
  82. * @return string
  83. */
  84. public static function getDocumentClass()
  85. {
  86. if (!static::isDocumentClass()) {
  87. throw new Shanty_Mongo_Exception(get_called_class().' is not a document. Please extend Shanty_Mongo_Document');
  88. }
  89. return get_called_class();
  90. }
  91. /**
  92. * Get the name of the document set class
  93. *
  94. * @return string
  95. */
  96. public static function getDocumentSetClass()
  97. {
  98. return static::$_documentSetClass;
  99. }
  100. /**
  101. * Get the inheritance of this collection
  102. */
  103. public static function getCollectionInheritance()
  104. {
  105. $calledClass = get_called_class();
  106. // Have we already computed this collections inheritance?
  107. if (array_key_exists($calledClass, static::$_cachedCollectionInheritance)) {
  108. return static::$_cachedCollectionInheritance[$calledClass];
  109. }
  110. $parentClass = get_parent_class($calledClass);
  111. if (is_null($parentClass::getCollectionName())) {
  112. $inheritance = array($calledClass);
  113. }
  114. else {
  115. $inheritance = $parentClass::getCollectionInheritance();
  116. array_unshift($inheritance, $calledClass);
  117. }
  118. static::$_cachedCollectionInheritance[$calledClass] = $inheritance;
  119. return $inheritance;
  120. }
  121. /**
  122. * Get requirements
  123. *
  124. * @param bolean $inherited Include inherited requirements
  125. * @return array
  126. */
  127. public static function getCollectionRequirements($inherited = true)
  128. {
  129. $calledClass = get_called_class();
  130. // Return if we only need direct requirements. ie no inherited requirements
  131. if (!$inherited || $calledClass === __CLASS__) {
  132. $reflector = new ReflectionProperty($calledClass, '_requirements');
  133. if ($reflector->getDeclaringClass()->getName() !== $calledClass) return array();
  134. return static::makeRequirementsTidy($calledClass::$_requirements);
  135. }
  136. // Have we already computed this collections requirements?
  137. if (array_key_exists($calledClass, self::$_cachedCollectionRequirements)) {
  138. return self::$_cachedCollectionRequirements[$calledClass];
  139. }
  140. // Get parent collections requirements
  141. $parentClass = get_parent_class($calledClass);
  142. $parentRequirements = $parentClass::getCollectionRequirements();
  143. // Merge those requirements with this collections requirements
  144. $requirements = static::mergeRequirements($parentRequirements, $calledClass::getCollectionRequirements(false));
  145. self::$_cachedCollectionRequirements[$calledClass] = $requirements;
  146. return $requirements;
  147. }
  148. /**
  149. * Process requirements to make sure they are in the correct format
  150. *
  151. * @param array $requirements
  152. * @return array
  153. */
  154. public static function makeRequirementsTidy(array $requirements) {
  155. foreach ($requirements as $property => $requirementList) {
  156. if (!is_array($requirementList)) {
  157. $requirements[$property] = array($requirementList);
  158. }
  159. $newRequirementList = array();
  160. foreach ($requirements[$property] as $key => $requirement) {
  161. if (is_numeric($key)) $newRequirementList[$requirement] = null;
  162. else $newRequirementList[$key] = $requirement;
  163. }
  164. $requirements[$property] = $newRequirementList;
  165. }
  166. return $requirements;
  167. }
  168. /**
  169. * Merge a two sets of requirements together
  170. *
  171. * @param array $requirements
  172. * @return array
  173. */
  174. public static function mergeRequirements($requirements1, $requirements2)
  175. {
  176. $requirements = $requirements1;
  177. foreach ($requirements2 as $property => $requirementList) {
  178. if (!array_key_exists($property, $requirements)) {
  179. $requirements[$property] = $requirementList;
  180. continue;
  181. }
  182. foreach ($requirementList as $requirement => $options) {
  183. // Find out if this is a Document or DocumentSet requirement
  184. $matches = array();
  185. preg_match("/^(Document|DocumentSet)(?::[A-Za-z][\w\-]*)?$/", $requirement, $matches);
  186. if (empty($matches)) {
  187. $requirements[$property][$requirement] = $options;
  188. continue;
  189. }
  190. // If requirement exists in existing requirements then unset it and replace it with the new requirements
  191. foreach ($requirements[$property] as $innerRequirement => $innerOptions) {
  192. $innerMatches = array();
  193. preg_match("/^{$matches[1]}(:[A-Za-z][\w\-]*)?/", $innerRequirement, $innerMatches);
  194. if (empty($innerMatches)) {
  195. continue;
  196. }
  197. unset($requirements[$property][$innerRequirement]);
  198. $requirements[$property][$requirement] = $options;
  199. break;
  200. }
  201. }
  202. }
  203. return $requirements;
  204. }
  205. /*
  206. * Get a connection
  207. *
  208. * @param $writable should the connection be writable
  209. * @return Shanty_Mongo_Connection
  210. */
  211. public static function getConnection($writable = true)
  212. {
  213. if ($writable) $connection = Shanty_Mongo::getWriteConnection(static::getConnectionGroupName());
  214. else $connection = Shanty_Mongo::getReadConnection(static::getConnectionGroupName());
  215. return $connection;
  216. }
  217. /**
  218. * Get an instance of MongoDb
  219. *
  220. * @return MongoDb
  221. * @param boolean $useSlave
  222. */
  223. public static function getMongoDb($writable = true)
  224. {
  225. if (!static::hasDbName()) {
  226. require_once 'Shanty/Mongo/Exception.php';
  227. throw new Shanty_Mongo_Exception(get_called_class().'::$_db is null');
  228. }
  229. return static::getConnection($writable)->selectDB(static::getDbName());
  230. }
  231. /**
  232. * Get an instance of MongoCollection
  233. *
  234. * @return MongoCollection
  235. * @param boolean $useSlave
  236. */
  237. public static function getMongoCollection($writable = true)
  238. {
  239. if (!static::hasCollectionName()) {
  240. throw new Shanty_Mongo_Exception(get_called_class().'::$_collection is null');
  241. }
  242. return static::getMongoDb($writable)->selectCollection(static::getCollectionName());
  243. }
  244. /**
  245. * Create a new document belonging to this collection
  246. * @param $data
  247. * @param boolean $new
  248. */
  249. public static function create(array $data = array(), $new = true)
  250. {
  251. if (isset($data['_type']) && is_array($data['_type']) && class_exists($data['_type'][0]) && is_subclass_of($data['_type'][0], 'Shanty_Mongo_Document')) {
  252. $documentClass = $data['_type'][0];
  253. }
  254. else {
  255. $documentClass = static::getDocumentClass();
  256. }
  257. $config = array();
  258. $config['new'] = ($new);
  259. $config['hasId'] = true;
  260. $config['connectionGroup'] = static::getConnectionGroupName();
  261. $config['db'] = static::getDbName();
  262. $config['collection'] = static::getCollectionName();
  263. return new $documentClass($data, $config);
  264. }
  265. /**
  266. * Find a document by id
  267. *
  268. * @param MongoId|String $id
  269. * @param array $fields
  270. * @return Shanty_Mongo_Document
  271. */
  272. public static function find($id, array $fields = array())
  273. {
  274. if (!($id instanceof MongoId)) {
  275. $id = new MongoId($id);
  276. }
  277. $query = array('_id' => $id);
  278. return static::one($query, $fields);
  279. }
  280. /**
  281. * Find one document
  282. *
  283. * @param array $query
  284. * @param array $fields
  285. * @return Shanty_Mongo_Document
  286. */
  287. public static function one(array $query = array(), array $fields = array())
  288. {
  289. $inheritance = static::getCollectionInheritance();
  290. if (count($inheritance) > 1) {
  291. $query['_type'] = $inheritance[0];
  292. }
  293. // If we are selecting specific fields make sure _type is always there
  294. if (!empty($fields) && !isset($fields['_type'])) {
  295. $fields['_type'] = 1;
  296. }
  297. $data = static::getMongoCollection(false)->findOne($query, $fields);
  298. if (is_null($data)) return null;
  299. return static::create($data, false);
  300. }
  301. /**
  302. * Find many documents
  303. *
  304. * @param array $query
  305. * @param array $fields
  306. * @return Shanty_Mongo_Iterator_Cursor
  307. */
  308. public static function all(array $query = array(), array $fields = array())
  309. {
  310. $inheritance = static::getCollectionInheritance();
  311. if (count($inheritance) > 1) {
  312. $query['_type'] = $inheritance[0];
  313. }
  314. // If we are selecting specific fields make sure _type is always there
  315. if (!empty($fields) && !isset($fields['_type'])) {
  316. $fields['_type'] = 1;
  317. }
  318. $cursor = static::getMongoCollection(false)->find($query, $fields);
  319. $config = array();
  320. $config['connectionGroup'] = static::getConnectionGroupName();
  321. $config['db'] = static::getDbName();
  322. $config['collection'] = static::getCollectionName();
  323. $config['documentClass'] = static::getDocumentClass();
  324. $config['documentSetClass'] = static::getDocumentSetClass();
  325. return new Shanty_Mongo_Iterator_Cursor($cursor, $config);
  326. }
  327. /**
  328. * Alias for one
  329. *
  330. * @param array $query
  331. * @param array $fields
  332. * @return Shanty_Mongo_Document
  333. */
  334. public static function fetchOne($query = array(), array $fields = array())
  335. {
  336. return static::one($query, $fields);
  337. }
  338. /**
  339. * Alias for all
  340. *
  341. * @param array $query
  342. * @param array $fields
  343. * @return Shanty_Mongo_Iterator_Cursor
  344. */
  345. public static function fetchAll($query = array(), array $fields = array())
  346. {
  347. return static::all($query, $fields);
  348. }
  349. /**
  350. * Select distinct values for a property
  351. *
  352. * @param String $property
  353. * @return array
  354. */
  355. public static function distinct($property)
  356. {
  357. $results = static::getMongoDb(false)->command(array('distinct' => static::getCollectionName(), 'key' => $property));
  358. return $results['values'];
  359. }
  360. /**
  361. * Insert a document
  362. *
  363. * @param array $document
  364. * @param array $options
  365. */
  366. public static function insert(array $document, array $options = array())
  367. {
  368. return static::getMongoCollection(true)->insert($document, $options);
  369. }
  370. /**
  371. * Insert a batch of documents
  372. *
  373. * @param array $documents
  374. * @param unknown_type $options
  375. */
  376. public static function insertBatch(array $documents, array $options = array())
  377. {
  378. return static::getMongoCollection(true)->batchInsert($documents, $options);
  379. }
  380. /**
  381. * Update documents from this collection
  382. *
  383. * @param $criteria
  384. * @param $object
  385. * @param $options
  386. */
  387. public static function update(array $criteria, array $object, array $options = array())
  388. {
  389. return static::getMongoCollection(true)->update($criteria, $object, $options);
  390. }
  391. /**
  392. * Remove documents from this collection
  393. *
  394. * @param array $criteria
  395. * @param unknown_type $justone
  396. */
  397. public static function remove(array $criteria, array $options = array())
  398. {
  399. // if you want to remove a document by MongoId
  400. if (array_key_exists('_id', $criteria) && !($criteria["_id"] instanceof MongoId)) {
  401. $criteria["_id"] = new MongoId($criteria["_id"]);
  402. }
  403. return static::getMongoCollection(true)->remove($criteria, $options);
  404. }
  405. /**
  406. * Drop this collection
  407. */
  408. public static function drop()
  409. {
  410. return static::getMongoCollection(true)->drop();
  411. }
  412. /**
  413. * Ensure an index
  414. *
  415. * @param array $keys
  416. * @param array $options
  417. */
  418. public static function ensureIndex(array $keys, $options = array())
  419. {
  420. return static::getMongoCollection(true)->ensureIndex($keys, $options);
  421. }
  422. /**
  423. * Delete an index
  424. *
  425. * @param string|array $keys
  426. */
  427. public static function deleteIndex($keys)
  428. {
  429. return static::getMongoCollection(true)->deleteIndex($keys);
  430. }
  431. /**
  432. * Remove all indexes from this collection
  433. */
  434. public static function deleteIndexes()
  435. {
  436. return static::getMongoCollection(true)->deleteIndexes();
  437. }
  438. /**
  439. * Get index information for this collection
  440. *
  441. * @return array
  442. */
  443. public static function getIndexInfo()
  444. {
  445. return static::getMongoCollection(false)->getIndexInfo();
  446. }
  447. }