Покращена валідація рядків у Laravel за допомогою інверсних методів

1
Перекладено ШІ
Оригінал: Laravel News
Оновлено: 17 грудня, 2025
Вас коли-небудь дратувала складність валідації рядків у Laravel через подвійні заперечення? У нашій статті ми розглянемо нові інверсивні методи Laravel, які спростять вашу логіку валідації, зробивши її більш зрозумілою та легкочитною. Читайте далі, щоб дізнатися, як ці зміни покращать ваш код

Валідація рядків у 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 перетворюють логіку валідації з важких для сприйняття потрійних заперечень на чіткі, виражені умови, що покращують підтримуваність коду та розуміння для розробників

Популярні

Logomark Logotype

Інтеграція Laravel Socialite з бібліотекою Google Client PHP

Ви хочете навчитися, як інтегрувати Google OAuth у вашому проекті Laravel, використовуючи Socialite? Дізнайтеся, як налаштувати доступ до сервісів Google, таких як Календар, у нашій сьогоднішній статті

Logomark Logotype

"SQLSTATE[HY000] [2002] Connection refused" у Laravel в GitHub Actions

Чи стикалися ви з помилкою «SQLSTATE[HY000] [2002] Connection refused» під час налаштування GitHub Actions для вашого додатку на Laravel? У нашій статті ми розглянемо три поширені причини цієї помилки та надамо рішення для їх усунення. Читайте далі, щоб дізнатися, як ваш CI/CD потік може працювати бездоганно!

Logomark Logotype

Laravel Boost — ваш стартовий набір для програмування з використанням штучного інтелекту

Вперше у світі Laravel з'являється можливість, яка значно спростить ваше повсякденне програмування завдяки новому пакету Laravel Boost. Читайте статтю, щоб дізнатися, як посилена інтеграція штучного інтелекту може підвищити ефективність вашої роботи та оптимізувати створення проектів у Laravel