/system-apps/includes/ringside/apps/model/active_record.php
PHP | 3257 lines | 1738 code | 201 blank | 1318 comment | 334 complexity | 893da4bfcc491e9314594838d95fdb3f MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0
Large files files are truncated, but you can click here to view the full file
- <?php
- /**
- * File containing the ActiveRecord class
- *
- * (PHP 5)
- *
- * @package PHPonTrax
- * @version $Id: active_record.php 283 2007-02-17 08:54:28Z john $
- * @copyright (c) 2005 John Peterson
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- /**
- * Load the {@link http://pear.php.net/manual/en/package.pear.php PEAR base class}
- */
- require_once('PEAR.php');
- /**
- * Load the {@link http://pear.php.net/manual/en/package.database.mdb2.php PEAR MDB2 package}
- * PEAR::DB is now deprecated.
- * (This package(DB) been superseded by MDB2 but is still maintained for bugs and security fixes)
- */
- require_once('MDB2.php');
- /**
- * Base class for the ActiveRecord design pattern
- *
- * <p>Each subclass of this class is associated with a database table
- * in the Model section of the Model-View-Controller architecture.
- * By convention, the name of each subclass is the CamelCase singular
- * form of the table name, which is in the lower_case_underscore
- * plural notation. For example,
- * a table named "order_details" would be associated with a subclass
- * of ActiveRecord named "OrderDetail", and a table named "people"
- * would be associated with subclass "Person". See the tutorial
- * {@tutorial PHPonTrax/naming.pkg}</p>
- *
- * <p>For a discussion of the ActiveRecord design pattern, see
- * "Patterns of Enterprise
- * Application Architecture" by Martin Fowler, pp. 160-164.</p>
- *
- * <p>Unit tester: {@link ActiveRecordTest}</p>
- *
- * @tutorial PHPonTrax/ActiveRecord.cls
- */
- class ActiveRecord {
- /**
- * Reference to the database object
- *
- * Reference to the database object returned by
- * {@link http://pear.php.net/manual/en/package.database.mdb2.intro-connect.php PEAR MDB2::Connect()}
- * @var object DB
- * see
- * {@link http://pear.php.net/manual/en/package.database.mdb2.php PEAR MDB2}
- */
- protected static $db = null;
- /**
- * Description of a row in the associated table in the database
- *
- * <p>Retrieved from the RDBMS by {@link set_content_columns()}.
- * See {@link
- * http://pear.php.net/package/MDB2/docs/2.3.0/MDB2/MDB2_Driver_Reverse_Common.html#methodtableInfo
- * DB_common::tableInfo()} for the format. <b>NOTE:</b> Some
- * RDBMS's don't return all values.</p>
- *
- * <p>An additional element 'human_name' is added to each column
- * by {@link set_content_columns()}. The actual value contained
- * in each column is stored in an object variable with the name
- * given by the 'name' element of the column description for each
- * column.</p>
- *
- * <p><b>NOTE:</b>The information from the database about which
- * columns are primary keys is <b>not used</b>. Instead, the
- * primary keys in the table are listed in {@link $primary_keys},
- * which is maintained independently.</p>
- * @var string[]
- * @see $primary_keys
- * @see quoted_attributes()
- * @see __set()
- */
- public $content_columns = null; # info about each column in the table
- /**
- * Table Info
- *
- * Array to hold all the info about table columns. Indexed on $table_name.
- * @var array
- */
- public static $table_info = array();
- /**
- * Class name
- *
- * Name of the child class. (this is optional and will automatically be determined)
- * Normally set to the singular camel case form of the table name.
- * May be overridden.
- * @var string
- */
- public $class_name = null;
- /**
- * Table name
- *
- * Name of the table in the database associated with the subclass.
- * Normally set to the pluralized lower case underscore form of
- * the class name by the constructor. May be overridden.
- * @var string
- */
- public $table_name = null;
-
- /**
- * Table prefix
- *
- * Name to prefix to the $table_name. May be overridden.
- * @var string
- */
- public $table_prefix = null;
- /**
- * Database name override
- *
- * Name of the database to use, if you are not using the value
- * read from file config/database.ini
- * @var string
- */
- public $database_name = null;
-
- /**
- * Index into the $active_connections array
- *
- * Name of the index to use to return or set the current db connection
- * Mainly used if you want to connect to different databases between
- * different models.
- * @var string
- */
- public $connection_name = TRAX_ENV;
- /**
- * Stores the database settings
- */
- public static $database_settings = array();
-
- /**
- * Stores the active connections. Indexed on $connection_name.
- */
- public static $active_connections = array();
- /**
- * Mode to use when fetching data from database
- *
- * See {@link
- * http://pear.php.net/package/MDB2/docs/2.3.0/MDB2/MDB2_Driver_Common.html#methodsetFetchMode
- * the relevant PEAR MDB2 class documentation}
- * @var integer
- */
- public $fetch_mode = MDB2_FETCHMODE_ASSOC;
- /**
- * Force reconnect to database every page load
- *
- * @var boolean
- */
- public $force_reconnect = false;
- /**
- * find_all() returns an array of objects,
- * each object index is off of this field
- *
- * @var string
- */
- public $index_on = "id";
- /**
- * Not yet implemented (page 222 Rails books)
- *
- * @var boolean
- */
- public $lock_optimistically = true;
-
- /**
- * Composite custom user created objects
- * @var mixed
- */
- public $composed_of = null;
- # Table associations
- /**
- * @todo Document this variable
- * @var string[]
- */
- protected $has_many = null;
- /**
- * @todo Document this variable
- * @var string[]
- */
- protected $has_one = null;
- /**
- * @todo Document this variable
- * @var string[]
- */
- protected $has_and_belongs_to_many = null;
- /**
- * @todo Document this variable
- * @var string[]
- */
- protected $belongs_to = null;
- /**
- * @todo Document this variable
- * @var string[]
- */
- protected $habtm_attributes = null;
- /**
- * @todo Document this property
- */
- protected $save_associations = array();
-
- /**
- * Whether or not to auto save defined associations if set
- * @var boolean
- */
- public $auto_save_associations = true;
- /**
- * Whether this object represents a new record
- *
- * true => This object was created without reading a row from the
- * database, so use SQL 'INSERT' to put it in the database.
- * false => This object was a row read from the database, so use
- * SQL 'UPDATE' to update database with new values.
- * @var boolean
- */
- protected $new_record = true;
- /**
- * Names of automatic update timestamp columns
- *
- * When a row containing one of these columns is updated and
- * {@link $auto_timestamps} is true, update the contents of the
- * timestamp columns with the current date and time.
- * @see $auto_timestamps
- * @see $auto_create_timestamps
- * @var string[]
- */
- public $auto_update_timestamps = array("updated_at","updated_on");
- /**
- * Names of automatic create timestamp columns
- *
- * When a row containing one of these columns is created and
- * {@link $auto_timestamps} is true, store the current date and
- * time in the timestamp columns.
- * @see $auto_timestamps
- * @see $auto_update_timestamps
- * @var string[]
- */
- public $auto_create_timestamps = array("created_at","created_on");
- /**
- * Date format for use with auto timestamping
- *
- * The format for this should be compatiable with the php date() function.
- * http://www.php.net/date
- * @var string
- */
- public $date_format = "Y-m-d";
- /**
- * Time format for use with auto timestamping
- *
- * The format for this should be compatiable with the php date() function.
- * http://www.php.net/date
- * @var string
- */
- public $time_format = "H:i:s";
-
- /**
- * Whether to keep date/datetime fields NULL if not set
- *
- * true => If date field is not set it try to preserve NULL
- * false => Don't try to preserve NULL if field is already NULL
- * @var boolean
- */
- protected $preserve_null_dates = true;
- /**
- * SQL aggregate functions that may be applied to the associated
- * table.
- *
- * SQL defines aggregate functions AVG, COUNT, MAX, MIN and SUM.
- * Not all of these functions are implemented by all DBMS's
- * @var string[]
- */
- protected $aggregations = array("count","sum","avg","max","min");
-
- /**
- * Primary key of the associated table
- *
- * Array element(s) name the primary key column(s), as used to
- * specify the row to be updated or deleted. To be a primary key
- * a column must be listed both here and in {@link
- * $content_columns}. <b>NOTE:</b>This
- * field is maintained by hand. It is not derived from the table
- * description read from the database.
- * @var string[]
- * @see $content_columns
- * @see find()
- * @see find_all()
- * @see find_first()
- */
- public $primary_keys = array("id");
- /**
- * Default for how many rows to return from {@link find_all()}
- * @var integer
- */
- public $rows_per_page_default = 20;
- /**
- * Pagination how many numbers in the list << < 1 2 3 4 > >>
- */
- public $display = 10;
- /**
- * @todo Document this variable
- */
- public $pagination_count = 0;
- /**
- * Description of non-fatal errors found
- *
- * For every non-fatal error found, an element describing the
- * error is added to $errors. Initialized to an empty array in
- * {@link valid()} before validating object. When an error
- * message is associated with a particular attribute, the message
- * should be stored with the attribute name as its key. If the
- * message is independent of attributes, store it with a numeric
- * key beginning with 0.
- *
- * @var string[]
- * @see add_error()
- * @see get_errors()
- */
- public $errors = array();
- /**
- * An array with all the default error messages.
- */
- public $default_error_messages = array(
- 'inclusion' => "is not included in the list",
- 'exclusion' => "is reserved",
- 'invalid' => "is invalid",
- 'confirmation' => "doesn't match confirmation",
- 'accepted ' => "must be accepted",
- 'empty' => "can't be empty",
- 'blank' => "can't be blank",
- 'too_long' => "is too long (max is %d characters)",
- 'too_short' => "is too short (min is %d characters)",
- 'wrong_length' => "is the wrong length (should be %d characters)",
- 'taken' => "has already been taken",
- 'not_a_number' => "is not a number",
- 'not_an_integer' => "is not an integer"
- );
- /**
- * An array of all the builtin validation function calls.
- */
- protected $builtin_validation_functions = array(
- 'validates_acceptance_of',
- 'validates_confirmation_of',
- 'validates_exclusion_of',
- 'validates_format_of',
- 'validates_inclusion_of',
- 'validates_length_of',
- 'validates_numericality_of',
- 'validates_presence_of',
- 'validates_uniqueness_of'
- );
- /**
- * Whether to automatically update timestamps in certain columns
- *
- * @see $auto_create_timestamps
- * @see $auto_update_timestamps
- * @var boolean
- */
- public $auto_timestamps = true;
- /**
- * Auto insert / update $has_and_belongs_to_many tables
- */
- public $auto_save_habtm = true;
- /**
- * Auto delete $has_and_belongs_to_many associations
- */
- public $auto_delete_habtm = true;
- /**
- * Transactions (only use if your db supports it)
- * This is for transactions only to let query() know that a 'BEGIN' has been executed
- */
- private static $begin_executed = false;
- /**
- * Transactions (only use if your db supports it)
- * This will issue a rollback command if any sql fails.
- */
- public static $use_transactions = false;
-
- /**
- * Keep a log of queries executed if in development env
- */
- public static $query_log = array();
- /**
- * Construct an ActiveRecord object
- *
- * <ol>
- * <li>Establish a connection to the database</li>
- * <li>Find the name of the table associated with this object</li>
- * <li>Read description of this table from the database</li>
- * <li>Optionally apply update information to column attributes</li>
- * </ol>
- * @param string[] $attributes Updates to column attributes
- * @uses establish_connection()
- * @uses set_content_columns()
- * @uses $table_name
- * @uses set_table_name_using_class_name()
- * @uses update_attributes()
- */
- function __construct($attributes = null) {
- # Open the database connection
- $this->establish_connection();
- # Set $table_name
- if($this->table_name == null) {
- $this->set_table_name_using_class_name();
- }
- # Set column info
- if($this->table_name) {
- $this->set_content_columns($this->table_name);
- }
- # If $attributes array is passed in update the class with its contents
- if(!is_null($attributes)) {
- $this->update_attributes($attributes);
- }
-
- # If callback is defined in model run it.
- # this could hurt performance...
- if(method_exists($this, 'after_initialize')) {
- $this->after_initialize();
- }
- }
- /**
- * Override get() if they do $model->some_association->field_name
- * dynamically load the requested contents from the database.
- * @todo Document this API
- * @uses $belongs_to
- * @uses get_association_type()
- * @uses $has_and_belongs_to_many
- * @uses $has_many
- * @uses $has_one
- * @uses find_all_has_many()
- * @uses find_all_habtm()
- * @uses find_one_belongs_to()
- * @uses find_one_has_one()
- */
- function __get($key) {
- if($association_type = $this->get_association_type($key)) {
- //error_log("association_type:$association_type");
- switch($association_type) {
- case "has_many":
- //print("DOING HASMANY $key\n");
- $parameters = is_array($this->has_many) ? $this->has_many[$key] : null;
- //var_dump($parameters);
- $this->$key = $this->find_all_has_many($key, $parameters);
- //var_dump($this->$key);
- break;
- case "has_one":
- $parameters = is_array($this->has_one) ? $this->has_one[$key] : null;
- $this->$key = $this->find_one_has_one($key, $parameters);
- break;
- case "belongs_to":
- $parameters = is_array($this->belongs_to) ? $this->belongs_to[$key] : null;
- $this->$key = $this->find_one_belongs_to($key, $parameters);
- break;
- case "has_and_belongs_to_many":
- $parameters = is_array($this->has_and_belongs_to_many) ? $this->has_and_belongs_to_many[$key] : null;
- $this->$key = $this->find_all_habtm($key, $parameters);
- break;
- }
- } elseif($this->is_composite($key)) {
- $composite_object = $this->get_composite_object($key);
- if(is_object($composite_object)) {
- $this->$key = $composite_object;
- }
- }
- //echo "<pre>getting: $key = ".$this->$key."<br></pre>";
- return $this->$key;
- }
- /**
- * Store column value or description of the table format
- *
- * If called with key 'table_name', $value is stored as the
- * description of the table format in $content_columns.
- * Any other key causes an object variable with the same name to
- * be created and stored into. If the value of $key matches the
- * name of a column in content_columns, the corresponding object
- * variable becomes the content of the column in this row.
- * @uses $auto_save_associations
- * @uses get_association_type()
- * @uses set_content_columns()
- */
- function __set($key, $value) {
- //echo "setting: $key = $value<br>";
- if($key == "table_name") {
- $this->set_content_columns($value);
- # this elseif checks if first its an object if its parent is ActiveRecord
- } elseif(is_object($value) && get_parent_class($value) == __CLASS__ && $this->auto_save_associations) {
- if($association_type = $this->get_association_type($key)) {
- $this->save_associations[$association_type][] = $value;
- if($association_type == "belongs_to") {
- $primary_key = $value->primary_keys[0];
- $foreign_key = Inflector::singularize($value->table_name)."_".$primary_key;
- $this->$foreign_key = $value->$primary_key;
- }
- }
- # this elseif checks if its an array of objects and if its parent is ActiveRecord
- } elseif(is_array($value) && $this->auto_save_associations) {
- if($association_type = $this->get_association_type($key)) {
- $this->save_associations[$association_type][] = $value;
- }
- }
-
- // Assignment to something else, do it
- $this->$key = $value;
- }
- /**
- * Override call() to dynamically call the database associations
- * @todo Document this API
- * @uses $aggregations
- * @uses aggregate_all()
- * @uses get_association_type()
- * @uses $belongs_to
- * @uses $has_one
- * @uses $has_and_belongs_to_many
- * @uses $has_many
- * @uses find_all_by()
- * @uses find_by()
- */
- function __call($method_name, $parameters) {
- if(method_exists($this, $method_name)) {
- # If the method exists, just call it
- $result = call_user_func_array(array($this, $method_name), $parameters);
- } else {
- # ... otherwise, check to see if the method call is one of our
- # special Trax methods ...
- # ... first check for method names that match any of our explicitly
- # declared associations for this model ( e.g. public $has_many = "movies" ) ...
- if(is_array($parameters[0])) {
- $parameters = $parameters[0];
- }
- $association_type = $this->get_association_type($method_name);
- switch($association_type) {
- case "has_many":
- $result = $this->find_all_has_many($method_name, $parameters);
- break;
- case "has_one":
- $result = $this->find_one_has_one($method_name, $parameters);
- break;
- case "belongs_to":
- $result = $this->find_one_belongs_to($method_name, $parameters);
- break;
- case "has_and_belongs_to_many":
- $result = $this->find_all_habtm($method_name, $parameters);
- break;
- }
- # check for the [count,sum,avg,etc...]_all magic functions
- if(substr($method_name, -4) == "_all" && in_array(substr($method_name, 0, -4), $this->aggregations)) {
- //echo "calling method: $method_name<br>";
- $result = $this->aggregate_all($method_name, $parameters);
- }
- # check for the find_all_by_* magic functions
- elseif(strlen($method_name) > 11 && substr($method_name, 0, 11) == "find_all_by") {
- //echo "calling method: $method_name<br>";
- $result = $this->find_by($method_name, $parameters, "all");
- }
- # check for the find_by_* magic functions
- elseif(strlen($method_name) > 7 && substr($method_name, 0, 7) == "find_by") {
- //echo "calling method: $method_name<br>";
- $result = $this->find_by($method_name, $parameters);
- }
- # check for find_or_create_by_* magic functions
- elseif(strlen($method_name) > 17 && substr($method_name, 0, 17) == "find_or_create_by") {
- $result = $this->find_by($method_name, $parameters, "find_or_create");
- }
- }
- return $result;
- }
-
- /**
- * Find all records using a "has_and_belongs_to_many" relationship
- * (many-to-many with a join table in between). Note that you can also
- * specify an optional "paging limit" by setting the corresponding "limit"
- * instance variable. For example, if you want to return 10 movies from the
- * 5th movie on, you could set $this->movies_limit = "10, 5"
- *
- * Parameters: $this_table_name: The name of the database table that has the
- * one row you are interested in. E.g. genres
- * $other_table_name: The name of the database table that has the
- * many rows you are interested in. E.g. movies
- * Returns: An array of ActiveRecord objects. (e.g. Movie objects)
- * @todo Document this API
- */
- private function find_all_habtm($other_table_name, $parameters = null) {
- $additional_conditions = null;
- # Use any passed-in parameters
- if(!is_null($parameters)) {
- if(@array_key_exists("conditions", $parameters)) {
- $additional_conditions = " AND (".$parameters['conditions'].")";
- } elseif($parameters[0] != "") {
- $additional_conditions = " AND (".$parameters[0].")";
- }
- if(@array_key_exists("order", $parameters)) {
- $order = $parameters['order'];
- } elseif($parameters[1] != "") {
- $order = $parameters[1];
- }
- if(@array_key_exists("limit", $parameters)) {
- $limit = $parameters['limit'];
- } elseif($parameters[2] != "") {
- $limit = $parameters[2];
- }
- if(@array_key_exists("class_name", $parameters)) {
- $other_object_name = $parameters['class_name'];
- }
- if(@array_key_exists("join_table", $parameters)) {
- $join_table = $parameters['join_table'];
- }
- if(@array_key_exists("foreign_key", $parameters)) {
- $this_foreign_key = $parameters['foreign_key'];
- }
- if(@array_key_exists("association_foreign_key", $parameters)) {
- $other_foreign_key = $parameters['association_foreign_key'];
- }
- if(@array_key_exists("finder_sql", $parameters)) {
- $finder_sql = $parameters['finder_sql'];
- }
- }
-
- if(!is_null($other_object_name)) {
- $other_class_name = Inflector::camelize($other_object_name);
- $other_table_name = Inflector::tableize($other_object_name);
- } else {
- $other_class_name = Inflector::classify($other_table_name);
- }
-
- # Instantiate an object to access find_all
- $other_class_object = new $other_class_name();
- # If finder_sql is specified just use it instead of determining the joins/sql
- if(!is_null($finder_sql)) {
- $conditions = $finder_sql;
- $order = null;
- $limit = null;
- $joins = null;
- } else {
- # Prepare the join table name primary keys (fields) to do the join on
- if(is_null($join_table)) {
- $join_table = $this->get_join_table_name($this->table_name, $other_table_name);
- }
-
- # Primary keys
- $this_primary_key = $this->primary_keys[0];
- $other_primary_key = $other_class_object->primary_keys[0];
-
- # Foreign keys
- if(is_null($this_foreign_key)) {
- $this_foreign_key = Inflector::singularize($this->table_name)."_".$this_primary_key;
- }
- if(is_null($other_foreign_key)) {
- $other_foreign_key = Inflector::singularize($other_table_name)."_".$other_primary_key;
- }
-
- # Primary key value
- if($this->attribute_is_string($this_primary_key)) {
- $this_primary_key_value = "'".$this->$this_primary_key."'";
- } elseif(is_numeric($this->$this_primary_key)) {
- $this_primary_key_value = $this->$this_primary_key;
- } else {
- #$this_primary_key_value = 0;
- # no primary key value so just return empty array same as find_all()
- return array();
- }
- # Set up the SQL segments
- $conditions = "{$join_table}.{$this_foreign_key} = {$this_primary_key_value}".$additional_conditions;
- $joins = "LEFT JOIN {$join_table} ON {$other_table_name}.{$other_primary_key} = {$join_table}.{$other_foreign_key}";
- }
-
- # Get the list of other_class_name objects
- return $other_class_object->find_all($conditions, $order, $limit, $joins);
- }
- /**
- * Find all records using a "has_many" relationship (one-to-many)
- *
- * Parameters: $other_table_name: The name of the other table that contains
- * many rows relating to this object's id.
- * Returns: An array of ActiveRecord objects. (e.g. Contact objects)
- * @todo Document this API
- */
- private function find_all_has_many($other_table_name, $parameters = null) {
- $additional_conditions = null;
- # Use any passed-in parameters
- if(is_array($parameters)) {
- if(@array_key_exists("conditions", $parameters)) {
- $additional_conditions = " AND (".$parameters['conditions'].")";
- } elseif($parameters[0] != "") {
- $additional_conditions = " AND (".$parameters[0].")";
- }
- if(@array_key_exists("order", $parameters)) {
- $order = $parameters['order'];
- } elseif($parameters[1] != "") {
- $order = $parameters[1];
- }
- if(@array_key_exists("limit", $parameters)) {
- $limit = $parameters['limit'];
- } elseif($parameters[2] != "") {
- $limit = $parameters[2];
- }
- if(@array_key_exists("foreign_key", $parameters)) {
- $foreign_key = $parameters['foreign_key'];
- }
- if(@array_key_exists("class_name", $parameters)) {
- $other_object_name = $parameters['class_name'];
- }
- if(@array_key_exists("finder_sql", $parameters)) {
- $finder_sql = $parameters['finder_sql'];
- }
- }
- if(!is_null($other_object_name)) {
- $other_class_name = Inflector::camelize($other_object_name);
- } else {
- $other_class_name = Inflector::classify($other_table_name);
- }
- # Instantiate an object to access find_all
- $other_class_object = new $other_class_name();
-
- # If finder_sql is specified just use it instead of determining the association
- if(!is_null($finder_sql)) {
- $conditions = $finder_sql;
- $order = null;
- $limit = null;
- $joins = null;
- } else {
- # This class primary key
- $this_primary_key = $this->primary_keys[0];
-
- if(!$foreign_key) {
- # this should end up being like user_id or account_id but if you specified
- # a primaray key other than 'id' it will be like user_field
- $foreign_key = Inflector::singularize($this->table_name)."_".$this_primary_key;
- }
- //print("FORGIN KEY= $foreign_key\n");
- $foreign_key_value = $this->$this_primary_key;
- //print("FORGIN KEY VALUE = $foreign_key_value\n");
- if($other_class_object->attribute_is_string($foreign_key)) {
- $conditions = "{$foreign_key} = '{$foreign_key_value}'";
- } elseif(is_numeric($foreign_key_value)) {
- $conditions = "{$foreign_key} = {$foreign_key_value}";
- } else {
- #$conditions = "{$foreign_key} = 0";
- # no primary key value so just return empty array same as find_all()
- return array();
- }
- $conditions .= $additional_conditions;
- }
-
- # Get the list of other_class_name objects
- return $other_class_object->find_all($conditions, $order, $limit, $joins);
- }
- /**
- * Find all records using a "has_one" relationship (one-to-one)
- * (the foreign key being in the other table)
- * Parameters: $other_table_name: The name of the other table that contains
- * many rows relating to this object's id.
- * Returns: An array of ActiveRecord objects. (e.g. Contact objects)
- * @todo Document this API
- */
- private function find_one_has_one($other_object_name, $parameters = null) {
- $additional_conditions = null;
- # Use any passed-in parameters
- if(is_array($parameters)) {
- //echo "<pre>";print_r($parameters);
- if(@array_key_exists("conditions", $parameters)) {
- $additional_conditions = " AND (".$parameters['conditions'].")";
- }
- // Removed, WREICHARDT
- //elseif($parameters[0] != "") {
- // $additional_conditions = " AND (".$parameters[0].")";
- //}
- if(@array_key_exists("order", $parameters)) {
- $order = $parameters['order'];
- } elseif($parameters[1] != "") {
- $order = $parameters[1];
- }
- if(@array_key_exists("foreign_key", $parameters)) {
- $foreign_key = $parameters['foreign_key'];
- }
- if(@array_key_exists("class_name", $parameters)) {
- $other_object_name = $parameters['class_name'];
- }
- }
-
- $other_class_name = Inflector::camelize($other_object_name);
-
- # Instantiate an object to access find_all
- $other_class_object = new $other_class_name();
- # This class primary key
- $this_primary_key = $this->primary_keys[0];
-
- if(!$foreign_key){
- $foreign_key = Inflector::singularize($this->table_name)."_".$this_primary_key;
- }
- $foreign_key_value = $this->$this_primary_key;
- if($other_class_object->attribute_is_string($foreign_key)) {
- $conditions = "{$foreign_key} = '{$foreign_key_value}'";
- } elseif(is_numeric($foreign_key_value)) {
- $conditions = "{$foreign_key} = {$foreign_key_value}";
- } else {
- #$conditions = "{$foreign_key} = 0";
- return null;
- }
- $conditions .= $additional_conditions;
-
- # Get the list of other_class_name objects
- return $other_class_object->find_first($conditions, $order);
- }
- /**
- * Find all records using a "belongs_to" relationship (one-to-one)
- * (the foreign key being in the table itself)
- * Parameters: $other_object_name: The singularized version of a table name.
- * E.g. If the Contact class belongs_to the
- * Customer class, then $other_object_name
- * will be "customer".
- * @todo Document this API
- */
- private function find_one_belongs_to($other_object_name, $parameters = null) {
- $additional_conditions = null;
- # Use any passed-in parameters
- if(is_array($parameters)) {
- //echo "<pre>";print_r($parameters);
- if(@array_key_exists("conditions", $parameters)) {
- $additional_conditions = " AND (".$parameters['conditions'].")";
- } elseif($parameters[0] != "") {
- $additional_conditions = " AND (".$parameters[0].")";
- }
- if(@array_key_exists("order", $parameters)) {
- $order = $parameters['order'];
- } elseif($parameters[1] != "") {
- $order = $parameters[1];
- }
- if(@array_key_exists("foreign_key", $parameters)) {
- $foreign_key = $parameters['foreign_key'];
- }
- if(@array_key_exists("class_name", $parameters)) {
- $other_object_name = $parameters['class_name'];
- }
- }
-
- $other_class_name = Inflector::camelize($other_object_name);
-
- # Instantiate an object to access find_all
- $other_class_object = new $other_class_name();
- # This class primary key
- $other_primary_key = $other_class_object->primary_keys[0];
- if(!$foreign_key) {
- $foreign_key = $other_object_name."_".$other_primary_key;
- }
-
- $other_primary_key_value = $this->$foreign_key;
- if($other_class_object->attribute_is_string($other_primary_key)) {
- $conditions = "{$other_primary_key} = '{$other_primary_key_value}'";
- } elseif(is_numeric($other_primary_key_value)) {
- $conditions = "{$other_primary_key} = {$other_primary_key_value}";
- } else {
- #$conditions = "{$other_primary_key} = 0";
- return null;
- }
- $conditions .= $additional_conditions;
-
- # Get the list of other_class_name objects
- return $other_class_object->find_first($conditions, $order);
- }
- /**
- * Implement *_all() functions (SQL aggregate functions)
- *
- * Apply one of the SQL aggregate functions to a column of the
- * table associated with this object. The SQL aggregate
- * functions are AVG, COUNT, MAX, MIN and SUM. Not all DBMS's
- * implement all of these functions.
- * @param string $agrregrate_type SQL aggregate function to
- * apply, suffixed '_all'. The aggregate function is one of
- * the strings in {@link $aggregations}.
- * @param string[] $parameters Conditions to apply to the
- * aggregate function. If present, must be an array of three
- * strings:<ol>
- * <li>$parameters[0]: If present, expression to apply
- * the aggregate function to. Otherwise, '*' will be used.
- * <b>NOTE:</b>SQL uses '*' only for the COUNT() function,
- * where it means "including rows with NULL in this column".</li>
- * <li>$parameters[1]: argument to WHERE clause</li>
- * <li>$parameters[2]: joins??? @todo Document this parameter</li>
- * </ol>
- * @throws {@link ActiveRecordError}
- * @uses query()
- * @uses is_error()
- */
- private function aggregate_all($aggregate_type, $parameters = null) {
- $aggregate_type = strtoupper(substr($aggregate_type, 0, -4));
- ($parameters[0]) ? $field = $parameters[0] : $field = "*";
- $sql = "SELECT {$aggregate_type}({$field}) AS agg_result FROM {$this->table_prefix}{$this->table_name} ";
-
- # Use any passed-in parameters
- if(is_array($parameters[1])) {
- extract($parameters[1]);
- } elseif(!is_null($parameters)) {
- $conditions = $parameters[1];
- $order = $parameters[2];
- $joins = $parameters[3];
- }
- if(!empty($joins)) $sql .= " $joins ";
- if(!empty($conditions)) $sql .= " WHERE $conditions ";
- if(!empty($order)) $sql .= " ORDER BY $order ";
- # echo "$aggregate_type sql:$sql<br>";
- if($this->is_error($rs = $this->query($sql))) {
- $this->raise($rs->getMessage());
- } else {
- $row = $rs->fetchRow();
- if($row["agg_result"]) {
- return $row["agg_result"];
- }
- }
- return 0;
- }
- /**
- * Returns a the name of the join table that would be used for the two
- * tables. The join table name is decided from the alphabetical order
- * of the two tables. e.g. "genres_movies" because "g" comes before "m"
- *
- * Parameters: $first_table, $second_table: the names of two database tables,
- * e.g. "movies" and "genres"
- * @todo Document this API
- */
- public function get_join_table_name($first_table, $second_table) {
- $tables = array($first_table, $second_table);
- @sort($tables);
- return $this->table_prefix.@implode("_", $tables);
- }
- /**
- * Test whether this object represents a new record
- * @uses $new_record
- * @return boolean Whether this object represents a new record
- */
- function is_new_record() {
- return $this->new_record;
- }
- /**
- * get the attributes for a specific column.
- * @uses $content_columns
- * @todo Document this API
- */
- function column_for_attribute($attribute) {
- if(is_array($this->content_columns)) {
- foreach($this->content_columns as $column) {
- if($column['name'] == $attribute) {
- return $column;
- }
- }
- }
- return null;
- }
- /**
- * get the columns data type.
- * @uses column_for_attribute()
- * @todo Document this API
- */
- function column_type($attribute) {
- $column = $this->column_for_attribute($attribute);
- if(isset($column['type'])) {
- return $column['type'];
- }
- return null;
- }
-
- /**
- * Check whether a column exists in the associated table
- *
- * When called, {@link $content_columns} lists the columns in
- * the table described by this object.
- * @param string Name of the column
- * @return boolean true=>the column exists; false=>it doesn't
- * @uses content_columns
- */
- function column_attribute_exists($attribute) {
- if(is_array($this->content_columns)) {
- foreach($this->content_columns as $column) {
- if($column['name'] == $attribute) {
- return true;
- }
- }
- }
- return false;
- }
- /**
- * Get contents of one column of record selected by id and table
- *
- * When called, {@link $id} identifies one record in the table
- * identified by {@link $table}. Fetch from the database the
- * contents of column $column of this record.
- * @param string Name of column to retrieve
- * @uses $db
- * @uses column_attribute_exists()
- * @throws {@link ActiveRecordError}
- * @uses is_error()
- */
- function send($column) {
- if($this->column_attribute_exists($column) && ($conditions = $this->get_primary_key_conditions())) {
- # Run the query to grab a specific columns value.
- $sql = "SELECT {$column} FROM {$this->table_prefix}{$this->table_name} WHERE {$conditions}";
- $this->log_query($sql);
- $result = self::$db->queryOne($sql);
- if($this->is_error($result)) {
- $this->raise($result->getMessage());
- }
- }
- return $result;
- }
- /**
- * Only used if you want to do transactions and your db supports transactions
- *
- * @uses $db
- * @todo Document this API
- */
- function begin() {
- self::$db->query("BEGIN");
- $this->begin_executed = true;
- }
- /**
- * Only used if you want to do transactions and your db supports transactions
- *
- * @uses $db
- * @todo Document this API
- */
- function commit() {
- self::$db->query("COMMIT");
- $this->begin_executed = false;
- }
- /**
- * Only used if you want to do transactions and your db supports transactions
- *
- * @uses $db
- * @todo Document this API
- */
- function rollback() {
- self::$db->query("ROLLBACK");
- }
- /**
- * Perform an SQL query and return the results
- *
- * @param string $sql SQL for the query command
- * @return $mdb2->query {@link http://pear.php.net/manual/en/package.database.mdb2.intro-query.php}
- * Result set from query
- * @uses $db
- * @uses is_error()
- * @uses log_query()
- * @throws {@link ActiveRecordError}
- */
- function query($sql) {
-
- //print($sql."\n");
- # Run the query
- $this->log_query($sql);
- $rs =& self::$db->query($sql);
- if ($this->is_error($rs)) {
- if(self::$use_transactions && self::$begin_executed) {
- $this->rollback();
- }
- $this->raise($rs->getMessage());
- }
- return $rs;
- }
- /**
- * Implement find_by_*() and =_* methods
- *
- * Converts a method name beginning 'find_by_' or 'find_all_by_'
- * into a query for rows matching the rest of the method name and
- * the arguments to the function. The part of the method name
- * after '_by' is parsed for columns and logical relationships
- * (AND and OR) to match. For example, the call
- * find_by_fname('Ben')
- * is converted to
- * SELECT * ... WHERE fname='Ben'
- * and the call
- * find_by_fname_and_lname('Ben','Dover')
- * is converted to
- * SELECT * ... WHERE fname='Ben' AND lname='Dover'
- *
- * @uses find_all()
- * @uses find_first()
- */
- private function find_by($method_name, $parameters, $find_type = null) {
- //print("find_by $method_name, $parameters, $find_type");
-
- if($find_type == "find_or_create") {
- $explode_len = 18;
- } elseif($find_type == "all") {
- $explode_len = 12;
- } else {
- $explode_len = 8;
- }
- $method_name = substr(strtolower($method_name), $explode_len);
- $method_parts = explode("|", str_replace("_and_", "|AND|", $method_name));
- if(count($method_parts)) {
- $options = array();
- $create_fields = array();
- $param_index = 0;
- foreach($method_parts as $part) {
- if($part == "AND") {
- $conditions .= " AND ";
- $param_index++;
- } else {
- $value = $this->attribute_is_string($part) ?
- "'".$parameters[$param_index]."'" :
- $parameters[$param_index];
- $create_fields[$part] = $parameters[$param_index];
- $conditions .= "{$part} = {$value}";
- }
- }
- # If last param exists and is a string set it as the ORDER BY clause
- # or if the last param is an array set it as the $options
- if($last_param = $parameters[++$param_index]) {
- if(is_string($last_param)) {
- $options['order'] = $last_param;
- } elseif(is_array($last_param)) {
- $options = $last_param;
- }
- }
- # Set the conditions
- if($options['conditions'] && $conditions) {
- $options['conditions'] = "(".$options['conditions'].") AND (".$conditions.")";
- } else {
- $options['conditions'] = $conditions;
- }
- # Now do the actual find with condtions from above
- if($find_type == "find_or_create") {
- # see if we can find a record with specified parameters
- $object = $this->find($options);
- if(is_object($object)) {
- # we found a record with the specified parameters so return it
- return $object;
- } elseif(count($create_fields)) {
- # can't find a record with specified parameters so create a new record
- # and return new object
- foreach($create_fields as $field => $value) {
- $this->$field = $value;
- }
- $this->save();
- return $this->find($options);
- }
- } elseif($find_type == "all") {
- return $this->find_all($options);
- } else {
- return $this->find($options);
- }
- }
- }
- /**
- * Builds a sql statement.
- *
- * @uses $rows_per_page_default
- * @uses $rows_per_page
- * @uses $offset
- * @uses $page
- *
- */
- function build_sql($conditions = null, $order = null, $limit = null, $joins = null) {
-
- $offset = null;
- $per_page = null;
- $select = null;
- # this is if they passed in an associative array to emulate
- # named parameters.
- if(is_array($conditions)) {
- if(@array_key_exists("per_page", $conditions) && !is_numeric($conditions['per_page'])) {
- extract($conditions);
- $per_page = 0;
- } else {
- extract($conditions);
- }
- # If conditions wasn't in the array set it to null
- if(is_array($conditions)) {
- $conditions = null;
- }
- }
- # Test source of SQL for query
- if(stristr($conditions, "SELECT")) {
- # SQL completely specified in argument so use it as is
- $sql = $conditions;
- } else {
- # If select fields not specified just do a SELECT *
- if(is_null($select)) {
- $select = "*";
- }
- # SQL will be built from specifications in argument
- $sql = "SELECT {$select} FROM {$this->table_prefix}{$this->table_name} ";
-
- # If join specified, include it
- if(!is_null($joins)) {
- $sql .= " $joins ";
- }
- # If conditions specified, include them
- if(!is_null($conditions)) {
- $sql .= "WHERE $conditions ";
- }
- # If ordering specified, include it
- if(!is_null($order)) {
- $sql .= "ORDER BY $order ";
- }
- # Is output to be generated in pages?
- if(is_numeric($limit) || is_numeric($offset) || is_numeric($per_page)) {
- if(is_numeric($limit)) {
- $this->rows_per_page = $limit;
- }
- if(is_numeric($per_page)) {
- $this->rows_per_page = $per_page;
- }
- # Default for rows_per_page:
- if ($this->rows_per_page <= 0) {
- $this->rows_per_page = $this->rows_per_page_default;
- }
-
- # Only use request's page if you are calling from find_all_with_pagination() and if it is int
- if(strval(intval($_REQUEST['page'])) == $_REQUEST['page']) {
- $this->page = $_REQUEST['page'];
- }
-
- if($this->page <= 0) {
- $this->page = 1;
- }
-
- # Set the LIMIT string segment for the SQL
- if(is_null($offset)) {
- $offset = ($this->page - 1) * $this->rows_per_page;
- }
- $sql .= "LIMIT {$this->rows_per_page} OFFSET {$offset}";
- # $sql .= "LIMIT $offset, $this->rows_per_page";
-
- # Set number of total pages in result set
- if($count = $this->count_all($this->primary_keys[0], $conditions, $joins)) {
- $this->pagination_count = $count;
- $this->pages = (($count % $this->rows_per_page) == 0)
- ? $count / $this->rows_per_page
- : floor($count / $this->rows_per_page) + 1;
- }
- }
- }
- //print("SQL:$sql\n");
- return $sql;
- }
- /**
- * Return rows selected by $conditions
- *
- * If no rows match, an empty array is returned.
- * @p…
Large files files are truncated, but you can click here to view the full file