Database klasse ontwerpen

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Pagina: 1 2 volgende »

Write Down

Write Down

06/07/2012 14:39:36
Quote Anchor link
Hi

Naar aanleiding van een ander topic van mij, ontwerpen usermanagement open ik dit nieuwe topic. Aangezien ik in dit topic specifiek het ontwerp van de databaseclass wil bespreken. Iets wat toch een iets wat ander topic is.

Ik zou graag jullie mening krijgen over het ontwerpen van een database klasse. Ik wil hier toch wel wat aandacht aan besteden, aangezien dit toch één van de meest cruciale onderdelen van een applicatie is.

Om te beginnen wil ik Wouter citeren (citaat afkomstig uit het hierboven genoemde topic):

Wouter J op 06/07/2012 12:15:10:
Ook zou ik dit over meerdere objecten spreiden:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
- Database (connectie en uitvoeren query)
- Result (het resultaat van een query)
  - DataResult (resultaat van een select query)
  - ...Result (resultaat van een UPDATE, DELETE of INSERT query)

En misschien zelf een query object om je queries op te kunnen bouwen.


Dit zelf vind ik wel een prettig model. Hoe ik dit praktisch zou implementeren, heb ik nog niet direct een idee van. Graag jullie meningen / ideeën / voorbeelden!
 
PHP hulp

PHP hulp

01/12/2024 10:05:48
 
Erwin H

Erwin H

06/07/2012 15:54:03
Quote Anchor link
Lijkt me een goed idee om het eens duidelijk uit te werken, aangezien ik er de laatste tijd (van meerdere mensen) redelijk wat topics over voorbij zie komen. Als het dan uiteindelijk een correct opgebouwd en goed werkende set classes oplevert hebben meerdere mensen er wel iets aan denk ik.

Even voor leesvoer hier nog twee topics waar redelijk wat tips in worden gegeven:
http://www.phphulp.nl/php/forum/topic/model-mvc-database-ophalen/85471/
http://www.phphulp.nl/php/forum/topic/oop-syntaxen-en-werkingen/85111/

Wat volgens mij belangrijk is in de eerste stap is duidelijk uitschrijven welke functionaliteit je wilt, welke verantwoordelijkheden er dus zijn en hoe je dat wilt onderverdelen in verschillende classes. Ik zie redelijk vaak voorbeelden voorbij komen waar een class meerdere verantwoordelijkheden krijgt en waardoor je dus heel veel aan flexibiliteit inlevert.
 
Write Down

Write Down

06/07/2012 16:12:35
Quote Anchor link
Ik ben niet zo'n fan van het MVC model, dus ik ga me daar niet bewust aan houden. Ik ben over het algemeen niet zo erg gericht op allerlei modelletjes. Ik ben al erg tevreden als het logisch, efficiënt en bruikbaar is. Uiteraard is het MVC dat, maar persoonlijk heeft het voor mij geen meerwaarde t.o.v. andere modellen.

Ik ben van plan wat code te schrijven, hier te posten en dan kunnen we wellicht samen tot een beter resultaat komen.
 
Erwin H

Erwin H

06/07/2012 16:23:20
Quote Anchor link
Het topic heeft weliswaar MVC In de titel, maar het meeste wat er besproken wordt is niet specifiek MVC. Een goede database class (of set aan classes) kan je in bijna elke wijze van implementatie gebruiken.
 
Write Down

Write Down

06/07/2012 16:30:24
Quote Anchor link
Ik zal het straks is nalezen. Nu ben ik al rustig aan aan het programmeren. (Verloopt moeizaam, hele tijd geleden dat ik nog een letter PHP schreef :P)

Wat ik me in eerste instantie al afvraag, hoe behandelen jullie de configuratie instellen? Ikzelf dacht aan een .ini bestand te maken met daarin de basis gegevens voor de database en dit in de laden via een methode: readConfig. Deze geeft dan een array terug die ik op zijn beurt doorgeef aan een connect functie.

Dus de constructor zou er dan bijvoorbeeld zo kunnen uitzien:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
<?php
public function __construct {
 $this->databaseHandle = connect(readConfig());
}

?>
Gewijzigd op 06/07/2012 16:30:45 door Write Down
 
Wouter J

Wouter J

06/07/2012 16:35:11
Quote Anchor link
Ik zou eerst ALLES gaan uitdenken en dan pas scripten. Schrijf op een papiertje welke taken er allemaal zijn, schrijf dan op welke objecten deze taken moeten uitvoeren, wat hun rechten zijn, enz.
Vervolgens ga je UML diagrammen tekenen, geef exact aan welke objecten welke properties en methods hebben en welke objecten een relatie hebben (HAS_A, IS_A, IMPLEMENT_A relaties).

Pas als je ALLES hebt uitgedacht begin je met scripten.

Tevens bestaan losse functies nooit in OO... Gebruik dus een Config klasse waarin je gegevens kunt setten en getten, vervolgens geef je die mee aan de Database klasse (HAS_A relatie) en die kan vervolgens de gegevens eruit halen.

Uitgetekend in UML wordt het dan:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
+-------------------------+                    +-----------------------+
|        Database         |                    |        Config         |
+-------------------------+                    +-----------------------+
|  # config : Config      | <----------------- | # settings : array    |
+-------------------------+                    +-----------------------+
| + init(config : Config) |                    | + set(setting, value) |
+-------------------------+                    | + get(setting)        |
                                               +-----------------------+
Gewijzigd op 06/07/2012 16:41:14 door Wouter J
 
Write Down

Write Down

06/07/2012 17:38:21
Quote Anchor link
En de config class, die kan dan bijvoorbeeld werken met een .ini bestand, een ander bestand, config parameters in de database zelf. Of zie ik dat verkeerd?

Overigens, met jouw idee, dan krijg je dus iets als volgt:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
<?php
$cf
= new Config();
$log = new FileLog();            //of bv MailLog
$db = new Database($log, $cf);
$result = new DataResult($db->query('SELECT foo FROM bar'));

foreach($result->iterator() as $key => $value) {
    echo $key . ' => ' . $value . "\n";
}

?>


Ik ga deze avond nog trachten een UML te maken. Hangt er vanaf hoeveel tijd ik heb. (moet straks nog weg)


Toevoeging op 06/07/2012 17:42:25:

Overigens, is het dan ook weer niet interessant om Config als een interface te bouwen en bv IniConfig die te laten implementeren e.t.c. ?
Gewijzigd op 06/07/2012 17:39:22 door Write Down
 
Erwin H

Erwin H

06/07/2012 17:46:06
Quote Anchor link
Dat laatste ABSOLUUT!
Wie weet wil je het volgend jaar in een XML bestand hebben, dan heb je met een interface het jezelf al makkelijk gemaakt.

Maar hoe je al die objecten aanmaakt zou ik niet zo doen. Als je het in elk geval echt op een OOP manier doet, dan heb je op je index pagina maar 1 regel staan en dat is het aanmaken van je basisobject (in MVC je controller). Vanuit dat object wordt de rest in gang gezet.
 
Write Down

Write Down

06/07/2012 17:58:36
Quote Anchor link
Misschien moet ik deze keer dan toch eerder naar MVC grijpen. Is al tijdje geleden, kan jij mij een bepaalde tutorial aanraden? Misschein deze?
 
Erwin H

Erwin H

06/07/2012 18:25:06
Quote Anchor link
MVC hoeft niet hoor, als je het echt OOP doet dan geldt mijn opmerking ook over de enige lijn code in je index.
Een MVC tutorial heb ik niet, ik heb het uit een boek gehaald (Head First Design Patterns - uberhaupt een enorme aanrader).
 
Write Down

Write Down

08/07/2012 12:25:33
Quote Anchor link
Excuses voor de late reactie. Ik heb nog geen tijd gehad.

Maar goed, heb je dan een ergens een voorbeeld hoe ik het wel zou moeten ontwerpen? Want als je alles vanuit één object laat vertrekken, dan maak je de boel toch te veel van mekaar afhankelijk? Wat volgens mij nét niet de bedoeling kan zijn.
 
Wouter J

Wouter J

08/07/2012 12:28:02
Quote Anchor link
Nee, want in dat Applicatie object kun je alles mooi afhankelijk maken. Er is niks van elkaar afhankelijk, het enige wat afhankelijk aan elkaar is dat je website/project/applicatie afhankelijk is van 1 object: De Applicatie object/controller.
 
Write Down

Write Down

08/07/2012 12:33:52
Quote Anchor link
Dan zal ik mij daar eerst eens in moeten verdiepen :-)
 
Wouter J

Wouter J

08/07/2012 13:03:12
Quote Anchor link
Echt verdiepen is het niet. In plaats van dat je doet:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$config
= new Config();

$logger = new FileLog('log/database.log');

$database = new Database($logger, $config);

// Database::query() retourneert een DataRow object
// deze implementeert het IteratorAggregate interface
// zodat we hem straks zo in de foreach kunnen plaatsen

$result = $database->query('SELECT foo FROM bar');

foreach ($result as $key => $value) {
    echo $key . ': ' . $value . "\n";
}

?>


Maak je hier een speciaal object voor aan:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
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
<?php
class Application
{
    protected $loggers = new StdClass();

    public function __construct()
    {

        $this->loggers->database = new FileLog('log/database.log');
    }


    public function run()
    {

        $config = new Config();
        $database = new Database($this->loggers->database, $config);

        $result = $database->query('SELECT foo FROM bar');

        foreach ($result as $key => $value) {
            echo $key . ': ' . $value . "\n";
        }
    }
}


// index.php
$app = new Application();
$app->run();
?>


Tevens vind ik ook hier dat een Service Container gebruiken erg handig zou zijn:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
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
43
44
45
46
47
<?php
// Application::construct();
$this->container = new Container();
$this->container->set('logger.class', 'FileLog');
$this->container->set('logger.destination', 'logs/database.log');
$this->container->set('logger', function($c) {
    return new $c->get('logger.class')($c->get('logger.destination'));
});


$this->container->set('config.file', 'config/database.yml');
$this->container->set('config.parser', 'YmlParser');
$this->container->set('config.parse', function($c) {
    $parser = new $c->get('config.parser')($c->get('config.file'));

    if (!$parse instanceof ParserInterface) {
        // fout afhandeling
    }

    return $parser->parse();
});

$this->container->set('config', function($c) {
    $config = new Config();

    foreach ($c->get('config.parse') as $key => $value) {
        $config->set($key, $value);
    }


    return $config;
});


$this->container->set('database.class', 'PDOMySQLDatabase');
$this->container->set('database.config', $container->get('config'));
// dit is nog niet helemaal correct, dit hoort bij de Config service
$this->container->set('database.logger', $container->get('logger'));
$this->container->set('database', function($c) {
    return new $c->get('database.class')($c->get('database.config'), $c->get('database.logger'));
});


// Application::run()
$database = $this->container->get('database');

$result = $database->query('SELECT foo FROM bar');

foreach ($result as $key => $value) {
    echo $key . ': ' . $value . "\n";
};

?>
 
Write Down

Write Down

09/07/2012 11:05:53
Quote Anchor link
Het is weer week, dus hebben we weer wat meer tijd :-)

Ik zie nu in de applicatie klasse dat je bepaalde zaken 'vast zet'. Daarmee bedoel ik de code die zorgt voor de query en de foreach. Ik denk dat dit in werkelijkheid toch niet de bedoeling kan zijn? Aangezien op index.php wil ik bijvoorbeeld één simpele query maar op mijn admin page wil ik er bijvoorbeeld 5.

Of is het zo dat je in die run() functie een of meerdere basis query's plaats die je altijd nodig hebt? En dat je daarna bv. $app->database->query('SELECT pint FROM beer'); uitvoert in beer.php?

Overigens wil ik je bedanken voor de code i.v.m. de container. Deze is voor mij zeer verhelderend en lijkt mij inderdaad praktisch om te gebruiken.
 
Wouter J

Wouter J

09/07/2012 11:19:06
Quote Anchor link
Dan heb je dus meerdere Applicatie klassen nodig en dan ga je dus opweg naar het gebruik van controllers (wat overigens niks met MVC te maken hoeft hebben). Je hebt dan 1 basis controller, de superklasse, die de dingen uitvoert die er standaard gebeuren en de subcontrollers voeren dan pagina specifieke code uit:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
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
<?php
interface ControllerInterface
{
    // ...
}

class PageController implements ControllerInterface
{
    // ...
}

class BlogController extends PageController
{
    public function showAction($id)
    {

        // ... krijg de blogpost met id $id en stuur die door naar een template
    }

    public function showAllAction()
    {

        // ... krijg alle blogposts en stuur die naar een template
    }

    // ...
}
 
Write Down

Write Down

09/07/2012 12:59:56
Quote Anchor link
Bedankt! Ik heb intussen een (voorlopig) UML uitgewerkt. Nog niet alles is tot in de puntjes afgewerkt. Ik moet over bepaalde zaken nog eens grondig nadenken. Graag jullie meningen!

Afbeelding
 
Wouter J

Wouter J

09/07/2012 13:33:52
Quote Anchor link
Ziet er in het algemeen al mooi uit, even wat puntjes en wat tips:

- Wat is nu het verschil tussen Config en YamlConfig? Ze houden allebei settings vast in hetzelfde formaat. Ik denk dat je maar 1 Config object nodig hebt en dan een Parser interface moet maken, met meerdere parsers als YamlParser, XmlParser, enz. I.p.v. zelf een hele ingewikkelde YamlParser te maken raad ik aan gebruik te maken van het Yaml Symfony Component.
- Wat doet die addLogger in de controller? Dat lijkt me niet echt een taak van een controller. Het lijkt me handiger om de ControlerInterface abstract te maken met wat standaard functies als getDatabase() (om database object te krijgen), getConfig(), enz. Vervolgens maak je aparte Handlers, bijv. een ExceptionHandler.
- De container kun je natuurlijk simpel zelf maken (zoals Pim hier uitlegt), maar ik gebruik hiervoor altijd Pimple, misschien eens leuk om naar te kijken.
 
Write Down

Write Down

09/07/2012 13:55:26
Quote Anchor link
I.v.m. met die Config, klopt. Bedankt voor het goede idee. En die parser zou ik dan bv. via de constructor kunnen meegeven. Of zie ik dat verkeerd?

Verder wat betreft de parser, je hebt toch sowieso een niet erg ingewikkelde parser nodig? Ik ga er vanuit dat de configuratie iets vrij statisch is. Ik als ontwerpen van de applicatie zal kiezen welke instellingen er zijn en wat de mogelijke waarden zijn. Op zich kan ik die instellingen verwerken d.m.v. een array en dan yaml_emit. Wanneer ik de instellingen wil, kan ik dan yaml_parse_file gebruiken.

Uiteindelijk is dit toch eerder iets dat in de GUI wordt afgehandeld. (controleren wat voor een bepaalde instelling mogelijk is etc.)

Wat betreft de controllers, ook hier volg ik je mening. Alleen begrijp ik niet goed waarom je hier de handlers bij haalt. Wat zou je er dan willen mee willen doen bij de controllers?

Wat betreft de container, ik ben nu aan het bekijken of ik zelf iets leuk maak of inderdaad ga voor Pimple.
 
Wouter J

Wouter J

09/07/2012 14:01:05
Quote Anchor link
Quote:
En die parser zou ik dan bv. via de constructor kunnen meegeven. Of zie ik dat verkeerd?

Ik zou het gewoon weer in 2 dependencies opdelen (2 services) en die in de Container stoppen. 1 service is de parser, deze parsed het bestand en de andere is de Config die dan over de parse resultaten loopt en deze mooi met de set methods aan de Config toevoegt.

Quote:
je hebt toch sowieso een niet erg ingewikkelde parser nodig?

Je zult het bestand moeten parsen en voor Yaml heeft PHP nog geen eigen parse functies, voor zover ik weet. Ik raad daarom het gebruik van die YamlParser aan. Mocht PHP wel een goede Yaml parser hebben dan kun je die gewoon gebruiken en dan heb je geen Parse object meer nodig en heb je alleen een Parse service.

Quote:
Wat zou je er dan willen mee willen doen bij de controllers?

Met een Handler handel je een exception af bijv. Ik begreep niet echt goed waarvoor je de addLogger method had toegevoegd aan de Controller en dacht toen dat je dat deed om Exceptions/fouten af te handelen, vandaar dat ik begon over Handlers.
 
Write Down

Write Down

09/07/2012 14:41:15
Quote Anchor link
Quote:
Quote:
Wat zou je er dan willen mee willen doen bij de controllers?

Met een Handler handel je een exception af bijv. Ik begreep niet echt goed waarvoor je de addLogger method had toegevoegd aan de Controller en dacht toen dat je dat deed om Exceptions/fouten af te handelen, vandaar dat ik begon over Handlers.


Oorspronkelijk was dat niet zozeer mijn bedoeling. Maar nu heb jij mij wel op een idee gebracht. Maar alsnog, wat zou je dan doen met die handlers binnen de controller?
 

Pagina: 1 2 volgende »



Overzicht Reageren

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.