Spojování tabulek v NotORM

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

V NotORM se poměrně krkolomně provádí operace, které jdou přes více tabulek. Obvykle jsou sice možné, ale dá to trochu práce a nezvyklého přemýšlení. Vezměme si třeba, že bychom chtěli vypsat všechny aplikace setříděné podle jména autora. Já bych to udělal takhle:

<?php
foreach ($db->author()->order("name") as $author) {
    foreach ($author->application() as $application) {
        echo "$author[name]: $application[title]\n";
    }
}
?>

Položené dotazy (se zapnutou cache):

SELECT id, name FROM author ORDER BY name;
SELECT id, author_id, title FROM application WHERE (application.author_id IN ('12', '11'));

Pokud bychom ale do dotazu chtěli přidat nějakou podmínku z tabulky application, tak se dotaz dost zkomplikuje (pokud zbytečně nechceme procházet i autory vyřazených aplikací).

<?php
$authors = $db->application("slogan LIKE ?", "Database%")->select("author_id");
foreach ($db->author("id", $authors)->order("name") as $author) {
    foreach ($author->application("slogan LIKE ?", "Database%") as $application) {
        echo "$author[name]: $application[title]\n";
    }
}
?>

Položené dotazy:

-- v MySQL
SELECT author_id FROM application WHERE (slogan LIKE ?);
SELECT id, name FROM author WHERE (id IN ('11', '12')) ORDER BY name;
SELECT id, author_id, title FROM application WHERE (application.author_id IN ('12', '11')) AND (slogan LIKE ?);

-- v jiných databázích
SELECT id, name FROM author WHERE (id IN (SELECT author_id FROM application WHERE (slogan LIKE ?))) ORDER BY name;
SELECT id, author_id, title FROM application WHERE (application.author_id IN ('12', '11')) AND (slogan LIKE ?);

Omezit počet vrácených záznamů by tímto způsobem nešlo už vůbec.

Pro tyto úlohy se prostě nejlíp hodí spojování tabulek. Ladislav Ševcůj mi poslal patch, který tuto možnost doplňuje, já jsem ale NotORM napsal proto, abych už nikdy žádný JOIN napsat nemusel, i když bych k tomu měl příjemnější API.

Zamyslel jsem se proto nad tím, jaké API bych si pro řešení této úlohy představoval, a dospěl jsem k tomuto:

<?php
foreach ($db->application("slogan LIKE ?", "Database%")->order("author.name") as $application) {
    echo $application->author["name"] . ": $application[title]\n";
}
?>

A to je vše, pohodlnější to být už nemůže. Cizí tabulky se rozpoznávají kdekoliv v dotazu. Položené dotazy:

SELECT application.id, application.author_id, application.title FROM application LEFT JOIN author ON application.author_id = author.id WHERE (slogan LIKE ?) ORDER BY author.name;
SELECT id, name FROM author WHERE (author.id IN ('12', '11'));

Pokud by nám nevadilo, že se jméno autora bude přenášet opakovaně (to v tomto případě asi skutečně nevadí), tak můžeme jeden dotaz ušetřit:

<?php
foreach ($db->application("slogan LIKE ?", "Database%")->select("author.name, application.title")->order("author.name") as $application) {
    echo "$application[name]: $application[title]\n";
}
?>

Položený dotaz:

SELECT author.name, application.title
FROM application
LEFT JOIN author ON application.author_id = author.id
WHERE (slogan LIKE ?)
ORDER BY author.name;
Jakub Vrána, Seznámení s oblastí, 8.11.2010, diskuse: 42 (nové: 0)

Diskuse

ikona Pekelník:

Mohl bys, prosím, doplnit SQL dotaz(y), které tohle provede?

ikona Jakub Vrána OpenID:

Dobrý nápad, doplnil jsem to.

Franta:

Ten první výpis SQL (dva dotazy) patří k tomu prvnímu výpisu PHP? Nějak to nechápu – vidím tam dva cykly, kolik prvků má $author? Dva? Jak může program při prvním průchodu vnějším cyklem vědět, že budou ještě další autoři a že se má nakonec v SQL objevit IN ('12', '11')? Přijde mi, že v tom článku něco chybí. Co tedy znamená „zapnout cache“?

ikona Jakub Vrána OpenID:

Přečti si nejprve http://php.vrana.cz/notorm.php. Pak ti snad všechno bude jasné.

Franta:

Tak nějak jsem to čekal – při prvním průchodu cyklem se získají data ke všem dalším prvkům (pro další průchody cyklem). Tenhle styl mi zrovna nesedne (zavolání metody, resp. zjištění vlastnosti, jednoho objektu způsobí načtení dat úplně jiných objektů). Ale už nebudu rýpat, nepoužívám to, ptal jsem se jen tak ze zvědavosti :-)

ikona Jakub Vrána OpenID:

Tento styl se využije v naprosté většině případů, u webových aplikací prakticky vždy – když chci zjistit detail jednoho objektu v cyklu, budu nejspíš chtít zjistit i detail všech ostatních. Pokud bych to ve výjimečném případě nepotřeboval (třeba bych chtěl seznam všech aplikací, ale jen u první bych chtěl jejího autora), dá se to celkem snadno obejít:

<?php
foreach ($db->application() as $application) {
    if ($first) {
        $author = $db->author[$application["author_id"]];
    }
}
?>

Ale ten případ, kdy budu potřebovat všechno, je o tolik běžnější, že pohodlnější syntaxe patří jednoznačně jemu.

Miroslav Hruška:

Mohu se zeptat, zda lze NotORM využít při spojení dvou databází? Díky.

ikona Jakub Vrána OpenID:

Asi by to nějak šlo, ale s takovou možností nepočítám. Na co by to mělo být potřeba?

Miroslav Hruška:

Potřebuji spravovat 3 projekty s jedním administračním rozhraním a pro rozdělení výkonu bych to rád postavil na různých databázích.

Miroslav Hruška:

Tak nakonec to asi vyřeším replikací, ale mám ještě jeden dotaz. Lze nějak pracovat s prefixy tabulek? Díky.

ikona Jakub Vrána OpenID:

Já prefixy tabulek nesnáším, ale mělo by to jít dvěma způsoby:

1. Definicí vlastní struktury (třída implementující NotORM_Structure) – to považuji za vhodný způsob. http://www.notorm.com/#api

2. Psaním kódu v podobě $notORM->{$prefix . "tab"}().

Tharos:

Jakube, je možné nějak rozumně pracovat s více než dvěmi tabulkami? Mám následující strukturu:

• user
id
username

• companies
id
users_id

• projects
id
companies_id

• log
id
projects_id
date

A potřebuji následující: vypsat všechny položky z tabulky log, které patří k určitému uživateli (restrikce podle users.username) a které jsou dále limitovány určitým datem (log.date) - podle něj také mají být seřazené. Opravdu jsem nenašel hezký způsob, jak tohle v NotORMu napsat. Předem díky za pomoc.

ikona Jakub Vrána OpenID:

Jde to takhle:

<?php
$users
= $db->user("username", $username);
$companies = $db->companies("users_id", $users);
$projects = $db->projects("companies_id", $companies);
$db->log("projects_id", $projects)->where("date", $date)->order("date");
?>

Mám připravený patch, který dovolí psát log.projects.companies.users.username, ale nejsem si jist, že ho zveřejním – jde o poměrně neobvyklý zápis.

Tharos:

Funguje to výborně, děkuji vřele za reakci. Co se patche týče, minimálně já bych měl o jeho zveřejnění zájem. Osobně se totiž se strukturou databáze, kde se pro některá data zkrátka musí přes více než jednu spojovací tabulku, setkávám ani ne až tak vzácně... A pak už je přece jen NotORM zápis poměrně složitý a dlouhý.

ikona Jakub Vrána OpenID:

Je to v Gitu: https://github.com/vrana/notorm/. Teď by tedy mělo stačit napsat <?php $db->log("projects.companies.users.username", $username); ?>.

Tharos:

Díky za uveřejnění patche, dnes jsem se konečně dostal k jeho otestování a funguje dobře. Nekoukal jsi ještě na nekorektně vygenerovaný obsah klauzuje IN, o kterém píši v komentáří vedle?

Tharos:

Tak, malý zádrhel. Řekněme, že v šabloně při vypisování budeme nakonec chtít i nějaké další sloupce například z tabulky companies (řekněme jméno společnosti, ke kterému se záznam v logu vztahuje). Když upravím kód na následující podobu, vygenerovaný dotaz získávající projekty je nesmyslný (je nesmyslná část v IN(...)):

<?php
$users
= $db->user("username", $username);
$companies = $db->companies("users_id", $users)->select('*');
$projects = $db->projects("companies_id", $companies);
$db->log("projects_id", $projects)->where("date", $date)->order("date");
?>

ikona Jakub Vrána OpenID:

K těm stačí přistoupit normálně: $log->projects->companies[""].

Miroslav Hruška:

Nešlo by do NotROM doplnit něco takového?
<?php
foreach ($notORM->application as $application) {
  echo $application['author.name'] . ' ' . $application['author.web'];
}
?>

Zatím jsem si to upravil sám ve vlastní \NotORM_Row ale něco takového by bylo přínosné myslím pro všechny. Jde o to, že NotORM nepracuje s plošnými daty (jak je většina rozšíření například v Nette zvyklá [DataGrid]). Ve třídě \NotORM_Row stačí upravit metodu offsetGet:
<?php
function offsetGet($key)
{
  if (strstr($key, '.') !== FALSE) {
    $exp = array_filter(explode('.', $key));
    if (count($exp) != 2) {
      return parent::offsetGet($key);
    }
    else {
      $row = $this->__get($exp[0]);
      return $row[$exp[1]];
    }
  }
  else {
    return parent::offsetGet($key);
  }
}
?>

Dalo by se to asi upravit i lépe (další zanoření), například:
<?php
foreach ($notORM->application as $application) {
  echo $application['author.name'] . ' ' . $application['author.company.web'] . ' ' . $application['author.company.region.capital'];
}
?>

Díky za odpověď.

Miroslav Hruška:

Beru zpět, v NotORM to již takto funguje, předchozí nefunkčnost způsobil špatné pojmenovaný vazební sloupec.

ikona Jakub Vrána OpenID:

Co je špatného na syntaxi $application->author['name']?

Miroslav Hruška:

Nic, já s tím mám problém pouze v mém datagridu, který přijímá data jako pole a následně generuje tabulku například podle jednoduchého zápisu:
<?php
$table
->data = $notORM->user;
$table->addColumn('name', 'Jméno');
$table->addColumn('sname', 'Příjmení');
$table->addColumn('company.name', 'Firma');
?>

Jinak bych musel psát něco takového:
<?php
  $table
->data = $notORM->user;
  $table->addColumn('name', 'Jméno');
  $table->addColumn('sname', 'Příjmení');
  $table->addColumn('companyName', 'Firma', function($row) {
    return $row->company['name'];
  });
?>

ikona Jakub Vrána OpenID:

Já jsem přemýšlel o tom, že by syntaxe $row['company.name'] přistupovala k datům pomocí JOINu, ale bez zapnuté keše by to bylo strašně pomalé.

Miroslav Hruška:

Mno to by bylo naprosto super. Možná je to trochu moc magické a nevím, do jaké míry realizovatelné, ale nešlo by tam jen přidat něco jako:
<?php
 
...

  if ($this->cache instanceof NotORM_Cache) {
    //join
  }
  else {
    //2 selecty
  }

  ...
?>

Je to jen nástřel ale myslím si, že rozdílné chování (pokud bude naprosto transparentní) při zapnuté / vypnuté cache by nevadilo, pokud se to napíše do dokumentace. Dalo by se jít ještě dál a založit něco jako "procesor", což by nebylo nic jiného než třída s jednoduchým interface, která by se zaváděla na základě nějakých enviromentálních stavů (cache ano/ne, driver, etc...).

ikona Jakub Vrána OpenID:

Problém je v tom, že pomocí PDO se nedá univerzálně napsat company.name AS `company.name` (neexistuje funkce pro ošetřování identifikátorů). Do NotORM tuto vrstvu dávat nechci, ale v Nette je, takže tam by to přidat šlo.

ikona Schmutzka:

Pokud s notorm začínáte, nebo potřebujete vědět, jak co napsat (OR, IN s více poli, JOIN, COUNT("*")), vytvořil jsem přehlednou tabulku příkazů, tedy helpku, jak to psát: http://bit.ly/SQLcQ

Snad vám bude nápomocna a ušetří spousty hodin a nervů :)

Roman Svoboda:

Ahoj,
potřeboval bych pomocí NotORM napsat dotaz:
SELECT c.* FROM content c JOIN content ci ON (c.id = ci.content_id AND ci.template_item_id = $promena)
ORDER BY ci.value DESC, ale netuším, jak na to. :(

Zkoušel jsem :
$this->db->content()->order('content_item.value DESC')->where('content_item.template_item_id = ?',$promenna);

NotORM mi však vygeneruje:
SELECT content.* FROM content
LEFT JOIN content_item ON content.content_item_id = content_item.id
WHERE (content_item.template_item_id = ?)
ORDER BY content_item.value DESC

Nešlo by to udělat nějak jinak prosím?

Předem děkuji za odpověď.

Roman Svoboda:

Pardon, právě jsem objevil  https://github.com/vrana/notorm/pull/32/files ,
který vyřešil můj problém :)
Pokud však existuje i jiný způsob v NotOrm, jak si to zapsat, určitě by mne zajímal.

ikona Jakub Vrána OpenID:

Já bych to napsal takhle:

<?php
foreach ($db->content_item("template_item_id", $promenna)->order("value DESC") as $content_item) {
    $content = $content_item->content;
}
?>

Martin Kopeček:

Zdravím, koukám, že už je to tu trošku starší, ale zkusím to:

mám podobný problém s hledáním rozdílu více "stejných" tabulek (šlo by to i v jedné s dalším sloupcem definujícím kategorii:

zpevniky_akordy > zpevniky_id, akordy_id
zpevniky_akordy_koncept > zpevniky_id, akordy_id

a potřeboval bych najít co je v konceptu navíc a co na míň.

např: to, co je navíc:

SELECT zpevniky_akordy_koncept.akordy_id FROM zpevniky_akordy_koncept LEFT JOIN zpevniky_akordy USING(zpevniky_id,akordy_id) WHERE zpevniky_akordy.akordy_id IS NULL

Jde to výše popsaným způsobem, ale přijde mi zbytečné tahat z DB stovky ID, abych je vložil do další query, když je možné vytáhnout jen ty tři podstatné.

Mimochodem, proč je pro mysql vypnuté IN (SELECT...) ? Tím by se to snad dalo také vyřešit.

ikona Jakub Vrána OpenID:

Šlo by to udělat poddotazem. Ten je v MySQL vypnutý, protože ho MySQL vyhodnocuje při každém čtení řádky (postupuje zvenku dovnitř).

Behe:

Ahoj, předem bych rád poděkoval za NotOrm, ale mám trošku problém s hledáním v několika na sobě závislých tabulkách:

team:
- id
- number
- name

costcenter
- id
- number

team_costcenter
- id
- team_id
- costcenter_segment_id

team_costcenter
- id
- team_id
- costcenter_segment_id

costcenter_segment
- id
- name
- segment_id
- costcenter_id

<?php
foreach ($db->team_costcenter()
            ->where("team.name LIKE '%" . $search . "%' OR
                     team.number LIKE '%"
. $search . "%' OR
                     costcenter_segment.name LIKE '%"
. $search . "%'")
as
$q)
{
    $this->query[] = array('id' => $q->team['id'],
                           'number' => $q->team['number'],
                           'name' => $q->team['name'],
                           'costcenter' => $q->costcenter_segment->costcenter['number'] . ' - ' . $q->costcenter_segment['name']);
}
?>

toto funguje bezproblémů, ale chtěl bych hledat i podle čísla nákl. střediska - tedy přidat do where('costcenter.number LIKE '%" . $search . "%') a to už nefunguje

jde to vůbec tímto způsobem?

Za případnou odpověď předem velice děkuji

Behe:

jej, tabulka team_costcenter je tam samozřejmě jen jednou.

Lukas:

Ahoj,

jenom bych chtěl poděkovat za NotORM (a vlastně i za Nette\Database). Ne že by mi to šetřilo práci, ale už nějakou dobu si tvořím vlastní framework (rád si napíšu vlastní než abych se učil cizí) a nad implementováním databází si lámu delší dobu hlavu, hlavně nad automatickým tvořením JOINů (tyhle bastardy píšu fakt nerad). Dost šmejdím po různých frameworcích a hotových řešeních, abych chytl inspiraci (takže se je stejně učím :-/ ). Jenže až díky NotORMu mě cosi "trklo" a já se konečně dopracoval k tomuto (což si myslím vypadá pěkně):

Schéma: http://imageshack.us/photo/my-images/840/dbschema.png/

Jako příklad bude třeba vybrat články seřazené podle nejnovějších komentářů a vybrat k nim i čas a jméno autora posledního komentáře:

<?php
$sql
= $db->table('articles')
    /* addReference přiřadí jakýsi alias cizím klíčům - comments.author_id => users.id */
    ->addReference('author', 'user')
    ->select(
        'id',    // automaticky se přepíše na articles.id
        'title',
        'author.name AS article_author',
        'MAX(comments.posted) AS last_comment_time',    // Agregační (i jiné) funkce lze použít
        'comments.author.name AS last_comment_author')
    ->order('last_comment_time DESC');¨

echo $sql->getSql();
?>
Vypíše
<?php
'
SELECT
    articles.id,
    articles.title,
    author.name AS article_author,
    MAX(comments.posted) AS last_comment_time,
    comments_author.name AS last_comment_author
FROM
    articles
LEFT JOIN
    users AS author ON
    articles.author_id = author.id
LEFT JOIN
    comments AS comments ON
    comments.article_id = articles.id
LEFT JOIN
    users AS comments_author ON
    comments.author_id = comments_author.id
GROUP BY
    articles.id
ORDER BY
    last_comment_time DESC
'
?>
Dobrý, ne?
Finta je v jednotném a množném čísle, díky tomu moje třída ví, zda má hledat author_id v tabulce comments nebo comment_id v tabulce users.

Pokud člověk není vyloženě prase a dodržuje nějakou (jednoduchou) konvenci při návrhu databáze, nepotřebuje ani reflection.

Chtěl jsem se zeptat na dvě věci:
Líbí?
Dokáže NotORM obdobně jednoduchým zápisem vygenerovat takovýto mnohanásobný JOIN?

A ještě se přiznám, že jsem použil toto http://www.kavoir.com/2011/04/php-class-converting-…-english.html pro konverzi z množného čísla na jednotné a opačně (nejsem typ člověka co se spokojí s přidáním "s" na konec, tabulku "propertys" bych nezkousl).

Ještě jednou moc díky Jakube za velkou inspiraci :)

(BTW toto není reklama na nic, svůj FW nejspíš nikdy nikde zveřejňovat nebudu, chtěl jsem jen ukázat, že NotORM pomáhá nejen svým uživatelům, ale dokonce i těm, kteří se snaží udělat si "něco jako NotORM sobě na míru" :D )

ikona Jakub Vrána OpenID:

NotORM klade jednoduché a rychlé dotazy. Tento dotaz nikdy rychlý nebude, protože MAX(comments.posted) se musí při každém dotazu vypočítat znovu, nejde nad ním postavit index. Takže s větším počtem článků bude toto řešení nesnesitelně pomalé.

Když bych to řešil já, tak bych schéma denormalizoval a pomocí triggeru řešil aktualizaci přidaných sloupců. Takže řešení by pak vypadalo takhle:

<?php
foreach ($db->article()->order("last_comment_time DESC") as $article) {
    $comment_author = $article->last_comment->author['name'];
}
?>

Lukas:

Díky za reakci, je vidět, že se mám ještě hodně co učit. Jediné štěstí, že jsem nikdy na MySQL nestavěl větší databáze. Skutečnost je taková, že osobně triggery (a obecně jakékoliv formy uložené procedury) příliš nemusím kvůli údržbě, člověk na ně musí myslet, nebo si aspoň pustit select * from information_schema.triggers aby věděl, co jaký trigger dělá nebo že tam vůbec nějaký trigger je. Zkušenější to asi sám vyčte už ze struktury, zeptá se "kdepak se mi tady bere ten last_comment_time?", ale u složitějších databází to jasné být nemusí a když má těch triggerů víc, musí pátrat. Máš na to nějakou bezva fintu nebo prostě píšeš komentáře?

Ukazuješ mi naprosto luxusní zápis
<?php $comment_author = $article->last_comment->author['name']; ?>
který je předpokládám ekvivalentní s mým ještě oblíbenějším
<?php $comment_author = $article->last_comment->author->name; ?>
jenže já se musím zeptat: jak NotORM pozná, do jaké tabulky má sáhnout, když se dotážu na $article->last_comment->author[] (tabulka authors neexistuje, chci tabulku users) ? Můžeme totiž například u každého článku chtít třeba i ID moderátora, který povolil zveřejnění neregistrovaným a opět se dotazujeme na tabulku users, je třeba ty názvy odlišit. Nebo dokonce odkazuji na stejnou tabulku (častý příklad: komentáře a reakce) Přiznám se, že mě docela zaskočilo, že to v článku není, protože tento problém se objevuje často a rozhodně si na to nechci psát reflection class, když to můžu vyřešit naprosto jednoduše
<?php $db->addReference('author', 'user')
    ->addReference('reply_to', 'comment'); ?>
případně jsem ještě přemýšlel o možnosti omezení na jednotlivé tabulky
<?php $db->addReference('articles.author', 'user')
    ->addReference('comments.author', 'user')
    ->addReference('comments.reply_to', 'comment'); ?>
Samozřejmě pracuji na MyISAM a cizí klíče neznám. Příklad cos mi ukázal, vypadá příliš "magicky" a nevyčtu z něj, jak NotORM pozná, že author je uložený v tabulce users. Myslím, že by to vyžadovalo jazykově vzdělanou neuronovou síť :-)

A ještě jednou díky za inspiraci a za reakci.

ikona Jakub Vrána OpenID:

Já pro správu databáze používám Adminer, kde triggery vidím u každé tabulky a taky je hned můžu upravit.

Někdo pro dopočítávané sloupce používá nějaký prefix, např. 'computed_'.

NotORM si může strukturu vyčuchat z cizích klíčů pomocí NotORM_Structure_Discovery. To jde samozřejmě jen u InnoDB. Jinak jde strukturu popsat podle vlastní konvence. Viz http://www.notorm.com/#structure.

Z MyISAM doporučuji přejít na InnoDB, zlepší se konzistence dat, sníží riziko jejich ztráty a při víceuživatelském přístupu (běžném u webových aplikací) se kupodivu může i zlepšit výkon.

Ještě ke sloupci reply_to_id – doporučuji přečtení http://php.vrana.cz/traverzovani-kolem-stromu-prakticky.php.

Lukas:

Dobrá tedy, pochopil jsem, že InnoDB řeší většinu věcí :-) Nicméně (můžu-li trošku OT) jak bys řešil například chat? Všichni píšou jak diví (InnoDB má pomalé inserty, pokud jsem tedy skutečně totálně nezaspal dobu - jakože zatím vidím že spíš jo), k tomu potřebuji cizí klíč author_id => users.id (nemůžu user_id, protože chci ještě třeba last_moderated_by_id => users.id) a i reply_to_id se hodí, když chci mít "namakaný" chat s reakcemi. Bojím se, že traverzování zde vůbec nepomůže, spíš naopak. Nicméně předělání komentářů pod články určitě zvážím, vypadá to vcelku užitečně.

<?php
$db
->addReference('author', 'user')
    ->addReference('reply_to', 'comment');
?>
vs
<?php
class UserStructure extends NotORM_Structure_Convention {
    function getReferencedTable($name, $table) {
        $keys = array(
            'author' => 'user',
            'reply_to' => 'comment'
        );

        if(isset($keys[$name])) {
            return $keys[$name];
        }

        return parent::getReferencedTable($name, $table);
    }
}
?>
Asi chápeš co se tím snažím říct. Jde-li nám prakticky jen o doplnění cizích klíčů do MyISAM (99% případů, řekl bych), hodí se nějaký jednodušší zápis. Proto taky NotORM vznikl - aby nám usnadnil práci s db, nemám pravdu? :-)

Znovu dík, lidi by ti měli za tvé reakce (a články) platit, protože jsou poučnější než 4 roky na střední obor IT!

ikona Jakub Vrána OpenID:

Ony ty inserty zase tak pomalé nejsou. Navíc i u chatu je mnohem častější čtení.

reply_to_id bych opravdu nedoporučoval, mimo jiné proto, že s ním nejde rozumně udělat stránkování. Spíš bych zvolil thread_id a lft+rgt z traverzování.

V NotORM jsem zvolil tohle řešení, protože je univerzální, dovoluje popsat prakticky jakoukoliv strukturu. Nicméně nic nebrání postavit nad tím nějakou další abstrakci, která bude omezenější, ale bude se snadněji používat.

Můj PayPal je např. na https://sourceforge.net/donate/?group_id=264133 :-). Jednu dobu byl i pod každým článkem, ale nikdo to nepoužíval…

Lukáš:

Super, asi už mě nenapadá žádná otázka, ty máš prostě řešení vždy na všechno :-) Dík za tvůj čas, já si jdu nastudovat InnoDB a pak si napíšu nějakou třídu na traverzování, vypadá to, že to budu potřebovat často, tak ať se to naučím.

PP nemám, ale nějaký drobný ti pošlu třeba přes nějakýho kámoše ;-) Adminer je k nezaplacení.

Petr:

Zdravím,
mám pár dotazů ohledně spojování tabulek:
mějme tabulky :
kategorie
-id
-název

kategorie_sekce
-kategorie_id
-sekce_id

sekce
-id
název

sekce_produkt
-sekce_id
-produkt_id

produkt
-id
-název

1. jak zjistím název kategorie, název sekce ainformace o produktu,  pokud znám id produktu ?

2. jak vypíšu, postupně kategorie, sekce a produkty
viz podobně jako v demo databázi, ale s třema tabulkama:
foreach ($software->application() as $application) {
    echo "$application[title]\n";
    foreach ($application->application_tag() as $application_tag) {
        echo "\t" . $application_tag->tag["name"] . "\n";
    }
}

3. pokoušel jsem se vytvořit join následovně:
$result = $dotaz->db->q_category()->where("q_category.id",1)
->join("q_category", "LEFT JOIN q_catSub ON q_category.id = q_catSub.q_category_id")
->join("q_catSub", "LEFT JOIN q_subCategory ON q_catSub.q_subCategory_id = q_subCategory.id")
->join("q_subCategory", "LEFT JOIN q_subProd ON q_subCategory.id = q_subProd.q_subCategory_id")
->join("q_products", "LEFT JOIN q_products ON q_subProd.q_products_id = q_products.id")
->select("q_category.*");

což vytvořilo dotaz:
SELECT q_category.* FROM q_category LEFT JOIN q_catSub ON q_category.id = q_catSub.category_id
* LEFT JOIN q_subCategory ON q_catSub.subCategory_id = q_subCategory.id
* LEFT JOIN q_subProd ON q_subCategory.id = q_subProd.subCategory_id
* LEFT JOIN q_products ON q_subProd.products_id = q_products.id
* WHERE (q_category.id = 1)

pokud jsem dal do selektu i výpis jiné tabulky, dotaz se zpřeházel... (chtěl jsem do selektu i výpis q_products.*)

Budu moc rád, za jakoukoli pomoc, "nakopnutí" správnou cestou.

Dkuji

ikona Jakub Vrána OpenID:

Struktura je dost podivná. Produkt může být ve více sekcích, dejme tomu. Ale sekce může být ve více kategoriích? To nedává smysl. Nicméně s touto strukturou databáze by kód vypadal takhle:

<?php
$produkt
= $notORM->produkt[$id];
foreach (
$produkt->sekce_produkt() as $sekce_produkt) {
   
$sekce = $sekce_produkt->sekce;
    foreach (
$sekce->kategorie_sekce() as $kategorie_sekce) {
       
$kategorie = $kategorie_sekce->kategorie;
    }
}
?>

Join vůbec není potřeba.

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.