Vázání proměnných v MySQLi

Školení, která pořádám

Na všech serverech mám k dispozici MySQL 4.1, většinou také PHP 5, rozhodl jsem se proto vyzkoušet MySQLi. Nejvíce mě na něm lákalo vázání proměnných, kterýžto koncept je mi mnohem sympatičtější než míchání SQL dotazu s daty se všemi problémy s tím spojenými.

Bohužel musím říct, že jsem poměrně zklamán. Porovnejte následující dva zápisy:

<?php
// tradiční přístup
$row = $mysqli->query("SELECT * FROM vyrobky WHERE skupina = " . intval($_GET["skupina"]))->fetch_assoc();
echo "<a href='?id=$row[id]'>" . htmlspecialchars($row["nazev"]) . "</a>\n";

// vázání proměnných
$stmt = $mysqli->prepare("SELECT id, nazev FROM vyrobky WHERE skupina = ?");
$stmt->bind_param("i", $_GET["skupina"]);
$stmt->bind_result($id, $nazev);
$stmt->execute();
$stmt->fetch();
$stmt->close();
echo "<a href='?id=$id'>" . htmlspecialchars($nazev) . "</a>\n";
?>

Vázání proměnných ani výsledků není možné najednou prostřednictvím pole jako třeba v PDO, takže pokud chci vytvořit trošku univerzálnější řešení, dá se leda tak použít funkce call_user_func_array. Typy se navíc nepřebírají z PHP jako v případě PDO, ale je nutné je explicitně uvést. To může mít své výhody, ale většinou to zápis pouze znepřehledňuje. Raději na patřičných místech použiji intval než luštit zápis sssiiiis. Obejít se nedá ani bez $stmt->close, protože před jeho zavoláním nelze tímto způsobem položit další dotaz (alternativně lze před fetch zavolat $stmt->store_result). Metoda $stmt->bind_param navíc přijímá parametry referencí, protože jejich hodnota se dosazuje až v okamžiku volání metody $stmt->execute. To považuji jednak za poměrně nepohodlné a jednak za výrazný WTF faktor.

Na můj vkus je tento přístup příliš nízkoúrovňový. Samozřejmě, dá se napsat patřičná obálka s rozhraním, které by mi vyhovovalo, tím se ale ztrácí kouzlo společného jazyka. Vzhledem k tomu, že nevyužiji ani většinu ostatních novinek, MySQLi asi vynechám… Funkce týkající se replikace by se možná hodily, ale celkem snadno se dají napsat i ručně.

Ještě mě zaujal přístup k různým hodnotám jako $stmt->insert_id. Ten není realizován metodami, ale vlastnostmi pouze pro čtení. To by se v PHP dalo zařídit asi jen přetěžováním:

<?php
class ReadOnlyProperties {
    public $readWrite;
    protected $vars = array('readOnly' => 5);
    
    function __set($name, $value) {
        throw new Exception("Read-only property.");
    }
    function __get($name) {
        return $this->vars[$name];
    }
}
?>

Přijďte si o tomto tématu popovídat na školení Návrh a používání MySQL databáze.

Jakub Vrána, Seznámení s oblastí, 29.3.2006, diskuse: 13 (nové: 0)

Diskuse

ŠZ:

a nezkoušel jste CREOLE?

Gagarin:

Přesně tak, rozumní lidé používají Creole :-). Já jsem si do něj ještě doplnil pár metod jako je getOne(), getRows() atd. (v podstatě jen copy and paste z Jargonu) a teď už to myslím nemá chybu. Jednak je to malinko rychlejší než AdoDB a hlavně tisíckrát přehlednější, navíc ti, kdo znají javovské JDBC se nemusí nic nového učit, těm ostatním to zabere jen chvilku.

legoxx:

No ja som si napisal nieco ako bolo DGXove slubovane DiBi, ked to ma k dispozicii DBO tak ho pouziva inac fallback na standartne mysql prikazy. Co som porovnaval creole tak velmi podobne len jednoduchsie.

Aleš Dostál:

I když je to možná trošku offtopic (ale nedá mi to, když vidím tady v příspěvcích to "creole"), tak v dnešních dnech řeším přístup k několika DB systémům (naštěstí pouze MySQL 5 a Oracle 9) jednotným způsobem. U jednoduchých web.aplikací, bych to asi příliš neřešil, ale když člověk má vytvářet intranet aplikaci, situaci to značně zkomplikuje ve chvíli, kdy má člověk data tahat z jiného DB systému.
Koukal jsem na několik Layers, jako CREOLE, PDO, apod., ale nenašel jsem nic, co by mi vyhovovalo. Každý z nich se snaží o co nejrychlejší a nejpohodlnější přístup, ale ve finále to spíše plete hlavu.
Jsem spíše zastánce znalosti stored procedure, triggers, ... čímž větší váhu převedu rovnou na DB což mi umožní rychlejší přechod na např. z PHP na Javu.
Proto jsem si tvořím vlastní DB Layer, kde se snažím vycházet z třídy mysqli. Tedy jinými slovy používám rozhranní (vzor Factory) pro volání třídy, ve které pro mysqli nic řešit nemusím a pouze přepisuji metody pro connect Oracle, kde navíc použiji ošetření connectu, execute, .....
Přijdou mi naprosto zbytečné některé fce, které danné Layers používají. Upřímně, většina lidí stejne používá základní metody jako: fetch_assoc, insert_id, apod. Zbytek je spíše tak dobrý k tomu, abych si programoval vlastního phpmyadmina :)
Tím mám vše pod svou kontrolou a pokud mi něco bude scházet, jednoduše si to do své třídy pro Oracle, doprogramuji.

ŠZ:

zdravím,

"Jsem spíše zastánce znalosti stored procedure, triggers"

mohu Vás uctivě poprosti o vysvětlení? (prosím :-)) S procedurama sem nepřišel do styku, takže to asi nic. Ale triggery jsem využíval pro udržení konzistence databáze tam, kde to jinak nešlo (typicky, že datum úmrtí nemůže být před datem narození) a nechtěl jsem to nechávat na aplikační logiku... jiný způsob využití mě ještě nenapadl... proto mi nejde do hlavy, proč triggery dáváte do souvislosti s využitelností db layeru...

pokud sem Vás nějako špatně pochopil, nezlobte se prosím :-) dobrou

Aleš Dostál:

Myslím to tak, že se snažím spíše programovat nad DB. Díky tomu nepotřebuji nějaký extra-super db layer, který by mi vracel potřebné informace pro další zpracování. Píšu zde, že jsem šel tou cestou, že si ten DB layout vytvořím sám.
Jinak řečeno, nač hledat v php fce, které mi dokáží z DB dostat maximum, když to nikdy nevyužiji?
Když mám možnost delat ošetření při mázání záznamů, záložní tabulky, uživatelsky definované fce, subselecty, použitelné pivotní tabulky.... zpracování rovnou nad DB, proč bych měl zatěžovat klienta (mám nyní na mysli PHP)?
Prolém je, že v MySQL 4, lidem asi nic nezbyde, ale je to škoda. Kdybych si měl vybrat mezi PosgreSQL, MySQL4, Firebird, na MySQL4 by padla volba jako na poslední možnost ;)

Aleš Dostál:

"Vzhledem k tomu, že nevyužiji ani většinu ostatních novinek, MySQLi asi vynechám..."
S tím nemůžu souhlasit. Když jsem se pokoušel používat stored procedure, měl jsem s klasickým rozhranním mysql dost potíže.
Sice se to dá vyřešit, ale rozhodně to není nic extra.
Navíc zápis:
<?php
$query
= "SELECT id, nazev FROM tabulka";
if (
$result = $mysqli->query($query)) {
   while ($row = $result->fetch_assoc()) {
      // vypis
   }
   $result->close();
}
$mysqli->close();
?>
Je rozhodně přívětivější. ;)

Kalda:

Kromě samotné realizace bych ještě přidal odkazy na benchmarky:
http://www.johnjawed.com/benchmarks/

honza:

Kdyz jsem poprve zahlid ze existuje PDO, zajasal jsem. Ovsem jasal jsem jen do chvile nez jsem ho videl. Doufal jsem ze to bude obdoba JDO, ale nema toho s tim mnoho spolecneho. Bohuzel.

kutny:

zdravim

po přečtení tohoto článku jsem zkoušel použít popisovaný postup na PDO a narazil jsem na malý problém:

Potřeboval bych místo klasického bindování do proměnných a až do property objektu:

<?php
$sql
->bindColumn('id', $id);
$sql->bindColumn('typ', $typ);
$sql->fetch();

$objekt->setId($id);
$objekt->setTypPluginu($typ);
?>

bindovat rovnou do property

<?php
# tohle je nesmysl - je jen pro představu
$sql->bindColumn('id', $objekt->setId($id));
$sql->bindColumn('typ', $objekt->setTypPluginu($typ));
$sql->fetch();
?>

Nevíte jak na to?

Díky

ikona Jakub Vrána OpenID:

Dá se použít <?php $sql->bindColumn('id', $objekt->id); ?> a přetěžování: http://www.php.net/manual/en/language.oop5.overloading.php.

kutny:

Ano. Overloading mě taky napadl, ale mám všechny třídy psány s klasickými getry a setry a overloading jsem kdysi používal, ale nesedl mi. Takže je to asi neřešitelné...

tomasr:

Proc toto nefunguje?

function overUzivatele($login) {
      $mysqli = Mysql::getInstance();
      $uzivatel = $mysqli->dbc->prepare("SELECT id_osoby FROM osoba WHERE login = ? LIMIT 1");
      [bold] $uzivatel->bind_param("s",$login); [/bold]
      $uzivatel->execute();
      $uzivatel->bind_result($id_osoby);
      $uzivatel->fetch();
      return $id_osoby;
}

proc to vyhazuje pořád nulu? problem je nekde vradku
$uzivatel->bind_param("s",$login);
ponevadz
var_dump($uzivatel->execute());
vypisuje false,pritom je to naprosto vporadku prece...kdyz ten login napisu primo anepouziju bind_param,tak to funguje a kdyz mam nejake cislo vparametru tak to taky funguje...podotykam že použitím LIKE místo rovná se se nic nemeni.

Vložit komentář

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

avatar © 2005-2018 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.