Completed
Push — master ( 31b5db...524ecf )
by Michael
03:26
created

Creator   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 260
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 0%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 31
c 4
b 0
f 0
lcom 1
cbo 5
dl 0
loc 260
ccs 0
cts 184
cp 0
rs 9.8

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
C createSql() 0 71 8
A setPlatform() 0 5 1
C processRowset() 0 31 7
B processValueOnly() 0 28 5
A getPlatform() 0 4 1
A getSqlKeys() 0 7 2
A hasOwner() 0 4 1
B inferTypeFromName() 0 31 5
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * Contains Creator class.
5
 *
6
 * PHP version 7.0+
7
 *
8
 * LICENSE:
9
 * This file is part of Yet Another Php Eve Api Library also know as Yapeal which can be used to access the Eve Online
10
 * API data and place it into a database.
11
 * Copyright (C) 2015-2016 Michael Cummings
12
 *
13
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General
14
 * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option)
15
 * any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
18
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
19
 * details.
20
 *
21
 * You should have received a copy of the GNU Lesser General Public License along with this program. If not, see
22
 * <http://spdx.org/licenses/LGPL-3.0.html>.
23
 *
24
 * You should be able to find a copy of this license in the LICENSE.md file. A copy of the GNU GPL should also be
25
 * available in the GNU-GPL.md file.
26
 *
27
 * @copyright 2015-2016 Michael Cummings
28
 * @license   http://www.gnu.org/copyleft/lesser.html GNU LGPL
29
 * @author    Michael Cummings <[email protected]>
30
 */
31
namespace Yapeal\Sql;
32
33
use Yapeal\Console\Command\EveApiCreatorTrait;
34
use Yapeal\Event\EveApiEventInterface;
35
use Yapeal\Event\MediatorInterface;
36
use Yapeal\Log\Logger;
37
38
/**
39
 * Class Creator
40
 */
41
class Creator
42
{
43
    use EveApiCreatorTrait;
44
    /**
45
     * Creator constructor.
46
     *
47
     * @param \Twig_Environment $twig
48
     * @param string            $dir
49
     * @param string            $platform
50
     */
51
    public function __construct(\Twig_Environment $twig, string $dir = __DIR__, $platform = 'MySql')
52
    {
53
        $this->setDir($dir);
54
        $this->setPlatform($platform);
55
        $this->setTwig($twig);
56
    }
57
    /**
58
     * @param EveApiEventInterface $event
59
     * @param string               $eventName
60
     * @param MediatorInterface    $yem
61
     *
62
     * @return EveApiEventInterface
63
     * @throws \DomainException
64
     * @throws \InvalidArgumentException
65
     * @throws \LogicException
66
     */
67
    public function createSql(
68
        EveApiEventInterface $event,
69
        string $eventName,
70
        MediatorInterface $yem
71
    ): EveApiEventInterface
72
    {
73
        $this->setYem($yem);
74
        $data = $event->getData();
75
        $yem->triggerLogEvent('Yapeal.Log.log',
76
            Logger::DEBUG,
77
            $this->getReceivedEventMessage($data, $eventName, __CLASS__));
78
        // Only work with raw unaltered XML data.
79
        if (false !== strpos($data->getEveApiXml(), '<?yapeal.parameters.json')) {
80
            return $event->setHandledSufficiently();
81
        }
82
        $this->sectionName = $data->getEveApiSectionName();
83
        $this->apiName = $data->getEveApiName();
84
        $outputFile = sprintf('%1$s%2$s/Create%3$s.sql',
85
            $this->getDir(),
86
            ucfirst($this->sectionName),
87
            ucfirst($this->apiName));
88
        // Nothing to do if NOT overwriting and file exists.
89
        if (false === $this->isOverwrite() && is_file($outputFile)) {
90
            return $event->setHandledSufficiently();
91
        }
92
        $sxi = new \SimpleXMLIterator($data->getEveApiXml());
93
        $this->tables = [];
94
        $this->processValueOnly($sxi, $this->apiName);
95
        $this->processRowset($sxi, $this->apiName);
96
        $tCount = count($this->tables);
97
        if (0 === $tCount) {
98
            $mess = 'No SQL tables to create for';
99
            $yem->triggerLogEvent('Yapeal.Log.log', Logger::NOTICE, $this->createEveApiMessage($mess, $data));
100
            return $event->setHandledSufficiently();
101
        }
102
        ksort($this->tables);
103
        list($mSec, $sec) = explode(' ', microtime());
104
        $vars = [
105
            'className' => lcfirst($this->apiName),
106
            'tables' => $this->tables,
107
            'sectionName' => lcfirst($this->sectionName),
108
            'version' => gmdate('YmdHis', $sec) . substr($mSec, 1, 4)
109
        ];
110
        $templateName = $this->getTemplateName($this->getPlatform(),
111
            ucfirst($this->sectionName),
112
            $data->getEveApiName());
113
        if (false === $templateName) {
114
            $mess = sprintf('Failed to find usable %1$s template file during', $this->getPlatform());
115
            $yem->triggerLogEvent('Yapeal.Log.log',
116
                Logger::WARNING,
117
                $this->createEventMessage($mess, $data, $eventName));
118
            return $event;
119
        }
120
        try {
121
            $contents = $this->getTwig()
122
                ->render($templateName, $vars);
123
        } catch (\Twig_Error $exp) {
124
            $yem->triggerLogEvent('Yapeal.Log.log',
125
                    Logger::WARNING,
126
                    $this->getFailedToWriteFile($data, $eventName, $outputFile));
127
            $yem->triggerLogEvent('Yapeal.Log.log', Logger::ERROR, 'Twig error', ['exception' => $exp]);
128
            return $event;
129
        }
130
        if (false === $this->safeFileWrite($contents, $outputFile, $this->getYem())) {
131
            $yem->triggerLogEvent($eventName,
132
                    Logger::WARNING,
133
                    $this->getFailedToWriteFile($data, $eventName, $outputFile));
134
            return $event;
135
        }
136
        return $event->setHandledSufficiently();
137
    }
138
    /**
139
     * @param string $platform
140
     *
141
     * @return self Fluent interface.
142
     */
143
    public function setPlatform(string $platform)
144
    {
145
        $this->platform = $platform;
146
        return $this;
147
    }
148
    /**
149
     * @param \SimpleXMLIterator $sxi
150
     * @param string             $apiName
151
     * @param string             $xPath
152
     */
153
    protected function processRowset(\SimpleXMLIterator $sxi, string $apiName, string $xPath = '//result/rowset')
154
    {
155
        $items = $sxi->xpath($xPath);
156
        if (0 === count($items)) {
157
            return;
158
        }
159
        foreach ($items as $ele) {
160
            $rsName = ucfirst((string)$ele['name']);
161
            $colNames = explode(',', (string)$ele['columns']);
162
            $keyNames = explode(',', (string)$ele['key']);
163
            $columns = [];
164
            foreach ($keyNames as $keyName) {
165
                $columns[$keyName] = $this->inferTypeFromName($keyName);
166
            }
167
            foreach ($colNames as $colName) {
168
                $columns[$colName] = $this->inferTypeFromName($colName);
169
            }
170
            if ($this->hasOwner()) {
171
                $columns['ownerID'] = 'BIGINT(20) UNSIGNED NOT NULL';
172
            }
173
            uksort($columns,
174
                function ($alpha, $beta) {
175
                    return strtolower($alpha) <=> strtolower($beta);
176
                });
177
            if (0 === count($this->tables)) {
178
                $this->tables[$apiName] = ['columns' => $columns, 'keys' => $this->getSqlKeys($keyNames)];
179
            } else {
180
                $this->tables[$rsName] = ['columns' => $columns, 'keys' => $this->getSqlKeys($keyNames)];
181
            }
182
        }
183
    }
184
    /**
185
     * @param \SimpleXMLIterator $sxi
186
     * @param string             $tableName
187
     * @param string             $xpath
188
     */
189
    protected function processValueOnly(
190
        \SimpleXMLIterator $sxi,
191
        string $tableName,
192
        string $xpath = '//result/child::*[not(*|@*|self::dataTime)]'
193
    ) {
194
        $items = $sxi->xpath($xpath);
195
        if (0 === count($items)) {
196
            return;
197
        }
198
        $columns = [];
199
        foreach ($items as $ele) {
200
            $name = (string)$ele->getName();
201
            $columns[$name] = $this->inferTypeFromName($name, true);
202
        }
203
        if ($this->hasOwner()) {
204
            $columns['ownerID'] = 'BIGINT(20) UNSIGNED NOT NULL';
205
        }
206
        uksort($columns,
207
            function ($alpha, $beta) {
208
                return strtolower($alpha) <=> strtolower($beta);
209
            });
210
        $keys = $this->getSqlKeys();
211
        if (0 !== count($keys)) {
212
            $this->tables[$tableName] = ['columns' => $columns, 'keys' => $keys];
213
        } else {
214
            $this->tables[$tableName] = ['columns' => $columns];
215
        }
216
    }
217
    /**
218
     * @return string
219
     */
220
    private function getPlatform(): string
221
    {
222
        return $this->platform;
223
    }
224
    /**
225
     * @param string[] $keyNames
226
     *
227
     * @return string[]
228
     */
229
    private function getSqlKeys(array $keyNames = []): array
230
    {
231
        if ($this->hasOwner()) {
232
            array_unshift($keyNames, 'ownerID');
233
        }
234
        return array_unique($keyNames);
235
    }
236
    /**
237
     * Used to determine if API is in section that has an owner.
238
     *
239
     * @return bool
240
     */
241
    private function hasOwner(): bool
242
    {
243
        return in_array(strtolower($this->sectionName), ['account', 'char', 'corp'], true);
244
    }
245
    /**
246
     * Used to infer(choose) type from element or attribute's name.
247
     *
248
     * @param string $name Name of the element or attribute.
249
     * @param bool   $forValue Determines if returned type is going to be used for element or an attribute.
250
     *
251
     * @return string Returns the inferred type from the name.
252
     */
253
    private function inferTypeFromName(string $name, bool $forValue = false): string
254
    {
255
        if ('ID' === substr($name, -2)) {
256
            return 'BIGINT(20) UNSIGNED NOT NULL';
257
        }
258
        $name = strtolower($name);
259
        foreach ([
260
                     'descr' => 'TEXT NOT NULL',
261
                     'name' => 'CHAR(100) NOT NULL',
262
                     'balance' => 'DECIMAL(17, 2) NOT NULL',
263
                     'isk' => 'DECIMAL(17, 2) NOT NULL',
264
                     'tax' => 'DECIMAL(17, 2) NOT NULL',
265
                     'timeefficiency' => 'TINYINT(3) UNSIGNED NOT NULL',
266
                     'date' => 'DATETIME NOT NULL DEFAULT \'1970-01-01 00:00:01\'',
267
                     'time' => 'DATETIME NOT NULL DEFAULT \'1970-01-01 00:00:01\'',
268
                     'until' => 'DATETIME NOT NULL DEFAULT \'1970-01-01 00:00:01\'',
269
                     'errorcode' => 'SMALLINT(4) UNSIGNED NOT NULL',
270
                     'level' => 'SMALLINT(4) UNSIGNED NOT NULL',
271
                     'logoncount' => 'BIGINT(20) UNSIGNED NOT NULL',
272
                     'logonminutes' => 'BIGINT(20) UNSIGNED NOT NULL',
273
                     'trainingend' => 'DATETIME NOT NULL DEFAULT \'1970-01-01 00:00:01\'',
274
                     'skillintraining' => 'BIGINT(20) UNSIGNED NOT NULL',
275
                     'trainingdestinationsp' => 'BIGINT(20) UNSIGNED NOT NULL',
276
                     'trainingstartsp' => 'BIGINT(20) UNSIGNED NOT NULL'
277
                 ] as $search => $replace) {
278
            if (false !== strpos($name, $search)) {
279
                return $replace;
280
            }
281
        }
282
        return $forValue ? 'TEXT NOT NULL' : 'VARCHAR(255) DEFAULT \'\'';
283
    }
284
    /**
285
     * @var string $apiName
286
     */
287
    private $apiName;
288
    /**
289
     * @var string $platform Sql connection platform being used.
290
     */
291
    private $platform;
292
    /**
293
     * @var string $sectionName
294
     */
295
    private $sectionName;
296
    /**
297
     * @var array $tables
298
     */
299
    private $tables;
300
}
301