Programujeme chat s dynamickým načítáním

Ve svém prvním článku jsem psal o dynamickém načítání dat na www stránce. V tomto článku bych chtěl ukázat, jak se dá toto načítání použít například u velice zjednodušeného chatu. K tomuto chatu uvedu i popsaný skript na validaci formuláře, která je u tohoto chatu také použita.

14.3.2013 10:00 | Vladimír Macháček | přečteno 12125×

Úvodem

Nejdříve bych se rád zmínil o tom, že tento chat, na kterém budu demonstrovat dynamické načítání dat, je jen praktická ukázka a aby jej bylo možné umístit na stránky, muselo by se ještě pořešit zabezpečení a provést pár úprav. Tím ale nechci říci, že chat o kterém budu dále psát je nefunkční.

Co bude chat umět

Chat o kterém píšu bude pracovat pouze s daty z databáze. Uvádím to proto, že data z chatu, jako například zprávy, je možné načítát ze souboru i je do něj ukládat. Tento jednoduchý chat bude umět pravidelně zobrazovat nové zprávy, online uživatele a mazat neaktivní uživatele. Dále bude mít validaci formuláře, kterou si nebudeme stahovat, ale vytvoříme si ji. Validace bude zahrnovat zobrazování zbývajích znaků, které je ještě možno napsat do jména/zprávy a obarvení inputů podle splněné nebo nesplněné podmínky.

Ukázky z chatu

Jak to bude fungovat

Při načtení dokumentu se provede funkce rf();. Nejdříve se u chatu zobrazí vstupní adresář. Ten zahrnuje input pro vložení přezdívky. Jakmile si uživatel zvolí přezdívku a klikne na tlačítko vstoupit, skryje se vstupní formulář, spustí se interval funkce rf(); a zobrazí se mu právě probíhající konverzace na chatu, online uživatelé a formulář pro psaní zpráv. Při každé odeslané zprávě se provede aktualizace času poslední akce tohoto uživatele a kontrola, jestli nebyl uživatel odhlášen kvůli nečinnosti. Pokud byl odhlášen, skript ho vrátí na úvodní stránku.

Začínáme

Na začátek si jako v předchozím článku stáhneme knihovnu jQuery a vytvoříme adresáře se soubory ze kterých se bude náš chat skládat. Budou to tyto soubory:

Tabulky v databázi

Databáze, ve které budou uloženy tyto dvě tabulky se jmenuje test a porovnávánání v této db je nastaveno na UTF_8_czech_ci. K tomuto příkladu bude potřeba vytvořit 2 tabulky. Jedna bude chatusers a druhá chatmsg. V tabulce chatusers budou následující sloupce:

Tabulka chatmsg bude vypadat takto:

Skript pro vytvoření tabulek


<?php
$dblogin='';//Přihlašovací jméno
$dbpassword='';//Heslo
$db = new PDO('mysql:host=localhost;dbname=test;charset=UTF-8', $dblogin, $dbpassword);//Připojení k databázi
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Nastavení řízení chyb
try {
$stmt=$db->query('CREATE TABLE chatusers (id INT NOT NULL AUTO_INCREMENT ,name VARCHAR( 20 ) NOT NULL ,
time INT NOT NULL ,PRIMARY KEY ( id ))');
    echo 'Tabulka chatusers byla úspěšně vytvořena!<br/>';
} catch(PDOException $e) {
    echo 'Nastala chyba:'.$e;
    };
try {
$stmt=$db->query('CREATE TABLE chatmsg (id INT NOT NULL AUTO_INCREMENT ,name VARCHAR( 20 ) NOT NULL ,
text VARCHAR( 500 ) NOT NULL ,time TIME NOT NULL ,PRIMARY KEY ( id ))');
    echo '<br/>Tabulka chatmsg byla úspěšně vytvořena!';
} catch(PDOException $e) {
    echo 'Nastala chyba'.$e;
    }
?>

Chat.php

V tomto souboru bude hlavní struktura chatu. Chat bude obsahovat 2 formuláře. Jeden bude vstupní, ten bude pro přezdívku a druhý bude pro komentář. Formuláře si budou vzájemně prohazovat pozice a to tak, že se jim budou měnit hodnoty z display:none na display:block a opačně. Struktura tohoto souboru je následující:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/chat.js"></script>
<link rel="stylesheet" href="css/chat.css" />
<title>Chat</title>
</head>

<body>
    <div id="chat">
        <div id="chenter">
        <h3>Chat</h3>
            <form method="post" name="cheform" id="cheform">
                <label for="chnick">Nick:</label><input type="text" name="chnick" id="chnick" placeholder="3-20 znaků" />
                <input type="button" id="ech" disabled="disabled" onclick="eCh();" value="Vstoupit" />
                Zbývající znaky:<span id="nlch"></span>
                <span id="errors"></span>
            </form>
        </div>
           <div id="chroom">
            <div id="chmessages">
                <?php 
                    include('php/messages.php');
                ?>
            </div>
            <div id="chusers">
                <?php 
                    include('php/users.php');
                ?>
            </div>
            <div id="chbottom">
                <form method="post" name="chform" id="chform">
                    <label for="chmessage">Zpráva:<br /><span id="mlch"></span></label>
<
textarea placeholder="3-300 znaků." name="chmessage" id="chmessage"></textarea> <input type="button" id="chsm" disabled="disabled" onclick="sChM();" value="Odeslat"/> </form> </div> </div> </div> </body> </html>

Chat.css

V tomto souboru je css stylování chatu. Je na vás, jaký vzhled si vytvoříte. Důležité jsou pouze následující hodnoty:


#chroom {
    display: none;
}
#chenter {
       display:block;
}
#chusers {
    overflow:auto;
}
#chmessages {
    overflow: auto;
}

Chat.js

Na tomto souboru to vše stojí. Jsou zde vytvořeny funkce pro vstup do chatu, odeslání zpráv, refreshování zpráv a uživatelů a validace formuláře. Každý řádek skriptu je pro jednoduchost, přehlednost a lepší pochopení popsán.


// JavaScript Document
$(document).ready(function(){//Načtení dokumentu
    rf();//Provedení refreshe zpráv a uživatelů v chatu
    chControll();//Provedení kontroly formulářů
    $('#chnick').keyup(chControll);//Začátek zaznamenávání akce kláves
    $('#chmessage').keyup(chControll);//Totéž
    });

function eCh() {//Funkce enter chat(eCh)
    var nl=$('#chnick').val().length;
    if(nl>=3) {
    $('#chenter').css('display','none');
    $('#chroom').css('display','block');
    $.post('php/login.php',{nick:cheform.chnick.value},
        function(output) {//Funkce která vypíše vrácená data
                if(output){//Pokud byla nějaká data vrácena
                    $('#chenter').css('display','block');//Změna displeje z none na block
                    $('#chroom').css('display','none');//Změna displeje z block na none
                    $('#errors').html(output);//#chenter tyto data vypíše
                    } else {
                        $('#chform').css('display','block');//Pokud ne, #chform se nastaví na display:block
                        }
            }
        );
    setInterval(rf,2500);//Nastavení intervalu refreshe
    } else {
        $('#errors').html('Váš nick je příliš krátký');//Pokud je Nick příliš krátký, vypiš varovaní
        }
    };

function sChM() {//Funkce send chat message(sChM)
    var ml=$('#chmessage').val().length;
    var nl=$('#chnick').val().length;
    if(ml>=3 && nl>=3) {
        $.post('php/send.php',{nick:cheform.chnick.value, message:chform.chmessage.value});
        $.post('php/logout.php',{nick:cheform.chnick.value},//Zjisti, jestli nebyl uživatel odhlášen z důvodu neaktivity
            function(output){
                if(output) {
                    $('#chenter').css('display','block');//Změna displeje z none na block
                    $('#chroom').css('display','none');//Změna displeje z block na none
                    $('#errors').html(output);//Vypiš output
                    };
                }
        );
        } else {
            $('#chbottom').append('<span class="chlwarning">Vaše zpráva je příliš krátká.</span>');
            }
    };
    
function rf() {//Funkce refreshování zpráv a uživatelů
    $('#chmessages').load('php/messages.php');//Načtení požadovaného souboru
    $('#chusers').load('php/users.php');//Totéž
    };
    
function chControll() {//Funkce pro kontrolu formuláře
    var lname=$('#chnick').val().length;//Počet znaků #chnick
    var lmessage=$('#chmessage').val().length;//Počet znaků v #chmessage
    var name=$('#chnick').val();//Získání hodnoty #chnick
    var message=$('#chmessage').val();//Získání hodnoty #chmessage
    var nsubstr=name.substring(0,20);//Zkrácení počtu znaků name substring(nsubstr) na maximálních 20
    var msubstr=message.substring(0,500);//Zkrácení počtu znaků message substring(msubstr) na maximálních 500
    var nchleft=20-lname;//Zjištění zbývajících počtu  znaků v #chnick
    var mchleft=500-lmessage;//Zjištění zbývajícího počtu znaků v #chmessage
    $('#nlch').html(nchleft);//Vložení zbývajícíh počtu znaků do nick left characters(nlch)
    $('#mlch').html(mchleft);//Vložení zbývajícího počtu znaků do message left characters(mlch)
    $('#chnick').val(nsubstr);//Vložení omezeného počtu znaků do #chnick
    $('#chmessage').val(msubstr);//Vložení omezeného počtu znaků do #chmessage
    if(lname>=3 && lmessage>=3) {//Pokud jsou splněny podmínky lname a lmessage
        $('#chsm').removeAttr('disabled');//Odeber atribut disable u tlačítka chat send message(chsm)
        } else {//Pokud pomínky nejsou splněny
            $('#chsm').attr('disabled','disabled');//Přidej atribut disabled k tlačítku #chsm
            };
    if(lname<3) {//Pokud je splěna podmínka
        $('#chnick').css('border','3px solid #F00');//Vytvoř červený rámeček okolo #chnick
        $('#ech').attr('disabled','disabled');//Přidej atribut disabled ke tlačítku enter chat(ech)
        } else {//Pokud pomínka není splněna
            $('#chnick').css('border','3px solid #00ff00');//Změn barvu rámečku na zelenou
            $('#ech').removeAttr('disabled');//Odeber atribut disabled
            };
    if(lmessage<3) {//Pokud je podmínka splněna
        $('#chmessage').css('border','3px solid #F00');//Vytvoř červený rámeček kolem zprávy
        } else {//Pokud podmínka není splněna
            $('#chmessage').css('border','3px solid #00ff00');//Změn barvu rámečku na zelenou
            };
    };

Validace formuláře:Validaci formuláře má na starost funkce chControll();. Jsou v ní zaznamenávány hodnoty z inputů #chnick a #chmessage. U nich se měří počet znaků a ten je vkládán na určitá místa. Konkrétně u zprávy to je #mlch a u přezdívky (nicku) #nlch. Obarvení rámečku je zelené pokud je v inputech více znaků než 3. Červeně pokud není. Překročení maximálního počtu znaků je chráněno pomocí nchleft a mchleft. Hodnoty těchto dvou proměnných jsou vkládány na místo základního textu.

Config.php

V tomto souboru bude pouze jméno databáze, přistupovací jméno a heslo, připojení k databázi a proměnná $rewrite.


<?php 
$dbname='';//Jméno databáze
$dblogin='';//Přihlašovací jméno
$dbpassword='';//Heslo
$db = new PDO('mysql:host=localhost;dbname=test;charset=UTF-8', $dblogin, $dbpassword);//Připojení k databázi
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Nastavení řízení chyb
$rewrite=array('<'=>'&lt;', '&'=>'&amp;')//Změna prvku v poli
?>

Login.php

Do tohoto souboru se odešle přezdívka, kterou si uživatel zvolil. Zkontroluje se, zdali již přezdívka neexistuje. Pokud ne, žádná data se nevrátí a uživateli se zobrazí formulář pro psaní zpráv a do tabulky se vloží jeho jméno. Pokud se ale jméno již v tabulce nachází, uživatel bude vrácen na vstupní formulář a bude požádán o zvolení jiné přezdívky.


<?php 
include('config.php');//Načti soubor
$nick=substr(strip_tags(StrTr($_POST['nick'],$rewrite)),0,20);
$time=Time();
try {
    $stmt = $db->prepare("SELECT * FROM `$dbname`.`chatusers` WHERE `name`=?");//Připrav dotaz
    $stmt ->bindParam(1, $nick, PDO::PARAM_STR);//Oddělení parametru od hlavního programu
    $stmt->execute();//Provedení dotazu
    if($stmt->rowCount()==NULL) {//Pokud se počet vrácených řádků rovná 0
    $stmt = $db->prepare("INSERT INTO `$dbname`.`chatusers` (`id` ,`name` ,`time`)VALUES (NULL, ?, ?)");//Vlož do tabulky nick
    $stmt ->bindParam(1, $nick, PDO::PARAM_STR);//Oddělení parametru od hlavního programu
    $stmt ->bindParam(2, $time, PDO::PARAM_INT);//Totéž
    $stmt->execute();//Provedení dotazu
                } else {//Pokud se řádek rovná 1
                    echo 'Toto jméno už existuje, musíte zvolit jiné.';//Vypiš varování
                    }
    } catch (PDOException $e){//Pokud nastala chyba
        exit("Nepodařilo se přihlásit.".$e->getMessage());//Vypiš chybu a hlášku
        }
?>

Logout.php

Skript v tomto souboru zajišťuje, aby uživatel, který byl již odhlášen pro neaktivitu, nemohl pokračovat v konverzaci a musel se znovu přihlásit.


<?php 
include('config.php');//Načti soubor config.php
$nick=substr(strip_tags(StrTr($_POST['nick'],$rewrite)),0,20);//Zkrať počet znaků, odstraň tagy a nahraď "<" za &lt;
try {
        $stmt = $db->prepare("SELECT * FROM `$dbname`.`chatusers` WHERE `name`=?");//Připravení dotazu 
        $stmt ->bindParam(1, $nick, PDO::PARAM_STR);//Rozdělení parametru od hl. programu
        $stmt->execute();//Provedení dotazu
        if($stmt->rowCount()==NULL) echo 'Byly jste odhlášeni pro neaktivitu.';//Pokud nebyl nalezen řádek se jménem vypiš zprávu
        } catch (PDOException $e) {//Pokud nastala chyba
            exit("Nepodařilo se vypsat zprávy.".$e->getMessage());//Vypiš chybu a hlášku
            }
?>

Users.php

Zde se získají všechny řádky v tabulce chusers a vypíší se. Jeden řádek = jeden uživatel.


<?php 
include('config.php');//Načti soubor
try {
    $stmt = $db->query("SELECT * FROM `$dbname`.`chatusers` ");//Dotaz do db
    while ($row = $stmt->fetch()) {//Vypsání dat
        echo '<div class="user">'.$row['name'].'</div>';
        }
    } catch (PDOException $e) {//Pokud nastala chyba
        exit("Nepodařilo se vypsat uživatele.".$e->getMessage());//Vypiš chybu a hlášku
        }
?>

Messages.php

Následujících pár řádků skriptu zajišťuje získání všech zpráv z databáze, seřazení a následný výpis.


<?php 
include('config.php');//Načti soubor config.php
try {
        $stmt = $db->query("SELECT * FROM `$dbname`.`chatmsg` ORDER BY `time`");//Dotaz do db
        while ($row = $stmt->fetch()) {//Vypsání dat podle potřeby
            echo '<div class="msg"><div class="msginf"><span class="msgnick">'.$row['name'].'</span>
<span class="msgdate">'
.$row['time'].'</span></div><div class="msgtext">'.$row['text'].'</div></div>';     }         } catch (Exception $e) {//Pokud nastala chyba             exit("Nepodařilo se vypsat zprávy.".$e->getMessage());//Vypiš chybu a hlášku             } ?>

Send.php

V tomto skriptu jsou 2 try-catch bloky. Uvnitř prvního se provádí odesílání dat do databáze a ve druhém updatovaní času poslední akce uživatele. Čas se updatuje proto aby skript, který bude spouštěn CRONem, mohl smazat offline uživatele, kteří byly po určitou dobu neaktivni. Nastavení této hodnoty je na vás. Je dobré to přizpůsobit minimálnímu časovému intervalu spouštění CRONu. Tak zajistíte, že bude seznam online uživatelů pořád a co nejvíce aktuální.


<?php 
include('config.php');//Načti soubor config.php
$time=Time();
$nick=substr(strip_tags(StrTr($_POST['nick'],$rewrite)),0,20);//Odstraň tagy, přepiš "<" na entitu &lt; a zkrať počet znaků
$message=substr(strip_tags(StrTr($_POST['message'],$rewrite)),0,500);//Totéž
$strftime=StrFTime("%H:%M:%S");//Získání času typu 14:22:36
try {
    $stmt = $db->prepare("INSERT INTO `$dbname`.`chatmsg` (`id` ,`name` ,`text` ,`time`)VALUES (NULL, ?, ?, ?)");//Připravení dotazu
    $stmt ->bindParam(1, $nick, PDO::PARAM_STR);//Rozdělení parametru od hlavního programu
    $stmt ->bindParam(2, $message, PDO::PARAM_STR);//Totéž
    $stmt ->bindParam(3, $strftime, PDO::PARAM_STR);//Totéž
    $stmt->execute();//Proveď dotaz
    } catch (PDOException $e){//Pokud se dotaz nezdařil
        exit("Zprávu se nepodařilo odeslat.".$e->getMessage());//Vypiš zprávu a chybovou hlášku
        }
try {//Zde se provede update času poslední akce tohoto uživatele
    $stmt = $db->prepare("UPDATE `$dbname`.`chatusers` SET `time` =? WHERE `chatusers`.`name` ==?");//Připravení dotazu
    $stmt ->bindParam(1, $strftime, PDO::PARAM_INT);//Rozdělení parametru od hlavního programu
    $stmt ->bindParam(2, $nick, PDO::PARAM_STR);//Totéž
    $stmt->execute();//Proveď dotaz
    } catch (PDOException $e){//Pokud se dotaz nezdařil
        exit("Nepodařilo se aktualizovat váš stav.".$e->getMessage());//Vypiš zprávu a chybovou hlášku
        }
?>

Mazání neaktivních uživatelů

Tento skript musí být spouštěn CRONem. Záleží na vás, jaký interval nastavíte. Pokud nastavíte například na 5 minut, tak musíte u $timeout nastavit na místo hodnoty 3600 jen 300.


<?php 
$dbname='test';//Jméno databáze
$dblogin='';//Přihlašovací jméno
$dbpassword='';//Heslo
$db = new PDO('mysql:host=localhost;dbname=test;charset=UTF-8', $dblogin, $dbpassword);//Připojení k databázi
$time=Time();
$timeout=$time-3600;
$stmt = $db->query("DELETE FROM `$dbname`.`chatusers` WHERE `chatusers`.`time <  $timeout;");//Dotaz do db
$stmt = $db->query("SELECT * FROM `$dbname`.`chatmsg`");
$nrows=$stmt->rowCount();
if($nrows>=80) {//Pokud je počet zpráv větší než 80
    $stmt = $db->query("DELETE FROM `$dbname`.`chatmsg` LIMIT 40");//Smaž polovinu zpráv
};
?>

Online verze článku: http://www.linuxsoft.cz/article.php?id_article=1969