Skip to content

Commit d656a6f

Browse files
authored
Merge pull request #22 from laravel/cleanup_display_helper
Refactor DisplayHelper and add unit tests for improved clarity
2 parents fe19a07 + f8890c9 commit d656a6f

File tree

2 files changed

+308
-135
lines changed

2 files changed

+308
-135
lines changed

src/Install/Cli/DisplayHelper.php

Lines changed: 136 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -4,183 +4,184 @@
44

55
namespace Laravel\Boost\Install\Cli;
66

7+
use InvalidArgumentException;
8+
79
class DisplayHelper
810
{
11+
private const UNICODE_TOP_LEFT = '';
12+
private const UNICODE_TOP_RIGHT = '';
13+
private const UNICODE_BOTTOM_LEFT = '';
14+
private const UNICODE_BOTTOM_RIGHT = '';
15+
private const UNICODE_HORIZONTAL = '';
16+
private const UNICODE_VERTICAL = '';
17+
private const UNICODE_CROSS = '';
18+
private const UNICODE_TOP_T = '';
19+
private const UNICODE_BOTTOM_T = '';
20+
private const UNICODE_LEFT_T = '';
21+
private const UNICODE_RIGHT_T = '';
22+
23+
private const BORDER_TOP = 'top';
24+
private const BORDER_MIDDLE = 'middle';
25+
private const BORDER_BOTTOM = 'bottom';
26+
27+
private const CELL_PADDING = 2;
28+
private const GRID_CELL_PADDING = 4;
29+
private const ANSI_BOLD = "\e[1m";
30+
private const ANSI_RESET = "\e[0m";
31+
private const SPACE = ' ';
32+
933
/**
1034
* @param array<int, array<int|string, mixed>> $data
1135
*/
12-
public static function datatable(array $data, int $cols = 80): void
36+
public static function datatable(array $data, int $maxWidth = 80): void
1337
{
14-
if (empty($data)) {
38+
if (! $data) {
1539
return;
1640
}
1741

18-
// Calculate column widths
19-
$columnWidths = [];
20-
foreach ($data as $row) {
21-
$colIndex = 0;
22-
foreach ($row as $cell) {
23-
$length = mb_strlen((string) $cell);
24-
if (! isset($columnWidths[$colIndex]) || $length > $columnWidths[$colIndex]) {
25-
$columnWidths[$colIndex] = $length;
26-
}
27-
$colIndex++;
28-
}
29-
}
42+
$columnWidths = self::calculateColumnWidths($data);
43+
$columnWidths = array_map(fn ($width) => $width + self::CELL_PADDING, $columnWidths);
3044

31-
// Add padding
32-
$columnWidths = array_map(fn ($width) => $width + 2, $columnWidths);
33-
34-
// Unicode box drawing characters
35-
$topLeft = '';
36-
$topRight = '';
37-
$bottomLeft = '';
38-
$bottomRight = '';
39-
$horizontal = '';
40-
$vertical = '';
41-
$cross = '';
42-
$topT = '';
43-
$bottomT = '';
44-
$leftT = '';
45-
$rightT = '';
46-
47-
// Draw top border
48-
$topBorder = $topLeft;
49-
foreach ($columnWidths as $index => $width) {
50-
$topBorder .= str_repeat($horizontal, $width);
51-
if ($index < count($columnWidths) - 1) {
52-
$topBorder .= $topT;
53-
}
54-
}
55-
$topBorder .= $topRight;
56-
echo $topBorder.PHP_EOL;
45+
[$leftChar, $rightChar, $joinChar] = self::getBorderChars(self::BORDER_TOP);
46+
echo self::buildBorder($columnWidths, $leftChar, $rightChar, $joinChar).PHP_EOL;
5747

58-
// Draw rows
5948
$rowCount = 0;
6049
foreach ($data as $row) {
61-
$line = $vertical;
62-
$colIndex = 0;
63-
foreach ($row as $cell) {
64-
$cellStr = ($colIndex === 0) ? "\e[1m".$cell."\e[0m" : $cell;
65-
$padding = $columnWidths[$colIndex] - mb_strlen($cell);
66-
$line .= ' '.$cellStr.str_repeat(' ', $padding - 1).$vertical;
67-
$colIndex++;
68-
}
69-
echo $line.PHP_EOL;
50+
echo self::buildDataRow($row, $columnWidths).PHP_EOL;
7051

71-
// Draw separator between rows (except after last row)
7252
if ($rowCount < count($data) - 1) {
73-
$separator = $leftT;
74-
foreach ($columnWidths as $index => $width) {
75-
$separator .= str_repeat($horizontal, $width);
76-
if ($index < count($columnWidths) - 1) {
77-
$separator .= $cross;
78-
}
79-
}
80-
$separator .= $rightT;
81-
echo $separator.PHP_EOL;
53+
[$leftChar, $rightChar, $joinChar] = self::getBorderChars(self::BORDER_MIDDLE);
54+
echo self::buildBorder($columnWidths, $leftChar, $rightChar, $joinChar).PHP_EOL;
8255
}
8356
$rowCount++;
8457
}
8558

86-
// Draw bottom border
87-
$bottomBorder = $bottomLeft;
88-
foreach ($columnWidths as $index => $width) {
89-
$bottomBorder .= str_repeat($horizontal, $width);
90-
if ($index < count($columnWidths) - 1) {
91-
$bottomBorder .= $bottomT;
92-
}
93-
}
94-
$bottomBorder .= $bottomRight;
95-
echo $bottomBorder.PHP_EOL;
59+
[$leftChar, $rightChar, $joinChar] = self::getBorderChars(self::BORDER_BOTTOM);
60+
echo self::buildBorder($columnWidths, $leftChar, $rightChar, $joinChar).PHP_EOL;
9661
}
9762

9863
/**
9964
* @param array<int, string> $items
10065
*/
101-
public static function grid(array $items, int $cols = 80): void
66+
public static function grid(array $items, int $maxWidth = 80): void
10267
{
10368
if (empty($items)) {
10469
return;
10570
}
10671

107-
$cols -= 2;
108-
// Calculate the longest item length
72+
$maxWidth -= 2; // account for grid margins
10973
$maxItemLength = max(array_map('mb_strlen', $items));
110-
111-
// Add padding (2 spaces on each side + 1 for border)
112-
$cellWidth = $maxItemLength + 4;
113-
114-
// Calculate how many cells can fit per row
115-
$cellsPerRow = max(1, (int) floor(($cols - 1) / ($cellWidth + 1)));
116-
117-
// Unicode box drawing characters
118-
$topLeft = '';
119-
$topRight = '';
120-
$bottomLeft = '';
121-
$bottomRight = '';
122-
$horizontal = '';
123-
$vertical = '';
124-
$cross = '';
125-
$topT = '';
126-
$bottomT = '';
127-
$leftT = '';
128-
$rightT = '';
129-
130-
// Group items into rows
74+
$cellWidth = $maxItemLength + self::GRID_CELL_PADDING;
75+
$cellsPerRow = max(1, (int) floor(($maxWidth - 1) / ($cellWidth + 1)));
13176
$rows = array_chunk($items, $cellsPerRow);
13277

133-
// Draw top border
134-
$topBorder = $topLeft;
135-
for ($i = 0; $i < $cellsPerRow; $i++) {
136-
$topBorder .= str_repeat($horizontal, $cellWidth);
137-
if ($i < $cellsPerRow - 1) {
138-
$topBorder .= $topT;
139-
}
140-
}
141-
$topBorder .= $topRight;
142-
echo ' '.$topBorder.PHP_EOL;
78+
$cellWidths = array_fill(0, $cellsPerRow, $cellWidth);
79+
80+
[$leftChar, $rightChar, $joinChar] = self::getBorderChars(self::BORDER_TOP);
81+
echo self::SPACE.self::buildBorder($cellWidths, $leftChar, $rightChar, $joinChar).PHP_EOL;
14382

144-
// Draw rows
14583
$rowCount = 0;
14684
foreach ($rows as $row) {
147-
$line = $vertical;
148-
for ($i = 0; $i < $cellsPerRow; $i++) {
149-
if (isset($row[$i])) {
150-
$item = $row[$i];
151-
$padding = $cellWidth - mb_strlen($item) - 2;
152-
$line .= ' '.$item.str_repeat(' ', $padding + 1).$vertical;
153-
} else {
154-
// Empty cell
155-
$line .= str_repeat(' ', $cellWidth).$vertical;
156-
}
157-
}
158-
echo ' '.$line.PHP_EOL;
85+
echo self::SPACE.self::buildGridRow($row, $cellWidth, $cellsPerRow).PHP_EOL;
15986

160-
// Draw separator between rows (except after last row)
16187
if ($rowCount < count($rows) - 1) {
162-
$separator = $leftT;
163-
for ($i = 0; $i < $cellsPerRow; $i++) {
164-
$separator .= str_repeat($horizontal, $cellWidth);
165-
if ($i < $cellsPerRow - 1) {
166-
$separator .= $cross;
167-
}
168-
}
169-
$separator .= $rightT;
170-
echo ' '.$separator.PHP_EOL;
88+
[$leftChar, $rightChar, $joinChar] = self::getBorderChars(self::BORDER_MIDDLE);
89+
echo self::SPACE.self::buildBorder($cellWidths, $leftChar, $rightChar, $joinChar).PHP_EOL;
17190
}
17291
$rowCount++;
17392
}
17493

175-
// Draw bottom border
176-
$bottomBorder = $bottomLeft;
177-
for ($i = 0; $i < $cellsPerRow; $i++) {
178-
$bottomBorder .= str_repeat($horizontal, $cellWidth);
179-
if ($i < $cellsPerRow - 1) {
180-
$bottomBorder .= $bottomT;
94+
[$leftChar, $rightChar, $joinChar] = self::getBorderChars(self::BORDER_BOTTOM);
95+
echo self::SPACE.self::buildBorder($cellWidths, $leftChar, $rightChar, $joinChar).PHP_EOL;
96+
}
97+
98+
private static function getBorderChars(string $type): array
99+
{
100+
return match($type) {
101+
self::BORDER_TOP => [self::UNICODE_TOP_LEFT, self::UNICODE_TOP_RIGHT, self::UNICODE_TOP_T],
102+
self::BORDER_MIDDLE => [self::UNICODE_LEFT_T, self::UNICODE_RIGHT_T, self::UNICODE_CROSS],
103+
self::BORDER_BOTTOM => [self::UNICODE_BOTTOM_LEFT, self::UNICODE_BOTTOM_RIGHT, self::UNICODE_BOTTOM_T],
104+
default => throw new InvalidArgumentException('Border type should be valid'),
105+
};
106+
}
107+
108+
/**
109+
* @param array<int, array<int|string, mixed>> $data
110+
* @return array<int, int>
111+
*/
112+
private static function calculateColumnWidths(array $data): array
113+
{
114+
$columnWidths = [];
115+
foreach ($data as $row) {
116+
foreach ($row as $colIndex => $cell) {
117+
$length = mb_strlen((string) $cell);
118+
$columnWidths[$colIndex] = max($columnWidths[$colIndex] ?? 0, $length);
119+
}
120+
}
121+
122+
return $columnWidths;
123+
}
124+
125+
/**
126+
* @param array<int, int> $widths
127+
*/
128+
private static function buildBorder(array $widths, string $leftChar, string $rightChar, string $joinChar): string
129+
{
130+
$border = $leftChar;
131+
foreach ($widths as $index => $width) {
132+
$border .= str_repeat(self::UNICODE_HORIZONTAL, $width);
133+
if ($index < count($widths) - 1) {
134+
$border .= $joinChar;
181135
}
182136
}
183-
$bottomBorder .= $bottomRight;
184-
echo ' '.$bottomBorder.PHP_EOL;
137+
$border .= $rightChar;
138+
139+
return $border;
140+
}
141+
142+
/**
143+
* @param array<int|string, mixed> $row
144+
* @param array<int, int> $columnWidths
145+
*/
146+
private static function buildDataRow(array $row, array $columnWidths): string
147+
{
148+
$line = self::UNICODE_VERTICAL;
149+
$colIndex = 0;
150+
foreach ($row as $cell) {
151+
$cellStr = ($colIndex === 0) ? self::ANSI_BOLD.$cell.self::ANSI_RESET : $cell;
152+
$padding = $columnWidths[$colIndex] - mb_strlen((string) $cell);
153+
$line .= self::SPACE.$cellStr.str_repeat(self::SPACE, $padding - 1).self::UNICODE_VERTICAL;
154+
$colIndex++;
155+
}
156+
157+
return $line;
158+
}
159+
160+
/**
161+
* @param array<int, string> $row
162+
*/
163+
private static function buildGridRow(array $row, int $cellWidth, int $cellsPerRow): string
164+
{
165+
$line = self::UNICODE_VERTICAL;
166+
167+
$cells = array_map(
168+
fn ($index) => self::formatGridCell($row[$index] ?? '', $cellWidth),
169+
range(0, $cellsPerRow - 1)
170+
);
171+
172+
$line .= implode(self::UNICODE_VERTICAL, $cells).self::UNICODE_VERTICAL;
173+
174+
return $line;
175+
}
176+
177+
private static function formatGridCell(string $item, int $cellWidth): string
178+
{
179+
if (! $item) {
180+
return str_repeat(self::SPACE, $cellWidth);
181+
}
182+
183+
$padding = $cellWidth - mb_strlen($item) - 2;
184+
185+
return self::SPACE.$item.str_repeat(self::SPACE, $padding + 1);
185186
}
186187
}

0 commit comments

Comments
 (0)