Ejemplo n.º 1
0
 protected function populateRelated($recursive = false)
 {
     if ($recursive) {
         $one_to_many_relationships = $schema->getRelationships($table, 'one-to-many');
         foreach ($one_to_many_relationships as $relationship) {
             $route_name = fORMSchema::getRouteNameFromRelationship('one-to-many', $relationship);
             $related_class = fORM::classize($relationship['related_table']);
             $method = 'populate' . fGrammar::pluralize($related_class);
             $this->{$method}(TRUE, $route_name);
         }
         $one_to_one_relationships = $schema->getRelationships($table, 'one-to-one');
         foreach ($one_to_one_relationships as $relationship) {
             $route_name = fORMSchema::getRouteNameFromRelationship('one-to-one', $relationship);
             $related_class = fORM::classize($relationship['related_table']);
             $this->__call('populate' . $related_class, array(TRUE, $route_name));
         }
     }
     return $this;
 }
Ejemplo n.º 2
0
 /**
  * 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;
 }
Ejemplo n.º 3
0
 /**
  * Adds information about methods provided by this class to fActiveRecord
  *
  * @internal
  *
  * @param  string  $class                 The class to reflect the related record methods for
  * @param  array   &$signatures           The associative array of `{method_name} => {signature}`
  * @param  boolean $include_doc_comments  If the doc block comments for each method should be included
  * @return void
  */
 public static function reflect($class, &$signatures, $include_doc_comments)
 {
     $table = fORM::tablize($class);
     $schema = fORMSchema::retrieve($class);
     $one_to_one_relationships = $schema->getRelationships($table, 'one-to-one');
     $one_to_many_relationships = $schema->getRelationships($table, 'one-to-many');
     $many_to_one_relationships = $schema->getRelationships($table, 'many-to-one');
     $many_to_many_relationships = $schema->getRelationships($table, 'many-to-many');
     $to_one_relationships = array_merge($one_to_one_relationships, $many_to_one_relationships);
     $to_many_relationships = array_merge($one_to_many_relationships, $many_to_many_relationships);
     $to_one_created = array();
     foreach ($to_one_relationships as $relationship) {
         $related_class = fORM::classize($relationship['related_table']);
         $related_class = fORM::getRelatedClass($class, $related_class);
         if (isset($to_one_created[$related_class])) {
             continue;
         }
         $routes = fORMSchema::getRoutes($schema, $table, $relationship['related_table'], '*-to-one');
         $route_names = array();
         foreach ($routes as $route) {
             $route_names[] = fORMSchema::getRouteNameFromRelationship('*-to-one', $route);
         }
         $signature = '';
         if ($include_doc_comments) {
             $signature .= "/**\n";
             $signature .= " * Creates the related " . $related_class . "\n";
             $signature .= " * \n";
             if (sizeof($route_names) > 1) {
                 $signature .= " * @param  string \$route  The route to the related class. Must be one of: '" . join("', '", $route_names) . "'.\n";
             }
             $signature .= " * @return " . $related_class . "  The related object\n";
             $signature .= " */\n";
         }
         $create_method = 'create' . $related_class;
         $signature .= 'public function ' . $create_method . '(';
         if (sizeof($route_names) > 1) {
             $signature .= '$route';
         }
         $signature .= ')';
         $signatures[$create_method] = $signature;
         $to_one_created[$related_class] = TRUE;
     }
     $one_to_one_created = array();
     foreach ($one_to_one_relationships as $relationship) {
         $related_class = fORM::classize($relationship['related_table']);
         $related_class = fORM::getRelatedClass($class, $related_class);
         if (isset($one_to_one_created[$related_class])) {
             continue;
         }
         $routes = fORMSchema::getRoutes($schema, $table, $relationship['related_table'], 'one-to-one');
         $route_names = array();
         foreach ($routes as $route) {
             $route_names[] = fORMSchema::getRouteNameFromRelationship('one-to-one', $route);
         }
         $signature = '';
         if ($include_doc_comments) {
             $signature .= "/**\n";
             $signature .= " * Populates the related " . $related_class . "\n";
             $signature .= " * \n";
             if (sizeof($route_names) > 1) {
                 $signature .= " * @param  string \$route  The route to the related class. Must be one of: '" . join("', '", $route_names) . "'.\n";
             }
             $signature .= " * @return fActiveRecord  The record object, to allow for method chaining\n";
             $signature .= " */\n";
         }
         $populate_method = 'populate' . $related_class;
         $signature .= 'public function ' . $populate_method . '(';
         if (sizeof($route_names) > 1) {
             $signature .= '$route';
         }
         $signature .= ')';
         $signatures[$populate_method] = $signature;
         $signature = '';
         if ($include_doc_comments) {
             $signature .= "/**\n";
             $signature .= " * Associates the related " . $related_class . " to this record\n";
             $signature .= " * \n";
             $signature .= " * @param  fActiveRecord|array|string|integer \$record  The record, or the primary key of the record, to associate\n";
             if (sizeof($route_names) > 1) {
                 $signature .= " * @param  string \$route  The route to the related class. Must be one of: '" . join("', '", $route_names) . "'.\n";
             }
             $signature .= " * @return fActiveRecord  The record object, to allow for method chaining\n";
             $signature .= " */\n";
         }
         $associate_method = 'associate' . $related_class;
         $signature .= 'public function ' . $associate_method . '($record';
         if (sizeof($route_names) > 1) {
             $signature .= ', $route';
         }
         $signature .= ')';
         $signatures[$associate_method] = $signature;
         $signature = '';
         if ($include_doc_comments) {
             $signature .= "/**\n";
             $signature .= " * Indicates if a related " . $related_class . " exists\n";
             $signature .= " * \n";
             if (sizeof($route_names) > 1) {
                 $signature .= " * @param  string \$route  The route to the related class. Must be one of: '" . join("', '", $route_names) . "'.\n";
             }
             $signature .= " * @return boolean  If a related record exists\n";
             $signature .= " */\n";
         }
         $has_method = 'has' . $related_class;
         $signature .= 'public function ' . $has_method . '($record';
         if (sizeof($route_names) > 1) {
             $signature .= ', $route';
         }
         $signature .= ')';
         $signatures[$has_method] = $signature;
         $one_to_one_created[$related_class] = TRUE;
     }
     $to_many_created = array();
     foreach ($to_many_relationships as $relationship) {
         $related_class = fORM::classize($relationship['related_table']);
         $related_class = fORM::getRelatedClass($class, $related_class);
         if (isset($to_many_created[$related_class])) {
             continue;
         }
         $routes = fORMSchema::getRoutes($schema, $table, $relationship['related_table'], '*-to-many');
         $route_names = array();
         $many_to_many_route_names = array();
         $one_to_many_route_names = array();
         foreach ($routes as $route) {
             if (isset($route['join_table'])) {
                 $route_name = fORMSchema::getRouteNameFromRelationship('many-to-many', $route);
                 $route_names[] = $route_name;
                 $many_to_many_route_names[] = $route_name;
             } else {
                 $route_name = fORMSchema::getRouteNameFromRelationship('one-to-many', $route);
                 $route_names[] = $route_name;
                 $one_to_many_route_names[] = $route_name;
             }
         }
         if ($one_to_many_route_names) {
             $signature = '';
             if ($include_doc_comments) {
                 $related_table = fORM::tablize($related_class);
                 $signature .= "/**\n";
                 $signature .= " * Calls the ::populate() method for multiple child " . $related_class . " records. Uses request value arrays in the form " . $related_table . "::{column_name}[].\n";
                 $signature .= " * \n";
                 if (sizeof($one_to_many_route_names) > 1) {
                     $signature .= " * @param  string \$route  The route to the related class. Must be one of: '" . join("', '", $one_to_many_route_names) . "'.\n";
                 }
                 $signature .= " * @return fActiveRecord  The record object, to allow for method chaining\n";
                 $signature .= " */\n";
             }
             $populate_related_method = 'populate' . fGrammar::pluralize($related_class);
             $signature .= 'public function ' . $populate_related_method . '(';
             if (sizeof($one_to_many_route_names) > 1) {
                 $signature .= '$route';
             }
             $signature .= ')';
             $signatures[$populate_related_method] = $signature;
         }
         if ($many_to_many_route_names) {
             $signature = '';
             if ($include_doc_comments) {
                 $related_table = fORM::tablize($related_class);
                 $signature .= "/**\n";
                 $signature .= " * Creates entries in the appropriate joining table to create associations with the specified " . $related_class . " records. Uses request value array(s) in the form " . $related_table . "::{primary_key_column_name(s)}[].\n";
                 $signature .= " * \n";
                 if (sizeof($many_to_many_route_names) > 1) {
                     $signature .= " * @param  string \$route  The route to the related class. Must be one of: '" . join("', '", $many_to_many_route_names) . "'.\n";
                 }
                 $signature .= " * @return fActiveRecord  The record object, to allow for method chaining\n";
                 $signature .= " */\n";
             }
             $link_related_method = 'link' . fGrammar::pluralize($related_class);
             $signature .= 'public function ' . $link_related_method . '(';
             if (sizeof($many_to_many_route_names) > 1) {
                 $signature .= '$route';
             }
             $signature .= ')';
             $signatures[$link_related_method] = $signature;
             $signature = '';
             if ($include_doc_comments) {
                 $related_table = fORM::tablize($related_class);
                 $signature .= "/**\n";
                 $signature .= " * Creates entries in the appropriate joining table to create associations with the specified " . $related_class . " records\n";
                 $signature .= " * \n";
                 $signature .= " * @param  fRecordSet|array \$records_to_associate  The records to associate - should be an fRecords, an array of records or an array of primary keys\n";
                 if (sizeof($many_to_many_route_names) > 1) {
                     $signature .= " * @param  string           \$route  The route to the related class. Must be one of: '" . join("', '", $many_to_many_route_names) . "'.\n";
                 }
                 $signature .= " * @return fActiveRecord  The record object, to allow for method chaining\n";
                 $signature .= " */\n";
             }
             $associate_related_method = 'associate' . fGrammar::pluralize($related_class);
             $signature .= 'public function ' . $associate_related_method . '($records_to_associate';
             if (sizeof($many_to_many_route_names) > 1) {
                 $signature .= ', $route';
             }
             $signature .= ')';
             $signatures[$associate_related_method] = $signature;
         }
         $signature = '';
         if ($include_doc_comments) {
             $signature .= "/**\n";
             $signature .= " * Builds an fRecordSet of the related " . $related_class . " objects\n";
             $signature .= " * \n";
             if (sizeof($route_names) > 1) {
                 $signature .= " * @param  string \$route  The route to the related class. Must be one of: '" . join("', '", $route_names) . "'.\n";
             }
             $signature .= " * @return fRecordSet  A record set of the related " . $related_class . " objects\n";
             $signature .= " */\n";
         }
         $build_method = 'build' . fGrammar::pluralize($related_class);
         $signature .= 'public function ' . $build_method . '(';
         if (sizeof($route_names) > 1) {
             $signature .= '$route';
         }
         $signature .= ')';
         $signatures[$build_method] = $signature;
         $signature = '';
         if ($include_doc_comments) {
             $signature .= "/**\n";
             $signature .= " * Indicates if related " . $related_class . " objects exist\n";
             $signature .= " * \n";
             if (sizeof($route_names) > 1) {
                 $signature .= " * @param  string \$route  The route to the related class. Must be one of: '" . join("', '", $route_names) . "'.\n";
             }
             $signature .= " * @return boolean  If related " . $related_class . " objects exist\n";
             $signature .= " */\n";
         }
         $has_method = 'has' . fGrammar::pluralize($related_class);
         $signature .= 'public function ' . $has_method . '(';
         if (sizeof($route_names) > 1) {
             $signature .= '$route';
         }
         $signature .= ')';
         $signatures[$has_method] = $signature;
         $signature = '';
         if ($include_doc_comments) {
             $signature .= "/**\n";
             $signature .= " * Returns an array of the primary keys for the related " . $related_class . " objects\n";
             $signature .= " * \n";
             if (sizeof($route_names) > 1) {
                 $signature .= " * @param  string \$route  The route to the related class. Must be one of: '" . join("', '", $route_names) . "'.\n";
             }
             $signature .= " * @return array  The primary keys of the related " . $related_class . " objects\n";
             $signature .= " */\n";
         }
         $list_method = 'list' . fGrammar::pluralize($related_class);
         $signature .= 'public function ' . $list_method . '(';
         if (sizeof($route_names) > 1) {
             $signature .= '$route';
         }
         $signature .= ')';
         $signatures[$list_method] = $signature;
         $signature = '';
         if ($include_doc_comments) {
             $signature .= "/**\n";
             $signature .= " * Counts the number of related " . $related_class . " objects\n";
             $signature .= " * \n";
             if (sizeof($route_names) > 1) {
                 $signature .= " * @param  string \$route  The route to the related class. Must be one of: '" . join("', '", $route_names) . "'.\n";
             }
             $signature .= " * @return integer  The number related " . $related_class . " objects\n";
             $signature .= " */\n";
         }
         $count_method = 'count' . fGrammar::pluralize($related_class);
         $signature .= 'public function ' . $count_method . '(';
         if (sizeof($route_names) > 1) {
             $signature .= '$route';
         }
         $signature .= ')';
         $signatures[$count_method] = $signature;
         $to_many_created[$related_class] = TRUE;
     }
 }
Ejemplo n.º 4
0
 /**
  * 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);
 }
Ejemplo n.º 5
0
 /**
  * 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))));
 }
Ejemplo n.º 6
0
 /**
  * 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];
 }
Ejemplo n.º 7
0
 /**
  * @dataProvider singularizePluralizeProvider
  */
 public function testPluralizer($output, $input)
 {
     $this->assertEquals($output, fGrammar::pluralize($input));
 }
Ejemplo n.º 8
0
 /**
  * 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];
 }