Completed
Push — master ( c82e60...68cb49 )
by Sébastien
05:32
created

SqlSource::getDefaultMetadataReader()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.576

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 13
ccs 6
cts 10
cp 0.6
rs 9.4285
cc 3
eloc 10
nc 3
nop 0
crap 3.576
1
<?php
2
3
/**
4
 *
5
 * @author Vanvelthem Sébastien
6
 */
7
8
namespace Soluble\FlexStore\Source\Zend;
9
10
use Soluble\FlexStore\Source\AbstractSource;
11
use Soluble\FlexStore\Source\QueryableSourceInterface;
12
use Soluble\FlexStore\ResultSet\ResultSet;
13
use Soluble\FlexStore\Exception;
14
use Soluble\FlexStore\Options;
15
use Zend\Db\Adapter\Adapter;
16
use Zend\Db\Sql\Sql;
17
use Zend\Db\Sql\Select;
18
use Zend\Db\Sql\Expression;
19
use ArrayObject;
20
use Soluble\FlexStore\Column\ColumnModel;
21
use Soluble\FlexStore\Column\Type\MetadataMapper;
22
use Soluble\Metadata\Reader as MetadataReader;
23
24
class SqlSource extends AbstractSource implements QueryableSourceInterface
25
{
26
    /**
27
     * @var Sql
28
     */
29
    protected $sql;
30
31
    /**
32
     *
33
     * @var Select
34
     */
35
    protected $select;
36
37
    /**
38
     *
39
     * @var Adapter
40
     */
41
    protected $adapter;
42
43
    /**
44
     * Initial params received in the constructor
45
     * @var ArrayObject
46
     */
47
    protected $params;
48
49
    /**
50
     *
51
     * @var string
52
     */
53
    protected $query_string;
54
55
    /**
56
     *
57
     * @var \Zend\Db\Adapter\Driver\Mysqli\Statement
58
     */
59
    protected static $cache_stmt_prototype;
60
61
    /**
62
     *
63
     * @var \Zend\Db\Adapter\Driver\ResultInterface
64
     */
65
    protected static $cache_result_prototype;
66
67
    /**
68
     *
69
     * @var ColumnModel
70
     */
71
    protected $columnModel;
72
73
    /**
74
     *
75
     * @param Adapter $adapter
76
     * @param Select $select
77
     */
78 67
    public function __construct(Adapter $adapter, Select $select = null)
79
    {
80 67
        $this->adapter = $adapter;
81 67
        $this->sql = new Sql($this->adapter);
82 67
        if ($select !== null) {
83 58
            $this->setSelect($select);
84 58
        }
85 67
    }
86
87
    /**
88
     * @param Select $select
89
     * @return SqlSource
90
     */
91 62
    public function setSelect(Select $select)
92
    {
93 62
        $this->select = $select;
94 62
        return $this;
95
    }
96
97
    /**
98
     *
99
     * @return Select
100
     */
101 38
    public function getSelect()
102
    {
103 38
        return $this->select();
104
    }
105
106
    /**
107
     *
108
     * @return Select
109
     */
110 43
    public function select()
111
    {
112 43
        if ($this->select === null) {
113 14
            $this->select = $this->sql->select();
114 14
        }
115 43
        return $this->select;
116
    }
117
118
119
    /**
120
     *
121
     * @param Select $select
122
     * @param Options $options
123
     * @return Select
124
     */
125 38
    protected function assignOptions(Select $select, Options $options)
126
    {
127 38
        if ($options->hasLimit()) {
128 8
            $select->limit($options->getLimit());
129 8
            if ($options->hasOffset()) {
130 3
                $select->offset($options->getOffset());
131 3
            }
132
133 8
            if ($options->getLimit() > 0) {
134
135
                /**
136
                 * For mysql queries, to allow counting rows we must prepend
137
                 * SQL_CALC_FOUND_ROWS to the select quantifiers
138
                 */
139 6
                $calc_found_rows = 'SQL_CALC_FOUND_ROWS';
140 6
                $quant_state = $select->getRawState($select::QUANTIFIER);
141 6
                if ($quant_state !== null) {
142 1
                    if ($quant_state instanceof Expression) {
143 2
                        $quant_state->setExpression($calc_found_rows . ' ' . $quant_state->getExpression());
144 1
                    } elseif (is_string($quant_state)) {
145 1
                        $quant_state = $calc_found_rows . ' ' . $quant_state;
146 1
                    }
147 1
                    $select->quantifier($quant_state);
148 1
                } else {
149 6
                    $select->quantifier(new Expression($calc_found_rows));
150
                }
151 6
            }
152 8
        }
153 38
        return $select;
154
    }
155
156
    /**
157
     *
158
     * @param Options $options
159
     * @throws Exception\EmptyQueryException
160
     * @throws Exception\ErrorException
161
     * @return ResultSet
162
     */
163 38
    public function getData(Options $options = null)
164
    {
165 38
        if ($options === null) {
166 21
            $options = $this->getOptions();
167 21
        }
168
169 38
        $select = $this->assignOptions(clone $this->getSelect(), $options);
170
171
172 38
        $sql = new Sql($this->adapter);
173 38
        $sql_string = (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...
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 38
        if (in_array($sql_string, ['', 'SELECT .*', 'SELECT *'])) {
179 2
            throw new Exception\EmptyQueryException(__METHOD__ . ': Cannot return data of an empty query');
180
        }
181 36
        $this->query_string = $sql_string;
182
183
        try {
184 36
            $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 36
            $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...ultSet\ResultInterface>.

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 36
            $r->setSource($this);
191 36
            $r->setHydrationOptions($options->getHydrationOptions());
192
193 36
            if ($options->hasLimit() && $options->getLimit() > 0) {
194
                //$row = $this->adapter->query('select FOUND_ROWS() as total_count')->execute()->current();
195 6
                $row = $this->adapter->createStatement('select FOUND_ROWS() as total_count')->execute()->current();
196 6
                $r->setTotalRows($row['total_count']);
197 6
            } else {
198 32
                $r->setTotalRows($r->count());
199
            }
200 36
        } catch (\Exception $e) {
201
            throw new Exception\ErrorException(__METHOD__ . ': Cannot retrieve data (' . $e->getMessage() . ')');
202
        }
203 36
        return $r;
204
    }
205
206
    /**
207
     *
208
     */
209 31
    public function loadDefaultColumnModel()
210
    {
211 30
        $sql = new Sql($this->adapter);
212 30
        $select = clone $this->select;
213 31
        $select->limit(0);
214 30
        $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...
215 30
        $metadata_columns = $this->getMetadataReader()->getColumnsMetadata($sql_string);
216 30
        $this->setColumnModel(MetadataMapper::getColumnModelFromMetadata($metadata_columns));
217 30
    }
218
219
    /**
220
     * {@inheritdoc}
221
     * @throws Exception\UnsupportedFeatureException
222
     */
223 32
    public function getMetadataReader()
224
    {
225 32
        if ($this->metadataReader === null) {
226 32
            $this->setMetadataReader($this->getDefaultMetadataReader());
227 32
        }
228 32
        return $this->metadataReader;
229
    }
230
231
    /**
232
     * @throws Exception\UnsupportedFeatureException
233
     */
234 32
    protected function getDefaultMetadataReader()
235
    {
236 32
        $conn = $this->adapter->getDriver()->getConnection()->getResource();
237 32
        $class = strtolower(get_class($conn));
238
        switch ($class) {
239 32
            case 'pdo':
240
                return new MetadataReader\PdoMysqlMetadataReader($conn);
241 32
            case 'mysqli':
242 32
                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
     * @throws Exception\InvalidUsageException
251
     * @return string
252
     */
253 5
    public function getQueryString()
254
    {
255 5
        if ($this->query_string == '') {
256 1
            throw new Exception\InvalidUsageException(__METHOD__ . ": Invalid usage, getQueryString must be called after data has been loaded (performance reason).");
257
        }
258 4
        return str_replace("\n", ' ', $this->query_string);
259
    }
260
261
    /**
262
     * Return the query string
263
     *
264
     * @throws Exception\InvalidUsageException
265
     * @return string
266
     */
267 2
    public function __toString()
268
    {
269 2
        if (trim($this->query_string) != '') {
270 2
            $sql = str_replace("\n", ' ', $this->query_string);
271 2
        } elseif ($this->select !== null) {
272
            $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...
273
        } else {
274
            throw new Exception\InvalidUsageException(__METHOD__ . ": No select given.");
275
        }
276 2
        return $sql;
277
    }
278
}
279