/** * Stores the data in \a $obj to database. * * a little modification - stores tehe placeholdervalues for the virtual list so we now if we should get * a value from parent list * * fieldFilters If specified only certain fields will be stored. * \note Transaction unsafe. If you call several transaction unsafe methods you must enclose * the calls within a db transaction; thus within db->begin and db->commit. * * @param unknown_type $obj * @param unknown_type $fieldFilters * @see eZPersistentObject::storeObject */ static function storeObject($obj, $fieldFilters = null) { $db = eZDB::instance(); $useFieldFilters = isset($fieldFilters) && is_array($fieldFilters) && $fieldFilters; $def = $obj->definition(); $fields = $def["fields"]; $keys = $def["keys"]; $table = $def["name"]; $relations = isset($def["relations"]) ? $def["relations"] : null; $insert_object = false; $exclude_fields = array(); foreach ($keys as $key) { // $value = $obj->attribute( $key ); $value = $obj->attributeContentToStore($key); if ($value === null) { $insert_object = true; $exclude_fields[] = $key; } } if ($useFieldFilters) { $insert_object = false; } $use_fields = array_diff(array_keys($fields), $exclude_fields); // If we filter out some of the fields we need to intersect it with $use_fields if (is_array($fieldFilters)) { $use_fields = array_intersect($use_fields, $fieldFilters); } $doNotEscapeFields = array(); $changedValueFields = array(); $numericDataTypes = array('integer', 'float', 'double'); foreach ($use_fields as $field_name) { $field_def = $fields[$field_name]; //$value = $obj->attribute( $field_name ); $value = $obj->attributeContentToStore($field_name); if ($value === null) { if (!is_array($field_def)) { $exclude_fields[] = $field_name; } else { if (array_key_exists('default', $field_def) && ($field_def['default'] !== null || $field_name == 'data_int' && array_key_exists('required', $field_def) && $field_def['required'] == false)) { $obj->setAttribute($field_name, $field_def['default']); } else { //if ( in_array( $field_def['datatype'], $numericDataTypes ) $exclude_fields[] = $field_name; } } } if (strlen($value) == 0 && is_array($field_def) && in_array($field_def['datatype'], $numericDataTypes) && array_key_exists('default', $field_def) && ($field_def['default'] === null || is_numeric($field_def['default']))) { $obj->setAttribute($field_name, $field_def['default']); } if ($value !== null && $field_def['datatype'] === 'string' && array_key_exists('max_length', $field_def) && $field_def['max_length'] > 0 && strlen($value) > $field_def['max_length']) { $obj->setAttribute($field_name, substr($value, 0, $field_def['max_length'])); eZDebug::writeDebug($value, "truncation of {$field_name} to max_length=" . $field_def['max_length']); } $bindDataTypes = array('text'); if ($db->bindingType() != eZDBInterface::BINDING_NO && strlen($value) > 2000 && is_array($field_def) && in_array($field_def['datatype'], $bindDataTypes)) { $boundValue = $db->bindVariable($value, $field_def); // $obj->setAttribute( $field_name, $value ); $doNotEscapeFields[] = $field_name; $changedValueFields[$field_name] = $boundValue; } } $key_conds = array(); foreach ($keys as $key) { //$value = $obj->attribute( $key ); $value = $obj->attributeContentToStore($key); $key_conds[$key] = $value; } unset($value); $important_keys = $keys; if (is_array($relations)) { // $important_keys = array(); foreach ($relations as $relation => $relation_data) { if (!in_array($relation, $keys)) { $important_keys[] = $relation; } } } if (count($important_keys) == 0 && !$useFieldFilters) { $insert_object = true; } else { if (!$insert_object) { $rows = eZPersistentObject::fetchObjectList($def, $keys, $key_conds, array(), null, false, null, null); if (count($rows) == 0) { /* If we only want to update some fields in a record * and that records does not exist, then we should do nothing, only return. */ if ($useFieldFilters) { return; } $insert_object = true; } } } if ($insert_object) { // Note: When inserting we cannot hone the $fieldFilters parameters $use_fields = array_diff(array_keys($fields), $exclude_fields); $use_field_names = $use_fields; if ($db->useShortNames()) { $use_short_field_names = $use_field_names; eZPersistentObject::replaceFieldsWithShortNames($db, $fields, $use_short_field_names); $field_text = implode(', ', $use_short_field_names); unset($use_short_field_names); } else { $field_text = implode(', ', $use_field_names); } $use_values_hash = array(); $escapeFields = array_diff($use_fields, $doNotEscapeFields); foreach ($escapeFields as $key) { //$value = $obj->attribute( $key ); $value = $obj->attributeContentToStore($key); $field_def = $fields[$key]; if ($field_def['datatype'] == 'float' || $field_def['datatype'] == 'double') { if ($value === null) { $use_values_hash[$key] = 'NULL'; } else { $use_values_hash[$key] = sprintf('%F', $value); } } else { if ($field_def['datatype'] == 'int' || $field_def['datatype'] == 'integer') { if ($value === null) { $use_values_hash[$key] = 'NULL'; } else { $use_values_hash[$key] = sprintf('%d', $value); } } else { // Note: for more colherence, we might use NULL for sql strings if the php value is NULL and not an empty sring // but to keep compatibility with ez db, where most string columns are "not null default ''", // and code feeding us a php null value without meaning it, we do not. $use_values_hash[$key] = "'" . $db->escapeString($value) . "'"; } } } foreach ($doNotEscapeFields as $key) { $use_values_hash[$key] = $changedValueFields[$key]; } $use_values = array(); foreach ($use_field_names as $field) { $use_values[] = $use_values_hash[$field]; } unset($use_values_hash); $value_text = implode(", ", $use_values); $sql = "INSERT INTO {$table} ({$field_text}) VALUES({$value_text})"; $db->query($sql); if (isset($def["increment_key"]) && is_string($def["increment_key"]) && !($obj->attributeContentToStore($def["increment_key"]) > 0)) { $inc = $def["increment_key"]; $id = $db->lastSerialID($table, $inc); if ($id !== false) { $obj->setAttribute($inc, $id); } } } else { $use_fields = array_diff(array_keys($fields), array_merge($keys, $exclude_fields)); if (count($use_fields) > 0) { // If we filter out some of the fields we need to intersect it with $use_fields if (is_array($fieldFilters)) { $use_fields = array_intersect($use_fields, $fieldFilters); } $use_field_names = array(); foreach ($use_fields as $key) { if ($db->useShortNames() && is_array($fields[$key]) && array_key_exists('short_name', $fields[$key]) && strlen($fields[$key]['short_name']) > 0) { $use_field_names[$key] = $fields[$key]['short_name']; } else { $use_field_names[$key] = $key; } } $field_text = ""; $field_text_len = 0; $i = 0; foreach ($use_fields as $key) { //$value = $obj->attribute( $key ); $value = $obj->attributeContentToStore($key); if ($fields[$key]['datatype'] == 'float' || $fields[$key]['datatype'] == 'double') { if ($value === null) { $field_text_entry = $use_field_names[$key] . '=NULL'; } else { $field_text_entry = $use_field_names[$key] . "=" . sprintf('%F', $value); } } else { if ($fields[$key]['datatype'] == 'int' || $fields[$key]['datatype'] == 'integer') { if ($value === null) { $field_text_entry = $use_field_names[$key] . '=NULL'; } else { $field_text_entry = $use_field_names[$key] . "=" . sprintf('%d', $value); } } else { if (in_array($use_field_names[$key], $doNotEscapeFields)) { $field_text_entry = $use_field_names[$key] . "=" . $changedValueFields[$key]; } else { $field_text_entry = $use_field_names[$key] . "='" . $db->escapeString($value) . "'"; } } } $field_text_len += strlen($field_text_entry); $needNewline = false; if ($field_text_len > 60) { $needNewline = true; $field_text_len = 0; } if ($i > 0) { $field_text .= "," . ($needNewline ? "\n " : ' '); } $field_text .= $field_text_entry; ++$i; } $cond_text = eZPersistentObject::conditionText($key_conds); $sql = "UPDATE {$table} SET {$field_text}{$cond_text}"; $db->query($sql); } } $obj->setHasDirtyData(false); }
/** * Creates an SQL query out of the different parameters and returns an array with the result. * * A full example: * <code> * $filter = array( 'id', 'name' ); * $conds = array( 'type' => 5, * 'size' => array( false, array( 200, 500 ) ) ); * $sorts = array( 'name' => 'asc' ); * $limit = array( 'offset' => 50, 'length' => 10 ); * eZPersistentObject::fetchObjectList( $def, $filter, $conds, $sorts, $limit, true, false, null ) * </code> * * Counting number of elements. * <code> * $custom = array( array( 'operation' => 'count( id )', * 'name' => 'count' ) ); * // Here $field_filters is set to an empty array, that way only count is used in fields * $rows = eZPersistentObject::fetchObjectList( $def, array(), null, null, null, false, false, $custom ); * return $rows[0]['count']; * </code> * * Counting elements per type using grouping * <code> * $custom = array( array( 'operation' => 'count( id )', * 'name' => 'count' ) ); * $group = array( 'type' ); * $rows = eZPersistentObject::fetchObjectList( $def, array(), null, null, null, false, $group, $custom ); * return $rows[0]['count']; * </code> * * Example to fetch a result with custom conditions. The following example will fetch the attributes to * the contentobject with id 1 and add the contentobject.name in each attribute row with the array key * contentobject_name. * <code> * $objectDef = eZContentObject::definition(); * $objectAttributeDef = eZContentObjectAttribute::definition(); * * $fields = array(); * $conds = array( $objectDef['name'] . '.id' => 1 ); * $sorts = array( $objectAttributeDef['name'] . '.sort_key_string' => 'asc' ); * * $limit = null; * $asObject = false; * $group = false; * * $customFields = array( $objectAttributeDef['name'] . '.*', * array( 'operation' => $objectDef['name'] . '.name', * 'name' => 'contentobject_name' ) ); * * $customTables = array( $objectDef['name'] ); * * $languageCode = 'eng-GB'; * $customConds = ' AND ' . $objectDef['name'] . '.current_version=' . $objectAttributeDef['name'] . '.version' . * ' AND ' . $objectDef['name'] . '.id=' . $objectAttributeDef['name'] . '.contentobject_id' . * ' AND ' . $objectAttributeDef['name'] . '.language_code=\'' . $languageCode . '\''; * * $rows = eZPersistentObject::fetchObjectList( $objectAttributeDef, $fields, $conds, $sorts, $limit, $asObject, * $group, $customFields, $customTables, $customConds ); * </code> * * @param array $def A definition array of all fields, table name and sorting (see {@link eZPersistentObject::definition()} for more info) * @param array|null $field_filters If defined determines the fields which are extracted (array of field names), if not all fields are fetched * @param array|null $conds null for no special condition or an associative array of fields to filter on. * Syntax is FIELD => CONDITION * CONDITION can be one of: * - Scalar value: Creates a condition where FIELD must match the value, e.g * <code>array( 'id' => 5 )</code> generates <code>SQL id = 5</code> * - Array with two scalar values: The first value is the match operator, the second is the scalar value * <code>array( 'priority' => array( '>', 5 ) )</code> generates SQL <code>priority > 5</code> * - Array with range: The first value is <code>false</code>, the second value is an array with start and stop of range in array * <code>array( 'type' => array( false, array( 1, 5 ) ) )</code> generates SQL <code>type BETWEEN 1 AND 5</code> * - Array with multiple elements: The first value is the field identifier, the second is an array with scalar values * <code>array( 'id' => array( array( 1, 5, 7 ) ) )</code> generates SQL <code>id IN ( 1, 5, 7 )</code> * @param array|null|bool $sorts An associative array of sorting conditions, if set to false ignores settings in $def, if set to null uses settingss in $def. * Syntax is FIELD => DIRECTION. * DIRECTION must either be 'asc' for ascending or 'desc' for descending. * @param array|null $limit An associative array with limitiations, can contain * - 'offset': Numerical value defining the start offset for the fetch * - 'length': Numerical value defining the max number of items to return * @param bool $asObject If true then it will return an array with objects, objects are created from class defined in $def. * If falseit will just return the rows fetch from database. * @param array|null|bool $grouping An array of fields to group by or null to use grouping in defintion $def. * @param null $custom_fields Array of FIELD elements to add to SQL, can be used to perform custom fetches, e.g counts. * FIELD is an associative array containing: * - 'operation': A text field which is included in the field list * - 'name': If present it adds <code>AS name</code> to the operation. * @param array|null $custom_tables Array of additional tables * @param string|null $custom_conds String with sql conditions for 'WHERE' clause. * @return eZPersistentObject[]|array|null An array of objects or rows, null on error */ public static function fetchObjectList($def, $field_filters = null, $conds = null, $sorts = null, $limit = null, $asObject = true, $grouping = false, $custom_fields = null, $custom_tables = null, $custom_conds = null) { $db = eZDB::instance(); $fields = $def["fields"]; $tables = $def["name"]; $class_name = $def["class_name"]; if (is_array($custom_tables)) { foreach ($custom_tables as $custom_table) { $tables .= ', ' . $db->escapeString($custom_table); } } eZPersistentObject::replaceFieldsWithShortNames($db, $fields, $conds); if (is_array($field_filters)) { $field_array = array_unique(array_intersect($field_filters, array_keys($fields))); } else { $field_array = array_keys($fields); } if ($custom_fields !== null and is_array($custom_fields)) { foreach ($custom_fields as $custom_field) { if (is_array($custom_field)) { $custom_text = $custom_field["operation"]; if (isset($custom_field["name"])) { $field_name = $custom_field["name"]; $custom_text .= " AS {$field_name}"; } } else { $custom_text = $custom_field; } $field_array[] = $custom_text; } } eZPersistentObject::replaceFieldsWithShortNames($db, $fields, $field_array); $field_text = ''; $i = 0; foreach ($field_array as $field_item) { if ($i % 7 == 0 and $i > 0) { $field_text .= ", "; } else { if ($i > 0) { $field_text .= ', '; } } $field_text .= $field_item; ++$i; } $where_text = eZPersistentObject::conditionText($conds); if ($custom_conds) { $where_text .= $custom_conds; } $sort_text = ""; if ($sorts !== false and (isset($def["sort"]) or is_array($sorts))) { $sort_list = array(); if (is_array($sorts)) { $sort_list = $sorts; } else { if (isset($def['sort'])) { $sort_list = $def["sort"]; } } if (count($sort_list) > 0) { $sort_text = " ORDER BY "; $i = 0; foreach ($sort_list as $sort_id => $sort_type) { if ($i > 0) { $sort_text .= ", "; } if ($sort_type == "desc") { $sort_text .= "{$sort_id} DESC"; } else { $sort_text .= "{$sort_id} ASC"; } ++$i; } } } $grouping_text = ""; if (isset($def["grouping"]) or is_array($grouping) and count($grouping) > 0) { $grouping_list = isset($def["grouping"]) ? $def["grouping"] : array(); if (is_array($grouping)) { $grouping_list = $grouping; } if (count($grouping_list) > 0) { $grouping_text = " GROUP BY "; $i = 0; foreach ($grouping_list as $grouping_id) { if ($i > 0) { $grouping_text .= ", "; } $grouping_text .= "{$grouping_id}"; ++$i; } } } $db_params = array(); if (is_array($limit)) { if (isset($limit["offset"])) { $db_params["offset"] = $limit["offset"]; } if (isset($limit['limit'])) { $db_params["limit"] = $limit["limit"]; } else { $db_params["limit"] = $limit["length"]; } } $sqlText = "SELECT {$field_text}\n FROM {$tables}" . $where_text . $grouping_text . $sort_text; $rows = $db->arrayQuery($sqlText, $db_params); // Indicate that a DB error occured. if ($rows === false) { return null; } $objectList = eZPersistentObject::handleRows($rows, $class_name, $asObject); return $objectList; }