New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LQL a best practices pro modely #46

Open
Tharos opened this Issue Apr 11, 2014 · 11 comments

Comments

Projects
None yet
3 participants
@Tharos
Owner

Tharos commented Apr 11, 2014

Rád bych zde vytvořil prostor pro diskuzi nad tímto nápadem.

@Tharos Tharos changed the title from LQL to LQL, best practices pro modely Apr 11, 2014

@Tharos Tharos changed the title from LQL, best practices pro modely to LQL a best practices pro modely Apr 11, 2014

@Tharos Tharos added the RFC label Apr 11, 2014

@mibk

This comment has been minimized.

Show comment
Hide comment
@mibk

mibk Apr 14, 2014

Contributor

A nějaké nápady, jak na to jít? Podporovat obě varianty? Tedy jak fluent (z ukázky), tak čistý dotaz $...->createQuery('select a, b from ...').

Contributor

mibk commented Apr 14, 2014

A nějaké nápady, jak na to jít? Podporovat obě varianty? Tedy jak fluent (z ukázky), tak čistý dotaz $...->createQuery('select a, b from ...').

@Tharos

This comment has been minimized.

Show comment
Hide comment
@Tharos

Tharos Apr 14, 2014

Owner

Mám uzrálou vcelku pokročilou představu, jak by to celé mohlo fungovat. Jak jsem naznačil, existovaly by tři vrstvy. Zatím se mi je obtížně pojmenovává, ale popsat je jsem schopný:

1. Helper pro sestavování fluentu

Půjde o vrstvu (v praxi možná o jednu třídu), která bude nápomoci při sestavování fluentu. Chceme-li například v dotazu získat hodnoty pro více entity najednou a shodují-li se nám napříč tabulkami názvy nějakých sloupců (třeba id…), nejspolehlivější je všechny potřebné sloupce v dotazu vyjmenovat a opatřit aliasy. No a protože tohle dělat ručně by byla hrozná pruda, tato vrstva tomu bude pomáhat. V uvedeném případě třeba tak, že potřebné sloupce vytáhne zjistí z reflexe a opatří aliasy.

Hlavním přínosem této třídy bude to, že ji bude interně využívat další vrstva, o které budu psát. :)

Může ji ale využívat i přímo programátor, a to buďto pro psaní dotazů úplně ručně (takových, které půjdou snadno „hydratovat“), anebo pro rozšíření dotazů, které půjde získat z následující vrstvy (ve formě instancí Fluent).

2. LQL

Půjde v podstatě o builder, který bude mít velmi podobné API, jako má Query Builder v Doctrine 2:

$query = $queryHelper->createDomainQuery();

$query->select('a, b')
    ->from(Author::class, 'a')
    ->join('a.books', 'b')
    ->where('b.name = ?', Some name)
    ->andWhere('a.name != ?', 'John Doe');

$authors = $query->getResult();

foreach ($authors as $author) {
    echo "$author->name\n;"
    foreach ($author->books as $book) {
        echo "- $book->name\n";
    }
}

Jsem rozhodně za nepodporovat čístý zápis LQL (nějaký string), protože mi to připadá zbytné. Generovat z toho builderu string, který by se pak zase parsoval, považuji za úplně zbytečný overhead. Ten čistý zápis by mi spíše dával smysl jako nadstavba nad tím builderem, ale popravdě řečeno jsem přesvědčen, že stejně by drtivá většina programátorů upřednostnila ten builder. Je to „objektové“, v PHP kódu to vypadá lépe, než nějaký string, IDE lépe napovídá… Takže teď bych se určitě soustředil jenom na ten builder.

Význam toho builderu je jasný. Půjde o vrstvu, která už bude maximálně praktické pro přímé použití v modelech či kdekoliv jinde v aplikaci.

3. Query objekty

Moc se mi líbí API LeanMapperQuery a Kdyby\Doctrine. Vzal bych to nejlepší z toho všeho a udělal bych z toho nějaký robustní základ. Považuji důležité, aby se ty query objekty překládaly do toho LQL (sekvence volání nad builderem), protože pak bude souzvuk jednotlivých vrstev optimální. Z toho důvodu už ty query objekty ale budou velmi tenkrá vrstva, protože ony už toho až tak moc řešit nebudou muset. Třeba LeanMapperQuery toho díky tomu, že se překládá přímo do low-level fluent volání, musí řešit podstatě více.


Tak co vy na to? :)

Owner

Tharos commented Apr 14, 2014

Mám uzrálou vcelku pokročilou představu, jak by to celé mohlo fungovat. Jak jsem naznačil, existovaly by tři vrstvy. Zatím se mi je obtížně pojmenovává, ale popsat je jsem schopný:

1. Helper pro sestavování fluentu

Půjde o vrstvu (v praxi možná o jednu třídu), která bude nápomoci při sestavování fluentu. Chceme-li například v dotazu získat hodnoty pro více entity najednou a shodují-li se nám napříč tabulkami názvy nějakých sloupců (třeba id…), nejspolehlivější je všechny potřebné sloupce v dotazu vyjmenovat a opatřit aliasy. No a protože tohle dělat ručně by byla hrozná pruda, tato vrstva tomu bude pomáhat. V uvedeném případě třeba tak, že potřebné sloupce vytáhne zjistí z reflexe a opatří aliasy.

Hlavním přínosem této třídy bude to, že ji bude interně využívat další vrstva, o které budu psát. :)

Může ji ale využívat i přímo programátor, a to buďto pro psaní dotazů úplně ručně (takových, které půjdou snadno „hydratovat“), anebo pro rozšíření dotazů, které půjde získat z následující vrstvy (ve formě instancí Fluent).

2. LQL

Půjde v podstatě o builder, který bude mít velmi podobné API, jako má Query Builder v Doctrine 2:

$query = $queryHelper->createDomainQuery();

$query->select('a, b')
    ->from(Author::class, 'a')
    ->join('a.books', 'b')
    ->where('b.name = ?', Some name)
    ->andWhere('a.name != ?', 'John Doe');

$authors = $query->getResult();

foreach ($authors as $author) {
    echo "$author->name\n;"
    foreach ($author->books as $book) {
        echo "- $book->name\n";
    }
}

Jsem rozhodně za nepodporovat čístý zápis LQL (nějaký string), protože mi to připadá zbytné. Generovat z toho builderu string, který by se pak zase parsoval, považuji za úplně zbytečný overhead. Ten čistý zápis by mi spíše dával smysl jako nadstavba nad tím builderem, ale popravdě řečeno jsem přesvědčen, že stejně by drtivá většina programátorů upřednostnila ten builder. Je to „objektové“, v PHP kódu to vypadá lépe, než nějaký string, IDE lépe napovídá… Takže teď bych se určitě soustředil jenom na ten builder.

Význam toho builderu je jasný. Půjde o vrstvu, která už bude maximálně praktické pro přímé použití v modelech či kdekoliv jinde v aplikaci.

3. Query objekty

Moc se mi líbí API LeanMapperQuery a Kdyby\Doctrine. Vzal bych to nejlepší z toho všeho a udělal bych z toho nějaký robustní základ. Považuji důležité, aby se ty query objekty překládaly do toho LQL (sekvence volání nad builderem), protože pak bude souzvuk jednotlivých vrstev optimální. Z toho důvodu už ty query objekty ale budou velmi tenkrá vrstva, protože ony už toho až tak moc řešit nebudou muset. Třeba LeanMapperQuery toho díky tomu, že se překládá přímo do low-level fluent volání, musí řešit podstatě více.


Tak co vy na to? :)

@Tharos

This comment has been minimized.

Show comment
Hide comment
@Tharos

Tharos Apr 14, 2014

Owner

No a dále bych chtěl upozornit na to, že ta první vrstva se už rýsuje. :)

V tomto testu je vidět, jak ji lze použít a k čemu vlastně slouží.

Takhle skládat dotazy je pořád dost „dřevácké“ a předpokládám, že každý v praxi použije hlavně LQL, ale už nyní je z kódu patrné, jak šikovnou práci ten helper odvádí.

Owner

Tharos commented Apr 14, 2014

No a dále bych chtěl upozornit na to, že ta první vrstva se už rýsuje. :)

V tomto testu je vidět, jak ji lze použít a k čemu vlastně slouží.

Takhle skládat dotazy je pořád dost „dřevácké“ a předpokládám, že každý v praxi použije hlavně LQL, ale už nyní je z kódu patrné, jak šikovnou práci ten helper odvádí.

@achtan

This comment has been minimized.

Show comment
Hide comment
@achtan

achtan Apr 15, 2014

$connection->select($queryHelper->formatSelect(Author::class) + $queryHelper->formatSelect(Book::class)) mi pride ako dost dlhy a pracny zapis, nechcel by si to trocha prerobit napr na: $connection->select(Author::class . ' AS a, ' . Book::class . ' AS b') ?

a co sa tyka tych prefixou asi by bolo dobre robit prefxi ako ma doctrina ze tam rpidava aj cislo tabulky, ak nahodou pouzijes tu istu tabulku v jednom query

inak super praca, paci sa mi tento koncept.

A asi by bolo fajn mat ako prvu vec query objekt nakolko imho to je dost zavazna a potrebna vec. Nehovorim ze LQL nieje, ale ako si sam pisal toto LQL robis preto aby si vedel poriesit niektore specificke situacie...

achtan commented Apr 15, 2014

$connection->select($queryHelper->formatSelect(Author::class) + $queryHelper->formatSelect(Book::class)) mi pride ako dost dlhy a pracny zapis, nechcel by si to trocha prerobit napr na: $connection->select(Author::class . ' AS a, ' . Book::class . ' AS b') ?

a co sa tyka tych prefixou asi by bolo dobre robit prefxi ako ma doctrina ze tam rpidava aj cislo tabulky, ak nahodou pouzijes tu istu tabulku v jednom query

inak super praca, paci sa mi tento koncept.

A asi by bolo fajn mat ako prvu vec query objekt nakolko imho to je dost zavazna a potrebna vec. Nehovorim ze LQL nieje, ale ako si sam pisal toto LQL robis preto aby si vedel poriesit niektore specificke situacie...

@Tharos

This comment has been minimized.

Show comment
Hide comment
@Tharos

Tharos Apr 15, 2014

Owner

Co se zápisu týče, jak už jsem psal, předpokládám, že v praxi bude koncový programátor tyto metody Query Helperu používat úplně minimálně. A ta další vrstva nad tím bude mít už mnohem příjemnější API.

Co se aliasů týče, to už samozřejmě lze. :)

Co se Query Objektu týče, tak, jak jej plánuji, se ale bez těch vrstev pod ním nelze obejít. Bohužel… Než bude tohle hotové, mohu vřele doporučit LeanMapperQuery, který je velmi zdařilý. Jeho největší slabinu vidím právě v tom, že se překládá do low-level Fluent volání, takže právě musí na každém kroku řešit detaily O/R mapování. Plus, pokud by se rezignovalo na podporu PHP 5.3, bych možná nějaké věci uvítal mít spíše jako traity. Ale to nic nemění na tom, že to je výborný počin, ze kterého sám budu čerpat inspiraci.

Owner

Tharos commented Apr 15, 2014

Co se zápisu týče, jak už jsem psal, předpokládám, že v praxi bude koncový programátor tyto metody Query Helperu používat úplně minimálně. A ta další vrstva nad tím bude mít už mnohem příjemnější API.

Co se aliasů týče, to už samozřejmě lze. :)

Co se Query Objektu týče, tak, jak jej plánuji, se ale bez těch vrstev pod ním nelze obejít. Bohužel… Než bude tohle hotové, mohu vřele doporučit LeanMapperQuery, který je velmi zdařilý. Jeho největší slabinu vidím právě v tom, že se překládá do low-level Fluent volání, takže právě musí na každém kroku řešit detaily O/R mapování. Plus, pokud by se rezignovalo na podporu PHP 5.3, bych možná nějaké věci uvítal mít spíše jako traity. Ale to nic nemění na tom, že to je výborný počin, ze kterého sám budu čerpat inspiraci.

@mibk

This comment has been minimized.

Show comment
Hide comment
@mibk

mibk Apr 16, 2014

Contributor

@Tharos Mě se ten návrh líbí 👍 . Budu sledovat, jak se to postupně vyvíjí, abych mohl případně s něčím pomoct.

Contributor

mibk commented Apr 16, 2014

@Tharos Mě se ten návrh líbí 👍 . Budu sledovat, jak se to postupně vyvíjí, abych mohl případně s něčím pomoct.

@Tharos

This comment has been minimized.

Show comment
Hide comment
@Tharos

Tharos Apr 19, 2014

Owner

Takže, součástí LeanQuery je další třída zvaná Hydrator, která řeší vlastní „hydrataci“ relace získané pomocí SQL (ať už sestaveného ručně, pomocí třídy QueryHelper či jakkoliv jinak) do vzájemně propojených instancí Result. Třídě Hydrator se pouze předá zmíněná relace a pár metadat, aby se hydrátor v té relaci vyznal.

Hydrátor produkuje pole instancí Result, které už jsou požadovaným způsobem vzájemně propojené (vazby jsou již inicializované). To, jakým způsobem se mají výsledky pospojovat, vyjadřuje parametr $relationships. Je zajímavý tím, že umožňuje propojit výsledky i oboustranně (lze traverzovat z knih na autory a z autorů zase zpátky na knihy, aniž by se pokládal další dotaz).

No, asi to zní dost divoce, ale vše by mělo být lépe srozumitelné z testu.

Byť API této třídy není úplně otřesné (zejména to, jak se zapisují $relationships, považuji za zdařilé), stále ještě se jedná o část, kterou asi málokdo bude přímo použítvat. Primárně bude interně používaná v LQL.


Všimněte si, jak je celé hydratování hezky výkonné. Každá položka v relaci se projde jen jednou. Uvědomuji si, že například k Doktríně je hydratace výsledku často hodně úzké hrdlo, a proto se v LQL od začátku snažím, aby řešení bylo maximálně efektivní.

Nyní už zbývá jenom posadit něco na připravenou půdu. Stay tunned. :)

Owner

Tharos commented Apr 19, 2014

Takže, součástí LeanQuery je další třída zvaná Hydrator, která řeší vlastní „hydrataci“ relace získané pomocí SQL (ať už sestaveného ručně, pomocí třídy QueryHelper či jakkoliv jinak) do vzájemně propojených instancí Result. Třídě Hydrator se pouze předá zmíněná relace a pár metadat, aby se hydrátor v té relaci vyznal.

Hydrátor produkuje pole instancí Result, které už jsou požadovaným způsobem vzájemně propojené (vazby jsou již inicializované). To, jakým způsobem se mají výsledky pospojovat, vyjadřuje parametr $relationships. Je zajímavý tím, že umožňuje propojit výsledky i oboustranně (lze traverzovat z knih na autory a z autorů zase zpátky na knihy, aniž by se pokládal další dotaz).

No, asi to zní dost divoce, ale vše by mělo být lépe srozumitelné z testu.

Byť API této třídy není úplně otřesné (zejména to, jak se zapisují $relationships, považuji za zdařilé), stále ještě se jedná o část, kterou asi málokdo bude přímo použítvat. Primárně bude interně používaná v LQL.


Všimněte si, jak je celé hydratování hezky výkonné. Každá položka v relaci se projde jen jednou. Uvědomuji si, že například k Doktríně je hydratace výsledku často hodně úzké hrdlo, a proto se v LQL od začátku snažím, aby řešení bylo maximálně efektivní.

Nyní už zbývá jenom posadit něco na připravenou půdu. Stay tunned. :)

@achtan

This comment has been minimized.

Show comment
Hide comment
@achtan

achtan Jun 6, 2014

ahoj, kedy planujes relesnut QO a LQL ?

achtan commented Jun 6, 2014

ahoj, kedy planujes relesnut QO a LQL ?

@Tharos

This comment has been minimized.

Show comment
Hide comment
@Tharos

Tharos Jun 6, 2014

Owner

Dej mi tak patnáct minut, budu pushovat jednu takovou malou ochutnávku. :)

V Lean Query chybí ze základních věcí už jenom WHERE, které určitě dokončím během příštího týdne, protože ho teď sám potřebuji.

QO nad tím bude doufám úplně triviální, takže snad bude vzápětí následovat.

Owner

Tharos commented Jun 6, 2014

Dej mi tak patnáct minut, budu pushovat jednu takovou malou ochutnávku. :)

V Lean Query chybí ze základních věcí už jenom WHERE, které určitě dokončím během příštího týdne, protože ho teď sám potřebuji.

QO nad tím bude doufám úplně triviální, takže snad bude vzápětí následovat.

@achtan

This comment has been minimized.

Show comment
Hide comment
@achtan

achtan commented Jun 6, 2014

image

@Tharos

This comment has been minimized.

Show comment
Hide comment
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment