Skip to content
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
Open

LQL a best practices pro modely #46

Tharos opened this issue Apr 11, 2014 · 11 comments
Labels

Comments

@Tharos
Copy link
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 LQL LQL, best practices pro modely Apr 11, 2014
@Tharos Tharos changed the title LQL, best practices pro modely LQL a best practices pro modely Apr 11, 2014
@Tharos Tharos added the RFC label Apr 11, 2014
@mibk
Copy link
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
Copy link
Owner Author

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
Copy link
Owner Author

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
Copy link

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
Copy link
Owner Author

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
Copy link
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
Copy link
Owner Author

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
Copy link

achtan commented Jun 6, 2014

ahoj, kedy planujes relesnut QO a LQL ?

@Tharos
Copy link
Owner Author

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
Copy link

achtan commented Jun 6, 2014

image

@Tharos
Copy link
Owner Author

Tharos commented Jun 6, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants