1 | <?php |
||||
2 | |||||
3 | declare(strict_types = 1); |
||||
4 | |||||
5 | namespace Protoqol\Prequel\Classes\Database; |
||||
6 | |||||
7 | use Illuminate\Database\Eloquent\Model; |
||||
8 | use Illuminate\Support\Arr; |
||||
9 | use Illuminate\Support\Facades\DB; |
||||
10 | use Illuminate\Support\Str; |
||||
11 | |||||
12 | /** |
||||
13 | * Class DatabaseTraverser |
||||
14 | * |
||||
15 | * @package Protoqol\Prequel\Classes\Database |
||||
16 | */ |
||||
17 | class DatabaseTraverser |
||||
18 | { |
||||
19 | |||||
20 | /** |
||||
21 | * Type of database e.g. mysql, postgres, sqlite or sql server |
||||
22 | * |
||||
23 | * @var string $databaseConn |
||||
24 | */ |
||||
25 | private $databaseConn; |
||||
26 | |||||
27 | /** |
||||
28 | * Query collection based on $DB_CONN |
||||
29 | * |
||||
30 | * @var $databaseQueries |
||||
0 ignored issues
–
show
Documentation
Bug
introduced
by
![]() |
|||||
31 | */ |
||||
32 | private $databaseQueries; |
||||
33 | |||||
34 | /** |
||||
35 | * DatabaseTraverser constructor. |
||||
36 | * |
||||
37 | * @param string|null $databaseType Type of database see $_databaseConn |
||||
38 | */ |
||||
39 | public function __construct(?string $databaseType = null) |
||||
40 | { |
||||
41 | $this->databaseConn = $databaseType |
||||
42 | ?: config('database.default'); |
||||
43 | |||||
44 | $this->databaseQueries = new SequelAdapter($this->databaseConn); |
||||
45 | } |
||||
46 | |||||
47 | /** |
||||
48 | * Build array of all databases and their respective tables and |
||||
49 | * sort alphabetically. |
||||
50 | * |
||||
51 | * @return array |
||||
52 | * @throws \Exception |
||||
53 | */ |
||||
54 | public function getAll() :array |
||||
55 | { |
||||
56 | $collection = []; |
||||
57 | $flatTableCollection = []; |
||||
58 | |||||
59 | foreach ($this->getAllDatabases() as $value) { |
||||
60 | $databaseName = (object) $value['name']; |
||||
61 | |||||
62 | $collection[$databaseName->pretty] = [ |
||||
63 | "official_name" => $databaseName->official, |
||||
64 | "pretty_name" => $databaseName->pretty, |
||||
65 | "tables" => $this->getTablesFromDB($databaseName->official), |
||||
66 | ]; |
||||
67 | |||||
68 | foreach ($collection[$databaseName->pretty]['tables'] as $table) { |
||||
69 | $tableName = $databaseName->official.'.' |
||||
70 | .$table['name']['official']; |
||||
71 | |||||
72 | array_push($flatTableCollection, $tableName); |
||||
73 | } |
||||
74 | |||||
75 | } |
||||
76 | |||||
77 | ksort($collection); |
||||
78 | |||||
79 | return [ |
||||
80 | 'collection' => $collection, |
||||
81 | 'flatTableCollection' => $flatTableCollection, |
||||
82 | ]; |
||||
83 | } |
||||
84 | |||||
85 | /** |
||||
86 | * Tries to find matching model for the given table. |
||||
87 | * |
||||
88 | * @param string|null $tableName |
||||
89 | * |
||||
90 | * @return Model|bool |
||||
91 | */ |
||||
92 | public function getModel(?string $tableName) |
||||
93 | { |
||||
94 | $model = 'App\\'.Str::studly(Str::singular($tableName)); |
||||
95 | if (class_exists($model)) { |
||||
96 | return new $model; |
||||
97 | } |
||||
98 | |||||
99 | $model = 'App\\Models\\'.Str::studly(Str::singular($tableName)); |
||||
100 | if (class_exists($model)) { |
||||
101 | return new $model; |
||||
102 | } |
||||
103 | |||||
104 | $model = 'App\\Model\\'.Str::studly(Str::singular($tableName)); |
||||
105 | if (class_exists($model)) { |
||||
106 | return new $model; |
||||
107 | } |
||||
108 | |||||
109 | return false; |
||||
110 | } |
||||
111 | |||||
112 | /** |
||||
113 | * Get information about a specific column |
||||
114 | * |
||||
115 | * @param string $database Database name |
||||
116 | * @param string $table Table name |
||||
117 | * @param array $column Column name |
||||
118 | * |
||||
119 | * @return array |
||||
120 | */ |
||||
121 | public function getColumnData( |
||||
122 | string $database, |
||||
123 | string $table, |
||||
124 | array $column |
||||
125 | ) :array { |
||||
126 | $select = [ |
||||
127 | 'TABLE_SCHEMA', |
||||
128 | 'TABLE_NAME', |
||||
129 | 'COLUMN_NAME', |
||||
130 | 'COLUMN_DEFAULT', |
||||
131 | 'IS_NULLABLE', |
||||
132 | 'COLUMN_COMMENT', |
||||
133 | ]; |
||||
134 | |||||
135 | $result = (DB::table("information_schema.COLUMNS") |
||||
136 | ->select($select) |
||||
137 | ->where([ |
||||
138 | ['TABLE_SCHEMA', '=', $database], |
||||
139 | ['TABLE_NAME', '=', $table], |
||||
140 | ['COLUMN_NAME', '=', $column], |
||||
141 | ]) |
||||
142 | ->get())->toArray(); |
||||
143 | |||||
144 | return Arr::flatten((array) $result); |
||||
145 | } |
||||
146 | |||||
147 | /** |
||||
148 | * Get table structure |
||||
149 | * |
||||
150 | * @param string $database Database name |
||||
151 | * @param string $table Table name |
||||
152 | * |
||||
153 | * @return array |
||||
154 | */ |
||||
155 | public function getTableStructure(string $database, string $table) :array |
||||
156 | { |
||||
157 | $columns = DB::select("SHOW COLUMNS FROM `$database`.`$table`"); |
||||
158 | return $columns; |
||||
159 | } |
||||
160 | |||||
161 | /** |
||||
162 | * Get all tables from database |
||||
163 | * |
||||
164 | * @param string $database Database name |
||||
165 | * |
||||
166 | * @return array |
||||
167 | * @throws \Exception |
||||
168 | */ |
||||
169 | public function getTablesFromDB(string $database) :array |
||||
170 | { |
||||
171 | $tables = DB::select($this->databaseQueries->showTablesFrom($database)); |
||||
172 | return $this->normalise($tables); |
||||
173 | } |
||||
174 | |||||
175 | /** |
||||
176 | * Get all tables from "main" database (DB_DATABASE in .env) |
||||
177 | * |
||||
178 | * @return array |
||||
179 | * @throws \Exception |
||||
180 | */ |
||||
181 | public function getAllTables() :array |
||||
182 | { |
||||
183 | $tables = DB::select($this->databaseQueries->showTables()); |
||||
184 | return $this->normalise($tables); |
||||
185 | } |
||||
186 | |||||
187 | /** |
||||
188 | * Get all databases |
||||
189 | * |
||||
190 | * @return array |
||||
191 | * @throws \Exception |
||||
192 | */ |
||||
193 | public function getAllDatabases() :array |
||||
194 | { |
||||
195 | $databases = DB::select($this->databaseQueries->showDatabases()); |
||||
196 | return $this->normalise($databases); |
||||
197 | } |
||||
198 | |||||
199 | /** |
||||
200 | * Normalise query results; assumes a lot about the structure, which can |
||||
201 | * potentially cause problems later on. |
||||
202 | * Assumed structure: |
||||
203 | * ----------------- |
||||
204 | * Array [ |
||||
205 | * Object { |
||||
206 | * 'String': Mixed (single value) |
||||
207 | * ----------------- |
||||
208 | * |
||||
209 | * @param array $arr | Query results |
||||
210 | * |
||||
211 | * @return array |
||||
212 | */ |
||||
213 | public function normalise(array $arr) |
||||
214 | { |
||||
215 | $normalised = []; |
||||
216 | |||||
217 | for ($iterator = 0; $iterator < count($arr); $iterator++) { |
||||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||||
218 | foreach ($arr[$iterator] as $value) { |
||||
219 | $arrayValue = ((array) $value)[0]; |
||||
220 | |||||
221 | $normalised[$iterator]['name'] = [ |
||||
222 | "official" => $arrayValue, |
||||
223 | "pretty" => $this->prettifyName($arrayValue), |
||||
224 | ]; |
||||
225 | } |
||||
226 | } |
||||
227 | |||||
228 | return $normalised; |
||||
229 | } |
||||
230 | |||||
231 | /** |
||||
232 | * Prettify names, meaning: remove special characters; capitalise each word. |
||||
233 | * |
||||
234 | * @param string $name |
||||
235 | * |
||||
236 | * @return string |
||||
237 | */ |
||||
238 | public function prettifyName(string $name) :string |
||||
239 | { |
||||
240 | $words = preg_split('/[!@#$%^&*(),.?":{}|<>_-]/', $name); |
||||
241 | $prettyName = ''; |
||||
242 | |||||
243 | for ($iterator = 0; $iterator < count($words); $iterator++) { |
||||
0 ignored issues
–
show
It seems like
$words can also be of type false ; however, parameter $var of count() does only seem to accept Countable|array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||||
244 | $prettyName .= ucfirst(strtolower($words[$iterator])); |
||||
245 | |||||
246 | if ($iterator !== (count($words) - 1)) { |
||||
247 | $prettyName .= ' '; |
||||
248 | continue; |
||||
249 | } |
||||
250 | } |
||||
251 | |||||
252 | return $prettyName; |
||||
253 | } |
||||
254 | } |
||||
255 |