Completed
Push — master ( 68cb49...5652ae )
by Sébastien
07:10
created

QuerySource::loadDefaultColumnModel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
nop 0
crap 1
1
<?php
2
3
/**
4
 *
5
 * @author Vanvelthem Sébastien
6
 */
7
8
namespace Soluble\FlexStore\Source\DbWrapper;
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 ArrayObject;
16
use Soluble\DbWrapper\Adapter\AdapterInterface;
17
use Soluble\DbWrapper\Result\Resultset as DbWrapperResultset;
18
use Soluble\FlexStore\Column\ColumnModel;
19
use Soluble\FlexStore\Column\Type\MetadataMapper;
20
use Soluble\Metadata\Reader as MetadataReader;
21
22
class QuerySource extends AbstractSource implements QueryableSourceInterface
23
{
24
25
    /**
26
     *
27
     * @var string
28
     */
29
    protected $query;
30
31
    /**
32
     *
33
     * @var AdapterInterface
34
     */
35
    protected $adapter;
36
37
    /**
38
     * Initial params received in the constructor
39
     * @var ArrayObject
40
     */
41
    protected $params;
42
43
    /**
44
     * The query string contains the query as it has been crafted (with options, limits...)
45
     * @var string
46
     */
47
    protected $query_string;
48
49
    /**
50
     *
51
     * @var ColumnModel
52
     */
53
    protected $columnModel;
54
55
    /**
56
     *
57
     * @param AdapterInterface $adapter
58
     * @param string $query
59
     */
60 9
    public function __construct(AdapterInterface $adapter, $query = null)
61
    {
62 9
        $this->adapter = $adapter;
63
64 9
        if ($query !== null) {
65 8
            $this->setQuery($query);
66 6
        }
67 9
    }
68
69
    /**
70
     * @param string $query
71
     */
72 9
    public function setQuery($query)
73
    {
74 9
        $this->query = $query;
75 9
    }
76
77
    /**
78
     *
79
     * @return string
80
     */
81
    public function getQuery()
82
    {
83
        return $this->query;
84
    }
85
86
    /**
87
     *
88
     * @param string $query
89
     * @param Options $options
90
     * @return query
91
     */
92 5
    protected function assignOptions($query, Options $options)
93
    {
94 5
        if ($options->hasLimit()) {
95
96
            /**
97
             * For mysql queries, to allow counting rows we must prepend
98
             * SQL_CALC_FOUND_ROWS to the select quantifiers
99
             */
100 4
            if ($options->getLimit() > 0) {
101 3
                $calc_found_rows = 'SQL_CALC_FOUND_ROWS';
102 3
                if (!preg_match("/$calc_found_rows/", $query)) {
103 3
                    $q = trim($query);
104 3
                    $query = preg_replace('/^select\b/i', "SELECT $calc_found_rows", $q);
105 3
                }
106 3
            }
107
108
            // mysql only, @todo make it rule everything (use traits)
109 4
            $replace_regexp = "LIMIT[\s]+[\d]+((\s*,\s*\d+)|(\s+OFFSET\s+\d+)){0,1}";
110
111 4
            $search_regexp = "$replace_regexp";
112 4
            if (!preg_match("/$search_regexp$/i", $query)) {
113
                // Limit is not already present
114 3
                $query .= " LIMIT " . $options->getLimit();
115 3
            } else {
116 1
                $query = preg_replace("/($replace_regexp)/i", "LIMIT " . $options->getLimit(), $query);
117
            }
118
119
120 4
            if ($options->hasOffset()) {
121 3
                $query .= " OFFSET " . $options->getOffset();
122 3
            }
123 4
        }
124 5
        return $query;
125
    }
126
127
    /**
128
     *
129
     * @param Options $options
130
     * @throws Exception\EmptyQueryException
131
     * @throws Exception\ErrorException
132
     * @return ResultSet
133
     */
134 5
    public function getData(Options $options = null)
135
    {
136 5
        if ($options === null) {
137 2
            $options = $this->getOptions();
138 2
        }
139
140 5
        $this->query_string = $this->assignOptions($this->query, $options);
141
142
        try {
143 5
            $results = $this->adapter->query($this->query_string, DbWrapperResultset::TYPE_ARRAYOBJECT);
144
145 5
            $r = new ResultSet($results);
0 ignored issues
show
Documentation introduced by
$results is of type object<Soluble\DbWrapper\Result\Resultset>, 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...
146 5
            $r->setSource($this);
147 5
            $r->setHydrationOptions($options->getHydrationOptions());
148
149 5
            if ($options->hasLimit() && $options->getLimit() > 0) {
150
                //$row = $this->adapter->query('select FOUND_ROWS() as total_count')->execute()->current();
151 3
                $row = $this->adapter->query('select FOUND_ROWS() as total_count')->current();
152 3
                $r->setTotalRows($row['total_count']);
153 3
            } else {
154 3
                $r->setTotalRows($r->count());
155
            }
156 5
        } catch (\Exception $e) {
157
            throw new Exception\ErrorException(__METHOD__ . ': Cannot retrieve data (' . $e->getMessage() . ')');
158
        }
159 5
        return $r;
160
    }
161
162
    /**
163
     *
164
     */
165 2
    public function loadDefaultColumnModel()
166
    {
167 2
        $metadata_columns = $this->getMetadataReader()->getColumnsMetadata($this->query);
168 2
        $this->setColumnModel(MetadataMapper::getColumnModelFromMetadata($metadata_columns));
169 2
    }
170
171
    /**
172
     * {@inheritdoc}
173
     * @throws Exception\UnsupportedFeatureException
174
     */
175 4
    public function getMetadataReader()
176
    {
177 4
        if ($this->metadataReader === null) {
178 4
            $this->setMetadataReader($this->getDefaultMetadataReader());
179 4
        }
180 4
        return $this->metadataReader;
181
    }
182
183
    /**
184
     * @throws Exception\UnsupportedFeatureException
185
     */
186 4
    protected function getDefaultMetadataReader()
187
    {
188 4
        $conn = $this->adapter->getConnection()->getResource();
189 4
        $class = strtolower(get_class($conn));
190
        switch ($class) {
191 4
            case 'pdo':
192
                return new MetadataReader\PdoMysqlMetadataReader($conn);
193 4
            case 'mysqli':
194 4
                return new MetadataReader\MysqliMetadataReader($conn);
195
            default:
196
                throw new Exception\UnsupportedFeatureException(__METHOD__ . " Does not support default metadata reader for driver '$class'");
197
        }
198
    }
199
200
    /**
201
     * Return the query string that was executed with options etc
202
     * 
203
     * @throws Exception\InvalidUsageException
204
     * @return string
205
     */
206 2
    public function getQueryString()
207
    {
208 2
        if ($this->query_string == '') {
209
            throw new Exception\InvalidUsageException(__METHOD__ . ": Invalid usage, getQueryString must be called after data has been loaded (performance reason).");
210
        }
211 2
        return str_replace("\n", ' ', $this->query_string);
212
    }
213
214
    /**
215
     * Return the query string
216
     *
217
     * @throws Exception\InvalidUsageException
218
     * @return string
219
     */
220
    public function __toString()
221
    {
222
        if (trim($this->query) == '') {
223
            throw new Exception\InvalidUsageException(__METHOD__ . ": Empty query given.");
224
        }
225
        return $this->query;
226
    }
227
}
228