PHP/Studium przypadku/System newsów: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Zyx (dyskusja | edycje)
m +wymaga dopracowania
Zyx (dyskusja | edycje)
rozpoczęcie przepisywania
Linia 1:
{{prognav|PHP|phpMyAdmin|Bazy danych - co dalej?}}
 
== System newsów ==
{{poprawić|Moduł zupełnie nie trzyma się przyjętej konwencji podręcznika oraz materiałów w nim zawartych. Zostanie on niedługo przepisany od nowa przez użytkownika [[Wikipedysta:Zyx|Zyx]]}}
 
Spróbujmy teraz zaimplementować zdobytą przez nas wiedzę w praktyce. Napiszemy prosty system newsów z podziałem na kategorie i możliwością dodawania komentarzy. Podręcznik ten pokaże, jak zacząć, natomiast twoim zadaniem będzie dopisanie (z pomocą wskazówek) wszystkich dodatków niezbędnych do tego, aby system był w pełni funkcjonalny. Nowością w porównaniu do księgi gości będzie to, iż nie będziemy już umieszczać kodu HTML bezpośrednio we właściwym pliku, lecz umieścimy go jako zbiór funkcji w osobnym. Dzięki temu poprawi się czytelność kodu, a ty poznasz zalety separacji tych dwóch elementów jeszcze przed przejściem do omawiania systemów szablonów.
Mamy do wykonania zadanie: napisać system newsów w PHP. Zastanówmy się, jakie funkcje chcemy w nim zaimplementować:
* na stronie głównej 30 najnowszych newsów, reszta dostępna w archiwum,
* edycja i usuwanie,
* oznaczanie jako ''przyklejony'',
* termin ważności newsa,
* generowanie kanału RSS.
 
=== Projekt bazy danych ===
Wydaje się to skomplikowane, ale małymi krokami dojdziemy do finału.
System newsów oparty będzie o bazę danych MySQL, w której ulokujemy trzy tabelki. Będą one połączone ze sobą relacją jeden do wielu:
# Istnieje grupa kategorii
# Każda kategoria może zawierać dowolną liczbę newsów, ale pojedynczy news może należeć tylko do jednej z nich.
# Każdy news może zawierać dowolną ilość komentarzy.
 
Układ tabelek zostanie tak zoptymalizowany, aby jak najrzadziej zmuszać bazę do wykonywania funkcji ''COUNT()'' w celu zliczenia ilości wpisów.
== Projektujemy tabelę ==
 
Zacznijmy od tabeli kategorii:
Zastanówmy się, jak może wyglądać tabela z newsami:
 
<nowiki>CREATE TABLE newsy`categories` (
news_id`id` int UNSIGNED(11) NOT NULL auto_increment,
news_title`title` varchar(8040) NOT NULL,
news_author`description` varchar(40255) NOT NULL,
news_created`news_num` int(11) NOT NULL,
PRIMARY KEY (`id`)
news_edited int NOT NULL,
) ENGINE=MyISAM DEFAULT CHARSET=latin2;</nowiki>
news_expires int NOT NULL,
news_sticky tinyint(1) NOT NULL,
news_summary text NOT NULL,
news_text text,
PRIMARY KEY (news_id)
);
 
Oprócz typowych pól informacyjnych: '''title''' oraz '''description''' (tytuł i opis), mamy też pole '''news_num''' przechowujące aktualną liczbę newsów wewnątrz kategorii. Musimy pamiętać, aby nasz system poprawnie zmniejszał i zwiększał jego zawartość w trakcie edycji i wprowadzania nowych elementów do bazy.
Mamy więc tytuł, autora, daty: utworzenia, modyfikacji i ważności, flagę ''przyklejony'', wstęp i opcjonalną dalszą część.
 
Następne w kolejności są newsy:
Dodajmy teraz np. za pomocą [[Programowanie:PHP:phpMyAdmin|phpMyAdmin]] kilka rekordów.
 
<nowiki>CREATE TABLE `news` (
== Pierwszy pokaz ==
`id` int(11) NOT NULL auto_increment,
`title` varchar(128) NOT NULL,
`body` text NOT NULL,
`date` int(11) NOT NULL,
`author` varchar(30) NOT NULL,
`category_id` int(11) NOT NULL,
`comment_num` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `category_id` (`category_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin2;</nowiki>
 
Tabela zaczyna się od pól informacyjnych. Z punktu widzenia projektanta bazy najistotniejsze są jednak dwa ostatnie. '''category_id''' przechowuje ID kategorii, do której należy news; w tym miejscu tworzymy naszą relację. '''comment_num''' działa na podobnej zasadzie, jak w kategoriach. Dzięki temu nie trzeba będzie wykonywać skomplikowanych zapytań przy pobieraniu listy newsów, aby pokazać tam jednocześnie ilość komentarzy.
Tworzymy skrypt ''news.php''. Pobieramy pierwsze 30 newsów. Sortujemy je od najnowszego do najstarszego, z uwzględnieniem flagi ''przyklejony''. Pokazujemy tylko fragmenty treści.
 
Na samym końcu zapoznamy się z tabelą komentarzy:
<pre><nowiki>$result = mysql_query('SELECT news_id, news_title, news_author, news_created, '.
'news_edited, news_expires, news_sticky, news_summary '.
'FROM newsy ORDER BY news_sticky DESC, news_created DESC');
 
<nowiki>CREATE TABLE `comments` (
// pobieramy news po newsie
`id` int(11) NOT NULL auto_increment,
while ($row = db_fetch_array($result)) {
`news_id` int(11) NOT NULL,
echo '<h1>'.$row['news_title'].'</h1>'. // tytuł
`author` varchar(30) NOT NULL,
'<p>Utworzony: '.date('Y-m-d H:i', $row['news_created']);
`date` int(11) NOT NULL,
`body` text NOT NULL,
PRIMARY KEY (`id`),
KEY `news_id` (`news_id`,`date`)
) ENGINE=MyISAM DEFAULT CHARSET=latin2;</nowiki>
 
Zwróć uwagę, w jaki sposób założony jest podwójny indeks na pola '''news_id''' oraz '''date'''. Gdyby utworzyć tutaj dwa osobne indeksy, w zasadzie nic byśmy nie uzyskali. Popatrzmy na to tak: nigdy nie wyświetlamy wszystkich komentarzy, jakie mamy w bazie. Zawsze są one powiązane z jakimś konkretnym newsem, dlatego tak istotne jest odzwierciedlenie tego w strukturze indeksów. Przy osobnych indeksach pole '''date''' zostanie posortowane globalnie, a wybierając komentarze tylko dla pojedynczego newsa, system DB i tak będzie musiał przeskanować całą tabelę, aby je względem tejże daty wybrać.
// czy czas edycji różni się od utworzenia?
if ($row['news_created'] > $row['news_edited']) {
echo ', zmodyfikowany '.date('Y-m-d H:i');
}
 
Od strony bazy danych to tyle. Przejdźmy do kodowania.
// czy news nie jest przeterminowany?
if ($row['news_expires'] > time()) {
echo '<br />News stracił ważność '.date('Y-m-d H:i', $row['news_expires']);
} elseif ($row['news_expires'] > $row['news_created']) {
echo '<br />News traci ważność '.date('Y-m-d H:i', $row['news_expires']);
}
 
=== Funkcje podstawowe ===
// wstęp do newsa, odnośnik do pełnej treści i informacja o autorze
Napiszemy teraz plik ''functions.php''. Umieścimy w nim różne podstawowe funkcje. W tej chwili jest ich stosunkowo niewiele: łączenie się z bazą, kontrola długości wprowadzonego tekstu, prymitywne zabezpieczenie przed floodem. Plik ten przyda Ci się jednak podczas samodzielnej rozbudowy; prawdopodobnie rozrośnie się wtedy znacznie. Jego zawartość wygląda następująco:
echo '</p>'.
$row['news_summary'].
'(<a href="news.php?id='.$row['news_id'].'">więcej</a>)'.
'<br />Autor: '.$row['news_author'];
}</nowiki></pre>
 
<nowiki><?php
(...)
 
function initSystem()
{
global $sql;
$sql = new pdo('host=localhost;port=3305;dbname=artykuly', 'root', 'root');
$sql -> exec('SET NAMES `latin2`');
ob_start();
} // end initSystem();
function doneSystem()
{
ob_end_flush();
} // end doneSystem();
function commentsAllowed()
{
if(isset($_COOKIE['a84skljf']))
{
return false;
}
setcookie('a84skljf', 1, time() + 3600);
return true;
} // end commentsAllowed();
function validTextField($text, $min, $max)
{
if(strlen($text) > $min && strlen($text) < $max)
{
return true;
}
return false;
} // end validTextField();
 
?></nowiki>
 
Opis poszczególnych funkcji:
# ''initSystem()'' - inicjacja systemu; nawiązuje połączenie z bazą danych i włącza buforowanie wyjścia.
# ''doneSystem()'' - aktualnie tylko kończy buforowanie wyjścia. Być może znajdziesz dla niej jakieś ciekawe dodatkowe zastosowania.
# ''commentsAllowed()'' - funkcja zwraca '''true''', jeżeli internauta ma prawo dodawać komentarze i '''false''', jeżeli już takowy niedawno dodał.
# ''validTextField()'' - prosta funkcja do kontroli danych. Zwraca '''true''', jeżeli długość tekstu mieści się w podanym zakresie.
 
=== DAO ===
 
 
=== Kod HTML ===
 
=== Składamy system w całość ===
 
=== Ćwiczenia ===
 
=== Zakończenie ===