[PHP] (Skrypty) – Własny CMS? cz. 1

danielek blog 3

[PHP] (Skrypty) – Własny CMS? cz. 1

CMS – System zarządzania treścią

W świecie, gdzie otacza nas wiele różnych systemów do zarządzania treścią stron internetowych, można zadać sobie pytanie, czy jest sens tworzenia własnego.

Często wybranie gotowego już CMS’a zdaje swoje zdanie. Lecz czasami zdarza się, że nie ma odpowiedniego wyboru, ze względu np. na zbyt duże rozbudowanie, gdzie kontrola całości staje się trudnością.Inną decyzją na wybranie zaprogramowania własnego CMS’a, to po prostu chęć nauczenia się tworzenia takich systemów od podstaw.

Na wstępie zaznaczę, że chciałem aby tekst był jak najbardziej prosty i zrozumiały.Również skrypty są w miarę proste, aby przedstawić działania. Oczywiście wszystko to działa i można bezproblemowo stosować.

  1. Zarządzanie zmiennymi, danymi, etc …

Aby nasz system spełniał dobrze swoje zadanie, musi mieć możliwość ustawienia parametrów, tak podstawowych, jak:

  • nazwa strony
  • tytuł strony
  • słowa kluczowe
  • opis strony

Takich, które umożliwią kontrolę:

  • nazwa moduły
  • ID modułu
  • sprawdzenie, czy strona wyświetlana jest pierwszą stroną

Oczywiście nasza system będzie mieć menu:

  • górne
  • środkowe lub boczne
  • dolne – w tzw. stopce

Jako dobrzy programiści trzymamy się dobrych zasad nie używania danych globalnych w programowaniu obiektowym. Jest to spowodowane tym, że przypadkowe można by było zmienić coś. Także daje kontrole nad jednoczesną obróbką tych danych.

Wszystko to opakujemy w jedną klasę, która będzie czuwać nad danymi tymi w systemie.Dlatego też można ją uczynić ogólnie dostępną bez potrzeby tworzenia z niej obiektu. Takie klasy tworzy się wtedy jako statyczne.blog Danielka 1 foto

System zarządzania danymi/zmiennymi – statyczna klasa

 

class CMainFrame{

public static $bGlownaStrona = false;

private static $params = array(

„szSiteTitle” => „”,

„szPageTitle” => „”,

„szPageDescription” => „”,

„szPageKeywords” => „”,

„bFirstPage” => true,

„szModule” => „”,

„iModID” => „”,

„bMenuItem” => false,

„szSubPage” => „”,

„aMenus” => array(),

„aTopMenu” => array(),

„aMiddleMenu” => array(),

„aBottomMenu” => array()

);

 

 

private function __construct() {

}

private function __destruct() {

}

 

public static function setParam($paramName, $paramValue) {

if(is_array(self::$params[$paramName]))

self::$params[$paramName][] = $paramValue;

else self::$params[$paramName] = $paramValue;

}

 

public static function getParam($paramName) {

if(isset(self::$params[$paramName]))

return self::$params[$paramName];

return false;

}

};

Tutaj konstruktor i destruktor są prywatne, aby zapobiec tworzeniu obiektów – są to tzw.singletony. Metoda setParam sprawdza również czy czasem zmienna nie jest tablica, aby poprawnie obsłużyć dodanie danych tej tablicy.

Użycie tego wygląda tak:

// Ustawienie zmiennejCMainFrame::setParam(‚szSiteTitle’, ‚Dobreprogramy’);

// Pobranie zmiennej

echo ‚<title>’.CMainFrame::getParam(‚szSiteTitle’).'</title>’;

Nasz kod można ulepszyć, co da nam większą wygodę w używaniu. Jednym z takich ulepszeń było by dodanie możliwości odwoływania się do zmiennych tak jak do tablicy, czyli:

// Ustawienie zmiennej$MainFrame[‚szSiteTitle’] = ‚Dobreprogramy.pl’;

// Pobranie zmiennej

echo ‚<title>’.$MainFrame[‚szSiteTitle’].'</title>’;

Aby tego dokonać nasz klasa zaimplementuje jeden z interfejsów jakim jest ArrayAccess oraz zaimplementuje się w nim 4 wirtualne metody, które owy interfejs nam udostępnia.Są nimi:

  • offsetGet($key) -> służy do pobrania danej/zmiennej
  • offsetSet($key, $value) -> służy do ustawienia danej/zmiennej
  • offsetExists($key) -> sprawdza czy dana/zmienna istnieje
  • offsetUnset($key) -> usuwamy zmienną

Dzięki implementacji interfejsu ArrayAccess nasz klasa, a dokładniej obiekt działa tak jakbyśmy działali na tablicach. (nie do końca tak samo, ponieważ musielibyśmy jeszcze zaimplementować przynajmniej iteratory)Do tego trzeba zauważyć, aby takie coś działało, to nie można już używać naszej statycznej klasy, a zaimplementować to jako obiekt, czyli działamy na obiekcie a nie na klasie.blog Danielka 2 foto

System zarządzania danymi/zmiennymi – wersja obiektowa

A kod wygląda następująco:

class CMainFrame implements ArrayAccess{

private $params = array(

„szSiteTitle” => „”,

„szPageTitle” => „”,

„szPageDescription” => „”,

„szPageKeywords” => „”,

„bFirstPage” => true,

„szModule” => „”,

„iModID” => „”,

„bMenuItem” => false,

„szSubPage” => „”,

„aMenus” => array(),

„aTopMenu” => array(),

„aMiddleMenu” => array(),

„aBottomMenu” => array()

);

 

public function __construct() { }

public function __destruct() {}

 

public function setParam($paramName, $paramValue) {

if(is_array($this->params[$paramName]))

$this->params[$paramName][] = $paramValue;

else $this->params[$paramName] = $paramValue;

}

 

public function getParam($paramName) {

if(isset($this->params[$paramName]))

return $this->params[$paramName];

return false;

}

 

public function offsetGet($paramName) {

return $this->getParam($paramName);

}

 

public function offsetSet($paramName, $paramValue) {

$this->setParam($paramName, $paramValue);

}

 

public function offsetExists($paramName) {

return isset($this->params[$paramName]);

}

 

public function offsetUnset($paramName) {}

};

Nie używamy tutaj offsetUnset do usuwania, ponieważ nie jest to nam potrzebne, a do tego nie chcielibyśmy aby nam coś skasowali ;)Wnikliwy również zauważą, że konstruktor i destruktor nie jest już deklarowany jako prywatny. A to dlatego, że już tworzymy normalne obiekty.Równocześnie zmieniło się wew. odwołanie do składowych obiektu – nie klasy.Aby odwołać się do klasy, stosuje się self::, natomiast do obiektu poprzez $this->.

Użycie tego wygląda następująco:

// utworzenie obiektu$SiteData = new CMainFrame;

// Ustawienie zmiennej

$SiteData [‚szSiteTitle’] = ‚Dobreprogramy’;

// Pobranie zmiennej

echo ‚<title>’.$SiteData[‚szSiteTitle’].'</title>’;

Jak widać wygląda to trochę lepiej, chociaż to pojęcie względne 😉 Na pewno jest krócej. Do tego jak używamy obiektów, to można stworzyć kilka takich ‚kontenerów’ w zależności od działania.

  1. Obsługa modułów

Aby nasz strona miała trochę więcej możliwości niż tylko wyświetlanie wcześniej ustawionych danych, można zaprzęgnąć systemów modułów. System ten umożliwi nam rejestrowania i ładowania modułów.

Dzięki modułom można dodać do strony galerie, wideo, system newsów, typy stron (listy etc.) itp., które będą ładowane na żądanie.

Nasz system modułów będzie mógł byś zagnieżdżany. Czyli w jednym module, będzie można ładować następny a nawet ten sam. I tak w nieskończoność. Bardzo to ułatwi nam życie, bo np. newsie będziemy mogli załadować galerię lub np. jakieś filmiki, które wcześniej były utworzone. Będzie można nawet dołączyć kolejny news w samym już newsie – jak komu to będzie potrzebne.

Ten nasz system do obsługi modułów, to nic innego jak kolejny singleton, czyli statyczna klasa. Klasa ta będzie zawierać 5 metod:

  • register– rejestrowania modułu
  • load– ładowanie modułu
  • check– sprawdzanie, czy dany moduł istnieje
  • isEmbedded– sprawdzenie, czy aktualny moduł jest wbudowanym
  • getEmbeddedParam– pobranie dodatkowego parametru wbudowanego modułu
danielek blog 3

System zadządzania modułami – klasa statyczna

A tak wygląda kod:

class CModule {private static $aModules = array();

private static $bEmbedded = false;

private static $embeddedParam = null;

 

public static function register($szModuleName, $szModuleFile) {

self::$aModules[$szModuleName] = $szModuleFile;

}

 

public static function load($szModuleName, $iModuleID, $embeddedParam = null) {

if($szModuleName != ” && is_numeric($iModuleID) && ($bExists = self::check($szModuleName))) {

$_szModule  = CMainFrame::getParam(‚szModule’);

$_iModID    = CMainFrame::getParam(‚iModID’);

if($_szModule !== $szModuleName) {

self::$bEmbedded = true;

self::$embeddedParam = $embeddedParam;

}

 

CMainFrame::setParam(‚szModule’, $szModuleName);

CMainFrame::setParam(‚iModID’, $iModuleID);

if($bExists) {

if(file_exists(self::$aModules[$szModuleName]))

include(self::$aModules[$szModuleName]);

}

if(self::$bEmbedded) {

self::$embeddedParam = null;

self::$bEmbedded = false;

}

CMainFrame::setParam(‚szModule’, $_szModule);

CMainFrame::setParam(‚iModID’, $_iModID);

return true;

}

return false;

}

 

 

private static function check($szModuleName) {

foreach(self::$aModules as $key => $val)

if($key === $szModuleName) return true;

 

return false;

}

 

public static function isEmbedded() { return self::$bEmbedded; }

public static function getEmbeddedParam() { return self::$embeddedParam; }

}

Nasz wcześniejszy system do zarządzania danymi/zmiennymi strony – CMainFrame – jest tutaj używany. Tutaj widać, do czego się to m.in. przydaje.

Jak widać z powyższego kodu, najbardziej rozbudowaną jest metoda load.Przyjmuje on 2 lub 3 parametry, gdzie 3 parametr jest domyślnie ustawiony na null, czyli pustą zmienną. Służy ona – $embeddedParam – do ustawienia dodatkowego parametru dla modułów. Czasami taki parametr może się przydać. W następnym artykule, będzie jego zastosowanie.Dwa pierwsze parametry odpowiadają za nazwę oraz ID modułu. Parametry te będą pobierane na początku strony. Jest to takie same ‚coś’, jak na portalu DP, podczas np. podglądy tworzonego wpisu na blogu, tj. ‚PodgladBloga,11111.html’, gdzie ‚PodgladBloga’, to $szModuleName, a ‚11111’ to $iModuleID.

Co robi metoda load?

  • sprawdza poprawność parametrów oraz czy podany moduł został zarejestrowany
  • zapamiętanie zmiennych modułu ładującego, aby później je odtworzyć i powrócić do dalszego wykonywania po zakończeniu modułu ładowanego
  • jeśli dodatkowy parametr istnieje to zapamiętanie go
  • ustawienie danych modułu (nazwa modułu oraz jego ID – potrzebne do załadowania z bazy)
  • jeśli plik modułu istnieje to załadowanie jego kodu
  • przywrócenie zmiennych modułu ładującego, które zapamiętaliśmy na początku

A jak to wygląda w działaniu? Najprościej tak:

// ustawienie danych modułu// ?module=news&modID=23

if(isset($_GET[‚module’]) && $_GET[‚module’] != „”) {

CMainFrame::setParam(‚szModule’, $_GET[‚module’]);

CMainFrame::setParam(‚bFirstPage’, false);

}

if(isset($_GET[‚modId’]) && $_GET[‚modId’] != „”) CMainFrame::setParam(‚iModID’, $_GET[‚modId’]);

 

// ‚rejstracja’ przykładowych modułów

CModule::register(‚list’, ‚modules/list.php’);

CModule::register(‚content’, ‚modules/content.php’);

CModule::register(‚news’, ‚modules/news.php’);

CModule::register(‚article’, ‚modules/article.php’);

CModule::register(‚gallery’, ‚modules/gallery.php’);

CModule::register(‚video’, ‚modules/video.php’);

CModule::register(‚contact’, ‚modules/contact.php’);

 

// oraz załadowanie modułu: CMainFrame::load(‚news’, 23);

CModule::load(CMainFrame::getParam(‚szModule’), CMainFrame::getParam(‚iModID’));

Dzięki takiemu ładowaniu modułów, pomijamy bloki if lub switch, co zmniejsza kod oraz co najważniejsze daje nam większą kontrolę nad kodem. Osiągamy to właśnie dzięki obiektowości. Przy dodawaniu większej ilości modułów, trzeba było by dodawać kolejny if lub kolejny argument w switch. Mniej więcej wyglądało by to tak:

// to wszystko zamiast: CModule::load(CMainFrame::getParam(‚szModule’), CMainFrame::getParam(‚iModID’));switch(CMainFrame::getParam(‚szModule’)) {

case ‚news’:

include(‚modules/news.php’);

break;

case ‚list’:

include(‚modules/list.php’);

break;

case ‚article’:

include(‚modules/article.php’);

break;

case ‚gallery’:

include(‚modules/gallery.php’);

break;

case ‚video’:

include(‚modules/video.php’);

break;

case ‚contact’:

include(‚modules/contact.php’);

break;

case ‚content’:

default:

include(‚modules/content.php’);

}

Gdzie trzeba by było jeszcze dodatkowo sprawdzać ID. Jest to mnie wygodne i przejrzyste niż użycie CModule::register().A do tego jeśli robimy ładowanie modułów z panela administracyjnego, to wtedy widzi się zalety ładowania poprzez obiektowość, tak jakCModule::load()

Ciężej by również było zagnieżdżać moduły, ze względu na powtórzenia tego samego kodu. A my jako programiści, którzy chcąc stosować się do dobrych zasad, stosujemy metodę DRY, czyli Don’t repeat yourself – co oznacza: Nie powtarzaj się.

Co dalej?

W następnej części pokażę jak w prosty sposób używać znaczników w tekście na stronie.Czyli tak samo jak jest podczas tworzenia wpisu na blogu DP.Stworzymy dwa znaczniki {Gallery}{/Gallery} odpowiedzialny za ładowanie zdjęć na stronie oraz {Video}{/Video}, który będzie wczytywał film z YouTube’a.

Także stworzymy kilka prostych modułów, w tym między innymi:

  • galeria
  • wideo z YT
  • content – zawartość strony

A w części trzeciej zajmiemy się obsługą bazy danych, po czym skleimy wszystko w całość. I tak powstanie nasz prosty CMS. Do obsługi bazy danych użyjemy PDO, a bazą będzie SQLite.

Zostanie jeszcze stworzenie prostego panelu administracyjnego, systemu logowania i proste zabezpieczenia zapobiegające przejęciu sesji.

 

No Comments

Post a comment