/ * @todo Add support for the following commands: * fix * fixes */ class P4_Job extends P4_Spec_PluralAbstract { const FETCH_BY_FILTER = 'filter'; const FETCH_DESCRIPTION = 'descriptions'; protected static $_specType = 'job'; protected static $_idField = 'Job'; protected static $_accessors = array( 102 => 'getStatus', 103 => 'getUser', 104 => 'getDate', 105 => 'getDescription', ); protected static $_mutators = array( 102 => 'setStatus', 103 => 'setUser', 105 => 'setDescription', ); /** * Get field value. If a custom field accessor exists, it will be used. * Extends parent to add support for accessors keyed on field code instead of name. * * @param string $field the name of the field to get the value of. * @return mixed the value of the field. * @throws P4_Spec_Exception if the field does not exist. */ public function getValue($field) { // if field has custom accessor based on field code, use it. $fieldCode = $this->_fieldNameToCode($field); if (isset(static::$_accessors[$fieldCode])) { return $this->{static::$_accessors[$fieldCode]}(); } return parent::getValue($field); } /** * Set field value. If a custom field mutator exists, it will be used. * Extends parent to add support for mutators keyed on field code instead of name. * * @param string $field the name of the field to set the value of. * @param mixed $value the value to set in the field. * @return P4_SpecAbstract provides a fluent interface */ public function setValue($field, $value) { // if field has custom mutator based on field code, use it. $fieldCode = $this->_fieldNameToCode($field); if (isset(static::$_mutators[$fieldCode])) { return $this->{static::$_mutators[$fieldCode]}($value); } return parent::setValue($field, $value); } /** * Get all Jobs from Perforce. Adds filtering options. * * @param array $options optional - array of options to augment fetch behavior. * supported options are: * * FETCH_MAXIMUM - set to integer value to limit to the * first 'max' number of entries. * FETCH_BY_FILTER - set to jobview filter * FETCH_DESCRIPTION - description will be fetched if true, * left for later lazy loading if false. * * defaults to true if not specified * @param P4_Connection_Interface $connection optional - a specific connection to use. * @return P4_Model_Iterator all records of this type. */ public static function fetchAll($options = array(), P4_Connection_Interface $connection = null) { // simply return parent - method exists to document options. return parent::fetchAll($options, $connection); } /** * Determine if the given job id exists. * * @param string|int $id the id to check for. * @param P4_Connection_Interface $connection optional - a specific connection to use. * @return bool true if the given id matches an existing job. */ public static function exists($id, P4_Connection_Interface $connection = null) { // check id for valid format if (!static::_isValidId($id)) { return false; } $jobs = static::fetchAll( array( static::FETCH_BY_FILTER => static::_getIdField() .'='. $id, static::FETCH_MAXIMUM => 1 ), $connection ); return (bool) count($jobs); } /** * Override parent to set id to 'new' if unset and capture id returned by save. * * @return P4_Job provides a fluent interface */ public function save() { $values = $this->_getValues(); if ($this->getId() === null) { $values[static::_getIdField()] = "new"; } // ensure all required fields have values. $this->_validateRequiredFields($values); $result = $this->getConnection()->run(static::_getSpecType(), "-i", $values); // Saved job Id is returned as a string, capture it. $data = $result->getData(0); if (!preg_match('/^Job ([^ ]+) saved\./', $data, $match)) { throw new P4_Spec_Exception('Cannot find ID for saved Job.'); } // Store the retrieved ID $this->setId($match[1]); // should re-populate (server may change values). $this->_deferPopulate(true); return $this; } /** * Returns the status of this job. This will return the value of field 102 even if the * field name has been changed in the jobspec. * * Out of the box valid status options are: open/suspended/closed or null. Modifying the * jobspec can change the list of valid options. * * @return string|null Status of this job or null if unset. */ public function getStatus() { return $this->_getValue($this->_fieldCodeToName(102)); } /** * Update the status of this job. This will update the value of field 102 even if the * field name has been changed in the jobspec. * * @param string|null $status Status of this job or null * @return P4_Job provides a fluent interface. * @throws InvalidArgumentException For input which isn't a string or null */ public function setStatus($status) { if (!is_string($status) && !is_null($status)) { throw new InvalidArgumentException('Status must be a string or null'); } return $this->_setValue($this->_fieldCodeToName(102), $status); } /** * Returns the user who created this job. This will return the value of field 103 * even if the field name has been changed in the jobspec. * * @return string|null User who created this job or null if unset. */ public function getUser() { return $this->_getValue($this->_fieldCodeToName(103)); } /** * Update the user who created this job. This will update the value of field 103 * even if the field name has been changed in the jobspec. * * @param string|P4_User|null $user User who created this job, or null * @return P4_Job provides a fluent interface. * @throws InvalidArgumentException For input which isn't a string, P4_User or null */ public function setUser($user) { if ($user instanceof P4_User) { $user = $user->getId(); } if (!is_null($user) && !is_string($user)) { throw new InvalidArgumentException('User must be a string, P4_User or null'); } return $this->_setValue($this->_fieldCodeToName(103), $user); } /** * Returns the date this job was created. This will return the value of field 104 * even if the field name has been changed in the jobspec. * * @return string|null Date this job was created or null if unset. */ public function getDate() { return $this->_getValue($this->_fieldCodeToName(104)); } /** * Returns the description for this job. This will return the value of field 105 * even if the field name has been changed in the jobspec. * * @return string|null Description for this job or null if unset. */ public function getDescription() { return $this->_getValue($this->_fieldCodeToName(105)); } /** * Update the decription for this job. This will update the value of field 105 * even if the field name has been changed in the jobspec. * * @param string|null $description Description for this job, or null * @return P4_Job provides a fluent interface. * @throws InvalidArgumentException For input which isn't a string or null */ public function setDescription($description) { if (!is_null($description) && !is_string($description)) { throw new InvalidArgumentException('Description must be a string or null'); } return $this->_setValue($this->_fieldCodeToName(105), $description); } /** * Produce set of flags for the spec list command, given fetch all options array. * Extends parent to add support for filter option. * * @param array $options array of options to augment fetch behavior. * see fetchAll for documented options. * @return array set of flags suitable for passing to spec list command. */ protected static function _getFetchAllFlags($options) { $flags = parent::_getFetchAllFlags($options); if (isset($options[static::FETCH_BY_FILTER])) { $filter = $options[static::FETCH_BY_FILTER]; if (!is_string($filter) || trim($filter) === "") { throw new InvalidArgumentException( 'Fetch by Filter expects a non-empty string as input' ); } $flags[] = '-e'; $flags[] = $filter; } // if they have not specified FETCH_DESCRIPTION or // they have and its true; include full descriptions if (!isset($options[static::FETCH_DESCRIPTION]) || $options[static::FETCH_DESCRIPTION]) { $flags[] = '-l'; } return $flags; } /** * Check if the given id is in a valid format for this spec type. * * @param string|int $id the id to check * @return bool true if id is valid, false otherwise */ protected static function _isValidId($id) { $validator = new P4_Validate_SpecName; $validator->allowPurelyNumeric(true); return $validator->isValid($id); } /** * Extends parent to control description inclusion based on FETCH options. * * @param array $listEntry a single spec entry from spec list output. * @param array $flags the flags that were used for this 'fetchAll' run. * @param P4_Connection_Interface $connection a specific connection to use. * @return P4_Job a (partially) populated instance of this spec class. */ protected static function _fromSpecListEntry($listEntry, $flags, P4_Connection_Interface $connection) { // discard the description if it isn't the 'long' version if (!in_array('-l', $flags)) { unset($listEntry['Description']); } return parent::_fromSpecListEntry($listEntry, $flags, $connection); } /** * Given a field name this function will return the associated field code. * * @param string $name String representing the field's name. * @return int The field code associated with the passed name. */ protected function _fieldNameToCode($name) { $field = $this->getSpecDefinition()->getField($name); return (int) $field['code']; } /** * Given a field code this function will return the associated field name. * * @param int|string $code Int or string representing value between 101-199 inclusive. * @return string The field name associated with the passed code * @throws InvalidArgumentException If passed an invalid or non-existent field code */ protected function _fieldCodeToName($code) { // if we are passed a string, and casting through int doesn't change it, // it is purely numeric, cast to an int. if (is_string($code) && $code === (string)(int)$code) { $code = (int)$code; } // if we made it this far, fail unless we have an int if (!is_int($code)) { throw new InvalidArgumentException('Field must be a purely numeric string or int.'); } // job spec defines this is the valid range for field id's if ($code < 101 || $code > 199) { throw new InvalidArgumentException('Field code must be between 101 and 199 inclusive.'); } $fields = $this->getSpecDefinition()->getFields(); foreach ($fields as $name => $field) { if ($field['code'] == $code) { return $name; } } throw new InvalidArgumentException('Specified field code does not exist.'); } }