Kaip sukurti efektyvų produktų palyginimo sistemą elektroninėje parduotuvėje naudojant JavaScript ir PHP

Šiuolaikinės elektroninės prekybos aplinkoje pirkėjai vis dažniau nori palyginti kelis produktus prieš priimant pirkimo sprendimą. Tai ypač aktualu technikos, namų ūkio prekių ar drabužių kategorijose, kur specifikacijų skirtumai gali lemti galutinį pasirinkimą. Produktų palyginimo sistema ne tik pagerina vartotojų patirtį, bet ir gali padidinti konversijos rodiklius bei sumažinti grąžinimų skaičių.

Efektyvus palyginimo įrankis turi būti greitas, intuityvus ir informatyvus. Jis neturėtų apsunkinti pirkimo proceso, o priešingai – padėti klientams greičiau priimti sprendimus. Šiame straipsnyje išnagrinėsime, kaip sukurti tokią sistemą naudojant JavaScript frontend’ui ir PHP backend’ui.

Architektūros planavimas ir duomenų struktūra

Prieš pradedant kodavimą, būtina apgalvoti sistemos architektūrą. Palyginimo funkcionalumas susideda iš kelių pagrindinių komponentų: produktų pasirinkimo mechanizmo, duomenų saugojimo, palyginimo logikos ir rezultatų pateikimo.

Duomenų bazės struktūra turėtų palaikyti lankstų produktų atributų valdymą. Rekomenduojama sukurti atskirą lentelę produktų atributams, nes skirtingos produktų kategorijos gali turėti visiškai skirtingas charakteristikas. Pavyzdžiui, telefonams svarbūs ekrano dydis ir baterijos talpa, o drabužiams – medžiaga ir dydžių lentelė.

CREATE TABLE products (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255),
    category_id INT,
    price DECIMAL(10,2),
    image_url VARCHAR(500),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE product_attributes (
    id INT PRIMARY KEY AUTO_INCREMENT,
    product_id INT,
    attribute_name VARCHAR(100),
    attribute_value TEXT,
    display_order INT,
    FOREIGN KEY (product_id) REFERENCES products(id)
);

CREATE TABLE comparison_sessions (
    id INT PRIMARY KEY AUTO_INCREMENT,
    session_id VARCHAR(100),
    product_ids JSON,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Tokia struktūra leidžia lanksčiai pridėti naujus atributus nepakeičiant duomenų bazės schemos. JSON laukas comparison_sessions lentelėje padeda išsaugoti vartotojų palyginimo sąrašus tarp sesijų.

Frontend’o sprendimas su JavaScript

Vartotojo sąsaja turi būti reaktyvi ir intuityvi. Palyginimo funkcionalumas prasideda nuo galimybės pridėti produktus į palyginimo sąrašą. Tai galima įgyvendinti per „Pridėti prie palyginimo” mygtukus produktų sąrašuose.

class ProductComparison {
    constructor() {
        this.compareList = JSON.parse(localStorage.getItem('compareList') || '[]');
        this.maxCompareItems = 4; // Ribojame palyginimo skaičių
        this.init();
    }

    init() {
        this.updateCompareCounter();
        this.bindEvents();
        this.loadSavedComparison();
    }

    addToCompare(productId, productName, productImage, productPrice) {
        if (this.compareList.length >= this.maxCompareItems) {
            this.showNotification('Galite palyginti ne daugiau kaip ' + this.maxCompareItems + ' produktus', 'warning');
            return false;
        }

        if (this.compareList.find(item => item.id === productId)) {
            this.showNotification('Šis produktas jau yra palyginimo sąraše', 'info');
            return false;
        }

        const product = {
            id: productId,
            name: productName,
            image: productImage,
            price: productPrice,
            addedAt: new Date().getTime()
        };

        this.compareList.push(product);
        this.saveToStorage();
        this.updateCompareCounter();
        this.showNotification('Produktas pridėtas į palyginimą', 'success');
        
        return true;
    }

    removeFromCompare(productId) {
        this.compareList = this.compareList.filter(item => item.id !== productId);
        this.saveToStorage();
        this.updateCompareCounter();
        this.updateCompareView();
    }

    saveToStorage() {
        localStorage.setItem('compareList', JSON.stringify(this.compareList));
        // Papildomai išsaugome serveryje registruotiems vartotojams
        if (this.isUserLoggedIn()) {
            this.syncWithServer();
        }
    }
}

Svarbu paminėti, kad localStorage naudojimas leidžia išsaugoti palyginimo sąrašą tarp sesijų, tačiau registruotiems vartotojams geriau duomenis saugoti serveryje, kad jie būtų prieinami iš skirtingų įrenginių.

Dinamiškas palyginimo lentelės generavimas

Palyginimo rezultatų pateikimas yra vienas sudėtingiausių aspektų, nes reikia dinamiškai sugeneruoti lentelę su skirtingais atributais. Ne visi produktai gali turėti visus atributus, todėl reikia protingai tvarkyti tuščias reikšmes.

async function generateComparisonTable(productIds) {
    try {
        const response = await fetch('/api/compare-products.php', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ product_ids: productIds })
        });

        const data = await response.json();
        
        if (data.success) {
            const tableHTML = buildComparisonHTML(data.products, data.attributes);
            document.getElementById('comparison-container').innerHTML = tableHTML;
            this.highlightDifferences();
        }
    } catch (error) {
        console.error('Klaida kraunant palyginimo duomenis:', error);
        this.showError('Nepavyko įkelti palyginimo duomenų');
    }
}

function buildComparisonHTML(products, attributes) {
    let html = '
'; html += ''; // Produktų antraštės html += ''; products.forEach(product => { html += ``; }); html += ''; // Atributų eilutės html += ''; attributes.forEach(attr => { html += ``; html += ``; products.forEach(product => { const value = product.attributes[attr.name] || '—'; const isDifferent = this.isValueDifferent(attr.name, value, products); html += ``; }); html += ''; }); html += '
Charakteristika
${product.name}

${product.name}

${formatPrice(product.price)}
${attr.display_name}${value}
'; return html; }

PHP backend’o implementacija

Serverio pusėje reikia efektyviai apdoroti palyginimo užklausas ir grąžinti struktūrizuotus duomenis. PHP kodas turi optimizuoti duomenų bazės užklausas ir tinkamai formatuoti atsakymus.

db = $database;
    }
    
    public function compareProducts($productIds) {
        if (empty($productIds) || count($productIds) > 4) {
            return ['success' => false, 'error' => 'Neteisingas produktų skaičius'];
        }
        
        // Gauname produktų pagrindinę informaciją
        $products = $this->getProductsBasicInfo($productIds);
        
        if (empty($products)) {
            return ['success' => false, 'error' => 'Produktai nerasti'];
        }
        
        // Gauname visus atributus šiems produktams
        $attributes = $this->getProductsAttributes($productIds);
        
        // Struktūrizuojame duomenis palyginimui
        $comparisonData = $this->structureComparisonData($products, $attributes);
        
        // Išsaugome palyginimo statistiką
        $this->logComparison($productIds);
        
        return [
            'success' => true,
            'products' => $comparisonData['products'],
            'attributes' => $comparisonData['attributes']
        ];
    }
    
    private function getProductsBasicInfo($productIds) {
        $placeholders = str_repeat('?,', count($productIds) - 1) . '?';
        $sql = "SELECT id, name, price, image_url, category_id 
                FROM products 
                WHERE id IN ($placeholders) AND status = 'active'
                ORDER BY FIELD(id, $placeholders)";
        
        $stmt = $this->db->prepare($sql);
        $stmt->execute(array_merge($productIds, $productIds));
        
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    
    private function getProductsAttributes($productIds) {
        $placeholders = str_repeat('?,', count($productIds) - 1) . '?';
        $sql = "SELECT pa.product_id, pa.attribute_name, pa.attribute_value, 
                       pa.display_order, ac.display_name, ac.attribute_type
                FROM product_attributes pa
                LEFT JOIN attribute_config ac ON pa.attribute_name = ac.name
                WHERE pa.product_id IN ($placeholders)
                ORDER BY pa.display_order ASC, ac.display_name ASC";
        
        $stmt = $this->db->prepare($sql);
        $stmt->execute($productIds);
        
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    
    private function structureComparisonData($products, $attributes) {
        $structuredProducts = [];
        $allAttributes = [];
        
        // Organizuojame produktų duomenis
        foreach ($products as $product) {
            $structuredProducts[$product['id']] = [
                'id' => $product['id'],
                'name' => $product['name'],
                'price' => $product['price'],
                'image_url' => $product['image_url'],
                'attributes' => []
            ];
        }
        
        // Pridedame atributus
        foreach ($attributes as $attr) {
            $productId = $attr['product_id'];
            $attrName = $attr['attribute_name'];
            
            if (isset($structuredProducts[$productId])) {
                $structuredProducts[$productId]['attributes'][$attrName] = 
                    $this->formatAttributeValue($attr['attribute_value'], $attr['attribute_type']);
            }
            
            // Registruojame visus unikalius atributus
            if (!isset($allAttributes[$attrName])) {
                $allAttributes[$attrName] = [
                    'name' => $attrName,
                    'display_name' => $attr['display_name'] ?: ucfirst($attrName),
                    'type' => $attr['attribute_type'] ?: 'text'
                ];
            }
        }
        
        return [
            'products' => array_values($structuredProducts),
            'attributes' => array_values($allAttributes)
        ];
    }
}

Palyginimo funkcionalumo optimizavimas

Vienas iš svarbiausių aspektų yra sistemos našumo optimizavimas. Palyginimo funkcionalumas gali generuoti daug duomenų bazės užklausų, todėl reikia taikyti kešavimo strategijas ir optimizuoti SQL užklausas.

Rekomenduojama naudoti Redis arba Memcached produktų atributų kešavimui. Ypač tai aktualu populiariems produktams, kurie dažnai lyginami. Taip pat galima implementuoti „smart caching” – išsaugoti dažniausiai lyginamų produktų kombinacijų rezultatus.

class ComparisonCache {
    private $redis;
    private $cacheExpiry = 3600; // 1 valanda
    
    public function getCachedComparison($productIds) {
        $cacheKey = 'comparison_' . md5(implode('_', sort($productIds)));
        $cached = $this->redis->get($cacheKey);
        
        if ($cached) {
            return json_decode($cached, true);
        }
        
        return null;
    }
    
    public function cacheComparison($productIds, $data) {
        $cacheKey = 'comparison_' . md5(implode('_', sort($productIds)));
        $this->redis->setex($cacheKey, $this->cacheExpiry, json_encode($data));
    }
    
    public function invalidateProductCache($productId) {
        // Ištriname visus kešus, kuriuose dalyvauja šis produktas
        $pattern = "comparison_*";
        $keys = $this->redis->keys($pattern);
        
        foreach ($keys as $key) {
            // Čia reikėtų patikrinti, ar raktas susijęs su konkrečiu produktu
            // Paprastesniam sprendimui galima ištrinti visus palyginimo kešus
            $this->redis->del($key);
        }
    }
}

Vartotojo patirties gerinimas

Geras palyginimo įrankis turi ne tik rodyti skirtumus, bet ir padėti juos interpretuoti. Galima pridėti funkcionalumą, kuris išryškina svarbiausius skirtumus arba net rekomenduoja geriausią pasirinkimą pagal tam tikrus kriterijus.

Vizualinis skirtumų paryškinimas padeda vartotojams greičiau identifikuoti svarbiausius aspektus. Tai galima įgyvendinti CSS klasėmis ir JavaScript logika:

function highlightDifferences() {
    const rows = document.querySelectorAll('.attribute-row');
    
    rows.forEach(row => {
        const values = Array.from(row.querySelectorAll('.attribute-value'))
                           .map(cell => cell.textContent.trim());
        
        const uniqueValues = [...new Set(values)];
        
        if (uniqueValues.length > 1) {
            // Yra skirtumų - pažymime
            row.classList.add('has-differences');
            
            // Pažymime geriausią reikšmę, jei galima
            this.highlightBestValue(row, values);
        } else {
            row.classList.add('no-differences');
        }
    });
}

function highlightBestValue(row, values) {
    const attributeName = row.dataset.attribute;
    const cells = row.querySelectorAll('.attribute-value');
    
    // Skirtingiems atributų tipams taikome skirtingas taisykles
    switch (attributeName) {
        case 'price':
            // Mažiausia kaina yra geriausia
            const minPrice = Math.min(...values.map(v => parseFloat(v.replace(/[^\d.]/g, ''))));
            cells.forEach(cell => {
                const price = parseFloat(cell.textContent.replace(/[^\d.]/g, ''));
                if (price === minPrice) {
                    cell.classList.add('best-value');
                }
            });
            break;
            
        case 'battery_capacity':
        case 'ram':
        case 'storage':
            // Didesnė reikšmė yra geresnė
            const maxValue = Math.max(...values.map(v => parseInt(v.replace(/[^\d]/g, ''))));
            cells.forEach(cell => {
                const value = parseInt(cell.textContent.replace(/[^\d]/g, ''));
                if (value === maxValue) {
                    cell.classList.add('best-value');
                }
            });
            break;
    }
}

Mobilių įrenginių pritaikymas

Palyginimo lentelės mobiliuose įrenginiuose kelia ypatingų iššūkių dėl riboto ekrano pločio. Tradicinė lentelės struktūra neveiks mažuose ekranuose, todėl reikia alternatyvaus sprendimo.

Vienas iš efektyviausių sprendimų yra kortelių (card) sistema, kur kiekvienas produktas rodomas atskiroje kortelėje, o vartotojas gali slinkti horizontaliai tarp jų. Taip pat galima implementuoti „accordion” stilių, kur atributai grupuojami į kategorijas.

@media (max-width: 768px) {
    .comparison-table-wrapper {
        display: none;
    }
    
    .mobile-comparison {
        display: block;
    }
    
    .product-card {
        background: white;
        border-radius: 8px;
        box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        margin-bottom: 20px;
        padding: 20px;
    }
    
    .attribute-group {
        margin-bottom: 15px;
    }
    
    .attribute-group h4 {
        color: #333;
        margin-bottom: 10px;
        font-size: 16px;
    }
    
    .attribute-item {
        display: flex;
        justify-content: space-between;
        padding: 8px 0;
        border-bottom: 1px solid #eee;
    }
    
    .attribute-name {
        font-weight: 500;
        color: #666;
    }
    
    .attribute-value {
        font-weight: 600;
        color: #333;
    }
}

Analitikos ir tobulinimo galimybės

Palyginimo sistemos efektyvumą galima matuoti keliais būdais. Svarbu stebėti, kokie produktai dažniausiai lyginami, kiek laiko vartotojai praleidžia palyginimo puslapyje, ir ar palyginimas padeda priimti pirkimo sprendimą.

Duomenų rinkimas padės optimizuoti sistemą ir suprasti vartotojų elgesį. Pavyzdžiui, jei pastebite, kad tam tikri atributai retai žiūrimi, galite juos perkelti į mažiau matomą vietą arba sugrupuoti su panašiais.

class ComparisonAnalytics {
    public function trackComparison($productIds, $userId = null) {
        $data = [
            'product_ids' => $productIds,
            'user_id' => $userId,
            'session_id' => session_id(),
            'timestamp' => time(),
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
            'referrer' => $_SERVER['HTTP_REFERER'] ?? ''
        ];
        
        // Išsaugome duomenų bazėje arba siunčiame į analitikos sistemą
        $this->logEvent('product_comparison_viewed', $data);
    }
    
    public function trackComparisonAction($action, $productId, $additionalData = []) {
        $data = array_merge([
            'action' => $action, // 'add_to_compare', 'remove_from_compare', 'compare_to_cart'
            'product_id' => $productId,
            'timestamp' => time(),
            'session_id' => session_id()
        ], $additionalData);
        
        $this->logEvent('comparison_action', $data);
    }
    
    public function getPopularComparisons($limit = 10) {
        $sql = "SELECT product_ids, COUNT(*) as comparison_count 
                FROM comparison_logs 
                WHERE created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
                GROUP BY product_ids 
                ORDER BY comparison_count DESC 
                LIMIT ?";
        
        $stmt = $this->db->prepare($sql);
        $stmt->execute([$limit]);
        
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
}

Praktiniai patarimai ir ateities vizija

Efektyvios palyginimo sistemos kūrimas reikalauja ne tik techninio įgyvendinimo, bet ir nuolatinio tobulinimo pagal vartotojų poreikius. Svarbu pradėti nuo paprastos versijos ir palaipsniui pridėti funkcionalumą, stebint, kaip vartotojai naudojasi sistema.

Ateityje palyginimo sistemas galima papildyti dirbtinio intelekto elementais – automatiniais rekomendacijų generavimu, išmaniais filtrais pagal vartotojo preferencijas, ar net chatbot’u, kuris padėtų interpretuoti palyginimo rezultatus. Taip pat galima integruoti su socialiniais tinklais, leisdama dalintis palyginimais su draugais.

Svarbu nepamiršti, kad palyginimo sistema turi papildyti, o ne pakeisti pagrindinį pirkimo procesą. Ji turi būti greita, intuityvi ir suteikti realią naudą vartotojams. Tik tokiu atveju ji taps vertingu įrankiu, kuris padidins pardavimus ir pagerins klientų pasitenkinimą.

Techninio įgyvendinimo prasme, visada vertėkite našumą ir saugumą. Naudokite kešavimą intensyviai naudojamoms užklausoms, optimizuokite duomenų bazės struktūrą ir reguliariai testuokite sistemos veikimą skirtingose situacijose. Palyginimo funkcionalumas gali tapti vienu iš svarbiausių jūsų elektroninės parduotuvės konkurencinių pranašumų, jei bus tinkamai įgyvendintas ir nuolat tobulinamas.

You May Also Like

More From Author