Autor Zpráva
SteveO
Profil *
Dobrý den, proč nelze uložit objekt do parametru 1. třídy, a v další třídě, která rozšiřuje tu první, k němu přistoupit?

$hlavni = new Hlavni();

$db = new DB(DB_SERVER, DB_LOGIN, DB_PASSWORD, DB_DATABASE);
$hlavni->db = $db;

$druha = new Druha(); // class Druha extends Hlavni
$druha->db->query("xxxx"); // vyhodi chybu Call to a member function query() on a non-object...

Díky moc.
Alphard
Profil
Vždyť to je úplně jiná třída*. Zkuste to domyslet dál, jak by to fungovalo, kdyby se proměnné (a i metody) sdílely mezi všemi třídami. Celé by se to rozsypalo.

Pokud svým modelovým třídám dáte společného předka, můžete si odkaz na databází držet ve statickém parametru. Ale tady by teda už bylo lepší vytvořit si DbConnection jako singleton a instanci si získat v konstruktoru každé třídy**, která ho potřebuje (ne že by tento návrh byla výhra, ale lepší než to, o co se snažíte).

* Teď mě napadá, že je to možná potomek. Z kódu to není zřejmé. Nicméně zbytek mé odpovědi zůstává platný.
** Tj. rodičovské třídy, od které další třídy dědí, možná té vaší Hlavni, nevím.
Krakatoa
Profil
Alphard:
Nejspíš řeším něco podobného jak SteveO. Zkouším pochopit základy OOP a jak vhodně přistupovat k databázi a držet připojení. Ne úplně chápu cos psal. Našel jsem tento příklad:
http://www.itnetwork.cz/php-tutorial-objektove-pocitadlo-navstev-dokonceni
Jestli tomu rozumím, je tam třída Databaze a v ní statické funkce pripoj a dotaz. No a pak se stačí jednou připojit a pak házet dotazy odkudkoliv, třeba v nějaké další třídě, která nemusí být ani potomek. Je na takovém postupu něco špatně?
Alphard
Profil
Krakatoa:
No a pak se stačí jednou připojit a pak házet dotazy odkudkoliv, třeba v nějaké další třídě, která nemusí být ani potomek.
Ano, tak to mají navržené. Vzor singleton, který jsem zmínil, je obdobou. Rozdíl je v tom, že tam se staticky udržuje jenom odkaz na instanci. Implementace je snadno dohledatelná, nebudu ji tady uvádět.

Pokud bych měl třídu DbConnection naimplementovanou jako singleton, mohu psát jednoduše
class Cart {
  private $dbConnection;
  
  public function __constructor() {
    $this->db = DbConnection::getInstance();
  }
  
  public function getPrice($id) {
    $this->db->query('...');
    // případně 
    DbConnection::getInstance()->query('...');
  }
}

$cart = new Cart();
$myPrice = $cart->getPrice();
čímž se dostávám přesně k tomu, co bylo požadováno v [#1] SteveO.

V odkazovaném článku mají staticky prakticky všechno, takže pak volají přímo
Databaze::pripoj('localhost', 'root', '', 'pocitadlo_db');
Databaze::dotaz('...');


Sice to vypadá jednoduše, ale když se na to podíváš s nadhledem, tak třída Cart skrývá závislosti.
Z jejího rozhraní není patrné, co vlastně potřebuje ke své funkci.
$cart = new Cart();
$myPrice = $cart->getPrice();
Potřebuje připojení k databázi? Potřebuje připojení k platební bráně? Potřebuje si registrovat nějaké triggery? Těžko říct. Zvlášť pokud pracuji v týmu, používám cizí modul a nemám nastudované zdrojáky třídy Cart.

A mohu se na to podívat i obráceně. Já takto vlastně dávám k dispozici připojení k databázi/platební kartě/... komukoliv, aniž bych nad tím měl jakoukoliv kontrolu. V podstatě je to návrat ke global.

Proto se prosazuje vzor Dependency injection, pro rychlý přehled raději viz phpfashion.com/co-je-dependency-injection.

A k čemu to je? Nebudeme si žádné závislosti předávat skrytě, ale naopak zcela explicitně. Naše třída Cart bude v konstruktoru požadovat připojení k databázi, a pokud ho nedostane, vůbec se nevytvoří.
public function __constructor(DbConnection $db) { // vyžaduji instanci konkrétní třídy
  $this->db = $db;
}

A při volání se samozřejmě musí tato závislosti přímo předat.

$dbLogin = [];
$dbConnection = new DbDonnection($dbLogin);

$cart = new Cart($dbConnection); // jasně vidím, že třída Cart vyžaduje připojení k databázi
$myPrice = $cart->getPrice();

Přesně vím, co se tam děje, mám vzájemné závislosti pod kontrolou. Pokud se rozhodnu začlenit nový modul, stačí mi znát jeho rozhraní. Aplikace mi nespadne v runtime fázi na tom, že chybí nějaká statická závislost.
Tahle technika samozřejmě trochu zvyšuje pracnost při psaní, ale zjednodušuje čtení a chápání kódu. Frameworky to řeší tím, že mnohé třídy jsou konstruovány automaticky na základě konfigurace, ale to bych šel ještě dál...

Další čtivo k tomu, že singleton je anti-pattern na stackoverflow.com/a/138012.


A má mě to zajímat?
Když se podíváte na dibi (které všem doporučuji pro jako databázový layer), zjistíte, že všechny příklady používají statický registr (s dibi jde pracovat i bez něj). Podobně jako se zde zachovalo staré rozhraní dibi, byla v původních verzích Nette frameworku statická třída Environment, do které naházeli vše, co „někdo někde potřeboval“. Postupně přešli na předávání kontejneru (starý známý $context), což je další anti-pattern, tentokrát service locator, aby ho následně zavrhli a začali používat výhradně dependency injection.

Byly původní statické návrhy špatně? Ano byly, top framework tak fungovat nemohl. Jenom díky dependency injection bylo možné Nette rozbít rozdělit do oddělených komponent a vytvořit tak návrh afaik odpovídající nejmodernějším trendům přiravený na automatizované skládání aplikace z různých komponent a údržbu těchto závislostí (viz práce s Composerem).

Ne každý se na tuto úroveň dostane a hodně tvůrců webů zůstane spokojených někde napůl cesty. Dojít až na konec není úplně jednoduché.

Odkazovaný článek zůstává někde v první polovině cesty. Tím neříkám, že je špatný, jen že by po jeho pochopení měl následovat další krok.

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

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