Example #1
0
 static function sort(array &$dbRows, $field_name)
 {
     if (empty($field_name)) {
         throw new Exception('Must supply field name to compare by');
     }
     self::$compare_field_name = $field_name;
     usort($dbRows, array('Db_Utils', 'compare_dbRows'));
 }
Example #2
0
 /**
  * Dumps the result as a table in text mode
  * @method __toString
  */
 function __toString()
 {
     try {
         $ob = new Q_OutputBuffer();
         $results = array();
         foreach ($this->fields as $key => $value) {
             $results[] = array('Field name:' => $key, 'Field value:' => $value, 'Field modified:' => $this->wasModified($key) ? 'Yes' : 'No');
         }
         Db_Utils::dump_table($results);
         return $ob->getClean();
     } catch (Exception $e) {
         return $e->getMessage();
     }
 }
Example #3
0
    /**
     * Generates code for a base class for the model
     * @method codeForModelBaseClass
     * @param {string} $table The name of the table to generate the code for.
     * @param {string} $directory The path of the directory in which to place the model code.
     * @param {string} [$classname_prefix=''] The prefix to prepend to the generated class names
     * @param {&string} [$class_name_base=null] If set, this is the class name that is used.
     *  If an unset variable is passed, it is filled with the
     *  class name that is ultimately chosen, without the $classname_prefix
     * @param {string} [$prefix=null] Defaults to the prefix of the tables, as specified in the connection.
     *  Pass null here to use the default, or a string to override it.
     * @param {&string} [$js_code=null] The javascript code for the base class
     * @param {&string} [$table_comment=''] The comment from the MySQL table if any
     * @return {string} The generated code for the class.
     */
    function codeForModelBaseClass($table_name, $directory, $classname_prefix = '', &$class_name_base = null, $prefix = null, &$js_code = null, &$table_comment = '')
    {
        $dc = '/**';
        if (empty($table_name)) {
            throw new Exception('table_name parameter is empty', -2);
        }
        if (empty($directory)) {
            throw new Exception('directory parameter is empty', -3);
        }
        $connectionName = $this->connectionName();
        $conn = Db::getConnection($connectionName);
        if (!isset($prefix)) {
            $prefix = empty($conn['prefix']) ? '' : $conn['prefix'];
        }
        if (!empty($prefix)) {
            $prefix_len = strlen($prefix);
            $table_name_base = substr($table_name, $prefix_len);
            $table_name_prefix = substr($table_name, 0, $prefix_len);
            if (empty($table_name_base) or $table_name_prefix != $prefix) {
                return '';
            }
            // no class generated
        } else {
            $table_name_base = $table_name;
        }
        if (empty($classname_prefix)) {
            $classname_prefix = '';
        }
        if (!isset($class_name_base)) {
            $class_name_base = Db::generateTableClassName($table_name_base);
        }
        $class_name = ucfirst($classname_prefix) . $class_name_base;
        $table_cols = $this->rawQuery("SHOW COLUMNS FROM {$table_name}")->execute()->fetchAll(PDO::FETCH_ASSOC);
        $table_status = $this->rawQuery("SHOW TABLE STATUS WHERE Name = '{$table_name}'")->execute()->fetchAll(PDO::FETCH_COLUMN, 17);
        $table_comment = !empty($table_status[0]) ? " * <br/>{$table_status[0]}\n" : '';
        // Calculate primary key
        $pk = array();
        foreach ($table_cols as $table_col) {
            if ($table_col['Key'] == 'PRI') {
                $pk[] = $table_col['Field'];
            }
        }
        $pk_exported = var_export($pk, true);
        $pk_json = json_encode($pk);
        // Calculate validation functions
        $functions = array();
        $js_functions = array();
        $field_names = array();
        $properties = array();
        $js_properties = array();
        $required_field_names = array();
        $magic_field_names = array();
        foreach ($table_cols as $table_col) {
            $is_magic_field = null;
            $field_name = $table_col['Field'];
            $field_names[] = $field_name;
            $field_null = $table_col['Null'] == 'YES' ? true : false;
            $field_default = $table_col['Default'];
            $auto_inc = strpos($table_col['Extra'], 'auto_increment') !== false;
            $type = $table_col['Type'];
            $pieces = explode('(', $type);
            if (isset($pieces[1])) {
                $pieces2 = explode(')', $pieces[1]);
                $pieces2_count = count($pieces2);
                if ($pieces2_count > 2) {
                    $pieces2 = array(implode(')', array_slice($pieces2, 0, -1)), end($pieces2));
                }
            }
            $type_name = $pieces[0];
            if (isset($pieces2)) {
                $type_display_range = $pieces2[0];
                $type_modifiers = $pieces2[1];
                $type_unsigned = strpos($type_modifiers, 'unsigned') !== false;
            }
            $isTextLike = false;
            $isNumberLike = false;
            $isTimeLike = false;
            switch ($type_name) {
                case 'tinyint':
                    $type_range_min = $type_unsigned ? 0 : -128;
                    $type_range_max = $type_unsigned ? 255 : 127;
                    break;
                case 'smallint':
                    $type_range_min = $type_unsigned ? 0 : -32768;
                    $type_range_max = $type_unsigned ? 65535 : 32767;
                    break;
                case 'mediumint':
                    $type_range_min = $type_unsigned ? 0 : -8388608;
                    $type_range_max = $type_unsigned ? 16777215 : 8388607;
                    break;
                case 'int':
                    $type_range_min = $type_unsigned ? 0 : -2147483648;
                    $type_range_max = $type_unsigned ? 4294967295 : 2147483647;
                    break;
                case 'bigint':
                    $type_range_min = $type_unsigned ? 0 : -9.223372036854776E+18;
                    $type_range_max = $type_unsigned ? 1.8446744073709552E+19 : 9223372036854775807;
                    break;
                case 'tinytext':
                    $type_display_range = 255;
                    break;
                case 'text':
                    $type_display_range = 65535;
                    break;
                case 'mediumtext':
                    $type_display_range = 16777215;
                    break;
                case 'longtext':
                    $type_display_range = 4294967295;
                    break;
            }
            $null_check = $field_null ? "if (!isset(\$value)) {\n\t\t\treturn array('{$field_name}', \$value);\n\t\t}\n\t\t" : '';
            $null_fix = $field_null ? '' : "if (!isset(\$value)) {\n\t\t\t\$value='';\n\t\t}\n\t\t";
            $dbe_check = "if (\$value instanceof Db_Expression) {\n\t\t\treturn array('{$field_name}', \$value);\n\t\t}\n\t\t";
            $js_null_check = $field_null ? "if (!value) return value;\n\t\t" : '';
            $js_null_fix = $field_null ? '' : "if (value == null) {\n\t\t\tvalue='';\n\t\t}\n\t\t";
            $js_dbe_check = "if (value instanceof Db.Expression) return value;\n\t\t";
            if (!isset($functions["beforeSet_{$field_name}"])) {
                $functions["beforeSet_{$field_name}"] = array();
            }
            if (!isset($js_functions["beforeSet_{$field_name}"])) {
                $js_functions["beforeSet_{$field_name}"] = array();
            }
            switch (strtolower($type_name)) {
                case 'tinyint':
                case 'smallint':
                case 'int':
                case 'mediumint':
                case 'bigint':
                    $isNumberLike = true;
                    $properties[] = "{integer} {$field_name}";
                    $js_properties[] = "{integer} {$field_name}";
                    $functions["maxSize_{$field_name}"]['comment'] = <<<EOT
\t{$dc}
\t * @method maxSize_{$field_name}
\t * Returns the maximum integer that can be assigned to the {$field_name} field
\t * @return {integer}
\t */
EOT;
                    $functions["maxSize_{$field_name}"]['args'] = '';
                    $functions["maxSize_{$field_name}"]['return_statement'] = <<<EOT
\t\treturn {$type_range_max};
EOT;
                    $functions["beforeSet_{$field_name}"][] = <<<EOT
\t\t{$null_check}{$dbe_check}if (!is_numeric(\$value) or floor(\$value) != \$value)
\t\t\tthrow new Exception('Non-integer value being assigned to '.\$this->getTable().".{$field_name}");
\t\t\$value = intval(\$value);
\t\tif (\$value < {$type_range_min} or \$value > {$type_range_max}) {
\t\t\t\$json = json_encode(\$value);
\t\t\tthrow new Exception("Out-of-range value \$json being assigned to ".\$this->getTable().".{$field_name}");
\t\t}
EOT;
                    $functions["beforeSet_{$field_name}"]['comment'] = <<<EOT
\t{$dc}
\t * Method is called before setting the field and verifies if integer value falls within allowed limits
\t * @method beforeSet_{$field_name}
\t * @param {integer} \$value
\t * @return {array} An array of field name and value
\t * @throws {Exception} An exception is thrown if \$value is not integer or does not fit in allowed range
\t */
EOT;
                    $js_functions["maxSize_{$field_name}"]['comment'] = <<<EOT
{$dc}
 * Returns the maximum integer that can be assigned to the {$field_name} field
 * @return {integer}
 */
EOT;
                    $js_functions["maxSize_{$field_name}"]['args'] = '';
                    $js_functions["maxSize_{$field_name}"]['return_statement'] = <<<EOT
\t\treturn {$type_range_max};
EOT;
                    $js_functions["beforeSet_{$field_name}"][] = <<<EOT
\t\t{$js_null_check}{$js_dbe_check}value = Number(value);
\t\tif (isNaN(value) || Math.floor(value) != value) 
\t\t\tthrow new Error('Non-integer value being assigned to '+this.table()+".{$field_name}");
\t\tif (value < {$type_range_min} || value > {$type_range_max})
\t\t\tthrow new Error("Out-of-range value "+JSON.stringify(value)+" being assigned to "+this.table()+".{$field_name}");
EOT;
                    $js_functions["beforeSet_{$field_name}"]['comment'] = <<<EOT
{$dc}
 * Method is called before setting the field and verifies if integer value falls within allowed limits
 * @method beforeSet_{$field_name}
 * @param {integer} value
 * @return {integer} The value
 * @throws {Error} An exception is thrown if 'value' is not integer or does not fit in allowed range
 */
EOT;
                    break;
                case 'enum':
                    $properties[] = "{string} {$field_name}";
                    $js_properties[] = "{String} {$field_name}";
                    $functions["beforeSet_{$field_name}"][] = <<<EOT
\t\t{$null_check}{$dbe_check}if (!in_array(\$value, array({$type_display_range})))
\t\t\tthrow new Exception("Out-of-range value '\$value' being assigned to ".\$this->getTable().".{$field_name}");
EOT;
                    $functions["beforeSet_{$field_name}"]['comment'] = <<<EOT
\t{$dc}
\t * Method is called before setting the field and verifies if value belongs to enum values list
\t * @method beforeSet_{$field_name}
\t * @param {string} \$value
\t * @return {array} An array of field name and value
\t * @throws {Exception} An exception is thrown if \$value does not belong to enum values list
\t */
EOT;
                    $js_functions["beforeSet_{$field_name}"][] = <<<EOT
\t\t{$js_null_check}{$js_dbe_check}if ([{$type_display_range}].indexOf(value) < 0)
\t\t\tthrow new Error("Out-of-range value "+JSON.stringify(value)+" being assigned to "+this.table()+".{$field_name}");
EOT;
                    $js_functions["beforeSet_{$field_name}"]['comment'] = <<<EOT
{$dc}
 * Method is called before setting the field and verifies if value belongs to enum values list
 * @method beforeSet_{$field_name}
 * @param {string} value
 * @return {string} The value
 * @throws {Error} An exception is thrown if 'value' does not belong to enum values list
 */
EOT;
                    break;
                case 'varchar':
                case 'varbinary':
                case 'tinytext':
                case 'text':
                case 'mediumtext':
                case 'longtext':
                    $isTextLike = true;
                    $properties[] = "{string} {$field_name}";
                    $js_properties[] = "{String} {$field_name}";
                    $functions["maxSize_{$field_name}"]['comment'] = <<<EOT
\t{$dc}
\t * Returns the maximum string length that can be assigned to the {$field_name} field
\t * @return {integer}
\t */
EOT;
                    $functions["maxSize_{$field_name}"]['args'] = '';
                    $functions["maxSize_{$field_name}"]['return_statement'] = <<<EOT
\t\treturn {$type_display_range};
EOT;
                    $functions["beforeSet_{$field_name}"][] = <<<EOT
\t\t{$null_check}{$null_fix}{$dbe_check}if (!is_string(\$value) and !is_numeric(\$value))
\t\t\tthrow new Exception('Must pass a string to '.\$this->getTable().".{$field_name}");
\t\tif (strlen(\$value) > {$type_display_range})
\t\t\tthrow new Exception('Exceedingly long value being assigned to '.\$this->getTable().".{$field_name}");
EOT;
                    $functions["beforeSet_{$field_name}"]['comment'] = <<<EOT
\t{$dc}
\t * Method is called before setting the field and verifies if value is string of length within acceptable limit.
\t * Optionally accept numeric value which is converted to string
\t * @method beforeSet_{$field_name}
\t * @param {string} \$value
\t * @return {array} An array of field name and value
\t * @throws {Exception} An exception is thrown if \$value is not string or is exceedingly long
\t */
EOT;
                    $js_functions["maxSize_{$field_name}"]['comment'] = <<<EOT
\t{$dc}
\t * Returns the maximum string length that can be assigned to the {$field_name} field
\t * @return {integer}
\t */
EOT;
                    $js_functions["maxSize_{$field_name}"]['args'] = '';
                    $js_functions["maxSize_{$field_name}"]['return_statement'] = <<<EOT
\t\treturn {$type_display_range};
EOT;
                    $js_functions["beforeSet_{$field_name}"][] = <<<EOT
\t\t{$js_null_check}{$js_null_check}{$js_null_fix}{$js_dbe_check}if (typeof value !== "string" && typeof value !== "number")
\t\t\tthrow new Error('Must pass a string to '+this.table()+".{$field_name}");
\t\tif (typeof value === "string" && value.length > {$type_display_range})
\t\t\tthrow new Error('Exceedingly long value being assigned to '+this.table()+".{$field_name}");
EOT;
                    $js_functions["beforeSet_{$field_name}"]['comment'] = <<<EOT
{$dc}
 * Method is called before setting the field and verifies if value is string of length within acceptable limit.
 * Optionally accept numeric value which is converted to string
 * @method beforeSet_{$field_name}
 * @param {string} value
 * @return {string} The value
 * @throws {Error} An exception is thrown if 'value' is not string or is exceedingly long
 */
EOT;
                    break;
                case 'date':
                    $isTimeLike = true;
                    $properties[] = "{string|Db_Expression} {$field_name}";
                    $js_properties[] = "{String|Db.Expression} {$field_name}";
                    $functions["beforeSet_{$field_name}"][] = <<<EOT
\t\t{$null_check}{$dbe_check}\$value = date("Y-m-d h:i:s", strtotime(\$value));
\t\t\$date = date_parse(\$value);
\t\tif (!empty(\$date['errors'])) {
\t\t\t\$json = json_encode(\$value);
\t\t\tthrow new Exception("Date \$json in incorrect format being assigned to ".\$this->getTable().".{$field_name}");
\t\t}
\t\tforeach (array('year', 'month', 'day', 'hour', 'minute', 'second') as \$v) {
\t\t\t\$\$v = \$date[\$v];
\t\t}
\t\t\$value = sprintf("%04d-%02d-%02d", \$year, \$month, \$day);
EOT;
                    $functions["beforeSet_{$field_name}"]['comment'] = <<<EOT
\t{$dc}
\t * Method is called before setting the field and normalize the date string
\t * @method beforeSet_{$field_name}
\t * @param {string} \$value
\t * @return {array} An array of field name and value
\t * @throws {Exception} An exception is thrown if \$value does not represent valid date
\t */
EOT;
                    $js_functions["beforeSet_{$field_name}"][] = <<<EOT
\t\t{$js_null_check}{$js_dbe_check}value = (value instanceof Date) ? Base.db().toDateTime(value) : value;
EOT;
                    $js_functions["beforeSet_{$field_name}"]['comment'] = <<<EOT
{$dc}
 * Method is called before setting the field
 * @method beforeSet_{$field_name}
 * @param {String} value
 * @return {Date|Db.Expression} If 'value' is not Db.Expression the current date is returned
 */
EOT;
                    break;
                case 'datetime':
                case 'timestamp':
                    $isTimeLike = true;
                    $properties[] = "{string|Db_Expression} {$field_name}";
                    $js_properties[] = "{String|Db.Expression} {$field_name}";
                    $possibleMagicFields = array('insertedTime', 'updatedTime', 'created_time', 'updated_time');
                    $possibleMagicInsertFields = array('insertedTime', 'created_time');
                    if (in_array($field_name, $possibleMagicFields)) {
                        if (!in_array($field_name, $possibleMagicInsertFields) or !isset($field_default)) {
                            $magic_field_names[] = $field_name;
                            $is_magic_field = true;
                        }
                    }
                    $functions["beforeSet_{$field_name}"][] = <<<EOT
\t\t{$null_check}{$dbe_check}\$date = date_parse(\$value);
\t\tif (!empty(\$date['errors'])) {
\t\t\t\$json = json_encode(\$value);
\t\t\tthrow new Exception("DateTime \$json in incorrect format being assigned to ".\$this->getTable().".{$field_name}");
\t\t}
\t\t\$value = sprintf("%04d-%02d-%02d %02d:%02d:%02d", 
\t\t\t\$date['year'], \$date['month'], \$date['day'], 
\t\t\t\$date['hour'], \$date['minute'], \$date['second']
\t\t);
EOT;
                    $functions["beforeSet_{$field_name}"]['comment'] = <<<EOT
\t{$dc}
\t * Method is called before setting the field and normalize the DateTime string
\t * @method beforeSet_{$field_name}
\t * @param {string} \$value
\t * @return {array} An array of field name and value
\t * @throws {Exception} An exception is thrown if \$value does not represent valid DateTime
\t */
EOT;
                    $js_functions["beforeSet_{$field_name}"][] = <<<EOT
\t\t{$js_null_check}{$js_dbe_check}value = (value instanceof Date) ? Base.db().toDateTime(value) : value;
EOT;
                    $js_functions["beforeSet_{$field_name}"]['comment'] = <<<EOT
{$dc}
 * Method is called before setting the field
 * @method beforeSet_{$field_name}
 * @param {String} value
 * @return {Date|Db.Expression} If 'value' is not Db.Expression the current date is returned
 */
EOT;
                    break;
                case 'numeric':
                case 'decimal':
                case 'float':
                case 'double':
                    $isNumberLike = true;
                    $properties[] = "{float} {$field_name}";
                    $js_properties[] = "{number} {$field_name}";
                    $functions["beforeSet_{$field_name}"][] = <<<EOT
\t\t{$null_check}{$dbe_check}if (!is_numeric(\$value))
\t\t\tthrow new Exception('Non-numeric value being assigned to '.\$this->getTable().".{$field_name}");
\t\t\$value = floatval(\$value);
EOT;
                    $js_functions["beforeSet_{$field_name}"][] = <<<EOT
\t\t{$js_null_check}{$js_dbe_check}value = Number(value);
\t\tif (isNaN(value))
\t\t\tthrow new Error('Non-number value being assigned to '+this.table()+".{$field_name}");
EOT;
                    $js_functions["beforeSet_{$field_name}"]['comment'] = <<<EOT
{$dc}
 * Method is called before setting the field to verify if value is a number
 * @method beforeSet_{$field_name}
 * @param {integer} value
 * @return {integer} The value
 * @throws {Error} If 'value' is not number
 */
EOT;
                    break;
                default:
                    $properties[] = "{mixed} {$field_name}";
                    $js_properties[] = "{mixed} {$field_name}";
                    break;
            }
            if (!empty($functions["beforeSet_{$field_name}"])) {
                $functions["beforeSet_{$field_name}"]['return_statement'] = <<<EOT
\t\treturn array('{$field_name}', \$value);
EOT;
            }
            if (!empty($js_functions["beforeSet_{$field_name}"])) {
                $js_functions["beforeSet_{$field_name}"]['return_statement'] = <<<EOT
\t\treturn value;
EOT;
            }
            if (!$field_null and !$is_magic_field and (!$isNumberLike and !$isTextLike or in_array($field_name, $pk)) and !$auto_inc and !isset($field_default)) {
                $required_field_names[] = "'{$field_name}'";
            }
        }
        $field_names_json = json_encode($field_names);
        $field_names_json_indented = str_replace(array("[", ",", "]"), array("[\n\t\t", ",\n\t\t", "\n\t]"), $field_names_json);
        $field_names_exported = "\$this->fieldNames()";
        $functions['beforeSave'] = array();
        $js_functions['beforeSave'] = array();
        if ($required_field_names) {
            $required_fields_string = implode(',', $required_field_names);
            $beforeSave_code = <<<EOT
\t\tif (!\$this->retrieved) {
\t\t\t\$table = \$this->getTable();
\t\t\tforeach (array({$required_fields_string}) as \$name) {
\t\t\t\tif (!isset(\$value[\$name])) {
\t\t\t\t\tthrow new Exception("the field \$table.\$name needs a value, because it is NOT NULL, not auto_increment, and lacks a default value.");
\t\t\t\t}
\t\t\t}
\t\t}
EOT;
            $js_beforeSave_code = <<<EOT
\tvar fields = [{$required_fields_string}], i;
\tif (!this._retrieved) {
\t\tvar table = this.table();
\t\tfor (i=0; i<fields.length; i++) {
\t\t\tif (typeof this.fields[fields[i]] === "undefined") {
\t\t\t\tthrow new Error("the field "+table+"."+fields[i]+" needs a value, because it is NOT NULL, not auto_increment, and lacks a default value.");
\t\t\t}
\t\t}
\t}
EOT;
            $return_statement = <<<EOT
\t\treturn \$value;
EOT;
            $js_return_statement = <<<EOT
\treturn value;
EOT;
            $functions["beforeSave"][] = $beforeSave_code;
            $functions['beforeSave']['return_statement'] = $return_statement;
            $functions['beforeSave']['comment'] = <<<EOT
\t{$dc}
\t * Check if mandatory fields are set and updates 'magic fields' with appropriate values
\t * @method beforeSave
\t * @param {array} \$value The array of fields
\t * @return {array}
\t * @throws {Exception} If mandatory field is not set
\t */
EOT;
            $js_functions["beforeSave"][] = $js_beforeSave_code;
            $js_functions['beforeSave']['return_statement'] = $js_return_statement;
            $js_functions['beforeSave']['comment'] = <<<EOT
{$dc}
 * Check if mandatory fields are set and updates 'magic fields' with appropriate values
 * @method beforeSave
 * @param {array} value The array of fields
 * @return {array}
 * @throws {Error} If mandatory field is not set
 */
EOT;
        }
        //$functions['beforeSave'] = array();
        if (count($magic_field_names) > 0) {
            $beforeSave_code = '';
            $js_beforeSave_code = '';
            foreach (array('created_time', 'insertedTime') as $cmf) {
                if (in_array($cmf, $magic_field_names)) {
                    $beforeSave_code .= <<<EOT

\t\tif (!\$this->retrieved and !isset(\$value['{$cmf}'])) {
\t\t\t\$this->{$cmf} = \$value['{$cmf}'] = new Db_Expression('CURRENT_TIMESTAMP');
\t\t}

EOT;
                    $js_beforeSave_code .= <<<EOT

\tif (!this._retrieved && !value['{$cmf}']) {
\t\tthis['{$cmf}'] = value['{$cmf}'] = new Db.Expression('CURRENT_TIMESTAMP');
\t}
EOT;
                    break;
                }
            }
            foreach (array('updated_time', 'updatedTime') as $umf) {
                if (in_array($umf, $magic_field_names)) {
                    $beforeSave_code .= <<<EOT
\t\t\t\t\t\t
\t\t// convention: we'll have {$umf} = {$cmf} if just created.
\t\t\$this->{$umf} = \$value['{$umf}'] = new Db_Expression('CURRENT_TIMESTAMP');
EOT;
                    $js_beforeSave_code .= <<<EOT

\t// convention: we'll have {$umf} = {$cmf} if just created.
\tthis['{$umf}'] = value['{$umf}'] = new Db.Expression('CURRENT_TIMESTAMP');
EOT;
                    break;
                }
            }
            $return_statement = <<<EOT
\t\treturn \$value;
EOT;
            $js_return_statement = <<<EOT
\treturn value;
EOT;
            $functions['beforeSave'][] = $beforeSave_code;
            $functions['beforeSave']['return_statement'] = $return_statement;
            $js_functions['beforeSave'][] = $js_beforeSave_code;
            $js_functions['beforeSave']['return_statement'] = $js_return_statement;
        }
        $functions['fieldNames'] = array();
        $fieldNames_exported = Db_Utils::var_export($field_names);
        $fieldNames_code = <<<EOT
\t\t\$field_names = {$fieldNames_exported};
\t\t\$result = \$field_names;
\t\tif (!empty(\$table_alias)) {
\t\t\t\$temp = array();
\t\t\tforeach (\$result as \$field_name)
\t\t\t\t\$temp[] = \$table_alias . '.' . \$field_name;
\t\t\t\$result = \$temp;
\t\t} 
\t\tif (!empty(\$field_alias_prefix)) {
\t\t\t\$temp = array();
\t\t\treset(\$field_names);
\t\t\tforeach (\$result as \$field_name) {
\t\t\t\t\$temp[\$field_alias_prefix . current(\$field_names)] = \$field_name;
\t\t\t\tnext(\$field_names);
\t\t\t}
\t\t\t\$result = \$temp;
\t\t}
EOT;
        $return_statement = <<<EOT
\t\treturn \$result;
EOT;
        $functions['fieldNames'][] = $fieldNames_code;
        $functions['fieldNames']['return_statement'] = $return_statement;
        $functions['fieldNames']['args'] = '$table_alias = null, $field_alias_prefix = null';
        $functions['fieldNames']['modifiers'] = 'static';
        $functions['fieldNames']['comment'] = <<<EOT
\t{$dc}
\t * Retrieves field names for class table
\t * @method fieldNames
\t * @static
\t * @param {string} [\$table_alias=null] If set, the alieas is added to each field
\t * @param {string} [\$field_alias_prefix=null] If set, the method returns associative array of `'prefixed field' => 'field'` pairs
\t * @return {array} An array of field names
\t */
EOT;
        $functions_code = array();
        foreach ($functions as $func_name => $func_code) {
            $func_args = isset($func_code['args']) ? $func_code['args'] : '$value';
            $func_modifiers = isset($func_code['modifiers']) ? $func_code['modifiers'] . ' ' : '';
            $func_code_string = isset($func_code['comment']) ? $func_code['comment'] . "\n" : '';
            $func_code_string .= <<<EOT
\t{$func_modifiers}function {$func_name}({$func_args})
\t{

EOT;
            if (is_array($func_code) and !empty($func_code)) {
                foreach ($func_code as $key => $code_tool) {
                    if (is_string($key)) {
                        continue;
                    }
                    $func_code_string .= $code_tool;
                }
                $func_code_string .= "\n" . $func_code['return_statement'];
            }
            $func_code_string .= <<<EOT
\t\t\t
\t}
EOT;
            if (!empty($func_code)) {
                $functions_code[] = $func_code_string;
            }
        }
        $functions_string = implode("\n\n", $functions_code);
        foreach ($js_functions as $func_name => $func_code) {
            $func_args = isset($func_code['args']) ? $func_code['args'] : 'value';
            $instance = isset($func_code['instance']) ? '.prototype' : '';
            $func_code_string = isset($func_code['comment']) ? $func_code['comment'] . "\n" : '';
            $func_code_string .= <<<EOT
Base.prototype.{$func_name} = function ({$func_args}) {

EOT;
            if (is_array($func_code) and !empty($func_code)) {
                foreach ($func_code as $key => $code_tool) {
                    if (is_string($key)) {
                        continue;
                    }
                    $func_code_string .= $code_tool;
                }
                $func_code_string .= "\n" . $func_code['return_statement'];
            }
            $func_code_string .= <<<EOT

};
EOT;
            if (!empty($func_code)) {
                $js_functions_code[] = $func_code_string;
            }
        }
        $js_functions_string = implode("\n\n", $js_functions_code);
        $pk_exported_indented = str_replace("\n", "\n\t\t\t", $pk_exported);
        $pk_json_indented = str_replace(array("[", ",", "]"), array("[\n\t\t", ",\n\t\t", "\n\t]"), $pk_json);
        $connectionName_var = var_export($connectionName, true);
        $class_name_var = var_export($class_name, true);
        $class_name_prefix = rtrim(ucfirst($classname_prefix), "._");
        $properties2 = array();
        foreach ($properties as $k => $v) {
            $tmp = explode(' ', $v);
            $properties[$k] = <<<EOT
\t{$dc}
\t * @property \${$tmp[1]}
\t * @type {$tmp['0']}
\t */
EOT;
            $properties2[$k] = <<<EOT
 * @property {$tmp[0]} \${$tmp[1]}
EOT;
        }
        foreach ($js_properties as $k => $v) {
            $tmp = explode(' ', $v);
            $js_properties[$k] = <<<EOT
{$dc}
 * @property {$tmp['0']}
 * @type {$tmp['1']}
 */
EOT;
        }
        $field_hints = implode("\n", $properties);
        $field_hints2 = implode("\n", $properties2);
        $js_field_hints = implode("\n", $js_properties);
        // Here is the base class:
        $base_class_string = <<<EOT
<?php

{$dc}
 * Autogenerated base class representing {$table_name_base} rows
 * in the {$connectionName} database.
 *
 * Don't change this file, since it can be overwritten.
 * Instead, change the {$class_name}.php file.
 *
 * @module {$connectionName}
 */
{$dc}
 * Base class representing '{$class_name_base}' rows in the '{$connectionName}' database
 * @class Base_{$class_name}
 * @extends Db_Row
 *
{$field_hints2}
 */
abstract class Base_{$class_name} extends Db_Row
{
{$field_hints}
\t{$dc}
\t * The setUp() method is called the first time
\t * an object of this class is constructed.
\t * @method setUp
\t */
\tfunction setUp()
\t{
\t\t\$this->setDb(self::db());
\t\t\$this->setTable(self::table());
\t\t\$this->setPrimaryKey(
\t\t\t{$pk_exported_indented}
\t\t);
\t}

\t{$dc}
\t * Connects to database
\t * @method db
\t * @static
\t * @return {iDb} The database object
\t */
\tstatic function db()
\t{
\t\treturn Db::connect({$connectionName_var});
\t}

\t{$dc}
\t * Retrieve the table name to use in SQL statement
\t * @method table
\t * @static
\t * @param {boolean} [\$with_db_name=true] Indicates wheather table name should contain the database name
 \t * @return {string|Db_Expression} The table name as string optionally without database name if no table sharding
\t * was started or Db_Expression class with prefix and database name templates is table was sharded
\t */
\tstatic function table(\$with_db_name = true)
\t{
\t\tif (Q_Config::get('Db', 'connections', '{$connectionName}', 'indexes', '{$class_name_base}', false)) {
\t\t\treturn new Db_Expression((\$with_db_name ? '{\$dbname}.' : '').'{\$prefix}'.'{$table_name_base}');
\t\t} else {
\t\t\t\$conn = Db::getConnection({$connectionName_var});
  \t\t\t\$prefix = empty(\$conn['prefix']) ? '' : \$conn['prefix'];
  \t\t\t\$table_name = \$prefix . '{$table_name_base}';
  \t\t\tif (!\$with_db_name)
  \t\t\t\treturn \$table_name;
  \t\t\t\$db = Db::connect({$connectionName_var});
  \t\t\treturn \$db->dbName().'.'.\$table_name;
\t\t}
\t}
\t{$dc}
\t * The connection name for the class
\t * @method connectionName
\t * @static
\t * @return {string} The name of the connection
\t */
\tstatic function connectionName()
\t{
\t\treturn {$connectionName_var};
\t}

\t{$dc}
\t * Create SELECT query to the class table
\t * @method select
\t * @static
\t * @param {array} \$fields The field values to use in WHERE clauseas as 
\t * an associative array of `column => value` pairs
\t * @param {string} [\$alias=null] Table alias
\t * @return {Db_Query_Mysql} The generated query
\t */
\tstatic function select(\$fields, \$alias = null)
\t{
\t\tif (!isset(\$alias)) \$alias = '';
\t\t\$q = self::db()->select(\$fields, self::table().' '.\$alias);
\t\t\$q->className = {$class_name_var};
\t\treturn \$q;
\t}

\t{$dc}
\t * Create UPDATE query to the class table
\t * @method update
\t * @static
\t * @param {string} [\$alias=null] Table alias
\t * @return {Db_Query_Mysql} The generated query
\t */
\tstatic function update(\$alias = null)
\t{
\t\tif (!isset(\$alias)) \$alias = '';
\t\t\$q = self::db()->update(self::table().' '.\$alias);
\t\t\$q->className = {$class_name_var};
\t\treturn \$q;
\t}

\t{$dc}
\t * Create DELETE query to the class table
\t * @method delete
\t * @static
\t * @param {object} [\$table_using=null] If set, adds a USING clause with this table
\t * @param {string} [\$alias=null] Table alias
\t * @return {Db_Query_Mysql} The generated query
\t */
\tstatic function delete(\$table_using = null, \$alias = null)
\t{
\t\tif (!isset(\$alias)) \$alias = '';
\t\t\$q = self::db()->delete(self::table().' '.\$alias, \$table_using);
\t\t\$q->className = {$class_name_var};
\t\treturn \$q;
\t}

\t{$dc}
\t * Create INSERT query to the class table
\t * @method insert
\t * @static
\t * @param {object} [\$fields=array()] The fields as an associative array of `column => value` pairs
\t * @param {string} [\$alias=null] Table alias
\t * @return {Db_Query_Mysql} The generated query
\t */
\tstatic function insert(\$fields = array(), \$alias = null)
\t{
\t\tif (!isset(\$alias)) \$alias = '';
\t\t\$q = self::db()->insert(self::table().' '.\$alias, \$fields);
\t\t\$q->className = {$class_name_var};
\t\treturn \$q;
\t}
\t{$dc}
\t * Inserts multiple records into a single table, preparing the statement only once,
\t * and executes all the queries.
\t * @method insertManyAndExecute
\t * @static
\t * @param {array} [\$records=array()] The array of records to insert. 
\t * (The field names for the prepared statement are taken from the first record.)
\t * You cannot use Db_Expression objects here, because the function binds all parameters with PDO.
\t * @param {array} [\$options=array()]
\t *   An associative array of options, including:
\t *
\t * * "chunkSize" {integer} The number of rows to insert at a time. defaults to 20.<br/>
\t * * "onDuplicateKeyUpdate" {array} You can put an array of fieldname => value pairs here,
\t * \t\twhich will add an ON DUPLICATE KEY UPDATE clause to the query.
\t *
\t */
\tstatic function insertManyAndExecute(\$records = array(), \$options = array())
\t{
\t\tself::db()->insertManyAndExecute(
\t\t\tself::table(), \$records,
\t\t\tarray_merge(\$options, array('className' => {$class_name_var}))
\t\t);
\t}
\t
{$functions_string}
};
EOT;
        // Set the JS code
        $js_code = <<<EOT
{$dc}
 * Autogenerated base class representing {$table_name_base} rows
 * in the {$connectionName} database.
 *
 * Don't change this file, since it can be overwritten.
 * Instead, change the {$class_name_prefix}/{$class_name_base}.js file.
 *
 * @module {$connectionName}
 */

var Q = require('Q');
var Db = Q.require('Db');
var {$connectionName} = Q.require('{$connectionName}');
var Row = Q.require('Db/Row');

{$dc}
 * Base class representing '{$class_name_base}' rows in the '{$connectionName}' database
 * @namespace Base.{$class_name_prefix}
 * @class {$class_name_base}
 * @extends Db.Row
 * @constructor
 * @param {object} [fields={}] The fields values to initialize table row as 
 * an associative array of `{column: value}` pairs
 */
function Base (fields) {
\tBase.constructors.apply(this, arguments);
}

Q.mixin(Base, Row);

{$js_field_hints}

{$dc}
 * This method calls Db.connect() using information stored in the configuration.
 * If this has already been called, then the same db object is returned.
 * @method db
 * @return {Db} The database connection
 */
Base.db = function () {
\treturn {$connectionName}.db();
};

{$dc}
 * Retrieve the table name to use in SQL statements
 * @method table
 * @param {boolean} [withoutDbName=false] Indicates wheather table name should contain the database name
 * @return {String|Db.Expression} The table name as string optionally without database name if no table sharding was started
 * or Db.Expression object with prefix and database name templates is table was sharded
 */
Base.table = function (withoutDbName) {
\tif (Q.Config.get(['Db', 'connections', '{$connectionName}', 'indexes', '{$class_name_base}'], false)) {
\t\treturn new Db.Expression((withoutDbName ? '' : '{\$dbname}.')+'{\$prefix}{$table_name_base}');
\t} else {
\t\tvar conn = Db.getConnection('{$connectionName}');
\t\tvar prefix = conn.prefix || '';
\t\tvar tableName = prefix + '{$table_name_base}';
\t\tvar dbname = Base.table.dbname;
\t\tif (!dbname) {
\t\t\tvar dsn = Db.parseDsnString(conn['dsn']);
\t\t\tdbname = Base.table.dbname = dsn.dbname;
\t\t}
\t\treturn withoutDbName ? tableName : dbname + '.' + tableName;
\t}
};

{$dc}
 * The connection name for the class
 * @method connectionName
 * @return {string} The name of the connection
 */
Base.connectionName = function() {
\treturn '{$connectionName}';
};

{$dc}
 * Create SELECT query to the class table
 * @method SELECT
 * @param {object|string} fields The field values to use in WHERE clauseas as an associative array of `{column: value}` pairs
 * @param {string} [alias=null] Table alias
 * @return {Db.Query.Mysql} The generated query
 */
Base.SELECT = function(fields, alias) {
\tvar q = Base.db().SELECT(fields, Base.table()+(alias ? ' '+alias : ''));
\tq.className = '{$class_name}';
\treturn q;
};

{$dc}
 * Create UPDATE query to the class table. Use Db.Query.Mysql.set() method to define SET clause
 * @method UPDATE
 * @param {string} [alias=null] Table alias
 * @return {Db.Query.Mysql} The generated query
 */
Base.UPDATE = function(alias) {
\tvar q = Base.db().UPDATE(Base.table()+(alias ? ' '+alias : ''));
\tq.className = '{$class_name}';
\treturn q;
};

{$dc}
 * Create DELETE query to the class table
 * @method DELETE
 * @param {object}[table_using=null] If set, adds a USING clause with this table
 * @param {string} [alias=null] Table alias
 * @return {Db.Query.Mysql} The generated query
 */
Base.DELETE = function(table_using, alias) {
\tvar q = Base.db().DELETE(Base.table()+(alias ? ' '+alias : ''), table_using);
\tq.className = '{$class_name}';
\treturn q;
};

{$dc}
 * Create INSERT query to the class table
 * @method INSERT
 * @param {object} [fields={}] The fields as an associative array of `{column: value}` pairs
 * @param {string} [alias=null] Table alias
 * @return {Db.Query.Mysql} The generated query
 */
Base.INSERT = function(fields, alias) {
\tvar q = Base.db().INSERT(Base.table()+(alias ? ' '+alias : ''), fields || {});
\tq.className = '{$class_name}';
\treturn q;
};

{$dc}
 * The name of the class
 * @property className
 * @type string
 */
Base.prototype.className = "{$class_name}";

// Instance methods

{$dc}
 * Create INSERT query to the class table
 * @method INSERT
 * @param {object} [fields={}] The fields as an associative array of `{column: value}` pairs
 * @param {string} [alias=null] Table alias
 * @return {Db.Query.Mysql} The generated query
 */
Base.prototype.setUp = function() {
\t// does nothing for now
};

{$dc}
 * Create INSERT query to the class table
 * @method INSERT
 * @param {object} [fields={}] The fields as an associative array of `{column: value}` pairs
 * @param {string} [alias=null] Table alias
 * @return {Db.Query.Mysql} The generated query
 */
Base.prototype.db = function () {
\treturn Base.db();
};

{$dc}
 * Retrieve the table name to use in SQL statements
 * @method table
 * @param {boolean} [withoutDbName=false] Indicates wheather table name should contain the database name
 * @return {String|Db.Expression} The table name as string optionally without database name if no table sharding was started
 * or Db.Expression object with prefix and database name templates is table was sharded
 */
Base.prototype.table = function () {
\treturn Base.table();
};

{$dc}
 * Retrieves primary key fields names for class table
 * @method primaryKey
 * @return {string[]} An array of field names
 */
Base.prototype.primaryKey = function () {
\treturn {$pk_json_indented};
};

{$dc}
 * Retrieves field names for class table
 * @method fieldNames
 * @return {array} An array of field names
 */
Base.prototype.fieldNames = function () {
\treturn {$field_names_json_indented};
};

{$js_functions_string}

module.exports = Base;
EOT;
        // Return the base class
        return $base_class_string;
        // unless the above line threw an exception
    }
Example #4
0
    /**
     * Generates code for a base class for the model
     * 
     * @param string $table
     *  The name of the table to generate the code for.
     * @param string $directory
     *  The path of the directory in which to place the model code.
     * @param string $classname_prefix
     *  The prefix to prepend to the generated class names
     * @param string& $class_name
     *  If set, this is the class name that is used.
     *  If an unset variable is passed, it is filled with the
     *  class name that is ultimately chosen from the $classname_prefix
     *  and $table_name.
     * @return string
     *  The generated code for the class.
     */
    function codeForModelBaseClass($table_name, $directory, $classname_prefix = '', &$class_name = null, $prefix = null)
    {
        if (empty($table_name)) {
            throw new Exception('table_name parameter is empty', -2);
        }
        if (empty($directory)) {
            throw new Exception('directory parameter is empty', -3);
        }
        $conn_name = $this->connectionName();
        $conn = Db::getConnection($conn_name);
        if (!isset($prefix)) {
            $prefix = empty($conn['prefix']) ? '' : $conn['prefix'];
        }
        if (!empty($prefix)) {
            $prefix_len = strlen($prefix);
            $table_name_base = substr($table_name, $prefix_len);
            $table_name_prefix = substr($table_name, 0, $prefix_len);
            if (empty($table_name_base) or $table_name_prefix != $prefix) {
                return '';
            }
            // no class generated
        } else {
            $table_name_base = $table_name;
        }
        if (empty($classname_prefix)) {
            $classname_prefix = '';
        }
        if (!isset($class_name)) {
            $class_name_base = Db::generateTableClassName($table_name_base);
            $class_name = ucfirst($classname_prefix) . $class_name_base;
        }
        $table_cols = $this->rawQuery("SHOW COLUMNS FROM {$table_name}")->execute()->fetchAll();
        // Calculate primary key
        $pk = array();
        foreach ($table_cols as $table_col) {
            if ($table_col['Key'] == 'PRI') {
                $pk[] = $table_col['Field'];
            }
        }
        $pk_exported = var_export($pk, true);
        // Calculate validation functions
        $functions = array();
        $field_names = array();
        $properties = array();
        $required_field_names = array();
        $magic_field_names = array();
        foreach ($table_cols as $table_col) {
            $field_name = $table_col['Field'];
            $field_names[] = $field_name;
            $field_null = $table_col['Null'] == 'YES' ? true : false;
            $field_default = $table_col['Default'];
            $auto_inc = strpos($table_col['Extra'], 'auto_increment') !== false ? true : false;
            $type = $table_col['Type'];
            $pieces = explode('(', $type);
            if (isset($pieces[1])) {
                $pieces2 = explode(')', $pieces[1]);
                $pieces2_count = count($pieces2);
                if ($pieces2_count > 2) {
                    $pieces2 = array(implode(')', array_slice($pieces2, 0, -1)), end($pieces2));
                }
            }
            $type_name = $pieces[0];
            if (isset($pieces2)) {
                $type_display_range = $pieces2[0];
                $type_modifiers = $pieces2[1];
                $type_unsigned = strpos($type_modifiers, 'unsigned') !== false;
            }
            if (!$field_null and !$auto_inc and (in_array($type_name, array('tinyint', 'smallint', 'mediumint', 'int', 'bigint', 'enum')) and $field_default === '')) {
                $required_field_names[] = "'{$field_name}'";
            }
            switch ($type_name) {
                case 'tinyint':
                    $type_range_min = $type_unsigned ? 0 : -128;
                    $type_range_max = $type_unsigned ? 255 : 127;
                    break;
                case 'smallint':
                    $type_range_min = $type_unsigned ? 0 : -32768;
                    $type_range_max = $type_unsigned ? 65535 : 32767;
                    break;
                case 'mediumint':
                    $type_range_min = $type_unsigned ? 0 : -8388608;
                    $type_range_max = $type_unsigned ? 16777215 : 8388607;
                    break;
                case 'int':
                    $type_range_min = $type_unsigned ? 0 : -2147483648;
                    $type_range_max = $type_unsigned ? 4294967295 : 2147483647;
                    break;
                case 'bigint':
                    $type_range_min = $type_unsigned ? 0 : -9.223372036854776E+18;
                    $type_range_max = $type_unsigned ? 1.8446744073709552E+19 : 9223372036854775807;
                    break;
            }
            $null_check = $field_null ? "if (!isset(\$value)) return array('{$field_name}', \$value);\n\t\t" : '';
            $dbe_check = "if (\$value instanceof Db_Expression) return array('{$field_name}', \$value);\n\t\t";
            if (!isset($functions["beforeSet_{$field_name}"])) {
                $functions["beforeSet_{$field_name}"] = array();
            }
            switch (strtolower($type_name)) {
                case 'tinyint':
                case 'smallint':
                case 'int':
                case 'mediumint':
                case 'bigint':
                    $properties[] = "int \${$field_name}";
                    $functions["beforeSet_{$field_name}"][] = <<<EOT
\t\t{$null_check}{$dbe_check}if (!is_numeric(\$value) or floor(\$value) != \$value)
\t\t\tthrow new Exception('Non-integer value being assigned to {$table_name}.{$field_name}');
\t\tif (\$value < {$type_range_min} or \$value > {$type_range_max})
\t\t\tthrow new Exception('Out-of-range value being assigned to {$table_name}.{$field_name}');
EOT;
                    break;
                case 'enum':
                    $properties[] = "mixed \${$field_name}";
                    $functions["beforeSet_{$field_name}"][] = <<<EOT
\t\t{$null_check}{$dbe_check}if (!in_array(\$value, array({$type_display_range})))
\t\t\tthrow new Exception('Out-of-range value being assigned to {$table_name}.{$field_name}');
EOT;
                    break;
                case 'varchar':
                case 'varbinary':
                    $properties[] = "string \${$field_name}";
                    $functions["beforeSet_{$field_name}"][] = <<<EOT
\t\t{$null_check}{$dbe_check}if (!is_string(\$value))
\t\t\tthrow new Exception('Must pass a string to {$table_name}.{$field_name}');
\t\tif (strlen(\$value) > {$type_display_range})
\t\t\tthrow new Exception('Exceedingly long value being assigned to {$table_name}.{$field_name}');
EOT;
                    break;
                case 'date':
                    $properties[] = "string \${$field_name}";
                    $functions["beforeSet_{$field_name}"][] = <<<EOT
\t\t{$null_check}{$dbe_check}\$date = date_parse(\$value);
\t\tif (!empty(\$date['errors']))
\t\t\tthrow new Exception("Date \$value in incorrect format being assigned to {$table_name}.{$field_name}");
\t\tforeach (array('year', 'month', 'day', 'hour', 'minute', 'second') as \$v)
\t\t\t\$\$v = \$date[\$v];
\t\t\$value = sprintf("%04d-%02d-%02d", \$year, \$month, \$day);
EOT;
                    break;
                case 'datetime':
                    $properties[] = "string \${$field_name}";
                    if ($field_name == 'time_created' or $field_name == 'time_updated') {
                        $magic_field_names[] = $field_name;
                    }
                    $functions["beforeSet_{$field_name}"][] = <<<EOT
       {$null_check}{$dbe_check}\$date = date_parse(\$value);
       if (!empty(\$date['errors']))
           throw new Exception("DateTime \$value in incorrect format being assigned to {$table_name}.{$field_name}");
       foreach (array('year', 'month', 'day', 'hour', 'minute', 'second') as \$v)
           \$\$v = \$date[\$v];
       \$value = sprintf("%04d-%02d-%02d %02d:%02d:%02d", \$year, \$month, \$day, \$hour, \$minute, \$second);
EOT;
                    break;
                case 'timestamp':
                    $properties[] = "string \${$field_name}";
                    break;
                case 'decimal':
                    $properties[] = "float \${$field_name}";
                    break;
                default:
                    $properties[] = "mixed \${$field_name}";
                    break;
            }
            if (!empty($functions["beforeSet_{$field_name}"])) {
                $functions["beforeSet_{$field_name}"]['return_statement'] = <<<EOT
\t\treturn array('{$field_name}', \$value);
EOT;
            }
        }
        $functions['afterSet'] = array();
        $field_names_exported = "\$this->fieldNames()";
        $afterSet_code = <<<EOT
\t\tif (!in_array(\$name, {$field_names_exported}))
\t\t\t\$this->notModified(\$name);
EOT;
        $return_statement = <<<EOT
\t\treturn \$value;
EOT;
        $functions["afterSet"][] = $afterSet_code;
        $functions['afterSet']['return_statement'] = $return_statement;
        $functions['afterSet']['args'] = '$name, $value';
        $functions['beforeSave'] = array();
        if ($required_field_names) {
            $required_fields_string = implode(',', $required_field_names);
            $beforeSave_code = <<<EOT
\t\tif (!\$this->retrieved) {
\t\t\tforeach (array({$required_fields_string}) as \$name) {
\t\t\t\tif (!isset(\$value[\$name]))
\t\t\t\t\tthrow new Exception("The field {$table_name}.\$name needs a value, because it is NOT NULL, not auto_increment, and lacks a default value.");
\t\t\t}
\t\t}
EOT;
            $return_statement = <<<EOT
\t\treturn \$value;
EOT;
            $functions["beforeSave"][] = $beforeSave_code;
            $functions['beforeSave']['return_statement'] = $return_statement;
        }
        //$functions['beforeSave'] = array();
        if (count($magic_field_names) > 0) {
            $beforeSave_code = '';
            if (in_array('time_created', $magic_field_names)) {
                $beforeSave_code .= <<<EOT
\t\tif (!\$this->retrieved and !isset(\$value['time_created']))
\t\t\t\$value['time_created'] = new Db_Expression('CURRENT_TIMESTAMP');\t\t\t
EOT;
            }
            if (in_array('time_updated', $magic_field_names)) {
                $beforeSave_code .= <<<EOT
\t\t//if (\$this->retrieved and !isset(\$value['time_updated']))
\t\t// convention: we'll have time_updated = time_created if just created.
\t\t\t\$value['time_updated'] = new Db_Expression('CURRENT_TIMESTAMP');\t\t\t
EOT;
            }
            $return_statement = <<<EOT
\t\treturn \$value;
EOT;
            $functions['beforeSave'][] = $beforeSave_code;
            $functions['beforeSave']['return_statement'] = $return_statement;
        }
        $functions['fieldNames'] = array();
        $fieldNames_exported = Db_Utils::var_export($field_names);
        $fieldNames_code = <<<EOT
\t\t\$field_names = {$fieldNames_exported};
\t\t\$result = \$field_names;
\t\tif (!empty(\$table_alias)) {
\t\t\t\$temp = array();
\t\t\tforeach (\$result as \$field_name)
\t\t\t\t\$temp[] = \$table_alias . '.' . \$field_name;
\t\t\t\$result = \$temp;
\t\t} 
\t\tif (!empty(\$field_alias_prefix)) {
\t\t\t\$temp = array();
\t\t\treset(\$field_names);
\t\t\tforeach (\$result as \$field_name) {
\t\t\t\t\$temp[\$field_alias_prefix . current(\$field_names)] = \$field_name;
\t\t\t\tnext(\$field_names);
\t\t\t}
\t\t\t\$result = \$temp;
\t\t}
EOT;
        $return_statement = <<<EOT
\t\treturn \$result;
EOT;
        $functions['fieldNames'][] = $fieldNames_code;
        $functions['fieldNames']['return_statement'] = $return_statement;
        $functions['fieldNames']['args'] = '$table_alias = null, $field_alias_prefix = null';
        $functions_code = array();
        foreach ($functions as $func_name => $func_code) {
            $func_args = isset($func_code['args']) ? $func_code['args'] : '$value';
            $func_code_string = <<<EOT
\tfunction {$func_name}({$func_args})
\t{

EOT;
            if (is_array($func_code) and !empty($func_code)) {
                foreach ($func_code as $key => $code_tool) {
                    if (is_string($key)) {
                        continue;
                    }
                    $func_code_string .= $code_tool;
                }
                $func_code_string .= "\n" . $func_code['return_statement'];
            }
            $func_code_string .= <<<EOT
\t\t\t
\t}
EOT;
            if (!empty($func_code)) {
                $functions_code[] = $func_code_string;
            }
        }
        $functions_string = implode("\n\n", $functions_code);
        $pk_exported_indented = str_replace("\n", "\n\t\t\t", $pk_exported);
        $conn_name_var = var_export($conn_name, true);
        $class_name_var = var_export($class_name, true);
        foreach ($properties as $k => $v) {
            $properties[$k] = " * @property {$v}";
        }
        $field_hints = implode("\n", $properties);
        // Here is the base class:
        $base_class_string = <<<EOT
<?php

/**
 * Autogenerated base class representing {$table_name_base} rows
 * in the {$conn_name} database.
 * 
 * Don't change this file, since it can be overwritten.
 * Instead, change the {$class_name}.php file.
 *
 * @package {$conn_name}
 *
{$field_hints}
 */
abstract class Base_{$class_name} extends Db_Row
{
\tfunction setUp()
\t{
\t\t\$this->setDb(self::db());
\t\t\$this->setTable(self::table());
\t\t\$this->setPrimaryKey(
\t\t\t{$pk_exported_indented}
\t\t);
\t}

\tstatic function db()
\t{
\t\treturn Db::connect({$conn_name_var});
\t}

\tstatic function table(\$with_db_name = true)
\t{
\t\t\$conn = Db::getConnection({$conn_name_var});
\t\t\$prefix = empty(\$conn['prefix']) ? '' : \$conn['prefix'];
\t\t\$table_name = \$prefix . '{$table_name_base}';
\t\tif (!\$with_db_name)
\t\t\treturn \$table_name;
\t\t\$db = Db::connect({$conn_name_var});
\t\treturn \$db->dbName().'.'.\$table_name;
\t}
\t
\tstatic function connectionName()
\t{
\t\treturn {$conn_name_var};
\t}

\t/** @return Db_Query_Mysql */
\tstatic function select(\$fields, \$alias = null)
\t{
\t\tif (!isset(\$alias)) \$alias = '';
\t\t\$q = self::db()->select(\$fields, self::table().' '.\$alias);
\t\t\$q->className = {$class_name_var};
\t\treturn \$q;
\t}

\t/** @return Db_Query_Mysql */
\tstatic function update(\$alias = null)
\t{
\t\tif (!isset(\$alias)) \$alias = '';
\t\t\$q = self::db()->update(self::table().' '.\$alias);
\t\t\$q->className = {$class_name_var};
\t\treturn \$q;
\t}

\t/** @return Db_Query_Mysql */
\tstatic function delete(\$table_using = null, \$alias = null)
\t{
\t\tif (!isset(\$alias)) \$alias = '';
\t\t\$q = self::db()->delete(self::table().' '.\$alias, \$table_using);
\t\t\$q->className = {$class_name_var};
\t\treturn \$q;
\t}

\t/** @return Db_Query_Mysql */
\tstatic function insert(\$fields = array(), \$alias = null)
\t{
\t\tif (!isset(\$alias)) \$alias = '';
\t\t\$q = self::db()->insert(self::table().' '.\$alias, \$fields);
\t\t\$q->className = {$class_name_var};
\t\treturn \$q;
\t}

\t/** @return Db_Query_Mysql */
\tstatic function insertManyAndExecute(\$records = array(), \$chunk_size = 1, \$alias = null)
\t{
\t\tif (!isset(\$alias)) \$alias = '';
\t\t\$q = self::db()->insertManyAndExecute(self::table().' '.\$alias, \$records, \$chunk_size);
\t\t\$q->className = {$class_name_var};
\t\treturn \$q;
\t}
\t
{$functions_string}
};
EOT;
        // Return the base class
        return $base_class_string;
        // unless the above line threw an exception
    }
Example #5
0
            break;
        default:
            echo "Wrong parameters to {$argv[$i]}\n{$usage}";
            exit;
    }
}
if (!$config->get('plugin', false)) {
    echo "You must supply --part argument\n{$usage}";
    exit;
}
if (!$config->get('connection', false)) {
    $config->set('connection', $config->get('plugin', ''));
}
if (!$config->get('class', false)) {
    $config->set('class', $config->get('connection', '') . '_' . $config->get('table', ''));
}
$class = $config->get('class', false);
$row = new $class();
$fields = array_fill_keys($row->getPrimaryKey(), 'md5');
foreach ($config->get('fields', array()) as $field => $hash) {
    if (isset($fields[$field])) {
        $fields[$field] = $hash;
    }
}
$config->set('fields', $fields);
try {
    echo 'Shards splitting script' . PHP_EOL;
    Db_Utils::split($config);
} catch (Exception $e) {
    die('[ERROR] ' . $e->getMessage() . PHP_EOL . (isset($trace) ? $e->getTraceAsString() . PHP_EOL : ''));
}