Místo pro SQL dotazy – PHP – Fórum – Programujte.com
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

Místo pro SQL dotazy – PHP – Fórum – Programujte.comMísto pro SQL dotazy – PHP – Fórum – Programujte.com

 

Nickname
~ Anonymní uživatel
5 příspěvků
20. 6. 2015   #1
-
0
-

Ahoj,

chci se zeptat kam vkládáte veškeré SQL dotazy, pokud pracujete s nějakým MVC. Je správný způsob, pokud si pro každý Controller vytvořím samostatnou třídu, která se stará pouze o SQL dotazy, které je možné používat pouze na daném Controlleru? Validace a podobné věci se zase provádí ve zcela jiné třídě. Také mě zajímá, jak takové třídy s SQL dotazama pojmenovat a jestli k nim postavit nějaké rozhraní či ne. Neměl byste někdo  nějaký pěkný příklad či správný postup kam umístit SQL dotazy?

Děkuji za odpověď.

Nahlásit jako SPAM
IP: 86.49.47.–
Kit+15
Guru
20. 6. 2015   #2
-
0
-

#1 Nickname
Pro každou doménu mám samostatný controller v jedné třídě. Validaci dělám v konstruktoru objektu s daty, která injektuji do modelu. Mám v něm i potřebné SQL dotazy pro manipulaci s databází, které model volá.

A konvence? Controllerům dávám příponu _POST. View má příponu _GET. To je podle metody, kterou se volají z prohlížeče. Třída, ve které mám SQL dotazy a která drží doménová data, příponu nemá.

Uvažuji o změně, že jednotlivé domény rozdělím do různých namespace. V každé bude samostatné MVC, které se bude starat pouze o svou doménu. Domény se mohou překrývat. Ještě však nemám dořešeny všechny potřebné detaily.

Nahlásit jako SPAM
IP: 2a00:1028:83a0:37a6:221:5...–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
Nickname
~ Anonymní uživatel
5 příspěvků
20. 6. 2015   #3
-
0
-

#2 Kit
Myslíš, že bys mohl nadhodit jednoduchý kód, ve kterém bys uvedl komentáři, kde co děláš? Špatně se mi to představuje. Děkuji

Nahlásit jako SPAM
IP: 86.49.47.–
Kit+15
Guru
21. 6. 2015   #4
-
0
-

#3 Nickname 

<?php
$db = new MyPDO(...);
$model = new Model($db);
echo factory($model, $method, parseURL($url));

Ve funkci factory() se podle metody a URL vyrobí potřebný view nebo controller a předá se mu model. Typický controller:

class Clanek_POST extends Controller {
    function __construct(Modelable $model, $atributy) {
        parent::__construct($model, $atributy);
        if (isset($_POST['Update'])) {
            $model->update(new Clanek($_POST));
            unset($_SESSION['stroj']);
            header('Location: /NastaveniClanku/');
            exit;
        }
        if (isset($_POST['Delete'])) {
            $model->delete(new Clanek($_POST));
            unset($_SESSION['stroj']);
            header('Location: /NastaveniClanku/');
            exit;
        }
        if (isset($_POST['Insert'])) {
            $model->insert(new Clanek($_POST));
            unset($_SESSION['stroj']);
            header('Location: /NastaveniClanku/');
            exit;
        }
        header('Location: /NastaveniClanku/');
        exit;
    }
}

A ještě viewer: 

class Clanek_GET extends Viewer {
    function __construct(Modelable $model, $atributy, $template = 'clanek.xsl') {
        parent::__construct($model, $atributy, $template);
        $this->clanek = $this->mkElement('clanek');
        $this->add($this->clanek);
    }   
}
Nahlásit jako SPAM
IP: 2a00:1028:83a0:37a6:221:5...–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
Nickname
~ Anonymní uživatel
5 příspěvků
21. 6. 2015   #5
-
0
-

#4 Kit
Děkuji za vyčerpávající odpověď. Pokud tomu rozumím, tak ke každému controlleru máš připojený daný model, který se stará o validaci a zároveň o SQL dotazy na daném controlleru?

Nahlásit jako SPAM
IP: 86.49.47.–
Kit+15
Guru
21. 6. 2015   #6
-
0
-

#5 Nickname
Nějak jsem zapomněl, co je to validace :-)

Ta doménová komponenta modelu vypadá ve zkrácené verzi takto: 

<?php
class Clanek implements Insertable, Selectable {
    private $post;
    const select = "SELECT * FROM clanek WHERE id=?";
    const insert = "INSERT INTO clanek (title, content) VALUES (?, ?)";
    function __construct(array $post) {
        $this->post = $post;
    }

    function select(MyPDO $db) {
        try {
            $select = $db->prepare(self::select);
            $select->execute(array($this->post['id'] + 0));
            if ($select->rowCount() == 0) {
                throw new ModelException('Clanek nenalezen');
            }
            return $select->fetch(\PDO::FETCH_OBJ);
        } catch (\PDOException $e) {
            throw new ModelException(__METHOD__ . $e->getMessage());
        }
    }

    function insert(MyPDO $db) {
        try {
            $insert = $db->prepare(self::insert);
            $insert->execute(array(
                $this->post['title'],
                $this->post['content']
            ));
        } catch (\PDOException $e) {
            throw new ModelException(__METHOD__ . $e->getMessage());
        }
    }
}
Nahlásit jako SPAM
IP: 2a00:1028:83a0:37a6:221:5...–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
Nickname
~ Anonymní uživatel
5 příspěvků
21. 6. 2015   #7
-
0
-

#6 Kit
Takže ty ke controlleru Clanek_POST stavíš třídu s SQL dotazy Clanek. Třída Clanek poté obsauje všechny dotazy, které je možné zavolat v Clanek_POST?

Mě trápí ta věc, že před tím, než si vyrobím třídu s SQL dotazy si musím uvědomit pro co to vlastně budu psát. Budu ty SQL psát pro daný kontroller? Pro danou tabulku, kde mi vznikne spousta dotazů? Nevím a s tím potřebuji poradit.

Nahlásit jako SPAM
IP: 86.49.47.–
Kit+15
Guru
21. 6. 2015   #8
-
0
-

#7 Nickname
Těch dotazů obvykle moc není - select, insert, update a delete. Těch controllerů mám samozřejmě víc, takže pokud pracuji třeba se seznamem nebo skupinou článků, je to v další doméně, např. Clanky. Samozřejmě i doména Autor je samostatně, Hodnoceni také.

SQL dotazy jsou nezávislé na controlleru. Jsou však závislé na použité databázi a její struktuře. Controller ani neví, s jakou pracuje databází nebo jestli pracuje s databází - o její struktuře neví vůbec nic. O vieweru platí totéž.

Nahlásit jako SPAM
IP: 2a00:1028:83a0:37a6:221:5...–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
Nickname
~ Anonymní uživatel
5 příspěvků
21. 6. 2015   #9
-
0
-

#8 Kit
Zajisté, tomu rozumím. Ale předtím, než budeš psát třídu, která se ti stará data - ať už bere data z databáze nebo někde z nějakého souboru, musíš vědět pro koho bude tato třída určena - neboli kdo jí bude používat. Pokud se v tvém případě budeme bavit o controlleru Clanek, který uživatel může zobrazit či upravit, tak by bylo dobré mít třídu, která bude pracovat s daným úložištěm (databáze, či něco jiného) a která bude mít metody jako select nebo update.

Dále, co když budu mít controller AdministraceClanku, kde budu potřebovat článek zobrazit, upravit a ještě k tomu smazat. Co to pro mě teď znamená? Pokud bych si zase vytvořil čiště novou třídu, která se mi bude starat o select, update, delete, měl bych duplicitní kód.

Nahlásit jako SPAM
IP: 86.49.47.–
Kit+15
Guru
21. 6. 2015   #10
-
0
-

#9 Nickname
Clanek není controller, ale servisní vrstva, která pracuje s úložištěm.

Controller se jmenuje Clanek_POST. Umí článek vložit, změnit nebo smazat. Neumí ho zobrazit - to je práce pro viewer Clanek_GET.

Pokud sis nevšiml, tak administraci tam už mám. Vlastně ten controller nedělá nic jiného.

Nahlásit jako SPAM
IP: 2a00:1028:83a0:37a6:221:5...–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
peter
~ Anonymní uživatel
4013 příspěvků
22. 6. 2015   #11
-
0
-

Je dobre si udelat tridu, ktera se stara jen o sql. Kdyz uvazis kolik tech sql ruznych je, abys to nemusel pozdeji prepisovat v celem programu.

Nahlásit jako SPAM
IP: 2001:718:2601:1f7:a9f9:41...–
Kit+15
Guru
22. 6. 2015   #12
-
0
-

#11 peter
To se mi právě neosvědčilo. Odtrhne se tím deklarace SQL dotazu od jeho použití a při úpravách je nutné paralelně pracovat se dvěma soubory.

Zkoušel jsem i uložení SQL dotazů v databázi. Fungovalo to pěkně, ale vadilo mi totéž. Uložení SQL dotazů do servisní vrstvy se mi jeví jako nejlepší varianta ze všech, které jsem testoval.

BTW: Ta třída v servisní vrstvě se stará jen o SQL.

Nahlásit jako SPAM
IP: 2a00:1028:83a0:37a6:221:5...–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
peter
~ Anonymní uživatel
4013 příspěvků
22. 6. 2015   #13
-
0
-

Nn, ja nemyslim odtrhnout dotaz z mista pouziti. Jen si proste na sql postavit vlastni tridu. Jestli je uvnitr prikaz pdo nebo mysql je pak jedno. Kdyz to budes chtit pak upgradovat, tak staci prepsat tu tridu. Sice je to mozna trochu pracnejsi to odladit nez resit na miste, ale jevi se mi to elegantnejsi.
Treba, ja ted mam neco takoveho. Neni to asi jeste vsechno vyladne. Ale, kde to pouzivam, tam mi to zatim staci. 

<?php
class classSql
{
public $conn  = null;
//public $db    = null;
public $sth = null;
public $sth_exec = null;
private $cfg = null;
//private $log = null;

	function __construct($params)
	{
	$this->createConfig($params);
	$this->connect();
	$this->query("SET NAMES utf8");
//	$this->log = array(
//		'logdate'=>'logdate',
//		'loguser'=>'loguser'
//		);
	}

	function __destruct()
	{
	$this->disconnect();
	}

	function createConfig($params)
	{
	$cfg = array(
		'host'  => '',
		'user'  => '',
		'psw'   => '',
		'db'    => '',
		'debug' => false
		);
	foreach($cfg as $key=>$value)
		{
		$cfg[$key] = isset($params[$key]) ? $params[$key] : $value;
		}
	//$cfg['query'] = ;
	$this->cfg   = $cfg;
//	if ($this->cfg['debug'])
//		{
//		$this->query = function ($query) {$this->queryDebug($query);};
//		}
//	else	{
//		$this->query = function ($query) {$this->querySafe($query);};
//		}
//var_dump($this->query);
//	return (!$this->cfg['debug']) ? $this->query1($query) : $this->query2($query);

//echo $this->query = $this->($this->query);
//	$this->query = $this->{$this->query};
//($query);
//function($query) {}
	}

	function connect()
	{
//phpinfo();
	$this->conn = new PDO('mysql:host='.$this->cfg['host'].';dbname='.$this->cfg['db'], $this->cfg['user'], $this->cfg['psw'])
			or $this->error();
//	$x = $this->cfg['query'];
//	$x = $this->{$x};
//	$x($query);
	}

	public function disconnect()
	{
	if (isset($this->conn))
		{
		$this->conn = null;
		}
	}  

//	function free($result)
//	{
//	mysql_free_result($result);
//	}

	public function query($query)
	{
//	$x = $this->cfg['query'];
//	$x = $this->{$x};
//	$x($query);
	if ($this->cfg['debug'])
		{
		return $this->queryDebug($query);
		}
	else	{
		return $this->querySafe($query);
		}
	}

	public function querySafe($query)
	{
	$this->sth = $this->conn->prepare($query);
	$this->sth_exec = $this->sth->execute();
	return $this->sth;
	}

	public function queryDebug($query)
	{
	echo "\n<div class=\"query\">query = ".$query."</div>";
	$this->querySafe($query)
		    or die('Chyba pdo_query: ' . $this->sth->errorCode().' '.implode(" - ",$this->sth->errorInfo()));
	}

	public function fetch($sth=null)
	{
//	var_dump( $this->sth->fetch(PDO::FETCH_ASSOC));	//$sth
	$sth = !$sth ? $this->sth : $sth;
	return $sth->fetch(PDO::FETCH_ASSOC);	//$sth
	}

//	public function numRows($sth)
//	{
//	return $sth->query('SELECT FOUND_ROWS()')->fetchColumn();
//	}

	public function createInsert($_table,$_data,$_idname='')
	{
	if ($_idname!='' && isset($_data[$_idname])) {unset($_data[$_idname]);}
	$keys   = array();
	$values = array();
	foreach ($_data as $key=>$value)
		{
		$keys[]   = $this->escapeKey($key);
		$values[] = $this->escape($value);
		}
	return "INSERT INTO ".$this->escapeKey($_table)." (".implode(", ",$keys).") VALUES (".implode(", ",$values).")";
	}

	public function createUpdate($_table,$_data,$_idname)
	{
	$_id    = array($_idname=>$_data[$_idname]);
	unset($_data[$_idname]);
	$values = array();
	$where  = array();
	foreach ($_data as $key=>$value)
		{
		$values[] = $this->escapeKey($key)."=" . $this->escape($value);
		}
	foreach ($_id as $key=>$value)
		{
		$where[] = $this->escapeKey($key)."=" . $this->escape($value);
		}
	return "UPDATE ".$this->escapeKey($_table)." SET ".implode(", ",$values)." WHERE ".implode(' AND ',$where);
	}

	public function createDelete($_table,$_id)
	{
	$where  = array();
	foreach ($_id as $key=>$value)
		{
		$where[] = $this->escapeKey($key)."=" . $this->escape($value);
		}
	return "DELETE FROM ".$this->escapeKey($_table)." WHERE ".implode(' AND ',$where);
	}

	public function escape($value='')
	{
	return $this->conn->quote($value);
	}

	public function escapeKey($value='')
	{
	return "`".str_replace("`","``",$value)."`";
	}

	public function escapeSelect($value='')
	{
	$m = strlen($value);
	$value = preg_replace('~UPDATE|REPLACE|DELETE|CREATE~i','',$value);
	$n = strlen($value);
	return $m==$n ? $value : '';
	}

};

/*
$sql = new classSql(array(
	'host'  => 'localhost',
	'user'  => 'root',
	'psw'   => '',
	'db'    => 'urb_news',
	'debug' => true
	));
*/
?>
Nahlásit jako SPAM
IP: 2001:718:2601:1f7:a9f9:41...–
Kit+15
Guru
22. 6. 2015   #14
-
+1
-
Zajímavé

#13 peter
Promiň, ale máš tam hromady skrytých závislostí. Z mého pohledu je to nepoužitelné. Vždyť se ta třída ani nedá testovat. Navíc ta třída ani ty SQL dotazy nijak nezpracovává a nutíš volajícího, aby znal strukturu tabulek.

Nahlásit jako SPAM
IP: 147.229.242.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
peter
~ Anonymní uživatel
4013 příspěvků
22. 6. 2015   #15
-
0
-

KIT - Vsechno spravne.
Prvni pouziti pdo, takze zatim nemam moc prehled, jak napsat, co potrebuji a jak to scrozumitelne pojmenovat.
Ano, volajici musi znat strukturu. Tudiz to muze byt pro kazdy program trochu jinaci, treba, kdybych chtel logovat automaticky, kdo ten radek menil a tak. to byl ucel. A taky, aby se to dalo zamenit mysql prikazy. Z meho pohledu si PDO vymyslelo strasne nesrozumitelnou syntaxi, zvlast pro novacka. Nevim, co je zkratka SMTH, ale vim, co je slovo result nebo handle.

Nahlásit jako SPAM
IP: 2001:718:2601:1f7:a9f9:41...–
Kit+15
Guru
22. 6. 2015   #16
-
0
-

#15 peter
V pohodě. Dlouho jsem laboroval s tím, aby model nebyl příliš velký. Proto jsem ho rozdělil do domén. A protože ty domény potřebovaly konstruktor, udělal jsem ho co nejjednodušší - nacpal jsem do objektu vstupní data. V této konstrukci už nejsem omezen počtem tabulek v DB ani počtem domén v modelu, ale přitom velikost jednotlivých souborů zpravidla nepřekročí mých oblíbených 65 řádek. Zároveň si neblokuji možnost rozšíření na další databázové stroje, např. PostgreSQL, SQLite, DB4, filesystém apod.

V mém případě volající nemusí znát strukturu databáze. Stačí vědět, jakou strukturu mají mít data, která mohou být ve stromové struktuře a mohou být určena pro naplnění více tabulek.

Pokud by se mělo logovat, stačí tuto funkci aktivovat v modelu. Obvykle to však není nutné, protože mé databáze se logují samy nezávisle na aplikaci. Zalogují se tedy i změny udělané přes PMA.

PDO jsem dlouho nemohl přijít na chuť. Až do doby, kdy jsem pochopil, že umí vytvářet virtuální procesory pro práci s daty. Od té doby na PDO nedám dopustit.

Nevím, co je SMTH - snad STMT - statement. Nejsem příznivcem takových zkratek a nepoužívám je.

Nahlásit jako SPAM
IP: 147.229.242.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
Zjistit počet nových příspěvků

Přidej příspěvek

Toto téma je starší jak čtvrt roku – přidej svůj příspěvek jen tehdy, máš-li k tématu opravdu co říct!

Ano, opravdu chci reagovat → zobrazí formulář pro přidání příspěvku

×Vložení zdrojáku

×Vložení obrázku

Vložit URL obrázku Vybrat obrázek na disku
Vlož URL adresu obrázku:
Klikni a vyber obrázek z počítače:

×Vložení videa

Aktuálně jsou podporována videa ze serverů YouTube, Vimeo a Dailymotion.
×
 
Podporujeme Gravatara.
Zadej URL adresu Avatara (40 x 40 px) nebo emailovou adresu pro použití Gravatara.
Email nikam neukládáme, po získání Gravatara je zahozen.
-
Pravidla pro psaní příspěvků, používej diakritiku. ENTER pro nový odstavec, SHIFT + ENTER pro nový řádek.
Sledovat nové příspěvky (pouze pro přihlášené)
Sleduj vlákno a v případě přidání nového příspěvku o tom budeš vědět mezi prvními.
Reaguješ na příspěvek:

Uživatelé prohlížející si toto vlákno

Uživatelé on-line: 0 registrovaných, 4 hosté

 

Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032024 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý