|
Server : Apache/2.4.18 (Ubuntu) System : Linux canvaswebdesign 3.13.0-71-generic #114-Ubuntu SMP Tue Dec 1 02:34:22 UTC 2015 x86_64 User : oppastar ( 1041) PHP Version : 7.0.33-0ubuntu0.16.04.15 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority, Directory : /var/www/laciasmara.com/public_html/shop/application/controllers/ |
Upload File : |
<?php defined('BASEPATH') or exit('No direct script access allowed');
class Search extends Public_Controller
{
public function __construct()
{
parent::__construct();
$this->load->model('Top_banner_m');
$this->load->model('Review_m');
$this->load->model('Footer_m');
$this->load->model('Statistic_m');
$this->load->model('Category_m');
$this->load->library('GoogleClient');
$this->load->library('VisitorTracking');
$loginUrl = $this->googleclient->getLoginUrl();
$this->data_footer['googleUrl'] = $loginUrl;
}
public function index()
{
redirect(base_url());
}
public function product()
{
// Ambil query dari URL
$query = $this->input->get('q', TRUE); // 'TRUE' untuk XSS filtering
$source = $this->input->get('utm_source', TRUE);
$this->visitortracking->trackVisitor();
// Ambil data konfigurasi website
$website_data = $this->db->select('website_icon, browser_title, meta_description')
->from('configuration')
->where('id_configuration', 1)
->get()
->row();
$banners = $this->Top_banner_m->get_active_banners();
// Gunakan optimized query untuk pencarian produk
$optimized_query = $this->get_optimized_product_query();
// Tambahkan filter pencarian ke query
$search_query = "
SELECT * FROM (
$optimized_query
) AS product_results
WHERE (
product_results.title LIKE ?
OR product_results.alias LIKE ?
OR EXISTS (
SELECT 1 FROM products p2
WHERE p2.id_products = product_results.id_products
AND (
p2.description LIKE ?
OR p2.meta_description LIKE ?
)
)
OR EXISTS (
SELECT 1 FROM brands b2
WHERE b2.id_brands = product_results.brand_id
AND (
b2.brand LIKE ?
OR b2.alias LIKE ?
)
)
)
ORDER BY
product_results.sort_order ASC,
product_results.best_seller DESC,
product_results.new_arrival DESC,
product_results.popular_product DESC,
product_results.priority DESC,
product_results.brand_priority DESC,
product_results.created_at DESC
";
// Prepare search parameters
$search_term = "%{$query}%";
$search_params = [
$search_term, // title
$search_term, // alias
$search_term, // description
$search_term, // meta_description
$search_term, // brand
$search_term // brand alias
];
// Execute query
$products = $this->db->query($search_query, $search_params)->result_array();
$campaign = $this->campaignmanager->get_campaign_from_session();
if ($campaign) {
$this->load->model('Promotional_campaign_m');
// Filter hanya produk yang eligible
foreach ($products as $key => &$product) {
$product_category = $this->db->select('id_category')
->from('category_product')
->where('id_product', $product['id_products'])
->get()
->row();
if ($product_category) {
$is_eligible = $this->Promotional_campaign_m->is_product_eligible(
$campaign->id,
$product['id_products'],
$product_category->id_category
);
if ($is_eligible) {
$product['is_campaign_eligible'] = true;
}
}
}
// Apply discount ke semua yang eligible sekaligus
$products = $this->campaignmanager->apply_discount_to_products_array(
$products,
$campaign
);
}
// Format data produk
$formatted_products = $this->prepare_search_products($products);
$meta_description = ($this->session->userdata('site_lang') == 'english')
? "Can't decide? Just type and find the perfect pleasure to level up your intimate experience!"
: "Jangan bingung pilih yang mana! Cukup ketik dan temukan piranti asmara yang cocok untuk nemenin pengalaman intim kamu.";
$this->data_header['browser_title'] .= ' - Search Results for "' . htmlspecialchars($query, ENT_QUOTES, 'UTF-8') . '"';
$this->data_header['meta_description'] = $meta_description;
$this->data_header['products'] = $formatted_products;
$this->data_header['query'] = $query;
$this->data_header['total_products'] = count($formatted_products);
if ($this->session->userdata('site_lang') == 'english') {
$this->lang->load('mainpage', 'english');
} else {
$this->lang->load('mainpage', 'indonesian');
}
// Load view
$this->load->view("themes/$this->theme_no/header_new", $this->data_header);
$this->load->view("themes/$this->theme_no/search_result", $this->data);
$this->load->view("themes/$this->theme_no/footer_new", $this->data_footer);
}
private function get_optimized_product_query()
{
return "
SELECT
p.id_products,
p.title,
p.alias,
p.brand_id,
p.created_at,
p.best_seller,
p.new_arrival,
p.popular_product,
p.priority,
-- Product Details
COALESCE(primary_variant.id, first_variant.id) AS id_detail,
COALESCE(primary_variant.price, first_variant.price) AS price,
COALESCE(primary_variant.discounted_price, first_variant.discounted_price) AS discounted_price,
COALESCE(primary_variant.sku, first_variant.sku) AS sku,
-- Product Images
COALESCE(
main_image.image,
COALESCE(primary_variant_image.image, first_variant_image.image),
p.image
) AS image,
COALESCE(
main_image_secondary.image,
COALESCE(primary_variant_image_secondary.image, first_variant_image_secondary.image),
p.image1
) AS image_secondary,
-- Stock Information
stock_summary.total_stock,
stock_summary.total_stock_keep,
stock_summary.total_stock_sell,
stock_summary.available_variants,
stock_summary.total_variants,
-- Current variant stock
COALESCE(
GREATEST(0, COALESCE(primary_variant_stock.stock, 0) - COALESCE(primary_variant_stock.stock_keep, 0)),
GREATEST(0, COALESCE(first_variant_stock.stock, 0) - COALESCE(first_variant_stock.stock_keep, 0)),
0
) AS current_variant_stock_sell,
-- Availability Flags
CASE
WHEN stock_summary.total_stock_sell <= 0 THEN 1
ELSE 0
END AS is_completely_sold_out,
CASE
WHEN COALESCE(
GREATEST(0, COALESCE(primary_variant_stock.stock, 0) - COALESCE(primary_variant_stock.stock_keep, 0)),
GREATEST(0, COALESCE(first_variant_stock.stock, 0) - COALESCE(first_variant_stock.stock_keep, 0)),
0
) <= 0 THEN 1
ELSE 0
END AS current_variant_out_of_stock,
CASE
WHEN stock_summary.available_variants > 1 THEN 1
ELSE 0
END AS has_other_variants_available,
-- Variants
COALESCE(
primary_variant_attrs.variants,
first_variant_attrs.variants,
'No variants available'
) AS variants,
-- Badges Information
COALESCE(product_badges_data.badges_names, '') AS badges_names,
COALESCE(product_badges_data.badges_data, '') AS badges_data,
COALESCE(product_badges_data.badges_count, 0) AS badges_count,
-- Additional data
COALESCE(total_sales.total_sales, 0) AS total_sales,
COALESCE(total_reviews.total_reviews, 0) AS total_reviews,
COALESCE(reseller_price.price, 0) AS reseller_price,
COALESCE(reseller_price.min_quantity, 0) AS reseller_min_quantity,
COALESCE(b.priority, 0) AS brand_priority,
-- Discount calculations
CASE
WHEN COALESCE(primary_variant.discounted_price, first_variant.discounted_price) > 0
AND COALESCE(primary_variant.discounted_price, first_variant.discounted_price) < COALESCE(primary_variant.price, first_variant.price)
THEN (COALESCE(primary_variant.price, first_variant.price) - COALESCE(primary_variant.discounted_price, first_variant.discounted_price))
ELSE 0
END AS savings_amount,
CASE
WHEN COALESCE(primary_variant.discounted_price, first_variant.discounted_price) > 0
AND COALESCE(primary_variant.discounted_price, first_variant.discounted_price) < COALESCE(primary_variant.price, first_variant.price)
THEN ROUND(((COALESCE(primary_variant.price, first_variant.price) - COALESCE(primary_variant.discounted_price, first_variant.discounted_price)) / COALESCE(primary_variant.price, first_variant.price)) * 100, 0)
ELSE 0
END AS discount_percentage,
-- Sort order
CASE
WHEN stock_summary.total_stock_sell <= 0 THEN 1
ELSE 0
END AS sort_order
FROM products p
-- Stock Summary
LEFT JOIN (
SELECT
pd.product_id,
COUNT(DISTINCT pd.id) AS total_variants,
SUM(COALESCE(s.stock, 0)) AS total_stock,
SUM(COALESCE(s.stock_keep, 0)) AS total_stock_keep,
SUM(GREATEST(0, COALESCE(s.stock, 0) - COALESCE(s.stock_keep, 0))) AS total_stock_sell,
SUM(CASE WHEN GREATEST(0, COALESCE(s.stock, 0) - COALESCE(s.stock_keep, 0)) > 0 THEN 1 ELSE 0 END) AS available_variants
FROM product_details pd
LEFT JOIN stock s ON pd.id = s.id_product_detail
GROUP BY pd.product_id
) stock_summary ON p.id_products = stock_summary.product_id
-- Primary Variant
LEFT JOIN (
SELECT pd.*
FROM product_details pd
INNER JOIN (
SELECT product_id, MIN(id) as min_id
FROM product_details
GROUP BY product_id
) min_pd ON pd.product_id = min_pd.product_id AND pd.id = min_pd.min_id
) primary_variant ON p.id_products = primary_variant.product_id
LEFT JOIN (
SELECT pd.*
FROM product_details pd
LEFT JOIN stock s ON pd.id = s.id_product_detail
INNER JOIN (
SELECT pd.product_id, MAX(GREATEST(0, COALESCE(s.stock, 0) - COALESCE(s.stock_keep, 0))) AS max_stock
FROM product_details pd
LEFT JOIN stock s ON pd.id = s.id_product_detail
GROUP BY pd.product_id
) max_stock_pd ON pd.product_id = max_stock_pd.product_id
LEFT JOIN stock s2 ON pd.id = s2.id_product_detail
WHERE GREATEST(0, COALESCE(s2.stock, 0) - COALESCE(s2.stock_keep, 0)) = max_stock_pd.max_stock
) first_variant ON p.id_products = first_variant.product_id
-- Stock for displayed variants
LEFT JOIN stock primary_variant_stock ON primary_variant.id = primary_variant_stock.id_product_detail
LEFT JOIN stock first_variant_stock ON first_variant.id = first_variant_stock.id_product_detail
-- Product Images
LEFT JOIN product_images main_image ON p.id_products = main_image.product_id
AND main_image.product_details_id = 0
AND main_image.priority = 1
AND main_image.status = '1'
LEFT JOIN product_images main_image_secondary ON p.id_products = main_image_secondary.product_id
AND main_image_secondary.product_details_id = 0
AND main_image_secondary.priority = 2
AND main_image_secondary.status = '1'
LEFT JOIN product_images primary_variant_image ON primary_variant.id = primary_variant_image.product_details_id
AND primary_variant_image.priority = 1
AND primary_variant_image.status = '1'
LEFT JOIN product_images primary_variant_image_secondary ON primary_variant.id = primary_variant_image_secondary.product_details_id
AND primary_variant_image_secondary.priority = 2
AND primary_variant_image_secondary.status = '1'
LEFT JOIN product_images first_variant_image ON first_variant.id = first_variant_image.product_details_id
AND first_variant_image.priority = 1
AND first_variant_image.status = '1'
LEFT JOIN product_images first_variant_image_secondary ON first_variant.id = first_variant_image_secondary.product_details_id
AND first_variant_image_secondary.priority = 2
AND first_variant_image_secondary.status = '1'
-- Variant Attributes
LEFT JOIN (
SELECT
pc.product_details_id,
GROUP_CONCAT(DISTINCT CONCAT_WS(': ', pa.product_attribute, pad.attribute_detail) SEPARATOR '; ') AS variants
FROM product_combination pc
JOIN product_attributes pa ON pc.attribute_id = pa.id
JOIN product_attributes_detail pad ON pc.attribute_detail_id = pad.id
GROUP BY pc.product_details_id
) primary_variant_attrs ON primary_variant.id = primary_variant_attrs.product_details_id
LEFT JOIN (
SELECT
pc.product_details_id,
GROUP_CONCAT(DISTINCT CONCAT_WS(': ', pa.product_attribute, pad.attribute_detail) SEPARATOR '; ') AS variants
FROM product_combination pc
JOIN product_attributes pa ON pc.attribute_id = pa.id
JOIN product_attributes_detail pad ON pc.attribute_detail_id = pad.id
GROUP BY pc.product_details_id
) first_variant_attrs ON first_variant.id = first_variant_attrs.product_details_id
-- Product Badges (Enhanced but still simple)
LEFT JOIN (
SELECT
pb.product_id,
COUNT(pb.badge_id) AS badges_count,
GROUP_CONCAT(b.name ORDER BY pb.priority ASC SEPARATOR ', ') AS badges_names,
GROUP_CONCAT(
CONCAT(
b.id, ':',
b.name, ':',
b.slug, ':',
IFNULL(b.description, ''), ':',
IFNULL(b.background_color, '#FF6B6B'), ':',
IFNULL(b.text_color, '#FFFFFF'), ':',
IFNULL(b.icon, ''), ':',
pb.position, ':',
pb.priority
)
ORDER BY pb.priority ASC
SEPARATOR '|'
) AS badges_data
FROM product_badges pb
INNER JOIN badges b ON pb.badge_id = b.id
WHERE pb.is_active = 1
AND b.is_active = 1
AND (pb.start_date IS NULL OR pb.start_date <= NOW())
AND (pb.end_date IS NULL OR pb.end_date >= NOW())
GROUP BY pb.product_id
) product_badges_data ON p.id_products = product_badges_data.product_id
-- Additional joins
LEFT JOIN (
SELECT item_id, SUM(quantity) AS total_sales
FROM orders_detail
WHERE warehouse_id = 1
GROUP BY item_id
) total_sales ON COALESCE(primary_variant.id, first_variant.id) = total_sales.item_id
LEFT JOIN (
SELECT product_id, COUNT(id) AS total_reviews
FROM product_review
GROUP BY product_id
) total_reviews ON p.id_products = total_reviews.product_id
LEFT JOIN resellers_price reseller_price ON COALESCE(primary_variant.id, first_variant.id) = reseller_price.product_detail_id
LEFT JOIN brands b ON p.brand_id = b.id_brands
INNER JOIN category_product cp ON p.id_products = cp.id_product
WHERE p.product_status = '1'
AND p.deleted_at IS NULL
GROUP BY
p.id_products
ORDER BY
sort_order ASC,
p.best_seller DESC,
p.new_arrival DESC,
p.popular_product DESC,
p.priority DESC,
COALESCE(b.priority, 0) DESC,
p.created_at DESC
";
}
private function prepare_search_products($products)
{
$customer = $this->session->userdata('customer');
$is_reseller = false;
$reseller_price_map = [];
// Get reseller information (existing code)
if (!empty($customer['customer_id'])) {
$customer_id = $customer['customer_id'];
$customer_data = $this->db->where('id_customers', $customer_id)->get('customers')->row_array();
if (!empty($customer_data['reseller_id'])) {
$reseller = $this->db->where('id_resellers', $customer_data['reseller_id'])->get('resellers')->row_array();
if ($reseller) {
$is_reseller = true;
$reseller_prices = $this->db->where('reseller_id', $reseller['id_resellers'])->get('resellers_price')->result_array();
foreach ($reseller_prices as $price) {
$reseller_price_map[$price['product_detail_id']] = $price['price'];
}
}
}
}
$formatted_products = [];
foreach ($products as $product) {
// Price calculations
$is_discounted = $product['discounted_price'] > 0;
$default_price = $product['price'];
$discounted_price = $product['discounted_price'];
$current_price = $is_discounted ? $discounted_price : $default_price;
$savings_amount = ($discounted_price > 0 && $discounted_price < $default_price)
? ($default_price - $discounted_price)
: 0;
$msrp_price = null;
// Handle reseller pricing
if ($is_reseller && isset($reseller_price_map[$product['id_detail']])) {
$msrp_price = $current_price;
$current_price = $reseller_price_map[$product['id_detail']];
}
// Get reviews
$review_data = $this->Review_m->get_product_reviews($product['id_products']);
$average_rating = isset($review_data['average_rating']) ? round($review_data['average_rating'], 1) : 0;
$total_reviews = isset($review_data['total_reviews']) ? $review_data['total_reviews'] : 0;
// Parse variants
$variants = !empty($product['variants']) ? explode('; ', $product['variants']) : [];
$badges = $this->parse_badges_data($product['badges_data'] ?? '');
$badges_names = $product['badges_names'] ?? '';
// Stock calculations - all done in SQL now, just ensure non-negative values
$current_variant_stock_sell = max(0, intval($product['current_variant_stock_sell'] ?? 0));
$total_stock_sell = max(0, intval($product['total_stock_sell'] ?? 0));
$formatted_products[] = [
'id' => $product['id_products'],
'title' => $product['title'],
'id_detail' => $product['id_detail'],
'alias' => $product['alias'],
'sku' => $product['sku'],
'current_price' => $current_price,
'original_price' => $is_discounted ? $default_price : null,
'msrp_price' => $msrp_price,
'image' => $product['image'],
'image_secondary' => $product['image_secondary'],
'stock' => $product['total_stock'],
'stock_sell' => $current_variant_stock_sell,
'total_stock_sell' => $total_stock_sell,
'average_rating' => $average_rating,
'total_reviews' => $total_reviews,
'variants' => $variants,
'is_wishlisted' => $this->_check_wishlist_status($product['id_products'], $customer['customer_id'] ?? null),
'created_at' => $product['created_at'],
'brand_priority' => $product['brand_priority'],
'savings_amount' => $savings_amount,
'is_discounted' => $is_discounted,
'discount_percentage' => $product['discount_percentage'],
// Stock flags - now calculated in SQL
'is_completely_sold_out' => (bool) $product['is_completely_sold_out'],
'current_variant_out_of_stock' => (bool) $product['current_variant_out_of_stock'],
'has_other_variants_available' => (bool) $product['has_other_variants_available'],
// Additional stock information
'total_variants' => (int) $product['total_variants'],
'available_variants' => (int) $product['available_variants'],
// Badges
'badges' => $badges,
'badges_names' => $badges_names, // Simple comma-separated names for quick display
'badges_count' => (int) ($product['badges_count'] ?? 0),
'has_badges' => !empty($badges),
// New arrivals flag for badges
'new_arrivals' => ($product['new_arrival'] === 'yes'),
'best_seller' => ($product['best_seller'] === 'yes'),
'popular_product' => ($product['popular_product'] === 'yes'),
];
}
return $formatted_products;
}
private function parse_badges_data($badges_data)
{
$badges = [];
if (empty($badges_data)) {
return $badges;
}
$badges_raw = explode('|', $badges_data);
foreach ($badges_raw as $badge_raw) {
if (empty($badge_raw)) continue;
$badge_parts = explode(':', $badge_raw);
if (count($badge_parts) >= 9) {
$badges[] = [
'id' => (int) $badge_parts[0],
'name' => $badge_parts[1],
'slug' => $badge_parts[2],
'description' => $badge_parts[3],
'background_color' => !empty($badge_parts[4]) ? $badge_parts[4] : '#FF6B6B',
'text_color' => !empty($badge_parts[5]) ? $badge_parts[5] : '#FFFFFF',
'icon' => $badge_parts[6],
'position' => $badge_parts[7],
'priority' => (int) $badge_parts[8],
// Additional computed properties
'css_class' => 'badge-' . $badge_parts[2],
'has_icon' => !empty($badge_parts[6]),
'style' => sprintf(
'background-color: %s; color: %s;',
!empty($badge_parts[4]) ? $badge_parts[4] : '#FF6B6B',
!empty($badge_parts[5]) ? $badge_parts[5] : '#FFFFFF'
)
];
}
}
// Already sorted by priority in SQL, but double check
usort($badges, function ($a, $b) {
return $a['priority'] - $b['priority'];
});
return $badges;
}
private function _check_wishlist_status($product_id, $customer_id)
{
if (!$customer_id) return false;
$exists = $this->db->where([
'customer_id' => $customer_id,
'product_id' => $product_id
])->get('wishlists')->num_rows();
return $exists > 0;
}
public function search_dropdown()
{
header('Content-Type: application/json');
$query = $this->input->get('query', TRUE);
// Debug query
if (!$query) {
echo json_encode(['error' => 'No query provided']);
return;
}
$this->load->model('product_m');
$this->load->model('brand_m');
$this->load->model('category_m');
// Get search results for each type
$products = $this->product_m->search_products($query);
$brands = $this->brand_m->search_brands($query);
$categories = $this->category_m->search_categories($query);
// Process product results
foreach ($products as &$product) {
$product['url'] = base_url('product/' . $product['alias']);
$product['image'] = base_url('uploads/product/' . $product['image']);
$product['type'] = 'product';
}
// Process brand results
foreach ($brands as &$brand) {
$brand['url'] = base_url('brand/' . $brand['alias']);
$brand['image'] = !empty($brand['logo']) ? base_url('uploads/brand/' . $brand['logo']) : base_url('assets/images/no-image.png');
$brand['type'] = 'brand';
// Add a price placeholder for consistency in UI (can be empty)
$brand['price'] = null;
}
// Process category results
foreach ($categories as &$category) {
$category['url'] = base_url('category/' . $category['alias']);
$category['image'] = !empty($category['image']) ? base_url('uploads/category/' . $category['image']) : base_url('assets/images/no-image.png');
$category['type'] = 'category';
// Add a price placeholder for consistency in UI (can be empty)
$category['price'] = null;
}
$all_results = [
'products' => $products,
'brands' => $brands,
'categories' => $categories
];
// Send the combined results
echo json_encode(['results' => $all_results]);
}
}