Exemplo n.º 1
0
 /**
  * @param array $where
  * @return WpTesting_Model_AbstractTerm
  * @throws fNotFoundException
  */
 protected function findByParams(array $where = array())
 {
     $taxonomyTable = fORM::tablize('WpTesting_Model_Taxonomy');
     try {
         return fRecordSet::build($this->modelName, array($taxonomyTable . '.taxonomy=' => $this->getTaxonomy()) + $where)->getRecord(0);
     } catch (fNoRemainingException $e) {
         throw new fNotFoundException($this->modelName . ' not found by conditions: ' . var_export($where, true));
     }
 }
Exemplo n.º 2
0
 public function testCustomClassTableMapping()
 {
     $this->assertEquals('users', fORM::tablize('User'));
     $this->assertEquals('User', fORM::classize('users'));
     fORM::mapClassToTable('User', 'person');
     $this->assertEquals('person', fORM::tablize('User'));
     $this->assertEquals('User', fORM::classize('person'));
     $this->assertNotEquals('users', fORM::tablize('User'));
     $this->assertEquals('bicycles', fORM::tablize('Bicycle'));
     $this->assertEquals('Bicycle', fORM::classize('bicycles'));
     fORM::mapClassToTable('Bicycle', 'bike');
     $this->assertEquals('bike', fORM::tablize('Bicycle'));
     $this->assertEquals('Bicycle', fORM::classize('bike'));
     $this->assertNotEquals('bicycles', fORM::tablize('Bicycle'));
 }
Exemplo n.º 3
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;
 }
Exemplo n.º 4
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 related records for an fActiveRecord object
  *
  * @internal
  *
  * @param  fActiveRecord $object            The object to validate
  * @param  array         &$values           The values for the object
  * @param  array         &$related_records  The related records for the object
  * @return array         An array of messages
  */
 public static function validateRelated($object, &$values, &$related_records)
 {
     $class = get_class($object);
     $table = fORM::tablize($class);
     $validation_messages = array();
     // Check related rules
     foreach (self::$related_one_or_more_rules[$class] as $related_class => $routes) {
         foreach ($routes as $route => $enabled) {
             $message = self::checkRelatedOneOrMoreRule($object, $values, $related_records, $related_class, $route);
             if ($message) {
                 $validation_messages[fORM::tablize($related_class)] = $message;
             }
         }
     }
     $related_messages = fORMRelated::validate($class, $values, $related_records);
     $validation_messages = array_merge($validation_messages, $related_messages);
     return $validation_messages;
 }
Exemplo n.º 6
0
 public function tearDown()
 {
     if (defined('SKIPPING')) {
         return;
     }
     self::$db->query('DELETE FROM %r WHERE user_id > 4', fORM::tablize('User'));
     __reset();
 }
 /**
  * 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));
         }
     }
 }
Exemplo n.º 8
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)
 {
     $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($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;
 }
 public function testBuildFromSQLNonLimitedCount()
 {
     $set = fRecordSet::buildFromSQL('User', sprintf("SELECT %s.* FROM %s LIMIT 2", fORM::tablize('User'), fORM::tablize('User')), sprintf("SELECT count(*) FROM %s", fORM::tablize('User')));
     $this->assertEquals(array(1, 2), $set->getPrimaryKeys());
     $this->assertEquals(4, $set->count(TRUE));
 }
Exemplo n.º 10
0
 public function testCustomMapping()
 {
     eval("class TestUser extends fActiveRecord {\n\t\t\tprotected function configure() {\n\t\t\t\tfORM::mapClassToTable(\$this, '" . fORM::tablize('User') . "');\n\t\t\t}\t\n\t\t}");
     $user = new TestUser(1);
     $this->assertEquals(1, $user->getUserId());
 }
Exemplo n.º 11
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;
 }
Exemplo n.º 12
0
 /**
  * Sets the appropriate column values to a random string if the object is new
  *
  * @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
  * @return string  The formatted link
  */
 public static function setRandomStrings($object, &$values, &$old_values, &$related_records, &$cache)
 {
     if ($object->exists()) {
         return;
     }
     $class = get_class($object);
     $table = fORM::tablize($class);
     foreach (self::$random_columns[$class] as $column => $settings) {
         if (fActiveRecord::hasOld($old_values, $column) && $values[$column]) {
             continue;
         }
         self::generate($object, $values, $old_values, $related_records, $cache, 'generate' . fGrammar::camelize($column, TRUE), array());
     }
 }
Exemplo n.º 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;
 }
Exemplo n.º 14
0
 /**
  * @param integer $respondentId
  * @param array $orderBy
  * @return WpTesting_Model_Test[]
  */
 public function findAllByPassingRespondent($respondentId, array $orderBy = array('Test.ID'))
 {
     return fRecordSet::buildFromSQL($this->modelName, array(implode(PHP_EOL, array('SELECT DISTINCT Test.* FROM %r AS Test', 'JOIN %r AS Passing ON Passing.test_id = Test.ID AND Passing.respondent_id = %i', 'WHERE Test.post_type = %s', 'ORDER BY %s')), array($this->tableName, fORM::tablize('WpTesting_Model_Passing'), $respondentId, 'wpt_test', implode(',', $orderBy))));
 }
Exemplo n.º 15
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;
 }
Exemplo n.º 16
0
 /**
  * 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);
     $column = self::$ordering_columns[$class]['column'];
     $other_columns = self::$ordering_columns[$class]['other_columns'];
     $current_value = $values[$column];
     $old_value = fActiveRecord::retrieveOld($old_values, $column);
     $sql = "SELECT max(" . $column . ") FROM " . $table;
     if ($other_columns) {
         $sql .= " WHERE " . self::createOtherFieldsWhereClause($table, $other_columns, $values);
     }
     $current_max_value = (int) fORMDatabase::retrieve()->translatedQuery($sql)->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_message) {
         if (!preg_match('#^' . str_replace('___', '(.*?)', preg_quote(fValidationException::formatField('___' . $column_name . '___'), '#')) . '#', $validation_message)) {
             $filtered_messages[] = $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) {
         return;
     }
     if (!is_numeric($current_value) || strlen((int) $current_value) != strlen($current_value)) {
         $validation_messages[] = self::compose('%sPlease enter an integer', fValidationException::formatField($column_name));
     } elseif ($current_value < 1) {
         $validation_messages[] = self::compose('%sThe value can not be less than 1', fValidationException::formatField($column_name));
     }
 }
Exemplo n.º 17
0
 /**
  * Validates many-to-many related records
  *
  * @param  string $class          The class to validate the related records for
  * @param  string $related_class  The name of the class for this record set
  * @param  string $route          The route between the table and related table
  * @param  array  $related_info   The related info to validate
  * @return array  An array of validation messages
  */
 private static function validateManyToMany($class, $related_class, $route, $related_info)
 {
     $related_record_name = self::getRelatedRecordName($class, $related_class, $route);
     $record_number = 1;
     $messages = array();
     $related_records = $related_info['record_set'] ? $related_info['record_set'] : $related_info['primary_keys'];
     foreach ($related_records as $record) {
         if (is_object($record) && !$record->exists() || !$record) {
             $messages[fORM::tablize($related_class)] = self::compose('%1$sPlease select a %2$s', fValidationException::formatField(self::compose('%1$s #%2$s', $related_record_name, $record_number)), $related_record_name);
         }
         $record_number++;
     }
     return $messages;
 }
Exemplo n.º 18
0
 protected function __construct()
 {
     $this->modelName = str_replace('_Query_', '_Model_', get_class($this));
     $this->tableName = fORM::tablize($this->modelName);
     $this->db = fORMDatabase::retrieve($this->modelName, 'read');
 }
Exemplo n.º 19
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;
 }
Exemplo n.º 20
0
 /**
  * @param bool $isRecursive
  * @return WpTesting_Model_Test
  */
 public function populateQuestions($isRecursive = false)
 {
     $this->populateWpTesting_Model_Questions($isRecursive);
     $table = fORM::tablize('WpTesting_Model_Question');
     $records =& $this->related_records[$table]['test_id']['record_set'];
     $records = $records->filter(array('getTitle!=' => ''));
     return $this;
 }
Exemplo n.º 21
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;
 }
Exemplo n.º 22
0
 public function tearDown()
 {
     if (defined('SKIPPING')) {
         return;
     }
     $this->sharedFixture['db']->query('DELETE FROM %r WHERE user_id > 4', fORM::tablize('User'));
     __reset();
 }