/** * @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)); } }
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')); }
/** * 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; }
/** * 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; }
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)); } } }
/** * 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)); }
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()); }
/** * 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; }
/** * 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()); } }
/** * 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; }
/** * @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)))); }
/** * 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; }
/** * 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)); } }
/** * 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; }
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'); }
/** * 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; }
/** * @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; }
/** * 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; }
public function tearDown() { if (defined('SKIPPING')) { return; } $this->sharedFixture['db']->query('DELETE FROM %r WHERE user_id > 4', fORM::tablize('User')); __reset(); }