PageRenderTime 51ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/src/classes/team.inc

http://leaguerunner.googlecode.com/
PHP | 553 lines | 487 code | 34 blank | 32 comment | 23 complexity | 0465c9250011e1feb762e4bd259a7711 MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. class Team extends LeaguerunnerObject
  3. {
  4. var $team_id;
  5. var $name;
  6. var $website;
  7. var $shirt_colour;
  8. var $home_field;
  9. var $region_preference;
  10. var $status;
  11. var $rating;
  12. var $league_name;
  13. var $league_tier;
  14. var $roster;
  15. var $preferred_ratio;
  16. function __construct( $array = array() )
  17. {
  18. global $dbh;
  19. // Fixups
  20. if($this->league_tier) {
  21. $this->league_name = sprintf("$this->league_name Tier %02d", $this->league_tier);
  22. }
  23. // Clean up website
  24. if( $this->website ) {
  25. if(strncmp($this->website, "http://", 7) != 0) {
  26. $this->website = "http://" .$this->website;
  27. }
  28. }
  29. // Set default for calculated values to -1, so that load-on-demand
  30. // will work
  31. $this->_avg_skill = null;
  32. $this->_player_count = null;
  33. $this->home_games = -1;
  34. $this->away_games = -1;
  35. $this->preferred_ratio = -1;
  36. return true;
  37. }
  38. /**
  39. * Add a player to the roster, with the given status
  40. */
  41. function add_player( &$player, $status )
  42. {
  43. if( !is_object($player) ) {
  44. $object->user_id = $player;
  45. $player = &$object;
  46. }
  47. // TODO
  48. return false;
  49. }
  50. /**
  51. * Update status of a player currently on the roster
  52. */
  53. function set_player_status( &$player, $status )
  54. {
  55. if( !is_object($player) ) {
  56. $object->user_id = $player;
  57. $player = &$object;
  58. }
  59. // TODO
  60. return false;
  61. }
  62. /**
  63. * Remove a player from the roster.
  64. */
  65. function remove_player( &$player )
  66. {
  67. if( !is_object($player) ) {
  68. $object->user_id = $player;
  69. $player = &$object;
  70. }
  71. // TODO
  72. return false;
  73. }
  74. function get_roster()
  75. {
  76. global $dbh;
  77. $sth = $dbh->prepare(
  78. "SELECT
  79. p.user_id as id,
  80. CONCAT(p.firstname, ' ', p.lastname) as fullname,
  81. p.gender,
  82. p.shirtsize,
  83. p.skill_level,
  84. p.status AS player_status,
  85. r.status,
  86. r.date_joined
  87. FROM
  88. teamroster r
  89. LEFT JOIN person p ON (r.player_id = p.user_id)
  90. WHERE
  91. r.team_id = ?
  92. ORDER BY r.status, p.gender, p.lastname");
  93. $sth->execute(array($this->team_id));
  94. $this->roster = array();
  95. while($player = $sth->fetch(PDO::FETCH_OBJ) ) {
  96. $this->roster[] = $player;
  97. }
  98. }
  99. function count_players()
  100. {
  101. global $dbh;
  102. if( is_null($this->_player_count) ) {
  103. $sth = $dbh->prepare("SELECT COUNT(r.player_id)
  104. FROM teamroster r
  105. WHERE
  106. r.status IN ('player', 'captain', 'assistant')
  107. AND r.team_id = ?");
  108. $sth->execute(array( $this->team_id));
  109. $this->_player_count = $sth->fetchColumn();
  110. }
  111. return $this->_player_count;
  112. }
  113. function save ()
  114. {
  115. global $dbh;
  116. if(! count($this->_modified_fields)) {
  117. // No modifications, no need to save
  118. return true;
  119. }
  120. if( ! $this->_in_database ) {
  121. if( ! $this->create() ) {
  122. error_exit("Couldn't create team record");
  123. }
  124. }
  125. $fields = array();
  126. $fields_data = array();
  127. foreach ( $this->_modified_fields as $key => $value) {
  128. $fields[] = "$key = ?";
  129. $fields_data[] = $this->{$key};
  130. }
  131. if(count($fields_data) != count($fields)) {
  132. error_exit("Internal error: Incorrect number of fields set");
  133. }
  134. $sth = $dbh->prepare('UPDATE team SET '
  135. . join(", ", $fields)
  136. . ' WHERE team_id = ?');
  137. $fields_data[] = $this->team_id;
  138. $sth->execute( $fields_data );
  139. if(1 != $sth->rowCount()) {
  140. # Affecting zero rows is possible but usually unwanted
  141. error_exit("Internal error: Strange number of rows affected");
  142. }
  143. unset($this->_modified_fields);
  144. # TODO: process roster list and add/remove as necessary
  145. return true;
  146. }
  147. // TODO: this belongs in league.inc
  148. function validate_unique ($name)
  149. {
  150. global $dbh;
  151. $opt = '';
  152. $params = array(
  153. $name,
  154. );
  155. $sth = $dbh->prepare('SELECT year, season, day
  156. FROM league
  157. WHERE league_id = ?');
  158. $sth->execute( array( $this->league_id ) );
  159. $league_data = $sth->fetch(PDO::FETCH_ASSOC);
  160. foreach( array_keys( $league_data ) as $key )
  161. {
  162. if( isset( $league_data[$key] ) && $league_data[$key] != '' ) {
  163. $opt .= " AND league.$key = ?";
  164. $params[] = $league_data[$key];
  165. } else {
  166. $opt .= " AND league.$key IS NULL";
  167. }
  168. }
  169. if (isset ($this->team_id)) {
  170. $opt .= ' AND team.team_id != ?';
  171. $params[] = $this->team_id;
  172. }
  173. $sth = $dbh->prepare('SELECT COUNT(*) FROM team, leagueteams, league
  174. WHERE team.name = ?
  175. AND team.team_id = leagueteams.team_id
  176. AND leagueteams.league_id = league.league_id
  177. ' . $opt);
  178. $sth->execute( $params );
  179. return ($sth->fetchColumn() == 0 );
  180. }
  181. function create ()
  182. {
  183. global $dbh;
  184. if( $this->_in_database ) {
  185. return false;
  186. }
  187. if( ! $this->name ) {
  188. return false;
  189. }
  190. $sth = $dbh->prepare('INSERT INTO team (name) VALUES(?)');
  191. $sth->execute( array( $this->name ) );
  192. if( 1 != $sth->rowCount() ) {
  193. return false;
  194. }
  195. $sth = $dbh->prepare('SELECT LAST_INSERT_ID() FROM team');
  196. $sth->execute();
  197. $this->team_id = $sth->fetchColumn();
  198. return true;
  199. }
  200. function delete()
  201. {
  202. global $dbh;
  203. if ( ! $this->_in_database ) {
  204. return false;
  205. }
  206. // Can only delete inactive teams
  207. if ( $this->league_id != 1 ) {
  208. error_exit("Cannot delete team: Team must be in the 'Inactive Teams' league to be deleted");
  209. }
  210. // Check that this team is not scheduled for any games. If so,
  211. // we bail
  212. $sth = $dbh->prepare('SELECT COUNT(*) FROM schedule WHERE home_team = :team_id OR away_team = :team_id');
  213. $sth->execute( array( 'team_id' => $this->team_id ) );
  214. if( $sth->fetchColumn() > 0) {
  215. error_exit("Cannot delete team: Team must not have any games on the schedule");
  216. }
  217. $queries = array(
  218. 'DELETE FROM teamroster WHERE team_id = ?',
  219. 'DELETE FROM leagueteams WHERE team_id = ?',
  220. 'DELETE FROM team WHERE team_id = ?',
  221. );
  222. return $this->generic_delete( $queries, $this->team_id );
  223. }
  224. /**
  225. * Calculates the "Spence Balancing Factor" or SBF for the team.
  226. * This is the average of all score differentials for games played
  227. * to-date. A lower value indicates more even match-ups with opponents.
  228. *
  229. * The team SBF can be compared to the SBF for the division. If it's too far
  230. * off from the division/league SBF, it's an indication that the team is
  231. * involved in too many blowout wins/losses.
  232. */
  233. function calculate_sbf()
  234. {
  235. global $dbh;
  236. $sth = $dbh->prepare('SELECT ROUND(AVG(ABS(s.home_score - s.away_score)),2) FROM schedule s WHERE s.home_team = :id OR s.away_team = :id');
  237. $sth->execute(array( 'id' => $this->team_id) );
  238. $sbf = $sth->fetchColumn();
  239. if( $sbf == "") {
  240. $sbf = "n/a";
  241. }
  242. return $sbf;
  243. }
  244. /**
  245. * Calculate the average skill for this team
  246. */
  247. function avg_skill()
  248. {
  249. global $dbh;
  250. if( is_null($this->_avg_skill) ) {
  251. $sth = $dbh->prepare('SELECT ROUND(AVG(p.skill_level),2) FROM teamroster r INNER JOIN person p ON (r.player_id = p.user_id) WHERE r.team_id = ?');
  252. $sth->execute(array( $this->team_id));
  253. $this->_avg_skill = $sth->fetchColumn();
  254. }
  255. return $this->_avg_skill;
  256. }
  257. /**
  258. * Home/away ratio
  259. *
  260. * Returns ratio (between 0 and 1) of games for which this team was the
  261. * home team.
  262. */
  263. function home_away_ratio()
  264. {
  265. global $dbh;
  266. if( $this->home_games < 0 || $this->away_games < 0 ) {
  267. $sth = $dbh->prepare('SELECT home_team = ? AS is_home, count(*) AS num_games FROM schedule WHERE home_team = ? or away_team = ? GROUP BY is_home ORDER BY is_home');
  268. // Grr... if we were using a better database, we could pass one
  269. // parameter and use it multiple times. Alas, mysql doesn't
  270. // allow this, so we have to pass team_id three times.
  271. $sth->execute( array($this->team_id, $this->team_id, $this->team_id) );
  272. while($row = $sth->fetch( PDO::FETCH_ASSOC ) ) {
  273. if($row['is_home']) {
  274. $this->home_games = $row['num_games'];
  275. } else {
  276. $this->away_games = $row['num_games'];
  277. }
  278. }
  279. // Make sure we have a value >= 0 for each, so we don't hit the db again
  280. $this->home_games = ($this->home_games < 0 ? 0 : $this->home_games);
  281. $this->away_games = ($this->away_games < 0 ? 0 : $this->away_games);
  282. }
  283. if( $this->home_games + $this->away_games < 1 ) {
  284. # Avoid divide-by-zero
  285. return 0;
  286. }
  287. return( $this->home_games / ($this->home_games + $this->away_games) );
  288. }
  289. function preferred_field_ratio()
  290. {
  291. global $dbh;
  292. if( $this->preferred_ratio >= 0 ) {
  293. return $this->preferred_ratio;
  294. }
  295. if( ! $this->region_preference
  296. || $this->region_preference == '---' ) {
  297. // No preference means they're always happy. We set
  298. // this to over 100% to force them to sort last when
  299. // ordering by ratio, so that teams with a preference
  300. // always appear before them.
  301. $this->preferred_ratio = 2;
  302. return ($this->preferred_ratio);
  303. }
  304. // It's not the most evil SQL hack in Leaguerunner, but it's
  305. // probably a runner-up. The idea is to get a count of the
  306. // games played in the preferred region or on a home field, and
  307. // a count played outside.
  308. $sth = $dbh->prepare(
  309. 'SELECT
  310. IF(g.fid = t.home_field, 1, COALESCE(f.region,p.region) = t.region_preference) AS is_preferred,
  311. COUNT(*) AS num_games
  312. FROM schedule s
  313. LEFT JOIN gameslot g USING (game_id)
  314. LEFT JOIN field f USING (fid)
  315. LEFT JOIN field p ON (f.parent_fid = p.fid),
  316. team t
  317. WHERE (s.home_team = t.team_id OR s.away_team = t.team_id)
  318. AND t.team_id = ? GROUP BY is_preferred');
  319. $sth->execute( array( $this->team_id) );
  320. $preferred = 0;
  321. $not_preferred = 0;
  322. while($row = $sth->fetch( PDO::FETCH_ASSOC ) ) {
  323. if($row['is_preferred']) {
  324. $preferred = $row['num_games'];
  325. } else {
  326. $not_preferred = $row['num_games'];
  327. }
  328. }
  329. if( $preferred + $not_preferred < 1 ) {
  330. # Avoid divide-by-zero
  331. return 0;
  332. }
  333. $this->preferred_ratio = $preferred / ($preferred + $not_preferred);
  334. return ($this->preferred_ratio);
  335. }
  336. /**
  337. * Move team to another league.
  338. */
  339. function move_team_to( $league_id )
  340. {
  341. global $dbh;
  342. $sth = $dbh->prepare('UPDATE leagueteams SET league_id = ? WHERE team_id = ? AND league_id = ?');
  343. $sth->execute( array($league_id, $this->team_id, $this->league_id) );
  344. if( 1 != $sth->rowCount() ) {
  345. error_exit("Couldn't move team between leagues");
  346. }
  347. $this->league_id = $league_id;
  348. return true;
  349. }
  350. /**
  351. * Swap this team with another team, updating schedules
  352. */
  353. function swap_team_with( $other )
  354. {
  355. if( ! $other ) {
  356. return false;
  357. }
  358. $this_league_id = $this->league_id;
  359. $other_league_id = $other->league_id;
  360. // Moving to same league is silly
  361. if( $this_league_id == $other_league_id ) {
  362. return false;
  363. }
  364. # Move team to other league
  365. $this->move_team_to( $other_league_id );
  366. $other->move_team_to( $this_league_id );
  367. # Get future games for $this and $other
  368. $this_games = game_load_many(array('either_team' => $this->team_id, 'game_date_future' => 1));
  369. $other_games = game_load_many(array('either_team' => $other->team_id, 'game_date_future' => 1));
  370. # Update future unplayed games in both leagues
  371. foreach ($this_games as $game) {
  372. if( $game->home_id == $this->team_id ) {
  373. $game->set('home_team', $other->team_id);
  374. } else {
  375. $game->set('away_team', $other->team_id);
  376. }
  377. $game->save();
  378. }
  379. foreach ($other_games as $game) {
  380. if( $game->home_id == $other->team_id ) {
  381. $game->set('home_team', $this->team_id);
  382. } else {
  383. $game->set('away_team', $this->team_id);
  384. }
  385. $game->save();
  386. }
  387. return true;
  388. }
  389. /**
  390. * Compare two teams by their home field ratio
  391. */
  392. static function cmp_home_field_ratio ( $a, $b )
  393. {
  394. $a_ratio = $a->preferred_field_ratio();
  395. $b_ratio = $b->preferred_field_ratio();
  396. if( $a_ratio == $b_ratio ) {
  397. return 0;
  398. }
  399. return ($a_ratio > $b_ratio) ? 1 : -1;
  400. }
  401. }
  402. function team_query( $array = array() )
  403. {
  404. global $CONFIG, $dbh;
  405. $order = '';
  406. $query = array();
  407. $params = array();
  408. foreach ($array as $key => $value) {
  409. switch( $key ) {
  410. case '_extra':
  411. /* Just slap on any extra query fields desired */
  412. $query[] = $value;
  413. break;
  414. case '_order':
  415. $order = ' ORDER BY ' . $value;
  416. break;
  417. case 'league_id':
  418. $query[] = "l.$key = ?";
  419. $params[] = $value;
  420. break;
  421. default:
  422. $query[] = "t.$key = ?";
  423. $params[] = $value;
  424. }
  425. }
  426. $local_adjust_secs = $CONFIG['localization']['tz_adjust'] * 60;
  427. $sth = $dbh->prepare("SELECT
  428. t.*,
  429. 1 AS _in_database,
  430. l.name AS league_name,
  431. l.tier AS league_tier,
  432. l.day AS league_day,
  433. l.season AS league_season,
  434. l.year AS league_year,
  435. l.league_id,
  436. (UNIX_TIMESTAMP(CONCAT(roster_deadline,' 23:59:59')) + $local_adjust_secs) AS roster_deadline
  437. FROM team t
  438. INNER JOIN leagueteams s ON (s.team_id = t.team_id)
  439. INNER JOIN league l ON (s.league_id = l.league_id)
  440. WHERE " . implode(' AND ',$query) . $order);
  441. $sth->execute( $params );
  442. return $sth;
  443. }
  444. function team_load( $array = array() )
  445. {
  446. $result = team_query( $array );
  447. return $result->fetchObject('Team');
  448. }
  449. function team_load_many( $array = array() )
  450. {
  451. $sth = team_query( $array );
  452. $teams = array();
  453. while( $t = $sth->fetchObject('Team', array(LOAD_RELATED_DATA))) {
  454. $teams[$t->team_id] = $t;
  455. }
  456. return $teams;
  457. }
  458. ?>