Issues (52)

src/Concerns/HasDataSource.php (2 issues)

Labels
Severity
1
<?php
2
declare(strict_types=1);
3
4
namespace Level23\Druid\Concerns;
5
6
use Closure;
7
use InvalidArgumentException;
8
use Level23\Druid\DruidClient;
9
use Level23\Druid\Types\JoinType;
10
use Level23\Druid\Queries\QueryBuilder;
11
use Level23\Druid\DataSources\JoinDataSource;
12
use Level23\Druid\DataSources\TableDataSource;
13
use Level23\Druid\DataSources\QueryDataSource;
14
use Level23\Druid\DataSources\UnionDataSource;
15
use Level23\Druid\DataSources\LookupDataSource;
16
use Level23\Druid\DataSources\InlineDataSource;
17
use Level23\Druid\DataSources\DataSourceInterface;
18
19
trait HasDataSource
20
{
21
    protected DruidClient $client;
22
23
    protected DataSourceInterface $dataSource;
24
25
    /**
26
     * Alias method from "dataSource"
27
     *
28
     * @param string|DataSourceInterface $dataSource
29
     *
30
     * @return self
31
     */
32 2
    public function from(DataSourceInterface|string $dataSource): self
33
    {
34 2
        return $this->dataSource($dataSource);
35
    }
36
37
    /**
38
     * Update/set the dataSource
39
     *
40
     * @param string|DataSourceInterface $dataSource
41
     *
42
     * @return self
43
     */
44 32
    public function dataSource(DataSourceInterface|string $dataSource): self
45
    {
46 32
        if (is_string($dataSource)) {
47 30
            $this->dataSource = new TableDataSource($dataSource);
48
        } else {
49 4
            $this->dataSource = $dataSource;
50
        }
51
52 32
        return $this;
53
    }
54
55
    /**
56
     * @param \Closure|string|DataSourceInterface $dataSourceOrClosure
57
     * @param string                              $as
58
     * @param string                              $condition
59
     * @param string|JoinType                     $joinType
60
     *
61
     * @return self
62
     */
63 4
    public function join(DataSourceInterface|Closure|string $dataSourceOrClosure, string $as, string $condition, string|JoinType $joinType = JoinType::INNER): self
64
    {
65 4
        if ($this->dataSource instanceof TableDataSource && $this->dataSource->dataSourceName == '') {
0 ignored issues
show
Accessing dataSourceName on the interface Level23\Druid\DataSources\DataSourceInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
66 1
            throw new InvalidArgumentException('You first have to define your "from" dataSource before you can join!');
67
        }
68
69 3
        if (!str_ends_with($as, '.')) {
70 3
            $as .= '.';
71
        }
72
73 3
        $this->dataSource = new JoinDataSource(
74 3
            $this->dataSource,
75 3
            $this->getDataSource($dataSourceOrClosure),
76 3
            $as,
77 3
            $condition,
78 3
            $joinType
79 3
        );
80
81 3
        return $this;
82
    }
83
84
    /**
85
     * Join a lookup dataSource. Lookup datasources correspond to Druid's key-value lookup objects.
86
     *
87
     * Lookup datasources are key-value oriented and always have exactly two columns: k (the key) and v (the value),
88
     * and both are always strings.
89
     *
90
     * @param string $lookupName The name of the lookup dataSource.
91
     * @param string $as         The alias name as the dataSource will be used in the query.
92
     * @param string $condition  The condition how the match will be made.
93
     * @param string|JoinType $joinType   The join type to use. This can be INNER or LEFT.
94
     *
95
     * @return self
96
     */
97 1
    public function joinLookup(
98
        string $lookupName,
99
        string $as,
100
        string $condition,
101
        string|JoinType $joinType = JoinType::INNER
102
    ): self {
103 1
        $lookupDataSource = new LookupDataSource($lookupName);
104
105 1
        return $this->join($lookupDataSource, $as, $condition, $joinType);
106
    }
107
108
    /**
109
     * @param \Closure|string|DataSourceInterface $dataSourceOrClosure
110
     * @param string                              $as
111
     * @param string                              $condition
112
     *
113
     * @return self
114
     */
115 1
    public function leftJoin(DataSourceInterface|Closure|string $dataSourceOrClosure, string $as, string $condition): self
116
    {
117 1
        return $this->join($dataSourceOrClosure, $as, $condition, JoinType::LEFT);
118
    }
119
120
    /**
121
     * @param \Closure|string|DataSourceInterface $dataSourceOrClosure
122
     * @param string                              $as
123
     * @param string                              $condition
124
     *
125
     * @return self
126
     */
127 1
    public function innerJoin(DataSourceInterface|Closure|string $dataSourceOrClosure, string $as, string $condition): self
128
    {
129 1
        return $this->join($dataSourceOrClosure, $as, $condition);
130
    }
131
132
    /**
133
     * Inline datasources allow you to query a small amount of data that is embedded in the query itself.
134
     * They are useful when you want to write a query on a small amount of data without loading it first.
135
     * They are also useful as inputs into a join.
136
     *
137
     * Each row is an array that must be exactly as long as the list of columnNames. The first element in
138
     * each row corresponds to the first column in columnNames, and so on.
139
     *
140
     * @param string[]        $columnNames
141
     * @param array<scalar[]> $rows
142
     */
143 1
    public function fromInline(array $columnNames, array $rows): self
144
    {
145 1
        $this->dataSource = new InlineDataSource($columnNames, $rows);
146
147 1
        return $this;
148
    }
149
150
    /**
151
     * Unions allow you to treat two or more tables as a single datasource.
152
     *
153
     * With the native union datasource, the tables do not need to have identical schemas. If they do not
154
     * fully match up, then columns that exist in one table but not another will be treated as if they contained all
155
     * null values in the tables where they do not exist.
156
     *
157
     * @param string|string[] $dataSources
158
     * @param bool            $append When true, we will append the current used dataSource in the union.
159
     *
160
     * @return $this
161
     * @see https://druid.apache.org/docs/latest/querying/datasource.html#union
162
     */
163 3
    public function union(array|string $dataSources, bool $append = true): self
164
    {
165 3
        $dataSources = (array)$dataSources;
166
167 3
        if ($append) {
168 2
            if (!$this->dataSource instanceof TableDataSource) {
169 1
                throw new InvalidArgumentException('We can only union an table dataSource! You currently are using a ' . get_class($this->dataSource));
170
            }
171
172 1
            $dataSources[] = $this->dataSource->dataSourceName;
0 ignored issues
show
Accessing dataSourceName on the interface Level23\Druid\DataSources\DataSourceInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
173
        }
174
175 2
        $this->dataSource = new UnionDataSource($dataSources);
176
177 2
        return $this;
178
    }
179
180
    /**
181
     * @param \Closure|string|DataSourceInterface $dataSourceOrClosure
182
     *
183
     * @return DataSourceInterface
184
     * @throws InvalidArgumentException
185
     */
186 3
    protected function getDataSource(DataSourceInterface|Closure|string $dataSourceOrClosure): DataSourceInterface
187
    {
188 3
        if ($dataSourceOrClosure instanceof DataSourceInterface) {
189 1
            return $dataSourceOrClosure;
190 2
        } elseif ($dataSourceOrClosure instanceof Closure) {
191
192 1
            $builder = new QueryBuilder($this->client);
193 1
            call_user_func($dataSourceOrClosure, $builder);
194
195 1
            return new QueryDataSource($builder->getQuery());
196
        } else {
197 1
            return new TableDataSource($dataSourceOrClosure);
198
        }
199
    }
200
}