Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
CsvExportService
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
3 / 3
7
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 resolveDelimiter
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 downloadWithUserDelimiter
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Http\Services;
4
5use App\Http\Models\Auth\User;
6use App\Http\Repositories\UserSettingRepository;
7use Symfony\Component\HttpFoundation\StreamedResponse;
8use Vitorccs\LaravelCsv\Entities\CsvConfig;
9
10/**
11 * Service for applying user-specific CSV delimiter settings to exports.
12 *
13 * Reads the user's `csv_split_separator` setting and applies it as the
14 * CSV delimiter for both V1 (Vitorccs LaravelCSV) and V2 (native fputcsv) exports.
15 */
16class CsvExportService
17{
18    public function __construct(
19        private UserSettingRepository $settingRepository
20    ) {}
21
22    /**
23     * Resolve the CSV delimiter for a given user.
24     *
25     * Returns the user's `csv_split_separator` setting if it is a valid
26     * single character. Falls back to comma (`,`) when no user is provided
27     * (e.g., masquerade mode where there is no effective user) or the user
28     * has no setting.
29     *
30     * @param  User|null  $effectiveUser  The user whose setting should be used, or null to default to comma
31     * @return string A single-character CSV delimiter
32     */
33    public function resolveDelimiter(?User $effectiveUser = null): string
34    {
35        if ($effectiveUser === null) {
36            return ',';
37        }
38
39        $setting = $this->settingRepository->findByUserId($effectiveUser->getKey());
40
41        if (! $setting) {
42            return ',';
43        }
44
45        $separator = $setting->csv_split_separator;
46
47        if (! is_string($separator) || mb_strlen($separator) !== 1) {
48            return ',';
49        }
50
51        return $separator;
52    }
53
54    /**
55     * Download a V1 (Vitorccs LaravelCSV) export using the user's delimiter.
56     *
57     * Temporarily overrides the CsvConfig singleton's `csv_delimiter` property
58     * for the duration of the export callback, then restores the original value.
59     *
60     * @param  User|null  $user  The authenticated user, or null for masquerade (defaults to comma)
61     * @param  callable  $exportCallback  A callable that returns a StreamedResponse (e.g., `fn() => $export->download('file.csv')`)
62     */
63    public function downloadWithUserDelimiter(?User $user, callable $exportCallback): StreamedResponse
64    {
65        $csvConfig = app(CsvConfig::class);
66        $originalDelimiter = $csvConfig->csv_delimiter;
67
68        try {
69            $csvConfig->csv_delimiter = $this->resolveDelimiter($user);
70
71            return $exportCallback();
72        } finally {
73            $csvConfig->csv_delimiter = $originalDelimiter;
74        }
75    }
76}