ActiveRecord en Method Chaining
Maar nu loop ik vast bij de method chaining. In rails kun je zoiets doen:
Maar ook:
Code (php)
1
2
2
# Haal 10 berichten, geordend bij postDate, van de author Wouter J
Post.where(:author => 'Wouter J').order(:postDate).limit(10)
Post.where(:author => 'Wouter J').order(:postDate).limit(10)
De where method moet dus een result terug geven als het aan het eind staat, maar $this als er daarna functies gebruikt worden.
Ik denk dat het in normale PHP niet op te lossen is, maar heeft iemand er misschien een workaround voor?
PS: Een kijkje nemen in de Rails source code is ook al in me op gekomen. Maar daar wordt je niet veel wijzer van: https://github.com/rails/rails/tree/master/activerecord
Gewijzigd op 29/02/2012 17:08:35 door Wouter J
Code (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
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
<?php
class Chain_Test{
private $results = 0;
public function add( $value ){
$this->results += $value;
return $this;
}
public function subtract( $value ){
$this->results -= $value;
return $this;
}
public function divide( $value ){
$this->results /= $value;
return $this;
}
public function multiply( $value ){
$this->results *= $value;
return $this;
}
public function getResults(){
return $this->results;
}
}
$object = new Chain_Test;
echo $object->add(10)->subtract(1)->multiply(2)->getResults();
?>
class Chain_Test{
private $results = 0;
public function add( $value ){
$this->results += $value;
return $this;
}
public function subtract( $value ){
$this->results -= $value;
return $this;
}
public function divide( $value ){
$this->results /= $value;
return $this;
}
public function multiply( $value ){
$this->results *= $value;
return $this;
}
public function getResults(){
return $this->results;
}
}
$object = new Chain_Test;
echo $object->add(10)->subtract(1)->multiply(2)->getResults();
?>
Net getest en het werkt.
Gewijzigd op 29/02/2012 17:41:45 door Erwin H
Ga voorlopig zo werken, maar mocht iemand nog een geweldige ingeving hebben dan zeggen ze het maar.
Offtopic:
Waarom is PHP niet zo mooi gescript als Ruby :S
Tja, OO regels zijn er om gebroken te worden natuurlijk.....
Code (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
43
44
45
46
47
48
49
50
51
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
48
49
50
51
<?php
abstract class ActiveRecord
{
// Bevat mapping data
protected $mapping;
public static function get()
{
return new ActiveRecordStatement($this->mapping, $this->db);
}
}
class ActiveRecordStatement implements IteratorAggregate
{
protected $mapping;
protected $db;
protected $where;
public function __construct(array $mapping, Database $db)
{
//...
}
public function where(array $where)
{
$this->where = $where;
return $this;
}
public function getIterator()
{
$items = $this->db->query($this->buildQuery());
return new ArrayIterator($items);
}
protected function buildQuery()
{
// Bouw de query op adv de configuratie
}
}
// Dus
class Post extends ActiveRecord
{
$this->mapping = array(...);
}
foreach(Post::get()->where(['author' => 'Pim']) as $post) { // PHP 5.4 ;)
print $post;
}
?>
abstract class ActiveRecord
{
// Bevat mapping data
protected $mapping;
public static function get()
{
return new ActiveRecordStatement($this->mapping, $this->db);
}
}
class ActiveRecordStatement implements IteratorAggregate
{
protected $mapping;
protected $db;
protected $where;
public function __construct(array $mapping, Database $db)
{
//...
}
public function where(array $where)
{
$this->where = $where;
return $this;
}
public function getIterator()
{
$items = $this->db->query($this->buildQuery());
return new ArrayIterator($items);
}
protected function buildQuery()
{
// Bouw de query op adv de configuratie
}
}
// Dus
class Post extends ActiveRecord
{
$this->mapping = array(...);
}
foreach(Post::get()->where(['author' => 'Pim']) as $post) { // PHP 5.4 ;)
print $post;
}
?>
Duidelijk?
Wat ik begrijp van de IteratorAggregate is dat als je de klasse oproept in een foreach hij de method getIterator() aanroept, die vervolgens een ArrayIterator teruggeeft? Hierdoor kun je een object ombouwen tot array?
Die static get method heb je alleen gemaakt zodat je geen instance van Post hoeft aan te maken, toch?
Vooral de 'uitleg' van Fabian en Symfony source code helpen me een beetje in het doorkrijgen van Iterators... Wel leuk, weer wat nieuws geleerd!
Pim - op 29/02/2012 21:52:37:
Je geeft de AR abstract klasse een functie get(). Deze retourneerd een query of statement-object. Met de method chaining kan je deze dan configureren en met getIterator() van IteratorAggregate kan je dan de query uitvoeren en over de resultaten itereren.
....
Duidelijk?
....
Duidelijk?
Euhhh :-/ ...ja hoor, hééél duidelijk ;)
Dit is nu alleen voor het krijgen van records. Moet ik nu ook aparte Statement klasse maken voor write, create en delete? En is de ActiveRecord klasse dus eigenlijk niks anders dan een klasse die 4 andere klassen bij elkaar houd en voor 1 algemeen aanspreekpunt zorgt?
Moeten die Statement klasse nou allemaal een eigen db property krijgen, of zou ik die in de ActiveRecord klasse zetten en de Statement klassen deze laten extenden?
Pff, ik ga eerst maar wat meer lezen over AR voordat ik hier aan kan beginnen...
Query Object Pattern. Dit pattern omschrijft hoe je een SQL query kan abstraheren in klassen.
Volgens mij ben je meer op zoek naar het Writes, deletes en updates zijn vaak heel simpel, omdat je precies weet welk object je wil aanpassen. Dat zou ik dan in de AR laag doen. Dus $post->delete().
Ik zou de DB in een statisch veld van de AR klasse doen. Dan beschikt elk object erover en kan je met r9 in het voorbeeld dat meegeven aan de statement klasse.
@Mark,
Dat is idd nog nodig en heb ik niet in het voorbeeld gestopt, leek me niet zo nodig. Mijn voorbeeld laat vooral zien hoe je de communicatie met de AR laag en de DB laag regelt. Hoe je dan de SQL opbouwt, mag je zelf bedenken (Doctine DBAL SQL builder is mss wel de moeite waard).
Ik zou mijn domeinmodel juist niet afhankelijk willen hebben van de persistence layer. Wat in jouw voorbeeld wel is. De Post klasse moet opeens weten hoe hij zichzelf opslaat. Het liefst houdt je alleen business logic in je Post klasse en maak je een aparte klasse om de Post op te slaan.
Voorbeeldje:
Code (php)
1
2
3
4
5
6
2
3
4
5
6
<?php
$post = new Post();
$post->setBody('Lorem ipsum...');
$postRepository->persist($post);
?>
$post = new Post();
$post->setBody('Lorem ipsum...');
$postRepository->persist($post);
?>
En voor het ophalen:
Ja, natuurlijk. Maar we hebben het hier over Active Record, waarbij die lagen inherent gekoppeld zijn. Een DataMapper oid is natuurlijk mooier, maar daar gaat het topic niet over.