WebCraft - ремесло веб разработки
WebCraft - это Telegram канал, который предлагает интересный контент для тех, кто интересуется php, js, css, html, Laravel и Vue. В канале вы найдете полезные материалы, связанные с разработкой веб-приложений. Он предлагает информацию о последних тенденциях и новостях в мире веб-разработки. Канал также предлагает обучающие материалы и руководства по использованию различных технологий, таких как Laravel и Vue. Присоединяйтесь к WebCraft, чтобы быть в курсе последних событий и улучшить свои навыки веб-разработки.
Публикации канала
✨ Правила валидации в Laravel теперь можно писать без строк Многие правила валидации в Laravel задаются строками вроде required_without:email. Редактор их не подсказывает, а опечатка в названии всплывёт только при запуске валидатора. В Laravel 13.36 у класса Rule появился метод для каждого правила, которое раньше было только строкой. Заодно ушла проблема с запятыми. В строке параметры делятся по запятой, поэтому accepted_if:plan,Team, Annual валидатор прочитает как два значения: Team и Annual. Правило не сработает никогда. Билдер передаёт параметры валидатору массивом, без разбора строки, и запятая остаётся внутри значения: 'terms' => [Rule::acceptedIf('plan', 'Team, Annual')], Строковые правила при этом никуда не делись: оба стиля можно смешивать даже в одном массиве. 👍 - если было полезно 👉 @web_craft
🚀 Laravel снимает лимиты PlanetScale с тяжёлых запросов MySQL-базы PlanetScale работают на Vitess в режиме OLTP. Там запрос может затронуть максимум 100 000 строк, а результат весит не больше 64 MiB. Большой отчёт или CSV-экспорт на этом падает, хотя на локальном MySQL работает. У Vitess есть режим OLAP без этих лимитов. В Laravel 13.36 появился DB::olap(). Он включает OLAP на время колбэка для соединений на запись и на чтение, а потом гарантированно возвращает OLTP. На обычном MySQL метод просто вызывает колбэк. Поэтому один и тот же код работает локально, в CI и на PlanetScale. Главный подвох: cursor(), lazy() и lazyById() выполняют запрос только при переборе. Перебирать их нужно внутри колбэка, иначе запрос уйдёт уже в режиме OLTP. Это инструмент для редкого экспорта или админского отчёта: для обычных запросов приложения сама PlanetScale OLAP не рекомендует. 👍 - если было полезно 👉 @web_craft
🧮 echo печатает 58, а (int) из того же числа даёт 57 PHP хранит float в двоичном виде, и 0.58 так точно не записать. Поэтому 0.58 * 100 в памяти чуть меньше 58. <?php ini_set('precision', '14'); // 14 is the default value. $value = 0.58 * 100; echo $value, "\n"; // 58 var_dump(intval($value)); // int(57) echo округляет вывод до 14 знаков (настройка precision) и прячет хвост. А intval() и (int) берут настоящее значение и просто отбрасывают дробную часть. Для точных десятичных расчётов есть расширение BCMath: bcmul('0.58', '100', 0) вернёт "58". Числа ему передают строками. Если отдать уже готовый float, потерянные цифры не вернуть. А на 64-битной сборке даже (9223372036854775808 - 1) === 9223372036854775807 даёт false, хотя дробей там нет вовсе. 👍 - если было полезно 👉 @web_craft
🔑 Пусть менеджер паролей знает правила вашего сайта Менеджер паролей придумал пароль, а сайт его отклонил: нет цифры или спецсимвола. Браузер просто не знает серверных правил. В Laravel правило Password умеет выдавать строку для HTML-атрибута passwordrules. По ней менеджер паролей генерирует пароль, который пройдёт валидацию. > use Illuminate\Validation\Rules\Password; > Password::min(12)->max(64)->mixedCase()->numbers()->symbols()->toPasswordRulesString(); = "minlength: 12; maxlength: 64; required: lower; required: upper; required: digit; required: special;" Учтите, что passwordrules не входит в стандарт HTML. Его добавила Apple в Safari, понимают его и некоторые менеджеры паролей вроде 1Password. Остальные атрибут игнорируют, так что вреда нет. Строку удобно брать из Password::defaults() прямо в Blade, и правила в форме не разойдутся с сервером. 👍 - если было полезно 👉 @web_craft
🚀 Страница 4000: 127 мс против 3 мс Обычная пагинация через смещение (LIMIT 10000,20) медленнеет с каждой страницей. База всё равно читает первые 10 000 строк, перебирает их и выбрасывает. Чем дальше страница, тем больше пустой работы. Курсорная пагинация обходится без смещения. Приложение запоминает значение колонки сортировки на границе страницы, например last_message_at. Следующую страницу оно берёт через WHERE last_message_at <= ? и LIMIT 0,20. Если колонка в индексе, скорость почти не зависит от глубины. В одном тесте на MySQL 8.4 и 300 000 строк страница 4000 открывалась за 127 мс через смещение и за 3 мс через курсор. В Doctrine ORM 3.7 для этого появляется класс CursorPaginator. Подвох в том, что прыгнуть сразу на страницу 57 нельзя. Доступны только первая, следующая и последняя. Если по вашим длинным спискам ходят краулеры или API-клиенты, самое время посмотреть, как выглядит переход в коде. 👍 - если было полезно 👉 @web_craft
🔒 readonly в PHP защищает свойство, но не объект внутри readonly запрещает присвоить свойству новое значение. Но изменяемый объект в таком свойстве по-прежнему можно менять: final readonly class DeliveryWindow { public function __construct( public DateTime $startsAt, ) {} } $window = new DeliveryWindow(new DateTime('2026-08-01 09:00:00')); $window->startsAt->modify('+1 day'); echo $window->startsAt->format('Y-m-d'); // 2026-08-02 Свойство никто не переприсваивал, и PHP не возражает. Но дата доставки уже другая: readonly не действует вглубь. Поэтому внутри стоит хранить только неизменяемые значения. Например, DateTimeImmutable вместо DateTime: его modify() возвращает новый объект. С массивами та же ловушка: вызывающий код получит копию массива, но объекты в ней общие с оригиналом. В статье ещё разобрано, почему clone тут не помогает. 👍 - если было полезно 👉 @web_craft
🤖Podlodka Backend Crew: AI в бэкенд-задачах Демо с AI чаще всего выглядят одинаково: агент за десять минут пишет сервис с нуля. Вот только в работе бэкендера всё сложнее: легаси, рискованные миграции, неработающие тесты и ревью, которое становится бутылочным горлышком. 🗓 С 26 по 30 октября в сезоне Podlodka Backend Crew «AI в бэкенд-задачах» участники: • Научатся передавать агенту инженерные стандарты и правила, чтобы он писал так, как нужно, а не как получится • Соберут ревью, где несколько моделей независимо проверяют изменения, а финальное решение остаётся за человеком • Посмотрят, куда переезжает сложность миграции легаси, когда код пишет агент • Выяснят, где ломается мечта отдать миграцию агенту целиком • Разберут, какие антипаттерны тестов агенты уже успели перенять от разработчиков. Формат — пять дней живых Zoom-сессий утром и вечером, закрытое сообщество в Telegram и общение со спикерами. Будет много интерактива и практики. Базовый ликбез оставят за бортом — этот сезон для тех, кто уже пробует AI в реальных проектах. Присоединяйтесь с 26 октября! 🔗 Билеты и программа: https://podlodka.io/becrew
✨ Symfony 8.2: контроллер, который не запустится дважды одновременно Некоторые действия нельзя запускать параллельно: импорт каталога, оплату заказа, продажу последних билетов. Раньше блокировку через компонент Lock приходилось брать и снимать вручную прямо в контроллере. В Symfony 8.2 хватит атрибута #[Lock]. Пока запрос выполняется, второй с тем же ключом получит 409 Conflict, а с blocking: true подождёт. Ключ можно собрать из параметра маршрута, тогда у каждой конференции своя блокировка: #[Route('/conference/{conference}/tickets', methods: 'POST')] #[Lock('ticket-sale-{conference}', blocking: true, ttl: 10)] public function buyTicket(Conference $conference): Response { /* ... */ } Подвох в TTL: по умолчанию блокировка живёт 30 секунд и сама не продлевается. Если контроллер работает дольше, её захватит другой запрос. Продлить её или отпустить раньше можно, если принять LockInterface аргументом контроллера. 👍 - если было полезно 👉 @web_craft
🧩 Исключение, которое само объясняет, что сломалось Обычно ошибку бизнес-логики бросают как InvalidArgumentException с текстом. Это работает, пока не понадобится поймать именно её. Тот же класс летит ещё из десятка мест. В Spatie, где пишут хелпдеск There There на Laravel, заводят свой класс на каждую доменную ошибку. На каждую причину в нём есть статический метод. Код читается как фраза: throw CouldNotDeleteMessage::onlyNotesCanBeDeleted($message) Метод принимает типизированный $message. Поэтому текст ошибки без id и типа сообщения просто не собрать. Одна тонкость: такие классы помечают интерфейсом ShouldntReport, и они не попадают в трекер ошибок. Это ожидаемые ситуации от действий пользователя, а не баги, так что трекер не засоряется. Выглядит как оверинжиниринг ровно до первого раза, когда нужно поймать конкретную ошибку. 👍 - если было полезно 👉 @web_craft
⚡️ В сети начали массово сливать IT-курсы и книги известных онлайн школ Вот отсортированная база с тонной материала (постепенно пополняется): (363 видео, 87 книги) — Python (117 видео, 53 книги) — ВАЙБКОД (174 видео, 91 книги) — DevOps (415 видео, 68 книги) — Frontend (137 видео, 93 книги) — DS/ML (143 видео, 33 книги) — ИБ/Хакинг (352 видео, 89 книги) — С/С++/C (343 видео, 87 книги) — Java/QA (176 видео, 32 книги) — Git/Linux (167 видео, 53 книги) — PHP/1С (227 видео, 83 книги) — SQL/БД (114 видео, 77 книги) — Сисадмин (107 видео, 43 книги) — BA/SA (181 видео, 32 книги) — Go/Rust (167 видео, 43 книги) — Kotlin/Swift (112 видео, 24 книги) — Flutter (113 видео, 82 книги) — GameDev (183 видео, 37 книги) — Дизайн (136 видео, 33 книги) — PM/HR Скачивать ничего не нужно — все выложили в Telegram
🚀 TypeScript-типы, которые Laravel пишет сам Если бэкенд на Laravel, а фронтенд на TypeScript, каждый ответ сервера описан дважды. Переименовали поле в PHP, а фронт об этом не знает. В Spatie, авторах пакетов ниже, такие типы руками не пишут. Ответ описывают PHP-классом из пакета spatie/laravel-data с атрибутом #[TypeScript]. Пакет spatie/laravel-typescript-transformer собирает эти классы в файл generated.d.ts: ?string становится string | null, у enum появляется свой тип. Теперь после переименования свойства в PHP компилятор TypeScript подсвечивает каждое место на фронте, где оно используется. Сам по себе файл не обновляется, его пишет php artisan typescript:transform. В версии 2.6 у команды нет --watch, поэтому Spatie запускают её из своего небольшого плагина для Vite. В v3 режим --watch уже встроен. 👍 - если было полезно 👉 @web_craft
АЙТИШНИКИ БЕСПЛАТНОЕ ОБУЧЕНИЕ Проект «Terminal» стал крупнейшей библиотекой бесплатного образования. В одном канале собраны курсы, книги, полезные инструменты и практические тренажёры для всех разработчиков: • Практические курсы и задания • Книги и статьи известных авторов • Полезные инструменты и ресурсы • IT-новости и инсайды Обучение по всем направлениям: SQL, Python, ML, Frontend, PHP, C++, Go, Git, Linux, QA, Java, Vibe-coding, InfoSec и др. ⌨️ подписаться
🔴В 2025 году Россияне заработали 5,3 млрд рублей на арбитраже криптовалют По данным Минфин 32% граждан покинули своё место работы поскольку уже в первый месяц смогли заработать 250 000 рублей, а через 2-3 месяца с легкостью вышли на семизначные числа. Самое смешное, что лишь 8% россиян что-то слышали об этой профессии хотя все инструкции бесплатно лежат на канале Арбитраж Криптовалют Там объясняют, как имея телефон и доступ в интернет заработать свои первые $3000. Причём разжёвывают так доступно, что поймёт даже ребенок. Кстати, на канале сегодня вышла новая рабочая связка! Ссылка для входа — https://t.me/+GY-3aS-FN5c0Yjhi
Собрали статический сайт. А дальше что? Привычные хостинги для статических сайтов вроде Cloudflare Pages, Vercel и Netlify в России работают нестабильно. А опубликовать сайт где-то нужно, и желательно бесплатно. Поднимать ради лендинга VPS с nginx и certbot скучно. А сайт на вашем компьютере никто, кроме вас, не увидит. Выложить статический сайт на свой домен в WebShield можно в несколько простых шагов: ⚡️ сборка Hugo, Vite, Astro, Next.js и других: для каждого генератора есть готовая настройка (если пишете HTML вручную, этот шаг не нужен) 🚀 публикация из веб-интерфейса или через CLI, который заливает только изменённые файлы 🔁 автодеплой из GitHub Actions по пушу в main 📬 формы обратной связи работают без собственного бэкенда webshield sites publish www.example.com --dir ./dist Всё, сайт в интернете. 👉 Руководство по публикации статического сайта #hosting #hugo #vite #astro #nextjs #webshield
Scroll-driven анимации в CSS постепенно выходят на новый уровень. Раньше для анимаций при прокрутке почти всегда использовали JavaScript: Intersection Observer, GSAP, обработчики scroll-событий и сложную синхронизацию состояния. Теперь браузеры начинают предлагать нативный подход. Новые CSS-возможности вроде animation-timeline, view() и обсуждаемого animation-trigger позволяют запускать анимации в момент появления элемента во viewport — без постоянного «scrubbing», когда прогресс анимации жёстко привязан к позиции скролла. Что это даёт на практике: ➖меньше JavaScript-кода более стабильный FPS ➖снижение нагрузки на main thread ➖декларативное описание поведения прямо в CSS ➖упрощение поддержки UI-анимаций Подход особенно хорошо подходит для reveal-анимаций, появления карточек, секций лендингов и контентных интерфейсов. Фактически frontend постепенно движется к модели, где браузер сам управляет жизненным циклом анимаций, а разработчик описывает только правила и состояния. 👉 @web_craft
Комментарии
Комментариев пока нет
Комментарии пишут вошедшие — так видно, кто автор.
Войти