Completed
Push — master ( 3d70bc...1f76a9 )
by Rasmus
02:31
created

ReturningQuery::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 3
crap 1
1
<?php
2
3
namespace mindplay\sql\model;
4
5
use mindplay\sql\framework\Driver;
6
use mindplay\sql\framework\TypeProvider;
7
use OutOfBoundsException;
8
9
/**
10
 * Abstract base class for Query types that return and map results, such as `SELECT` or `UPDATE RETURNING`.
11
 */
12
abstract class ReturningQuery extends ProjectionQuery
13
{
14
    /**
15
     * @var string[] list of return variable expressions (for use in a SELECT or RETURNING clause)
16
     */
17
    protected $return_vars = [];
18
19
    /**
20
     * @var Type[] map where return variable name maps to Type
21
     */
22
    protected $type_map = [];
23
    
24
    /**
25
     * @param Table        $root
26
     * @param Driver       $driver
27
     * @param TypeProvider $types
28
     */
29 1
    public function __construct(Table $root, Driver $driver, TypeProvider $types)
30
    {
31 1
        parent::__construct($root, $driver, $types);
32 1
    }
33
34
    /**
35
     * Add all the Columns of a full Table to be selected and returned
36
     * 
37
     * @param Table $table Table to select and return
38
     *                     
39
     * @return $this
40
     */
41 1
    public function table(Table $table)
42
    {
43 1
        $this->return_vars[] = "{$table}.*";
44
45 1
        $this->type_map = array_merge($this->type_map, $this->createTypeMap($table));
46
47 1
        return $this;
48
    }
49
    
50
    /**
51
     * Add one or more Columns to select and return
52
     * 
53
     * @param Column|Column[] one or more Columns to select and return
54
     *
55
     * @return $this
56
     */
57 1
    public function columns($cols)
58
    {
59
        /**
60
         * @var Column[] $cols
61
         */
62
        
63 1
        $cols = is_array($cols) ? $cols : [$cols];
64
        
65 1
        foreach ($cols as $col) {
66 1
            $alias = $col->getAlias();
67
68 1
            $col_name = $alias ?: $col->getName();
69
70 1
            $table = $col->getTable();
71
72 1
            $table_name = $table->getAlias() ?: $table->getName();
73
74 1
            $column_expr = $this->driver->quoteName($table_name) . '.' . $this->driver->quoteName($col->getName());
75
76 1
            $this->return_vars[$col_name] = $alias
77 1
                ? "{$column_expr} AS " . $this->driver->quoteName($col_name)
78 1
                : "{$column_expr}";
79
80 1
            $this->type_map[$col_name] = $col->getType();
81
        }
82
83 1
        return $this;
84
    }
85
86
    /**
87
     * Add an SQL expression to select and return
88
     * 
89
     * @param string           $expr return expression
90
     * @param string|null      $name return variable name (optional, but usually required)
91
     * @param Type|string|null $type optional Type (or Type class-name)
92
     *
93
     * @return $this
94
     */
95 1
    public function value($expr, $name = null, $type = null)
96
    {
97 1
        if (isset($this->return_vars[$name])) {
98
            throw new OutOfBoundsException("duplicate return variable name: {$name}");
99
        }
100
101 1
        if ($name === null) {
102 1
            $this->return_vars[] = "{$expr}";
103
        } else {
104 1
            $quoted_name = $this->driver->quoteName($name);
105
106 1
            $this->return_vars[$name] = "{$expr} AS {$quoted_name}";
107
108 1
            if ($type !== null) {
109 1
                $this->type_map[$name] = is_string($type)
110 1
                    ? $this->types->getType($type)
111
                    : $type; // assumes Type instance
112
            }
113
        }
114
115 1
        return $this;
116
    }
117
    
118
    /**
119
     * @inheritdoc
120
     */
121 1
    public function getMappers()
122
    {
123 1
        $type_map = $this->type_map;
124
125 1
        if (count($this->return_vars) === 0) {
126
            // no defined return vars - buildReturnVars() will auto-select the root node, so
127
            // we need to add root Column Types to the TypeMapper we're creating here:
128
129 1
            $type_map = array_merge($this->createTypeMap($this->root), $type_map);
130
        }
131
132 1
        return array_merge([new TypeMapper($type_map)], parent::getMappers());
133
    }
134
135
    /**
136
     * @return string comma-separated return expressions (for use in the SELECT or RETURNING clause of an SQL query)
137
     */
138 1
    protected function buildReturnVars()
139
    {
140 1
        $return_vars = $this->return_vars;
141
142 1
        if (count($return_vars) === 0) {
143
            // no defined return vars - getMappers() will create a Type-map for the root node,
144
            // so we need to auto-select the root node here:
145
146 1
            $return_vars[] = "{$this->root}.*";
147
        }
148
149 1
        return implode(",\n  ", $return_vars);
150
    }
151
152
    /**
153
     * Internally creates a full Type-map for all Columns in a given Table
154
     *
155
     * @param Table $table
156
     *
157
     * @return Type[] map where Column Alias maps to Type
158
     */
159 1
    protected function createTypeMap(Table $table)
160
    {
161 1
        $type_map = [];
162
163 1
        foreach ($table->listColumns() as $column) {
164 1
            $type_map[$column->getAlias() ?: $column->getName()] = $column->getType();
165
        }
166
167 1
        return $type_map;
168
    }
169
}
170