JavaScript >> Javascript 文檔 >  >> Tags >> CSS

使用 PHP、MySQL 和 jQuery 的功能建議應用程序

在規劃網站的新功能或更改時,傾聽訪問者的意見總是有益的。很長一段時間以來,我們一直僅限於設置聯繫表格並希望隨後能獲得高質量的反饋,但不幸的是,情況並非總是如此。

今天,我們將事情提升了一個檔次 - 我們正在應用已成功分享網站(例如 Digg 和美味)的相同社會原則,並鼓勵訪問者提出他們希望在您的網站上實施的功能的建議和投票。

XHTML

從新的 HTML5 文檔類型開始,我們定義了開始和結束的 head 和 title 標籤,並包含了應用程序的主要樣式表 - styles.css , 在文檔中。

suggestions.php

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Feature Suggest w/ PHP, jQuery & MySQL | Tutorialzine Demo</title>

<link rel="stylesheet" type="text/css" href="styles.css" />

</head>

<body>

<div id="page">

    <div id="heading" class="rounded">
        <h1>Feature Suggest<i>for Tutorialzine.com</i></h1>
    </div>

    <!-- The generated suggestion list comes here -->

    <form id="suggest" action="" method="post">
        <p>
            <input type="text" id="suggestionText" class="rounded" />
            <input type="submit" value="Submit" id="submitSuggestion" />
        </p>
    </form>

</div>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script src="script.js"></script>
</body>
</html>

之後是 body 標籤和 #page div,它是主要的容器元素。它包含標題、包含所有建議的無序列表(由 PHP 生成,稍後您將看到)和提交表單。

最後,我們包含了來自 Google 的 AJAX 庫 CDN 的 jQuery 庫,以及我們自己的 script.js 文件,這將在本教程的最後一節中詳細討論。

表架構

該應用程序使用兩個 MySQL 表來存儲數據。建議和 Suggestions_votes。第一個表包含建議的文本和數據,例如評分和項目收到的票數。第二個表記錄了投票者的 IP,防止每個 IP 在一天內投出超過一票。

為了加快選擇查詢,在 rating 上定義了一個索引 場地。這有助於顯示按受歡迎程度排序的建議。

建議投票表有一個由三個字段組成的主鍵 - suggestion_id ,IP 選民的姓名和日期 的投票。而且由於主鍵不允許重複行,我們可以確保用戶每天只能投票一次,只需在插入後檢查affected_rows變量的值即可。

PHP

在深入研究建議項的生成和 AJAX 交互之前,首先我們必須看一下建議 PHP 類。它使用兩個 PHP 魔術方法(除了構造函數)為我們的代碼提供豐富的功能。生成首頁時,PHP 對數據庫運行 MySQL 選擇查詢,並為每個表行創建此類的對象。行的列作為屬性添加到對像中。

suggestion.class.php

class Suggestion
{
    private $data = array();

    public function __construct($arr = array())
    {
        if(!empty($arr)){

            // The $arr array is passed only when we manually
            // create an object of this class in ajax.php

            $this->data = $arr;
        }
    }

    public function __get($property){

        // This is a magic method that is called if we
        // access a property that does not exist.

        if(array_key_exists($property,$this->data)){
            return $this->data[$property];
        }

        return NULL;
    }

    public function __toString()
    {
        // This is a magic method which is called when
        // converting the object to string:

        return '
        <li id="s'.$this->id.'">
            <div class="vote '.($this->have_voted ? 'inactive' : 'active').'">
                <span class="up"></span>
                <span class="down"></span>
            </div>

            <div class="text">'.$this->suggestion.'</div>
            <div class="rating">'.(int)$this->rating.'</div>
        </li>';
    }
}

__toString() 方法用於創建對象的字符串表示。在它的幫助下,我們可以構建 HTML 標記,包括建議標題和投票數。

__get() 方法用於將對類的未定義屬性的訪問路由到 $data 大批。這意味著如果我們訪問 $obj->suggestion ,並且這個屬性是未定義的,它將從 $data 數組中獲取,並像存在一樣返回給我們。這樣我們就可以將一個數組傳遞給構造函數,而不是設置所有屬性。我們在 ajax.php 中創建對象時使用它 .

現在讓我們繼續在首頁生成無序列表。

suggestions.php

require "connect.php";
require "suggestion.class.php";

// Converting the IP to a number. This is a more effective way
// to store it in the database:

$ip = sprintf('%u',ip2long($_SERVER['REMOTE_ADDR']));

// The following query uses a left join to select
// all the suggestions and in the same time determine
// whether the user has voted on them.

$result = $mysqli->query("
    SELECT s.*, if (v.ip IS NULL,0,1) AS have_voted
    FROM suggestions AS s
    LEFT JOIN suggestions_votes AS v
    ON(
        s.id = v.suggestion_id
        AND v.day = CURRENT_DATE
        AND v.ip = $ip
    )
    ORDER BY s.rating DESC, s.id DESC
");

$str = '';

if(!$mysqli->error)
{
    // Generating the UL

    $str = '<ul class="suggestions">';

    // Using MySQLi's fetch_object method to create a new
    // object and populate it with the columns of the result query:

    while($suggestion = $result->fetch_object('Suggestion')){

        $str.= $suggestion; // Uses the __toString() magic method.

    }

    $str .='</ul>';
}

運行查詢後,我們使用 fetch_object() $result 的方法 目的。此方法為結果中的每一行創建一個給定類的對象,並將該行的列作為公共屬性分配給該對象。

PHP 還管理 jQuery 發送的 AJAX 請求。這是在 ajax.php 中完成的 .為了區分一個 AJAX 動作和另一個動作,腳本採用 $_GET['action'] 參數,可以是兩個值之一 - 'vote ' 或 '提交 '。

ajax.php

require "connect.php";
require "suggestion.class.php";

// If the request did not come from AJAX, exit:
if($_SERVER['HTTP_X_REQUESTED_WITH'] !='XMLHttpRequest'){
    exit;
}

// Converting the IP to a number. This is a more effective way
// to store it in the database:

$ip = sprintf('%u',ip2long($_SERVER['REMOTE_ADDR']));

if($_GET['action'] == 'vote'){

    $v = (int)$_GET['vote'];
    $id = (int)$_GET['id'];

    if($v != -1 && $v != 1){
        exit;
    }

    // Checking to see whether such a suggest item id exists:
    if(!$mysqli->query("SELECT 1 FROM suggestions WHERE id = $id")->num_rows){
        exit;
    }

    // The id, ip and day fields are set as a primary key.
    // The query will fail if we try to insert a duplicate key,
    // which means that a visitor can vote only once per day.

    $mysqli->query("
        INSERT INTO suggestions_votes (suggestion_id,ip,day,vote)
        VALUES (
            $id,
            $ip,
            CURRENT_DATE,
            $v
        )
    ");

    if($mysqli->affected_rows == 1)
    {
        $mysqli->query("
            UPDATE suggestions SET
                ".($v == 1 ? 'votes_up = votes_up + 1' : 'votes_down = votes_down + 1').",
                rating = rating + $v
            WHERE id = $id
        ");
    }

}
else if($_GET['action'] == 'submit'){

    // Stripping the content
    $_GET['content'] = htmlspecialchars(strip_tags($_GET['content']));

    if(mb_strlen($_GET['content'],'utf-8')<3){
        exit;
    }

    $mysqli->query("INSERT INTO suggestions SET suggestion = '".$mysqli->real_escape_string($_GET['content'])."'");

    // Outputting the HTML of the newly created suggestion in a JSON format.
    // We are using (string) to trigger the magic __toString() method.

    echo json_encode(array(
        'html'  => (string)(new Suggestion(array(
            'id'            => $mysqli->insert_id,
            'suggestion'    => $_GET['content']
        )))
    ));
}

當 jQuery 觸發 'vote ' 請求,它不期望任何返回值,因此腳本不輸出任何值。在'提交 ' 操作,然而,jQuery 期望返回一個 JSON 對象,其中包含剛剛插入的建議的 HTML 標記。這是我們創建新建議的地方 對象僅用於使用其 __toString() 魔術方法並使用內置的 json_encode() 進行轉換 功能。

jQuery

所有 jQuery 代碼都駐留在 script.js 中 .它監聽綠色和紅色箭頭上的點擊事件。但是由於可以在任何時候插入建議,我們使用 live() jQuery 方法,因此我們甚至可以在尚未創建的元素上監聽事件。

script.js

$(document).ready(function(){

    var ul = $('ul.suggestions');

    // Listening of a click on a UP or DOWN arrow:

    $('div.vote span').live('click',function(){

        var elem        = $(this),
            parent      = elem.parent(),
            li          = elem.closest('li'),
            ratingDiv   = li.find('.rating'),
            id          = li.attr('id').replace('s',''),
            v           = 1;

        // If the user's already voted:

        if(parent.hasClass('inactive')){
            return false;
        }

        parent.removeClass('active').addClass('inactive');

        if(elem.hasClass('down')){
            v = -1;
        }

        // Incrementing the counter on the right:
        ratingDiv.text(v + +ratingDiv.text());

        // Turning all the LI elements into an array
        // and sorting it on the number of votes:

        var arr = $.makeArray(ul.find('li')).sort(function(l,r){
            return +$('.rating',r).text() - +$('.rating',l).text();
        });

        // Adding the sorted LIs to the UL
        ul.html(arr);

        // Sending an AJAX request
        $.get('ajax.php',{action:'vote',vote:v,'id':id});
    });

    $('#suggest').submit(function(){

        var form        = $(this),
            textField   = $('#suggestionText');

        // Preventing double submits:
        if(form.hasClass('working') || textField.val().length<3){
            return false;
        }

        form.addClass('working');

        $.getJSON('ajax.php',{action:'submit',content:textField.val()},function(msg){
            textField.val('');
            form.removeClass('working');

            if(msg.html){
                // Appending the markup of the newly created LI to the page:
                $(msg.html).hide().appendTo(ul).slideDown();
            }
        });

        return false;
    });
});

當點擊這些箭頭之一時,jQuery 確定 LI 元素上是否存在“非活動”類。此類僅分配給建議,如果用戶在最後一天投票,並且如果存在,腳本將忽略任何點擊事件。

注意 $.makeArray 如何 用於將包含 LI 元素的 jQuery 對象轉換為真正的數組。這樣就完成了,所以我們可以使用 array.sort() 方法並向其傳遞一個自定義排序函數,該函數同時接受兩個 LI,並根據兩個元素中的哪一個具有更高的評分輸出一個負整數、零或正整數。這個數組稍後會被插入到無序列表中。

CSS

現在我們已經生成了所有標記,我們可以繼續進行樣式設置。由於樣式非常簡單,我只想向您展示將其應用於元素的左上角和右下角的類。您可以在styles.css 中查看其餘的CSS 規則。

styles.css

.rounded,
#suggest,
.suggestions li{
    -moz-border-radius-topleft:12px;
    -moz-border-radius-bottomright:12px;

    -webkit-border-top-left-radius:12px;
    -webkit-border-bottom-right-radius:12px;

    border-top-left-radius:12px;
    border-bottom-right-radius:12px;
}

請注意,Mozilla 語法與標準的不同之處在於它針對元素的不同角落。牢記這一點,我們可以將這個類應用於幾乎所有元素,正如您從演示中看到的那樣。

有了這個,我們的功能建議應用程序就完成了!

結論

如果您打算在自己的服務器上設置此腳本,則需要通過運行 tables.sql 中的代碼來創建兩個建議表 在 phpMyAdmin 的 SQL 選項卡中。還要記得在 connect.php 中填寫你的數據庫連接詳細信息 .

您可以使用此腳本從訪問者那裡收集寶貴的反饋。您還可以禁用用戶添加新建議的選項,並將其用作一種高級投票系統。

請務必在下面的評論部分分享您的想法。


Tutorial JavaScript 教程
  1. JavaScript 執行和瀏覽器限制

  2. 將 JavaScript 添加到 Fireworks

  3. WhatsTer Bot - 我為 Twilio Hackathon 構建的多功能機器人

  4. 有效處理日期和時區的 3 個簡單規則

  5. 推遲所有事情

  6. Yarn Workspaces:沒有 Lerna 的 monorepo 管理,用於應用程序和編碼示例

  7. 如何選擇採用哪種 SSR 策略?

  1. 語法和基本結構 (Pt a) - 前端開發系列的第 8 部分

  2. 對象的 jQuery 返回鍵

  3. 使用 create-react-app 和 yarn 設置 Tailwind Css

  4. 使用 Turbolinks 加速您的網站

  5. 將 Prisma 與 NestJS GraphQL 服務器一起使用

  6. 為 Node.js 核心做貢獻的 6 步指南

  7. 前 45 個 AngularJS 面試問題及答案

  1. 如何使用 Hooks 和 AG Grid 優化 React 應用程序

  2. React Pluggable:快速指南

  3. 使用 JavaScript 數組方法 flatMap()

  4. 使用 React 和 NodeJS 上傳文件