Completed
Push — master ( 2d4410...211342 )
by Michael
05:20
created

PreserverTrait::setPreserve()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 5
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
/**
3
 * Contains PreserverTrait Trait.
4
 *
5
 * PHP version 5.5
6
 *
7
 * LICENSE:
8
 * This file is part of Yet Another Php Eve Api Library also know as Yapeal
9
 * which can be used to access the Eve Online API data and place it into a
10
 * database.
11
 * Copyright (C) 2014-2016 Michael Cummings
12
 *
13
 * This program is free software: you can redistribute it and/or modify it
14
 * under the terms of the GNU Lesser General Public License as published by the
15
 * Free Software Foundation, either version 3 of the License, or (at your
16
 * option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful, but WITHOUT
19
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
21
 * for more details.
22
 *
23
 * You should have received a copy of the GNU Lesser General Public License
24
 * along with this program. If not, see
25
 * <http://www.gnu.org/licenses/>.
26
 *
27
 * You should be able to find a copy of this license in the LICENSE.md file. A
28
 * copy of the GNU GPL should also be available in the GNU-GPL.md file.
29
 *
30
 * @copyright 2014-2016 Michael Cummings
31
 * @license   http://www.gnu.org/copyleft/lesser.html GNU LGPL
32
 * @author    Michael Cummings <[email protected]>
33
 */
34
namespace Yapeal\Sql;
35
36
use Yapeal\Event\EveApiEventInterface;
37
use Yapeal\Event\MediatorInterface;
38
use Yapeal\Log\Logger;
39
40
/**
41
 * Trait PreserverTrait
42
 *
43
 * @method CommonSqlQueries getCsq()
44
 * @method \PDO getPdo()
45
 * @method MediatorInterface getYem()
46
 */
47
trait PreserverTrait
48
{
49
    /**
50
     * @return \string[]
51
     * @throws \LogicException
52
     */
53
    public function getPreserveTos()
54
    {
55
        if (0 === count($this->preserveTos)) {
56
            $mess = 'Tried to access preserveTos before it was set';
57
            throw new \LogicException($mess);
58
        }
59
        return $this->preserveTos;
60
    }
61
    /**
62
     * @param EveApiEventInterface $event
63
     * @param string               $eventName
64
     * @param MediatorInterface    $yem
65
     *
66
     * @return EveApiEventInterface
67
     * @throws \DomainException
68
     * @throws \InvalidArgumentException
69
     * @throws \LogicException
70
     */
71
    public function preserveEveApi(EveApiEventInterface $event, $eventName, MediatorInterface $yem)
72
    {
73
        if (!$this->shouldPreserve()) {
74
            return $event;
75
        }
76
        $this->setYem($yem);
0 ignored issues
show
Bug introduced by
It seems like setYem() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
77
        $data = $event->getData();
78
        $xml = $data->getEveApiXml();
79
        if (false === $xml) {
80
            return $event->setHandledSufficiently();
81
        }
82
        $this->getYem()
83
            ->triggerLogEvent('Yapeal.Log.log',
84
                Logger::DEBUG,
85
                $this->getReceivedEventMessage($data, $eventName, __CLASS__));
1 ignored issue
show
Bug introduced by
It seems like getReceivedEventMessage() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
86
        $this->getPdo()
87
            ->beginTransaction();
88
        try {
89
            foreach ($this->getPreserveTos() as $preserveTo) {
90
                $this->$preserveTo($data);
91
            }
92
            $this->getPdo()
93
                ->commit();
94
        } catch (\PDOException $exc) {
95
            $mess = 'Failed to upsert data of';
96
            $this->getYem()
97
                ->triggerLogEvent('Yapeal.Log.log',
98
                    Logger::WARNING,
99
                    $this->createEveApiMessage($mess, $data),
1 ignored issue
show
Bug introduced by
It seems like createEveApiMessage() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
100
                    ['exception' => $exc]);
101
            $this->getPdo()
102
                ->rollBack();
103
            return $event;
104
        }
105
        $this->getYem()
106
            ->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, $this->getFinishedEventMessage($data, $eventName));
1 ignored issue
show
Bug introduced by
It seems like getFinishedEventMessage() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
107
        return $event->setHandledSufficiently();
108
    }
109
    /**
110
     * Turn on or off preserving of Eve API data by this preserver.
111
     *
112
     * Allows class to stay registered for events but be enabled or disabled during runtime.
113
     *
114
     * @param boolean $value
115
     *
116
     * @return $this Fluent interface
117
     */
118
    public function setPreserve($value = true)
119
    {
120
        $this->preserve = (boolean)$value;
121
        return $this;
122
    }
123
    /**
124
     * @param \SimpleXMLElement[] $rows
125
     * @param array               $columnDefaults
126
     * @param string              $tableName
127
     *
128
     * @return self Fluent interface.
129
     * @throws \DomainException
130
     * @throws \InvalidArgumentException
131
     * @throws \LogicException
132
     */
133
    protected function attributePreserveData(array $rows, array $columnDefaults, $tableName)
134
    {
135
        $maxRowCount = 1000;
136
        if (0 === count($rows)) {
137
            return $this;
138
        }
139
        $rows = array_chunk($rows, $maxRowCount, true);
140
        $columnNames = array_keys($columnDefaults);
141
        foreach ($rows as $chunk) {
142
            $this->flush($this->processXmlRows($columnDefaults, $chunk), $columnNames, $tableName);
143
        }
144
        return $this;
145
    }
146
    /**
147
     * @param string[] $columns
148
     * @param string[] $columnNames
149
     * @param string   $tableName
150
     *
151
     * @return self Fluent interface.
152
     * @throws \DomainException
153
     * @throws \InvalidArgumentException
154
     * @throws \LogicException
155
     */
156
    protected function flush(array $columns, array $columnNames, $tableName)
157
    {
158
        if (0 === count($columns)) {
159
            return $this;
160
        }
161
        $rowCount = count($columns) / count($columnNames);
162
        $mess = sprintf('Have %1$s row(s) to upsert into %2$s table', $rowCount, $tableName);
163
        $this->getYem()
164
            ->triggerLogEvent('Yapeal.Log.log', Logger::INFO, $mess);
165
        $sql = $this->getCsq()
166
            ->getUpsert($tableName, $columnNames, $rowCount);
167
        $mess = preg_replace('/(,\(\?(?:,\?)*\))+/', ',...', $sql);
168
        $this->getYem()
169
            ->triggerLogEvent('Yapeal.Log.log', Logger::INFO, $mess);
170
        $mess = implode(',', $columns);
171
        if (512 < strlen($mess)) {
172
            $mess = substr($mess, 0, 512) . '...';
173
        }
174
        $this->getYem()
175
            ->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, $mess);
176
        $this->getPdo()
177
            ->prepare($sql)
178
            ->execute($columns);
179
        return $this;
180
    }
181
    /**
182
     * @param array               $columnDefaults
183
     * @param \SimpleXMLElement[] $rows
184
     *
185
     * @return array
186
     */
187
    protected function processXmlRows(array $columnDefaults, array $rows)
188
    {
189
        $columns = [];
190
        foreach ($rows as $row) {
191
            // Replace empty values with any existing defaults.
192
            foreach ($columnDefaults as $key => $value) {
193
                if (null === $value || '' !== (string)$row[$key]) {
194
                    $columns[] = (string)$row[$key];
195
                    continue;
196
                }
197
                $columns[] = (string)$value;
198
            }
199
        }
200
        return $columns;
201
    }
202
    /**
203
     * @param \SimpleXMLElement[] $elements
204
     * @param array               $columnDefaults
205
     * @param string              $tableName
206
     *
207
     * @return self Fluent interface.
208
     * @throws \DomainException
209
     * @throws \InvalidArgumentException
210
     * @throws \LogicException
211
     */
212
    protected function valuesPreserveData(array $elements, array $columnDefaults, $tableName)
213
    {
214
        if (false === $elements || 0 === count($elements)) {
215
            return $this;
216
        }
217
        $eleCount = 0;
218
        foreach ($elements as $element) {
219
            $columnName = $element->getName();
220
            if (!array_key_exists($columnName, $columnDefaults)) {
221
                continue;
222
            }
223
            ++$eleCount;
224
            if ('' !== (string)$element || null === $columnDefaults[$columnName]) {
225
                $columnDefaults[$columnName] = (string)$element;
226
            }
227
        }
228
        $required = array_reduce($columnDefaults,
229
            function ($carry, $item) {
230
                return $carry + (int)(null === $item);
231
            },
232
            0);
233
        if ($required > $eleCount) {
234
            return $this;
235
        }
236
        uksort($columnDefaults,
237
            function ($alpha, $beta) {
238
                $alpha = strtolower($alpha);
239
                $beta = strtolower($beta);
240
                if ($alpha < $beta) {
241
                    return -1;
242
                } elseif ($alpha > $beta) {
243
                    return 1;
244
                }
245
                return 0;
246
            });
247
        return $this->flush(array_values($columnDefaults), array_keys($columnDefaults), $tableName);
248
    }
249
    /**
250
     * @var string[] preserveTos
251
     */
252
    protected $preserveTos = [];
253
    /**
254
     * @return boolean
255
     */
256
    private function shouldPreserve()
257
    {
258
        return $this->preserve;
259
    }
260
    /**
261
     * @var bool $preserve
262
     */
263
    private $preserve = true;
264
}
265