Пример #1
0
 /**
  * Sets a column to be formatted as an fMoney object
  * 
  * @param  mixed  $class            The class name or instance of the class to set the column format
  * @param  string $column           The column to format as an fMoney object
  * @param  string $currency_column  If specified, this column will store the currency of the fMoney object
  * @return void
  */
 public static function configureMoneyColumn($class, $column, $currency_column = NULL)
 {
     $class = fORM::getClass($class);
     $table = fORM::tablize($class);
     $schema = fORMSchema::retrieve($class);
     $data_type = $schema->getColumnInfo($table, $column, 'type');
     $valid_data_types = array('float');
     if (!in_array($data_type, $valid_data_types)) {
         throw new fProgrammerException('The column specified, %1$s, is a %2$s column. Must be %3$s to be set as a money column.', $column, $data_type, join(', ', $valid_data_types));
     }
     if ($currency_column !== NULL) {
         $currency_column_data_type = $schema->getColumnInfo($table, $currency_column, 'type');
         $valid_currency_column_data_types = array('varchar', 'char', 'text');
         if (!in_array($currency_column_data_type, $valid_currency_column_data_types)) {
             throw new fProgrammerException('The currency column specified, %1$s, is a %2$s column. Must be %3$s to be set as a currency column.', $currency_column, $currency_column_data_type, join(', ', $valid_currency_column_data_types));
         }
     }
     $camelized_column = fGrammar::camelize($column, TRUE);
     fORM::registerActiveRecordMethod($class, 'encode' . $camelized_column, self::encodeMoneyColumn);
     fORM::registerActiveRecordMethod($class, 'prepare' . $camelized_column, self::prepareMoneyColumn);
     if (!fORM::checkHookCallback($class, 'post::validate()', self::validateMoneyColumns)) {
         fORM::registerHookCallback($class, 'post::validate()', self::validateMoneyColumns);
     }
     fORM::registerReflectCallback($class, self::reflect);
     fORM::registerInspectCallback($class, $column, self::inspect);
     $value = FALSE;
     if ($currency_column) {
         $value = $currency_column;
         if (empty(self::$currency_columns[$class])) {
             self::$currency_columns[$class] = array();
         }
         self::$currency_columns[$class][$currency_column] = $column;
         if (!fORM::checkHookCallback($class, 'post::loadFromResult()', self::makeMoneyObjects)) {
             fORM::registerHookCallback($class, 'post::loadFromResult()', self::makeMoneyObjects);
         }
         if (!fORM::checkHookCallback($class, 'pre::validate()', self::makeMoneyObjects)) {
             fORM::registerHookCallback($class, 'pre::validate()', self::makeMoneyObjects);
         }
         fORM::registerActiveRecordMethod($class, 'set' . $camelized_column, self::setMoneyColumn);
         fORM::registerActiveRecordMethod($class, 'set' . fGrammar::camelize($currency_column, TRUE), self::setCurrencyColumn);
     } else {
         fORM::registerObjectifyCallback($class, $column, self::objectifyMoney);
     }
     if (empty(self::$money_columns[$class])) {
         self::$money_columns[$class] = array();
     }
     self::$money_columns[$class][$column] = $value;
 }
Пример #2
0
 /**
  * Stores a record in the database, whether existing or new
  * 
  * This method will start database and filesystem transactions if they have
  * not already been started.
  * 
  * @throws fValidationException  When ::validate() throws an exception
  * 
  * @param  boolean $force_cascade  When storing related records, this will force deleting child records even if they have their own children in a relationship with an RESTRICT or NO ACTION for the ON DELETE clause
  * @return fActiveRecord  The record object, to allow for method chaining
  */
 public function store($force_cascade = FALSE)
 {
     $class = get_class($this);
     if (fORM::getActiveRecordMethod($class, 'store')) {
         return $this->__call('store', array());
     }
     fORM::callHookCallbacks($this, 'pre::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
     $db = fORMDatabase::retrieve($class, 'write');
     $schema = fORMSchema::retrieve($class);
     try {
         $table = fORM::tablize($class);
         // New auto-incrementing records require lots of special stuff, so we'll detect them here
         $new_autoincrementing_record = FALSE;
         if (!$this->exists()) {
             $pk_columns = $schema->getKeys($table, 'primary');
             $pk_column = $pk_columns[0];
             $pk_auto_incrementing = $schema->getColumnInfo($table, $pk_column, 'auto_increment');
             if (sizeof($pk_columns) == 1 && $pk_auto_incrementing && !$this->values[$pk_column]) {
                 $new_autoincrementing_record = TRUE;
             }
         }
         $inside_db_transaction = $db->isInsideTransaction();
         if (!$inside_db_transaction) {
             $db->translatedQuery('BEGIN');
         }
         fORM::callHookCallbacks($this, 'post-begin::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
         $this->validate();
         fORM::callHookCallbacks($this, 'post-validate::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
         // Storing main table
         if (!$this->exists()) {
             $params = $this->constructInsertParams();
         } else {
             $params = $this->constructUpdateParams();
         }
         $result = call_user_func_array($db->translatedQuery, $params);
         // If there is an auto-incrementing primary key, grab the value from the database
         if ($new_autoincrementing_record) {
             $this->set($pk_column, $result->getAutoIncrementedValue());
         }
         // Fix cascade updated columns for in-memory objects to prevent issues when saving
         $one_to_one_relationships = $schema->getRelationships($table, 'one-to-one');
         $one_to_many_relationships = $schema->getRelationships($table, 'one-to-many');
         $relationships = array_merge($one_to_one_relationships, $one_to_many_relationships);
         foreach ($relationships as $relationship) {
             $type = in_array($relationship, $one_to_one_relationships) ? 'one-to-one' : 'one-to-many';
             $route = fORMSchema::getRouteNameFromRelationship($type, $relationship);
             $related_table = $relationship['related_table'];
             $related_class = fORM::classize($related_table);
             $related_class = fORM::getRelatedClass($class, $related_class);
             if ($relationship['on_update'] != 'cascade') {
                 continue;
             }
             $column = $relationship['column'];
             if (!fActiveRecord::changed($this->values, $this->old_values, $column)) {
                 continue;
             }
             if (!isset($this->related_records[$related_table][$route]['record_set'])) {
                 continue;
             }
             $record_set = $this->related_records[$related_table][$route]['record_set'];
             $related_column = $relationship['related_column'];
             $old_value = fActiveRecord::retrieveOld($this->old_values, $column);
             $value = $this->values[$column];
             if ($old_value === NULL) {
                 continue;
             }
             foreach ($record_set as $record) {
                 if (isset($record->old_values[$related_column])) {
                     foreach (array_keys($record->old_values[$related_column]) as $key) {
                         if ($record->old_values[$related_column][$key] === $old_value) {
                             $record->old_values[$related_column][$key] = $value;
                         }
                     }
                 }
                 if ($record->values[$related_column] === $old_value) {
                     $record->values[$related_column] = $value;
                 }
             }
         }
         // Storing *-to-many and one-to-one relationships
         fORMRelated::store($class, $this->values, $this->related_records, $force_cascade);
         fORM::callHookCallbacks($this, 'pre-commit::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
         if (!$inside_db_transaction) {
             $db->translatedQuery('COMMIT');
         }
         fORM::callHookCallbacks($this, 'post-commit::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
     } catch (fException $e) {
         if (!$inside_db_transaction) {
             $db->translatedQuery('ROLLBACK');
         }
         fORM::callHookCallbacks($this, 'post-rollback::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
         if ($new_autoincrementing_record && self::hasOld($this->old_values, $pk_column)) {
             $this->values[$pk_column] = self::retrieveOld($this->old_values, $pk_column);
             unset($this->old_values[$pk_column]);
         }
         throw $e;
     }
     fORM::callHookCallbacks($this, 'post::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
     $was_new = !$this->exists();
     // If we got here we succefully stored, so update old values to make exists() work
     foreach ($this->values as $column => $value) {
         $this->old_values[$column] = array($value);
     }
     // If the object was just inserted into the database, save it to the identity map
     if ($was_new) {
         $hash = self::hash($this->values, $class);
         if (!isset(self::$identity_map[$class])) {
             self::$identity_map[$class] = array();
         }
         self::$identity_map[$class][$hash] = $this;
     }
     return $this;
 }
Пример #3
0
 /**
  * Validates one-to-* related records
  *
  * @param  string $class             The class to validate the related records for
  * @param  array  &$values           The values for the object
  * @param  array  &$related_records  The related records for the object
  * @param  string $related_class     The name of the class for this record set
  * @param  string $route             The route between the table and related table
  * @return array  An array of validation messages
  */
 private static function validateOneToStar($class, &$values, &$related_records, $related_class, $route)
 {
     $schema = fORMSchema::retrieve($class);
     $table = fORM::tablize($class);
     $related_table = fORM::tablize($related_class);
     $relationship = fORMSchema::getRoute($schema, $table, $related_table, $route);
     $first_pk_column = self::determineFirstPKColumn($class, $related_class, $route);
     $filter = self::determineRequestFilter($class, $related_class, $route);
     $pk_field = $filter . $first_pk_column;
     $input_keys = array_keys(fRequest::get($pk_field, 'array', array()));
     $related_record_name = self::getRelatedRecordName($class, $related_class, $route);
     $messages = array();
     $one_to_one = fORMSchema::isOneToOne($schema, $table, $related_table, $route);
     if ($one_to_one) {
         $records = array(self::createRecord($class, $values, $related_records, $related_class, $route));
     } else {
         $records = self::buildRecords($class, $values, $related_records, $related_class, $route);
     }
     foreach ($records as $i => $record) {
         fRequest::filter($filter, isset($input_keys[$i]) ? $input_keys[$i] : $i);
         $record_messages = $record->validate(TRUE);
         foreach ($record_messages as $column => $record_message) {
             // Ignore validation messages about the primary key since it will be added
             if ($column == $relationship['related_column']) {
                 continue;
             }
             if ($one_to_one) {
                 $token_field = fValidationException::formatField('__TOKEN__');
                 $extract_message_regex = '#' . str_replace('__TOKEN__', '(.*?)', preg_quote($token_field, '#')) . '(.*)$#D';
                 preg_match($extract_message_regex, $record_message, $matches);
                 $column_name = self::compose('%1$s %2$s', $related_record_name, $matches[1]);
                 $messages[$related_table . '::' . $column] = self::compose('%1$s%2$s', fValidationException::formatField($column_name), $matches[2]);
             } else {
                 $main_key = $related_table . '[' . $i . ']';
                 if (!isset($messages[$main_key])) {
                     if (isset(self::$validation_name_methods[$class][$related_class][$route])) {
                         $name = $record->{self::$validation_name_methods[$class][$related_class][$route]}($i + 1);
                     } else {
                         $name = $related_record_name . ' #' . ($i + 1);
                     }
                     $messages[$main_key] = array('name' => $name, 'errors' => array());
                 }
                 $messages[$main_key]['errors'][$column] = $record_message;
             }
         }
         fRequest::unfilter();
     }
     return $messages;
 }
Пример #4
0
 /**
  * Sets a timestamp column to store the timezone in another column
  *
  * Since not all databases support timezone information in timestamp
  * columns, this method allows storing the timezone in another columns.
  * When the timestamp and timezone are retrieved from the database, they
  * will be automatically combined together into an fTimestamp object.
  *
  * @param  mixed  $class             The class name or instance of the class to set the column format
  * @param  string $timestamp_column  The timestamp column to store the timezone for
  * @param  string $timezone_column   The column to store the timezone in
  * @return void
  */
 public static function configureTimezoneColumn($class, $timestamp_column, $timezone_column)
 {
     $class = fORM::getClass($class);
     $table = fORM::tablize($class);
     $schema = fORMSchema::retrieve($class);
     $timestamp_data_type = $schema->getColumnInfo($table, $timestamp_column, 'type');
     if ($timestamp_data_type != 'timestamp') {
         throw new fProgrammerException('The timestamp column specified, %1$s, is a %2$s column. Must be a %3$s to have a related timezone column.', $timestamp_column, $data_type, 'timestamp');
     }
     $timezone_column_data_type = $schema->getColumnInfo($table, $timezone_column, 'type');
     $valid_timezone_column_data_types = array('varchar', 'char', 'text');
     if (!in_array($timezone_column_data_type, $valid_timezone_column_data_types)) {
         throw new fProgrammerException('The timezone column specified, %1$s, is a %2$s column. Must be %3$s to be set as a timezone column.', $timezone_column, $timezone_column_data_type, join(', ', $valid_timezone_column_data_types));
     }
     if (!fORM::checkHookCallback($class, 'post::validate()', self::validateTimezoneColumns)) {
         fORM::registerHookCallback($class, 'post::validate()', self::validateTimezoneColumns);
     }
     if (!fORM::checkHookCallback($class, 'post::loadFromResult()', self::makeTimestampObjects)) {
         fORM::registerHookCallback($class, 'post::loadFromResult()', self::makeTimestampObjects);
     }
     if (!fORM::checkHookCallback($class, 'pre::validate()', self::makeTimestampObjects)) {
         fORM::registerHookCallback($class, 'pre::validate()', self::makeTimestampObjects);
     }
     fORM::registerInspectCallback($class, $timezone_column, self::inspect);
     fORM::registerActiveRecordMethod($class, 'set' . fGrammar::camelize($timestamp_column, TRUE), self::setTimestampColumn);
     fORM::registerActiveRecordMethod($class, 'set' . fGrammar::camelize($timezone_column, TRUE), self::setTimezoneColumn);
     if (empty(self::$timestamp_columns[$class])) {
         self::$timestamp_columns[$class] = array();
     }
     self::$timestamp_columns[$class][$timestamp_column] = $timezone_column;
     if (empty(self::$timezone_columns[$class])) {
         self::$timezone_columns[$class] = array();
     }
     self::$timezone_columns[$class][$timezone_column] = $timestamp_column;
 }
Пример #5
0
 /** 
  * Creates the objects for related records that are in a one-to-one or many-to-one relationship with the current class in a single DB query
  *  
  * @param  string $related_class  This should be the name of a related class
  * @param  string $route          This should be the column name of the foreign key and is only required when there are multiple routes to a related table. If there are multiple routes and this is not specified, an fProgrammerException will be thrown.
  * @return fRecordSet  The record set object, to allow for method chaining
  */
 private function precreate($related_class, $route = NULL)
 {
     if (!$this->records) {
         return $this;
     }
     $this->validateSingleClass('precreate');
     // If there are no primary keys we can just exit
     if (!array_merge($this->getPrimaryKeys())) {
         return $this;
     }
     fActiveRecord::validateClass($related_class);
     fActiveRecord::forceConfigure($related_class);
     $relationship = fORMSchema::getRoute(fORMSchema::retrieve($this->class), fORM::tablize($this->class), fORM::tablize($related_class), $route, '*-to-one');
     $values = $this->call('get' . fGrammar::camelize($relationship['column'], TRUE));
     $values = array_unique($values);
     self::build($related_class, array($relationship['related_column'] . '=' => $values));
     return $this;
 }
 /**
  * Validates values for an fActiveRecord object against the database schema and any additional rules that have been added
  *
  * @internal
  *
  * @param  fActiveRecord  $object      The instance of the class to validate
  * @param  array          $values      The values to validate
  * @param  array          $old_values  The old values for the record
  * @return array  An array of messages
  */
 public static function validate($object, $values, $old_values)
 {
     $class = get_class($object);
     $table = fORM::tablize($class);
     $schema = fORMSchema::retrieve($class);
     self::initializeRuleArrays($class);
     $validation_messages = array();
     // Convert objects into values for validation
     foreach ($values as $column => $value) {
         $values[$column] = fORM::scalarize($class, $column, $value);
     }
     foreach ($old_values as $column => $column_values) {
         foreach ($column_values as $key => $value) {
             $old_values[$column][$key] = fORM::scalarize($class, $column, $value);
         }
     }
     $message_array = self::checkPrimaryKeys($schema, $object, $values, $old_values);
     if ($message_array) {
         $validation_messages[key($message_array)] = current($message_array);
     }
     $column_info = $schema->getColumnInfo($table);
     foreach ($column_info as $column => $info) {
         $message = self::checkAgainstSchema($schema, $object, $column, $values, $old_values);
         if ($message) {
             $validation_messages[$column] = $message;
         }
     }
     $messages = self::checkUniqueConstraints($schema, $object, $values, $old_values);
     if ($messages) {
         $validation_messages = array_merge($validation_messages, $messages);
     }
     foreach (self::$valid_values_rules[$class] as $column => $valid_values) {
         $message = self::checkValidValuesRule($class, $values, $column, $valid_values);
         if ($message) {
             $validation_messages[$column] = $message;
         }
     }
     foreach (self::$regex_rules[$class] as $column => $rule) {
         $message = self::checkRegexRule($class, $values, $column, $rule['regex'], $rule['message']);
         if ($message) {
             $validation_messages[$column] = $message;
         }
     }
     foreach (self::$conditional_rules[$class] as $rule) {
         $messages = self::checkConditionalRule($schema, $class, $values, $rule['main_columns'], $rule['conditional_values'], $rule['conditional_columns']);
         if ($messages) {
             $validation_messages = array_merge($validation_messages, $messages);
         }
     }
     foreach (self::$one_or_more_rules[$class] as $rule) {
         $message = self::checkOneOrMoreRule($schema, $class, $values, $rule['columns']);
         if ($message) {
             $validation_messages[join(',', $rule['columns'])] = $message;
         }
     }
     foreach (self::$only_one_rules[$class] as $rule) {
         $message = self::checkOnlyOneRule($schema, $class, $values, $rule['columns']);
         if ($message) {
             $validation_messages[join(',', $rule['columns'])] = $message;
         }
     }
     return $validation_messages;
 }
 /**
  * Makes sure the ordering value is sane, removes error messages about missing values
  *
  * @internal
  *
  * @param  fActiveRecord $object                The fActiveRecord instance
  * @param  array         &$values               The current values
  * @param  array         &$old_values           The old values
  * @param  array         &$related_records      Any records related to this record
  * @param  array         &$cache                The cache array for the record
  * @param  array         &$validation_messages  An array of ordered validation messages
  * @return void
  */
 public static function validate($object, &$values, &$old_values, &$related_records, &$cache, &$validation_messages)
 {
     $class = get_class($object);
     $table = fORM::tablize($class);
     $db = fORMDatabase::retrieve($class, 'read');
     $schema = fORMSchema::retrieve($class);
     foreach (self::$ordering_columns[$class] as $column => $other_columns) {
         $current_value = $values[$column];
         $old_value = fActiveRecord::retrieveOld($old_values, $column);
         $params = array("SELECT MAX(%r) FROM %r", $column, $table);
         if ($other_columns) {
             $params[0] .= " WHERE ";
             $params = self::addOtherFieldsWhereParams($schema, $params, $table, $other_columns, $values);
         }
         $current_max_value = (int) call_user_func_array($db->translatedQuery, $params)->fetchScalar();
         $new_max_value = $current_max_value;
         if ($new_set = self::isInNewSet($column, $other_columns, $values, $old_values)) {
             $new_max_value = $current_max_value + 1;
             $new_set_new_value = fActiveRecord::changed($values, $old_values, $column);
         }
         $column_name = fORM::getColumnName($class, $column);
         // Remove any previous validation warnings
         $filtered_messages = array();
         foreach ($validation_messages as $validation_column => $validation_message) {
             if (!preg_match('#(^|,)' . preg_quote($column, '#') . '(,|$)#D', $validation_column)) {
                 $filtered_messages[$validation_column] = $validation_message;
             }
         }
         $validation_messages = $filtered_messages;
         // If we have a completely empty value, we don't need to validate since a valid value will be generated
         if ($current_value === '' || $current_value === NULL) {
             continue;
         }
         if (!is_numeric($current_value) || strlen((int) $current_value) != strlen($current_value)) {
             $validation_messages[$column] = self::compose('%sPlease enter an integer', fValidationException::formatField($column_name));
         } elseif ($current_value < 1) {
             $validation_messages[$column] = self::compose('%sThe value can not be less than 1', fValidationException::formatField($column_name));
         }
     }
 }
Пример #8
0
 /**
  * Finds all of the table names in the SQL and creates the appropriate `FROM` and `GROUP BY` clauses with all necessary joins
  * 
  * The SQL string should contain two placeholders, `:from_clause` and
  * `:group_by_clause`. All columns should be qualified with their full table
  * name. Here is an example SQL string to pass in presumming that the
  * tables users and groups are in a relationship:
  * 
  * {{{
  * SELECT users.* FROM :from_clause WHERE groups.group_id = 5 :group_by_clause ORDER BY lower(users.first_name) ASC
  * }}}
  * 
  * @internal
  * 
  * @param  string $table  The main table to be queried
  * @param  string $sql    The SQL to insert the `FROM` clause into
  * @return string  The SQL `FROM` clause
  */
 public static function insertFromAndGroupByClauses($table, $sql)
 {
     $joins = array();
     if (strpos($sql, ':from_clause') === FALSE) {
         throw new fProgrammerException("No %1\$s placeholder was found in:%2\$s", ':from_clause', "\n" . $sql);
     }
     if (strpos($sql, ':group_by_clause') === FALSE && !preg_match('#group\\s+by#i', $sql)) {
         throw new fProgrammerException("No %1\$s placeholder was found in:%2\$s", ':group_by_clause', "\n" . $sql);
     }
     $has_group_by_placeholder = strpos($sql, ':group_by_clause') !== FALSE ? TRUE : FALSE;
     // Separate the SQL from quoted values
     preg_match_all("#(?:'(?:''|\\\\'|\\\\[^']|[^'\\\\])*')|(?:[^']+)#", $sql, $matches);
     $table_alias = $table;
     $used_aliases = array();
     $table_map = array();
     // If we are not passing in existing joins, start with the specified table
     if (!$joins) {
         $joins[] = array('join_type' => 'none', 'table_name' => $table, 'table_alias' => $table_alias);
     }
     $used_aliases[] = $table_alias;
     foreach ($matches[0] as $match) {
         if ($match[0] != "'") {
             preg_match_all('#\\b((?:(\\w+)(?:\\{(\\w+)\\})?=>)?(\\w+)(?:\\{(\\w+)\\})?)\\.\\w+\\b#m', $match, $table_matches, PREG_SET_ORDER);
             foreach ($table_matches as $table_match) {
                 if (!isset($table_match[5])) {
                     $table_match[5] = NULL;
                 }
                 // This is a related table that is going to join to a once-removed table
                 if (!empty($table_match[2])) {
                     $related_table = $table_match[2];
                     $route = fORMSchema::getRouteName($table, $related_table, $table_match[3]);
                     $join_name = $table . '_' . $related_table . '{' . $route . '}';
                     self::createJoin($table, $table_alias, $related_table, $route, $joins, $used_aliases);
                     $once_removed_table = $table_match[4];
                     $route = fORMSchema::getRouteName($related_table, $once_removed_table, $table_match[5]);
                     $join_name = self::createJoin($related_table, $joins[$join_name]['table_alias'], $once_removed_table, $route, $joins, $used_aliases);
                     $table_map[$table_match[1]] = $joins[$join_name]['table_alias'];
                     // This is a related table
                 } elseif (($table_match[4] != $table || fORMSchema::getRoutes($table, $table_match[4])) && $table_match[1] != $table) {
                     $related_table = $table_match[4];
                     $route = fORMSchema::getRouteName($table, $related_table, $table_match[5]);
                     // If the related table is the current table and it is a one-to-many we don't want to join
                     if ($table_match[4] == $table) {
                         $one_to_many_routes = fORMSchema::getRoutes($table, $related_table, 'one-to-many');
                         if (isset($one_to_many_routes[$route])) {
                             $table_map[$table_match[1]] = $table_alias;
                             continue;
                         }
                     }
                     $join_name = self::createJoin($table, $table_alias, $related_table, $route, $joins, $used_aliases);
                     $table_map[$table_match[1]] = $joins[$join_name]['table_alias'];
                 }
             }
         }
     }
     // Determine if we joined a *-to-many relationship
     $joined_to_many = FALSE;
     foreach ($joins as $name => $join) {
         if (is_numeric($name)) {
             continue;
         }
         if (substr($name, -5) == '_join') {
             $joined_to_many = TRUE;
             break;
         }
         $main_table = preg_replace('#_' . $join['table_name'] . '{\\w+}$#iD', '', $name);
         $second_table = $join['table_name'];
         $route = preg_replace('#^[^{]+{(\\w+)}$#D', '\\1', $name);
         $routes = fORMSchema::getRoutes($main_table, $second_table, '*-to-many');
         if (isset($routes[$route])) {
             $joined_to_many = TRUE;
             break;
         }
     }
     $found_order_by = FALSE;
     $from_clause = self::createFromClauseFromJoins($joins);
     // If we are joining on a *-to-many relationship we need to group by the
     // columns in the main table to prevent duplicate entries
     if ($joined_to_many) {
         $column_info = fORMSchema::retrieve()->getColumnInfo($table);
         $group_by_clause = ' GROUP BY ';
         $columns = array();
         foreach ($column_info as $column => $info) {
             $columns[] = $table . '.' . $column;
         }
         $group_by_columns = join(', ', $columns) . ' ';
         $group_by_clause .= $group_by_columns;
     } else {
         $group_by_clause = ' ';
         $group_by_columns = '';
     }
     // Put the SQL back together
     $new_sql = '';
     foreach ($matches[0] as $match) {
         $temp_sql = $match;
         // Get rid of the => notation and the :from_clause placeholder
         if ($match[0] !== "'") {
             foreach ($table_map as $arrow_table => $alias) {
                 $temp_sql = str_replace($arrow_table, $alias, $temp_sql);
             }
             // In the ORDER BY clause we need to wrap columns in
             if ($found_order_by && $joined_to_many) {
                 $temp_sql = preg_replace('#(?<!avg\\(|count\\(|max\\(|min\\(|sum\\(|cast\\(|case |when )\\b((?!' . preg_quote($table, '#') . '\\.)\\w+\\.\\w+)\\b#i', 'max(\\1)', $temp_sql);
             }
             if ($joined_to_many && preg_match('#order\\s+by#i', $temp_sql)) {
                 $order_by_found = TRUE;
                 $parts = preg_split('#(order\\s+by)#i', $temp_sql, -1, PREG_SPLIT_DELIM_CAPTURE);
                 $parts[2] = $temp_sql = preg_replace('#(?<!avg\\(|count\\(|max\\(|min\\(|sum\\(|cast\\(|case |when )\\b((?!' . preg_quote($table, '#') . '\\.)\\w+\\.\\w+)\\b#i', 'max(\\1)', $parts[2]);
                 $temp_sql = join('', $parts);
             }
             $temp_sql = str_replace(':from_clause', $from_clause, $temp_sql);
             if ($has_group_by_placeholder) {
                 $temp_sql = preg_replace('#\\s:group_by_clause\\s#', strtr($group_by_clause, array('\\' => '\\\\', '$' => '\\$')), $temp_sql);
             } elseif ($group_by_columns) {
                 $temp_sql = preg_replace('#(\\sGROUP\\s+BY\\s((?!HAVING|ORDER\\s+BY).)*)\\s#i', '\\1, ' . strtr($group_by_columns, array('\\' => '\\\\', '$' => '\\$')), $temp_sql);
             }
         }
         $new_sql .= $temp_sql;
     }
     return $new_sql;
 }
Пример #9
0
 /**
  * Returns the metadata about a column including features added by this class
  * 
  * @internal
  * 
  * @param  fActiveRecord $object            The fActiveRecord instance 
  * @param  array         &$values           The current values 
  * @param  array         &$old_values       The old values 
  * @param  array         &$related_records  Any records related to this record 
  * @param  array         &$cache            The cache array for the record 
  * @param  string        $method_name       The method that was called 
  * @param  array         $parameters        The parameters passed to the method 
  * @return mixed  The metadata array or element specified 
  */
 public static function inspect($object, &$values, &$old_values, &$related_records, &$cache, $method_name, $parameters)
 {
     list($action, $column) = fORM::parseMethod($method_name);
     $class = get_class($object);
     $table = fORM::tablize($class);
     $info = fORMSchema::retrieve()->getColumnInfo($table, $column);
     $element = isset($parameters[0]) ? $parameters[0] : NULL;
     $column = self::$ordering_columns[$class]['column'];
     $other_columns = self::$ordering_columns[$class]['other_columns'];
     // Retrieve the current max ordering index from the database
     $sql = "SELECT max(" . $column . ") FROM " . $table;
     if ($other_columns) {
         $sql .= " WHERE " . self::createOtherFieldsWhereClause($table, $other_columns, $values);
     }
     $max_value = (int) fORMDatabase::retrieve()->translatedQuery($sql)->fetchScalar();
     // If this is a new record, or in a new set, we need one more space in the ordering index
     if (self::isInNewSet($column, $other_columns, $values, $old_values)) {
         $max_value += 1;
     }
     $info['max_ordering_value'] = $max_value;
     $info['feature'] = 'ordering';
     fORM::callInspectCallbacks($class, $column, $info);
     if ($element) {
         return isset($info[$element]) ? $info[$element] : NULL;
     }
     return $info;
 }
Пример #10
0
 /**
  * Generates phpdoc for class
  * @return string
  */
 public function reflectPhpDoc()
 {
     $signatures = array();
     $class = get_class($this);
     $table = fORM::tablize($class);
     $schema = fORMSchema::retrieve($class);
     foreach ($schema->getColumnInfo($table) as $column => $columnInfo) {
         $camelizedColumn = fGrammar::camelize($column, TRUE);
         // Get and set methods
         $fixedType = $columnInfo['type'];
         if ($fixedType == 'blob') {
             $fixedType = 'string';
         }
         if ($fixedType == 'varchar') {
             $fixedType = 'string';
         }
         if ($fixedType == 'date') {
             $fixedType = 'fDate|string';
         }
         if ($fixedType == 'timestamp') {
             $fixedType = 'fTimestamp|string';
         }
         if ($fixedType == 'time') {
             $fixedType = 'fTime|string';
         }
         $firstFixedType = reset(explode('|', $fixedType));
         $signatures[] = $this->generateMagicMethodPhpDoc('get' . $camelizedColumn, array(), $firstFixedType, "Gets the current value of {$column}");
         $signatures[] = $this->generateMagicMethodPhpDoc('set' . $camelizedColumn, array($fixedType => $column), $class, "Sets the value for {$column}");
     }
     return $signatures;
 }
Пример #11
0
 /**
  * Adjusts the fActiveRecord::reflect() signatures of columns that have been configured in this class
  *
  * @internal
  *
  * @param  string  $class                 The class to reflect
  * @param  array   &$signatures           The associative array of `{method name} => {signature}`
  * @param  boolean $include_doc_comments  If doc comments should be included with the signature
  * @return void
  */
 public static function reflect($class, &$signatures, $include_doc_comments)
 {
     if (isset(self::$link_columns[$class])) {
         foreach (self::$link_columns[$class] as $column => $enabled) {
             $signature = '';
             if ($include_doc_comments) {
                 $signature .= "/**\n";
                 $signature .= " * Prepares the value of " . $column . " for output into HTML\n";
                 $signature .= " * \n";
                 $signature .= " * This method will ensure all links that start with a domain name are preceeded by http://\n";
                 $signature .= " * \n";
                 $signature .= " * @param  boolean \$create_link  Will cause link to be automatically converted into an [a] tag\n";
                 $signature .= " * @return string  The HTML-ready value\n";
                 $signature .= " */\n";
             }
             $prepare_method = 'prepare' . fGrammar::camelize($column, TRUE);
             $signature .= 'public function ' . $prepare_method . '($create_link=FALSE)';
             $signatures[$prepare_method] = $signature;
         }
     }
     if (isset(self::$number_columns[$class])) {
         $table = fORM::tablize($class);
         $schema = fORMSchema::retrieve($class);
         foreach (self::$number_columns[$class] as $column => $enabled) {
             $camelized_column = fGrammar::camelize($column, TRUE);
             $type = $schema->getColumnInfo($table, $column, 'type');
             // Get and set methods
             $signature = '';
             if ($include_doc_comments) {
                 $signature .= "/**\n";
                 $signature .= " * Gets the current value of " . $column . "\n";
                 $signature .= " * \n";
                 $signature .= " * @return fNumber  The current value\n";
                 $signature .= " */\n";
             }
             $get_method = 'get' . $camelized_column;
             $signature .= 'public function ' . $get_method . '()';
             $signatures[$get_method] = $signature;
             $signature = '';
             if ($include_doc_comments) {
                 $signature .= "/**\n";
                 $signature .= " * Sets the value for " . $column . "\n";
                 $signature .= " * \n";
                 $signature .= " * @param  fNumber|string|integer \$" . $column . "  The new value - don't use floats since they are imprecise\n";
                 $signature .= " * @return fActiveRecord  The record object, to allow for method chaining\n";
                 $signature .= " */\n";
             }
             $set_method = 'set' . $camelized_column;
             $signature .= 'public function ' . $set_method . '($' . $column . ')';
             $signatures[$set_method] = $signature;
             $signature = '';
             if ($include_doc_comments) {
                 $signature .= "/**\n";
                 $signature .= " * Encodes the value of " . $column . " for output into an HTML form\n";
                 $signature .= " * \n";
                 $signature .= " * If the value is an fNumber object, the ->__toString() method will be called\n";
                 $signature .= " * resulting in the value without any thousands separators\n";
                 $signature .= " * \n";
                 if ($type == 'float') {
                     $signature .= " * @param  integer \$decimal_places  The number of decimal places to display - not passing any value or passing NULL will result in the intrisinc number of decimal places being shown\n";
                 }
                 $signature .= " * @return string  The HTML form-ready value\n";
                 $signature .= " */\n";
             }
             $encode_method = 'encode' . $camelized_column;
             $signature .= 'public function ' . $encode_method . '(';
             if ($type == 'float') {
                 $signature .= '$decimal_places=NULL';
             }
             $signature .= ')';
             $signatures[$encode_method] = $signature;
             $signature = '';
             if ($include_doc_comments) {
                 $signature .= "/**\n";
                 $signature .= " * Prepares the value of " . $column . " for output into HTML\n";
                 $signature .= " * \n";
                 $signature .= " * If the value is an fNumber object, the ->format() method will be called\n";
                 $signature .= " * resulting in the value including thousands separators\n";
                 $signature .= " * \n";
                 if ($type == 'float') {
                     $signature .= " * @param  integer \$decimal_places  The number of decimal places to display - not passing any value or passing NULL will result in the intrisinc number of decimal places being shown\n";
                 }
                 $signature .= " * @return string  The HTML-ready value\n";
                 $signature .= " */\n";
             }
             $prepare_method = 'prepare' . $camelized_column;
             $signature .= 'public function ' . $prepare_method . '(';
             if ($type == 'float') {
                 $signature .= '$decimal_places=NULL';
             }
             $signature .= ')';
             $signatures[$prepare_method] = $signature;
         }
     }
     if (isset(self::$random_columns[$class])) {
         foreach (self::$random_columns[$class] as $column => $settings) {
             $signature = '';
             if ($include_doc_comments) {
                 $signature .= "/**\n";
                 $signature .= " * Generates a new random " . $settings['type'] . " character " . $settings['type'] . " string for " . $column . "\n";
                 $signature .= " * \n";
                 $signature .= " * If there is a UNIQUE constraint on the column and the value is not unique it will be regenerated until unique\n";
                 $signature .= " * \n";
                 $signature .= " * @return string  The randomly generated string\n";
                 $signature .= " */\n";
             }
             $generate_method = 'generate' . fGrammar::camelize($column, TRUE);
             $signature .= 'public function ' . $generate_method . '()';
             $signatures[$generate_method] = $signature;
         }
     }
 }
Пример #12
0
 /** 
  * Counts the related records for all records in this set in one DB query
  *  
  * @param  string $related_class  This should be the name of a related class
  * @param  string $route          This should be a column name or a join table name and is only required when there are multiple routes to a related table. If there are multiple routes and this is not specified, an fProgrammerException will be thrown.
  * @return fRecordSet  The record set object, to allow for method chaining
  */
 private function precount($related_class, $route = NULL)
 {
     if (!$this->records) {
         return $this;
     }
     $this->validateSingleClass('precount');
     // If there are no primary keys we can just exit
     if (!array_merge($this->getPrimaryKeys())) {
         return $this;
     }
     $related_table = fORM::tablize($related_class);
     $table = fORM::tablize($this->class);
     $route = fORMSchema::getRouteName($table, $related_table, $route, '*-to-many');
     $relationship = fORMSchema::getRoute($table, $related_table, $route, '*-to-many');
     $table_with_route = $route ? $table . '{' . $route . '}' : $table;
     // Build the query out
     $where_sql = $this->constructWhereClause($route);
     $order_by_sql = $this->constructOrderByClause($route);
     $related_table_keys = fORMSchema::retrieve()->getKeys($related_table, 'primary');
     $related_table_keys = fORMDatabase::addTableToValues($related_table, $related_table_keys);
     $related_table_keys = join(', ', $related_table_keys);
     $column = $table_with_route . '.' . $relationship['column'];
     $new_sql = 'SELECT count(' . $related_table_keys . ') AS __flourish_count, ' . $column . ' AS __flourish_column ';
     $new_sql .= ' FROM :from_clause ';
     $new_sql .= ' WHERE ' . $where_sql;
     $new_sql .= ' GROUP BY ' . $column;
     $new_sql .= ' ORDER BY ' . $column . ' ASC';
     $new_sql = fORMDatabase::insertFromAndGroupByClauses($related_table, $new_sql);
     // Run the query and inject the results into the records
     $result = fORMDatabase::retrieve()->translatedQuery($new_sql);
     $counts = array();
     foreach ($result as $row) {
         $counts[$row['__flourish_column']] = (int) $row['__flourish_count'];
     }
     unset($result);
     $total_records = sizeof($this->records);
     $get_method = 'get' . fGrammar::camelize($relationship['column'], TRUE);
     $tally_method = 'tally' . fGrammar::pluralize($related_class);
     for ($i = 0; $i < $total_records; $i++) {
         $record = $this->records[$i];
         $count = isset($counts[$record->{$get_method}()]) ? $counts[$record->{$get_method}()] : 0;
         $record->{$tally_method}($count, $route);
     }
     return $this;
 }
Пример #13
0
 /**
  * Stores a record in the database, whether existing or new
  * 
  * This method will start database and filesystem transactions if they have
  * not already been started.
  * 
  * @throws fValidationException  When ::validate() throws an exception
  * 
  * @return fActiveRecord  The record object, to allow for method chaining
  */
 public function store()
 {
     $class = get_class($this);
     if (fORM::getActiveRecordMethod($class, 'store')) {
         return $this->__call('store', array());
     }
     fORM::callHookCallbacks($this, 'pre::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
     try {
         $table = fORM::tablize($class);
         $column_info = fORMSchema::retrieve()->getColumnInfo($table);
         // New auto-incrementing records require lots of special stuff, so we'll detect them here
         $new_autoincrementing_record = FALSE;
         if (!$this->exists()) {
             $pk_columns = fORMSchema::retrieve()->getKeys($table, 'primary');
             if (sizeof($pk_columns) == 1 && $column_info[$pk_columns[0]]['auto_increment'] && !$this->values[$pk_columns[0]]) {
                 $new_autoincrementing_record = TRUE;
                 $pk_column = $pk_columns[0];
             }
         }
         $inside_db_transaction = fORMDatabase::retrieve()->isInsideTransaction();
         if (!$inside_db_transaction) {
             fORMDatabase::retrieve()->translatedQuery('BEGIN');
         }
         fORM::callHookCallbacks($this, 'post-begin::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
         $this->validate();
         fORM::callHookCallbacks($this, 'post-validate::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
         // Storing main table
         $sql_values = array();
         foreach ($column_info as $column => $info) {
             $value = fORM::scalarize($class, $column, $this->values[$column]);
             $sql_values[$column] = fORMDatabase::escapeBySchema($table, $column, $value);
         }
         // Most databases don't like the auto incrementing primary key to be set to NULL
         if ($new_autoincrementing_record && $sql_values[$pk_column] == 'NULL') {
             unset($sql_values[$pk_column]);
         }
         if (!$this->exists()) {
             $sql = $this->constructInsertSQL($sql_values);
         } else {
             $sql = $this->constructUpdateSQL($sql_values);
         }
         $result = fORMDatabase::retrieve()->translatedQuery($sql);
         // If there is an auto-incrementing primary key, grab the value from the database
         if ($new_autoincrementing_record) {
             $this->set($pk_column, $result->getAutoIncrementedValue());
         }
         // Storing *-to-many relationships
         fORMRelated::store($class, $this->values, $this->related_records);
         fORM::callHookCallbacks($this, 'pre-commit::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
         if (!$inside_db_transaction) {
             fORMDatabase::retrieve()->translatedQuery('COMMIT');
         }
         fORM::callHookCallbacks($this, 'post-commit::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
     } catch (fException $e) {
         if (!$inside_db_transaction) {
             fORMDatabase::retrieve()->translatedQuery('ROLLBACK');
         }
         fORM::callHookCallbacks($this, 'post-rollback::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
         if ($new_autoincrementing_record && self::hasOld($this->old_values, $pk_column)) {
             $this->values[$pk_column] = self::retrieveOld($this->old_values, $pk_column);
             unset($this->old_values[$pk_column]);
         }
         throw $e;
     }
     fORM::callHookCallbacks($this, 'post::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
     $was_new = !$this->exists();
     // If we got here we succefully stored, so update old values to make exists() work
     foreach ($this->values as $column => $value) {
         $this->old_values[$column] = array($value);
     }
     // If the object was just inserted into the database, save it to the identity map
     if ($was_new) {
         $hash = self::hash($this->values, $class);
         if (!isset(self::$identity_map[$class])) {
             self::$identity_map[$class] = array();
         }
         self::$identity_map[$class][$hash] = $this;
     }
     return $this;
 }
Пример #14
0
 /**
  * Associates a set of many-to-many related records with the current record
  * 
  * @internal
  * 
  * @param  array &$values       The current values for the main record being stored
  * @param  array $relationship  The information about the relationship between this object and the records in the record set
  * @param  array $related_info  An array containing the keys `'record_set'`, `'count'`, `'primary_keys'` and `'associate'` 
  * @return void
  */
 public static function storeManyToMany(&$values, $relationship, $related_info)
 {
     $column_value = $values[$relationship['column']];
     // First, we remove all existing relationships between the two tables
     $join_table = $relationship['join_table'];
     $join_column = $relationship['join_column'];
     $join_column_value = fORMDatabase::escapeBySchema($join_table, $join_column, $column_value);
     $delete_sql = 'DELETE FROM ' . $join_table;
     $delete_sql .= ' WHERE ' . $join_column . ' = ' . $join_column_value;
     fORMDatabase::retrieve()->translatedQuery($delete_sql);
     // Then we add back the ones in the record set
     $join_related_column = $relationship['join_related_column'];
     $related_pk_columns = fORMSchema::retrieve()->getKeys($relationship['related_table'], 'primary');
     $related_column_values = array();
     // If the related column is the primary key, we can just use the primary keys if we have them
     if ($related_pk_columns[0] == $relationship['related_column'] && $related_info['primary_keys']) {
         $related_column_values = $related_info['primary_keys'];
         // Otherwise we need to pull the related values out of the record set
     } else {
         // If there is no record set, build it from the primary keys
         if (!$related_info['record_set']) {
             $related_class = fORM::classize($relationship['related_table']);
             $related_info['record_set'] = fRecordSet::build($related_class, array($related_pk_columns[0] . '=' => $related_info['primary_keys']));
         }
         $get_related_method_name = 'get' . fGrammar::camelize($relationship['related_column'], TRUE);
         foreach ($related_info['record_set'] as $record) {
             $related_column_values[] = $record->{$get_related_method_name}();
         }
     }
     // Ensure we aren't storing duplicates
     $related_column_values = array_unique($related_column_values);
     foreach ($related_column_values as $related_column_value) {
         $related_column_value = fORMDatabase::escapeBySchema($join_table, $join_related_column, $related_column_value);
         $insert_sql = 'INSERT INTO ' . $join_table . ' (' . $join_column . ', ' . $join_related_column . ') ';
         $insert_sql .= 'VALUES (' . $join_column_value . ', ' . $related_column_value . ')';
         fORMDatabase::retrieve()->translatedQuery($insert_sql);
     }
 }
Пример #15
0
 protected function setupORM()
 {
     if ($this->isOrmSettedUp) {
         return;
     }
     $this->defineConstants();
     // Extract port from host. See wpdb::db_connect
     $port = null;
     $host = $this->wp->getDbHost();
     if (preg_match('/^(.+):(\\d+)$/', trim($host), $m)) {
         $host = $m[1];
         $port = $m[2];
     }
     $database = new fDatabase('mysql', $this->wp->getDbName(), $this->wp->getDbUser(), $this->wp->getDbPassword(), $host, $port);
     // $database->enableDebugging(true);
     fORMDatabase::attach($database);
     fORM::mapClassToTable('WpTesting_Model_Test', WP_DB_PREFIX . 'posts');
     fORM::mapClassToTable('WpTesting_Model_Question', WPT_DB_PREFIX . 'questions');
     fORM::mapClassToTable('WpTesting_Model_Taxonomy', WP_DB_PREFIX . 'term_taxonomy');
     fORM::mapClassToTable('WpTesting_Model_GlobalAnswer', WP_DB_PREFIX . 'terms');
     fORM::mapClassToTable('WpTesting_Model_Answer', WPT_DB_PREFIX . 'answers');
     fORM::mapClassToTable('WpTesting_Model_Scale', WP_DB_PREFIX . 'terms');
     fORM::mapClassToTable('WpTesting_Model_Score', WPT_DB_PREFIX . 'scores');
     fORM::mapClassToTable('WpTesting_Model_Passing', WPT_DB_PREFIX . 'passings');
     fORM::mapClassToTable('WpTesting_Model_Result', WP_DB_PREFIX . 'terms');
     fORM::mapClassToTable('WpTesting_Model_Formula', WPT_DB_PREFIX . 'formulas');
     fORM::mapClassToTable('WpTesting_Model_Respondent', WP_DB_PREFIX . 'users');
     fGrammar::addSingularPluralRule('Taxonomy', 'Taxonomy');
     fGrammar::addSingularPluralRule('Score', 'Score');
     fGrammar::addSingularPluralRule('Answer', 'Answer');
     $schema = fORMSchema::retrieve('name:default');
     $fkOptions = array('on_delete' => 'cascade', 'on_update' => 'cascade');
     $schema->setKeysOverride(array(array('column' => 'test_id', 'foreign_table' => WP_DB_PREFIX . 'posts', 'foreign_column' => 'ID') + $fkOptions), WPT_DB_PREFIX . 'questions', 'foreign');
     $schema->setKeysOverride(array(array('column' => 'answer_id', 'foreign_table' => WPT_DB_PREFIX . 'answers', 'foreign_column' => 'answer_id') + $fkOptions, array('column' => 'scale_id', 'foreign_table' => WP_DB_PREFIX . 'terms', 'foreign_column' => 'term_id') + $fkOptions), WPT_DB_PREFIX . 'scores', 'foreign');
     $schema->setKeysOverride(array(array('column' => 'test_id', 'foreign_table' => WP_DB_PREFIX . 'posts', 'foreign_column' => 'ID') + $fkOptions, array('column' => 'respondent_id', 'foreign_table' => WP_DB_PREFIX . 'users', 'foreign_column' => 'ID') + $fkOptions), WPT_DB_PREFIX . 'passings', 'foreign');
     $schema->setKeysOverride(array(array('column' => 'answer_id', 'foreign_table' => WPT_DB_PREFIX . 'answers', 'foreign_column' => 'answer_id') + $fkOptions, array('column' => 'passing_id', 'foreign_table' => WPT_DB_PREFIX . 'passings', 'foreign_column' => 'passing_id') + $fkOptions), WPT_DB_PREFIX . 'passing_answers', 'foreign');
     $schema->setKeysOverride(array(array('column' => 'test_id', 'foreign_table' => WP_DB_PREFIX . 'posts', 'foreign_column' => 'ID') + $fkOptions, array('column' => 'result_id', 'foreign_table' => WP_DB_PREFIX . 'terms', 'foreign_column' => 'term_id') + $fkOptions), WPT_DB_PREFIX . 'formulas', 'foreign');
     $schema->setColumnInfoOverride(null, WP_DB_PREFIX . 'term_relationships', 'term_order');
     $schema->setKeysOverride(array(array('column' => 'object_id', 'foreign_table' => WP_DB_PREFIX . 'posts', 'foreign_column' => 'ID') + $fkOptions, array('column' => 'term_taxonomy_id', 'foreign_table' => WP_DB_PREFIX . 'term_taxonomy', 'foreign_column' => 'term_taxonomy_id') + $fkOptions), WP_DB_PREFIX . 'term_relationships', 'foreign');
     $schema->setKeysOverride(array(array('column' => 'term_id', 'foreign_table' => WP_DB_PREFIX . 'terms', 'foreign_column' => 'term_id') + $fkOptions), WP_DB_PREFIX . 'term_taxonomy', 'foreign');
     $schema->setKeysOverride(array(array('column' => 'question_id', 'foreign_table' => WPT_DB_PREFIX . 'questions', 'foreign_column' => 'question_id') + $fkOptions, array('column' => 'global_answer_id', 'foreign_table' => WP_DB_PREFIX . 'terms', 'foreign_column' => 'term_id') + $fkOptions), WPT_DB_PREFIX . 'answers', 'foreign');
     $schema->setKeysOverride(array(), WPT_DB_PREFIX . 'sections', 'foreign');
     $schema->setKeysOverride(array(), WPT_DB_PREFIX . 'fields', 'foreign');
     $schema->setKeysOverride(array(), WPT_DB_PREFIX . 'field_values', 'foreign');
     $this->wp->doAction('wp_testing_orm_setup', $schema, $database);
     $this->isOrmSettedUp = true;
 }
Пример #16
0
 /**
  * Takes a scalar value and turns it into an object if applicable
  *
  * @internal
  *
  * @param  string $class   The class name of the class the column is part of
  * @param  string $column  The database column
  * @param  mixed  $value   The value to possibly objectify
  * @return mixed  The scalar or object version of the value, depending on the column type and column options
  */
 public static function objectify($class, $column, $value)
 {
     // This short-circuits computation for already checked columns, providing
     // a nice little performance boost to pages with lots of records
     if (isset(self::$cache['objectify'][$class . '::' . $column])) {
         return $value;
     }
     if (!empty(self::$objectify_callbacks[$class][$column])) {
         return call_user_func(self::$objectify_callbacks[$class][$column], $class, $column, $value);
     }
     $table = self::tablize($class);
     $schema = fORMSchema::retrieve($class);
     // Turn date/time values into objects
     $column_type = $schema->getColumnInfo($table, $column, 'type');
     if (in_array($column_type, array('date', 'time', 'timestamp'))) {
         if ($value === NULL) {
             return $value;
         }
         try {
             // Explicit calls to the constructors are used for dependency detection
             switch ($column_type) {
                 case 'date':
                     $value = new fDate($value);
                     break;
                 case 'time':
                     $value = new fTime($value);
                     break;
                 case 'timestamp':
                     $value = new fTimestamp($value);
                     break;
             }
         } catch (fValidationException $e) {
             // Validation exception results in the raw value being saved
         }
     } else {
         self::$cache['objectify'][$class . '::' . $column] = TRUE;
     }
     return $value;
 }
Пример #17
0
 /**
  * Sets a column to be a file upload column
  * 
  * Configuring a column to be a file upload column means that whenever
  * fActiveRecord::populate() is called for an fActiveRecord object, any
  * appropriately named file uploads (via `$_FILES`) will be moved into
  * the directory for this column.
  * 
  * Setting the column to a file path will cause the specified file to
  * be copied into the directory for this column.
  * 
  * @param  mixed             $class      The class name or instance of the class
  * @param  string            $column     The column to set as a file upload column
  * @param  fDirectory|string $directory  The directory to upload/move to
  * @return void
  */
 public static function configureFileUploadColumn($class, $column, $directory)
 {
     $class = fORM::getClass($class);
     $table = fORM::tablize($class);
     $schema = fORMSchema::retrieve($class);
     $data_type = $schema->getColumnInfo($table, $column, 'type');
     $valid_data_types = array('varchar', 'char', 'text');
     if (!in_array($data_type, $valid_data_types)) {
         throw new fProgrammerException('The column specified, %1$s, is a %2$s column. Must be one of %3$s to be set as a file upload column.', $column, $data_type, join(', ', $valid_data_types));
     }
     if (!is_object($directory)) {
         $directory = new fDirectory($directory);
     }
     if (!$directory->isWritable()) {
         throw new fEnvironmentException('The file upload directory, %s, is not writable', $directory->getPath());
     }
     $camelized_column = fGrammar::camelize($column, TRUE);
     fORM::registerActiveRecordMethod($class, 'upload' . $camelized_column, self::upload);
     fORM::registerActiveRecordMethod($class, 'set' . $camelized_column, self::set);
     fORM::registerActiveRecordMethod($class, 'encode' . $camelized_column, self::encode);
     fORM::registerActiveRecordMethod($class, 'prepare' . $camelized_column, self::prepare);
     fORM::registerReflectCallback($class, self::reflect);
     fORM::registerInspectCallback($class, $column, self::inspect);
     fORM::registerReplicateCallback($class, $column, self::replicate);
     fORM::registerObjectifyCallback($class, $column, self::objectify);
     $only_once_hooks = array('post-begin::delete()' => self::begin, 'pre-commit::delete()' => self::delete, 'post-commit::delete()' => self::commit, 'post-rollback::delete()' => self::rollback, 'post::populate()' => self::populate, 'post-begin::store()' => self::begin, 'post-validate::store()' => self::moveFromTemp, 'pre-commit::store()' => self::deleteOld, 'post-commit::store()' => self::commit, 'post-rollback::store()' => self::rollback, 'post::validate()' => self::validate);
     foreach ($only_once_hooks as $hook => $callback) {
         if (!fORM::checkHookCallback($class, $hook, $callback)) {
             fORM::registerHookCallback($class, $hook, $callback);
         }
     }
     if (empty(self::$file_upload_columns[$class])) {
         self::$file_upload_columns[$class] = array();
     }
     self::$file_upload_columns[$class][$column] = $directory;
 }
Пример #18
0
 /**
  * Validates one-to-* related records
  *
  * @param  string $class             The class to validate the related records for
  * @param  array  &$values           The values for the object
  * @param  array  &$related_records  The related records for the object
  * @param  string $related_class     The name of the class for this record set
  * @param  string $route             The route between the table and related table
  * @return array  An array of validation messages
  */
 private static function validateOneToStar($class, &$values, &$related_records, $related_class, $route)
 {
     $schema = fORMSchema::retrieve($class);
     $table = fORM::tablize($class);
     $related_table = fORM::tablize($related_class);
     $first_pk_column = self::determineFirstPKColumn($class, $related_class, $route);
     $filter = self::determineRequestFilter($class, $related_class, $route);
     $pk_field = $filter . $first_pk_column;
     $input_keys = array_keys(fRequest::get($pk_field, 'array', array()));
     $related_record_name = self::getRelatedRecordName($class, $related_class, $route);
     $messages = array();
     $one_to_one = fORMSchema::isOneToOne($schema, $table, $related_table, $route);
     if ($one_to_one) {
         $records = array(self::createRecord($class, $values, $related_records, $related_class, $route));
     } else {
         $records = self::buildRecords($class, $values, $related_records, $related_class, $route);
     }
     // Ignore validation messages about the primary key since it will be added
     $primary_key_name = fValidationException::formatField(fORM::getColumnName($related_class, $route));
     $primary_key_regex = '#^' . preg_quote($primary_key_name, '#') . '.*$#D';
     fORMValidation::addRegexReplacement($related_class, $primary_key_regex, '');
     foreach ($records as $i => $record) {
         fRequest::filter($filter, isset($input_keys[$i]) ? $input_keys[$i] : $i);
         $record_messages = $record->validate(TRUE);
         foreach ($record_messages as $record_message) {
             $token_field = fValidationException::formatField('__TOKEN__');
             $extract_message_regex = '#' . str_replace('__TOKEN__', '(.*?)', preg_quote($token_field, '#')) . '(.*)$#D';
             preg_match($extract_message_regex, $record_message, $matches);
             if ($one_to_one) {
                 $column_name = self::compose('%1$s %2$s', $related_record_name, $matches[1]);
             } else {
                 $column_name = self::compose('%1$s #%2$s %3$s', $related_record_name, $i + 1, $matches[1]);
             }
             $messages[] = self::compose('%1$s%2$s', fValidationException::formatField($column_name), $matches[2]);
         }
         fRequest::unfilter();
     }
     fORMValidation::removeRegexReplacement($related_class, $primary_key_regex, '');
     return $messages;
 }