/** * 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 }
/** * 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 }