Валідація рядків у Laravel часто передбачає перевірку, які вмісти НЕ повинні відповідати певним шаблонам. Нові інверсні методи рядків у Laravel усувають незручну логіку заперечення, що робить код валідації більш інтуїтивним та легким для підтримки.
Відмова від потрійних заперечень
Традиційна валідація рядків часто вимагала заперечення існуючих методів, що створювало додаткове навантаження при читанні умов:
use Illuminate\Support\Str;
if (!Str::startsWith($document, ['draft_', 'temp_', 'backup_'])) {
$this->processPublishedDocument($document);
}
if (!Str::endsWith($username, ['_admin', '_system', '_test'])) {
$this->createStandardUser($username);
}
Інверсні методи Laravel перетворюють ці умови на чіткі, позитивні твердження:
if (Str::doesntStartWith($document, ['draft_', 'temp_', 'backup_'])) {
$this->processPublishedDocument($document);
}
if (Str::doesntEndWith($username, ['_admin', '_system', '_test'])) {
$this->createStandardUser($username);
}
Обидва методи приймають одиничні рядки або масиви, зберігаючи ту ж гнучкість, що й їх аналоги, покращуючи читабельність коду.
Далі наведено приклад системи управління контентом, яка демонструє ці методи в практичних сценаріях валідації:
class ContentValidationService
{
private array $draftPrefixes = ['draft_', 'wip_', 'preview_'];
private array $systemSuffixes = ['_config', '_admin', '_internal'];
private array $reservedWords = ['system', 'admin', 'root', 'api'];
private array $unsafeTags = ['<script>', '<iframe>', '<embed>'];
public function validateSlugCreation(string $title): array
{
$slug = Str::slug($title);
$errors = [];
if (Str::doesntStartWith($slug, $this->reservedWords)) {
// Безпечний префікс для слугу
} else {
$errors[] = 'Slug не може починатися з зарезервованих слів';
}
if (Str::doesntEndWith($slug, $this->systemSuffixes)) {
// Дійсний закінчення
} else {
$errors[] = 'Slug не може закінчуватися системними суфіксами';
}
return [
'valid' => empty($errors),
'slug' => $slug,
'errors' => $errors
];
}
public function sanitizeContentInput(string $content): array
{
$maliciousPatterns = ['<script', 'javascript:', 'data:image'];
$suspiciousEndings = ['.exe"', '.bat"', '.cmd"'];
$validation = [
'safe_opening' => Str::doesntStartWith(
Str::lower($content),
array_map('strtolower', $maliciousPatterns)
),
'safe_references' => Str::doesntEndWith(
Str::lower($content),
$suspiciousEndings
),
'content_length' => strlen($content)
];
return array_merge($validation, [
'is_safe' => $validation['safe_opening'] && $validation['safe_references']
]);
}
public function categorizeUserFiles(array $filePaths): array
{
$categories = [
'published' => [],
'drafts' => [],
'user_content' => [],
'restricted' => []
];
foreach ($filePaths as $path) {
$filename = basename($path);
if (Str::doesntStartWith($filename, $this->draftPrefixes)) {
if (Str::doesntEndWith($filename, $this->systemSuffixes)) {
$categories['published'][] = $path;
} else {
$categories['restricted'][] = $path;
}
} else {
$categories['drafts'][] = $path;
}
}
return $categories;
}
public function validateApiEndpoint(string $endpoint): bool
{
$restrictedPrefixes = ['/admin/', '/system/', '/internal/'];
$restrictedSuffixes = ['/delete', '/purge', '/reset'];
return Str::doesntStartWith($endpoint, $restrictedPrefixes) &&
Str::doesntEndWith($endpoint, $restrictedSuffixes);
}
}
class PostController extends Controller
{
public function __construct(
private ContentValidationService $validator
) {}
public function store(Request $request)
{
$title = $request->input('title');
$content = $request->input('content');
$slugValidation = $this->validator->validateSlugCreation($title);
$contentValidation = $this->validator->sanitizeContentInput($content);
if (!$slugValidation['valid'] || !$contentValidation['is_safe']) {
return back()->withErrors([
'title' => $slugValidation['errors'] ?? [],
'content' => $contentValidation['is_safe'] ? [] : ['Контент містить небезпечні шаблони']
]);
}
Post::create([
'title' => $title,
'slug' => $slugValidation['slug'],
'content' => $content,
'status' => 'published'
]);
return redirect()->route('posts.index')
->with('success', 'Пост успішно створено');
}
}
Інверсні методи створюють самодокументуючий код, де умови читаються природно: "якщо це не починається з префіксів чернетки" замість "якщо не починається з префіксів чернетки". Ця перевага стає особливо важливою у складних ланцюгах валідації, де поєднуються кілька умов.
Ці методи безшовно інтегруються з флюентним API рядків Laravel, що дозволяє елегантно об'єднувати методи:
$isValid = str($filename)
->lower()
->doesntStartWith(['temp_', 'cache_'])
->doesntEndWith(['.tmp', '.bak']);
Інверсні методи рядків Laravel перетворюють логіку валідації з важких для сприйняття потрійних заперечень на чіткі, виражені умови, що покращують підтримуваність коду та розуміння для розробників