Free tutorials & notes in Hindi & English · Clean code examples 🧰 मुफ़्त PDF और फ़ोटो टूल्स →
🛠️ PHP + MySQL REST API  ·  Lesson 08

Wishlist और Address API

Wishlist: एक toggle endpoint

♡ tapPOST wishlist/toggle.phpDELETE row1 row delete हुई?हाँ → हट गयाin_wishlist: falseनहीं → INSERTin_wishlist: true
पहले delete करके देखें; कुछ delete नहीं हुआ तो पहले save नहीं था, इसलिए insert

Step 1: wishlist/toggle.php और list.php

rowCount() बताता है कि DELETE ने कितनी rows हटाईं। यही एक number बता देता है कि product wishlist में था या नहीं। UNIQUE (user_id, product_id) key double-tap पर भी duplicates नहीं बनने देती।

PHP – backend/api/wishlist/toggle.php
<?php
// POST {product_id} -> adds if missing, removes if present
require_once __DIR__ . '/../../includes/helpers.php';
require_method('POST');
$user = require_auth();
$productId = in_int('product_id');

$chk = db()->prepare('SELECT id FROM products WHERE id = ? AND is_active = 1');
$chk->execute(array($productId));
if (!$chk->fetch()) {
    fail('Product not found.', 404);
}

$del = db()->prepare('DELETE FROM wishlist WHERE user_id = ? AND product_id = ?');
$del->execute(array($user['id'], $productId));
if ($del->rowCount() > 0) {
    ok(array('product_id' => $productId, 'in_wishlist' => false), 'Removed from wishlist.');
}
db()->prepare('INSERT INTO wishlist (user_id, product_id) VALUES (?, ?)')->execute(array($user['id'], $productId));
ok(array('product_id' => $productId, 'in_wishlist' => true), 'Added to wishlist.');
PHP – backend/api/wishlist/list.php
<?php
require_once __DIR__ . '/../../includes/helpers.php';
require_method('GET');
$user = require_auth();

$stmt = db()->prepare(
    'SELECT p.* FROM wishlist w JOIN products p ON p.id = w.product_id
     WHERE w.user_id = ? AND p.is_active = 1 ORDER BY w.id DESC'
);
$stmt->execute(array($user['id']));
$out = array();
foreach ($stmt->fetchAll() as $r) {
    $p = product_out($r);
    $p['in_wishlist'] = true;
    $out[] = $p;
}
ok($out);

Addresses: checkout को क्या चाहिए

9:41▮▮ 85%←Saved addresses⋮Riya Sharma · 98765 …12 MG Road, Indore – 45…DefaultEditDeleteOffice · 98765 000004th Floor, Tech Park, I…+ Add address9:41▮▮ 85%←Add address⋮Full name+91 Mobile numberHouse no, building, streetCity PincodeState ▾Make this my default addressSave address
Address screens address/list.php और address/save.php use करती हैं

Rules: Indian mobile (10 digits, 6–9 से शुरू), pincode (6 digits, 0 से शुरू नहीं), और ठीक एक default address। पहला address हमेशा default होता है।

Step 2: address/save.php

"Add" (बिना id) और "edit" (id के साथ) दोनों के लिए एक endpoint। Address को default बनाने के लिए पहले पुराना default हटाना पड़ता है — दो queries जो साथ होनी चाहिए, इसलिए वे transaction में चलती हैं।

PHP – backend/api/address/save.php
<?php
// POST {id?, name, phone, line1, line2?, city, state, pincode, is_default?}
// Without id -> new address. With id -> update that address.
require_once __DIR__ . '/../../includes/helpers.php';
require_once __DIR__ . '/../../includes/address.php';
require_method('POST');
$user = require_auth();
require_fields(array(
    'name' => 'Name', 'phone' => 'Phone', 'line1' => 'Address line 1',
    'city' => 'City', 'state' => 'State', 'pincode' => 'Pincode',
));

$id = in_int('id');
$phone = in_str('phone');
$pincode = in_str('pincode');
if (!preg_match('/^[6-9][0-9]{9}$/', $phone)) {
    fail('Please enter a valid 10-digit mobile number.', 422);
}
if (!preg_match('/^[1-9][0-9]{5}$/', $pincode)) {
    fail('Please enter a valid 6-digit pincode.', 422);
}

$countStmt = db()->prepare('SELECT COUNT(*) FROM addresses WHERE user_id = ?');
$countStmt->execute(array($user['id']));
$isFirst = (int) $countStmt->fetchColumn() === 0;
$makeDefault = $isFirst || in_int('is_default') === 1 || in_str('is_default') === 'true';

$pdo = db();
$pdo->beginTransaction();
if ($makeDefault) {
    $pdo->prepare('UPDATE addresses SET is_default = 0 WHERE user_id = ?')->execute(array($user['id']));
}
$line2 = in_str('line2');
$values = array(in_str('name'), $phone, in_str('line1'), $line2 === '' ? null : $line2, in_str('city'), in_str('state'), $pincode);

if ($id > 0) {
    $own = $pdo->prepare('SELECT id FROM addresses WHERE id = ? AND user_id = ?');
    $own->execute(array($id, $user['id']));
    if (!$own->fetch()) {
        $pdo->rollBack();
        fail('Address not found.', 404);
    }
    $sql = 'UPDATE addresses SET name = ?, phone = ?, line1 = ?, line2 = ?, city = ?, state = ?, pincode = ?'
         . ($makeDefault ? ', is_default = 1' : '') . ' WHERE id = ? AND user_id = ?';
    $values[] = $id;
    $values[] = $user['id'];
    $pdo->prepare($sql)->execute($values);
} else {
    $values[] = $makeDefault ? 1 : 0;
    $values[] = $user['id'];
    $pdo->prepare(
        'INSERT INTO addresses (name, phone, line1, line2, city, state, pincode, is_default, user_id)
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'
    )->execute($values);
    $id = (int) $pdo->lastInsertId();
}
$pdo->commit();

$stmt = db()->prepare('SELECT * FROM addresses WHERE id = ?');
$stmt->execute(array($id));
ok(address_out($stmt->fetch()), 'Address saved.');
PHP – backend/includes/address.php
<?php
function address_out($a)
{
    return array(
        'id' => (int) $a['id'],
        'name' => $a['name'],
        'phone' => $a['phone'],
        'line1' => $a['line1'],
        'line2' => $a['line2'],
        'city' => $a['city'],
        'state' => $a['state'],
        'pincode' => $a['pincode'],
        'is_default' => (int) $a['is_default'] === 1,
    );
}

function address_text($a)
{
    $parts = array($a['line1']);
    if ($a['line2'] !== null && $a['line2'] !== '') {
        $parts[] = $a['line2'];
    }
    $parts[] = $a['city'] . ', ' . $a['state'] . ' - ' . $a['pincode'];
    return implode(', ', $parts);
}

Ownership checks (बहुत ज़रूरी)

असली दुनिया का सबसे आम API bug IDOR (Insecure Direct Object Reference) कहलाता है: app से आए id पर बिना check किए भरोसा करना कि वह किसका है।

PHP
<?php
// ❌ Dangerous: deletes ANY address whose id is sent
db()->prepare('DELETE FROM addresses WHERE id = ?')->execute(array($id));
// User 1 sends {"id": 57} → deletes user 2's address!

// ✅ Safe: the row must belong to the logged-in user
db()->prepare('DELETE FROM addresses WHERE id = ? AND user_id = ?')->execute(array($id, $user['id']));
❌ id वाली हर query में "AND user_id = ?" चाहिए

KartEasy यह addresses, cart rows, wishlist, orders, order details और cancel में करता है। Backend में user_id = ? search करें — हर customer endpoint में मिलेगा।

Step 3: list.php और delete.php

Default address delete होने पर बचे हुए में सबसे नया default बन जाता है, ताकि checkout के पास pre-select करने को हमेशा address हो।

PHP – backend/api/address/list.php
<?php
require_once __DIR__ . '/../../includes/helpers.php';
require_once __DIR__ . '/../../includes/address.php';
require_method('GET');
$user = require_auth();

$stmt = db()->prepare('SELECT * FROM addresses WHERE user_id = ? ORDER BY is_default DESC, id DESC');
$stmt->execute(array($user['id']));
$out = array();
foreach ($stmt->fetchAll() as $a) {
    $out[] = address_out($a);
}
ok($out);
PHP – backend/api/address/delete.php
<?php
// POST {id}
require_once __DIR__ . '/../../includes/helpers.php';
require_method('POST');
$user = require_auth();
$id = in_int('id');

$stmt = db()->prepare('SELECT is_default FROM addresses WHERE id = ? AND user_id = ?');
$stmt->execute(array($id, $user['id']));
$row = $stmt->fetch();
if (!$row) {
    fail('Address not found.', 404);
}
db()->prepare('DELETE FROM addresses WHERE id = ?')->execute(array($id));

// If the default was deleted, make the newest remaining address the default.
if ((int) $row['is_default'] === 1) {
    db()->prepare('UPDATE addresses SET is_default = 1 WHERE user_id = ? ORDER BY id DESC LIMIT 1')
        ->execute(array($user['id']));
}
ok(null, 'Address deleted.');

असली responses

Heart toggle
POST /karteasy/api/wishlist/toggle.php Authorization: Bearer 3f9c…e1a7 {"product_id": 4} HTTP 200 { "success": true, "message": "Added to wishlist.", "data": { "product_id": 4, "in_wishlist": true } }
Wishlist
GET /karteasy/api/wishlist/list.php Authorization: Bearer 3f9c…e1a7 HTTP 200 { "success": true, "message": "OK", "data": [ { "id": 4, "category_id": 2, "name": "Men Running Sneakers", "short_desc": "Lightweight mesh, sizes 6–11", "price": 1299, "mrp": 2499, "discount_pct": 48, "stock": 60, "image": "https://yourdomain.com/karteasy/uploads/products/p4.png", "rating": 4.2, "rating_count": 330, "in_wishlist": true } ] }
गलत phone number
POST /karteasy/api/address/save.php Authorization: Bearer 3f9c…e1a7 {"name": "Riya Sharma", "phone": "12345", "line1": "12 MG Road", "city": "Indore", "state": "Madhya Pradesh", "pincode": "452001"} HTTP 422 { "success": false, "message": "Please enter a valid 10-digit mobile number.", "data": null }
पहला address save (default बनता है)
POST /karteasy/api/address/save.php Authorization: Bearer 3f9c…e1a7 {"name": "Riya Sharma", "phone": "9876543210", "line1": "12 MG Road", "line2": "Near City Mall", "city": "Indore", "state": "Madhya Pradesh", "pincode": "452001"} HTTP 200 { "success": true, "message": "Address saved.", "data": { "id": 1, "name": "Riya Sharma", "phone": "9876543210", "line1": "12 MG Road", "line2": "Near City Mall", "city": "Indore", "state": "Madhya Pradesh", "pincode": "452001", "is_default": true } }
Address list
GET /karteasy/api/address/list.php Authorization: Bearer 3f9c…e1a7 HTTP 200 { "success": true, "message": "OK", "data": [ { "id": 1, "name": "Riya Sharma", "phone": "9876543210", "line1": "12 MG Road", "line2": "Near City Mall", "city": "Indore", "state": "Madhya Pradesh", "pincode": "452001", "is_default": true } ] }

अक्सर पूछे जाने वाले प्रश्न (FAQ)

Add और remove अलग की जगह toggle क्यों?
Heart icon सिर्फ "tap" जानता है। एक endpoint जो state पलटे और नया in_wishlist value लौटाए, app code को बहुत छोटा रखता है और कभी out of sync नहीं होता।
क्या pincode असली database से validate कर सकते हैं?
हाँ। India Post pincode list publish करता है; उसे table में import करके city/state check कर सकते हैं, या PHP से pincode API call करें। यहाँ regex सिर्फ format check करता है।
← E-commerce App Tutorial
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।

अगला topicOrders और Checkout API आपकी प्रगति: 8 / 28 (29%) · Lesson 9 / 28 आगे बढ़ें →

यह lesson कैसा लगा?

आपकी राय से हम content और बेहतर बनाते हैं।

अपना review और सुझाव लिखें (वैकल्पिक)

Review approve होने के बाद आपका नाम और शहर इसके साथ public दिख सकते हैं। Email या phone न लिखें।

▶ CodingEasily on YouTube

Video से सीखें – आसान Hindi में

चुनी हुई video: RDBMS भाग 3 – Class XI Commerce IT

▶ Channel subscribe करें