PageRenderTime 81ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/extensions/Survey/includes/SurveyDBClass.php

https://github.com/ChuguluGames/mediawiki-svn
PHP | 732 lines | 293 code | 87 blank | 352 comment | 31 complexity | 4ff775c911f84833081f9f36047f496d MD5 | raw file
  1. <?php
  2. /**
  3. * Abstract base class for representing objects that are stored in some DB table.
  4. *
  5. * @since 0.1
  6. *
  7. * @file SurveyDBClass.php
  8. * @ingroup Survey
  9. *
  10. * @licence GNU GPL v3 or later
  11. * @author Jeroen De Dauw < jeroendedauw@gmail.com >
  12. */
  13. abstract class SurveyDBClass {
  14. /**
  15. * The fields of the object.
  16. * field name (w/o prefix) => value
  17. *
  18. * @since 0.1
  19. * @var array
  20. */
  21. protected $fields = array();
  22. /**
  23. * Constructor.
  24. *
  25. * @since 0.1
  26. *
  27. * @param array|null $fields
  28. * @param boolean $loadDefaults
  29. */
  30. public function __construct( $fields, $loadDefaults = false ) {
  31. $this->setField( static::getIDField(), null );
  32. if ( !is_array( $fields ) ) {
  33. $fields = array();
  34. }
  35. if ( $loadDefaults ) {
  36. $fields = array_merge( static::getDefaults(), $fields );
  37. }
  38. $this->setFields( $fields );
  39. }
  40. /**
  41. * Returns an array with the fields and their types this object contains.
  42. * This corresponds directly to the fields in the database, without prefix.
  43. *
  44. * field name => type
  45. *
  46. * Allowed types:
  47. * * id
  48. * * str
  49. * * int
  50. * * bool
  51. * * array
  52. *
  53. * @since 0.1
  54. *
  55. * @return array
  56. */
  57. protected static function getFieldTypes() {
  58. return array();
  59. }
  60. /**
  61. * Returns an array with the fields and their descriptions.
  62. *
  63. * field name => field description
  64. *
  65. * @since 0.1
  66. *
  67. * @return array
  68. */
  69. public static function getFieldDescriptions() {
  70. return array();
  71. }
  72. /**
  73. * Returns the name of the database table objects of this type are stored in.
  74. *
  75. * @since 0.1
  76. *
  77. * @throws MWException
  78. * @return string
  79. */
  80. public static function getDBTable() {
  81. throw new MWException( 'Class did not implement getDBTable' );
  82. }
  83. /**
  84. * Gets the db field prefix.
  85. *
  86. * @since 0.1
  87. *
  88. * @throws MWException
  89. * @return string
  90. */
  91. protected static function getFieldPrefix() {
  92. throw new MWException( 'Class did not implement getFieldPrefix' );
  93. }
  94. /**
  95. * Returns the name of the id db field, without prefix.
  96. *
  97. * @since 0.1
  98. *
  99. * @return string
  100. */
  101. protected static function getIDField() {
  102. return 'id';
  103. }
  104. /**
  105. * Get a new instance of the class from a database result.
  106. *
  107. * @since 0.1
  108. *
  109. * @param object $result
  110. *
  111. * @return SurveyDBClass
  112. */
  113. public static function newFromDBResult( $result ) {
  114. $result = (array)$result;
  115. $data = array();
  116. $idFieldLength = strlen( static::getFieldPrefix() );
  117. foreach ( $result as $name => $value ) {
  118. $data[substr( $name, $idFieldLength )] = $value;
  119. }
  120. return static::newFromArray( $data );
  121. }
  122. /**
  123. * Get a new instance of the class from an array.
  124. *
  125. * @since 0.1
  126. *
  127. * @param array $data
  128. * @param boolean $loadDefaults
  129. *
  130. * @return SurveyDBClass
  131. */
  132. public static function newFromArray( array $data, $loadDefaults = false ) {
  133. return new static( $data, $loadDefaults );
  134. }
  135. /**
  136. * Selects the the specified fields of the records matching the provided
  137. * conditions. Field names get prefixed.
  138. *
  139. * @since 0.1
  140. *
  141. * @param array|null $fields
  142. * @param array $conditions
  143. * @param array $options
  144. *
  145. * @return array of self
  146. */
  147. public static function select( $fields = null, array $conditions = array(), array $options = array() ) {
  148. if ( is_null( $fields ) ) {
  149. $fields = array_keys( static::getFieldTypes() );
  150. }
  151. $result = static::rawSelect(
  152. static::getPrefixedFields( $fields ),
  153. static::getPrefixedValues( $conditions ),
  154. $options
  155. );
  156. $objects = array();
  157. foreach ( $result as $record ) {
  158. $objects[] = static::newFromDBResult( $record );
  159. }
  160. return $objects;
  161. }
  162. /**
  163. * Selects the the specified fields of the first matching record.
  164. * Field names get prefixed.
  165. *
  166. * @since 0.1
  167. *
  168. * @param array|null $fields
  169. * @param array $conditions
  170. * @param array $options
  171. *
  172. * @return self|false
  173. */
  174. public static function selectRow( $fields = null, array $conditions = array(), array $options = array() ) {
  175. $options['LIMIT'] = 1;
  176. $objects = static::select( $fields, $conditions, $options );
  177. return count( $objects ) > 0 ? $objects[0] : false;
  178. }
  179. /**
  180. * Returns if there is at least one record matching the provided conditions.
  181. * Condition field names get prefixed.
  182. *
  183. * @since 0.1
  184. *
  185. * @param array $conditions
  186. *
  187. * @return boolean
  188. */
  189. public static function has( array $conditions = array() ) {
  190. return static::selectRow( array( static::getIDField() ), $conditions ) !== false;
  191. }
  192. /**
  193. * Returns the amount of matching records.
  194. * Condition field names get prefixed.
  195. *
  196. * @since 0.1
  197. *
  198. * @param array $conditions
  199. * @param array $options
  200. *
  201. * @return integer
  202. */
  203. public static function count( array $conditions = array(), array $options = array() ) {
  204. $res = static::rawSelect(
  205. array( 'COUNT(*) AS rowcount' ),
  206. static::getPrefixedValues( $conditions ),
  207. $options
  208. )->fetchObject();
  209. return $res->rowcount;
  210. }
  211. /**
  212. * Selects the the specified fields of the records matching the provided
  213. * conditions. Field names do NOT get prefixed.
  214. *
  215. * @since 0.1
  216. *
  217. * @param array|null $fields
  218. * @param array $conditions
  219. * @param array $options
  220. *
  221. * @return ResultWrapper
  222. */
  223. public static function rawSelect( $fields = null, array $conditions = array(), array $options = array() ) {
  224. $dbr = wfGetDB( DB_SLAVE );
  225. return $dbr->select(
  226. static::getDBTable(),
  227. $fields,
  228. count( $conditions ) == 0 ? '' : $conditions,
  229. '',
  230. $options
  231. );
  232. }
  233. public static function update( array $values, array $conditions = array() ) {
  234. $dbw = wfGetDB( DB_MASTER );
  235. return $dbw->update(
  236. static::getDBTable(),
  237. static::getPrefixedValues( $values ),
  238. static::getPrefixedValues( $conditions )
  239. );
  240. }
  241. /**
  242. * Writes the answer to the database, either updating it
  243. * when it already exists, or inserting it when it doesn't.
  244. *
  245. * @since 0.1
  246. *
  247. * @return boolean Success indicator
  248. */
  249. public function writeToDB() {
  250. if ( $this->hasIdField() ) {
  251. return $this->updateInDB();
  252. } else {
  253. return $this->insertIntoDB();
  254. }
  255. }
  256. /**
  257. * Updates the object in the database.
  258. *
  259. * @since 0.1
  260. *
  261. * @return boolean Success indicator
  262. */
  263. protected function updateInDB() {
  264. $dbw = wfGetDB( DB_MASTER );
  265. return $dbw->update(
  266. $this->getDBTable(),
  267. $this->getWriteValues(),
  268. array( static::getFieldPrefix() . static::getIDField() => $this->getId() )
  269. );
  270. }
  271. /**
  272. * Inserts the object into the database.
  273. *
  274. * @since 0.1
  275. *
  276. * @return boolean Success indicator
  277. */
  278. protected function insertIntoDB() {
  279. $dbw = wfGetDB( DB_MASTER );
  280. $result = $dbw->insert(
  281. static::getDBTable(),
  282. static::getWriteValues()
  283. );
  284. $this->setField( static::getIDField(), $dbw->insertId() );
  285. return $result;
  286. }
  287. /**
  288. * Removes the object from the database.
  289. *
  290. * @since 0.1
  291. *
  292. * @return boolean Success indicator
  293. */
  294. public function removeFromDB() {
  295. $dbw = wfGetDB( DB_MASTER );
  296. $sucecss = $dbw->delete(
  297. static::getDBTable(),
  298. array( static::getFieldPrefix() . static::getIDField() => $this->getId() )
  299. );
  300. if ( $sucecss ) {
  301. $this->setField( static::getIDField(), null );
  302. }
  303. return $sucecss;
  304. }
  305. /**
  306. * Return the names of the fields.
  307. *
  308. * @since 0.1
  309. *
  310. * @return array
  311. */
  312. public function getFields() {
  313. return $this->fields;
  314. }
  315. /**
  316. * Return the names of the fields.
  317. *
  318. * @since 0.1
  319. *
  320. * @return array
  321. */
  322. public static function getFieldNames() {
  323. return array_keys( static::getFieldTypes() );
  324. }
  325. /**
  326. * Return the names of the fields.
  327. *
  328. * @since 0.1
  329. *
  330. * @return array
  331. */
  332. public function getSetFieldNames() {
  333. return array_keys( $this->fields );
  334. }
  335. /**
  336. * Sets the value of a field.
  337. * Strings can be provided for other types,
  338. * so this method can be called from unserialization handlers.
  339. *
  340. * @since 0.1
  341. *
  342. * @param string $name
  343. * @param mixed $value
  344. *
  345. * @throws MWException
  346. */
  347. public function setField( $name, $value ) {
  348. $fields = static::getFieldTypes();
  349. if ( array_key_exists( $name, $fields ) ) {
  350. switch ( $fields[$name] ) {
  351. case 'int':
  352. $value = (int)$value;
  353. break;
  354. case 'bool':
  355. if ( is_string( $value ) ) {
  356. $value = $value !== '0';
  357. } elseif ( is_int( $value ) ) {
  358. $value = $value !== 0;
  359. }
  360. break;
  361. case 'array':
  362. if ( is_string( $value ) ) {
  363. $value = unserialize( $value );
  364. }
  365. break;
  366. case 'id':
  367. if ( is_string( $value ) ) {
  368. $value = (int)$value;
  369. }
  370. break;
  371. }
  372. $this->fields[$name] = $value;
  373. } else {
  374. throw new MWException( 'Attempted to set unknonw field ' . $name );
  375. }
  376. }
  377. /**
  378. * Gets the value of a field.
  379. *
  380. * @since 0.1
  381. *
  382. * @param string $name
  383. *
  384. * @throws MWException
  385. * @return mixed
  386. */
  387. public function getField( $name ) {
  388. if ( $this->hasField( $name ) ) {
  389. return $this->fields[$name];
  390. } else {
  391. throw new MWException( 'Attempted to get not-set field ' . $name );
  392. }
  393. }
  394. /**
  395. * Remove a field.
  396. *
  397. * @since 0.1
  398. *
  399. * @param string $name
  400. */
  401. public function removeField( $name ) {
  402. unset( $this->fields[$name] );
  403. }
  404. /**
  405. * Returns the objects database id.
  406. *
  407. * @since 0.1
  408. *
  409. * @return integer|null
  410. */
  411. public function getId() {
  412. return $this->getField( static::getIDField() );
  413. }
  414. /**
  415. * Sets the objects database id.
  416. *
  417. * @since 0.1
  418. *
  419. * @param integere|null $id
  420. */
  421. public function setId( $id ) {
  422. return $this->setField( static::getIDField(), $id );
  423. }
  424. /**
  425. * Gets if a certain field is set.
  426. *
  427. * @since 0.1
  428. *
  429. * @param string $name
  430. *
  431. * @return boolean
  432. */
  433. public function hasField( $name ) {
  434. return array_key_exists( $name, $this->fields );
  435. }
  436. /**
  437. * Gets if the object can take a certain field.
  438. *
  439. * @since 0.1
  440. *
  441. * @param string $name
  442. *
  443. * @return boolean
  444. */
  445. public static function canHasField( $name ) {
  446. return array_key_exists( $name, static::getFieldTypes() );
  447. }
  448. /**
  449. * Gets if the id field is set.
  450. *
  451. * @since 0.1
  452. *
  453. * @return boolean
  454. */
  455. public function hasIdField() {
  456. return $this->hasField( static::getIDField() )
  457. && !is_null( $this->getField( static::getIDField() ) );
  458. }
  459. /**
  460. * Sets multiple fields.
  461. *
  462. * @since 0.1
  463. *
  464. * @param array $fields The fields to set
  465. * @param boolean $override Override already set fields with the provided values?
  466. */
  467. public function setFields( array $fields, $override = true ) {
  468. foreach ( $fields as $name => $value ) {
  469. if ( $override || !$this->hasField( $name ) ) {
  470. $this->setField( $name, $value );
  471. }
  472. }
  473. }
  474. /**
  475. * Gets the fields => values to write to the surveys table.
  476. *
  477. * @since 0.1
  478. *
  479. * @return array
  480. */
  481. protected function getWriteValues() {
  482. $values = array();
  483. foreach ( static::getFieldTypes() as $name => $type ) {
  484. if ( array_key_exists( $name, $this->fields ) ) {
  485. $value = $this->fields[$name];
  486. switch ( $type ) {
  487. case 'array':
  488. $value = serialize( (array)$value );
  489. }
  490. $values[static::getFieldPrefix() . $name] = $value;
  491. }
  492. }
  493. return $values;
  494. }
  495. /**
  496. * Takes in a field or array of fields and returns an
  497. * array with their prefixed versions, ready for db usage.
  498. *
  499. * @since 0.1
  500. *
  501. * @param array|string $fields
  502. *
  503. * @return array
  504. */
  505. public static function getPrefixedFields( $fields ) {
  506. $fields = (array)$fields;
  507. foreach ( $fields as &$field ) {
  508. $field = static::getFieldPrefix() . $field;
  509. }
  510. return $fields;
  511. }
  512. /**
  513. * Takes in a field and returns an it's prefixed version, ready for db usage.
  514. *
  515. * @since 0.1
  516. *
  517. * @param string $field
  518. *
  519. * @return string
  520. */
  521. public static function getPrefixedField( $field ) {
  522. return static::getFieldPrefix() . $field;
  523. }
  524. /**
  525. * Takes in an associative array with field names as keys and
  526. * their values as value. The field names are prefixed with the
  527. * db field prefix.
  528. *
  529. * @since 0.1
  530. *
  531. * @param array $values
  532. *
  533. * @return array
  534. */
  535. public static function getPrefixedValues( array $values ) {
  536. $prefixedValues = array();
  537. foreach ( $values as $field => $value ) {
  538. $prefixedValues[static::getFieldPrefix() . $field] = $value;
  539. }
  540. return $prefixedValues;
  541. }
  542. /**
  543. * Serializes the survey to an associative array which
  544. * can then easily be converted into JSON or similar.
  545. *
  546. * @since 0.1
  547. *
  548. * @param null|array $props
  549. *
  550. * @return array
  551. */
  552. public function toArray( $fields = null ) {
  553. $data = array();
  554. $setFields = array();
  555. if ( !is_array( $fields ) ) {
  556. $setFields = $this->getSetFieldNames();
  557. } else {
  558. foreach ( $fields as $field ) {
  559. if ( $this->hasField( $field ) ) {
  560. $setFields[] = $field;
  561. }
  562. }
  563. }
  564. foreach ( $setFields as $field ) {
  565. $data[$field] = $this->getField( $field );
  566. }
  567. return $data;
  568. }
  569. public function loadDefaults( $override = true ) {
  570. $this->setFields( static::getDefaults(), $override );
  571. }
  572. /**
  573. * Returns a list of default field values.
  574. * field name => field value
  575. *
  576. * @since 0.1
  577. *
  578. * @return array
  579. */
  580. public static function getDefaults() {
  581. return array();
  582. }
  583. /**
  584. * Get API parameters for the fields supported by this object.
  585. *
  586. * @since 0.1
  587. *
  588. * @param boolean $requireParams
  589. *
  590. * @return array
  591. */
  592. public static function getAPIParams( $requireParams = true ) {
  593. $typeMap = array(
  594. 'id' => 'integer',
  595. 'int' => 'integer',
  596. 'str' => 'string',
  597. 'bool' => 'integer',
  598. 'array' => 'string'
  599. );
  600. $params = array();
  601. $defaults = static::getDefaults();
  602. foreach ( static::getFieldTypes() as $field => $type ) {
  603. if ( $field == static::getIDField() ) {
  604. continue;
  605. }
  606. $hasDefault = array_key_exists( $field, $defaults );
  607. $params[$field] = array(
  608. ApiBase::PARAM_TYPE => $typeMap[$type],
  609. ApiBase::PARAM_REQUIRED => $requireParams && !$hasDefault
  610. );
  611. if ( $type == 'array' ) {
  612. $params[$field][ApiBase::PARAM_ISMULTI] = true;
  613. }
  614. if ( $hasDefault ) {
  615. $default = is_array( $defaults[$field] ) ? implode( '|', $defaults[$field] ) : $defaults[$field];
  616. $params[$field][ApiBase::PARAM_DFLT] = $default;
  617. }
  618. }
  619. return $params;
  620. }
  621. /**
  622. * Takes an array of field name => field value and
  623. * filters it on valid field names.
  624. *
  625. * @since 0.1
  626. *
  627. * @param array $conditions
  628. * @param false|integer $id
  629. *
  630. * @return array
  631. */
  632. public static function getValidFields( array $conditions, $id = false ) {
  633. $validFields = array();
  634. $fields = static::getFieldTypes();
  635. foreach ( $conditions as $name => $value ) {
  636. if ( array_key_exists( $name, $fields ) ) {
  637. $validFields[$name] = $value;
  638. }
  639. }
  640. if ( $id !== false ) {
  641. $validParams[static::getIDField()] = $id;
  642. }
  643. return $validFields;
  644. }
  645. }