コード例 #1
0
 public function testPrebuildOneToManyMultiColumn()
 {
     fORMRelated::setOrderBys('User', 'FavoriteAlbum', array('album_id' => 'desc'));
     $set = fRecordSet::build('User');
     $set->prebuildFavoriteAlbums();
     ob_start();
     fORMDatabase::retrieve()->enableDebugging(TRUE);
     foreach ($set as $user) {
         $primary_keys = $user->listFavoriteAlbums();
         switch ($user->getUserId()) {
             case 1:
                 $expected_primary_keys = array(array('email' => '*****@*****.**', 'album_id' => 7), array('email' => '*****@*****.**', 'album_id' => 4), array('email' => '*****@*****.**', 'album_id' => 3), array('email' => '*****@*****.**', 'album_id' => 2), array('email' => '*****@*****.**', 'album_id' => 1));
                 break;
             case 2:
                 $expected_primary_keys = array(array('email' => '*****@*****.**', 'album_id' => 2));
                 break;
             case 3:
                 $expected_primary_keys = array();
                 break;
             case 4:
                 $expected_primary_keys = array();
                 break;
         }
         $this->assertEquals($expected_primary_keys, $primary_keys);
     }
     fORMDatabase::retrieve()->enableDebugging(FALSE);
     $output = ob_get_clean();
     $this->assertEquals('', $output);
 }
コード例 #2
0
ファイル: fActiveRecord.php プロジェクト: mrjwc/printmaster
 /**
  * Stores a record in the database, whether existing or new
  * 
  * This method will start database and filesystem transactions if they have
  * not already been started.
  * 
  * @throws fValidationException  When ::validate() throws an exception
  * 
  * @param  boolean $force_cascade  When storing related records, this will force deleting child records even if they have their own children in a relationship with an RESTRICT or NO ACTION for the ON DELETE clause
  * @return fActiveRecord  The record object, to allow for method chaining
  */
 public function store($force_cascade = FALSE)
 {
     $class = get_class($this);
     if (fORM::getActiveRecordMethod($class, 'store')) {
         return $this->__call('store', array());
     }
     fORM::callHookCallbacks($this, 'pre::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
     $db = fORMDatabase::retrieve($class, 'write');
     $schema = fORMSchema::retrieve($class);
     try {
         $table = fORM::tablize($class);
         // New auto-incrementing records require lots of special stuff, so we'll detect them here
         $new_autoincrementing_record = FALSE;
         if (!$this->exists()) {
             $pk_columns = $schema->getKeys($table, 'primary');
             $pk_column = $pk_columns[0];
             $pk_auto_incrementing = $schema->getColumnInfo($table, $pk_column, 'auto_increment');
             if (sizeof($pk_columns) == 1 && $pk_auto_incrementing && !$this->values[$pk_column]) {
                 $new_autoincrementing_record = TRUE;
             }
         }
         $inside_db_transaction = $db->isInsideTransaction();
         if (!$inside_db_transaction) {
             $db->translatedQuery('BEGIN');
         }
         fORM::callHookCallbacks($this, 'post-begin::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
         $this->validate();
         fORM::callHookCallbacks($this, 'post-validate::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
         // Storing main table
         if (!$this->exists()) {
             $params = $this->constructInsertParams();
         } else {
             $params = $this->constructUpdateParams();
         }
         $result = call_user_func_array($db->translatedQuery, $params);
         // If there is an auto-incrementing primary key, grab the value from the database
         if ($new_autoincrementing_record) {
             $this->set($pk_column, $result->getAutoIncrementedValue());
         }
         // Fix cascade updated columns for in-memory objects to prevent issues when saving
         $one_to_one_relationships = $schema->getRelationships($table, 'one-to-one');
         $one_to_many_relationships = $schema->getRelationships($table, 'one-to-many');
         $relationships = array_merge($one_to_one_relationships, $one_to_many_relationships);
         foreach ($relationships as $relationship) {
             $type = in_array($relationship, $one_to_one_relationships) ? 'one-to-one' : 'one-to-many';
             $route = fORMSchema::getRouteNameFromRelationship($type, $relationship);
             $related_table = $relationship['related_table'];
             $related_class = fORM::classize($related_table);
             $related_class = fORM::getRelatedClass($class, $related_class);
             if ($relationship['on_update'] != 'cascade') {
                 continue;
             }
             $column = $relationship['column'];
             if (!fActiveRecord::changed($this->values, $this->old_values, $column)) {
                 continue;
             }
             if (!isset($this->related_records[$related_table][$route]['record_set'])) {
                 continue;
             }
             $record_set = $this->related_records[$related_table][$route]['record_set'];
             $related_column = $relationship['related_column'];
             $old_value = fActiveRecord::retrieveOld($this->old_values, $column);
             $value = $this->values[$column];
             if ($old_value === NULL) {
                 continue;
             }
             foreach ($record_set as $record) {
                 if (isset($record->old_values[$related_column])) {
                     foreach (array_keys($record->old_values[$related_column]) as $key) {
                         if ($record->old_values[$related_column][$key] === $old_value) {
                             $record->old_values[$related_column][$key] = $value;
                         }
                     }
                 }
                 if ($record->values[$related_column] === $old_value) {
                     $record->values[$related_column] = $value;
                 }
             }
         }
         // Storing *-to-many and one-to-one relationships
         fORMRelated::store($class, $this->values, $this->related_records, $force_cascade);
         fORM::callHookCallbacks($this, 'pre-commit::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
         if (!$inside_db_transaction) {
             $db->translatedQuery('COMMIT');
         }
         fORM::callHookCallbacks($this, 'post-commit::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
     } catch (fException $e) {
         if (!$inside_db_transaction) {
             $db->translatedQuery('ROLLBACK');
         }
         fORM::callHookCallbacks($this, 'post-rollback::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
         if ($new_autoincrementing_record && self::hasOld($this->old_values, $pk_column)) {
             $this->values[$pk_column] = self::retrieveOld($this->old_values, $pk_column);
             unset($this->old_values[$pk_column]);
         }
         throw $e;
     }
     fORM::callHookCallbacks($this, 'post::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
     $was_new = !$this->exists();
     // If we got here we succefully stored, so update old values to make exists() work
     foreach ($this->values as $column => $value) {
         $this->old_values[$column] = array($value);
     }
     // If the object was just inserted into the database, save it to the identity map
     if ($was_new) {
         $hash = self::hash($this->values, $class);
         if (!isset(self::$identity_map[$class])) {
             self::$identity_map[$class] = array();
         }
         self::$identity_map[$class][$hash] = $this;
     }
     return $this;
 }
コード例 #3
0
 /**
  * Adds an `ORDER BY` clause to the SQL for the primary keys of this record set
  * 
  * @param  fDatabase $db             The database the query will be executed on 
  * @param  fSchema   $schema         The schema for the database
  * @param  array     $params         The parameters for the fDatabase::query() call
  * @param  string    $related_class  The related class to add the order bys for
  * @param  string    $route          The route to this table from another table
  * @return array  The params with the `ORDER BY` clause added
  */
 private function addOrderByParams($db, $schema, $params, $related_class, $route = NULL)
 {
     $table = fORM::tablize($this->class);
     $table_with_route = $route ? $table . '{' . $route . '}' : $table;
     $pk_columns = $schema->getKeys($table, 'primary');
     $first_pk_column = $pk_columns[0];
     $escaped_pk_columns = array();
     foreach ($pk_columns as $pk_column) {
         $escaped_pk_columns[$pk_column] = $db->escape('%r', $table_with_route . '.' . $pk_column);
     }
     $column_info = $schema->getColumnInfo($table);
     $sql = '';
     $number = 0;
     foreach ($this->getPrimaryKeys() as $primary_key) {
         $sql .= 'WHEN ';
         if (is_array($primary_key)) {
             $conditions = array();
             foreach ($pk_columns as $pk_column) {
                 $value = $primary_key[$pk_column];
                 // This makes sure the query performs the way an insert will
                 if ($value === NULL && $column_info[$pk_column]['not_null'] && $column_info[$pk_column]['default'] !== NULL) {
                     $value = $column_info[$pk_column]['default'];
                 }
                 $conditions[] = str_replace('%r', $escaped_pk_columns[$pk_column], fORMDatabase::makeCondition($schema, $table, $pk_column, '=', $value));
                 $params[] = $value;
             }
             $sql .= join(' AND ', $conditions);
         } else {
             $sql .= str_replace('%r', $escaped_pk_columns[$first_pk_column], fORMDatabase::makeCondition($schema, $table, $first_pk_column, '=', $primary_key));
             $params[] = $primary_key;
         }
         $sql .= ' THEN ' . $number . ' ';
         $number++;
     }
     $params[0] .= 'CASE ' . $sql . 'END ASC';
     if ($related_order_bys = fORMRelated::getOrderBys($this->class, $related_class, $route)) {
         $params[0] .= ', ';
         $params = fORMDatabase::addOrderByClause($db, $schema, $params, fORM::tablize($related_class), $related_order_bys);
     }
     return $params;
 }
コード例 #4
0
 /**
  * Stores any many-to-many associations or any one-to-many records that have been flagged for association
  *
  * @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  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 store($class, &$values, &$related_records, $force_cascade)
 {
     $table = fORM::tablize($class);
     $schema = fORMSchema::retrieve($class);
     foreach ($related_records as $related_table => $relationships) {
         foreach ($relationships as $route => $related_info) {
             if (!$related_info['associate']) {
                 continue;
             }
             $relationship = fORMSchema::getRoute($schema, $table, $related_table, $route);
             if (isset($relationship['join_table'])) {
                 fORMRelated::storeManyToMany($class, $values, $relationship, $related_info);
             } else {
                 $related_class = fORM::classize($related_table);
                 $related_class = fORM::getRelatedClass($class, $related_class);
                 fORMRelated::storeOneToStar($class, $values, $related_records, $related_class, $route, $force_cascade);
             }
         }
     }
 }
コード例 #5
0
 public function testChildValidationName()
 {
     try {
         fORMOrdering::configureOrderingColumn('FavoriteAlbum', 'position');
         fORMRelated::registerValidationNameMethod('User', 'FavoriteAlbum', 'makeName');
         $user = $this->createUser();
         $favorite_album_1 = new FavoriteAlbum();
         $favorite_album_2 = new FavoriteAlbum();
         $user->associateFavoriteAlbums(array($favorite_album_1, $favorite_album_2));
         $user->validate();
     } catch (fValidationException $e) {
         $message = preg_replace('#\\s+#', ' ', strip_tags($e->getMessage()));
         $this->assertContains('The following problems were found: Album 1 Album ID: Please enter a value Album 2 Album ID: Please enter a value', $message);
     }
 }
コード例 #6
0
 /**
  * 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;
 }
コード例 #7
0
ファイル: Test.php プロジェクト: pmanterys/wp-mw-newsletter
 protected function configure()
 {
     fORMRelated::setOrderBys($this, 'WpTesting_Model_Taxonomy', array(WP_DB_PREFIX . 'term_relationships.`term_order`' => 'asc'));
 }
 public function testPrebuildOneToMany()
 {
     fORMRelated::setOrderBys('Flourish2Artist', 'Flourish2Album', array('album_id' => 'desc'));
     $set = fRecordSet::build('Flourish2Artist');
     $set->prebuildFlourish2Albums();
     ob_start();
     fORMDatabase::retrieve()->enableDebugging(TRUE);
     foreach ($set as $artist) {
         $album_ids = $artist->listFlourish2Albums();
         switch ($artist->getArtistId()) {
             case 1:
                 $expected_album_ids = array(3, 2, 1);
                 break;
             case 2:
                 $expected_album_ids = array(5, 4);
                 break;
             case 3:
                 $expected_album_ids = array(6);
                 break;
         }
         $this->assertEquals($expected_album_ids, $album_ids);
     }
     fORMDatabase::retrieve()->enableDebugging(FALSE);
     $output = ob_get_clean();
     $this->assertEquals('', $output);
 }
コード例 #9
0
 /**
  * Associate many records with it's related records by foreign key in one query
  * @param fRecordset|array $records
  * @param string $relatedClassName
  * @param string $foreignKeyName
  * @return array Objects fo type relatedClassName by it's ids
  */
 protected function associateManyRelated($records, $relatedClassName, $foreignKeyName)
 {
     $recordsById = array();
     if ($records instanceof fRecordSet) {
         foreach ($records as $record) {
             $recordsById[$record->getId()] = $record;
         }
     } else {
         $recordsById = $records;
     }
     if (empty($recordsById)) {
         return array();
     }
     // Get related records
     $orderBys = fORMRelated::getOrderBys(get_class(reset($recordsById)), $relatedClassName, $foreignKeyName);
     $relatedRecords = fRecordSet::build($relatedClassName, array($foreignKeyName . '=' => array_keys($recordsById)), $orderBys);
     $relatedRecordsById = array();
     $relatedRecordsByByForeignKey = array();
     foreach ($relatedRecords as $relatedRecord) {
         $relatedRecordsById[$relatedRecord->getId()] = $relatedRecord;
         $relatedRecordsByByForeignKey[$relatedRecord->get($foreignKeyName)][] = $relatedRecord;
     }
     // Assoc related records to records
     $associateMethodName = 'associate' . $relatedClassName;
     foreach ($relatedRecordsByByForeignKey as $foreignKeyValue => $relatedRecords) {
         $recordsById[$foreignKeyValue]->{$associateMethodName}($relatedRecords);
     }
     return $relatedRecordsById;
 }
コード例 #10
0
 /**
  * @dataProvider listManyToManyProvider
  */
 public function testListManyToMany($user_id, $list)
 {
     fORMRelated::setOrderBys('User', 'Group', array('group_id' => 'asc'), 'users_groups');
     $user = new User($user_id);
     $this->assertEquals($list, $user->listGroups('users_groups'));
 }
コード例 #11
0
ファイル: User.php プロジェクト: rberger/Graphite-Tattle
 protected function configure()
 {
  fORMRelated::setOrderBys($this,'Subscription',array('subscriptions.subscription_id' => 'desc'));
 }
コード例 #12
0
ファイル: fRecordSet.php プロジェクト: jsuarez/MyDesign
 /** 
  * Builds the related records for all records in this set in one DB query
  *  
  * @param  string $related_class  This should be the name of a related class
  * @param  string $route          This should be a column name or a join table name and is only required when there are multiple routes to a related table. If there are multiple routes and this is not specified, an fProgrammerException will be thrown.
  * @return fRecordSet  The record set object, to allow for method chaining
  */
 private function prebuild($related_class, $route = NULL)
 {
     if (!$this->records) {
         return $this;
     }
     $this->validateSingleClass('prebuild');
     // If there are no primary keys we can just exit
     if (!array_merge($this->getPrimaryKeys())) {
         return $this;
     }
     $related_table = fORM::tablize($related_class);
     $table = fORM::tablize($this->class);
     $route = fORMSchema::getRouteName($table, $related_table, $route, '*-to-many');
     $relationship = fORMSchema::getRoute($table, $related_table, $route, '*-to-many');
     $table_with_route = $route ? $table . '{' . $route . '}' : $table;
     // Build the query out
     $where_sql = $this->constructWhereClause($route);
     $order_by_sql = $this->constructOrderByClause($route);
     if ($related_order_bys = fORMRelated::getOrderBys($this->class, $related_class, $route)) {
         $order_by_sql .= ', ' . fORMDatabase::createOrderByClause($related_table, $related_order_bys);
     }
     $new_sql = 'SELECT ' . $related_table . '.*';
     // If we are going through a join table we need the related primary key for matching
     if (isset($relationship['join_table'])) {
         $new_sql .= ", " . $table_with_route . '.' . $relationship['column'];
     }
     $new_sql .= ' FROM :from_clause ';
     $new_sql .= ' WHERE ' . $where_sql;
     $new_sql .= ' :group_by_clause ';
     $new_sql .= ' ORDER BY ' . $order_by_sql;
     $new_sql = fORMDatabase::insertFromAndGroupByClauses($related_table, $new_sql);
     // Add the joining column to the group by
     if (strpos($new_sql, 'GROUP BY') !== FALSE) {
         $new_sql = str_replace(' ORDER BY', ', ' . $table . '.' . $relationship['column'] . ' ORDER BY', $new_sql);
     }
     // Run the query and inject the results into the records
     $result = fORMDatabase::retrieve()->translatedQuery($new_sql);
     $total_records = sizeof($this->records);
     for ($i = 0; $i < $total_records; $i++) {
         // Get the record we are injecting into
         $record = $this->records[$i];
         $keys = array();
         // If we are going through a join table, keep track of the record by the value in the join table
         if (isset($relationship['join_table'])) {
             try {
                 $current_row = $result->current();
                 $keys[$relationship['column']] = $current_row[$relationship['column']];
             } catch (fExpectedException $e) {
             }
             // If it is a straight join, keep track of the value by the related column value
         } else {
             $method = 'get' . fGrammar::camelize($relationship['related_column'], TRUE);
             $keys[$relationship['related_column']] = $record->{$method}();
         }
         // Loop through and find each row for the current record
         $rows = array();
         try {
             while (!array_diff_assoc($keys, $result->current())) {
                 $row = $result->fetchRow();
                 // If we are going through a join table we need to remove the related primary key that was used for matching
                 if (isset($relationship['join_table'])) {
                     unset($row[$relationship['column']]);
                 }
                 $rows[] = $row;
             }
         } catch (fExpectedException $e) {
         }
         // Set up the result object for the new record set
         $set = new fRecordSet($related_class, new ArrayIterator($rows));
         // Inject the new record set into the record
         $method = 'inject' . fGrammar::pluralize($related_class);
         $record->{$method}($set, $route);
     }
     return $this;
 }
コード例 #13
0
ファイル: fActiveRecord.php プロジェクト: jsuarez/MyDesign
 /**
  * Stores a record in the database, whether existing or new
  * 
  * This method will start database and filesystem transactions if they have
  * not already been started.
  * 
  * @throws fValidationException  When ::validate() throws an exception
  * 
  * @return fActiveRecord  The record object, to allow for method chaining
  */
 public function store()
 {
     $class = get_class($this);
     if (fORM::getActiveRecordMethod($class, 'store')) {
         return $this->__call('store', array());
     }
     fORM::callHookCallbacks($this, 'pre::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
     try {
         $table = fORM::tablize($class);
         $column_info = fORMSchema::retrieve()->getColumnInfo($table);
         // New auto-incrementing records require lots of special stuff, so we'll detect them here
         $new_autoincrementing_record = FALSE;
         if (!$this->exists()) {
             $pk_columns = fORMSchema::retrieve()->getKeys($table, 'primary');
             if (sizeof($pk_columns) == 1 && $column_info[$pk_columns[0]]['auto_increment'] && !$this->values[$pk_columns[0]]) {
                 $new_autoincrementing_record = TRUE;
                 $pk_column = $pk_columns[0];
             }
         }
         $inside_db_transaction = fORMDatabase::retrieve()->isInsideTransaction();
         if (!$inside_db_transaction) {
             fORMDatabase::retrieve()->translatedQuery('BEGIN');
         }
         fORM::callHookCallbacks($this, 'post-begin::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
         $this->validate();
         fORM::callHookCallbacks($this, 'post-validate::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
         // Storing main table
         $sql_values = array();
         foreach ($column_info as $column => $info) {
             $value = fORM::scalarize($class, $column, $this->values[$column]);
             $sql_values[$column] = fORMDatabase::escapeBySchema($table, $column, $value);
         }
         // Most databases don't like the auto incrementing primary key to be set to NULL
         if ($new_autoincrementing_record && $sql_values[$pk_column] == 'NULL') {
             unset($sql_values[$pk_column]);
         }
         if (!$this->exists()) {
             $sql = $this->constructInsertSQL($sql_values);
         } else {
             $sql = $this->constructUpdateSQL($sql_values);
         }
         $result = fORMDatabase::retrieve()->translatedQuery($sql);
         // If there is an auto-incrementing primary key, grab the value from the database
         if ($new_autoincrementing_record) {
             $this->set($pk_column, $result->getAutoIncrementedValue());
         }
         // Storing *-to-many relationships
         fORMRelated::store($class, $this->values, $this->related_records);
         fORM::callHookCallbacks($this, 'pre-commit::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
         if (!$inside_db_transaction) {
             fORMDatabase::retrieve()->translatedQuery('COMMIT');
         }
         fORM::callHookCallbacks($this, 'post-commit::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
     } catch (fException $e) {
         if (!$inside_db_transaction) {
             fORMDatabase::retrieve()->translatedQuery('ROLLBACK');
         }
         fORM::callHookCallbacks($this, 'post-rollback::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
         if ($new_autoincrementing_record && self::hasOld($this->old_values, $pk_column)) {
             $this->values[$pk_column] = self::retrieveOld($this->old_values, $pk_column);
             unset($this->old_values[$pk_column]);
         }
         throw $e;
     }
     fORM::callHookCallbacks($this, 'post::store()', $this->values, $this->old_values, $this->related_records, $this->cache);
     $was_new = !$this->exists();
     // If we got here we succefully stored, so update old values to make exists() work
     foreach ($this->values as $column => $value) {
         $this->old_values[$column] = array($value);
     }
     // If the object was just inserted into the database, save it to the identity map
     if ($was_new) {
         $hash = self::hash($this->values, $class);
         if (!isset(self::$identity_map[$class])) {
             self::$identity_map[$class] = array();
         }
         self::$identity_map[$class][$hash] = $this;
     }
     return $this;
 }
コード例 #14
0
 protected function configure()
 {
     fORMRelated::setOrderBys($this, 'WpTesting_Model_Answer', array(WPT_DB_PREFIX . 'answers.answer_sort' => 'asc', WPT_DB_PREFIX . 'answers.answer_id' => 'asc'));
 }
コード例 #15
0
ファイル: fORMRelated.php プロジェクト: jsuarez/MyDesign
 /**
  * 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 = fORMRelated::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[] = self::compose('%sPlease select a %3$s', fValidationException::formatField(self::compose('%1$s #%2$s', $related_record_name, $record_number)), $related_record_name);
         }
         $record_number++;
     }
     return $messages;
 }