Completed
Push — master ( 4ea723...0a266a )
by Matt
10s
created

manager   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 427
Duplicated Lines 22.95 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 97.75%

Importance

Changes 0
Metric Value
wmc 36
lcom 1
cbo 0
dl 98
loc 427
ccs 174
cts 178
cp 0.9775
rs 8.8
c 0
b 0
f 0

20 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A get_ad() 0 11 2
B get_ads() 0 33 5
A get_all_ads() 10 10 1
A get_ads_by_owner() 11 11 1
A increment_ads_views() 10 10 2
A increment_ad_clicks() 0 7 1
A insert_ad() 0 15 1
A update_ad() 0 17 2
A delete_ad() 0 8 1
A remove_ad_owner() 12 12 2
A get_ad_locations() 16 16 2
A insert_ad_locations() 12 12 2
A delete_ad_locations() 0 6 1
A load_memberships() 15 15 2
A load_groups() 0 17 1
A intersect_ad_data() 0 15 1
B sql_random() 0 19 5
A insert_ad_group_data() 12 12 2
A remove_ad_group_data() 0 6 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 *
4
 * Advertisement management. An extension for the phpBB Forum Software package.
5
 *
6
 * @copyright (c) 2017 phpBB Limited <https://www.phpbb.com>
7
 * @license GNU General Public License, version 2 (GPL-2.0)
8
 *
9
 */
10
11
namespace phpbb\ads\ad;
12
13
class manager
14
{
15
	/** @var \phpbb\db\driver\driver_interface */
16
	protected $db;
17
18
	/** @var \phpbb\config\config */
19
	protected $config;
20
21
	/** @var string */
22
	protected $ads_table;
23
24
	/** @var string */
25
	protected $ad_locations_table;
26
27
	/** @var string */
28
	protected $ad_group_table;
29
30
	/**
31
	 * Constructor
32
	 *
33
	 * @param    \phpbb\db\driver\driver_interface $db                 DB driver interface
34
	 * @param    \phpbb\config\config              $config             Config object
35
	 * @param    string                            $ads_table          Ads table
36
	 * @param    string                            $ad_locations_table Ad locations table
37
	 * @param    string                            $ad_group_table 	   Ad group table
38
	 */
39 56
	public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\config\config $config, $ads_table, $ad_locations_table, $ad_group_table)
40
	{
41 56
		$this->db = $db;
42 56
		$this->config = $config;
43 56
		$this->ads_table = $ads_table;
44 56
		$this->ad_locations_table = $ad_locations_table;
45 56
		$this->ad_group_table = $ad_group_table;
46 56
	}
47
48
	/**
49
	 * Get specific ad
50
	 *
51
	 * @param	int	$ad_id	Advertisement ID
52
	 * @return	array	Array with advertisement data
53
	 */
54 13
	public function get_ad($ad_id)
55
	{
56
		$sql = 'SELECT *
57 13
			FROM ' . $this->ads_table . '
58 13
			WHERE ad_id = ' . (int) $ad_id;
59 13
		$result = $this->db->sql_query($sql);
60 13
		$data = $this->db->sql_fetchrow($result);
61 13
		$this->db->sql_freeresult($result);
62
63 13
		return $data !== false ? $data : array();
64
	}
65
66
	/**
67
	 * Get one ad per every location
68
	 *
69
	 * @param    array $ad_locations List of ad locations to fetch ads for
70
	 * @param    array $user_groups List of user groups
71
	 * @param    bool  $non_content_page Is current page non-content oriented (e.g.: login, UCP, MCP)? Default is false.
72
	 * @return    array    List of ad codes for each location
73
	 */
74 8
	public function get_ads($ad_locations, $user_groups, $non_content_page = false)
75
	{
76 8
		$sql_where_views = $this->config['phpbb_ads_enable_views'] ? 'AND (a.ad_views_limit = 0 OR a.ad_views_limit > a.ad_views)' : '';
77 8
		$sql_where_clicks = $this->config['phpbb_ads_enable_clicks'] ? 'AND (a.ad_clicks_limit = 0 OR a.ad_clicks_limit > a.ad_clicks)' : '';
78 8
		$sql_where_non_content = $non_content_page ? 'AND a.ad_content_only = 0' : '';
79 8
		$sql_where_user_groups = !empty($user_groups) ? 'AND NOT EXISTS (SELECT ag.group_id FROM ' . $this->ad_group_table . ' ag WHERE ag.ad_id = a.ad_id AND ' . $this->db->sql_in_set('ag.group_id', $user_groups) . ')' : '';
80
81
		$sql = 'SELECT al.location_id, a.ad_id, a.ad_code
82 8
				FROM ' . $this->ad_locations_table . ' al
83 8
				LEFT JOIN ' . $this->ads_table . ' a
84
					ON (al.ad_id = a.ad_id)
85
				WHERE a.ad_enabled = 1
86
					AND (a.ad_end_date = 0
87 8
						OR a.ad_end_date > ' . time() . ")
88
					$sql_where_views
89 8
					$sql_where_clicks
90 8
					$sql_where_non_content
91 8
					$sql_where_user_groups
92 8
					AND " . $this->db->sql_in_set('al.location_id', $ad_locations) . '
93 8
				ORDER BY al.location_id, (' . $this->sql_random() . ' * a.ad_priority) DESC';
94 8
		$result = $this->db->sql_query($sql);
95 8
		$data = $this->db->sql_fetchrowset($result);
96 8
		$this->db->sql_freeresult($result);
97
98 8
		$current_location_id = '';
99 8
		$data = array_filter($data, function ($row) use (&$current_location_id) {
100 5
			$return = $current_location_id !== $row['location_id'];
101 5
			$current_location_id = $row['location_id'];
102 5
			return $return;
103 8
		});
104
105 8
		return $data;
106
	}
107
108
	/**
109
	 * Get all advertisements.
110
	 *
111
	 * @return    array    List of all ads
112
	 */
113 2 View Code Duplication
	public function get_all_ads()
114
	{
115
		$sql = 'SELECT ad_id, ad_priority, ad_name, ad_enabled, ad_end_date, ad_views, ad_clicks, ad_views_limit, ad_clicks_limit
116 2
			FROM ' . $this->ads_table;
117 2
		$result = $this->db->sql_query($sql);
118 2
		$data = $this->db->sql_fetchrowset($result);
119 2
		$this->db->sql_freeresult($result);
120
121 2
		return $data;
122
	}
123
124
	/**
125
	 * Get all owner's ads
126
	 *
127
	 * @param    int $user_id Ad owner
128
	 * @return    array    List of owner's ads
129
	 */
130 5 View Code Duplication
	public function get_ads_by_owner($user_id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
131
	{
132
		$sql = 'SELECT ad_id, ad_name, ad_enabled, ad_end_date, ad_views, ad_views_limit, ad_clicks, ad_clicks_limit
133 5
			FROM ' . $this->ads_table . '
134 5
			WHERE ad_owner = ' . (int) $user_id;
135 5
		$result = $this->db->sql_query($sql);
136 5
		$data = $this->db->sql_fetchrowset($result);
137 5
		$this->db->sql_freeresult($result);
138
139 5
		return $data;
140
	}
141
142
	/**
143
	 * Increment views for specified ads
144
	 *
145
	 * Note, that views are incremented only by one even when
146
	 * an ad is displayed multiple times on the same page.
147
	 *
148
	 * @param    array $ad_ids IDs of ads to increment views
149
	 * @return    void
150
	 */
151 2 View Code Duplication
	public function increment_ads_views($ad_ids)
152
	{
153 2
		if (!empty($ad_ids))
154 2
		{
155 2
			$sql = 'UPDATE ' . $this->ads_table . '
156
				SET ad_views = ad_views + 1
157 2
				WHERE ' . $this->db->sql_in_set('ad_id', $ad_ids);
158 2
			$this->db->sql_query($sql);
159 2
		}
160 2
	}
161
162
	/**
163
	 * Increment clicks for specified ad
164
	 *
165
	 * @param    int $ad_id ID of an ad to increment clicks
166
	 * @return    void
167
	 */
168 2
	public function increment_ad_clicks($ad_id)
169
	{
170 2
		$sql = 'UPDATE ' . $this->ads_table . '
171
			SET ad_clicks = ad_clicks + 1
172 2
			WHERE ad_id = ' . (int) $ad_id;
173 2
		$this->db->sql_query($sql);
174 2
	}
175
176
	/**
177
	 * Insert new advertisement to the database
178
	 *
179
	 * @param    array $data New ad data
180
	 * @return    int        New advertisement ID
181
	 */
182 2
	public function insert_ad($data)
183
	{
184
		// extract ad groups here because it gets filtered in intersect_ad_data()
185 2
		$ad_groups = $data['ad_groups'];
186 2
		$data = $this->intersect_ad_data($data);
187
188
		// add a row to ads table
189 2
		$sql = 'INSERT INTO ' . $this->ads_table . ' ' . $this->db->sql_build_array('INSERT', $data);
190 2
		$this->db->sql_query($sql);
191 2
		$ad_id = $this->db->sql_nextid();
192
193 2
		$this->insert_ad_group_data($ad_id, $ad_groups);
194
195 2
		return $ad_id;
196
	}
197
198
	/**
199
	 * Update advertisement
200
	 *
201
	 * @param    int   $ad_id Advertisement ID
202
	 * @param    array $data  List of data to update in the database
203
	 * @return    int        Number of affected rows. Can be used to determine if any ad has been updated.
204
	 */
205 4
	public function update_ad($ad_id, $data)
206
	{
207
		// extract ad groups here because it gets filtered in intersect_ad_data()
208 4
		$ad_groups = isset($data['ad_groups']) ? $data['ad_groups'] : array();
209 4
		$data = $this->intersect_ad_data($data);
210
211 4
		$sql = 'UPDATE ' . $this->ads_table . '
212 4
			SET ' . $this->db->sql_build_array('UPDATE', $data) . '
213 4
			WHERE ad_id = ' . (int) $ad_id;
214 4
		$this->db->sql_query($sql);
215 4
		$result = $this->db->sql_affectedrows();
216
217 4
		$this->remove_ad_group_data($ad_id);
218 4
		$this->insert_ad_group_data($ad_id, $ad_groups);
219
220 4
		return $result;
221
	}
222
223
	/**
224
	 * Delete advertisement
225
	 *
226
	 * @param    int $ad_id Advertisement ID
227
	 * @return    int        Number of affected rows. Can be used to determine if any ad has been deleted.
228
	 */
229 1
	public function delete_ad($ad_id)
230
	{
231 1
		$sql = 'DELETE FROM ' . $this->ads_table . '
232 1
			WHERE ad_id = ' . (int) $ad_id;
233 1
		$this->db->sql_query($sql);
234
235 1
		return $this->db->sql_affectedrows();
236
	}
237
238
	/**
239
	 * Remove ad owner
240
	 *
241
	 * @param    array $user_ids User IDs
242
	 * @return    void
243
	 */
244 3 View Code Duplication
	public function remove_ad_owner(array $user_ids)
245
	{
246 3
		if (empty($user_ids))
247 3
		{
248
			return;
249
		}
250
251 3
		$sql = 'UPDATE ' . $this->ads_table . '
252
			SET ad_owner = 0
253 3
			WHERE ' . $this->db->sql_in_set('ad_owner', $user_ids);
254 3
		$this->db->sql_query($sql);
255 3
	}
256
257
	/**
258
	 * Get all locations for specified advertisement
259
	 *
260
	 * @param	int		$ad_id	Advertisement ID
261
	 * @return	array	List of template locations for specified ad
262
	 */
263 7 View Code Duplication
	public function get_ad_locations($ad_id)
264
	{
265 7
		$ad_locations = array();
266
267
		$sql = 'SELECT location_id
268 7
			FROM ' . $this->ad_locations_table . '
269 7
			WHERE ad_id = ' . (int) $ad_id;
270 7
		$result = $this->db->sql_query($sql);
271 7
		while ($row = $this->db->sql_fetchrow($result))
272
		{
273 5
			$ad_locations[] = $row['location_id'];
274 5
		}
275 7
		$this->db->sql_freeresult($result);
276
277 7
		return $ad_locations;
278
	}
279
280
	/**
281
	 * Insert advertisement locations
282
	 *
283
	 * @param	int		$ad_id			Advertisement ID
284
	 * @param	array	$ad_locations	List of template locations for this ad
285
	 * @return	void
286
	 */
287 2 View Code Duplication
	public function insert_ad_locations($ad_id, $ad_locations)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
288
	{
289 2
		$sql_ary = array();
290 2
		foreach ($ad_locations as $ad_location)
291
		{
292 2
			$sql_ary[] = array(
293 2
				'ad_id'			=> $ad_id,
294 2
				'location_id'	=> $ad_location,
295
			);
296 2
		}
297 2
		$this->db->sql_multi_insert($this->ad_locations_table, $sql_ary);
298 2
	}
299
300
	/**
301
	 * Delete advertisement locations
302
	 *
303
	 * @param	int		$ad_id	Advertisement ID
304
	 * @return	void
305
	 */
306 3
	public function delete_ad_locations($ad_id)
307
	{
308 3
		$sql = 'DELETE FROM ' . $this->ad_locations_table . '
309 3
			WHERE ad_id = ' . (int) $ad_id;
310 3
		$this->db->sql_query($sql);
311 3
	}
312
313
	/**
314
	 * Load memberships of the user
315
	 *
316
	 * @param	int		$user_id	User ID to load memberships
317
	 * @return	array	List of group IDs user is member of
318
	 */
319 2 View Code Duplication
	public function load_memberships($user_id)
320
	{
321 2
		$memberships = array();
322
		$sql = 'SELECT group_id
323 2
			FROM ' . USER_GROUP_TABLE . '
324 2
			WHERE user_id = ' . (int) $user_id . '
325 2
			AND user_pending = 0';
326 2
		$result = $this->db->sql_query($sql, 3600);
327 2
		while ($row = $this->db->sql_fetchrow($result))
328
		{
329 2
			$memberships[] = $row['group_id'];
330 2
		}
331 2
		$this->db->sql_freeresult($result);
332 2
		return $memberships;
333
	}
334
335
	/**
336
	 * Load all board groups
337
	 *
338
	 * @param	int		$ad_id	Advertisement ID
339
	 * @return	array	List of groups
340
	 */
341 1
	public function load_groups($ad_id)
342
	{
343
		$sql = 'SELECT g.group_id, g.group_name, (
344
				SELECT COUNT(ad_id)
345 1
				FROM ' . $this->ad_group_table . ' ag
346 1
				WHERE ag.ad_id = ' . (int) $ad_id . '
347
					AND ag.group_id = g.group_id
348
			) as group_selected
349 1
			FROM ' . GROUPS_TABLE . " g
350
			WHERE g.group_name <> 'BOTS'
351 1
			ORDER BY g.group_name ASC";
352 1
		$result = $this->db->sql_query($sql);
353 1
		$groups = $this->db->sql_fetchrowset($result);
354 1
		$this->db->sql_freeresult($result);
355
356 1
		return $groups;
357
	}
358
359
	/**
360
	 * Make sure only necessary data make their way to SQL query
361
	 *
362
	 * @param	array	$data	List of data to query the database
363
	 * @return	array	Cleaned data that contain only valid keys
364
	 */
365 6
	protected function intersect_ad_data($data)
366
	{
367 6
		return array_intersect_key($data, array(
368 6
			'ad_name'			=> '',
369 6
			'ad_note'			=> '',
370 6
			'ad_code'			=> '',
371 6
			'ad_enabled'		=> '',
372 6
			'ad_end_date'		=> '',
373 6
			'ad_priority'		=> '',
374 6
			'ad_views_limit'	=> '',
375 6
			'ad_clicks_limit'	=> '',
376 6
			'ad_owner'			=> '',
377 6
			'ad_content_only'	=> '',
378 6
		));
379
	}
380
381
	/**
382
	 * Get the random statement for this database layer
383
	 * Random function should generate a float value between 0 and 1
384
	 *
385
	 * @return	string	Random statement for current database layer
386
	 */
387 8
	protected function sql_random()
388
	{
389 8
		switch ($this->db->get_sql_layer())
390
		{
391 8
			case 'oracle':
392
				return 'VALUE()';
393
394 8
			case 'postgres':
395
				return 'RANDOM()';
396
397
			// https://stackoverflow.com/a/35369410/2908600
398 8
			case 'sqlite':
399 8
			case 'sqlite3':
400
				return '(0.5 - RANDOM() / CAST(-9223372036854775808 AS REAL) / 2)';
401
402 8
			default:
403 8
				return 'RAND()';
404 8
		}
405
	}
406
407
	/**
408
	 * Add rows to ad_group table.
409
	 *
410
	 * @param int   $ad_id     Advertisement ID
411
	 * @param array $ad_groups List of groups that should not see this ad
412
	 * @return void
413
	 */
414 6 View Code Duplication
	protected function insert_ad_group_data($ad_id, $ad_groups)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
415
	{
416 6
		$sql_ary = array();
417 6
		foreach ($ad_groups as $group)
418
		{
419 2
			$sql_ary[] = array(
420 2
				'ad_id'		=> $ad_id,
421 2
				'group_id'	=> $group,
422
			);
423 6
		}
424 6
		$this->db->sql_multi_insert($this->ad_group_table, $sql_ary);
425 6
	}
426
427
	/**
428
	 * Remove all rows of specified ad in ad_group table
429
	 *
430
	 * @param int	$ad_id	Advertisement ID
431
	 * @return void
432
	 */
433 4
	protected function remove_ad_group_data($ad_id)
434
	{
435 4
		$sql = 'DELETE FROM ' . $this->ad_group_table . '
436 4
			WHERE ad_id = ' . (int) $ad_id;
437 4
		$this->db->sql_query($sql);
438 4
	}
439
}
440