Index: branches/5.2.x/core/kernel/kbase.php =================================================================== diff -u -N -r13840 -r14095 --- branches/5.2.x/core/kernel/kbase.php (.../kbase.php) (revision 13840) +++ branches/5.2.x/core/kernel/kbase.php (.../kbase.php) (revision 14095) @@ -1,6 +1,6 @@ Application =& kApplication::Instance(); - } + protected $prefixSpecial = ''; /** - * Create new instance of object + * Set's references to kApplication and kDBConnection class instances * * @return kBase + * @access public + * @see kApplication + * @see kDBConnection */ - function &makeClass() + public function __construct($application = null) { - $object = new kBase(); - return $object; + if (!isset($application)) { + $this->Application =& kApplication::Instance(); + } + else { + $this->Application =& $application; + } + + $this->Conn =& $this->Application->GetADODBConnection(); } /** @@ -77,245 +89,259 @@ * @param string $special * @access public */ - function Init($prefix,$special,$event_params=null) + public function Init($prefix, $special) { - $prefix=explode('_',$prefix,2); - $this->Prefix=$prefix[0]; - $this->Special=$special; - $this->OriginalParams = $event_params; + $prefix = explode('_', $prefix, 2); + $this->Prefix = $prefix[0]; + $this->Special = $special; + + $this->prefixSpecial = rtrim($this->Prefix . '.' . $this->Special, '.'); } /** - * Returns joined prefix - * and special if any + * Returns prefix and special (when present) joined by a "." * - * @param bool $from_submit if true, then joins prefix & special by "_", uses "." otherwise * @return string - * @access protected + * @access public */ - function getPrefixSpecial($from_submit = false) + public function getPrefixSpecial() { - $separator = !$from_submit ? '.' : '_'; - $ret = $this->Prefix.$separator.$this->Special; - return rtrim($ret, $separator); + return $this->prefixSpecial; } +} - function &getProperty($property_name) +class kHelper extends kBase { + + /** + * Performs helper initialization + * + * @access public + */ + public function InitHelper() { - return $this->$property_name; + } - function setProperty($property_name, &$property_value) + /** + * Append prefix and special to tag + * params (get them from tagname) like + * they were really passed as params + * + * @param string $prefix_special + * @param Array $tag_params + * @return Array + * @access protected + */ + protected function prepareTagParams($prefix_special, $tag_params = Array()) { - $this->$property_name =& $property_value; - } -} + $parts = explode('.', $prefix_special); - class kHelper extends kBase { + $ret = $tag_params; + $ret['Prefix'] = $parts[0]; + $ret['Special'] = count($parts) > 1 ? $parts[1] : ''; + $ret['PrefixSpecial'] = $prefix_special; - /** - * Connection to database - * - * @var kDBConnection - * @access public - */ - var $Conn; - - function kHelper() - { - parent::kBase(); - $this->Conn =& $this->Application->GetADODBConnection(); - } - - function InitHelper() - { - - } - - /** - * Append prefix and special to tag - * params (get them from tagname) like - * they were really passed as params - * - * @param string $prefix_special - * @param Array $tag_params - * @return Array - * @access protected - */ - function prepareTagParams($prefix_special, $tag_params = Array()) - { - $parts = explode('.', $prefix_special); - - $ret = $tag_params; - $ret['Prefix'] = $parts[0]; - $ret['Special'] = count($parts) > 1 ? $parts[1] : ''; - $ret['PrefixSpecial'] = $prefix_special; - - return $ret; - } + return $ret; } +} -class kDBBase extends kBase { +abstract class kDBBase extends kBase { + /** - * Connection to database + * Name of primary key field for the unit * - * @var kDBConnection + * @var string * @access public + * @see kDBBase::TableName */ - var $Conn; + public $IDField = ''; /** - * Description + * Unit's database table name * - * @var string Name of primary key field for the item + * @var string * @access public */ - var $IDField; + public $TableName = ''; /** - * Holds SELECT, FROM, JOIN parts of SELECT query + * SELECT, FROM, JOIN parts of SELECT query (no filters, no limit, no ordering) * * @var string - * @access public + * @access protected */ - var $SelectClause; + protected $SelectClause = ''; /** - * Fields allowed to be set (from table + virtual) + * Unit fields definitions (fields from database plus virtual fields) * * @var Array - * @access private + * @access protected */ - var $Fields = Array(); + protected $Fields = Array (); /** - * Holds custom field names for item + * Mapping between unit custom field IDs and their names * * @var Array + * @access protected */ - var $customFields = Array(); + protected $customFields = Array (); /** - * All virtual field names + * Unit virtual field definitions * * @var Array - * @access private + * @access protected */ - var $VirtualFields = Array(); + protected $VirtualFields = Array (); /** * Fields that need to be queried using custom expression, e.g. IF(...) AS value * * @var Array - * @access private + * @access protected */ - var $CalculatedFields = Array(); + protected $CalculatedFields = Array (); /** - * Calculated fields, that contain aggregated functions, e.g. COUNT, SUM, etc. + * Fields that contain aggregated functions, e.g. COUNT, SUM, etc. * * @var Array + * @access protected */ - var $AggregatedCalculatedFields = Array(); + protected $AggregatedCalculatedFields = Array (); /** - * Description - * - * @var string Item' database table name, without prefix - * @access public - */ - var $TableName; - - /** - * Allows to determine object's table status ('temp' - temp table, '' - live table) + * Tells, that multilingual fields sould not be populated by default. + * Can be overriden from kDBBase::Configure method * - * @var string - * @access public + * @var bool + * @access protected */ - var $mode=''; + protected $populateMultiLangFields = false; - function kDBBase() - { - parent::kBase(); - $this->Conn =& $this->Application->GetADODBConnection(); - } - /** - * Set current item' database table name - * - * @access public - * @param string $table_name - * @return void - */ - function setTableName($table_name) - { - $this->TableName = $table_name; - } - - /** - * Set object' TableName to Live table from config + * Set object' TableName to LIVE table, defined in unit config * * @access public */ - function SwitchToLive() + public function SwitchToLive() { $this->TableName = $this->Application->getUnitOption($this->Prefix, 'TableName'); - $this->mode = ''; } /** - * Set object' TableName to Temp table from config + * Set object' TableName to TEMP table created based on table, defined in unit config * * @access public */ - function SwitchToTemp() + public function SwitchToTemp() { - $this->TableName = $this->Application->getUnitOption($this->Prefix, 'TableName'); - $this->SetTableName( $this->Application->GetTempName($this->TableName, 'prefix:'.$this->Prefix) ); - $this->mode = 't'; + $table_name = $this->Application->getUnitOption($this->Prefix, 'TableName'); + $this->TableName = $this->Application->GetTempName($table_name, 'prefix:' . $this->Prefix); } /** * Checks if object uses temp table * * @return bool + * @access public */ - function IsTempTable() + public function IsTempTable() { return $this->Application->IsTempTable($this->TableName); } /** * Sets SELECT part of list' query * - * @access public * @param string $sql SELECT and FROM [JOIN] part of the query up to WHERE - * @return void + * @access public */ - function SetSelectSQL($sql) + public function SetSelectSQL($sql) { $this->SelectClause = $sql; } - function GetSelectSQL($base_query = null) + /** + * Returns SELECT part of list' query. + * 1. Occurences of "%1$s" and "%s" are replaced to kDBBase::TableName + * 2. Occurences of "%3$s" are replaced to temp table prefix (only for table, using TABLE_PREFIX) + * + * @param string $base_query given base query will override unit default select query + * @param bool $replace_table replace all possible occurences + * @return string + * @access public + * @see kDBBase::replaceModePrefix + */ + public function GetSelectSQL($base_query = null, $replace_table = true) { if (!isset($base_query)) { $base_query = $this->SelectClause; } - $query = str_replace( Array('%1$s','%s'), $this->TableName, $base_query); - $query = $this->replaceModePrefix($query); + + if (!$replace_table) { + return $base_query; + } + + $query = str_replace(Array('%1$s', '%s'), $this->TableName, $base_query); + + return $this->replaceModePrefix($query); + } + + /** + * Allows substables to be in same mode as main item (e.g. LEFT JOINED ones) + * + * @param string $query + * @return string + * @access protected + */ + protected function replaceModePrefix($query) + { + $live_table = substr($this->Application->GetLiveName($this->TableName), strlen(TABLE_PREFIX)); + + if (preg_match('/'.preg_quote(TABLE_PREFIX, '/').'(.*)'.preg_quote($live_table, '/').'/', $this->TableName, $rets)) { + // will only happen, when table has a prefix (like in K4) + return str_replace('%3$s', $rets[1], $query); + } + + // will happen, when K3 table without prefix is used return $query; } + /** + * Sets calculated fields + * + * @param Array $fields + * @access public + */ + public function setCalculatedFields($fields) + { + $this->CalculatedFields = $fields; + } /** + * Adds calculated field declaration to object. + * + * @param string $name + * @param string $sql_clause + * @access public + */ + public function addCalculatedField($name, $sql_clause) + { + $this->CalculatedFields[$name] = $sql_clause; + } + + /** * Returns required mixing of aggregated & non-aggregated calculated fields * * @param int $aggregated 0 - having + aggregated, 1 - having only, 2 - aggregated only * @return Array + * @access public */ - function getCalculatedFields($aggregated = 0) + public function getCalculatedFields($aggregated = 1) { switch ($aggregated) { case 0: @@ -327,7 +353,7 @@ break; case 2: - $fields = $this->AggregatedCalculatedFields; + $fields = $this->AggregatedCalculatedFields; // TODO: never used break; default: @@ -339,14 +365,27 @@ } /** + * Checks, that given field is a calculated field + * + * @param string $field + * @return bool + * @access public + */ + public function isCalculatedField($field) + { + return array_key_exists($field, $this->CalculatedFields); + } + + /** * Insert calculated fields sql into query in place of %2$s, * return processed query. * * @param string $query * @param int $aggregated 0 - having + aggregated, 1 - having only, 2 - aggregated only * @return string + * @access protected */ - function addCalculatedFields($query, $aggregated = 1) + protected function addCalculatedFields($query, $aggregated = 1) { $fields = $this->getCalculatedFields($aggregated); if ($fields) { @@ -357,127 +396,151 @@ $sql[] = '('.$field_expression.') AS `'.$field_name.'`'; } $sql = implode(',',$sql); + return $this->Application->ReplaceLanguageTags( str_replace('%2$s', ','.$sql, $query) ); } - else { - return str_replace('%2$s', '', $query); - } + + return str_replace('%2$s', '', $query); } /** - * Allows substables to be in same mode as main item (e.g. LEFT JOINED ones) + * Performs initial object configuration, which includes setting the following: + * - primary key and table name + * - field definitions (including field modifiers, formatters, default values) * - * @param string $query - * @return string + * @param bool $populate_ml_fields create all ml fields from db in config or not + * @access public */ - function replaceModePrefix($query) + public function Configure($populate_ml_fields = null) { - $live_table = substr($this->Application->GetLiveName($this->TableName), strlen(TABLE_PREFIX)); - if (preg_match('/'.preg_quote(TABLE_PREFIX, '/').'(.*)'.preg_quote($live_table, '/').'/', $this->TableName, $rets)) { - // will only happen, when table has a prefix (like in K4) - return str_replace('%3$s', $rets[1], $query); + if ( isset($populate_ml_fields) ) { + $this->populateMultiLangFields = $populate_ml_fields; } - // will happen, when K3 table without prefix is used - return $query; + $this->IDField = $this->Application->getUnitOption($this->Prefix, 'IDField'); + $this->TableName = $this->Application->getUnitOption($this->Prefix, 'TableName'); + + $this->defineFields(); + + $this->ApplyFieldModifiers(); // should be called only after all fields definitions been set + $this->prepareConfigOptions(); // this should go last, but before setDefaultValues, order is significant! + + // only set on first call of method + if ( isset($populate_ml_fields) ) { + $this->SetDefaultValues(); + } } /** - * Adds calculated field declaration to object. + * Add field definitions from all possible sources + * Used field sources: database fields, custom fields, virtual fields, calculated fields, aggregated calculated fields * - * @param string $name - * @param string $sql_clause + * @access protected */ - function addCalculatedField($name, $sql_clause) + protected function defineFields() { - $this->CalculatedFields[$name] = $sql_clause; + $this->Fields = $this->Application->getUnitOption($this->Prefix, 'Fields', Array ()); + $this->customFields = $this->Application->getUnitOption($this->Prefix, 'CustomFields', Array()); + + $this->setVirtualFields( $this->Application->getUnitOption($this->Prefix, 'VirtualFields', Array ()) ); + + $calculated_fields = $this->Application->getUnitOption($this->Prefix, 'CalculatedFields', Array()); + $this->CalculatedFields = $this->getFieldsBySpecial($calculated_fields); + + $aggregated_calculated_fields = $this->Application->getUnitOption($this->Prefix, 'AggregatedCalculatedFields', Array()); + $this->AggregatedCalculatedFields = $this->getFieldsBySpecial($aggregated_calculated_fields); } /** - * Sets ID Field name used as primary key for loading items - * - * @access public - * @param string $field_name - * @return void - * @see kDBBase::IDField - */ - function setIDField($field_name) + * Only exteracts fields, that match current object Special + * + * @param Array $fields + * @return Array + * @access protected + */ + protected function getFieldsBySpecial($fields) { - $this->IDField = $field_name; + if ( array_key_exists($this->Special, $fields) ) { + return $fields[$this->Special]; + } + + return array_key_exists('', $fields) ? $fields[''] : Array(); } /** - * Performs initial object configuration + * Sets aggeregated calculated fields * - * @param bool $populate_ml_fields create all ml fields from db in config or not + * @param Array $fields + * @access public */ - function Configure($populate_ml_fields = false) + public function setAggregatedCalculatedFields($fields) { - $this->setTableName( $this->Application->getUnitOption($this->Prefix, 'TableName') ); - $this->setIDField( $this->Application->getUnitOption($this->Prefix, 'IDField') ); - - $this->defineFields(); - - $this->ApplyFieldModifiers(); // should be called only after all fields definitions been set - $this->prepareConfigOptions(); // this should go last, but before setDefaultValues, order is significant! - - $this->SetDefaultValues($populate_ml_fields); + $this->AggregatedCalculatedFields = $fields; } /** - * Add field definitions from all possible sources (DB Fields, Virtual Fields, Calcualted Fields, e.t.c.) + * Set's field names from table from config * + * @param Array $fields + * @access public */ - function defineFields() + public function setCustomFields($fields) { - $this->setConfigFields( $this->Application->getUnitOption($this->Prefix, 'Fields') ); - $this->setCustomFields( $this->Application->getUnitOption($this->Prefix, 'CustomFields', Array()) ); - $this->setVirtualFields( $this->Application->getUnitOption($this->Prefix, 'VirtualFields') ); - $this->setCalculatedFields( $this->Application->getUnitOption($this->Prefix, 'CalculatedFields', Array()) ); - $this->setAggragatedCalculatedFields( $this->Application->getUnitOption($this->Prefix, 'AggregatedCalculatedFields', Array()) ); + $this->customFields = $fields; } - function setCalculatedFields($fields) + /** + * Returns custom fields information from table from config + * + * @return Array + * @access public + */ + public function getCustomFields() { - $this->CalculatedFields = isset($fields[$this->Special]) ? $fields[$this->Special] : (isset($fields['']) ? $fields[''] : Array()); + return $this->customFields; } - function setAggragatedCalculatedFields($fields) + /** + * Set's fields information from table from config + * + * @param Array $fields + * @access public + */ + public function setFields($fields) { - $this->AggregatedCalculatedFields = isset($fields[$this->Special]) ? $fields[$this->Special] : (isset($fields['']) ? $fields[''] : Array()); + $this->Fields = $fields; } /** - * Set's field names from table - * from config + * Returns fields information from table from config * - * @param Array $fields + * @return Array * @access public */ - function setCustomFields($fields) + public function getFields() { - $this->customFields = $fields; + return $this->Fields; } /** - * Set's field names from table - * from config + * Checks, that given field exists * - * @param Array $fields + * @param string $field + * @return bool * @access public */ - function setConfigFields($fields) + public function isField($field) { - $this->Fields = $fields; + return array_key_exists($field, $this->Fields); } /** * Override field options with ones defined in submit via "field_modfiers" array (common for all prefixes) * - * @access private * @author Alex + * @access public */ - function ApplyFieldModifiers($field_modifiers = null) + public function ApplyFieldModifiers($field_modifiers = null) { $allowed_modifiers = Array('required', 'multiple'); @@ -517,46 +580,135 @@ * @param Array $fields * @access public */ - function setVirtualFields($fields) + public function setVirtualFields($fields) { - if($fields) - { + if ($fields) { $this->VirtualFields = $fields; $this->Fields = array_merge($this->VirtualFields, $this->Fields); -// $this->Fields = array_merge_recursive2($this->VirtualFields, $this->Fields); } } - function SetDefaultValues($populate_ml_fields = false) + /** + * Returns virtual fields + * + * @return Array + * @access public + */ + public function getVirtualFields() { - foreach($this->Fields as $field => $options) - { - if( isset($options['default']) && $options['default'] === '#NOW#') - { + return $this->VirtualFields; + } + + /** + * Checks, that given field is a virtual field + * + * @param string $field + * @return bool + * @access public + */ + public function isVirtualField($field) + { + return array_key_exists($field, $this->VirtualFields); + } + + /** + * Performs additional initialization for field default values + * + * @access protected + */ + protected function SetDefaultValues() + { + foreach ($this->Fields as $field => $options) { + if ( array_key_exists('default', $options) && $options['default'] === '#NOW#' ) { $this->Fields[$field]['default'] = adodb_mktime(); } } } - function SetFieldOptions($field, $options) + /** + * Overwrites field definition in unit config + * + * @param string $field + * @param Array $options + * @param bool $is_virtual + * @access public + */ + public function SetFieldOptions($field, $options, $is_virtual = false) { - $this->Fields[$field] = $options; + if ($is_virtual) { + $this->VirtualFields[$field] = $options; + $this->Fields = array_merge($this->VirtualFields, $this->Fields); + } + else { + $this->Fields[$field] = $options; + } } - function GetFieldOptions($field) + /** + * Changes/sets given option's value in given field definiton + * + * @param string $field + * @param string $option_name + * @param mixed $option_value + * @param bool $is_virtual + * @access public + */ + public function SetFieldOption($field, $option_name, $option_value, $is_virtual = false) { - if (isset($this->Fields[$field])) { - $options_prepared = array_key_exists('options_prepared', $this->Fields[$field]) ? $this->Fields[$field]['options_prepared'] : false; - if (!$options_prepared) { + if ($is_virtual) { + $this->VirtualFields[$field][$option_name] = $option_value; + } + + $this->Fields[$field][$option_name] = $option_value; + } + + /** + * Returns field definition from unit config. + * Also executes sql from "options_sql" field option to form "options" field option + * + * @param string $field + * @param bool $is_virtual + * @return Array + * @access public + */ + public function GetFieldOptions($field, $is_virtual = false) + { + $property_name = $is_virtual ? 'VirtualFields' : 'Fields'; + + if ( !array_key_exists($field, $this->$property_name) ) { + return Array (); + } + + if (!$is_virtual) { + if (!array_key_exists('options_prepared', $this->Fields[$field]) || !$this->Fields[$field]['options_prepared']) { + // executes "options_sql" from field definition, only when field options are accessed (late binding) $this->PrepareFieldOptions($field); $this->Fields[$field]['options_prepared'] = true; } + } - return $this->Fields[$field]; + return $this->{$property_name}[$field]; + } + + /** + * Returns field option + * + * @param string $field + * @param string $option_name + * @param string $is_virtual + * @param string $default + * @return string + * @access public + */ + public function GetFieldOption($field, $option_name, $is_virtual = false, $default = false) + { + $field_options = $this->GetFieldOptions($field, $is_virtual); + + if (!$field_options) { + return $field_options; } - else { - return Array(); - } + + return array_key_exists($option_name, $field_options) ? $field_options[$option_name] : $default; } /** @@ -566,9 +718,9 @@ * @param string $format * * @return string - * @access public + * @access protected */ - function GetField($name, $format = null) + public function GetField($name, $format = null) { $options = $this->GetFieldOptions($name); @@ -583,17 +735,40 @@ return $this->GetDBField($name); } - function HasField($name) - { + /** + * Returns unformatted field value + * + * @param string $field + * @return string + * @access protected + */ + abstract protected function GetDBField($field); - } + /** + * Checks of object has given field + * + * @param string $name + * @return bool + * @access protected + */ + abstract protected function HasField($name); - function GetFieldValues() - { + /** + * Returns field values + * + * @return Array + * @access protected + */ + abstract protected function GetFieldValues(); - } - - function UpdateFormattersSubFields($fields=null) + /** + * Populates values of sub-fields, based on formatters, set to mater fields + * + * @param Array $fields + * @access public + * @todo maybe should not be publically accessable + */ + public function UpdateFormattersSubFields($fields = null) { if (!is_array($fields)) { $fields = array_keys($this->Fields); @@ -606,12 +781,24 @@ } } - function prepareConfigOptions() + /** + * Use formatters, specified in field declarations to perform additional field initialization in unit config + * + * @access protected + */ + protected function prepareConfigOptions() { - foreach (array_keys($this->Fields) as $field_name) - { -// $this->PrepareFieldOptions($field_name); - $this->PrepareOptions($field_name); + $field_names = array_keys($this->Fields); + + foreach ($field_names as $field_name) { + if ( !array_key_exists('formatter', $this->Fields[$field_name]) ) { + continue; + } + + $formatter =& $this->Application->recallObject( $this->Fields[$field_name]['formatter'] ); + /* @var $formatter kFormatter */ + + $formatter->PrepareOptions($field_name, $this->Fields[$field_name], $this); } } @@ -620,19 +807,21 @@ * * @param string $field_expr * @return string + * @access protected */ - function escapeField($field_expr) + protected function escapeField($field_expr) { - return preg_match('/[.(]/', $field_expr) ? $field_expr : '`'.$field_expr.'`'; + return preg_match('/[.(]/', $field_expr) ? $field_expr : '`' . $field_expr . '`'; } /** * Replaces current language id in given field options * * @param string $field_name * @param Array $field_option_names + * @access protected */ - function _replaceLanguageId($field_name, $field_option_names) + protected function _replaceLanguageId($field_name, $field_option_names) { // don't use GetVar('m_lang') since it's always equals to default language on editing form in admin $current_language_id = $this->Application->Phrases->LanguageId; @@ -645,7 +834,13 @@ } } - function PrepareFieldOptions($field_name) + /** + * Transforms "options_sql" field option into valid "options" array for given field + * + * @param string $field_name + * @access protected + */ + protected function PrepareFieldOptions($field_name) { $field_options =& $this->Fields[$field_name]; if (array_key_exists('options_sql', $field_options) ) { @@ -673,38 +868,17 @@ } $options_hash = array_key_exists('options', $field_options) ? $field_options['options'] : Array (); - $field_options['options'] = array_merge_recursive2($options_hash, $dynamic_options); + $field_options['options'] = kUtil::array_merge_recursive($options_hash, $dynamic_options); // because of numeric keys } } - function PrepareOptions($field_name) - { - if ( isset($this->Fields[$field_name]['formatter']) ) - { - $formatter =& $this->Application->recallObject( $this->Fields[$field_name]['formatter'] ); - $formatter->PrepareOptions($field_name, $this->Fields[$field_name], $this); - } - } - /** - * Returns unformatted field value - * - * @param string $field - * @return string - * @access public - */ - function GetDBField($field) - { - - } - - /** * Returns ID of currently processed record * * @return int * @access public */ - function GetID() + public function GetID() { return $this->GetDBField($this->IDField); } @@ -713,21 +887,22 @@ * Allows kDBTagProcessor.SectionTitle to detect if it's editing or new item creation * * @return bool + * @access public */ - function IsNewItem() + public function IsNewItem() { return $this->GetID() ? false : true; } /** * Returns parent table information * - * @param bool $from_temp load parent item from temp table * @param string $special special of main item * @param bool $guess_special if object retrieved with specified special is not loaded, then try not to use special * @return Array + * @access public */ - function getLinkedInfo($special = '', $guess_special = false) + public function getLinkedInfo($special = '', $guess_special = false) { $parent_prefix = $this->Application->getUnitOption($this->Prefix, 'ParentPrefix'); if ($parent_prefix) { @@ -762,25 +937,20 @@ } /** - * Returns true if item was queried/loaded + * Returns true, when list/item was queried/loaded * * @return bool + * @access protected */ - function isLoaded() - { - return false; - } + abstract protected function isLoaded(); /** * Returns specified field value from all selected rows. * Don't affect current record index * * @param string $field * @return Array + * @access protected */ - function GetCol($field) - { - return Array (); - } - + abstract protected function GetCol($field); } \ No newline at end of file