Toon posts:

[PHP] Database Abstraction Layer, complexe(re) queries

Pagina: 1
Acties:

Verwijderd

Topicstarter
Goedendag,

Om mezelf iets meer te verdiepen in het OOP priciepe, heb ik besloten een DAL te gaan (proberen te) maken. Ik wil dit in PHP5 doen, om meteen de nieuwe functies daarvan te leren kennen. Ik heb me al enigzins verdiept in de oop middelen van PHP5, maar waar ik nu mee zit heeft daar niet veel mee te maken.

Stel, ik wil een simpele select query maken.
PHP:
1
2
3
4
$query1 = $db->newQuery( 'select' );
$db->addFiels( $query1, 'veld1', 'veld2' );
$db->addTable( $query1, 'table1' );
$db->addWhere( $query1, 'veld1', '>', $waarde );


Dit kan ik wel maken, maar stel dat ik uit meerdere tabellen velden wil selecteren (joins dus). En als er in de where statement iets komt als table1.veld >= table2.veld4?

Ik kan ervoor kiezen om in m'n applicaties gewoon de query te schrijven, en het afhandelen e.d in de DAL te stoppen, maar dan vind ik het niet erg nuttig. Bijvoorbeeld, Postgre behandeld de LIMIT anders dan MySQL, en dat soort dingen wil ik juist afvangen.

Hoe kan ik dit het beste oplossen? Heeft er iemand tips of links waar ze zulke dingen uitleggen?

Roemer

  • Mithrandir
  • Registratie: Januari 2001
  • Laatst online: 15:43
Tsja, de 'simpele' query die je maakt is eigenlijk helemaal niet zo simpel. Je maakt het schrijven van queries nu ingewikkelder dan gewoon SQL tikken, en dat is toch niet de bedoeling van een DAL?

Als ik jou was zou ik eerst eens bedenken waarom je zoiets bijvoorbeeld wilt, en dan iets van een O/R mapping functie schrijven; dat bespaard veel meer codewerk dan zo'n DAL. En daar gaat 't natuurlijk uiteindelijk om; snellere en bugvrijere code schrijven.

Verbouwing


Verwijderd

Topicstarter
Het doel van een DAL lijkt mij dat het niet uit maakt welke database eronder ligt, het moet werken. Dus als ik bijvoorbeeld van MySQL over wil stappen naar PostgreSQL, ik niet m'n hele app hoef te doorzoeken en de LIMIT statement moet veranderen. Tweede puntje lijkt mij bugvrij, en als laatste snelheid van het schrijven.

Wat bedoel je met een O/R mapping functie? Ik ben die term volgens mij nog nooit tegen gekomen...

  • Hydra
  • Registratie: September 2000
  • Laatst online: 22-01 13:59
Sorry hoor, maar een DB object met daarin mogelijkheden een query op te bouwen is helemaal geen data abstractie laag. Een DAL levert objecten aan de applicatie, deze laag zet dus bijvoorbeeld een SQL resultset om in een lijst met Order items, welke dan door de applicatie verwerkt worden. Bovenstaande voorbeeld is weinig meer dan een moeilijk OO-esque schilletje om de MySQL API.
Verwijderd schreef op dinsdag 27 december 2005 @ 12:59:
Het doel van een DAL lijkt mij dat het niet uit maakt welke database eronder ligt, het moet werken. Dus als ik bijvoorbeeld van MySQL over wil stappen naar PostgreSQL, ik niet m'n hele app hoef te doorzoeken en de LIMIT statement moet veranderen. .
Nee, dat is niet het doel van een DAL.

[ Voor 35% gewijzigd door Hydra op 27-12-2005 13:03 ]

https://niels.nu


Verwijderd

uberhaupt een wonder dat je zover bent gekomen, met zulke typevauten gaat het nooit goed

Verwijderd

Topicstarter
@sjaak101: He, bedankt voor je positieve inbreng man! Je helpt me hier echt een heel stuk mee, echt bedankt joh! Goh, was iedereen hier maar zo behulpzaam!

@Hydra: Bedankt, ik zal me nog's wat meer proberen te verdiepen in DAL-artikelen. Maar wat jij zegt is dat de applicatie gewoon complete queries naar de DAL moet sturen, en dat die het in nette results terug kan geven? Heb je misschien (pseudo-) voorbeelden wat er dan wel en niet onder valt?

  • Hydra
  • Registratie: September 2000
  • Laatst online: 22-01 13:59
Verwijderd schreef op dinsdag 27 december 2005 @ 13:15:
@Hydra: Bedankt, ik zal me nog's wat meer proberen te verdiepen in DAL-artikelen. Maar wat jij zegt is dat de applicatie gewoon complete queries naar de DAL moet sturen, en dat die het in nette results terug kan geven? Heb je misschien (pseudo-) voorbeelden wat er dan wel en niet onder valt?
Je haalt in de eerste plaats de data abstractie laag en de database abstractie laag een beetje door mekaar. het hele punt is dat de applicatie helemaal NIKS van queries e.d. moet weten, dat is het hele punt achter een DAL.

Als simpel voorbeeld neem ik een systeem met een lijst auto's (waar je het voor gebruikt mag je zelf verzinnen).

Applicatie calls:
$db->getAutoLijst();
$db->getAuto('XX-YY-11');

Auto class:
-kenteken
-merk
-kilometers

DB class:
functie getAutoList(): select alle auto's dmv een query, bouw rows om naar auto objecten, return array van auto objecten.
functie getAutobyKenteken(string kenteken): select auto dmv een kenteken, bouw row om naar auto object, return auto object.

Dat is een DAL. Een dal ontkoppelt je business objecten (auto objecten e.d.) van de onderliggende tables. Binnen dat DB object heb je dus methodes die die autos ophalen/updaten etc. Daar kun je voor mijn part de meest ranzige SQL in gebruiken, dat maakt niet uit, zolang de applicatie alleen maar auto's ziet.

Edit:
Hecht trouwens niet al te veel waarde aan artikelen die je op 't internet tegenkomt. Er is geen enkele garantie dat die mensen die het geschreven hebben weten waar ze het over hebben. Elke idioot kan een artikel schrijven ;)

[ Voor 10% gewijzigd door Hydra op 27-12-2005 13:45 ]

https://niels.nu


  • Hydra
  • Registratie: September 2000
  • Laatst online: 22-01 13:59
Ter verduidelijking:
Afbeeldingslocatie: http://www.xs4all.nl/~nielsnu/pic/dal.png

Een DB abstractie laag biedt aan de bovenliggende laag een uniforme interface aan om data op te halen zodat de bovenliggende laag geen benul hoeft te hebben van wat de onderliggende DB is. Omdat je SQL gebruikt, kun je de DAL prima SQL laten praten met de DBAL, en is het in het geval van PHP alleen maar een kwestie van API-calls te renamen. Veel platformen (java, .net) lossen dit zelf al voor je op trouwens.

De DAL biedt dan naar 'boven' objecten aan, dus auto's i.p.v. queries. Grote systemen bestaan uit een aantal van dergelijke lagen, maar voor kleinere systemen werkt een 3 laags systeem (userinterface -> applicatie logica -> DB logica) prima.

https://niels.nu


  • Michali
  • Registratie: Juli 2002
  • Laatst online: 22-03 18:12
Even een stukje code hoe ik het doe:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

$query = Database::createSelectQuery();

$query->addTable('table1', 't1');
$query->addTable('table2', 't2');

$query->addField('*', 't1');

$selection = $query->getSelection();
$selection->addTableCompare('t1', 'veld', 't2', 'veld4', Operator::EQUAL | Operator::GREATER_THAN);

$resultSet = $query->execute();

while ( $record = $resultSet->fetch() )
{
    print $record->get('veld');
}

?>

Zoals je ziet heb ik dus een Selection object welke de gehele where clause regelt. Ook kan ik meerdere Selection object koppelen om clauses tussen haakjes te maken.

[ Voor 165% gewijzigd door Michali op 27-12-2005 14:48 ]

Noushka's Magnificent Dream | Unity


Verwijderd

Topicstarter
ahh, je plaatje maakt erg veel duidelijk Hydra! Ik denk ik een DBAL (als dat voor DataBase Abstraction Layer staat) een bedoel... DAL betekent dan Data Abstraction Layer (oid)?

Maar als ik nu dus een DBAL wil maken, dan kom ik dus op het probleem dat niet elke database dezelfde statement(structuren) gebruikt. Dus ik wil bijvoorbeeld een lijst met auto's krijgen, in MySQL: LIMIT 10, 15. Volgens mij is het in PostgreSQL dan iets van LIMIT 15 OFFSET 10 (hoe het precies zit maakt even niet uit... het is iig verschillend). Waar moet ik dat verschil dan afvangen? In de DAL moet de query gemaakt worden neem ik dan aan?
Ik hoop dat je m'n probleem snapt...

[edit]
Ah, bedankt Michali! Maakt veel duidelijker :)

[ Voor 6% gewijzigd door Verwijderd op 27-12-2005 14:51 ]


  • Michali
  • Registratie: Juli 2002
  • Laatst online: 22-03 18:12
Dat doe je dus door een Super class te maken (in mijn geval Selection) en door die geheel (of gedeeltelijk) voor iedere database te implementeren. Ik zal eens een stukje code tikken.

Dit is bijvoorbeeld een manier om dit te doen (veel gebruik makend van PHP5 OO features):
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php

abstract class LimitClause
{
    private $limit;
    public function setLimit($limit)
    {
        $this->limit = $limit;
    }
    
    private $offset;
    public function setOffset($offset)
    {
        $this->offset = $offset;
    }
    
    public function getSQL()
    {
        if ( is_null($this->limit) ) return "";
        return $this->buildLimitClause($this->limit, $this->offset);
    }
    
    abstract protected function buildLimitClause($limit, $offset);
}

class MySQLLimitClause extends LimitClause 
{
    protected function buildLimitClause($limit, $offset)
    {
        return "LIMIT " . ( is_null($offset) ? $limit : $offset . ", " . $limit );
    }
}

class PostgreSQLLimitClause extends LimitClause 
{
    protected function buildLimitClause($limit, $offset)
    {
        return "LIMIT " . $limit . ( is_null($offset) ? "" : " OFFSET " . $offset );
    }
}

?>


Nu vraag je je mischien af hoe je dan zorgt welke van welke class een instance aangemaakt wordt. Dat kun je door door te configureren. Je zou dan bijvoorbeeld iets als dit kunnen krijgen:

PHP:
1
2
3
4
5
// je config file
$config['limit_clause_implementation'] = "MySQLLimitClause";

// de plaats van het aanmaken
$limitClause = new $config['limit_clause_implementation']();


Wat je ook kunt doen is een bepaalde overkoepelende class hebben die dat allemaal regelt. Dan hoef je alleen voor die class een config waarde te maken. Zoiets heet overigens een Factory (specifiek Abstract Factory, zoek maar op op Google).

[ Voor 112% gewijzigd door Michali op 27-12-2005 17:52 ]

Noushka's Magnificent Dream | Unity


  • tombo_inc
  • Registratie: December 2004
  • Laatst online: 10-03 13:21

tombo_inc

uhuh

is het niet zo dat je bij een DBAL voor iedere database die je wil ondersteunen een soort driver maakt. die driver implementeerd dan de juiste db calls (de verschillen dus). die dbdriver laad je dan in in een soort superclass en daarmee communiceer je. (of heb ik nu een verkeerd idee in mijn hoofd?)

Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition


  • Michali
  • Registratie: Juli 2002
  • Laatst online: 22-03 18:12
Tombo_inc schreef op dinsdag 27 december 2005 @ 15:06:
is het niet zo dat je bij een DBAL voor iedere database die je wil ondersteunen een soort driver maakt. die driver implementeerd dan de juiste db calls (de verschillen dus). die dbdriver laad je dan in in een soort superclass en daarmee communiceer je. (of heb ik nu een verkeerd idee in mijn hoofd?)
Dat klopt zeker. Je zou het een driver kunnen noemen of in Design Pattern taal een Adapter. Maar je hoeft het niet alleen bij 1 class te houden. Je kan een hele familie classes maken die werk doen voor een bepaalde database. Je hebt dan een set interfaces en abstracte classes waarmee je kunt werken zonder dat je weet welke implementatie er gebruikt wordt.

Noushka's Magnificent Dream | Unity

Pagina: 1