Completed
Push — master ( 004cad...7a714b )
by Sébastien
07:11
created

SqlSource   B

Complexity

Total Complexity 30

Size/Duplication

Total Lines 255
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 17

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 30
lcom 1
cbo 17
dl 0
loc 255
ccs 0
cts 123
cp 0
rs 7.8571
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 2
A setSelect() 0 6 1
A getSelect() 0 4 1
A select() 0 8 2
C assignOptions() 0 30 7
B getData() 0 43 6
A loadDefaultColumnModel() 0 9 1
A getMetadataReader() 0 8 2
A getDefaultMetadataReader() 0 13 3
A getQueryString() 0 8 2
A __toString() 0 12 3
1
<?php
2
3
/*
4
 * soluble-flexstore library
5
 *
6
 * @author    Vanvelthem Sébastien
7
 * @link      https://github.com/belgattitude/soluble-flexstore
8
 * @copyright Copyright (c) 2016-2017 Vanvelthem Sébastien
9
 * @license   MIT License https://github.com/belgattitude/soluble-flexstore/blob/master/LICENSE.md
10
 *
11
 */
12
13
namespace Soluble\FlexStore\Source\Zend;
14
15
use Soluble\FlexStore\Source\AbstractSource;
16
use Soluble\FlexStore\Source\QueryableSourceInterface;
17
use Soluble\FlexStore\ResultSet\ResultSet;
18
use Soluble\FlexStore\Exception;
19
use Soluble\FlexStore\Options;
20
use Zend\Db\Adapter\Adapter;
21
use Zend\Db\Sql\Sql;
22
use Zend\Db\Sql\Select;
23
use Zend\Db\Sql\Expression;
24
use ArrayObject;
25
use Soluble\FlexStore\Column\ColumnModel;
26
use Soluble\FlexStore\Column\Type\MetadataMapper;
27
use Soluble\Metadata\Reader as MetadataReader;
28
29
class SqlSource extends AbstractSource implements QueryableSourceInterface
30
{
31
    /**
32
     * @var Sql
33
     */
34
    protected $sql;
35
36
    /**
37
     * @var Select
38
     */
39
    protected $select;
40
41
    /**
42
     * @var Adapter
43
     */
44
    protected $adapter;
45
46
    /**
47
     * Initial params received in the constructor.
48
     *
49
     * @var ArrayObject
50
     */
51
    protected $params;
52
53
    /**
54
     * @var string
55
     */
56
    protected $query_string;
57
58
    /**
59
     * @var \Zend\Db\Adapter\Driver\Mysqli\Statement
60
     */
61
    protected static $cache_stmt_prototype;
62
63
    /**
64
     * @var \Zend\Db\Adapter\Driver\ResultInterface
65
     */
66
    protected static $cache_result_prototype;
67
68
    /**
69
     * @var ColumnModel
70
     */
71
    protected $columnModel;
72
73
    /**
74
     * @param Adapter $adapter
75
     * @param Select  $select
76
     */
77
    public function __construct(Adapter $adapter, Select $select = null)
78
    {
79
        $this->adapter = $adapter;
80
        $this->sql = new Sql($this->adapter);
81
        if ($select !== null) {
82
            $this->setSelect($select);
83
        }
84
    }
85
86
    /**
87
     * @param Select $select
88
     *
89
     * @return SqlSource
90
     */
91
    public function setSelect(Select $select)
92
    {
93
        $this->select = $select;
94
95
        return $this;
96
    }
97
98
    /**
99
     * @return Select
100
     */
101
    public function getSelect()
102
    {
103
        return $this->select();
104
    }
105
106
    /**
107
     * @return Select
108
     */
109
    public function select()
110
    {
111
        if ($this->select === null) {
112
            $this->select = $this->sql->select();
113
        }
114
115
        return $this->select;
116
    }
117
118
    /**
119
     * @param Select  $select
120
     * @param Options $options
121
     *
122
     * @return Select
123
     */
124
    protected function assignOptions(Select $select, Options $options)
125
    {
126
        if ($options->hasLimit()) {
127
            $select->limit($options->getLimit());
128
            if ($options->hasOffset()) {
129
                $select->offset($options->getOffset());
130
            }
131
132
            if ($options->getLimit() > 0) {
133
                /**
134
                 * For mysql queries, to allow counting rows we must prepend
135
                 * SQL_CALC_FOUND_ROWS to the select quantifiers.
136
                 */
137
                $calc_found_rows = 'SQL_CALC_FOUND_ROWS';
138
                $quant_state = $select->getRawState($select::QUANTIFIER);
139
                if ($quant_state !== null) {
140
                    if ($quant_state instanceof Expression) {
141
                        $quant_state->setExpression($calc_found_rows . ' ' . $quant_state->getExpression());
142
                    } elseif (is_string($quant_state)) {
143
                        $quant_state = $calc_found_rows . ' ' . $quant_state;
144
                    }
145
                    $select->quantifier($quant_state);
146
                } else {
147
                    $select->quantifier(new Expression($calc_found_rows));
148
                }
149
            }
150
        }
151
152
        return $select;
153
    }
154
155
    /**
156
     * @param Options $options
157
     *
158
     * @throws Exception\EmptyQueryException
159
     * @throws Exception\ErrorException
160
     *
161
     * @return ResultSet
162
     */
163
    public function getData(Options $options = null)
164
    {
165
        if ($options === null) {
166
            $options = $this->getOptions();
167
        }
168
169
        $select = $this->assignOptions(clone $this->getSelect(), $options);
170
171
        $sql = new Sql($this->adapter);
172
173
        $sql_string = (string) $sql->buildSqlString($select);
174
        //echo $this->select->getSqlString($this->adapter->getPlatform());
175
        //echo "----" . var_dump($sql_string) . "----\n";
176
        // In ZF 2.3.0 an empty query will return SELECT .*
177
        // In ZF 2.4.0 and empty query will return SELECT *
178
        if (in_array($sql_string, ['', 'SELECT .*', 'SELECT *'], true)) {
179
            throw new Exception\EmptyQueryException(__METHOD__ . ': Cannot return data of an empty query');
180
        }
181
        $this->query_string = $sql_string;
182
183
        try {
184
            $results = $this->adapter->query($sql_string, Adapter::QUERY_MODE_EXECUTE);
185
            //$stmt = $sql->prepareStatementForSqlObject( $select );
186
            //$results = $stmt->execute();
187
            //var_dump(get_class($results));
188
189
            $r = new ResultSet($results);
0 ignored issues
show
Documentation introduced by
$results is of type object<Zend\Db\Adapter\D...Driver\ResultInterface>, but the function expects a object<Zend\Db\ResultSet\ResultSet>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
190
            $r->setSource($this);
191
            $r->setHydrationOptions($options->getHydrationOptions());
192
193
            if ($options->hasLimit() && $options->getLimit() > 0) {
194
                //$row = $this->adapter->query('select FOUND_ROWS() as total_count')->execute()->current();
195
                $row = $this->adapter->createStatement('select FOUND_ROWS() as total_count')->execute()->current();
196
                $r->setTotalRows($row['total_count']);
197
            } else {
198
                $r->setTotalRows($r->count());
199
            }
200
        } catch (\Exception $e) {
201
            throw new Exception\ErrorException(__METHOD__ . ': Cannot retrieve data (' . $e->getMessage() . ')');
202
        }
203
204
        return $r;
205
    }
206
207
    public function loadDefaultColumnModel()
208
    {
209
        $sql = new Sql($this->adapter);
210
        $select = clone $this->select;
211
        $select->limit(0);
212
        $sql_string = $sql->getSqlStringForSqlObject($select);
0 ignored issues
show
Deprecated Code introduced by
The method Zend\Db\Sql\Sql::getSqlStringForSqlObject() has been deprecated with message: Deprecated in 2.4. Use buildSqlString() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
213
        $metadata_columns = $this->getMetadataReader()->getColumnsMetadata($sql_string);
214
        $this->setColumnModel(MetadataMapper::getColumnModelFromMetadata($metadata_columns));
215
    }
216
217
    /**
218
     * {@inheritdoc}
219
     *
220
     * @throws Exception\UnsupportedFeatureException
221
     */
222
    public function getMetadataReader()
223
    {
224
        if ($this->metadataReader === null) {
225
            $this->setMetadataReader($this->getDefaultMetadataReader());
226
        }
227
228
        return $this->metadataReader;
229
    }
230
231
    /**
232
     * @throws Exception\UnsupportedFeatureException
233
     */
234
    protected function getDefaultMetadataReader()
235
    {
236
        $conn = $this->adapter->getDriver()->getConnection()->getResource();
237
        $class = strtolower(get_class($conn));
238
        switch ($class) {
239
            case 'pdo':
240
                return new MetadataReader\PdoMysqlMetadataReader($conn);
241
            case 'mysqli':
242
                return new MetadataReader\MysqliMetadataReader($conn);
243
            default:
244
                throw new Exception\UnsupportedFeatureException(__METHOD__ . " Does not support default metadata reader for driver '$class'");
245
        }
246
    }
247
248
    /**
249
     * Return the query string that was executed.
250
     *
251
     * @throws Exception\InvalidUsageException
252
     *
253
     * @return string
254
     */
255
    public function getQueryString()
256
    {
257
        if ($this->query_string == '') {
258
            throw new Exception\InvalidUsageException(__METHOD__ . ': Invalid usage, getQueryString must be called after data has been loaded (performance reason).');
259
        }
260
261
        return str_replace("\n", ' ', $this->query_string);
262
    }
263
264
    /**
265
     * Return the query string.
266
     *
267
     * @throws Exception\InvalidUsageException
268
     *
269
     * @return string
270
     */
271
    public function __toString()
272
    {
273
        if (trim($this->query_string) != '') {
274
            $sql = str_replace("\n", ' ', $this->query_string);
275
        } elseif ($this->select !== null) {
276
            $sql = $this->sql->getSqlStringForSqlObject($this->select);
0 ignored issues
show
Deprecated Code introduced by
The method Zend\Db\Sql\Sql::getSqlStringForSqlObject() has been deprecated with message: Deprecated in 2.4. Use buildSqlString() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
277
        } else {
278
            throw new Exception\InvalidUsageException(__METHOD__ . ': No select given.');
279
        }
280
281
        return $sql;
282
    }
283
}
284