UnitOfWork   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 144
Duplicated Lines 0 %

Test Coverage

Coverage 79.66%

Importance

Changes 0
Metric Value
dl 0
loc 144
ccs 47
cts 59
cp 0.7966
rs 10
c 0
b 0
f 0
wmc 23

9 Methods

Rating   Name   Duplication   Size   Complexity  
D save() 0 30 9
A isDirty() 0 9 3
A change() 0 5 1
A register() 0 12 2
A detectChanges() 0 7 4
A append() 0 5 1
A remove() 0 5 1
A __construct() 0 3 1
A hash() 0 3 1
1
<?php
2
namespace vakata\orm;
3
4
use vakata\database\DBInterface;
5
6
class UnitOfWork
7
{
8
    const CLEAN   = 0;
9
    const DIRTY   = 1;
10
    const ADDED   = 2;
11
    const REMOVED = 3;
12
13
    /**
14
     * @var DBInterface
15
     */
16
    protected $db;
17
    /**
18
     * @var array
19
     */
20
    protected $map = [];
21
22
    /**
23
     * Create an instance
24
     *
25
     * @param DBInterface $db the database object (a transaction will be used when calling save)
26
     */
27 1
    public function __construct(DBInterface $db)
28
    {
29 1
        $this->db = $db;
30 1
    }
31
32 1
    protected function hash($entity)
33
    {
34 1
        return \spl_object_hash($entity);
35
    }
36 1
    protected function detectChanges()
37
    {
38 1
        foreach ($this->map as $hash => $data) {
39 1
            if ($data['state'] === static::CLEAN &&
40 1
                $data['hash']  !== sha1(serialize($data['repository']->toArray($data['entity']))) // TODO
41
            ) {
42 1
                $this->map[$hash]['state'] = static::DIRTY;
43
            }
44
        }
45 1
    }
46 1
    protected function isDirty()
47
    {
48 1
        $this->detectChanges();
49 1
        foreach ($this->map as $hash => $data) {
50 1
            if ($this->map[$hash]['state'] !== static::CLEAN) {
51 1
                return true;
52
            }
53
        }
54 1
        return false;
55
    }
56
    /**
57
     * Register an entity in the unit (will be checked for changes when save is called)
58
     *
59
     * @param mixed $entity
60
     * @param Repository $repository the entity's repository
61
     * @return string the entity's hash
62
     */
63 1
    public function register($entity, Repository $repository)
64
    {
65 1
        $hash = $this->hash($entity);
66 1
        if (!isset($this->map[$hash])) {
67 1
            $this->map[$hash] = [
68 1
                'hash'       => sha1(serialize($repository->toArray($entity))),
69 1
                'state'      => static::CLEAN,
70 1
                'entity'     => $entity,
71 1
                'repository' => $repository
72
            ];
73
        }
74 1
        return $hash;
75
    }
76
    /**
77
     * Mark an entity as changed
78
     *
79
     * @param mixed $entity
80
     * @param Repository $repository the entity's repository
81
     * @return string the entity's hash
82
     */
83
    public function change($entity, Repository $repository)
84
    {
85
        $hash = $this->register($entity, $repository);
86
        $this->map[$hash]['state'] = static::DIRTY;
87
        return $hash;
88
    }
89
    /**
90
     * Add a new entity (mark for insertion)
91
     *
92
     * @param mixed $entity
93
     * @param Repository $repository the entity's repository
94
     * @return string the entity's hash
95
     */
96 1
    public function append($entity, Repository $repository)
97
    {
98 1
        $hash = $this->register($entity, $repository);
99 1
        $this->map[$hash]['state'] = static::ADDED;
100 1
        return $hash;
101
    }
102
    /**
103
     * Mark an entity for removal
104
     *
105
     * @param mixed $entity
106
     * @param Repository $repository the entity's repository
107
     * @return string the entity's hash
108
     */
109
    public function remove($entity, Repository $repository)
110
    {
111
        $hash = $this->register($entity, $repository);
112
        $this->map[$hash]['state'] = static::REMOVED;
113
        return $hash;
114
    }
115
    /**
116
     * Persist all changes
117
     *
118
     * @return void
119
     */
120 1
    public function save()
121
    {
122 1
        $this->db->begin();
123
        try {
124 1
            while ($this->isDirty()) {
125 1
                foreach ($this->map as $hash => $data) {
126 1
                    if ($data['state'] === static::ADDED) {
127 1
                        $data['repository']->append($data['entity']);
128 1
                        $this->map[$hash]['hash'] = sha1(serialize($data['repository']->toArray($data['entity'])));
129 1
                        $this->map[$hash]['state'] = static::CLEAN;
130
                    }
131
                }
132 1
                foreach ($this->map as $hash => $data) {
133 1
                    if ($data['state'] === static::DIRTY) {
134 1
                        $data['repository']->change($data['entity']);
135 1
                        $this->map[$hash]['hash'] = sha1(serialize($data['repository']->toArray($data['entity'])));
136 1
                        $this->map[$hash]['state'] = static::CLEAN;
137
                    }
138
                }
139 1
                foreach ($this->map as $hash => $data) {
140 1
                    if ($data['state'] === static::REMOVED) {
141
                        $data['repository']->remove($data['entity']);
142 1
                        unset($this->map[$hash]);
143
                    }
144
                }
145
            }
146 1
            $this->db->commit();
147
        } catch (\Exception $e) {
148
            $this->db->rollback();
149
            throw $e;
150
        }
151
    }
152
}