Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Junior: питання на співбесіді з теми «Хелпери й рядки»

Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.

3 питання

Laravel має два способи працювати з рядками, і обидва побудовані на тому самому наборі методів.

1. Статичний клас Str - кожен метод приймає рядок і повертає новий рядок:

use Illuminate\Support\Str;

Str::limit('Довгий текст статті', 10);      // 'Довгий тек...'
Str::headline('laravel_framework');          // 'Laravel Framework'
Str::mask('olena@example.com', '*', 3);      // 'ole**************'
Str::contains($title, ['Laravel', 'PHP']);
Str::uuid();

2. Fluent-рядки - Str::of() чи коротший хелпер str() повертають об'єкт Stringable, і методи можна складати в ланцюжок:

$slug = str('  Нова версія Laravel  ')
    ->squish()                 // прибрати зайві пробіли
    ->limit(50, '')
    ->slug('-', 'uk');

Str::of($path)->afterLast('/')->beforeLast('.');

Між Str::of('...') і str('...') різниці немає: це той самий Stringable. А виклик str() без аргументів повертає об'єкт, що перенаправляє виклики до статичних методів Str: str()->uuid().

Коли що обирати:

Ситуація Що зручніше
одна операція Str::limit($text, 100)
кілька перетворень поспіль str($text)->squish()->lower()->...
умовні кроки str($x)->when($cond, fn ($s) => $s->append('...'))

Stringable - не рядок. Він приводиться до рядка в echo, Blade і конкатенації, але === з рядком дасть false, а функція з типом string у строгому режимі отримає об'єкт. Тому в кінці ланцюжка часто викликають ->toString() чи ->value():

$name = str($input)->trim()->title()->toString();

Кирилиця: методи Str враховують багатобайтові символи - str('Привіт')->length() повертає 6, а не 12, як strlen(). Але Str::slug() і Str::ascii() транслітерують за правилами мови, яку треба вказати явно, якщо результат має бути українською транслітерацією.

Перед тим як писати власну функцію для рядків варто переглянути список методів Str: там уже є between, excerpt, initials, wordCount, plural, squish, isUuid, isJson і десятки інших.

Докладніше в документації: Рядки: fluent-рядки

Клас Illuminate\Support\Number - обгортка над розширенням intl для форматування чисел з урахуванням локалі. Для більшості методів розширення intl обов'язкове.

use Illuminate\Support\Number;

Number::format(1234567.891, precision: 2, locale: 'uk');  // '1 234 567,89'
Number::currency(1500, in: 'UAH', locale: 'uk');          // '1 500,00 ₴'
Number::percentage(12.345, precision: 1);                 // '12.3%'
Number::fileSize(1536000, precision: 1);                  // '1.5 MB'
Number::abbreviate(1500000, precision: 1);                // '1.5M'
Number::forHumans(1500000);                               // '2 million'
Number::spell(42, locale: 'uk');                          // 'сорок два'
Number::ordinal(3);                                       // '3rd'
Number::clamp($value, min: 1, max: 100);

Що варто знати:

  • precision за замовчуванням 0 у fileSize, abbreviate і forHumans: Number::forHumans(1500000) округлює до '2 million'. Для дробової частини вказуйте precision;
  • fileSize рахує в степенях 1024, а не 1000;
  • forHumans і abbreviate не локалізовані: вони завжди пишуть англійською (million, M);
  • format, currency, percentage, spell приймають locale, а в українській локалі роздільник тисяч - нерозривний пробіл, десятковий - кома.

Локаль для всього застосунку чи фрагмента:

// у сервіс-провайдері
Number::useLocale('uk');
Number::useCurrency('UAH');

// тимчасово для одного блоку
Number::withLocale('de', fn () => Number::format(1234.5));   // '1.234,5'

Зворотна операція - розібрати введене користувачем число з урахуванням локалі:

Number::parseFloat('1 234,5', locale: 'uk');

Гроші: Number::currency() - лише для відображення. Рахувати гроші через float не можна: зберігайте суму в копійках цілим числом (або в decimal у базі) і форматуйте тільки при виводі:

Number::currency($order->total_cents / 100, in: 'UAH');

У Blade це зручно загорнути в accessor моделі чи Blade-компонент, щоб формат валюти й точність були однаковими на всьому сайті.

Докладніше в документації: Хелпери: числа

Вкладені масиви (конфігурація, JSON зі стороннього API, payload вебхука) зручно читати крапковою нотацією замість ланцюжка isset:

use Illuminate\Support\Arr;

$data = [
    'user' => ['profile' => ['name' => 'Olena']],
    'orders' => [['total' => 10], ['total' => 20]],
];

Arr::get($data, 'user.profile.name');          // 'Olena'
Arr::get($data, 'user.email', 'n/a');          // 'n/a' - значення за замовчуванням
Arr::has($data, 'user.profile.name');          // true

data_get - те саме, але для масивів і об'єктів разом і з підтримкою *:

data_get($data, 'orders.*.total');             // [10, 20]
data_get($request->json(), 'payment.status');
data_get($model, 'author.profile.city');       // працює й з Eloquent-моделями

Запис:

Arr::set($config, 'mail.from.address', 'hello@example.com');   // за посиланням
data_set($data, 'orders.*.paid', false);       // усім елементам
data_fill($data, 'meta.source', 'api');        // лише якщо ключа ще немає
Arr::forget($data, 'user.password');

Інші корисні методи:

Метод Що робить
Arr::only($a, ['name', 'email']) лише вказані ключі
Arr::except($a, ['password']) усі, крім вказаних
Arr::wrap($value) гарантує масив: null → [], 'a' → ['a']
Arr::dot($a) / Arr::undot($a) вкладений масив ↔ плаский з ключами a.b.c
Arr::pluck($a, 'user.name') значення поля з кожного елемента
Arr::first($a, fn ($v) => ...) перший елемент за умовою
Arr::string($a, 'key'), Arr::integer(...) значення з перевіркою типу, інакше виняток

Пастка крапкової нотації: якщо в ключі масиву справді є крапка ('version.major' => 1), Arr::get($a, 'version.major') спершу перевіряє точний ключ і знайде його, але data_get і запис через Arr::set трактуватимуть крапку як вкладеність. Для таких даних краще звичайний доступ $a['version.major'].

Arr::wrap особливо корисний для параметрів, що приймають «одне значення або масив»: foreach (Arr::wrap($recipients) as $email) - без перевірок типу.

Для колекцій є ті самі можливості: collect($data)->get(...), ->pluck('user.name'), ->dot(). Якщо дані обробляються ланцюжком перетворень - колекція зручніша, для одного звернення - Arr чи data_get.

Докладніше в документації: Хелпери: data_get