Autor Zpráva
Jcas
Profil *
1.Pročetl jsem si pravidla diskuse a přesto jsem se rozhodl tohle téma založit (kdyžtak ho smažte)
2."tlachy sem nepatří" Přesto bych rád, kdyby si se mnou někdo popovídal o OOP
(Je toto forum pouze na problémy, nebo i na diskusi)
3. Pročetl jsem všechny staré vlákna. (proč používat a proč ne už vím nazpaměť)
4. S tímto jsem se ještě nesetkal. Asi 5* jsem prostudoval seriál.
Mám pocit, že všemu rozumím, ale absolutně netuším, jak to aplikovat.
5. Každý někdy začínal (hlavní důvod, proč toto vlákno zakládám)

K oop jsem se dostal, když jsem řešil zpracování výstupu (buď echo, nebo $html pro pdf)
Z následujícího příkladu by Vám mělo být jasné, o co se pokouším, ale momentálně mám pocit, že k tomu neustále přistupuji špatně. (konstruktor na nic, dědičnost i polymorfizmus k nevyužití)
class Zobrazeni {
	#definice atributů
	var $text;
	var $nadpis;
	var $sql;
	
	#definice konstruktoru
	function Zobrazeni($t='') {
		$this->text = $t;
		}
		
	#metoda na zobrazení, nebo na $html pro pdf
	function pdf($t='') {
		if(!isset($_GET['pdf'])) {echo $t;}
		else {
			global $html;
			$html.=$t;
			}
		}
	#metoda pouze na zobrazení
	function zobraz($t='') {
		if(!isset($_GET['pdf'])) echo $t;
		}	
	
	#metoda na specifické části
	function vyber() {
		if($_GET['uzivatel']!='admin'||$_GET['zobrazit']=='vlastni') {	#obyč. uživatel
			$this->sql = '';
			$this->nadpis = 'Přihlášky uživatele'.$_SESSION['user'];
			}
		else {	#admin
			switch($_GET['zobrazit']) {
				case 'uzivatele':
					$this->sql = '';
					$this->nadpis = '';	#doplním
					break;
				case 'vse':
					$this->sql = ''; #doplním
					$this->nadpis = 'Přihlášky všech chovatelů';
					break;
				}
			}
		}
	}

$stranka = new Zobrazeni();
$stranka->vyber();
$stranka->pdf('<div id="zahlavi"><span id="jmeno">'.$stranka->nadpis.'</span>');
$stranka->zobraz('<span id="nabidka">
<a href="./uzivatel.php">
	<img src="http://www.zocschmoravskebranice.eu/spolecne_soubory/icony/setting.png" alt="setting" title="nastaveni" width="36" height="36" />
</a>
<a href="./login.php">
	<img src="http://www.zocschmoravskebranice.eu/spolecne_soubory/icony/exit.png" alt="exit" title="odhlásit" width="36" height="36" />
</a></span>');
$stranka->pdf('</div>');

Jak vidíte, jsou části kódu, které budou pouze zobrazeny, ale nebudou v pdf. Také jsou části kódu, které se mění dle $_GET. Napadlo mě, že je ideální přistupovat k nim, jako k jednotlivým objektům. Nicméně ve své třídě jeden jediný objekt-takže to dělám blbě.
Lamicz
Profil
Bohužel Tě musím zklamat, ale kód co píšeš je pro PHP 4, v PHP 5 je to (skoro) úplně jinak... Najdi si nějaký novější zdroj informací.
Tori
Profil
Jcas:
Na první pohled jsou v jedné třídě nesouvisející věci: Chcete výstup buď v HTML nebo PDF - jak s tím souvisí proměnná $sql? (spíš bych téhle třídě předala výstup modelu, pole dat z DB). Závislost na parametrech z URL a na globálních proměnných znamená, že třídu nemůžete bez úprav a kontroly celého kódu použít v jiném projektu.
Dala bych společné prvky do abstraktní třídy a od ní odvodila (jako specializaci) třídy pro různé typy výstupu, přibližně něco takovéhoto:
abstract class Vystup {
	
	protected $typ; /* tady si každý potomek nastaví svůj typ - HTML/PDF */
	protected $titulek='';
	protected $text='';
	
	/* společné pro všechny potomky */
	public function titulek($titulek) {
		$this->titulek = $titulek;
	}
	/* druhým parametrem můžete určit, že text se přidá jen do určitého typu výstupu */
	public function pridejText($text, $typVystupu=null)	{
		if ($typVystupu === null || $this->typ === $typVystupu)	{
			$this->text .= $text;
		}
	}
	
	/* tuhle metodu si potomci udělají po svém */
	abstract public function zobraz();
	
	/* vrací instanci zvoleného potomka */
	public static function typ($typ)	{
		$class = ucfirst(strtolower($typ)).'Vystup';
		if (class_exists($class, true))	{
			$tmp = new $class;
			if ($tmp instanceof self)	{
				return $tmp;
			} else {
				throw new InvalidArgumentException("Třída $class není můj potomek.");
			}
		} else {
			throw new InvalidArgumentException("Třída $class neexistuje.");
		}
	}
	
}

class PdfVystup extends Vystup {
	protected $typ = 'PDF';
	
	public function zobraz()	{
		echo $this->text;
	}
}

class HtmlVystup extends Vystup {
	protected $typ = 'HTML';
	
	public function zobraz()	{
		echo $this->text;
	}
}

/* pokus, jestli to funguje spravne: */
foreach (array('HTML', 'PDF') as $typ)	{

	$obj = Vystup::typ($typ);
	$obj->pridejText('spolecny text');
	$obj->pridejText('<br>text jen pro html', 'HTML');
	$obj->pridejText('<br>text jen pro pdf', 'PDF');
	
	echo "<hr>Zobrazuji $typ<br>";
	$obj->zobraz(); 
}
Tori
Profil
Ještě k tomu seriálu článků: V PHP5 je oop dost jiné, hlavně poslední dva díly seriálu (např. řízení přístupu k vlastnostem a metodám třídy) už pro tuto verzi neplatí (víc info např. manuál, popis změn PHP4 -> 5, ohledně kopie objektu vs. reference např. vlákno Jak zpřístupnit metodu). Tahle knížka o objektovém PHP mi připadá dobrá, nebo koukněte do vlákna Návody, články a knihy.
Jcas
Profil *
Tori mockrát děkuju. Jdu si prostudovat, co jsi vlastně udělala. A děkuju za odkazy.
Na první pohled zatím vidím, že v třídách máš opravdu pouze procesy.

Je mi jasné, že když tam vrazím podmínky na par. z URL, tak je to nepoužitelné jinde, ale měl jsem pocit, že když tyto podmínky jsou společné pro dvě věci, tak by se to dalo chápat jako určitá metoda zpracování.
Na začátku php mám tyto podmínky na vytvoření sql dotazu,, který se liší dle situace a na konci pro zpracování výstupu. Proto mě taky napadlo, že by to bylo jako sql=jeden objekt a výstup=druhý objekt.
Jcas
Profil *
Tu knihu si koupím. díky. Ale na začátek sem přidám dva odkazy, kde jsem našel pár odpovědí na to, co vidím výše.
http://interval.cz/clanky/oop-v-php-zaklady-oop/
A tohle je asi dobrý tahák
Jcas
Profil *
Nechci pouze okopírovat, co udělá někdo jiný - tím se nic nenaučím.

Jestli jsem to pochopil správně, tak řešení "Tori" vytváří 2 třídy a text paraelně.
To mi připadá zbytečné. Vystup bude buď a nebo, ale vždy pouze jenom jeden.
Tak jsem to zkusil trochu jinak. Vyřadil jsem tedy všechna $_GET ze třídy.

Typ výstupu stránky určím statickou vlastností. Typ vystupu daného řetezce předám jako par konstruktoru. Pro prvky, které nabívají různých hodnot mám asociativní pole (prvek['nadpis']='prihlášky uživatele....';).
A pro menu jsem si zkusil dědičnost.

class Vystup {
	#definice atributů
	public static $vystup='html';		#typ výstupu stránky (html, pdf)
	protected $typ;		#typ vystupu pro danou instanci (pouze pro html, pouze pro pdf, pro obojí)
	public $prvek;			#pole, pro rozdílné řetězce 
	
	#definice konstruktoru
	public function __construct($typ='') {
		$this->typ = $typ;	#$this->typ='html' Pouze pr html. Pouze pro pdf='pdf'', Pro obojí = '' 
		global $html;
		$prvek = array();
		}
	
	public function zobraz($t) {
		if(self::$vystup!='html'&&$this->typ!='html') {	$html.=$t;}
		elseif($this->typ!='pdf') {echo $t;}
		}
		
	public function vloz($key, $hodnota='') {
		$this->prvek[$key] = $hodnota;
		}
	}
	
class Menu extends Vystup {	#Potomek třídy Vystup
	public $polozka = array();
	
	public function __construct($typ='') {
		parent::__construct($typ);
		}
	
	public function vloz($hodnota='') {
		$this->polozka[]=$hodnota;
		}
		
	public function zobraz() {
		foreach($this->polozka as $hodnota) {
			parent::zobraz($hodnota);		#volání rodičovské metody
			}
		}
	}

if(isset($_GET['pdf'])) {Vystup::vystup='pdf';}
$stranka = new Vystup();	#pro oba vystupy
$hlavicka = new Vystup('html');	#pouze pro html
$menu = new Menu('html');
...
$stranka->vloz('nadpis', 'Přihlášky uživatele '.$_SESSION['user']);
$stranka->zobraz('<div id="zahlavi"><h3><span id="jmeno">'.$stranka->prvek['nadpis'].'</span></h3></div>');
...
$menu->vloz('<p>nabídka</p>');
...
$menu->zobraz();


Zjišťuju, že u oop se blbě testuje funkčnost.
Mám to prosím správně - hlavně tu dědičnost????
Mastodont
Profil
Možná to funguje, ale logicky správně to není.
public static $vystup='html';

- $vystup nemusí být static, protože normálně vytváříš instance
global $html;

- global je ZLO, navíc zde tento řádek vůbec nic nedělá, dá se vypustit

- proč máš dvě vlastnosti pro typ výstupu?

A mimo OOP se mi vůbec nelíbí to míchání HTML značek a textu, na to jsou šablony.
Joker
Profil
Jcas:
Jedno z pravidel objektově orientovaného návrhu: Žádné globální proměnné.
(Navíc objekt používající globální proměnné nemůže být uzavřený vůči čemukoliv jinému co do nich zapisuje a tedy porušuje open-closed principle)
Připomínám, že i $_GET a $_SESSION jsou globální proměnné, takže by se uvnitř tříd neměly používat (třída pak není znovupoužitelná v aplikaci která má jiné GET parametry).

A s návrhem podle [#7], co se stane když v budoucnosti přibude další typ výstupu? Třída Vystup se bude muset přepsat (minimálně metoda Zobraz, pokud ten výstup bude vyžadovat nějaké nové vlastnosti, tak i zbytek třídy).
Což opět porušuje open-closed principle: „Uzavřené pro změny, otevřené pro rozšíření“ (dá se to taky napsat jako: „Třída by měla být otevřená pro rozšíření své funkčnosti, přičemž to ale nesmí znamenat změnu zdrojového kódu samotné třídy“).
Sice žádnou (netriviální) třídu nelze uzavřít stoprocentně, ale zrovna přidání nového typu výstupu je věc vůči které by ta třída uzavřená být měla.
Kód od Tori to splňuje, přidání nového typu výstupu znamená vytvoření nové odvozené třídy, aniž by se jakkoliv zasáhlo do těch už existujících.

řešení "Tori" vytváří 2 třídy a text paraelně.
To mi připadá zbytečné. Vystup bude buď a nebo, ale vždy pouze jenom jeden.
Skutečně?
Cituji komentář z řádku 9 kódu:
$this->typ='html' Pouze pr html. Pouze pro pdf='pdf'', Pro obojí = ''
Takže se zjevně počítá s tím, že výstup může být PDF i HTML.
Mastodont
Profil
Joker:
$_GET a $_SESSION jsou globální proměnné, takže by se uvnitř tříd neměly používat
To nedodržuje ani ZF, viz Http.php ve větvi Controller/Request. DI s požadavkem předávat naprosto vše přes parametry je zrcadlově převrácený global. Zlo jako zlo.
Joker
Profil
Mastodont:
Jasně, ono v PHP se tomu asi vyhnout nedá, pokud aplikace má pracovat se session nebo nějakými vstupy zvenku.
Taky je to formulované méně razantně než ty předchozí vyjádření Žádné globální proměnné a global je ZLO.

Zkusím to formulovat lépe: V PHP superglobální pole uvnitř třídy budiž pokud se tomu nedá vyhnout, ale rozhodně ne víc než je nezbytné.

Konkrétně v [#1]:
    function zobraz($t='') { 
        if(!isset($_GET['pdf'])) echo $t; 
        }     
to jde jednoduše přepsat například na:
    function zobraz($t='', $typ) { 
        if($typ == 'pdf') echo $t; 
        }     
(resp. jelikož se to používá na více místech převést na atribut třídy) a hned je třída lépe zapouzdřená.
Jcas
Profil *
global $html mám pro $mpdf->WriteHTML($html);
Děkuju za informaci - nepoužívat. To vím jak předělat.

$_GET a $_SESSION jsou globální proměnné, takže by se uvnitř tříd neměly používat
To platí pro i pro tohle???
$stranka->vloz('nadpis', 'Přihlášky uživatele '.$_SESSION['user']);


Nechcu se hádat-vy máte znalosti-chcu to pochopit.
A s návrhem podle [#7], co se stane když v budoucnosti přibude další typ výstupu? Třída Vystup se bude muset přepsat.
Na to je přeci dědičnost. Nějakou metodu přepíšu.

- proč máš dvě vlastnosti pro typ výstupu? Nechápu.
static $vystup - pro typ výstupu stránky. Buď a nebo Buď pouze html-echo, nebo pouze pdf - $html
$this->typ - je pro text. Ten může být společný, nebo pouze pro jedno. Jako například menu se nikdy nebude exportovat do pdf.

Říkal jsem si, že když pomocí statického atributu určím druh výstupu, tak potom mohu vytvářet jaké chcu instance a každou zpracuji dle potřeby na základě toho, jaký chcu výstup.
U tori opravdu vidím zdvojení. Text pro obojí vloží do dvou tříd. Vytváří dvě třídy a je jí jedno, jestli potřebuji pouze jednu z nich.

Můj první pokus (bez statické vlastnosti) kolaboval na tom, že při pdf se mi provádělo jak připsání k $html, tak echo. A nenašel jsem způsob, jak udělat, aby se při pdf echo neprovedlo.
Tori
Profil
Jcas:
global $html mám pro $mpdf->WriteHTML($html);
To echo v PdfVystup::zobraz() bylo jen pro potřeby toho testu, může tam klidně být return $text, anebo odeslání PDF souboru ke stažení.
Předpokládala jsem, že třída PdfVystup bude fungovat i jako adaptér pro konkrétní třídu na generování PDF (např implemenací rozhraní s metodami typu vlozObrazek, vlozTabulku apod.). Takže místo globálního objektu $mpdf by se používaly jen veřejné metody třídy PdfVystup - v případě, že byste mPDF vyměnil za třeba TCPDF, tak si jen přepíšete soukromé metody třídy PdfVystup, ale její rozhraní se nijak nezmění. edit: Vlastně asi by byl lepší postup v takovém případě používat adaptéry mezi PdfVystup a mPDF/TCPDF/..., jako např.dibi drivery pro různé typy databází.

U tori opravdu vidím zdvojení. Text pro obojí vloží do dvou tříd. Vytváří dvě třídy a je jí jedno, jestli potřebuji pouze jednu z nich.
Kde a co přesně zdvojuji?
Jcas
Profil *
Tori:
Jsem blb. To zdvojování je zřejmě pouze pro potřeby testu a v praxi to nebude.
$obj = Vystup::typ('PDF');

Takže Vaše třída by se použila stylem:
$mpdf->WriteHTML($obj->zobraz());


Tedy i druhý pokus je naprosto na ho... (on už je teda asi 10-tý) a mám (s)prostě okopírovat Tori?
Joker
Profil
Jcas:
To platí pro i pro tohle???
To záleží na tom kde to bude.
Ve třídě generující výstup by to rozhodně být nemělo, protože není dobré aby třída generující výstup přímo závisela na způsobu práce s uživateli.
V samotné „hlavní stránce“ která ty funkčnosti volá by to asi bylo přípustné.

U tori opravdu vidím zdvojení. Text pro obojí vloží do dvou tříd. Vytváří dvě třídy a je jí jedno, jestli potřebuji pouze jednu z nich.
Když potřebuji jednu z nich, vytvořím si jednu.
Když potřebuji obě, vytvořím si obě.
Tori
Profil
Jcas:
To zdvojování je zřejmě pouze pro potřeby testu
Jo tohle! No ano, řádky 55-64 byly jen pro ukázku, jak se ty třídy chovají s různým nastavením. Proto i to echo ve výstupních funkcích, obvyklejší by bylo (asi i u HTML) použít return.
Jcas
Profil *
Tak stále se mi nedaří. Vzniká mi z toho směs nepřehledného kódu. V podstatě řeším kravinu, ale chěl bych to udělat šikovně..
1. Jestli někde musím použít podmínku - if(isset($_GET[..])) - tak abych ju už nemusel použít nikdy znova
2. Jestliže už někde vytvořím string-vystup, tak abych ho nemusel nikde duplikovat.


A to mám při tom blbé dva výstupy.
Mám pocit, že oop je na to ideál, ale nejsem schopen udělat strukturu.
Momentálně jsem se zasekl na editačních prvcích, které jsou pouze pro admina a přihl. uživatele (colspan= jednou 8, jednou 7) a také pozadí <tr>, které je pouze u zobrazení.
Takových odlišných prvků je spousty a to je problém. Ze samotného výstupu mi vzniká absolutní chaos.

Admin - pravo zobrazit vlastní, upravit - právo zobrazit jednotlivce a upravit - právo zobrazit vše a upravit, právo zobrazit dle selekce. Pdf pro tisk bez zbytečností okolo
přihl.už - právo zobrazit vlastní a edit, právo zobrazit vše bez editu, právo zobrazit něco dle selekce bez editu a pdf pro tisk.
nepř.už - právo zobariz dle vše a dle selekce bez editu. Pdf pro tisk.

Zatím mi vždy pomohl vývojový diagram, ale já už počmáral asi 10 papírů a stále na nic.

ps. Vím že je to skoro zakázka pro výdělečnou činnost, ale zde bohužel cesta neveda. Já sám to dělám zadarmo z dobré vůle pro neziskovou org. Kdo již odpovídal, tak asi dobře tuší, o co mi jde. Jakou strukturu zvolit? Tori mi napsala (děkuji), jak rozdělit pdf od html, ale to prolínaní v jednotlivých strig je úděsné.
Tori
Profil
Jcas:
Možná na to jdu z opačného konce, než na co se ptáte, ale: třeba by šlo začít tím, že si některé části kódu "uklidíte" do tříd. Z toho, co zmiňujete, by to určitě bylo ověřování uživatelů, autorizace uživatelů, nějaká DB vrstva*, a šablony pro výstup (třeba úplně jednoduše napsané v PHP, ale tak, aby bylo možné jim jen předat velké pole dat a nechat je vykreslit). Nakonec by vám měla zbýt aplikační logika - teda jak na který požadavek uživatele reagovat, pak kód, obstarávající jednotlivé akce, a různá nastavení. S tím by se už mělo dát přehledněji pracovat, než s tisíci řádků naráz.
(Ohledně celkové architektury, jestli použít MVC vzor nebo co jiného, si radit netroufnu.) edit: anebo jste nemluvil o celkové struktuře, ale o tom, jak vyřešit výstup, který je ovlivněný kopcem různých podmínek? Teď si nejsem jistá, zda jsem správně pochopila.

* resp.alespoň dvě vrsty - jedna s metodami typu načtiČlánky, uložProfilUživatele, a druhá, která bude zapouzdřovat použitý typ databáze, s metodami typu query, fetchRow, fetchAllRows, lastInsertId atd.

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:

0