PHP.EE FOORUM   
Nimi:   Pass:   Mäleta mind! 
   Teemad | php.ee esilehele | registreeri | Märgi kõik teemad loetuks | #php.ee Skype vestlus | RSS
UUS TEEMA  OTSI  Lehekülgi: 1
Kirjutasin ActiveRecord klassi
Postitaja: Seramis 2013-09-22 12:26:14
https://github.com/Seramis/PHP_ActiveRecord

Sellise asjaga olen siin nüüd natuke aega tegelenud. Jah, selliseid asju on terve nett täis, kuid ühtpidi õpib nii alati paremini ja teistpidi pole ma leidnud päris sellist ActiveRecord lahendust, nagu sooviks. Võib-olla vaatan ma seda teemat veidi teise nurga alt, kui teised. Seetõttu tegingi selle, sest ta on võib-olla natuke teistmoodi, kui kõik teised.

Ühesõnaga laske hea maitsta ja igasugused mõtted sellel teemal on väga teretulnud. Ega ei pruugi ka mina kõikide probleemide ja lahenduste peale tulla. ;)
RE: Kirjutasin ActiveRecord klassi
Postitaja: rtfm 2013-09-23 12:54:05
ja nüüd , et sa midagi sellest õpiks, proovi järgmisena teha sama asi , aga nii, et selle kasutamiseks peaks genereerima 99% vähem boilerplate koodi
RE: Kirjutasin ActiveRecord klassi
Postitaja: Seramis 2013-09-23 15:15:17
Ja ongi plaanis. Väike generaator nt mysql'i jaoks, mis loeb maha tabeli struktuurid baasist ja kribab sulle valmis Base_User klassi. Base_User extends ARActiveRecord ja User extends Base_User.

Generaator kribab lisaks koodile ka nii täpse phpdoc'i, kui võimalik, seega on iga property täielikult dokumenteeritud. (Nii täielikult, kui ta seda baasis on)

Seega juba plaanis. ;)
RE: Kirjutasin ActiveRecord klassi
Postitaja: tobre 2013-09-23 22:50:50
Minule jääb paraku lõplik eesmärk selgusetuks. Ühelt poolt ma saan aru, et sellega õpib palju ja olengi sellega täiesti nõus. Omal ajal sai ka paar AR implementatsiooni tehtud ja olen õnnelik, et need kõik prügikastis on oma koha leidnud.

Teiselt poolt ma ei saa aru sinu pointist, mille pärast see on parem kui mõni muu active record impl? Praegusel hetkel on tal ridamisi puudusi mõnede teiste AR-ide ees.

Seega ma tõesti soovitan sul läbi mõelda, et kas sinu elu mugavaks tegemine on ka teiste elu mugavaks tegemine. Sa võid mulle vastu vaielda, et sa kasutad seda ainult enda projektides ja keegi teine sinu koodi ei puutu, aga vaevalt nii läheb.

Mul on hea meel, kui see teema kunagi lõppeb lausega, et "sain palju targemaks ja tänu sellele improvesin hoopis olemasolevat ActiveRecord/ORM raamistikku X, Y või Z."

Ma ei taha vinguda, aga ma ei saa ka vait olla siis, kui näen, et jälle leiutatakse jalgratast.

Edu!
RE: Kirjutasin ActiveRecord klassi
Postitaja: tobre 2013-09-23 22:59:08
Vaatasin just uuesti diffi, mis sa viimati committinud oled.

https://github.com/Seramis/PHP_ActiveRecord/commit/b883f91095f4e167c3b7b790bf79d97a3afa2fb4

Ja et sa ei arvaks, et ma lihtsalt vingun, ütlen et selle kahe koodireaga oled sa ka kaks väga suurt viga sisse kirjutanud.

Esiteks pole su mudelid enam unittestitavad ja trigger_errori asemel peaksid viskama exceptioni.

Rohkem edasi ei hakanud vaatama. Palun vaata mõnda olemasolevat raamistikku ja küsi parem nõu, miks nad mõnda asja nii teevad nagu nad teevad. :)

Edu!
RE: Kirjutasin ActiveRecord klassi
Postitaja: Seramis 2013-09-24 08:10:34
Et mille poolest ta parem on? Selle poolest näiteks, et erinevalt enamus AR implementatsioonidest, ei pea sa näiteks uut sql'i laadset keelt õppima!
Ja ei genereerita on-the-fly sulle mingeid meetodeid, mida su IDE elusees ei suuda auto-suggest'ida.
Ja kõik ülejäänud punktid on ka github'is välja toodud.

Olen propel'it, doctrine'i ja php activerecord'it uurinud ja puurinud küll ja ikka jube kohmakad on. Mis pagana pärat pean nt AR olema nii tark ja oskab öelda, et "kuule, sellise e-mailiga tegelane on juba olemas"? Selleks on mudel/validaator ja AR'i ei huvita see teema üldse. (Igaüks tehku ühte asja ja tehku seda hästi)


trigger_error vs throw new exception... point on õige, aga suhteliselt väike asi, mille kallal nuriseda, kas pole? :P

Ja lõpetuseks, tobre, uuri veidi MVC'd ja saad teada, et AR != Model. AR istub Model'i all/sees ja unittestid ja Model'it, mitte AR'i. Mina sain selle siin eriti selgeks, kui oma AR'i kirjutasin, kuidas on lood sinuga? Ega sa templeidi faili view'ks pea MVC's? :P
RE: Kirjutasin ActiveRecord klassi
Postitaja: tobre 2013-09-24 11:31:00
TSITEERITUD:
Et mille poolest ta parem on? Selle poolest näiteks, et erinevalt enamus AR implementatsioonidest, ei pea sa näiteks uut sql'i laadset keelt õppima!
Ja ei genereerita on-the-fly sulle mingeid meetodeid, mida su IDE elusees ei suuda auto-suggest'ida.
Ja kõik ülejäänud punktid on ka github'is välja toodud.

Olen propel'it, doctrine'i ja php activerecord'it uurinud ja puurinud küll ja ikka jube kohmakad on. Mis pagana pärat pean nt AR olema nii tark ja oskab öelda, et "kuule, sellise e-mailiga tegelane on juba olemas"? Selleks on mudel/validaator ja AR'i ei huvita see teema üldse. (Igaüks tehku ühte asja ja tehku seda hästi)


trigger_error vs throw new exception... point on õige, aga suhteliselt väike asi, mille kallal nuriseda, kas pole? :P

Ja lõpetuseks, tobre, uuri veidi MVC'd ja saad teada, et AR != Model. AR istub Model'i all/sees ja unittestid ja Model'it, mitte AR'i. Mina sain selle siin eriti selgeks, kui oma AR'i kirjutasin, kuidas on lood sinuga? Ega sa templeidi faili view'ks pea MVC's? :P


Palun võta vastu mõningane kriitika ja arutame asjade üle konstruktiivselt, mitte ära lahmi teadmata, kas ma olen veidi uurinud MVC-d või MVC patterni järgi aastaid progenud.

Nagu ma ütlesin, siis mulle meeldib sinu entusiasm, aga ma ei kannata arrogantsust ning jalgratta leiutamist.

Paranda, kui ma eksin, aga mul on tunne, et sa oled visanud AR ja ORMi täiesti ühte katlasse? Tegelikult on neil implementatsioonidel vahe sees, kui sa teed AR implementatasiooni, siis see on kõigest ORMi üks osa.

Aga võtame siis punktide haaval:
* Sa ütled, et nende puhul pead SQL laadset keelt õppima.
Ei pea, kui ei taha, võid MySQLi ka kirjutada, enamik ORM raamistikest võimaldab kirjutada DML-is, mis transleeritakse mingisuguseks abstraktseks SQLiks, mis siis omakorda transleeritakse vastavaks SQLiks. Tegelikult on see layerdamine suurema eesmärki nimel, kuna SQLid ei ole standardsed ja nende vahel on variatsioone, siis kasutades abstraktset päringukeelt, siis piirab sind põhimõtteliselt ainult andmebaasi draiverite toetus. Kui sa nüüd küsid, et millal peaks rakenduse andmebaasi taga ära vahetama, siis see on täiesti uus topic, millest võin sulle palju rääkida. Aga mõnikord on seda vaja.

* On-the-fly meetodite genereerimine.
Ma ei ole kohanud, et ühtegi meetodit on-the-fly koodi genereeritakse. Pigem ikka kasutades läbi __call magic meetodit luuakse võimalus teha stiilis päringuid findByNameAndStatus(), mis on ju tegelikult väga mugav. Olen sinuga nõus, IDEdele see ei meeldi ja ise ma ei ole ka seda väga palju praktiseerinud, vastav interface on ikka service kihis juba eelnevalt ära defineeritud. Siinkohal on sinu näites propertyte defineerimine läbi magic __set meetodi sama. IDE ei tea, mis sul mudelis on.

* Mis pagana pärat pean nt AR olema nii tark ja oskab öelda, et "kuule, sellise e-mailiga tegelane on juba olemas"?
Kahe otsaga asi. Küsin vastu, mis pärast andmebaas sulle vastu näppe andma peaks, kui sa progeda ei oska? No hästi, kasutad mingisugust andmebaasiadapterit, PDO-d näiteks. Nüüd annab PDO sulle vastu näppe. Aga see on ikkagi 2 kihti liiga sügaval. Exceptioni võiksid saada juba inputi valideerimisel, kui sealt midagi läbi läheb, siis võiks vähemalt mudel sulle vastu näppe anda.

* Unittestid
Ma väidan, et sinu mudelit ei saa unittestida, kuna sinu ARi, kuhu mudel extendib on võimatu mockida, seda enam, et AR sekkub andmete manipulatsiooni.

Lõpetuseks soovitan sul mõelda Model vs Entity teema peale, mõelda mis nende vahe on ja kuidas see sind aitaks.

Edu!
RE: Kirjutasin ActiveRecord klassi
Postitaja: Seramis 2013-09-24 17:18:44
Ära nüüd nii tõsiselt võta. See, et ma põhjendan oma valikuid, ei ole mitte teps arrogantsus, kriitika mitte talumine vmt, vaid just see sama diskuteerimine... mõtlemine, mida ja kuidas teha. Sa süüdistad mind milleski, mida pole teinud ja soovitad teha seda, mida juba teen. Võta sa paremini vastu väljakutsed, mida ma vastates toon. ;)

SQL'id:
Ei ole leidnud ühtegi AR/ORM raamistikku, mis käsitsi kirjutatud SQL'i puhul suudab ise laadida sisse kõrvalised objektid. On igasugu "poolkõvasid" lahendusi tehtud, aga sellist pole veel näinud. (näita, kuid tead mõnda, huvitab väga.)
Kirjuta ANSI/ISO Standard SQL'i ja ka baasi vahetudes ei ole sul enamasti probleemi. (Ei, sa ei pea mulle seletama, millal või miks peaks aplikatsiooni taga andmebaasid välja vahetama.)

On-The-Fly:
__call()'i ja __callStatic()'ut just mõtlesingi. Ja neid ei soovitagi ette. Kui väljade arv baasis on piiratud (ja sa võid need kenasti phpdoc'iga ära kirjeldada), siis võid veidi matemaatikat praktiseerides arvutada, palju peaks defineerima meetodeid phpdoc'is, et kõik kombinatsioonid paika saaks. Pealegi, staatiliste meetodite jaoks ei näe phpdoc ette mingit @methodStatic tag'i. @method on ise-enesest olemas.

AR tarkus:
Kui input tuleb, siis sa valideerid selle ikka enne ära, kui see AR'i jõuab. PDO error on väga süsteemi sisene viga nagunii, sellest ei päästa sind miski.

Unittestid:
Millal ma ütlesin, et mudel peab AR'i extendima? Ta võib, kui sa tahad väiksema süsteemi puhul kiiremini "pilti ette saada", aga suurte süsteemide puhul on sul mudel nagunii eraldi. Kui võtad ka minu AR'i endale ette olemasolevasse süsteemi, siis eeldusel, et sul kõik normaalselt kribatud on, on sul juba mõistlikud mudelid ees.
http://karwin.blogspot.com/2008/05/activerecord-does-not-suc

Ütled küll, et kõik on valesti, aga et kuidas siis midagi teha, selle soovitamine on veidi raske tükk, nagu aru saan?

Lähen ja loen nüüd model vs entity't ja saan targemaks. :D
RE: Kirjutasin ActiveRecord klassi
Postitaja: Seramis 2013-09-24 17:35:08
http://blogsh.de/2011/09/08/models-vs-entities/

Kas see vastab tõele, või mitte? Kui ja, siis mida ma siin kogu aeg räägin. Seal näites ka ORM entity rollis (kus oleks ka minu AR) ja model eraldi. Miks sa kogu aeg arvad, et ma räägin neist kahest kui ühest asjast? Täiesti vastupidi ju. Sina olid see, kes ütles, et AR on mudel ja nüüd on seda raske testida...

Kui see artikkel aga valetab, palun anna mõni link, mis sinu tõe välja räägib.
RE: Kirjutasin ActiveRecord klassi
Postitaja: tobre 2013-09-24 18:30:56
TSITEERITUD:
http://blogsh.de/2011/09/08/models-vs-entities/

Kas see vastab tõele, või mitte? Kui ja, siis mida ma siin kogu aeg räägin. Seal näites ka ORM entity rollis (kus oleks ka minu AR) ja model eraldi. Miks sa kogu aeg arvad, et ma räägin neist kahest kui ühest asjast? Täiesti vastupidi ju. Sina olid see, kes ütles, et AR on mudel ja nüüd on seda raske testida...

Kui see artikkel aga valetab, palun anna mõni link, mis sinu tõe välja räägib.


Vastan su teistele küsimustele pärast, aga et sind mitte pidurdada teadmiste ammutamisel, siis ma ütlen, et see artikkel on väga hea ja räägib täpselt õigest asjast.

Bookmarkisin selle ise ka kohe ära.

Lisaks võid uurida ka service layer patterni. Igatahes need ja MVC on kindlasti mustrid, mida ühest enterprise prjektis on ja peaksid olema. Vaata, et sa oma ARiga neid kinni ei proge. See on minu jutu point. :)
RE: Kirjutasin ActiveRecord klassi
Postitaja: tobre 2013-09-25 00:29:34
Lubasin, et kommenteerin su eelnevat postitust ka.

TSITEERITUD:

Ei ole leidnud ühtegi AR/ORM raamistikku, mis käsitsi kirjutatud SQL'i puhul suudab ise laadida sisse kõrvalised objektid. On igasugu "poolkõvasid" lahendusi tehtud, aga sellist pole veel näinud. (näita, kuid tead mõnda, huvitab väga.)
Kirjuta ANSI/ISO Standard SQL'i ja ka baasi vahetudes ei ole sul enamasti probleemi. (Ei, sa ei pea mulle seletama, millal või miks peaks aplikatsiooni taga andmebaasid välja vahetama.)

Mida sa pead silmas sisse kõrvaliste objektide sisselaadimisega? Kas seda, et kui kirjutan custom abstraktset raamistiku SQLi, siis oleks tulemuseks juba collection entititest või üks konkreetne entity? Kui sa seda mõtled, siis see on korraliku ORM raamistiku üks peamisi featuure. Võin nimetada mitmeid selliseid ORM raamistikke ja mitte ainult PHP maailmast.

Samuti on väga tore, kui sa ANSI standardi järgi kirjutad, aga selge on see, et MySQL ANSI standardist lugu suurt ei pea ning migreerimisel tekib sul toredaid probleeme. Välja võiks tuua AUTO_INCREMENT vs SEQUENCE, column aliased postgres alati ASiga olema, agregeerimisfunktsioonid tahavad group by-sse kõiki argumente, üle mille agregeeriti jne + veel paljud funktsioonid, mis käituvad erinevalt.
Seega kindlasti ei saa öelda seda, et "ohh, ma kirjutan standardi järgi ja probleeme ei teki." Isegi projekti puhul, milles on kasutatud korralikke raamistikke, mis seda peaks toetama, tekib probleeme. Vahe on lihtsalt selles, kui valutult need probleemid lahenevad.

TSITEERITUD:

Ütled küll, et kõik on valesti, aga et kuidas siis midagi teha, selle soovitamine on veidi raske tükk, nagu aru saan?


Ei ole raske, ma lihtsalt ei vaevunud, aga kui sa juba küsid, siis mõned mõtted lisaks eelnevatele mõtetele, mis ma eelnevalt jaganud olen:

* Transaktsioonid. Ilma transaktsioonideta pole sellega midagi peale hakata. Siinkohal tuleks jälle arvestada nested transactionitega ka. No ja siis võid ette kujutada, et läheb lõbusaks.
* Cache draiverid, praegu sul ainult array cache, järgmine request hakkab jälle kõik baasi möll uuesti otsast peale. Korralikus raamistikus on ka see kenasti sisseehitatud erinevad cache draiverid: APC, memcache, xcache jne, kus cachetakse ära metadata, päringu tulemused, mida sa ütled, et on vaja cacheda jne. Samuti saad kasutada vastavalt oma production env järgi seda draiverit, mida env toetab.
* Listener pluginad. Sagedaste tegevuste jaoks oleks neid hea kirjutada.
* Column change tracking või mutation tracking, kuidas iganes seda nimetada. Uuri ise selle kohta.
* Locking, nii optimistic kui pessimistic. Väga sagedane probleem rida lukku panna niikaua kui muudetakse või muud toimingut tehakse. Eriti siis kui tekib mõni race conditioni võimalus
* Versioneerimine. Jällegi sagedane probleem, kuidas andmebaasis hoida versioone kirjetest.
* jne

Ma võiksin veel pikalt jätkata. Nagu näha on kõik need loetletud asjad väga vajalikud ära lahendada persistence layeris. Või vaidled mulle vastu?
Igatahes, kui sa kõik need ja teised featuurid ära realiseerid, siis on sinu raamistikust saanud üks neist kelle moodi sa olla ei taha. Kui sa need implementeerimata jätad, siis kirjutad sa oma rakendusse palju korduvat koodi. Esimese variandi puhul on veel see miinus ka juures, et sa kulutad selleks kohutavalt palju oma aega. Samuti kui keegi peaks üle võtma projekti, kus sellist põlve otsas tehtud raamistikku kasutatud on, siis tekib tal kohe probleemid. Ta ei ole kursis, kuidas asjad käivad, sest suure tõenäosusega dokumentatsiooni pole või on algeline, googeldades tulemusi ei tule jne. Samuti kui midagi tõsist juurde vaja teha, siis tehakse pigem häkk kuhugi.

Need on pointid, miks tasuks sügavalt kaaluda enne ise sellise asja kirjutamist. Üksinda on väga raske teha midagi paremat, kui juba olemasolev, mis on ennast tõestanud. Teiseltpoolt ma suhtun väga skeptiliselt sellistesse ideedesse, nagu sul: "õu, mul on äge idee, sest kõik mida ma vaadanud olen on nii suured ja kohmakad, aga ma ei ole ühtegi neist päriselt kasutanud". Parem oleks, kui sa võtaksid need implementatsioonid ette, kasutaksid neid reaalselt ja siis näeksid, mis probleemid on, mida saaks paremini või teistmoodi teha ja siis teed seda kogemust arvestades.

Ma ei väida, et kuskilt alustada on vale. Kuskilt peab alustama, võib-olla läheb suuremaks crowdsourcinguks ja saad omale community taha seda arendama - võib-olla, aga minu meelest on see sellise konseptsiooniga unlikely.

Edu!
RE: Kirjutasin ActiveRecord klassi
Postitaja: Seramis 2013-09-25 08:51:11
Tänud põhjaliku vastuse eest. Osade asjadega täiesti nõus, osadega natuke veel diskuteeriks.

Kõrvalised objektid on N+1 näide github'i readme's. ;) Ja osad ORM'id ja AR'id isegi teevad midagi sarnast, aga enamasti on metsikult mingeid options array'sid (Array'd on pahad, kuna key'd ja value'd on stringid - typo'd kerged tulema ja ide ei soovita midagi ette) ja ka siis enamasti, mis juhtub on 2 query't ühe asemel:
SELECT * FROM my_table WHERE [minu tingumused]
SELECT * FROM table2 WHERE id IN(...)
Minu raamistik võtab aga konkreetselt sinu kirjutatud query tulemuse ja kasutab sellest nii palju ära, kui vähegi võimalik: Kui ükskõik millise kõrvalise objekti id on olemas, loob objekti juba cache'i, kui on olemas ka full data, preloadib selle objekti kohe ära. Ja seda ainult ühe query'ga, mille kirjutad sina täpselt sellise, nagu tahad ja selectid välja täpselt need väljad, mis tahad.

Universaalse sql'i kribamiseks võib ju ka täiesti eraldi klassi/mooduli vmt ehitada... Taaskord, AR'i ei tasu suureks ajada vaid õiged lego klotsid tuleb pannna õigesse kohta. Ehk ma pigem eelistaks vaadata selles suunas, et sa genereerid ise sql'i valmis, sql'i genereerimiseks kasutad aga oma lemmik generaatorit, taaskord. Hetkel veel väike probleem, et kuidas AR oskaks oma querysid teha läbi selle mooduli ilma, et ta väga teaks sellest moodulist midagi (et ei tekiks dependancy't), aga ma veel mõtlen selle peale...

===

Transaktsioonid: Alustamist ja lõpetamist ütled sa oma enda PDO objektile (mis haldab php pdo'd; näiteks) ja nested transaktsioonid on lihtsad (vähemalt kui sinust õigesti aru sain): Minu enda PDO klass oskab pidada counter'it, palju transaktsioone alustatud ja korras... Olen sellise "oma pdo" klassi teinud, töötab väga hästi. Pole seda lihtsalt github'i visanud.

Cache: Nõus, eks veidi mõtteski olnud, et kui mitte muud moodi, siis data ise võiks olla kuskil "laiemas" cache'is kui seda on konkreetne php protsess, mis kliendile lehte genereerib.

Listener pluginad: Aga just seepärast mul ongi _pre* ja _post* meetodid. Haagi sinna külge oma lemmik event handler ja korras. ;)

Column change tracking...: Kas see ei ole põhimõtteliselt see sama, mis eventid-listenerid? Kui ei, mida peaksin täpsemalt googeldama, et taas targemaks saada?

Locking: Uurin, vaatan, mida täpselt selle all mõtled.

Versioneerimine: See lahendatakse tavaliselt ju mudeli tasemes ära... Mite AR'is. Ehk tase kõrgemal.

Ehk lõpetuseks, AR'i suhtes tahan, et AR teeb ainult seda, mida AR tegema peab. Jah, on veel mõned asjad, mida võiks ehitada (nt cache), aga nii mõndagi ei tahaks põhimõtte pärast AR'i sisse kirjutada, vaid pigem kõrvale eraldi moodulina ja ehitada asjad nii, et sa saaksid ükskõik mida, ükskõik kuidas omavahel ise kokku panna. (Teed oma AR klassi, mis extendib minu AR'i ja implementeerib kõik _pre* ja _post* meetodid ära, mis saadavad event'e sinu event handlerile... miks mitte?)
RE: Kirjutasin ActiveRecord klassi
Postitaja: tobre 2013-09-25 22:32:55
Diskuteerin hea meelega.

TSITEERITUD:


Kõrvalised objektid on N+1 näide github'i readme's. ;) Ja osad ORM'id ja AR'id isegi teevad midagi sarnast, aga enamasti on metsikult mingeid options array'sid (Array'd on pahad, kuna key'd ja value'd on stringid - typo'd kerged tulema ja ide ei soovita midagi ette) ja ka siis enamasti, mis juhtub on 2 query't ühe asemel:
SELECT * FROM my_table WHERE [minu tingumused]
SELECT * FROM table2 WHERE id IN(...)
Minu raamistik võtab aga konkreetselt sinu kirjutatud query tulemuse ja kasutab sellest nii palju ära, kui vähegi võimalik: Kui ükskõik millise kõrvalise objekti id on olemas, loob objekti juba cache'i, kui on olemas ka full data, preloadib selle objekti kohe ära. Ja seda ainult ühe query'ga, mille kirjutad sina täpselt sellise, nagu tahad ja selectid välja täpselt need väljad, mis tahad.


Naljakas nimi "kõrvalised objektid N+1". Pigem kutsuks neid parent ja child objektideks. Või andmebaasis parent ja child row-ks või owning side ja inverse sideks.

Sa ütled, et array-d on pahad, aga su mudelile välju ju kirjutada ei saa. Siis praegune implementatsioon oma __set ja __get meetoditega ei tööta. $post->user_id pead ikkagi kuskilt ise välja võluma.

Muidu olen sinuga nõus, typod on kerged tulema. Siiski see probleem, mida sa lahendada üritad, on oluliselt keerulisem. Mis siis saab, kui objekti state on vahepeal baasis muutunud? Keegi on asünkroonselt seda teise threadiga seda muutnud. Kui keerata su githubi näide tagurpidi, kus sul on vaja userite listi ja välja kuvada kasutaja küljes olevad postitused, mis siis saab? Kuidas neid postitusi saab laadida lazy või eager loadinguga? Kuidas seda lahendad? Küsimusi on palju, mis tekib ning mis minu meelest on sul läbimõtlemata.

Sinu näite puhul mitte keegi, kes seda kasutab ja kes ei tea seda spetsiifilist nüanssi, et objekt cachest laetakse, ei oska seda ka eeldada, kui sa küsid User mudelilt objekti id järgi. See viib olukorrani, kus sul on raske teada, mis kood teeb. Juhtub mingi maagia.

Kaks väikest abstraktset koodinäidist korralikust ORM raamistiku implementatsioonist, üks esimene päringu objekt lazy, teine eager:



$query = $this->createQuery("SELECT User FROM EntityUser");

vs

$query = $this->createQuery("SELECT User FROM EntityUser");
$query->setFetchMode('EntityUser', 'posts', self::FETCH_MODE_EAGER);


foreach ($query->getResult() as $user) {
echo $user->getName() . ':';
foreach ($user->getPosts() as $post) {
echo $post->getContent() . '<br/>';
}
}


Nagu näha on kood palju puhtam ja selgem ning ütleb üks-üheselt ära mis toimub. See tõstatab järgmise küsimuse. Vaevalt, et sinu implementatsioonis Post mudelis User mudel peidus on ja vastupidi: User mudelis Post mudeli collectionit. See on see koht, kui AR on küll tore, aga on piisavalt kauge reaalse ORM implementatsioonist.

Kusjuures, kui see eelneva näite query on keerulisem, siis selle kokkupanemisel võiks vabalt mingit querybuilderit kasutada, teeks asjad veel lihtsamaks.

TSITEERITUD:

Universaalse sql'i kribamiseks võib ju ka täiesti eraldi klassi/mooduli vmt ehitada... Taaskord, AR'i ei tasu suureks ajada vaid õiged lego klotsid tuleb pannna õigesse kohta. Ehk ma pigem eelistaks vaadata selles suunas, et sa genereerid ise sql'i valmis, sql'i genereerimiseks kasutad aga oma lemmik generaatorit, taaskord. Hetkel veel väike probleem, et kuidas AR oskaks oma querysid teha läbi selle mooduli ilma, et ta väga teaks sellest moodulist midagi (et ei tekiks dependancy't), aga ma veel mõtlen selle peale...


Palju õnne selle probleemi lahendamisel. Ainuke mitte dependecyt omav output on naitive SQL. Ma võin vastuse öelda, kuidas sa selle probleemi lahendada saaks, kui sul huvi on. Vastus on tegelikult lihtne: täpselt nii nagu korralikud ORM raamistikud selle sama probleemi lahendanud on. :)
Kui hätta jääd, võin seletada.

TSITEERITUD:

Transaktsioonid: Alustamist ja lõpetamist ütled sa oma enda PDO objektile (mis haldab php pdo'd; näiteks) ja nested transaktsioonid on lihtsad (vähemalt kui sinust õigesti aru sain): Minu enda PDO klass oskab pidada counter'it, palju transaktsioone alustatud ja korras... Olen sellise "oma pdo" klassi teinud, töötab väga hästi. Pole seda lihtsalt github'i visanud.

Lühinägelik lahendus, kuid olen nõus, siiski lahendus. Probleem on selles, et ma mitte kunagi ei taha, et minu rakendus omaks dependenceid php moodulitega. Ever-never. Asjad peavad olema implementeeritud läbi strategy patterni (kui sa seda kasutanud ei ole, siis tutvu sellega). Igatahes rakendus peab dependima mingist iterfacest, kuidas iganes see implementeeritud on, pole rakenduse asi. Sul tuleb olukordi, kus sa võid und näha deploymisel, et sul php5-pdo moodul enabletud on. Selle pead saama oma rakenduses lihtsalt factorys ära öelda, mida kasutad.
Ehk sul on vaja lisada oma AR implementatsioonile adapteri tugi. Nüüd kui sa ise ei provaidi mingisugust database abstraction layerit, siis palju õnne, et keegi selle sinu interface järgi progeks. Seega on sinu lahenduse puhul ainuke võimalus, et sa võtad ise mingi DBALi kasutusele, mis on iseenesest väga hea mõte, aga tekitad kohe dependency juurde.
Korralikud ORM raamistikud omavad eraldi moodulina sellist layerit. ORM moodul omab source code dependencyt sellele, vastupidi dependence puudub. Ja nii on ka normaalne. Ma ei näe põhjust, miks sa peaks kasutama n erinevat third party libi andmebaasiga suhtlemisel.

TSITEERITUD:

Cache: Nõus, eks veidi mõtteski olnud, et kui mitte muud moodi, siis data ise võiks olla kuskil "laiemas" cache'is kui seda on konkreetne php protsess, mis kliendile lehte genereerib.

Selle implementeerimine viib su suurte probleemideni, trust me.

TSITEERITUD:

Listener pluginad: Aga just seepärast mul ongi _pre* ja _post* meetodid. Haagi sinna külge oma lemmik event handler ja korras. ;)

Sul on täiesti õigus. Ei saa sulle vastu vaielda. Saab ka nii, kuigi referencina muutujat sisse anda ei ole teab mis tark tegu, millel on omad probleemid, pigem muuta juba otse objekti state'i.

TSITEERITUD:

Column change tracking...: Kas see ei ole põhimõtteliselt see sama, mis eventid-listenerid? Kui ei, mida peaksin täpsemalt googeldama, et taas targemaks saada?


Ei ole. Aga pole ka alguses väga oluline tegelikult.

TSITEERITUD:

Versioneerimine: See lahendatakse tavaliselt ju mudeli tasemes ära... Mite AR'is. Ehk tase kõrgemal.


Siis sa hakkad koodi oma mudelites dubleerima, milleks? Oleneb muidugi versioneerimise loogikast, aga labasemad olukorrad võiksid olla kindlasti sellega lahendatud. Ära unusta, et sinu rakendus ei tohiks andmebaasist midagi teada. Sinu AR peaks andma sulle kätte õige objekti, mitte sa ei pea ise õiget objekti taga otsima.

TSITEERITUD:

Ehk lõpetuseks, AR'i suhtes tahan, et AR teeb ainult seda, mida AR tegema peab. Jah, on veel mõned asjad, mida võiks ehitada (nt cache), aga nii mõndagi ei tahaks põhimõtte pärast AR'i sisse kirjutada, vaid pigem kõrvale eraldi moodulina ja ehitada asjad nii, et sa saaksid ükskõik mida, ükskõik kuidas omavahel ise kokku panna. (Teed oma AR klassi, mis extendib minu AR'i ja implementeerib kõik _pre* ja _post* meetodid ära, mis saadavad event'e sinu event handlerile... miks mitte?)


Kõik õige, viskan ainult veel õli tulle ja küsin, kuidas sa kavatsed lahendada inheritance probleemi? Kui sul on üks abstraktne mudel ja mitu concrete selle klassi implementatsiooni, kuidas sa siis õige objekti õigest tabelist laed?

Minu point on see, et kõikide nende usecasede lahendamine, mis AR pärusmaa on, on suur töö, tulemuseks sinu algsest ideest on kasvanud välja yet another AR raamistik. Kellele seda siis vaja on?

Lõpetuseks ma soovitan tutvuda mõne koodi konventsiooniga ning CS korda teha, praegu on asjad kaootilised. PHP-s on järjest rohkem kanda kinnitamas PSR-2 code style. Kindlasti soovitan tutvuda ka PSR-3, sest see annab päris toredaid ideid juurde, mida enda koodis tähele panna.

Edu!
RE: Kirjutasin ActiveRecord klassi
Postitaja: Seramis 2013-09-26 17:40:43
Tänud väga hea ja pika vastuse eest! :)

Property'd saab genereerida ja dokumenteerida phpdoc'iga väga hästi. Kuna definitsiooni saab lihtsalt ka genereerida, siis seal ei toimu käsitsi array'de kirjutamist. Seetõttu võtan seda natuke "käib kah" suhtumisega. (Generaator on hetkel mul töös)

Pea peale pööramisega, väga lihtsalt saab lahendatud (sest ma mõtlesin selle peale ikkagi):


$aPost = User::getManyBySql(
"SELECT %self.table%.*, %Post.table%.* FROM %Post.table% INNER JOIN %self.table% ON %Post.table%.%self.id% = %self.table%.%self.id%"
);


Paned tähele? Ma küsin user'eid aga from on vastu post'i ja user'id ma hoopis joinin külge. Ja see töötab. Ja oligi mõeldud nii töötama. ;)

Sinu näite puhul, kas sellist asja ei juhtu, et kui eager flag on peal, siis suudetakse user'ist mööda ketti minna aina edasi ja lõpuks laetakse terve baas kogemata sisse. Kuidas see piiratud on? Mul sql'i kirjutades saad ise valida, mida joinida ja kas joinida.

Ka see, et post'i sees oleks user ja user'i sees list post'idest on tahtlikult välja jäätud, kuna ma tahtsin kirjutada AR'i ja *ainult* AR'i. Kui sul on küpsis ja sa tahad user'it, kelle oma see on, siis ütled praegu konkreetselt: User::getById($oCookie->user_id);
(Mis võiks muidugi olemas olla, on getByObject, mis ise leiab endale sobiva id field'i, kuid seal on aga, et kui mul on stiilis user_id_to ja user_id_from, siis peab midagi leiutama hakkama.)

Cache on jah problemaatiline... seepärast veel midagi ei kirjutagi, kuna vaja asi endale selgeks mõelda, et igast poosist asi töötaks.

Listenerid:
Aga sa ei pea oma _pre ja _post funktsioonides referentsina muutujat sisse võtma. Selle poole kirjutad juba sina ju. ;) AR lihtsalt võimaldab sul referentsi kaudu väärtust mõjutada ja ma toon selle lihtsalt välja.

Versioneerimite:
Oot oot, räägime me ikka AR'ist või juba Mudelitest, mudeli factory'test jmt?


Koodi stiilis austan väga PSR-* standardit, kuid päris kätte ei ole see veel sisse jäänud. Laias laastus midagi ei tohiks väga valesti olla. Variaableid ei defineerita, if'i kirjutan nagu function'it ja leian, et tab character on olemas põhjusega ja kasutan seda. Kuid ehk peaks end kätte võtma ja need põhimõtted ka eemale lükkama, saaks kood "ühesugusem" teistega.
RE: Kirjutasin ActiveRecord klassi
Postitaja: Seramis 2013-09-26 17:41:35
Koodinäites peaks olema:
$aUser = User::...
RE: Kirjutasin ActiveRecord klassi
Postitaja: Seramis 2013-09-28 16:27:22
https://github.com/domnikl/DesignPatternsPHP

See peaks üpris korralikult kontsentreeritud info olema vist, eks?
RE: Kirjutasin ActiveRecord klassi
Postitaja: Interneti Troll 2013-10-01 13:37:34
Vaatasin githubis seda AR API-t. Suti liiga palju käsitsi koodi kirjutamist minu arust. Soovitaksin sulle katsetada mingit koodi genereerimise lahendust, kus koodigeneraatorile antakse ette mingi PHP klass ja väljundiks on selline AR klass/PHP fail, nagu sa näitena Githubi lehel kuvad. Saad koodi genereerimise etapi lisada ilma, et peaks praegust koodi muutma. Koodi genereerimisega kaasneb see pluss, et peale mudeli muutmist ei pea käima käsitsi kümnes kohas parandusi tegemas. Koodi genereerimisel soovitan genereeritud klassile ka mingisuguse Hook-ide API sisse kirjutada, mis võimaldab genereeritud koodile käsitsi täiendusmooduleid külge pookida. See jätab võimaluse uuesti genereeritud failide juures vajadusel samu käsitsi kirjutatud lisafunktsionaalsuseid kasutada. See lihtsalt üks mõte. Igal juhul on kasulik selliseid asju ise läbi katsetada. Jõudu tööle.
RE: Kirjutasin ActiveRecord klassi
Postitaja: Seramis 2013-10-01 14:50:55
Tänud hea mõtte eest!

Samas, koduses masinas on mul generaator juba tehtud. Veel pole teda siia üles pushinud lihtsalt.

Struktuur oleks: My_AR extends Base_My_AR extends AR.
Ja Base_* on see, mida genereeritakse alati üle ning sina kirjutad käsitsi koodi ainult My_AR klassi. ;)
RE: Kirjutasin ActiveRecord klassi
Postitaja: Seramis 2013-11-20 20:47:18
Vahepeal palju muutunud. Generaatorit pole veel piisavalt ilusaks kirjutanud, et teda veel välja lasta, küll aga tegin järgnevad uuendused ja parandused:


Improvements:
getById() supports second argument to get object only from cache. (false, if object is not in cache)
getOne(), getMany(), getManyByWhere() support additional suffix parameter. (Good for ordering and limiting)
setMany() introduced to set data to object from array or object. (Affects construct the same way)
getIsInDb() renamed to isInDb()
_preSet*() and _postSet*() have additional input for old value.
_preSave*() and _postSave*() have additional input for old value.
isDirty() introduced to check if object or one field is dirty. (set but not saved)
_preCreate*() and _postCreate*() triggers introduced in addition to _*Save*() triggers.
Many self::method() calls replaced with static::method() for better extendability.

Fixes:
getById() does not make a query if object is in cache already.
construct() (and setMany()) now ignore fields that are not in AR.
ID field can now be set when AR is completely new.

Leheküljed: 1

©2002-2013 Martin Rebane & PHP.ee kaasautorid
  0.0927541255951