Location: PHPKode > projects > Online Fantasy Football League > offl-0.2.6/www/lib/classes/offl_trade.php
<?php
/**
 * Contains the implementation of the {@link OFFL_Trade} class
 *
 * @package offl
 * @author Stephen Rochelle <hide@address.com>
 * @see OFFL_tradeplayer.php
 * @see OFFL_tradeprotest.php
 */

if (strtr(__FILE__, "\\", "/") == $_SERVER["SCRIPT_FILENAME"])
{	die ("Cannot access file directly!");	}

require_once($DOC_ROOT . "/lib/classes/offl_dbobject.php");
/**
 * Defines interfaces for the trade process.
 *
 * @see OFFL_TradeProtest
 * @see OFFL_TradePlayer
 * @package offl
 */
class OFFL_Trade extends OFFL_DBObject
{
	/**
	 * @var integer
	 */
	var $_trade_id = NULL;
	/**
	 * @var integer
	 */
	var $_league_id = NULL;
	/**
	 * @var integer
	 */
	var $_proposed_fflteam_id = NULL;
	/**
	 * @var integer
	 */
	var $_target_fflteam_id = NULL;
	/**
	 * @var array Array of integers (player ids)
	 */
	var $_proposed_player_id_array = array();
	/**
	 * @var array Array of integers (player ids)
	 */
	var $_target_player_id_array = array();
	/**
	 * @var string
	 * I need to list allowed values
	 */
	var $_status = "proposed";
	/**
	 * @var integer
	 */
	var $_protest_votes = NULL;
	/**
	 * @var integer
	 */
	var $_protest_votes_needed = NULL;
	/**
	 * @var string SQL DATETIME format, GMT
	 */
	var $_deadline = NULL;

	/**
	 * Constructor
	 *
	 * @param integer $trade_id Optional: if set, loads info from database
	 * @see populate()
	 */
	function OFFL_Trade ($trade_id = NULL)
	{
		OFFL_DBObject::OFFL_DBObject();

	    if(isset($trade_id))
		{
			$this->_trade_id = $trade_id;
			$this->populate();
		}
	}

	/**
	 * Pulls info from database.  {@link $_trade_id} must be set.
	 *
	 * @return boolean TRUE if successful, FALSE otherwise.
	 */
	function populate ()
	{
		if(is_null($this->_trade_id))
		{
			$this->_emsg = "\$_trade_id is not set.  Cannot populate Trade object.";
			error_log ($this->_emsg);
			return FALSE;
		}
		$sql = "SELECT * FROM trades WHERE trade_id = $this->_trade_id";
		$result = mysql_query($sql,$this->_conn) or die (mysql_error() . ": $sql");

		if(mysql_num_rows($result) == 0)
		{
			$this->_emsg = "No trade records found for trade_id " . $this->_trade_id . ".";
			error_log ($this->_emsg);
			mysql_free_result($result);
			return FALSE;
		}
		$this->_league_id = mysql_result($result,0,"league_id");
		$this->_proposed_fflteam_id = mysql_result($result,0,"proposed_fflteam_id");
		$this->_target_fflteam_id = mysql_result($result,0,"target_fflteam_id");
		$this->_status = htmlspecialchars(stripslashes(mysql_result($result,0,"status")));
		$this->_deadline = mysql_result($result,0,"deadline");
		$this->_proposed_player_id_array = array_diff(explode(",", mysql_result($result,0,"proposed_player_id_array")), array(""));
		$this->_target_player_id_array = array_diff(explode(",", mysql_result($result,0,"target_player_id_array")), array(""));
		mysql_free_result($result);
		
		$x = new OFFL_TradeProtest();
		$x->setTradeID($this->_trade_id);
		$this->_protest_votes = $x->getNumberOfTradeProtests();

		$x = new OFFL_Control($this->_league_id);
		$league = new OFFL_League($this->_league_id);

		$this->_protest_votes_needed = ceil($x->getValue("CONFIG_TRADE_PROTEST_PCT") * $league->getNumberOfFFLTeams() / 100);

		return TRUE;
	}

	/**
	 * Saves the transaction to the database
	 *
	 * Determines INSERT vs UPDATE automagically
	 */
	function save ()
	{
		if (is_null($this->_trade_id)) // insert a new trade
		{
			// status is set by a default value in the database
			$sql = "INSERT INTO trades (league_id, proposed_fflteam_id, target_fflteam_id, proposed_player_id_array, target_player_id_array) VALUES ('" . $this->_league_id . "', '" . $this->_proposed_fflteam_id . "', '" . $this->_target_fflteam_id . "', '" . implode(",", $this->_proposed_player_id_array) ."', '" . implode(",", $this->_target_player_id_array) . "')";
		}
		else
		{
			$sql = "UPDATE trades SET league_id='" . $this->_league_id . "', proposed_fflteam_id='" . $this->_proposed_fflteam_id . "', target_fflteam_id='" . $this->_target_fflteam_id . "', proposed_player_id_array='" . implode(",", $this->_proposed_player_id_array) ."', target_player_id_array='" . implode(",", $this->_target_player_id_array) ."', status='" . mysql_escape_string($this->_status) . "', deadline='" . mysql_escape_string($this->_deadline) . "' WHERE trade_id='" . $this->_trade_id . "'";
		}
	    $result = mysql_query($sql,$this->_conn) or die (mysql_error() . ": $sql");
		return $result;
	}

	/**
	 * @return integer
	 */
	function getTradeID()
	{
		return $this->_trade_id;
	}

	function setLeagueID($league_id)
	{
		$this->_league_id = $league_id;
	}

	/**
	 * @return integer
	 */
	function getLeagueID()
	{
		return $this->_league_id;
	}

	function setProposedFFLTeamID($proposed_fflteam_id)
	{
		$this->_proposed_fflteam_id = $proposed_fflteam_id;
	}

	/**
	 * @return integer
	 */
	function getProposedFFLTeamID()
	{
		return $this->_proposed_fflteam_id;
	}

	function setTargetFFLTeamID($target_fflteam_id)
	{
		$this->_target_fflteam_id = $target_fflteam_id;
	}

	/**
	 * @return integer
	 */
	function getTargetFFLTeamID()
	{
		return $this->_target_fflteam_id;
	}

	function setProposedPlayerIDArray($proposed_player_id_array)
	{
		$this->_proposed_player_id_array = $proposed_player_id_array;
	}

	/**
	 * @return array Array of integers
	 */
	function getProposedPlayerIDArray()
	{
		return $this->_proposed_player_id_array;
	}

	function setTargetPlayerIDArray($target_player_id_array)
	{
		$this->_target_player_id_array = $target_player_id_array;
	}

	/**
	 * @return array Array of integers
	 */
	function getTargetPlayerIDArray()
	{
		return $this->_target_player_id_array;
	}

	function setStatus($status)
	{
		$this->_status = $status;
	}

	/**
	 * @return string Need to document allowed values
	 */
	function getStatus()
	{
		return $this->_status;
	}

	/**
	 * @param boolean $refresh Optional: If set TRUE, updates from database
	 * @return integer
	 */
	function getProtestVotes($refresh=FALSE)
	{
		if ($refresh)
		{
				$this->_protest_votes = $this->getNumberOfTradeProtests();
		}
		return $this->_protest_votes;
	}

	/**
	 * @return integer
	 */
	function getProtestVotesNeeded()
	{
		return $this->_protest_votes_needed;
	}

// ****************************************************
// The Deadline functions must be fleshed out and then
// inserted properly into populate, save, etc.
// v1.0 Note: what does this mean?

	/**
	 * 
	 * @param integer $deadline Timestamp, GMT, or NULL to unset
	 * @param string $time Optional: If set "trade" (default), uses CONFIG_TRADE_HOURS for timestamp offset, otherwise uses CONFIG_ROSTERVIOLATION_HOURS
	 */
	function setDeadline($deadline,$time="trade")
	{
		if (is_null($deadline))
		{
			$this->_deadline = NULL;
		}
		else
		{
			$x = new OFFL_Control($this->_league_id);
			if ($time == "trade")
			{	$trade_delay = $x->getValue("CONFIG_TRADE_HOURS");	}
			else
			{	$trade_delay = $x->getValue("CONFIG_ROSTERVIOLATION_HOURS");	}
			$deadline += $trade_delay * 3600; // convert hours to seconds
			$this->_deadline = gmdate("Y-m-d H:i:s", $deadline);
		}
	}

	/**
	 * @return integer Timestamp, GMT (output from {@link gmmktime()})
	 */
	function getDeadline()
	{
		if (is_null($this->_deadline))
			return $this->_deadline;
		// assemble DATETIME format into UNIX timestamp
		list($date, $time) = explode(" ", $this->_deadline);
		list($Y, $M, $D) = explode("-", $date);
		list($h, $i, $s) = explode(":", $time);
		$time = gmmktime($h, $i, $s, $M, $D, $Y);
		return $time;
	}

	/**
	 * Returns trade protests lodged against this trade.  {@link $_trade_id} must be set.
	 *
	 * @return array Array of {@link OFFL_TradeProtest} objects, FALSE if {@link $_trade_id} not set
	 */
	function getTradeProtests()
	{
		$retArr = array();
		if(is_null($this->_trade_id))
		{
			$this->_emsg = "\$_trade_id is not set.  Cannot retrieve TradeProtest objects.";
			error_log ($this->_emsg);
			return FALSE;
		}
		$sql = "SELECT protest_id FROM tradeprotests WHERE trade_id=" . $this->_trade_id;
		$result = mysql_query($sql,$this->_conn) or die (mysql_error() . ": $sql");

		for($i=0; $i<mysql_num_rows($result); $i++)
		{
			$tp = new OFFL_TradeProtest(mysql_result($result,$i,"protest_id"));
			array_push($retArr, $tp);
		}
		mysql_free_result($result);

		return $retArr;
	}

	/**
	 * Returns trades linked to a given {@link OFFL_FFLTeam}, optionally restricted
	 *
	 * @param integer $fflteam_id OFFL_FFLTeam UID used for trade lookup
	 * @param string $target Optional: default "all" returns both proposed and target trades, may also be set "proposed" or "target" to return only the relevant trades.  Other values return nothing.
	 * @return array Array of OFFL_Trades
	 */
	function getAllTeamTrades($fflteam_id, $target="all")
	{
		$retArr = array();
		$sql = "SELECT trade_id FROM trades WHERE ";
		switch ($target)
		{
			case "all":
				$sql .= "proposed_fflteam_id=" . $fflteam_id . " OR target_fflteam_id=" . $fflteam_id;
				break;
			case "proposed":
				$sql .= "proposed_fflteam_id=" . $fflteam_id;
				break;
			case "target":
				$sql .= "target_fflteam_id=" . $fflteam_id;
				break;
			default:
				$sql .= "1=0";
				break;
		}
		$result = mysql_query($sql,$this->_conn) or die (mysql_error() . ": $sql");

		for($i=0; $i<mysql_num_rows($result); $i++)
		{
			$t = new OFFL_Trade(mysql_result($result,$i,"trade_id"));
			array_push($retArr, $t);
		}
		mysql_free_result($result);

		return $retArr;
	}

	/**
	 * Pulls trades from an {@link OFFL_League} <i>not</i> related to a given {@link OFFL_FFLTeam}
	 *
	 * Normally used to display pending trades for possible protest and the like.  It pulls all non-team
	 * trades within a league that are accepted / vetoed / protested, with optional refinement.
	 *
	 * @param integer $league_id League to reference for trades
	 * @param integer $fflteam_id Team to exclude from returned trades
	 * @param string $status Optional: specific trade status to return (default is "all")
	 * @return array Array of {@link OFFL_Trade} objects
	 */
	function getOtherViewableTrades($league_id, $fflteam_id, $status="all")
	{
		$retArr = array();
		$sql = "SELECT trade_id FROM trades WHERE league_id=" . $league_id . " AND proposed_fflteam_id<>" . $fflteam_id . " AND target_fflteam_id<>" . $fflteam_id . " AND ";
		switch($status)
		{
			case "all":
				$sql .= "status<>'proposed' AND status<>'declined'";
			break;
			default:
				$sql .= "status='" . mysql_escape_string($status) . "'";
		}
		$result = mysql_query($sql,$this->_conn) or die (mysql_error() . ": $sql");

		for($i=0; $i<mysql_num_rows($result); $i++)
		{
			$t = new OFFL_Trade(mysql_result($result,$i,"trade_id"));
			array_push($retArr, $t);
		}
		mysql_free_result($result);

		return $retArr;
	}

	/**
	 * Returns trade objects with optional conditions
	 *
	 * Intended for admin and resolution, refinements can be made to the pending status and {@link OFFL_League} association.
	 *
	 * @param boolean $pending_only Optional: set true to return only trades with some pending deadline (usually for trade processing purposes)
	 * @param integer $league_id Optional: restrict returned trades to a given league
	 * @return array Array of {@link OFFL_Trade} objects
	 */
	function getAllTrades($pending_only=FALSE, $league_id=NULL)
	// if pending_only is set, only trades with deadlines (i.e. those eligible for processing) are returned
	{
		$retArr = array();
		$sql = "SELECT trade_id FROM trades";
		if ($pending_only)
		{
			$sql .= " WHERE deadline IS NOT NULL";
			if (isset($league_id))
			{	$sql .= " AND league_id=" . $league_id;	}
		}
		$sql .= " ORDER BY deadline";
		$result = mysql_query($sql, $this->_conn) or die (mysql_error() . ": $sql");

		for($i=0; $i<mysql_num_rows($result); $i++)
		{
			$t = new OFFL_Trade(mysql_result($result,$i,"trade_id"));
			array_push($retArr, $t);
		}
		mysql_free_result($result);

		return $retArr;
	}

	/**
	 * Sets the "trade lock" status on players involved in an approved trade
	 *
	 * "Trade lock" is the process by which players involved in an approved, but not yet processed, trade are
	 * restricted from drops or other trades that would exempt this transaction from completion.  Trade lock is
	 * a timestamp concept rather than a simple boolean so that, if a trade streches on without completion due to roster
	 * violations, a team's roster cannot be held hostage indefinitely.  However, the timestamp itself is contained by
	 * the {@link OFFL_Trade} class, so the tradelock variable is boolean
	 *
	 * @param integer $tradelock Optional: Defaults to setting trade lock, set 0 to unset tradeock
	 */
	function setTradelock($tradelock = 1)
	{
		$player_ids = array_merge($this->_proposed_player_id_array, $this->_target_player_id_array);

		if ($tradelock)
		{
			foreach ($player_ids as $player_id)
			{
				$player = new OFFL_TradePlayer();
				$player->setPlayerID($player_id);
				$player->setLeagueID($this->_league_id);
				$player->setTradelock(1);
				$player->save();
			}
		}
		else
		{
			foreach ($player_ids as $player_id)
			{
				$player = new OFFL_TradePlayer($player_id, $this->_league_id);
				$player->deleteTradePlayer();
			}
		}
	}

	/**
	 * Checks for tradelock status on players involved in trade
	 *
	 * Used to test for pre-existing trade lock before allowing submission of new trades.
	 * <ul>
	 * <li><b>0:</b> No trade lock</li>
	 * <li><b>-1:</b> Trade lock on proposing team only</li>
	 * <li><b>-2:</b> Trade lock on target team only</li>
	 * <li><b>-3:</b> Trade lock on both teams</li>
	 * </ul>
	 *
	 * @return integer
	 */
	function checkTradelock()
	{
		$returnVal = 0;
		foreach ($this->_proposed_player_id_array as $player_id)
		{
			$player = new OFFL_TradePlayer($player_id, $this->_league_id);
			if ($player->getTradelock())
			{	
				$returnVal = -1;
				break;
			}
		}
		foreach ($this->_target_player_id_array as $player_id)
		{
			$player = new OFFL_TradePlayer($player_id, $this->_league_id);
			if ($player->getTradelock())
			{	
				$returnVal -= 2;
				break;
			}
		}
		return $returnVal;
	}

	/**
	 * Tentatively executes this trade and tests resulting rosters for correctness
	 *
	 * @param string $team Optional: which team is checked.  Default "both", also allowed are "proposed" and "target"
	 * @return array Array of strings (error messages).  If array is empty, no errors found.
	 */
	function checkTradeRosters($team="both")
	{
		$errors = array();

		$thisTeam = new OFFL_FFLTeam($this->_proposed_fflteam_id);
		$thatTeam = new OFFL_FFLTeam($this->_target_fflteam_id);

		$thisRosterOld = $thisTeam->getRoster();
		$thatRosterOld = $thatTeam->getRoster();
		$thisRoster = array();
		$thatRoster = array();

		foreach ($thisRosterOld as $player)
		{
			if (in_array($player->getPlayerID(), $this->_proposed_player_id_array)) // being traded
			{
				$player->setStarter(0);
				$thatRoster[] = $player;
			}
			else
			{	$thisRoster[] = $player;	}
		}
		foreach ($thatRosterOld as $player)
		{
			if (in_array($player->getPlayerID(), $this->_target_player_id_array)) // being traded
			{
				$player->setStarter(0);
				$thisRoster[] = $player;
			}
			else
			{	$thatRoster[] = $player;	}
		}

		usort($thisRoster, "cmp_players");
		usort($thatRoster, "cmp_players");

		$theseErrors = $thisTeam->validateRosterFlex($thisRoster);
		$thoseErrors = $thatTeam->validateRosterFlex($thatRoster);

		switch ($team)
		{
			case "both":
				$errors = array_merge($theseErrors, $thoseErrors);
				break;
			case "proposed";
				$errors = $theseErrors;
				if (sizeof($thoseErrors) > 0)
					$errors[] = $thatTeam->getFFLTeamFullName() . " has remaining roster violations.";
				break;
			case "target";
				$errors = $thoseErrors;
				if (sizeof($theseErrors) > 0)
					$errors[] = $thisTeam->getFFLTeamFullName() . " has remaining roster violations.";
				break;
		}
		return $errors;
	}

	/**
	 * Actual execution of trade, plus {@link OFFL_TradePlayer trade lock} cleanup if needed
	 *
	 * Several key assumptions are made by this function, most notably that the deadline is appropriate and that resulting
	 * rosters will be valid.  The only internal check is that traded players are on the correct teams.
	 *
	 * No database cleanup of the trade or {@link OFFL_TradeProtest protests} is done by this function (reserved for {@link deleteTrade()}).
	 *
	 * @return array Array of strings.  All array elements are error messages.  If the array returns 0 elements, trade successfully processed.
	 */
	function processTrade()
	{
		$errors = array();
		$TradeOK = TRUE;
		
		foreach($this->_proposed_player_id_array as $player_id)
		{
			$player = new OFFL_Player($player_id, $this->_league_id);
			if ($player->getFFLTeamID() != $this->_proposed_fflteam_id)
			{
				$TradeOK = FALSE;
				array_push($errors, $player->getName() . " is not on expected team.");
			}
		}
		foreach($this->_target_player_id_array as $player_id)
		{
			$player = new OFFL_Player($player_id, $this->_league_id);
			if ($player->getFFLTeamID() != $this->_target_fflteam_id)
			{
				$TradeOK = FALSE;
				array_push($errors, $player->getName() . " is not on expected team.");
			}
		}

		if ($TradeOK)
		{
			$transaction = new OFFL_Transaction();
			$proposedTeam = new OFFL_FFLTeam($this->_proposed_fflteam_id);
			$targetTeam = new OFFL_FFLTeam($this->_target_fflteam_id);

			$transaction->setTransType("trade");
			$transaction->setLeagueID($this->_league_id);
			$transaction->save();
			foreach($this->_proposed_player_id_array as $player_id)
			{
				$tradeLock = new OFFL_TradePlayer($player_id, $this->_league_id);
				$tradeLock->deleteTradePlayer();
				unset ($tradeLock);

				$transItem = new OFFL_TransactionItem();

				$transItem->setTransID($transaction->getTransID());
				$transItem->setTransType("trade");
				$transItem->setPlayerID($player_id);
				$transItem->setFromFFLTeamID($this->_proposed_fflteam_id);
				$transItem->setToFFLTeamID($this->_target_fflteam_id);
				$transItem->save();

				$proposedTeam->dropPlayer($player_id);
				$targetTeam->addPlayer($player_id);
			}
			foreach($this->_target_player_id_array as $player_id)
			{
				$tradeLock = new OFFL_TradePlayer($player_id, $this->_league_id);
				$tradeLock->deleteTradePlayer();
				unset ($tradeLock);

				$transItem = new OFFL_TransactionItem();

				$transItem->setTransID($transaction->getTransID());
				$transItem->setTransType("trade");
				$transItem->setPlayerID($player->getPlayerID());
				$transItem->setFromFFLTeamID($this->_target_fflteam_id);
				$transItem->setToFFLTeamID($this->_proposed_fflteam_id);
				$transItem->save();

				$targetTeam->dropPlayer($player_id);
				$proposedTeam->addPlayer($player_id);
			}
			$proposedTeam->save();
			$targetTeam->save();
		}

		return $errors;
	}

	/**
	 * Deletes this trade from the database as well as associated {@link OFFL_TradeProtest} objects
	 * @return boolean TRUE on success, FALSE if {@link $_trade_id} not set
	 */
	function deleteTrade()
	{
		if(isset($this->_trade_id))
		{
			$protests = $this->getTradeProtests();
			foreach ($protests as $protest)
			{
				$protest->deleteTradeProtest();
			}
			$sql = "DELETE FROM trades WHERE trade_id=" . $this->_trade_id;
			$result = mysql_query($sql,$this->_conn) or die (mysql_error() . ": $sql");
			return TRUE;
		}
		$this->_emsg = "Unable to delete trade: trade_id not defined.";
        error_log ($this->_emsg);
		return FALSE;
	}

} // end OFFL_Trade class
?>
Return current item: Online Fantasy Football League