Co je nového v NotORM

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

Knihovnu pro snadný přístup k datům z databáze NotORM vyvíjím přesně podle hesla „release early, release often“. Nové verze ani nijak nečísluji a vždy po odladění nějaké funkčnosti knihovnu pouze vystavím označenou datem vydání. Od prvního představení přibylo několik novinek:

Modifikace dat

Asi nejzásadnější novinkou je možnost vkládat, upravovat a mazat stávající data. Tuto funkčnost jsem doplnil krátce po představení knihovny, ale dlouho si žila v samostatné branchi. Považuji ji nicméně za užitečnou a s API jsem spokojen, takže jsem ji zahrnul i do hlavní verze. API je opět jednoduché:

<?php
// vložení záznamu
$id = $db->application()->insert(array(
    "author_id" => $software->author[12],
    "published" => new NotORM_Literal("NOW()"),
    "slogan" => "The best humane Web text generator",
));

// modifikace a smazání všech vrácených řádek
$affected = $db->application()->update(array("web" => ""));
$affected = $db->application()->delete();

// modifikace podle skutečně provedených změn
$application->web = "";
$application->update();
?>

Stejně jako při získávání dat je velký důraz kladen na výkonnost – dotazy nad celým výsledkem zbytečně nekladou SELECT a modifikace přenáší jen ty sloupce, které byly skutečně změněny.

Ještě důležitější je bezpečnost – všechna data jsou automaticky ošetřena s výjimkou objektu třídy NotORM_Literal. Původně se používalo pole (inspiroval jsem se způsobem předáváním metod v PHP), to by ale mohlo způsobit riziko.

Metodě insert lze předat i řetězec nebo výsledek dotazu, což se dá použít pro vložení více záznamů najednou. Jde ale spíše o experimentální vlastnost.

Zakázání modifikace dat

Modifikace dat by rozhodně neměla být k dispozici třeba v šabloně, proto je možné ji snadno zakázat. Původně to šlo pro každý výsledek zvlášť, ale pro to jsem nenašel rozumné uplatnění, proto jde nyní o volbu společnou pro celý objekt reprezentující databázi. API je jednoduché:

<?php
$db->freeze = true;
?>

Vlastnost je určena pouze pro zápis, protože pokus o čtení vrátí data ze stejnojmenné tabulky.

Tvorba rozšíření

O možnosti specifikovat vlastní třídu pro řádky vracené z databáze už jsem psal. Připomenu, že pomocí této funkčnosti lze nad NotORM postavit další abstrakci použitelnou třeba pro přímočaré získávání dat z překladových tabulek. Marek Lichtner připravil navíc verzi, která do těchto tabulek dovoluje i snadno ukládat. API se nakonec ustálilo takto:

<?php
$db->rowClass = 'ClassName'; // potomek NotORM_Row
?>

Agregace

Řádky výsledku lze snadno agregovat:

<?php
list($count, $max_published) = $db->application()->group("COUNT(*), MAX(published)");
?>

Druhým parametrem lze specifikovat i podmínku pro skupiny (HAVING).

Možnost ladění

Při vývoji mě samozřejmě eminentně zajímá, jaké dotazy se databázi vlastně pokládají. Původně jsem to řešil zakomentovaným kódem, pokud si ale výsledek chcete poslat třeba do Laděnky, tak je potřeba sofistikovanější mechanismus:

<?php
$db->debug = function ($query, array $parameters) {
    // tady bude zalogování dotazu a parametrů
};
// před PHP 5.3 lze předat i konvenční callback
?>

Pro vlastní ladění jsem si nechal možnost nastavit tuto vlastnost na true, což pošle dotaz na chybový výstup.

Podpora databází

NotORM jsem úspěšně vyzkoušel na Oracle. Metodu limit jsem upravil tak, aby respektovala odlišnosti jednotlivých databází, nefunguje pouze $offset na MS SQL. Pro složitější podmínky jsem přidal podporu poddotazů, které se v MySQL kvůli výkonnosti materializují.

Další vývoj

Momentálně mi nic nechybí a knihovnu považuji za hotovou. Občas se mě někdo zeptá, jestli bych do verze pro Dibi nechtěl doplnit podporu pro ukládání, ale do toho se mi moc nechce. Jednak mi přijde ukládání v Dibi samo o sobě dost jednoduché a jednak jsem zvědav, jestli se něco začne dít na druhé straně hřiště.

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

Diskuse

jozko:

moze mi niekto povedat, ze preco niekto pouziva na mena tried, funkcii atd. KazdeDalsieSlovoVelke a niekto kazde_dalsie_slovo_velke ?

php pouziva oddelovanie pomocou podciarkovnikov, nepride Vam lepsie pouzivat prave to?

vyhody/nevyhody?

dakujem

ikona Jakub Vrána OpenID:

Asi nejrozšířenější konvence je, že funkce oddělují slova podtržítky, třídy a metody potom zvětšením písmene. Až na výjimky (jako např. imagecreate nebo mysqli::set_charset) to platí i u extenzí PHP.

ikona Jakub Vrána OpenID:

A v názvech tříd se podtržítko obvykle používá pro oddělení jmenných prostorů (od PHP 5.3 by se místo něj použilo zpětné lomítko).

ikona v6ak:

Přesné to není, viz Zend. Možná ten příklad taky nebude přesný, ale pokusím se: zatímco třídě Zend_Form_Element odpovídá Zend\Forms\Element, třídě Zend_Form odpovídá Zend\Forms\Form.

S dokumentací jsem to nekonzultoval, ale myslím, že rozdíl to demostruje dostatečně dobře. Ostatně, podobně je na tom třída NotORM.

Zpočátku může být tento rozdíl trošku nezvyk, ale je to tak asi i lepší. Podle Zendovské adresářové struktury tak patří soubor s Zend_Exception (Exception.php) vedle souboru s Zend_Form (Form.php), ale Zend_Form přitom mnohem více souvísí s Zend_Form_Element než s Zend_Ecxeption.

ikona v6ak:

Konvence jsou v PHP, bohužel, různé. Navíc, PHP je jedna věc a knihovny pro PHP jsou věc druhá, bohužel. Zajímavý je zde i fakt, že php je v názvech funkcí, tříd a metod (s výjimkou volání přes __call) case insensitive. Tím  je vpodstatě htmlspecialchars kompatibilní s camelCase variantou htmlSpecialChars.

Tharos:

Chválím vývoj, NotORM jsem teď na jednom projektu experimentálně nasadil a můj dojem z něj byl vskutku dobrý. Jenom jedna věc mi chybí - možnost získat výsledek jako asociativní pole (například pro další zpracování). Teď to řeším konstrukcí:

<?php

$result
= (array) $result->getIterator();

?>

Ale přijde mi to vcelku kostrbaté... Neexistuje nějaký lepší způsob?

ikona Jakub Vrána OpenID:

Elegantní způsob nabízí funkce iterator_to_array().

Tharos:

Výborně, to jsem přesně hledal a nenašel. Díky!

Oggy:

Můžu se zeptat na syntaxi zápisu INSERT ON DUPLICATE KEY UPDATE?

ikona Jakub Vrána OpenID:

<?php $affected = $table->insert_update($unique, $insert, $update); ?>
Je to popsané na http://www.notorm.com/#persistence.

Oggy:

Já se omlouvám, ale v dokumentaci http://www.notorm.com/#persistence metodu insert_update nevidím .. Ale je možné, že jen špatně koukám :-)

ikona Jakub Vrána OpenID:

Ona tam dřív nebyla, takže to asi bude chtít refresh.

Oggy:

Ha, je to tak .. děkuju

Oggy:

ještě jestli můžu a jak má vypadat zápis proměnné $update, když by se měl udělat třeba součet se stávající?
tohle pole $update updatuje jen na hodnotu $amount
<?php
array('amount' => $amount + 'amount')
?>

ikona Jakub Vrána OpenID:

<?php array('amount' => new NotORM_Literal("$amount + amount")) ?>

ikona v6ak:

Napadá mě udělat řízení transakcí pomocí anonymek:

<?php
$db
(function($db){
    ...
    ...
});
?>

Může to být třeba i na tabulce a ne jen na DB.

Jen škoda, že PHP nemá anonymky/lambdy jako Scala.

ikona Jakub Vrána OpenID:

Zajímavý nápad, ale asi si tuhle syntaxi nechám ještě pro něco jiného. Navíc funguje až od PHP 5.3, takže to nemůže být jediná možnost. Navíc já občas potřebuji transakci zahájit někde úplně jinde, než kde ji potvrzuji (např. zahájení session, pak je celá aplikace a pak je uložení session).

Na tabulce by to šlo udělat i normálně pomocí <?php $db->table()->commit(); ?>, ale to mi moc nedává smysl, protože transakce s tabulkou nemají nic společného.

Robert-Antonio:

Narazil jsem na problém, že objekt načtený notormem z databáze nemůžu jednoduše uložit do session. Třída Row v sobě obsahuje referenci na neserializovatelné PDO, a PHP při jejím ukládání do session nic nezahlásí, ale ani nic neuloží :(

ikona Jakub Vrána OpenID:

Vyhodí to výjimku, na což session handler zareaguje fatální chybou "Exception thrown without a stack frame in Unknown on line 0". Nevím, co jiného bych s tím mohl dělat.

Serializaci NotORM dost dobře podporovat nemůže, protože v konstruktoru přijímá přímo PDO, takže při wakeup se nemůže znovu připojit.

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-2017 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.