NotORM

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

Dopsal jsem knihu

Většina webových aplikací potřebuje pracovat s propojenými daty uloženými v databázi. Psát SQL dotazy spojující třeba šest tabulek v databázi může být zpočátku docela zábavné, pak se z toho ale stane nudná rutina. Navíc spojení tabulek nemusí být vždy nejefektivnější, protože se znovu přenáší už jednou přenesená data. Takže někdy je výhodnější položit šest jednoduchých dotazů a propojit až jejich výsledky, což už je úplná nuda.

Článek vyšel na serveru Zdroják.

Základní API NotORM

Elegance

Pro získání dat pomocí PHP proto vznikla knihovna, která propojená data z databáze dokáže elegantně a zároveň efektivně získat. Jmenuje se NotORM a připodobnil bych ji k PHP extenzi SimpleXML, která podobným způsobem pracuje s XML dokumenty. Základní API je jednoduché:

<?php
// připojení k databázi
include "NotORM.php";
$pdo = new PDO("mysql:dbname=software");
$software = new NotORM($pdo);

// získání prvních deseti aplikací
foreach ($software->application()->order("title")->limit(10) as $application) {
    // vypsání jejich titulku
    echo $application["title"];
    
    // získání jména autora z propojené tabulky
    echo " (" . $application->author["name"] . ")\n";
    
    // vypsání všech nálepek z propojené tabulky M:N
    foreach ($application->application_tag() as $application_tag) {
        echo "- " . $application_tag->tag["name"] . "\n";
    }
}
?>
Schéma ukázkové databáze software

Efektivita

Jak už bylo řečeno, tak knihovna je nejen elegantní, ale zároveň i efektivní. Klade tedy jen minimální počet jednoduchých dotazů a z databáze přenáší jen ta data, která jsou potřeba. Dosahuje se toho dvěma způsoby:

  1. Získání souvisejících záznamů se provádí najednou pro všechny řádky výsledku jedním dotazem.
  2. Přenášejí se jen ty sloupce, které se nakonec skutečně použijí (při povolení této vlastnosti).

Díky těmto vlastnostem se pro zpracování příkladu položí jen čtyři jednoduché dotazy:

SELECT id, title, author_id FROM application ORDER BY title LIMIT 10;
SELECT id, name FROM author WHERE (id IN ('11', '12'));
SELECT application_id, tag_id FROM application_tag WHERE (application_id IN ('1', '4', '2', '3'));
SELECT id, name FROM tag WHERE (id IN ('21', '22', '23'));

Všimněte si, že z tabulek se přenáší jen ty sloupce, které se nakonec použijí, aniž by se jejich seznam musel kdekoliv specifikovat. To je zařízeno tak, že knihovna napoprvé získá všechny sloupce, pak si ale zapamatuje, které sloupce skutečně použila a příště už si vyžádá jen ty použité. Když se skript změní a chceme vypsat nějaké sloupce navíc, tak se automaticky zase získají všechny a seznam se rozšíří. Pro zajištění tohoto chování si knihovna potřebuje ukládat data, což lze nastavit třetím parametrem konstruktoru NotORM – jsou připravena úložiště do session proměnných, souborů, databáze a sdílené paměti, další si můžete snadno doplnit.

Kešování výsledků dotazů knihovna nepoužívá, protože takováto keš zastarává a navíc zabírá paměť, kterou lze využít k něčemu užitečnějšímu. Lepší je data získávat efektivně rovnou od databáze, pro což dává knihovna výborné podmínky.

Struktura databáze

NotORM umí pracovat se všemi databázemi, které podporuje PDO, testovaná je s MySQL, SQLite, PostgreSQL a MS SQL. Kromě toho je NotORM k dispozici i pro oblíbenou knihovnu Dibi.

Co se pojmenování sloupců týče, tak se používá nejrozšířenější konvence – primární klíč je id, cizí klíč table_id, kde table je název odkazované tabulky. Předáním druhého parametru konstruktoru NotORM si ale můžeme nastavit i vlastní konvenci. K dispozici je i třída načítající informace o primárních a cizích klíčích přímo z meta-informací databáze (tabulky InnoDB v MySQL >= 5), ta má ale určitou režii, takže je lepší dodržovat nějakou konvenci.

Omezení výsledku hodnotami v jiné tabulce

Řekněme, že bychom chtěli vědět, jakým oblastem se věnuje určitý autor. Neboli zjistit nálepky všech aplikací daného autora. V NotORM to jde velmi snadno:

<?php
$applications = $software->application("author_id", 11);
foreach ($software->application_tag("application_id", $applications) as $application_tag) {
    echo $application_tag->tag["name"] . "\n";
}
?>

První řádek zkonstruuje dotaz vracející všechny aplikace daného autora. Tento dotaz se ale databázi samostatně nepoloží, protože jeho výsledkem neprocházíme. Místo toho se použije jako poddotaz na druhém řádku kódu. Celkově se tedy položí tyto dotazy:

SELECT tag_id FROM application_tag WHERE (application_id IN (SELECT id FROM application WHERE (author_id = '11')));
SELECT id, name FROM tag WHERE (id IN ('21', '22', '23'));

Validace a ukládání dat

NotORM se nestará o ukládání a tím pádem ani validaci dat. Ve webových aplikacích je totiž ukládání obvykle mnohem méně časté než načítání a není problém ho zajistit konvenčními způsoby. Pro administrační rozhraní lze použít Adminer Editor, který ukládání vyřeší zadarmo.

Validaci dat je lepší řešit na úrovni databáze pomocí unikátních a cizích klíčů, datových typů, povinných sloupců a dalších kontrol. Jinak totiž hrozí, že se nám do databáze dostanou nekonzistentní data, se kterými aplikace stejně bude muset umět pracovat. Ohlašování chyb uživateli je zase vhodné řešit co nejblíže místu jejich vzniku, výborně se o to starají třeba formuláře Nette.

Část schéma složitější databáze

Větší příklad

NotORM se báječně hodí pro malé webíky se třemi propojenými tabulkami stejně jako pro velké projekty s desítkami složitě propojených tabulek. U těchto projektů dává NotORM pocit klidu, že se bez velkého přemýšlení vyhodnocují všechny dotazy efektivně (samozřejmě pokud jsou definované indexy na vyhledávání a třídění). Ukážeme si proto použití u obchodu, který organizuje produkty do kategorií a u každého produktu eviduje jeho možné dodavatele. Kromě toho se k produktům evidují parametry, které se načítají z tabulky všech možných hodnot (ta se používá třeba i pro sestavení vyhledávacího formuláře), která se zase odkazuje do tabulky všech možných názvů parametrů. Na obrázku je pouze relevantní část schématu celé databáze a jsou vynechány nevyužité sloupce.

<?php
include "NotORM.php";
$pdo = new PDO("mysql:dbname=shop");
$shop = new NotORM($pdo, null, new NotORM_Cache_Database($pdo));

$category = $shop->category[$_GET["id"]];
if (!$category) {
    // 404
    exit;
}
echo "<h1>" . htmlspecialchars($category["name"]) . "</h1>\n";

$products = $category->product()
    ->where("disabled", 0)
    ->order("price")
    ->limit(10)
;
foreach ($products as $product) {
    echo "<h3>" . htmlspecialchars($product["name"]) . "</h3>\n";
    list($suppliers) = $product->product_supplier()->group("COUNT(*)");
    echo "<p>Cena: $product[price] Kč, dodavatelů: $suppliers</p>\n";
    
    $product_parameters = $product->product_parameter();
    if (count($product_parameters)) {
        echo "<table cellspacing='0'>\n";
        foreach ($product_parameters as $product_parameter) {
            $parameter = $product_parameter->parameter;
            $parameter_name = $parameter->parameter_name;
            echo "<tr>";
            echo "<th>" . htmlspecialchars($parameter_name["name"]) . "</th>";
            echo "<td>" . htmlspecialchars("$parameter[value] $parameter_name[unit]") . "</td>";
            echo "</tr>\n";
        }
        echo "</table>\n";
    }
}
?>

Při spuštění se položí následující dotazy:

SELECT id, name FROM category WHERE (id = '25');
SELECT id, category_id, name, price FROM product WHERE (category_id IN ('25')) AND (disabled = '0') ORDER BY price LIMIT 10;
SELECT product_id, COUNT(*) FROM product_supplier WHERE product_id IN ('102920', '102915', '116549', '102902', '108993', '102907', '102900', '102922', '102930', '102909') GROUP BY product_id;
SELECT product_id, parameter_id FROM product_parameter WHERE (product_id IN ('102920', '102915', '116549', '102902', '108993', '102907', '102900', '102922', '102930', '102909'));
SELECT id, parameter_name_id, value FROM parameter WHERE (id IN ('1', '131', '3780', '6109', '2561', '2576', '6110', '3787', '5374', '6111', '6113', '6114', '8783'));
SELECT id, name, unit FROM parameter_name WHERE (id IN ('1', '46', '120', '187'));

Dávat tyto dotazy dohromady ručně nebo konstruovat dotazy spojující více tabulek by se mi opravdu nechtělo. Obslužná logika pro jejich projití by navíc byla prakticky stejná jako u NotORM. Navíc bych nejspíš skoro všude z pohodlnosti použil SELECT *, abych nemusel sloupec dodatečně přidaný do výpisu dopisovat ještě do dotazu.

Závěr

Knihovna NotORM se dá použít pro pohodlné a efektivní procházení propojených záznamů v databázi ať už u malých nebo u velkých projektů. Neřeší validaci ani ukládání dat, které je obvykle lepší řešit na jiné úrovni.

Jakub Vrána, Řešení problému, 6.5.2010, diskuse: 36 (nové: 0)

Diskuse

Dr.Diesel:

Hned u vzniku NotORM jsem pro přejmenování order() metody na orderby(), ten order() tam mě osobně bije do očí.

11.$products = $category->product()
12.    ->where("disabled", 0)
13.    ->order("price")
14.    ->limit(10)
15.;

P.S. A psaní s diakritikou mi vadí taky, ale máš/máte ji mít :-D
# 17.5.2010 00:05:50 reagovat

cucací potřeby:

Dr.Diesel: nevím, proč by to muselo být zrovna orderby()

Jakub: NotORM se mi zalíbilo a musím uznat, že je opravdu elegantní. Z přílišné skromnosti jsi zřejmě neuvedl adresu oné knihovny - http://www.notorm.com/
Why Not ORM? ;-)

mimochodem, může být řádek
<?php $category = $shop->category[$_GET["id"]]; ?>
vystaven SQL injection nebo se případné uvozovky vyescapují?

btw. snahu zjistit nárory druhých pomocí vlastní ankety a zdrojovat to ve článku kvituji kladně, ale něco přes 100 hlasů nyní již uzavřené ankety (na v Čechách ne tak známém serveru) ještě stále nemá takovou vypovídací hodnotu a nevím, jestli bych to propagoval jako (a teď myšleno obecně a globálně) "nejrozšířenější konvence".

a ještě ad nejrozšířenější konvence) obávám se, že ta mixuje singulár u klíče a plurál u názvů tabulky, tj. např. tabulka "categories" ale cizí klíč "category_id", nikoli "categories_id". Mám za to, že plurál se používá pro názvy tabulek častěji, protože takové "SELECT id FROM categories" zní oproti "SELECT id FROM category" lépe. (Možná by to bylo na další anketu :-) )

SimpleXML má s NotORM podobný zápis v kódu, používání metod a přístup k objektům, které ovládá, ale - opravte mě, jestli se mýlím - při vytvoření instance načítá celý zadaný XML soubor do paměti, což je pravý opak toho, co vyzvihuješ na NotORM, není-liž pravda.
# 22.5.2010 04:10:51 reagovat

ikona Jakub Vrána:

Odkaz na web je vytvořen z první zmínky o NotORM v článku, jak je to zvykem.

<?php $shop->category[$_GET["id"]] ?> je vůči SQL injection imunní – obecně řečeno se všechna data ošetřují, naopak žádné identifikátory ne.

S tím množným číslem to je asi pravda, ale kód by se kvůli nepravidelnostem zbytečně komplikoval. Nicméně konvence "tables" a "tables_id" bude fungovat taky.

NotORM má se SipleXML podobné API, způsob práce samozřejmě ne.
# 24.5.2010 11:40:16 reagovat

cucací potřeby:

ad SQL injection) tzn. NotORM všechna data (uvnitř SQL) dává do uvozovek a escapuje?

ad množné číslo) právě takové komplikace nemám rád (a to přidávání "-s" nebo "-y" --> "-ies" v angličtině patří ještě mezi ty jednodušší, v češtině by kód na zjišťování množného čísla byl delší než celý projekt). Takže v příštím návrhu nějaké databázové struktury, co se namane, vyzkouším 'singulár'.
# 27.5.2010 18:35:50 reagovat

ikona Jakub Vrána:

Ošetří se všechno, co se posílá zvlášť. Když napíšeš where("id = $_GET[id]"), tak to samozřejmě ošetřené nebude, ale where("id", $_GET["id"]) ano, stejně jako where("id = ?", array($_GET["id"])).

Ono je celkem jedno, jestli jednotné nebo množné číslo - důležité je, aby se sloupce jmenovaly stejně jako tabulky. Tedy buď products a products_id nebo product a product_id.
# 28.5.2010 09:58:09 reagovat

Dr.Diesel:

Pokud dobre chapu abstrakci notace NotORM:

<?php
include "NotORM.php";
$connection = new PDO("mysql:dbname=software");
$software = new NotORM($connection);
$applications = $software->application()
    ->
select("id, title")
    ->
where("web LIKE ?", "http://%")
    ->
order("title")
    ->
limit(10)
;
?>

SQL:
select id, title
from application
where web like 'http://%'
order by title
limit 10

Proc asi orderby()...
# 25.5.2010 14:59:26 reagovat

PHX:

Rad bych to zkusil pouzit v jednom projektu, ale mam problem. Napr hned tvuj prvni priklad s App, Autorem a Tagy. Rekneme, ze to bude vypsane v tabulce, ale co kdyz bude pozadavkem uzivatelske razeni dle sloupecku.

Jde to nejak jednoduse? Osobne me nenapada cesta. Jedine snad radit v PHP, ale to bych musel nacist vsechny zaznamy.
# 3.6.2010 23:01:41 reagovat

ikona Jakub Vrána:

Všimni si metody order(). Tou můžeš řadit podle libosti.
# 4.6.2010 10:28:07 reagovat

PHX:

Jeste jsem nezkousel. Zkusim. Ale pokud to chapu dobre tak tim vlastne poslu do DB dotaz s JOIN jen kuli serazeni (chci radit dle dat z tabulky co budou teprve navazany ve foreach). To uz mozna vyjde levneji rovnou ziskat vsechna data, kdyz uz to DB joinuje kuli razeni, ne?
# 4.6.2010 11:39:05 reagovat

ikona Jakub Vrána:

Pokud chceš WHERE z jedné tabulky a ORDER BY z druhé, tak to efektivní nebude nikdy.
# 4.6.2010 11:49:16 reagovat

ok3x:

Díval jsem se do mauálu a tohle mě bije do očí:
<?php $row2 = $row->$column ?>    Get row from referenced table
K čemu se to používá, myslím prakticky? Čekal bych, že to bude něco jako alias zápisu <?php $data = $row[$column] ?> Get data from column, když už je to celé tak pěkně objektové.
# 30.7.2010 08:36:57 reagovat

ikona Jakub Vrána:

Jde o jeden ze dvou základních obratů. Např. $application->author["name"] získá jméno autora aktuální aplikace. Možná tě mate to $column, ono to je totiž spíš $tableName, i když v závislosti na mapování to může být i něco mezi tím (sloupec se může jmenovat author_id, tabulka authors).
# 31.7.2010 04:27:54 reagovat

ikona v6ak:

Nazdar,
trošku jsem se na NotORM díval a přišel jsem na jednu potenciálně nebezpečnou vlastnost. Není to bezpečnostní chyba, je to vlastnost, která umožňuje bezpečnostní chybu (SQL injekci) velmi snadno vytvořit.
Když jsem se díval na http://www.notorm.com/#persistence , nemohl jsem si nevšimnout tohoto:
The $array  parameter holds column names in keys and data in values. Values are automatically quoted, SQL literals can be passed as array, for example:
<?php
$array
= array(
   
"title" => "NotORM",
   
"author_id" => null,
   
"created" => array("NOW()"),
);
?>
Když si představím třeba "title" => $_POST["title"], je tu slušný prostor pro díru, protože tam mohu dostat i pole. POST bude asi často ošetřen formulářovou knihovnou, ne až tak GET.
Komu nedává smysl to pole na vstupu, toho odkážu na: http://v6ak.profitux.cz/clanky/musi-byt-na-…-string.php
# 1.8.2010 16:32:44 reagovat

ikona Jakub Vrána:

To je zajímavý postřeh. Mě se tam to pole nelíbí i proto, že jeho smysl není intuitivní. Nahradil bych to objektem, takže pak by se předávalo třeba new NotORM_Literal("NOW()"), souhlasíš?
# 2.8.2010 11:35:59 reagovat

ikona v6ak:

Jo, naprosto souhlasím. Pole mi přišlo jako hack podobný callbacku array(objekt, metoda).
# 2.8.2010 14:06:49 reagovat

ikona Jakub Vrána:

Ano, přesně tím jsem se nechal inspirovat.
# 2.8.2010 14:08:46 reagovat

ikona Jakub Vrána:

Díky za tip, změnil jsem to na onen objekt třídy NotORM_Literal.
# 2.8.2010 13:37:59 reagovat

Juan:

Jakube, předně díky za NotORM, je to fantastická věc!

Mám jeden námět na vylepšení naming conventions. Myslím, že situace, kdy máme dva a více cizích klíčů z jedné tabulky do jediné jiné, může být poměrně častá. Způsob, jak toto řešit, který popisuješ na webu NotORM mi nepřijde úplně elegantní, obvzlášť pokud takových tabulek bude více.

Vede to ke zbytečnému dědění třídy NotORM_Structure_Convention a přidávání podmínek typu:
<?php
if ($name == 'created_by' || $name == 'modified_by') {
    return
'user';
}
?>

Co takhle zavést modifikátor, který by odpovídal libovolnému řetězci? Pak by šlo automaticky brát všechny následující varianty jako odkaz do tabulky user:

user_id
user_id_created
user_id_modified

------

Když už jsem u těch námětů na vylepšení, napíšu to sem:
Adminer 2.3.2 krásně automaticky vytváří cizí klíče, pokud je název ve tvaru tabulka_id. To je super věc! Chybí mi tam ale klíč do stejné tabulky (vztah "parent").
# 11.8.2010 14:54:32 reagovat

ikona Jakub Vrána:

Konvence user_id_created podle mě moc častá není, takže kvůli tomu nechci upravovat společný kód. Potomek si nicméně modifikátor klidně zavést může.

V Admineru chybí cizí klíč do vlastní tabulky proto, že buď se tabulka teprve vytváří (takže její jméno ještě není známé) nebo se může přejmenovat. Taky ještě nemusí existovat primární klíč. Dalo by se to vyřešit JavaScriptem, který by aktuální název tabulky do seznamu doplnil, ale kód by to zbytečně zkomplikovalo. Odkazy na sebe sama lze vytvořit pomocí obecných cizích klíčů (které jsou použitelné třeba i pro vytváření vícesloupcových klíčů nebo odkazů na neprimární klíč).
# 11.8.2010 14:59:12 reagovat

Juan:

Díky za odpověď. Co se týče Adminera, tak teď už důvody chápu. Já myslel, jestli se na něj třeba jen "nezapomnělo".

K NotORM - nemyslel jsem konvenci user_id_created, ale např. modifikátor, který by bral řekněme jakýkoliv znak kromě podtržítka (jen pro příklad) a nazvu ho třeba %x.

Pak by přece šlo snadno zapsat konvenci %s_id_%x anebo třeba %x_by_%s (tedy user_id_created anebo created_by_user), atd. Žádnou jednotnou konvenci by to nevnucovalo, jen by to přineslo větší volnost.
# 11.8.2010 15:12:22 reagovat

ikona Jakub Vrána:

Já tomu rozumím. Ale mě nepřijde příliš rozšířená ani obecná konvence, že se tabulka z názvu sloupce získá po vyhození něčeho.
# 11.8.2010 15:17:02 reagovat

Juan:

Mám další dotaz k NotORM. Při používání Dibi verze NotORM jsem narazil na následující problém:

Pokud použiji zápis $db->table[$id] (možná se to může vyskytnou i v jiných situacích, nevím) pro výběr jednoho řádku a v Dibi mám nastaveno lazy = TRUE, skončím s chybou:

PHP Warning: mysql_real_escape_string() expects parameter 2 to be resource, null given...

Po troše pátrání jsem zjistil následující. Funkce quote (Result.php) vypadá takto:
<?php
protected function quote($string) {
    return
$this->notORM->connection->getDriver()->escape($string, dibi::TEXT);
}
?>

A protože máme aktivovaný lazy připojení, zápis $this->notORM->connection->getDriver() vrací nenastavené hodnoty. Zápis:

<?php
return $this->notORM->connection->escape($string, dibi::TEXT);
?>

sice funguje, ale je deprecated... Co s tím?
# 13.8.2010 14:02:46 reagovat

ikona Jakub Vrána:

Zdá se, že je to už opravené na úrovni Dibi. getDriver() se nyní pokusí připojit.
# 28.8.2010 00:45:42 reagovat

Lweek:

Zdravím, chci se prosím pěkně zeptat, zda-li je možné přes NotORM propojit i dvě tabulky kde klíčový sloupce mají rozdílný název? Rád bych to totiž napasoval na již existující web, kde ale uživatelé mají klíčový sloupec [id] a tabulka kterou potřebuji napojit používá [id_user]. Používám Dibi verzi. Děkuji a přeji krásný den!
# 27.8.2010 04:43:38 reagovat

ikona Jakub Vrána:

Ano, lze to snadno. Postup je popsaný na http://www.notorm.com/#identifiers.
# 28.8.2010 00:35:38 reagovat

ikona v6ak:

"NotORM uses the PDO driver to access the database."
To je, vzhledem k existenci Dibi verze, trošku nešikovná formulace na http://www.notorm.com/#examples . Celkově mi Dibi verze přijde taková schovaná, což je škoda.
# 31.8.2010 20:27:46 reagovat

ikona Jakub Vrána:

Je to tak, snažím se upřednostnit verzi pro PDO.
# 2.9.2010 13:03:01 reagovat

ikona v6ak:

Hmm, po bližším zkoumání mé nadšení trošku opadá. To je možná normální, ale hned řeknu, čím je to způsobeno.

NotORM_Result je ukázkové porušení SRP, protože reprezentuje jak filtrovanou tabulku, tak iterátor nad ní. Pokud chci vrátit iterator, musím použít clone. Je to snad podruhé, kdy jsem clone potřeboval.

Druhé nepříjemné zjištění bylo ve stejné třídě:
<?php

   
/** Count number of rows
    * @return int
    */
   
function count() {
       
$this->execute();
        return
count($this->data);
    }
?>

Pokud to chci použít pro stránkování, načítám zbytečně mnoho dat. Ale nevím, jak to obecně udělat efektivně. SELECT COUNT(*) FROM (SELECT ...) as t1 není v MySQL efektivní (na druhou stranu, možná je to efektivnější, než zmíněná implementace), trvá to dlouho. Nahradit sloupce v SELECT něčím jako COUNT(*) taky není ono, protože tam může být nějaká agregace. Na druhou stranu, je to možné v některých případech udělat, například při chybějícím seznamu sloupců.
# 1.9.2010 18:07:02 reagovat

ikona v6ak:

Měl jsem to bufferovat a poslat najednou.

Chybí mi tu metoda setOrder, něco jako:
<?php
   
/** Set order clause and <strong>replaces all previous columns in order clause</strong>
    * @param string for example "column1, column2 DESC"
    * @return NotORM_Result fluent interface
    */
   
function setOrder($columns) {
       
$this->order = array($columns);
        return
$this;
    }

?>
Nechceš to, prosím, commitnout do Result.php?
# 1.9.2010 18:15:01 reagovat

ikona Jakub Vrána:

Uveď prosím nějakou ukázku využití.
# 2.9.2010 13:22:29 reagovat

ikona v6ak:

Pokud chci implementovat setSorting v http://github.com/janmarek/Gridito/blob/mas…/IModel.php , tak se bez toho těžko obejdu.
# 2.9.2010 19:10:44 reagovat

ikona v6ak:

Jak to vlastně je v případě těch agregací? Zvažuji implementaci s COUNT(*). V nejjednodušší formě by to zlobilo asi jen bez group by a s nějakým agregovaným sloupcem, protože stávající implementace vrací v takovém případě 1, zatímco nová by vracela celkový počet řádků. Podobně by to dopadlo, kdybychom sloupec count(*) přidali.
Na druhou stranu, těžko si tu dovedu představit, kde by to mohlo vadit. Je to jen mou nedostatečnou představivostí a agregační funkce nad celou tabulkou mají smysl (popř. je tu jiný případ, na který jsem zapomněl), nebo to opravdu reálně vadit nemůže?
# 1.9.2010 18:42:58 reagovat

ikona Jakub Vrána:

Metoda count() původně byla namapovaná právě na COUNT(*), ale problém nastal v případě, kdy jsem chtěl jednak zjistit celkový počet řádek a jednak potom s výsledkem i pracovat (což je poměrně běžné). V takovém případě se zbytečně kladly dva dotazy. Proto jsem count() zjednodušil na současné přímočaré řešení.

Když chceš zjistit samotný počet, vždycky můžeš zavolat $table->group("COUNT(*)"), což i považuji za čistší řešení.
# 2.9.2010 13:20:08 reagovat

ikona v6ak:

Hmm, já to vidím jinak. Nejdřív zjistím počet, pak přidám limit a offset a zjistím si data pro jednu stránku. Takto nejdříve načtu všechna data jen kvůli počtu a pak načtu data pro jednu stránku.
# 2.9.2010 19:13:08 reagovat

ikona Jakub Vrána:

Popiš mi prosím situaci, v které potřebuješ vrátit iterátor a ukaž to použití clone.
# 2.9.2010 13:07:37 reagovat

ikona v6ak:

Při implementaci http://github.com/janmarek/Gridito/blob/mas…/IModel.php v metodě getIterator:
<?php
   
public function getIterator() {
        return clone
$this->table;
    }
?>
Nevím proč, ale z použití clone mám vždy takový divný pocit. Aspoň pokud se pro něj rozhodnu já.

Ta třída NotORM_Result by se dala rozdělit i na tři. NotORM_Table, NotORM_Query a NotORM_Result.
# 2.9.2010 19:17:19 reagovat

Vložit příspěvek

Používejte diakritiku. Nelze používat HTML značky, 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:

© 2005-2010 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.