Webcity.pl


  Sablotron - arkusze ...

  Pliki XML generowane...

  Upload obrazka do ba...

  PHP w praktyce

  Instalacja forum php...

 

 02.02.06 - [new] E-video - artykuł

 30.01.06 - [update] Artykuł o sesjach

 18.12.05 - [update] PDO - artykuł

 19.08.05 - [new+upd] Aktualizacja materia...

 26.04.05 - [update] Nowy artykuł



  Pomocy WML
[php]Sesje nie działają, ...
[actionscript 2.0] [flash...
Problem z talicami w php
[ocena] llll.pl darmowe a...
Kamera na stronie interne...
Kamera na stronie interne...
[xml] Jak wyciągnąć dane
Wizualizacja w JS - POMOC
Web Developer - stała / f...

... i wiele innych wątków na forum ›

Partnerzy
› allRSS.info - katalog zasobów RSS
› iloveflyer.org - webdesign
› webserv.pl - serverpack
› skryptoteka.pl - mnóstwo skryptów
› vel.pl - hosting
› TelePraca.net - pracuj swobodnie
› PHP Solutions - magazyn PHP
› HELION - wydawnictwo informatyczne

Ksišżka dnia

CityMag
Wpisz swój e-mail, aby zaprenumerować nasz Magazyn, który zawiera najnowsze informacje ze świata i najciekawsze teksty.
 

Szukasz czegoś?
Nasza wyszukiwarka znajdzie wszystko, czego szukasz.
 

Szukanie zawansowane


  Reklama
  Redakcja
  Hosting
  Kanał

© WebCity.pl Team
 
 
  Forum Forum
Kursy Kursy
Porady Porady
Recenzje Recenzje
Newsy Newsy
Katalog stron WWW Katalog
Skrypty PHP Skrypty
Download Oferty i praca
Artykuły:
 Teoria
 Praktyka
 Promocja
 Inne
 

 Webcity.pl |

Proste forum dyskusyjne

Autor: Zyx
   Fora dyskusyjne są bardzo ciekawymi dodatkami do stron WWW - są one html'owym odpowiednikiem grup dyskusyjnych. Jednak tu kryje się pewna pułapka, gdyż wyróżniamy dwa rodzaje for, klasyfikowane ze względu na sposób prezentowania tematów. Pierwsze z nich, które z usenetu czerpią znacznie więcej, posiadają drzewiastą organizację tematów (czyli temat może się dowolnie rozgałęziać, itp.). Drugie natomiast posiadają tematy zorganizowane w sposób listy, czyli post pod postem. Nie ma możliwości rozgałęziania tematu, wszystko toczy się w jednym ciągu. Takim forum jest np. to używane na naszej stronie - InvisionBoard. W artykule tym opiszę sposób tworzenia pierwszego rodzaju for.

   Dobrze, więc zamierzamy napisać forum o drzewiastej strukturze. Zdecydowałem, że dodatkowo będzie posiadało ono prosty system autoryzacji użytkowników (podczas dodawania wiadomości autor musi podać swój login i hasło i jeśli będą się one zgadzały, wiadomość zostanie dodana), by ktoś się pod nas nie podszywał. Dodatkowo zaimplementujemy proste formatowanie w stylu: pogrubienie, kursywa, podkreślenie, oraz dwa rodzaje linków: URL'e i adresy e-mail. Z pewnością umili to użytkownikom korzystanie z naszego systemu :). Całość kodu jest krótka, zajmuje niecałe 8 kb (ale pamiętaj: jest on własnością moją i Webcity, choć możesz w nim dokonywać dowolnych zmian (open-source)).

   A więc zacznijmy od początku. Utwórz sobie bazę danych o nazwie np. "moja_kurde_baza" :). Wgraj do niej następujące tabele:

CREATE TABLE messages (
   msg_id int(10) NOT NULL auto_increment,
   msg_title varchar(255) default NULL,
   msg_text blob,
   msg_date int(11) default NULL,
   msg_parent int(10) default NULL,
   msg_author int(10) default NULL,
   PRIMARY KEY (msg_id),
   KEY msg_parent (msg_parent)
);

CREATE TABLE users (
   user_id int(10) NOT NULL auto_increment,
   user_name varchar(128) NOT NULL default '',
   user_pass varchar(32) NOT NULL default '',
   PRIMARY KEY (user_id)
);


Całe forum będzie mieściło się w tych oto dwóch tabelach :). Przypatrz się szczególnie polu msg_parent w tabeli 'messages'. Będzie ono zawierało ID wiadomości, która jest wiadomością-matką. To dzięki temu będzie możliwe proste zaprogramowanie drzewiastej struktury - wszystkie wiadomości-tematy będą miały w tym polu 0. Teraz uruchamiamy funkcję rekurencyjną za ID nadrzędnej wiadomości wprowadzając 0, wyświetli nam wszystkie tematy, przy czym dla każdego z nich wywoływana będzie ta sama funkcja z ID tej wiadomości, przez co pobrane zostaną odpowiedzi na dany temat, dla każdej z odpowiedzi będzie wywoływana ta funkcja z ID tej odpowiedzi itd. I tak wywołując jedną funkcję, otrzymamy całe drzewo. Dodatkowo podając jako parametr ID jakiejś wiadomości, wyświetlimy część tego drzewa, czyli wszystkie wiadomości potomne.

   Dobrze, otwórz teraz sobie notatnik, czy czego tam używasz. Napiszemy teraz plik funkcje.php, który zawierać będzie wszystkie podstawowe funkcje. Pierwszą z nich będzie ta odpowiedzialna za dopisanie usera do bazy (tak jakoś mi się zaczęło, więc lećmy za kolejnością):

   function DodajUsera($login, $haslo){
      $r = mysql_query("SELECT user_id FROM users WHERE user_name='$login'");
      if(!$rows = mysql_fetch_row($r)){
         mysql_query("INSERT INTO users (user_name, user_pass) VALUES('$login', '".md5($haslo)."')");
         if($error = mysql_error()){
            die("MySQL error: $error<br>");
         }
      }else{
         echo "Na forum jest już użytkownik o takim loginie. Wybierz inny.<br>";
      }
   }


Zauważ, że posiada ona ciekawą blokadę przed zarejestrowaniem dwa razy usera o takim samym loginie - najpierw próbuje pobrać ID użytkownika, który wprowadził swój login. Jeżeli wynik nie zostanie zwrócony, znaczy to, że użytkownika takiego nie ma i można go spokojnie dodać. W przeciwnym wypadku o zaistniałym fakcie zostaniemy powiadomieni. Mamy również pewność, że nikt nie odczyta cudzych haseł - zostały one zakodowane algorytmem MD5.

Drugą funkcją będzie ta, która sprawdzi, czy login i hasło są poprawnie wpisane (przy weryfikacji autora postu):

   function Autoryzuj($login, $haslo){
      $r = mysql_query("SELECT user_id FROM users WHERE user_name='$login' AND user_pass = MD5('$haslo')");
      if($rows = mysql_fetch_row($r)){
         if($rows[0] != 0){
            return $rows[0];
         }
      }
      return 0;
   }


I znów mamy do czynienia z tą samą metodą - jest wynik, ID jest różny od zera, czyli dane poprawnie wprowadzone i wiadomość może zostać dodana do bazy. Jako że musimy podać autora wiadomości (tzn. jego ID), funkcja tenże ID zwraca.

Kolejna funkcja będzie miała za zadanie dopisać wiadomość do bazy danych:

   function DodajWiadomosc($tytul, &$tresc, $rodzic, $autor){
      mysql_query("INSERT INTO messages(
                     msg_title,
                     msg_text,
                     msg_date,
                     msg_parent,
                     msg_author
                  ) VALUES(
                     '$tytul',
                     '$tresc',
                     '".time()."',
                     '$rodzic',
                     '$autor'
                  )
      ");
      if($error = mysql_error()){
         die("MySQL error: $error<br>");
      }
   }


W tej funkcji zastosowałem pewien mechanizm przekazywania tekstów do jej wnętrza, który oszczędza pamięć na serwerze. Mam na myśli referencję: "$tytul, &$tresc, $rodzic, $autor". Znaczek & powiadamia PHP, że do funkcji ma być załadowana nie zawartość zmiennej, lecz jakby odnośnik do niej. Dzięki temu nie jest w pamięci tworzona osobna kopia tekstu (szczególnie, gdy jest on długi), lecz wszystkie operacje odbywają się na jednym, jedynym egzemplarzu. Oszczędza to również procesor, który nie traci czasu na zbędne kopiowanie. O referencjach możesz poczytać w manualu PHP, którego wersja on-line znajduje sie na stronie www.php.net (bardzo często wpadam tam). Będziemy je jeszcze wykorzystywać.

Skrypt powinien też mieć możliwość pobrania treści wiadomości i najważniejszych danych, aby zaprezentować to czytelnikowi. Za pobranie odpowiada odpowiednia funkcja oczywiście:

   function PobierzWiadomosc($id, &$tytul, &$tresc, &$autor, &$data){
      $r = mysql_query("SELECT m.msg_title, m.msg_text, m.msg_date, u.user_name FROM messages m, users u WHERE u.user_id = m.msg_author AND m.msg_id='$id'");
      if($rows = mysql_fetch_row($r)){
         $tytul = $rows[0];
         $tresc = $rows[1];
         $data = $rows[2];
         $autor = $rows[3];
         return 1;
      }
      return 0;
   }


Zauważ, jak pomysłowo pobrałem dane o wiadomości - wykorzystałem referencje, więc jakiekolwiek zmodyfikowanie wartości parametrów spowoduje też zmianę zawartości zewnętrznych zmiennych. I tak oto w prosty sposób ominąłem niemożność normalnego zwrócenia kilku wartości (co prawda można zapisać wszystko do tablicy i potem zwrócić, ale nie jest to eleganckie rozwiązanie).

Teraz bardzo ciekawa funkcja - będzie ona potrafiła przetwarzać specjalne znaczniki na kod HTML za pomocą wyrażeń regularnych Perla, oraz zamieniać entery na znaczniki zejścia do następnego wiersza (BR), a także kasować niebezpieczne tagi HTML'a :). Wbrew pozorom jest to proste zadanko:

   function Przetworz(&$tekst){
      $tekst = preg_replace('#\[b\](.*?)\[/b\]#si', '<b>\\1</b>', $tekst);
      $tekst = preg_replace('#\[i\](.*?)\[/i\]#si', '<i>\\1</i>', $tekst);
      $tekst = preg_replace('#\[u\](.*?)\[/u\]#si', '<u>\\1</u>', $tekst);

      $tekst = preg_replace('#\[http\](.*?)\[/http\]#si', '<a href="http://\\1">\\1</a>', $tekst);
      $tekst = preg_replace('#\[mail\](.*?)\[/mail\]#si', '<a href="mailto:\\1">\\1</a>', $tekst);

      $tekst = nl2br(strip_tags($tekst, '<a><b><i><u><font><div><hr><span><code> <center><h1><h2><h3><h4><h5><h6><h7><form><textarea>'));
   }


Tu także użyłem mechanizmu referencji, by dokonywać zmian bezpośrednio w treści wiadomości.

Kolejne dwie funkcje będą pobierały dane wyłącznie informacyjne: ilość wiadomości i użytkowników, oraz tytuł wiadomości o podanym ID.

   function Statystyka(&$wiadomosci, &$userzy){
      $r = mysql_query("SELECT COUNT(msg_id) AS total FROM messages");
      if($rows = mysql_fetch_row($r)){
         $wiadomosci = $rows[0];
      }
      $r = mysql_query("SELECT COUNT(user_id) AS total FROM users");
      if($rows = mysql_fetch_row($r)){
         $userzy = $rows[0];
      }
      return 0;
   }

   function PobierzTytul($id){
      $r = mysql_query("SELECT msg_title FROM messages WHERE msg_id='$id'");
      if($rows = mysql_fetch_row($r)){
         return $rows[0];
      }
      return 0;
   }


Zwróć uwagę na zapytanie w funkcji "Statystyka" - użyłem tam funkcji COUNT do policzenia rekordów w wiadomościach i postach. Jeśli chcesz wiedzieć coś więcej o języku SQL, przeczytaj poświęcony temu artykuł pt. "MySQL w praktyce" mojego autorstwa.

Nadszedł czas na najważniejszą funkcję całego skryptu: "PokazListe". To ona będzie odpowiadała za wyświetlenie wszystkich wiadomości-potomków na ekranie przeglądarki. Zastosowałem w niej mechanizm rekurencji, czyli wywoływania funkcji X z wnętrza tejże funkcji. Niektórych rzeczy inaczej niż w ten sposób rozwiązać się nie da:

   function PokazListe($id){
      tstart_lista();
      $r = mysql_query("SELECT m.msg_id, m.msg_title, m.msg_date, u.user_name FROM messages m, users u WHERE (u.user_id = m.msg_author) AND (m.msg_parent='$id') ORDER BY m.msg_date DESC");
      if($error = mysql_error()){
         die("MySQL error: $error<br>");
      }
      while($rows = mysql_fetch_row($r)){
         telement($rows[0], $rows[1], date("d.m.Y H:i", $rows[2]), $rows[3]);
         PokazListe($rows[0]);
      }

      tend_lista();
   }


Z pewnością zauważyłeś już istnienie tu trzech tajemniczych funkcji: "tstart_lista", "telement" i "tend_lista". Są to funkcje wyświetlające poszczególne części forum - znajdą się one w pliku szablon.php, który zaraz napiszemy. W przedstawionym tu kodzie pogrubiłem nazwę funkcji, oraz miejsce jej wywołania w niej samej, byś lepiej zrozumiał zasadę rekurencji.

   Dobrze, postaw znacznik końca skryptu i zapisz funkcje.php. Utwórz kolejny plik, wpisz "<?php" :) i tak oto zacząłeś pisanie pliku szablon.php, w którym znajdą się szablony poszczególnych elementów forum. Kodu raczej komentować nie muszę, więc całą bibliotekę wrzucę w całości:

   function theader(){
      echo '
<html>
   <head>
      <title>Filar Forum</title>
      <meta http-equiv="content-type" content="text/html; charset=iso-8859-2">
      <meta http-equiv="content-Language" content="pl">
      <meta http-equiv="reply-to" content="zyxwvu@me2.pl">
   </head>
   <body>
      <h1>Filar Forum</h1>
      ';
   }

   function tfooter(){
      echo '
   </body>
</html>
      ';
   }

   function twiadomosc($tytul, &$tresc, $autor, $data){
      echo '
      <table width=100% border=1>
         <tr>
            <td><h1>'.$tytul.'</h1> by ['.$autor.'] on '.$data.'</td>
         </tr>
         <tr>
            <td>'.$tresc.'</td>
         </tr>
      </table>
      Odpowiedzi:<br>
      ';
   }

   function tpanel($id, $wiadomosci, $userow){
      echo '
      <b><center>.::<a href="forum.php">Index</a>::. .::<a href="forum.php?m=form&id='.$id.'">Dodaj wiadomo&para;ć</a>::. .::<a href="forum.php?m=register">Zarejestruj się</a>::. .::Ł&plusmn;cznie Wiadomo&para;ci ['.$wiadomosci.']; Ł&plusmn;cznie Użytkowników ['.$userow.']::.</center></b><br>
      ';
   }

   function tstart_lista(){
      echo '<ul>';
   }

   function tend_lista(){
      echo '</ul>';
   }

   function telement($id, $tytul, $data, $autor){
      echo '
         <li><a href="forum.php?id='.$id.'&m=msg">'.$tytul.' on '.$data.' by [<b>'.$autor.'</b>]</a>
      ';
   }

   function tformularz($tytul_tematu, $id){
      if($id == 0){
         $tytul_tematu = "";
         $tresc = "Nowy temat";
      }else{
         $tytul_tematu = 'Re: '.$tytul_tematu;
         $tresc = "Odpowiedz na $tytul_tematu";
      }
      echo '
      <table border=1 width=100%>
         <tr>
            <td colspan=2 align=center><b>'.$tresc.'</b></td>
         </tr>
         <form method="post" action="forum.php?id='.$id.'&m=add">
         <tr>
            <td width=40%>Tytuł wiadomo&para;ci:</td>
            <td width=60%><input type="text" name="tytul" value="'.$tytul_tematu.'"></td>
         </tr>
         <tr>
            <td width=40%>Autor:</td>
            <td width=60%><input type="text" name="autor"></td>
         </tr>
         <tr>
            <td width=40%>Hasło:</td>
            <td width=60%><input type="password" name="haslo"></td>
         </tr>
         <tr>
            <td width=40%>Tre&para;ć:<br> tekst - pogrubienie<br>tekst - kursywa<br>tekst - podkre&para;lenie<br>[http]url[/http] - link.<br>[mail]adres[/mail] - adres e-mail.<br>HTML dozwolony</td>
            <td width=60%><textarea name="tresc" rows=9 cols=60></textarea></td>
         </tr>
         <tr>
            <td colspan=2><input type="submit" value="Dodaj"></td>
         </tr>
         </form>
      </table>
      ';
   }

   function trejestracja(){
      echo '
      <table border=1 width=100%>
         <tr>
            <td colspan=2>Rejestracja na forum</td>
         </tr>
         <form method="post" action="forum.php?&m=add_user">
         <tr>
            <td width=40%>Login:</td>
            <td width=60%><input type="text" name="login"></td>
         </tr>
         <tr>
            <td width=40%>Hasło:</td>
            <td width=60%><input type="password" name="haslo"></td>
         </tr>
         <tr>
            <td colspan=2><input type="submit" value="Dodaj"></td>
         </tr>
         </form>
      </table>
      ';
   }


Dobrze, wstaw znacznik końca skryptu i bierzemy się za pisanie serca naszego forum :)... Utwórz kolejny plik (o nazwie forum.php), wstaw znacznik rozpoczęcia skryptu i bierz się do dzieła...

   Na początku załadujemy szablon i nasze funkcje, oraz ustawimy konfigurację połączenia z bazą:

   $host = "localhost";
   $user = "root";
   $pass = "";
   $db = "moja_kurde_baza";

   require("szablon.php");
   require("funkcje.php");

Potem wyświetlimy nagłówek HTML, połączymy się z bazą i pobierzemy statystykę:
   theader();

   mysql_connect($host, $user, $pass) or die("Nie mogę nawiązać połączenia z bazą!");
   mysql_select_db($db) or die("Nie mogę wybrać tej bazy danych!");

   Statystyka($wiadomosci, $userzy);

Kolejnym krokiem będzie sprawdzenie wartości podstawowych zmiennych ($id - ID wiadomości, która nas interesuje i $m - czynność do wykonania), oraz wyświetlenie statystyki:

   if(!isset($id)){
      $id = 0;
   }

   if($m == "add_user"){
      $userzy++;
   }

   tpanel($id, $wiadomosci, $userzy);


Jako że użytkownik dodawany jest później, gdy dodajemy go, od razu zwiększmy licznik, by nie było przekłamań. Jest tylko jedna rzecz: licznik pokaże się zwiększony nawet wtedy, gdy użytkownik nie zostanie dodany z powodu błędnego loginu. Zostawiłem to dla ciebie: spróbuj przesunąć dodawanie usera przed pobranie statystyki tak, by w liczniku nie było niedomówień.

Dobrze, teraz obsłużymy wyświetlanie formularzy (rejestracja i nowa wiadomość). W ich wypadku lista tematów nie powinna się ukazać...

   if($m == "form"){
      tformularz(PobierzTytul($id), $id);
   }elseif($m == "register"){
      trejestracja();


Więc zawrzemy ją wraz z akcjami, przy których można ją wyświetlić, w ostatniej klazuli ELSE:

   }else{
      switch($m){
         case "msg": PobierzWiadomosc($id, $tytul, $tresc, $autor, $data);
                     Przetworz($tresc);
                     twiadomosc($tytul, $tresc, $autor, date("d.m.y H:i", $data));
                     break;
         case "add": $uid = Autoryzuj($HTTP_POST_VARS['autor'], $HTTP_POST_VARS['haslo']);
                     if($uid > 0){
                        DodajWiadomosc($HTTP_POST_VARS['tytul'], $HTTP_POST_VARS['tresc'], $id, $uid);
                     }else{
                        echo "Musisz się zarejestrować, aby móc dodawać wiadomo&para;ci<br>";
                     }
                     $id = 0;
                     break;
         case "add_user":
                     DodajUsera($HTTP_POST_VARS['login'], $HTTP_POST_VARS['haslo']);
                     break;
      }
      PokazListe($id);
   }


Przypatrz się jednej rzeczy uważnie: zmienna $id przychodzi jako parametr - ID interesującego nas wątku, jeśli jest nieustawiona, skrypt na początku ustawiał 0. A tutaj zmienną tą dajemy jako parametr do funkcji "PokazListe". Dzięki temu jeśli np. oglądamy odpowiedź do tematu "Jaki dziś jesteś, Mickiewiczu", wyświetli się TYLKO lista odpowiedzi na czytaną przez nas wiadomość.

No i przed nami ostatnia partia kodu:

   mysql_close();
   tfooter();
?>


Jej chyba komentować nie muszę.

Gratulacje, możesz teraz uruchomić plik forum.php i zobaczyć, czy przypadkiem czegoś nie pochrzaniłeś :). To już właściwie koniec mojego artykułu.

- Jak daleko stąd do phpBB?
- Oj, bardzo daleko. Nad takimi systemami można przesiedzieć nawet cały rok (ja nad moim Forusem siedzę już 10 miesiąc, a to, które tu zaprezentowałem, napisałem w niecałą godzinę), trzeba mieć tzw. "wizję", przemyśleć każdy krok, by nie okazał się on pułapką, która pogrąży nasz projekt. No ale cóż... jeśli będziesz ćwiczył, na pewno kiedyś stworzysz swojego własnego pogromcę phpBB :D. Wierzę w Ciebie...

- Co można jeszcze wykonać w tym kodzie?
- Spróbuj - tak, jak pisałem - przenieść obsługę akcji "add_user" przed pobranie statystyki forum. Drugą rzeczą, jaką możesz zrobić, jest specjalny link przenoszący Cię do wiadomości nadrzędnej (wystarczy dopisać trochę do funkcji "twiadomosc" i dopisac do skryptu, by pobierał jeszcze informację o nadrzędnej wiadomości). A reszta? Pomyśl... popracuj, to przecież też twoje forum :).

Powrót

 
 
Twój domowy serwer!