<!DOCTYPE html>
<html class="h-100" data-bs-theme="light" data-mantine-color-scheme="light" lang="ru" prefix="og: https://ogp.me/ns#">
<head>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<link crossorigin="true" href="https://cdn.hexlet.io" rel="preconnect">
<link href="https://mc.yandex.ru" rel="preconnect">
<meta content="aa2vrdtq64dub8knuf83lwywit311w" name="facebook-domain-verification">
<link href="/favicon.ico" rel="icon" sizes="any">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<link href="/apple-touch-icon.png" rel="apple-touch-icon">
<link href="/manifest.webmanifest" rel="manifest">
<script>
//<![CDATA[
window.gon={};gon.ym_counter="25559621";gon.is_bot=true;gon.applications={};gon.current_user={"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26 18:40:33 UTC","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false};gon.token="sM2SEM4J_OkIL-W-jUGUq35do61va0UOWurVjPoBMnxfHFknPHdRib5swSaBTmTcvlSOB2dcu6znCk_YqAbVEg";gon.locale="ru";gon.language="ru";gon.theme="light";gon.rails_env="production";gon.mobile=false;gon.google={"analytics_key":"UA-1360700-51","optimize_key":"GTM-5QDVFPF"};gon.captcha={"google_v3_site_key":"6LenGbgZAAAAAM7HbrDbn5JlizCSzPcS767c9vaY","yandex_site_key":"ysc1_Vyob5ZPPUdPBsu0ykt8bVFdzsfpoVjQChLGl2b4g19647a89","verification_failed":null};gon.social_signin=false;gon.typoreporter_google_form_id="1FAIpQLSeibfGq-KvWQ2Fyru-zkFFRVTLBuzXAHAoEyN1p49FtDmNoNA";
//]]>
</script>
<meta charset="utf-8">
<title>CRUD | Веб-разработка на PHP</title>
<meta name="description" content="CRUD / Веб-разработка на PHP: Знакомимся с типичными операциями веб-приложений">
<link rel="canonical" href="https://ru.hexlet.io/courses/php-mvc/lessons/crud/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="CRUD">
<meta property="og:title" content="Веб-разработка на PHP">
<meta property="og:description" content="CRUD / Веб-разработка на PHP: Знакомимся с типичными операциями веб-приложений">
<meta property="og:url" content="https://ru.hexlet.io/courses/php-mvc/lessons/crud/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="69Oy4qlMVGOMUFqoSeKR0P5E2w0MxG1zScjlEXCzNu8EAnnVWzL5AzoTfjBF7WGnPk32pwTzk9H0KH9FIrTRgQ" />
<script src="/vite/assets/inertia-DfXos102.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/preload-helper-BJ4cLWpC.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ahoy-DrlRQ-1D.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/analytics-cb8xch9l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Surface-DL2bpZA-.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/extends-C-EagtpE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/inheritsLoose-BBd-DCVI.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/objectWithoutPropertiesLoose-DRHXDhjp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/index.esm-DAqKOkZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Button-CGPUux8l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/CloseButton-D1euiPao.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Group-BX48WcuU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Loader-BQEY8g6v.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Modal-Cy3HByv7.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/OptionalPortal-1Hza5P2w.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Stack-CtjJzfw4.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Textarea-Ck64llAy.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/DirectionProvider-Dc9zdUke.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/events-DJQOhap0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-reduced-motion-D2owz4wa.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-disclosure-zKtK5W1r.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-hotkeys-Cnc_Rwkb.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/random-id-DOQyszCZ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/exports-C_MrNx_T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<link rel="stylesheet" href="/vite/assets/application-BqhCP46M.js" />
<script src="/vite/assets/application-Df9RExpe.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/autocomplete-VMNbxKGl.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/createPopper-C3aM9r1M.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/js.cookie-D1-O8zkX.js" as="script" crossorigin="anonymous"><link rel="stylesheet" href="/vite/assets/application-C8HjmMaq.css" media="screen" />
<script>
window.ym = function(){(ym.a=ym.a||[]).push(arguments)};
window.addEventListener('load', function() {
setTimeout(function() {
ym.l = 1*new Date();
ym(window.gon.ym_counter, "init", {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true
});
// Загружаем скрипт
var k = document.createElement('script');
k.async = 1;
k.src = 'https://mc.yandex.ru/metrika/tag.js';
document.head.appendChild(k);
ym(window.gon.ym_counter, 'getClientID', function(clientID) {
window.ymClientId = clientID;
});
}, 1500);
});
</script>
<!-- Google Tag Manager - deferred -->
<script>
// dataLayer stub сразу — пуши работают до загрузки скрипта
window.dataLayer = window.dataLayer || [];
// Сам скрипт — отложенно после load
window.addEventListener('load', function() {
setTimeout(function() {
dataLayer.push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
var j = document.createElement('script');
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-WK88TH';
document.head.appendChild(j);
}, 1500);
});
</script>
<!-- End Google Tag Manager -->
</head>
<body>
<noscript>
<div>
<img alt="" src="https://mc.yandex.ru/watch/25559621" style="position:absolute; left:-9999px;">
</div>
</noscript>
<header class="sticky-top bg-body">
<nav class="navbar navbar-expand-lg">
<div class="container-xxl">
<a class="navbar-brand" href="/"><img alt="Логотип Хекслета" height="24" src="https://ru.hexlet.io/vite/assets/logo_ru_light-BpiEA1LT.svg" width="96">
</a><button aria-controls="collapsable" aria-expanded="false" aria-label="Меню" class="navbar-toggler border-0 mb-0 mt-1" data-bs-target="#collapsable" data-bs-toggle="collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsable">
<ul class="navbar-nav mb-lg-0 mt-lg-1">
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
Все курсы
<span class="bi bi-chevron-down align-middle ms-1"></span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item d-flex py-2" href="/courses"><div class="fw-bold me-auto">Все что есть</div>
<div class="text-muted">117</div>
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные категории</b>
</li>
<li>
<a class="dropdown-item py-2" href="/courses_devops">Курсы по DevOps
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_data_analytics">Курсы по аналитике данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_programming">Курсы по программированию
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_testing">Курсы по тестированию
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные курсы</b>
</li>
<li>
<a class="dropdown-item py-2" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/go">Go-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/java">Java-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/python">Python-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/qa-auto-engineer-java">Автоматизатор тестирования на Java
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/data-analytics">Аналитик данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/frontend">Фронтенд-разработчик
</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
О Хекслете
<span class="bi bi-chevron-down align-middle"></span>
</button>
<ul class="dropdown-menu bg-body">
<li>
<a class="dropdown-item py-2" href="/pages/about">О нас
</a></li>
<li>
<a class="dropdown-item py-2" href="/blog">Блог
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/hse-research" role="button">Результаты (Исследование)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://career.hexlet.io" role="button">Хекслет Карьера
</span></li>
<li>
<a class="dropdown-item py-2" href="/testimonials">Отзывы студентов
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://t.me/hexlet_help_bot" role="button">Поддержка (В ТГ)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/referal-program/?promo_creative=priglasite-druzei&promo_name=referal-program&promo_position=promo_position&promo_start=010724&promo_type=link" role="button">Реферальная программа
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/certificate" role="button">Подарочные сертификаты
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://hh.ru/employer/4307094" role="button">Вакансии
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://b2b.hexlet.io" data-target="_blank" role="button">Компаниям
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexly.ru/" data-target="_blank" role="button">Колледж
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexlyschool.ru/" data-target="_blank" role="button">Частная школа
</span></li>
</ul>
</li>
<li><a class="nav-link" href="/subscription/new">Подписка</a></li>
</ul>
<ul class="navbar-nav flex-lg-row align-items-lg-center gap-2 ms-auto">
<li>
<a class="nav-link" aria-label="Переключить тему" href="/theme/switch?new_theme=dark"><span aria-hidden="true" class="bi bi-moon"></span>
</a></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="/u/new" role="button"><span>Регистрация</span>
</span></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="https://ru.hexlet.io/session/new" role="button"><span>Вход</span>
</span></li>
</ul>
</div>
</div>
</nav>
</header>
<div class="x-container-xxxl">
</div>
<main class="mb-6 min-vh-100 h-100">
<link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzk5MiwicHVyIjoiYmxvYl9pZCJ9fQ==--e9d0f30948ea766a7e6bc3e3d56c192344d45fb8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Programming-cuate%20(1).png"/><link rel="preload" as="image" href="/vite/assets/development-BVihs_d5.png"/><div id="app" data-page="{"component":"web/courses/lessons/theory_unit","props":{"errors":{},"locale":"ru","language":"ru","httpsHost":"https://ru.hexlet.io","host":"ru.hexlet.io","colorScheme":"light","auth":{"user":{"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26T18:40:32.992Z","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false}},"cloudflareTurnstileSiteKey":"0x4AAAAAAA15KmeFXzd2H0Xo","vkIdClientId":"51586979","yandexIdClientId":"88d071f1d3384eb4bd1deb37910235c7","formAuthToken":"3bR5JSkNAfVCM_KzO-acF0p2T8_RXSucLkAj0mFgw6wyZbIS23OslfRw1is36Wxgin9iZdlq1T6ToLmGM2ckwg","topics":[{"id":49548,"title":"Не совсем понял, почему в РУ $post['name'] оборачивается в htmlspecialchars, а $post['id'] и $post['body'] нет? Ну id допустим формируется на сервере, а body почему не обернут?","plain_title":"Не совсем понял, почему в РУ $post['name'] оборачивается в htmlspecialchars, а $post['id'] и $post['body'] нет? Ну id допустим формируется на сервере, а body почему не обернут? ","creator":{"public_name":"Maxim Balyura","id":244081,"is_tutor":false},"comments":[{"creator":{"public_name":"Roman Ashikov","id":226258,"is_tutor":true},"id":106282,"body":"Приветствую, Максим!\n\nСпасибо, что обратили внимание! Исправил этот момент в решении учителя. Можно нажать на кнопку Сброс, чтобы обновить упражнение. Только не забудьте сохранить решение, так как при сбросе оно удаляется.","topic_id":49548}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":42262,"title":"В решении учителя: \n```\n<h1><?= htmlspecialchars($post['name']) ?></h1>\n```\n\nВозможно htmlspecialchars тут лишний, с учетом того, что эти данные берутся из файла или БД.","plain_title":"Возможно htmlspecialchars тут лишний, с учетом того, что эти данные берутся из файла или БД. ","creator":{"public_name":"Артур Крючок","id":253055,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":92063,"body":"А вот это не должно сбивать с истинного пути. База данных не является доверенным источником данных. Там вполне может оказаться все что угодно и невозможно гарантировать безопасность.","topic_id":42262},{"creator":{"public_name":"Артур Крючок","id":253055,"is_tutor":false},"id":92130,"body":"Хм, следуя такой логике тело <?= $post['body'] ?> так же стоит пропустить через htmlspecialchars. ","topic_id":42262},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":92206,"body":"А вот тут история чуть хитрее на самом деле. Дело в том что если у нейма гарантированно не может быть внутри html, то в теле поста он не просто может быть, он и будет и скорее всего прямо в базе. В таких ситуациях (они относительно редки), мы должны опираться на весь процесс. Обычно при сохранении данных и редакторов генерирующих html, производится санитизация (sanitize), в процессе которой удаляются всякие опасные штуки, но оставляется безопасный html. И вот только в этом случае выводится все без доп обработки.","topic_id":42262}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":49894,"title":"Проверка прошла без условия из задачи <<Если поста не существует, то страница обработчик должен вернуть код ответа 404 и текст Page not found. >> Так и должно быть или я не понимаю чего-то?","plain_title":"Проверка прошла без условия из задачи <<Если поста не существует, то страница обработчик должен вернуть код ответа 404 и текст Page not found. >> Так и должно быть или я не понимаю чего-то? ","creator":{"public_name":"Александр Тамразов","id":282309,"is_tutor":false},"comments":[{"creator":{"public_name":"Roman Ashikov","id":226258,"is_tutor":true},"id":107015,"body":"Приветствую, Александр!\n\nНужно больше контекста, чтобы ответить на ваш вопрос. Сохраните, пожалуйста, решение и отправьте ссылку на код-ревью в комментарии к топику. О том как это сделать рассказывается в статье — https://help.hexlet.io/article/40-code-review","topic_id":49894}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":28605,"title":"В чём суть использования репозитория? это прослойка для того чтобы упаковать сущность перед персистом в БД?","plain_title":"В чём суть использования репозитория? это прослойка для того чтобы упаковать сущность перед персистом в БД? ","creator":{"public_name":"Zahar Steblovskiy","id":111870,"is_tutor":false},"comments":[{"creator":{"public_name":"Zahar Steblovskiy","id":111870,"is_tutor":false},"id":61692,"body":"Я прочитал по ссылке, и пожалуй переведу своими словами то, что, как мне показалось, написано между строк:\n\nРепозиторий для сущности - еще один слой между тем, что занимается собственно сохранением в бд (pdo, orm) и тем, что представляет саму сущность. Оно по сути говорит \"как\" достать сущность или \"как\" её сохранить.\n\nВедь БЫВАЕТ ТАК, что нужно перед сохранением(доставанием) данных в таблицу:\n1. как-то поменять сущность (сделать тот же serialise в качестве примера)\n2. отображать НЕ 1в1 на таблицу, а делать сущность собирательной перед сохранения в таблицу бд - например обратиться к доп.данным\n3. собрать сущность из других классов, и комплексно уже сохранять\n4. доставать из нескольких таблиц БД и собирать сущность\nи т.д. и т.п.\n\nверно?\n","topic_id":28605},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":61694,"body":"> еще один слой между тем, что занимается собственно сохранением в бд (pdo, orm) и тем, что представляет саму сущность. \n\nНе совсем. Репозитории это часть ORM. Поверх ORM такие штуки уже не наворачивают.\n\n> верно?\n\nНет. Семантика репозитория в том, что он создает ощущение хранения сущностей в памяти (у фаулера это подчеркивается). Мы туда кладем сущность и получаем ее в том же виде, в котором положили. Все остальное это уже разного рода механизмы маппинга, которые могут быть реализованы где угодно и как угодно.","topic_id":28605},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":61678,"body":"Репозиторий это именно та штука, которая кладет сущность в бд. https://martinfowler.com/eaaCatalog/repository.html","topic_id":28605}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":32117,"title":"Непонятно, как в задании быть с репозиторием? Создавать его как стандартный объект Repository? Или он тут вообще не нужен?\n","plain_title":"Непонятно, как в задании быть с репозиторием? Создавать его как стандартный объект Repository? Или он тут вообще не нужен? ","creator":{"public_name":"Денис Потехин","id":239601,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":69795,"body":"Поправил ридми.\n\n","topic_id":32117},{"creator":{"public_name":"Денис Потехин","id":239601,"is_tutor":false},"id":69792,"body":"Понимаю, что когда ломаешь голову лучше откладывается и потом толк есть. Понимаю, что дистанционно не объяснишь все, а с помощью фидбека можно строить наиболее продуктивные уроки.\nНо это перебор. \n\nСначала сутки ломал голову над репозиторием. Теперь задание поменяли, спасибо, только в задание ясно написано реализовать маршрут posts/slug. Я сижу полдня бьюсь, не работает. Лезу в решение, смотрю там posts/id.\n\nКороче давайте первопроходцам молоко за вредность, нервные клетки не восстанавливаются..\n","topic_id":32117},{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":69777,"body":"В этой практике посты доступны напрямую через переменную `$posts`, в задании об этом упоминается.\n\nНо я сейчас глянул и понял что получилось отлично от теории. Репозиторий внедрил. Если сбросить прогресс минут через 5, то он там будет.","topic_id":32117},{"creator":{"public_name":"Денис Потехин","id":239601,"is_tutor":false},"id":69711,"body":"\nПонял, что он нужен, но как с ним работать? В теории используют готовый класс, тут такого нет. \n**Денис Потехин**, ","topic_id":32117}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":47071,"title":"Добрый день, не получается реализовать переход по страницам \"Вперед\" и \"Назад\", подскажите куда копать плз, вот код https://ru.hexlet.io/code_reviews/311393","plain_title":"Добрый день, не получается реализовать переход по страницам \"Вперед\" и \"Назад\", подскажите куда копать плз, вот код https://ru.hexlet.io/code_reviews/311393 ","creator":{"public_name":"","id":141768,"is_tutor":false},"comments":[{"creator":{"public_name":"Roman Ashikov","id":226258,"is_tutor":true},"id":101450,"body":"Приветствую!\n\nВзгляните на то место, где вы используете `array_slice()`. Эта функция принимает вторым параметром `$offset`, который показывает с какого элемента от начала массива нужно взять срез. Очевидно, что если у нас страница номер 2 и на странице мы должны выводить по 5 элементов, то начинать нужно не с нуля, а с элемента с индексом 5. Для страницы номер 3 это будет уже элемент с индексом 10. Подумайте, каким образом рассчитать offset для любой страницы. Поэкспериментируйте с `array_slice()` на repl.it.\n\nВ шаблоне нужно реализовать ссылки на предыдущую и следующую страницы. Предыдущая это ведь `$page - 1`, верно? ","topic_id":47071}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":29465,"title":"В решении учителя в навигации по списку постов не используется \"per\" (собственно этот параметр даже не передается в шаблон, хотя в обработчике присутствует), поэтому если \"per\" будет нестандартным (например 2), то навигация перестанет правильно работать. Мне кажется этот момент логично было бы подправить.","plain_title":"В решении учителя в навигации по списку постов не используется \"per\" (собственно этот параметр даже не передается в шаблон, хотя в обработчике присутствует), поэтому если \"per\" будет нестандартным (например 2), то навигация перестанет правильно работать. Мне кажется этот момент логично было бы подправить. ","creator":{"public_name":"Антон Попов","id":202007,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":63922,"body":"Я его решил убрать чтобы не усложнять решение.","topic_id":29465}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":43214,"title":"Добрый день! Тесты хотят видеть в ответе строку определенного вида для запроса следующей и предыдущей страницы.\n```\n$response2 = $this->client->get('/posts?page=2');\n$body2 = $response2->getBody()->getContents();\n$this->assertStringContainsString('?page=1', $body2);\n$this->assertStringContainsString('?page=3', $body2);\n```\nЯ сделал немного [иначе](https://ru.hexlet.io/code_reviews/271195), соответственно тесты не пропускали, может быть стоит скорректировать тесты, хотя всегда можно сделать по-другому. Человек, дошедший до этого упражнения, должен уметь \"проходить\" тесты :)) И кстати, в задаче сказано \"Переключение между страницами нужно сделать двумя кнопками...\", а не ссылками.","plain_title":"Добрый день! Тесты хотят видеть в ответе строку определенного вида для запроса следующей и предыдущей страницы. $response2 = $this->client->get('/posts?page=2'); $body2 = $response2->getBody()->getContents(); $this->assertStringContainsString('?page=1', $body2); $this->assertStringContainsString('?page=3', $body2); Я сделал немного иначе (https://ru.hexlet.io/code_reviews/271195), соответственно тесты не пропускали, может быть стоит скорректировать тесты, хотя всегда можно сделать по-другому. Человек, дошедший до этого упражнения должен уметь \"проходить\" тесты :)) И кстати, в задаче сказано \"Переключение между страницами нужно сделать двумя кнопками...\", а не ссылками. ","creator":{"public_name":"Дмитрий Темин","id":202664,"is_tutor":false},"comments":[{"creator":{"public_name":"Stanislav Dzisiak","id":212236,"is_tutor":true},"id":94183,"body":"Приветствую, Дмитрий!\n\nСогласовал описание задачи с решением и тестами. Спасибо, что обратили внимание.","topic_id":43214},{"creator":{"public_name":"Дмитрий Темин","id":202664,"is_tutor":false},"id":93974,"body":"В файле `index.phtml` удалите строки после `<!--For tests only-->`\nтогда тесты будут ругаться на то, что ответ не содержит `'?page=1'`\n","topic_id":43214},{"creator":{"public_name":"Александр Максин","id":154813,"is_tutor":false},"id":93970,"body":"Доброго времени суток, Дмитрий. Можете уточнить, какую ошибку выдавали вам тесты? Запустил ваш код - тесты прошли(если не считать ошибок линтера).","topic_id":43214},{"creator":{"public_name":"Александр Максин","id":154813,"is_tutor":false},"id":93984,"body":"Оригинальное решение)","topic_id":43214}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":43861,"title":"Скажите пожалуйста, в самостоятельной работе, когда строим маршрут '/users/{id}'. 'id' тут это именно 'id', типа такого '5ee39cdfd7276', или же все таки имя пользователя nickname? Ясно, что я могу делать на свое усмотрение, но все же, что тут предполагается. Если пользователь не найден, нужно вывести ошибку 404, имеется в виду, я ввожу в адресной строку /users/ivan, и если такого пользователя нет, выводится Not found, но будет как то несуразно вводить адрес /users/5ee39cdfd7276 или даже более короткое число. То есть тут используется именно имя. Но в примерах урока используется как будто именно id и поиск в базе ведется по id. ","plain_title":"Скажите пожалуйста, в самостоятельной работе, когда строим маршрут '/users/{id}'. 'id' тут это именно 'id', типа такого '5ee39cdfd7276', или же все таки имя пользователя nickname? Ясно, что я могу делать на свое усмотрение, но все же, что тут предполагается. Если пользователь не найден, нужно вывести ошибку 404, имеется в виду, я ввожу в адресной строку /users/ivan, и если такого пользователя нет, выводится Not found, но будет как то несуразно вводить адрес /users/5ee39cdfd7276 или даже более короткое число. То есть тут используется именно имя. Но в примерах урока используется как будто именно id и поиск в базе ведется по id. ","creator":{"public_name":"Вильдан Фазлыев","id":252958,"is_tutor":false},"comments":[{"creator":{"public_name":"Nikolai Gagarinov","id":104929,"is_tutor":true},"id":95288,"body":"Добрый день. ID пользоваться может любым. Как обычное целое число, так UUID. Все зависит от того, где это используется.\nТ.е. если нам нужны красивые урлы, чтобы по ссылке можно было понять, что внутри находится, мы можем подставлять ник пользователя.\nНо, надо учитывать, что ID должен быть уникальным, т.е. у вас не может быть 2 Ивана с например с никами ivan и Ivan. \nВ `/users/{id}` может быть что угодно, переменную пути стоит называть по подходящему смыслу. Если используете ник пользователя, то будет например `/users/{username}`","topic_id":43861}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}},{"id":48950,"title":"Добрый день! У меня появилось два общих вопроса:\n1.как принято и как лучше - нужно передавать в шаблон готовые значения переменных или это нормально выполнять проверки и доп. вычисления в файле самого шаблона? вроде бы удобно, но не является это мешаниной html+php? вроде бы это плохо?\n2. никак не могу привыкнуть к коротким записям типа <?= и ( ? : ), нужно переучиваться или делать как удобнее и понятнее?\n","plain_title":"Добрый день! У меня появилось два общих вопроса: 1.как принято и как лучше - нужно передавать в шаблон готовые значения переменных или это нормально выполнять проверки и доп. вычисления в файле самого шаблона? вроде бы удобно, но не является это мешаниной html+php? вроде бы это плохо? 2. никак не могу привыкнуть к коротким записям типа <?= и ( ? : ), нужно переучиваться или делать как удобнее и понятнее? ","creator":{"public_name":"Динара Хажипова","id":312372,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":105179,"body":"1. Вычисления выполнять точно не нужно (за исключением примитивных, типа умножить на 100 чтобы вывести процент), а вот проверки это совершенно нормально. Наша задача подготавливать нужный шаблон и сделать это без условных конструкций часто невозможно. Это не проблема кода, это сама суть задачи.\n2. Короткие гораздо удобнее, но! В дальше вы узнаете что встроенным шаблонизатором в PHP никто не пользуется) Все равно реальный код пишется на каком нибудь Twig или Blade. Современные шаблонизаторы рассматриваются позже.\n","topic_id":48950}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"CRUD","entity_url":null,"active":true}}],"lesson":{"exercise":{"id":762,"slug":"php_mvc_crud_exercise","name":null,"state":"active","kind":"exercise","language":"php","locale":"ru","has_web_view":true,"has_test_view":false,"reviewable":true,"readme":"## public/index.php\n\nРеализуйте следующие обработчики:\n\n* Список постов: */posts*\n* Конкретный пост */posts/:id* (например */posts/3*)\n\nПосты находятся в репозитории `$repo`. Каждый пост содержит внутри себя четыре поля:\n\n* id\n* name\n* body\n* slug\n\nКаждый пост из списка ведет на страницу конкретного поста. Список нужно вывести с пейджингом по 5 постов на странице. На первой странице первые пять постов, на второй вторые пять и так далее. Переключение между страницами нужно сделать с помощью двух ссылок: *назад* и *вперед*. То какая сейчас страница открыта, определяется параметром *page*. По умолчанию загружается первая страница.\n\nСтраница конкретного поста отображает данные поста и позволяет вернуться на список. Если поста не существует, то страница обработчик должен вернуть код ответа 404 и текст *Page not found*.\n\n## templates/posts/index.phtml\n\nВыведите список добавленных постов. Каждый пост это имя, которое представлено ссылкой ведущей на отображение (show).\n\n## templates/posts/show.phtml\n\nВывод информации о конкретном посте. Выводить только имя и содержимое поста.\n\n### Подсказки\n\n* Для реализации пейджинга понадобится извлечь все посты из репозитория с помощью метода `all()`.\n","prepared_readme":"## public/index.php\n\nРеализуйте следующие обработчики:\n\n* Список постов: */posts*\n* Конкретный пост */posts/:id* (например */posts/3*)\n\nПосты находятся в репозитории `$repo`. Каждый пост содержит внутри себя четыре поля:\n\n* id\n* name\n* body\n* slug\n\nКаждый пост из списка ведет на страницу конкретного поста. Список нужно вывести с пейджингом по 5 постов на странице. На первой странице первые пять постов, на второй вторые пять и так далее. Переключение между страницами нужно сделать с помощью двух ссылок: *назад* и *вперед*. То какая сейчас страница открыта, определяется параметром *page*. По умолчанию загружается первая страница.\n\nСтраница конкретного поста отображает данные поста и позволяет вернуться на список. Если поста не существует, то страница обработчик должен вернуть код ответа 404 и текст *Page not found*.\n\n## templates/posts/index.phtml\n\nВыведите список добавленных постов. Каждый пост это имя, которое представлено ссылкой ведущей на отображение (show).\n\n## templates/posts/show.phtml\n\nВывод информации о конкретном посте. Выводить только имя и содержимое поста.\n\n### Подсказки\n\n* Для реализации пейджинга понадобится извлечь все посты из репозитория с помощью метода `all()`.\n","has_solution":true,"entity_name":"CRUD"},"units":[{"id":2443,"name":"theory","url":"/courses/php-mvc/lessons/crud/theory_unit"},{"id":2678,"name":"quiz","url":"/courses/php-mvc/lessons/crud/quiz_unit"},{"id":2457,"name":"exercise","url":"/courses/php-mvc/lessons/crud/exercise_unit"}],"links":[{"id":428367,"name":"RESTFull Routing","url":"http://rusrails.ru/routing#resource-routing-the-rails-default"},{"id":428368,"name":"Scaffolding","url":"https://en.wikipedia.org/wiki/Scaffold_(programming)"}],"ordered_units":[{"id":2443,"name":"theory","url":"/courses/php-mvc/lessons/crud/theory_unit"},{"id":2678,"name":"quiz","url":"/courses/php-mvc/lessons/crud/quiz_unit"},{"id":2457,"name":"exercise","url":"/courses/php-mvc/lessons/crud/exercise_unit"}],"id":1179,"slug":"crud","state":"approved","name":"CRUD","course_order":395,"goal":"Знакомимся с типичными операциями веб-приложений","self_study":"1. Приведите маршруты и их имена в соответствии с указанной выше схемой\n2. Переделайте получение пользователей так, чтобы данные о пользователях брались из файла. Не забудьте предотвратить XSS\n","theory_video_provider":null,"theory_video_uid":null,"theory":"Несмотря на огромное число разнообразных сайтов, практически всю веб-разработку можно свести к [CRUD](https://ru.wikipedia.org/wiki/CRUD)-операциям.\n\nCRUD означает четыре стандартные операции над любой сущностью: создание, чтение, обновление и удаление. Например, в случае с пользователем можно составить такое соответствие:\n\n**Create**\n\n* Регистрация\n\n**Read**\n\n* Просмотр профиля пользователями сайта\n* Просмотр пользователя в административном интерфейсе\n\n**Update**\n\n* Обновление личных данных\n* Смена емейла\n* Смена пароля\n\n**Delete**\n\n* Удаление\n\nТочно так же можно расписать действия над любыми другими ресурсами, фотографиями пользователя, его друзьями, сообщениями.\n\nСоздание полного круда включает в себя следующие действия:\n\n* Создание сущности в коде (как правило, класса)\n* Добавление таблицы в базу\n* Написание тестов на обработчики\n* Добавление обработчиков\n* Добавление шаблонов\n\nНовички тратят на создание такого круда не один день. У опытного разработчика в прокачанном фреймворке этот процесс занимает максимум часы. Slim не предоставляет средств автоматизации, поэтому придется многое делать руками. В целях обучения это оправданно, но в промышленной разработке, то что может быть автоматизировано, должно быть автоматизировано.\n\nНиже мы пройдемся по всему процессу создания круда пользователя за исключением работы с базой данных и тестов. Начнем с роутинга.\n\nПолный круд пользователя включает минимум семь маршрутов. Их может быть больше, так как любое из действий может повторяться не один раз.\n\n| Метод | Маршрут | Шаблон | Описание |\n|-----------|------------------|-------------------|------------------------------------|\n| GET | /users | users/index.phtml | Список пользователей |\n| GET | /users/{id} | users/show.phtml | Профиль пользователя |\n| GET | /users/new | users/new.phtml | Форма создания нового пользователя |\n| POST | /users | | Создание нового пользователя |\n| GET | /users/{id}/edit | users/edit.phtml | Форма редактирования пользователя |\n| PATCH/PUT | /users/{id} | | Обновление пользователя |\n| DELETE | /users/{id} | | Удаление пользователя |\n\nТакое соглашение изначально появилось в Rails и затем было адаптировано во многих фреймворках на языках, отличных от Ruby.\n\nВ этом уроке мы разберем первые два маршрута: просмотр списка и конкретного ресурса. Остальные рассмотрим в следующих уроках.\n\n## Список (index)\n\nВывод списка мы уже делали не раз. Повторим для закрепления. Общий алгоритм действий такого обработчика всегда проходит по одному сценарию и не зависит от языка программирования:\n\n1. Извлекаем список из хранилища (базы данных). Обычно с учетом пейджинга\n2. Передаем данные в шаблон\n3. Выводим данные в шаблоне, используя цикл\n\n\n\nОбычно в этот список добавляют различные действия, которые можно выполнять над сущностями, например, редактирование, удаление или просмотр.\n\n**Обработчик**\n\n```php\n<?php\n\n$app->get('/schools', function ($request, $response) {\n $repository = new App\\SchoolRepository();\n $schools = $repository->all();\n $params = ['schools' => $schools];\n return $this->get('renderer')->render($response, \"schools/index.phtml\", $params);\n})->setName('schools');\n```\n\n**Шаблон**\n\n```php\n<table>\n <?php foreach ($schools as $school): ?>\n <tr>\n <td>\n <?= $school['id'] ?>\n </td>\n <td>\n <a href=\"/schools/<?= $school['id'] ?>\"><?= $school['name'] ?></a>\n </td>\n </tr>\n <?php endforeach ?>\n</table>\n```\n\n## Отображение (show)\n\nСтраница конкретной сущности. Например на Хекслете к таким страницам относятся: профиль пользователя, страница курса, страница профессии, страница урока и многие другие. Как и в случае со списком, порядок действий для отображения всегда один:\n\n1. Из адреса извлекается идентификатор сущности\n2. Выполняется поиск сущности в хранилище\n3. Она передается в шаблон\n4. В шаблоне рисуется красивый вывод\n\n**Обработчик**\n\n```php\n<?php\n\n$app->get('/schools/{id}', function ($request, $response, array $args) {\n $id = $args['id'];\n $repository = new App\\SchoolRepository();\n $school = $repository->find($id);\n\n $params = [\n 'school' => $school\n ];\n\n return $this->get('renderer')->render($response, 'school/show.phtml', $params);\n})->setName('school');\n```\n\n**Шаблон**\n\n```php\n<?php foreach ($school as $key => $value): ?>\n <div>\n <?= $key ?>: <?= $value ?>\n </div>\n<?php endforeach ?>\n```\n\nЕсли сущность была удалена или ее вообще не существовало, как тогда должен вести себя сайт? С точки зрения HTTP такой адрес должен вернуть HTTP-код 404. Сделать это можно явно, вернув соответствующий ответ:\n\n```php\n<?php\n\n$app->get('/schools/{id}', function ($request, $response, array $args) use ($repo) {\n $id = $args['id'];\n $school = $repo->find($id);\n\n if (!$school) {\n return $response->write('Page not found')\n ->withStatus(404);\n }\n})->setName('school');\n```\n\nОбычно механизм обработки таких ошибок построен через исключения. С ними мы познакомимся в следующих курсах.\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":2433,"name":"theory","url":"/courses/php-mvc/lessons/about/theory_unit"}],"links":[{"id":428346,"name":"Что такое протокол HTTPS, и как он защищает вас в интернете","url":"https://guides.hexlet.io/ru/https-yandex-guide/"},{"id":428347,"name":"Как работает DNS","url":"https://guides.hexlet.io/ru/dns/"}],"ordered_units":[{"id":2433,"name":"theory","url":"/courses/php-mvc/lessons/about/theory_unit"}],"id":1169,"slug":"about","state":"approved","name":"Введение","course_order":1,"goal":"Узнаем о курсе, его структуре, задачах и целях","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"Чтобы перейти от написания скриптов к созданию полноценных сайтов, нужно изучить много новых понятий и инструментов, которые не относятся к языку программирования. Например, нужно знать, как работают операционные системы, как работать с сетями, регистраторами, хостингом и деплоем сайта.\n\nНа собеседованиях веб-программистам часто задают вопрос «Что происходит после того, как в адресной строке браузера набирается сайт www.google.com и нажимается Enter?». Обычно на этот вопрос хотят услышать ключевые понятия, связанные с веб-разработкой:\n\n* Выполнение DNS-запроса для получения IP-адреса домена\n* Соединение с веб-сервером, находящемся по этому адресу на порту 443 (или 80) по TCP\n* Выполнение HTTP-запроса для получения содержимого сайта по указанному домену\n* Получение ответа и рендеринг содержимого во вкладке браузера.\n\nКаждый из этих пунктов подразумевает знание следующих тем:\n\n* Протокол HTTP. Понятие виртуальных хостов. Желательно понимание принципов работы HTTPS\n* Принципы работы DNS\n* Знание TCP/IP. Понятия: порт, маска, подсети. Модель OSI. Сетевые сокеты\n* Веб-сервер. Что это такое, как работает и зачем нужен\n\nПодробный ответ на этот вопрос доступен [здесь](https://github.com/alex/what-happens-when).\n\nТакже на Хекслете есть ответы на некоторые из перечисленных пунктов, но большую часть материала придется взять из сторонних источников. Большинство ответов на указанные темы можно получить в книгах по операционным системам. В наших [рекомендованных книгах](https://ru.hexlet.io/pages/recommended-books) есть все необходимое.\n\nЗнание HTTP можно взять из соответствующего курса. Общее понимание DNS, хостинга, деплоя — из курса [Введение в веб-разработку](https://ru.hexlet.io/courses/intro_to_web_development). Остальное есть в дополнительных ссылках.\n\nТакже рекомендуем посмотреть [публичное собеседование](https://www.youtube.com/watch?v=JERUf-xKU1o&index=6&list=PLo6puixMwuSOa_0EH6X4OXzFAmyQGS3a3), где поднимались эти вопросы.\n\nВ процессе разработки сайта нужно изучать фреймворки, микрофреймворки, роутинг, куки, сессии, безопасность, шаблонизацию и взаимодействие с базой данных. И даже после того, как вы научитесь создавать сайты, обучение нужно продолжать.\n\nЧтобы сайт был доступен пользователям, его нужно развернуть на удаленном сервере. Этот процесс называется «деплой» — процесс разворачивания сайта на хостинге. При этом существует разный тип хостинга: IaaS (AWS), PaaS (Render), Shared Hosting (виртуальный хостинг), VPS/VDS. Также перед этим нужно настроить удаленную машину с помощью инструмента, например, Ansible, что тоже стоит изучить.\n\nДанный курс посвящен разработке сайтов с использованием микрофреймворков. Темы, указанные выше, также включены в курс, но разбираются поверхностно. Поэтому рекомендуем выполнять задачи не только в среде Хекслета, но и локально, параллельно выкатывая код на сервис, подобный [Render](https://www.render.com/).\n"},"id":165,"slug":"php-mvc","challenges_count":3,"name":"Веб-разработка на PHP","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"paid","description":"На этом курсе вы познакомитесь с веб-разработкой на PHP. Вы узнаете о MVC, шаблонизации, роутинге и отправке форм. В итоге поймете, как создаются элементы типичных сайтов, например, аутентификация или полный набор операций по работе с сущностью (CRUD). Знания из этого курса пригодятся, если вы решите написать свой сайт на микрофреймворке Slim.","kind":"basic","updated_at":"2026-02-20T10:25:58.282Z","language":"php","duration_cache":95100,"skills":["Создавать с помощью PHP полноценные сайты","Пользоваться встроенным в PHP веб-сервером","Работать с микрофреймворком Slim","Правильно строить архитектуру веб-приложений. Разбираться в MVC"],"keywords":["CGI","slim framework","шаблонизация","отправка форм","MVC","сессии","роутинг"],"lessons_count":28,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MTUyODgsInB1ciI6ImJsb2JfaWQifX0=--ba054c4897951eb2e6cea29301c46f15adb3aeb2/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJwbmciLCJyZXNpemVfdG9fZmlsbCI6WzYwMCw0MDBdfSwicHVyIjoidmFyaWF0aW9uIn19--6067466c2912ca31a17eddee04b8cf2a38c6ad17/image.png"},"recommendedLandings":[{"stack":{"id":2,"slug":"php","title":"PHP-разработчик","audience":"for_beginners","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"finished","order":60,"duration_in_months":10},"id":1,"slug":"php","title":"РНР-разработчик","subtitle":"Изучите PHP и Laravel для разработки и проектирования REST API","subtitle_for_lists":"Изучите PHP и Laravel для разработки и проектирования REST API","locale":"ru","current":true,"duration_in_months_text":"10 месяцев","stack_slug":"php","price_text":"от 5 650 ₽","duration_text":"10 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzk5MiwicHVyIjoiYmxvYl9pZCJ9fQ==--e9d0f30948ea766a7e6bc3e3d56c192344d45fb8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Programming-cuate%20(1).png"}],"lessonMemberUnit":null,"accessToLearnUnitExists":false,"accessToCourseExists":false},"url":"/courses/php-mvc/lessons/crud/theory_unit","version":"8f286f6358a90a7bef2263b3a6edf5a90a94fa42","encryptHistory":false,"clearHistory":false}"><style data-mantine-styles="true">:root, :host{--mantine-font-family: Arial, sans-serif;--mantine-font-family-headings: Arial, sans-serif;--mantine-heading-font-weight: normal;--mantine-radius-default: 0rem;--mantine-primary-color-filled: var(--mantine-color-indigo-filled);--mantine-primary-color-filled-hover: var(--mantine-color-indigo-filled-hover);--mantine-primary-color-light: var(--mantine-color-indigo-light);--mantine-primary-color-light-hover: var(--mantine-color-indigo-light-hover);--mantine-primary-color-light-color: var(--mantine-color-indigo-light-color);--mantine-spacing-xxl: calc(4rem * var(--mantine-scale));--mantine-font-size-xs: 12px;--mantine-font-size-sm: 14px;--mantine-font-size-md: 16px;--mantine-font-size-lg: clamp(16.0000px, calc(15.2727px + 0.2273vw), 18.0000px);--mantine-font-size-xl: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-display-3: clamp(32.0000px, calc(26.1818px + 1.8182vw), 48.0000px);--mantine-font-size-display-2: clamp(36.0000px, calc(25.8182px + 3.1818vw), 64.0000px);--mantine-font-size-display-1: clamp(40.0000px, calc(25.4545px + 4.5455vw), 80.0000px);--mantine-font-size-h1: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-font-size-h2: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-font-size-h3: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-font-size-h4: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-font-size-h5: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-h6: 1rem;--mantine-primary-color-0: var(--mantine-color-indigo-0);--mantine-primary-color-1: var(--mantine-color-indigo-1);--mantine-primary-color-2: var(--mantine-color-indigo-2);--mantine-primary-color-3: var(--mantine-color-indigo-3);--mantine-primary-color-4: var(--mantine-color-indigo-4);--mantine-primary-color-5: var(--mantine-color-indigo-5);--mantine-primary-color-6: var(--mantine-color-indigo-6);--mantine-primary-color-7: var(--mantine-color-indigo-7);--mantine-primary-color-8: var(--mantine-color-indigo-8);--mantine-primary-color-9: var(--mantine-color-indigo-9);--mantine-color-red-0: #ffeaea;--mantine-color-red-1: #fed4d4;--mantine-color-red-2: #f4a7a8;--mantine-color-red-3: #ec7878;--mantine-color-red-4: #e55050;--mantine-color-red-5: #e03131;--mantine-color-red-6: #e02829;--mantine-color-red-7: #c71a1c;--mantine-color-red-8: #b21218;--mantine-color-red-9: #9c0411;--mantine-color-violet-0: #fce9ff;--mantine-color-violet-1: #f1cfff;--mantine-color-violet-2: #e09bff;--mantine-color-violet-3: #d16fff;--mantine-color-violet-4: #be37fe;--mantine-color-violet-5: #b51afe;--mantine-color-violet-6: #b009ff;--mantine-color-violet-7: #9b00e4;--mantine-color-violet-8: #8a00cc;--mantine-color-violet-9: #7800b3;--mantine-color-indigo-0: #edecff;--mantine-color-indigo-1: #d6d5fe;--mantine-color-indigo-2: #aaa9f4;--mantine-color-indigo-3: #7b79eb;--mantine-color-indigo-4: #5451e4;--mantine-color-indigo-5: #3b37e0;--mantine-color-indigo-6: #2d2adf;--mantine-color-indigo-7: #1f1ec7;--mantine-color-indigo-8: #1819b2;--mantine-color-indigo-9: #0c149e;--mantine-color-cyan-0: #dffdff;--mantine-color-cyan-1: #caf5ff;--mantine-color-cyan-2: #99e8ff;--mantine-color-cyan-3: #64daff;--mantine-color-cyan-4: #3ccffe;--mantine-color-cyan-5: #24c8fe;--mantine-color-cyan-6: #00c2ff;--mantine-color-cyan-7: #00ade4;--mantine-color-cyan-8: #009acd;--mantine-color-cyan-9: #0085b5;--mantine-color-green-0: #e9fdec;--mantine-color-green-1: #d7f6dc;--mantine-color-green-2: #b0eab9;--mantine-color-green-3: #86df94;--mantine-color-green-4: #62d574;--mantine-color-green-5: #4ccf5f;--mantine-color-green-6: #3fcc54;--mantine-color-green-7: #2fb344;--mantine-color-green-8: #25a03b;--mantine-color-green-9: #138a2e;--mantine-color-yellow-0: #fff7e2;--mantine-color-yellow-1: #ffeecd;--mantine-color-yellow-2: #ffdc9c;--mantine-color-yellow-3: #ffc966;--mantine-color-yellow-4: #feb93a;--mantine-color-yellow-5: #feae1e;--mantine-color-yellow-6: #ffa90f;--mantine-color-yellow-8: #ca8200;--mantine-color-yellow-9: #af7000;--mantine-h1-font-size: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-h1-font-weight: normal;--mantine-h2-font-size: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-h2-font-weight: normal;--mantine-h3-font-size: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-h3-font-weight: normal;--mantine-h4-font-size: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-h4-font-weight: normal;--mantine-h5-font-size: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-h5-font-weight: normal;--mantine-h6-font-size: 1rem;--mantine-h6-font-weight: normal;}
:root[data-mantine-color-scheme="dark"], :host([data-mantine-color-scheme="dark"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-dark-filled: var(--mantine-color-dark-5);--mantine-color-dark-filled-hover: var(--mantine-color-dark-6);--mantine-color-dark-light: rgba(105, 105, 105, 0.15);--mantine-color-dark-light-hover: rgba(105, 105, 105, 0.2);--mantine-color-dark-light-color: var(--mantine-color-dark-0);--mantine-color-dark-outline: var(--mantine-color-dark-1);--mantine-color-dark-outline-hover: rgba(184, 184, 184, 0.05);--mantine-color-gray-filled: var(--mantine-color-gray-5);--mantine-color-gray-filled-hover: var(--mantine-color-gray-6);--mantine-color-gray-light: rgba(222, 226, 230, 0.15);--mantine-color-gray-light-hover: rgba(222, 226, 230, 0.2);--mantine-color-gray-light-color: var(--mantine-color-gray-0);--mantine-color-gray-outline: var(--mantine-color-gray-1);--mantine-color-gray-outline-hover: rgba(241, 243, 245, 0.05);--mantine-color-red-filled: var(--mantine-color-red-5);--mantine-color-red-filled-hover: var(--mantine-color-red-6);--mantine-color-red-light: rgba(236, 120, 120, 0.15);--mantine-color-red-light-hover: rgba(236, 120, 120, 0.2);--mantine-color-red-light-color: var(--mantine-color-red-0);--mantine-color-red-outline: var(--mantine-color-red-1);--mantine-color-red-outline-hover: rgba(254, 212, 212, 0.05);--mantine-color-pink-filled: var(--mantine-color-pink-5);--mantine-color-pink-filled-hover: var(--mantine-color-pink-6);--mantine-color-pink-light: rgba(250, 162, 193, 0.15);--mantine-color-pink-light-hover: rgba(250, 162, 193, 0.2);--mantine-color-pink-light-color: var(--mantine-color-pink-0);--mantine-color-pink-outline: var(--mantine-color-pink-1);--mantine-color-pink-outline-hover: rgba(255, 222, 235, 0.05);--mantine-color-grape-filled: var(--mantine-color-grape-5);--mantine-color-grape-filled-hover: var(--mantine-color-grape-6);--mantine-color-grape-light: rgba(229, 153, 247, 0.15);--mantine-color-grape-light-hover: rgba(229, 153, 247, 0.2);--mantine-color-grape-light-color: var(--mantine-color-grape-0);--mantine-color-grape-outline: var(--mantine-color-grape-1);--mantine-color-grape-outline-hover: rgba(243, 217, 250, 0.05);--mantine-color-violet-filled: var(--mantine-color-violet-5);--mantine-color-violet-filled-hover: var(--mantine-color-violet-6);--mantine-color-violet-light: rgba(209, 111, 255, 0.15);--mantine-color-violet-light-hover: rgba(209, 111, 255, 0.2);--mantine-color-violet-light-color: var(--mantine-color-violet-0);--mantine-color-violet-outline: var(--mantine-color-violet-1);--mantine-color-violet-outline-hover: rgba(241, 207, 255, 0.05);--mantine-color-indigo-filled: var(--mantine-color-indigo-5);--mantine-color-indigo-filled-hover: var(--mantine-color-indigo-6);--mantine-color-indigo-light: rgba(123, 121, 235, 0.15);--mantine-color-indigo-light-hover: rgba(123, 121, 235, 0.2);--mantine-color-indigo-light-color: var(--mantine-color-indigo-0);--mantine-color-indigo-outline: var(--mantine-color-indigo-1);--mantine-color-indigo-outline-hover: rgba(214, 213, 254, 0.05);--mantine-color-blue-filled: var(--mantine-color-blue-5);--mantine-color-blue-filled-hover: var(--mantine-color-blue-6);--mantine-color-blue-light: rgba(116, 192, 252, 0.15);--mantine-color-blue-light-hover: rgba(116, 192, 252, 0.2);--mantine-color-blue-light-color: var(--mantine-color-blue-0);--mantine-color-blue-outline: var(--mantine-color-blue-1);--mantine-color-blue-outline-hover: rgba(208, 235, 255, 0.05);--mantine-color-cyan-filled: var(--mantine-color-cyan-5);--mantine-color-cyan-filled-hover: var(--mantine-color-cyan-6);--mantine-color-cyan-light: rgba(100, 218, 255, 0.15);--mantine-color-cyan-light-hover: rgba(100, 218, 255, 0.2);--mantine-color-cyan-light-color: var(--mantine-color-cyan-0);--mantine-color-cyan-outline: var(--mantine-color-cyan-1);--mantine-color-cyan-outline-hover: rgba(202, 245, 255, 0.05);--mantine-color-teal-filled: var(--mantine-color-teal-5);--mantine-color-teal-filled-hover: var(--mantine-color-teal-6);--mantine-color-teal-light: rgba(99, 230, 190, 0.15);--mantine-color-teal-light-hover: rgba(99, 230, 190, 0.2);--mantine-color-teal-light-color: var(--mantine-color-teal-0);--mantine-color-teal-outline: var(--mantine-color-teal-1);--mantine-color-teal-outline-hover: rgba(195, 250, 232, 0.05);--mantine-color-green-filled: var(--mantine-color-green-5);--mantine-color-green-filled-hover: var(--mantine-color-green-6);--mantine-color-green-light: rgba(134, 223, 148, 0.15);--mantine-color-green-light-hover: rgba(134, 223, 148, 0.2);--mantine-color-green-light-color: var(--mantine-color-green-0);--mantine-color-green-outline: var(--mantine-color-green-1);--mantine-color-green-outline-hover: rgba(215, 246, 220, 0.05);--mantine-color-lime-filled: var(--mantine-color-lime-5);--mantine-color-lime-filled-hover: var(--mantine-color-lime-6);--mantine-color-lime-light: rgba(192, 235, 117, 0.15);--mantine-color-lime-light-hover: rgba(192, 235, 117, 0.2);--mantine-color-lime-light-color: var(--mantine-color-lime-0);--mantine-color-lime-outline: var(--mantine-color-lime-1);--mantine-color-lime-outline-hover: rgba(233, 250, 200, 0.05);--mantine-color-yellow-filled: var(--mantine-color-yellow-5);--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-6);--mantine-color-yellow-light: rgba(255, 201, 102, 0.15);--mantine-color-yellow-light-hover: rgba(255, 201, 102, 0.2);--mantine-color-yellow-light-color: var(--mantine-color-yellow-0);--mantine-color-yellow-outline: var(--mantine-color-yellow-1);--mantine-color-yellow-outline-hover: rgba(255, 238, 205, 0.05);--mantine-color-orange-filled: var(--mantine-color-orange-5);--mantine-color-orange-filled-hover: var(--mantine-color-orange-6);--mantine-color-orange-light: rgba(255, 192, 120, 0.15);--mantine-color-orange-light-hover: rgba(255, 192, 120, 0.2);--mantine-color-orange-light-color: var(--mantine-color-orange-0);--mantine-color-orange-outline: var(--mantine-color-orange-1);--mantine-color-orange-outline-hover: rgba(255, 232, 204, 0.05);--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-9) 0%, var(--mantine-color-cyan-7) 100%);--app-color-surface: #2e2e2e;}
:root[data-mantine-color-scheme="light"], :host([data-mantine-color-scheme="light"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-red-light: rgba(224, 40, 41, 0.1);--mantine-color-red-light-hover: rgba(224, 40, 41, 0.12);--mantine-color-red-outline-hover: rgba(224, 40, 41, 0.05);--mantine-color-violet-light: rgba(176, 9, 255, 0.1);--mantine-color-violet-light-hover: rgba(176, 9, 255, 0.12);--mantine-color-violet-outline-hover: rgba(176, 9, 255, 0.05);--mantine-color-indigo-light: rgba(45, 42, 223, 0.1);--mantine-color-indigo-light-hover: rgba(45, 42, 223, 0.12);--mantine-color-indigo-outline-hover: rgba(45, 42, 223, 0.05);--mantine-color-cyan-light: rgba(0, 194, 255, 0.1);--mantine-color-cyan-light-hover: rgba(0, 194, 255, 0.12);--mantine-color-cyan-outline-hover: rgba(0, 194, 255, 0.05);--mantine-color-green-light: rgba(63, 204, 84, 0.1);--mantine-color-green-light-hover: rgba(63, 204, 84, 0.12);--mantine-color-green-outline-hover: rgba(63, 204, 84, 0.05);--mantine-color-yellow-light: rgba(255, 169, 15, 0.1);--mantine-color-yellow-light-hover: rgba(255, 169, 15, 0.12);--mantine-color-yellow-outline-hover: rgba(255, 169, 15, 0.05);--app-color-surface: #f1f3f5;--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-5) 100%);}</style><style data-mantine-styles="classes">@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}</style><div style="position:absolute;top:0rem" class=""></div><div style="max-width:var(--container-size-xl);height:100%;min-height:0rem" class=""><style data-mantine-styles="inline">.__m__-_R_5ub_{--grid-gutter:0rem;}</style><div style="height:100%;min-height:0rem" class="m_410352e9 mantine-Grid-root __m__-_R_5ub_"><div class="m_dee7bd2f mantine-Grid-inner" style="height:100%"><style data-mantine-styles="inline">.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:91.66666666666667%;--col-max-width:91.66666666666667%;}@media(min-width: 48em){.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:83.33333333333334%;--col-max-width:83.33333333333334%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem;display:flex" class="m_96bdd299 mantine-Grid-col __m__-_R_rdub_"><style data-mantine-styles="inline">.__m__-_R_6qrdub_{margin-top:0rem;padding-inline:var(--mantine-spacing-xs);width:100%;}@media(min-width: 48em){.__m__-_R_6qrdub_{margin-top:var(--mantine-spacing-xl);width:80%;}}@media(min-width: 62em){.__m__-_R_6qrdub_{padding-inline:var(--mantine-spacing-xl);}}</style><div style="margin-inline:auto;max-width:var(--mantine-breakpoint-xl)" class="__m__-_R_6qrdub_"><div style="color:var(--mantine-color-dimmed)" class="m_4451eb3a mantine-Center-root" data-inline="true"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:calc(0.125rem * var(--mantine-scale));color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-lock "><path d="M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6"></path><path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0"></path><path d="M8 11v-4a4 4 0 1 1 8 0v4"></path></svg></div><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Веб-разработка на PHP</p></div><h1 style="--title-fw:var(--mantine-h1-font-weight);--title-lh:var(--mantine-h1-line-height);--title-fz:var(--mantine-h1-font-size);margin-bottom:var(--mantine-spacing-xl)" class="m_8a5d1357 mantine-Title-root" data-order="1">Теория: CRUD</h1><script type="application/ld+json">{"@context":"https://schema.org","@type":"LearningResource","name":"CRUD","inLanguage":"ru","isPartOf":{"@type":"LearningResource","name":"Веб-разработка на PHP"},"isAccessibleForFree":"False","hasPart":{"@type":"WebPageElement","isAccessibleForFree":"False","cssSelector":".paywalled"}}</script><div class=""><div style="--alert-color:var(--mantine-color-indigo-light-color);margin-bottom:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-lg)" class="m_66836ed3 mantine-Alert-root" id="mantine-_R_remqrdub_" role="alert" aria-describedby="mantine-_R_remqrdub_-body" aria-labelledby="mantine-_R_remqrdub_-title"><div class="m_a5d60502 mantine-Alert-wrapper"><div class="m_667f2a6a mantine-Alert-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-rocket "><path d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3 -5a9 9 0 0 0 6 -8a3 3 0 0 0 -3 -3a9 9 0 0 0 -8 6a6 6 0 0 0 -5 3"></path><path d="M7 14a6 6 0 0 0 -3 6a6 6 0 0 0 6 -3"></path><path d="M14 9a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path></svg></div><div class="m_667c2793 mantine-Alert-body"><div class="m_6a03f287 mantine-Alert-title"><span id="mantine-_R_remqrdub_-title" class="m_698f4f23 mantine-Alert-label">Полный доступ к материалам</span></div><div id="mantine-_R_remqrdub_-body" class="m_7fa78076 mantine-Alert-message"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Зарегистрируйтесь и получите доступ к этому и десяткам других курсов</p><a style="--button-height:var(--button-height-xs);--button-padding-x:var(--button-padding-x-xs);--button-fz:var(--mantine-font-size-xs);--button-bg:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-hover:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-color:var(--mantine-color-white);--button-bd:none" class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root" data-variant="gradient" data-size="xs" href="/u/new"><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">Зарегистрироваться</span></span></a></div></div></div></div></div><div class="paywalled m_d08caa0 mantine-Typography-root"><p>Несмотря на огромное число разнообразных сайтов, практически всю веб-разработку можно свести к <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://ru.wikipedia.org/wiki/CRUD" rel="noopener noreferrer" target="_blank">CRUD</a>-операциям.</p>
<p>CRUD означает четыре стандартные операции над любой сущностью: создание, чтение, обновление и удаление. Например, в случае с пользователем можно составить такое соответствие:</p>
<p><strong>Create</strong></p>
<ul>
<li>Регистрация</li>
</ul>
<p><strong>Read</strong></p>
<ul>
<li>Просмотр профиля пользователями сайта</li>
<li>Просмотр пользователя в административном интерфейсе</li>
</ul>
<p><strong>Update</strong></p>
<ul>
<li>Обновление личных данных</li>
<li>Смена емейла</li>
<li>Смена пароля</li>
</ul>
<p><strong>Delete</strong></p>
<ul>
<li>Удаление</li>
</ul>
<p>Точно так же можно расписать действия над любыми другими ресурсами, фотографиями пользователя, его друзьями, сообщениями.</p>
<p>Создание полного круда включает в себя следующие действия:</p>
<ul>
<li>Создание сущности в коде (как правило, класса)</li>
<li>Добавление таблицы в базу</li>
<li>Написание тестов на обработчики</li>
<li>Добавление обработчиков</li>
<li>Добавление шаблонов</li>
</ul>
<p>Новички тратят на создание такого круда не один день. У опытного разработчика в прокачанном фреймворке этот процесс занимает максимум часы. Slim не предоставляет средств автоматизации, поэтому придется многое делать руками. В целях обучения это оправданно, но в промышленной разработке, то что может быть автоматизировано, должно быть автоматизировано.</p>
<p>Ниже мы пройдемся по всему процессу создания круда пользователя за исключением работы с базой данных и тестов. Начнем с роутинга.</p>
<p>Полный круд пользователя включает минимум семь маршрутов. Их может быть больше, так как любое из действий может повторяться не один раз.</p>
<div style="--table-min-width:calc(50rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_a100c15 mantine-TableScrollContainer-scrollContainer m_d57069b5 mantine-ScrollArea-root"><div style="overflow-x:hidden;overflow-y:hidden" class="m_c0783ff9 mantine-ScrollArea-viewport" data-offset-scrollbars="x" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><div class="m_62259741 mantine-TableScrollContainer-scrollContainerInner"><table><thead><tr><th>Метод</th><th>Маршрут</th><th>Шаблон</th><th>Описание</th></tr></thead><tbody><tr><td>GET</td><td>/users</td><td>users/index.phtml</td><td>Список пользователей</td></tr><tr><td>GET</td><td>/users/{id}</td><td>users/show.phtml</td><td>Профиль пользователя</td></tr><tr><td>GET</td><td>/users/new</td><td>users/new.phtml</td><td>Форма создания нового пользователя</td></tr><tr><td>POST</td><td>/users</td><td></td><td>Создание нового пользователя</td></tr><tr><td>GET</td><td>/users/{id}/edit</td><td>users/edit.phtml</td><td>Форма редактирования пользователя</td></tr><tr><td>PATCH/PUT</td><td>/users/{id}</td><td></td><td>Обновление пользователя</td></tr><tr><td>DELETE</td><td>/users/{id}</td><td></td><td>Удаление пользователя</td></tr></tbody></table></div></div></div></div>
<p>Такое соглашение изначально появилось в Rails и затем было адаптировано во многих фреймворках на языках, отличных от Ruby.</p>
<p>В этом уроке мы разберем первые два маршрута: просмотр списка и конкретного ресурса. Остальные рассмотрим в следующих уроках.</p>
<h2 id="heading-2-1">Список (index)</h2>
<p>Вывод списка мы уже делали не раз. Повторим для закрепления. Общий алгоритм действий такого обработчика всегда проходит по одному сценарию и не зависит от языка программирования:</p>
<ol>
<li>Извлекаем список из хранилища (базы данных). Обычно с учетом пейджинга</li>
<li>Передаем данные в шаблон</li>
<li>Выводим данные в шаблоне, используя цикл</li>
</ol>
<p><img style="--image-object-fit:contain;width:auto" class="m_9e117634 mantine-Image-root" src="/rails/active_storage/blobs/proxy/eyJfcmFpbHMiOnsiZGF0YSI6MTUzMDQsInB1ciI6ImJsb2JfaWQifX0=--4ccdd9cfe3ddd15937314c91e3369e68705cd2ce/crud-index.png" alt="CRUD Список" loading="lazy"/></p>
<p>Обычно в этот список добавляют различные действия, которые можно выполнять над сущностями, например, редактирование, удаление или просмотр.</p>
<p><strong>Обработчик</strong></p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"><?php
$app->get('/schools', function ($request, $response) {
$repository = new App\SchoolRepository();
$schools = $repository->all();
$params = ['schools' => $schools];
return $this->get('renderer')->render($response, "schools/index.phtml", $params);
})->setName('schools');</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p><strong>Шаблон</strong></p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"><table>
<?php foreach ($schools as $school): ?>
<tr>
<td>
<?= $school['id'] ?>
</td>
<td>
<a href="/schools/<?= $school['id'] ?>"><?= $school['name'] ?></a>
</td>
</tr>
<?php endforeach ?>
</table></code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<h2 id="heading-2-2">Отображение (show)</h2>
<p>Страница конкретной сущности. Например на Хекслете к таким страницам относятся: профиль пользователя, страница курса, страница профессии, страница урока и многие другие. Как и в случае со списком, порядок действий для отображения всегда один:</p>
<ol>
<li>Из адреса извлекается идентификатор сущности</li>
<li>Выполняется поиск сущности в хранилище</li>
<li>Она передается в шаблон</li>
<li>В шаблоне рисуется красивый вывод</li>
</ol>
<p><strong>Обработчик</strong></p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"><?php
$app->get('/schools/{id}', function ($request, $response, array $args) {
$id = $args['id'];
$repository = new App\SchoolRepository();
$school = $repository->find($id);
$params = [
'school' => $school
];
return $this->get('renderer')->render($response, 'school/show.phtml', $params);
})->setName('school');</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p><strong>Шаблон</strong></p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"><?php foreach ($school as $key => $value): ?>
<div>
<?= $key ?>: <?= $value ?>
</div>
<?php endforeach ?></code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Если сущность была удалена или ее вообще не существовало, как тогда должен вести себя сайт? С точки зрения HTTP такой адрес должен вернуть HTTP-код 404. Сделать это можно явно, вернув соответствующий ответ:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"><?php
$app->get('/schools/{id}', function ($request, $response, array $args) use ($repo) {
$id = $args['id'];
$school = $repo->find($id);
if (!$school) {
return $response->write('Page not found')
->withStatus(404);
}
})->setName('school');</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Обычно механизм обработки таких ошибок построен через исключения. С ними мы познакомимся в следующих курсах.</p></div><div style="margin-block:var(--mantine-spacing-xl)" class=""><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md)" class="m_8a5d1357 mantine-Title-root" data-order="2">Рекомендуемые программы</h2><style data-mantine-styles="inline">.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xs);--carousel-slide-size:70%;}@media(min-width: 36em){.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xl);--carousel-slide-size:50%;}}</style><div style="--carousel-control-size:calc(2.5rem * var(--mantine-scale));--carousel-controls-offset:var(--mantine-spacing-sm);margin-bottom:var(--mantine-spacing-lg);padding-block:var(--mantine-spacing-sm);background:var(--app-color-surface)" class="m_17884d0f mantine-Carousel-root responsiveClassName" data-orientation="horizontal" data-include-gap-in-size="true"><div class="m_39bc3463 mantine-Carousel-controls" data-orientation="horizontal"><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="previous" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="next" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(-90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button></div><div class="m_a2dae653 mantine-Carousel-viewport" data-type="media"><div class="m_fcd81474 mantine-Carousel-container __m__-_R_2mremqrdub_" data-orientation="horizontal"><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/php?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">10 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">С нуля</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">РНР-разработчик</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите PHP и Laravel для разработки и проектирования REST API</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzk5MiwicHVyIjoiYmxvYl9pZCJ9fQ==--e9d0f30948ea766a7e6bc3e3d56c192344d45fb8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Programming-cuate%20(1).png" alt="РНР-разработчик" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 5 650 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md);font-size:var(--mantine-font-size-h3)" class="m_8a5d1357 mantine-Title-root" data-order="2" data-responsive="true">Каталог</h2><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Полный список доступных курсов по разным направлениям</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="/vite/assets/development-BVihs_d5.png" alt="Orientation"/></div></div></div></a></div></div></div></div></div></div></div></div></div><style data-mantine-styles="inline">.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:8.333333333333334%;--col-max-width:8.333333333333334%;}@media(min-width: 48em){.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:16.666666666666668%;--col-max-width:16.666666666666668%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem" class="m_96bdd299 mantine-Grid-col __m__-_R_1bdub_"><div style="margin-inline:var(--mantine-spacing-xs)" class="mantine-visible-from-sm"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-lg);text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/php-mvc/lessons/crud/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label"><span style="margin-inline-end:var(--mantine-spacing-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Дальше</span>→</span></span></a><a style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Навигация по теме</span><span class="m_57492dcc mantine-NavLink-description">Теория</span></div><span class="m_690090b5 mantine-NavLink-section" data-position="right"></span></a><div style="margin-block:var(--mantine-spacing-lg)" class="m_3eebeb36 mantine-Divider-root" data-orientation="horizontal" role="separator"></div><div style="margin-block:var(--mantine-spacing-lg)" class=""><div style="justify-content:space-between;margin-bottom:calc(0.1875rem * var(--mantine-scale));color:var(--mantine-color-dimmed);font-size:var(--mantine-font-size-xs)" class="m_8bffd616 mantine-Flex-root __m__-_R_qimrbdub_"><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Завершено</p><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">0 / 28</p></div><div style="--progress-size:var(--progress-size-sm)" class="m_db6d6462 mantine-Progress-root" data-size="sm"><div style="--progress-section-size:0%;--progress-section-color:var(--mantine-color-gray-filled)" class="m_2242eb65 mantine-Progress-section" role="progressbar" aria-valuemax="100" aria-valuemin="0" aria-valuenow="0" aria-valuetext="0%"></div></div></div><button style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root" type="button"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Обсуждения (архив)</span><span class="m_57492dcc mantine-NavLink-description"></span></div></button><div style="--toc-bg:var(--mantine-color-blue-light);--toc-color:var(--mantine-color-blue-light-color);--toc-size:var(--mantine-font-size-sm);--toc-radius:var(--mantine-radius-sm);margin-top:var(--mantine-spacing-xl)" class="m_bcaa9990 mantine-TableOfContents-root" data-variant="light" data-size="sm"></div></div><div class="mantine-hidden-from-sm"><div style="--stack-gap:0rem;--stack-align:stretch;--stack-justify:flex-start" class="m_6d731127 mantine-Stack-root"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-xs);padding:0rem;text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/php-mvc/lessons/crud/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">→</span></span></a><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" data-disabled="true" type="button" disabled=""><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></span></button><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto mantine-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" type="button"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></span></button></div></div></div></div></div></div></div>
</main>
<footer class="bg-dark fw-light text-light px-3 py-5">
<div class="row small">
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 mb-3">Хекслет</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/about">О нас</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/testimonials">Отзывы</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://b2b.hexlet.io" role="button">Корпоративное обучение</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/blog">Блог</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/qna">Вопросы и ответы</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/glossary">Глоссарий</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://help.hexlet.io" data-target="_blank" role="button">Справка</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" target="_blank" rel="noopener noreferrer" href="/map">Карта сайта</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 fw-normal mb-3">Направления</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_devops">DevOps
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_data_analytics">Аналитика
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_backend_development">Бэкенд
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_programming">Программирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_testing">Тестирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_front_end_dev">Фронтенд
</a></li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Профессии</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/go">Go-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/java">Java-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python">Python-разработчик </a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/data-analytics">Аналитик данных</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/qa-engineer">Инженер по ручному тестированию</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php">РНР-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/frontend">Фронтенд-разработчик</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Навыки</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python-django-developer">Django</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/docker">Docker</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php-laravel-developer">Laravel</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/postman">Postman</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-react-developer">React</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-rest-api">REST API в Node.js</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/spring-boot">Spring Boot</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/typescript">Typescript</a>
</li>
</ul>
</div>
</div>
<hr>
<div class="row">
<div class="col-12 col-sm-4 col-md-2">
<div class="fs-4">
<ul class="list-unstyled d-flex">
<li class="me-3">
<a aria-label="Telegram" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://t.me/hexlet_ru"><span class="bi bi-telegram"></span>
</a></li>
<li>
<a aria-label="Youtube" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://www.youtube.com/user/HexletUniversity"><span class="bi bi-youtube"></span>
</a></li>
</ul>
</div>
<div class="mb-2 d-flex flex-column">
<a class="link-light text-decoration-none" rel="nofollow" href="mailto:support@hexlet.io">support@hexlet.io</a>
<a class="link-light text-decoration-none py-2" target="_blank" href="https://t.me/hexlet_help_bot">t.me/hexlet_help_bot</a>
</div>
<ul class="list-unstyled d-flex">
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://hexlet.io/locale/switch?new_locale=en" data-target="_self" role="button"><span class="my-auto">EN</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 opacity-100 external-link" rel="nofollow" data-href="https://ru.hexlet.io/locale/switch?new_locale=ru" data-target="_self" role="button"><span class="my-auto">RU</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://kz.hexlet.io/locale/switch?new_locale=kz" data-target="_self" role="button"><span class="my-auto">KZ</span>
</span></li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<ul class="list-unstyled fs-4">
<li class="mb-3">
<a class="link-light text-decoration-none" href="tel:8%20800%20100%2022%2047">8 800 100 22 47</a>
<span class="d-block opacity-50 small">бесплатно по РФ</span>
</li>
<li>
<a class="link-light text-decoration-none" href="tel:%2B7%20495%20085%2021%2062">+7 495 085 21 62</a>
<span class="d-block opacity-50 small">бесплатно по Москве</span>
</li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<div class="small mb-3">Образовательные услуги оказываются на основании Л035-01298-77/01989008 от 14.03.2025</div>
<ul class="list-unstyled small">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/legal">Правовая информация</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/offer">Оферта</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/license">Лицензия</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/contacts">Контакты</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-12 col-md-4 small">
<div class="mb-2">
<div>ООО «<a href="/" class="text-decoration-none link-light">Хекслет Рус</a>»</div>
<div>108813 г. Москва, вн.тер.г. поселение Московский,</div>
<div>г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3</div>
<div>ОГРН 1217300010476</div>
<div>ИНН 7325174845</div>
</div>
<hr>
<div>АНО ДПО «<a href="/" class="text-decoration-none link-light">Учебный центр «Хекслет</a>»</div>
<div>119331 г. Москва, вн. тер. г. муниципальный округ</div>
<div>Ломоносовский, пр-кт Вернадского, д. 29</div>
<div>ОГРН 1247700712390</div>
<div>ИНН 7736364948</div>
</div>
</div>
</footer>
<div id="root-assistant-offcanvas"></div>
<script src="/vite/assets/assistant-Bukl1lYy.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/MarkdownBlock-DbyKWoR_.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/shiki-V011pkdv.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-XR8Qr8kR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dist-GCHh59xr.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useIsomorphicEffect-HJ6VK0D3.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-KSp6QbZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/classnames-l6ipYlLR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/debounce-jMQ_Cf4f.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v67327c56f0bb4ef8b305cae61679db8f1769101564043" integrity="sha512-rdcWY47ByXd76cbCFzznIcEaCN71jqkWBBqlwhF1SY7KubdLKZiEGeP7AyieKZlGP9hbY/MhGrwXzJC/HulNyg==" data-cf-beacon='{"version":"2024.11.0","token":"d11015b65d11429ea6b4a2ef37dd7e0b","server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
</body>
</html>