Completed
Pull Request — master (#31)
by Damian
03:07 queued 20s
created

MSSQLQueryBuilder::buildSelectQuery()   B

Complexity

Conditions 8
Paths 20

Size

Total Lines 59
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 59
rs 7.132
cc 8
eloc 32
nc 20
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace SilverStripe\MSSQL;
4
5
6
use InvalidArgumentException;
7
use SilverStripe\ORM\Queries\SQLSelect;
8
use SilverStripe\ORM\Connect\DBQueryBuilder;
9
10
11
12
/**
13
 * Builds a SQL query string from a SQLExpression object
14
 * 
15
 * @package mssql
16
 */
17
class MSSQLQueryBuilder extends DBQueryBuilder
18
{
19
    
20
    protected function buildSelectQuery(SQLSelect $query, array &$parameters)
21
    {
22
        list($limit, $offset) = $this->parseLimit($query);
23
        
24
        // If not using ofset then query generation is quite straightforward
25
        if (empty($offset)) {
26
            $sql = parent::buildSelectQuery($query, $parameters);
27
            // Inject limit into SELECT fragment
28
            if (!empty($limit)) {
29
                $sql = preg_replace('/^(SELECT (DISTINCT)?)/i', '${1} TOP '.$limit, $sql);
30
            }
31
            return $sql;
32
        }
33
        
34
        // When using offset we must use a subselect
35
        // @see http://stackoverflow.com/questions/2135418/equivalent-of-limit-and-offset-for-sql-server
36
        $orderby = $query->getOrderBy();
37
38
        // workaround for subselect not working with alias functions
39
        // just use the function directly in the order by instead of the alias
40
        $selects = $query->getSelect();
41
        foreach ($orderby as $field => $dir) {
42
            if (preg_match('/_SortColumn/', $field)) {
43
                unset($orderby[$field]);
44
                $orderby[$selects[str_replace('"', '', $field)]] = $dir;
45
            }
46
        }
47
48
        // Create order expression, using the first column if none explicitly specified
49
        if ($orderby) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $orderby of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
50
            // Simple implementation of buildOrderByFragment
51
            $statements = array();
52
            foreach ($orderby as $clause => $dir) {
53
                $statements[] = trim("$clause $dir");
54
            }
55
            $orderByClause = "ORDER BY " . implode(', ', $statements);
56
        } else {
57
            $selects = $query->getSelect();
58
            $firstCol = reset($selects);
59
            $orderByClause = "ORDER BY $firstCol";
60
        }
61
        
62
        // Build main query SQL
63
        $sql = parent::buildSelectQuery($query, $parameters);
64
        
65
        // Inject row number into selection
66
        $sql = preg_replace('/^(SELECT (DISTINCT)?)/i', '${1} ROW_NUMBER() OVER ('.$orderByClause.') AS Number, ', $sql);
67
        
68
        // Sub-query this SQL
69
        if (empty($limit)) {
70
            $limitCondition = "Number > ?";
71
            $parameters[] = $offset;
72
        } else {
73
            $limitCondition = "Number BETWEEN ? AND ?";
74
            $parameters[] = $offset + 1;
75
            $parameters[] = $offset + $limit;
76
        }
77
        return "SELECT * FROM ($sql) AS Numbered WHERE $limitCondition ORDER BY Number";
78
    }
79
    
80
    public function buildLimitFragment(SQLSelect $query, array &$parameters)
81
    {
82
        // Limit is handled at the buildSelectQuery level
83
        return '';
84
    }
85
    
86
    public function buildOrderByFragment(SQLSelect $query, array &$parameters)
87
    {
88
        // If doing a limit/offset at the same time then don't build the orde by fragment here
89
        list($offset, $limit) = $this->parseLimit($query);
90
        if (empty($offset) || empty($limit)) {
91
            return parent::buildOrderByFragment($query, $parameters);
92
        }
93
        return '';
94
    }
95
    
96
    /**
97
     * Extracts the limit and offset from the limit clause
98
     * 
99
     * @param SQLSelect $query
100
     * @return array Two item array with $limit and $offset as values
101
     * @throws InvalidArgumentException
102
     */
103
    protected function parseLimit(SQLSelect $query)
104
    {
105
        $limit = '';
106
        $offset = '0';
107
        if (is_array($query->getLimit())) {
108
            $limitArr = $query->getLimit();
109
            if (isset($limitArr['limit'])) {
110
                $limit = $limitArr['limit'];
111
            }
112
            if (isset($limitArr['start'])) {
113
                $offset = $limitArr['start'];
114
            }
115
        } elseif (preg_match('/^([0-9]+) offset ([0-9]+)$/i', trim($query->getLimit()), $matches)) {
116
            $limit = $matches[1];
117
            $offset = $matches[2];
118
        } else {
119
            //could be a comma delimited string
120
            $bits = explode(',', $query->getLimit());
121
            if (sizeof($bits) > 1) {
122
                list($offset, $limit) = $bits;
123
            } else {
124
                $limit = $bits[0];
125
            }
126
        }
127
        return array($limit, $offset);
128
    }
129
}
130