La validation des chaînes dans les applications Laravel implique souvent de vérifier quel contenu ne doit PAS correspondre à certains modèles. Les nouvelles méthodes de chaîne inverse de Laravel éliminent la logique de négation maladroite, créant un code de validation plus intuitif et plus facile à maintenir.
Aller au-delà de la double négation La validation traditionnelle des chaînes de caractères nécessitait souvent l’annulation des méthodes existantes, créant une surcharge mentale lors de la lecture des conditions :
1use Illuminate\Support\Str;
2
3if (!Str::startsWith($document, ['draft_', 'temp_', 'backup_'])) {
4 $this->processPublishedDocument($document);
5}
6
7if (!Str::endsWith($username, ['_admin', '_system', '_test'])) {
8 $this->createStandardUser($username);
9}Les méthodes inverses de Laravel transforment ces conditions en énoncés clairs et positifs :
1if (Str::doesntStartWith($document, ['draft_', 'temp_', 'backup_'])) {
2 $this->processPublishedDocument($document);
3}
4
5if (Str::doesntEndWith($username, ['_admin', '_system', '_test'])) {
6 $this->createStandardUser($username);
7}
8Les deux méthodes acceptent des chaînes ou des tableaux uniques, conservant la même flexibilité que leurs homologues tout en améliorant la lisibilité du code.
Voici un système de gestion de contenu qui illustre ces méthodes dans des scénarios de validation pratiques :
1class ContentValidationService
2{
3 private array $draftPrefixes = ['draft_', 'wip_', 'preview_'];
4 private array $systemSuffixes = ['_config', '_admin', '_internal'];
5 private array $reservedWords = ['system', 'admin', 'root', 'api'];
6 private array $unsafeTags = ['<script>', '<iframe>', '<embed>'];
7
8 public function validateSlugCreation(string $title): array
9 {
10 $slug = Str::slug($title);
11 $errors = [];
12
13 if (Str::doesntStartWith($slug, $this->reservedWords)) {
14 // Safe slug prefix
15 } else {
16 $errors[] = 'Slug cannot start with reserved words';
17 }
18
19 if (Str::doesntEndWith($slug, $this->systemSuffixes)) {
20 // Valid ending pattern
21 } else {
22 $errors[] = 'Slug cannot end with system suffixes';
23 }
24
25 return [
26 'valid' => empty($errors),
27 'slug' => $slug,
28 'errors' => $errors
29 ];
30 }
31
32 public function sanitizeContentInput(string $content): array
33 {
34 $maliciousPatterns = ['<script', 'javascript:', 'data:image'];
35 $suspiciousEndings = ['.exe"', '.bat"', '.cmd"'];
36
37 $validation = [
38 'safe_opening' => Str::doesntStartWith(
39 Str::lower($content),
40 array_map('strtolower', $maliciousPatterns)
41 ),
42 'safe_references' => Str::doesntEndWith(
43 Str::lower($content),
44 $suspiciousEndings
45 ),
46 'content_length' => strlen($content)
47 ];
48
49 return array_merge($validation, [
50 'is_safe' => $validation['safe_opening'] && $validation['safe_references']
51 ]);
52 }
53
54 public function categorizeUserFiles(array $filePaths): array
55 {
56 $categories = [
57 'published' => [],
58 'drafts' => [],
59 'user_content' => [],
60 'restricted' => []
61 ];
62
63 foreach ($filePaths as $path) {
64 $filename = basename($path);
65
66 if (Str::doesntStartWith($filename, $this->draftPrefixes)) {
67 if (Str::doesntEndWith($filename, $this->systemSuffixes)) {
68 $categories['published'][] = $path;
69 } else {
70 $categories['restricted'][] = $path;
71 }
72 } else {
73 $categories['drafts'][] = $path;
74 }
75 }
76
77 return $categories;
78 }
79
80 public function validateApiEndpoint(string $endpoint): bool
81 {
82 $restrictedPrefixes = ['/admin/', '/system/', '/internal/'];
83 $restrictedSuffixes = ['/delete', '/purge', '/reset'];
84
85 return Str::doesntStartWith($endpoint, $restrictedPrefixes) &&
86 Str::doesntEndWith($endpoint, $restrictedSuffixes);
87 }
88}
89
90class PostController extends Controller
91{
92 public function __construct(
93 private ContentValidationService $validator
94 ) {}
95
96 public function store(Request $request)
97 {
98 $title = $request->input('title');
99 $content = $request->input('content');
100
101 $slugValidation = $this->validator->validateSlugCreation($title);
102 $contentValidation = $this->validator->sanitizeContentInput($content);
103
104 if (!$slugValidation['valid'] || !$contentValidation['is_safe']) {
105 return back()->withErrors([
106 'title' => $slugValidation['errors'] ?? [],
107 'content' => $contentValidation['is_safe'] ? [] : ['Content contains unsafe patterns']
108 ]);
109 }
110
111 Post::create([
112 'title' => $title,
113 'slug' => $slugValidation['slug'],
114 'content' => $content,
115 'status' => 'published'
116 ]);
117
118 return redirect()->route('posts.index')
119 ->with('success', 'Post created successfully');
120 }
121}