Autor Zpráva
Tomáš123
Profil
Ahoj všetci
Mám v pláne vytvoriť vlastný systém na správu článkov na stránke. Kód v OOP je vraj prehľadnejší a lepšie udržiavateľný a rozšíriteľný. A práve to potrebujem, vzhľadom na to, že ešte nezvládam naprogramovať všetko, čo by som v mojom vysnívanom systéme chcel.

Začať by som chcel kdesi u spracovania URL adresy a poskladania SQL dotazu zo získaných údajov.
Momentálne používam toto:
<?php
  $dir = "content/";
  $default = "default.php";
  $error = "error.php";
  if(isset($_GET['section'], $_GET['page']) and is_string($_GET['section']) and is_string($_GET['page']) and file_exists($dir.$_GET['section']."/".$_GET['page'].".php")) {
    $path = $dir.$_GET['section']."/".$_GET['page'].".php";
  }
  elseif(isset($_GET['section']) and empty($_GET['page']) and is_string($_GET['section']) and file_exists($dir.$_GET['section']."/".$_GET['section'].".php")) {
    $path = $dir.$_GET['section']."/".$_GET['section'].".php";
  }
  elseif(empty($_GET['section']) and file_exists($dir.$default)) {
    $path = $dir.$default;
  }
  else {
    $path = $error;
  }
  ob_start();
  include $path;
  $content = ob_get_clean();
?>
... ale nie som spokojný s prevedením. Celé je to tak rozťahané.

Ako by to šlo prerobiť do OOP? Akú triedu použiť?

S aktuálnymi vedomosťami by som navrhoval niečo takéto:
<?php
class spracovanie_url {
  # metóda, ktorá zistí, čo všetko je vyplnené, vráti pole s indexami kategórie a stránky a príslušnými hodnotami
  # metóda, ktorá indexy poľa ošetrí metódou mysqli::real_escape_string
  # metóda, ktorá z poľa poskladá SQL dotaz
  # metóda, ktorá získa potrebné údaje z databázy
}

Je takýto predpoklad práce s OOP správny, alebo som to vôbec nepochopil?

Ďakujem veľmi pekne za odpovede.
bestik_63
Profil
To co hledáš je pravděpodobně MVC architektura
Tomáš123
Profil
bestik_63:
To co hledáš je pravděpodobně MVC architektura
Bohužiaľ nie, to je zas niečo iné. Navyše v tomto prípade nepletiem zobrazenie a spracovanie. Skrátene a všeobecne sa pýtam na to, čo by mala trieda obsahovať. Obalom čoho by mala byť? Jedno zo starších vláken o OOP malo v kóde triedu pomenovanú prihlásenie, v ktorej boli príslušné metódy... „Nesprávny postup“, napísal ktosi. Ako potom?
Alphard
Profil
Tomáš123:
Kód v OOP je vraj prehľadnejší a lepšie udržiavateľný a rozšíriteľný
Řekněme spíše, že objektový kód je přehlednější a lépe rozšířitelný. Zapsat špatný kód do OOP syntaxe nic neřeší.

Je takýto predpoklad práce s OOP správny, alebo som to vôbec nepochopil?
Spíš to druhé. Jedna třída má mít jednu zodpovědnost, určitě nemíchat zpracování url a komunikaci s databází dohromady.

Pro vysvětlování principů podle mě není tvorba webů úplně vhodná. Aby se vytvořil použitelný controller a databázový model, je třeba velké množství tříd, ve kterých se začátečník ztratí.

# metóda, ktorá zistí, čo všetko je vyplnené, vráti pole s indexami kategórie a stránky a príslušnými hodnotami
# metóda, ktorá indexy poľa ošetrí metódou mysqli::real_escape_string
# metóda, ktorá z poľa poskladá SQL dotaz
# metóda, ktorá získa potrebné údaje z databázy

1. Hned ze začátku bych doporučil použít dibi, nemá cenu tvořit vlastní databázový layer. Používejte ho ale instančně, ne staticky (jak je snad ve všech příkladech :-)), protože tím se nic nenaučíte.
2. Vytvořte si třídy Sections a Pages, které budou představovat model komunikující s databází prostřednictvím dibi, pro začátek bude v každé z nich stačit jedna metoda, řekněme findBySlug(), která vrátí daný záznam.
3. Vytvořte třídu Controller s metodou parseUrl, která zpracuje požadavek, a metodou např. run, která použije získané údaje z url pro stažení dat z databáze přes třídy Sections a Pages a provede další akce.
Tomáš123
Profil
Alphard:
Pro vysvětlování principů podle mě není tvorba webů úplně vhodná.
Čo potom?

nemá cenu tvořit vlastní databázový layer
Prečo je potrebný? Na zabezpečenie som mal v pláne pravidelne zálohovať obsah webu a zapisovať do súboru dátum a IP adresu pristupujúcich.

jedna metoda, která vrátí daný záznam.
Tú som sa trochu stratil. Má vracať záznam z databázy? Vo väčšine prípadov budú v poli GET vyplnené obidva kľúče a samostatne nebudú dosť konkrétne.

která použije získané údaje z url pro stažení dat z databáze přes třídy Sections a Pages a provede další akce
Ako cez triedy? Nie je mi jasné, čo by mala vykonávať metóda v triedach Sections a Pages.

Skúsil som niečo vytvoriť cez funkcie:
<?php
$elements = array('section', 'page')
function parse_url($elements, $dir = "/", $default) { # domnievam sa, že predávať pole GET nie je potrebné, vzhľadom na to, že je superglobálne
    $empty = false;
    foreach($elements as $element) {
        if(empty($_GET[$element])) { 
            $empty = true;
            return $dir.$default; # index.php, ktorý spracúva URL
    }
    if($empty !== true) {
        # poznáte nejakú funkciu, ktorá priradí prvkom poľa $elements hodnoty z poľa $_GET s príslušnými indexami
        # výstupom by malo byť pole array($element => $_GET['element'], $element => $_GET['element']), cyklom sa mi to nepodarilo
        # return array
    }    
}
# funkcia, ktorá sa na základe údajov z funkcie parse_url() skúsi pripojiť do databázy a vytiahnuť potrebné záznamy
# teoreticky controller, ktorý výsledok zobrazí v použiteľnej podobe
?>

V OOP by mala každá jednotka (trieda, metóda) spravovať iba jednu činnosť, však. Teóriu o MVC som čítal, ale neviem ako si mám predstaviť controller. Ako k nemu chodia údaje? Ako je to celé rozdelené, že zo zobrazenia prídu údaje controlleru, ten ich vyhodnotí a pošle modelu, ktorý ich po spracovaní zas pošle controlleru a ten zobrazeniu?

Ďakujem
Alphard
Profil
Tomáš123:
Prečo je potrebný?
Spíš bych řekl užitečný, umožňuje např. vkládat do tabulky jednoduše celá pole $db->insert('tabulka', $hodnoty)->execute(), seskupovat výsledky $query->fetchAssoc('state,city,#') nebo filtrovat výsledky where('id in %l', $idList) a hromadu dalších užitečných věcí.

Na zabezpečenie som mal v pláne pravidelne zálohovať obsah webu a zapisovať do súboru dátum a IP adresu pristupujúcich.
Zálohy a log jsou samozřejmě dobrý nápad, ale jako jediné zabezpečení to snad nemyslíte vážně. Zkuste to říct někde na pohovoru :-) Taková jedna praktická věc, když vám někdo s IP adresou z Indie ukradne osobní údaje uživatelů, zálohy i logy vám budou k ničemu.

Na delší odpověď teď nemám čas.
snazimse
Profil
Tomáš123:
Teóriu o MVC som čítal, ale neviem ako si mám predstaviť controller

Jednoduše.Je to část, která především reaguje na uživatelův podnět (události) a předává to dále do Model a View.
Tomáš123
Profil
Alphard:
ale jako jediné zabezpečení to snad nemyslíte vážně
Nemyslím. Údaje do databázy budú prechádzať escapovacími funkciami. A samozrejme, kým si nebudem istý zabezpečením, nikoho tam nepustím a už vôbec nebudem žiadať osobné údaje, takže najhoršiu hrozbu predstavuje vymazaná databáza. To vyrieši zálohovanie.

Prosím, doplnili by ste svoju odpoveď.

snazimse:
Takto jednoducho si to predstaviť viem. Skôr mi ide o hlbšie pochopenie, správne techniky a stavbu a rozdelenie súborov.
Alphard
Profil
Tomáš123:
Prosím, doplnili by ste svoju odpoveď.
Nevím, co doplňovat, kódu [#5] nerozumím.

Ako cez triedy? Nie je mi jasné, čo by mala vykonávať metóda v triedach Sections a Pages.
Přistupovala by do databáze.

Údaje do databázy budú prechádzať escapovacími funkciami.
Db layer tohle automatizuje, čímž snižuje riziko chyby. Jediný důvod proč nepoužít aspoň dibi je, že už se používá něco lepšího. Nativní nástroje pro práci s databází jsou v PHP otřesné. Navíc sám jste chtěl psát metodu, která z pole poskládá dotaz. Proč to psát znovu, když to už existuje (a mnohem lepší).

ale neviem ako si mám predstaviť controller. Ako k nemu chodia údaje?
Tady začíná být pojem controller moc široký. Obvyklý je postup načtení takový, že router dostává řetězec s url adresou a routovací mapu, na základě těchto údajů pak volá konkrétní controller (např. GalleryController, potomek základního Controlleru). Mezitím je mnoho dalších tříd, může tam být třeba vřazen controller vyžadující admin přihlášení, musí tam být mechanismus pro ošetření chybových requestů,... Složitost v současnosti uznávaných frameworků je hodně nad vašimi kódy a znalostmi, takže začněte studovat.
Třeba doc.nette.org/cs/2.3/presenters, ať Nette používáte nebo ne, je jeden z příkladů implementace. Až dočtete, pokračujte u jiného framworku. A jestli dosud neumíte OOP, tak se prvně naučte to.
Tomáš123
Profil
Mám otázku týkajúcu sa OOP v praxi. Súvisí s triedami, tak som to zaradil sem.

Chcel by som vytvoriť triedu, ktorá vytiahne z databázy základné údaje (dajme tomu meno a priezvisko) a ak nájde v druhej tabuľke riadok, ktorý obsahuje obidva údaje vráti základný aj rozšírený záznam. Celé sa to potom cyklom usporiada do HTML tabuľky, ktorá môže vyzerať napríklad takto:
 Meno | Priezvisko         Meno | Priezvisko | Vek*
 Ján  | Novák              Ján  | Novák      | 20
* to by pochádzalo z druhej tabuľky

Mám teoretické poňatie o tom, ako niečo podobné docieliť. Ako vstup si predstavujem údaje od databázy a názvy tabuliek.
Chcem sa opýtať, ako nazvať takúto triedu. Fakt neviem, ako by sa mohla volať, ani podľa čoho ju pomenovať (tak napríklad metóda má byť sloveso). Viete mi na základe tohoto popisu poradiť? Nápomocné budú aj všeobecnosti.
Alphard
Profil
Tomáš123:
Chcel by som vytvoriť triedu, ktorá vytiahne z databázy základné údaje (...) a ak nájde v druhej tabuľke riadok (...) Celé sa to potom cyklom usporiada do HTML tabuľky (...)
Ako vstup si predstavujem údaje od databázy a názvy tabuliek.

Chcem sa opýtať, ako nazvať takúto triedu.
Problém s pojmenováním této třídy máte zřejmě z toho důvodu, že tato třída je moc složitá. Její název by měl být přibližně ConnectDBSelectUserAndRenderHtmlTable. Asi je jasné, že tohle není dobrý nápad.
Objektové programování je o kombinaci více objetků, v tomto případě by se měla použít třída Connection pro připojení k databázi, třída User pro výběr dat o uživateli a třída např. HtmlRenderer UserView. Tohle je absolutní minimum, v praxi by těch tříd bylo ještě mnohem více, nějaký BaseModel, podpůrné třídy pro vykreslení html, aspoň náznak controlleru a routeru.

Držte se principu Single responsibility principle, každá třída má mít jedinou zodpovědnost (a pak lze vymyslet i název).
Joker
Profil
Tomáš123:
Chcel by som vytvoriť triedu, ktorá vytiahne z databázy základné údaje (dajme tomu meno a priezvisko) a ak nájde v druhej tabuľke riadok, ktorý obsahuje obidva údaje vráti základný aj rozšírený záznam. Celé sa to potom cyklom usporiada do HTML tabuľky

Tak začneme tím, že to určitě nebude jedna třída.
Už z toho popisu je zřejmé, že tam je víc nesouvisejících činností.
Podle mě je na splnění pravidla „jedné odpovědnosti“ celkem jednoduchý ukazatel. Stačí si položit otázku „Co to dělá?“ Když odpověď je „Dělá tohle a něco jiného a ještě něco dalšího a…“ (přičemž to jsou nesouvisející věcí), má to být více tříd.

Takže tam bude potřeba:
1. Třída, která načte data z databáze.
2. Data potřebují nějakou reprezentaci, takže objektová reprezentace těch dat.
3. Třída, která umí z dat udělat HTML tabulku.

Resp. ve skutečnosti 1. a 3. budou asi jen součásti jiných tříd, protože načítat data z databáze bude určitě potřeba častěji, stejně jako generovat pro data HTML reprezentaci.
Čili to první by měla zařídit třída, která obhospodařuje datový model, to třetí nějaká prezentační třída.
Tomáš123
Profil
Alphard:
Tohle je absolutní minimum, v praxi by těch tříd bylo ještě mnohem více, nějaký BaseModel, podpůrné třídy pro vykreslení html, aspoň náznak controlleru a routeru.
Vedel by si mi na jednoduchom príklade ukázať činnosť controllera?

Joker:
2. Data potřebují nějakou reprezentaci, takže objektová reprezentace těch dat.
Čo si mám pod tým predstaviť? Niečo, čo sa bude starať o to, aby boli údaje z tabuľky napríklad v poli names[] a teda sa s nimi ľahšie pracovalo pri výpise?

Resp. ve skutečnosti 1. a 3. budou asi jen součásti jiných tříd
Ako by to vyzeralo? Použiť extends?

Čosi som vytvoril kombináciou vašich príspevkov:
<?php
class Connection {
    private $host;
    private $login;
    private $password;
    private $database;
    
    function __construct($host, $login, $password, $database) {
        $this->host = $host;
        $this->login = $login;
        $this->password = $password;
        $this->database = $database;
    }
    # Možno by sa hodilo vytvoriť si setter pre posledný argument, aby som sa nemusel pripájať dvakrát.*
    function connect() {
        # V dokumentácii som našiel funkciu mysqli::__construct(). Nie je mi jasné, čo s ňou.*
    }
    function disconnect() {
        mysqli::close();
    }
}
class User {
    private $result;
    
    private function get_info() {
        # mysqli->query();
    }
    
}
class User_view {
    function render_html {
        # Nejako sa dostať k údajom z triedy User
        # nejakým cyklom to usporiadať do tabuľky
      }
}
?>
<?php
# Predstava, ako by to mohlo celé nakoniec vyzerať
$host = '';
$login = '';
$password = '';
$database = '';

$small_db = new Connection($host, $login, $password, $database); # Ako automaticky pripojiť hneď po vytvorení novej inštancie?*
$small_db->connect();

$small_db->disconnect(); # Bude správne vyhodnotené metódou ktorej triedy je disconnect?*

$large_db = new Connection($host, $login, $password, $database);

$large_db->disconnect();

$output = new User_view(); # Musím postupovať takto ak neexistuje žiadny konštruktor, alebo môžem prejsť rovno k ďalšiemu riadku?*
$output = User_view->render_html(); # Ak by som mal nejaké nároky na vzhľad, zrejme by bolo vhodné postaviť to tak, aby som to mohol uviesť v argumentoch.
?>

Otázky v kóde, ktoré mi priveľmi vŕtajú hlavou som označil hviezdičkou. Budem rád, ak mi ich zodpoviete.

Ďakujem, vážim si vašej pomoci.
Alphard
Profil
Tomáš123:
Vedel by si mi na jednoduchom príklade ukázať činnosť controllera?
Jedna vrstva MVC architektury. Bylo by v ní přibližně to, co je teď na konci kódu.

K tomu kódu:
- Nevytvářej vlastní třídu pro připojení k databázi (nemá to cenu, zřejmě v ní bude hromada chyb, nebude moc chytrá a bude nebezpečná). Stáhni si http://dibiphp.com/cs/quick-start a nauč se ho používat.
- Třída User: metoda getInfo (tohle je standard pro pojmenování proměnných) bude muset být public. Nebude tam žádné mysqli, ale volání dibi (Dibi lze volat staticky. Už vidím, že cokoliv jiného bude problém.). Vlastnost $result je špatně pojmenováná.

Ako automaticky pripojiť hneď po vytvorení novej inštancie?*
Právě v konstuktoru, tj. __construct().

Ten kód je v této podobě k ničemu. Smiř se s tím, že kód se neustále přepisuje. Stáhni si dibi a s jeho pomocí (bez dalších objektů, kterým nerozumíš) dokončí své zadání (výpis uživatelů do tabulky) do funkční podoby. Pak sem dej kód a řekneme ti, co a jak změnit.

Vaše odpověď

Mohlo by se hodit

Odkud se sem odkazuje


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: