PageRenderTime 38ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/models/twitter_status.php

http://github.com/neilcrookes/CakePHP-Twitter-API-Plugin
PHP | 295 lines | 168 code | 13 blank | 114 comment | 21 complexity | 83599e8da6bb710b296859a04ecea8cf MD5 | raw file
  1. <?php
  2. /**
  3. * TwitterStatus model provides custom find types and other methods for managing
  4. * twitter statuses through the twitter API.
  5. *
  6. * @author Neil Crookes <neil@neilcrookes.com>
  7. * @link http://www.neilcrookes.com
  8. * @copyright (c) 2010 Neil Crookes
  9. * @license MIT License - http://www.opensource.org/licenses/mit-license.php
  10. */
  11. class TwitterStatus extends TwitterAppModel {
  12. /**
  13. * The model's schema. Used by FormHelper
  14. *
  15. * @var array
  16. */
  17. public $_schema = array(
  18. 'id' => array('type' => 'integer', 'length' => '11'),
  19. 'text' => array('type' => 'string', 'length' => '140'),
  20. 'in_reply_to_status_id' => array('type' => 'integer', 'length' => '11'),
  21. 'in_reply_to_user_id' => array('type' => 'integer', 'length' => '11'),
  22. 'in_reply_to_screen_name' => array('type' => 'string', 'length' => '255'),
  23. );
  24. /**
  25. * Validation rules for the model
  26. *
  27. * @var array
  28. */
  29. public $validate = array(
  30. 'text' => array(
  31. 'notEmpty' => array(
  32. 'rule' => 'notEmpty',
  33. 'message' => 'Please enter some text',
  34. ),
  35. 'maxLength' => array(
  36. 'rule' => array('maxLength', 140),
  37. 'message' => 'Text cannot exceed 140 characters',
  38. ),
  39. ),
  40. 'in_reply_to_status_id' => array(
  41. 'numeric' => array(
  42. 'rule' => 'numeric',
  43. 'message' => 'The ID of the status you are replying to should be numeric',
  44. 'required' => false,
  45. 'allowEmpty' => true,
  46. ),
  47. ),
  48. 'in_reply_to_user_id' => array(
  49. 'numeric' => array(
  50. 'rule' => 'numeric',
  51. 'message' => 'The ID of the user you are replying to should be numeric',
  52. 'required' => false,
  53. 'allowEmpty' => true,
  54. ),
  55. ),
  56. );
  57. /**
  58. * Custom find types available on this model
  59. *
  60. * @var array
  61. */
  62. public $_findMethods = array(
  63. 'publicTimeline' => true,
  64. 'homeTimeline' => true,
  65. 'userTimeline' => true,
  66. 'mentions' => true,
  67. 'retweetedByMe' => true,
  68. 'retweetedToMe' => true,
  69. 'show' => true,
  70. 'retweetsOfMe' => true,
  71. 'retweets' => true,
  72. 'retweetedBy' => true,
  73. );
  74. /**
  75. * The custom find types that require authentication
  76. *
  77. * @var array
  78. */
  79. public $findMethodsRequiringAuth = array(
  80. 'homeTimeline',
  81. 'userTimeline',
  82. 'mentions',
  83. 'retweetedByMe',
  84. 'retweetedToMe',
  85. 'retweetsOfMe',
  86. 'retweets',
  87. 'retweetedBy'
  88. );
  89. /**
  90. * The options allowed by each of the custom find types
  91. *
  92. * @var array
  93. */
  94. public $allowedFindOptions = array(
  95. 'publicTimeline' => array('skip_user', 'include_rts', 'include_entities'),
  96. 'homeTimeline' => array('since_id', 'max_id', 'count', 'page', 'skip_user', 'include_entities'),
  97. 'userTimeline' => array('since_id', 'max_id', 'count', 'page', 'skip_user', 'include_rts', 'include_entities', 'id', 'user_id', 'screen_name'),
  98. 'mentions' => array('since_id', 'max_id', 'count', 'page', 'include_rts', 'include_entities'),
  99. 'retweetedByMe' => array('since_id', 'max_id', 'count', 'page'),
  100. 'retweetedToMe' => array('since_id', 'max_id', 'count', 'page'),
  101. 'retweetsOfMe' => array('since_id', 'max_id', 'count', 'page'),
  102. 'show' => array('id'),
  103. 'retweets' => array('count', 'id'),
  104. 'retweetedBy' => array('count', 'page', 'id'),
  105. );
  106. /**
  107. * The vast majority of the custom find types actually follow the same format
  108. * so there was little point explicitly writing them all out. Instead, if the
  109. * method corresponding to the custom find type doesn't exist, the options are
  110. * applied to the model's request property here and then we just call
  111. * parent::find('all') to actually trigger the request and return the response
  112. * from the API.
  113. *
  114. * In addition, if you try to fetch a timeline that supports paging, but you
  115. * don't specify paging params, you really want all tweets in that timeline
  116. * since time imemoriam. But twitter will only return a maximum of 200 per
  117. * request. So, we make multiple calls to the API for 200 tweets at a go, for
  118. * subsequent pages, then merge the results together before returning them.
  119. *
  120. * Twitter's API uses a count parameter where in CakePHP we'd normally use
  121. * limit, so we also copy the limit value to count so we can use our familiar
  122. * params.
  123. *
  124. * @param string $type
  125. * @param array $options
  126. * @return mixed
  127. */
  128. public function find($type, $options = array()) {
  129. if (!empty($options['limit']) && empty($options['count'])) {
  130. $options['count'] = $options['limit'];
  131. }
  132. if ((empty($options['page']) || empty($options['count']))
  133. && array_key_exists($type, $this->allowedFindOptions)
  134. && in_array('page', $this->allowedFindOptions[$type])
  135. && in_array('count', $this->allowedFindOptions[$type])) {
  136. $options['page'] = 1;
  137. $options['count'] = 200;
  138. $results = array();
  139. while (($page = $this->find($type, $options)) != false) {
  140. $results = array_merge($results, $page);
  141. $options['page']++;
  142. }
  143. return $results;
  144. }
  145. if (method_exists($this, '_find' . Inflector::camelize($type))) {
  146. return parent::find($type, $options);
  147. }
  148. $this->request['uri']['path'] = '1/statuses/' . Inflector::underscore($type);
  149. if (array_key_exists($type, $this->allowedFindOptions)) {
  150. $this->request['uri']['query'] = array_intersect_key($options, array_flip($this->allowedFindOptions[$type]));
  151. }
  152. if (in_array($type, $this->findMethodsRequiringAuth)) {
  153. $this->request['auth'] = true;
  154. }
  155. return parent::find('all', $options);
  156. }
  157. /**
  158. * Retweeted By
  159. * -------------
  160. *
  161. * TwitterStatus::find('retweetedBy', $options)
  162. *
  163. * **Options:**
  164. *
  165. * - id integer
  166. * - limit integer
  167. * - page integer
  168. *
  169. * See http://dev.twitter.com/doc/get/statuses/user_timeline for details
  170. *
  171. * **Results:**
  172. *
  173. * An array of tweets
  174. *
  175. * **Notes:**
  176. *
  177. * You should provide either user_id or screen_name in the conditions
  178. *
  179. * @param $state string 'before' or 'after'
  180. * @param $query array
  181. * @param $results array
  182. * @return mixed
  183. * @access protected
  184. * */
  185. protected function _findRetweetedBy($state, $query = array(), $results = array()) {
  186. if ($state == 'before') {
  187. if (empty($query['id'])) {
  188. return false;
  189. }
  190. $this->request = array(
  191. 'uri' => array(
  192. 'path' => '1/statuses/' . $query['id'] . '/retweeted_by'
  193. ),
  194. 'auth' => true,
  195. );
  196. $this->request['uri']['query'] = array_intersect_key($query, array_flip($this->allowedFindOptions['retweetedBy']));
  197. return $query;
  198. } else {
  199. return $results;
  200. }
  201. }
  202. /**
  203. * Creates a tweet
  204. *
  205. * @param mixed $data
  206. * @param mixed $validate
  207. * @param mixed $fieldList
  208. * @return mixed
  209. */
  210. public function tweet($data = null, $validate = true, $fieldList = array()) {
  211. $this->request = array(
  212. 'uri' => array(
  213. 'path' => '1/statuses/update',
  214. ),
  215. );
  216. if (isset($data['TwitterStatus']['text'])) {
  217. $this->request['body'] = array(
  218. 'status' => $data['TwitterStatus']['text'],
  219. );
  220. }
  221. return $this->save($data, $validate, $fieldList);
  222. }
  223. /**
  224. * Retweets a tweet
  225. *
  226. * @param integer $id Id of the tweet you want to retweet
  227. * @return mixed
  228. */
  229. public function retweet($id = null) {
  230. if (!$id) {
  231. return false;
  232. }
  233. if (!is_numeric($id)) {
  234. return false;
  235. }
  236. $this->request = array(
  237. 'uri' => array(
  238. 'path' => '1/statuses/retweet/'.$id,
  239. ),
  240. );
  241. $this->create();
  242. // Dummy data ensures Model::save() does in fact call DataSource::create()
  243. $data = array('TwitterStatus' => array('text' => 'dummy'));
  244. return $this->save($data);
  245. }
  246. /**
  247. * Called by tweet or retweet
  248. *
  249. * @param mixed $data
  250. * @param mixed $validate
  251. * @param mixed $fieldList
  252. * @return mixed
  253. */
  254. public function save($data = null, $validate = true, $fieldList = array()) {
  255. $this->request['auth'] = true;
  256. $result = parent::save($data, $validate, $fieldList);
  257. if ($result && !empty($this->response['id'])) {
  258. $this->setInsertID($this->response['id']);
  259. }
  260. return $result;
  261. }
  262. /**
  263. * Deletes a tweet
  264. *
  265. * @param integer $id Id of the tweet to be deleted
  266. * @param boolean $cascade
  267. * @return boolean
  268. */
  269. public function delete($id = null, $cascade = true) {
  270. $this->request = array(
  271. 'uri' => array(
  272. 'path' => '1/statuses/destroy',
  273. 'query' => array(
  274. 'id' => $id,
  275. ),
  276. ),
  277. 'auth' => true,
  278. );
  279. return parent::delete($id, $cascade);
  280. }
  281. }
  282. ?>