Doctrine 2 – Speichern von Verknüpfungen

Im Moment das heißeste Thema hier im Blog (wenn “heiß” auch stark übertrieben wäre), ist noch immer Doctrine 2. Ich hatte mich vor einiger Zeit entschieden, die neue Version des Propel-Konkurrenten für ein Projekt zu verwenden. Zwar befindet sich die Software noch im alpha Stadium, (die Beta wird laut der aktualisierten Roadmap mit zwei Wochen Verspätung erscheinen) ich konnte bisher aber keine Bugs feststellen – lediglich einige Verbesserungswünsche und Unzulänglichkeiten.

Eine davon möchte ich hier besprechen.

Ich habe in dem besagten Projekt eine Menge Formulare, die eine Menge Tabellen füllen, die mit einer Menge Verknüpfungen versehen sind. Beim Abschicken eines Formulares, übermittele ich lediglich die ID, wenn ich eine 1:1 oder 1:n Beziehung festlegen will.
Bevor mein Model in der Datenbank gespeichert wird, wird jedes Feld der Tabelle lediglich durch eine Klassenvariable repräsentiert. Mein Wunsch: Ich speichere die ID in der Variable, der EntityManager erkennt das, kennt außerdem die Verknüpfung, zählt 1 und 1 zusammen und speichert die ID in der Tabelle. Dazu kann er mir gleich das zugehörige Model liefern. Leider klappt das nicht. Der EntityManager will ein Objekt in der Variable haben, schnappt sich dessen ID und speichert diese dann.
Eine elegante Lösung habe ich bisher nicht gefunden, ohne am EntityManager rumzuwerkeln.

Hier trotzdem mal mein Ansatz:

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
<?php
/**
 * @Entity
 * @Table(name="model1")
 */
class Model1 extends BaseModel
{
    /**
     * @Column(name="id", type="integer")
     * @Id
     * @GeneratedValue(strategy="AUTO")
     */
    protected $id;
 
    /**
     * @Column(name="name", type="string", length=255)
     */
    protected $name;
 
    /**
     * @OneToOne(targetEntity="Model2")
     * @JoinColumns({
     *   @JoinColumn(name="model2_id", referencedColumnName="id", nullable=false)
     * })
     */
    protected $model2;
 
    protected $relationClasses = array(
        'model2' => 'Model2'
    );
}

Ein stinknormales Doctrine Model mit einer Verknüpfung zu Model2. Allerdings fällt euch sicher auf, dass die Klasse von einer BaseModel erbt. Außerdem gibt es die Variable $relationClasses, in welcher zu jeder Verknüpfung der Klassenname gesichert ist.

Meine Idee ist folgende: In der BaseModel gibt es eine from() Methode, welche ein Array mit Werten für das Entity akzeptiert. Stößt die Methode auf einen Arrayschlüssel, welcher auch in $relationClasses existiert, wird eine Instanz der zu verknüpfenden Klasse erstellt und statt der ID in der Variablen gesichert.

Die aktuelle BaseModel.php gibts eben auch noch, schauts euch selbst an:

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<?php
 
/** @MappedSuperclass */
class BaseModel {
 
    /**
     * just in case...
     * @return string
     */
    public function __toString() {
        return 'this is not a string!!!';
    }
 
    /**
     * magic method which processes getFieldName() and setFieldName($value)
     * setFieldName() returns the object
     *
     * @param string $method
     * @param array $args
     * @return mixed
     */
    public function __call($method, $args) {
        if(substr($method,0,3)=='get') {
            if(isset($this->{$this->_getFieldName($method)}))
                return $this->{$this->_getFieldName($method)};
        }
 
        if(substr($method,0,3)=='set') {
            if(property_exists(get_class($this), $this->_getFieldName($method))) {
                $this->{$this->_getFieldName($method)} = $args[0];
                return $this;
            }
        }
    }
 
    /**
     * transforms getCamelCaseFieldName to camelCaseFieldName
     *
     * @param string $methodname
     * @return string
     */
    private function _getFieldName($methodname) {
        return lcfirst(substr($methodname, 3));
    }
 
    /**
     * fetches all rows of the database table
     * @return collection
     */
    public static function getAll() {
        $registry = Zend_Registry::getInstance();
        $em = $registry->entitymanager;
        return $em->getRepository(get_called_class())->findAll();
    }
 
    /**
     * finds the row with id $id. If it doesn't exist and $create is true,
     * it creates the object and returns it
     *
     * @param int $id
     * @param boolean $create
     * @return entityname
     */
    public static function find($id, $create = false) {
        $entityname = get_called_class();
        $registry = Zend_Registry::getInstance();
        $em = $registry->entitymanager;
        $match = $em->getRepository($entityname)->find($id);
        if(!is_null($match))
            return $match;
 
        if($create)
            return new $entityname;
 
        return null;
    }
 
    /**
     * populates the object with values from $array, if it's keys are
     * valid table fields.
     *
     * @param array $array
     * @return $this
     */
    public function from($array) {
        foreach($array as $field=>$value) {
 
            if(empty($value) || $value ==  'NULL' || is_null($value)) {
                continue;
            }
            $setter = 'set' . ucfirst($field);
            if(isset($this->relationClasses[$field])) {
                $obj = call_user_func($this->relationClasses[$field] . '::find',$value);
                $this->$setter($obj);
            } else
                $this->$setter($value);
        }
        return $this;
    }
}

0 Responses to “Doctrine 2 – Speichern von Verknüpfungen”


  • No Comments

Leave a Reply