惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Stack Overflow Blog
Stack Overflow Blog
Simon Willison's Weblog
Simon Willison's Weblog
L
LINUX DO - 最新话题
T
The Exploit Database - CXSecurity.com
C
CXSECURITY Database RSS Feed - CXSecurity.com
Scott Helme
Scott Helme
L
Lohrmann on Cybersecurity
博客园 - 【当耐特】
P
Privacy & Cybersecurity Law Blog
V2EX - 技术
V2EX - 技术
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Recent Commits to openclaw:main
Recent Commits to openclaw:main
IT之家
IT之家
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Cloudbric
Cloudbric
云风的 BLOG
云风的 BLOG
K
Kaspersky official blog
T
Threat Research - Cisco Blogs
Webroot Blog
Webroot Blog
A
Arctic Wolf
C
CERT Recently Published Vulnerability Notes
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
H
Hacker News: Front Page
G
Google Developers Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
P
Proofpoint News Feed
Last Week in AI
Last Week in AI
The Hacker News
The Hacker News
MyScale Blog
MyScale Blog
Vercel News
Vercel News
B
Blog RSS Feed
F
Fortinet All Blogs
Google DeepMind News
Google DeepMind News
A
About on SuperTechFans
Microsoft Security Blog
Microsoft Security Blog
腾讯CDC
博客园 - 三生石上(FineUI控件)
D
Docker
O
OpenAI News
有赞技术团队
有赞技术团队
MongoDB | Blog
MongoDB | Blog
H
Help Net Security
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Help Net Security
Help Net Security
AI
AI
T
Tor Project blog
Project Zero
Project Zero
H
Heimdal Security Blog
Latest news
Latest news
NISL@THU
NISL@THU

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Как навести порядок в запросах Laravel с помощью кастомных Query Builders
adobry96 · 2026-04-28 · via Все публикации подряд на Хабре

Уровень сложностиПростой

Время на прочтение5 мин

Охват и читатели13

Туториал

Написано уже не мало статей о том как всё же укротить строители запросов в Laravel чтобы вся команда понимала "Что происходит?" на проекте. Но даже в 2026 году я всё ещё встречаю проекты, где программисты продолжают писать запросы везде где только можно, но не в одном обозначенном месте.

Да я раньше тоже писал запросы в Serviсe, Controller, Model - да и вообще, где только приходилось. Но все мы развиваемся и хотим создать вокруг себя сообщество, я бы даже сказал команду, команду которая будет писать код понятный каждому.

Поэтому я решил снова осветить данную тематику. В данной статье я покажу на личном примере, где я храню эти Builder и как использую. Также мы рассмотрим связку с Repository чтобы ваш код стал ещё лучше.

Рассмотрим банальный пример на модели User. Но мы с вами сделаем так чтобы мы могли пользоваться нашими Builder быстро и легко.

Для этого я предлагаю первым шагом реализовать trait. Чтобы не писать каждый раз в модели данный код в будущем.

Сразу хочу сказать эта реализация имеет некоторые ограничения вы можете перенести данные метод в модель для которой пишите Builder.

<?php

declare(strict_types=1);

namespace App\Models\Traits;

use Illuminate\Database\Eloquent\Builder;

trait HasCustomEloquentBuilder
{
    /**
     * Путь к папке с билдерами
     */
    private const string ELOQUENT_BUILDER_PATH = 'EloquentBuilders';

    /**
     * Метод для создания билдера
     * 
     * @return Builder<static>
     */
    public function newEloquentBuilder($query): Builder
    {
        /** @var Builder<static> */
        return new (str_replace('Models', self::ELOQUENT_BUILDER_PATH, static::class).'Builder')($query);
    }
}

Далее можем создать папку EloquentBuilders внутри app и далее создать там наш первый кастомный Builder.

<?php

declare(strict_types=1);

namespace App\EloquentBuilders;

use App\Models\User;
use Illuminate\Database\Eloquent\Builder;

/**
 * @extends Builder<User>
 */
final class UserBuilder extends Builder
{
    public function whereActive(bool $isActive = true): self
    {
        return $this->where('is_active', $isActive);
    }
}

Далее мы можем создать модель и перейти к использованию нашего trait.

<?php

declare(strict_types=1);

namespace App\Models;

use App\EloquentBuilders\UserBuilder;
use App\Traits\HasCustomEloquentBuilder;
use Database\Factories\UserFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Sanctum\HasApiTokens;

/**
 * User model
 *
 * @property int $id
 * @property string $name
 * @property bool $is_active
 *
 * @method static UserBuilder query()
 */
class User extends Authenticatable
{
    use HasApiTokens;
    use HasCustomEloquentBuilder;

    /** @use HasFactory<UserFactory> */
    use HasFactory;

    protected $fillable = [
        'name',
        'is_active',
    ];
}

Обычно после этого все идут в контроллер и пишут там примерно такое:

<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

use App\UseCases\FetchUserList\FetchUseListHandler;
use Illuminate\Http\JsonResponse;

class UserController
{
    public function index(): JsonResponse
    {
        return new JsonResponse(User::query()->whereActive()->get());
    }
}

Но согласитесь, такой вариант реализации в будущем нас мало устроит, даже если у вас там появится пагинация. Сейчас мы говорим про архитектуру)

Нам надо пойти дальше и реализовать репозиторий, куда мы вынесем с вами наш код.

<?php

declare(strict_types=1);

namespace App\Repositories;

use App\Models\User;
use App\Repositories\Contracts\UserRepositoryInterface;
use Illuminate\Support\Collection;

class UserRepository implements UserRepositoryInterface
{
    /**
     * @return Collection<int, User>
     */
    public function fetchListActive(): Collection
    {
        /** @var Collection<int, User> */
        return User::query()->whereActive()->get();
    }
}

Теперь всё стало намного лучше и контроллер может выглядеть вот так:

<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

use App\Repositories\Contracts\UserRepositoryInterface;
use Illuminate\Http\JsonResponse;

class UserController
{
    public function index(UserRepositoryInterface $repository): JsonResponse
    {
        return new JsonResponse($repository->fetchListActive());
    }
}

Но я прибегну к использованию UseCase и напишу код ещё чище. Реализацию полной версии вы сможете посмотреть в проекте примере: https://github.com/palachX/laravel-usecase

<?php

namespace App\Http\Controllers\Api;

use App\UseCases\FetchUserList\FetchUseListHandler;
use Illuminate\Http\JsonResponse;

class UserController
{
    public function index(FetchUseListHandler $fetchUseListHandler): JsonResponse
    {
        return new JsonResponse($fetchUseListHandler->handle());
    }
}

В последствии вы можете добавить разные фильтры в ваш Builder и это всё будет в ручном управлении и в одном месте. Если вдруг вам надо будет написать чистые запросы у вас есть прослойка репозитория. Если вдруг какие-то join с поисками то пишите это в Builder.

Пример метода фильтрации и более сложных запросов.

<?php

declare(strict_types=1);

namespace App\EloquentBuilders;

use App\Models\User;
use Illuminate\Database\Eloquent\Builder;

/**
 * @extends Builder<User>
 */
final class UserBuilder extends Builder
{
    public function whereActive(bool $isActive = true): self
    {
        return $this->where('is_active', $isActive);
    }

    public function filter(?UserFilterData $filter = null): self
    {
        if ($filter === null) {
            return $this;
        }

        if ($filter->name !== null) {
            $this->where('name', 'like', "%{$filter->name}%");
        }

        if ($filter->isActive !== null) {
            $this->where('is_active', $filter->isActive);
        }

        return $this;
    }

    public function withOrdersCount(): self
    {
        return $this->withCount('orders');
    }

    public function whereHasActiveSubscription(): self
    {
        return $this->whereHas('subscriptions',
            fn($q) => $q->where('active', true)
        );
    }
}

Главное правило - чтобы это всё было в одном месте.

Итог

Со временем я пришёл к тому, что держать запросы где попало - плохая идея. Сначала это кажется удобным, но по мере роста проекта превращается в хаос: одинаковые условия копируются, логика размазывается по коду, а разобраться “что происходит” становится всё сложнее.

Кастомные Query Builders для меня стали точкой, где вся работа с запросами наконец-то собралась в одном месте. В связке с Repository и UseCase это дало понятную структуру: где данные, где запросы, где бизнес-логика.

Мне это дало несколько вещей:

1. Я всегда знаю, где искать нужный запрос
2. Могу переиспользовать фильтры без копипаста
3. Код стал читаться проще и быстрее

Но важно понимать - это не серебряная пуля. Если проект маленький, можно спокойно жить и без этого. Такой подход начинает реально окупаться, когда проект растёт и в нём работает несколько человек.

Для себя я сделал простой вывод: лучше один раз договориться о структуре и придерживаться её, чем потом разбираться в разбросанных по проекту where.

Полезные ссылки:

Проект пример: https://github.com/palachX/laravel-usecase
Статья которую я прочитал в 2024: https://martinjoo.dev/build-your-own-laravel-query-builders
Статья перевод: https://laravel.su/p/kastomnye-query-builders-v-laravel
Статья по UseCase: https://habr.com/ru/articles/1012988/