Passed
Push — master ( 269005...e9dd98 )
by Richard
01:51
created

Database   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 142
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 142
rs 10
c 0
b 0
f 0
wmc 30

15 Methods

Rating   Name   Duplication   Size   Complexity  
A tryUpdate() 0 5 2
A getIfNew() 0 9 3
A deleteById() 0 3 1
A getTryAgain() 0 2 2
A save() 0 20 4
A updatePK() 0 2 3
A findById() 0 2 1
A __construct() 0 20 3
A findAggregate() 0 2 1
A getPrimaryKey() 0 2 1
A insert() 0 14 4
A checkIfUpdateWorked() 0 4 2
A deleteByField() 0 8 1
A selectQuery() 0 2 1
A findByField() 0 2 1
1
<?php
2
namespace Maphper\DataSource;
3
class Database implements \Maphper\DataSource {
4
	const EDIT_STRUCTURE = 1;
5
	const EDIT_INDEX = 2;
6
	const EDIT_OPTIMISE = 4;
7
8
	private $table;
9
    private $options;
10
	private $primaryKey;
11
	private $fields = '*';
12
	private $defaultSort;
13
	private $adapter;
14
	private $crudBuilder;
15
    private $whereBuilder;
16
    private $databaseModify;
17
    private $databaseSelect;
18
    private $alterDb;
19
20
	public function __construct($db, $table, $primaryKey = 'id', array $options = []) {
21
		$this->options = new DatabaseOptions($db, $options);
22
		$this->adapter = $this->options->getAdapter();
23
24
		$this->table = $table;
25
		$this->primaryKey = is_array($primaryKey) ? $primaryKey : [$primaryKey];
26
27
		$this->crudBuilder = new \Maphper\Lib\CrudBuilder();
28
        $this->whereBuilder = new \Maphper\Lib\Sql\WhereBuilder();
29
30
		$this->fields = implode(',', array_map([$this->adapter, 'quote'], (array) $this->options->read('fields')));
31
32
		$this->defaultSort = $this->options->read('defaultSort') !== false ? $this->options->read('defaultSort')  : implode(', ', $this->primaryKey);
33
34
        $this->databaseModify = new DatabaseModify($this->adapter, $this->options->getEditMode(), $this->table);
35
        $this->databaseSelect = new DatabaseSelect($this->adapter, $this->databaseModify, $this->table);
36
37
        $this->alterDb = $this->options->getEditMode();
38
39
		$this->databaseModify->optimizeColumns();
40
	}
41
42
	public function getPrimaryKey() {
43
		return $this->primaryKey;
44
	}
45
46
	public function deleteById($id) {
47
		$this->adapter->query($this->crudBuilder->delete($this->table, [$this->primaryKey[0] . ' = :id'], [':id' => $id], 1));
48
		$this->databaseSelect->deleteIDFromCache($id);
49
	}
50
51
	public function findById($id) {
52
		return $this->databaseSelect->findById($id, $this->getPrimaryKey()[0]);
53
	}
54
55
	public function findAggregate($function, $field, $group = null, array $criteria = [], array $options = []) {
56
		return $this->databaseSelect->findAggregate($function, $field, $group, $criteria, $options);
57
	}
58
59
	public function findByField(array $fields, $options = []) {
60
        return $this->databaseSelect->findByField($fields, $options, $this->defaultSort);
61
	}
62
63
	public function deleteByField(array $fields, array $options = []) {
64
		$query = $this->whereBuilder->createSql($fields);
65
		$this->adapter->query($this->crudBuilder->delete($this->table, $query['sql'], $query['args'], $options['limit'], null, $options['order']));
66
		$this->databaseModify->addIndex(array_keys($query['args']));
67
68
		//Clear the cache
69
		$this->databaseSelect->clearIDCache();
70
		$this->databaseSelect->clearResultCache();
71
	}
72
73
    private function getIfNew($data) {
74
        $new = false;
75
        foreach ($this->primaryKey as $k) {
76
            if (empty($data->$k)) {
77
                $data->$k = null;
78
                $new = true;
79
            }
80
        }
81
        return $new;
82
    }
83
84
	public function save($data, $tryagain = true) {
85
        $new = $this->getIfNew($data);
86
87
		try {
88
            $result = $this->insert($this->table, $this->primaryKey, $data);
89
90
			//If there was an error but PDO is silent, trigger the catch block anyway
91
			if ($result->errorCode() !== '00000') throw new \Exception('Could not insert into ' . $this->table);
92
		}
93
		catch (\Exception $e) {
94
			if (!$this->getTryAgain($tryagain)) throw $e;
95
96
			$this->adapter->alterDatabase($this->table, $this->primaryKey, $data);
97
			$this->save($data, false);
98
		}
99
100
		$this->updatePK($data, $new);
101
		//Something has changed, clear any cached results as they may now be incorrect
102
		$this->databaseSelect->clearResultCache();
103
		$this->databaseSelect->updateCache($data, $data->{$this->primaryKey[0]});
104
	}
105
106
    private function getTryAgain($tryagain) {
107
        return $tryagain && self::EDIT_STRUCTURE & $this->alterDb;
108
    }
109
110
    private function updatePK($data, $new) {
111
        if ($new && count($this->primaryKey) == 1) $data->{$this->primaryKey[0]} = $this->adapter->lastInsertId();
112
    }
113
114
    private function checkIfUpdateWorked($data) {
115
        $updateWhere = $this->whereBuilder->createSql($data);
116
        $matched = $this->findByField($updateWhere['args']);
117
        if (count($matched) == 0) throw new \InvalidArgumentException('Record inserted into table ' . $this->table . ' fails table constraints');
118
    }
119
120
	private function insert($table, array $primaryKey, $data) {
121
		$error = 0;
122
		try {
123
			$result = $this->adapter->query($this->crudBuilder->insert($table, $data));
124
		}
125
		catch (\Exception $e) {
126
			$error = 1;
127
		}
128
129
 		if ($error || $result->errorCode() !== '00000') {
130
            $result = $this->tryUpdate($table, $primaryKey, $data);
131
        }
132
133
		return $result;
134
	}
135
136
    private function tryUpdate($table, array $primaryKey, $data) {
137
        $result = $this->adapter->query($this->crudBuilder->update($table, $primaryKey, $data));
138
        if ($result->rowCount() === 0) $this->checkIfUpdateWorked($data);
139
140
        return $result;
141
    }
142
143
    private function selectQuery(\Maphper\Lib\Query $query) {
0 ignored issues
show
Unused Code introduced by
The method selectQuery() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
144
        return $this->adapter->query($query)->fetchAll(\PDO::FETCH_OBJ);
145
    }
146
}
147