Diskuze: PDO - Duplicita

PHP PHP PDO - Duplicita American English version English version

Avatar
katrincsak
Člen
Avatar
katrincsak:

Ahoj,

s PHP celkem začínám a využil jsem zdejších návodů k učení. Z důvodu toho že mi OOP a PDO přijde přehlednější, bezpečnější a hezčí rád bych v tom pokračoval. Nějaké amatérské zkušenosti s MySQL mám také. Jenže potýkám se s problémem, že se snažím vložit výsledek do DB a zápis je OK. Ale rád bych vložil podmínky jako např. duplicita již podle jména, emailu atp. Nevím jak v PDO to zapsat.

třídu databáze používám ze zdejšího návodu:

class Databaze {

    private static $spojeni;

        private static $nastaveni = Array(
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8",
                PDO::ATTR_EMULATE_PREPARES => false,
    );

    public static function pripoj($host, $uzivatel, $heslo, $databaze)
        {
        if (!isset(self::$spojeni))
                {
            self::$spojeni = @new PDO(
                "mysql:host=$host;dbname=$databaze",
                $uzivatel,
                $heslo,
                self::$nastaveni
            );
        }
        return self::$spojeni;
    }

    public static function dotaz($sql, $parametry = array())
        {
                $dotaz = self::$spojeni->prepare($sql);
        $dotaz->execute($parametry);
        return $dotaz;
    }
}

zpracování "registrace" je to jen test bez šifrování atp a v zápisu se snažím kontrolovat jen "login"

class Registrace
{
        public function __construct($login, $heslo, $email)
        {
                $this->login = $login;
                $this->heslo = $heslo;
                $this->email = $email;
        }

        public function registrace($login, $heslo, $email)
    {
                $existuje = Databaze::dotaz('
            SELECT COUNT(*)
                        FROM `prihlaseni`
                        WHERE `login` = ?
                        LIMIT 1
                        ', array($login));

        if(!$existuje)
        {
                echo "Text1";
                 Databaze::dotaz('
            INSERT INTO `prihlaseni`
                        (`login`, `heslo`, `email`)
            VALUES (?, ?, ?)'
                        , array($login, $heslo, $email));
                        return 'Chybně vyplněný antispam';
        }
        else
                echo "Text2";
 
Odpovědět 27.3.2015 14:09
Avatar
katrincsak
Člen
Avatar
katrincsak:

Prosím kdyžtak i o vysvětlení proč tak a tak, takový mini tutorial... myslím že pro klasický zápis OOP jsem pochopil dost věcí, ale nějak se snažím pochytit to PDO a to mi nějak nejde.

děkuji mnohokrát.

 
Nahoru Odpovědět 27.3.2015 14:12
Avatar
Odpovídá na katrincsak
Martin Konečný (pavelco1998):

Ahoj,

první, obvykle lepší, možnost je nastavit unikátní klíč přímo v MySQL tabulce. PDO ti pak vyhodí chybu - obvykle je nastaveno vyhazování výjimek.

try {
  Databaze::dotaz("
    INSERT INTO ...
  ");
} catch (PDOException $e) {
  // $e->getMessage() obsahuje text chyby
  // prohlédni si i co dělá metoda getCode() a atribut errorInfo
}

Druhá možnost je se před insertem dotázat, zda uživatel s takovým jménem nebo mailem existuje.
To v podstatě máš ty v metodě Registrace::re­gistrace(). To ti nefunguje?

Trochu mimo téma - proč metoda registrace() přijímá parametry a nevyužije atributů, které jsi naplnil v konstruktoru?
Název metody není moc OK, kromě toho, že kvůli kompatibilitě se staršími verzemi název metody = název třídy = konstruktor (takže kdybys to napsal velkými, asi by se to nechovalo podle představ), tak název by spíš měl být slovesem, takže třeba zaregistruj().

Ještě bych být tebou zkusil pročíst pár článků o tom, zda je dobrý mít statickou třídu pro databázi.

Edit: Pokud chceš zjistit, zda není duplicitní jméno nebo mail, můžeš využít operátoru OR přímo v dotazu.

SELECT data FROM tabulka WHERE login = ? OR email = ?
Editováno 27.3.2015 14:32
 
Nahoru Odpovědět  +4 27.3.2015 14:30
Avatar
katrincsak
Člen
Avatar
katrincsak:

Děkuji za pěkný popis a reakcí ke kodu.

  • Unikátní klíče v MySQL používám, to beru již možná automaticky. Ale právě že mi to PDO vyhazuje chybu na základě těch klíčů. Což by měl vyřešit ten kus kodu co jsi napsal. Rozhodně vyzkouším hned zítra jakmile budu mít více času si k tomu zase sednout.

  • Dotaz v podstatě funguje, ale výsledně neřeší problém chyby. Protože se podmínka splní a PDO vyhodí chybu stejně jak bez podmínky.

  • Proč metoda registrace() přijímá parametry? Protože tvou otázkou mi došlo jak zbytečnou věc jsem tam vlastně udělal a proč musím mít v obou instancích právě ty parametry. Rozhodně zkusím změnit jakmile si k tomu sednu. (děkuji za postřeh).

  • Názvy tolik neřeším, protože to ještě předělám 150x, tudíž se spíše soustředím jak to může fungovat atp. Rozhodně i tak budu za podobný postřeh a reakci moc rád. Protože logiku třeba slovesem pojmenovat je fakt super a lze se tak lépe zaměřit na pojmenování než řešit malá a velká písmena. Taky bezva, děkuji ;-)

  • staticka třída pro DB asi dobrá není i když je pro naučení. (Tím se zatím trápit nebudu, ale rozhodně si budu pamatovat, že je třeba se na to později také zaměřit).. Je mi jasné, že se jedná o zvýšení bezpečnosti.
Editováno 28.3.2015 22:46
 
Nahoru Odpovědět 28.3.2015 22:45
Avatar
Odpovídá na katrincsak
Martin Konečný (pavelco1998):

Teď jsem si všiml, že to nastavené máš, aby ti PDO vyhazovalo výjimky. Pak pouze stačí obalit registrační script (respektive stačí ta část scriptu, která udělá INSERT) blokem try-catch a máš v podstatě vystaráno.
Ve tvém případě by mělo fungovat něco jako

class Registrace
{

  private $login;
  private $heslo;
  private $email;

  public function __construct($login, $heslo, $email)
  {
    $this->login = $login;
    $this->heslo = $heslo;
    $this->email = $email;
  }

  public function zaregistrujUzivatele()
  {
    try {
      Databaze::dotaz("
        INSERT INTO `prihlaseni` (`login`, `heslo`, `email`)
        VALUES (?, ?, ?)
      ", array($this->login, $this->heslo, $this->email));
    } catch (PDOException $e) {
      return "Uživatel s tímto jménem již existuje";
    }
  }
}

Nicméně se mi moc nelíbí, že ta metoda pomocí return vrací přímo nějaký text. Hůř se s tím potom pracuje, když se kromě samotného vložení řádku do tabulky má provést ještě něco dalšího.
Řešil bych to spíš nějak takto:

class Registrace
{

  private $login;
  private $heslo;
  private $email;

  public function __construct($login, $heslo, $email)
  {
    $this->login = $login;
    $this->heslo = $heslo;
    $this->email = $email;
  }

  public function zaregistrujUzivatele()
  {
    try {
      Databaze::dotaz("
        INSERT INTO `prihlaseni` (`login`, `heslo`, `email`)
        VALUES (?, ?, ?)
      ", array($this->login, $this->heslo, $this->email));

      mail($this->email, "poslání třeba aktivačního kódu apod.");
      // mail se nepošle, pokud v databázi již takový uživatel bude (automaticky se skočí na blok catch)

    } catch (PDOException $e) {

      // vyhodí se nová výjimka, která obsahuje user-friendly text
      throw new Exception("Uživatel s tímto jménem již existuje");
    }
  }
}


// neřešme teď různé validace vstupu apod.
$jmeno = $_POST["login"];
$heslo = $_POST["heslo"];
$email = $_POST["email"];

$chyba = NULL;
try {
  $registrace = new Registrace($jmeno, $heslo, $email);
  $registrace->zaregistrujUzivatele();

  // nějaký další script
} catch (Exception $e) {
  $chyba = $e->getMessage();
}


// někde na stránce
if ($chyba !== NULL) {
  echo "<div id='chybaFormulare'>{$chyba}</div>";
}
Editováno 28.3.2015 23:05
 
Nahoru Odpovědět  +1 28.3.2015 23:04
Avatar
katrincsak
Člen
Avatar
katrincsak:

Dnes, nebo zítra budu mít dostatek času, abych se řádně na tohle podíval. Dám vědět jak jsem to díky tobě vyřešil a jak moc jsem to i pochopil.

Hodně mi to pomůže se posunout zase o kus dál. Mnohokrát děkuji ;-)

Editováno 29.3.2015 1:23
 
Nahoru Odpovědět 29.3.2015 1:22
Avatar
Odpovídá na katrincsak
Martin Konečný (pavelco1998):

Nemáš zač. Příště prosím použij tlačítko Odpovědět, ať se snáz dozvim, že je nějaký koment :)

 
Nahoru Odpovědět 29.3.2015 14:53
Avatar
katrincsak
Člen
Avatar
Odpovídá na Martin Konečný (pavelco1998)
katrincsak:

Tak jsem si vyzkoušel obě dvě možnosti, které jsem si čistě přepsal. Je to přesně tak jak jsem si představoval a většinu chápu až na pár věcí.

  1. První možnost se mi líbí nejvíce, přijde mi taková čistá a dost přehledná. Samozřejmě do toho lze implementovat stejným způsobem funkci mail() atp. jako je v možnosti níže. Ale nechápu jednu věc.

Zpráva se mi žádná nevypíše a ani chyba:

catch (PDOException $e) {
      return "Uživatel s tímto jménem již existuje";

Musel jsem napsat, aby se zpráva vypsala:

catch (PDOException $e) {
echo "Uživatel s tímto jménem již existuje. <br>";
return;

Jen by mě zajímalo proč? "return" ukončuje relaci, nebo díky tomu lze začít jinou. Logika mi říká, že se relace ukončí, ale až pak se má vypsat text, takže se nevypíše.

  1. Jaký je rozdíl mezi příkladem prvním a druhým? Jediný rozdíl vidím v zápisu, že podmínka/výjimka "try a catch" jsou již na úplném začátku celého procesu na místo až ve zpracování metody. Co se týče bezpečnosti, nebo jakéhokoliv významu mi to přijde absolutně totožné. Je to opravdu stejné?

podmínka if a else je dost podobná jako try a catch. Velký rozdíl vidím v řešení mého problému. Opravdu try a catch se používá jen kvůli kontrole chyb? "Pakliže je vše bez chyby, nebo bez varování, tak se splním?" -> "Ale pakliže obsahuji chybu, nebo varování, tak tě pošlu za 'catch' " ?

Proč je lepší použít:

throw new Exception("Uživatel s tímto jménem již existuje");

než:

return "Uživatel s tímto jménem již existuje";
//nebo
echo "Uživatel s tímto jménem již existuje";
return;
 
Nahoru Odpovědět 30.3.2015 13:18
Avatar
Odpovídá na katrincsak
Martin Konečný (pavelco1998):
  1. Příkaz return ukončuje běh funkce a vrací daný výraz. Ve tvém případě vracela text (string) "Uživatel s tímto jménem již existuje". Díky tomu můžeš s návratovou hodnotou pracovat - uložit si ji do proměnné, vypsat ji atp.

Ve tvém případě bys musel napsat

echo $registrace->zaregistrujUzivatele();

Je to ale poměrně nepraktické, a je proto lepší buď vracet třeba bool (TRUE/FALSE), hodnotu z výčtového typu, nebo při chybovém stavu vyhodit výjimku.

  1. Když místo return použiješ výjimku, můžeš v bloku catch() ještě provést nějaký kód (např. zalogovat chybu, ukončit databázovou transakci atd.). Navíc výjimka může i tzv. probublat - když např. jedna metoda volá druhou metodu a ta volá třetí, pak výjimka ze třetí může probublat až do první metody a teprve tam se zpracovat. Kdybys to řešil přes return, musel bys v druhé metodě použít podmínku a teprve na základě ní rozhodnout, co se bude dál dělat.

Pomocí výjimek lze také snáz ošetřit určitý typ chyb. Můžeš si udělat vlastní třídu (ta musí dědit od základní Exception) a podle ní usoudit, co se dál provede.

Pro příklad by se mohla třída Registrace starat i o validaci vstupů. Kód v hlavním programu by se pak zjednodušil:

class Registrace
{

  // kód jako předtím

  public function zaregistrujUzivatele()
  {
    if (empty($this->login)) {
      throw new InvalidArgumentException("Nevyplnil jsi jméno");
    }
    if (strlen($this->login) < 5) {
      throw new InvalidArgumentException("Jméno musí obsahovat minimálně 5 znaků");
    }

    // nějaká další validace

    Db::query("INSERT INTO ...");  // automaticky vyhodí PDOException, pokud již uživatel s daným jménem existuje
  }

}

try {
  $registrace = new Registrace($_POST["jmeno"], $_POST["heslo"], $_POST["email"]);
  $registrace->zaregistrujUzivatele();
} catch (InvalidArgumentException $e) {
  echo "Zadal jsi špatný vstup: " . $e->getMessage();
} catch (PDOException $e) {
  echo "Uživatel s tímto jménem již existuje.";
}

V tomto příkladu jsme v obou případech jen vypsali chybové hlášení (i když trochu jiným způsobem, takže ten rozdíl je možná trochu vidět).
V určitých případech ale může být žádoucí, aby se chyba v databázi např. logovala. Pro druhý příklad předpokládejme, že se chyba stane z nějakého neočekávaného důvodu.

try {
  $registrace = new Registrace($_POST["jmeno"], $_POST["heslo"], $_POST["email"]);
  $registrace->zaregistrujUzivatele();
} catch (InvalidArgumentException $e) {
  echo "Zadal jsi špatný vstup: " . $e->getMessage();
} catch (PDOException $e) {  // neočekávaná chyba databáze

  // budeme chtít chybu zalogovat
  file_put_contents("log.txt", "Chyba databáze: {$e->getMessage()}", FILE_APPEND);

  // a poslat mail adminovi
  mail("admin@web.cz", "upozornění pro admina");

  // a třeba přesměrovat uživatele na nějakou stránku, kde se mu omluvíme za chybu
  header("location:service_unavailable.html");
  exit;
}

Zde je už snad rozdíl vidět. Pokud uživatel zadá špatné údaje, vyhodí se InvalidArgumen­tException a ty poznáš, že máš pouze vypsat, že je uživatel blb a vyplňuje to špatně.
Pokud je nějaká serverová chyba, zachytí to blok catch(PDOException $e) a provede se jiný script.

Akceptované řešení
+20 Zkušeností
+1 bodů
Řešení problému
 
Nahoru Odpovědět  +1 30.3.2015 14:15
Avatar
katrincsak
Člen
Avatar
Odpovídá na Martin Konečný (pavelco1998)
katrincsak:

Ano rozdíl vidím i jsem si všiml použitých jiných funkcí na vypsání chyby. Na to se ještě podívám detailněji s pomocí dokumentace.
Děkuji určitě za perfektní vysvětlení, dalo mi to celkem dost nových informací.

I když je to jen příklad, ale pro jistotu se zeptám. Všiml jsem si že jsi rovnou do instance $registrace vypsal $_POST[''] bez ošetření? Je opravdu potřeba to ošetřovat když vlastně hodnotu předávám do chráněné třídy (private) ?

Aktuálně to mám takto:

case "login":
        echo '<center><table border="1">
                <tr>
                <label> Registrace / Login </label>
                <form action="#" method="post">
                <tr><td>Login: <input type="text" name="login"></td></tr>
                <tr><td>Email: <input type="email" name="email"></td></tr>
                <tr><td>Heslo:  <input type="password" name="heslo"></td></tr>
                <tr><td><input type="submit" value="Odeslat" name="odeslat"></td></tr>
                </form>
                </tr>
                </table></center>
                ';

                if (isset($_POST['odeslat']) AND ($_POST['odeslat']))
                {
                        if ($_POST['login'] AND $_POST['heslo'] AND $_POST['email'])
                        {
                                $login = htmlspecialchars($_POST['login']);
                                $heslo = htmlspecialchars($_POST['heslo']);
                                $email = htmlspecialchars($_POST['email']);

                                $registrace = new Registrace($login, $heslo, $email);
                                $registrace->zaregistrujUzivatele();
                        }
                        else
                                echo ("Nemáš vyplněné vše co je třeba");
                }
                break;
 
Nahoru Odpovědět 30.3.2015 15:14
Avatar
katrincsak
Člen
Avatar
katrincsak:

A ten popis "return" je fakt k použití divný a praktičtější je pak co jsi vypsal.

 
Nahoru Odpovědět 30.3.2015 15:18
Avatar
Odpovídá na katrincsak
Martin Konečný (pavelco1998):

Ta třída si ty hodnoty z formuláře může ošetřit sama. Např. kdybys povolil registraci i jiným způsobem, než přes jeden určitý formulář na webu, tak bys musel těch několik podmínek, htmlspecialchars() atd. psát pokaždý znovu. Pokud se o to bude umět postarat ta třída, vždycky tomu předáš surová data a ona si s nimi poradí.

Otázkou pak je, kdy si ty vstupy může ošetřit sama a kdy ne, aby to neporušovalo princip single responsibility (každá třída má být zodpovědná jen za jednu věc). Když to přeženu, tak by ta třída Registrace neměla umět ošetřovat vstupy obecně, abys nedělal něco ve smyslu

$jmeno = Registrace::osetriPromennou($_POST["jmeno"]);
Db::query("SELECT neco, co vubec nesouvisi s registraci uzivatele ...", array($jmeno));

Pokud bys třeba používal nějaký framework či jen nějakou knihovnu, která provádí validace (měla by třeba třídu Validators nebo tak něco), pak by ta metoda zaregistrujUzi­vatele() mohla uvnitř použít

// ošetření, zda se jedná o řetězec (pro příklad)
$login = Validators::validateString($this->login);

Ale samotná třída Registrace by neměla umět obecně nějaké vstupy ošetřit - maximálně pro svoje určité potřeby.

 
Nahoru Odpovědět 30.3.2015 15:25
Avatar
Martin Konečný (pavelco1998):

Pro úplnost příkladů:

Kód č. 1

$jmeno = Registrace::osetriPromennou($_POST["jmeno"]);
$heslo = hashFunkce($_POST["heslo"]);

$id = Db::queryOne("SELECT id FROM uzivatel WHERE login = ? AND heslo = ?", array($jmeno, $heslo));

if ($id) {
  $_SESSION["uzivatelId"] = $id;
  echo "eeej, podařilo se ti přihlásit";
}

Zde jen ukázka, že je nesmysl, aby třída Registrace uměla ošetřit vstupy obecně (použiješ ji na něco jinýho než na registraci).

Kód č. 2

class Validators
{

  public static function validateString($input)
  {
    $result = nejaka_funkce_ktera_zkontroluje_vstup($input);

    return $result;  // TRUE | FALSE
  }

}

public function zaregistrujUzivatele()
{
  if (Validators::validateString($this->jmeno) === FALSE) {
    throw new InvalidArgumentException("Jméno není správný řetězec");
  }

  // ostatní část scriptu
}

Obecnou validaci jsme přesunuli do třídy Validators, se kterou pak třída Registrace může pracovat.

Editováno 30.3.2015 15:33
 
Nahoru Odpovědět  +1 30.3.2015 15:31
Avatar
katrincsak
Člen
Avatar
Odpovídá na Martin Konečný (pavelco1998)
katrincsak:

Jestli tomu rozumím správně, tak teoreticky..

  1. Třída co mi ošetří vstup.
  2. Třída co mi případně zkontroluje jestli obsahuje daný input to co má.
  3. Zašifruje heslo.
  4. Zkontroluje se zápis a v případě všeho OK se provede zápis do DB..(to co jsme hlavně řešili).

Tak abych případně danou třídu mohl kdykoliv vyvolat i třeba kvůli něčemu jinému podobnému. Viz třeba hashování hesla, které by mělo být určitě oddělené samostatně "password_hash() .. "

Můžu se jen ještě zeptat, je ten můj zápis $_POST správný? Vůči zneužití? Já si myslím, že ano :-)

 
Nahoru Odpovědět 30.3.2015 15:35
Avatar
Martin Konečný (pavelco1998):

Teď si nejsem jistý, co přesně máš na mysli s tím postupem.
Já bych to řešil tak, že by třída Registrace buď ošetřila svoje vstupy sama (v podstatě bys ty podmínky naházel do metody), nebo že by využila jiné knihovny, která s těmi vstupy umí pracovat.
Teoreticky na hashování hesla by mohla (měla?) být taky třída, ale zas mi přijde zbytečný, aby to byla jedna třída s jedinou primitivní metodou, to pak nemá moc smysl.

Co se týká toho $_POST, tak když chceš zjistit, zda bylo něco odesláno, stačí obvykle podmínka if ($_POST). Pak záleží, jestli ve stejném souboru zpracováváš víc formulářů, pak to musíš oddělit, např. tebou zvolenou podmínkou if ($_POST["name_sub­mit_tlacitka"]).

Nevím, jaký je zbytek tvého kódu, ale moc se mi na něm nelíbí dvě věci:

  1. máš hodně řádků včetně dalšího větvení ve větvi case. Switch se obvykle používá v souvislosti s kratšími scripty, něco jako
switch ($value) {
  case 1: return ...;
  case 2: return ...;
}

když tam pak napíšeš víceřádkový kód včetně dalšího větvení, dost snadno se může stát, že bude kód nepřehledný.

  1. Zpracování formulářů by mělo proběhnout na začátku souboru. Po zpracování formuláře obvykle následuje přesměrování (třeba na stejnou stránku), aby se mohly rovnou zobrazit změny, které zpracování formuláře způsobilo.
 
Nahoru Odpovědět 30.3.2015 15:54
Avatar
katrincsak
Člen
Avatar
Odpovídá na Martin Konečný (pavelco1998)
katrincsak:

Myslím, že jsi můj postup pochopil správně a asi máš určitě pravdu, že by samotná třída jen s čistým hashem, byla asi zbytečná. Každopádně na tohle se dívám tak, že způsob zápisu ještě mnohokrát změním než najdu to co mi bude sedět a než poznám více věcí. Každou informací zjišťuji že spousta věcí být musí, nebo naopak jsou zbytečné.

switch jsem použil na výpis obsahu webu a zároveň úpravy url. Takže pod daným case se zobrazuje obsah. Zatím ještě vymýšlím logiku a zřejmě zkusím nahlédnout i do jiných webů jak to mají řešené. Ale OOP v PHP ještě ani dnes mnoho lidí nepoužívá a knížku na OOP PHP jsem nenašel. Mistrovství v PHP se už nevydává :( . Ale již dříve jsem to řešil, tak že jsem každé case přiřadil cestu k funkci ve které byl obsah stránky, ale to mi přijde stejně "nepřehledné" jak toto. Ale rozhodně se to zmenší, protože obsah budu číst z MySQL.. Takže když vlastně přemýšlím, tak dojdu k tomu co píšeš. Protože budu jen odkazovat na čtení z DB.

Editováno 30.3.2015 16:25
 
Nahoru Odpovědět 30.3.2015 16:25
Avatar
Martin Konečný (pavelco1998):

Pokud neděláš nějaký blog, ale máš přesně určené stránky, pak je dle mě zbytečný házet do do DB (zvlášt, když tam máš nějaký formuláře).
Jestli se chceš věnovat OOP, podívej se na nějaké návody o MVC. Myslím, že tady na itnetwork jsou taky.
Můžeš si zkusit stáhnout a podívat se na můj MVC/MVP návrh, který jsem aplikoval na evidenci knih.

http://www.itnetwork.cz/…vidence-knih

Není snad nic přehlednějšího, než mít výstup v šablonách a o logiku se starat jinde :)

E: sorry, zapomněl jsem kliknout na tlačítko katrincsak

Editováno 30.3.2015 16:47
 
Nahoru Odpovědět 30.3.2015 16:46
Avatar
katrincsak
Člen
Avatar
Odpovídá na Martin Konečný (pavelco1998)
katrincsak:

Děkuji za veškeré vzácné rady, určitě se podívám na to MVC. Již jsem si ho stáhnul.

Vlákno beru jako za vyřešené i když mám spoustu ještě dotazů ohledně jiných věcí, ale něco dohledám a na něco bude lepší vytvořit nové vlákno, ať není vše v jednom.

Děkuji ještě jednou za perfektní rady.

 
Nahoru Odpovědět 30.3.2015 18:12
Avatar
Odpovídá na katrincsak
Martin Konečný (pavelco1998):

V pohodě, klidně se ptej. Byl bych ale rád, kdyby se k tématu vyjádřil ještě někdo jiný. V OOP je různých principů strašně moc a je možný, že jsem ti v něčem neporadil úplně nejlíp.

 
Nahoru Odpovědět  +1 30.3.2015 18:23
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 19 zpráv z 19.