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; }
/** * Resets the configuration of the class * * @internal * * @return void */ public static function reset() { self::$cache = array('camelize' => array(0 => array(), 1 => array()), 'humanize' => array(), 'pluralize' => array(), 'singularize' => array(), 'underscorize' => array()); self::$camelize_rules = array(); self::$humanize_rules = array(); self::$join_array_callback = NULL; self::$plural_to_singular_rules = array('([ml])ice' => '\\1ouse', '(media|info(rmation)?|news)$' => '\\1', '(q)uizzes$' => '\\1uiz', '(c)hildren$' => '\\1hild', '(p)eople$' => '\\1erson', '(m)en$' => '\\1an', '((?!sh).)oes$' => '\\1o', '((?<!o)[ieu]s|[ieuo]x)es$' => '\\1', '([cs]h)es$' => '\\1', '(ss)es$' => '\\1', '([aeo]l)ves$' => '\\1f', '([^d]ea)ves$' => '\\1f', '(ar)ves$' => '\\1f', '([nlw]i)ves$' => '\\1fe', '([aeiou]y)s$' => '\\1', '([^aeiou])ies$' => '\\1y', '(la)ses$' => '\\1s', '(.)s$' => '\\1'); self::$singular_to_plural_rules = array('([ml])ouse$' => '\\1ice', '(media|info(rmation)?|news)$' => '\\1', '(phot|log)o$' => '\\1os', '^(q)uiz$' => '\\1uizzes', '(c)hild$' => '\\1hildren', '(p)erson$' => '\\1eople', '(m)an$' => '\\1en', '([ieu]s|[ieuo]x)$' => '\\1es', '([cs]h)$' => '\\1es', '(ss)$' => '\\1es', '([aeo]l)f$' => '\\1ves', '([^d]ea)f$' => '\\1ves', '(ar)f$' => '\\1ves', '([nlw]i)fe$' => '\\1ves', '([aeiou]y)$' => '\\1s', '([^aeiou])y$' => '\\1ies', '([^o])o$' => '\\1oes', 's$' => 'ses', '(.)$' => '\\1s'); }
/** * Validates the URL fields, requiring that any URL fields that have a value are valid URLs * * @param array &$messages The messages to display to the user * @return void */ private function checkURLFields(&$messages) { foreach ($this->url_fields as $url_field) { $value = trim(fRequest::get($url_field)); if (self::stringlike($value) && !preg_match('#^https?://[^ ]+$#iD', $value)) { $messages[] = self::compose('%sPlease enter a URL in the form http://www.example.com/page', fValidationException::formatField(fGrammar::humanize($url_field))); } } }
/** * Generates a clone of the current record, removing any auto incremented primary key value and allowing for replicating related records * * This method will accept three different sets of parameters: * * - No parameters: this object will be cloned * - A single `TRUE` value: this object plus all many-to-many associations and all child records (recursively) will be cloned * - Any number of plural related record class names: the many-to-many associations or child records that correspond to the classes specified will be cloned * * The class names specified can be a simple class name if there is only a * single route between the two corresponding database tables. If there is * more than one route between the two tables, the class name should be * substituted with a string in the format `'RelatedClass{route}'`. * * @param string $related_class The plural related class to replicate - see method description for details * @param string ... * @return fActiveRecord The cloned record */ public function replicate($related_class = NULL) { fORM::callHookCallbacks($this, 'pre::replicate()', $this->values, $this->old_values, $this->related_records, $this->cache, fActiveRecord::$replicate_level); fActiveRecord::$replicate_level++; $class = get_class($this); $hash = self::hash($this->values, $class); $schema = fORMSchema::retrieve($class); $table = fORM::tablize($class); // If the object has not been replicated yet, do it now if (!isset(fActiveRecord::$replicate_map[$class])) { fActiveRecord::$replicate_map[$class] = array(); } if (!isset(fActiveRecord::$replicate_map[$class][$hash])) { fActiveRecord::$replicate_map[$class][$hash] = clone $this; // We need the primary key to get a hash, otherwise certain recursive relationships end up losing members $pk_columns = $schema->getKeys($table, 'primary'); if (sizeof($pk_columns) == 1 && $schema->getColumnInfo($table, $pk_columns[0], 'auto_increment')) { fActiveRecord::$replicate_map[$class][$hash]->values[$pk_columns[0]] = $this->values[$pk_columns[0]]; } } $clone = fActiveRecord::$replicate_map[$class][$hash]; $parameters = func_get_args(); $recursive = FALSE; $many_to_many_relationships = $schema->getRelationships($table, 'many-to-many'); $one_to_many_relationships = $schema->getRelationships($table, 'one-to-many'); // When just TRUE is passed we recursively replicate all related records if (sizeof($parameters) == 1 && $parameters[0] === TRUE) { $parameters = array(); $recursive = TRUE; foreach ($many_to_many_relationships as $relationship) { $parameters[] = fGrammar::pluralize(fORM::classize($relationship['related_table'])) . '{' . $relationship['join_table'] . '}'; } foreach ($one_to_many_relationships as $relationship) { $parameters[] = fGrammar::pluralize(fORM::classize($relationship['related_table'])) . '{' . $relationship['related_column'] . '}'; } } $record_sets = array(); foreach ($parameters as $parameter) { // Parse the Class{route} strings if (strpos($parameter, '{') !== FALSE) { $brace = strpos($parameter, '{'); $related_class = fGrammar::singularize(substr($parameter, 0, $brace)); $related_class = fORM::getRelatedClass($class, $related_class); $related_table = fORM::tablize($related_class); $route = substr($parameter, $brace + 1, -1); } else { $related_class = fGrammar::singularize($parameter); $related_class = fORM::getRelatedClass($class, $related_class); $related_table = fORM::tablize($related_class); $route = fORMSchema::getRouteName($schema, $table, $related_table); } // Determine the kind of relationship $many_to_many = FALSE; $one_to_many = FALSE; foreach ($many_to_many_relationships as $relationship) { if ($relationship['related_table'] == $related_table && $relationship['join_table'] == $route) { $many_to_many = TRUE; break; } } foreach ($one_to_many_relationships as $relationship) { if ($relationship['related_table'] == $related_table && $relationship['related_column'] == $route) { $one_to_many = TRUE; break; } } if (!$many_to_many && !$one_to_many) { throw new fProgrammerException('The related class specified, %1$s, does not appear to be in a many-to-many or one-to-many relationship with %$2s', $parameter, get_class($this)); } // Get the related records $record_set = fORMRelated::buildRecords($class, $this->values, $this->related_records, $related_class, $route); // One-to-many records need to be replicated, possibly recursively if ($one_to_many) { if ($recursive) { $records = $record_set->call('replicate', TRUE); } else { $records = $record_set->call('replicate'); } $record_set = fRecordSet::buildFromArray($related_class, $records); $record_set->call('set' . fGrammar::camelize($route, TRUE), NULL); } // Cause the related records to be associated with the new clone fORMRelated::associateRecords($class, $clone->related_records, $related_class, $record_set, $route); } fActiveRecord::$replicate_level--; if (!fActiveRecord::$replicate_level) { // This removes the primary keys we had added back in for proper duplicate detection foreach (fActiveRecord::$replicate_map as $class => $records) { $table = fORM::tablize($class); $pk_columns = $schema->getKeys($table, 'primary'); if (sizeof($pk_columns) != 1 || !$schema->getColumnInfo($table, $pk_columns[0], 'auto_increment')) { continue; } foreach ($records as $hash => $record) { $record->values[$pk_columns[0]] = NULL; } } fActiveRecord::$replicate_map = array(); } fORM::callHookCallbacks($this, 'post::replicate()', $this->values, $this->old_values, $this->related_records, $this->cache, fActiveRecord::$replicate_level); fORM::callHookCallbacks($clone, 'cloned::replicate()', $clone->values, $clone->old_values, $clone->related_records, $clone->cache, fActiveRecord::$replicate_level); return $clone; }
/** * Uploads a file * * @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 fFile The uploaded file */ public static function upload($object, &$values, &$old_values, &$related_records, &$cache, $method_name, $parameters) { $class = get_class($object); list($action, $subject) = fORM::parseMethod($method_name); $column = fGrammar::underscorize($subject); $existing_temp_file = FALSE; // Try to upload the file putting it in the temp dir incase there is a validation problem with the record try { $upload_dir = self::$file_upload_columns[$class][$column]; $temp_dir = self::prepareTempDir($upload_dir); if (!fUpload::check($column)) { throw new fExpectedException('Please upload a file'); } $uploader = self::setUpFUpload($class, $column); $file = $uploader->move($temp_dir, $column); // If there was an eror, check to see if we have an existing file } catch (fExpectedException $e) { // If there is an existing file and none was uploaded, substitute the existing file $existing_file = fRequest::get('existing-' . $column); $delete_file = fRequest::get('delete-' . $column, 'boolean'); $no_upload = $e->getMessage() == self::compose('Please upload a file'); if ($existing_file && $delete_file && $no_upload) { $file = NULL; } elseif ($existing_file) { $file_path = $upload_dir->getPath() . $existing_file; $file = fFilesystem::createObject($file_path); $current_file = $values[$column]; // If the existing file is the same as the current file, we can just exit now if ($current_file && $current_file instanceof fFile && $file->getPath() == $current_file->getPath()) { return; } $existing_temp_file = TRUE; } else { $file = NULL; } } // Assign the file fActiveRecord::assign($values, $old_values, $column, $file); // Perform the file upload inheritance if (!empty(self::$column_inheritence[$class][$column])) { foreach (self::$column_inheritence[$class][$column] as $other_column) { if ($file) { // Image columns will only inherit if it is an fImage object if (!$file instanceof fImage && isset(self::$image_upload_columns[$class]) && array_key_exists($other_column, self::$image_upload_columns[$class])) { continue; } $other_upload_dir = self::$file_upload_columns[$class][$other_column]; $other_temp_dir = self::prepareTempDir($other_upload_dir); if ($existing_temp_file) { $other_file = fFilesystem::createObject($other_temp_dir->getPath() . $file->getName()); } else { $other_file = $file->duplicate($other_temp_dir, FALSE); } } else { $other_file = $file; } fActiveRecord::assign($values, $old_values, $other_column, $other_file); if (!$existing_temp_file && $other_file) { self::processImage($class, $other_column, $other_file); } } } // Process the file if (!$existing_temp_file && $file) { self::processImage($class, $column, $file); } return $file; }
/** * Prints out a piece of a template * * @param string $template The name of the template to print * @param string $piece The piece of the template to print * @param array $data The data to replace the variables with * @return void */ private static function printPiece($template, $name, $data) { if (!isset(self::$templates[$template]['pieces'][$name])) { throw new fProgrammerException('The template piece, %s, was not specified when defining the %s template', $name, $template); } $piece = self::$templates[$template]['pieces'][$name]; preg_match_all('#\\{\\{ (\\w+)((?:\\|\\w+)+)? \\}\\}#', $piece, $matches, PREG_SET_ORDER); foreach ($matches as $match) { $variable = $match[1]; $value = !isset($data[$variable]) ? NULL : $data[$variable]; if (isset($match[2])) { $filters = array_slice(explode('|', $match[2]), 1); foreach ($filters as $filter) { if (!in_array($filter, self::$filters)) { throw new fProgrammerException('The filter specified, %1$s, is invalid. Must be one of: %2$s.', $filter, join(', ', self::$filters)); } if (!strlen($value)) { continue; } if ($filter == 'inflect') { $value = fGrammar::inflectOnQuantity($data['total_records'], $value); } elseif ($filter == 'lower') { $value = fUTF8::lower($value); } elseif ($filter == 'url_encode') { $value = urlencode($value); } elseif ($filter == 'humanize') { $value = fGrammar::humanize($value); } } } $piece = preg_replace('#' . preg_quote($match[0], '#') . '#', fHTML::encode($value), $piece, 1); } echo $piece; }
/** * Creates the name for a field taking into account custom field names * * @param string $field The field to get the name for * @return string The field name */ private function makeFieldName($field) { if (isset($this->field_names[$field])) { return $this->field_names[$field]; } $suffix = ''; $bracket_pos = strpos($field, '['); if ($bracket_pos !== FALSE) { $array_dereference = substr($field, $bracket_pos); $field = substr($field, 0, $bracket_pos); preg_match_all('#(?<=\\[)[^\\[\\]]+(?=\\])#', $array_dereference, $array_keys, PREG_SET_ORDER); $array_keys = array_map('current', $array_keys); foreach ($array_keys as $array_key) { if (is_numeric($array_key)) { $suffix .= ' #' . ($array_key + 1); } else { $suffix .= ' ' . fGrammar::humanize($array_key); } } } return fGrammar::humanize($field) . $suffix; }
/** * 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()); } }
/** * 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; }
/** * 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) { $image_columns = isset(self::$image_upload_columns[$class]) ? array_keys(self::$image_upload_columns[$class]) : array(); $file_columns = isset(self::$file_upload_columns[$class]) ? array_keys(self::$file_upload_columns[$class]) : array(); foreach ($file_columns as $column) { $camelized_column = fGrammar::camelize($column, TRUE); $noun = 'file'; if (in_array($column, $image_columns)) { $noun = 'image'; } $signature = ''; if ($include_doc_comments) { $signature .= "/**\n"; $signature .= " * Encodes the filename of " . $column . " for output into an HTML form\n"; $signature .= " * \n"; $signature .= " * Only the filename will be returned, any directory will be stripped.\n"; $signature .= " * \n"; $signature .= " * @return string The HTML form-ready value\n"; $signature .= " */\n"; } $encode_method = 'encode' . $camelized_column; $signature .= 'public function ' . $encode_method . '()'; $signatures[$encode_method] = $signature; if (in_array($column, $image_columns)) { $signature = ''; if ($include_doc_comments) { $signature .= "/**\n"; $signature .= " * Takes the existing image and runs it through the prescribed fImage method calls\n"; $signature .= " * \n"; $signature .= " * @return void\n"; $signature .= " */\n"; } $process_method = 'process' . $camelized_column; $signature .= 'public function ' . $process_method . '()'; $signatures[$process_method] = $signature; } $signature = ''; if ($include_doc_comments) { $signature .= "/**\n"; $signature .= " * Prepares the filename of " . $column . " for output into HTML\n"; $signature .= " * \n"; $signature .= " * By default only the filename will be returned and any directory will be stripped.\n"; $signature .= " * The \$include_web_path parameter changes this behaviour.\n"; $signature .= " * \n"; $signature .= " * @param boolean \$include_web_path If the full web path to the " . $noun . " should be included\n"; $signature .= " * @return string The HTML-ready value\n"; $signature .= " */\n"; } $prepare_method = 'prepare' . $camelized_column; $signature .= 'public function ' . $prepare_method . '($include_web_path=FALSE)'; $signatures[$prepare_method] = $signature; $signature = ''; if ($include_doc_comments) { $signature .= "/**\n"; $signature .= " * Takes a file uploaded through an HTML form for " . $column . " and moves it into the specified directory\n"; $signature .= " * \n"; $signature .= " * Any columns that were designated as inheriting from this column will get a copy\n"; $signature .= " * of the uploaded file.\n"; $signature .= " * \n"; if ($noun == 'image') { $signature .= " * Any fImage calls that were added to the column will be processed on the uploaded image.\n"; $signature .= " * \n"; } $signature .= " * @return void\n"; $signature .= " */\n"; } $upload_method = 'upload' . $camelized_column; $signature .= 'public function ' . $upload_method . '()'; $signatures[$upload_method] = $signature; $signature = ''; if ($include_doc_comments) { $signature .= "/**\n"; $signature .= " * Takes a file that exists on the filesystem and copies it into the specified directory for " . $column . "\n"; $signature .= " * \n"; if ($noun == 'image') { $signature .= " * Any fImage calls that were added to the column will be processed on the copied image.\n"; $signature .= " * \n"; } $signature .= " * @return void\n"; $signature .= " */\n"; } $set_method = 'set' . $camelized_column; $signature .= 'public function ' . $set_method . '()'; $signatures[$set_method] = $signature; $signature = ''; if ($include_doc_comments) { $signature .= "/**\n"; $signature .= " * Returns metadata about " . $column . "\n"; $signature .= " * \n"; $signature .= " * @param string \$element The element to return. Must be one of: 'type', 'not_null', 'default', 'valid_values', 'max_length', 'feature', 'directory'.\n"; $signature .= " * @return mixed The metadata array or a single element\n"; $signature .= " */\n"; } $inspect_method = 'inspect' . $camelized_column; $signature .= 'public function ' . $inspect_method . '($element=NULL)'; $signatures[$inspect_method] = $signature; } }
/** * Takes a class name (or class) and turns it into a table name - Uses custom mapping if set * * @param mixed $class he class name or instance of the class * @return string The table name */ public static function tablize($class) { if (!isset(self::$class_table_map[$class])) { self::$class_table_map[$class] = fGrammar::underscorize(fGrammar::pluralize($class)); } return self::$class_table_map[$class]; }
/** * Recursivly builds records. * * @param array* $completed_fixtures * Completed records is stored in this array * @param $fixture_data * Build records of this fixture */ private function buildRecords(&$completed_fixtures, $fixture_name, $traverse = TRUE) { if (array_key_exists($fixture_name, $completed_fixtures)) { return; } // Load data if (isset($this->fixture_data[$fixture_name]) === FALSE) { $this->loadFixture($fixture_name); } $class_name = fORM::classize($fixture_name); // If the class does not exists created it if (class_exists($class_name) === FALSE) { fORM::defineActiveRecordClass($class_name); } // Create the records $method_name = NULL; $record = NULL; $records = array(); foreach ($this->fixture_data[$fixture_name] as $record_data) { $record = new $class_name(); foreach ($record_data as $key => $value) { $method_name = 'set' . fGrammar::camelize($key, $upper = TRUE); $value = $this->applyHookCallbacks(self::PreSetBuildHook, $fixture_name, $key, $value); if ($this->isRelationshipKey($fixture_name, $key)) { $related_table = $this->getRelatedTable($fixture_name, $key); $required = $this->isRequiredKey($fixture_name, $key); if ($traverse && array_key_exists($related_table, $completed_fixtures) === FALSE && $fixture_name !== $related_table) { if (isset($value) && array_key_exists($related_table, $this->fixture_sources)) { $this->buildRecords($completed_fixtures, $related_table); array_unshift($this->tables_to_tear_down, $related_table); } } } $record->{$method_name}($value); } $record->store(); $records[] = $record; } $completed_fixtures[$fixture_name] = fRecordSet::buildFromArray($class_name, $records); }
/** * 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; }
/** * Creates an object to represent an image on the filesystem * * @throws fValidationException When no image was specified, when the image does not exist or when the path specified is not an image * * @param string $file_path The path to the image * @param boolean $skip_checks If file checks should be skipped, which improves performance, but may cause undefined behavior - only skip these if they are duplicated elsewhere * @return fImage */ public function __construct($file_path, $skip_checks = FALSE) { self::determineProcessor(); parent::__construct($file_path, $skip_checks); if (!self::isImageCompatible($file_path)) { $valid_image_types = array('GIF', 'JPG', 'PNG'); if (self::$processor == 'imagemagick') { $valid_image_types[] = 'TIF'; } throw new fValidationException('The image specified, %1$s, is not a valid %2$s file', $file_path, fGrammar::joinArray($valid_image_types, 'or')); } }
/** * Prints an RSS `link` HTML tag to the output * * @param mixed $info The path or array containing the `'path'` to the RSS xml file. May also contain a `'title'` key for the title of the RSS feed. * @return void */ protected function placeRSS($info) { if (!is_array($info)) { $info = array('path' => $info, 'title' => fGrammar::humanize(preg_replace('#.*?([^/]+).(rss|xml)$#iD', '\\1', $info))); } if (!isset($info['title'])) { throw new fProgrammerException('The RSS value %s is missing the title key', $info); } echo '<link rel="alternate" type="application/rss+xml" href="' . $info['path'] . '" title="' . $info['title'] . '" />' . "\n"; }
public function tearDown() { fGrammar::reset(); }
/** * Returns the approximate difference in time, discarding any unit of measure but the least specific. * * The output will read like: * * - "This timestamp is `{return value}` the provided one" when a timestamp it passed * - "This timestamp is `{return value}`" when no timestamp is passed and comparing with the current timestamp * * Examples of output for a timestamp passed might be: * * - `'5 minutes after'` * - `'2 hours before'` * - `'2 days after'` * - `'at the same time'` * * Examples of output for no timestamp passed might be: * * - `'5 minutes ago'` * - `'2 hours ago'` * - `'2 days from now'` * - `'1 year ago'` * - `'right now'` * * You would never get the following output since it includes more than one unit of time measurement: * * - `'5 minutes and 28 seconds'` * - `'3 weeks, 1 day and 4 hours'` * * Values that are close to the next largest unit of measure will be rounded up: * * - `'55 minutes'` would be represented as `'1 hour'`, however `'45 minutes'` would not * - `'29 days'` would be represented as `'1 month'`, but `'21 days'` would be shown as `'3 weeks'` * * @param fTimestamp|object|string|integer $other_timestamp The timestamp to create the difference with, `NULL` is interpreted as now * @return string The fuzzy difference in time between the this timestamp and the one provided */ public function getFuzzyDifference($other_timestamp = NULL) { $relative_to_now = FALSE; if ($other_timestamp === NULL) { $relative_to_now = TRUE; } $other_timestamp = new fTimestamp($other_timestamp); $diff = $this->timestamp - $other_timestamp->timestamp; if (abs($diff) < 10) { if ($relative_to_now) { return self::compose('right now'); } return self::compose('at the same time'); } $break_points = array(45 => array(1, self::compose('second'), self::compose('seconds')), 2700 => array(60, self::compose('minute'), self::compose('minutes')), 64800 => array(3600, self::compose('hour'), self::compose('hours')), 432000 => array(86400, self::compose('day'), self::compose('days')), 1814400 => array(604800, self::compose('week'), self::compose('weeks')), 23328000 => array(2592000, self::compose('month'), self::compose('months')), 2147483647 => array(31536000, self::compose('year'), self::compose('years'))); foreach ($break_points as $break_point => $unit_info) { if (abs($diff) > $break_point) { continue; } $unit_diff = round(abs($diff) / $unit_info[0]); $units = fGrammar::inflectOnQuantity($unit_diff, $unit_info[1], $unit_info[2]); break; } if ($relative_to_now) { if ($diff > 0) { return self::compose('%1$s %2$s from now', $unit_diff, $units); } return self::compose('%1$s %2$s ago', $unit_diff, $units); } if ($diff > 0) { return self::compose('%1$s %2$s after', $unit_diff, $units); } return self::compose('%1$s %2$s before', $unit_diff, $units); }
/** * Sets the money column and then tries to objectify it with an related currency column * * @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 fActiveRecord The record object, to allow for method chaining */ public static function setMoneyColumn($object, &$values, &$old_values, &$related_records, &$cache, $method_name, $parameters) { list($action, $subject) = fORM::parseMethod($method_name); $column = fGrammar::underscorize($subject); $class = get_class($object); if (count($parameters) < 1) { throw new fProgrammerException('The method, %s(), requires at least one parameter', $method_name); } $value = $parameters[0]; fActiveRecord::assign($values, $old_values, $column, $value); $currency_column = self::$money_columns[$class][$column]; // See if we can make an fMoney object out of the values self::objectifyMoneyWithCurrency($values, $old_values, $column, $currency_column); if ($currency_column) { if ($value instanceof fMoney) { fActiveRecord::assign($values, $old_values, $currency_column, $value->getCurrency()); } } return $object; }
/** * Sets the timezone column and then tries to objectify the related timestamp column * * @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 fActiveRecord The record object, to allow for method chaining */ public static function setTimezoneColumn($object, &$values, &$old_values, &$related_records, &$cache, $method_name, $parameters) { list($action, $subject) = fORM::parseMethod($method_name); $column = fGrammar::underscorize($subject); $class = get_class($object); if (!isset($parameters[0])) { throw new fProgrammerException('The method, %s(), requires at least one parameter', $method_name); } fActiveRecord::assign($values, $old_values, $column, $parameters[0]); // See if we can make an fTimestamp object out of the values self::objectifyTimestampWithTimezone($values, $old_values, self::$timezone_columns[$class][$column], $column); return $object; }
/** * Takes a class name (or class) and turns it into a table name - Uses custom mapping if set * * @param string $class The class name * @return string The table name */ public static function tablize($class) { if (!isset(self::$class_table_map[$class])) { self::$class_table_map[$class] = fGrammar::underscorize(fGrammar::pluralize(preg_replace('#^.*\\\\#', '', $class))); } return self::$class_table_map[$class]; }
/** * Stores a set of one-to-many related records in the database * * @throws fValidationException When one of the "many" records throws an exception from fActiveRecord::store() * @internal * * @param string $class The class to store the related records for * @param array &$values The current values for the main record being stored * @param array &$related_records The related records array * @param string $related_class The related class being stored * @param string $route The route to the related class * @param boolean $force_cascade This flag will be passed to the fActiveRecord::delete() method on related records that are being deleted * @return void */ public static function storeOneToStar($class, &$values, &$related_records, $related_class, $route, $force_cascade) { $table = fORM::tablize($class); $related_table = fORM::tablize($related_class); $schema = fORMSchema::retrieve($class); $relationship = fORMSchema::getRoute($schema, $table, $related_table, $route); $column_value = $values[$relationship['column']]; if (!empty($related_records[$related_table][$route]['record_set'])) { $record_set = $related_records[$related_table][$route]['record_set']; } else { $record_set = self::buildRecords($class, $values, $related_records, $related_class, $route); } $where_conditions = array($relationship['related_column'] . '=' => $column_value); $existing_records = fRecordSet::build($related_class, $where_conditions); $existing_primary_keys = $existing_records->getPrimaryKeys(); $new_primary_keys = $record_set->getPrimaryKeys(); $primary_keys_to_delete = self::multidimensionArrayDiff($existing_primary_keys, $new_primary_keys); foreach ($primary_keys_to_delete as $primary_key_to_delete) { $object_to_delete = new $related_class($primary_key_to_delete); // don't delete record if the relationship does not call for it if ($relationship['on_delete'] == 'set_null') { $related_set_method = 'set' . fGrammar::camelize($relationship['related_column'], TRUE); $object_to_delete->{$related_set_method}(NULL); $object_to_delete->store(); continue; } $object_to_delete->delete($force_cascade); } $set_method_name = 'set' . fGrammar::camelize($relationship['related_column'], TRUE); $first_pk_column = self::determineFirstPKColumn($class, $related_class, $route); $filter = self::determineRequestFilter(fORM::classize($relationship['table']), $related_class, $route); $pk_field = $filter . $first_pk_column; $input_keys = array_keys(fRequest::get($pk_field, 'array', array())); // Set all of the values first to prevent issues with recursive relationships foreach ($record_set as $i => $record) { $record->{$set_method_name}($column_value); } foreach ($record_set as $i => $record) { fRequest::filter($filter, isset($input_keys[$i]) ? $input_keys[$i] : $i); $record->store(); fRequest::unfilter(); } }
/** * Returns the approximate difference in time, discarding any unit of measure but the least specific. * * The output will read like: * * - "This date is `{return value}` the provided one" when a date it passed * - "This date is `{return value}`" when no date is passed and comparing with today * * Examples of output for a date passed might be: * * - `'2 days after'` * - `'1 year before'` * - `'same day'` * * Examples of output for no date passed might be: * * - `'2 days from now'` * - `'1 year ago'` * - `'today'` * * You would never get the following output since it includes more than one unit of time measurement: * * - `'3 weeks and 1 day'` * - `'1 year and 2 months'` * * Values that are close to the next largest unit of measure will be rounded up: * * - `6 days` would be represented as `1 week`, however `5 days` would not * - `29 days` would be represented as `1 month`, but `21 days` would be shown as `3 weeks` * * @param fDate|object|string|integer $other_date The date to create the difference with, `NULL` is interpreted as today * @param boolean $simple When `TRUE`, the returned value will only include the difference in the two dates, but not `from now`, `ago`, `after` or `before` * @param boolean :$simple * @return string The fuzzy difference in time between the this date and the one provided */ public function getFuzzyDifference($other_date = NULL, $simple = FALSE) { if (is_bool($other_date)) { $simple = $other_date; $other_date = NULL; } $relative_to_now = FALSE; if ($other_date === NULL) { $relative_to_now = TRUE; } $other_date = new fDate($other_date); $diff = $this->date - $other_date->date; if (abs($diff) < 86400) { if ($relative_to_now) { return self::compose('today'); } return self::compose('same day'); } static $break_points = array(); if (!$break_points) { $break_points = array(432000 => array(86400, self::compose('day'), self::compose('days')), 1814400 => array(604800, self::compose('week'), self::compose('weeks')), 23328000 => array(2592000, self::compose('month'), self::compose('months')), 2147483647 => array(31536000, self::compose('year'), self::compose('years'))); } foreach ($break_points as $break_point => $unit_info) { if (abs($diff) > $break_point) { continue; } $unit_diff = round(abs($diff) / $unit_info[0]); $units = fGrammar::inflectOnQuantity($unit_diff, $unit_info[1], $unit_info[2]); break; } if ($simple) { return self::compose('%1$s %2$s', $unit_diff, $units); } if ($relative_to_now) { if ($diff > 0) { return self::compose('%1$s %2$s from now', $unit_diff, $units); } return self::compose('%1$s %2$s ago', $unit_diff, $units); } if ($diff > 0) { return self::compose('%1$s %2$s after', $unit_diff, $units); } return self::compose('%1$s %2$s before', $unit_diff, $units); }
/** * Prints a sortable column header `a` tag * * The a tag will include the CSS class `'sortable_column'` and the * direction being sorted, `'asc'` or `'desc'`. * * {{{ * #!php * fCRUD::printSortableColumn('name', 'Name'); * }}} * * would create the following HTML based on the page context * * {{{ * #!html * <!-- If name is the current sort column in the asc direction, the output would be --> * <a href="?sort=name&dir=desc" class="sorted_column asc">Name</a> * * <!-- If name is not the current sort column, the output would be --> * <a href="?sort-name&dir=asc" class="sorted_column">Name</a> * }}} * * @param string $column The column to create the sortable header for * @param string $column_name This will override the humanized version of the column * @return void */ public static function printSortableColumn($column, $column_name = NULL) { if ($column_name === NULL) { $column_name = fGrammar::humanize($column); } if (self::$sort_column == $column) { $sort = $column; $direction = self::$sort_direction == 'asc' ? 'desc' : 'asc'; } else { $sort = $column; $direction = 'asc'; } $columns = array_merge(array('sort', 'dir'), array_keys(self::$search_values)); $values = array_merge(array($sort, $direction), array_values(self::$search_values)); $url = fHTML::encode(fURL::get() . fURL::replaceInQueryString($columns, $values)); $css_class = self::$sort_column == $column ? ' ' . self::$sort_direction : ''; $column_name = fHTML::prepare($column_name); echo '<a href="' . $url . '" class="sortable_column' . $css_class . '">' . $column_name . '</a>'; }
/** * 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::$ordering_columns[$class])) { return; } foreach (self::$ordering_columns[$class] as $column => $enabled) { $camelized_column = fGrammar::camelize($column, TRUE); $signature = ''; if ($include_doc_comments) { $signature .= "/**\n"; $signature .= " * Returns metadata about " . $column . "\n"; $signature .= " * \n"; $signature .= " * @param string \$element The element to return. Must be one of: 'type', 'not_null', 'default', 'feature', 'max_ordering_value'.\n"; $signature .= " * @return mixed The metadata array or a single element\n"; $signature .= " */\n"; } $inspect_method = 'inspect' . $camelized_column; $signature .= 'public function ' . $inspect_method . '($element=NULL)'; $signatures[$inspect_method] = $signature; } }
/** * Returns the approximate difference in time, discarding any unit of measure but the least specific. * * The output will read like: * * - "This time is `{return value}` the provided one" when a time it passed * - "This time is `{return value}`" when no time is passed and comparing with the current time * * Examples of output for a time passed might be: * * - `'5 minutes after'` * - `'2 hours before'` * - `'at the same time'` * * Examples of output for no time passed might be: * * - `'5 minutes ago'` * - `'2 hours ago'` * - `'right now'` * * You would never get the following output since it includes more than one unit of time measurement: * * - `'5 minutes and 28 seconds'` * - `'1 hour, 15 minutes'` * * Values that are close to the next largest unit of measure will be rounded up: * * - `'55 minutes'` would be represented as `'1 hour'`, however `'45 minutes'` would not * * @param fTime|object|string|integer $other_time The time to create the difference with, `NULL` is interpreted as now * @param boolean $simple When `TRUE`, the returned value will only include the difference in the two times, but not `from now`, `ago`, `after` or `before` * @param boolean :$simple * @return string The fuzzy difference in time between the this time and the one provided */ public function getFuzzyDifference($other_time = NULL, $simple = FALSE) { if (is_bool($other_time)) { $simple = $other_time; $other_time = NULL; } $relative_to_now = FALSE; if ($other_time === NULL) { $relative_to_now = TRUE; } $other_time = new fTime($other_time); $diff = $this->time - $other_time->time; if (abs($diff) < 10) { if ($relative_to_now) { return self::compose('right now'); } return self::compose('at the same time'); } static $break_points = array(); if (!$break_points) { $break_points = array(45 => array(1, self::compose('second'), self::compose('seconds')), 2700 => array(60, self::compose('minute'), self::compose('minutes')), 64800 => array(3600, self::compose('hour'), self::compose('hours')), 432000 => array(86400, self::compose('day'), self::compose('days'))); } foreach ($break_points as $break_point => $unit_info) { if (abs($diff) > $break_point) { continue; } $unit_diff = round(abs($diff) / $unit_info[0]); $units = fGrammar::inflectOnQuantity($unit_diff, $unit_info[1], $unit_info[2]); break; } if ($simple) { return self::compose('%1$s %2$s', $unit_diff, $units); } if ($relative_to_now) { if ($diff > 0) { return self::compose('%1$s %2$s from now', $unit_diff, $units); } return self::compose('%1$s %2$s ago', $unit_diff, $units); } if ($diff > 0) { return self::compose('%1$s %2$s after', $unit_diff, $units); } return self::compose('%1$s %2$s before', $unit_diff, $units); }
/** * Validates against a *-to-many one or more rule * * @param fActiveRecord $object The object being checked * @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 related class * @param string $route The name of the route from the class to the related class * @return string An error message for the rule */ private static function checkRelatedOneOrMoreRule($object, &$values, &$related_records, $related_class, $route) { $related_table = fORM::tablize($related_class); $class = get_class($object); $exists = $object->exists(); $records_are_set = isset($related_records[$related_table][$route]); $has_records = $records_are_set && $related_records[$related_table][$route]['count']; if ($exists && (!$records_are_set || $has_records)) { return; } if (!$exists && $has_records) { return; } return self::compose('%sPlease select at least one', fValidationException::formatField(fGrammar::pluralize(fORMRelated::getRelatedRecordName($class, $related_class, $route)))); }
/** * 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::$money_columns[$class])) { return; } foreach (self::$money_columns[$class] as $column => $enabled) { $camelized_column = fGrammar::camelize($column, TRUE); // Get and set methods $signature = ''; if ($include_doc_comments) { $signature .= "/**\n"; $signature .= " * Gets the current value of " . $column . "\n"; $signature .= " * \n"; $signature .= " * @return fMoney 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 fMoney|string|integer \$" . $column . " The new value - a string or integer will be converted to the default currency (if defined)\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 fMoney object, the ->__toString() method will be called\n"; $signature .= " * resulting in the value minus the currency symbol and thousands separators\n"; $signature .= " * \n"; $signature .= " * @return string The HTML form-ready value\n"; $signature .= " */\n"; } $encode_method = 'encode' . $camelized_column; $signature .= 'public function ' . $encode_method . '()'; $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 fMoney object, the ->format() method will be called\n"; $signature .= " * resulting in the value including the currency symbol and thousands separators\n"; $signature .= " * \n"; $signature .= " * @param boolean \$remove_zero_fraction If a fraction of all zeros should be removed\n"; $signature .= " * @return string The HTML-ready value\n"; $signature .= " */\n"; } $prepare_method = 'prepare' . $camelized_column; $signature .= 'public function ' . $prepare_method . '($remove_zero_fraction=FALSE)'; $signatures[$prepare_method] = $signature; } }
/** * Throws an fEmptySetException if the record set is empty * * @throws fEmptySetException When there are no record in the set * * @param string $message The message to use for the exception if there are no records in this set * @return fRecordSet The record set object, to allow for method chaining */ public function tossIfEmpty($message = NULL) { if ($this->records) { return $this; } if ($message === NULL) { if (is_array($this->class)) { $names = array_map(array('fORM', 'getRecordName'), $this->class); $names = array_map(array('fGrammar', 'pluralize'), $names); $name = join(', ', $names); } else { $name = fGrammar::pluralize(fORM::getRecordName($this->class)); } $message = self::compose('No %s could be found', $name); } throw new fEmptySetException($message); }
/** * Copies a file from the filesystem to the file upload directory and sets it as the file for the specified column * * This method will perform the fImage calls defined for the column. * * @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 fActiveRecord The record object, to allow for method chaining */ public static function set($object, &$values, &$old_values, &$related_records, &$cache, $method_name, $parameters) { $class = get_class($object); list($action, $column) = fORM::parseMethod($method_name); $doc_root = realpath($_SERVER['DOCUMENT_ROOT']); if (!array_key_exists(0, $parameters)) { throw new fProgrammerException('The method %s requires exactly one parameter', $method_name . '()'); } $file_path = $parameters[0]; // Handle objects being passed in if ($file_path instanceof fFile) { $file_path = $file_path->getPath(); } elseif (is_object($file_path) && is_callable(array($file_path, '__toString'))) { $file_path = $file_path->__toString(); } elseif (is_object($file_path)) { $file_path = (string) $file_path; } if ($file_path !== NULL && $file_path !== '' && $file_path !== FALSE) { if (!$file_path || !file_exists($file_path) && !file_exists($doc_root . $file_path)) { throw new fEnvironmentException('The file specified, %s, does not exist. This may indicate a missing enctype="multipart/form-data" attribute in form tag.', $file_path); } if (!file_exists($file_path) && file_exists($doc_root . $file_path)) { $file_path = $doc_root . $file_path; } if (is_dir($file_path)) { throw new fProgrammerException('The file specified, %s, is not a file but a directory', $file_path); } $upload_dir = self::$file_upload_columns[$class][$column]; try { $temp_dir = new fDirectory($upload_dir->getPath() . self::TEMP_DIRECTORY . DIRECTORY_SEPARATOR); } catch (fValidationException $e) { $temp_dir = fDirectory::create($upload_dir->getPath() . self::TEMP_DIRECTORY . DIRECTORY_SEPARATOR); } $file = fFilesystem::createObject($file_path); $new_file = $file->duplicate($temp_dir); } else { $new_file = NULL; } fActiveRecord::assign($values, $old_values, $column, $new_file); // Perform column inheritance if (!empty(self::$column_inheritence[$class][$column])) { foreach (self::$column_inheritence[$class][$column] as $other_column) { self::set($object, $values, $old_values, $related_records, $cache, 'set' . fGrammar::camelize($other_column, TRUE), array($file)); } } if ($new_file) { self::processImage($class, $column, $new_file); } return $object; }
private function hasProperty($property_name) { if (is_null($this->column_info)) { $this->column_info = $this->schema->getColumnInfo($this->table_name); } return isset($this->column_info[fGrammar::underscorize($property_name)]); }