# 04. Контроллеры

[← Оглавление](./index.md)

---

## Тонкие контроллеры — вся логика в сервисах

**ПЛОХО — ЛОГИКА ПРЯМО В КОМПОНЕНТЕ**
```php
// result_modifier.php
if ($arParams['USE_FILTER'] == 'Y') {
    $filter = ['ACTIVE' => 'Y'];
    if ($_REQUEST['search']) {
        $filter['%NAME'] = $_REQUEST['search'];
    }
    $res = \CIBlockElement::GetList([], $filter, false, false, ['ID','NAME']);
    while ($el = $res->Fetch()) {
        $arResult['ITEMS'][] = $el;
    }
}
```

**ХОРОШО — СЕРВИС С КОРРЕКТНЫМ ORM API**
```php
class NewsService {
    public function getFilteredNews(array $criteria): array {
        $query = \Bitrix\Iblock\Elements\ElementNewsTable::query()
            ->setFilter(['=ACTIVE' => 'Y'])
            ->setSelect(['ID', 'NAME']);
        
        if (!empty($criteria['search'])) {
            $query->whereLike('NAME', '%' . $criteria['search'] . '%');
        }
        return $query->exec()->fetchAll();
    }
}

// В компоненте:
$arResult['ITEMS'] = (new NewsService())->getFilteredNews([
    'search' => $_REQUEST['search'] ?? '',
]);
```

---

## DTO для валидации входящих данных

**ПЛОХО — СЫРАЯ ВАЛИДАЦИЯ В КОНТРОЛЛЕРЕ**
```php
$title = $_POST['TITLE'] ?? '';
if (strlen($title) < 3) {
    $error = 'Заголовок слишком короткий';
} else {
    \CCrmLead::Add(['TITLE' => $title]);
}
```

**ХОРОШО — DTO + КОНТРОЛЛЕР**
```php
readonly class CreateLeadDto {
    public function __construct(
        public string $title,
        public ?string $phone = null,
        public ?string $email = null,
    ) {}
    
    /** @throws \InvalidArgumentException */
    public static function fromArray(array $data): self {
        if (empty($data['TITLE']) || mb_strlen($data['TITLE']) < 3) {
            throw new \InvalidArgumentException('Заголовок — не менее 3 символов');
        }
        return new self(
            title: trim($data['TITLE']),
            phone: $data['PHONE'] ?? null,
            email: $data['EMAIL'] ?? null,
        );
    }
}

class LeadController {
    public function __construct(private LeadCreationService $leadService) {}
    
    public function createAction(\Bitrix\Main\Request $request): \Bitrix\Main\Response {
        try {
            $dto = CreateLeadDto::fromArray($request->getPostList()->toArray());
            $lead = $this->leadService->create($dto);
            return new \Bitrix\Main\Response(
                json_encode(['id' => $lead->getId()]),
                'application/json'
            );
        } catch (\InvalidArgumentException $e) {
            return new \Bitrix\Main\Response(
                json_encode(['error' => $e->getMessage()]),
                'application/json',
                400
            );
        }
    }
}
```