Completed
Push — master ( 2cff5f...81de03 )
by Michael
03:19
created

Creator::processValueOnly()   C

Complexity

Conditions 7
Paths 9

Size

Total Lines 38
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

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