PageRenderTime 59ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Maps/MapDB.php

http://github.com/modolabs/Kurogo-Mobile-Web
PHP | 362 lines | 314 code | 42 blank | 6 comment | 48 complexity | 5849a0b10b362ad783934fba4b2415d6 MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.1
  1. <?php
  2. includePackage('db');
  3. class MapDB
  4. {
  5. const PLACEMARK_TABLE = 'map_placemarks';
  6. const PLACEMARK_STYLES_TABLE = 'map_styles';
  7. const PLACEMARK_PROPERTIES_TABLE = 'map_placemark_properties';
  8. const CATEGORY_TABLE = 'map_categories';
  9. const PLACEMARK_CATEGORY_TABLE = 'map_placemark_categories';
  10. private static $db = null;
  11. private static $categoryIds = null;
  12. public static function getAllCategoryIds()
  13. {
  14. if (self::$categoryIds === null) {
  15. $sql = 'SELECT * FROM '.self::CATEGORY_TABLE
  16. .' WHERE parent_category_id IS NULL';
  17. $results = self::connection()->query($sql);
  18. while (($row = $results->fetch(PDO::FETCH_ASSOC))) {
  19. self::$categoryIds[] = $row['category_id'];
  20. }
  21. }
  22. return self::$categoryIds;
  23. }
  24. public static function updateCategory(MapFolder $category, $items, $projection=null, $parentCategoryId=null) {
  25. $categoryId = $category->getId();
  26. $name = $category->getTitle();
  27. $description = $category->getSubtitle();
  28. $isStored = $category instanceof MapDBCategory && $category->isStored();
  29. if (!$isStored) {
  30. $sql = 'SELECT * FROM '.self::CATEGORY_TABLE.' WHERE category_id=?';
  31. $params = array($categoryId);
  32. $results = self::connection()->query($sql, $params);
  33. if ($results->fetch()) {
  34. $isStored = true;
  35. }
  36. }
  37. if (!$isStored) {
  38. if ($parentCategoryId === null) {
  39. $sql = 'INSERT INTO '.self::CATEGORY_TABLE.' (category_id, name, description) VALUES (?, ?, ?)';
  40. $params = array($categoryId, $name, $description);
  41. } else {
  42. $sql = 'INSERT INTO '.self::CATEGORY_TABLE.' (category_id, name, description, parent_category_id) VALUES (?, ?, ?, ?)';
  43. $params = array($categoryId, $name, $description, $parentCategoryId);
  44. }
  45. } else {
  46. if ($parentCategoryId === null) {
  47. $sql = 'UPDATE '.self::CATEGORY_TABLE.' SET name=?, description=? WHERE category_id=?';
  48. $params = array($name, $description, $categoryId);
  49. } else {
  50. $sql = 'UPDATE '.self::CATEGORY_TABLE.' SET name=?, description=?, parent_category_id=? WHERE category_id=?';
  51. $params = array($name, $description, $parentCategoryId, $categoryId);
  52. }
  53. }
  54. self::connection()->query($sql, $params);
  55. if ($projection === null || $projection instanceof MapProjector) {
  56. $projector = $projection;
  57. } else {
  58. $projector = new MapProjector();
  59. $projector->setSrcProj($projection);
  60. }
  61. foreach ($items as $item) {
  62. if ($item instanceof MapFolder) {
  63. self::updateCategory($item, $item->getListItems(), $projector, $categoryId);
  64. } elseif ($item instanceof Placemark) {
  65. self::updateFeature($item, $categoryId, $projector);
  66. }
  67. }
  68. }
  69. public static function updateFeature(Placemark $feature, $parentCategoryId, $projector=null) {
  70. $style = $feature->getStyle();
  71. if (method_exists($style, 'getId')) {
  72. $styleId = $style->getId();
  73. } else {
  74. $styleId = null;
  75. }
  76. $geometry = $feature->getGeometry();
  77. if ($geometry) {
  78. if ($projector) {
  79. $geometry = $projector->projectGeometry($geometry);
  80. }
  81. $centroid = $geometry->getCenterCoordinate();
  82. $wkt = WKTParser::wktFromGeometry($geometry);
  83. } else {
  84. // TODO: handle this instead of throwing exception
  85. throw new KurogoDataException("feature has no geometry");
  86. }
  87. $placemarkId = $feature->getId();
  88. // placemark table
  89. $isStored = $feature instanceof MapDBPlacemark && $feature->isStored();
  90. if (!$isStored) {
  91. $sql = 'SELECT * FROM '.self::PLACEMARK_TABLE.' WHERE placemark_id=? AND lat=? AND lon=?';
  92. $params = array($placemarkId, $centroid['lat'], $centroid['lon']);
  93. $results = self::connection()->query($sql, $params);
  94. if ($results->fetch()) {
  95. $isStored = true;
  96. }
  97. }
  98. $params = array(
  99. $feature->getTitle(), $feature->getAddress(), $styleId, $wkt,
  100. $placemarkId, $centroid['lat'], $centroid['lon'],
  101. );
  102. if ($isStored) {
  103. $sql = 'UPDATE '.self::PLACEMARK_TABLE
  104. .' SET name=?, address=?, style_id=?, geometry=?'
  105. .' WHERE placemark_id=? AND lat=? AND lon=?';
  106. } else {
  107. $sql = 'INSERT INTO '.self::PLACEMARK_TABLE
  108. .' (name, address, style_id, geometry, placemark_id, lat, lon)'
  109. .' VALUES (?, ?, ?, ?, ?, ?, ?)';
  110. }
  111. self::connection()->query($sql, $params);
  112. if ($placemarkId === null) {
  113. // TODO: check db compatibility for this function
  114. $placemarkId = self::connection()->lastInsertId();
  115. }
  116. // categories
  117. $categories = $feature->getCategoryIds();
  118. if (!is_array($categories)) {
  119. $categories = array();
  120. }
  121. if (!in_array($parentCategoryId, $categories)) {
  122. $categories[] = $parentCategoryId;
  123. }
  124. foreach ($categories as $categoryId) {
  125. $sql = 'INSERT INTO '.self::PLACEMARK_CATEGORY_TABLE
  126. .' (placemark_id, lat, lon, category_id)'
  127. .' VALUES (?, ?, ?, ?)';
  128. $params = array($placemarkId, $centroid['lat'], $centroid['lon'], $categoryId);
  129. self::connection()->query($sql, $params, db::IGNORE_ERRORS);
  130. }
  131. // properties
  132. $sql = 'DELETE FROM '.self::PLACEMARK_PROPERTIES_TABLE.' WHERE placemark_id=?';
  133. $params = array($placemarkId);
  134. self::connection()->query($sql, $params);
  135. $properties = $feature->getFields();
  136. foreach ($properties as $name => $value) {
  137. $sql = 'INSERT INTO '.self::PLACEMARK_PROPERTIES_TABLE
  138. .' (placemark_id, lat, lon, property_name, property_value)'
  139. .' VALUES (?, ?, ?, ?, ?)';
  140. $params = array($placemarkId, $centroid['lat'], $centroid['lon'], $name, $value);
  141. self::connection()->query($sql, $params);
  142. }
  143. }
  144. public static function getFeatureByIdAndCategory($featureId, $categoryIds)
  145. {
  146. $sql = 'SELECT p.*, pc.category_id FROM '
  147. .self::PLACEMARK_TABLE.' p, '.self::PLACEMARK_CATEGORY_TABLE.' pc'
  148. .' WHERE p.placemark_id = ?'
  149. .' AND p.placemark_id = pc.placemark_id'
  150. .' AND p.lat = pc.lat AND p.lon = pc.lon';
  151. $orClauses = array();
  152. $params = array($featureId);
  153. foreach ($categoryIds as $categoryId) {
  154. $orClauses[] = ' pc.category_id = ?';
  155. $params[] = $categoryId;
  156. }
  157. if ($orClauses) {
  158. $sql .= ' AND ('.implode(' OR ', $orClauses).')';
  159. }
  160. $result = self::connection()->query($sql, $params);
  161. $placemark = null;
  162. $row = $result->fetch();
  163. if ($row) {
  164. $placemark = new MapDBPlacemark($row, true);
  165. }
  166. return $placemark;
  167. }
  168. public static function styleForId($styleId) {
  169. $sql = 'SELECT * FROM '.self::PLACEMARK_STYLE_TABLE
  170. .' WHERE style_id = ?';
  171. $params = array($styleId);
  172. $results = self::connection()->query($sql, $params);
  173. if ($results && $row = $results->fetch(PDO::FETCH_ASSOC)) {
  174. return new MapDBStyle($row, $this);
  175. } else {
  176. return new MapDBStyle(array('style_id' => $styleId));
  177. }
  178. }
  179. public static function categoryForId($categoryId)
  180. {
  181. $sql = 'SELECT * FROM '.self::CATEGORY_TABLE
  182. .' WHERE category_id = ?';
  183. $params = array($categoryId);
  184. $results = self::connection()->query($sql, $params);
  185. if ($results && $row = $results->fetch()) {
  186. return new MapDBCategory($row, true);
  187. } else {
  188. return new MapDBCategory(array('category_id' => $categoryId));
  189. }
  190. }
  191. public static function childrenForCategory($categoryId) {
  192. $sql = 'SELECT * FROM '.self::CATEGORY_TABLE
  193. .' WHERE parent_category_id = ?';
  194. $params = array($categoryId);
  195. $results = self::connection()->query($sql, $params);
  196. $categories = array();
  197. if ($results) {
  198. while ($row = $results->fetch(PDO::FETCH_ASSOC)) {
  199. $categories[] = new MapDBCategory($row);
  200. }
  201. }
  202. return $categories;
  203. }
  204. public static function featuresForCategory($categoryId)
  205. {
  206. $sql = 'SELECT p.*, pc.category_id FROM '
  207. .self::PLACEMARK_TABLE.' p, '.self::PLACEMARK_CATEGORY_TABLE.' pc'
  208. .' WHERE p.placemark_id = pc.placemark_id'
  209. .' AND pc.category_id = ?'
  210. .' AND p.lat = pc.lat AND p.lon = pc.lon';
  211. $params = array($categoryId);
  212. $results = self::connection()->query($sql, $params);
  213. $features = array();
  214. if ($results) {
  215. while ($row = $results->fetch(PDO::FETCH_ASSOC)) {
  216. $features[] = new MapDBPlacemark($row);
  217. }
  218. }
  219. return $features;
  220. }
  221. public static function propertiesForFeature(MapDBPlacemark $feature) {
  222. $sql = 'SELECT property_name, property_value FROM '.self::PLACEMARK_PROPERTIES_TABLE
  223. .' WHERE placemark_id = ? AND lat = ? AND lon = ?';
  224. $center = $feature->getGeometry()->getCenterCoordinate();
  225. $params = array($feature->getId(), $center['lat'], $center['lon']);
  226. $results = self::connection()->query($sql, $params);
  227. if ($results) {
  228. return $results->fetchAll();
  229. }
  230. return array();
  231. }
  232. public static function connection()
  233. {
  234. if (self::$db === null) {
  235. // TODO: get other db config values
  236. self::$db = SiteDB::connection();
  237. self::createTables();
  238. }
  239. return self::$db;
  240. }
  241. private static function createTables() {
  242. if (!self::checkTableExists(self::PLACEMARK_TABLE)) {
  243. $sql = 'CREATE TABLE '.self::PLACEMARK_TABLE.' (
  244. placemark_id CHAR(32) NOT NULL,
  245. name VARCHAR(128),
  246. address TEXT,
  247. style_id CHAR(32),
  248. lat DOUBLE,
  249. lon DOUBLE,
  250. geometry_type VARCHAR(16),
  251. geometry TEXT,
  252. CONSTRAINT placemark_id_pk PRIMARY KEY (placemark_id, lat, lon) )';
  253. self::$db->query($sql);
  254. }
  255. if (!self::checkTableExists(self::PLACEMARK_STYLES_TABLE)) {
  256. $sql = 'CREATE TABLE '.self::PLACEMARK_STYLES_TABLE.' (
  257. style_id CHAR(32) NOT NULL,
  258. color CHAR(8),
  259. fill_color CHAR(8),
  260. stroke_color CHAR(8),
  261. stroke_width DOUBLE,
  262. height DOUBLE,
  263. width DOUBLE,
  264. icon VARCHAR(128),
  265. scale DOUBLE,
  266. shape VARCHAR(32),
  267. consistency VARCHAR(32),
  268. CONSTRAINT style_id_pk PRIMARY KEY (style_id) )';
  269. self::$db->query($sql);
  270. }
  271. if (!self::checkTableExists(self::PLACEMARK_PROPERTIES_TABLE)) {
  272. $sql = 'CREATE TABLE '.self::PLACEMARK_PROPERTIES_TABLE.' (
  273. placemark_id CHAR(32) NOT NULL,
  274. lat DOUBLE,
  275. lon DOUBLE,
  276. property_name VARCHAR(32),
  277. property_value TEXT,
  278. CONSTRAINT placemark_id_fk FOREIGN KEY (placemark_id, lat, lon)
  279. REFERENCES '.self::PLACEMARK_TABLE.' (placemark_id, lat, lon)
  280. ON UPDATE CASCADE ON DELETE CASCADE )';
  281. self::$db->query($sql);
  282. }
  283. if (!self::checkTableExists(self::CATEGORY_TABLE)) {
  284. $sql = 'CREATE TABLE '.self::CATEGORY_TABLE.' (
  285. category_id CHAR(32) NOT NULL,
  286. name VARCHAR(128),
  287. description TEXT,
  288. projection CHAR(16),
  289. parent_category_id CHAR(32),
  290. CONSTRAINT category_id_pk PRIMARY KEY (category_id),
  291. CONSTRAINT category_id_fk FOREIGN KEY (parent_category_id)
  292. REFERENCES '.self::CATEGORY_TABLE.' (category_id)
  293. ON UPDATE CASCADE ON DELETE SET NULL )';
  294. self::$db->query($sql);
  295. }
  296. if (!self::checkTableExists(self::PLACEMARK_CATEGORY_TABLE)) {
  297. $sql = 'CREATE TABLE '.self::PLACEMARK_CATEGORY_TABLE.' (
  298. placemark_id CHAR(32) NOT NULL,
  299. lat DOUBLE,
  300. lon DOUBLE,
  301. category_id CHAR(32) NOT NULL,
  302. CONSTRAINT placemark_category_fk_placemark FOREIGN KEY (placemark_id, lat, lon)
  303. REFERENCES '.self::PLACEMARK_TABLE.' (placemark_id, lat, lon)
  304. ON UPDATE CASCADE ON DELETE CASCADE,
  305. CONSTRAINT placemark_category_fk_category FOREIGN KEY (category_id)
  306. REFERENCES '.self::CATEGORY_TABLE.' (category_id)
  307. ON UPDATE CASCADE ON DELETE CASCADE,
  308. CONSTRAINT unique_placemark_category UNIQUE (placemark_id, lat, lon, category_id) )';
  309. self::$db->query($sql);
  310. }
  311. }
  312. private static function checkTableExists($table) {
  313. $sql = 'SELECT 1 FROM '.$table;
  314. $result = self::connection()->limitQuery($sql, array(), db::IGNORE_ERRORS);
  315. if (!$result) {
  316. return false;
  317. }
  318. return true;
  319. }
  320. }