<!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 16:40:25 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="gNIIVChfnlwTtsgpViBoF1mo5w9NO52rPAlPuWcCmzdvA8Nj2iEzPKX17LFaL5hgmaHKpUUMYwmB6dXtNQV8WQ";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>Объекты-Сущности, Объекты-Значения и встраиваемые объекты | PHP: Объектно-ориентированный дизайн</title>
<meta name="description" content="Объекты-Сущности, Объекты-Значения и встраиваемые объекты / PHP: Объектно-ориентированный дизайн: Знакомимся с основными принципами организации объектов, описывающих предметную область">
<link rel="canonical" href="https://ru.hexlet.io/courses/php-object-oriented-design/lessons/modeling/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="Объекты-Сущности, Объекты-Значения и встраиваемые объекты">
<meta property="og:title" content="PHP: Объектно-ориентированный дизайн">
<meta property="og:description" content="Объекты-Сущности, Объекты-Значения и встраиваемые объекты / PHP: Объектно-ориентированный дизайн: Знакомимся с основными принципами организации объектов, описывающих предметную область">
<meta property="og:url" content="https://ru.hexlet.io/courses/php-object-oriented-design/lessons/modeling/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="fdXoOXl5GGg0qpc6YmvTazAOncdXj3G614KheSJIbViSBCMOiwe1CILps6JuZCMc8AewbV-4jxhqYjstcE-KNg" />
<script src="/vite/assets/inertia-INZxX8jp.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-nkZBEvfU.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-6pOtQ3OW.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="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc1MSwicHVyIjoiYmxvYl9pZCJ9fQ==--e5793a1818ff43d73135cc7ed88c1998d7650470/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Developer%20activity-bro.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTIxNSwicHVyIjoiYmxvYl9pZCJ9fQ==--ee06f514098550b5ac7eebce6227b1543399a37c/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Hand%20coding-rafiki.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-26T16:40:25.355Z","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":"CV8p7GxggHiiGUeqrkDCyGHsrpwZrasni06MOwt1rZPmjuLbnh4tGBRaYzKiTzK_oeWDNhGaVYU2rhZvWXJK_Q","topics":[{"id":50513,"title":"Добрый день!\nКажется [тест](https://ru.hexlet.io/courses/php-object-oriented-design/lessons/modeling/exercise_unit) не запускается. Вывод:\n```\nmake: Entering directory '/usr/src/app'\nphpunit --colors=always tests\n\nPHPUnit 9.4.2 by Sebastian Bergmann and contributors.\n\nsyntax error, unexpected '[', expecting variable (T_VARIABLE)\n\nmake: *** [Makefile:2: test] Error 1\n\nmake: Leaving directory '/usr/src/app'\n```","plain_title":"Добрый день! Кажется тест (https://ru.hexlet.io/courses/php-object-oriented-design/lessons/modeling/exercise_unit) не запускается. Вывод: ``` make: Entering directory '/usr/src/app' phpunit --colors=always tests PHPUnit 9.4.2 by Sebastian Bergmann and contributors. syntax error, unexpected '[', expecting variable (T_VARIABLE) make: *** [Makefile:2: test] Error 1 make: Leaving directory '/usr/src/app' ``` ","creator":{"public_name":"Konstantin Lukyanenok","id":227517,"is_tutor":false},"comments":[{"creator":{"public_name":"Stanislav Dzisiak","id":212236,"is_tutor":true},"id":108171,"body":"Приветствую, Константин!\n\nВозможно в процессе работы над упражнением был изменён какой-то из файлов. Попробуйте выполнить сброс упражнения (справа кнопка) и заново проверить ваше решение. Только перед выполнением сброса сохраните ваш код, так как при сбросе он будет удалён. Если вопрос будет всё ещё актуален, то отправьте ваше решение на ревью, а в сообщение вложите на него ссылку. И будем дальше разбираться.","topic_id":50513}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Объекты-Сущности, Объекты-Значения и встраиваемые объекты","entity_url":null,"active":true}},{"id":51421,"title":"Не уверен, но пару раз сбрасывал задание и выходят ошибки линтера в тестовом файле - \n\n\nПоправил и всё отлично прошло. Там незначительные, отступы и пустые строки.\n","plain_title":"Не уверен, но пару раз сбрасывал задание и выходят ошибки линтера в тестовом файле - enter image description here https://gyazo.com/74662dfa5f25eb7ae04ee974f9cbea3d.png Поправил и всё отлично прошло. Там незначительные, отступы и пустые строки. ","creator":{"public_name":"Alexander Karakin","id":98341,"is_tutor":false},"comments":[{"creator":{"public_name":"Roman Ashikov","id":226258,"is_tutor":true},"id":109906,"body":"Приветствую!\n\nСпасибо! Пофиксил! Чтобы обновить упражнение, выполните сброс с помощью кнопки на панели справа. Только не забудьте сохранить решение, так как оно удаляется при сбросе.","topic_id":51421}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Объекты-Сущности, Объекты-Значения и встраиваемые объекты","entity_url":null,"active":true}},{"id":65981,"title":"**Как же бесят** вопросы, в которых нужно выбрать все правильные варианты ответов. Тут второй такой. Прям веслом хочется кого-нибудь по голове ударить. \nТупо перебираешь все что возможно после пары неверных попыток и потом уже изучаешь правильный вариант. \n\nКонкретно тут, почему вариант `Добавлен, чтобы красиво форматировать адреса` неправильный? ","plain_title":"Как же бесят вопросы, в которых нужно выбрать все правильные варианты ответов. Тупо перебираешь все что возможно после пары неверных попыток и потом уже изучаешь правильный вариант. ","creator":{"public_name":"Andrey","id":438142,"is_tutor":false},"comments":[{"creator":{"public_name":"Aleksandr Litvinov","id":117182,"is_tutor":true},"id":138913,"body":"Бить веслом никого не надо, но я вас понимаю :)\n\nВариант про красивое удалил, он действительно запутывает.","topic_id":65981}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Объекты-Сущности, Объекты-Значения и встраиваемые объекты","entity_url":null,"active":true}},{"id":55570,"title":"[Ревью](https://ru.hexlet.io/code_reviews/419120) \n1) App\\Tests\\UrlTest::testYandex\nFailed asserting that false is true.\n\n/usr/src/app/tests/UrlTest.php:33\nНе понимаю почему не проходят тесты. В repl.it всё нормально. В чём может быть причина?","plain_title":"Ревью (https://ru.hexlet.io/code_reviews/419120) 1) App\\Tests\\UrlTest::testYandex Failed asserting that false is true. /usr/src/app/tests/UrlTest.php:33 Не понимаю почему не проходят тесты. В repl.it всё нормально. В чём может быть причина? ","creator":{"public_name":"Константин Ситников","id":323584,"is_tutor":false},"comments":[{"creator":{"public_name":"Stanislav Dzisiak","id":212236,"is_tutor":true},"id":117926,"body":"Константин, день добрый!\n\nСейчас в ревью видно, что падает тест, который использует метод equals. Посмотрите в примере, в описании, что именно передаётся на вход в метод equals - `$url->equals(new Url('http://yandex.ru:80?key=value'))` и проверьте что какие значения сравниваются в реализации вашего метода equals.","topic_id":55570}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Объекты-Сущности, Объекты-Значения и встраиваемые объекты","entity_url":null,"active":true}},{"id":53743,"title":"Здравствуйте, разве public function toString() в теории не должно писаться с нижним подчеркиванием public function __toString()?","plain_title":"Здравствуйте, разве public function toString() в теории не должно писаться с нижним подчеркиванием public function __toString()? ","creator":{"public_name":"Igor Shuntov","id":227768,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":114439,"body":"Добрый день! Здесь мы создаем обычный метод `toString()`, а не магический `__toString()`. Посмотрите ниже на вызов этого метода:\n```\n$user->getAddress()->toString();\n```\nМетод вызывается, как обычный метод. Магический же метод `__toString()` вызывается автоматически тогда, когда объект пытаются использовать как строку.","topic_id":53743}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Объекты-Сущности, Объекты-Значения и встраиваемые объекты","entity_url":null,"active":true}},{"id":87770,"title":"В решении учителя получается что если поменять параметры url местами, но сохранить значения - метод equals возвращает false.\nХотя по сути оба url ведут в одно и то же место, с одинаковыми параметрами (просто в разном порядке) и должно возвращаться true.\n\nЯ слишком заморочился, или это небольшой недочёт?","plain_title":"В решении учителя получается что если поменять параметры url местами, но сохранить значения - метод equals возвращает false. Хотя по сути оба url ведут в одно и то же место, с одинаковыми параметрами (просто в разном порядке) и должно возвращаться true. Я слишком заморочился, или это небольшой недочёт? ","creator":{"public_name":"Евгений Ашурков","id":480411,"is_tutor":false},"comments":[{"creator":{"public_name":"Roman Ashikov","id":226258,"is_tutor":true},"id":175577,"body":"Имеете в виду из URL:\n\n http://yandex.ru?key=value&key2=value2\n\nСделать URL вида:\n\n http://yandex.ru?key2=value2&key=value\n\nЯ проверил. Решение работает. Можете сами поменять URL в тестах и запустить проверку. Решение учителя их пройдет.","topic_id":87770}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Объекты-Сущности, Объекты-Значения и встраиваемые объекты","entity_url":null,"active":true}},{"id":88739,"title":"Добрый день, насколько я понимаю, в теории обсуждался Value Object. В задаче от учителя, работа ведется через ассоциативный массив (url). И к сожалению, не смог проверить правильность понимания Объект-Значение.\n\nЕсли следовать теории, как раз url-адресс может быть этим самым Объект-Значением?\n\n```\nclass UrlAddress\n{\n public string $scheme;\n public string $hostname;\n public array $queryParams;\n\n public function __construct(string $scheme, string $hostname, array $queryParams)\n {\n $this->scheme = $scheme;\n $this->hostname = $hostname;\n $this->queryParams = $queryParams;\n }\n}\n```","plain_title":"Добрый день, насколько я понимаю, в теории обсуждался Value Object. В задаче от учителя, работа ведется через ассоциативный массив (url). И к сожалению, не смог проверить правильность понимания Объект-Значение. Если следовать теории, как раз url-адресс может быть этим самым Объект-Значением? class UrlAddress { public string $scheme; public string $hostname; public array $queryParams; public function __construct(string $scheme, string $hostname, array $queryParams) { $this->scheme = $scheme; $this->hostname = $hostname; $this->queryParams = $queryParams; } } ","creator":{"public_name":"Oo11o","id":414463,"is_tutor":false},"comments":[{"creator":{"public_name":"Roman Ashikov","id":226258,"is_tutor":true},"id":177053,"body":"Приветствую! Да, вы правильно понимаете. В теории объектно-ориентированного программирования Value Object - это объект, который используется для представления значения. Класс UrlAddress может быть использован как Value Object для представления URL-адреса. ","topic_id":88739}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Объекты-Сущности, Объекты-Значения и встраиваемые объекты","entity_url":null,"active":true}},{"id":54431,"title":"Пссс всем новичкам как и я, в php есть функция парса url.","plain_title":"Пссс всем новичкам как и я, в php есть функция парса url. ","creator":{"public_name":"Kirill Zyryanov","id":262179,"is_tutor":false},"comments":[{"creator":{"public_name":"Кирилл Черный","id":307105,"is_tutor":false},"id":181311,"body":"Спасибо тебе, добрый человек! Даже если ты не увидишь этот комментарий, пусть вселенная пошлет тебе лучи добра! А то я чуть уже сам не начал парсинг URL писать )))","topic_id":54431}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Объекты-Сущности, Объекты-Значения и встраиваемые объекты","entity_url":null,"active":true}},{"id":102816,"title":"Почему в теории при создании класса User свойства класса определяются в конструкторе\n~~~\nclass User\n{\n public function __construct($id, $name)\n {\n $this->id = $id;\n $this->name = $name;\n...\n~~~\n\nа линтер при похожем решении задачи\n~~~\npublic function __construct($url)\n {\n $this->url = $url;\n~~~\nругается:\n\n> Deprecated: Creation of dynamic property App\\Url::$url is deprecated in /usr/src/app/src/Url.php on line 10\n\nСтоит поправить теорию?","plain_title":"Почему в теории при создании класса User свойства класса определяются в конструкторе ~~~ class User { public function __construct($id, $name) { $this->id = $id; $this->name = $name; ... ~~~ а линтер при похожем решении задачи ~~~ public function __construct($url) { $this->url = $url; ~~~ ругается: Deprecated: Creation of dynamic property App\\Url::$url is deprecated in /usr/src/app/src/Url.php on line 10 Стоит поправить теорию? ","creator":{"public_name":"Сергей Кавардак","id":88933,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":195159,"body":"Приветствую, Сергей! Да, динамическое создание свойств класса стало считаться устаревшим в всерсии 8.2. Нужно явно их объявить. Поправлю примеры, спасибо","topic_id":102816}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Объекты-Сущности, Объекты-Значения и встраиваемые объекты","entity_url":null,"active":true}},{"id":103803,"title":"Добрый день, не очень понял фразу:\n\n> При такой организации хранения, иногда, возникают ситуации, когда часть свойств сущности описывают собой что-то одно. Распространенный пример – почтовый адрес:\n\n```\n// Поиск из базы по идентификатору\n// Гипотетический код\n$user = $users->find(5);\n\n$user->street; // 'lenina'\n$user->zipcode; // 432111\n$user->house; // 10\n```\n\nГде тут совпадающие свойства сущности? Вроде все свойства разные","plain_title":"Добрый день, не очень понял фразу: При такой организации хранения, иногда, возникают ситуации, когда часть свойств сущности описывают собой что-то одно. Распространенный пример – почтовый адрес: // Поиск из базы по идентификатору // Гипотетический код $user = $users->find(5); $user->street; // 'lenina' $user->zipcode; // 432111 $user->house; // 10 Где тут совпадающие свойства сущности? Вроде все свойства разные ","creator":{"public_name":"Михаил М","id":154628,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":196362,"body":"> Как связано то, что у нас есть совпадение по сущностям и организация кода в виде объекта\n\nНе очень понял вопрос\n\n","topic_id":103803},{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":196285,"body":"Речь не о совпадающих свойствах, а о том, что несколько свойств сущности описывают по сути одну другую сущность, в данном случае это адрес:\n\n```php\n$user->street; // 'lenina'\n$user->zipcode; // 432111\n$user->house; // 10\n```","topic_id":103803},{"creator":{"public_name":"Михаил М","id":154628,"is_tutor":false},"id":196330,"body":"Понятно, спасибо. Вообще, не совсем очевидное, на мой взгляд, описание, что они описывают собой одну сущность именно в базе.\n\nТогда еще один вопрос:\n\nКак связано то, что у нас есть совпадение по сущностям и организация кода в виде объекта (в случае с теорией Address)?\n\nP.S.\nЯ понимаю логику передачи объекта (Address) в свойство и с его помощью снижения количества повторяющегося кода при создании других классов","topic_id":103803}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Объекты-Сущности, Объекты-Значения и встраиваемые объекты","entity_url":null,"active":true}}],"lesson":{"exercise":{"id":1364,"slug":"php_object_oriented_design_modeling_exercise","name":null,"state":"active","kind":"exercise","language":"php","locale":"ru","has_web_view":false,"has_test_view":false,"reviewable":true,"readme":"## src/Url.php\n\nВ данном упражнении вам предстоит реализовать класс `Url`, который позволяет извлекать из HTTP адреса, представленного строкой, его части.\n\nКласс должен содержать конструктор и методы:\n\n* конструктор - принимает на вход HTTP адрес в виде строки.\n* `getScheme()` - возвращает протокол передачи данных (без двоеточия).\n* `getHostName()` - возвращает имя хоста.\n* `getQueryParams()` - возвращает параметры запроса в виде пар ключ-значение объекта.\n* `getQueryParam()` - получает значение параметра запроса по имени. Если параметр с переданным именем не существует, метод возвращает значение заданное вторым параметром (по умолчанию равно `null`).\n* `equals($url)` - принимает объект класса `Url` и возвращает результат сравнения с текущим объектом - `true` или `false`.\n\n```php\n<?php\n\nuse App\\Url;\n\n$url = new Url('http://yandex.ru:80?key=value&key2=value2');\n$url->getScheme(); // 'http'\n$url->getHostName(); // 'yandex.ru'\n$url->getQueryParams();\n// [\n// 'key' => 'value',\n// 'key2' => 'value2',\n// ];\n$url->getQueryParam('key'); // 'value'\n// второй параметр - значение по умолчанию\n$url->getQueryParam('key2', 'lala'); // 'value2'\n$url->getQueryParam('new', 'ehu'); // 'ehu'\n$url->getQueryParam('new'); // null\n$url->equals(new Url('http://yandex.ru:80?key=value&key2=value2')); // true\n$url->equals(new Url('http://yandex.ru:80?key=value')); // false\n```\n","prepared_readme":"## src/Url.php\n\nВ данном упражнении вам предстоит реализовать класс `Url`, который позволяет извлекать из HTTP адреса, представленного строкой, его части.\n\nКласс должен содержать конструктор и методы:\n\n* конструктор - принимает на вход HTTP адрес в виде строки.\n* `getScheme()` - возвращает протокол передачи данных (без двоеточия).\n* `getHostName()` - возвращает имя хоста.\n* `getQueryParams()` - возвращает параметры запроса в виде пар ключ-значение объекта.\n* `getQueryParam()` - получает значение параметра запроса по имени. Если параметр с переданным именем не существует, метод возвращает значение заданное вторым параметром (по умолчанию равно `null`).\n* `equals($url)` - принимает объект класса `Url` и возвращает результат сравнения с текущим объектом - `true` или `false`.\n\n```php\n<?php\n\nuse App\\Url;\n\n$url = new Url('http://yandex.ru:80?key=value&key2=value2');\n$url->getScheme(); // 'http'\n$url->getHostName(); // 'yandex.ru'\n$url->getQueryParams();\n// [\n// 'key' => 'value',\n// 'key2' => 'value2',\n// ];\n$url->getQueryParam('key'); // 'value'\n// второй параметр - значение по умолчанию\n$url->getQueryParam('key2', 'lala'); // 'value2'\n$url->getQueryParam('new', 'ehu'); // 'ehu'\n$url->getQueryParam('new'); // null\n$url->equals(new Url('http://yandex.ru:80?key=value&key2=value2')); // true\n$url->equals(new Url('http://yandex.ru:80?key=value')); // false\n```\n","has_solution":true,"entity_name":"Объекты-Сущности, Объекты-Значения и встраиваемые объекты"},"units":[{"id":4235,"name":"theory","url":"/courses/php-object-oriented-design/lessons/modeling/theory_unit"},{"id":4236,"name":"quiz","url":"/courses/php-object-oriented-design/lessons/modeling/quiz_unit"},{"id":4237,"name":"exercise","url":"/courses/php-object-oriented-design/lessons/modeling/exercise_unit"}],"links":[],"ordered_units":[{"id":4235,"name":"theory","url":"/courses/php-object-oriented-design/lessons/modeling/theory_unit"},{"id":4236,"name":"quiz","url":"/courses/php-object-oriented-design/lessons/modeling/quiz_unit"},{"id":4237,"name":"exercise","url":"/courses/php-object-oriented-design/lessons/modeling/exercise_unit"}],"id":1882,"slug":"modeling","state":"approved","name":"Объекты-Сущности, Объекты-Значения и встраиваемые объекты","course_order":250,"goal":"Знакомимся с основными принципами организации объектов, описывающих предметную область","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"Чаще всего когда говорят про ООП, то рассуждают про сущности предметной области, например пользователи, заказы, товары и тому подобное. У такого использования объектов есть определенные условия, которые должны соблюдаться для обеспечения нормального функционирования.\n\n*Стоит сказать, что подобное использование ООП, хоть и описывается во всех учебниках как пример нужности ООП, имеет слабое отношение к реальному коду. На практике, большинство существующих классов и объектов в коде приложений, библиотек и фреймворков не имеют никакой связи с предметной областью. Их появление и использование крутится вокруг такой темы как полиморфизм, которая изучается в соответствующем [курсе](https://ru.hexlet.io/courses/php-polymorphism).*\n\n**Время жизни**. Подобные объекты создаются не ради одноразового использования, а живут какое-то время во время запуска программы или, что чаще, между запусками в каком-то хранилище. Например, пользователи на Хекслете представлены объектами класса User. Они создаются во время регистрации и потом существуют в системе бесконечное время. Изредка они удаляются по инициативе самих пользователей.\n\n**Идентификация**. Каким образом один пользователь отличается от другого? На первый взгляд, кажется, что можно использовать имя и фамилию. Но если разобраться, то никакой набор параметров не даст 100% надежности с одной стороны, а с другой они все могут измениться и точно изменятся со временем. Поэтому при работе с сущностями вводят искусственные идентификаторы, которые, как правило, формирует база данных. Затем сравнение происходит именно по ним.\n\n```php\n<?php\n\nclass User\n{\n public function __construct($id, $name)\n {\n $this->id = $id;\n $this->name = $name;\n }\n \n public function equals($user)\n {\n return $this->id === $user->id;\n }\n}\n\n// С точки зрения нашей системы это один и тот же пользователь\n// С точки зрения PHP – разные объекты\n$user1 = new User(3, 'mike');\n$user2 = new User(3, 'mike');\n\n$user3 = new User(1, 'mike');\n\n// Подобная схема проверки существует во всех ORM\n$user1->equals($user2); // true\n$user1->equals($user3); // false\n```\n\n*Это очень напоминает механизм строгого сравнения объектов в PHP. Они сравниваются не по совпадению данных, а по ссылке, которая внутри представлена каким-то числовым значением. Поэтому разные объекты хранящие одинаковые данные это всегда разные объекты, что логично.*\n\nОбъекты имеющие свою идентификацию и время жизни называют объектами-сущностями (entities), но кроме них существует и другая разновидность объектов, тоже, как правило, связанная с предметной областью – это объекты-значения. Что это?\n\n## Объекты-Значения (value objects)\n\nКогда у нас в кошельке лежит 10$, то нам не важно какая конкретно это купюра. Мы легко можем взять эту купюру, поменять ее на другую эквивалентную по номиналу. Для нас в этот момент ничего не меняется. Десять долларов остались десятью долларами. То же самое можно сказать и про многое другое, например: адреса доставки, страна проживания, путь до файла, адрес страницы сайта, точки на плоскости. Во всех этих ситуациях нас волнует само значение, сам факт его существования.\n\nПредставьте себе систему в которой идет работа с деньгами. Причем в разных валютах. В такой ситуации удобно представить деньги в виде объекта, который помимо номинала хранит информацию о валюте. Как в таком случае должно работать сравнение?\n\n```php\n<?php\n\n$m1 = new Money(150, 'usd');\n$m2 = new Money(130, 'eur');\n\n// Предположим что 150 долларов по текущему курсу равны 130 евро\n// Функция конвертирует деньги для сравнения\n$m1->equals($m2); // true\n```\n\nДанный код выражает озвученную ранее идею. Нам не важны объекты, нам важны значения. Объект здесь служит лишь способом организации кода, но он никак не идентифицирует хранящиеся внутри него данные. Такие объекты называют [объектами-значениями](https://martinfowler.com/bliki/ValueObject.html).\n\n*Если говорить откровенно, то реальность чуть сложнее. Одни и те же вещи могут быть как объектами-значениями так и объектами-сущностями. Всё зависит от конкретной предметной области. Для большинства компаний деньги - это действительно просто значения, но не для тех кто их печатает. Им очень важно различать купюры между собой и поэтому на каждой из них есть уникальный номер, который и позволяет проводить идентификацию.*\n\nОбъекты-значения – искусственная штука. Часто они не нужны и достаточно пользоваться простым значением, особенно если оно примитивное. С другой стороны, когда значение составное, такое как точка на плоскости, адрес или адрес страницы сайта (она состоит из многих частей), подобные объекты помогают упростить код за счет удобной абстракции.\n\n## Встраиваемые объекты (embedded objects)\n\nКак правило, данные, с которыми работают веб-приложения, хранятся в реляционных базах данных. В них каждая сущность представляется строкой в таблице, где каждое поле соответствует свойству объекта. При такой организации хранения, иногда, возникают ситуации, когда часть свойств сущности описывают собой что-то одно. Распространенный пример – почтовый адрес:\n\n```php\n<?php\n\n// Поиск из базы по идентификатору\n// Гипотетический код\n$user = $users->find(5);\n\n$user->street; // 'lenina'\n$user->zipcode; // 432111\n$user->house; // 10\n```\n\nСуществует два подхода для работы с такими данными. Первый, любая логика по работе с этими данными описывается внутри самой сущности. Например, вывод адреса в виде текста:\n\n```php\n<?php\n\nclass User\n{\n // Где-то здесь конструктор и другие методы\n\n public function getFullAddress()\n {\n return \"{$this->street}, {$this->house}, {$this->zipcode}\";\n }\n}\n\n$user->getFullAddress();\n```\n\nГлавная проблема здесь — возможное дублирование, если адрес встречается где-то еще, кроме пользователя. Тогда придется реализовывать методы по работе с этими данными везде, где они встречаются.\n\nВторой подход – создать отдельный класс и **внедрить** объект этого класса в основной объект. Звучит страшно, но на практике очень просто:\n\n```php\n<?php\n\nclass Address\n{\n public function __construct($street, $house, $zipcode)\n {\n $this->street = $street;\n $this->house = $house;\n $this->zipcode = $zipcode;\n }\n\n public function toString()\n {\n return \"{$this->street}, {$this->house}, {$this->zipcode}\";\n }\n}\n\nclass User\n{\n // Где-то здесь конструктор и другие методы\n\n public function getAddress()\n {\n // Так как у нас объект-значение,\n // то можно, не боясь, создавать его любое количество раз,\n // но в случае необходимости можно этот процесс оптимизировать\n return new Address($this->street, $this->house, $this->zipcode);\n }\n}\n\n$user->getAddress()->toString();\n```\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":2389,"name":"theory","url":"/courses/php-object-oriented-design/lessons/about/theory_unit"}],"links":[],"ordered_units":[{"id":2389,"name":"theory","url":"/courses/php-object-oriented-design/lessons/about/theory_unit"}],"id":1141,"slug":"about","state":"approved","name":"О курсе","course_order":10,"goal":"Знакомимся с целями и задачами курса","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"Если предыдущий курс был о том, как описывать абстракции с помощью классов, то в этом упор делается на использование объектов с примерами реальных библиотек. Построение цепочек вызовов, работа со структурами данных, оперирование датами и строками в объектно-ориентированном стиле — это неполный перечень того, с чем мы столкнёмся.\n\nВ этом курсе не изучается новый синтаксис, его и так было очень много. ООП — сложная тема, и вводить новые понятия нужно достаточно осторожно, набравшись немного опыта работы с основными концепциями. Отдельной темой, проходящей красной нитью сквозь все дальнейшее обучение являются шаблоны проектирования или паттерны — подходы для решения типовых задач.\n\n* Текучий интерфейс (Fluent Interface)\n* stdClass — встроенный в PHP класс, который автоматически используется при преобразовании типов\n* Структуры данных — ОО-версии популярных структур данных.\n* PHPUnit — фреймворк для тестирования в PHP\n* Collect — библиотека для работы с коллекциями в ОО-стиле\n* Carbon — библиотека для работы с датами в ОО-стиле\n* Stringy — библиотека для работы со строками в ОО-стиле\n"},"id":164,"slug":"php-object-oriented-design","challenges_count":4,"name":"PHP: Объектно-ориентированный дизайн","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"paid","description":"На этом курсе вы познакомитесь с объектно ориентированным дизайном. Вы узнаете о паттернах, текучем интерфейсе и полезных популярных библиотеках. В итоге поймете, как правильно организовывать код, написанный на классах. Знания объектно ориентированного дизайна пригодятся, чтобы создавать и использовать цепочки функций, в том числе в неизменяемом стиле. Также они помогут правильно организовывать состояние объектов и проектировать безопасные сеттеры.","kind":"advanced","updated_at":"2026-01-20T11:47:15.653Z","language":"php","duration_cache":50040,"skills":["Пользоваться популярными библиотеками для работы со строками, датами и коллекциями","Создавать и использовать цепочки функций (включая неизменяемый стиль)","Научитесь отличать классы для сущностей от классов для конфигурации","Сможете организовать безопасную работу с сеттерами"],"keywords":["collect","phpunit","паттерны","текучий интерфейс","stdclass"],"lessons_count":13,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NzgxMSwicHVyIjoiYmxvYl9pZCJ9fQ==--4a04c51cf0e4505e5da608caa709fe29b58d05e4/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"},{"stack":{"id":25,"slug":"php-oop","title":"ООП В PHP","audience":"for_programmers","start_type":"anytime","pricing_model":"subscription","priority":"medium","kind":"track","state":"published","stack_state":"finished","order":4300,"duration_in_months":2},"id":38,"slug":"php-oop","title":"ООП В PHP","subtitle":"Навык глубокого понимания архитектуры и написания чистого кода, позволяющий решать сложные задачи","subtitle_for_lists":"Изучите архитектуру и чистый код на PHP","locale":"ru","current":true,"duration_in_months_text":"2 месяца","stack_slug":"php-oop","price_text":"от 3 900 ₽","duration_text":"2 месяца","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc1MSwicHVyIjoiYmxvYl9pZCJ9fQ==--e5793a1818ff43d73135cc7ed88c1998d7650470/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Developer%20activity-bro.png"},{"stack":{"id":468,"slug":"middle-php","title":"Middle-php разработчик","audience":"for_programmers","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"not_finished","order":2003,"duration_in_months":5},"id":596,"slug":"middle-php","title":"Middle-php разработчик","subtitle":"Освоите продвинутую работу с БД и серверной логикой","subtitle_for_lists":"Освоите продвинутую работу с БД и серверной логикой","locale":"ru","current":true,"duration_in_months_text":"5 месяцев","stack_slug":"middle-php","price_text":"от 4 050 ₽","duration_text":"5 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTIxNSwicHVyIjoiYmxvYl9pZCJ9fQ==--ee06f514098550b5ac7eebce6227b1543399a37c/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Hand%20coding-rafiki.png"}],"lessonMemberUnit":null,"accessToLearnUnitExists":false,"accessToCourseExists":false},"url":"/courses/php-object-oriented-design/lessons/modeling/theory_unit","version":"0b0c6d4ebbd40fd58630a0dd89cc25544ccdf24e","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">Теория: Объекты-Сущности, Объекты-Значения и встраиваемые объекты</h1><script type="application/ld+json">{"@context":"https://schema.org","@type":"LearningResource","name":"Объекты-Сущности, Объекты-Значения и встраиваемые объекты","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>Чаще всего когда говорят про ООП, то рассуждают про сущности предметной области, например пользователи, заказы, товары и тому подобное. У такого использования объектов есть определенные условия, которые должны соблюдаться для обеспечения нормального функционирования.</p>
<p><em>Стоит сказать, что подобное использование ООП, хоть и описывается во всех учебниках как пример нужности ООП, имеет слабое отношение к реальному коду. На практике, большинство существующих классов и объектов в коде приложений, библиотек и фреймворков не имеют никакой связи с предметной областью. Их появление и использование крутится вокруг такой темы как полиморфизм, которая изучается в соответствующем <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.hexlet.io/courses/php-polymorphism" rel="noopener noreferrer" target="_blank">курсе</a>.</em></p>
<p><strong>Время жизни</strong>. Подобные объекты создаются не ради одноразового использования, а живут какое-то время во время запуска программы или, что чаще, между запусками в каком-то хранилище. Например, пользователи на Хекслете представлены объектами класса User. Они создаются во время регистрации и потом существуют в системе бесконечное время. Изредка они удаляются по инициативе самих пользователей.</p>
<p><strong>Идентификация</strong>. Каким образом один пользователь отличается от другого? На первый взгляд, кажется, что можно использовать имя и фамилию. Но если разобраться, то никакой набор параметров не даст 100% надежности с одной стороны, а с другой они все могут измениться и точно изменятся со временем. Поэтому при работе с сущностями вводят искусственные идентификаторы, которые, как правило, формирует база данных. Затем сравнение происходит именно по ним.</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
class User
{
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
public function equals($user)
{
return $this->id === $user->id;
}
}
// С точки зрения нашей системы это один и тот же пользователь
// С точки зрения PHP – разные объекты
$user1 = new User(3, 'mike');
$user2 = new User(3, 'mike');
$user3 = new User(1, 'mike');
// Подобная схема проверки существует во всех ORM
$user1->equals($user2); // true
$user1->equals($user3); // false</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><em>Это очень напоминает механизм строгого сравнения объектов в PHP. Они сравниваются не по совпадению данных, а по ссылке, которая внутри представлена каким-то числовым значением. Поэтому разные объекты хранящие одинаковые данные это всегда разные объекты, что логично.</em></p>
<p>Объекты имеющие свою идентификацию и время жизни называют объектами-сущностями (entities), но кроме них существует и другая разновидность объектов, тоже, как правило, связанная с предметной областью – это объекты-значения. Что это?</p>
<h2 id="heading-2-1">Объекты-Значения (value objects)</h2>
<p>Когда у нас в кошельке лежит 10$, то нам не важно какая конкретно это купюра. Мы легко можем взять эту купюру, поменять ее на другую эквивалентную по номиналу. Для нас в этот момент ничего не меняется. Десять долларов остались десятью долларами. То же самое можно сказать и про многое другое, например: адреса доставки, страна проживания, путь до файла, адрес страницы сайта, точки на плоскости. Во всех этих ситуациях нас волнует само значение, сам факт его существования.</p>
<p>Представьте себе систему в которой идет работа с деньгами. Причем в разных валютах. В такой ситуации удобно представить деньги в виде объекта, который помимо номинала хранит информацию о валюте. Как в таком случае должно работать сравнение?</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
$m1 = new Money(150, 'usd');
$m2 = new Money(130, 'eur');
// Предположим что 150 долларов по текущему курсу равны 130 евро
// Функция конвертирует деньги для сравнения
$m1->equals($m2); // true</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>Данный код выражает озвученную ранее идею. Нам не важны объекты, нам важны значения. Объект здесь служит лишь способом организации кода, но он никак не идентифицирует хранящиеся внутри него данные. Такие объекты называют <a style="text-decoration:underline" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="https://martinfowler.com/bliki/ValueObject.html" rel="noopener noreferrer" target="_blank">объектами-значениями</a>.</p>
<p><em>Если говорить откровенно, то реальность чуть сложнее. Одни и те же вещи могут быть как объектами-значениями так и объектами-сущностями. Всё зависит от конкретной предметной области. Для большинства компаний деньги - это действительно просто значения, но не для тех кто их печатает. Им очень важно различать купюры между собой и поэтому на каждой из них есть уникальный номер, который и позволяет проводить идентификацию.</em></p>
<p>Объекты-значения – искусственная штука. Часто они не нужны и достаточно пользоваться простым значением, особенно если оно примитивное. С другой стороны, когда значение составное, такое как точка на плоскости, адрес или адрес страницы сайта (она состоит из многих частей), подобные объекты помогают упростить код за счет удобной абстракции.</p>
<h2 id="heading-2-2">Встраиваемые объекты (embedded objects)</h2>
<p>Как правило, данные, с которыми работают веб-приложения, хранятся в реляционных базах данных. В них каждая сущность представляется строкой в таблице, где каждое поле соответствует свойству объекта. При такой организации хранения, иногда, возникают ситуации, когда часть свойств сущности описывают собой что-то одно. Распространенный пример – почтовый адрес:</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
// Поиск из базы по идентификатору
// Гипотетический код
$user = $users->find(5);
$user->street; // 'lenina'
$user->zipcode; // 432111
$user->house; // 10</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 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
class User
{
// Где-то здесь конструктор и другие методы
public function getFullAddress()
{
return "{$this->street}, {$this->house}, {$this->zipcode}";
}
}
$user->getFullAddress();</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>
<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
class Address
{
public function __construct($street, $house, $zipcode)
{
$this->street = $street;
$this->house = $house;
$this->zipcode = $zipcode;
}
public function toString()
{
return "{$this->street}, {$this->house}, {$this->zipcode}";
}
}
class User
{
// Где-то здесь конструктор и другие методы
public function getAddress()
{
// Так как у нас объект-значение,
// то можно, не боясь, создавать его любое количество раз,
// но в случае необходимости можно этот процесс оптимизировать
return new Address($this->street, $this->house, $this->zipcode);
}
}
$user->getAddress()->toString();</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></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="/programs/php-oop?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">2 месяца</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">ООП В PHP</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите архитектуру и чистый код на PHP</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/eyJfcmFpbHMiOnsiZGF0YSI6Mzc1MSwicHVyIjoiYmxvYl9pZCJ9fQ==--e5793a1818ff43d73135cc7ed88c1998d7650470/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Developer%20activity-bro.png" alt="ООП В PHP" 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">от 3 900 ₽</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="/programs/middle-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">5 месяцев</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">Middle-php разработчик</p><p 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="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTIxNSwicHVyIjoiYmxvYl9pZCJ9fQ==--ee06f514098550b5ac7eebce6227b1543399a37c/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Hand%20coding-rafiki.png" alt="Middle-php разработчик" 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">от 4 050 ₽</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-object-oriented-design/lessons/modeling/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 / 13</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-object-oriented-design/lessons/modeling/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-CdBlNCiQ.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-nkZBEvfU.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>