Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
82.61% covered (warning)
82.61%
57 / 69
33.33% covered (danger)
33.33%
2 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractRdsStorage
82.61% covered (warning)
82.61%
57 / 69
33.33% covered (danger)
33.33%
2 / 6
30.83
0.00% covered (danger)
0.00%
0 / 1
 search
95.24% covered (success)
95.24%
20 / 21
0.00% covered (danger)
0.00%
0 / 1
11
 count
76.92% covered (warning)
76.92%
10 / 13
0.00% covered (danger)
0.00%
0 / 1
4.20
 addFilter
83.33% covered (warning)
83.33%
20 / 24
0.00% covered (danger)
0.00%
0 / 1
7.23
 getQueryBuilder
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPersistence
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 delete
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; under version 2
7 * of the License (non-upgradable).
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 * Copyright (c) 2016  (original work) Open Assessment Technologies SA;
19 *
20 */
21
22namespace oat\taoEventLog\model\storage;
23
24use common_persistence_Manager;
25use common_persistence_Persistence;
26use common_persistence_SqlPersistence;
27use oat\oatbox\service\ConfigurableService;
28use oat\taoEventLog\model\RdsStorageInterface;
29use oat\taoEventLog\model\StorageInterface;
30use Doctrine\DBAL\Query\QueryBuilder;
31
32/**
33 * Class AbstractRdsStorage
34 * @package oat\taoEventLog\model\storage
35 * @author Aleh Hutnikau, <hutnikau@1pt.com>
36 */
37abstract class AbstractRdsStorage extends ConfigurableService implements StorageInterface, RdsStorageInterface
38{
39    public const OPTION_PERSISTENCE = 'persistence';
40    public const DATE_TIME_FORMAT = 'Y-m-d H:i:s';
41    public const ID = 'id';
42
43    /**
44     * Persistence for DB
45     * @var common_persistence_Persistence
46     */
47    protected $persistence;
48
49    /** @var string */
50    protected $sql = '';
51
52    /** @var array */
53    protected $parameters = [];
54
55    /**
56     *
57     * Filters parameter example:
58     * ```
59     * [
60     *   ['user_id', 'in', ['http://sample/first.rdf#i1490617729993174', 'http://sample/first.rdf#i1490617729993174'],
61     *   ['occurred', 'between', '2017-04-13 15:29:21', '2017-04-14 15:29:21'],
62     *   ['action', '=', '/tao/Main/login'],
63     * ]
64     * ```
65     * Available operations: `<`, `>`, `<>`, `<=`, `>=`, `=`, `between`, `like`
66     *
67     * Options parameter example:
68     * ```
69     * [
70     *      'limit' => 100,
71     *      'offset' => 200,
72     *      'sort' => 'occurred',
73     *      'order' => 'ASC',
74     *      'group' => 'user_id,
75     * ]
76     * ```
77     *
78     *
79     * @param array $filters
80     * @param array $options
81     * @return array
82     * @todo return \Iterator instead of array
83     */
84    public function search(array $filters = [], array $options = [])
85    {
86        $queryBuilder = $this->getQueryBuilder();
87        $queryBuilder->select('*');
88        if (isset($options['limit'])) {
89            $queryBuilder->setMaxResults(intval($options['limit']));
90        }
91        if (isset($options['offset'])) {
92            $queryBuilder->setFirstResult(intval($options['offset']));
93        }
94        if (isset($options['group']) && in_array($options['group'], static::tableColumns())) {
95            $queryBuilder->groupBy($options['group']);
96        }
97
98        foreach ($filters as $filter) {
99            $this->addFilter($queryBuilder, $filter);
100        }
101
102        $sort = isset($options['sort']) ? $options['sort'] : '';
103        $order = isset($options['order']) ? strtoupper($options['order']) : ' ASC';
104
105        if (in_array($sort, static::tableColumns()) && in_array($order, ['ASC', 'DESC'])) {
106            $queryBuilder->addOrderBy($sort, $order);
107        }
108
109        if ($sort !== self::ID) {
110            $queryBuilder->addOrderBy(self::ID, 'DESC');
111        }
112
113
114        $sql = $queryBuilder->getSQL();
115        $params = $queryBuilder->getParameters();
116        $stmt = $this->getPersistence()->query($sql, $params);
117        $data = $stmt->fetchAll(\PDO::FETCH_ASSOC);
118        return $data;
119    }
120
121    /**
122     * @param array $filters
123     * @param array $options
124     * @return integer
125     */
126    public function count(array $filters = [], array $options = [])
127    {
128        $queryBuilder = $this->getQueryBuilder();
129        $queryBuilder->select(self::ID);
130
131        foreach ($filters as $filter) {
132            $this->addFilter($queryBuilder, $filter);
133        }
134        if (isset($options['group']) && in_array($options['group'], static::tableColumns())) {
135            $queryBuilder->select($options['group']);
136            $queryBuilder->groupBy($options['group']);
137        }
138
139        $stmt = $this->getPersistence()->query(
140            'SELECT count(*) as count FROM (' . $queryBuilder->getSQL() . ') as group_q',
141            $queryBuilder->getParameters()
142        );
143        $data = $stmt->fetch(\PDO::FETCH_ASSOC);
144        return intval($data['count']);
145    }
146
147    /**
148     * @param QueryBuilder $queryBuilder
149     * @param array $filter
150     */
151    private function addFilter(QueryBuilder $queryBuilder, array $filter)
152    {
153        $colName = strtolower($filter[0]);
154        $operation = strtolower($filter[1]);
155        $val = $filter[2];
156        $val2 = isset($filter[3]) ? $filter[3] : null;
157
158        if (!in_array($colName, static::tableColumns())) {
159            return;
160        }
161
162        if (!in_array($operation, ['<', '>', '<>', '<=', '>=', '=', 'between', 'like', 'in'])) {
163            return;
164        }
165        $params = [];
166        if ($operation === 'between') {
167            $condition = "$colName between ? AND ?";
168            $params[] = $val;
169            $params[] = $val2;
170        } elseif ($operation === 'like') {
171            $condition = "lower($colName$operation ?";
172            $params[] = strtolower($val);
173        } elseif ($operation === 'in') {
174            $condition = "$colName $operation (" . implode(',', array_fill(0, count($val), '?')) . ")";
175            $params = array_values($val);
176        } else {
177            $condition = "$colName $operation ?";
178            $params[] = $val;
179        }
180
181        $queryBuilder->andWhere($condition);
182
183        $params = array_merge($queryBuilder->getParameters(), $params);
184        $queryBuilder->setParameters($params);
185    }
186
187     /**
188      * @return \Doctrine\DBAL\Query\QueryBuilder
189      * @throws \oat\oatbox\service\exception\InvalidServiceManagerException
190      */
191    private function getQueryBuilder()
192    {
193        return $this->getPersistence()->getPlatForm()->getQueryBuilder()->from($this->getTableName());
194    }
195
196     /**
197      * @return common_persistence_SqlPersistence
198      * @throws \oat\oatbox\service\exception\InvalidServiceManagerException
199      */
200    public function getPersistence()
201    {
202        $persistenceId = $this->getOption(self::OPTION_PERSISTENCE);
203        if (is_null($this->persistence)) {
204            $this->persistence = $this->getServiceManager()
205                ->get(common_persistence_Manager::SERVICE_ID)
206                ->getPersistenceById($persistenceId);
207        }
208
209        return $this->persistence;
210    }
211
212     /**
213      * @param array $filters
214      * @return integer
215      * @throws \oat\oatbox\service\exception\InvalidServiceManagerException
216      */
217    public function delete(array $filters)
218    {
219        $qb = $this->getPersistence()->getPlatForm()->getQueryBuilder()->delete($this->getTableName());
220        foreach ($filters as $filter) {
221            $this->addFilter($qb, $filter);
222        }
223        return $qb->execute();
224    }
225}