Monday, 1 June 2026

123456789 chuyển thành PHP

 

FULL UPGRADE SHOP PHP 2026

MỤC TIÊU

Nâng cấp shop PHP mini thành:

  • MVC architecture

  • PWA giống app mobile

  • UI hiện đại kiểu TikTok Shop/Shopee

  • Responsive mobile cực đẹp

  • Dark mode

  • AJAX realtime

  • API JSON

  • Upload ảnh thật

  • WebP optimization

  • CDN + cache

  • One page checkout

  • Thanh toán QR tự động

  • Voucher system

  • Theo dõi vận đơn

  • Dashboard thống kê

  • Export Excel

  • SEO chuẩn Google

  • Bảo mật production


1. CẤU TRÚC THƯ MỤC MỚI

shop/
├── app/
│   ├── Controllers/
│   ├── Models/
│   ├── Views/
│   ├── Middleware/
│   ├── Services/
│   ├── Helpers/
│   └── Core/
│
├── bootstrap/
├── config/
├── public/
│   ├── assets/
│   ├── uploads/
│   ├── api/
│   ├── manifest.json
│   └── service-worker.js
│
├── routes/
├── storage/
├── vendor/
└── sql/

2. .ENV CONFIG

.env

APP_NAME=Shop Online
APP_URL=https://domain.com

DB_HOST=localhost
DB_NAME=shopdb
DB_USER=root
DB_PASS=

APP_DEBUG=true

3. AUTOLOAD

composer.json

{
  "autoload": {
    "psr-4": {
      "App\\": "app/"
    }
  }
}

Run:

composer dump-autoload

4. ROUTER

routes/web.php

<?php

use App\Controllers\HomeController;
use App\Controllers\ProductController;

$router->get('/', [HomeController::class, 'index']);
$router->get('/product/{slug}', [ProductController::class, 'show']);

5. BASE CONTROLLER

app/Core/Controller.php

<?php

namespace App\Core;

class Controller {

  protected function view($view, $data = []) {

    extract($data);

    require __DIR__ . '/../Views/' . $view . '.php';
  }
}

6. DATABASE CLASS

app/Core/Database.php

<?php

namespace App\Core;

use PDO;

class Database {

  private static $pdo;

  public static function connect() {

    if (!self::$pdo) {

      self::$pdo = new PDO(
        "mysql:host=" . $_ENV['DB_HOST'] . ";dbname=" . $_ENV['DB_NAME'] . ";charset=utf8mb4",
        $_ENV['DB_USER'],
        $_ENV['DB_PASS']
      );

      self::$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }

    return self::$pdo;
  }
}

7. PRODUCT MODEL

app/Models/Product.php

<?php

namespace App\Models;

use App\Core\Database;

class Product {

  public static function latest() {

    return Database::connect()
      ->query("SELECT * FROM products ORDER BY id DESC")
      ->fetchAll();
  }

  public static function findBySlug($slug) {

    $stmt = Database::connect()->prepare(
      "SELECT * FROM products WHERE slug=? LIMIT 1"
    );

    $stmt->execute([$slug]);

    return $stmt->fetch();
  }
}

8. PRODUCT CONTROLLER

app/Controllers/ProductController.php

<?php

namespace App\Controllers;

use App\Core\Controller;
use App\Models\Product;

class ProductController extends Controller {

  public function show($slug) {

    $product = Product::findBySlug($slug);

    if (!$product) {
      http_response_code(404);
      die('404');
    }

    return $this->view('product/show', compact('product'));
  }
}

9. UPLOAD ẢNH THẬT

public/uploads/products

New-Item -ItemType Directory -Force -Path "public/uploads/products"

Upload image

$dir = __DIR__ . '/../../public/uploads/products/';

if (!empty($_FILES['image']['name'])) {

  $ext = strtolower(pathinfo(
    $_FILES['image']['name'],
    PATHINFO_EXTENSION
  ));

  $allow = ['jpg','jpeg','png','webp'];

  if (in_array($ext, $allow)) {

    $filename = uniqid() . '.' . $ext;

    move_uploaded_file(
      $_FILES['image']['tmp_name'],
      $dir . $filename
    );

    $image_url = '/uploads/products/' . $filename;
  }
}

10. AUTO WEBP

$img = imagecreatefromjpeg($source);

imagewebp($img, $destination, 80);

11. LAZY LOAD + RESPONSIVE IMAGE

<img
  loading="lazy"
  decoding="async"
  src="<?= $product['image_url'] ?>"
  alt=""
>

12. CDN TỐI ƯU

Cloudflare

  • bật Brotli

  • bật cache everything

  • bật Polish WebP

  • bật Rocket Loader


13. PWA APP

public/manifest.json

{
  "name": "Shop Online",
  "short_name": "Shop",
  "display": "standalone",
  "start_url": "/",
  "theme_color": "#ff4f9a",
  "background_color": "#ffffff",
  "icons": [
    {
      "src": "/assets/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    }
  ]
}

14. SERVICE WORKER

public/service-worker.js

self.addEventListener('install', e => {
  self.skipWaiting();
});

self.addEventListener('fetch', event => {

  event.respondWith(
    caches.match(event.request).then(response => {
      return response || fetch(event.request);
    })
  );
});

15. REGISTER SERVICE WORKER

<script>
if ('serviceWorker' in navigator) {

  navigator.serviceWorker.register('/service-worker.js');
}
</script>

16. DARK MODE

CSS

:root {
  --bg:#fff;
  --text:#111;
}

.dark {
  --bg:#0f172a;
  --text:#fff;
}

body {
  background:var(--bg);
  color:var(--text);
}

JS

const themeBtn = document.querySelector('#themeBtn');

if(localStorage.getItem('theme') === 'dark') {
  document.body.classList.add('dark');
}

themeBtn.onclick = () => {

  document.body.classList.toggle('dark');

  localStorage.setItem(
    'theme',
    document.body.classList.contains('dark')
      ? 'dark'
      : 'light'
  );
};

17. AJAX SEARCH

API

public/api/search.php

<?php

header('Content-Type: application/json');

$q = $_GET['q'] ?? '';

$stmt = db()->prepare(
  "SELECT * FROM products WHERE name LIKE ? LIMIT 10"
);

$stmt->execute(["%$q%"]);

echo json_encode($stmt->fetchAll());

FRONTEND

const search = document.querySelector('#search');

search.addEventListener('input', async () => {

  const res = await fetch('/api/search.php?q=' + search.value);

  const products = await res.json();

  renderProducts(products);
});

18. API JSON

public/api/products.php

<?php

header('Content-Type: application/json');

echo json_encode(Product::latest());

19. ADMIN LOGIN 2026

CSS

.login-box {
  backdrop-filter: blur(24px);
  border:1px solid rgba(255,255,255,.2);
  box-shadow:
    0 20px 60px rgba(255,79,154,.2);

  border-radius:24px;
}

20. RESPONSIVE MOBILE UI

Bottom navigation

<nav class="bottom-nav">
  <a href="/">Home</a>
  <a href="/cart">Cart</a>
  <a href="/profile">Profile</a>
</nav>

CSS

.bottom-nav {
  position:fixed;
  bottom:0;
  left:0;
  right:0;
  height:64px;
  display:flex;
  justify-content:space-around;
  background:#fff;
}

21. TIKTOK SHOP STYLE

Sticky buy button

.buy-bar {
  position:fixed;
  bottom:64px;
  left:0;
  right:0;
}

22. ONE PAGE CHECKOUT

Layout

[cart summary]
[address]
[payment]
[voucher]
[submit]

23. VIETQR THANH TOÁN

QR image

<img src="https://img.vietqr.io/image/MB-123456789-print.png?amount=300000">

24. AUTO PAYMENT CHECK

Có thể dùng:

  • Momo API

  • VietQR API

  • PayOS

  • ZaloPay


25. VOUCHER SYSTEM

SQL

CREATE TABLE vouchers (

  id INT AUTO_INCREMENT PRIMARY KEY,

  code VARCHAR(50) UNIQUE,

  type ENUM('percent','fixed') DEFAULT 'percent',

  value INT DEFAULT 0,

  min_order INT DEFAULT 0,

  max_discount INT DEFAULT 0,

  expired_at DATETIME,

  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

26. APPLY VOUCHER

if ($voucher['type'] === 'percent') {

  $discount = ($total * $voucher['value']) / 100;
}

27. TRACKING VẬN ĐƠN

SQL

ALTER TABLE orders
ADD tracking_code VARCHAR(100),
ADD shipping_provider VARCHAR(100);

28. SHIPPING APIs

Tích hợp:

  • GHN

  • GHTK

  • Viettel Post

  • Ninja Van


29. ADMIN DASHBOARD

Stats SQL

SELECT COUNT(*) FROM orders;
SELECT SUM(total) FROM orders;

30. CHART.JS

<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

31. EXPORT EXCEL

Install

composer require phpoffice/phpspreadsheet

Export

$sheet->setCellValue('A1', 'Mã đơn');

32. SEO META

<meta name="description" content="Shop Online">
<meta property="og:title" content="Shop Online">
<meta property="og:image" content="...">

33. PRODUCT SCHEMA

<script type="application/ld+json">
{
  "@context":"https://schema.org",
  "@type":"Product",
  "name":"Tên sản phẩm",
  "offers":{
    "@type":"Offer",
    "price":"300000",
    "priceCurrency":"VND"
  }
}
</script>

34. MESSENGER CHAT FLOAT

<a
 href="https://m.me/yourpage"
 class="messenger-float"
>
Messenger
</a>

35. SKELETON LOADING

.skeleton {
  animation:pulse 1.5s infinite;
}

36. CACHE SYSTEM

Redis

composer require predis/predis

37. SECURITY

CSRF

$_SESSION['csrf'] = bin2hex(random_bytes(32));

Rate limit

if ($attempts > 5) {
  die('Too many attempts');
}

Secure session

session_set_cookie_params([
  'httponly' => true,
  'secure' => true,
  'samesite' => 'Lax'
]);

38. IMAGE OPTIMIZATION

Responsive image

<picture>
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg">
</picture>

39. INFINITE SCROLL

window.addEventListener('scroll', () => {

  if(window.innerHeight + window.scrollY >= document.body.offsetHeight - 500) {

    loadMore();
  }
});

40. NOTIFICATION TOAST

Toastify({
  text: 'Đã thêm giỏ hàng'
}).showToast();

41. RECOMMENDED LIBRARIES

Frontend

  • Swiper.js

  • AOS animation

  • Toastify

  • Alpine.js

Backend

  • PHPMailer

  • PhpSpreadsheet

  • Predis

  • Intervention Image


42. SERVER RECOMMEND

Shared hosting

  • PHP 8.3

  • LiteSpeed

  • Redis

VPS

  • NGINX

  • MariaDB

  • Redis

  • Supervisor


43. FINAL 2026 STACK

PHP 8.3
MVC
PWA
Redis cache
Cloudflare CDN
WebP images
Dark mode
AJAX realtime
Responsive mobile
One page checkout
QR payment
Voucher system
SEO schema
Messenger chat
Admin dashboard
Excel export

44. KẾT QUẢ CUỐI CÙNG

Sau khi nâng cấp đầy đủ:

  • giống app mobile thật

  • tốc độ rất nhanh

  • SEO tốt hơn WordPress

  • UI giống TikTok Shop

  • tối ưu chuyển đổi

  • chạy tốt mobile

  • có thể scale lớn

  • dễ chạy quảng cáo

  • dễ làm landing page

  • production ready