Добро пожаловать. Тут мы изучаем язык PHP (а также JS/CSS/HTML/SQL) и учимся делать сайты. Зачем? Кто-то хочет открыть стартап, кто-то заработать на лапшу быстрого приготовления, кому-то просто нечего делать.
Это тред для начинающих. Слово «классы» у тебя ассоциируется только со школой, а в аттестате тройка по математике? Ты наш человек.
Также, у нас есть задачи которые позволят тебе изучить или подтянуть до нормального уровня знания JS/HTML/CSS/SQL. Решай их параллельно с задачами выше.
- скачать учебник: зайди на https://github.com/codedokode/phpbook, нажми зеленую кнопку Code -> Download ZIP, распакуй на рабочий стол и открой index.html - что будут спрашивать на собеседовании, если 0 опыта - будут гонять по теории, по официальному мануалу PHP, давать дурацкие задачки на переворачивание строк, гонять по SQL (транзакции, внешние ключи, напиши запрос), по JS (как сделать анимацию при нажатии кнопки), ну погугли, не ленись - сколько времени надо изучать все это? - все зависит от тебя, в районе 12-24 месяцев
Опять в шапке написано не то! Самое важное забыли написать! Сколько можно это терпеть? Выскажи свое справедливое негодование под этим постом и через несколько тредов шапку исправят (но это не точно).
В куках он нужен только когда ты хочешь в одну куку поместить сложные данные, а иначе не нужен. А так JSON используется для передачи данных на сервер и с сервера, например, при AJAX запросах.
Я понимаю что сложно, инструкция то не для пхп треда писалась >1. add -javaagent:/path/to/ja-netfilter.jar=jetbrains to your vmoptions >/path/to/ja-netfilter.jar >ja-netfilter.jar
Как научиться докеру и композеру и поставить код сниффер или другой линтер? Какие пср обычно используются? Надо ли их как-то запоминать или хватит линтера? Есть норм гайд с разжёвыванием где как и чего.
>>2933206 >Как научиться докеру и композеру и поставить код сниффер или другой линтер? В смысле как? Читаешь и выполняешь на практике. Лучший обучающий ресурс на мой взгляд - Хекслет. Начни бесплатный курс по основам PHP https://ru.hexlet.io/courses/php-basics
>>2933196 Ты промазал. Тебе сюда >>2932791 Челик сказал что на офф сайт у него "не заходит". Я напомнил где качается весь платный софт последние лет двадцать.
>>2933685 Он спросил как научиться. Не только докеру и композеру. Я дал не рекламу, а бесплатный курс, большой хороший бесплатный курс. Была бы реклама, вставил бы реферальную ссылку. И еще, как в прошлом посте написал, считаю этот ресурс лучшим по всем показателям.
>>2933816 >базовый курс, который проходится за пару часов Ебанат, там 42 урока и 47 практических упражнений. Ты за 2 часа даже теорию не успеешь прочитать, а уж новичок и подавно. Там есть про PSR и линтеры. И вообще, пошел на хуй, умник ебаный, не для тебя ссылка.
>>2933981 Там про PSR и линтеры 2 строчки написано, а основного - как всё это скрестить с докером и компотом нет. Про "что это такое" я и в википедии прочитаю без твоего ебучего курса говна.
>>2933992 Как будто мне надо учиться, а не тебе. Хорошо, что такие дураки сразу отсеиваются. Между прочим, это единственное место где есть курс СИКП на PHP и еще некоторые по теме абстракции данных. Хотя тебе это знать бесполезно.
У меня есть класс-сущность, у него приватные свойства, геттер и сеттер. Значения в свойствах должна пройти валидацию. Должен ли я делать посредника между сущностью и валидатором? Или же валидатору достаточно напрямую обращаться к сущности через геттер, чтобы брать ее значения и проверять?
Уже третий день(!) не могу сделать элементарное: отправить через jQuery AJAX в Ларавел данные вместе с файлами. Если собираю данные так: let form = $(this).closest('form') let formData = form.serialize(); и отправляю formData, то всё ок. Они принимаются контроллером. Если же пытаюсь добавить туда файл - не получается. В контролер приходит пустота. Ни файла, ни текстовых данных, ничего. Гуглил и пробовал чуть ли не всё - не помогает. Не работают у меня эти примеры. Напомню, мне нужна отправка именно через аякс. Я спрашивал ЧатГПТ - советует то же самое, что и гугл. Спрашивал на стековерфло - тупо закрыли вопрос, с отпиской "такое уже спрашивали". Но там где спрашивали, мне тоже ни один из примеров не помог. Я в тотальном тупике. Это срань какая-то. Настолько простое действие, но у меня третий, сука, день, не получается понять, что вообще тут блядь не так.
Везде пишут, что-то типа let form = $(this).closest('form')[0]; // Получаем нативный элемент формы let formData = new FormData(form);// Создаем объект FormData и отправляем её в аякс без обработки: processData: false, // Не обрабатываем данные (нужно для передачи файлов) contentType: false, // Не устанавливаем Content-Type (также нужно для передачи файлов) data: formData, // Передаем объект FormData А в ларе получаем: $title = $request->input('title'); Но нет. У меня в ларе title = null.
>>2935272 И да ты не можешь бинардые данные жсоном отправлять, если хочешь json то надо файлы кодировать в base64 а на бекенде раскодировать. Например в мидлеваре ларки. Но это нахуй не надо.
Зачем нужны интерфейсы? Извините за такой вкатунский вопрос, но я серьезно не понимаю практического смысла в интерфейсах. Код без интерфейсов это плохой ООП-код?
>>2935304 Если я формирую данные для отправки так: const form = document.querySelector("#property-form") const formData = new FormData(form) , то бинарник появится. В консоле браузера, да. А ларавел его не видит. Вообще никакие поля. Все данные пстые. Файла нет, текстовых полей нет.
>>2935432 Анон, СПАСИБО! Реально PUT не анализируется. Передаю постом с добавлением formData.append('_method', 'PUT') - все данные проходят на бек нормально. А я почти три дня ебался. Браузер говорит "мамой клянусь, я отправил!", а ларавел "мамой клянусь, я не получал!". И так почти три дня. Я уже начал подумывать, что с ума сошёл. Перегуглил всё что можно, даже ютуб смотрел от индусов и французов каких-то.
Мне в прошлом треде посоветовали для каждой проверки в валидации (проверка на пустоту, на минимальное количество символов, на максимальное количество символов, на уникальность емайл, на качество символов) сделать свой отдельный класс (как вариант). Только вот три класса - проверка на пустоту, на мин. и макс. количество символов будут почти похожи. Получается, что я дублирую код, пусть и частично (вся ведь проверка заключается в том, чтобы узнать сколько символов в строке и сравнить с каким-то стандартом). С одной стороны вроде как это разные проверки, но руки тянутся скинуть все это в одну кучу. Как мне поступить, знающие аноны?
>>2935489 Когда говорят о "дублировании кода" говорят о "дублировании функционала". Причем именно "бизнес функционала". Ты же не сидишь и не думаешь: "ах бля беда какая я дублирую использование сложения и объединения строк, я ДВАЖДЫ написал foreach, мне пизда" ну я на это надеюсь
И вообще ты сидишь и думаешь о хуйне. У тебя будут сотни классов и тысячи методов, нет никакого смысла их считать. Важно чтобы их было удобно читать. Сам факт что что у тебя есть код, который явно выделен как "проверка строки на пустоту" важнее всего остального.
В тоже же время при отправке с multipart/form-data php://input недоступен https://www.php.net/manual/en/wrappers.php.php Соответственно нужен заголовок Content-Type: application/x-www-form-urlencoded
Типа есть оправдание, что в пыхе все строго RFC POST - это для процесса обрабатывающего данные. URI идентифицирует обработчик PUT - для размещения файла на сервере. URI идентифицирует сам ресурс Соответственно пут не должен парсить (обрабатывать) данные,
>>2935505 Окей... Просто у меня есть класс, проверяющий на пустоту Он считает количество символов и сравнивает с нулем. Другой класс проверяет на минимальное количество символов. Он считает количество символов и сравнивает с...минимальным значением для этого поля. Следующий класс проверяет на максимальное количество символов. Он считает количество символов и сравнивает с максимальным значением символов для этого поля. Немного в глаза бросается эта общая схожесть действий.
>>2935760 >Просто у меня есть класс. Он использует умножение. >Просто у меня есть класс. Он использует перебор массива. >Просто у меня есть класс. Он использует array_sum. Что такого особенного в этом "подсчете символов"? Там какие-то специальные правила, про которые знает только написавший этот подсчет? Допустим там ебейшее дублирование, страшное. Как по твоему это решается? Созданием еще одного класса, в который помещается дублирующаяся логика. Так что классов у тебя один хуй станет только больше.
>>2935791 Да, я думал сделать один класс и назвать "Проверка на количество символов", а там уже были бы свои методы на пустоту, на минимальное количество и максимальное. Но правильно ли это? Сваливать все три проверки в одном классе.
>>2935799 Ты по прежнему не понимаешь что такое ООП. Объекты это данные + способы работы с этими данными. А у тебя все разбросано. Раз строка содержит в себе буквы, которые надо посчитать. Так создай строку, которая это умеет: https://3v4l.org/XhAE2 А потом учишь свой валидатор работать с умными строками: https://3v4l.org/jGPb4
Ты вместо того чтобы создать объекты с соответствующей этим объектам структурой и поведением используешь их как папки для хранения процедур. Очевидно что при таком подходе тебе от ооп никакой пользы, одни заебы и беготня между шкафчиками и папочками.
>>2935882 Я не знаю что мне делать. Я начал решать задачу со списком студентов месяц назад и до сих пор я нахожусь на этапе проектирования классов. И ничего мне не помогает, ни советы анонов из треда, ни книги (читал Банду Четырех, книгу Зандстры). Все равно как дурачок топчусь на одном и том же месте. Спасибо за код, я разберу его завтра и постараюсь понять какую мысль ты хочешь до меня донести.
>>2935898 Я пробовал менторить вкатунов из треда. Правило одно единственное было: быть в определенное время, а если не получается - предупредить заранее. Казалось бы хуль еще надо? Да только хуй там. Проебываются постоянно. В лучшем случае за пять минут до созвона сказать что "ну бля никак" и похуй что ты свои дела под это время двигал. А в худшем просто молча проебаться. Короче дело неблагодарное.
Ну вообще, тогда можно не делать класс проверки на пустоту, или сделать так, чтобы он наследовал класс проверки с мин. числом символов с условием, что там будет минимум 1 символ:
class NotEmptyValidator extends MinLengthValidator { ... parent::__construct(1);
Нет, ты даешь плохую идею. В PHP уже есть тип string. Не нужно создавать новый вид строк, так как у тебя в приложении будет 2 вида строк и куча возни с преобразованиями туда-обратно.
Чтобы нормально изучить докер, тебе надо начать с изучения основ Линукса и работы в командной строке (так как Докер-файл это по сути набор консольных команд). Ищи любой курс, хоть текстовый, хоть видео, по этой теме. Команды для работы с файлами, перенаправление ввода-вывода, управление пользователями и правами, менеджер пакетов.
После освоения Линукса можешь читать любой туториал по Докеру. Должно быть понятно. Докер создает пустой контейнер (что-то вроде виртуальной машины), в который ты с помощью команд устанавливаешь нужный софт.
Композер
Тут все проще - либо читай документацию на англ., либо ищи туториалы.
> поставить код сниффер
С помощью команды composer require (название пакета)
> Какие пср обычно используются? Надо ли их как-то запоминать или хватит линтера?
PSR пока не много, я советую быстро по диагонали (не заучивая) прочитать все в статусе Accepted: https://www.php-fig.org/psr/
Для оформления кода надо тщательно прочесть PSR-1 и PSR-12.
Основная идея: с помощью интерфейса ты можешь указать требования к классу. И писать код, не имея этого класса либо имея несколько вариантов реализации интерфейса.
Ну например: тебе надо написать код для регистрации с подтверждением по СМС. Но класс отправки СМС поручили писать другому человеку, и хуже того, он его еще не написал.
Ты можешь сделать интерфейс:
interface MessageSender { public function send(string $phone, string $message); }
И затем ты даешь свой интерфейс своему коллеге и говоришь: программировай код отправки СМС. А сам пишешь свою часть кода. И если твой коллега не совсем тупой, то когда он напишет класс отправки СМС, то он будет идеально работать с твоим кодом.
Конечно, ты бы мог объяснить требования к классу словами, без интерфейса, а потом сам вручную их проверить. Но зачем это делать, когда PHP может проверять требования за тебя.
Вторая польза от интерфейсов. Допустим, ты хочешь у себя протестировать свой код. Но отправка СМС платная, и тебе не дали ключ доступа к сервису отправки СМС, и вдобавок коллега еще не доделал свой код. Ты можешь написать свой класс, который соответствует интерфейсу, но вместо отправки СМС просто пишет их в файл. И ты у себя тестируешь код с этой заглушкой, а на продакшене используешь реальный отправщик СМС.
Дальше, может вы решите подключиться к нескольким сервисам отправки СМС. И тут опять интерфейс пригодится, так как твоемукоду без разницы, какой класс используется, лишь бы он соответствовал интерфейсу.
Интерфейсы также часто используют в библиотеках и фреймворках, когда автор хочет дать возможность заменять какие-то компоненты. Тогда он делает для этих заменяемых компонентов интерфейсы. Ну например, есть open-source движок для форума, и мы хотим сделать возможность заменять в нем капчу. Мы можем сделать интерфейс для класса генерации и проверки капчи, и тогда пользователь сможет сделать свою собственную капчу, не меняя код движка.
Как я понимаю, идея такая, что если PHP парсит тело, то оно недоступно (так как PHP его прочел и распарсил). А если не парсит, то доступно в php://input.
То есть ты берешь их концепты, их паттерны, их подходы, но совершенно не понимаешь что и для чего делается. Это даже не карго культ, карго культисты знают чего хотят, просто не знают что по наушникам из кокоса нельзя получить ништяки. Но цель-то ништяки получить у них есть. А тут даже нет понимания что должно получиться в итоге, нахуя все это ооп затевалось, цель этих смешных классов какая.
>>2932466 (OP) Продублирую из прошлого треда. На Битриксе всегда потогонка ебаная? Вкатился пару месяцев назад что-то обомлел. Задачи заранее оценены лидом - 5 часов, 7 часов и т.д. Я не укладываюсь совсем, приходиться перерабатывать, могу несколько дней просидеть. Плюс прилетают задачи на джсе. Короче работа в авральном режиме, из-за этого куча стресса.
Здравствуйте, дайте подсказочку хочу шорткод для вордпресса сделать, который берет текст и ссылку и на страницу выплёвывает <a> тег с атрибутом href в котором ссылка будет.
Зацените плес код. Как его можно улучшить по части веб-безопасности?
>>2938658 >Как его можно улучшить по части веб-безопасности? Избавиться от массивов и уебанских названий типа atts и esc. Если для этого придется выкинуть к хуям вордпресс - тем лучше для безопасности человечества.
Докер состоит из 2 частей: демон, который работает в фоновом режим, и клиент (это как раз команда docker, которую ты запускаешь). У тебя наверно не запущен демон.
Протестируй, что будет, если в ссылку и текст вставить символы вроде <, >, &, ', ". Будут ли они экранироваться или вставятся как есть? Если второе, то это плохо.
Также, протестируй, можно ли вставлять ссылки вроде javascript:alert(1); или data:12345. Если можно вставлять что-то, кроме http:// и https://, то это тоже уязвимость.
>>2939934 >>2938525 Я уже расписывал как вкатился в прошлых тредах. Прошел курс Лаврика с торрентов, сделал задачку про Вектор, потратил кучу времени на поиск нормальных курсов по ооп. Проебал чуть не год на все это. Потом какой-то анон в этом треде отписался, что пошел учиться на Хекслет по подписке - я слышал про них, но не знал, что есть подписка. Ну пошел туда, оказалось годнота. Но это было за неделю до начала "сво". Просидел еще год, обосрасля с поиском работы на ларе и пришлось идти в битрикс, се ля ви
Какие вспомогательные методы правильно прописывать в классе-сущности, а какие следует выносить в отдельный класс? Как понять, определенный метод лучше оставить в классе-сущности или вынести метод в отдельный класс?
Не до конца понимаю DataMapper в контексте задачи Список студентов. Есть сущность, в которой хранятся данные из БД (т.е. в объекты этой сущности выгружаются данные из БД). Есть класс, который обслуживает эту сущность (реализует SQL-запросы).
В списке студентов код работает с данными об абитуриенте. Сначала он их хранит в сущности Enrollee (мое название). Значения свойств этой сущности проверяет Валидатор. Если ошибок нет, данные загружаются в БД. Ага...с вводом информации в БД понятно, а вот с выгрузкой информации из БД... Чтобы выгрузить из БД, нужно будет создавать объекты, которые будут хранить эту информацию из БД и тут вопрос... А что будет этой сущностью? Тот же класс Enrollee (ведь он представляет абитуриента в коде)? Или же нужно создавать отдельный класс-сущность для выгрузки из БД?...
>>2940088 Я бы не стал повторять за этим челом, ты что не видишь как он не смог вкатиться в ларавель и в итоге страдает на битриксе? Стоит зодуматься...
>>2940089 И еще с кое-чем смежным я путаюсь. Если у меня есть DIContainer и он один раз создает объект, то как буду создавать эти объекты, которые будут храниться данные из БД, ведь DIContainer только один раз создает объект. Ага, да... Простите за глупые вопросы, самому стыдно от своей тупости перед анонами.
>>2940088 Почти все из профессии по пхп, начиная с фунций. По ларе там действительно инфы маловато, в дополнение можно посмотреть ларакасты, повторить пару проектов.
>>2940089 >Не до конца понимаю DataMapper Ты вообще не понимаешь DataMapper.
Есть три самых ходовых варианта абстракции хранилища данных. 1) Table gateway. Это абстракция таблицы в базе. У тебя есть объект "таблица" через который ты взаимодействуешь с её содержимым.
2) Active record. Это абстракция записи в базе. У тебя есть объект "запись в базе", через который ты взаимодействуешь с содержимым. Каждая отдельная запись умеет все что умеет Table gateway. Коннектиться к базе, сохраняться итд.
3) Data mapper. Это абстракция хранилища. У тебя есть некое хранилище, которое хранит не записи в таблице, а нужные тебе объекты. Эти объекты понятия не имеют как их на самом деле хранят. Data mapper "маппит" хранимые данные в объекты с нужной тебе структурой. Часто даже сам факт того что они куда-то сохраняются скрыт.
Зачем вообще используют Data mapper. 1) Структура таблицы в реляционной базе принципиально отличается от структуры объектов. Это называется object-relational impedance mismatch. Например, junction table в которой хранятся связи "многие ко многим" в мире объектов просто не существует. В мире объектов "связь" это не колонка с внешним ключем и не запись в таблице, а просто факт помещения одного объекта в другой.
2) В современном приложении используется несколько хранилищ данных. И только одно из них реляционное. Условный "товар" хранится в основной базе, в кэше и в поисковой системе. Изменилось название, а у тебя абстракция хранилища только для таблицы. В таких ситуациях рожают жутких чудовищ из лапши. А Data mapper позволяет все эти телодвижения скрыть за простым $products->add($changedProduct) или вообще автоматически отслеживать все изменения как доктрина.
Пока писал анон скинул хорошую ссылку >>2940175. Хорошую, но не идеальную. В этом примере у него в Data mapper есть метод save(), в доктрине же никаких save() нет. Сам факт наличия объекта в хранилище должен означать что он сохранен. А большинство хранилищ все равно не гарантирую никакой save и работают асинхронно с твои кодом.
>>2940102 >Если у меня есть DIContainer и он один раз создает объект, то как буду создавать эти объекты Di container создает объекты, которым для работы нужны другие объекты или эти объекты нужно как-то сконфигурировать перед использованием. Твоим объектам из хранилища все это надо? Конечно нет, никому это не надо. В модели все зависимости инжектятся через методы, а не через конструктор.
>>2940188 Да? А я думал, что DIContainer нужен для того, чтобы инструкции с созданием объекта были в одном месте... Не претендую на истину, только рассказываю свое понимание вещи, чтобы опытные аноны указали на ошибки (видимо придется постоянно писать эту фразу, иначе аноны будут агриться и думать, что я херь несу с умным лицом)
>>2940201 Да, я не знаю ничего. У меня даже ни одного класса для работы с БД нету. Я только сегодня прочитал статью codedokode про паттерны. В голове каша пока что, думал что-нибудь проясню, задав вопрос, но не особо. Завтра на свежую голову прочитаю то, что ты описал про Action Record, DM, TDG.
>>2940323 У меня виртуализация на винде так и не заработала. Вообще. И уж поверь я сидел с парнями которые больше десяти лет админят и нихуя не вышло. WSL это кал, сынок. Это дерьмо не стоит времени, котрое придется на него убить. Просто поставь линукс.
>>2940327 у меня обратная ситуация, я пытался установить линукс больше недели, но нихуя не вышло и я решил забить, потом я чета вспомнил про всл и поперло
>>2940329 Звучит как "я пытался снять свитер в течении недели, а потом понял что можно срать не снимая свитер". Бубунта или и мята ставится с любой флешки на любое железо уже лет десять как.
>>2940327 >я сидел с парнями которые больше десяти лет админят и нихуя не вышло То что они 10 дет дрочат домен не говорит что они умеют в Hyper-V или WSL. Зачастую они и в домене нихуя не умеют по итогу, и делают всё как в методичках мелкомягких.
>>2940342 Пчел. 90% бухгалтеров работают через виртуалки со старым софтом. У нас в офисе больше тысячи сотрудников. Админы на виртуализации виндовской собаку съели. И через них прошли тысячи разных кампуктеров и ноутов с тысячами конфигураций.
Если ты такой дохуя умный, то давай сконтачимся, застримлю тебе рабочий стол ты будешь говорить что делать. Потом запостишь сюда чем дело кончится.
>>2940338 Я дрочил secure boot, boot legcy, fast boot. Я делал разметку через MBR и GPT. Пробовал все доступные файловые системы. Попробовал разные флешки, разные дистрибутивы. И НИ-ХУ-Я Самое обидное что, создав флешку с параметрами по умолчанию через руфус, и вставив её в другой комп которому миллиард лет, мне просто пришлось поставить ее первой в загрузчике. И ВСЁ БЛЯДЬ ВСТАЛО НАХУЙ
У тебя не установлен какой-то компонент в Windows, нужный для работы виртуальных машин. Я не пользуюсь виндой уже много лет, поэтому даже не знаю какой. Выясни и установи.
> Есть класс, который обслуживает эту сущность (реализует SQL-запросы).
Это скорее всего TableDataGateway.
> Чтобы выгрузить из БД, нужно будет создавать объекты, которые будут хранить эту информацию из БД и тут вопрос... > А что будет этой сущностью? Тот же класс Enrollee (ведь он представляет абитуриента в коде)?
Да. Зачем тебе для одного студента делать 2 разных класса? Логично использовать тот же класс.
В контейнере обычно хранят сервисы, а не сущности. Контейнер нужен для двух целей:
- чтобы для каждого сервиса был один экземпляр, например, чтобы все классы использовали один и тот же объект подключения к БД - чтобы внедрять зависимости при создании сервисов
Во-первых, забудь про MBR. Это устаревший формат разбиения диска и если твой комп новее хотя бы 2015 года, то не используй его вообще. Используй GPT. Твой комп должен поддерживать UEFI (если он не совсем старый).
Некоторые дистрибутивы линукс не поддерживают MBR и требуют GPT + UEFI. Если у тебя очень старый комп, то ищи дистрибутив с поддержкой MBR.
Если же у тебя GPT, то тебе нужно лишь создать для линукса раздел(ы). Увы, тут могут быть сложности, так как линуксу нужно минимум 2 раздела:
- корень с системой, обычно ext4 (минимум 100 Гб) - swap (около 4Гб хватит)
Некоторые дистрибутивы предлагают еще хранить /home и /var в отдельных разделах, но с этим не обязательно соглашаться.
Также, линуксу нужен UEFI boot partition, чтобы положить туда загрузчик, но если у тебя GPT и UEFI, то оно уже наверно есть.
Обычно в установщике линукса ты создаешь эти разделы. Но чтобы их создать, не уничтожая данные на диске, у тебя должно быть свободное место. А если ты ставил винду, то скорее всего у тебя весь диск занят виндой и свободного места нет. В такой ситуации поступают так:
- в винде делают дефрагментирование, чтобы данные были в начале диска - затем в винде диспетчером дисков ужимаешь раздел винды, чтобы в конце диска появилось свободное пространство - при желании можешь виндовым диспетчером создать 2 раздела для линукса из свободного пространства, можешь сделать это установщиком линукса
Ну и как тебе правильно написали, попробовать линукс можно вообще не устанавливая. Вставляешь флешку, при загрузке жмешь кнопку типа F10 или F9 для выбора загрузочного устройства и грузишься с флешки.
Также, линукс можно установить в виртуалку. Виртуалке нужно 3Гб памяти и 100 Гб диска.
А, еще бывают просто битые флешки. Я ставил линукс с флешки, которой больше 10 лет, и она просто возвращала для некоторых файлов неправильные данные. Ох, пришлось мне повозиться, я загрузился с флешки, а затем скачал на диск образ устновщика, примонтировал его и устанавливался не используя далее флешку.
>>2940033 Не лезь, оно тебя сожрет. Объем знаний по сравнению с той же ларой просто огромный, документации почти нет, код лютое говнище. Если хочешь залететь по-быстрому, присмотрись к другим направлениям, лучше вообще за пределами разработки
Что анон делает перед тем, как писать код? У меня всегда, когда я сажусь заниматься программированием, первые 1-2 часа вата в голове. Есть какие-нибудь советы?
>>2941609 >Что анон делает перед тем, как писать код? Пиздую по морозу до метро. Еду в ебучем забитом метро в час пик. Тащусь по снегу до офиса. Ползу через пост охраны к лифту. Еду на лифте на восьмой этаж. Здороваюсь с пацанами у кикера. Добегаю до рабочего места. Чекаю личку, мессенджеры, почту, джиру. Пишу код.
>>2940183 Я перечитал твой пост. И это дало мне почти ничего. Ты не мог бы подробнее рассказать про DM? Как он устроен? Мб даже код. Если, конечно, есть желание этим заниматься. Я не настаиваю. Если будешь расписывать, то помни, что я новичок, который никогда не писал код работы с БД и те вещи, которые понятны тебе, ввиду знаний и опыта, не будут понятны мне. Все, что я нашел - то, что есть сущность, которая представляет что-то и есть маппер. В маппере есть методы сохранения/загрузки из БД (по этой сущности). И это представление не соотносится с тем, что ты писал. Я не в смысле, что ты не прав, а в том смысле, что пока что текстовое описание для меня абстрактно звучит и, пытаясь переложить его на тот код, который я видел из интернета, я не вижу ничего общего.
>>2941669 >И это представление не соотносится с тем, что ты писал. Что ты считаешь несоответствием я конечно должен угадать.
Обучение это двунаправленный процесс. Тебе дают информацию. Ты отвечаешь учителю как ты истолковал его слова. Учитель говорит что ты истолковал неправильно и указывает на ошибки в рассуждениях. Несколько раз предлагал заниматься таким в дискорде с нормальным фидбеком. Никому не надо.
Кидать в пустоту страницы текста, получая в ответ вместо рассуждений "понял/не понял" предельно тупое и неэффективное занятие. Поэтому вот тебе ссылка, там все расписано https://martinfowler.com/eaaCatalog/dataMapper.html читай. У меня таких ссылок две страницы гугла, будем вести обучение в удобном тебе формате.
Как считаете, аноны? Я правильно понимаю, что в коде лучше использовать не название класса, а название его интерфейса (в том случае, если функции требуется объект определенного класса)? И правильно ли я понимаю, что если интерфейсы двух классов похожи, это еще не означает, что двум классам стоит реализовывать общий интерфейс? Пример: у меня есть сущность, представляющая абитуриента и есть сущность, представляющая ошибки валидации. Общий для них интерфейс - геттер и сеттер, потому что я задал им приватные свойства. Но дав им общий интерфейс и прописав в одной функции, что я принимаю объекты такого интерфейса, я делаю ошибку...? Почему я так думаю: у меня есть функция, которая принимает объект, представляющий абитуриента. Если я вместо названия класса-абитуриента напишу его интерфейс, то функция спокойна будет принимать объект, представляющий ошибки валидации, потому что он реализует тот же интерфейс. Я не считаю себя правым, не нужно общаться со мной так, как будто я пытаюсь что-то доказать. Я просто вкатун-вкатуночек, который пытается усвоить материал и просит для этого корректировку своих мыслей от опытных анонов.
Знаю жс тайпскрипт рякт цсс си базу плюсов и как срать не снимая свитер Начинаю оформлять вкат в ваш уютный пхп, чтоб съебать к лету со своей работы Если не получится отсосу мужику из треда Аллаху Акбар братья
>>2941719 >Несколько раз предлагал заниматься таким в дискорде с нормальным фидбеком. Никому не надо. Мне, мб, надо, но вряд-ли я буду общаться голосом. Но все-таки я не хотел бы, наверное, нагружать настолько чужого человека. Хотя и мои постоянные вопросы в треде - уже значительная нагрузка.
>>2941723 Окей, я прочел... Я шарил по интернету и читал такой ответ: DM - Тупо мостик между БД и объектом: считать данные из БД и записать в объект, сохранить объект в БД. Фактически CRUD. Способ автоматизировать рутинные операции.
Т.е. у меня есть сущность, вокруг которой крутится код, в данной задаче это Абитуриент. Есть маппер, который берет сущность и сохраняет/загружает данные из БД. И...все... Больше я ничего не понимаю в этой теме, если вообще мое понимание правильное.
>>2941722 На собесе/работе будешь языком жестов разговаривать? Там с тобой церемонится не будут. Стесняешься незнакомого дяди? Так у них очередь из тех для кого это не проблема. Еще и вебку подрубить попросят.
>>2941735 >А чем учеба от работы отличается? Тем, что с работодателем труднее будет договориться, чем с аноном? Человек, который приходит работать в определенную компанию, обычно принимает условия этой компании, а не предлагает свои (я не отрицаю, что есть возможность предлагать). >Мне интересно что ты понял из стать и что не понял. Ты хочешь, чтобы я прямо сейчас разбирал этот вопрос? Я недавно закончил сегодняшнюю учебу и морально отошел от того, чтобы прилагать умственные усилия. Но я могу перечитать и попробовать сформулировать выводы, если ты хочешь.
>>2941738 Вот об этом я и говорю. Мы уже три поста не понимаем друг друга. В войсе это недопонимание разрешилось бы за секунду. Я не буду в третий раз пытаться объяснить одно и тоже.
>Ты хочешь, чтобы я прямо сейчас разбирал этот вопрос? Я хочу чтобы ты разбирал "этот вопрос", а не "случайный вопрос" из интернета. Ну и разумеется я не хочу терять нить и вспоминать че там кто кому писал десять постов назад на прошлой неделе. Это просто не удобно.
>>2941740 Окей. В статье написано о том, что представление информации в виде объекта, и предоставление информации в виде БД могут не совпадать. Это создает какие-то сложности. И чтобы решить эту сложность, нужен DM. Что я не понимаю, а точнее не представляю, как должны выглядеть объект и таблица, чтобы не совпадать. Т.е. конкретный пример.
>>2941746 Я не знаю что такое коллекции. А когда пытался гуглить, чтобы сформировать хотя бы первичное представление - все равно ничего не понимал. Ну а наследование, зачем вообще его отображать в БД? Я думал, в БД просто колонки, где в строчках хранятся значение, зачем БД знать про связи, которые есть у объекта?
>>2941749 >А что такое массив знаешь? Да. >Затем что потом эти объекты надо из бд восстановить? А причем тут наследование? Недостаточно создать метод ВыгрузитьИзБД и прописать в теле как формировать объект?
>>2941751 Я не знаю. Я не думал в этом ключе, потому что у меня никакого наследования в коде нет и класс для хранения информации только один (куда я и планировал выгружать данные). Т.к. я не имел опыт с этой ситуацией, для меня она звучит абстрактно.
Основная особенность маппера - это то, что модель (объект Person) ничего не знает о базе данных и таблицах. Это значит, что в нем:
- никак не упоминаются названия таблиц - нет SQL-кода - не хранится ссылка на объекты типа PDO или соединение с БД
То есть, даже если у нас нет базы данных, мы все равно полноценно можем использовать объект Person, только сохранять его некуда.
Как работает Data Mapper? Расскажу на примере Доктрины. В Доктрине у тебя есть EntityManager - хранилище сущностей. Когда ты хочешь добавить в базу нового человека, ты делаешь так:
1) создаешь объект Person, и заполняешь его свойства
2) добавляешь этот объект в EntityManager: $em->persist($person). Этим мы добавляем объект в хранилище в памяти.
3) просишь EM скинуть накопившиеся изменения из хранилища в БД: $em->flush()
На первый взгляд, это похоже на TableDataGateway, только вместо вызова $table->save($person) мы тут делаем 2 вызова: persist и flush.
Но плюс Data Mapper проявляется, когда тебе надо внести изменения в несколько объектов. В случае TDG ты должен после внесения изменений для каждого объекта вызвать $table->save($object). В случае с DM все проще: ты просто меняешь объекты, а в конце вызываешь $em->flush() и EM сам обходит все объекты, составляет список изменений и обновляет таблицы в базе.
То есть, DM обычно поддерживает "хранилище" объектов, и умеет обнаруживать изменения в этих объектах и генерировать SQL-запросы для переноса изменений в БД. Ну то есть, если ты поменял в объекте одно поле, то EM выполнит запрос вроде UPDATE.
> в коде лучше использовать не название класса, а название его интерфейса (в том случае, если функции требуется объект определенного класса)?
Ты используешь интерфейс, если хочешь, чтобы можно было передать вместо одного класса другой ("абстрагироваться от реализации").
Ну например, тебе надо сохранять фото. Ты можешь конечно все захардкодить и прибить гвоздями, а можешь сделать интерфейс:
interface PhotoSaver { public function save(Photo $photo); }
И к нему 2 реализации: одна сохраняет на диск, а другая в облачное хранилище Amazon. И в тестах использовать диск, а в продакшене Амазон.
> если интерфейсы двух классов похожи, это еще не означает, что двум классам стоит реализовывать общий интерфейс?
Нет. Интерфейс, это когда есть несколько классов, решающих одну и ту же задачу разными способами. Например, выставление счета на оплату по СБП и через ЮКассу. 2 класса, делающих одно и то же - выставление счета - но по-разному.
> Пример: у меня есть сущность, представляющая абитуриента и есть сущность, представляющая ошибки валидации. > Общий для них интерфейс - геттер и сеттер, потому что я задал им приватные свойства.
Неправильно. Эти сущности разные и у них нет ничего общего. Геттер и сеттер не являются "общей задачей", так как они есть вообще у любого класса. Более того, геттеры у этих классов скорее всего разные (у студента - получить имя, у списка ошибок - получить текст ошибки и название поля с ошибкой).
Это хорошо, что ты пытаешься применить знания про интерфейсы, но тут ты их применяешь не туда, куда нужно.
Я подумал, где можно применить интерфейсы, и нашел. Если у тебя валидатор составной (состоит из основного валидатора и классов-правил), то для правил как раз нужно сделать интерфейс по типу:
interface RuleValidator { /** проверяет, что значение соответствует правилу*/ public function validate($value): ?Error; // Можно даже вместо Error сделать ErrorInterface, если у тебя несколько разных классов ошибок }
Коллекция - это объект, который хранит список других объектов или значений. Ну то есть аналог массива, только в виде объекта.
Проблема несовпадения может быть в том, что у тебя внутри объекта есть список других объектов, например:
- у Студента может храниться объект Группа, в которой он учится. А в Группе храниться Факультет, к которому она относится.
Соответственно, сохранить это в БД напрямую не получится: надо Студента сохранять в таблицу студентов, Группу в таблицу групп ит.д. С этим разбираться - как раз задача Дата Маппера.
А без Дата Маппера тебе надо решать эти задачи руками: самому сохранить разные объекты в разные таблицы и проставить в них id друг друга (в таблицу студентов - id группы, в группу - id факультета).
> Ну а наследование, зачем вообще его отображать в БД?
Бывает, что у тебя есть несколько очень похожих сущностей, которые можно представить с помощью наследования.
Например: ты занимаешься доставкой еды. У тебя есть вело-Курьеры и Курьеры на машине. У курьера с велосипедом есть одни свойства, а у курьера с машиной - другие (например: номер машины, модель, итд.). И у них есть общие свойства (например, имя, телефон, район работы).
Логично, что ты захочешь использовать наследование - сделать базовый класс БазовыйКурьер, от него унаследовать ВелоКурьер и АвтоКурьер. А теперь вопрос: как все это разнообразие сохранять в базу? Делать 1 таблицу? делать 2 таблицы?
Ты можешь решать эту проблему руками или использовать Data Mapper с поддержкой наследования, который применит один из 3 паттернов для сохранения таких объектов (нагугли, если интересно):
- Single Table Inheritance - Class Table Inheritance - Concrete Table inheritance
Также, бывает еще такая ситуация, как товары и характеристики. У товаров есть сотни характеристик в зависимости от категории (у компьютера - объем памяти, у чашки - объем в литрах). Опять же, чтобы это описать, придется сделать сложную структуру объектов и таблиц.
>>2942550 >Если у тебя валидатор составной (состоит из основного валидатора и классов-правил), то для правил как раз нужно сделать интерфейс по типу: Не понимаю зачем тут интерфейс. Вот код валидатора, который я написал получается, что списал, не я же его придумывал! по советам анонов (ну, или анона): https://3v4l.org/jZ4bH
rules это массив, у которого ключи - названия полей, которые нужно проверить, а значение этих ключей - массив с объектами-правилами. В классах-правилах есть метод __invoke, чтобы вызывать объекты как методы.
>>2942567 >- у Студента может храниться объект Группа Зачем Студенту хранить объект Группа? Почему нельзя сделать свойство, значение которого - группа, просто строка с названием группы. И если существует объект Группа, то что он из себя представляет и почему, обладая таким содержанием, находится в классе Студента?
>>2942742 >Почему нельзя сделать свойство, значение которого - группа, просто строка с названием группы. Потому что в модели университета есть студенты и группы, но нет "строк"? Потому что студент университета знает в какой он группе и может об этом рассказать? Потому что каждый год создается новая группа первокуров с тем же названием?
>>2942550 >Более того, геттеры у этих классов скорее всего разные (у студента - получить имя, у списка ошибок - получить текст ошибки и название поля с ошибкой). Зачем для отдельных полей делать отдельные геттеры/сеттеры? Я видел примеры такого кода и всегда не понимал зачем, потому что пишу так: https://3v4l.org/ZmK3K
>>2942758 Что такое студент в данном случае? Просто сущность с полями? Зачем студенту знать что-то о содержании объекта Группа? Я не понимаю тебя, потому что у меня объекты становятся свойствами только тогда, когда они требуются классу. Если класс что-то делает и ему нужно получить экземпляр определенного класса, то, да, он становится частью. А зачем в контексте этого примера ссылка на объект Группа?
>>2942767 Я понял эту строчку так: Есть отдельный сеттер/геттер для отдельного поля. Один геттер, чтобы получить значение поля, например, name. Второй геттер, чтобы получить значение поля, например, surname.
>>2942770 >Зачем студенту знать что-то о содержании объекта Группа? За тем что ты моделируешь студента, а студент знает в какой он группе. Зачем ты моделируешь студента вопрос к тебе.
>А зачем в контексте этого примера ссылка на объект Группа? Еще раз. Затем что у тебя модель университета. Модель части реального мира. А ты её путаешь со способом хранения данных. То как ты хранишь свои данные никак не влияет на то как работает универ.
>>2942778 >Этот объект не содержит изначально ошибки И где это отражено? Значения по умолчанию нет. В конструкторе пустой список с ошибками не создается. Чтобы понять как это работает надо каждый раз тебя обо всем спрашивать?
>>2942775 >Я понял эту строчку так: >Есть отдельный сеттер/геттер для отдельного поля А я должен понять как из его наличия следует его назначение? Разочарую - оно не следует.
>>2942783 >И где это отражено? Ну, мб, по этому коду и непонятно, что представляет собой Класс с текстом ошибок. Но в любом случае, какая разница? Я же ведь описал текстом.
>>2942789 О чем мы вообще говорим? Тебе полемику развести охота? Я иногда видел код, где для отдельных полей делают отдельные геттеры и сеттеры и решил спросить в чем практическая значимость этого, если можно написать функцию, которая будет отдавать значение любого поля.
>>2942790 Ну так должно быть понятно. Сегодня у тебя в башке и на словах одно, завтра другое. Через месяц сам будешь на этот код как баран на новые ворота смотреть.
>>2942798 >О чем мы вообще говорим? >Тебе полемику развести охота? Я видел некоторые люди чешут яйца. Зачем? Задавай бессмысленные вопросы - получай бессмысленные ответы.
>если можно написать функцию, которая будет отдавать значение любого поля Вот это и нужно было написать. "Почему бы не сделать функцию, которая умеет работать сразу со всеми полями объекта?"
И принимать эта функция будет какой тип? А возвращать какой тип? Мне чтобы её вызвать нужно знать что ей передать и что я получу. Откуда я это узнаю?
>>2942819 >"Почему бы не сделать функцию, которая умеет работать сразу со всеми полями объекта?" Мой вопрос не в этом, мой вопрос в том, почему, имея возможность написать геттер и сеттер, которые будут работать со всеми полями, кто-то пишет геттеры/сеттеры для отдельных полей? Мб есть какая-то практическая значимость или причина, чтобы так поступать, а я, ввиду неопытности, не знаю ответа?
Я вот так SQL запрос на создание таблицы для списка студентов сделал, подскажите что не так CREATE TABLE users { id INT PRIMARY KEY; first_name CHAR(60); last_name CHAR(60); email CHAR(120); group_num INT(4); points INT(100); birth_date YEAR; locate ENUM;
>>2942841 >Номер группы в БД надо делать строкой, а не числом. Если номера делать числом, то номер группы 0010 превратится просто в 10. Число используется для обозначения количества или значения какой-то величины. Из советов автора задачки.
>>2942896 А я не верю что ты не понимаешь о чем я. Ты спрашиваешь "почему так, а не как я себе нафантазировал". А я тебе говорю: ты не фантазируй, а попробуй действительно так сделать. И сразу поймешь почему так не делают. А ты ленишься.
>>2942908 Мб мне, который даже не дотягивает по знаниям и опыту до недоджуна, писать код на уровне сеньора? Чего мелочиться? Раз пишешь код, так давай с самого первого раза пиши идеальный, по всем правилам.
>>2942920 Ну начни с типизации. Твои объекты должны работать только так как это было задумано. Ничего лишнего нельзя засунуть, ничего лишнего нельзя вытащить. Нет никакого "на словах". Если что-то может быть использовано не так - оно будет использовано не так.
>>2942492 Хорошо, спасибо за подробный ответ. Можешь рассказать в чем отличие TDG от DM? Также я не понимаю как соотносится следующее: Другой анон пишет: >Table gateway. Это абстракция таблицы в базе. У тебя есть объект "таблица" через который ты взаимодействуешь с её содержимым. Статья codedokode про паттерны проектирования, раздел про TDG: насколько я понял, есть объект, который хранит информацию о сущности и есть класс, который выполняет SQL-запросы по этой сущности. Т.е. все-таки что такое класс TDG? Одновременно класс, который хранит данные таблицы и умеет работать по БД или не один класс, а два, где одна сущность (и она при этом одна запись в БД) и класс-сервис?
>>2943011 >не один класс, а два Имеется ввиду класс у которого ты вызываешь методы. Так-то орм это одна из самых сложных проблем в программировании. Офк там десятки классов под капотом. А доктрина так скорее всего вообще самый сложный софт на пхп написанный.
>>2940637 У меня есть класс-сервис. Для его работы требуется объект. Объект является сущностью. Я должен регистрировать этот объект в контейнере? Проще будет объяснить свой вопрос так: Фабрика по созданию класса-сервиса будет выглядеть так: new SomeClass ($container->get('AnotherClass')); Или так: new SomeClass (new AnotherClass).
>>2932466 (OP) А вот скажите, как лучше поступить с реквестами (в Ларавеле)? Допустим, у нас есть модель Product. У каждого продукта есть набор статусов и адресов. Конретные адреса и статусы хранятся в Statuses и Addresses. Ну и через таблицы связей и отношения многие-ко-многим прикручены к этим продуктам. Дальше, есть три ресурсных контроллера, по одному на каждую модель. Соответственно, один добвляет/показывает/редактирует/удаляет адреса, другой статусы, третий продукты. И всё ок. Вдруг потребовалось добавить ещё один эндпоинт, который показывает все адреса и все статусы. Просто листинг. Ок, добавляем контроллер InfoController и метод getInfo(). Дальше, как это инфо брать? Заново писать код для сбора адресов и статусов? Некрасиво, лучше просто обратиться к index() соответствующих контроллеров. Обращаемся. Но тут засада, у StatusController метод index() принимает реквест типа StatusRequest, а передать мы можем только пришедший InfoRequest. И как это решить? 1) Прямо на ходу родить экземпляр класса StatusRequest, вкрячить туда нужные данные и отправить в StatusController? Ну херня же. 2) Создать некий общий SharedRequestInterface, определить в нём общие свойства, родить от него InfoRequest и StatusRequest, ну и пользоваться на здоровье. Сложна. 3) Убрать кхуям из контроллеров обращения к моделям и перенести их в репозиторий. Тогда будут цепочки: StatusController принимает StatusRequest, вычленяет из него нужные данные и обращается к методу из репозитория для обращения к базе. Аналогично InfoController принимает InfoRequest, забирает данные и обращается к тому же методу того же репозитория. Вроде, самое адекватное.
>>2943262 >Дальше, как это инфо брать? Заново писать код для сбора адресов и статусов? Некрасиво Ты же выше написал >через таблицы связей и отношения многие-ко-многим прикручены Написан ведь код сбора адресов уже. Тебе целый элокент для этого даден.
>лучше просто обратиться к index() соответствующих контроллеров. Обращаемся. Но тут засада, у StatusController метод index() принимает реквест типа StatusRequest А что будет если объект предназначенный для обработки веб запроса использовать не по назначению? Будет весело.
>Убрать кхуям из контроллеров обращения к моделям и перенести их в репозиторий. Репозиторий не выдает листинги. Репозиторий предназначен для работы с сущностями. А у тебя для этого актив рекорд элокент есть, нахуя тебе вокруг него дата маппер городить?
Ты можешь просто вынести повторяющийся код и назвать его в соответствии с его назначением. Такие невероятно сложные способы рефакторинга как извлечение метода/класса сейчас не в моде? Ctrl+alt+m в шторме, одно нажатие.
Кто-нибудь может подсказать как установить Laravel? Каждый раз, как не брался за это, всегда все выглядит мудрено: то установить Докер (и разобраться как он работает), то установить Композер (который я также не ебу как работает). Качать какую-то утилиту сомнительную, которая все якобы тебе скачает, нужное для веб-разработки - тоже не очень вариант.
>>2943256 >У меня есть класс-сервис. >Для его работы требуется объект. >Я должен регистрировать этот объект в контейнере? Контейнер тебе зачем? Для инверсии зависимостей. А что написано в "принципе инверсии зависимостей"? "Классы должны зависеть от абстракций, а не от конкретных деталей" там написано. https://en.wikipedia.org/wiki/Dependency_inversion_principle Так каким хуем твой сервис может зависеть от конкретной сущности, а не от абстракции?
>>2943309 Разобраться в терминах, которыми ты сыпешь. Сущность это вообще из ddd Эванса. Инверсия контроля из солида и чистой архитектуры Мартина. Это две книги минимум, чисто погрузиться. И обе они не по пхп.
Тебе нужно начать с того что научиться объекты грамотно проектировать и методы. А уже потом об архитектуре думать.
>>2943772 Телепатически проникаю к тебе в голову. Вижу как работала машинка и вижу что стало не так с какашкой. Вижу твой стиль каким ты его хочешь, не сломанным...
Че поверил? В деда мороза тоже веришь? Каким хуем кто-то должен понять что у тебя там сломалось в башке твоей? https://jsfiddle.net/v9se6z13/
Вот смотри, допустим я хочу взять твой код и добавить новое правило валидации и класс, который его проверяет. Как я должен писать этот класс? Каким требованиям он должен соответствовать?
Ты мог бы написать документацию в стиле "если вы хотите добавить новое правило, то ...". Но зачем писать, когда можно сделать интерфейс для классов-правил. И я буду знать, что мой класс должен соответствовать этому интерфейсу. А PHP проверит это и выдаст ошибку, если вдруг я что-то забыл.
То есть, для классов-правил надо сделать общий интерфейс.
1) ты передаешь в конструктор студента для проверки. Это плохо, так как:
а) получается, твой валидатор можно использовать только один раз. Чтобы проверить нового студента, надо создавать новый валидатор. Это так же нелогично, как покупать новую стиральную машину перед каждой стиркой.
б) непонятно, зачем тебе метод carryOutVerification. Ведь все данные уже есть в конструкторе и ты мог бы там сразу сделать проверку.
Лучше передавать проверяемого студента в метод проверки.
2) нелогично, что ты в конструктор еще передаешь список ошибок. Логичнее было бы, если бы метод для проверки возвращал список ошибок. То есть, ты кладешь в валидатор студента и он тебе в ответ возвращает список найденных ошибок.
То есть, на мой взгляд, логично было бы сделать класс так:
class Validator { public function __construct($rules) { ... } public function validate(Enrollee $enrollee): ErrorList { ... } }
3) ты не в том месте делаешь добавление ошибки. Ты достаешь предудущую ошибку, склеиваешь с ней новую и записываешь обратно. Но это неправильно, так как работа с ошибками это задача класса ErrorList (или как он у тебя назван). Только он знает, как хранить и склеивать ошибки. Соответственно, ты не должен склеивать их в валидаторе, а должен просто вызывать метод $this->detectedValidationErrors->add($key, $errortext)
4) PHP позволяет указывать типа параметров функций, тип результата и тип полей класса. Ты этого не делаешь, из-за этого твой код труднее понимать. Надо прочесть мануал и расставить типы везде, где можно:
У полей тип ставится так: private int $x; private SomeClass $y; У функций и методов так: public function test(int $x, SomeClass $y, SomeInterface $z): SomeClass
Не согласен - возражай, не понял - спрашивай, иначе жду доработанный код.
В этой задаче достаточно хранить строку с названием.
Но представь, что мы делаем более продвинутую систему, где есть группы, факультеты, преподаватели, корпуса, аудитории, расписание занятий. Там у Группы есть свойства: название, факультет, год обучения. А раз есть свойства, значит нам нужен класс Группа и таблица для хранения групп, и надо как-то указать, какой студент в какую группу входит.
В такой системе у Студента будет свойство, в котором хранится объект Группы, а не название. Чтобы мы могли легко получить, например, к какому факультету относится студент или на каком он году обучения.
Отдельные геттеры и сеттеры нужны, чтобы было сразу видно, какие поля есть у класса и какого они типа.
В твоем коде мы можем написать $enrollee->set('zbcdefgh', 12345) и IDE или статический анализатор не увидит тут никакой ошибки. А если мы напишем $enrollee->setZbcdefgh(12345), то будет сразу видно, что такого метода нету, и IDE тебе подчеркнет это красненьким.
Ну и человеку проще, когда видно какие есть методы, а не когда тебе дают один метод set() и ищи, догадывайся, что в него можно передать, а что нельзя.
- id надо сделать не просто INT, а UNSIGNED INT - для всех полей, которые обязательны к заполнению, надо указать NOT NULL - вместо CHAR используют VARCHAR. CHAR это строка фиксированной длины, которая добавляет пробелы при вставке, если символов меньше, чем надо - по условиям задачи про номер группы написано: "номер группы (от 2 до 5 цифр или букв)". Читай внимательнее требования. - points INT(100); - 100 здесь не максимально возможное значение, а рекомендуемое количество цифр при выводе - для ENUM надо перечислить список разрешенных значений
TDG это просто класс, где собраны все возможные операции над одной таблицей. Весь SQL-код для работы с таблицей должен быть собран в этом классе, и нигде больше. Если у тебя 5 таблиц, то у тебя будет примерно 5 TDG-классов, для каждой таблицы свой. И в них могут быть разные операции.
TDG не хранит в себе данные из таблиц. Данные хранятся в сущности, и если ты например прочитал из базы 10 студентов, то TDG создаст 10 объектов Student (или Enrollee) для них. На каждую запись создается новый объект.
TDG это относительно простая штука. Например, в TDG может быть метод вроде updateStudentScore(int $id, int $score) - обновить баллы у студента по id. И другие методы, вроде "добавить студента в таблицу", "удалить студента", "найти студентов по поисковой строке", и тд.
DM это более сложная система. В ней обычно нет конкретных методов вроде "обновить баллы у студента". В ней ты просто загружаешь студента из базы, меняешь ему кол-во баллов, и просишь DM саму найти, что поменялось, и перенести изменения в БД.
DM пытается создать видимость, как будто БД не существует. Ты просто работаешь с объектами, загружаешь их, меняешь их, а DM сама обновляет базу в соответствие с твоими изменениями.
Для сравнения,в TDG, если ты хочешь что-то поменять, ты явно вызываешь нужный метод, вроде updateStudentScore. То есть, TDG намного примитивнее.
DM также обычно реализует паттерны вроде Identity Map, Unit of Work, Lazy Load, поддерживает связи между объектами, наследование и тд.
> насколько я понял, есть объект, который хранит информацию о сущности и есть класс, который выполняет SQL-запросы по этой сущности.
да
> Т.е. все-таки что такое класс TDG? > Одновременно класс, который хранит данные таблицы и умеет работать по БД или не один класс, а два, где одна сущность (и она при этом одна запись в БД) и класс-сервис?
TDG это сервис, в котором не хранятся никакие данные, а только есть операции над таблицей. TDG у тебя в одном экземпляре. А Студентов может быть много экземпляров.
> Я должен регистрировать этот объект в контейнере?
Давай разделим сущности на 2 вида:
1) сущности, которые заполняются данными от пользователя (из формы). Они никогда не кладутся в DI контейнер, так как контейнер не должен получать никаких данных от пользователя.
2) сущности, которые всегда одинаковые либо которые заполняются из конфига. Их можно класть в контейнер. Но такие сущности встречаются редко и в задаче про студентов их нету.
Если у тебя сущность типа 1, то ее обычно не передают в конструктор сервиса.
То есть, твой контейнер не должен содержать в себе что-то, что приходит от пользователя или из формы. Твой контейнер в идеале должен быть всегда одинаковый и в нем не должно быть меняющихся данных.
Почему? Потому что контейнер и сервисы создаются один раз, и могут (в теории) обработать много запросов от разных пользователей. Контейнер св идеале оздается до того, как мы получим данные от пользователя, поэтому он не должен иметь к ним доступа или как-то их использовать.
> Некрасиво, лучше просто обратиться к index() соответствующих контроллеров.
Это неправильно, ты не должен писать в контроллере код, который надо вызвать откуда-то из другого места. Контроллер только обрабатывает запрос от пользователя и все. Он не помогает другим классам что-то найти.
Такой код, который нужен в нескольких местах, ты помещаешь в модель или сервис (или репозиторий, хотя откуда они в Ларавеле).
Далее, если у тебя сущности связаны, то ты должен сделать эти связи на уровне модели. Чтобы ты мог просто взять Product и вызвать метод и получить список его адресов.
Надо изучить композер, а затем Докер. Чтобы изучить Докер, предварительно надо научиться работать в командной строке и выучить основные команды Линукс. Учебников и видео полно по этой теме.
"Сущность" посоветовал использовать я, как название для класса с данными, моделирующего какую-то сущность из предметной области (Товар, Заказ, Комментарий, Пользователь). Как противоположность классу-сервису, который не хранит данные, а выполняет операции над ними. Что тут не так? Зачем для этого изучать DDD?
>>2943316 И какой итог такого подхода к обучению? А итог такой: почти 2 месяца прошло, а я где начал задачу, там и остался. Я согласен с тобой, что нужно учиться правильно кодить, читать литературу. Я всем этим буду заниматься. Но я уже так устал. Меня уже тошнит от этой задачи про Список Студентов. Тошнит, что я маюсь-маюсь, а конца ей нет, да и продвижения нет. Я уже согласен написать посредственный код, но чтобы у меня получился хотя бы внешне работающий продукт. Да и не много ли я с себя требую? Я всего лишь новичок. Философию ООП-кода не понимаю. Много пробелов в знаниях. Какой еще код ждать от человека, который не умеет кодить и мало знает? Я считаю, что пусть я напишу плохой код, но пройду дальше, столкнусь с новыми знаниями, они подскажут мне как писать лучше, возможно, встречу толковых людей, указания которых я смогу понять и так и буду расти.
>>2944079 Да в целом я работал в вордпрессом, модули делал, вукомерс накатывал, с yii2, могу за солид с граспом пояснить и все такое. Но проблема в том что там куча заказов и везде нужен узкий профиль экспертности, либо делать параллельно изучая область. Задачи пиздец какие разноплановые
>>2944113 А как работать в РФ? Сам самозарядный из плюсов то что работодатель не может принести повестку как на обычной работе и ничего не сообщает в военкомат иначе ему штраф 400к
>>2943867 >получается, твой валидатор можно использовать только один раз. Чтобы проверить нового студента, надо создавать новый валидатор. Это так же нелогично, как покупать новую стиральную машину перед каждой стиркой. Я не представляю что будет находиться у меня в переменных, если я запущу сервер и моя страница будет запущена на двух отдельных устройствах и они будут посылать данные на мой сервер. Т.е. как php-скрипт будет обрабатывать эти данные. Одни данные перекроют другие? Для каждого браузера будет отдельный php-скрипт, у которого будут полностью чистые переменные и которые будут очищаться после того как php-скрипт отработает?
>непонятно, зачем тебе метод carryOutVerification Этот метод запускает отдельные проверятели-правил. Мне посоветовали в прошлом треде сделать главный класс и классы-проверятели (проверка на пустоту, на количество и т.д.). >Ведь все данные уже есть в конструкторе и ты мог бы там сразу сделать проверку. Не понимаю тебя. >Логичнее было бы, если бы метод для проверки возвращал список ошибок Я храню текста ошибок в виде объекта, где его свойства - name, surname etc. >ты передаешь в конструктор студента для проверки Я отдал его в конструктор, потому что думал, что если для работы Валидатора нужен объект другого класса, то я должен добавлять зависимости через конструктор, а не отдавать объекты в методы. >Ты достаешь предудущую ошибку, склеиваешь с ней новую и записываешь обратно. Я так сделал, потому что иначе бы один текст ошибки перекраивал другой. Ну, да ладно. >Только он знает, как хранить и склеивать ошибки Ну...Мой класс, связанный с ошибками, просто хранит данные. Он как студент, только вместо имени он принимает текст ошибки. А чтобы понимать в каком поле пользователь совершил ошибку, я классу, который содержит текст ошибок, дал поля, которые называются как поля студента (кроме тех, где пользователь не вводит, а выбирает вариант из списка). Ну, ладно. Я понял. Я должен переписать класс с текстом ошибок. Добавить в него метод, чтобы он...сам клал значения ошибок и склеивал их. Окей. >PHP позволяет указывать типа параметров функций, тип результата и тип полей класса. Да, я узнал об этом в прошлом треде. Нет пока что привычки добавлять все это, потому что я учился по урокам, когда указывать типы нельзя было(?). Да и код я не пишу. Как не обращусь с вопросом, так сразу неправильно у меня все. Начинаю вместо кода сидеть думать как правильно строить классы. В итоге просто бездарно проведенное время, ведь мне не хватает знаний и опыта, чтобы понять и применить с умом то, о чем говорят в тредах опытные аноны.
>>2944133 Ну и что, зато теперь же ведомства обязаны инфу передавать в ёнкомат. То есть ты самозарядный и ёнкомат узнает из налоговой, что ты самозарядный и щас дело не в том что принесут повистку, а в том что её тебе в госуслуги закинули, и ты по факту обязан. То есть знал ты или нет - поксуй, не пришел - с тебя штраф 10-30к
>>2944111 >посредственный код, но чтобы у меня получился хотя бы внешне работающий продукт тогда просто пиши, как получится, забудь пока про эти ооп, паттерны и прочая. Выпиши требования задачи на бумажке или в текстовик (важно - только требования, без комментариев, советов, рекомендаций и т.д.) . Дальше вся работа должна идти в порядке "хочу сделать это > гуглю, если не понятно как сделать > делаю > думаю, что дальше надо делать". Потом, когда напишешь, прочитаешь ту же шапку снова, сравнишь описываемый "правильный" подход со своим, начнёшь понимать, зачем вообще эти всякие ООПы нужны и как они бы помогли при написании кода. Тогда и дальнейшее изучение пойдёт попроще - это уже будет не абстрактная "философия", а практический инструмент.
>>2944281 >важно - только требования Даже не знаю. Прям только-только требования? Даже без раздела о подводных камнях от автора задачи (ну или человека, который разместил ее в гитхабе)? Если я буду гуглить, не будет ли это нечто схожее, чем как если бы я спрашивал тут, у анонов, или читал советы по задаче? И то, мб, это будет хуже. Потому что тут живые люди, у которых можно переспрашивать. А если я не смогу нагуглить, я также буду затупливаться, сидеть, думОть, но не кодить. Да и не бросать же мне те советы, которые уже писали аноны? Все-таки он(и) дело говорят.
>>2943920 >для класса с данными, моделирующего какую-то Моделирует "модель". А у сущности другие свойства.
>Как противоположность классу-сервису, который не хранит данные Сущность противопоставляется не сервису, а классу который полностью определяется его данными. Как адрес или дата.
>Что тут не так? Зачем для этого изучать DDD? И пользователь и его адрес это модель, и пользователь и адрес хранятся в базе в отдельных таблицах. Только пользователь это сущность, а адрес нет.
Вот как одно от другого отличить и написано у Эванса.
>>2944198 > и ёнкомат узнает из налоговой, Вообще похуй, нельзя будет открыть с нуля, но то что есть не закрывают.
> же ведомства обязаны инфу передавать в ёнкомат. ГПХ/Самозанятые работает без этой хуйни. На работе про тебя все твой директор сдает и повестку тебе приносит, от которой уже неоткажешься.
> в том что её тебе в госуслуги закинули, и ты по факту обязан. Реестра повесток нет, по слухам юристов не ранее 2025, да и должны обеспечить бесплатным инетом и компом ибо нет закона что я обязан все это иметь. Закон - пугалка.
Причина твоей тряски? Хочешь чтобы я на работу устроился или че?
>>2944198 Ну и пока повестка считается врученный когда на ней роспись поставил или заказное письмо пришло и ты его взял или пришел по извещению в почту и отказался. Другие варианты не предусмотрены законом.
Но если ты не запасник, а призывник у которого военника нет, то там свои законы и другая ответственность, более пиздецовая чем для запасников
>>2944327 >Прям только-только требования? Даже без раздела о подводных камнях от автора задачи (ну или человека, который разместил ее в гитхабе)? Да. Советы загружают мозг, заставляют думать о правильной структуре приложения, о всяких злых ООП, о том как бы всё сделать красиво и не обосраться, и т.д.
А когда сам пишешь, то всё просто - надо сделать приложение, которое будет иметь такой функционал. И всё. Это развязывает руки.
>Если я буду гуглить, не будет ли это нечто схожее, чем как если бы я спрашивал тут, у анонов, или читал советы по задаче? Для того, чтобы получить нормальный ответ от гугла, надо уметь гуглить - сформулировать запрос, потыкать по ссылкам, проанализировать ответы, если не подходят, то изменить запрос и загуглить ещё раз, и т.д. Это практический навык, его надо развивать и он очень сильно упрощает дальнейшее обучение.
Для того, чтобы (в теории) получить ответ от анона, надо... уметь писать на русском? При этом можно написать максимально расплывчатый и неконкретный вопрос, вообще не шевеля мозгами и никак не вовлекаясь в процесс. Это не очень сильно помогает и занимает время. Даже если ответ на такой вопрос будет хорошим, то всё равно не будет понятно, где его применить.
>Потому что тут живые люди, у которых можно переспрашивать. У гугла тоже можно переспрашивать. А ещё от анона ответы приходят за минуты и часы, а от гугла - за миллисекунды. И ещё гугл не устаёт, не бурчит, не спорит, и готов стерпеть самые глупые вопросы (которые даже в треде глупых вопросов бы не прошли).
>А если я не смогу нагуглить, я также буду затупливаться, сидеть, думОть, но не кодить. Если не получается нагуглить, то гугли по-другому. Гугли руководства к действию: что сделать, чтобы сайт запомнил юзера; что сделать, чтобы из кода залезть в базу данных, что делать, чтобы ... Результатом будут конкретные инструкции, которые можно попробовать и посмотреть, что получается. И дальше уже думать, как это можно использовать для решения проблемы.
>Да и не бросать же мне те советы, которые уже писали аноны? Если в какой-то момент возникнет ощущение, что ты можешь применить какой-то совет, и при этом будет ясно, какие именно строчки кода нужно писать для реализации этого совета - то флаг в руки, пиши, пробуй. С другой стороны, если есть только унылое ощущение, что по этой теме что-то советовали, но как реализовать не понимаешь - то не надо.
> Я уже согласен написать посредственный код, но чтобы у меня получился хотя бы внешне работающий продукт.
Не надо так. Надо сидеть и разбираться.
> Философию ООП-кода не понимаю.
А вот в учебнике есть глава про ООП и задача про ООО Вектор, ты ее делал? Она вроде как должна помогать разобраться в ООП. Или можем какую-нибудь другую задачу на ООП-проектирование тебе дать.
> Я считаю, что пусть я напишу плохой код, но пройду дальше
Если ты не разберешься с основами (ООП), как ты пойдешь дальше? Это как пропустить основы арифметики и браться сразу за матрицы и векторы.
> Я не представляю что будет находиться у меня в переменных, если я запущу сервер и моя страница будет запущена на двух отдельных устройствах и они будут посылать данные на мой сервер. > Т.е. как php-скрипт будет обрабатывать эти данные. > Одни данные перекроют другие? Для каждого браузера будет отдельный php-скрипт, у которого будут полностью чистые переменные и которые будут очищаться после того как php-скрипт отработает?
На каждый HTTP-запрос (неважно, от одного браузера или разных) создается новая чистая среда выполнения с пустыми переменными. Соответственно, если параллельно или по очереди выполняется несколько копий скрипта, они друг на друга не влияют.
Этот передовой stateless-подход, придуманный в PHP много лет назад, сейчас копируют ведующие облачные провайдеры (в сервисе AWS Lambda или Yandex Cloud Functions).
>>непонятно, зачем тебе метод carryOutVerification > Этот метод запускает отдельные проверятели-правил. Мне посоветовали в прошлом треде сделать главный класс и классы-проверятели (проверка на пустоту, на количество и т.д.).
Вот смотри, чтобы использовать твой класс, мы должны написать:
$validator = new Validator($rules, $enrollee, $errors); $validator->carryOutVerification();
Зачем здесь вызов carryOutVerification? Ты мог бы его сразу вписать в конструктор, чтобы он вызывался автоматически.
Правда это будет далеко от ООП. Тебе надо пойти одним из 2 путей:
В ООП-стиле:
// создаем валидатор, который можно многократно использовать $validator = new Validator($rules); // используем $errors = $validator->validate($enrollee); // используем еще раз $errors2 = $validator->validate($enrollee2);
В процедурном стиле:
$errors = validate($rules, $enrollee);
Но твой стиль просто выглядит странно и нелогично. Непонятно, зачем вызывать функцию, в которую ничего не передается и которая ничего не возвращает.
>>ты передаешь в конструктор студента для проверки > Я отдал его в конструктор, потому что думал, что если для работы Валидатора нужен объект другого класса, то я должен добавлять зависимости через конструктор, а не отдавать объекты в методы.
Ну ок, значит я плохо написал статью про DI и запутал. Обычно под "зависимостями" понимают другие сервисы. Например, если валидатору нужен лезть в базу данных, то можно передать в него объект для работы с БД как зависимость.
Студент не является зависимостью, он является объектом, который валидатор обрабатывает. Как и список ошибок - это не зависимость, а результат обработки студента.
Более того, в твоем случае еще плохо то, что студент содержит данные из формы, а в DI контейнер не должны попадать такие данные (так как он может создаваться и заполняться до обработки формы).
То есть, в ООП мы передаем студента в метод проверки, и он возвращает список ошибок (желательно объектом).
А вот правила можно передавать 2 способами:
- в конструктор - в метод проверки
Разберем оба варианта:
1) правила передаются в конструктор. В этом случае правила получаются как бы "заложены" в объект валидатора, и их нельзя поменять (можно только создать еще один валидатор). Но зато тому, кто использует валидатор, не нужно знать эти правила:
// в DI контейнере // создаем валидатор и закладываем в него правила // я называю его studentValidator, чтобы показать, что он // предназначен только для валидации студента и ничего больше $studentValidator = new Validator($rules);
// если мы хотим проверять что-то еще, то мы должны создать отдельный // объект-валидатор с другим набором правил
// где-то далеко в другом месте кода, где надо проверить студента $errors = $studentValidator->validate($enrollee);
Здесь правила заложены в объект. Плюс в том, что когда мы хотим проверить студента, нам не надо знать правила: мы получаем уже объект с встроенными в него правилами.
2) правила передаются в метод проверки
Здесь плюс в том, что одним валидатором можно проверять разные вещи, а минус в том, что теперь тот, кто хочет проверить что-то, должен знать правила. Если мы делаем проверку в нескольких местах, у нас может получиться, что правила будут дублироваться в несколькоих местах кода, что очень плохо.
// в DI контейнере создаем универсальный валидатор $validator = new Validator();
// в другом месте кода используем его $erros = $validator->validate($enrollee, $rules);
Вот такие 2 варианта обычно используют в ООП. Такой подход очень часто встречается:
- создаем объект и в конструктор передаем параметры, настройки объекта, которые не меняются - затем используем объект, передавая данные (сущности, числа, строки) в метод и возвращая из них результат
>>Ты достаешь предудущую ошибку, склеиваешь с ней новую и записываешь обратно. > Я так сделал, потому что иначе бы один текст ошибки перекраивал другой. > Ну, да ладно.
Ты поместил этот код не туда, куда нужно. Хранение ошибок это задача объекта СписокОшибок, и код склеивания должен быть в нем. Это не сфера ответственности валидатора, решать, как склеивать ошибки. Он лишь добавляет их в список. А список уже решает, что с ними сделать.
> Я должен переписать класс с текстом ошибок. > Добавить в него метод, чтобы он...сам клал значения ошибок и склеивал их.
Да.
> Нет пока что привычки добавлять все это
Самое время завести привычку.
> Как не обращусь с вопросом, так сразу неправильно у меня все.
Радуйся, тебя учат и дают новые знания, причем бесплатно.
> Начинаю вместо кода сидеть думать как правильно строить классы.
Так как ты начинающий, то ты перед написанием кода как раз должен был на листочке или на экране расписать структуру приложения: какие классы в нем есть, за что каждый отвечает и как они взаимодействуют. То есть, ты об этом и должен был думать с самого начала.
> ведь мне не хватает знаний и опыта, чтобы понять
Учись задавать вопросы и спрашивать, что непонятно.
1) есть domain model - ООП-модель предметной области из кучи классов, связанных стрелочками. Domain model обозначает не один класс, а все вместе.
2) есть model в MVC - это не только сущности, но и вообще весь код с бизнес-логикой. Я пробовал поискать определение, но гуглить такие темы бесперспективная задача. В Вики, например, написано:
> It is the application's dynamic data structure, independent of the user interface. It directly manages the data, logic and rules of the application. In Smalltalk-80, the design of a model type is left entirely to the programmer. With WebObjects, Rails, and Django, a model type typically represents a table in the application's database.
Тут видны противоречия: сначала написано, что в модели содержится "data. login and rules", а потом пишется что в Django модель просто представляет таблицу в БД. Таблица это не то же самое, что data, logic and rules.
В статье Microsoft написано лучше:
> An MVC model contains all of your application logic that is not contained in a view or a controller. The model should contain all of your application business logic, validation logic, and database access logic.
То есть, модель в MVC (по мнению автора) это не просто класс User, а еще куча сервисов для работы с ним. Тут я согласен.
3) многие PHP-разработчики традиционно понимают под "моделью" класс с SQL-запросами к таблице (TableDataGateway), который принимает и возвращает сложные массивы с данными
Поэтому я стараюсь этот термин не использовать вообще, так как он 100% вызовет у начинающего путаницу. Если у тебя есть идеи, какой термин использовать для классов с данными, то предлагай. Если у тебя есть ссылка на справочник где даны четкие и строгие определения (не как в Википедии) - давай.
Вот вообще, ты хорошо критикуешь, но плохо предлагаешь. А я ведь не против улучшить статьи, использовать более точные термины и определения.
> Сущность противопоставляется не сервису, а классу который полностью определяется его данными. Как адрес или дата.
Это ValueObject. Получается, у нас тогда как минимум 3 вида классов: сервисы,сущности и value objects. Но мы все равно можем разбить их на 2 группы: классы с данными без DI и классы с операциями, как правило, использующие DI (вопреки идее ООП, что данные и операции должны быть объединены в одном классе).
> И пользователь и его адрес это модель, и пользователь и адрес хранятся в базе в отдельных таблицах. Только пользователь это сущность, а адрес нет.
А по определению из статьи Entity-Relational Diagram адрес тоже сущность:
> An entity may be defined as a thing that is capable of an independent existence that can be uniquely identified, and is capable of storing data
Адрес существует сам по себе? Конечно. Есть id? Конечно. Хранит данные? Да.
У меня одна просьба ко всем: прежде чем отправлять в Гугл, воспользуйся им сам.
Проблема статей по программированию в Интернете в том, что они:
- часто недостаточно технические и не рассказывают нужные подробности и детали, а просто дают совет в стиле "скопируйте этот код" - иногда предполагают обширные знания и не рассчитаны на новичков - часто неточные, некачественные, устаревшие (особенно по PHP таких много) - используют плохие подходы - часто не на русском
Ну например, я написал статьи на codedokode/pasta именно потому, что по этим темам не смог ничего нормального найти. Если бы там были нормальные статьи, я бы просто давал на них ссылки, а не писал сам.
А я ждал техническое объяснение: рисунок, на котором показано, как они передаются и хранятся; рисунок, где они показаны в HTTP-запросе и ответе; как их просмотреть в curl и в браузере; какие у кук есть атрибуты; лучшие практики по безопасности; функции для работы с куками.
И чтобы это было понятно начинающему и на русском. Вот такие статьи я хочу в Гугле видеть на первой странице.
> На сервере Хекслета ваш запрос обрабатывается. Программа достаёт из базы данных все последние тексты из рубрики «Истории успеха», чтобы показать список. Это можно сравнить с кухней и поварами из примера с сэндвичем. Это модель.
А затем в примерах кода модель у них состоит из одной сущности:
> Модель отвечает за данные, которые хранятся и обрабатываются на сервере.
ну вот такого качества статьи заполняют первые страницы Гугла. Прежде чем отправлять в Гугл, проверь результат сам. Или пиши волшебный запрос, который дает нормальные статьи.
>>2945219 >задача про ООО Вектор, ты ее делал? Да. >Она вроде как должна помогать разобраться в ООП. Когда я впервые читал урок из мануала про ООП, для меня понятия "свойство", "метод", "тайп-хинт", "модификаторы доступа" и т.д. воспринимались чем-то абстрактным, я не мог усвоить их как само собой разумеющееся. Раздел с тайп-хинтом я вообще пробежал глазами и решил забыть о нем, потому что тогда ничего не понял, но сейчас уже понимаю. Так что думаю, прогресс есть. >Если ты не разберешься с основами (ООП), как ты пойдешь дальше? Это как пропустить основы арифметики и браться сразу за матрицы и векторы. Здесь я имел ввиду то, что, мне кажется, что я занимаюсь перфекционизмом в решении этой задачи. Чего я должен добиться этим? Сейчас сесть и с нуля научиться проектировать системы так, как это делают люди с опытом? Мне кажется, что я много на себя пытаюсь взять. Я понимаю, что от джуна будут требовать понимание основ ООП. Но ведь от него не будут требовать идеального проектирования системы? Просто мне кажется, что я ухожу не туда. Мне просто нужно усвоить основы. А что делаю я? Учусь на проектировщика систем? Я думаю, что мне нужно получить некий базис, который позволит мне кодить. А потом одни знания лягут на другие и те знания, которые были в тумане, усвоятся.
>>2945229 Окей, спасибо за ответ. >>2945232 >Зачем здесь вызов carryOutVerification? Ты мог бы его сразу вписать в конструктор, чтобы он вызывался автоматически. Действительно. Я в таком ключе не думал. >$errors = $validator->validate($enrollee); Не понимаю эту строку. Что из себя представляет $errors и что в таком случае будет возвращать $validator->validate($enrollee)? И если errors - это объект, то какое у него будет содержание?
>>2945268 >Радуйся, тебя учат и дают новые знания, причем бесплатно. Если ты думаешь, что я негодую от того, что мои решение (лучше сказать - попытки реализовать то, что тут советуют) считают неправильными, то это не так. Я всегда прихожу в тред с пониманием, что я неправ. Мб мои формулировки звучат нагло, но ими я пытаюсь донести какое представление о предмете находится у меня в голове. Я никогда не пытаюсь ничего доказать. Я очень благодарен всем, кто давал мне толковый совет или объяснение.
>>2945310 >есть domain model Что значит "есть"? Есть подход "domain model development", который основан на выделении "domain" - "предметной области" и моделировании её. Делается это определенным образом с соблюдением определенных правил. Сущность это часть понятийного аппарата DDD. Термин вырван из контекста (лол) и используется как баззворд.
>есть model в MVC - это не только сущности, но и вообще весь код с бизнес-логикой. Ты опять описал domain model.
>Я пробовал поискать определение, но гуглить такие темы бесперспективная задача. А спорить в интернете - перспективная. В смаллтолк MVC это observer паттерн, который отслеживает изменения в объектах представления. Джаваскрипт, который ловит события типа нажатий на кнопки это оно. Ко временам джанго и рельс модель из MVC путем баззвординга и непонимания концепта превратилась в "данные между веб запросами". А определение, которое ты считаешь "лучше" совсем бессмыслица, потому что слишком широкое. Согласно этому определению роутинг - часть модели. Потому что содержит логику, валидирует ввод и доступ. При этом роутинг по определению происходит перед контроллером. Чушь и бессмыслица.
>Поэтому я стараюсь этот термин не использовать вообще, так как он 100% вызовет у начинающего путаницу. Поэтому ты используешь вместо этого термины с другим значением и определением, из других областей и подходов. И никакой путаницы нет, все четко. Ну тут не доебешься, продолжай. Всем все понятно.
>>2945333 >классы с данными без DI и классы с операциями, как правило, использующие DI (вопреки идее ООП, что данные и операции должны быть объединены в одном классе). Даже я хуй пойму что тут написано. Опять все смешалось.
1) Наличие "данных" ничего не говорит о том является ли объект сущностью. Что является чем диктует исключительно предметная область. Логика работы сервиса вполне может храниться в базе, но сущностью сервис от этого не станет. Так же и адрес не станет сущностью если добавить ему идентификатор. Как только улицу переименуют, все объекты с этой улицей придется изменить одинаковым образом и похуй сколько там идентификаторов.
2) "Объекты с данными" вполне себе могут использовать DI. У них точно так же есть конструктор, и точно так же можно передать зависимость в вызываемый метод. Да, инъекция через метод это тоже DI
>>2946247 Ну ладно, я хуйню написал и пхп.ини-дев - это норма, пишут что ее просто переименовывают в .ини и не ебут мозги себе Но вообще, вы все из исходников собираете? Или не страдаете хуйней такой
Чет у меня то расширения при сборке не ставятся, то десять каких-то либ нехватает. А мануала норм свежего не нашел. В итоге 8.1 поставил через апт,лол
>>2946263 Ини, хуини. Все это уже сто лет как ставится через докер. У меня на работе проектов двадцать, и у каждого какая-то особая версия и либы. Какой еблан будет с этим вручную морочиться?
>1к+ вакансий на БУС >меньше 100 откликов на каждой >зарплаты ниже "трушных фрейворков" всего на 20-30% >вкатыши продолжают катиться в ларабэль (200-500 откликов на каждой, зп не сильно выше, требований больше, много где требуют vue) Ебало? Имагине? Вкатышей?
>>2946271 Будешь и там и там рыться и мучительно разбираться что и как. Из плюсов битрикса: больше работы, много интересных проектов, много смежных технологий (nodejs, laravel).
TL;DR: Если подразумевается, что у свойства есть метод для валидации значения, должен ли он вызываться внутри сеттера? Или я в коде должен делать что-то типа if (валидно) { вызвать_сеттер }?
Есть свойство класса, которое, как я знаю, может быть в определённом интервале. Ну, допустим, Yoba::$age, и я знаю, что это целое число 0 < $age < 101 Я добавляю метод Yoba::validateAgePretendent(), который, собственно, выполняет эту проверку. Вопрос - как быть с Yoba::setAge()? Нужно ли добавлять в него валидацию? Или нет? Или сделать ещё метод типа Yoba::safeSetAge(), который и проверяет значение, и присваивает его?
У меня мысли такие. Наиболее правильно никак сеттер не менять, а обёртку Yoba::safeSetAge() добавлять в случае нужды, если в десяти местах у меня будут появляться
if ($yobaObj->validateAgePretendent($value)) { $yobaObj->setAge($value) }
С одной стороны, в 99.99% случаев, Yoba::$age должен проходить мою валидацию. С другой стороны, а вдруг нет? Вдруг будет какая-то ситуация, которую я не предусмотрел, и в которой такой-то объект на такое-то время должен иметь $age, не вписывающийся в этот диапазон? Если я добавлю проверку в сеттер, то я не смогу этого сделать.
Хотя, я вот сейчас это написал и задумался. Есть же проверка типов в php, $age это, положим, int. И я никак не смогу присвоить $age значение 12.3, будет TypeError. Ибо нехуй. Вот и присваивать $age значение, которое не проходит валидацию, нехуй. Если возникла такая ситуация, значит, валидация кривая и нужно её редактировать.
Короче, хуй пойми. Как делать, чтобы сеньоры говорили малаца?
>>2946493 Погуглив и подумав, начал склоняться к варианту, в котором проверка значения выполняется внутри сеттера. Ещё такой аргумент есть - а нафиг тогда сеттер вообще нужен? Если всё, что он делает и я как бы ожидаю, что будет делать всегда - возвращает значение, то почему бы не сделать свойство public и не редактировать напрямую? Сеттер затем и нужен, что подразумевает возможность добавить валидацию или ещё что-нибудь.
>>2946653 В свойство может прилететь некорректное значение и из-за ошибки в коде программы. Если оно изменяется только через сеттер, то в сеттере можно делать проверку входящего значения и сразу выдавать исключение, если оно некорректное.
>>2947152 Не "класса", а типа. И в чем проблема? Тебе все равно эти функции писать придется. Если ты пишешь ооп код, так и пиши его в виде взаимодействия объектов. А если пишешь процедурную дрисню, так нахуя тебе тогда все эти di заморочки, модели какие-то.
>>2947314 >1) Метод setAge не знает каким образом происходит валидация, но с какого-то хуя формирует текст ошибки. Это для примера. На деле, там будет скорее throw new ValidationFailedException, который уже формирует текст.
>Метод ageIsValid не имеет никакого отношения к Yoba. Вынеси его отдельно и он будет точно так же работать. И? Это плохо? Предпочтительней было бы переписать это как-то так? https://3v4l.org/GCRfZ
>3) Какого хуя вообще можно поменять возраст? Ты блядь не можешь себе поменять возраст и купить пива, это не так работает. >4) Даже допустим возраст можно менять. Кто это блядь такой умный что этот возраст меняет? Кто вызывает setAge? Пикрелейтед.
>>2947382 >переписать это как-то так? Теперь этим методом можно провалидировать только возраст Yob'ы, а возраст Zalup'ы нельзя. И залупе придется писать свой валидатор.
>Пикрелейтед. Ты либо пишешь "модель", которая должна соответствоавть моделируемуму, любо просто какой-то код и тогда вообще похуй как оно там написано. Точно так же, ты либо пишешь ооп код и тогда ты соблюдаешь все эти инкапсуляции сингл респонсибилити итд, либо катаешься ебалом по клавиатуре и тебе все это не надо.
Это ведь ты выбрал писать "модель" в ооп стиле. Ну так пиши. Не хочешь - не пиши.
Двач, как научиться читать код на абстракциях? Вот мне дали ебаный DDD, решил поправить роутик, ну по классике зашел в экшен, а этот ебаный экшн вызвает формы, те еще какую-то хуйню, а те еще какую-то залупу, а те еще что-то. Репозитории, формы, dto, провайдеры, entity и прочая хуета. Пока пройдешься по цепочке, забудешь к хуям вообще все. По классике никаких комментов в коде нет, сам разбирайся. В итоге горбатюсь над заданием уже 2-й день. Как вообще такое понимать и читать?
>>2947889 Для веба на пхп вроде больше вакансий чем на питоне. На питоне не малая часть вакансий это ии, дата саенс, машин ленин, но это для математиков, а не макак
> Здесь я имел ввиду то, что, мне кажется, что я занимаюсь перфекционизмом в решении этой задачи. > Чего я должен добиться этим? Сейчас сесть и с нуля научиться проектировать системы так, как это делают люди с опытом?
Задача учит использованию ООП, работе с базой данных, формами, генерации HTML-страниц через шаблоны. Все это нужно в реальной работе.
Я думаю, что джун должен сразу писать правильный ООП-код. Или ты предполагаешь, что джун может писать как попало, а потом сеньор будет сидеть за него все исправлять? Никто джуна обучать заново не будет, его просто уволят с испытательного и скажут идти читать книжки.
> >$errors = $validator->validate($enrollee); > Не понимаю эту строку. > Что из себя представляет $errors и что в таком случае будет возвращать $validator->validate($enrollee)? > И если errors - это объект, то какое у него будет содержание?
О, хороший вопрос и по делу.
Раз у нас ООП то $errors это объект, который представляет (и хранит в себе) список ошибок валидации студента. Но почему только ошибки валидации студента? Давай обобщим, $errorList это объект класса ErrorList, который способен хранить список ошибок валидации любого объекта.
У ErrorList есть такие методы:
- добавить ошибку к заданному полю (например: добавить ошибку "использованы недопустимые символы" к полю "имя") - получить список всех ошибок для поля - узнать, пуст ли список ошибок или в нем есть ошибки
Вот как он используется:
$errorList = new ErrorList(); // создали пустой список
Что касается ошибки, то одну ошибку мы могли бы представить строкой:
Я тебе хотел просто сказать, что слово "модель" ассоциируется с несколькими терминами и сбивает начинающих, если они пытаются его гуглить. Поэтому я не хочу его использовать.
Ну то есть ты хочешь называть сущности "модель", но "модель" более широкое понятие и включает в себя и сервисы для обработки сущностей.
Также, судя по датам, понятие domain model старше чем DDD: оно упоминается в книге Фаулера от 2003 года. И MVC старше, чем DDD.
> А определение, которое ты считаешь "лучше" совсем бессмыслица, потому что слишком широкое. Согласно этому определению роутинг - часть модели. Потому что содержит логику, валидирует ввод и доступ. При этом роутинг по определению происходит перед контроллером. Чушь и бессмыслица.
Модель содержит бизнес-логику приложения. Роутинг это не бизнес-логика, а техническая штука.
1) в объекте не может быть невалидных значений ни при каких условиях. В этой ситуации мы ставим проверку в сеттер и при нарушении правил выкидываем исключение и роняем всю программу. Зато мы знаем, что в объекте правильные данные.
Тут надо учесть, что при создании объекта в нем изначально должны быть правильные данные. Это может потребовать ставить значения по умолчанию или передавать значения в конструктор.
2) в объекте могут быть невалидные значения, например, потому что они пришли от пользователя. Тогда мы принимаем все, но перед использованием объекта или записью в БД проверяем валидатором.
То есть, либо мы проверяем значения в момент записи, либо позже, перед использованием. Минус второго способа в том, что можно забыть сделать проверку, а в первом способе у нас просто не существует неправильных объектов. Минус первого способа в том, что он плохо сочетается с формами, так как в форму можно вводить неправильные значения.
Как не надо делать: при передаче неправильного значения просто не сохранять его и ничего не делать. Это самый плохой подход, так как ошибки в коде будут оставаться незамеченными.
> Если всё, что он делает и я как бы ожидаю, что будет делать всегда - возвращает значение, то почему бы не сделать свойство public и не редактировать напрямую? Сеттер затем и нужен, что подразумевает возможность добавить валидацию или ещё что-нибудь.
Да. Даже если тебе не нужна сегодня проверка, то она может понадобиться завтра и с сеттером тебе не придется переписывать пол-программы из-за этого.
Но есть и другой подход, например, в Питоне: там используют свойство напрямую, без сеттера. А если нужно добавить какую-то логику, то там используют магию и добавляют функцию, которая автоматически вызывается при изменении поля. Ну то есть по сути это тот же сеттер, только он вызывается при записи в поле. Просто синтаксис другой, а суть та же.
Это паттерн ValueObject. Идея в том, что значение представлено в виде объекта. То есть, у тебя возраст имеет не тип int, а тип Age. Минусы:
- код увеличивается
Плюсы:
- мы можем с помощью тайп-хинтов указать, что в функцию можно передать только Возраст, а не вообще любое число. Это повышает читабельность и защищает от ошибок - мы можем добавить в значение дополнительные поля
Вот пример: допустим, у тебя приложение работает с деньгами в разных валютах. И появяется риск, что ты сложишь рубли с долларами по ошибке. Ты можешь защититься от этого, представив деньги в виде объекта Money с полями "сумма" и "валюта":
$tenRoubles = new Money(10, Money::RUB); $twoRoubles = new Money(2, Money::RUB); echo ($tenRoubles->add($twoRoubles)->format()); // 12 руб
Так как мы заменили число на объект, теперь для сложения надо использовать метод add, а он проверяет, что суммы в одной валюте. Также, если функция принимает деньги, то это очевидно из тайп-хинта и ничто другое туда не передать (защита от ошибок):
function addToAccount(Money $sum) { ... }
Но я думаю, что делать типы вроде "имя", "возраст" - это уже перебор. Хотя идея интересная.
Но в задаче про студентов все же есть места для ValueObject. Пол и место жительства желательно сделать с помощью Enum (перечислений). Это по сути и есть ValueObject.
У твоего подхода есть недостаток: ты совместил Возраст и Правила проверки возраста в одном объекте. Представь, что у нас кроме абитуриентов будут еще бакалавры и магистры и у них тоже есть Возраст, но с другими ограничениями. Твой подход не сработает.
Также, зачем ты ставишь скобки после class не по PSR. Ставь нормально и не сбивай людей с толку.
Ты ударяешься в крайности. В реальном коде нужны как мутабельные, так и иммутабельные объекты. На мой взгляд, неправильно требовать, чтобы все было иммутабельно. Это неудобно, так как если у тебя есть например 10 ссылок на один иммутабельный объект, то для изменения данных тебе надо поменять их в 10 местах.
Данные могут меняться. В случае с возрастом - абитуриент мог ошибиться при вводе и хочет исправить это.
>>2948481 Ну я просто изначально в бекенд метил, но сильно перекосило в сторону нюхта и вуе. Единственное, что меня удерживает от вакансий чистого фронта - это ебануштейший отбор и собеседования
>>2948079 >Ну то есть ты хочешь называть сущности "модель", но "модель" более широкое понятие и включает в себя и сервисы для обработки сущностей. А я тебе сказал что тебе в твоей терминологии не нужен термин "сущность". Ты все равно используешь его неправильно. Достаточно просто различать классы по их названию и назначению. С тем же успехом ты мог бы говорить: вот этот объект кирпичик, а вот этот сервис, не перепутай. А если перепутаешь, то что? Да ничего. От того что он кирпичик тебе ни жарко ни холодно.
>Также, судя по датам >гуглить такие темы бесперспективная задача
>Модель содержит бизнес-логику приложения. Роутинг это не бизнес-логика, а техническая штука. Да ну? Права доступа это не бизнес логика? Параметры ссылки это не бизнес логика?
>>2948083 >термины для обозначения классов, которые представляют какую-то сущность Классы не представляют никакой сущности. У тебя есть "модель" реального мира, именно по параметру "моделирования" мы и отличаем этот набор объектов от любого другого. Отличать их между собой никакого практического смысла нет. В DDD они отличаются именно из-за способа моделирования. Нет способа - нет отличий.
>>2948113 >Это паттерн ValueObject. Это паттерн Object. Ну знаешь, из ооп. Ты инкапсулируешь поведение, а система типов гарантирует правильность программы. Оо "ебать его в сраку" п.
>будут еще бакалавры и магистры и у них тоже есть Возраст, но с другими ограничениями. Твой подход не сработает. И что же мне помешает держать одни ограничения в одном месте, а другие в другом? Тупость?
>Также, зачем ты ставишь скобки после class не по PSR. Ставь нормально и не сбивай людей с толку. Я делаю это чтобы листинг уместился на один экран. И вообще в 2к23 форматирование это ебля того кому не нравится формат. Отформатируй и перепости - помоги людям не сбиваться с толку. Делов-то - ctrl alt L нажать.
>Данные могут меняться. В случае с возрастом - абитуриент мог ошибиться при вводе и хочет исправить это. Модель мутабельна когда мы моделируем смену возраста и иммутабельна когда мы моделируем как студень чешет нос. Легко и просто. Железная гарантия что при прикосновению к носу яйца не отвалятся.
Короче все. Завязываем бессмысленный срач. Никакой полезной нагрузки он не несет. "Учиться" чему-то в таком формате невозможно, а писать простыни текста мне впадлу.
>>2948052 >Я думаю, что джун должен сразу писать правильный ООП-код Я с тобой полностью согласен. Я имел ввиду глубину знаний. Я себе представляю так, что от джуна требуется определенная глубина знаний в каждом из вопросов. Просто из этой задачи я ушел куда-то в чтение проектирование систем. Тут еще советуют какое-то DDD. Короче. У меня сложилось впечатление, что я пытаюсь копать так глубо, сколько не требуется пока что от джуна. Я думаю, что знания должны накладываться слоями. От простого к сложному. И для того, чтобы усвоить определенные знания, человек должен быть готов. Он должен обладать такими знаниями, которые позволят ему понять новое знание.
>>2948070 Окей. Я это понял так: Класс errorList состоит из: Свойств (например, name, surname) Трех методов, 2 из которых работают с отдельными полями, а один - со всеми. Что в таком случае класс Error? Судя по инструкции создания объекта, в конструктор передается строка с описанием ошибки, значит у него есть какое-то поле, которое хранит ошибку. А что если поле не прошло валидацию сразу по нескольким правилам? В конструкторе должен быть метод, которые берет текст ошибки (которую отдали в параметры конструктора) и склеивает его с другими текстами ошибок, а потом кладет в значение поля? Спасибо, что отвечаешь мне.
>>2948634 Я тут еще подумал. Вернемся немного назад. Мы обсуждали метод класса Validator - carryOutVerification(). Насколько я понял, в теле этой функции нужно вызывать метод класса ErrorList - add(). Касаемо последнего твоего ответа, я снова запутался. Что все-таки означает строка $error = $validator->validate($enrollee)? Просто у меня есть такое представление на задворках ума, что carryOutVerification() ничего не будет возвращать. Т.е. где-то будет инструкции: $errorList = new ErrorList; $validator = new Validator(...); И потом, возможно, это будет в коде контроллера, инструкция: $validator->carryOutVerification(); Я руководствуюсь тем, что объект всегда передается по ссылке. А значит зачем функции что-то возвращать, если она может сложить результат своей работы в объект, предварительно взяв его? Я не понимаю эту строку "$error = $validator->validate($enrollee)", потому что не понимаю что она возвращает. Ведь validate это и есть carryOutVerification? Эта часть "$error =" означает, что в переменную что-то кладется, но что? Вообще. У меня большая проблема с пониманием того, что и где будет в коде. Например, с тем же DIContainer. В каком файле я буду описывать регистрацию сервисов? Это лишь пример, чтобы показать, что я пока что не понимаю где какая будет инструкция. Не знаю, поймешь ли ты меня, я столько каши понаписал.
>>2947406 Проспавшись и пробздевшись, я, кажется, начал понимать, о чём ты говоришь. >>2948113 Этот помидор подтвердил мои догадки. В таком случае, я бы завёл
class Age
И там внутри уже пердолил всякие проверки и т. д. В данном же случае, Yoba - условно не делим. При большом желании можно, конечно, но зачем? На практике, там не число, а строка определённого формата. И, собственно, класс, в моём примере обозначенный Yoba, и нужен лишь для того, чтобы хранить эту строку определённого формата и не давать его нарушать.
То есть, твои ValidAge NotValidAge ValidateAge - более заёбная правильная наверное реализация того, чем является класс Yoba. А твой User, это, по сути, не мой Yoba, а тот, одно из свойств которого - объект Yoba.
Если следовать твоему пути, то должно получиться вообще как-то так, насколько я понимаю: https://3v4l.org/pJbBI#v8.3.0
>>2948119 Прикольно вы придумали, что я задался этим вопросом в процессе решения какой-то задачи про студентов.
Только ещё вопрос. Я нуб и как-то вообще пропустил момент, это что? Область видимости для аргументов метода? Как оно работает? По каким словам гуглить?
>>2945253 Я тут еще подумал. Да. Я сейчас понял, что я снова забыл, что Студент и Список ошибок - не зависимости. Значит нужно выкинуть их из полей Validator? Я думаю, строку $error = $validator->validate($enrollee) Нужно понимать так: error - переменная, которая будет хранить ссылку на объект. validate возвращает объект.
>>2948634 Зависимостью называется не любой класс, а только класс из другого модуля. Если оба класса часть "модели" то это не зависимость. Модель не может зависеть сама от себя.
Список ошибок не надо передавать в валидатор. Валидатор сам создаст его и заполнит по итогам проверки.
Ну подумай сам: мы вызываем валидатор, даем ему студента, он нам взамен возвращает список найденных ошибок. Зачем список ошибок передавать в валидатор? Зачем он нужен валидатору? Наоборот, логично: пока мы не передадим студента, мы не получим список ошибок. Нет другого способа его получить (разве что создать и заполнить самому, увы, в PHP это запретить пока что нельзя).
Ты наверно это придумал после чтения статьи про DI. Но в DI мы передаем в конструктор классы-сервисы, настройки, опции. Список ошибок это просто данные, и валидатор его создает сам. У нас также нет нужны подменять список ошибок на что-то другое, и передавать его в конструктор нам не нужно.
Что касается устройства списка, не надо делать в нем никакие поля. Сделай просто массив для хранения ошибок. Тогда у тебя получится универсальный список ошибок, который не только для студентов годится, а для любых других объектов.
>>2948809 Так какого хуя ты лезешь в архитектуру если определений не знаешь? Если ты не умеешь писать код. Не знаешь БАЗУ. То какого хуя ты лезешь туда, куда не каждый мидл полезет?
>>2948911 Я и не собирался лезть в архитектуру, когда начинал решать эту задачу. Но написав код, я понял, что у меня получается хуйня. И мало того, что хуйня, так мне стало трудно ориентироваться в этой хуйне. С этого момента и начались эти темы про проектирование, архитектуру. А вопросы о том, что такое модель и модуль я задал для того, чтобы сообщение, которое написал мне анон, имело для меня практический смысл. Зачем писать что-то человеку так, как он не поймет?
>>2948942 У всех хуйня и всем трудно. Начинать надо сначала. 1) Ты не умеешь писать код, никакой, не только ооп. 2) Ты не знаешь базовых определений. 3) Вместо того чтобы нормально учиться ты впитываешь шизоидный поток сознания на двачах.
>>2948953 >Ты не умеешь писать код, никакой, не только ооп. Да. И я учусь. И все эти тупые вопросы, которые я задаю в треде - часть обучения. >Ты не знаешь базовых определений. Да, не знаю. >Вместо того чтобы нормально учиться ты впитываешь шизоидный поток сознания на двачах Что такое шизоидный поток сознания на двачах? Что такое нормально учиться?
>>2948959 >И все эти тупые вопросы, которые я задаю в треде - часть обучения. Если ты получаешь правильные ответы или вообще получаешь вменяемые ответы. Если ты понимаешь что в этих ответах сказано.
>Что такое шизоидный поток сознания на двачах? Тред, в котором неколько человек одновременно спорят друг с другом в твит формате. Да еще и с рассинхроном больше чем в сутки. Даже человеку, который и так все знает, нужно прилагать большие усилия чтобы просто понять с кем и о чем он пытается говорить.
>Что такое нормально учиться? Иметь нормальный, последовательный план обучения. Ты в универе учился? Или хотя бы в шараге?
>>2949002 >Ты в универе учился? Или хотя бы в шараге? По программированию - нет. >Иметь нормальный, последовательный план обучения. Какой бы ты список тем для меня составил? Звучит нагло, но если у тебя есть желание поделиться опытом - можешь написать. Я приму его к сведению и попробую сопоставить с моей организацией обучения. Сейчас я занимаюсь так: 4 часа на задачу Список Студента (из-за того, что я не решил все вопросы с одним аноном из треда я занимаюсь тем же, чем занимаюсь следующие 4 часа) 4 часа на чтение материала по роад карте. Вот такое обучение... Еще я начал читать книгу СИКП.
>>2949029 >По программированию - нет. Нет никакой разницы.
>Какой бы ты список тем для меня составил? И как ты себе это представляешь? За несколько минут составить нормальную программу обучения для незнакомого человека, который несколько лет хуй пойми чем занимался. Так не бывает.
>Сейчас я занимаюсь так: >4 часа на задачу Список Студента Ты занимаешься хуйней. Если бы ты по четыре часа в сутки писал код всю последнюю неделю, то у тебя была бы сотня вариантов одной только валидации.
>>2949057 >И как ты себе это представляешь? За несколько минут составить нормальную программу обучения для незнакомого человека, который несколько лет хуй пойми чем занимался. Так не бывает. Да нет, хотя бы набросать список направлений куда смотреть. Ну да, я виноват, использовал слово "темы". Ты пишешь "ты не умеешь писать код". Окей. Я согласен с тобой. В каком направлении мне смотреть, чтобы исправить это? >Ты занимаешься хуйней. Если бы ты по четыре часа в сутки писал код всю последнюю неделю, то у тебя была бы сотня вариантов одной только валидации. Ну, мб и хуйней. В эти 4-ре часа входят не только написание кода, но и разбор ответов с двача, касаемо этой задачи (Да-да, иногда мозги так не работают, что приходится читать-перечитывать), разбор чужого кода (который кидают в треде), чтение статей (связанных с вещами, о которых я должен знать, чтобы решить задачу). Ты можешь сказать, что и это хуйня. Ну, мне и похуй. Я занимаюсь так, как могу.
>>2949062 >Я занимаюсь так, как могу. Ты просто делаешь вид что занимаешься, вот и все. Это такой способ развлечься на двачах. Только вот анонам ты не рассказал что просто убиваешь время и они всерьез пытаются тебе что-то объяснить. Я тебе предлагал заняться обучением всерьез, но ты сказал "пук среньк".
>>2949087 >Ты просто делаешь вид что занимаешься Считай так. Я не преподаватель, чтобы уметь грамотно организовывать учебу. Если нет варианта лучше чем тот, что уже есть - я остаюсь заниматься по старому методу. Этим я руководствуюсь, когда планирую свое обучение. >Я тебе предлагал заняться обучением всерьез, но ты сказал "пук среньк". Если голосом будешь говорить один ты, то, окей, я согласен. Мне голосом говорить нечего. Мои бе-ме вряд-ли тебе хочется слушать. В тексте можно подумать, выбрать формулировку, а в голосе?
>>2948754 >Тогда у тебя получится универсальный список ошибок, который не только для студентов годится, а для любых других объектов. Почему бы тогда не сделать интерфейс для класса ErrorList?
>>2948754 >Что касается устройства списка, не надо делать в нем никакие поля. Сделай просто массив для хранения ошибок. Я не понимаю что это означает. С тем аноном у нас два класса, связанные с ошибками - ErrorList и Error. У ErrorList не будет полей name, surname и т.д., а будет одно поле-массив, где ключ - название поля, а элемент - ссылка на объект Error? Или же ErrorList вообще не класс, а массив?
>>2948070 Вот, посмотри, я дописал классы, которые мы с тобой обсуждали. Это черновой вариант. Я не оттачивал его. Потому что у меня не варит голова. Да и не все я уточнил у тебя. https://3v4l.org/ElabY
>>2949277 Ты можешь написать мне, в том ли направлении я тебя понимаю или нет. Если не в том, то что конкретно исправить и почему. Если где-то забыл тайп-хинт или перепутал что-то в цикле, то извиняюсь, я хотел лишь в общих чертах набросать то, что мы с тобой обсуждали. Проверять и исправлять все то, что я там написал, я планирую завтра, на свежую голову.
>>2932466 (OP) Сап. Работаю бэк разрабом. Есть задание, при определенных действиях удалить все бонусные карты пользователя и создать для него новую. Сперва я хотел создать запрос на удаление всех бонусных карт. Старший разраб сказал, что не все так просто из-за обмена. Из-за этого мы поднимемся на этаж выше, устроим созвон с 1сниками. Я не совсем понял, что за проблема может быть с обменом? Что это значит?
>>2949819 В прошлых 2-3 тредах было погугли. Если не найдешь, иди на главный торрент-трекер страны, там в комментариях на последних страницах описан способ
У меня есть абстрактный класс AAA. От него наследуются XXXXXX YYYYYY ZZZZZZ. В классе AAA определён метод, который делает примерно следующее:
public function foo () { echo $this->editable; }
Свойство $editable у каждого дочернего класса имеет своё значение, у AAA его, по сути, быть не должно, в этом нет смысла. При этом, значение не изменяется и задаётся в коде класса.
Вопрос - как это правильно сделать? По сути, мне нужна абстрактная константа. Чтобы я в AAA её объявил, а в детях задал её значения. Но абстрактных констант в PHP нет.
Или факт того, что я в AAA объявляю метод, который рассчитан на "$this->editable", который в AAA вообще не имеет смысла, а смысл имеет лишь в его детях, в принципе неадекватен? И правильнее скопипастить этот одинаковый метод в каждом из детей?
>>2950190 Забываешь добавить Bar::TEST, скрипт не показывает никаких ошибок. Чисто технически, мне полностью подходит объявление в родительском-абстрактном классе абстрактного метода типа:
abstract public function returnConst (): string;
А у детей:
public function returnConst (): string { return "const-value-1"; }
И т. д. Если забудешь у ребёнка объявить returnConst(), интерпретатор тебе на это моментально пожалуется. Но это отдаёт какими-то костылями, я использую метод там, где по сути нужна константа. Это наталкивает меня на мысль о том, что я вообще херню какую-то горожу, раз нормальными средствами это не делается. Или нет, и ситуация, в которой родительский класс оперирует какими-то значением, которое будет определено в его дочерних классах, это вполне нормально?
>>2950316 Ну и что ты хочешь услышать? Да, технически это работает. Есть ли какие-то другие варианты? А как блядь об этом узнать если ты не рассказываешь что ты пытаешься сделать, а постишь какие-то невнятные иксы и игреки.
1. Выглядит ли код, который я скинул, как хтонический пиздец, или ничего особо криминального в нём нет. 2. Насколько адекватна ситуация, когда в коде РОДИТЕЛЬСКОГО абстрактного класса используется какое-то значение, которого в этом классе нигде нет. И весь расчёт на то, что оно должно быть объявлено в НАСЛЕДНИКЕ этого класса.
Говорю же, меня напрягает отсутствие абстрактных свойств в PHP. Какие причины для их отсутствия? Просто не завезли, или указание на то, что я хочу чего-то неадекватного?
>>2950358 А я тебе ответил. Что если у тебя вариантов нет, то какя хуй разница как оно выглядит, вариантов то нет. А понять какие у тебя варианты можно только если ты объяснишь что пытаешься сделать. По сути ты показал код и спросил: "как мне сделать? так? или угадай как?"
>Говорю же, меня напрягает отсутствие абстрактных свойств в PHP. А где они есть? В шарпе? А еще?
>>2950394 У меня есть как минимум два варианта. 1. Забить гвоздь микроскопом. Иди сделать всё чётко. Хуй знает, ты не оцениваешь адекватность моего варианта. https://3v4l.org/CRNMZ#v8.3.0 2. В родителе объявить функцию как абстрактную, и потом реализовать её для каждого ребёнка. Получится копипаст, потому что содержание этой функции у разных детей отличается натурально одним параметром. https://3v4l.org/t2mUm#v8.3.0
>>2950430 Ты нахуя используешь абстрактный класс вообще? Чтобы вынести в него повторяющийся код. В одном примере повторяющегося кода нет, в другом есть. Вот и все.
Однако если поведение хотя бы в одном наследнике хотя бы немного начнет отличаться, то придется заново выделять повторяющуюся часть и делать рефакторинг.
И если бы ты не вел себя как баран и привел реальный пример, то можно было бы определить возможные подводные камни. А твои высосанные из пальца синтетические умозрительные конструкции специально подстроены под конкретную ситуацию и ни о чем не говорят. "на 100% одинаков" ага, верю.
>>2950463 Ты чё выёбываешься? > В одном примере повторяющегося кода нет, в другом есть Ага, poshatatMakabu() и goToMailRu() это не повторяющийся для всех дочерних классов код. Нахуй там вообще нужен абстрактный класс-родитель? Действительно. > Однако если поведение хотя бы в одном наследнике хотя бы немного начнет отличаться, то придется заново выделять повторяющуюся часть и делать рефакторинг Так, блядь, оно и отличается. Отличается одной строчкой (string). Значением одного аргумента, которые передаётся в вызываемую внутри функцию. Больше ничем отличаться не будет. Я либо копипащу этот метод в каждый дочерний класс, меняя одну единственную строчку (ещё раз: не строчку кода, а аргумент string, передающийся в одну из вызываемых внутри функций). Либо ссылаюсь на что-то, чего в родительском классе нет, но должно быть в дочернем. На какую-то константу, свойство, результат выполнения метода. И из этих вариантов третий как будто самый годный. Константу я могу забыть объявить в дочернем классе, и узнаю об этом, только когда во время работы скрипта он дойдёт до этого момента и упадёт. Со свойством аналогично. А ещё, чтобы его нельзя было менять по ходу работы скрипта, нужно попердолиться с readonly и добавлением его установки в конструктор. Иначе, существует теоретическая вероятность того, что можно по ошибке в процессе работы изменить значение этого свойства. А это не подразумевается. Вот абстрактный метод, который просто возвращает значение - как бы заебись. Его точно не забудешь объявить у ребёнка, парсер сразу пожалуется. Возвращаемое им значение неизменно и задаётся в коде.
> Достаточно просто различать классы по их названию и назначению.
Нет, я вижу (в фрейморках, реальных проектах) что классы очень хорошо делятся на классы с данными и классы с операциями, и мне нужны названия для них. Как я без названий буду объяснять это начинающим?
> Да ну? Права доступа это не бизнес логика? Права доступа проверяются обычно в контроллерах. Так-то это бизнес-логика, но удобнее проверять в контроллере.
> Параметры ссылки это не бизнес логика? Строго говоря, нет. Бизнес-логика это логика вроде "пользователь должен иметь возможность заказать суши на дом", а не то, через какие параметры мы должны передать его адрес.
> Класс errorList состоит из: > Свойств (например, name, surname)
Нет. Если ты к свойствам будешь обращаться как $this->$name, то тебе нужны не свойства, а массив.
К тому же, если ты пропишешь туда свойства, то класс сможет хранить только ошибки для студента. А если сделаешь массив, то он сможет вообще для любых полей любого класса хранить ошибки.
Просто делаем массив вида array<string, Error[]> (то есть массив с ключом-именем поля и содержимым в виде массива Error).
> Что в таком случае класс Error?
Класс, представляющий одну ошибку. Хранит ее текст и, может быть, какие-то дополнительные подробности.
> А что если поле не прошло валидацию сразу по нескольким правилам?
Именно поэтому в списке ошибок мы храним для каждого поля не одну ошибку, а массив. Не надо ничего склеивать.
> Просто у меня есть такое представление на задворках ума, что carryOutVerification() ничего не будет возвращать.
Список ошибок это результат работы функции валидации. Для возврата результатов в PHP есть инструкция return. Зачем делать какие-то обходные способы, когда ты передаешь будущий результат в один метод, а потом другой метод его заполняет. Это же просто нелогично и запутывает при чтении кода.
То, что нужно функции, передается через аргументы. Результат работы возвращается через return.
> Я не понимаю эту строку "$error = $validator->validate($enrollee)", потому что не понимаю что она возвращает.
Объект класса ErrorList со списком найденных ошибок (объектов Error).
> Например, с тем же DIContainer. В каком файле я буду описывать регистрацию сервисов? Это лишь пример, чтобы показать, что я пока что не понимаю где какая будет инструкция.
Это зависит от того, как ты реализуешь DI: будешь писать сам или возьмешь готовый. Я покажу 2 варианта самодельных DI:
1) делаем класс с 2 методами: описать сервис и получить сервис. Выглядит это так:
$container = new Container(); // регистрируем сервис, то есть, описываем как его создать // в качестве имени сервиса используем имя класса $container->register(SomeService::class, fn () => new SomeService(1, 2, 3)); // в другом месте получаем сервис по имени $svc = $container->get(SomeService::class);
Обрати внимание, что для любителей создавать контейнеры есть рекомендация PSR-11: https://www.php-fig.org/psr/psr-11/ . Наверно, стоит ей следовать в таком случае.
2) делаем не по PSR: делаем класс, в котором для получения каждого сервиса сделан свой метод
class Container { public function getSomeService(): SomeService { // создаем или возвращаем ранее созданный объект return $this->createOrReturn(fn() => new SomeService(1, 2, 3)); } }
Минус - здесь все сервисы захардкожены в классе. Плюс - простота и хорошая поддержка типизации (у каждого метода указан тип возврата).
У него идея, что для представления возраста используется не 1, а сразу 2 класса: ValidAge с гарантированно валидным содержимым и NotValidAge с невалидным. И ты можешь с помощью тайм-хинтов просто запретить передавать невалидный возраст в функцию.
Хотя мне нравится использование типов для защиты от ошибок, но мне кажется, тут не очень удачно это придумано.
Вот кстати, если тебе неохота, как ты выражешься, писать "простыни текста", то можно же скидывать сюда ссылки на голосовые сообщения через какой-нибудь сервис (только без регистрации плиз и чтобы запись не исчезала через день). Я без проблем их прослушаю, и анон, который учится, думаю, тоже.
Потому, что у нас всего одна реализация и больше не планируется.
Например, что касается классов-правил валидации, у нас их много и можно дописывать новые. Нам надо как-то объяснить людям, как добавлять эти правила. Тут явно нужен интерфейс.
А список ошибок у нас один, и другие нам не понадобятся. Поэтому для него не нужен интерфейс или наследование.
>>2951078 Пчел. Просто создай канал в дискорде и не еби мозги. За пол часа можно обсудить больше чем тут за две недели написано. Записывай, хоть обзаписвайся если тебе это надо.
> use Getter; use принято писать в начале описания класса
ErrorList я бы сделал массивом, так как ты к полям обращаешься только через $this->$field, то есть ты с ними работаешь, как с массивом. Но твой вариант конечно тоже можно оставить, только тогда правильнее класс назвать EnrolleeErrorList, то есть не универсальный список ошибок, а список ошибок только для студентов.
> //Я сделал эту функцию приватной, потому что подумал, что она нужна только для работы errorListIsEmpty()
Тебе нужна еще функция для получения списка ошибок для вывода их в форме. Мы же добавляем ошибки, чтобы их выводить потом.
Обмен это значит данные из вашей БД копируются в 1С и обратно. Может там к этим картам привязан бухучет (сколько денег потрачено на бонусы), ты их удалишь и у них баланс не сойдется. Глупо же.
> 2. Насколько адекватна ситуация, когда в коде РОДИТЕЛЬСКОГО абстрактного класса используется какое-то значение, которого в этом классе нигде нет. И весь расчёт на то, что оно должно быть объявлено в НАСЛЕДНИКЕ этого класса.
Выглядит как полное непонимание наследования. Так делать нельзя.
> напрягает отсутствие абстрактных свойств в PHP.
А в чем смысл абстрактного свойства. Ты говоришь: хочу, чтобы в каждом наследнике было свойство int $x. Ну так объяви его в базовом классе, зачем его копипастить в каждом наследнике?
А вот абстрактные методы нужны, так как они могут быть разными в каждом классе. Их и надо тут использовать.
Во-первых, тред и ответы в нем видны всем, он даже архивируется.
Во-вторых, в тред я прихожу когда удобно и никого не жду.
В-третьих, в реальном времени начинающему трудно все понять. Ему надо 10 раз погуглить, написать 3 неработающих варианта кода, прежде чем до него дойдет.
Просто если кому-то неохота писать, а удобнее голосом сказать, можно оставить голосовое сообщение.
Аноны, хелп. день мучаюсьпочему у меня на пике куча с глазами весь мой стиль на странице ломает?Я делал машинку все работало, а тут все супер плохоДаже если смайл не в контейнере держать все тоже самое будетCSS .line-container { display: flex; position: fixed;} .line_bottom { margin-top: %; width: %; height: px; background-color: rgb, , ;}.glass-line { position: relative; font-size: px; animation: tyda_suda s infinite alternate;}@keyframes tyda_suda { from { left: ; } to { left: %; }}HTML<div class="line_container" <div class="line_bottom" </div <div class="glass-line line-" ú</div </div
>>2951102 >Во-первых, тред и ответы в нем видны всем, он даже архивируется. Старая песня. Дискорд тоже видно всем и там тоже все архивируется. Создай свой канал и прилепи в шапку. Дело не в том кому что ты хочешь показать. Я тебе уже сказал - записывай и пости куда хочешь. Дело в соотношении "сигнал/мусор". У тебя треть поста состоит из цитат, треть ты не договариваешь потому что надо в абзац уложиться, а последнюю треть у тебя переспрашивают.
Средняя статья только по одной из тем в этом треде занимает несколько экранов текста и представляет из себя бедную выдержку из целой книги или даже нескольких. И это еще и в фрагментарном формате. Когда мы просто вываливаем на человека кусочек нашей картины мира, а он без понятия как эта картина сложилась. А потом он спрашивает как мне заинжектить сущность в сервис с помощью di. Совершенно не одупляя ни что такое сущность, ни что такое di, ни зачем вообще что-то куда-то инжектить.
Да чего далеко ходить. Прямо в предыдущем посте ты не понял смысл абстрактного свойства, то ли перепутав его с шизой про "только в наследнике" то ли еще почему. Хотя очевидно что было бы удобно иметь свойство объявленное в базовом классе и которое нужно обязательно переопределять в наследнике. Сколько времени пройдет пока вы просто поймете что хотели друг другу сказать в начале разговора? День? Два?
>Во-вторых, в тред я прихожу когда удобно и никого не жду. Очень удобно. А главное столько времени сэкономил. Мне интересно сколько тредов эпопея с валидатором займет. Месяц он с ним сидит точно, может больше. Через пол года может выдаст что-то вменяемое. Такая-то учоба.
>В-третьих, в реальном времени начинающему трудно все понять. Точно так же как и не в реальном. Только в реальном времени есть человек, который сразу уточнит, напишет сниппет, тыкнет пальцем в книгу, приведет пример. И не придется сидеть ждать до завтра чтобы еще раз переспросить, чтобы ты переспросил в ответ.
>Ему надо 10 раз погуглить, написать 3 неработающих варианта кода, прежде чем до него дойдет. Опять же пусть сидит и гуглит при тебе, пока не дойдет как это правильно делать. Можешь ради интереса поспрашивать что по теме поста нагуглил "валидатор". Гарантирую - фейспалмом ебло разобъешь. То же самое с кодом. Ничего они не пишут, а мгновенно впадают в ступор. Нужно буквально за руку вести, пока они кода бояться перестанут. И нужно обязательно убедиться что он именно зашел в логический тупик, действительно столкнулся с проблемой. понял почему этот вариант не работает. А потом объяснить как этой проблемы избежать. Удачи сделать это в треде, особенно когда тебе даже реальный код не показывают, лол.
Говорил что не буду простыни писать, а в итоге опять насрал. В общем если ты настолько глупенький, что тебе все это нужно объяснять по пять раз, то все еще печальнее. В лучшем случае учеба будет проходить где-то параллельно с тредом. Имаджинировать лицо человека, который реально "учился" в этом потоке сознания лично я не могу.
>>2951091 А если в foo() много кода, и этот код будет повторяться в каждом классе-наследнике, за исключением одного аргумента?
public function foo () { someCode(); someCode2(); someCode3(); // ... bar($this->editable); // $this->editable - единственная строка, которая будет различаться в реализациях разных классов someCodeA(); SomeCodeB(); } Пытался примерно отразить это здесь https://3v4l.org/CRNMZ#v8.3.0 только там всего два метода для демонстрации. Я, конечно, не говорю, что там огромный метод на тысячу строк на деле. Но строчек 50-100 есть. И это копипасть каждому классу-наследнику ради изменения одного string-аргумента, передаваемого в функцию?
>>2951095 > Выглядит как полное непонимание наследования. Так делать нельзя. Вот меня что-то и напрягало. Ситуацию не спасает то, что класс-родитель - абстрактный? То есть, реализован объектом он быть не может. Как ты прокомментируешь этот код: https://3v4l.org/obOn0#v8.3.0 ? В родительском абстрактном классе вызывается свойство, которого он сам не содержит. Из расчёта на то, что оно есть в классе-наследнике. То, что я тут изобразил, не подходит мне по двум причинам: 1. Second::$foo не readonly, можно по ошибке в процессе выполнения изменить значение. Ок, допустим, readonly можно сделать. Тогда, правда, придётся инициализирующее значение задавать в конструкторе. Ведь я не могу сделать private readonly string $baz = "42"; Будет ошибка Fatal error: Readonly property Second::$baz cannot have default value in /tmp/preview on line 11 Readonly свойство, значение которого задано не в конструкторе, это константа по сути. Я и хотел изначально абстрактную константу. 2. Есть и вторая причина. Если бы я забыл в классе Second объявить свойство $foo, я бы узнал об этом только по факту вызова showInfo() Warning: Undefined property: Second::$foo in /tmp/preview on line 5 Он бы мне не пожаловался на этапе парсинга кода, мол, в ребёнке обязательно нужно объявить $foo, ты чё, охуел. С методами так можно, со свойствами - нет.
> Ну так объяви его в базовом классе, зачем его копипастить в каждом наследнике? Затем, что его значение фиксировано, как у константы. И для каждого класса-наследника оно отличается. Повторюсь, по смыслу это константа. Не переменная, в которую я кладу значение, извлекаю, меняю и т. д. А какое-то статичное значение. Уникальное для каждого из классов-наследников.
Когда я смотрю на конструкцию if () {}, я ее быстрее понимаю, чем когда смотрю на ту же конструкцию, но записанную с помощью тернарного оператора. Хотелось бы узнать, это я не привык с первого взгляда такое читать и наличие таких инструкций нормально для кода или лучше отдавать предпочтение if, а не тернарному оператору?
Нашел такую статью: https://habr.com/ru/articles/431866/ Неужели стоит так много учитывать? Я планировал просто проверять каждый символ строки со списком разрешенных значений. Например, для имени - только a-z A-Z а-я А-Я, пробел, апостроф, дефис.
>>2951837 Даже если отбросить SQL-инъекцию (потому что это задача уже плейсхолдеров, а не валидации), то имя, написанное как SQL-запрос выглядит тупо. Теперь я понимаю, что побуквенно проверять - глупая идея. Но все равно. Чего так сложно все с подбором регулярок для валидации? Какое обсуждение не откроешь - множество мнений.
>>2951838 С английским беда. Написано же "sanitize". Тебе придется согласиться что любой вводимый пользователем текст это юникод. Иначе даже казахские братушки отпадают. То есть в имени может быть литерали: ⚰️⚰️🪦🏳️🌈. И ничего страшного в этом нет. Потому что самое хуевое что можно сделать - это отказать в обслуживании кому-то потому что у него казахское ебало.
А от "страшного" избавляется именно санитайзер. В каждом фреймворке есть подобный функционал. Санитайзер автоматически изменяет данные. Например преобразует в число, удаляет пробелы в начале строки или удаляет определенные html теги.
>>2951844 Ты хочешь сказать, что мне нужно делать упор не на то, чтобы уметь отбрасывать некорректные имена, а принимать их и заменять в коде некорректные символы?
Мб я слишком мудрю и на вопрос о том, какие символы являются корректными стоит смотреть иначе? Не: какие вариации имен/фамилий существуют А: какие вариации имен/фамилий готов принять мой сайт Я ведь, да и другие аноны не кривят лицом, когда их просят в документах написать ФИО не кириллицей, а латиницей.
https://selectel.ru/blog/network-protocols/ Почему IP-протокол находится в разделе о протоколах транспортного уровня, если он, вроде как, является протоколом сетевого уровня?
Идея вообще была в том, чтобы учиться по статьям, а в треде уточнять непонятные моменты и проверять задания. Статью можно написать один раз, а прочтет ее много человек и это лучше, чем каждому объяснять лично.
> Вот меня что-то и напрягало. Ситуацию не спасает то, что класс-родитель - абстрактный? То есть, реализован объектом он быть не может. > Как ты прокомментируешь этот код: https://3v4l.org/obOn0#v8.3.0 ?
Ну это плохой код, и ты сам расписал, почему. Я не понимаю, что тебе мешает использовать абстрактный метод, который для таких случаев и придуман.
> И это копипасть каждому классу-наследнику ради изменения одного string-аргумента, передаваемого в функцию?
Там статья про имена во всем мире, которые, действительно, могут писаться самыми разными способами. Но мы можем ограничить себя только допустимыми в России (или в твоей стране, если ты не из России) именами. И для них можно попробовать найти официальные списки допустимых символов.
Это плохой совет. sanitize значит, что ты принимаешь от пользователя любые данные, но по-тихому удаляешь из них запрещенные символы, вместо того, чтобы указать на ошибку.
Не надо так делать.
> в имени может быть литерали: ⚰️⚰️🪦🏳️🌈.
Не может.
> Санитайзер автоматически изменяет данные.
ну и глупость. Получается, ты в базу запишешь не то, что ввел пользователь, то есть неверные данные.
Мне кажется, что IP протокол сетевого, а TCP - транспортного уровня. Но тут, конечно, лучше бы смотреть авторитетный источник. То есть определения уровней OSI и проверять, каким уровням он соответствует.
Надо понимать, что OSI это скорее абстракция, как можно разбивать протоколы на уровни. В реальности они могут не соответствовать OSI, хотя в общем, эта модель довольно близка к реальности.
>>2952155 >Не надо так делать. Не надо кому? В реальном мире за то что кто-то вообще попал на твою страницу заплачены деньги. Если этот кто-то развернется и уйдет из-за того что у него буква некрасивая в email'е хуже только тебе.
На самом деле вариантов всего два: экранирование и санитайзинг.
Ну так надо использовать экранирование. А санитайзинг не надо вообще нигде использовать, кроме технических вещей (типа номера страницы в URL, который пользователя руками не вводит).
И проверка валидности есть. Не знаю, как сейчас, а в магазинах типа DNS номер телефона не только проверяется на формат, но и проверяется через СМС. У тебя только стационарный телефон или вообще нет телефона? Иди тогда отсюда.
То же самое касается телеграмов, соцсетей, доставки, госуслуг. Они все проверяют и если ты вводишь что-то не то, то тебе отказывают в получении услугу.
>>2952590 >Ну так надо использовать экранирование. Сколько раз? Надо ли экранировать экранирующие символы? Если ты никогда в реальном проекте не работал, то нахуй ты это пишешь? Кто реально пытался текст экранировать глядя на пикрелейтед пример нервно икает. Гений, можешь хотя бы предположить почему так получилось?
Экранирование делается в том месте, где данные используются, а не в начале обрабочика. Если ты хочешь вставить данные в SQL-запрос, ты используешь подготовленные запросы. Если ты хочешь их вывести в HTML, ты используешь шаблонизатор с поддержкой экранирования.
И все символы отображаются и сохраняются корректно, и ничего не ломается.
А ты со свои санитайзеры из какого-то древнего неграмотного учебника достал скорее всего.
Как я уже писал выше, очистку можно применять для технических данных, которые пользователь не вводит вручную: номера страниц в URL, id комментария и тд.
>>2952784 Ссылка на что? Есть твой сервер с файлами, в твоем случае php скриптами и php-fpm для их выполнения. Есть промежуточное звено nginx, которое получает ссылку, читает её и решает какой из файлов на твоем сервере был запрошен.
Не нравятся прямые ссылки на файлы - добавляешь в nginx настройку согласно которой любая ссылка со словом "пидар" будет направлена на pidar.php
Очевидно что прописывать каждый вариант руками и настолько сильно зависеть от настроек ngix не выгодно. Поэтому обычно прописывают одно правило: все ссылки с твоим доменом ведут на index.php на твоем сервере, а уже index.php смотрит на параметры ссылки и решает какой скрипт выполнить.
>>2952787 >И все символы отображаются и сохраняются корректно, и ничего не ломается. Ты слепой или тупой? Я тебе привел пример как все сломалось. Потому что оно постоянно будет ломаться.
Я потому и сказал что ты ни дня в реальном проекте не работал. Хранить у себя sql инъекции и xss скрипты в надежде что ты все правильно сэкранировал. Это каким же ебланом надо быть.
Минимальное и максимальное количество символов для каждого поля: Имя: 1/20; Фамилия: 1/45; Номер группы: 2/5; Емайл: 4/50; Баллы ЕГЭ: 2/3; Год рождения: 4/4.
Регулярные выражения для каждого поля: имя: /^[А-ЯЁ]{1}[а-яё '-\.,\(\)А-ЯЁ?IV]{0,19}$/gm фамилия: /^[А-ЯЁ]{1}[а-яё '-\.,\(\)А-ЯЁ?IV]{0,44}$/gm номер группы: /^[0-9а-яёА-ЯЁ]{2,5}$/gm email: /^[a-zA-Z]{1}[a-zA-Z\.\-_0-9]+@{1}[a-z]+\.{1}[a-z]+$/gm баллы ЕГЭ: /^[0-9]{2,3}$/gm год рождения: /^[0-9]{4}$/gm
Класса, который проверяет уникальность емайл - пока что нет, потому что я не добрался до написания класса работы с БД. Я решил, что сначала напишу его, а потом допишу проверку.
>>2952107 Так я так и сделал, ёпт! Глянь >>2950316 Просто начал сомневаться на тему того, адекватно ли так делать, или я забиваю гвоздь микроскопом. Метод, который по сути ведёт себя как константа, возвращая всегда одно и то же неизменяемое значение.
>>2952116 >https://3v4l.org/BsCvr#v8.3.0 Ну нет, это вообще мимо. На практике же дочерние классы своими внутренностями отличаются сильнее. В одном такие-то дополнительные методы, в другом иные.
В общем, всем спасибо, остановлюсь на абстрактном методе, потому что чисто технически это работает именно так, как мне надо. А из обратной связи я получил, в сущности, следующее: 1. Твой код полная хуйня, а мать - шлюха. 2. Делай абстрактный метод. Первое не слишком конструктивно, остаётся второе.
Как глубоко надо задрачивать фронт? Разбираться в css-in-js/css-modules/БЭМ? Или это уже не нужно, если ты в основном бекендер, а не фуллстек? Или всё-таки надо прям настолько погружаться? Или хватит просто HTMl/SCSS/JS и просто умение именовать по БЭМ (мой текущий уровень знаний)?
>>2953305 Я тебе сразу указал что один вариант исключает дублирование кода, а другой нет. А когда спросил какая у тебя цель - ты полез в залупу. Очевидно что если цель "избавится от дублирования кода", то нужно выбирать вариант без дублирования. Но ты-то блядь свою цель так и не озвучил.
>Вот код. Он работает так как мне надо? >А как тебе надо? Что ты делаешь вообще? >Секрет.
А теперь ты пишешь: "это работает именно так, как мне надо". В натуре не очень конструктивно. Закидывать долбоебу в черный ящик какой-то код, чтобы услышать так ли он с его точки зрения работает. С первоклассником конструктивнее общаться, чем с шизами с двачей.
Сегодня только узнал про существование такой темы как "Структуры данных". Пока я решал свои вкатунские задачи, я пользовался переменными, массивами...И не чувствовал ощущения, что мне чего-то не хватает. Пробежался по гуглу. Узнал, что есть всякие стеки, очереди. Но так и не понял зачем они нужны. Вот я решаю задачу. Как понять, что стоит представить данные в виде определенной структуры данных? Насколько вообще часто программист имеет дело с чем-то кроме массива?
>>2953422 Ты пхп макака, твой код выполняется в виртуальной среде у тебя даже массивов нет, только хеш мапы и объекты.
Стек он стек именно потому что при такой структуре нужно меньше действий процессора и памяти. Ну напишешь ты стек на пхп, и чего? Прироста нет, пользы нет, лишняя абстракция.
>Как понять, что стоит представить данные в виде определенной структуры данных? Никак. Структуры данных выбираются исходя из приоритетов в расходовании производительности/памяти, пикрелейтед. А написать что-то быстрее пхпшных "массивов" ты физически не можешь, потому что они написаны на C.
У пхп есть своя сфера низкоуровневых оптимизаций. Называется "опкоды" https://php.watch/articles/php-dump-opcodes Это то во что транслируется твой пхп код. И можно написать код, который будет транслироваться в меньшее количество действий и будет немного производительнее. На практике таким никто не занимается, потому что для веба пхп достаточно быстрый и если не творить хуйню типа регулярных выражений в цикле, то сам пхп никогда бутылочным горлышком не будет.
Если посмотришь на трассировку симфони, то твой код это та маленькая синенькая пиздюлька перед твигом, а любой запрос в базу, даже SELECT 1; будет выполняться минимум 10ms, то есть в двое дольше.
>>2953649 Сижу потому что слезать = становиться опять джуном, со всеми вытекающими.
Если с полного нуля, то нет. Единственное преимущество пхп - меньше ебланов с улицы ломятся в джуны. Больше преимуществ я не вижу.
Если нужны быстрые бабки, то есть вротэнд. Если нужна карьера, то любой энтерпрайз язык. Изучишь один - считай изучил все. А изучив пхп ты изучил только микромир пхп. Для души и хобби тоже нахуй не надо. Пхп многосложный, косноязычный, забитый мусором. Красиво на нем не сделаешь.
> $errorList->add($fieldName, new Error($result));
лучше если класс-правило будет сразу возвращать Error, а не строку. Ведь при проверке ты можешь захотеть к ошибке добавить какую-то дополнительную информацию (например: подсказку или вариант исправления), а для этого нужен объект.
То есть, если ты решил обозначать ошибку объектом Error, то не надо ее возвращать как строку, возвращай сразу объект Error везде.
Непонятно, зачем в ValidationError ты сделал Setter. Зачем тебе менять содержимое ощибки? Ошибка это не какая-то сущность, которую можно редактировать. Ошибку логично бы сделать иммутабельным (неизменяемым) объектом для "защиты от дурака". Ошибка явно подходит под паттерн ValueObject, про который я писал выше.
То есть, думай, какие объекты делать мутабельными, а какие нет. Нужно ли тебе это и зачем.
> private array $listOfAllErrors;
лучше назвать просто $errors
> $result = $this->listOfAllErrors[$fieldName];
Здесь может быть ошибка, что в массиве нет такого ключа. Надо добавлять ?? [] для защиты от такой ошибки.
> errorListIsEmpty ()
Лучше просто назвать isEmpty, ведь ErrorList уже есть в названии класса и зачем его писать второй раз. Также, перечитай PSR-1 и PSR-12:
> Method and function names MUST NOT be declared with space after the method name.
> class CheckForSymbolQuality
Почему ты не хочешь сделать интерфейс для классов-проверяльщиков?
> CheckForSymbolQuality
лучше назвать типа CheckRegexp, ну то есть явно написать, что это проверяльщик регулярок. Вот видишь какая интересная штука ООП, в процедурном программировании ты просто пишешь preg_match, а в ООП пишешь целый класс И строишь архитектуру вокруг preg_match.
Что касается названий классов, то вот как классы-проверяльщики названы в Symfony:
> Это поле может содержать следующие символы - %s. Проверьте, не использовали ли Вы символ, который не входит в этот список.
А почему тупая железяка не может проверить сама и написать, какой символ неправильный, а не загадывать ребусы человеку?
> public function __invoke (mixed $value): ?string лучше возвращать ?ValidationError, а не ?string, как я написал выше
> iconv_strlen($value); Обычно используют mb_strlen... но твой вариант тоже годится.
В регулярке для email проблема: в доменах может быть и минус, и цифры по типу ivan@s7-consulting.org, более того, сейчас есть международные домены типа ivan@русский.рф
Некоторые рекомендуют просто проверять, что :
- в email есть @ и хотя бы 1 точка справа от @ - в email нет пробелов и других запрещенных символов (каких, подскажет интуиция)
То есть, проверка email нужна в первую очередь для защиты от опечаток, когда люди забывают ставить собачку либо ставят пробел вместо точки.
Но можно просто доработать твою регулярку.
Проблема с очень сложными регулярками в том, что их сложно читать, проверять на правильность, и трудно сообщать ошибки. Например, твоя регулярка требует писать имя с большой буквы. Если я напишу с маленькой, она мне об этом скажет, или напишет, что я использовал запрещенный символ? Лучше было сделать 2 проверки, одна на запрещенные символы, другая на большую букву в начале (а еще можно автоматически делать первую букву большой и не мучать пользователя).
> год рождения:
желательно проверять, что он не меньше 1900 и не больше текущего - 12.
Структуры данных, как правило, неразрывно связаны с алгоритмами и служат целям:
- хранить данные так, чтобы с ними было удобнее работать - хранить данные так, чтобы быстро их находить или менять - хранить данные так, чтобы код был понятнее
В PHP есть структуры данных (хотя не скажу, что идеально реализованные), смотри:
Ну например, ты хранишь в массиве черный список номеров телефонов (очень много): ['9701234567', '9701234568', ...]. И тебе надо проверять, есть ли номер телефона в черном списке или нет.
Если ты используешь массив, то это не очень хорошо:
- проверка будет медленной и неоптимизированной на больших объемах - допустим, тебе надо составить черный список. Тебе неочевидно: должны телефоны идти в определенном порядке или нет? какие ключи должны быть в массиве?
А если ты используешь множество, то все будет проще:
- проверка наличия во множестве быстрая - порядок телефонов во множестве не важен, ключей в нем нету, и читатель кода не задает лишних вопросов
Главная проблема конечно не оптимизация, а читабельность кода. В PHP массив используют вместо списка, неизменяемого списка, множества, хеш-таблицы и при чтении кода приходится догадываться, какой именно массив нужен в этом месте кода.
> у тебя даже массивов нет, только хеш мапы и объекты.
Не пиши, что не знаешь. В расширениях SPL и DS кое-что есть.
> Ну напишешь ты стек на пхп, и чего? Прироста нет, пользы нет, лишняя абстракция.
Код понятнее.
> Если посмотришь на трассировку симфони
ты бы для начала включил режим prod, а не в dev-окружении мерял, оптимизатор. Приходят такие неграмотные в тред и потом мифы про Симфони распространяют, а дураки им верят.
Также, у тебя на профиле нет ли одного запроса на 10ms, и еще у тебя используются подзапросы, что не рекомендуется, но ты похоже не читаешь рекомендации.
>>2953925 >Не пиши, что не знаешь. В расширениях SPL и DS кое-что есть. Точно такие же виртуальные обертки над C. Ты один хуй все действия делаешь в виртуальной машине через opcod'ы и zval'ы. Ты хоть знаешь что такое zval, чучело?
>Код понятнее. Понятнее чем что? Чем array_push/array_pop? Начнем с того что в какой блядь вообще задаче может понадобиться стек? Какие ты блядь деревья такие собрался в пхп обходить? Да даже хуй с ним, пусть и деревья. Зачем может вообще понадобиться структура из которой можно получить ТОЛЬКО последнее значение? В настоящем стеке понятно. LIFO это физическое расположение данных в памяти. Безопасный мультитрединг, меньше процессорных команд, простой указатель. Все это будет работать ТОЛЬКО при гарантиях LIFO. Твой же выдуманный стек лежит в куче, потому что это zval. Нахуя нам запрещать обращаться к любому его элементу, а не только к последнему? Ведь за это уже уплочено. Ресурсы на это потрачены. Какой смысл?
>ты бы для начала включил режим prod, а не в dev-окружении мерял, оптимизатор. Приходят такие неграмотные Так. Пикрелейтед прод. Что мы там должны увидеть? На проде пхп замедлился? Или запрос в базу ускорился? Какие нахуй мифы, додик? Какие блядь "дураки", кроме тебя офк во что верят? Какие блядь "подзапросы"? Это скриншоты из оф доки симфони https://symfony.com/doc/5.4/the-fast-track/ru/30-internals.html Особенно смешно как тебя порвало в контексте того что я назвал php быстрым, а бутылочным горлышком запросы в базу. Зашивайся, знаток.
Где в MVC могут происходить побочные эффекты? Допустим прилетает запрос и мне надо сгенерировать какой-то файл и отправить на клиент. Где это делать? В контроллере?
>>2954716 Побочные эффекты производят другие программисты. Они решают свои задачи с помощью твоего кода и данных, которые нужны тебе. Если назначение кода не очевидно, неоднозначно, нужное тебе поведение не изолированно а данные не гарантируют свою целостность.
Вот это поворот, все эти дебильные правила, солиды хуелиды нужны не для приручения злых данных, которые сами вдруг меняются. А для улучшения взаимопонимания и взаимодействия между людьми.
А как лучше стили компоновать с пхп? Бест практисы какие? В инете нашел такой вариант: Создавать стайлс.пхп, там указывать заголовок текст\цсс и описывать стили, линком подключить Почему-то также написали что просто подключать цсс и настроить нджинкс на передачу стилей - хуета
Класс ErrorList: https://3v4l.org/5cntA Зачем я сделал это: >throw new ArrayNotExistsException("Массива под таким ключом не существует. "); Потому что дальше идет цикл foreach, если result это null, то что он будет делить на ключ и значение?
Интерфейс ValidatorRuleInterface: https://3v4l.org/E1fAg С этим интерфейсом есть небольшая проблема. Ее суть будет изложена, когда я дойду (в этом посте) до класса-проверятеля, который проверяет, что год рождения находится в определенном диапазоне
Класс, проверяющий на пустоту: https://3v4l.org/TMHPR Класс, проверяющий минимум: https://3v4l.org/AePYA Класс, проверяющий максимум: https://3v4l.org/AcCki Класс, проверяющий качество: https://3v4l.org/of0e9 Небольшое объяснение этого класса: Я подумал, что клепать 10 свойств - тупо и решил сделать одно - $reviewResources, в котором буду хранить все, что нужно для этой валидации. Ведь именно для этой валидации мне понадобилось так много свойств... Я разделил проверку на две части: Проверить каждый символ на паттерн, в котором перечислены корректные символы, если символ не проходит совпадение = он некорректный Проверить каждое значение на то, что его структуру +- нормальная, чтобы не было всяких И,в,а,н,о,в,ы,х, потому что хоть запятая и разрешена в фамилии, такое ее использование - тупость. Если хотя бы одна из этих проверок выдает ошибку (в виде массива с некорректными словами или нуля), то формируется текст ошибки, а потом создается объект ValidationError и эта строка отдается этому объекту в конструктор. Мб паттерны ($patternForSymbols, $patternForString) стоит объединить в один массив ($patterns). Я не понимаю когда стоит представлять что-то отдельным, а когда массивом. Это касается просто переменных и свойств класса.
Класс, проверяющий адекватность года рождения: https://3v4l.org/3meF5 Вот мы и подошли к проблеме с интерфейсом. В сигнатуре метода интерфейса указано, что $value должно быть mixed, но, проверяя год, мы же не можем указать mixed, ведь нам конкретно нужен int (года же ведь хранятся в виде int, да? а то я туплю что-то в этом), поэтому я оставил mixed для того, чтобы сигнатуры методов совпадали, но добавил проверку типа в самом классе. Хуй знает как это исправлять. Я пока что ничего не придумал. Минимальный год я выбрал - 1923 Максимальный год - 2023 Итого 100 лет. Ну, вроде нормальный диапазон.
И насчет регулярного выражения для емайла. Проверять структуру я буду таким: /@{1}\S*\.{1}/gm Т.е. одна собачка, потом любой символ и одна точка. Проверять корректность каждого символа таким: /[a-zA-Z0-9_\.\-@]/gm Т.е. заглавные и строчные латинские буквы, цифры, нижнее подчеркивание, точка, минус, собака. Я это "украл" у майл.ру, от балды вбивал символы и ждал когда будет сообщение об ошибке валидации и просто внес в свою регулярку то, что они считают разрешенным.
>>2953889 Можешь ответить еще на несколько вопросов? Ты предлагал мне заменить несколько свойств в классе ErrorList на одно свойство. Мне понравилось это решение, потому что код стал меньше и универсальнее. Но я не понимаю какая логика лежит за этим решением. Как понять, смотря на совокупность свойств класса, что лучше их представить в виде одного свойства-массива? И еще вопрос. У меня есть две переменные. Когда лучше иметь две отдельные переменные, а когда лучше иметь массив с двумя отдельными элементами? Например, у меня есть две переменные - $patternForSymbols и $patternForString. Как мне понять, им лучше быть по одиночке или быть элементами одного массива?
>>2953889 >В сигнатуре метода интерфейса указано, что $value должно быть mixed, но, проверяя год, мы же не можем указать mixed, ведь нам конкретно нужен int (года же ведь хранятся в виде int, да? а то я туплю что-то в этом), поэтому я оставил mixed для того, чтобы сигнатуры методов совпадали, но добавил проверку типа в самом классе. Хуй знает как это исправлять.
Есть такая идея: У меня есть класс Enrollee с поля name, surname и т.д. Мб сделать так, чтобы поля year и email хранили не int и string соответственно, а объекты классов Year и Email? Это не решает проблему с интерфейсом. Но решает другую проблему. Если я уберу строку, что класс, который проверяет адекватность года, реализует интерфейс, уберу проверку типа и сделаю тип аргумента int, то функция invoke сможет принимать любое число. Год имеет тип числа, да. Но ведь в таком случае функция сможет принимать любое число. А нам нужен именно год. Для этого можно представлять числа и емайлы отдельными типами.
>>2954829 Почему я завел речь про емайлы - потому что будет отдельный класс-проверятель на уникальность емайл. И я думаю, он тоже не впишется в мой интерфейс.
Сколько фронтенда надо знать для битриксоида? Вообще как посоветуете развиваться в направлении битрикс? Сделать пет на чистых языках, только jQuery добавив, интернет-магаз? Или сразу на битриксе? Или и то и то?
Ну и кстати нашёл курс от webformyself длительностью 38 часов, кто-то проходил его? Норм? Походу в сети всего 5 курсов по битриксу: webformyself, Я-Кодер, гикбрейнс, Михаил базаров и офф. курсы битрикса.
>>2954852 Да просто на собес сходи, спрашивают обычно по пхп, на петпроекты всем похуй. По jquery есть старый курс дмитрия лаврика на торрентах "Js для верстальщица". Еще из джса нужен обязательно ajax. В битриксе есть своя всратая библиотека BX она процентов на 80% копирует jquery - ее изучать самому бессмыслено документации толком нет, натаскаешься на РАБоте.
>>2954829 Я сейчас понял чем чревата такая идея. Сейчас я обращаюсь к свойству Enrollee так: $enrollee->get("название свойства"). А для года и емайла придется так: $enrollee->get("название свойства")->get("название свойства"); Вторым способом нужно обращаться только к двум полям, получается, что я теряю универсальность в коде. Я сейчас сонный, поэтому рассуждения туманные, я написал это, чтобы ты ответил с учетом этого момента.
Генерация файла это не побочный эффект. Жаль, что ты не пишешь, какой именно файл тебе нужен и мне придется придумывать это за тебя.
Допустим, тебе надо отдавать Эксель-файл с доходами за последний месяц. Формирование данных делается в модели (так как бизнес-логика располагается в модели). Генерация файла - в модели либо в контроллере. Отдача файла - в контроллере.
>>2955119 >Жаль, что ты не пишешь, какой именно файл тебе нужен и мне придется придумывать это за тебя. На запрос пользователя, надо разбить видео на сегменты, сохранить их на диски и отдать плейлист на клиент.
Начну с главного: ни в коем случае не помещай все стили и скрипты в один файл. Это приводит к тому, что там получается свалка, в которой невозможно разобраться и из которой нельзя ничего удалить потом.
Используй разные файлы. Для маленького сайта я могу предложить такую схему:
- файл global.css, который подключается на всех страницах - файлы для каждого раздела типа blog.css, catalog.css, forum.css
И, например, в блоге ты подключаешь global.css и blog.css.
Для большого сайта можно разбивать код еще сильнее. Например, ты сделал форму обратной связи, стили для нее кладешь в отдельный файл. Сделал календарик, тоже стили кладешь в отдельный файл.
Для маленького сайта можно не заморачиваться со сборкой и не тратить на это время. В эпоху HTTP/2 и при маленьком количестве стилей сборка ничего особо не дает.
Для большого сайта можно настроить склеивание мелких файлов в 2 файла: сайт глобальных стилей и сайт раздела.
Отсутствие сборки дает много плюсов:
- разработчику не надо ничего устанавливать, запускать, не нужна нода и npm, не нужно ждать, пока стили соберутся (это часто занимает минуту и больше при холодном старте) - код в инструментах разработчика выглядит как в оригинале - браузер может задействовать кеширование, если один файл используется на нескольких страницах - при этом в HTTP/2 особого выигрыша от сборки нескольких файлов в один не должно быть
Но сборка может понадобиться, если:
- ты хочешь использовать нестандартные языки вроде SCSS, Typescript - ты хочешь автоматически добавлять CSS-префиксы для CSS-файлов - ты хочешь транспилировать новый JS в старый для лучшей поддержки старыми браузерами
Скажу еще про вебпак. Когда я читал про него статьи, то 10/10 статей хвалили его как идеальный инструмент. Но когда я столкнулся с ним на практике, я был шокирован насколько плохо он продуман:
1) вебпак по умолчанию пытается склеить все ресурсы в один JS-файл. То есть все твои 100 CSS-файлов, 100 JS и 100 картинок склеиваются в один гигантский файл. И пользователь вынужден грузить этот файл, даже если на странице нужно всего 5 скриптов из 100. Гениально придумано.
Чтобы разбить код на несколько файлов, надо добавлять специальные плагины, о чем в большинстве туториалов не пишут. Какие умные люди авторы этих туториалов.
2) так как CSS и картинки вставляются в JS, это ломает инструменты разработчика и делает отладку максимально неудобной.
3) при транспиляции код в браузере выглядит не как исходный. Инвалиды придумали map-файлы, которые должны в теории решать проблему, но в реальности, если у тебя используется несколько плагинов, то map ломаются где-то в одном из них и нужно много часов, чтобы разобраться, в каком. Я решил не тратить на это время, так как неизвестно, возможно ли починить это в итоге.
4) вебпак придумывает свои нестандартные расширения к языку JS вместо того, чтобы следовать стандартам. Он, например, поощряет импортировать картинки и CSS файлы в JS с помощью import. То есть у тебя код будет не соответствовать стандарту EcmaScript.
5) в конфиге webpack по умолчанию используются устаревшие убогие require вместо ES modules.
6) конфиг вебпак это скрипт, а не конфиг, и его невозможно обрабатывать амтоматизированными средствами
7) вебпак не позволяет в dev-среде просто подключать исходные файлы без склейки и обработки. Разработчику удобнее, когда не надо ставить ноду, npm и что-то компилировать и ждать минуту или больше
8) склеивание файлов было придумано как борьба с недостатками HTTP/1. В век HTTP/2 многие недостатки исправлены и я планирую найти время и изучить, можно ли отказаться от сборки вообще
> validate (Enroll Зачем ты пробел ставишь? Это нарушает PSR.
> private array $rules; Здесь хорошо бы добавлять phpDoc-комментарий с описанием структуры массива. А то при чтении кода непонятно, что в него можно передавать. Я давал выше ссылку, как это описывать (добавить phpDoc-тег @var).
> Зачем я сделал это: >>throw new ArrayNotExistsException("Массива под таким ключом не существует. "); > Потому что дальше идет цикл foreach, если result это null, то что он будет делить на ключ и значение?
Ты сделал неправильно. Вот представь, ты выводишь форму и хочешь получить список ошибок для поля name. И у тебя будет выбрасываться исключение, если ошибок нет. А должен просто возвращаться пустой массив.
Исключение обычно выбрасывают в исключительных ситуациях. То, что пользователь правильно заполнил форму без ошибок - это стандартная ситуация.
> private string $errorText = "Это поле обязательно для заполнения.";
А не лучше ли тут сделать константу, если ты никогда не меняешь это поле? Почему оно сделано полем? Или ты планировал разрешить указывать другое сообщение через конструктор при создании правила?
> private array $reviewResources =
Мне кажется, тут лучше сделать 4 поля, а не массив с 4 элементами. Так как код будет проще, ты все равно никак не используешь функции работы с массивами. То есть выгоды от использования массива я не вижу, а код без него будет короче и проще.
> public function __construct(string $patternForSymbol, string $patternForString, array $verbalDescriptionOfPatterns)
Длинные заголовки надо переносить по PSR.
> patternForSymbol
Английский язык позволяет записать это как symbolPattern (шаблон символа), что короче.
> $letter = $value[$i];
Так нельзя делать. Чтобы получить i-й символ, надо использовать mb_substr или iconv_substr. А через квадратные скобки ты получаешь не i-й символ, а i-й байт (символ может состоять из нескольких байт, и русскую букву ты так не получишь никогда).
> implode(", ", $resultForSymbol)
Здесь желательно удалить повторяющиеся символы, чтобы не было "вы использовали неправильные символы: &, &, &, &".
>>2955162 Да даже если и устарел. Замени название на любимый таск менеджер твоего протыка. По сути ничего не изменится. Есть файлы стилей, есть папка public, в которую их надо высрать.
> Я подумал, что клепать 10 свойств - тупо и решил сделать одно - $reviewResources, в котором буду хранить все, что нужно для этой валидации.
Это плохая идея, заменить свойства на элементы массива, так как:
- код становится длинее, ты пишешь $this->props['someProp'] вместо $this->someProp - код становится непонятнее, так как у тебя не описано, какие свойства могут быть в массиве, каких они типов - для свойств проверяются имена и типы данных, которые в них записываются, а в массив можно записать что угодно и ошибки не будет
Я не вижу плюсов. Если бы тебе надо было работать с набором свойств как с массивом, применять функции для работы с массивами, тогда еще можно было бы подумать. Но тут я вижу только недостатки и никаких плюсов. В твоем посте я тоже аргументов в пользу такого решения не вижу.
> Ведь именно для этой валидации мне понадобилось так много свойств...
4 это не много. Много это 50 и больше. Но даже в этом случае надо не заменять свойства на массив, а искать другие идеи (например, разбить класс на несколько классов)
> Проверить каждый символ на паттерн, в котором перечислены корректные символы, если символ не проходит совпадение = он некорректный > Проверить каждое значение на то, что его структуру +- нормальная, чтобы не было всяких И,в,а,н,о,в,ы,х,
Ну вот это кстати 2 разных задачи. Для их решения лучше сделать 2 разных правила. Ты смешиваешь 2 задачи в одном классе и усложняешь код. Тут надо сделать 2 отдельных класса и все проблемы с кодом решатся сами собой.
> Я не понимаю когда стоит представлять что-то отдельным, а когда массивом
Когда тебе надо что-то хранить, и заранее известны названия и типы данных, используем свойства. Если заранее не известны названия, или элементов может быть сколько угодно, используем массив.
Массив обычно используется для списков каких-то вещей или для "соответствий"/маппингов (например, соответствие номер месяца - название).
Например: у любого студента есть имя, email, дата рождения. Используем свойства. У любого товара есть цена, название, вес. Используем свойства для них.
Другой пример: надо хранить список цветов, в которых доступен товар. У товара может быть сколько угодно цветов. Используем массив (при этом цвета могут быть элементами перечисления Color), по типу:
> В сигнатуре метода интерфейса указано, что $value должно быть mixed, но, проверяя год, мы же не можем указать mixed, ведь нам конкретно нужен int (года же ведь хранятся в виде int, да? а то я туплю что-то в этом),
Вот ты и открыл одну из проблем ООП. В ООП есть "принцип Барбары Лисков". Он говорит следующее:
- если код рассчитан на работу с объектом класса A, то в него можно передать объект класса-наследника B и код не должен сломаться.
То есть, при наследовании мы должны сохранять совместимость. Именно по этой причине ты не можешь при наследовании удалить метод - так как в этом случае код, который думает, что в классе есть такой метод, сломается.
Ты можешь расширить список принимаемых значений, но не можешь сузить с mixed до int.
Увы, в нынешней реализации ООП никакого простого способа решить эту проблему нету.
> поэтому я оставил mixed для того, чтобы сигнатуры методов совпадали, но добавил проверку типа в самом классе.
Ок, это допустимо.
> YearIsValid
Вообще, вместо "год правильный" было бы лучше сделать облее общее правило "число лежит в диапазоне". Чтобы его можно было и с баллами использовать, например. Баллы это же тоже число.
> Максимальный год - 2023
Ты 1-летнего ребенка в студенты принимать собрался? Минимум надо лет 10-12 ставить.
> Минимальный год я выбрал - 1923
То есть дискриминируешь 102-летних? Ставь с запасом, чтобы не засудили.
> Но я не понимаю какая логика лежит за этим решением.
Логика такая. Мы делаем класс "список ошибок", который хранит поле и список ошибок в нем. Мы делаем универсальный список ошибок, который может работать с любой формой с любыми полями. Мы заранее не знаем, где он будет использоваться, для каких форм, как в них будут называться поля. У нас тут вариантов, кроме массива, не остается.
Если бы ты делал список ошибок только для студента, то можно было бы и поля использовать, и массив. Но лучше делать более обобщенные классы, более универсальные.
Почему? Потому, что завтра тебя могут попросить добавить к списку студентов список преподавателей, список лекций и еще кучу всего. Если у тебя универсальный валидатор и универсальный список ошибок, то ты сможешь их использовать и там. Если же ты делаешь валидтор только для студентов и список ошибок только для студентов, то ты не сможешь их повторно использовать.
Поэтому надо стараться делать классы более обобщенными, но не переходя в крайности. Не валидатор года, а валидатор целых чисел.
> Например, у меня есть две переменные - $patternForSymbols и $patternForString. > Как мне понять, им лучше быть по одиночке или быть элементами одного массива?
По одиночке, так как название и тип тут заранее известны.
Есть скрипт php, который запускается nginx через php-fpm. Хочу сделать кастомный конфиг, чтобы через него менять поведение. Как это лучше сделать? неужели каждый раз при запуске читать его?
>>2955195 >Здесь хорошо бы добавлять phpDoc-комментарий с описанием структуры массива. А то при чтении кода непонятно, что в него можно передавать. Я давал выше ссылку, как это описывать (добавить phpDoc-тег @var). Я раньше не читал про документирование кода. Погуглив, я понял следующее: phpDoc - приложение для автоматической генерации документации на основе docBlock. docBlock - это комментарий в коде, который описывает что-то с помощью краткого описания, более детального и тегов. Когда берусь за написание такого комментария - встаю в ступор. Как я понял, опираясь на это: https://docs.phpdoc.org/latest/guide/references/phpdoc/types.html#arrays Свойство $errors класса ErrorList можно описать так (я не буду писать краткое и детальное описание, а только затрону теги) / @var array[] */ Насколько я понял, за тегом идет название типа, который имеют элементы массива. Но мой массив двумерный и я не знаю как его описывать. По ссылке, которую ты изначально кинул, я ничего не понял. Да-да, прости. Да. Туплю. Спасибо за ответ.
Суп вкатуны. Скоро новогодние праздники, будет свободное время, есть идея запилить стрим интенсив для двачеров, написать свой чан. По результату у всех будет возможность форкнуть проект с гита и дальше развивать свои хард скиллы. Развернем докер окружение php Postgres ngnix redis, освоим базовые навыки в формате реального общения на таиче. Фреймворк будет yii2 . Как вам идея?
Конечно. Щас модно апихи делать а фронт отдельной приложухой. Но вообще чаще всего "фуллстэк" это "серверный рендеринг", а значит никакого node_modules. Админка на бутстрапе
>>2956502 Да там пишут список фреймврков иии vue / react и прочее. Если просто вбить в вакуху на хх название фреймворка то там везде хуйню из жс дописывают.
>>2956536 Да вообще. И пост для всех, кто знает - ответьте если не трудно.
0. Внизу много чего написано, но вы можете напасать свои знания или что нужно чтобы устроится на работу пыхарем, удаленно и на какие задачи, мол сайты делать, апи писать для мобилок и сайта и прочее, какие задачи там, или инет магазины делать, ну и на какие деньги этот список знаний по технологиям.
1.Если знаешь, напиши как и что искать, на что обращать внимание. 2.Что вообще требуется знать? Про solid, grasp, poeaa, ddd - что спрашивают? Насколько этим обмазываются на работе? 3. Что нужно знать из фреймворков? Что нужно умееть вообще сделать? 4. Как на счет этих знаний oauth2, фильтр товаров, атрибуты товаров EAV, импорт/экспорт обмен товарами между сайтом и 1с какой-нибудь. 5. По базам данных что требуется? Кто должен заниматься проектированием таблиц, или каждый хуярит свою миграцию? Должен ли разраб знать уметь заниматься репликацией,шардированием, партицированием? 6. Что нужно знать и уметь из нагрузки и балансировки нагрузок и маштабировании? 7. А с очередями что? Что должен уметь? Тупо апи вызывать? Или поднимать сервак, подключать апи, настраивать. Какую-нить Сагу замутить следящую? 8. Что по микросервисам, много где требуются? Что из этого знать нужно? 9. Что по Апи? REST API достаточно? Или еще нужно уметь в RPC, SOAP, GraphQL? 10. Докер - понятно. А кубер нужно знать? И Уметь работать с кубером и серверами? 11. Что вы делаете с redis/memcached - храните сессии чтобы люди на разных доменах имели одну сессию между сервисами, или что? Что нужно знать и уметь из этого? 12. Что на счет распределенных данных, баз данных, работа с uuid? 13. Что на счет логов, мониторинга, прометеуса, графаны, ELK стека, ClickHouse и прочее - что из этого нужно знать?
Ты оверпреппишься. Чаще всего тебя на собесе спрорят 5-10 вопросов по php.
5-10 вопросов по базам данных
5-10 вопросов по паттернам и всяким солид хуелид
5-10 вопросов про веб. Ну типа http(как работает протокол), как в вебе делается аутентификация, че такое cookie, че такое сессия.
Ну и все.
Судя по тому что ты высрал, ты уже блять готов. Иди уже на собес бля епта. Все что ты написал это реально пиздатый роадмап, но это не необходимо чтобы найти первую работу(и вторую тоже, лол).
Так шо я советую идти и не ссать. Если не ответишь, узнаешь где не ответил, ответишь потом на другом
>>2955195 Пока я жду ответа насчет кода валидации, я решил взяться за написание остальных классов. Посмотри, пожалуйста, эти классы: https://3v4l.org/i9DUD https://3v4l.org/rcKTp https://3v4l.org/hi99gh Я еще не знаю как буду реализовывать их, поэтому у методов пустое тело и большая часть тайп-хинтов отсутствует. Я хотел бы, чтобы ты оценил мое разделение функционала между классами и мой выбор функций. Над модификаторами доступа методов класса Cookie я пока что не думал. А вот для методов классов регистрации и авторизации, я выбирал модификаторы доступа сознательно. Я думаю, что это неплохо, если я скрою весь функционал за методами authorize() и register()?
Что у меня вызывает сомнение: Что такое класс Cookie? Часть Модели MVC? Если да, то почему модель лезет в суперглобальную переменную? Мне в прошлом треде кидали код с классом Cookie. И там было обращение к $_POST['cookie'], насколько я помню. Поэтому я думаю, что и мне придется прописывать это.
Я начал писать класс TDG. И для того, чтобы добавить объект Enrollee в БД, мне нужно было пройтись по его полям. Я подумал, мб для этой цели мне нужно сделать в классе Enrollee метод "получить список названий всех свойств"? public function getBlaBlaBla(): array;
$result = $enrollee->getBlaBlaBla();
array - это массив, где ключи - цифровые индексы, а элементы - строки. пример: array[0] = "name".
Думаю, что будет лучше, если я просто скину код, в котором покажу как буду добавлять новые записи в БД: https://3v4l.org/ZUk8u
Я хотел бы, чтобы ты в первую очередь оценил логику. Можно ли так загружать данные в БД? А потом указывал на ошибки в синтаксисе.
Еще я также подумал о том, что раз у меня и так универсальные геттер и сеттер, то почему бы не сделать их магическими? Чтобы вместо записи: $enrollee->get("name"); писать: $enrollee->name;
Спасибо за то, что отвечал мне. Извини, если я много тебя напрягаю. Мой код очень сильно преобразился благодаря твоим ответам. Я стал чувствовать себя более увереннее.
Объясните кто-нибудь, приватные свойства наследуются? Т.е. они как бы есть в классе, но нет возможности к ним обратиться? Или их нет в классе, который наследует?
>>2957410 Я думаю, что, мб, для того, чтобы Cookie не обращался к $_COOKIE, нужно сделать класс, который будет хранить куки? Контроллер будет создавать объект такого класса с куки, а Cookie извлекать информацию о куке из него?
>>2957410 >Еще я также подумал о том, что раз у меня и так универсальные геттер и сеттер, то почему бы не сделать их магическими? Наверное это плохое решение, если окажется, что я где-нибудь забуду инициализировать свойство.
phpDoc это приложение для генерации документации из кода и синтаксис для аннотаций. Аннотации пишутся в комментариях тегами вроде @var. Синтаксис там довольно старый, потому к нему придуманы нестандартные расширения, на которые я давал ссылку https://psalm.dev/docs/annotating_code/type_syntax/array_types/
Хотя эти расширения синтаксиса нестандартные, их понимают IDE вроде PhpStorm и анализаторы phpstan, psalm. С их помощью ты можешь описать структуру массива.
> Свойство $errors класса ErrorList можно описать так
Это массив, где ключом является строка, а значением - список объектов Error:
@var array<string, list<ValidatonError>>
PHP дает возможность указать только тип array, а с помощью нестандартных расширений ты можешь описать структуру массива, что делает код понятнее.
Класс Cookie правильнее назвать типа CookieManager, так как он не представляет одну куку, а класс для управления куками.
Вообще, в ООП-фреймворках обычно запрос (с GET, POST, куками) представляют как объект Request, а ответ (с установленными куками, заголовками, телом) как объект Response. И куки добавляют в Response. И никто не лезет в GET/POST и тд напрямую.
Если делать свое, то лучше по PSR. Но, возможно, для этой задачи это уже перебор.
> class Authorization
Тут стоит сделать такие публичные методы: залогинить человека, разлогинить, определить текущего пользователя. Ты можешь сделать, чтобы все функции работали с объектом Enrollee, либо чтобы в них передавался только токен(пароль). Но чтобы было везде одинаково, а не в одной функции пользователь, а в другой только токен.
Вспомогательный класс, который будет вызываться из контроллера наверно. Я бы лично его в MVC никуда не относил.
> Я подумал, мб для этой цели мне нужно сделать в классе Enrollee метод "получить список названий всех свойств"?
Можно. Но вообще, твой TDG не универсальный, потому список полей можно записать в TDG. Так даже логичнее, ведь это TDG отвечает за работу с БД и только он знает, какие в ней есть поля. А студент про базу данных ничего не знает. Тем более, что в студенте в будущем могут быть поля, не сохраняемые в БД.
> Можно ли так загружать данные в БД? Можно.
> Еще я также подумал о том, что раз у меня и так универсальные геттер и сеттер, то почему бы не сделать их магическими?
Потому что тебе придется все время писать $this->$name. Лучше сделать магические геттер и сеттер тогда через __call.
Да, наследуются, они есть в объекте, но просто недоступны из наследников и предков. Обратиться к ним можно только через методы класса, где они объявлены (то есть наследник может вызвать такой метод и через него что-то сделать).
Вот кстати интересный пример, который любят на собеседованиях:
class A { private int $x; }
class B extends A { private int $x; }
$b = new B();
Ты можешь подумать, что здесь одно и то же свойство $x. Но реально это 2 разных свойства. Код в классе A под именем $x видит свое свойство, а код в классе B свое. То есть, в объекте $b реально 2 свойства с одинаковым именем, но разной областью видимости.
Можно сделать и так. Хотя смысла особо не вижу. В Симфони каждая кука, которую ты ставишь, это это объект класса Cookie, а вот получаемые куки по моему просто идут как строка, а не объект.
Хочу написать свой пет-проект, но задумался, стоит ли работать через ORM фреймворка (Ларавель) или с базой через PDO? Есть какие-нибудь рекомендации по пет-проектам?
>>2958663 пиши через ларавель, с сидерами и фабриками, сразу покрывая тестами. Какие-то нативные запросы делаются на этапе оптимизации хайлоада, для получения каких-то особо выёбистых данных или в целом для ускорения сложных выборок. До этого даже не заморачивайся
Если тебе надо получить 20-50 объектов, берем ORM. Если нужен заковыристый запрос с группировками, суммированием, пишем на SQL, иначе глупо будет выбирать из базы >1000 объектов только чтобы циклом сложить пару полей в них.
Ну то есть, надо смотреть по ситуации, какие именно данные тебе нужны.
>>2960901 В ларавеле есть sum, и прочие стандартные арифметические плюшки типа модуля от числа, экстремумы, среднее арифметическое и т.д. И со строками он тоже нормально работает, и даже с чем-то более сложным типа json и датой. Так что я бы посоветовал пользовать функционал ларавеля до победного, пока не упрешься в действительно большие просадки по запросам
>>2958106 Спасибо за ответ. Я решил отложить пока что написание классов в сторону. Потому что нужно прояснить общую картину. Я хотел бы, чтобы ты помог мне с этим. Я чувствую, что я не понимаю как устроен мой проект и как его составляющие (файлы) взаимодействуют между собой. Напишу, что у меня есть сейчас в проекте, а ты, мб, сможешь помочь мне прояснить ситуацию. У меня есть папка public. По твоему совету (если ты codedokode) я вынес корень сервера в нее. В ней есть index.php Я положил в public .htaccess и сделал index.php обработчиком всех запросов. У меня есть папка src. Я скидывал в нее все файлы, в которых объявляются классы. В этой папке также находится conf.ini (содержит значения для соединения с БД) и init.php (который я завел, опираясь на твой текст, смутно его понимая). Я сделал автозагрузку классов через composer. Таким образом моя файловая структура проекта выглядит так: -public/ --index.php --.htaccess -src/ ...classes... --conf.ini --init.php -vendor/
У меня нет пока что шаблонов. У меня нет CSS-файлов. Я не знаю куда я буду их класть, если они будут. А точнее: как будут называться папки для них, где в моей файловой структуре они будут находиться (на том же уровне, что и public? или внутри public?).
Я не знаю как должна быть устроена папка src. Я должен выделить в ней отдельную папку Controllers, чтобы положить туда два своих контроллера (я решил назвать их homePage.php и form.php)? А что делать с остальным содержимым? Я должен завести папку Models, куда должен положить свои классы?
Вообще, у меня очень туманное представление о том, как будут выглядеть мои контроллеры. homePage.php даже не существует, а form.php только недавно начал заполняться содержимым (и он точь-в-точь выглядит так, как ты описывал код для регистрации/редактирования информации в своей статье про Формы).
Я еще очень плохо понимаю как у меня работает автозагрузка через composer.
В файле composer.json есть строка: "psr-4": {"Acme\\": "src/"} Это не мое название неймспейса, я скопировал содержимое composer.json из примера какой-то статьи. Автозагрузка у меня работает, если что.
Как я понял, любой файл, чей namespace === Acme, и который находится в папке src, будет подгружаться composerом.
И если я хочу использовать в глобальном пространстве имен (или любом другом, который отличается от неймспейса, в котором находится Validator) Validator, у которого неймспейс, пусть будет Acme, то я должен писать: $validator = new Acme\Validator(); или в начале use Acme\Validator;
Кстати, наверное можно написать и так в composer.json (я видел в одном примере, но тупил и у меня не получилось повторить): "psr-4": {"": "src/"} Таким образом будет подгружаться любой файл из src? И использовать namespace не нужно будет?
Также: я не понимаю зачем конкретно мне использовать namespace, потому что я не собирался подключать сторонние библиотеки. И мой проект - довольно маленький. Я понимаю, что попади я в большой проект, это могло бы мне аукнуться. У меня ощущение, что я использую namespace без нужды. Т.е., что, конкретно, в моей ситуации они не нужны. Я не против namespace, но ведь нужно же с умом применять что-либо, понимая нужно оно тебе или нет.
Сейчас, чтобы попасть на страницу localhost (ага, да)/form, должна осуществиться следующая цепочка: 0) index.php включит файл autoload.php от composer, чтобы подгружать классы (наверное стоит написать "файлы", ведь класс это файл, который содержит объявление класса, кстати, я вот этот момент туплю: composer подключает только файлы-классы или любые файлы, в которых namespace Acme и они находятся в папке src? т.е. будь там контроллер, он бы подключил? да, из-за того, что у меня большая часть кода не написана, я не знаю реализации, не знаю какие ситуации могут быть в моем коде и мне трудно понимать что происходит) 1) Пользователь вводит в адресной строке браузера localhost/form 2) index.php разбирает url, находит строку "/form" 3) index.php включает файл-контроллер form.php 4) form.php прописывает в начале инструкции use namespace для классов, которые он содержит. 5) Если у пользователя не будет ошибок валидации, то нужно вызвать следующую функцию: header('Location: http://localhost/redirect) (я не думал еще над редиректом, ну, пусть, это будет отдельная страница) 6) Этот запрос также уйдет index.php 7) index.php найдет строку "/refirect" и подключит файл redirect.html, допустим Конец.
Да, прости, пожалуйста, что я написал такие обрывки. Я уже второй час сижу и пытаюсь сформулировать эту ситуацию. Можешь прокомментировать мои обрывки, сказать где я заблуждаюсь. Пожалуйста, используй конкретные советы. Как если бы ты сидел сейчас за моим компьютером и сам бы все это делал. Я уже начитался общих формулировок в стиле: ну то, что не хочешь показывать пользователю - не кидай в public. Это, конечно, в силу возможности. Я понимаю, что всех деталей моего проекта ты не знаешь.
> У меня нет пока что шаблонов. Можно сделать папку templates
> У меня нет CSS-файлов. Они должны раздаваться наружу, поэтому можно сделать папку public/css/
> Я должен выделить в ней отдельную папку Controllers, чтобы положить туда два своих контроллера (я решил назвать их homePage.php и form.php)?
Если это классы, то названия файлов должны быть с большой буквы. Если не классы, то не обязательно. Название form.php не очень удачное, так как форм в будущем может быть много. Лучше назвать studentForm.php (или enrolleeForm.php).
> Я должен завести папку Models, куда должен положить свои классы?
Они группируют классы по типу: все контроллеры в одну папку (можно с поддиректориями если их много), все сущности в другую, все формы в третью итд, все сервисы в четвертую, все исключения в пятую.
Но проблема в том, что обычно в больших приложениях больше всего классов-сервисов и в Симфони они все отправляются в одну и ту же папку, хоть и отвечают за разные вещи. Ты можешь создавать подпапки, но у тебя функционал получается раскидан по разным папкам. Например: сущности для форума лежат в Entity/Forum, сервисы в Service/Forum, формы в Form/Forum.
То, что я чаще вижу (и в других языках) это разбить проект на компоненты по назначению. А компоненты в свою очередь на более мелкие компоненты так, что классы, делающие похожую задачу, лежат вместе. Например, если у тебя есть форум, блог, новости, то ты делаешь для каждого свою папку. А также папку или папки для общих сервисов, которые нужны везде, вроде загрузки картинок, валидации:
Controllers/ Blog/ все для блога .....Post/ Посты .....Comment/ комментарии .....Rating/ голосование и расчет рейтинга Forum/ .....Posts/ посты .....Polls/ функционал опросов .....Topics/ темы News/ новости Users/ регистрация, авторизация ....Profile/ профиль Shared/ общие сервисы .....Ads/ реклама .....Images/ обработка картинок .....Validation/ валидация .....Metrics/ сбор данных о посещаемости, просмотрах страниц
В твоем случае классов не очень много и можно их поделить на такие компоненты:
- роутер - контроллеры (этот компонент отвечает за прием и обработку HTTP-запросов пользователя) - валидация (универсальные классы, без привязки к студенту) - студент (модель студента, TDG для него, валидатор студента) - авторизация - утилиты (работа с куками и другие вспомогательные классы)
> Как я понял, любой файл, чей namespace === Acme, и который находится в папке src, будет подгружаться composerом.
Да. Класс с полным именем Acme\... будет искаться в папке src. То есть: Acme\SomeClass, Acme\Some\Other\Class и тд.
> И если я хочу использовать в глобальном пространстве имен (или любом другом, который отличается от неймспейса, в котором находится Validator) Validator, у которого неймспейс, пусть будет Acme, то я должен писать: $validator = new Acme\Validator(); > или в начале use Acme\Validator;
Лучше использовать use, код будет чище, и многие IDE умеют сами его вставлять. Но можно и ручками писать, пока учишься.
> Кстати, наверное можно написать и так в composer.json (я видел в одном примере, но тупил и у меня не получилось повторить): > "psr-4": {"": "src/"} > Таким образом будет подгружаться любой файл из src?
Это не рекомендуется, но это значит да, если класс не соответствует другим правилам (или других правил нет), то он берется из src.
> я не понимаю зачем конкретно мне использовать namespace, потому что я не собирался подключать сторонние библиотеки. И мой проект - довольно маленький.
Чтобы научиться хорошим практикам. Если нет неймспейсов, то может быть конфликт, когда в твоем коде и в какой-то библиотеке класс с одинаковым названием. Или если ты захочешь класс из одного проекта в другой перенести.
> Я не против namespace, но ведь нужно же с умом применять что-либо, понимая нужно оно тебе или нет.
По моему, проще применять всегда, даже если у тебя всего 5 классов в одной папке.
> наверное стоит написать "файлы", ведь класс это файл, который содержит объявление класса,
Нет, файл это файл, и он содержит описание класса. А "класс" это какая-то абстрактная штука, которая появляется в памяти после того, как PHP прочтет файл. Или, с другой стороны, класс - это "вид" объекта.
> composer подключает только файлы-классы или любые файлы, в которых namespace Acme и они находятся в папке src?
Он подключает только те файлы, которые соответствуют правилам автозагрузки в composer.json и рекомендации PSR-4. Подключает он их в момент первого обращения к классу, используя механизм автозагрузки. То есть если в коде встречается $x = new X(); то срабатывает автозагрузка и вызывается композер для поиска файла с классом X.
> Сейчас, чтобы попасть на страницу localhost (ага, да)/form, > должна осуществиться следующая цепочка: Примерно так и есть. При поступлении HTTP-запроса выполняется public/index.php и его задача выдать какой-то ответ на запрос и умереть.
Почему так? Почему в первом случае он добавляет > and defined in <file>:<line> А во втором > in <file>:<line> Куда делось and defined? Там какая-то дофига хитрая проверка под капотом, которая определяет, вывел ли я место вызова в сообщении? Вопрос, конечно, имеет чисто академический характер. Мне от этого поведения наоборот удобнее, но было неожиданно, что в зависимости от того, что я пищу в сообщении, он меняет автоматически добавляемую часть.
>>2962032 Спасибо за ответ. Я немного застрял на роутинге. Я поступил очень просто: https://3v4l.org/rVZCl Это не готовое решение. Оценивать его составляющие - бессмысленно. Акцент только на общем принципе работы. Хоть это решение неплохо смотрится на моем проекте, который состоит из 2-3 страниц, но, наверное стоит сделать это иначе? Возможно на ООП? Я попробовал читать Symfony, это дало мне почти ничего. Это вообще редко мне что-то дает. Читал другую статью, в которой автор создавал два класса Route (представляет собой маршрут) и Router (умеет работать с маршрутами). Сначала было понятно, а потом он ушел в какую-то кашу, которую я не понял. Как ты считаешь, если действительно, стоит сделать маршрутизатор в ООП виде, то как мне стоит это реализовывать? Извини, если это нагло звучит. У меня часто так получается, что люди искаженно понимают настроение, с которым я пишу им сообщение. Если писать откровенно, то я не знаю где грань в нашем диалоге. Наверное это неправильно, что я засыпаю и засыпаю тебя вопросами. Но я также понимаю, что мне нужна обратная связь. Что я многое не знаю. Не знаю даже как прогугливать определенные темы. Я стараюсь сначала разобраться в теме сам. Вообще, если бы не ты, я бы так и сидел, пытаясь делать валидацию. Только благодаря тебе я смог закончить этот этап создания проекта. Ну или я бы сидел с той кашей, которую написал, когда принимался за эту задачу.
1) по-простому, как у тебя, с использованием if. Просто, но не очень интересно и не помогает в изучении ООП.
2) по-сложному с использованием ООП.
Делаем класс Route ("маршрут"), представляющий один роут, со свойствами: URL, контроллер. Делаем класс Router ("маршрутизатор"), который в конструктор принимает список роутов, и содержит метод "маршрутизировать" route(string $path): ?Route, который по URL находит и возвращает подходящий роут.
Выглядеть будет так:
$router = new Router([ new Route('/test', 'Controller/test.php'), new Route('/example', 'Controller/example.php'), ... ]);
$route = $router->route($url);
Обрати внимание, если роут не найден, нужно отдавать страницу ошибки с кодом HTTP-состояния 404. Для этого можно сделать отдельный контроллер.
Также обрати внимание, в REQUEST_URI могут быть еще параметры, по типу /student?x=1&y=2. Их надо отрезать с помощью parse_url(), чтобы они не мешали роутингу.
Что касается роутинга в Симфони, там как раз сделан ООП-роутер, довольно навороченный. Ты описываешь список роутов либо в конфиге в формате YAML, пример:
blog_list: path: /blog # the controller value has the format 'controller_class::method_name' controller: App\Controller\BlogController::list
Либо ты можешь описать роут прямо в коде класса-контроллера с помощью атрибутов PHP8:
class BlogApiController extends AbstractController { #[Route('/api/posts/{id}', methods: ['GET', 'HEAD'])] public function show(int $id): Response { ....
Также, как ты наверно заметил, в Симфони в роутах могут быть параметры по типу
/api/posts/{id}
Здесь вместо {id} может стоять любое число. Оно будет извлечено из ротуа и передано в аргумент $id у метода show().
То есть при обращении к странице /api/posts/123 будет автоматически создан объект класса BlogApiController и будет вызван его метод show с параметром $id = 123. И как ты видишь из тайп-хинта, он должен сгенерировать ответ и вернуть объект Response.
Я скажу больше, Симфони умеет даже находить объект в БД по id из URL автоматически. То есть, мы могли бы написать:
... #[Route('/enrollee/view/{id})] public function viewEnrollee(Enrolllee $enrollee)
И Симфони автоматически найдет в БД студента с id, указанным в URL, и передаст в контроллер.
Что касается вопросов, то этот тред специально сделан для начинающих и их вопросов. Лучше ты тут будешь вопросы задавать, чем потом сеньоров отвлекать.
>>2962659 Не знаю, для меня выбить 150к - это уже что-то на грани невозможного. Но я забугром сейчас, ищу удаленку. Вообще, фирмам по большому счету уже плевать сколько платить, для бизнеса это вопрос десятой важности. Всё равно сколько бы ты айтишке не плати, расходы на разработку программного продукта в процентном соотношении к общей выручке минимальные. Сейчас мода наоборот идёт на то, чтобы выцепить из потока айтишной массы самых лучших, как начинающих, так и сеньоров пиздаболов. Я вот например живая посредственность, у меня хоть и есть относительно большой опыт и какие-то знания, меня никуда не берут, потому что нет нужных софт скилов и таланта прыгать выше головы, не чувствуется амбициозность. А знакомый Вася Пупкин, знающий столько же сколько и я, но имеющий себя продать, имеет оффер в 250к
>>2963344 > Не знаю, для меня выбить 150к - это уже что-то на грани невозможного. Но я забугром сейчас, ищу удаленку. Сколько лет опыта у тебя? Учился ли новому после трудоустройства или хуи пинал?
>>2963534 Ну, собственно, очередная причина почему пхп говноязык. >Error is the base class for all internal PHP errors. Вот бы можно было сделать свой класс приватным, чтобы всякие ебланы не использовали его не по назначению и не удивлялись чей-то он работает не так как им надо. Да не хуйня какая-то. Мама учила делиться.
Сап, что посоветуете изучить джуну?(вкатился на работку, но чувствую что надо прокачивать скиллы и хочется учить что-то вне работы) Интересно узнать мнение сеней
>>2963656 Советую пройти курс по русскому языку и курс оператора эвм. Гениям, которые могут два слова нормально напечатать, сразу лычку мидла дают. А когда научишься знаки препинания ставить - переведут в сеньоры и дадут почетную грамоту "Не еблан". Дерзай.
>>2962654 Забавно, что когда речь заходит о зарплате - в треде/чатах сразу наступает молчок. Пыхари, вы там за идею работаете, просто вам стыдно в этом признаться?
>Обрати внимание, если роут не найден, нужно отдавать страницу ошибки с кодом HTTP-состояния 404. Для этого можно сделать отдельный контроллер. У меня контроллер это просто скрипт. Что будет делать контроллер, который отвечает за 404-страницу? Просто подключать 404-page.html?
Кстати, до этого мы обсуждали файловую структуру проекта. Symfony разделяет понятия "форма" и "шаблон"? Я так подумал, пройдя по этой ссылке: https://symfony.com/doc/current/best_practices.html#use-the-default-directory-structure Там есть отдельная папка для templates и для form Почему? Я думал, что шаблон это HTML-файл, в котором есть php-переменные (я знаю про разделение HTML и php), а форма - частный случай шаблона. Т.е. форма - это HTML-страница с полями для ввода, в которую также можно вставлять php-переменные, например, для того, чтобы вывести уже заполненные пользователем данные или ошибки валидации. Короче, я думал, что буду класть в папку templates HTML-страницы, одна из которых будет графически отображать поля для ввода (т.е. форма).
Представление в MVC, в частности в моем проекте, это же просто HTML-страницы, которые будут подключаться контроллером так: require_once(somePage.html)? Т.е. вызвать представление = подключить HTML-файл?
Также ты упоминал про атрибуты. С ними еще связаны аннотации. Можешь по-простому ответить на следующие вопросы (в контексте php)? Что такое аннотация? Что такое атрибут? Чем они отличаются кроме синтаксиса? Когда нужно использовать аннотацию, а когда атрибут? Что получит полезного разработчик, если будет использовать аннотации или атрибуты (не общие примеры, а конкретные, когда я пытался разобраться в этой теме, у меня сложилось впечатление, что атрибуты нужны для того, чтобы IDE могло лучше подсказывать что есть в коде)? Стоит ли мне, новичку, лезть в эту тему? Не рано ли мне это? Просто я еще не понимаю зачем мне все это нужно, я даже комментарии не пишу в коде, а с момента когда я узнал про тайп-хинты, ведение документации выглядит в моих глазах как нечто лишнее. Если мне действительно стоит начать заниматься документированием своего кода, то с чего мне начать? Что вообще гуглить? Я пробовал гуглить, смотреть на ютубе. Но либо я не умею гуглить (что вполне может соответствовать истине), либо я еще не готов к такой информации. Еще у меня сложилось впечатление, что атрибуты/аннотации это не просто комментарий, текст, а будто что-то большее. Если это так, то, что это? Если я добавлю атрибут или аннотацию в код, что-то изменится? У меня начнет иначе работать код?
>>2963625 >чтобы всякие ебланы не использовали его не по назначению
ГУГЛИШЬ - А ЕСТЬ ЛИ В PHP ТИПИЗИРОВАННЫЕ МАССИВЫ? @ ИХ НЕТ @ НАХОДИШЬ ОБСУЖДЕНИЕ ПРЕДЛОЖЕНИЯ RFC С ЭТОЙ ФИЧЕЙ @ ИЗ ОБСУЖДЕНИЯ УЗНАЕШЬ, ЧТО ТЫ - ХУЙ, ТВОЯ МАТЬ - ШЛЮХА, А ИХ ОТСУТСТВИЕ - БАЗА. ЭТО БУДЕТ ОЧЕНЬ ПРОБЛЕМНО РЕАЛИЗОВАТЬ, ПОТОМУ ЧТО ЧИТАТЬ ДАЛЕЕ @ НО НЕ ПЛАЧЬ, МАКАКА. ВСЕГДА МОЖНО СДЕЛАТЬ ДЛЯ ЭТИХ ЦЕЛЕЙ КЛАСС-ОБЁРТКУ С МЕТОДОМ PUSH(), КОТОРЫЙ БУДЕТ КИДАТЬ TYPEERROR, ЕСЛИ ПЕРЕДАННЫЙ В НЕГО ЭЛЕМЕНТ НЕ ЯВЛЯЕТСЯ INSTANCEOF ОЖИДАЕМОГО ТОБОЮ КЛАССА @ НО ВООБЩЕ, КЛАСС TYPEERROR ДЛЯ ВНУТРЕННЕГО ИСПОЛЬЗОВАНИЯ, ПОШЁЛ НАХУЙ
>>2961985 >В твоем случае классов не очень много и можно их поделить на такие компоненты:
Кстати, у меня есть 2 трейта и интерфейс. Куда мне стоит их отнести в файловой структуре? Раз интерфейс относится к валидации (интерфейс для классов-проверятелей), то его стоит отнести в папку Validation? А трейты в Utility?
Также: >>2958095 >class Authorization >Тут стоит сделать такие публичные методы: залогинить человека, разлогинить, определить текущего пользователя.
Я тебя понял так: Залогинить человека = определить зарегистрированный пользователь или нет. Определить текущего пользователя = отличить одного зарегистрированного пользователя от другого.
Как я планирую это делать: Как отличить зарегистрированного пользователя = посмотреть $_COOKIE, если массив не пустой, то пользователь зарегистрирован (потому что при регистрации в куки сохранится пароль). Да, я знаю о том, что пользователь может потерять куки. Но мы же в решении задачи опускаем этот момент? Как отличить одного зарегистрированного пользователя от другого: Тут я два варианта придумал: 1) При регистрации пользователя создается же новая запись в БД, тогда нужно брать id новой записи и класть его в куки пользователя. 2) Взять пароль из куки пользователя, получить его соленый хеш, сравнить с солеными хешами в БД, если есть совпадение = отдать id.
>>2962880 Есть еще один момент, который для меня туманен. Я не могу четко сказать который класс Модель, а который, эм, просто класс? Эта проблема показала себя, когда мы заговорили про класс, работающий с куки. Я себе троицу Модель-Контроллер-Представление представлял так, что всю работу делает Модель, а контроллер только говорит что именно делать. Сейчас думаю о классах Регистрация и Авторизация. По логике выше, Регистрация и Авторизация это части Модели, потому что они выполняют работу - регистрируют и авторизовывают. В то время как Контроллер может указывать Модели, чтобы она зарегистрировала или авторизовала. Так...А что насчет класса CookieManager?... Немного есть сомнение насчет того, что этот класс должен существовать. Потому что там сейчас только 2 метода - установить и получить куки. Но ведь все это можно сделать и во внешнем коде, без всякого класса(?) Если только контроллер может лазать в суперглобальные переменные, то, наверное он и должен работать с куки? И если регистрации или авторизации нужны куки, то отдавать их должен контроллер? Так наверное... Да, короче, одна каша получается. Извини, если чтение моего поста вызвало у тебя фейспалм.
>>2963917 В index.php есть такая строка: require_once($route->get('controllerName')); Скорее всего нужно будет переписать ее. Ведь контроллеры будут храниться в отдельной папке src/Controllers/someController.php
Пришел а ваш тредик просить о помощи. Я не разработчик, что там происходит в коде не понимаю. Ситуация следущая, есть один нищий, ебаный сайт на bitrix развёрнут он на centos7 через скрипт bitrix-env с сайта Битрикса собственно. Он работает нормально, но мне нужно его перенести с этой машины на другую где нет этой обвязки Битрикса. Версии mysql и php аналогичные на другой машине. Что я сделал, просто скопировал содержимое /www/ и дамп базы, залил всё на новый сервак и нихуя не работает. При попытке зайти на https://mysite/index.php я получаю такой выпук из жопы если нажать f12: <? require($_SERVER["DOCUMENT_ROOT"]."/bitrix/header.php"); $APPLICATION->SetTitle("Главная"); ?> <?require($_SERVER["DOCUMENT_ROOT"]."/bitrix/footer.php");?>
В браузере это отображается как пикрил. То есть просто содержимое index.php котрый в корне сайта. При этом если выполнить тоже самое на работающей машине, там дохуя всего и собственно сам сайт погружается. В логах сервака ошибок не видно никаких. Есть какие-то идеи у местных php гуру и богов Битрикса? С меня как обычно нихуя
Блядь, какой же ебаный ад пишут битриксоиды. У них понятия качества когда вообще в голове нет никакого. До меня такой ебанат на проекте такого наворотил и съебал в закат. Баг баги баги баги. Пишет модель в базу, через 400 строк они всегда срут прямо в контроллерах пихает метод где ещё в 400 опять достаются модели, перезаписываются частично и опять сохраняются. Туда-сюда, дрочила ты контуженный, а меня потом просят выяснить почему тормозит. Да потому, что код вам обезьяна с битриксом писала. Правишь код в одном месте - вообще в другом баги лезут.
>>2964526 Что значит "отвечает за данные"? Конкретные действия напиши. Что такое сервис? Я думал класс, состоящий из методов, это сервис? И если сервис это не Модель, то где в MVC он находится? >Это методы контроллера Зачем контроллеру регистрировать и логинить пользователя, если можно попросить Модель (вызвав какой-нибудь метод authorize()) и она все сделает?
>>2964607 Ну плюс минус да, понятно что не всё переменные. Но я не думаю, что там это как-то влияет. Нигде не пишут что надо какие-то особые пляски с бубном устраивать. Да и другие сайты на php на этом серваке работают, на вордпрессе например. Так, что я думаю дело в чём-то другом.
>>2964602 >Конкретные действия напиши Конкретные задачи слоя модели ты сам загуглить можешь.
>И если сервис это не Модель, то где в MVC он находится? А он и не в MVC. Это слой бизнес-логики. Пишешь, например, сервис для работы со скидками где есть метод расчета скидки для проперженных стульев только сычей и потом ты его можешь вызвать как из контроллера, так и из cli консольной команды своего приложения. При этом когда ты его пишешь, то отлаживаешь именно через консоль, а не дергая браузер за писюн постоянно. Для него же юнит-тесты можно завезти. Потом просто вызываещь этот метод в контроллере с нужными параметрами из запроса и получаешь скидку для сыча по прореженному стулу красного цвета из осины. Сервисы вообще база разработки.
>>2964883 Чуть позже никто тебе не мешает в контроллере получать запрос и перекидывать его данные в планировщик и уже из планировщика вызывать этот сервис точно так же. Это называется асинхронная обработка запроса. Хотя теб етакое рановато еще - тут про хайлоад. Т.е. логика бизнеса у тебя инкапсулирована отдельно, а не в mvc и ты с ней отдельно работаешь.
>>2964735 Да ты прав, я тут напрасно быканул, мои извинения. Перекинул полностью конфиг php завелось, вопрос только не легли ли другие сайты...чувствую надо это говно по контейнерам распихать и не ебаться.
>>2964467 Тут соглы - хуярим цикл в цикле, внутри делаем запрос в базу. Особенно пикантно, когда это делается в "компоненте" между html и js-ом в куске пхп-кода
Правильно ли я понимаю, что если я использую какую-нибудь ублюдочную функцию из стандартной библиотеки PHP, у которой один из аргументов - ссылка, я должен использовать какую-то промежуточную переменную, если в качестве аргумента для передачи в функцию используется результат выполнения какого-то метода?
Вот. Я бы мог, конечно, обращаться напрямую к $this->arr, но что это за говнина, зачем геттеры и сеттеры вообще тогда нужны? Вдруг в них какие-то дополнительные проверки, модификации и т. д. Без переменной $tmp тут никак, получается?
>>2966045 Ну давай разберем по частям тобой написанное. 1) Можешь ли ты зафорсить использование геттера снаружи объекта? Да можешь. Если геттер это единственный способ получения данных из объекта у тебя есть гарантия что логика геттера будет выполнена.
2) Можешь ли ты зафорсить использование геттера внутри класса? Нет не можешь. Всегда есть прямой доступ к переменной.
Если нет гарантий что внутри объекта будет вызван геттер, то тупо на это закладываться. А значит в геттер нельзя пихать логику для внутренней работы объекта, а только логику для доступа извне.
>>2966287 Звучит логично, но я не уверен. Сейчас у меня геттер просто возвращает значение, сеттер проверяет каждый элемент переданного массива на соответствие типу. Из твоих слов следует, что я должен нигде внутри не использовать сеттер, а копипастить код переборки и проверки на соответствие типа?
Даже если так. Допустим, внутри сеттеры и геттеры использовать не следует. А если у меня дальше должно упасть какое-то исключение?
Понимаю, что пример ущербный, если исключение кидать ПЕРЕД сплайсом, то всё будет нормально. Но я так моментально не могу придумать более реалистичный.
Так или иначе, мне кажется, что алгоритм должен быть примерно такой:
1. Текущее значение списка сохраняем в какую-то промежуточную переменную. Использую я здесь геттер, или напрямую обращаюсь к свойству - вопрос вторичный. 2. Делаем всякое. 3. Если в процессе всякого ошибок не возникло, говняка не вылезло, то сохраняем в $this->arr значение нашей промежуточной переменной.
То есть, получается, промежуточную переменную в любом случае стоит использовать. А будет она в себе хранит результат вызова геттера, или являться результатом прямого обращения к $this->arr - вопрос второй.
>>2966405 То что ты тут пытаешься написать это типичный функциональный код. Есть некоторая структура данных, есть отдельная функция, которая с этими данными работает (у тебя это функция которая использует результат getArr()), а чтобы эту структуру изменить и ничего не сломать мы делаем её иммутабельной и изменяем копию. Классика. Детсадовский пример из хаскеля какого-нибудь.
В чем по сути твоя проблема? В том что в твой массив попали данные, которые туда не должны попасть. Почему? Потому что это блядь массив, в который можно запихнуть любую хуйню. Если ты пишешь ООП код, то у тебя в место массива должен быть объект в который нельзя поместить хуйню. Потому что объект, в отличии от функциональной тупой структуры данных имеет поведение в виде тех самых инкапсулированных правил, которые просто физически не дадут ему перейти в неправильное состояние.
И как итог у тебя из-за этих ебучих геттеров и сеттеров поведение размазано по коду в нескольких местах. В функциональных языках методы хоть и отдельно от структур данных, но хотя бы в одном и том же модуле лежат. А у тебя будет лютый пиздец, вся обработка данных снаружи. По каким правилам она происходит - пиздуй ищи. Да не забудь перебрать все места где эти функции вызывались.
>>2966587 >Если ты пишешь ООП код, то у тебя в место массива должен быть объект в который нельзя поместить хуйню
Так а что этот объект представляет из себя, если не свойство-массив и всякие методы вроде push, remove и т. д, которые позволяют менять содержимое этого массива, и не дают добавить в него говняк?
>>2966659 Как ты заебал. Ну если ты пишешь в функциональном стиле, так сделай блядь его иммутабельным и не еби мозги ни себе ни людям https://3v4l.org/0jCsX
И все все вопросы отпали нахуй сами собой. Не надо никаких блядских геттеров никаких ебаных сеттеров. И копию надо делать ОБЯЗАТЕЛЬНО. Только к ооп такой код никакого отношения не имеет. Да тебе и похуй на то ооп было.
Я советую для иммутабельных классов дополнительно писать у полей readonly, чтобы уж точно не поменять было. В index.php надо вызывать контроллер 404 страницы. Контроллеры не надо класть в public.
> Что будет делать контроллер, который отвечает за 404-страницу?
Ограничено лишь глубиной твоей фантазии, но в минимальной версии надо отдать код состояния HTTP 404 и показать хотя бы надпись, что страница не найдена.
А шаблон это файл с HTML-кодом + конструкции twig.
> Представление в MVC, в частности в моем проекте, это же просто HTML-страницы
Да
> Что такое аннотация? > Что такое атрибут?
Это штука, которая позволяет добавить к классу, полю, аргументу функции или методу дополнительную информацию. Какую? Да какую хочешь, атрибут это объект любого класса. Ты создаешь класс-атрибут, а затем указываешь его имя и параметры для конструктора и объект-атрибут создается и прикрепляется к классу или методу:
#[MyClass('xyz')] public int $x;
Дальше ты можешь скриптом получить атрибуты класса, поля итд. Погугли где-нибудь статью про это.
Это используется напрмер, так:
- можно указывать правила валидации поля с помощью атрибутов, валидатор читает атрибуты и применяет к полю указанные в них правила. Так сделано в валидаторе Доктрины. - можно указывать информацию для ORM, то есть какой колонке соответствует данное поле, какой таблице соответствует данный класс, и ORM будет это использовать.
Раньше атрибутов не было, а добавлять информацию хотелось, поэтому в комментариях писали аннотации типа @var или @ORM\Column. Для PHP это был ничего не значащий комментарий. Были библиотеки, которые парсили комментарии и извлекали из них аннотации в виде объектов. В PHP8 появились атрибуты, которые стали частью языка. Теперь аннотации используются только для phpDoc (так как он не использует классы), а все остальное надо делать атрибутами.
Хочешь - сделай атрибуты для маппинга в базу. Чтобы у тебя не был где-то прописан список полей, которые надо сохранять, а чтобы у полей класса был атрибут, говорящий, в какую колонку таблицы его надо сохранить. Или хочешь, добавь студенту правила валидации через атрибуты. Заодно изучишь их.
> Если мне действительно стоит начать заниматься документированием своего кода, то с чего мне начать?
С README: что это за проект, как его развернуть. Это первое, что нужно.
А так, надо ставить phpDoc-типы там, где не хватает нативных типов. Например, чтобы указать структуру массива.
Все комментировать не надо, комментировать надо не-очевидные вещи. Приватные методы обычно не комментируют, публичные комментируют. Свойства комментируют. Для полей, методов, переменных надо использовать понятные названия.
Приведу пример:
- если функция называется getSudentCount, не надо комментировать, что она считает количество студентов, это понятно из названия. А вот если она считает количество только подтвержденных аккаунтов студентов, то это не очевидно из названия и надо указать.
- если функция saveStudent может добавить нового или обновить существующего студента, это не очевидно
Обычно в названии все отразить невозможно (оно будет слишком длинным) и комментарии все-же нужны.
Поищи статью про правильное написание комментариев.
> Еще у меня сложилось впечатление, что атрибуты/аннотации это не просто комментарий, текст, а будто что-то большее.
Атрибут это способ добавления произвольной информации к элементам кода.
> Раз интерфейс относится к валидации (интерфейс для классов-проверятелей), то его стоит отнести в папку Validation?
Если ты следуешь принципу "класть вместе классы, делающие одно дело", то да. Если ты следуешь принципу "класть классы по типу в разные папки", то интерфейс идет в папку interfaces (не советую).
> А трейты в Utility?
Если они вспомогательные, то да.
> Залогинить человека = определить зарегистрированный пользователь или нет.
Это значит выставить правильные куки, чтобы он был залогиненным.
> Определить текущего пользователя = отличить одного зарегистрированного пользователя от другого.
Проверить куки на правильность и вернуть либо null, либо текущего залогиненного студента как объект.
> 1) При регистрации пользователя создается же новая запись в БД, тогда нужно брать id новой записи и класть его в куки пользователя.
Тогда хакер может положить себе в куки чужой id и взломать твою систему.
> 2) Взять пароль из куки пользователя, получить его соленый хеш, сравнить с солеными хешами в БД, если есть совпадение = отдать id.
Это уже лучше. Только в куке не надо хранить пароль в открытом виде, а тот самый соленый хеш.
Есть еще вариант 3 - при регистрации генерировать длинный случайный токен, и класть его в базу и в куки пользователю. Хакер его точно не подберет.
> , я должен использовать какую-то промежуточную переменную, если в качестве аргумента для передачи в функцию используется результат выполнения какого-то метода?
А как иначе? Ведь по ссылке ты должен передать "место", в которое можно что-то записать. Число или строка не подходят, подойдет переменная.
> Я бы мог, конечно, обращаться напрямую к $this->arr, но что это за говнина, зачем геттеры и сеттеры вообще тогда нужны?
Геттеры-сеттеры обычно для внешнего кода. А внутри класса можно лезть напрямую, ты ведь и так знаешь, есть там какая-то логика или нет. А можно и через сеттеры, хоязин класса - барин.
Посоны, расскажите про эти докблоки. Их самим нужно писать или это автоматом пхпшторм делает? Если я работаю в вскоде, то нужно их делать? Или вот захотел библиотечку на гитхаб залить, нужно эту хуйню делать. И нахуя она вообще нужна?
Прочитав несколько раз твое объяснение, прочитав мануал по php, пробуя писать тестовый код, вот что я понял про атрибуты:
Для того, чтобы создать класс-атрибут:
1) Создать просто класс: class SomeClass {}
2) Дописать сверху #[Attribute]
#[Attribute] class SomeClass{}
Как использовать?
Если нужно описать что-то с помощью этого атрибута:
#[SomeClass('description')] public int $number;
Получается атрибут для поля number.
Для того, чтобы получить атрибут:
1) Понять какую сущность описывает атрибут (поля, метод, класс и т.д.) 2) Создать объект соответствующего класса:
ReflectionClass::getAttributes() - Получает атрибуты класса ReflectionClassConstant::getAttributes() - Получает атрибуты констант ReflectionFunctionAbstract::getAttributes() - Получает атрибуты функций ReflectionParameter::getAttributes() - Получает атрибуты параметров
Особенности получения атрибутов, конечно, нужно смотреть в мануале уже.
например, получить атрибуты конкретного поля конкретного класса $property = new ReflectionProperty('вписать название класса, в котором содержится атрибут поля', 'вписать название поля для которого указан атрибут'); В этой строке я получаю только объект ReflectionProperty. После этого, как по мануалу, я должен получить атрибуты: $attributes = $property->getAttributes(); А потом уже могу вызывать методы атрибута (получить аргументы, имя).
У классов, которые начинаются с ReflectionClass... существуют методы: getAttributes() = получить все атрибуты чего-то конкретного
После получения атрибутов, можно вызывать соответствующие методы:
У атрибута есть методы: getArguments() = получить аргумент, который передали при создании атрибута getName() = получить имя атрибута
Как я понял по-вкатунски, раньше люди писали описание в виде комментариев, а теперь можно писать описание и представлять его объектом.
>>2967032 Чисто по вкатунски: аттрибуты нужны не для описания и не для комментариев. А за ReflectionClass в коде тебе фанеру пробьют и отправят толчки чистить.
>>2967054 >аттрибуты нужны не для описания и не для комментариев Окей, для чего они нужны? >А за ReflectionClass в коде Почему? И если не так получать атрибуты, то как?
>>2967073 >Окей, для чего они нужны? Тебе выше написали. Чтобы добавить метаинформацию. Например как свойство класса называется в базе.
>Почему? И если не так получать атрибуты, то как? Получить их можно только через рефлексию. Почему рефлексия кал наберешь в гугле, это не только к пхп относится.
>>2967081 >Чтобы добавить метаинформацию Да? А я думал, что описание/комментарии это и есть метаинформация. Разве метаданные это не данные о данных? А чем комментарий о том, какой тип у переменной, не метаинформация?
В статье на хабре про Reflection API написано: >И категорически не рекомендуется использовать в рабочем коде проекта, т.к. это ещё и не безопасно. Почему использовать Reflection API не безопасно?
>>2967086 >А чем комментарий о том, какой тип у переменной, не метаинформация? Метаинформация. Поэтому информацию о типах давным давно перенесли прямо в код и такие комментарии не нужны.
>>2966791 >Это значит выставить правильные куки, чтобы он был залогиненным.
Я не понимаю что это значит. Мне словосочетание "выставить правильные куки" ничего не говорит.
Мой код должен уметь две вещи, относительно запоминания пользователя: 1) Добавлять нового пользователя в БД и помечать этого пользователя как зарегистрированного (как уже внесенного в БД); 2) Определять заходил ли пользователь на страницу и если заходил, то внесен ли он в БД.
>Есть еще вариант 3 - при регистрации генерировать длинный случайный токен Токен и соленый хеш от пароля чем-то отличаются?
Во-первых, про Reflection. Это набор классов для получения информации о классах, функциях, методах, свойствах. Например, ты можешь с его помощью узнать тип поля, или от кого наследуется класс, или список методов класса. Также с его помощью ты можешь получить атрибуты.
Объект атрибута создается с помощью ReflectionAttribute#newInstance. Вот работающий пример: https://3v4l.org/P6jBF#v8.3.0
1) создаем класс-атрибут (как правило, это полностью иммутабельный класс) 2) навешиваем его на поле 3) через reflection и newInstance() получаем объект атрибута и используем его как хотим
Как я уже писал выше, атрибуты используются для навешивания дополнительной информации на элементы кода: правила валидации, правила сохранения в БД и подобное. Симфони много где использует атрибуты, с их помощью можно даже роуты назначать контроллеру, например.
Я тебе советую сделать что-то на атрибутах, например: добавление правил валидации к полям через атрибуты и добавление информации о том, какие поля сохранять в базу.
> 1) Создать просто класс: > class SomeClass {} > #[SomeClass('description')]
Неправильно. У тебя должен быть конструктор, принимающий description, в классе. Смотри мой пример.
> раньше люди писали описание в виде комментариев
Да. Была библиотека doctrine/annotations, которая парсила эти комментарии и создавала объекты-атриубуты. Сейчас это стало фичей языка и костыли больше не нужны.
Раньше это выглядело так:
/** * @Validator\Constraints\NotBlank("Должно быть заполнено поле") */ private string $name;
Потому что дурак статью писал. Если бы это было небезопасно, он бы показал пример и написал, почему. А если не может, то значит слышал звон, да не знает, где он.
> Идентификация — это процесс определения личности пользователя, например, по имени или его адресу электронной почты.
> Аутентификация — это сам процесс проверки подлинности пользователя, чтобы убедиться, что он является тем, за кого себя выдает. Для аутентификации пользователи могут вводить пароль, биометрические данные, СМС-код или другие секретные данные.
> Авторизация — это процесс проверки прав доступа пользователя к определенным ресурсам или функциям. Он проводится после успешной аутентификации. В результате авторизации пользователь может получить доступ к определенным файлам, программам и т. д.
Допустим, пользователь на странице банка вводит логин и пароль. Банк проверяет логин - это "идентификация", определение, кто пытается войти. Затем пароль - это "аутентификация", проверка, что пытается войти именно пользователь, а не хакер. Далее пользователь пытается посмотреть историю операций и банк делает "авторизацию" - проверку, что пользователь A имеет право на просмотр истории операций по счету X.
Заметь, что в случае с вебом, идентификация и авторизация происходят не только на форме логина, но и на каждой странице. Обычно послу успешного логина сайт выставляет куку с каким-то секретом (токеном). Браузер отправляет эту куку при заходе на страницу, и сайт идентифицирует и аутентифицирует пользователя по куке.
Кука используется, чтобы можно было войти один раз, и не надо было вводить пароль на каждой странице снова.
Эти 3 этапа есть всегда, даже если регистрации как таковой нет. В задаче про студентов предлагается такой подход: при вводе своих данных сайт генерирует токен и выставляет куку с ним. Когда пользователь заходит с этой кукой на любую страницу, сайт идентифицирует и аутентифицирует пользователя по этой куке.
>> Это значит выставить правильные куки, чтобы он был залогиненным. > Я не понимаю что это значит.
"залогинить" - установить куки, по которым сайт сможет идентифицировать и аутентифицировать пользователя. "разлогинить" - удалить эти куки.
>>2967182 Спасибо за ответ. Я не понимаю зачем мне нужен ReflectionAttribute#newInstance Разве я не могу получить массив атрибутов для чего-то конкретного в коде и вызывать getName()/getArguments(), чтобы оперировать значениями названия класса-атрибута и его параметров? Как, например, тут: https://3v4l.org/qMWIE Зачем мне промежуточная стадия в виде вызова newInstance()?
Потому, что мы используем ООП. Зачем нам выковыривать параметры из массивов каких-то, когда у нас есть объекты с удобными методами.
Также, при вызове getInstance проверяется, что класс-атрибут реально существует и аргументы правильные. А иначе не проводится никаких проверок.
К тому же параметр может быть записан несколькими способами, например: MyAttr('xyz') или MyAttr(name: 'xyz') и ты будешь получать разные форматы массивов.
приветик всем гениям пхп дев. я на этапе обучения верстки (css and html), но не_повезло найти работу и мне тимлид дает задания по бэку, где стеки пыха+ларавель, а я вот пару дней назад только начала изучение пхп, поэтому код не читается и сложно мной воспринимается, если кому-то нехуй делать, поотвечайте на мои вопросики в телеге, пожалуйста
>>2967828 А не подахуел ли твой тимлид? Тимлидит он, зарплату получает он. А на вопросы должны аноны с двачей отвечать? Вот пусть этот хуйлуша и отвечает, нихуя он четко устроился, на анонах воду возить, черт ебаный.
>>2967746 Окей. В этой строке: #[NotBlank('Имя заполнять кто будет?')] private string $name;
Объект какого класса создается? NotBlank или ReflectionAttribute?
Как я понял по var_dump, в этой строке: $reflProp->getAttributes() создается массив из объектов класса ReflectionAttribute И только у объектов этого класса есть методы: getName(), getArguments(). Если я сделаю так: $reflProp->getAttributes()[0]->newInstance() создается объект уже класса NotBlank, но NotBlack не обладает методами getName(), getArguments(), а его свойство является приватным, как мне добывать информацию из атрибута? Решение, которое я придумал - добавить в класс NotBlank геттер. Так приемлимо?
>>2966768 >Чтобы у тебя не был где-то прописан список полей, которые надо сохранять, а чтобы у полей класса был атрибут, говорящий, в какую колонку таблицы его надо сохранить
Дальше я пишу класс TDG, но тут загвоздка: чтобы получить атрибут поля, нужно сделать так: $property = new ReflectionProperty('Enrollee', 'имя поля');
Т.е. мой код все равно должен знать какие поля есть у Enrollee, чтобы получать атрибуты.
Изначально речь шла о списке полей Enrollee (о всех его полях, потому что все они добавляются в БД) внутри TDG, но теперь что...? TDG будет хранить список полей и еще обращаться к рефлексии, чтобы получать атрибуты?
Мб я должен прикреплять атрибуты не к конкретному полю, а к классу Enrollee, тогда я смогу получать атрибуты, не думая какие поля есть в Enrollee?
Смутно припоминаю, может быть 1 раз когда-то использовал. Обычно они нужны, когда тебе надо посчитать несколько разных группировок по одному набору данных.
Reflection содержит представления для всех элементов кода: классов (ReflectionClass), функций, свойств и тд. ReflectionAttribute это рефлекшен для атрибута, позволяющий получать данные о нем, а также создать его экземпляр.
Когда ты вызовешь newInstance() то ты получишь объект NotBlank.
> создается объект уже класса NotBlank, но NotBlack не обладает методами getName(), getArguments(), а его свойство является приватным, как мне добывать информацию из атрибута? > Решение, которое я придумал - добавить в класс NotBlank геттер.
Так и надо. Еще можно сделать свойства публичными, но это будет плохо сочетаться с остальным кодом, который использует геттеры.
Название registration не очень, так как я не вообще понимаю, что оно значит и что содержит. А код должен быть понятным другому человеку (ты же будешь в команде работать). Лучше выбрать другое название и добавить комментарий с пояснением.
Дальше, я бы советовал сделать имя колонки необязательным, чтобы его можно было не указывать, в этом случае класс работы с БД просто возьмет название поля. Зачем 2 раза писать одно и то же? То есть, чтобы было:
#[Column] private string $name;
#[Column('xyz')] private int $abc;
> Т.е. мой код все равно должен знать какие поля есть у Enrollee, чтобы получать атрибуты.
Ты можешь с помощью ReflectionClass получить легко список полей и дальше найти среди них поля с атрибутом. Вот рефлекшен тебе и пригодился.
>>2968212 >Дальше, я бы советовал сделать имя колонки необязательным, чтобы его можно было не указывать, в этом случае класс работы с БД просто возьмет название поля. Зачем 2 раза писать одно и то же? То есть, чтобы было Что ты имеешь ввиду? Ничего не передавать в конструктор объекту-атрибуту? Что вообще будет обозначать атрибут? Его наличие для кода будет означать, что это поле нужно добавлять в БД? Т.е. если у поля есть атрибут Column, то его нужно добавлять в БД? А если нет - то класс работы с БД игнорирует его? >Ты можешь с помощью ReflectionClass получить легко список полей и дальше найти среди них поля с атрибутом. Вот рефлекшен тебе и пригодился >Окей, я попробую. Спасибо за ответ.
Скажите, вот есть API. Как с ним обычно работают? Допустим есть сайт, к примеру ВК, чтобы получить разную инфу нужно обращаться к API. Как делать? Вот юзер открывает мой сайт и я сразу отправляю запрос, далее жду ответ, потом обрабатываю и так последовательно? Т.е. это анальная зависимость от работы чужого сервера? Так делают?
Кто в РФ работает, у вас на работе у всех PHPStorm? Или vs код? Как его добыть? Так неохота вирусню скачивать пиздец на лицензионную винду. Хотя есть вариант под линукс скачать и на виртуал боксе запускать.
В общем основной вопрос - вы все на работах на пхпшторме?
Прочитал на Хабре про библиотеку ajv, которую используют фронтендщики для валидации... Какое у них все осталое, в Симфони десятки валидаторов на любой вкус и цвет, а у них JSON Schema. JSON Schema просто плохо подходит для валидации по бизнес-правилам, она чисто чтобы описать структуру документа и все.
Не ну правда, почитали бы документацию по Симфони и не изобретали свои примитивные велосипеды. В очередной раз убеждаюсь, что в PHP все лучше, чем в мире фронтендщиков и других языков.
Извини, если я неправильно написал аннотации. Сегодня смотрел видео по psalm, узнал что это анализатор кода. Ага! Мне не очень хочется пока что разбираться в утилитах по php. Прости, пожалуйста, да, я очень плохой человек, мне очень стыдно. Просто ты из поста в пост пишешь, что мне следует этому научиться, а я ленюсь.
Ну и как раз вопрос (раз затронулась тема комментирования кода), мб мне нужно описывать структуру массива с помощью атрибутов? Я не знаю еще как. И, возможно, с многомерными массива будет проблема.
Сегодня я вернулся к DIContainer и решил реализовать в нем интерфейс ArrayAccess.
Если почитать мануал php и посмотреть на эту сигнатуру: public offsetSet(mixed $offset, mixed $value): void
Я точно знаю, что ключами своего контейнера хочу видеть строки (строками, мб, и правильнее, я читал текст по ссылке, которую ты скинул про PSR-11, ну и да, там был метод has(), на который я забил, это настолько критично?), но тут указывается mixed тип, должен ли я делать проверку (на тип) в начале метода и выкидывать исключение?
Это первый момент, а вот второй: Интерфейс обязывает иметь этот метод: public offsetExists(mixed $offset): bool Как мне следует его реализовывать, держа в уме, что у меня 2 свойства-массива - для зарегистрированных сервисов и для созданных объектов?
>>2970247 >Сегодня я вернулся к своему велосипеду и решил добавить в него гребной винт и якорь. >я читал текст по ссылке, которую ты скинул про сборку велосипедов, там была цепь, на которую я забил, это настолько критично? >но упоминается руль, должен ли я делать рулевое колесо с гидроусилителем или без? >Если что это черновик. >Я старался добиться только нужной мне функциональности.
Напишите кто-нибудь пример вызова setcookie с httpOnly, где только 4 параметра вбиты: имя, значение, время жизни и httpOnly. Я читал мануал по php, но у меня какая-то хуйня. Вообще не получается перевести httpOnly в true. Написал код с чужого примера, оставив только параметры name, value и время жизни с httponly: setcookie('test-1', 'Значение 1', array( 'expires' => time() + 60 60 24 * 30, 'path' => '/', 'domain' => 'example.com', 'secure' => true, 'httponly' => true, 'samesite' => 'None' )); сразу все заработало, как написать тоже самое, но только без массива?
Ситуация: нужно вывести php-переменные в html-коде. Значения хранятся в объектах. Не хочу в html прописывать логику получения значения из объекта. Хочу сделать класс ViewHelper, который будет преобразовывать объект так, чтобы в html его можно было вывести просто как переменную или элемент массива.
Дополнение: чтобы получить значение из объекта, мне нужно писать разные инструкции для каждого объекта, поэтому и возник вопрос.
Какой вариант лучше (мб анон предложит свой вариант)?
Первый вариант: Сделать 2 метода в одном классе: преобразоватьОбъект1, преобразоватьОбъект2. Мне не нравится этот вариант, потому что теряется универсальность класса.
Второй вариант: Сделать 2 класса ViewHelper: для Объекта1, для Объекта2 с соответ. методами. Мне не нравится этот вариант, потому что метод-то один, как-то тупо создавать класс для одного метода?
Третий вариант: Сделать просто 2 функции, не методы.
Никто не хочет перекатить тред? Я могу подготовить пикчи для шапки. Сам я перекатывать не очень хочу, никогда такое не делал, вдруг облажаюсь :) Ну и да, я хуй знает как тут перекат работает, перекатывает определенный человек или тот, кто хочет и может.
Вот кто просит симфони инициализировать гит-репозиторий? Я наверно сам решу нужен он мне или нет, а если да, то какой именно. Сейчас целый час потерял, чтобы исправить свой репозиторий из-за симфони-говна. Пришлось выковыривать этот кусок кала жесткими гит-командами и по новой создавать проект на симфони. И они ещё считают свой фреймворк минималистичным! Тот же Ларавель тащит кучу пакетов с собой, но в гит не лезет.
Ты наверно ставил через create-project или что-то подобное, а можно же просто вручную нужные пакеты поставить через композер.
А так вообще все эти заготовки проектов и установщики дурацко сделаны. Всегда надо ставить минимум всего, а не включать фичи, которые могут не понадобиться.
Ну кстати я слышал, у фронтендщиков еще хуже, там при установке какого-нибудь vue тебе предлагают кучу ненужной фигни заодно уставовить, прямо как мейл ру в свое время.
Для PHP есть библиотеки проверка по JSON Schema, но никто в здравом уме не будет использовать это для бизнес-логики. JSON Schema придумана, чтобы проверить, что данные имеют нужную структуру, а не то, что при заполнении заявки на кредит ты ввел имена и телефоны не менее 5 близких родственников.
> Сегодня я вернулся к DIContainer и решил реализовать в нем интерфейс ArrayAccess.
Почему бы и нет.
> но тут указывается mixed тип, должен ли я делать проверку (на тип) в начале метода и выкидывать исключение?
Можно, но не должен.
> public offsetExists(mixed $offset): bool > Как мне следует его реализовывать, держа в уме, что у меня 2 свойства-массива - для зарегистрированных сервисов и для созданных объектов?
ArrayAccess позволяет получать и задавать значения по ключу. Ты должен сам подумать, что у тебя тут будет ключом и значением. Можно, например, получать сервисы по имени, тогда offsetExists проверяет, зарегистрирован ли сервис, но тогда надо запретить их менять через ArrayAccess.
Вообще этот интерфейс придуман для классов, которые реализуют какую-то коллекцию, хранилище чего-то.
> Собственно вот какой код DIContainer у меня получился:
Я тут вижу некоторую нелогичность, что ты записываешь в контейнер функции, а получаешь из него по тому же ключу объекты. Мне кажется, это будет запутывать при чтении кода.
>>2968794 Кстати, подниму вопрос этого анона, самому интересно. Я пока ещё не работаю, только учусь, а в качестве редактора кода использую SublimeText. Однако понимаю что в будущем придётся накатить PHPStorm, но не знаю как это сделать. У самого тоже винда. С меня нихуя тонны нефти и цистерна чая, если кто скинет гайдик какой.
>>2973450 Мб ты не дожидался ответа? Тут регулярно отвечали только два человека. Один из них ОП. Он обычно всем отвечает, но раз в какой-то промежуток времени.
>>2973272 Спасибо за ответ. Как считаешь, должен ли я считать ReflectionClass зависимостью, если без него не работает мой класс? Почти не работает. Ну, конечно, можно было бы обойтись и без рефлексии, если бы я прописал где-то в классе список полей.
Почему у РНР всё так плохо с литературой? В той же джаве пишут фундаментальные книжки по языку, где Java Core разобрана от А до Я. В РНР же вечно какая-то мурзилка из разряда РНР + MySQL + JS + HTML на 400 страничек, где галопом по европам рассказывают верхушки. А хотелось бы книжку, где копают в глубь, чтоб прям разбирались неочевидные моменты, а также обзор сопутствующих тем типа композера, PSR, модель памяти, как там переменные живут внутри РНР, сборка мусора, JIT и т.п.
Что наиболее адекватно использовать, если я хочу по сути List, т. е. массив с ключами [0 ... n] идущими с шагом +1? Есть SplFixedArray, но у него, как я понимаю, основная фича в том, что он Fixed. Можно, конечно, постоянно при добавлении нового элемента вызывать setSize(), но это звучит как-то наркомански. Или я действительно должен сделать свой класс, реализующий интерфейс ArrayAccess, и в методе offsetSet() падать, если переданный ключ не является [0 ... n] с шагом +1? А встроенного SplList или чего-то типа нет?
Если у класса приватные свойства, есть геттер и сеттер, но я получаю значение с помощью рефлексии, то я делаю плохую вещь? Получается я нарушаю инкапсуляцию? И как-то нелогично получается - где-то в коде я получаю значения через геттер, а где-то через рефлексию?
Рефлексия оставлена для тех случаев, когда геттер и сеттер использовать нельзя или неправильно. Например: отладчик, показывающий содержимое объектов. Библиотека, которая добавляет к объектам новый фукнционал.
Тебе почти никогда не надо ее использовать.
Например, она используется в ORM Doctrine для создания объектов и установки им свойств.
Нет, ReflectionClass мы не будем считать зависимостью. Тут конечно получается некоторая путаница, но в конструктор обычно передают:
- классы-сервисы - классы с какими-то настройками, которые можно менять (поэтому их передают снаружи, чтобы мы могли менять эти настройки) - классы, которые можно подменить на другие
ReflectionClass под эти критерии не подходит, так как это встроенный в PHP класс и вряд ли мы будем заменять его на другой, и у него нет каких-то настроек, которые можно менять.
Вижу, что объяснение в статье про DI не очень понятное и логичное ... но пока не знаю, как его можно переписать.
>>2962734 Как-то по коллежу делал свой мини говнофреймворк mvc, там маршрутизация работала. Но по принципу {method} /[controller]/path И затем есть методы контроллеров, которые имеют аттрибуи HttpGet и так далее, а в них указан path соответствующий
Там магия рефлексии юзалась) Можешь подумать об этом