Completed
Push — master ( daa74a...43f9b6 )
by Sébastien
12:26
created

SqlSource::assignOptions()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 6

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 26
ccs 13
cts 13
cp 1
rs 8.439
cc 6
eloc 16
nc 9
nop 2
crap 6
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
    public function __construct(Adapter $adapter, Select $select = null)
79 61
    {
80
        $this->adapter = $adapter;
81 61
        $this->sql = new Sql($this->adapter);
82 61
        if ($select !== null) {
83 61
            $this->setSelect($select);
84 52
        }
85 52
    }
86 61
87
    /**
88
     * @param Select
89
     * @return SqlSource
90
     */
91
    public function setSelect(Select $select)
92 56
    {
93
        $this->select = $select;
94 56
        return $this;
95 56
    }
96
97
    /**
98
     *
99
     * @return Select
100
     */
101
    public function getSelect()
102 36
    {
103
        return $this->select();
104 36
    }
105
106
    /**
107
     *
108
     * @return Select
109
     */
110
    public function select()
111 40
    {
112
        if ($this->select === null) {
113 40
            $this->select = $this->sql->select();
114 13
        }
115 13
        return $this->select;
116 40
    }
117
118
119
    /**
120
     *
121
     * @param Select $select
122
     * @param Options $options
123
     * @return Select
124
     */
125
    protected function assignOptions(Select $select, Options $options)
126 36
    {
127
        if ($options->hasLimit()) {
128 36
            $select->limit($options->getLimit());
129 6
            if ($options->hasOffset()) {
130 6
                $select->offset($options->getOffset());
131 1
            }
132 1
            /**
133 6
             * For mysql queries, to allow counting rows we must prepend
134 6
             * SQL_CALC_FOUND_ROWS to the select quantifiers
135 36
             */
136
            $calc_found_rows = 'SQL_CALC_FOUND_ROWS';
137
            $quant_state = $select->getRawState($select::QUANTIFIER);
138
            if ($quant_state !== null) {
139
                if ($quant_state instanceof Expression) {
140
                    $quant_state->setExpression($calc_found_rows . ' ' . $quant_state->getExpression());
141
                } elseif (is_string($quant_state)) {
142
                    $quant_state = $calc_found_rows . ' ' . $quant_state;
143
                }
144
                $select->quantifier($quant_state);
145 36
            } else {
146
                $select->quantifier(new Expression($calc_found_rows));
147 36
            }
148 21
        }
149 21
        return $select;
150
    }
151 36
152
    /**
153
     *
154 36
     * @param Options $options
155 36
     * @throws Exception\EmptyQueryException
156
     * @throws Exception\ErrorException
157
     * @return ResultSet
158
     */
159
    public function getData(Options $options = null)
160 36
    {
161 2
        if ($options === null) {
162
            $options = $this->getOptions();
163 34
        }
164
165
        $select = $this->assignOptions(clone $this->getSelect(), $options);
166
167
168
        $sql = new Sql($this->adapter);
169
        $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...
170
        //echo $this->select->getSqlString($this->adapter->getPlatform());
171
        //echo "----" . var_dump($sql_string) . "----\n";
172
        // In ZF 2.3.0 an empty query will return SELECT .*
173
        // In ZF 2.4.0 and empty query will return SELECT *
174
        if (in_array($sql_string, ['', 'SELECT .*', 'SELECT *'])) {
175
            throw new Exception\EmptyQueryException(__METHOD__ . ': Cannot return data of an empty query');
176
        }
177
        $this->query_string = $sql_string;
178
179
        try {
180
            $results = $this->adapter->query($sql_string, Adapter::QUERY_MODE_EXECUTE);
181
            //$stmt = $sql->prepareStatementForSqlObject( $select );
182
            //$results = $stmt->execute();
183
            //var_dump(get_class($results));
184
185
            $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...
186
            $r->setSource($this);
187
            $r->setHydrationOptions($options->getHydrationOptions());
188
189
            if ($options->hasLimit()) {
190
                //$row = $this->adapter->query('select FOUND_ROWS() as total_count')->execute()->current();
191
                $row = $this->adapter->createStatement('select FOUND_ROWS() as total_count')->execute()->current();
192
                $r->setTotalRows($row['total_count']);
193
            } else {
194
                $r->setTotalRows($r->count());
195
            }
196
        } catch (\Exception $e) {
197
            throw new Exception\ErrorException(__METHOD__ . ': Cannot retrieve data (' . $e->getMessage() . ')');
198
        }
199
        return $r;
200
    }
201
202 34
    /**
203
     *
204
     */
205
    public function loadDefaultColumnModel()
206
    {
207 34
        $sql = new Sql($this->adapter);
208 34
        $select = clone $this->select;
209 34
        $select->limit(0);
210
        $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...
211 34
        $metadata_columns = $this->getMetadataReader()->getColumnsMetadata($sql_string);
212
        $this->setColumnModel(MetadataMapper::getColumnModelFromMetadata($metadata_columns));
213 6
    }
214 6
215 6
    /**
216 30
     * {@inheritdoc}
217
     * @throws Exception\UnsupportedFeatureException
218
     */
219
    public function getMetadataReader()
220
    {
221
        if ($this->metadataReader === null) {
222
            $this->setMetadataReader($this->getDefaultMetadataReader());
223
        }
224
        return $this->metadataReader;
225
    }
226
227
    /**
228
     * @throws Exception\UnsupportedFeatureException
229
     */
230 34
    protected function getDefaultMetadataReader()
231
    {
232
        $conn = $this->adapter->getDriver()->getConnection()->getResource();
233
        $class = strtolower(get_class($conn));
234
        switch ($class) {
235
            case 'pdo':
236
                return new MetadataReader\PdoMysqlMetadataReader($conn);
237
            case 'mysqli':
238
                return new MetadataReader\MysqliMetadataReader($conn);
239
            default:
240
                throw new Exception\UnsupportedFeatureException(__METHOD__ . " Does not support default metadata reader for driver '$class'");
241 34
        }
242
    }
243
244
    /**
245
     * Return the query string that was executed
246
     * @throws Exception\InvalidUsageException
247 29
     * @return string
248
     */
249 29
    public function getQueryString()
250 29
    {
251 29
        if ($this->query_string == '') {
252 29
            throw new Exception\InvalidUsageException(__METHOD__ . ": Invalid usage, getQueryString must be called after data has been loaded (performance reason).");
253 29
        }
254 29
        return str_replace("\n", ' ', $this->query_string);
255 29
    }
256
257
    /**
258
     * Return the query string
259
     *
260
     * @throws Exception\InvalidUsageException
261
     * @return string
262 30
     */
263
    public function __toString()
264 30
    {
265 30
        if ($this->query_string != '') {
266 30
            $sql = str_replace("\n", ' ', $this->query_string);
267 30
        } elseif ($this->select !== null) {
268
            $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...
269
        } else {
270
            throw new Exception\InvalidUsageException(__METHOD__ . ": No select given.");
271
        }
272
        return $sql;
273
    }
274
}
275