When generating filenames from dynamic input—user-provided names, database values, or API responses—a three-step Laravel string helper chain ensures clean, filesystem-safe output.
The pattern combines Str::lower(), Str::slug(), and Str::title() to handle edge cases you might not think of:
use Illuminate\Support\Str;
class FileGenerator
{
public static function sanitizeFilename(string $name): string
{
return Str::title(Str::slug(Str::lower($name), '_'));
}
}
// Examples
FileGenerator::sanitizeFilename('Q4 Sales Report');
// Returns: 'Q4_Sales_Report'
FileGenerator::sanitizeFilename('User Analytics (2024)');
// Returns: 'User_Analytics_2024'
FileGenerator::sanitizeFilename('Employee List - HR Dept.');
// Returns: 'Employee_List_Hr_Dept'
FileGenerator::sanitizeFilename('Données françaises');
// Returns: 'Donnees_Francaises'
Why this three-step chain works:
- Str::lower() normalizes case to avoid filesystem issues on case-sensitive systems (Linux servers are case-sensitive, Windows/Mac are not)
- Str::slug() converts to URL-safe format, replacing spaces and special characters with your chosen separator (underscore here)
- Str::title() capitalizes words for readable filenames without breaking filesystem compatibility
Real-world usage with timestamps:
class ReportExporter
{
public function export(string $reportName, array $data): string
{
$filename = FileGenerator::sanitizeFilename($reportName)
. '_' . now()->format('Y-m-d_His')
. '.csv';
Storage::put("exports/{$filename}", $this->toCsv($data));
return $filename;
}
}
// Output: 'Monthly_Revenue_2024-03-05_093045.csv'
Why not just use Str::slug() alone?
Plain Str::slug() would give you 'q4-sales-report' (all lowercase). The Str::title() step makes filenames more readable when users download them. Compare:
- Without title case:
employee_performance_report.csv - With title case:
Employee_Performance_Report.csv
The second is clearer at a glance in file explorers.
Alternative separators:
You can use hyphens instead of underscores by changing the second parameter to Str::slug():
return Str::title(Str::slug(Str::lower($name), '-'));
This is common for web-facing URLs. Underscores are traditional for downloaded files, but both work fine for filesystems.
Leave a Reply