* * @param array $_REQUEST Request parameters */ fn_set_hook('prepare_product_quick_view', $_REQUEST); return true; } function fn_get_product_pagination_steps($cols, $products_per_page) { $min_range = $cols * 4; $max_ranges = 4; $steps = array(); for ($i = 0; $i < $max_ranges; $i++) { $steps[] = $min_range; $min_range = $min_range * 2; } $steps[] = (int) $products_per_page; $steps = array_unique($steps); sort($steps, SORT_NUMERIC); return $steps; } function fn_get_product_option_data($option_id, $product_id, $lang_code = DESCR_SL) { $extra_variant_fields = ''; $fields = "a.*, b.option_name, b.option_text, b.description, b.inner_hint, b.incorrect_message, b.comment"; $join = db_quote(" LEFT JOIN ?:product_options_descriptions as b ON a.option_id = b.option_id AND b.lang_code = ?s" . " LEFT JOIN ?:product_global_option_links as c ON c.option_id = a.option_id", $lang_code); $condition = db_quote("a.option_id = ?i AND a.product_id = ?i", $option_id, $product_id); /** * Changes params before option data selecting * * @param int $option_id Option identifier * @param int $product_id Product identifier * @param string $fields Fields to be selected * @param string $condition String containing SQL-query condition possibly prepended with a logical operator (AND or OR) * @param string $join String with the complete JOIN information (JOIN type, tables and fields) for an SQL-query * @param string $extra_variant_fields Additional variant fields to be selected * @param string $lang_code 2-letters language code */ fn_set_hook('get_product_option_data_pre', $option_id, $product_id, $fields, $condition, $join, $extra_variant_fields, $lang_code); $opt = db_get_row( "SELECT " . $fields . " FROM ?:product_options as a" . $join . " WHERE " . $condition . " ORDER BY a.position" ); if (!empty($opt)) { $_cond = ($opt['option_type'] == 'C') ? ' AND a.position = 1' : ''; $join = ''; if (fn_allowed_for('ULTIMATE') && Registry::get('runtime.company_id')) { $extra_variant_fields .= 'IF(shared_option_variants.variant_id IS NOT NULL, shared_option_variants.modifier, a.modifier) as modifier, '; $extra_variant_fields .= 'IF(shared_option_variants.variant_id IS NOT NULL, shared_option_variants.modifier_type, a.modifier_type) as modifier_type, '; $join .= db_quote(' LEFT JOIN ?:ult_product_option_variants shared_option_variants ON shared_option_variants.variant_id = a.variant_id AND shared_option_variants.company_id = ?i', Registry::get('runtime.company_id')); } $opt['variants'] = db_get_hash_array("SELECT a.variant_id, a.position, a.modifier, a.modifier_type, a.weight_modifier, a.weight_modifier_type, a.status, $extra_variant_fields b.variant_name FROM ?:product_option_variants as a LEFT JOIN ?:product_option_variants_descriptions as b ON a.variant_id = b.variant_id AND b.lang_code = ?s $join WHERE a.option_id = ?i $_cond ORDER BY a.position", 'variant_id', $lang_code, $option_id); if (!empty($opt['variants'])) { foreach ($opt['variants'] as $k => $v) { $opt['variants'][$k]['image_pair'] = fn_get_image_pairs($v['variant_id'], 'variant_image', 'V', true, true, $lang_code); } } } /** * Changes option data * * @param array $opt Option data * @param int $product_id Product identifier * @param string $lang_code 2-letters language code */ fn_set_hook('get_product_option_data_post', $opt, $product_id, $lang_code); return $opt; } /** * Product fields for multi update * * @return array Product fields */ function fn_get_product_fields() { $fields = array( array( 'name' => '[data][popularity]', 'text' => __('popularity') ), array( 'name' => '[data][status]', 'text' => __('status'), 'disabled' => 'Y' ), array( 'name' => '[data][product]', 'text' => __('product_name'), 'disabled' => 'Y' ), array( 'name' => '[data][price]', 'text' => __('price') ), array( 'name' => '[data][list_price]', 'text' => __('list_price') ), array( 'name' => '[data][short_description]', 'text' => __('short_description') ), array( 'name' => '[data][promo_text]', 'text' => __('promo_text') ), array( 'name' => '[categories]', 'text' => __('categories') ), array( 'name' => '[data][full_description]', 'text' => __('full_description') ), array( 'name' => '[data][search_words]', 'text' => __('search_words') ), array( 'name' => '[data][meta_keywords]', 'text' => __('meta_keywords') ), array( 'name' => '[data][meta_description]', 'text' => __('meta_description') ), array( 'name' => '[main_pair]', 'text' => __('image_pair') ), array( 'name' => '[data][min_qty]', 'text' => __('min_order_qty') ), array( 'name' => '[data][max_qty]', 'text' => __('max_order_qty') ), array( 'name' => '[data][qty_step]', 'text' => __('quantity_step') ), array( 'name' => '[data][list_qty_count]', 'text' => __('list_quantity_count') ), array( 'name' => '[data][product_code]', 'text' => __('sku') ), array( 'name' => '[data][weight]', 'text' => __('weight') ), array( 'name' => '[data][shipping_freight]', 'text' => __('shipping_freight') ), array( 'name' => '[data][free_shipping]', 'text' => __('free_shipping') ), array( 'name' => '[data][zero_price_action]', 'text' => __('zero_price_action') ), array( 'name' => '[data][taxes]', 'text' => __('taxes') ), array( 'name' => '[data][features]', 'text' => __('features') ), array( 'name' => '[data][page_title]', 'text' => __('page_title') ), array( 'name' => '[data][timestamp]', 'text' => __('creation_date') ), array( 'name' => '[data][amount]', 'text' => __('quantity') ), array( 'name' => '[data][avail_since]', 'text' => __('available_since') ), array( 'name' => '[data][out_of_stock_actions]', 'text' => __('out_of_stock_actions') ), array( 'name' => '[data][details_layout]', 'text' => __('product_details_view') ), array( 'name' => '[data][min_items_in_box]', 'text' => __('minimum_items_in_box') ), array( 'name' => '[data][max_items_in_box]', 'text' => __('maximum_items_in_box') ), array( 'name' => '[data][box_length]', 'text' => __('box_length') ), array( 'name' => '[data][box_width]', 'text' => __('box_width') ), array( 'name' => '[data][box_height]', 'text' => __('box_height') ), ); if (Registry::get('settings.General.enable_edp') == 'Y') { $fields[] = array( 'name' => '[data][is_edp]', 'text' => __('downloadable') ); $fields[] = array( 'name' => '[data][edp_shipping]', 'text' => __('edp_enable_shipping') ); } if (!fn_allowed_for('ULTIMATE:FREE')) { if (Registry::get('config.tweaks.disable_localizations') == false) { $fields[] = array( 'name' => '[data][localization]', 'text' => __('localization') ); } $fields[] = array( 'name' => '[data][usergroup_ids]', 'text' => __('usergroups') ); } if (Registry::get('settings.General.inventory_tracking') == "Y") { $fields[] = array( 'name' => '[data][tracking]', 'text' => __('inventory') ); } if (fn_allowed_for('ULTIMATE,MULTIVENDOR') && !Registry::get('runtime.company_id')) { $fields[] = array( 'name' => '[data][company_id]', 'text' => fn_allowed_for('MULTIVENDOR') ? __('vendor') : __('store') ); } /** * Hook for change fields array * * @param array $fields Product fields */ fn_set_hook('get_product_fields', $fields); return $fields; } /** * Get product code by product_id * * @param int $product_id * @param array $product_options * @return (string) product code */ function fn_get_product_code($product_id, $product_options = array()) { if (!empty($product_id)) { $tracking = db_get_field("SELECT tracking FROM ?:products WHERE product_id = ?i", $product_id); $data['extra']['product_options'] = (array) $product_options; $combination_hash = fn_generate_cart_id($product_id, $data['extra']); $product_code = db_get_field("SELECT product_code FROM ?:product_options_inventory WHERE combination_hash = ?i AND product_id = ?i", $combination_hash, $product_id); if (empty($product_code) || $tracking != ProductTracking::TRACK_WITH_OPTIONS) { $product_code = db_get_field("SELECT product_code FROM ?:products WHERE product_id = ?i", $product_id); } return $product_code; } return ''; } function fn_get_product_sku($product_id) { if (!empty($product_id)) { $product_sku = db_get_field("SELECT sku FROM ?:products WHERE product_id = ?i", $product_id); return $product_sku; } return ''; } function fn_get_product_counts_by_category($params, $lang_code = CART_LANGUAGE) { $default_params = array( 'company_id' => 0, 'sort_by' => 'position', 'sort_order' => 'asc', ); $params = array_merge($default_params, $params); $sort_fields = array( 'position' => '?:categories.position', 'category' => '?:category_descriptions.category', 'count' => 'count', ); $sort = db_sort($params, $sort_fields, $default_params['sort_by'], $default_params['sort_order']); $condition = $join = ''; if (!empty($params['company_id'])) { if (is_array($params['company_id'])) { $condition .= db_quote(" AND ?:products.company_id IN (?n) ", $params['company_id']); } else { $condition .= db_quote(" AND ?:products.company_id = ?i ", $params['company_id']); } } $condition .= db_quote(" AND ?:category_descriptions.lang_code = ?s ", $lang_code); $join .= 'JOIN ?:products ON ?:products_categories.product_id = ?:products.product_id '; $join .= 'JOIN ?:categories ON ?:products_categories.category_id = ?:categories.category_id '; $join .= 'JOIN ?:category_descriptions ON ?:products_categories.category_id = ?:category_descriptions.category_id '; $result = db_get_array("SELECT COUNT(*) as count, ?:category_descriptions.category, ?:category_descriptions.category_id FROM ?:products_categories ?p WHERE 1 ?p GROUP BY ?:products_categories.category_id ?p", $join, $condition, $sort); return $result; } /** * Gets categefories and products totals data * * @return array Array with categories and products totals */ function fn_get_categories_stats() { $stats = array(); $params = array( 'only_short_fields' => true, // NOT NEEDED AT ALL BECAUSE WE DONT USE RESULTING $FIELDS 'extend' => array('companies', 'sharing'), 'get_conditions' => true, ); list($fields, $join, $condition) = fn_get_products($params); db_query('SELECT SQL_CALC_FOUND_ROWS 1 FROM ?:products AS products' . $join . ' WHERE 1 ' . $condition . 'GROUP BY products.product_id'); $stats['products_total'] = db_get_found_rows(); $params = array( 'get_conditions' => true ); list($fields, $join, $condition, $group_by, $sorting, $limit) = fn_get_categories($params); $stats['categories_total'] = db_get_field('SELECT COUNT(*) FROM ?:categories WHERE 1 ?p', $condition); $params = array( 'get_conditions' => true, 'status' => 'A' ); list($fields, $join, $condition, $group_by, $sorting, $limit) = fn_get_categories($params); $stats['categories_active'] = db_get_field('SELECT COUNT(*) FROM ?:categories WHERE 1 ?p', $condition); $params = array( 'get_conditions' => true, 'status' => 'H' ); list($fields, $join, $condition, $group_by, $sorting, $limit) = fn_get_categories($params); $stats['categories_hidden'] = db_get_field('SELECT COUNT(*) FROM ?:categories WHERE 1 ?p', $condition); $params = array( 'get_conditions' => true, 'status' => 'D' ); list($fields, $join, $condition, $group_by, $sorting, $limit) = fn_get_categories($params); $stats['categories_disabled'] = db_get_field('SELECT COUNT(*) FROM ?:categories WHERE 1 ?p', $condition); return $stats; } /** * Gets all available brands. * * @return array Found brands */ function fn_get_all_brands() { $params = array( 'exclude_group' => true, 'get_descriptions' => true, 'feature_types' => array(ProductFeatures::EXTENDED), 'variants' => true, 'plain' => true, 'is_popular' => 'Y', ); list($features) = fn_get_product_features($params, 0); $variants = array(); foreach ($features as $feature) { if (!empty($feature['variants'])) { foreach ($feature['variants'] as $variant) { if (!empty($variant['is_popular']) && $variant['is_popular'] === 'Y') { $variants[] = $variant; } } } } return $variants; } function fn_get_all_seriyes() { $params = array( 'exclude_group' => true, 'get_descriptions' => true, 'feature_types' => array(ProductFeatures::EXTENDED), 'variants' => true, 'plain' => true, 'is_seriya' => 'Y', ); list($features) = fn_get_product_features($params, 0); $variants = array(); foreach ($features as $feature) { if (!empty($feature['variants'])) { foreach ($feature['variants'] as $variant) { if (!empty($variant['is_seriya']) && $variant['is_seriya'] === 'Y') { $variants[] = $variant; } } } } // fn_print_die($variants); return $variants; } function fn_update_product_categories($product_id, $product_data) { /** * Change parameters before updating product categories * * @param integer $product_id product ID * @param array $product_data product data */ fn_set_hook('update_product_categories_pre', $product_id, $product_data); $existing_categories = db_get_hash_array("SELECT category_id, link_type, position FROM ?:products_categories WHERE product_id = ?i", 'category_id', $product_id); $rebuild = false; if (!empty($product_data['category_ids'])) { $product_data['category_ids'] = array_unique($product_data['category_ids']); if (empty($product_data['main_category'])) { $product_data['main_category'] = reset($product_data['category_ids']); } if (sizeof($product_data['category_ids']) == sizeof($existing_categories)) { if (isset($existing_categories[$product_data['main_category']]) && $existing_categories[$product_data['main_category']]['link_type'] != 'M') { $rebuild = true; } foreach ($product_data['category_ids'] as $cid) { if (!isset($existing_categories[$cid])) { $rebuild = true; } } } else { $rebuild = true; } } if ($rebuild == true) { db_query("DELETE FROM ?:products_categories WHERE product_id = ?i", $product_id); foreach ($product_data['category_ids'] as $cid) { $_data = array( 'product_id' => $product_id, 'category_id' => $cid, 'position' => isset($existing_categories[$cid]) ? $existing_categories[$cid]['position'] : (isset($product_data['position']) // Available on bulk product addition ? (int) $product_data['position'] : 0 ), 'link_type' => $product_data['main_category'] == $cid ? 'M' : 'A' ); db_query("INSERT INTO ?:products_categories ?e", $_data); } fn_update_product_count(fn_array_merge($product_data['category_ids'], array_keys($existing_categories), false)); } /** * Post processing after updating product categories * * @param integer $product_id product ID * @param array $product_data product data * @param array $existing_categories original product categories * @param boolean $rebuild flag that indicates that product categories were changed */ fn_set_hook('update_product_categories_post', $product_id, $product_data, $existing_categories, $rebuild); } /** * Checks if product linked to any category from the owner company * * @param int $product_id Product ID * @param array $category_ids List of category ids * @return bool True if linked */ function fn_check_owner_categories($company_id, $category_ids) { $linked_to_categories = db_get_field('SELECT COUNT(*) FROM ?:categories WHERE company_id = ?i AND category_id IN (?n)', $company_id, $category_ids); return !empty($linked_to_categories); } /* * * Filters * */ /** * Filters: gets available filters according to current products set * * @param array $params request params * @param string $lang_code language code * @return array available filters list */ function fn_get_filters_products_count($params = array(), $lang_code = CART_LANGUAGE) { /** * Change parameters for getting product filters count * * @param array $params Products filter search params */ fn_set_hook('get_filters_products_count_pre', $params); $cache_params = array( 'category_id', 'company_id', 'dispatch', 'search_performed', 'q', 'filter_id', 'item_ids', 'variant_id', ); $key = array(); foreach ($cache_params as $prop) { if (isset($params[$prop])) { $key[] = $params[$prop]; } } $key = 'pfilters_' . md5(implode('|', $key)); Registry::registerCache(array('pfilters', $key), array( 'products', 'product_descriptions', 'product_features', 'product_filters', 'product_features_values', 'products_categories', 'categories', 'product_filter_descriptions', 'product_features_descriptions', 'product_feature_variant_descriptions', 'ult_objects_sharing' // FIXME: this should not be here ), Registry::cacheLevel('user')); $selected_filters = array(); $available_variants = array(); if (!empty($params['item_ids'])) { $params['filter_item_ids'] = $params['item_ids']; unset($params['item_ids']); // unset item_ids because $params array is passed to fn_get_products, etc later } if (!empty($params['check_location'])) { // FIXME: this is bad style, should be refactored $valid_locations = array( 'categories.view', 'product_features.view', 'companies.products', 'products.search' ); if (!in_array($params['dispatch'], $valid_locations)) { return array(); } } if (!Registry::isExist($key)) { $condition = $join = ''; if (!empty($params['category_id'])) { if (Registry::get('settings.General.show_products_from_subcategories') == 'Y') { $id_path = db_get_field("SELECT id_path FROM ?:categories WHERE category_id = ?i", $params['category_id']); $category_ids = db_get_fields("SELECT category_id FROM ?:categories WHERE id_path LIKE ?l", $id_path . '/%'); } else { $category_ids = array(); } $category_ids[] = $params['category_id']; $condition .= db_quote(" AND (?:product_filters.categories_path = '' OR FIND_IN_SET(?i, ?:product_filters.categories_path))", $params['category_id']); } if (!empty($params['filter_id'])) { $condition .= db_quote(" AND ?:product_filters.filter_id = ?i", $params['filter_id']); } if (!empty($params['filter_item_ids'])) { $condition .= db_quote(" AND ?:product_filters.filter_id IN (?n)", explode(',', $params['filter_item_ids'])); } if (!empty($params['variant_id'])) { $exclude_feature_id = db_get_field("SELECT feature_id FROM ?:product_features_values WHERE variant_id = ?i", $params['variant_id']); $condition .= db_quote(" AND ?:product_filters.feature_id NOT IN (?n)", $exclude_feature_id); } if (fn_allowed_for('ULTIMATE')) { $condition .= fn_get_company_condition('?:product_filters.company_id'); } $sf_fields = db_quote( "?:product_filters.feature_id, " . "?:product_filters.filter_id," . "?:product_filters.field_type," . "?:product_filters.round_to," . "?:product_filters.display," . "?:product_filters.display_count," . "?:product_filter_descriptions.filter," . "?:product_features.feature_type," . "?:product_features_descriptions.prefix," . "?:product_features_descriptions.suffix" ); $sf_join = db_quote( " LEFT JOIN ?:product_filter_descriptions ON ?:product_filter_descriptions.filter_id = ?:product_filters.filter_id AND ?:product_filter_descriptions.lang_code = ?s" . " LEFT JOIN ?:product_features ON ?:product_features.feature_id = ?:product_filters.feature_id" . " LEFT JOIN ?:product_features_descriptions ON ?:product_features_descriptions.feature_id = ?:product_filters.feature_id AND ?:product_features_descriptions.lang_code = ?s", $lang_code, $lang_code); $sf_sorting = db_quote("?:product_filters.position, ?:product_filter_descriptions.filter"); /** * Change SQL parameters before select product filters * * @param array $sf_fields String of comma-separated SQL fields to be selected in an SQL-query * @param string $sf_join String with the complete JOIN information (JOIN type, tables and fields) for an SQL-query * @param string $condition String containing SQL-query condition possibly prepended with a logical operator (AND or OR) * @param string $sf_sorting String containing the SQL-query ORDER BY clause * @param array $params Products filter search params */ fn_set_hook('get_filters_products_count_before_select_filters', $sf_fields, $sf_join, $condition, $sf_sorting, $params); $filters = db_get_hash_array("SELECT $sf_fields FROM ?:product_filters ?p WHERE ?:product_filters.status = 'A' ?p ORDER BY $sf_sorting", 'filter_id', $sf_join, $condition); if (empty($filters)) { return array(array()); } list($variant_values, $range_values, $field_variant_values, $field_range_values) = fn_get_current_filters($params, $filters, array(), AREA, $lang_code); Registry::set($key, array($filters, $variant_values, $range_values, $field_variant_values, $field_range_values)); } else { list($filters, $variant_values, $range_values, $field_variant_values, $field_range_values) = Registry::get($key); } $range_values = fn_filter_process_ranges($range_values, $filters, $selected_filters); $field_range_values = fn_filter_process_ranges($field_range_values, $filters, $selected_filters); $merged = fn_array_merge($variant_values, $range_values, $field_variant_values, $field_range_values); $available_variants = $merged; if (!empty($params['features_hash']) && empty($params['skip_advanced_variants'])) { $selected_filters = fn_parse_filters_hash($params['features_hash']); // Get available variants for current selection $_params = $params; $_params['split_filters'] = true; list($available_variants, $available_ranges, $available_field_values, $available_field_ranges) = fn_get_current_filters($_params, $filters, $selected_filters, AREA, $lang_code); list($variant_filter_ids) = fn_split_selected_feature_variants($filters, $selected_filters, false); if (sizeof($variant_filter_ids) == 1 && sizeof($selected_filters) == 1) { $filter_id = key($variant_filter_ids); $available_variants[$filter_id] = $variant_values[$filter_id]; } $available_ranges = fn_filter_process_ranges($available_ranges, $filters, $selected_filters); $available_field_ranges = fn_filter_process_ranges($available_field_ranges, $filters, $selected_filters); $available_variants = fn_array_merge($available_variants, $available_ranges, $available_field_values, $available_field_ranges); $merged = fn_array_merge($merged, $available_variants); } foreach ($filters as $filter_id => $filter) { if (empty($merged[$filter_id]) || ( !empty($filter['feature_type']) && empty($available_variants[$filter_id])) ) { unset($filters[$filter_id]); continue; } $filters[$filter_id] = fn_array_merge($filters[$filter_id], $merged[$filter_id]); if (!empty($filters[$filter_id]['variants'])) { // Move selected variants to selected_variants key if (!empty($selected_filters[$filter_id])) { foreach ($selected_filters[$filter_id] as $variant_id) { if (!empty($filters[$filter_id]['variants'][$variant_id])) { $filters[$filter_id]['selected_variants'][$variant_id] = $filters[$filter_id]['variants'][$variant_id]; unset($filters[$filter_id]['variants'][$variant_id]); } } } // If we selected any variants in filter, disabled unavailable variants if (!empty($available_variants) && !empty($available_variants[$filter_id])) { foreach ($filters[$filter_id]['variants'] as $variant_id => $variant_data) { if (empty($available_variants[$filter_id]['variants'][$variant_id])) { // disable variant and move it to the end of list unset($filters[$filter_id]['variants'][$variant_id]); $variant_data['disabled'] = true; $filters[$filter_id]['variants'][$variant_id] = $variant_data; } } } } // If range is selected, mark this filter if (!empty($filters[$filter_id]['slider']) && !empty($selected_filters[$filter_id])) { if (!empty($filters[$filter_id]['slider']['left']) || !empty($filters[$filter_id]['right'])) { $filters[$filter_id]['selected_range'] = true; } } } return array($filters); } /** * Filters: removes variant or filter from selected filters list * * @param string $features_hash selected filters list * @param integer $filter_id filter ID * @param mixed $variant filter variant * @return string updated filters list */ function fn_delete_filter_from_hash($features_hash, $filter_id, $variant = '') { $filters = fn_parse_filters_hash($features_hash); if (!empty($filters[$filter_id])) { if (!empty($variant) && in_array($variant, $filters[$filter_id])) { $values = array_flip($filters[$filter_id]); unset($values[$variant]); if (!empty($values)) { $filters[$filter_id] = array_keys($values); } else { unset($filters[$filter_id]); } } elseif (empty($variant)) { unset($filters[$filter_id]); } } return fn_generate_filter_hash($filters); } /** * Filters: adds variant to selected filters list * * @param string $features_hash selected filters list * @param integer $filter_id filter ID * @param mixed $variant filter variant * @return string updated filters list */ function fn_add_filter_to_hash($features_hash, $filter_id, $variant = '') { $filters = fn_parse_filters_hash($features_hash); if (!isset($filters[$filter_id]) || !in_array($variant, $filters[$filter_id])) { $filters[$filter_id][] = $variant; } return fn_generate_filter_hash($filters); } /** * Filters: generates filter hash * @param array $filters selected filters list * @return string filter hash */ function fn_generate_filter_hash($filters) { $res = array(); foreach ($filters as $filter_id => $variants) { if (is_array($variants)) { $res[] = $filter_id . FILTERS_HASH_FEATURE_SEPARATOR . implode(FILTERS_HASH_FEATURE_SEPARATOR, $variants); } else { $res[] = $filter_id . FILTERS_HASH_FEATURE_SEPARATOR . $variants; } } return implode(FILTERS_HASH_SEPARATOR, $res); } /** * Filters: parses selected filters list * @param string $features_hash selected filters list * @return array parsed filters list */ function fn_parse_filters_hash($features_hash = '') { $result = array(); if (!empty($features_hash)) { $values = explode(FILTERS_HASH_SEPARATOR, $features_hash); foreach ($values as $value) { $variants = explode(FILTERS_HASH_FEATURE_SEPARATOR, $value); $filter_id = array_shift($variants); $result[$filter_id] = $variants; } } return $result; } /** * Filters: splits selected filter/feature variants by type * * @param array $items filters or features list * @param array $selected_items selected filter or feature variants * @param boolean $key_is_feature use filter_id or feature_id as array key * * @return array selected filter/feature variants, split by type */ function fn_split_selected_feature_variants($items, $selected_items, $key_is_feature = true) { $variant_features = array(); $value_features = array(); $valueint_features = array(); $key = $key_is_feature ? 'feature_id' : 'filter_id'; foreach ($items as $item) { $id = !empty($item['filter_id']) ? $item['filter_id'] : $item['feature_id']; if (!empty($item['feature_id']) && isset($selected_items[$id])) { if (in_array($item['feature_type'], array(ProductFeatures::TEXT_SELECTBOX, ProductFeatures::MULTIPLE_CHECKBOX, ProductFeatures::EXTENDED))) { $variant_features[$item[$key]] = $selected_items[$id]; } elseif (in_array($item['feature_type'], array(ProductFeatures::SINGLE_CHECKBOX, ProductFeatures::TEXT_FIELD))) { if (!empty($selected_items[$id][0])) { $value_features[$item[$key]] = $selected_items[$id][0]; } } elseif (in_array($item['feature_type'], array(ProductFeatures::NUMBER_SELECTBOX, ProductFeatures::NUMBER_FIELD, ProductFeatures::DATE))) { $min = 0; $max = 0; if (isset($selected_items[$id][0])) { if ($item['feature_type'] == ProductFeatures::DATE) { $selected_items[$id][0] = fn_parse_date($selected_items[$id][0]); } elseif (isset($item['round_to'])) { $selected_items[$id][0] = Math::floorToPrecision($selected_items[$id][0], $item['round_to']); } $min = $selected_items[$id][0]; } if (isset($selected_items[$id][1])) { if ($item['feature_type'] == ProductFeatures::DATE) { $selected_items[$id][1] = fn_parse_date($selected_items[$id][1]); } elseif (isset($item['round_to'])) { $selected_items[$id][1] = Math::ceilToPrecision($selected_items[$id][1], $item['round_to']); } $max = $selected_items[$id][1]; } if (!empty($min) || !empty($max)) { $valueint_features[$item[$key]] = array($min, $max); } } } } return array($variant_features, $value_features, $valueint_features); } /** * Filters: generates conditions to search products by selected filter/feature variant * @param array $items filters or features list * @param array $selected_items selected filter or feature variants * @param string $join "join" conditions * @param string $condition "where" conditions * @param string $lang_code language code * @param array $params additional params * @return array "join" and "where" conditions */ function fn_generate_feature_conditions($items, $selected_items, $join, $condition, $lang_code, $params = array()) { list($variant_features, $value_features, $valueint_features) = fn_split_selected_feature_variants($items, $selected_items); // find selected variants for features with variants if (!empty($variant_features)) { $conditions = array(); foreach ($variant_features as $fid => $variants) { $join .= db_quote(" LEFT JOIN ?:product_features_values as var_val_$fid ON var_val_$fid.product_id = products.product_id AND var_val_$fid.lang_code = ?s AND var_val_$fid.feature_id = ?i", $lang_code, $fid); $conditions[$fid] = db_quote("var_val_$fid.variant_id IN (?n)", $variants); } // This is used to get all available filter variants for current conditions (magic becomes here :)) if (!empty($params['split_filters']) && sizeof($variant_features) > 1) { // This condition gets available variants for all not selected filters $combined_conditions = array( '(' . implode(' AND ', $conditions) . db_quote(' AND ?:product_features_values.feature_id NOT IN (?n))', array_keys($conditions)) ); foreach ($variant_features as $fid => $variants) { $tmp = $conditions; unset($tmp[$fid]); // This condition gets available variants for certain filter with ID == $fid $combined_conditions[] = '(' . implode(' AND ', $tmp) . db_quote(' AND ?:product_features_values.feature_id = ?i)', $fid); } $condition .= ' AND (' . implode(' OR ', $combined_conditions) . ')'; } else { $condition .= ' AND (' . implode(' AND ', $conditions) . ')'; } } // find selected variants for features with custom values if (!empty($valueint_features)) { foreach ($valueint_features as $fid => $ranges) { $join .= db_quote(" LEFT JOIN ?:product_features_values as var_val_$fid ON var_val_$fid.product_id = products.product_id AND var_val_$fid.lang_code = ?s AND var_val_$fid.feature_id = ?i", $lang_code, $fid); $condition .= db_quote(" AND (var_val_$fid.value_int >= ?d AND var_val_$fid.value_int <= ?d AND var_val_$fid.value = '')", $ranges[0], $ranges[1]); } } // find selected variants for checkbox and text features if (!empty($value_features)) { foreach ($value_features as $fid => $value) { $join .= db_quote(" LEFT JOIN ?:product_features_values as ch_features_$fid ON ch_features_$fid.product_id = products.product_id AND ch_features_$fid.lang_code = ?s AND ch_features_$fid.feature_id = ?i", $lang_code, $fid); $condition .= db_quote(" AND ch_features_$fid.value = ?s", $value); } } return array($join, $condition); } /** * Filters: generates search params to search products by product fields * @param array $params request params * @param array $filters filters list * @param array $selected_filters selected filter variants * @return array search params */ function fn_generate_filter_field_params($params, $filters, $selected_filters) { $filter_fields = fn_get_product_filter_fields(); foreach ($filters as $filter) { if (!empty($filter['field_type'])) { $structure = $filter_fields[$filter['field_type']]; if ($structure['condition_type'] == 'F') { if (!empty($selected_filters[$filter['filter_id']])) { $params['filter_params'][$structure['db_field']] = $selected_filters[$filter['filter_id']]; } } elseif ($structure['condition_type'] == 'C') { if (!empty($selected_filters[$filter['filter_id']][0])) { foreach ($structure['map'] as $_param => $_value) { $params[$_param] = $_value; } } } elseif ($structure['condition_type'] == 'D') { $min = 0; $max = 0; $extra = ''; if (isset($selected_filters[$filter['filter_id']][0])) { if (isset($filter['round_to'])) { $min = Math::floorToPrecision($selected_filters[$filter['filter_id']][0], $filter['round_to']); } else { $min = intval($selected_filters[$filter['filter_id']][0]); } } if (isset($selected_filters[$filter['filter_id']][1])) { if (isset($filter['round_to'])) { $max = Math::floorToPrecision($selected_filters[$filter['filter_id']][1], $filter['round_to']); } else { $max = intval($selected_filters[$filter['filter_id']][1]); } } if (isset($selected_filters[$filter['filter_id']][2])) { $extra = $selected_filters[$filter['filter_id']][2]; } if (!empty($structure['convert'])) { list($min, $max) = $structure['convert']($min, $max, $extra); } $params[$structure['db_field'] . '_from'] = $min; $params[$structure['db_field'] . '_to'] = $max; } } } return $params; } /** * Filters: gets all available filter variants * @param array $params request params * @param array $filters filters list * @param array $selected_filters selected filter variants * @param string $area current working area * @param string $lang_code language code * @return array available filter variants, filter range values, product field variants and product field range values */ function fn_get_current_filters($params, $filters, $selected_filters, $area = AREA, $lang_code = CART_LANGUAGE) { $condition = $where = $join = ''; $variant_values = array(); $variant_descriptions = array(); $range_values = array(); $field_variant_values = array(); $field_range_values = array(); $feature_ids = array(); $standard_fields = array(); foreach ($filters as $filter) { if (!empty($filter['feature_id'])) { $feature_ids[] = $filter['feature_id']; } elseif (!empty($filter['field_type'])) { $standard_fields[$filter['filter_id']] = $filter; } } $_params = $params; $_params['features_hash'] = ''; $_params['get_conditions'] = true; $_params['custom_extend'] = array('categories'); if (!empty($params['category_id'])) { $_params['cid'] = $params['category_id']; $_params['subcats'] = ''; if (Registry::get('settings.General.show_products_from_subcategories') == 'Y') { $_params['subcats'] = 'Y'; } } list(, $join, $where) = fn_get_products($_params, 0, $lang_code); if (!empty($_params['split_filters'])) { list($variant_features, $value_features, $valueint_features) = fn_split_selected_feature_variants($filters, $selected_filters, false); } else { $variant_features = $value_features = $valueint_features = array(); } if (!empty($feature_ids)) { $selected_filters_variants = $selected_filters; if (!empty($_params['split_filters'])) { $_params['features_hash'] = ''; $other_filters = array_diff_key($selected_filters, $variant_features); if (!empty($other_filters)) { $_params['features_hash'] = fn_generate_filter_hash($other_filters); } $selected_filters_variants = $variant_features; } list(, $join, $where) = fn_get_products($_params, 0, $lang_code); list($join, $where) = fn_generate_feature_conditions($filters, $selected_filters_variants, $join, $where, $lang_code, $params); // Get all available variants $variant_values = db_get_hash_single_array( "SELECT ?:product_filters.filter_id, ?:product_features_values.variant_id FROM ?:product_features_values " . "LEFT JOIN ?:products as products ON products.product_id = ?:product_features_values.product_id " . "LEFT JOIN ?:product_filters ON ?:product_filters.feature_id = ?:product_features_values.feature_id " . "LEFT JOIN ?:product_features ON ?:product_features.feature_id = ?:product_features_values.feature_id ?p " . "WHERE " . "?:product_features_values.feature_id IN (?n) AND " . "?:product_features_values.lang_code = ?s ?p AND " . "?:product_features.feature_type IN (?s, ?s, ?s) " . "GROUP BY ?:product_features_values.variant_id", array('variant_id', 'filter_id'), $join, $feature_ids, $lang_code, $where, ProductFeatures::TEXT_SELECTBOX, ProductFeatures::MULTIPLE_CHECKBOX, ProductFeatures::EXTENDED); // Get descriptions and position if (!empty($variant_values)) { $variant_descriptions = db_get_hash_array( "SELECT ?:product_feature_variants.variant_id, ?:product_feature_variants.position, ?:product_feature_variant_descriptions.variant FROM ?:product_feature_variants " . "LEFT JOIN ?:product_feature_variant_descriptions ON ?:product_feature_variants.variant_id = ?:product_feature_variant_descriptions.variant_id AND ?:product_feature_variant_descriptions.lang_code = ?s " . "WHERE ?:product_feature_variants.variant_id IN (?n) " . "ORDER BY ?:product_feature_variants.position ASC, ?:product_feature_variant_descriptions.variant ASC", 'variant_id', $lang_code, array_keys($variant_values) ); } // swap array $converted = array(); foreach ($variant_descriptions as $variant_id => $variant_data) { $converted[$variant_values[$variant_id]]['variants'][$variant_id] = array( 'variant_id' => $variant_id, 'variant' => $variant_data['variant'], 'position' => $variant_data['position'], ); } if (!empty($_params['split_filters'])) { $_params['features_hash'] = ''; $other_filters = array_diff_key($selected_filters, $value_features); if (!empty($other_filters)) { $_params['features_hash'] = fn_generate_filter_hash($other_filters); } } list(, $join, $where) = fn_get_products($_params, 0, $lang_code); // Get checkbox feature variants $checkbox_values = db_get_fields( "SELECT ?:product_filters.filter_id FROM ?:product_features_values " . "LEFT JOIN ?:products as products ON products.product_id = ?:product_features_values.product_id " . "LEFT JOIN ?:product_filters ON ?:product_filters.feature_id = ?:product_features_values.feature_id " . "LEFT JOIN ?:product_features ON ?:product_features.feature_id = ?:product_features_values.feature_id ?p " . "WHERE " . "?:product_features_values.feature_id IN (?n) AND " . "?:product_features_values.value = 'Y' AND " . "?:product_features_values.lang_code = ?s ?p AND " . "?:product_features.feature_type = ?s " . "GROUP BY ?:product_features_values.feature_id", $join, $feature_ids, $lang_code, $where, ProductFeatures::SINGLE_CHECKBOX); if (!empty($checkbox_values)) { foreach ($checkbox_values as $filter_id) { $converted[$filter_id]['variants']['Y'] = array( 'variant_id' => 'Y', 'variant' => __('yes') ); } } $variant_values = $converted; if (!empty($_params['split_filters'])) { $_params['features_hash'] = ''; $other_filters = array_diff_key($selected_filters, $valueint_features); if (!empty($other_filters)) { $_params['features_hash'] = fn_generate_filter_hash($other_filters); } } list(, $join, $where) = fn_get_products($_params, 0, $lang_code); // Get range limits $range_values = db_get_hash_array( "SELECT ?:product_filters.filter_id, MIN(?:product_features_values.value_int) as min, MAX(?:product_features_values.value_int) as max FROM ?:product_features_values " . "LEFT JOIN ?:products as products ON products.product_id = ?:product_features_values.product_id " . "LEFT JOIN ?:product_filters ON ?:product_filters.feature_id = ?:product_features_values.feature_id " . "LEFT JOIN ?:product_features ON ?:product_features.feature_id = ?:product_features_values.feature_id ?p " . "WHERE " . "?:product_features_values.feature_id IN (?n) AND " . "?:product_features_values.lang_code = ?s ?p AND " . "?:product_features.feature_type IN (?s, ?s, ?s) " . "GROUP BY ?:product_features_values.feature_id", 'filter_id', $join, $feature_ids, $lang_code, $where, ProductFeatures::NUMBER_SELECTBOX, ProductFeatures::NUMBER_FIELD, ProductFeatures::DATE); } // Get range limits for standard fields if (!empty($standard_fields)) { $_params['features_hash'] = ''; if (!empty($_params['split_filters'])) { $_params['features_hash'] = fn_generate_filter_hash(fn_array_merge($variant_features, $value_features, $valueint_features)); } list(, $join, $where) = fn_get_products($_params, 0, $lang_code); $fields = fn_get_product_filter_fields(); foreach ($standard_fields as $filter_id => $filter) { $structure = $fields[$filter['field_type']]; $fields_join = $fields_where = $table_alias = ''; if ($structure['table'] == 'products') { $table_alias = ' as products '; $db_field = "products.$structure[db_field]"; } else { $db_field = "?:$structure[table].$structure[db_field]"; $fields_join .= " LEFT JOIN ?:products as products ON products.product_id = ?:$structure[table].product_id"; } if (!empty($structure['conditions']) && is_callable($structure['conditions'])) { list($db_field, $fields_join, $fields_where) = $structure['conditions']($db_field, $fields_join, $fields_where); } // Checkboxes (in stock, etc) if ($structure['condition_type'] == 'C') { $field_variant_values[$filter_id] = array( 'variants' => array( 'Y' => array( 'variant_id' => 'Y', 'variant' => __($structure['description']) ) ) ); // Dinamic ranges (price, etc) } elseif ($structure['condition_type'] == 'D') { $range = db_get_row("SELECT MIN($db_field) as min, MAX($db_field) as max FROM ?:$structure[table] $table_alias ?p WHERE products.status IN ('A') ?p", $fields_join . $join, $where . $fields_where); if (!fn_is_empty($range)) { $range['field_type'] = $filter['field_type']; $field_range_values[$filter_id] = $range; } // Variants (vendors, etc) } elseif ($structure['condition_type'] == 'F') { $field_variant_values[$filter_id]['variants'] = db_get_hash_array( "SELECT $db_field as variant_id, $structure[variant_name_field] as variant" . " FROM ?:$structure[table] $table_alias ?p" . " WHERE 1 ?p" . " GROUP BY $db_field" . " ORDER BY $structure[variant_name_field] ASC", 'variant_id', $fields_join . $join, $fields_where . $where ); } foreach (array('prefix', 'suffix') as $key) { if (!empty($structure[$key])) { if (!empty($field_variant_values[$filter_id])) { $field_variant_values[$filter_id][$key] = $structure[$key]; } elseif (!empty($field_range_values[$filter_id])) { $field_range_values[$filter_id][$key] = $structure[$key]; } } } } } return array($variant_values, $range_values, $field_variant_values, $field_range_values); } /** * Filters: corrects min/max and left/right values for range filter * * @param array $range_values range filter values * @param array $filters filters list * @param array $selected_filters selected filter variants * * @return array corrected values */ function fn_filter_process_ranges($range_values, $filters, $selected_filters) { if (!empty($range_values)) { $fields = fn_get_product_filter_fields(); foreach ($range_values as $filter_id => $values) { if (!empty($values)) { if (!empty($values['field_type'])) { // standard field $structure = $fields[$values['field_type']]; if (!empty($structure['convert'])) { list($values['min'], $values['max']) = $structure['convert']($values['min'], $values['max']); } $values['extra'] = !empty($structure['extra']) ? $structure['extra'] : ''; } $values['slider'] = true; $values['min'] = Math::floorToPrecision($values['min'], $filters[$filter_id]['round_to']); $values['max'] = Math::ceilToPrecision($values['max'], $filters[$filter_id]['round_to']); if (!empty($selected_filters[$filter_id])) { $slider_vals = $selected_filters[$filter_id]; // convert to base values if (!empty($values['field_type']) && !empty($structure['convert'])) { list($slider_vals[0], $slider_vals[1]) = $structure['convert']($slider_vals[0], $slider_vals[1], $slider_vals[2]); } // zeke: TODO - do not convert twice // convert back to current values if (!empty($values['field_type']) && !empty($structure['convert'])) { list($slider_vals[0], $slider_vals[1]) = $structure['convert']($slider_vals[0], $slider_vals[1]); } $values['left'] = $slider_vals[0]; $values['right'] = $slider_vals[1]; if ($values['left'] < $values['min']) { $values['left'] = $values['min']; } if ($values['left'] > $values['max']) { $values['left'] = $values['max']; } if ($values['right'] > $values['max']) { $values['right'] = $values['max']; } if ($values['right'] < $values['min']) { $values['right'] = $values['min']; } if ($values['right'] < $values['left']) { $tmp = $values['right']; $values['right'] = $values['left']; $values['left'] = $tmp; } $values['left'] = Math::floorToPrecision($values['left'], $filters[$filter_id]['round_to']); $values['right'] = Math::ceilToPrecision($values['right'], $filters[$filter_id]['round_to']); } $range_values[$filter_id] = $values; } } } return $range_values; } /** * Filters: gets list of product fields available for filtering * @return array filter product fields list */ function fn_get_product_filter_fields() { $filters = array ( // price filter 'P' => array ( 'db_field' => 'price', 'table' => 'product_prices', 'description' => 'price', 'condition_type' => 'D', 'slider' => true, 'convert' => function($min, $max, $extra = '') { if (!empty($extra) && $extra != CART_PRIMARY_CURRENCY && Registry::get('currencies.' . $extra)) { $currency = Registry::get('currencies.' . $extra); $min = round(floatval($min) * floatval($currency['coefficient']), $currency['decimals']); $max = round(floatval($max) * floatval($currency['coefficient']), $currency['decimals']); } elseif (empty($extra) && CART_PRIMARY_CURRENCY != CART_SECONDARY_CURRENCY) { $currency = Registry::get('currencies.' . CART_SECONDARY_CURRENCY); $min = round(floatval($min) / floatval($currency['coefficient']), $currency['decimals']); $max = round(floatval($max) / floatval($currency['coefficient']), $currency['decimals']); } return array($min, $max); }, 'conditions' => function($db_field, $join, $condition) { $join .= db_quote(" LEFT JOIN ?:product_prices as prices_2 ON ?:product_prices.product_id = prices_2.product_id AND ?:product_prices.price > prices_2.price AND prices_2.lower_limit = 1 AND prices_2.usergroup_id IN (?n)", array_merge(array(USERGROUP_ALL), Tygh::$app['session']['auth']['usergroup_ids']) ); $condition .= db_quote(" AND ?:product_prices.lower_limit = 1 AND ?:product_prices.usergroup_id IN (?n) AND prices_2.price IS NULL", array_merge(array(USERGROUP_ALL), Tygh::$app['session']['auth']['usergroup_ids']) ); if (fn_allowed_for('ULTIMATE') && Registry::get('runtime.company_id')) { $db_field = "IF(shared_prices.product_id IS NOT NULL, shared_prices.price, ?:product_prices.price)"; $join .= db_quote(" LEFT JOIN ?:ult_product_prices AS shared_prices ON shared_prices.product_id = products.product_id" . " AND shared_prices.lower_limit = 1" . " AND shared_prices.usergroup_id IN (?n)" . " AND shared_prices.company_id = ?i", array_merge(array(USERGROUP_ALL), Tygh::$app['session']['auth']['usergroup_ids']), Registry::get('runtime.company_id') ); } return array($db_field, $join, $condition); }, 'extra' => CART_SECONDARY_CURRENCY, 'prefix' => (Registry::get('currencies.' . CART_SECONDARY_CURRENCY . '.after') == 'Y' ? '' : Registry::get('currencies.' . CART_SECONDARY_CURRENCY . '.symbol')), 'suffix' => (Registry::get('currencies.' . CART_SECONDARY_CURRENCY . '.after') != 'Y' ? '' : Registry::get('currencies.' . CART_SECONDARY_CURRENCY . '.symbol')) ), // amount filter 'A' => array ( 'db_field' => 'amount', 'table' => 'products', 'description' => 'in_stock', 'condition_type' => 'C', 'map' => array( 'amount_from' => 1, ) ), // filter by free shipping 'F' => array ( 'db_field' => 'free_shipping', 'table' => 'products', 'description' => 'free_shipping', 'condition_type' => 'C', 'map' => array( 'free_shipping' => 'Y', ) ) ); /** * Changes product filter fields data * * @param array $filters Product filter fields */ fn_set_hook('get_product_filter_fields', $filters); return $filters; } /** * Filters: displays notifications when products were not found using current filters combination */ function fn_filters_not_found_notification() { Tygh::$app['view']->assign('product_info', __('text_no_products_found')); fn_set_notification('I', __('notice'), Tygh::$app['view']->fetch('views/products/components/notification.tpl')); Tygh::$app['ajax']->assign('no_products', true); } /** * Checks whether given filter appears as a numeric slider. * * @param array $filter_data Filter data returned by fn_get_product_filters() function * * @return bool Whether given filter accepts ranged numeric values. */ function fn_get_filter_is_numeric_slider($filter_data) { $is_ranged = false; if (!empty($filter_data['field_type'])) { $filter_fields = fn_get_product_filter_fields(); if (isset($filter_fields[$filter_data['field_type']])) { $is_ranged = !empty($filter_fields[$filter_data['field_type']]['slider']); } } elseif (!empty($filter_data['feature_type'])) { $is_ranged = in_array( $filter_data['feature_type'], array(ProductFeatures::NUMBER_FIELD, ProductFeatures::NUMBER_SELECTBOX) ); } return $is_ranged; } /** * Gets all created combinations for product(s) * * @param array $params Combination search params * @param int $items_per_page Items per page * @param string $lang_code Two-letter language code (e.g. 'en', 'ru', etc.) * @return array Combinations list and Search params */ function fn_get_product_options_inventory($params, $items_per_page = 0, $lang_code = DESCR_SL) { /** * Changes params before selecting option combinations * * @param array $params Combination search params * @param int $items_per_page Items per page * @param string $lang_code Two-letter language code (e.g. 'en', 'ru', etc.) */ fn_set_hook('get_product_options_inventory_pre', $params, $items_per_page, $lang_code); $default_params = array ( 'page' => 1, 'product_id' => 0, 'items_per_page' => $items_per_page ); $params = array_merge($default_params, $params); $limit = ''; if (!empty($params['items_per_page'])) { $params['total_items'] = db_get_field("SELECT COUNT(*) FROM ?:product_options_inventory WHERE product_id = ?i", $params['product_id']); $limit = db_paginate($params['page'], $params['items_per_page']); } $inventory = db_get_array("SELECT * FROM ?:product_options_inventory WHERE product_id = ?i ORDER BY position $limit", $params['product_id']); foreach ($inventory as $k => $v) { $inventory[$k]['combination'] = fn_get_product_options_by_combination($v['combination']); $inventory[$k]['image_pairs'] = fn_get_image_pairs($v['combination_hash'], 'product_option', 'M', true, true, $lang_code); } /** * Modifies option combinations * * @param array $params Combination search params * @param int $items_per_page Items per page * @param string $lang_code Two-letter language code (e.g. 'en', 'ru', etc.) * @param array List of available option combinations */ fn_set_hook('get_product_options_inventory_post', $params, $items_per_page, $lang_code, $inventory); return array($inventory, $params); } /** * Gets combination data by combination hash * * @param string $combination_hash Combination unique hash * @return array Combination data */ function fn_get_product_options_combination_data($combination_hash, $lang_code = DESCR_SL) { $combination = db_get_row("SELECT * FROM ?:product_options_inventory WHERE combination_hash = ?i", $combination_hash); $combination['combination'] = fn_get_product_options_by_combination($combination['combination']); $combination['image_pairs'] = fn_get_image_pairs($combination['combination_hash'], 'product_option', 'M', true, true, $lang_code); /** * Modifies selected combination data * * @param string $combination_hash Combination unique hash * @param array $combination_hash Combination data */ fn_set_hook('get_product_options_combination_data_post', $combination_hash, $combination); return $combination; } /** * Updates/Creates options combination * * @param array $combination_data Combination data * @param string $combination_hash Combination hash * @return string Combination hash */ function fn_update_option_combination($combination_data, $combination_hash = 0) { /** * Change parameters for updating options combination * * @param array $combination_data Combination data * @param string $combination_hash Combination hash */ fn_set_hook('update_option_combination_pre', $combination_data, $combination_hash); $inventory_amount = db_get_field('SELECT amount FROM ?:product_options_inventory WHERE combination_hash = ?s', $combination_hash); if (empty($combination_hash)) { $combination_hash = fn_generate_cart_id($combination_data['product_id'], array('product_options' => $combination_data['combination'])); $combination = fn_get_options_combination($combination_data['combination']); $product_code = fn_get_product_code($combination_data['product_id'], $combination_data['combination']); $_data = array( 'product_id' => $combination_data['product_id'], 'combination_hash' => $combination_hash, 'combination' => $combination, 'product_code' => !empty($product_code) ? $product_code : '', 'amount' => !empty($combination_data['amount']) ? $combination_data['amount'] : 0, 'position' => !empty($combination_data['position']) ? $combination_data['position'] : 0, ); db_query("REPLACE INTO ?:product_options_inventory ?e", $_data); } else { // Forbid to update options in the existing combination. Only qty/code/pos. unset($combination_data['combination']); db_query("UPDATE ?:product_options_inventory SET ?u WHERE combination_hash = ?s", $combination_data, $combination_hash); } if (isset($combination_data['amount']) && $combination_data['amount'] > 0 && $inventory_amount <= 0) { fn_send_product_notifications($combination_data['product_id']); } // Updating images fn_attach_image_pairs('combinations', 'product_option'); /** * Makes extra actions after updating options combination * * @param array $combination_data Combination data * @param string $combination_hash Combination hash * @param int $inventory_amount Previous (before update) inventory amount of the combination */ fn_set_hook('update_option_combination_post', $combination_data, $combination_hash, $inventory_amount); return $combination_hash; } /** * Deletes options combination * * @param string $combination_hash Combination hash * @return bool true */ function fn_delete_option_combination($combination_hash) { /** * Makes additional actions before deleting options combination * * @param string $combination_hash Combination hash */ fn_set_hook('delete_option_combination_pre', $combination_hash); db_query("DELETE FROM ?:product_options_inventory WHERE combination_hash = ?i", $combination_hash); fn_delete_image_pairs($combination_hash, 'product_option'); return true; } /** * Returns product creation timestamp * * @param int $product_id Product ID * @param bool $day_begin Set timestamp to beginning of the day * @return int product creation timestamp */ function fn_get_product_timestamp($product_id, $day_begin = false) { if (empty($product_id)) { return false; } $timestamp = db_get_field("SELECT timestamp FROM ?:products WHERE product_id = ?i", $product_id); if ($day_begin) { $timestamp = mktime(0,0,0, date("m", $timestamp), date("d", $timestamp), date("Y", $timestamp)); } return $timestamp; } /** * Creates category used for trash * * @param int $company_id Company ID * @return int ID of trash category */ function fn_create_trash_category($company_id) { $category_data = array( 'category' => __('trash_category'), 'description' => __('trash_category_description'), 'status' => 'D', // disabled 'is_trash' => 'Y', 'company_id' => $company_id, 'timestamp' => time(), 'selected_views' => '', 'product_details_view' => 'default', 'use_custom_templates' => 'N' ); $trash_id = fn_update_category($category_data); return $trash_id; } /** * Returns identifier of category used for trash * * @param int $company_id Company identifier * @return int|boolean Identifier of trash category, false when none exists */ function fn_get_trash_category($company_id) { $trash_id = db_get_field( "SELECT category_id" . " FROM ?:categories" . " WHERE is_trash = 'Y'" . " AND company_id = ?i", $company_id ); if (!is_numeric($trash_id)) { $trash_id = false; } return $trash_id; } /** * Checks if category is used for trash * * @param int $category_id Category ID to check for * @return boolean Category is used for trash */ function fn_is_trash_category($category_id) { $is_trash = db_get_field( "SELECT is_trash" . " FROM ?:categories" . " WHERE category_id = ?i", $category_id ); return $is_trash == 'Y'; } /** * Adds product to trash category * * @param int $product_id Product ID * @param int $trash_category_id Trash category ID */ function fn_add_product_to_trash($product_id, $trash_category_id) { $data = array( 'product_id' => $product_id, 'category_id' => $trash_category_id, 'position' => 0, 'link_type' => 'M' ); db_query("INSERT INTO ?:products_categories ?e", $data); } /** * Moves products left without categories in their store to trash * * @param array $category_ids Deleted categories identifiers * @return array Deleted products identifiers */ function fn_trash_orphaned_products($category_ids) { $orphaned_products = array(); $trashes = array(); $category_ids = array_unique($category_ids); if ($category_ids) { $narrowed_products_list = db_get_fields( "SELECT DISTINCT product_id" . " FROM ?:products_categories" . " WHERE category_id IN (?n)", $category_ids ); if (!empty($narrowed_products_list)) { $orphaned_products = db_get_hash_single_array( "SELECT" . " cp.product_id," . " p.company_id," . " c.category_id," . " GROUP_CONCAT(c.category_id) AS owner_groups" . " FROM ?:products p" . " LEFT JOIN ?:products_categories cp" . " ON p.product_id = cp.product_id" . " LEFT JOIN ?:categories c" . " ON cp.category_id = c.category_id" . " AND p.company_id = c.company_id" . " WHERE p.product_id in (?n)" . " GROUP BY cp.product_id" . " HAVING owner_groups IS NULL", array('product_id', 'company_id'), $narrowed_products_list ); if (!empty($orphaned_products)) { // Deleting product associations db_query("DELETE FROM ?:products_categories" . " WHERE category_id IN (?n)" . " OR product_id IN (?n)", $category_ids, array_keys($orphaned_products) ); // Moving products to trash foreach($orphaned_products as $product_id => $company_id) { if (!isset($trashes[$company_id])) { $trash_category_id = fn_get_trash_category($company_id); if (!$trash_category_id) { $trash_category_id = fn_create_trash_category($company_id); } $trashes[$company_id] = $trash_category_id; } fn_add_product_to_trash($product_id, $trashes[$company_id]); } fn_update_product_count(); } } } return array($orphaned_products, $trashes); } /** * Deletes products from trash category * * @param int $trash_category_id Trash category identifier * @return array Deleted product identifiers */ function fn_empty_trash($trash_category_id) { $products_to_delete = db_get_fields( "SELECT DISTINCT product_id" . " FROM ?:products_categories" . " WHERE category_id = ?i", $trash_category_id ); if (!empty($products_to_delete)) { foreach($products_to_delete as $product_id) { fn_delete_product($product_id); } } return $products_to_delete; } /** * Filtering product data before save * * @param array &$request $_REQUEST * @param array &$product_data Product data */ function fn_filter_product_data(&$request, &$product_data) { /** * Filtering product data * * @param array $request $_REQUEST * @param array $product_data $product_data */ fn_set_hook('filter_product_data', $request, $product_data); } Service unavailable
Sorry, service is temporarily unavailable.