Prosty Blog - Początek
14 July 2008
Comments
Do tej pory nauczyliśmy się:- Ciąć szablony HTML na widoki
- Tworzyć kontrolery, widoki i modele
- Operować na bazie danych
- Tworzyć formularze, walidować dane
Cel
- Stworzyć prosty Blog zawierający:- Moduł Newsów
- Stronicowanie newsów na stronie głównej- Moduł Komentarzy
- Słowa kluczowe
- Wyszukiwarka
- Dodawanie komentarzy przez użytkowników do danego newsa
WYKONANIE
UWAGA użytkownicy PHP4
W przykładach może pojawić się wywołanie więcej niż 1 metody na raz:
W przykładach może pojawić się wywołanie więcej niż 1 metody na raz:
$obiekt->metoda1()->metoda2();W PHP5 to zadziała lecz nie w PHP4. Należy rozbijać takie wywołania
$x = $obiekt->metoda1(); $x->metoda2();
Cięcie szablonu
Jako szablon wybrałem jesienny "Autumn 05" (link w warsztacie cięcia szablonów). Jest on podobny do Metrohackera. Zmiany wymagało:: - ścieżka do pliku CSS (umieszczony w /views)
- usunięcie kodu z index.html:
<?xml version="1.0" encoding="UTF-8"?>

<?php
class Blog extends Controller
{
function index()
{
$this->load->view('index');
}
}
- Po rozdzieleniu i ładowaniu obu pomocniczych widoków szablon wyglądał tak:


Generyczny główny szablon
Teraz rozszerzymy nieco "cięcie" szablonów do widoków. Otóż widok index.php to szkielet strony, niezmienny dla wszystkich modułów. Zmieniać się będzie zawartość głównej kolumny i ew. lewych bloków. Obecnie index.php ładuje widok "main" i dla każdego kontrolera np. wyświetlającego newsy czy komentarze trzeba byłoby tworzyć kopie i "main" (co jest oczywiste) jak i widoku "index" by ładował odpowiednią wersję widoku "main".Po prostu
< ?PHP $this->load->view('main'); ?>
Zastąpię
< ?PHP echo $content ?>
I pod zmienną $content zawsze będę podstawiał wynik odpowiedniego widoku "main" :) Próba wyświetlenia szablonu po podmianie wyrzuci "Undefined variable: content" - co zrozumiałe - zmienna $content nie istnieje, kontroler jej (jeszcze) nie przekazuje. Nie należy panikować.Odwiedzamy phpMyAdmin
By stworzyć tabele można skorzystać z phpMyAdmina albo jak go nie mamy z metody $this->db>query() wykonującej nasze zapytanie SQL. Jeżeli chcesz skorzystać z tej drugiej opcji to musisz najpierw skonfigurować dane bazy danych w CI o czym napiszę za chwilę. Jeżeli korzystasz z bazy SQLite czy PostgreSQL zapytania będą podobne lecz nie identyczne. Najpierw tabele. Dla modułu newsów skorzystam z rozbudowanej wersji tabeli z poprzedniego warsztatu. Tabela tworzona poleceniem SQL:CREATE TABLE kurs_news (
news_id smallint(5) unsigned NOT NULL auto_increment,
news_title varchar(255) default NULL,
news_text text,
news_author varchar(255) default NULL,
news_date int unsigned NOT NULL,
news_keywords varchar(255) default NULL,
PRIMARY KEY (news_id)
) ENGINE=MyISAM;
Tabela komentarzy wygląda podobnie:
CREATE TABLE kurs_comments (
com_id smallint(5) unsigned NOT NULL auto_increment,
news_id smallint(5) unsigned NOT NULL,
com_title varchar(255) default NULL,
com_text text,
com_author varchar(255) default NULL,
com_date int unsigned NOT NULL,
PRIMARY KEY (com_id)
) ENGINE=MyISAM;
Modele
Model newsów w porównaniu z ostatnim warsztatem poszerzy się o jedną metodę:<?php
<?PHP
class News extends Model
{
function News()
{
parent::Model();
}
function get_news()
{
// Wszystkie newsy sortowane malejąco po news_id
$this->db->orderby("news_id", "desc");
return $this->db->get('news');
}
function get_news_by_id($id)
{
// Pobiera określony news
$this->db->where('news_id', $id);
return $this->db->get('news');
}
function get_news_by_keyword($keyword)
{
// Wszystkie newsy z danym słowem kluczowym sortowane malejąco po news_id
$this->db->like($keyword);
$this->db->orderby("news_id", "desc");
return $this->db->get('news');
}
function add_news($data)
{
// dodanie newsa
return $this->db->insert('news', $data);
}
function update_news($id, $data)
{
// zmiana newsa o podanym numerze news_id
$this->db->where('news_id', $id);
return $this->db->update('news', $data);
}
function delete_news($id)
{
// skasowanie newsa o podanym news_od
$this->db->where('news_id', $id);
return $this->db->delete('news');
}
}
?>
Model komentarzy wygląda już nieco inaczej (comments.php):
<?php
class Comments extends Model
{
function Comments()
{
parent::Model();
}
function get_comments_for_news($news_id)
{
// Komentarze dla danego newsa
$this->db->where('news_id', $news_id);
$this->db->orderby("com_id", "asc");
return $this->db->get('comments');
}
function add_comment($data)
{
// dodanie komentarza
return $this->db->insert('comments', $data);
}
function delete_comment($id)
{
// skasowanie komentarza o podanym ID
$this->db->where('com_id', $id);
return $this->db->delete('comments');
}
function are_comments_for_news($news_id)
{
// ile jest komentarzy dla danego newsa
// tego Active Records nie potrafi
return $this->db->query("SELECT COUNT(*) AS comnumber FROM ".$this->db->dbprefix."comments WHERE news_id = '".$news_id."'");
}
}
Konfiguracja
Teraz warto odwiedzić katalog konfiguracyjny naszego projektu. Z autoload.php:$autoload['libraries'] = array('database', 'validation');
$autoload['helper'] = array('url', 'form');$db['default']['dbprefix'] = "kurs_";
routes.php nazwa domyślnego kontrolera. U mnie blog:
$route['default_controller'] = "blog";
Kontrolery
Zabieramy się za nasz kontroler Blog. Listowanie wszystkich newsów na stronie głównej zrobić łatwo:<?php
class Blog extends Controller
{
// konstruktor
function Blog()
{
parent::Controller();
$this->response = array();
}
function index()
{
$this->load->model('News');
$query = $this->News->get_news();
$content = '';
// czy są jakieś wiersze?
if ($query->num_rows() > 0)
{
foreach($query->result() as $item)
{
$content .= $this->load->view('news_loop', $item, True);
}
}
else
{
// brak newsów
$content = '<h1>Brak newsów</h1>';
}
// przekazanie danych do szablonu
$this->response['content'] = $content;
$this->load->view('index', $this->response);
}
}
<h1><?PHP echo $news_title ?></h1>
<p class="date"><?PHP echo date("Y-m-d", $news_date); ?></p>
<p><?PHP echo $news_text; ?></p>
<div class="rule"></div>

<?php
class Admin extends Controller
{
// konstruktor
function Admin()
{
parent::Controller();
$this->response = array();
}
function index()
{
$this->response['content'] = '<h1><a href="'.site_url('admin/news_add').'">Dodaj Newsa</a></h1>';
// wylistujmy sobie newsy (tytuły) z linkami do edycji i kasowania
$this->load->model('News');
$query = $this->News->get_news();
if ($query->num_rows() > 0)
{
$this->response['content'] .= '<table width="100%" border="1" cellspacing="3" cellpadding="3">';
foreach($query->result() as $item)
{
$this->response['content'] .= $this->load->view('news_loop_admin', $item, True);
}
$this->response['content'] .= '</table>';
}
$this->load->view('index', $this->response);
}
// dodanie newsa
function news_add()
{
$data["tytul"] = array('name' => 'tytul');
$data['tresc'] = array('name' => 'tresc', 'rows' => 8, 'cols' => 50);
$data['autor'] = array('name' => 'autor');
$data['keywords'] = array('name' => 'keywords');
$rules['tytul'] = "required|max_length[250]|xss_clean";
$rules['tresc'] = "required|xss_clean";
$rules['keywords'] = "required|max_length[250]|xss_clean";
$this->validation->set_rules($rules);
if ($this->validation->run() == FALSE)
{
$data['tytul']['value'] = $this->input->post('tytul');
$data['tresc']['value'] = $this->input->post('tresc');
$data['autor']['value'] = $this->input->post('autor');
$data['keywords']['value'] = $this->input->post('keywords');
$this->response['content'] = $this->load->view('news_add', $data, True);
}
else
{
$this->load->model('News');
$this->News->add_news(array('news_title' => $this->input->post('tytul'), 'news_text' => $this->input->post('tresc'), 'news_author' => $this->input->post('autor'), 'news_date' => time(), 'news_keywords' => $this->input->post('keywords')));
$this->response['content'] = '<h1>Dane zapisane</h1><META HTTP-EQUIV="Refresh" CONTENT="1; URL='.site_url('admin').'">';
}
$this->load->view('index', $this->response);
}
// edycja newsa
function news_edit()
{
$this->load->model('News');
$id = $this->uri->segment(3);
IF(isset($id) and is_numeric($id))
{
$ar = $this->News->get_news_by_id($id)->result_array();
$data["tytul"] = array('name' => 'tytul');
$data['tresc'] = array('name' => 'tresc', 'rows' => 8, 'cols' => 50);
$data['autor'] = array('name' => 'autor');
$data['keywords'] = array('name' => 'keywords');
$rules['tytul'] = "required|max_length[250]|xss_clean";
$rules['tresc'] = "required|xss_clean";
$rules['keywords'] = "required|max_length[250]|xss_clean";
$this->validation->set_rules($rules);
if ($this->validation->run() == FALSE)
{
IF(strlen($this->input->post('tytul')) > 0)
{
$data['tytul']['value'] = $this->input->post('tytul');
$data['tresc']['value'] = $this->input->post('tresc');
$data['autor']['value'] = $this->input->post('autor');
$data['keywords']['value'] = $this->input->post('keywords');
}
else
{
$data['tytul']['value'] = $ar[0]['news_title'];
$data['tresc']['value'] = $ar[0]['news_text'];
$data['autor']['value'] = $ar[0]['news_author'];
$data['keywords']['value'] = $ar[0]['news_keywords'];
}
$this->response['content'] = $this->load->view('news_edit', $data, True);
}
else
{
$this->News->update_news($id, array('news_title' => $this->input->post('tytul'), 'news_text' => $this->input->post('tresc'), 'news_author' => $this->input->post('autor'), 'news_date' => time(), 'news_keywords' => $this->input->post('keywords')));
$this->response['content'] = '<h1>Zmiany zapisane</h1><META HTTP-EQUIV="Refresh" CONTENT="1; URL='.site_url('admin').'">';
}
}
else
{
$this->response['content'] = '<h1>Niepoprawny URL</h1>';
}
$this->load->view('index', $this->response);
}
// usunięcie newsa
function news_delete()
{
$id = $this->uri->segment(3);
IF(isset($id) and is_numeric($id))
{
$this->load->model('News');
$this->News->delete_news($this->uri->segment(3));
$this->response['content'] = '<h1>News skasowany</h1><META HTTP-EQUIV="Refresh" CONTENT="1; URL='.site_url('admin').'">';
$this->load->view('index', $this->response);
}
}
// usunięcie komentarza
function com_delete()
{
$id = $this->uri->segment(3);
IF(isset($id) and is_numeric($id))
{
$this->load->model('Comments');
$this->Comments->delete_comment($this->uri->segment(3));
$this->response['content'] = '<h1>Komentarz skasowany</h1><META HTTP-EQUIV="Refresh" CONTENT="1; URL='.site_url('admin').'">';
$this->load->view('index', $this->response);
}
}
}
<tr><td><?PHP echo $news_title ?></td><td width="100"><center>[<a href="<?PHP echo site_url('admin/news_edit/'.$news_id); ?>">Edytuj</a>]</center></td><td width="100">[<a href="<?PHP echo site_url('admin/news_delete/'.$news_id); ?>" >Kasuj</a>]</td></tr>
Następnie mamy metodę dodającą dane, co już było przerabiane we wcześniejszym warsztacie, oraz drugą edytującą, która ma jedną większą zmianę:
IF(strlen($this->input->post('tytul')) > 0)
Jako że chcemy mieć dotychczasowe dane do edycji to musimy je przypisać do formularza jeżeli tej nie był jeszcze wysłany. Funkcja strlen zwraca rozmiar podanego łańcucha. Jeżeli formularz nie został wysłany to długość łańcucha z jakiegoś jego pola na pewno będzie równa 0. Jeżeli jest większa znaczy że formularz został wysłany ale pojawiły się problemy - przypisujemy wartości z samego formularza.W kodzie pojawia się:
$this->uri->segment(3)
Metoda $this->uri>segment(ID) zwróci wartość danego segmentu URLa. Np. index.php/foo/bar rozbija się na składowe: 1 to "foo", 2 to "bar". Wewnątrz CI nie ma tablic _POST, _GET, _cookie czy _SERVER (ze względów bezpieczeństwa i nie tylko). By pobrać określony element trzeba użyć owej metody. W naszym przypadku do kasowania i edycji newsów potrzebny jest numer ID danego newsa. Zakładamy URLe postaci index.php/news_OPERACJA/NUMER_ID i gotowe :)Widok news_add.php
<h1>Dodaj news</h1>
<center><?=$this->validation->error_string; ?></center>
<?php echo form_open('admin/news_add'); ?>
<table width="90%" border="0" cellspacing="3" cellpadding="3">
<tr><td width="180"><B>Tytuł</B></td><td><?php echo form_input($tytul); ?></td></tr>
<tr><td width="180"><B>Treść</B></td><td><?php echo form_textarea($tresc); ?></td></tr>
<tr><td width="180"><B>Autor</B></td><td><?php echo form_input($autor); ?></td></tr>
<tr><td width="180"><B>Słowa Kluczowe</B></td><td><?php echo form_input($keywords); ?></td></tr>
<tr><td> </td><td><?php echo form_submit('submit', 'Zapisz'); ?></td></tr>
</table>
<?php echo form_close(); ?>
<div class="rule"></div>
<h1>Edytuj news</h1>
<center><?=$this->validation->error_string; ?></center>
<?php echo form_open('admin/news_edit/'.$this->uri->segment(3)); ?>
<table width="90%" border="0" cellspacing="3" cellpadding="3">
<tr><td width="180"><B>Tytuł</B></td><td><?php echo form_input($tytul); ?></td></tr>
<tr><td width="180"><B>Treść</B></td><td><?php echo form_textarea($tresc); ?></td></tr>
<tr><td width="180"><B>Autor</B></td><td><?php echo form_input($autor); ?></td></tr>
<tr><td width="180"><B>Słowa Kluczowe</B></td><td><?php echo form_input($keywords); ?></td></tr>
<tr><td> </td><td><?php echo form_submit('submit', 'Zapisz'); ?></td></tr>
</table>
<?php echo form_close(); ?>
<div class="rule"></div>



Do Zrobienia
- Logowanie do PA- Dodawanie i usuwanie komentarza
- Wyszukiwarka
- Inne fajne dodatki
RkBlog
Comment article