Completed
Pull Request — 8.x-2.x (#24)
by Frédéric G.
02:40
created

TopController::top()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 70
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 5
eloc 46
c 2
b 0
f 1
nc 5
nop 1
dl 0
loc 70
rs 8.5454

1 Method

Rating   Name   Duplication   Size   Complexity  
B TopController::getRowData() 0 35 3

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 Drupal\mongodb_watchdog\Controller;
4
5
use Drupal\Core\Config\ImmutableConfig;
6
use Drupal\mongodb_watchdog\Logger;
7
use MongoDB\BSON\Javascript;
8
use MongoDB\Collection;
9
use MongoDB\Database;
10
use Psr\Log\LoggerInterface;
11
use Symfony\Component\DependencyInjection\ContainerInterface;
12
use Symfony\Component\HttpFoundation\Request;
13
14
/**
15
 * The Top403/Top404 controllers.
16
 */
17
class TopController extends ControllerBase {
18
  const TYPES = [
19
    'page not found',
20
    'access denied',
21
  ];
22
23
  /**
24
   * The database holding the logger collections.
25
   *
26
   * @var \MongoDB\Database
27
   */
28
  protected $database;
29
30
  /**
31
   * TopController constructor.
32
   *
33
   * @param \Psr\Log\LoggerInterface $logger
34
   *   The logger service, to log intervening events.
35
   * @param \Drupal\mongodb_watchdog\Logger $watchdog
36
   *   The MongoDB logger, to load stored events.
37
   * @param \Drupal\Core\Config\ImmutableConfig $config
38
   *   The module configuration.
39
   * @param \MongoDB\Database $database
40
   *   Needed because there is no group() command in phplib yet.
41
   *
42
   * @see https://jira.mongodb.org/browse/PHPLIB-177
43
   */
44
  public function __construct(
45
    LoggerInterface $logger,
46
    Logger $watchdog,
47
    ImmutableConfig $config,
48
    Database $database) {
49
    parent::__construct($logger, $watchdog, $config);
50
51
    $this->database = $database;
52
  }
53
54
  /**
55
   * Controller.
56
   *
57
   * @param \Symfony\Component\HttpFoundation\Request $request
58
   *   The current request.
59
   * @param string $type
60
   *   The type of top report to produce.
61
   *
62
   * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string|array>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
63
   *   A render array.
64
   */
65 View Code Duplication
  public function build(Request $request, $type) {
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...
66
    $top = $this->getTop();
67
68
    $rows = $this->getRowData($request, $type);
69
    $main = empty($rows)
70
      ? [
71
        '#markup' => t('No "%type" message found', ['%type' => $type]),
72
        '#prefix' => '<div class="mongodb_watchdog__message">',
73
        '#suffix' => '</div>',
74
      ]
75
      : $this->buildMainTable($rows);
76
77
    $ret = $this->buildDefaults($main, $top);
78
    return $ret;
79
  }
80
81
  /**
82
   * Build the main table.
83
   *
84
   * @param array $rows
85
   *   The event data.
86
   *
87
   * @return array<string,string|array>
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,\Drupal\Cor...g,array|string>|string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
88
   *   A render array for the main table.
89
   */
90 View Code Duplication
  protected function buildMainTable(array $rows) {
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...
91
    $ret = [
92
      '#header' => $this->buildMainTableHeader(),
93
      '#rows' => $this->buildMainTableRows($rows),
94
      '#type' => 'table',
95
    ];
96
    return $ret;
97
  }
98
99
  /**
100
   * Build the main table header.
101
   *
102
   * @return \Drupal\Core\StringTranslation\TranslatableMarkup[]
103
   *   A table header array.
104
   */
105
  protected function buildMainTableHeader() {
106
    $header = [
107
      t('#'),
108
      t('Paths'),
109
    ];
110
111
    return $header;
112
  }
113
114
  /**
115
   * Build the main table rows.
116
   *
117
   * @param array[] $counts
118
   *   The array of counts per 403/404 page.
119
   *
120
   * @return array<string,array|string>
121
   *   A render array for a table.
122
   */
123
  protected function buildMainTableRows($counts) {
0 ignored issues
show
introduced by
Type hint "array" missing for $counts
Loading history...
124
    $rows = [];
125
    foreach ($counts as $count) {
126
      $row = [
127
        $count['count'],
128
        $count['variables.@uri'],
129
      ];
130
      $rows[] = $row;
131
    }
132
133
    return $rows;
134
  }
135
136
  /**
137
   * {@inheritdoc}
138
   */
139 View Code Duplication
  public static function create(ContainerInterface $container) {
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...
140
    /** @var \Psr\Log\LoggerInterface $logger */
141
    $logger = $container->get('logger.channel.mongodb_watchdog');
142
143
    /** @var \Drupal\mongodb_watchdog\Logger $watchdog */
144
    $watchdog = $container->get('mongodb.logger');
145
146
    /** @var \Drupal\Core\Config\ImmutableConfig $config */
147
    $config = $container->get('config.factory')->get('mongodb_watchdog.settings');
148
149
    /** @var \MongoDB\Database $database */
150
    $database = $container->get('mongodb.watchdog_storage');
151
152
    return new static($logger, $watchdog, $config, $database);
153
  }
154
155
  /**
156
   * Obtain the data from the logger.
157
   *
158
   * @param \Symfony\Component\HttpFoundation\Request $request
159
   *   The current request. Needed for paging.
160
   * @param string $type
161
   *   The type of top list to retrieve.
162
   *
163
   * @return array
164
   *   The data array.
165
   */
166
  protected function getRowData(Request $request, $type) {
167
    // Find _id for the error type.
168
    $templateCollection = $this->watchdog->templateCollection();
169
    $template = $templateCollection->findOne(['type' => $type], ['_id']);
170
    if (empty($template)) {
171
      return [];
172
    }
173
174
    // Find occurrences of error type.
175
    $collectionName = $template['_id'];
176
    $eventCollection = $this->watchdog->eventCollection($collectionName);
177
178
    $key = ['variables.@uri' => 1];
179
    $cond = [];
180
    $reducer = <<<JAVASCRIPT
181
function reducer(doc, accumulator) { 
182
  accumulator.count++; 
183
}
184
JAVASCRIPT;
185
186
    $initial = ['count' => 0];
187
    $counts = $this->group($eventCollection, $key, $cond, $reducer, $initial);
188
    if (empty($counts['ok'])) {
189
      return [];
190
    }
191
192
    $counts = $counts['retval'];
193
    usort($counts, [$this, 'topSort']);
194
195
    $page = $this->setupPager($request, count($counts));
196
    $skip = $page * $this->itemsPerPage;
197
    $counts = array_slice($counts, $skip, $this->itemsPerPage);
198
199
    return $counts;
200
  }
201
202
  /**
203
   * Command wrapper for missing MongoDB group() implementation in PHPlib.
204
   *
205
   * @param \MongoDB\Collection $collection
206
   *   The collection on which to perform the command.
207
   * @param object $key
208
   *   The grouping key.
209
   * @param object $cond
210
   *   The condition.
211
   * @param string $reduce
212
   *   The reducer function: must be valid JavaScript code.
213
   * @param object $initial
214
   *   The initial document.
215
   *
216
   * @return array|void
217
   *   Void in case of error, otherwise an array with the following keys:
218
   *   - waitedMS: time spent waiting
219
   *   - retval: an array of command results, containing at least the key
220
   *   - count: the total number of documents matched
221
   *   - keys: the number of different keys, normally matching count(retval)
222
   *   - ok: 1.0 in case of success.
223
   */
224
  public function group(Collection $collection, $key, $cond, $reduce, $initial) {
225
    $cursor = $this->database->command([
226
      'group' => [
227
        'ns' => $collection->getCollectionName(),
228
        'key' => $key,
229
        'cond' => $cond,
230
        'initial' => $initial,
231
        '$reduce' => new Javascript($reduce),
232
      ],
233
    ], Logger::LEGACY_TYPE_MAP);
234
235
    $ret = $cursor->toArray();
236
    $ret = reset($ret);
237
    return $ret;
238
  }
239
240
  /**
241
   * Callback for usort() to sort top entries returned from a group query.
242
   *
243
   * @param array $first
244
   *   The first value to compare.
245
   * @param array $second
246
   *   The second value to compare.
247
   *
248
   * @return int
249
   *   The comparison result.
250
   *
251
   * @see \Drupal\mongodb_watchdog\Controller\TopController::build()
252
   */
253
  protected function topSort(array $first, array $second) {
254
    return $second['count'] <=> $first['count'];
255
  }
256
257
}
258