Completed
Push — master ( 2a510c...5bfa5e )
by Richard
03:56
created

IndexDBReader::get_inserts()   C

Complexity

Conditions 7
Paths 22

Size

Total Lines 41
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 41
ccs 32
cts 32
cp 1
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 27
nc 22
nop 0
crap 7
1
<?php
2
/******************************************************************************
3
 * An implementation of dicto (scg.unibe.ch/dicto) in and for PHP.
4
 *
5
 * Copyright (c) 2016, 2015 Richard Klees <[email protected]>
6
 *
7
 * This software is licensed under The MIT License. You should have received
8
 * a copy of the license along with the code.
9
 */
10
11
namespace Lechimp\Dicto\DB;
12
13
use Lechimp\Dicto\Indexer\Insert;
14
15
/**
16
 * Reads in an sql IndexDB to another Index.
17
 */
18
class IndexDBReader {
19
    /**
20
     * @var array
21
     */
22
    protected $tables;
23
24
    /**
25
     * Lists of fields that contain an id.
26
     *
27
     * @var array<string,int>
28
     */
29
    protected $id_fields;
30
31
    /**
32
     * List of all fields that contain an integer.
33
     *
34
     * @var array<string,0>
35
     */
36
    protected $int_fields;
37
38
    /**
39
     * @var \Closure
40
     */
41
    protected $get_query_builder;
42
43
    /**
44
     * @var Insert
45
     */
46
    protected $other;
47
48
    /**
49
     * @var array<int|null,mixed>|null
50
     */
51
    protected $id_map = null;
52
53
    /**
54
     * @var array|null
55
     */
56
    protected $results = null;
57
58
    /**
59
     * @var bool
60
     */
61
    protected $already_run;
62
63
    /**
64
     * @param   array   $tables     list of tables and fields according to IndexDB
65
     * @param   array   $id_fields  list of fields that contain a id
66
     * @param   array   $int_fields list of fields that contain an int
67
     * @param   \Closure    $get_query_builder  to get a QueryBuilder
68
     */
69 1
    public function __construct($tables, $id_fields, $int_fields, \Closure $get_query_builder, Insert $insert) {
70 1
        $this->tables = $tables;
71 1
        $this->id_fields = $id_fields;
72 1
        $this->int_fields = $int_fields;
73 1
        $this->get_query_builder = $get_query_builder;
74 1
        $this->other = $insert;
75 1
        $this->already_run = false;
76 1
    }
77
78
79
    /**
80
     * Read the index from the database and put it to insert.
81
     */
82 1
    public function run() {
83 1
        assert('!$this->already_run');
84
85 1
        $this->id_map = [null => null];
86
87 1
        $this->results = $this->build_results_with_id();
88 1
        $inserts = $this->get_inserts();
89 1
        $this->write_inserts($inserts);
90
91 1
        $this->already_run = true;
92 1
    }
93
94
    /**
95
     * Builds a list of inserts ordered by the id.
96
     *
97
     * @return  \Iterator   $table => $values
98
     */
99 1
    protected function get_inserts() {
100 1
        $count = count($this->results);
101 1
        $i = 0;
102 1
        $expected_id = 0;
103
        // TODO: This will loop forever if there are (unexpected) holes
104
        // in the sequence of ids.
105 1
        while ($count > 0) {
106 1
            if ($i >= $count) {
107 1
                $i = 0;
108 1
            }
109
110 1
            $current_res = $this->results[$i][1];
111 1
            if ($current_res !== null) {
112 1
                if ($current_res["id"] != $expected_id) {
113 1
                    $i++;
114 1
                    continue;
115
                }
116
117 1
                $this->transform_results($current_res);
118 1
                yield $this->results[$i][0] => $current_res;
119 1
                $this->results[$i][1] = null;
120 1
                $expected_id++;
121 1
            }
122
123 1
            $res = $this->results[$i][2]->fetch();
124 1
            if ($res) {
125 1
                $this->results[$i][1] = $res;
126 1
            }
127
            else {
128 1
                $count--;
129 1
                unset($this->results[$i]);
130 1
                $this->results = array_values($this->results);
131
            }
132 1
        }
133
134 1
        $rs = $this->select_all_from("relations");
135 1
        while ($res = $rs->fetch()) {
136 1
            $this->transform_results($res);
137 1
            yield "relations" => $res;
138 1
        }
139 1
    }
140
141
    /**
142
     * Initialize all tables that contain an id with their results.
143
     *
144
     * @return  array[] containing [$table_name, null, $results]
145
     */
146 1
    protected function build_results_with_id() {
147 1
        $results = [];
148 1
        foreach ($this->tables as $key => $_) {
149 1
            if ($key == "relations") {
150 1
                continue;
151
            }
152 1
            $results[] = [$key, null, $this->select_all_from($key)];
153 1
        }
154 1
        return $results;
155
    }
156
157
    /**
158
     * @param   string  $table
159
     * @return  \Iterator
160
     */
161 1
    protected function select_all_from($table) {
162 1
        $get_query_builder = $this->get_query_builder;
163 1
        return $get_query_builder()
164 1
            ->select($this->tables[$table][1])
165 1
            ->from($table)
166 1
            ->execute();
167
    }
168
169
    /**
170
     * Transforms results as retreived from sql to their appropriate internal
171
     * representation.
172
     *
173
     * TODO: Unrolling this loop might make things faster.
174
     *
175
     * @param   array   &$results    from database
176
     * @return  array
177
     */
178 1
    protected function transform_results(array &$results) {
179 1
        foreach (array_keys($results) as $field) {
180 1
            if (isset($this->id_fields[$field])) {
181 1
                $results[$field] = $this->id_map[$results[$field]];
182 1
            }
183 1
            else if (isset($this->int_fields[$field])) {
184 1
                $results[$field] = (int)$results[$field];
185 1
            }
186 1
        }
187 1
    }
188
189
    /**
190
     * @param   \Iterator       $inserts
191
     * @return  null
192
     */
193 1
    protected function write_inserts(\Iterator $inserts) {
194 1
        foreach ($inserts as $table => $insert) {
195 1
            assert('array_key_exists($table, $this->tables)');
196 1
            $method = $this->tables[$table][0];
197 1
            if (isset($insert["id"])) {
198 1
                $id = $insert["id"];
199 1
                unset($insert["id"]);
200 1
                $this->id_map[$id] = call_user_func_array([$this->other, $method], $insert);
201 1
            }
202
            else {
203 1
                call_user_func_array([$this->other, $method], $insert);
204
            }
205 1
        }
206 1
    }
207
}
208