بکندباز

آموزش ساخت فروشگاه اینترنتی با php و mvc

پیش نیاز های آموزش ساخت فروشگاه اینترنتی با php و معماری mvc:

قبل از شروع این آموزش باید با مباحث مقدماتی آموزش php آشنایی داشته باشید. اگر تازه کار هستید ابتدا از طریق دوره ی رایگان آموزشی زیر، نحوه ی برنامه نویسی php را فرا بگیرید:

پس از آشنایی با سینتکس های کلی php، نحوه ی تابع نویسی، اتصال با دیتابیس، کار با آرایه ها و … به این مقاله برگردید و ادامه ی آموزش را مطالعه کنید.

MVC چیست؟

mvc یک نوع ساختار در پوشه بندی فایل های یک پروژه است. mvc از سه حرف m (مخفف model) و v (مخفف view) و c (مخفف controller) تشکیل شده است. در حالت معمولی معمولاً برای برنامه نویسی یک صفحه، تمامی توابع، دستورات شرطی و حلقه ها و کد های html مربوط به آن صفحه را در یک فایل php می نویسیم. در مدل mvc توابع و کلاس ها را در فایلی جداگانه، دستورات کنترلی مثل حلقه ها را در فایلی جداگانه و فایل های html را در فایلی جداگانه می نویسیم. و هنگام اجرای صفحه مورد نظر هر سه فایل به نحوی با هم مرتبط می شوند. و در نهایت تمام فایل های توابع مربوط به همه ی صفحات سایت را در یک پوشه model، فایل های مربوط به دستورات کنترلی را در پوشه ی controller و فایل های مربوط به دستورات html را در پوشه ی view قرار می دهیم. این کار چندین مزیت دارد:

1- اشکال یابی پروژه خیلی ساده تر می شود.

2- از آنجایی که تمامی توابع در یک پوشه ی جداگانه نوشته شده اند به سادگی می توان از همان فایل ها برای پروژه های دیگر استفاده کرد و فقط بخش view یا ظاهر آن را تغییر داد. پس امکان استفاده ی مجدد از یک کد را خیلی بالا می برد.

3- در پروژه های بزرگ که هم برنامه نویس فرانت اند دارند و هم بک اند، هر دو گروه برنامه نویس می توانند مستقل از هم روی فایل های مربوط به خود کار کنند.

شروع آموزش برنامه نویسی یک فروشگاه اینترنتی با php و با معماری mvc

ابتدا لیست می کنیم که در یک فروشگاه اینترنتی چه صفحاتی باید وجود داشته باشد (از این لیست در ساخت فایل های controller و view استفاده می شود):

  • صفحه اصلی
  • صفحه لیست محصولات
  • صفحه جزئیات یک محصول
  • صفحه سبد خرید
  • صفحه صورت حساب
  • صفحه ورود
  • صفحه ثبت نام
  • صفحه پروفایل کاربر
  • صفحه لیست سفارشات کاربر
  • صفحه تنظیمات کاربر

و همچنین لیست می کنیم که چه مفاهیم مجزایی در یک فروشگاه اینترنتی وجود دارد (از این لیست در ساخت فایل های بخش model استفاده می شود):

  • محصول
  • کاربر
  • سفارش

برای هر کدام از آیتم های گفته شده در این دو لیست باید یک فایل ساخته شود و ساختار نهایی یک فروشگاه اینترنتی به شکل زیر در خواهد آمد:

> model
      DB.php //(اتصال به دیتابیس)
      Products.php
      Users.php
      Orders.php

> controller
      home.php
      products.php
      product-detail.php
      cart.php
      checkout.php
      login.php
      register.php
      user-account.php
      user-orders.php
      user-settings.php

> view
      home.php
      products.php
      product-detail.php
      cart.php
      checkout.php
      login.php
      register.php
      user-account.php
      user-orders.php
      user-settings.php
> index.php

حالا روش کلی نوشتن فایل های model و controller و view را با بررسی یک فایل از هر کدام بررسی می کنیم.

برنامه نویسی فایل های بخش model

فایل های بخش model شامل کلاس ها یا توابع مربوط به هر فایل است. همچنین اتصالات دیتابیس نیز درون همین فایل ها انجام می شود. معمولاً هر کدام از این فایل ها یک جدول متناسب در دیتابیس دارند. پس قبل از شروع کد نویسی جدول های مربوطه را در دیتابیس می سازیم. مثلاً جدول محصولات در یک فرم خیلی سایده می تواند شامل ستون های زیر باشد:

  • id = شناسه محصول
  • title = نام محصول
  • image_url = لینک تصویر محصول
  • description = توضیحات محصول
  • price = قیمت محصول

کد مربوط به این جدول و باقی جدول ها را مانند زیر در یک فایل با پسوند sql نوشته و آن فایل را در دیتابیس ایمپورت می کنیم تا جدول ها ساخته شوند.

CREATE TABLE `products` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `title` varchar(100) DEFAULT NULL,
  `image_url` varchar(200) DEFAULT NULL,
  `description` text  DEFAULT NULL,
  `price` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `orders` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `user_id` text DEFAULT NULL,
  `order_date` BIGINT  DEFAULT NULL,
  `stauts` varchar(10) DEFAULT 'pending'
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `order_products` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `order_id` int(11) DEFAULT NULL,
  `product_id` int(11)  DEFAULT NULL,
  `quantity` int(11) DEFAULT 1
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `users` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `username` varchar(20) DEFAULT NULL,
  `email` varchar(100)  DEFAULT NULL,
  `password` varchar(100) DEFAULT NULL
  `register_date` BIGINT DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

همانطور که می بینید برای سفارشات دو جدول ساخته ایم. در جدول orders هر سفارشی که ساخته شود با یک آیدی یکتا در این جدول اضافه می شود و شناسه ی مشتری، تاریخ ثبت سفارش و وضعیت سفارش از این جدول استخراج می شود. در جدول order_products محصولات مربوط به هر سفارش با تعداد هر محصول ثبت خواهد شد.

پس از ساخت دیتابیس شروع به نوشتن فایل های موجود در دسته ی model می کنیم. مثلاً فایل Products.php می تواند به شکل زیر باشد:

<?php

function get_products($limit = 20, $offset = 0, $sortby= 'id', $order = 'DESC'){
    $dbc = $GLOBALS['dbc'];
    if($limit) { // اگر صفر نبود، یعنی محدوده ی مشخص درخواست شده است
        $num = "LIMIT $limit OFFSET $offset";
    }
    $products = mysqli_query($dbc, "SELECT * FROM products ORDER BY $sortby $order $num");
    $result = [];
    while($row = mysqli_fetch_array($products)) {
        $result[] = $row;
    }

    return $result;
}

//تعداد کل محصولات
function get_all_products_num() {
    $dbc = $GLOBALS['dbc'];
    $products = mysqli_query($dbc, "SELECT * FROM products");
    return mysqli_num_rows($products);
}

function get_product($product_id) {
    $dbc = $GLOBALS['dbc'];
  
    $product = mysqli_query($dbc, "SELECT * FROM products WHERE id = $product_id");
    return mysqli_fetch_array($product);
}

//ویرایش محصول که در برنامه نویسی پنل مدیریت استفاده می شود
function edit_product($info, $product_id) {
    $dbc = $GLOBALS['dbc'];
    $update = '';
    $first = true;
    foreach($info as $key => $value) {
        if(!$first) {
            $update .= ', ';
        } else {
            $first = false;
        }
        $update .= $key." = '$value'";
        
    }

    mysqli_query($dbc, "UPDATE products SET $update WHERE id = $product_id");
}

//حذف محصول که در برنامه نویسی پنل مدیریت استفاده می شود
function delete_product($product_id) {
    $dbc = $GLOBALS['dbc'];
    mysqli_query($dbc, "DELETE FROM products WHERE id = $product_id");
}
  • این کد، کد اولیه برای آموزش ساده ی ساخت یک فایل model است و رفته رفته تا انتهای مقاله ویرایش شده و پیشرفته تر می شود.
  • توابع مربوط به اتصال به دیتابس در فایل DB.php نوشته شده است.
  • قبل از لود این فایل، فایل مربوط به دیتابیس را در index.php لود می کنیم و از یک متغیر global برای دسترسی به دیتابیس استفاده می کنیم. در مورد محتوای فایل index.php در بخش آدرس دهی بیشتر خواهید آموخت.
  • کل عملیات مربوط به دیتابیس (جستجو، آپدیت، حذف و …) در فایل های model باید نوشته شود.
  • توابعی مثل edit_product و delete_product که مربوط به ویرایش و حذف محصول هستند و برای برنامه نویسی پنل مدیریت کاربرد دارند نیز در فایل model مربوط به محصول نوشته می شوند.
  • می توانیم توابع خاص تر مثلاً is_in_stock که موجود بودن یا نبودن محصول را بررسی می کند یا is_on_sale که بررسی می کند آیا محصول در فروش ویژه قرار دارد یا نه را نیز در صورت نیاز به این فایل اضافه کنیم.
  • همانطور که می بینید تابع get_products آرگومان های زیادی دارد و شاید در برنامه نویسی یک فروشگاه حرفه ای تر مجبور باشیم پارامتر های بیشتری نیز به آن اضافه کنیم. در این مواقع به جای همه ی پارامتر ها یک پارامتر آرایه ای قرار می دهیم و مقادیر پیشفرض آن را در تابع تعریف می کنیم. این روش هم برنامه نویسی را ساده تر می کند و هم هنگام فراخوانی تابع راحت تر می توان آرگومان های دلخواه را تغییر داد. این شیوه را در فایل Orders.php می بینید:
<?php

/**
 * از این تابع برای دریافت لیست سفارشات استفاده می شود.
 * اگر پارامتر شناسه کاربر وارد شود، سفارشات مربوط به یک کاربر خاص برداشت می شود
 * @param $args با استفاده از این پارامتر می توانیم لیست سفارشات را فیلتر کرده یا تعداد سفارشات دریافتی را مشخص کنیم
 */
function get_orders($args = null){

    if(!$args) $args = get_default_order_args();
    extract($args); //این تابع تمام اعضای آرایه را به متغیر تبدیل می کند

    $dbc = $GLOBALS['dbc'];
    if($limit) { // اگر صفر نبود، یعنی محدوده ی مشخص درخواست شده است
        $num = "LIMIT $limit OFFSET $offset";
    }
    $where = '';

    if($user_id) {
        $where = 'WHERE user_id = '.$user_id;
    }
    $orders = mysqli_query($dbc, "SELECT * FROM orders $where ORDER BY $sortby $order $num");
    $result = [];
    while($row = mysqli_fetch_array($orders)) {
        $result[] = $row;
    }

    return $result;
}

//تعداد کل سفارشات
function get_all_orders_num($args = null) {
    if(!$args) $args = get_default_order_args();
    extract($args); //این تابع تمام اعضای آرایه را به متغیر تبدیل می کند
    $where = '';

    if($user_id) {
        $where = 'WHERE user_id = '.$user_id;
    }
    $dbc = $GLOBALS['dbc'];
    $orders = mysqli_query($dbc, "SELECT * FROM orders $where");
    return mysqli_num_rows($orders);
}


function get_order($order_id) {
    $dbc = $GLOBALS['dbc'];
  
    $order = mysqli_query($dbc, "SELECT * FROM orders WHERE id = $order_id");
    return mysqli_fetch_array($order);
}


function get_order_products($order_id) {
    $dbc = $GLOBALS['dbc'];
  
    $order_products = mysqli_query($dbc, "SELECT * FROM order_products INNER JOIN products
                                            ON order_products.product_id = products.id WHERE order_id = $order_id");
    $result = [];
    while($row = mysqli_fetch_array($order_products)) {
        $result[] = $row;
    }
    return $result;
}

//ویرایش وضعیت سفارش که در برنامه نویسی پنل مدیریت استفاده می شود
function edit_order_status($status, $order_id) {
    $dbc = $GLOBALS['dbc'];
    mysqli_query($dbc, "UPDATE orders SET status = '$status' WHERE id = $order_id");
}

//حذف سفارش که در برنامه نویسی پنل مدیریت استفاده می شود
function delete_order($order_id) {
    $dbc = $GLOBALS['dbc'];
    mysqli_query($dbc, "DELETE FROM orders WHERE id = $order_id");
}

function get_default_order_args(){
    return [
        'limit' => 20,
        'offset' => 0,
        'sortby' => 'id',
        'order' => 'DESC',
        'user_id' => 0,
    ];
}
  • مقادیر پیشفرض پارامتر args را از تابع get_default_order_args دریافت می کنیم.
  • با استفاده از تابع extract تمام کلید های موجود در آرایه ی args را به متغیر تبدیل می کنیم تا کار با آنها ساده تر شود.
  • در تابع get_order_products از دستور inner join برای ترکیب دو جدول order_products و products استفاده کرده ایم تا اطلاعات مربوط به هر محصول را نیز برداشت کنیم.

فایل مربوط به کاربران (Users.php) نیز به همین شکل نوشته می شود با این تفاوت که چندین تابع دیگر که مربوط به ثبت نام و ورود کاربر و اعتبارسنجی اطلاعات است به آن افزوده شده است.

<?php

function get_users($args = null){

    $args = array_merge(get_default_users_args(), $args);
    extract($args); //این تابع تمام اعضای آرایه را به متغیر تبدیل می کند

    $dbc = $GLOBALS['dbc'];
    if($limit) { // اگر صفر نبود، یعنی محدوده ی مشخص درخواست شده است
        $num = "LIMIT $limit OFFSET $offset";
    }
    $users = mysqli_query($dbc, "SELECT * FROM users ORDER BY $sortby $order $num");
    $result = [];
    while($row = mysqli_fetch_array($users)) {
        $result[] = $row;
    }

    return $result;
}

function get_default_users_args(){
    return [
        'limit' => 20,
        'offset' => 0,
        'sortby' => 'id',
        'order' => 'DESC',
    ];
}

//تعداد کل کاربران
function get_all_users_num() {
    $dbc = $GLOBALS['dbc'];
    $users = mysqli_query($dbc, "SELECT * FROM users");
    return mysqli_num_rows($users);
}

function get_user($user_id) {
    $dbc = $GLOBALS['dbc'];
  
    $user = mysqli_query($dbc, "SELECT * FROM users WHERE id = $user_id");
    return mysqli_fetch_array($user);
}


//حذف کاربر که در برنامه نویسی پنل مدیریت استفاده می شود
function delete_user($user_id) {
    $dbc = $GLOBALS['dbc'];
    mysqli_query($dbc, "DELETE FROM users WHERE id = $user_id");
}


function add_user($username, $email, $password) {
    $dbc = $GLOBALS['dbc'];

    $hashed_password = md5($password);
    $user_id = mysqli_query($dbc, "INSERT INTO users (username, email, password, register_date) 
                                            VALUES ('$username', '$email', '$hashed_password')");
    return $user_id;
}

//بررسی معتبر بودن نام کاربری
//می توانید شرایط دلخواه خود را برای نام کاربری تعیین کنید
function is_username_valid($username) {
    if(strlen($username) < 6) { // نام کاربری باید بیشتر از 6 کاراکتر باشد
        return false;
    }
    if(strpos($username, ' ') !== false ){//نام کاربری نباید شامل فاصله خالی باشد
        return false;
    }
    return true;
}

//بررسی اینکه آیا پسورد به اندازه کافی قوی است یا نه
//می توانید شرایط دلخواه خود را برای پسورد تعیین کنید
function is_password_strong($password) {
    if(strlen($password) < 6) { //پسورد باید حتماً بیشتر از 6 حرف باشد
        return false;
    }
    return true;
}

//چک کردن اینکه آیا کاربری با این اطلاعات وجود دارد یا نه
function is_user_exists($username, $password) {
    $dbc = $GLOBALS['dbc'];

    $hashed_password = md5($password);
    $user = mysqli_query($dbc, "SELECT * FROM users WHERE username = '$username' AND password = '$hashed_password'");
    if(mysqli_num_rows($user) > 0) return true;
    return false;
}


 

برنامه نویسی بخش controller

هنگامی که یک صفحه لود می شود، باید فایل controller مربوط به آن صفحه اجرا شود. در این فایل مقادیری که برای نمایش در صفحه نیاز داریم را از فایل model دریافت می کنیم و سپس آنها را به فایل view مربوطه ارسال می کنیم. یعنی فایل view مرتبط با این صفحه را در فایل controller خود include می کنیم. برای درک بهتر آنچه گفته شد، مثال زیر که مربوط به صفحه لیست محصولات است را با هم بررسی می کنیم. در این مثال می خواهیم لیست آخرین محصولات را در تقسیم 25 تایی نشان دهیم:

حالت اولیه ی فایل products.php در بخش controller:

<?php
//صفحه لیست محصولات
include "../model/Products.php";

$products = get_products(25);

include "../view/products.php";
  • فایل model مربوط به محصولات را در این فایل وارد می کنیم
  • با استفاده از تابع get_products لیست محصولات را دریافت کرده و پارامتر اول که تعداد محصولات در هر صفحه است را 25 می گذاریم
  • در آن فایل view مربوط به محصولات که محتوای آن همان کد HTML است را در این فایل وارد می کنیم تا متغیر products$ را به آن فرستاده و در صفحه نمایش دهیم

حال کمی این فایل را پیشرفته تر کرده و دستورات کنترلی مربوط به صفحه بندی را هم به آن اضافه می کنیم:

<?php
//صفحه لیست محصولات
include "../model/Products.php";

if(isset($_GET['page'])) {
    $page = $_GET['page'];
} else {
    $page = 1;
}
$limit = 25;
$offset = ($page - 1) * $limit;
$products = get_products($limit, $offset);

$total_products = get_all_products_num(); //برای استفاده در صفحه بندی
$total_pages = floor($total_products/$limit);

include "../view/products.php";
  • پارامتر page با متد GET توسط کاربر با انتخاب شماره صفحه به این فایل ارسال می شود
  • مقدار offset با توجه به صفحه درخواست شده تغییر می کند.
  • تعداد کل محصولات را برای به دست آوردن تعداد کل صفحات در فایل وارد می کنیم.
  • تعداد صفحات برابر می شود با تعداد کل محصولات تقسیم بر تعداد محصولات موجود در هر صفحه که به پایین رند شده است
  • متغیر های page که صفحه کنونی است، products و total_pages را برای فایل view خود لازم داریم

در همه ی فایل های بخش controller ابتدا فایل یا فایل هایی از بخش model که به آنها نیاز داریم را include می کنیم. سپس با استفاده از دستورات کنترلی و حلقه ها در php و توابع model، متغیر های مورد نیاز برای داینامیک کردن فایل html مربوط به آن صفحه را می سازیم. همچنین متغیر هایی که با متد های http مثل GET یا POST ارسال می شوند را نیز در این فایل دریافت می کنیم و در آخر، فایل view آن صفحه را include می کنیم.

بقیه ی فایل های موجود در بخش controller همگی با همین الگوریتم نوشته می شوند. چند فایل دیگر را نیز برای دیدن حالت های مختلف فایل های controller بررسی می کنیم. برای مشاهده ی باقی فایل ها سورس کدی که در انتهای مقاله برای دانلود قرار داده شده است را بررسی کنید.

 

برنامه نویسی بخش view

فایل های درون بخش view همان صفحات html هستند که با پارامتر هایی که در صفحه controller مربوطه ایجاد شده است، داینامیک می شوند. برای مثال صفحه ی زیر می تواند فایل view مربوط به لیست سفارشات باشد:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>لیست سفارشات</title>
    <?php include 'head.php';  ?>
</head>
<body>
    <?php include "header.php" ?>
    <?php include "sidebar.php" ?>
    <!-- لیست محصولات -->
    <div class="products">
        <?php
        foreach($products as $product) {
            ?>
            <div>
                <a href="/product/<?= $product['id'] ?>">
                    <h3><?= $product['title'] ?></h3>
                    <p><?= $product['description'] ?></p>
                    <span><?= number_format($product['price']) ?> تومان</span>
                </a>
            </div>
            <?php
        }
        ?>
    </div>
    <!-- صفحه بندی -->
    <div class="pagination">
        <?php
        for($i=1; $i< ($total_pages + 1); $i++) {
            ?>
            <span class="<?= ($page == $i)?'active':'' ?>"><?= $i ?></span>
            <?php
        }
        ?>
    </div>
    <?php include 'scripts.php';  ?>
</body>
</html>
  • با استفاده از حلقه ی foreach یکی یکی محصولات را از متغیر products برداشته و اطلاعات آن را درون تگ های مرتبط قرار می دهیم
  • با استفاده از متغیر total_pages و یک حلقه ی for شماره صفحات را در پایین لیست نوشته ایم
  • با استفاده از متغیر page تگ صفحه ای که در حال حاضر باز است را یک کلاس جدا گانه به نام active می دهیم تا بتواند رنگ متفاوت بگیرد
  • کد هایی که معمولا در همه ی فایل های html یک سایت مشترک است مثل هدر سایت، سایدبار، اسکریپت ها و استایل ها را در فایل هایی جداگانه می نویسیم تا از اضافه کاری جلوگیری شود. سپس آنها را در هر صفحه include می کنیم. (در اینجا فایل های head.php, header.php, sidebar.php, scripts.php)
  • هر محصول به آدرس /product/<?= $product['id'] ?> لینک شده است که صفحه جزئیات آن محصول را نمایش می دهد. نحوه ی این آدرس دهی را در بخش آدرس دهی در انتهای این آموزش خواهید آموخت.

 

فایل register.php از بخش controller

<?php
session_start();

//بررسی اینکه آیا کاربر در سایت وارد شده است یا نه
//اگر کاربر در سایت وارد شده بود به پروفایلش هدایت شود
if(isset($_SESSION['user_id']) || isset($_COOKIE['user_id'])){
    header("location : /my-account");
} 

include "../model/Users.php";

$error = [];
//بررسی می کنیم که آیا کاربر فرم را پر کرده و ارسال کرده است؟
if(isset($_POST['register'])) {
    //دریافت اطلاعاتی که کاربر از طریق فرم ارسال کرده است
    $username = $_POST['username'];
    $email = $_POST['email'];
    $password = $_POST['password'];
    $re_password = $_POST['re_password'];

    if(!is_username_valid($username)) {
        $error[] = 'نام کاربری معتبر نیست.';
    }
    if(!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $error[] = 'ایمیل وارد شده معتبر نیست!';
    }
    if($password != $re_password) {
        $error[] = 'پسورد ها با هم برابر نیستند!';
    }
    if(!is_password_strong($password)) {
        $error[] = 'لطفاً پسورد قوی تری انتخاب کنید.';
    }

    //اگر هیچ خطایی نبود، اطلاعات کاربر در دیتابیس ذخیره شود
    if(empty($error)) {
        $user_id = add_user($username, $email, $password);
        $_SESSION['user_id'] = $user_id;
        if(isset($_POST['remember'])) {
            setcookie('user_id', $user_id, time() + 30*24*60*60, '/');
        }
        header("location: /my-account");
    }
}

include "../view/register.php";
  • برای ساخت سشن ها پس از ورود کاربر باید ابتدای فایل تابع session_start را فراخوانی کنیم.
  • قبل از لود صفحه ثبت نام بررسی می کنیم که اگر کاربر از قبل در سایت وارد شده بود، صفحه ی ثبت نام باز نشود و کاربر به پروفایل خود هدایت شود. این دستور را در فایل login هم می نویسیم
  • اطلاعات فرم ثبت نام که از طریق متد POST ارسال می شود را دریافت کرده و با استفاده از توابعی که در فایل model مربوط به کاربران نوشتیم آن ها را اعتبار سنجی می کنیم.
  • و سپس اگر تمام اطلاعات وارد شده صحیح بود، با یکی از همان توابع، اطلاعات کاربر را در دیتابیس ثبت می کنیم
  • و در غیر اینصورت، پارامتر error را در فایل view نمایش می دهیم.

فایل register.php از بخش view

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ثبت نام</title>
    <?php include 'head.php';  ?>
</head>
<body>
    <?php include "header.php" ?>
    <?php include "sidebar.php" ?>
    <!-- فرم ثبت نام -->
    <div class="register">
    <?php
    //نمایش ارور ها در صورت وجود
    if(!empty($error)) {
        ?>
        <ul>
        <?php
        foreach($error as $err) {
            echo '<li>'.$err.'</li>';
        }
        ?>
        </ul>
        <?php
    }
    ?>
        <form action="" method="post">
            <input type="text" name="username" id="username" value="<?= $username??'' ?>" placeholder="نام کاربری">
            <input type="email" name="email" id="email" value="<?= $email??'' ?>" placeholder="ایمیل">
            <input type="password" name="password" id="password" value="<?= $password??'' ?>" placeholder="کلمه عبور">
            <input type="password" name="re_password" id="re_password" value="<?= $re_password??'' ?>" placeholder="تکرار کلمه عبور">
            <input type="checkbox" name="remember" id="remember"> مرا به خاطر بسپار
            <button type="submit" name="register">ثبت نام</button>
        </form>
    </div>
    <?php include 'scripts.php';  ?>
</body>
</html>
  • در بالای فرم چک می کنیم اگر متغیر error مقداری داشت آنها را به صورت لیست چاپ کند
  • اگر اروری وجود داشته باشد، مثلاً نام کاربری معتبر نباشد یا …، می خواهیم که اطلاعات فرم پاک نشوند و کاربر بتواند دوباره آنها را ویرایش کند. پس برای هر کدام از فیلد های فرم مقداری برای ویژگی value قرار می دهیم.
  • عبارت <?= $username??'' ?> به این معنی است که اگر متغیر username وجود داشته باشد، مقدار آن را چاپ کند

نحوه ی آدرس دهی صفحات

اگر به کد های فایل register دقت کرده باشید، برای ارجاع کاربر به پروفایلش از آدرس my-account/ استفاده کردیم. که هر گاه این آدرس در مرورگر ارسال شود، باید فایل user-account.php در پوشه ی controller لود بشود. برای اتصال آدرس my-account/ به فایل user-account.php در این پروژه از کتابخانه ی altorouter استفاده می کنیم. این کتابخانه که شامل یک فایل php است را با استفاده از لینک زیر دانلود می کنیم و فایل AltoRouter.php را در پوشه ی model کپی می کنیم:

دانلود کتابخانه ی altorouter

سپس در فایل index.php که در پوشه ی اصلی هاست قرار دارد، آدرس دهی my-account را به شیوه ی زیر انجام می دهیم:

<?php

require 'model/AltoRouter.php';

$router = new AltoRouter();

//لیست آدرس دهی ها
$router->map('GET', '/my-account', 'user-account.php', 'my-account');

$match = $router->match();

if ($match) {
    require 'controller/' . $match['target'];
} else {
    header("HTTP/1.0 404 Not Found");
    header("location: /404");
}

این خط مربوط به آدرس دهی است:

$router->map('GET', '/my-account', 'user-account.php', 'my-account');
  • در پارامتر اول مشخص می کنیم که این فایل از چه متدی می تواند اطلاعات را دریافت کند. اگر بخواهیم کاربر بتواند صفحه را باز کند و یا اطلاعاتی را با متد get به این صفحه ارسال کند در این قسمت GET می نویسیم. و اگر علاوه بر آن بخواهیم اطلاعاتی را با متد POST نیز به این صفحه ارسال کنیم در این پارامتر عبارت GET|POST می نویسیم. متد های دیگر هم به همین شکل می توانند اضافه شوند.
  • پارامتر دوم آدرسی است که می خواهیم کاربر برای باز کردن این صفحه در مروگر وارد کند و آدرس اصلی این صفحه است.
  • در پارامتر سوم، نام فایل موجود در پوشه ی controller را قرار می دهیم.
  • و در پارامتر آخر یک نام یکتا که در واقع شناسه ی این آدرس است.

متد match بررسی می کند که آیا آدرس وارد شده در مرورگر با یکی از آدرس های نوشته شده در لیست مطابقت دارد یا نه. اگر با هیچ آدرسی مطابقت نداشت ارور 404 را در خروجی بر می گرداند و اگر مطابقت داشت، مشخصات اولین آدرس مطابقت داده شده را در قالب یک آرایه ی انجمنی در خروجی بر می گرداند. این آرایه ی انجمنی کلیدی به نام target دارد که همان پارامتر سوم آدرس است. که ما نام فایل controller خود را در ان نوشته ایم. از این کلید برای لود فایل controller مربوطه استفاده می کنیم.

بقیه ی آدرس دهی ها را نیز به شکل زیر مشخص می کنیم. همچنین از آنجایی که فایل های controller انتهای فایل index.php نوشته می شوند، تابع session_start را از ابتدای همه ی فایل های controller حذف کرده و به ابتدای فایل index.php می آوریم. چرا که هنگام لود صفحه این تابع باید اول از همه لود شود:

<?php
session_start();

require 'model/AltoRouter.php';

$router = new AltoRouter();

//لیست آدرس دهی ها
$router->map('GET', '/', 'home.php', 'home');
$router->map('GET', '/products', 'products.php', 'products');
$router->map('GET', '/product/[i:id]', 'product-detail.php', 'product-detail');
$router->map('GET|POST', '/cart', 'cart.php', 'cart');
$router->map('GET|POST', '/checkout', 'checkout.php', 'checkout');
$router->map('GET|POST', '/login', 'login.php', 'login');
$router->map('GET|POST', '/register', 'register.php', 'register');
$router->map('GET', '/my-account', 'user-account.php', 'my-account');
$router->map('GET', '/my-orders', 'user-orders.php', 'my-orders');
$router->map('GET|POST', '/my-settings', 'user-settings.php', 'my-settings');

$match = $router->match();

if ($match) {
    include 'model/DB.php';
    //اگر پارامتر هایی به همراه آدرس ارسال شده باشند آنها را در یک متغیر میریزیم
    $parameters = $match['params'];
    require 'controller/' . $match['target'];
} else {
    header("HTTP/1.0 404 Not Found");
    header("location: /404");
}

همانطور که در فایل view مربوط به صفحه ی products دیدید، هر محصول به آدرس /product/<?= $product['id'] ?> لینک شده بود. این آدرس یک آدرس متغیر است که شناسه ی محصول که یک عدد است بستگی دارد. چنین آدرسی را به شکل زیر در فایل index.php مشخص کرده ایم:

$router->map('GET', '/product/[i:id]', 'product-detail.php', 'product-detail');

عبارت [i:id] می گوید که یک عدد باید در این قسمت قرار گرفته باشد، و همچنین آن عدد را به عنوان پارامتری به نام id به فایل product-detail.php ارسال می کند که با استفاده از کلید params از آرایه ی match می توانیم مقدار فرستاده شده را ببینیم..

نکته: پس از استفاده از این آدرس دهی دیگر نمی توانیم آدرس فایل های include شده در controller را به شکل آدرس نسبی بنویسیم. چرا که آدرس نسبت به url جدید محاسبه می شود نه نسبت به فایل php بخش controller. در نتیجه برای جلوگیری از بروز خطا بهتر است همه ی آدرس ها را به صورت مطلق بنویسیم. برای اینکار یک متغیر ثابت که آدرس مسیر پوشه ی اصلی را در خود نگه می دارد تعیین می کنیم و آدرس های دیگر را مطابق با این آدرس تعیین می کنیم. متغیر زیر را در فایل index.php تعریف کرده:

define('BASE_PATH', dirname(__FILE__));

و در فایل های controller به جای آدرس زیر:

include "../model/Products.php";

این آدرس را قرار می دهیم:

include BASE_PATH."/model/Products.php";

محتوای نهایی فایل index.php:

<?php
session_start();
define('BASE_PATH', dirname(__FILE__));

require 'model/AltoRouter.php';
$router = new AltoRouter();

//لیست آدرس دهی ها
$router->map('GET', '/', 'home.php', 'home');
$router->map('GET', '/products', 'products.php', 'products');
$router->map('GET', '/product/[i:id]', 'product-detail.php', 'product-detail');
$router->map('GET|POST', '/cart', 'cart.php', 'cart');
$router->map('GET|POST', '/checkout', 'checkout.php', 'checkout');
$router->map('GET|POST', '/login', 'login.php', 'login');
$router->map('GET|POST', '/register', 'register.php', 'register');
$router->map('GET', '/my-account', 'user-account.php', 'my-account');
$router->map('GET', '/my-orders', 'user-orders.php', 'my-orders');
$router->map('GET|POST', '/my-settings', 'user-settings.php', 'my-settings');

$match = $router->match();

if ($match) {
    
    include 'model/DB.php';
    //اگر پارامتر هایی به همراه آدرس ارسال شده باشند آنها را در یک متغیر میریزیم
    $parameters = $match['params'];
    require 'controller/' . $match['target'];
} else {
    header("HTTP/1.0 404 Not Found");
    header("location: /404");
}

آموزش کار با درگاه های پرداخت به زودی افزوده خواهد شد. درگاه های پرداختی درخواستی خود را در بخش کامنت ها بنویسید تا در آموزش های آتی به این صفحه افزوده شود.

دانلود سورس کد

توجه:  در این سورس کد تمرکزی بر بخش فرانت اند و html نشده است و شما بایستی بنا بر انتخاب خود و با سلیقه ی خود بخش فرانت اند را طراحی کنید. این سورس آموزش ساخت بک اند برای یک فروشگاه اینترنتی به زبان php و بر پایه ی معماری mvc است اما آنقدر کامل نیست که بتوانید به تنهایی برای ساخت یک فروشگاه از آن استفاده کنید. و شما با استفاده از آموزش های اینجا بایستی برنامه های خود را روی این کد پیاده کرده و آن را برای خود حرفه ای تر و شخصی سازی تر کنید.

دیدگاه‌ها

*
*

    کیوان پاسخ

    سلام ، بسیار کامل ، خلاصه ، قابل فهم بود. بهترین آموزش ایجاد MVC بود که در وب دیدم . امیدوارم با اضافه کردن کتابخانه های متنوع این آموزش را کامل تر و حرفه ای تر ببینم . ممنون از زحمتی که برای تهیه این آموزش بسیار مفید کشیدید .لطفا هرچه زود تر بخش درگاه پرداخت را به انتخاب خودتون قرار بدید .

پشتیبانی واتس اپ