Это копия, сохраненная 10 марта в 20:54.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.
Это тред для начинающих. Слово «классы» у тебя ассоциируется только со школой, а в аттестате тройка по математике? Ты наш человек.
Предыдущий тред был тут: >>2875886 (OP) . Старые треды тут https://2ch.hk/pr/arch/ (М) (искать по слову php).
С чего начать - основы PHP
Наши уроки по PHP собраны по адресу http://codedokode.github.io/phpbook . Это учебник для изучающих с нуля. Там есть задачи, их нужно решать. Но если этот учебник тебе не нравится, можно читать любой другой. Или официальный справочник ( https://www.php.net/manual/ru/langref.php ). Или все сразу.
Если что-то непонятно, запости код и попроси подсказку или поищи задачу в архиве тредов.
Какой редактор использовать
Простые задачки можно решать в онлайн-песочницах вроде https://onlinephp.io/ , https://paiza.io/en/projects/new?language=php , https://www.programiz.com/php/online-compiler/ , но для программ посложнее лучше установить редактор. Есть (дорогая) IDE PhpStorm, есть бесплатный Netbeans и VSCode, условно-бесплатный Sublime Text. Чтобы в последних получить автодополнение для PHP, нужно установить и настроить PHP language server.
Вот инструкции по установке PHP на компьютер: https://github.com/codedokode/pasta/blob/master/soft/php-install.md
Гайд по командной строке: https://github.com/codedokode/pasta/blob/master/soft/cli.md
Что изучать дальше
Зная лишь основы PHP, сайт ты не сделаешь и работу не найдешь. Обычно от начинающего требуют чуть-чуть больше:
PHP, ООП, основы HTTP, HTML/CSS (основы верстки), JS, SQL, PDO, MVC, git, composer, какой-нибудь фреймворк (Laravel или Symfony), основы автоматического тестирования, основы linux, английский.
Вот неофициальный роадмап (карта того, что можно изучать): https://miro.com/app/board/o9J_lbUUBBQ=/
По многим из этих тем у нас есть уроки или задачки:
- для понимания, что такое веб-сервер, прочти урок https://github.com/codedokode/pasta/blob/master/soft/web-server.md
- для понимая MVC, работы с БД и формами, реши задачу про студентов, в ней много полезных советов: https://github.com/codedokode/pasta/blob/master/student-list.md
- далее есть более сложная задача сделать файлообменник на микрофреймворке Slim: https://gist.github.com/codedokode/9424217
- задача, близкая по сложности к реальным задачам на Laravel/Symfony: https://gist.github.com/codedokode/8733007
- после нее можно изучать автоматизированное тестирование https://gist.github.com/codedokode/a455bde7d0748c0a351a
- если ты все решил, переходи к Symfony или Laravel
- почитать про паттерны можно тут https://designpatternsphp.readthedocs.io/ru/latest/ (если ты не изучил ни одного фреймворка, то это будет рановато). Если хочешь увидеть примеры использования паттернов в реальном коде - ковыряй исходники Симфони, например Symfony Forms. Ну и скажем честно, начинающему без опыта, который не видел сложный код, паттерны понять будет сложно.
- для улучшения английского можно читать news.ycombinator.com - там много статей на тему IT.
Также, у нас есть задачи которые позволят тебе изучить или подтянуть до нормального уровня знания JS/HTML/CSS/SQL. Решай их параллельно с задачами выше.
- задачи на HTML/CSS: https://github.com/codedokode/pasta/blob/master/html/html.md
- хороший учебник по JS: https://learn.javascript.ru/
- задачи на JS: https://gist.github.com/codedokode/ce30e7a036f18f416ae0
- задача на SPA (сложно): https://github.com/codedokode/pasta/blob/master/js/spa.md
- проверялка решений на JS: http://dkab.github.io/jasmine-tests/
- задачи на SQL: https://github.com/codedokode/pasta/blob/master/db/databases.md
Что еще почитать
- Мануал по PHP — http://www.php.net/manual/ru/langref.php
- https://phptherightway.com/
- Книга: Профессиональное программирование на PHP Джордж Шлосснейгл
- Книга: Мэтт Зандстра — PHP: Объекты, шаблоны, методики программирования
- Про Git: https://git-scm.com/book/ru/v2
- Задачи на алгоритмы: https://codeforces.com/problemset
Дополнительно
- скачать учебник: зайди на https://github.com/codedokode/phpbook, нажми зеленую кнопку Code -> Download ZIP, распакуй на рабочий стол и открой index.html
- что будут спрашивать на собеседовании, если 0 опыта - будут гонять по теории, по официальному мануалу PHP, давать дурацкие задачки на переворачивание строк, гонять по SQL (транзакции, внешние ключи, напиши запрос), по JS (как сделать анимацию при нажатии кнопки), ну погугли, не ленись
- сколько времени надо изучать все это? - все зависит от тебя, в районе 12-24 месяцев
Да и вообще зачем он нужен...
Я ничего сложнее списка студентов не делал, так что не понимаю.
Много нетерпеливых, которых хлебом не корми, дай только скорее перекатить тред, со сломанной шакой.
>>2932675
В куках он нужен только когда ты хочешь в одну куку поместить сложные данные, а иначе не нужен. А так JSON используется для передачи данных на сервер и с сервера, например, при AJAX запросах.
В куке можно хранить только строку. JSON это объект в виде строки.
Как можно теперь шторм поставить себе? Даже купить теперь нельзя, с впном на жетбрейнс не заходит.
Качай с офф сайта, потом ставь вот эту хуйню чтобы за лицухой он наружу не лез https://github.com/libin9iOak/ja-netfilter-all
Обновляться будет как официальный, это по сути даже не кряк.
Я понимаю что сложно, инструкция то не для пхп треда писалась
>1. add -javaagent:/path/to/ja-netfilter.jar=jetbrains to your vmoptions
>/path/to/ja-netfilter.jar
>ja-netfilter.jar
>Как научиться докеру и композеру и поставить код сниффер или другой линтер?
В смысле как? Читаешь и выполняешь на практике. Лучший обучающий ресурс на мой взгляд - Хекслет. Начни бесплатный курс по основам PHP https://ru.hexlet.io/courses/php-basics
>Я напомнил где качается весь платный софт последние лет двадцать.
С торрентов с вирусами качать? Если только в виртуалке юзать.
Человек спросил про докер и композер, а ты тут торопишься вставить рекламу платных PHP курсов. Ты бы хоть вопрос прочел.
Он спросил как научиться. Не только докеру и композеру. Я дал не рекламу, а бесплатный курс, большой хороший бесплатный курс. Была бы реклама, вставил бы реферальную ссылку. И еще, как в прошлом посте написал, считаю этот ресурс лучшим по всем показателям.
Там нет ничего про докер и композер. Ты дал ссылку на базовый курс, который проходится за пару часов.
>базовый курс, который проходится за пару часов
Ебанат, там 42 урока и 47 практических упражнений. Ты за 2 часа даже теорию не успеешь прочитать, а уж новичок и подавно. Там есть про PSR и линтеры. И вообще, пошел на хуй, умник ебаный, не для тебя ссылка.
Там про PSR и линтеры 2 строчки написано, а основного - как всё это скрестить с докером и компотом нет. Про "что это такое" я и в википедии прочитаю без твоего ебучего курса говна.
Оформи-ка ты срыг, маркетолог хуйни.
Как будто мне надо учиться, а не тебе. Хорошо, что такие дураки сразу отсеиваются. Между прочим, это единственное место где есть курс СИКП на PHP и еще некоторые по теме абстракции данных. Хотя тебе это знать бесполезно.
Иди говна наверни, как ты любишь.
Значения в свойствах должна пройти валидацию.
Должен ли я делать посредника между сущностью и валидатором?
Или же валидатору достаточно напрямую обращаться к сущности через геттер, чтобы брать ее значения и проверять?
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.
Можете дать пример реально работающего кода?
Уже. Вот код, который нормально работает без файлов:
<form name="property-form" id="property-form" role="form" enctype="multipart/form-data">
<input type="hidden" id="token" name="token" value="{{ $token }}">
<input type="hidden" id="id" name="id" value="{{isset($data['id']) ? $data['id']: '' }}">
<input type="text" id="title" name="title" value="{{isset($data['title']) ? $data['title']: '' }}">
<input type="file" name="files[]" multiple>
<input type="file" name="files[]" multiple>
<input type="button" id="save-property-button" value="Save" class="btn btn-primary">
</form>
$(document).ready(function (e) {
$('#save-property-button').click(function () {
let form = $(this).closest('form')
let formData = form.serialize();
$.ajax({
type: 'PUT',
url: '/api/info/'+ $('#id').val(),
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content'),
'Authorization': 'Bearer ' + $('#token').val()
},
dataType: 'json',
data: formData,
cache: false,
success: function (response) {
console.log(response);
},
error: function (xhr, status, error) {
console.log(xhr.responseJSON);
}
});
});
});
public function update(Request $request, int $id)
{
return response()->json([
'$request->all' => $request->all(),
'$request' => $request,
'$title' => $request->title,
'$title2' => $request->input('title'),
'$id' => $id,
]);
}
Пишут, что в аяксе нужно ставить
contentType: false,
processData: false,
но у меня с ними тоже ничего не работает.
Уже. Вот код, который нормально работает без файлов:
<form name="property-form" id="property-form" role="form" enctype="multipart/form-data">
<input type="hidden" id="token" name="token" value="{{ $token }}">
<input type="hidden" id="id" name="id" value="{{isset($data['id']) ? $data['id']: '' }}">
<input type="text" id="title" name="title" value="{{isset($data['title']) ? $data['title']: '' }}">
<input type="file" name="files[]" multiple>
<input type="file" name="files[]" multiple>
<input type="button" id="save-property-button" value="Save" class="btn btn-primary">
</form>
$(document).ready(function (e) {
$('#save-property-button').click(function () {
let form = $(this).closest('form')
let formData = form.serialize();
$.ajax({
type: 'PUT',
url: '/api/info/'+ $('#id').val(),
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content'),
'Authorization': 'Bearer ' + $('#token').val()
},
dataType: 'json',
data: formData,
cache: false,
success: function (response) {
console.log(response);
},
error: function (xhr, status, error) {
console.log(xhr.responseJSON);
}
});
});
});
public function update(Request $request, int $id)
{
return response()->json([
'$request->all' => $request->all(),
'$request' => $request,
'$title' => $request->title,
'$title2' => $request->input('title'),
'$id' => $id,
]);
}
Пишут, что в аяксе нужно ставить
contentType: false,
processData: false,
но у меня с ними тоже ничего не работает.
И да ты не можешь бинардые данные жсоном отправлять, если хочешь json то надо файлы кодировать в base64 а на бекенде раскодировать. Например в мидлеваре ларки. Но это нахуй не надо.
Извините за такой вкатунский вопрос, но я серьезно не понимаю практического смысла в интерфейсах.
Код без интерфейсов это плохой ООП-код?
Если я формирую данные для отправки так:
const form = document.querySelector("#property-form")
const formData = new FormData(form)
, то бинарник появится. В консоле браузера, да. А ларавел его не видит. Вообще никакие поля. Все данные пстые. Файла нет, текстовых полей нет.
Ты используешь метод PUT, а PHP анализирует тело запроса только для метода POST, если я не путаю. Попробуй PUT на POST переделать.
Анон, СПАСИБО!
Реально PUT не анализируется. Передаю постом с добавлением formData.append('_method', 'PUT') - все данные проходят на бек нормально. А я почти три дня ебался. Браузер говорит "мамой клянусь, я отправил!", а ларавел "мамой клянусь, я не получал!". И так почти три дня. Я уже начал подумывать, что с ума сошёл. Перегуглил всё что можно, даже ютуб смотрел от индусов и французов каких-то.
Только вот три класса - проверка на пустоту, на мин. и макс. количество символов будут почти похожи. Получается, что я дублирую код, пусть и частично (вся ведь проверка заключается в том, чтобы узнать сколько символов в строке и сравнить с каким-то стандартом). С одной стороны вроде как это разные проверки, но руки тянутся скинуть все это в одну кучу. Как мне поступить, знающие аноны?
Когда говорят о "дублировании кода" говорят о "дублировании функционала". Причем именно "бизнес функционала". Ты же не сидишь и не думаешь: "ах бля беда какая я дублирую использование сложения и объединения строк, я ДВАЖДЫ написал foreach, мне пизда" ну я на это надеюсь
И вообще ты сидишь и думаешь о хуйне. У тебя будут сотни классов и тысячи методов, нет никакого смысла их считать. Важно чтобы их было удобно читать. Сам факт что что у тебя есть код, который явно выделен как "проверка строки на пустоту" важнее всего остального.
Интересно https://www.php.net/manual/en/features.file-upload.put-method.php
При отправке данных PUT методом они будут доступны как ресрурс php://input
Но нет суперглобала в в который эти данные парсятся
В тоже же время при отправке с multipart/form-data php://input недоступен
https://www.php.net/manual/en/wrappers.php.php
Соответственно нужен заголовок Content-Type: application/x-www-form-urlencoded
О том что такое поведение баг спорят с 2011 https://bugs.php.net/bug.php?id=55815
Типа есть оправдание, что в пыхе все строго RFC
POST - это для процесса обрабатывающего данные. URI идентифицирует обработчик
PUT - для размещения файла на сервере. URI идентифицирует сам ресурс
Соответственно пут не должен парсить (обрабатывать) данные,
мимо крок
Окей...
Просто у меня есть класс, проверяющий на пустоту
Он считает количество символов и сравнивает с нулем.
Другой класс проверяет на минимальное количество символов. Он считает количество символов и сравнивает с...минимальным значением для этого поля.
Следующий класс проверяет на максимальное количество символов. Он считает количество символов и сравнивает с максимальным значением символов для этого поля.
Немного в глаза бросается эта общая схожесть действий.
>Просто у меня есть класс. Он использует умножение.
>Просто у меня есть класс. Он использует перебор массива.
>Просто у меня есть класс. Он использует array_sum.
Что такого особенного в этом "подсчете символов"? Там какие-то специальные правила, про которые знает только написавший этот подсчет?
Допустим там ебейшее дублирование, страшное. Как по твоему это решается? Созданием еще одного класса, в который помещается дублирующаяся логика. Так что классов у тебя один хуй станет только больше.
Да, я думал сделать один класс и назвать "Проверка на количество символов", а там уже были бы свои методы на пустоту, на минимальное количество и максимальное.
Но правильно ли это? Сваливать все три проверки в одном классе.
назови свой класс ValidationHelper и пихай туда все проверки
Ты по прежнему не понимаешь что такое ООП. Объекты это данные + способы работы с этими данными. А у тебя все разбросано. Раз строка содержит в себе буквы, которые надо посчитать. Так создай строку, которая это умеет: https://3v4l.org/XhAE2
А потом учишь свой валидатор работать с умными строками: https://3v4l.org/jGPb4
Ты вместо того чтобы создать объекты с соответствующей этим объектам структурой и поведением используешь их как папки для хранения процедур. Очевидно что при таком подходе тебе от ооп никакой пользы, одни заебы и беготня между шкафчиками и папочками.
Я не знаю что мне делать.
Я начал решать задачу со списком студентов месяц назад и до сих пор я нахожусь на этапе проектирования классов.
И ничего мне не помогает, ни советы анонов из треда, ни книги (читал Банду Четырех, книгу Зандстры). Все равно как дурачок топчусь на одном и том же месте.
Спасибо за код, я разберу его завтра и постараюсь понять какую мысль ты хочешь до меня донести.
Я пробовал менторить вкатунов из треда.
Правило одно единственное было: быть в определенное время, а если не получается - предупредить заранее. Казалось бы хуль еще надо? Да только хуй там. Проебываются постоянно. В лучшем случае за пять минут до созвона сказать что "ну бля никак" и похуй что ты свои дела под это время двигал. А в худшем просто молча проебаться. Короче дело неблагодарное.
Понимаю.
Ну вообще, тогда можно не делать класс проверки на пустоту, или сделать так, чтобы он наследовал класс проверки с мин. числом символов с условием, что там будет минимум 1 символ:
class NotEmptyValidator extends MinLengthValidator
{
...
parent::__construct(1);
Нет, ты даешь плохую идею. В PHP уже есть тип string. Не нужно создавать новый вид строк, так как у тебя в приложении будет 2 вида строк и куча возни с преобразованиями туда-обратно.
Докер
Чтобы нормально изучить докер, тебе надо начать с изучения основ Линукса и работы в командной строке (так как Докер-файл это по сути набор консольных команд). Ищи любой курс, хоть текстовый, хоть видео, по этой теме. Команды для работы с файлами, перенаправление ввода-вывода, управление пользователями и правами, менеджер пакетов.
Если ты не знаком с командной строкой, то тут написано что это (но это не отменяет необходимость учить Линукс): https://github.com/codedokode/pasta/blob/master/soft/cli.md
После освоения Линукса можешь читать любой туториал по Докеру. Должно быть понятно. Докер создает пустой контейнер (что-то вроде виртуальной машины), в который ты с помощью команд устанавливаешь нужный софт.
Композер
Тут все проще - либо читай документацию на англ., либо ищи туториалы.
> поставить код сниффер
С помощью команды composer require (название пакета)
> Какие пср обычно используются? Надо ли их как-то запоминать или хватит линтера?
PSR пока не много, я советую быстро по диагонали (не заучивая) прочитать все в статусе Accepted: https://www.php-fig.org/psr/
Для оформления кода надо тщательно прочесть PSR-1 и PSR-12.
> Есть норм гайд с разжёвыванием где как и чего.
Нет, но ты можешь задавать уточняющие вопросы.
Докер
Чтобы нормально изучить докер, тебе надо начать с изучения основ Линукса и работы в командной строке (так как Докер-файл это по сути набор консольных команд). Ищи любой курс, хоть текстовый, хоть видео, по этой теме. Команды для работы с файлами, перенаправление ввода-вывода, управление пользователями и правами, менеджер пакетов.
Если ты не знаком с командной строкой, то тут написано что это (но это не отменяет необходимость учить Линукс): https://github.com/codedokode/pasta/blob/master/soft/cli.md
После освоения Линукса можешь читать любой туториал по Докеру. Должно быть понятно. Докер создает пустой контейнер (что-то вроде виртуальной машины), в который ты с помощью команд устанавливаешь нужный софт.
Композер
Тут все проще - либо читай документацию на англ., либо ищи туториалы.
> поставить код сниффер
С помощью команды composer require (название пакета)
> Какие пср обычно используются? Надо ли их как-то запоминать или хватит линтера?
PSR пока не много, я советую быстро по диагонали (не заучивая) прочитать все в статусе Accepted: https://www.php-fig.org/psr/
Для оформления кода надо тщательно прочесть PSR-1 и PSR-12.
> Есть норм гайд с разжёвыванием где как и чего.
Нет, но ты можешь задавать уточняющие вопросы.
> Должен ли я делать посредника между сущностью и валидатором?
Нет, ты усложнишь все.
> Или же валидатору достаточно напрямую обращаться к сущности через геттер, чтобы брать ее значения и проверять?
Достаточно. Но для этого, конечно, валидатор должен знать имена полей и правила, которые к ним надо применить.
А вот класс-правило никаких сущностей знать не должен. Валидатор передаст ему нужное значение напрямую.
Урок: https://github.com/codedokode/pasta/blob/master/php/interfaces.md
Основная идея: с помощью интерфейса ты можешь указать требования к классу. И писать код, не имея этого класса либо имея несколько вариантов реализации интерфейса.
Ну например: тебе надо написать код для регистрации с подтверждением по СМС. Но класс отправки СМС поручили писать другому человеку, и хуже того, он его еще не написал.
Ты можешь сделать интерфейс:
interface MessageSender {
public function send(string $phone, string $message);
}
И затем ты даешь свой интерфейс своему коллеге и говоришь: программировай код отправки СМС. А сам пишешь свою часть кода. И если твой коллега не совсем тупой, то когда он напишет класс отправки СМС, то он будет идеально работать с твоим кодом.
Конечно, ты бы мог объяснить требования к классу словами, без интерфейса, а потом сам вручную их проверить. Но зачем это делать, когда PHP может проверять требования за тебя.
Вторая польза от интерфейсов. Допустим, ты хочешь у себя протестировать свой код. Но отправка СМС платная, и тебе не дали ключ доступа к сервису отправки СМС, и вдобавок коллега еще не доделал свой код. Ты можешь написать свой класс, который соответствует интерфейсу, но вместо отправки СМС просто пишет их в файл. И ты у себя тестируешь код с этой заглушкой, а на продакшене используешь реальный отправщик СМС.
Дальше, может вы решите подключиться к нескольким сервисам отправки СМС. И тут опять интерфейс пригодится, так как твоемукоду без разницы, какой класс используется, лишь бы он соответствовал интерфейсу.
Интерфейсы также часто используют в библиотеках и фреймворках, когда автор хочет дать возможность заменять какие-то компоненты. Тогда он делает для этих заменяемых компонентов интерфейсы. Ну например, есть open-source движок для форума, и мы хотим сделать возможность заменять в нем капчу. Мы можем сделать интерфейс для класса генерации и проверки капчи, и тогда пользователь сможет сделать свою собственную капчу, не меняя код движка.
Урок: https://github.com/codedokode/pasta/blob/master/php/interfaces.md
Основная идея: с помощью интерфейса ты можешь указать требования к классу. И писать код, не имея этого класса либо имея несколько вариантов реализации интерфейса.
Ну например: тебе надо написать код для регистрации с подтверждением по СМС. Но класс отправки СМС поручили писать другому человеку, и хуже того, он его еще не написал.
Ты можешь сделать интерфейс:
interface MessageSender {
public function send(string $phone, string $message);
}
И затем ты даешь свой интерфейс своему коллеге и говоришь: программировай код отправки СМС. А сам пишешь свою часть кода. И если твой коллега не совсем тупой, то когда он напишет класс отправки СМС, то он будет идеально работать с твоим кодом.
Конечно, ты бы мог объяснить требования к классу словами, без интерфейса, а потом сам вручную их проверить. Но зачем это делать, когда PHP может проверять требования за тебя.
Вторая польза от интерфейсов. Допустим, ты хочешь у себя протестировать свой код. Но отправка СМС платная, и тебе не дали ключ доступа к сервису отправки СМС, и вдобавок коллега еще не доделал свой код. Ты можешь написать свой класс, который соответствует интерфейсу, но вместо отправки СМС просто пишет их в файл. И ты у себя тестируешь код с этой заглушкой, а на продакшене используешь реальный отправщик СМС.
Дальше, может вы решите подключиться к нескольким сервисам отправки СМС. И тут опять интерфейс пригодится, так как твоемукоду без разницы, какой класс используется, лишь бы он соответствовал интерфейсу.
Интерфейсы также часто используют в библиотеках и фреймворках, когда автор хочет дать возможность заменять какие-то компоненты. Тогда он делает для этих заменяемых компонентов интерфейсы. Ну например, есть open-source движок для форума, и мы хотим сделать возможность заменять в нем капчу. Мы можем сделать интерфейс для класса генерации и проверки капчи, и тогда пользователь сможет сделать свою собственную капчу, не меняя код движка.
О, круто сделано. Может, нам стоит собрать список подобных сайтов и где-то вверху следующего треда повесить?
Можно, кстати, сделать единую проверялку длины с параметрами:
- минимум (можно указать 0 для пропуска)
- максимум (можно указать \INF - бесконечность - для пропуска)
А на основе этого класса унаследовать класс проверки на пустоту, проверяющий, что длина ≥ 1
Или можно сделать единую проверялку, а от нее унаследовать 3 правила: не пусто, длина не меньше X, длина не больше X.
Как я понимаю, идея такая, что если PHP парсит тело, то оно недоступно (так как PHP его прочел и распарсил). А если не парсит, то доступно в php://input.
Не уверен, что надо, так как проверка очень простая и ты пишешь кучу кода, чтобы избежать дублирования одной строчки.
Ну общение в треде тем и удобнее, что каждый приходит когда хочет. Или, что чаще, задает вопрос и уходит в закат.
>будет 2 вида строк и куча возни с преобразованиями туда-обратно
>__toString
>будет 2 вида строк
Пик релейтед.
>Нет, ты даешь плохую идею. В PHP уже есть тип string.
Ну вот и как учить ооп, когда вокруг дегенераты? То есть джаву и шарп идиоты писали? Почему им нужно, а тебе не нужно?
https://learn.microsoft.com/ru-ru/dotnet/api/system.string?view=net-8.0
https://docs.oracle.com/javase/8/docs/api/java/lang/String.html
То есть ты берешь их концепты, их паттерны, их подходы, но совершенно не понимаешь что и для чего делается. Это даже не карго культ, карго культисты знают чего хотят, просто не знают что по наушникам из кокоса нельзя получить ништяки. Но цель-то ништяки получить у них есть. А тут даже нет понимания что должно получиться в итоге, нахуя все это ооп затевалось, цель этих смешных классов какая.
>Ну общение в треде тем и удобнее
Ну если цель отвлечься на пару минут, шуткануть или пернуть что-то одной строчкой, то да "удобнее".
А если цель чему-то научиться, что-то понять, то нужно сидеть с открытой ide и буквально вместе код писать. И даже так не с первого раза доходит.
Продублирую из прошлого треда.
На Битриксе всегда потогонка ебаная? Вкатился пару месяцев назад что-то обомлел. Задачи заранее оценены лидом - 5 часов, 7 часов и т.д. Я не укладываюсь совсем, приходиться перерабатывать, могу несколько дней просидеть. Плюс прилетают задачи на джсе. Короче работа в авральном режиме, из-за этого куча стресса.
Зацените плес код. Как его можно улучшить по части веб-безопасности?
https://pastebin.com/eWMnLGC5
>Как его можно улучшить по части веб-безопасности?
Избавиться от массивов и уебанских названий типа atts и esc. Если для этого придется выкинуть к хуям вордпресс - тем лучше для безопасности человечества.
Докер состоит из 2 частей: демон, который работает в фоновом режим, и клиент (это как раз команда docker, которую ты запускаешь). У тебя наверно не запущен демон.
Протестируй, что будет, если в ссылку и текст вставить символы вроде <, >, &, ', ". Будут ли они экранироваться или вставятся как есть? Если второе, то это плохо.
Также, протестируй, можно ли вставлять ссылки вроде javascript:alert(1); или data:12345. Если можно вставлять что-то, кроме http:// и https://, то это тоже уязвимость.
>>2938525
Я уже расписывал как вкатился в прошлых тредах. Прошел курс Лаврика с торрентов, сделал задачку про Вектор, потратил кучу времени на поиск нормальных курсов по ооп. Проебал чуть не год на все это. Потом какой-то анон в этом треде отписался, что пошел учиться на Хекслет по подписке - я слышал про них, но не знал, что есть подписка. Ну пошел туда, оказалось годнота. Но это было за неделю до начала "сво". Просидел еще год, обосрасля с поиском работы на ларе и пришлось идти в битрикс, се ля ви
Как понять, определенный метод лучше оставить в классе-сущности или вынести метод в отдельный класс?
Есть сущность, в которой хранятся данные из БД (т.е. в объекты этой сущности выгружаются данные из БД).
Есть класс, который обслуживает эту сущность (реализует SQL-запросы).
В списке студентов код работает с данными об абитуриенте.
Сначала он их хранит в сущности Enrollee (мое название).
Значения свойств этой сущности проверяет Валидатор.
Если ошибок нет, данные загружаются в БД.
Ага...с вводом информации в БД понятно, а вот с выгрузкой информации из БД...
Чтобы выгрузить из БД, нужно будет создавать объекты, которые будут хранить эту информацию из БД и тут вопрос...
А что будет этой сущностью? Тот же класс Enrollee (ведь он представляет абитуриента в коде)? Или же нужно создавать отдельный класс-сущность для выгрузки из БД?...
Я бы не стал повторять за этим челом, ты что не видишь как он не смог вкатиться в ларавель и в итоге страдает на битриксе?
Стоит зодуматься...
И еще с кое-чем смежным я путаюсь.
Если у меня есть DIContainer и он один раз создает объект, то как буду создавать эти объекты, которые будут храниться данные из БД, ведь DIContainer только один раз создает объект.
Ага, да...
Простите за глупые вопросы, самому стыдно от своей тупости перед анонами.
Почти все из профессии по пхп, начиная с фунций. По ларе там действительно инфы маловато, в дополнение можно посмотреть ларакасты, повторить пару проектов.
Делал последний проект из профессии, прямо чтоб "свои" не писал
>Нужно детали выяснить
Я опираюсь на знания о DataMapper из этой статьи:
https://gist.github.com/codedokode/c4cbc4d7dc8e45ea074a
Мб тогда мой пост станет понятным.
>Не до конца понимаю 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 и работают асинхронно с твои кодом.
>Не до конца понимаю 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 и работают асинхронно с твои кодом.
>Если у меня есть DIContainer и он один раз создает объект, то как буду создавать эти объекты
Di container создает объекты, которым для работы нужны другие объекты или эти объекты нужно как-то сконфигурировать перед использованием.
Твоим объектам из хранилища все это надо? Конечно нет, никому это не надо. В модели все зависимости инжектятся через методы, а не через конструктор.
Да?
А я думал, что DIContainer нужен для того, чтобы инструкции с созданием объекта были в одном месте...
Не претендую на истину, только рассказываю свое понимание вещи, чтобы опытные аноны указали на ошибки (видимо придется постоянно писать эту фразу, иначе аноны будут агриться и думать, что я херь несу с умным лицом)
Ну вопрос остается открытым. Твоим объектам из базы нужны инструкции для создания?
Та же доктрина конструктор вообще не вызывает если че.
Да, я не знаю ничего.
У меня даже ни одного класса для работы с БД нету.
Я только сегодня прочитал статью codedokode про паттерны.
В голове каша пока что, думал что-нибудь проясню, задав вопрос, но не особо.
Завтра на свежую голову прочитаю то, что ты описал про Action Record, DM, TDG.
Кароче, я эту шизу часа 4 устанавливал, порадовался в одном чатике, о том что установил и мне сказали что это гавно и забанили.
У меня виртуализация на винде так и не заработала. Вообще. И уж поверь я сидел с парнями которые больше десяти лет админят и нихуя не вышло. WSL это кал, сынок. Это дерьмо не стоит времени, котрое придется на него убить. Просто поставь линукс.
у меня обратная ситуация, я пытался установить линукс больше недели, но нихуя не вышло и я решил забить, потом я чета вспомнил про всл и поперло
Звучит как "я пытался снять свитер в течении недели, а потом понял что можно срать не снимая свитер".
Бубунта или и мята ставится с любой флешки на любое железо уже лет десять как.
>я сидел с парнями которые больше десяти лет админят и нихуя не вышло
То что они 10 дет дрочат домен не говорит что они умеют в Hyper-V или WSL. Зачастую они и в домене нихуя не умеют по итогу, и делают всё как в методичках мелкомягких.
Пчел. 90% бухгалтеров работают через виртуалки со старым софтом. У нас в офисе больше тысячи сотрудников. Админы на виртуализации виндовской собаку съели. И через них прошли тысячи разных кампуктеров и ноутов с тысячами конфигураций.
Если ты такой дохуя умный, то давай сконтачимся, застримлю тебе рабочий стол ты будешь говорить что делать. Потом запостишь сюда чем дело кончится.
Я дрочил secure boot, boot legcy, fast boot.
Я делал разметку через MBR и GPT.
Пробовал все доступные файловые системы.
Попробовал разные флешки, разные дистрибутивы.
И НИ-ХУ-Я
Самое обидное что, создав флешку с параметрами по умолчанию через руфус, и вставив её в другой комп которому миллиард лет, мне просто пришлось поставить ее первой в загрузчике.
И ВСЁ БЛЯДЬ ВСТАЛО НАХУЙ
Начнем с того что линукс можно запустить прямо с флешки. Ты это сделал?
У тебя не установлен какой-то компонент в Windows, нужный для работы виртуальных машин. Я не пользуюсь виндой уже много лет, поэтому даже не знаю какой. Выясни и установи.
И да, Докер в Линуксе лучше работает.
Очень абстрактный вопрос.
Обычно, используют принцип "у каждого класса своя зона ответственности" + ориентируются на существующие примеры кода.
> Есть класс, который обслуживает эту сущность (реализует SQL-запросы).
Это скорее всего TableDataGateway.
> Чтобы выгрузить из БД, нужно будет создавать объекты, которые будут хранить эту информацию из БД и тут вопрос...
> А что будет этой сущностью? Тот же класс Enrollee (ведь он представляет абитуриента в коде)?
Да. Зачем тебе для одного студента делать 2 разных класса? Логично использовать тот же класс.
В контейнере обычно хранят сервисы, а не сущности. Контейнер нужен для двух целей:
- чтобы для каждого сервиса был один экземпляр, например, чтобы все классы использовали один и тот же объект подключения к БД
- чтобы внедрять зависимости при создании сервисов
Сущности обычно в контейнере не хранят.
Это старая версия статьи, тут есть чуть более новая: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Во-первых, забудь про 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 лет, и она просто возвращала для некоторых файлов неправильные данные. Ох, пришлось мне повозиться, я загрузился с флешки, а затем скачал на диск образ устновщика, примонтировал его и устанавливался не используя далее флешку.
Не лезь, оно тебя сожрет. Объем знаний по сравнению с той же ларой просто огромный, документации почти нет, код лютое говнище. Если хочешь залететь по-быстрому, присмотрись к другим направлениям, лучше вообще за пределами разработки
У меня всегда, когда я сажусь заниматься программированием, первые 1-2 часа вата в голове. Есть какие-нибудь советы?
>Что анон делает перед тем, как писать код?
Пиздую по морозу до метро.
Еду в ебучем забитом метро в час пик.
Тащусь по снегу до офиса.
Ползу через пост охраны к лифту.
Еду на лифте на восьмой этаж.
Здороваюсь с пацанами у кикера.
Добегаю до рабочего места.
Чекаю личку, мессенджеры, почту, джиру.
Пишу код.
Всего хорошего, заходите на наш пикабу еще.
Я перечитал твой пост.
И это дало мне почти ничего.
Ты не мог бы подробнее рассказать про DM?
Как он устроен?
Мб даже код.
Если, конечно, есть желание этим заниматься.
Я не настаиваю.
Если будешь расписывать, то помни, что я новичок, который никогда не писал код работы с БД и те вещи, которые понятны тебе, ввиду знаний и опыта, не будут понятны мне.
Все, что я нашел - то, что есть сущность, которая представляет что-то и есть маппер. В маппере есть методы сохранения/загрузки из БД (по этой сущности).
И это представление не соотносится с тем, что ты писал.
Я не в смысле, что ты не прав, а в том смысле, что пока что текстовое описание для меня абстрактно звучит и, пытаясь переложить его на тот код, который я видел из интернета, я не вижу ничего общего.
>И это представление не соотносится с тем, что ты писал.
Что ты считаешь несоответствием я конечно должен угадать.
Обучение это двунаправленный процесс. Тебе дают информацию. Ты отвечаешь учителю как ты истолковал его слова. Учитель говорит что ты истолковал неправильно и указывает на ошибки в рассуждениях. Несколько раз предлагал заниматься таким в дискорде с нормальным фидбеком. Никому не надо.
Кидать в пустоту страницы текста, получая в ответ вместо рассуждений "понял/не понял" предельно тупое и неэффективное занятие. Поэтому вот тебе ссылка, там все расписано https://martinfowler.com/eaaCatalog/dataMapper.html читай. У меня таких ссылок две страницы гугла, будем вести обучение в удобном тебе формате.
Я правильно понимаю, что в коде лучше использовать не название класса, а название его интерфейса (в том случае, если функции требуется объект определенного класса)?
И правильно ли я понимаю, что если интерфейсы двух классов похожи, это еще не означает, что двум классам стоит реализовывать общий интерфейс?
Пример: у меня есть сущность, представляющая абитуриента и есть сущность, представляющая ошибки валидации.
Общий для них интерфейс - геттер и сеттер, потому что я задал им приватные свойства.
Но дав им общий интерфейс и прописав в одной функции, что я принимаю объекты такого интерфейса, я делаю ошибку...?
Почему я так думаю: у меня есть функция, которая принимает объект, представляющий абитуриента.
Если я вместо названия класса-абитуриента напишу его интерфейс, то функция спокойна будет принимать объект, представляющий ошибки валидации, потому что он реализует тот же интерфейс.
Я не считаю себя правым, не нужно общаться со мной так, как будто я пытаюсь что-то доказать. Я просто вкатун-вкатуночек, который пытается усвоить материал и просит для этого корректировку своих мыслей от опытных анонов.
Начинаю оформлять вкат в ваш уютный пхп, чтоб съебать к лету со своей работы
Если не получится отсосу мужику из треда
Аллаху Акбар братья
>Несколько раз предлагал заниматься таким в дискорде с нормальным фидбеком. Никому не надо.
Мне, мб, надо, но вряд-ли я буду общаться голосом.
Но все-таки я не хотел бы, наверное, нагружать настолько чужого человека. Хотя и мои постоянные вопросы в треде - уже значительная нагрузка.
>Поэтому вот тебе ссылка, там все расписано https://martinfowler.com/eaaCatalog/dataMapper.html читай.
Окей, я прочту.
Окей, я прочел...
Я шарил по интернету и читал такой ответ:
DM - Тупо мостик между БД и объектом: считать данные из БД и записать в объект, сохранить объект в БД.
Фактически CRUD. Способ автоматизировать рутинные операции.
Т.е. у меня есть сущность, вокруг которой крутится код, в данной задаче это Абитуриент.
Есть маппер, который берет сущность и сохраняет/загружает данные из БД.
И...все...
Больше я ничего не понимаю в этой теме, если вообще мое понимание правильное.
На собесе/работе будешь языком жестов разговаривать?
Там с тобой церемонится не будут. Стесняешься незнакомого дяди? Так у них очередь из тех для кого это не проблема. Еще и вебку подрубить попросят.
>Я шарил по интернету и читал такой ответ
Не интересно что там в интернете пишут. Давай свои выводы по статье.
>Есть маппер, который берет сущность и сохраняет/загружает данные из БД.
Так а почему он тогда "маппер", а не "сохранятель"?
>Не интересно что там в интернете пишут.
Так то, что там написано, больше всего ближе к понятному для меня. Вот я и привел цитату.
>А чем учеба от работы отличается?
Тем, что с работодателем труднее будет договориться, чем с аноном? Человек, который приходит работать в определенную компанию, обычно принимает условия этой компании, а не предлагает свои (я не отрицаю, что есть возможность предлагать).
>Мне интересно что ты понял из стать и что не понял.
Ты хочешь, чтобы я прямо сейчас разбирал этот вопрос? Я недавно закончил сегодняшнюю учебу и морально отошел от того, чтобы прилагать умственные усилия. Но я могу перечитать и попробовать сформулировать выводы, если ты хочешь.
Вот об этом я и говорю. Мы уже три поста не понимаем друг друга. В войсе это недопонимание разрешилось бы за секунду. Я не буду в третий раз пытаться объяснить одно и тоже.
>Ты хочешь, чтобы я прямо сейчас разбирал этот вопрос?
Я хочу чтобы ты разбирал "этот вопрос", а не "случайный вопрос" из интернета. Ну и разумеется я не хочу терять нить и вспоминать че там кто кому писал десять постов назад на прошлой неделе. Это просто не удобно.
Окей.
В статье написано о том, что представление информации в виде объекта, и предоставление информации в виде БД могут не совпадать.
Это создает какие-то сложности. И чтобы решить эту сложность, нужен DM.
Что я не понимаю, а точнее не представляю, как должны выглядеть объект и таблица, чтобы не совпадать. Т.е. конкретный пример.
>Many parts of an object, such as collections and inheritance, aren't present in relational databases.
Коллекции и наследование.
Я не знаю что такое коллекции.
А когда пытался гуглить, чтобы сформировать хотя бы первичное представление - все равно ничего не понимал.
Ну а наследование, зачем вообще его отображать в БД?
Я думал, в БД просто колонки, где в строчках хранятся значение, зачем БД знать про связи, которые есть у объекта?
>Я не знаю что такое коллекции.
А что такое массив знаешь?
>зачем БД знать про связи, которые есть у объекта?
Затем что потом эти объекты надо из бд восстановить?
>А что такое массив знаешь?
Да.
>Затем что потом эти объекты надо из бд восстановить?
А причем тут наследование?
Недостаточно создать метод ВыгрузитьИзБД и прописать в теле как формировать объект?
Я не знаю.
Я не думал в этом ключе, потому что у меня никакого наследования в коде нет и класс для хранения информации только один (куда я и планировал выгружать данные).
Т.к. я не имел опыт с этой ситуацией, для меня она звучит абстрактно.
Ну можешь попробовать начать с перечитывания вчерашних туториалов. Так как ты их уже читал, то тебе будет легче их воспринимать.
Давай я поясню. Надеюсь, ты изучил ссылку https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Data Mapper - "преобразователь данных" - это штука, которая, судя по названию, должна преобразовывать PHP-объекты в строки таблиц в БД и обратно.
По ссылке, которую дал анон: https://martinfowler.com/eaaCatalog/dataMapper.html есть схема Data Mapper.
Основная особенность маппера - это то, что модель (объект 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.
Давай я поясню. Надеюсь, ты изучил ссылку https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Data Mapper - "преобразователь данных" - это штука, которая, судя по названию, должна преобразовывать PHP-объекты в строки таблиц в БД и обратно.
По ссылке, которую дал анон: https://martinfowler.com/eaaCatalog/dataMapper.html есть схема Data Mapper.
Основная особенность маппера - это то, что модель (объект 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.
Чтобы тебе было понятнее, попробуй полистать примеры кода в документации к Doctrine (это Data Mapper): https://www.doctrine-project.org/projects/doctrine-orm/en/2.17/reference/working-with-objects.html#persisting-entities
Вот кстати по ссылке на Фаулера ничего не написано про хранилище. Там у маппера есть методы insert/update/delete и все.
> в коде лучше использовать не название класса, а название его интерфейса (в том случае, если функции требуется объект определенного класса)?
Ты используешь интерфейс, если хочешь, чтобы можно было передать вместо одного класса другой ("абстрагироваться от реализации").
Ну например, тебе надо сохранять фото. Ты можешь конечно все захардкодить и прибить гвоздями, а можешь сделать интерфейс:
interface PhotoSaver
{
public function save(Photo $photo);
}
И к нему 2 реализации: одна сохраняет на диск, а другая в облачное хранилище Amazon. И в тестах использовать диск, а в продакшене Амазон.
> если интерфейсы двух классов похожи, это еще не означает, что двум классам стоит реализовывать общий интерфейс?
Нет. Интерфейс, это когда есть несколько классов, решающих одну и ту же задачу разными способами. Например, выставление счета на оплату по СБП и через ЮКассу. 2 класса, делающих одно и то же - выставление счета - но по-разному.
> Пример: у меня есть сущность, представляющая абитуриента и есть сущность, представляющая ошибки валидации.
> Общий для них интерфейс - геттер и сеттер, потому что я задал им приватные свойства.
Неправильно. Эти сущности разные и у них нет ничего общего. Геттер и сеттер не являются "общей задачей", так как они есть вообще у любого класса. Более того, геттеры у этих классов скорее всего разные (у студента - получить имя, у списка ошибок - получить текст ошибки и название поля с ошибкой).
Это хорошо, что ты пытаешься применить знания про интерфейсы, но тут ты их применяешь не туда, куда нужно.
Я подумал, где можно применить интерфейсы, и нашел. Если у тебя валидатор составной (состоит из основного валидатора и классов-правил), то для правил как раз нужно сделать интерфейс по типу:
interface RuleValidator
{
/** проверяет, что значение соответствует правилу*/
public function validate($value): ?Error; // Можно даже вместо Error сделать ErrorInterface, если у тебя несколько разных классов ошибок
}
> в коде лучше использовать не название класса, а название его интерфейса (в том случае, если функции требуется объект определенного класса)?
Ты используешь интерфейс, если хочешь, чтобы можно было передать вместо одного класса другой ("абстрагироваться от реализации").
Ну например, тебе надо сохранять фото. Ты можешь конечно все захардкодить и прибить гвоздями, а можешь сделать интерфейс:
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
Также, бывает еще такая ситуация, как товары и характеристики. У товаров есть сотни характеристик в зависимости от категории (у компьютера - объем памяти, у чашки - объем в литрах). Опять же, чтобы это описать, придется сделать сложную структуру объектов и таблиц.
На веб-сервере не включено выполнение PHP-кода и он просто отдает PHP-файлы как есть вместо их выполнения?
Ну тогда может обратишь внимание на то что insert update и delete относится к работе с базой, а не с маппером.
Все равно мозги ебет
А всё, надо было config.sample.inc.php переименовать в config.inc.php и там править
>Если у тебя валидатор составной (состоит из основного валидатора и классов-правил), то для правил как раз нужно сделать интерфейс по типу:
Не понимаю зачем тут интерфейс.
Вот код валидатора, который я написал получается, что списал, не я же его придумывал! по советам анонов (ну, или анона): https://3v4l.org/jZ4bH
rules это массив, у которого ключи - названия полей, которые нужно проверить, а значение этих ключей - массив с объектами-правилами.
В классах-правилах есть метод __invoke, чтобы вызывать объекты как методы.
>- у Студента может храниться объект Группа
Зачем Студенту хранить объект Группа?
Почему нельзя сделать свойство, значение которого - группа, просто строка с названием группы.
И если существует объект Группа, то что он из себя представляет и почему, обладая таким содержанием, находится в классе Студента?
>Почему нельзя сделать свойство, значение которого - группа, просто строка с названием группы.
Потому что в модели университета есть студенты и группы, но нет "строк"? Потому что студент университета знает в какой он группе и может об этом рассказать? Потому что каждый год создается новая группа первокуров с тем же названием?
>Более того, геттеры у этих классов скорее всего разные (у студента - получить имя, у списка ошибок - получить текст ошибки и название поля с ошибкой).
Зачем для отдельных полей делать отдельные геттеры/сеттеры?
Я видел примеры такого кода и всегда не понимал зачем, потому что пишу так: https://3v4l.org/ZmK3K
Что такое студент в данном случае?
Просто сущность с полями?
Зачем студенту знать что-то о содержании объекта Группа?
Я не понимаю тебя, потому что у меня объекты становятся свойствами только тогда, когда они требуются классу.
Если класс что-то делает и ему нужно получить экземпляр определенного класса, то, да, он становится частью.
А зачем в контексте этого примера ссылка на объект Группа?
А зачем в конструктор валидатора передаются "обнаруженные ошибки"? Это ведь валидатор должен находить ошибки.
Я понял эту строчку так:
Есть отдельный сеттер/геттер для отдельного поля.
Один геттер, чтобы получить значение поля, например, name.
Второй геттер, чтобы получить значение поля, например, surname.
Этот объект не содержит изначально ошибки, он позволяет класть в себя текст ошибки после прохождения валидации.
>Зачем студенту знать что-то о содержании объекта Группа?
За тем что ты моделируешь студента, а студент знает в какой он группе.
Зачем ты моделируешь студента вопрос к тебе.
>А зачем в контексте этого примера ссылка на объект Группа?
Еще раз. Затем что у тебя модель университета. Модель части реального мира. А ты её путаешь со способом хранения данных. То как ты хранишь свои данные никак не влияет на то как работает универ.
>Этот объект не содержит изначально ошибки
И где это отражено? Значения по умолчанию нет. В конструкторе пустой список с ошибками не создается. Чтобы понять как это работает надо каждый раз тебя обо всем спрашивать?
>Я понял эту строчку так:
>Есть отдельный сеттер/геттер для отдельного поля
А я должен понять как из его наличия следует его назначение? Разочарую - оно не следует.
>И где это отражено?
Ну, мб, по этому коду и непонятно, что представляет собой Класс с текстом ошибок.
Но в любом случае, какая разница?
Я же ведь описал текстом.
О чем мы вообще говорим?
Тебе полемику развести охота?
Я иногда видел код, где для отдельных полей делают отдельные геттеры и сеттеры и решил спросить в чем практическая значимость этого, если можно написать функцию, которая будет отдавать значение любого поля.
Ну так должно быть понятно. Сегодня у тебя в башке и на словах одно, завтра другое. Через месяц сам будешь на этот код как баран на новые ворота смотреть.
>О чем мы вообще говорим?
>Тебе полемику развести охота?
Я видел некоторые люди чешут яйца. Зачем? Задавай бессмысленные вопросы - получай бессмысленные ответы.
>если можно написать функцию, которая будет отдавать значение любого поля
Вот это и нужно было написать. "Почему бы не сделать функцию, которая умеет работать сразу со всеми полями объекта?"
И принимать эта функция будет какой тип? А возвращать какой тип? Мне чтобы её вызвать нужно знать что ей передать и что я получу. Откуда я это узнаю?
>"Почему бы не сделать функцию, которая умеет работать сразу со всеми полями объекта?"
Мой вопрос не в этом, мой вопрос в том, почему, имея возможность написать геттер и сеттер, которые будут работать со всеми полями, кто-то пишет геттеры/сеттеры для отдельных полей?
Мб есть какая-то практическая значимость или причина, чтобы так поступать, а я, ввиду неопытности, не знаю ответа?
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;
Ладно, в пизду.
Хватит меня разводить на полемику.
У тебя какая цель?
Попиздеть о хуйне, задавая как можно больше вопросов там, где их нет?
А как ты хотел? Чтобы я просто сказал "это не удобно"? И хули? Почему неудобно-то? А вот руками по пишешь такую лапшу, сразу дойдет.
>Номер группы в БД надо делать строкой, а не числом. Если номера делать числом, то номер группы 0010 превратится
просто в 10. Число используется для обозначения количества или значения какой-то величины.
Из советов автора задачки.
А я не верю что ты не понимаешь о чем я.
Ты спрашиваешь "почему так, а не как я себе нафантазировал". А я тебе говорю: ты не фантазируй, а попробуй действительно так сделать. И сразу поймешь почему так не делают.
А ты ленишься.
Мб мне, который даже не дотягивает по знаниям и опыту до недоджуна, писать код на уровне сеньора?
Чего мелочиться?
Раз пишешь код, так давай с самого первого раза пиши идеальный, по всем правилам.
Ну начни с типизации.
Твои объекты должны работать только так как это было задумано. Ничего лишнего нельзя засунуть, ничего лишнего нельзя вытащить. Нет никакого "на словах". Если что-то может быть использовано не так - оно будет использовано не так.
Хорошо, спасибо за подробный ответ.
Можешь рассказать в чем отличие TDG от DM?
Также я не понимаю как соотносится следующее:
Другой анон пишет:
>Table gateway. Это абстракция таблицы в базе. У тебя есть объект "таблица" через который ты взаимодействуешь с её содержимым.
Статья codedokode про паттерны проектирования, раздел про TDG:
насколько я понял, есть объект, который хранит информацию о сущности и есть класс, который выполняет SQL-запросы по этой сущности.
Т.е. все-таки что такое класс TDG?
Одновременно класс, который хранит данные таблицы и умеет работать по БД или не один класс, а два, где одна сущность (и она при этом одна запись в БД) и класс-сервис?
>не один класс, а два
Имеется ввиду класс у которого ты вызываешь методы. Так-то орм это одна из самых сложных проблем в программировании. Офк там десятки классов под капотом. А доктрина так скорее всего вообще самый сложный софт на пхп написанный.
⚰⚰⛼⚣
У меня есть класс-сервис.
Для его работы требуется объект.
Объект является сущностью.
Я должен регистрировать этот объект в контейнере?
Проще будет объяснить свой вопрос так:
Фабрика по созданию класса-сервиса будет выглядеть так:
new SomeClass ($container->get('AnotherClass'));
Или так:
new SomeClass (new AnotherClass).
А вот скажите, как лучше поступить с реквестами (в Ларавеле)?
Допустим, у нас есть модель Product. У каждого продукта есть набор статусов и адресов. Конретные адреса и статусы хранятся в Statuses и Addresses. Ну и через таблицы связей и отношения многие-ко-многим прикручены к этим продуктам. Дальше, есть три ресурсных контроллера, по одному на каждую модель. Соответственно, один добвляет/показывает/редактирует/удаляет адреса, другой статусы, третий продукты. И всё ок. Вдруг потребовалось добавить ещё один эндпоинт, который показывает все адреса и все статусы. Просто листинг. Ок, добавляем контроллер InfoController и метод getInfo(). Дальше, как это инфо брать? Заново писать код для сбора адресов и статусов? Некрасиво, лучше просто обратиться к index() соответствующих контроллеров. Обращаемся. Но тут засада, у StatusController метод index() принимает реквест типа StatusRequest, а передать мы можем только пришедший InfoRequest. И как это решить?
1) Прямо на ходу родить экземпляр класса StatusRequest, вкрячить туда нужные данные и отправить в StatusController? Ну херня же.
2) Создать некий общий SharedRequestInterface, определить в нём общие свойства, родить от него InfoRequest и StatusRequest, ну и пользоваться на здоровье. Сложна.
3) Убрать кхуям из контроллеров обращения к моделям и перенести их в репозиторий. Тогда будут цепочки: StatusController принимает StatusRequest, вычленяет из него нужные данные и обращается к методу из репозитория для обращения к базе. Аналогично InfoController принимает InfoRequest, забирает данные и обращается к тому же методу того же репозитория. Вроде, самое адекватное.
Что лучше? Или я вообще фигню несу?
>Дальше, как это инфо брать? Заново писать код для сбора адресов и статусов? Некрасиво
Ты же выше написал
>через таблицы связей и отношения многие-ко-многим прикручены
Написан ведь код сбора адресов уже. Тебе целый элокент для этого даден.
>лучше просто обратиться к index() соответствующих контроллеров. Обращаемся. Но тут засада, у StatusController метод index() принимает реквест типа StatusRequest
А что будет если объект предназначенный для обработки веб запроса использовать не по назначению? Будет весело.
>Убрать кхуям из контроллеров обращения к моделям и перенести их в репозиторий.
Репозиторий не выдает листинги. Репозиторий предназначен для работы с сущностями. А у тебя для этого актив рекорд элокент есть, нахуя тебе вокруг него дата маппер городить?
Ты можешь просто вынести повторяющийся код и назвать его в соответствии с его назначением. Такие невероятно сложные способы рефакторинга как извлечение метода/класса сейчас не в моде? Ctrl+alt+m в шторме, одно нажатие.
Каждый раз, как не брался за это, всегда все выглядит мудрено: то установить Докер (и разобраться как он работает), то установить Композер (который я также не ебу как работает).
Качать какую-то утилиту сомнительную, которая все якобы тебе скачает, нужное для веб-разработки - тоже не очень вариант.
>У меня есть класс-сервис.
>Для его работы требуется объект.
>Я должен регистрировать этот объект в контейнере?
Контейнер тебе зачем? Для инверсии зависимостей. А что написано в "принципе инверсии зависимостей"? "Классы должны зависеть от абстракций, а не от конкретных деталей" там написано. https://en.wikipedia.org/wiki/Dependency_inversion_principle
Так каким хуем твой сервис может зависеть от конкретной сущности, а не от абстракции?
Так учись епты. На работе че какой-то другой докер и другой композер будет что-ли?
Нет, но хочется подвинуть изучение докера и композера на позже. И немного ларку потрогать.
Разобраться в терминах, которыми ты сыпешь. Сущность это вообще из ddd Эванса. Инверсия контроля из солида и чистой архитектуры Мартина. Это две книги минимум, чисто погрузиться. И обе они не по пхп.
Тебе нужно начать с того что научиться объекты грамотно проектировать и методы. А уже потом об архитектуре думать.
Не стареющая классика.
Тупой вопрос. Посмотри пару видео по 5-10 минут и разберёшься.
Зачем тебе знать как он работает, берешь и без задней мысли ставишь: composer create-project laravel/laravel Govno
почему у меня на пике куча с глазами весь мой стиль на странице ломает?
Я делал машинку все работало, а тут все супер плохо
Даже если смайл не в контейнере держать все тоже самое будет
CSS
.line-container {
display: flex;
position: fixed;
}
.line_bottom {
margin-top: 10%;
width: 100%;
height: 11px;
background-color: rgb(255, 0, 0);
}
.glass-line {
position: relative;
font-size: 60px;
animation: tyda_suda 5s infinite alternate;
}
@keyframes tyda_suda {
from {
left: 0;
}
to {
left: 90%;
}
}
HTML
<div class="line_container">
<div class="line_bottom"></div>
<div class="glass-line line-1">💩</div>
</div>
почему у меня на пике куча с глазами весь мой стиль на странице ломает?
Я делал машинку все работало, а тут все супер плохо
Даже если смайл не в контейнере держать все тоже самое будет
CSS
.line-container {
display: flex;
position: fixed;
}
.line_bottom {
margin-top: 10%;
width: 100%;
height: 11px;
background-color: rgb(255, 0, 0);
}
.glass-line {
position: relative;
font-size: 60px;
animation: tyda_suda 5s infinite alternate;
}
@keyframes tyda_suda {
from {
left: 0;
}
to {
left: 90%;
}
}
HTML
<div class="line_container">
<div class="line_bottom"></div>
<div class="glass-line line-1">💩</div>
</div>
Телепатически проникаю к тебе в голову. Вижу как работала машинка и вижу что стало не так с какашкой. Вижу твой стиль каким ты его хочешь, не сломанным...
Че поверил? В деда мороза тоже веришь? Каким хуем кто-то должен понять что у тебя там сломалось в башке твоей? https://jsfiddle.net/v9se6z13/
Нет, insert/update/delete это методы класса Person Mapper. Посмотри внимательно на диаграмму: https://martinfowler.com/eaaCatalog/dataMapper.html
> Не понимаю зачем тут интерфейс.
Вот смотри, допустим я хочу взять твой код и добавить новое правило валидации и класс, который его проверяет. Как я должен писать этот класс? Каким требованиям он должен соответствовать?
Ты мог бы написать документацию в стиле "если вы хотите добавить новое правило, то ...". Но зачем писать, когда можно сделать интерфейс для классов-правил. И я буду знать, что мой класс должен соответствовать этому интерфейсу. А PHP проверит это и выдаст ошибку, если вдруг я что-то забыл.
То есть, для классов-правил надо сделать общий интерфейс.
> Вот код валидатора, который я написал ... https://3v4l.org/jZ4bH
Здесь есть такие недостатки:
1) ты передаешь в конструктор студента для проверки. Это плохо, так как:
а) получается, твой валидатор можно использовать только один раз. Чтобы проверить нового студента, надо создавать новый валидатор. Это так же нелогично, как покупать новую стиральную машину перед каждой стиркой.
б) непонятно, зачем тебе метод carryOutVerification. Ведь все данные уже есть в конструкторе и ты мог бы там сразу сделать проверку.
Лучше передавать проверяемого студента в метод проверки.
2) нелогично, что ты в конструктор еще передаешь список ошибок. Логичнее было бы, если бы метод для проверки возвращал список ошибок. То есть, ты кладешь в валидатор студента и он тебе в ответ возвращает список найденных ошибок.
То есть, на мой взгляд, логично было бы сделать класс так:
class Validator
{
public function __construct($rules) { ... }
public function validate(Enrollee $enrollee): ErrorList { ... }
}
3) ты не в том месте делаешь добавление ошибки. Ты достаешь предудущую ошибку, склеиваешь с ней новую и записываешь обратно. Но это неправильно, так как работа с ошибками это задача класса ErrorList (или как он у тебя назван). Только он знает, как хранить и склеивать ошибки. Соответственно, ты не должен склеивать их в валидаторе, а должен просто вызывать метод $this->detectedValidationErrors->add($key, $errortext)
4) PHP позволяет указывать типа параметров функций, тип результата и тип полей класса. Ты этого не делаешь, из-за этого твой код труднее понимать. Надо прочесть мануал и расставить типы везде, где можно:
- https://www.php.net/manual/ru/language.types.type-system.php
- https://www.php.net/manual/ru/language.oop5.properties.php#language.oop5.properties.typed-properties
У полей тип ставится так: private int $x; private SomeClass $y;
У функций и методов так: public function test(int $x, SomeClass $y, SomeInterface $z): SomeClass
Не согласен - возражай, не понял - спрашивай, иначе жду доработанный код.
> Не понимаю зачем тут интерфейс.
Вот смотри, допустим я хочу взять твой код и добавить новое правило валидации и класс, который его проверяет. Как я должен писать этот класс? Каким требованиям он должен соответствовать?
Ты мог бы написать документацию в стиле "если вы хотите добавить новое правило, то ...". Но зачем писать, когда можно сделать интерфейс для классов-правил. И я буду знать, что мой класс должен соответствовать этому интерфейсу. А PHP проверит это и выдаст ошибку, если вдруг я что-то забыл.
То есть, для классов-правил надо сделать общий интерфейс.
> Вот код валидатора, который я написал ... https://3v4l.org/jZ4bH
Здесь есть такие недостатки:
1) ты передаешь в конструктор студента для проверки. Это плохо, так как:
а) получается, твой валидатор можно использовать только один раз. Чтобы проверить нового студента, надо создавать новый валидатор. Это так же нелогично, как покупать новую стиральную машину перед каждой стиркой.
б) непонятно, зачем тебе метод carryOutVerification. Ведь все данные уже есть в конструкторе и ты мог бы там сразу сделать проверку.
Лучше передавать проверяемого студента в метод проверки.
2) нелогично, что ты в конструктор еще передаешь список ошибок. Логичнее было бы, если бы метод для проверки возвращал список ошибок. То есть, ты кладешь в валидатор студента и он тебе в ответ возвращает список найденных ошибок.
То есть, на мой взгляд, логично было бы сделать класс так:
class Validator
{
public function __construct($rules) { ... }
public function validate(Enrollee $enrollee): ErrorList { ... }
}
3) ты не в том месте делаешь добавление ошибки. Ты достаешь предудущую ошибку, склеиваешь с ней новую и записываешь обратно. Но это неправильно, так как работа с ошибками это задача класса ErrorList (или как он у тебя назван). Только он знает, как хранить и склеивать ошибки. Соответственно, ты не должен склеивать их в валидаторе, а должен просто вызывать метод $this->detectedValidationErrors->add($key, $errortext)
4) PHP позволяет указывать типа параметров функций, тип результата и тип полей класса. Ты этого не делаешь, из-за этого твой код труднее понимать. Надо прочесть мануал и расставить типы везде, где можно:
- https://www.php.net/manual/ru/language.types.type-system.php
- https://www.php.net/manual/ru/language.oop5.properties.php#language.oop5.properties.typed-properties
У полей тип ставится так: 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 от DM?
Эти паттерны описаны у Фаулера:
- https://martinfowler.com/eaaCatalog/tableDataGateway.html
- https://martinfowler.com/eaaCatalog/dataMapper.html
TDG это просто класс, где собраны все возможные операции над одной таблицей. Весь SQL-код для работы с таблицей должен быть собран в этом классе, и нигде больше. Если у тебя 5 таблиц, то у тебя будет примерно 5 TDG-классов, для каждой таблицы свой. И в них могут быть разные операции.
TDG не хранит в себе данные из таблиц. Данные хранятся в сущности, и если ты например прочитал из базы 10 студентов, то TDG создаст 10 объектов Student (или Enrollee) для них. На каждую запись создается новый объект.
Если ты совмещаешь в сущности хранение данных и операции, то это называется ActiveRecord - это сущность, которая сама умеет работать с базой. У Фаулера она есть: https://www.martinfowler.com/eaaCatalog/activeRecord.html
TDG это относительно простая штука. Например, в TDG может быть метод вроде updateStudentScore(int $id, int $score) - обновить баллы у студента по id. И другие методы, вроде "добавить студента в таблицу", "удалить студента", "найти студентов по поисковой строке", и тд.
DM это более сложная система. В ней обычно нет конкретных методов вроде "обновить баллы у студента". В ней ты просто загружаешь студента из базы, меняешь ему кол-во баллов, и просишь DM саму найти, что поменялось, и перенести изменения в БД.
DM пытается создать видимость, как будто БД не существует. Ты просто работаешь с объектами, загружаешь их, меняешь их, а DM сама обновляет базу в соответствие с твоими изменениями.
Для сравнения,в TDG, если ты хочешь что-то поменять, ты явно вызываешь нужный метод, вроде updateStudentScore. То есть, TDG намного примитивнее.
DM также обычно реализует паттерны вроде Identity Map, Unit of Work, Lazy Load, поддерживает связи между объектами, наследование и тд.
пример DM для PHP это библиотека Doctrine 2.
> насколько я понял, есть объект, который хранит информацию о сущности и есть класс, который выполняет SQL-запросы по этой сущности.
да
> Т.е. все-таки что такое класс TDG?
> Одновременно класс, который хранит данные таблицы и умеет работать по БД или не один класс, а два, где одна сущность (и она при этом одна запись в БД) и класс-сервис?
TDG это сервис, в котором не хранятся никакие данные, а только есть операции над таблицей. TDG у тебя в одном экземпляре. А Студентов может быть много экземпляров.
> Я должен регистрировать этот объект в контейнере?
Давай разделим сущности на 2 вида:
1) сущности, которые заполняются данными от пользователя (из формы). Они никогда не кладутся в DI контейнер, так как контейнер не должен получать никаких данных от пользователя.
2) сущности, которые всегда одинаковые либо которые заполняются из конфига. Их можно класть в контейнер. Но такие сущности встречаются редко и в задаче про студентов их нету.
Если у тебя сущность типа 1, то ее обычно не передают в конструктор сервиса.
То есть, твой контейнер не должен содержать в себе что-то, что приходит от пользователя или из формы. Твой контейнер в идеале должен быть всегда одинаковый и в нем не должно быть меняющихся данных.
Почему? Потому что контейнер и сервисы создаются один раз, и могут (в теории) обработать много запросов от разных пользователей. Контейнер св идеале оздается до того, как мы получим данные от пользователя, поэтому он не должен иметь к ним доступа или как-то их использовать.
> Некрасиво, лучше просто обратиться к index() соответствующих контроллеров.
Это неправильно, ты не должен писать в контроллере код, который надо вызвать откуда-то из другого места. Контроллер только обрабатывает запрос от пользователя и все. Он не помогает другим классам что-то найти.
Такой код, который нужен в нескольких местах, ты помещаешь в модель или сервис (или репозиторий, хотя откуда они в Ларавеле).
Далее, если у тебя сущности связаны, то ты должен сделать эти связи на уровне модели. Чтобы ты мог просто взять Product и вызвать метод и получить список его адресов.
Надо изучить композер, а затем Докер. Чтобы изучить Докер, предварительно надо научиться работать в командной строке и выучить основные команды Линукс. Учебников и видео полно по этой теме.
"Сущность" посоветовал использовать я, как название для класса с данными, моделирующего какую-то сущность из предметной области (Товар, Заказ, Комментарий, Пользователь). Как противоположность классу-сервису, который не хранит данные, а выполняет операции над ними. Что тут не так? Зачем для этого изучать DDD?
Надо не пол, а гендер, чтобы никого не ущемлять
Меня больше бесит -> писать вместо точки.
Норм, напоминает что нужно на хх ставить зп в баксах, а не в непостоянной валюте
Ок, спасибо
И какой итог такого подхода к обучению?
А итог такой: почти 2 месяца прошло, а я где начал задачу, там и остался.
Я согласен с тобой, что нужно учиться правильно кодить, читать литературу.
Я всем этим буду заниматься.
Но я уже так устал.
Меня уже тошнит от этой задачи про Список Студентов.
Тошнит, что я маюсь-маюсь, а конца ей нет, да и продвижения нет.
Я уже согласен написать посредственный код, но чтобы у меня получился хотя бы внешне работающий продукт.
Да и не много ли я с себя требую?
Я всего лишь новичок.
Философию ООП-кода не понимаю.
Много пробелов в знаниях.
Какой еще код ждать от человека, который не умеет кодить и мало знает?
Я считаю, что пусть я напишу плохой код, но пройду дальше, столкнусь с новыми знаниями, они подскажут мне как писать лучше, возможно, встречу толковых людей, указания которых я смогу понять и так и буду расти.
Да в целом я работал в вордпрессом, модули делал, вукомерс накатывал, с yii2, могу за солид с граспом пояснить и все такое.
Но проблема в том что там куча заказов и везде нужен узкий профиль экспертности, либо делать параллельно изучая область. Задачи пиздец какие разноплановые
А как работать в РФ? Сам самозарядный из плюсов то что работодатель не может принести повестку как на обычной работе и ничего не сообщает в военкомат иначе ему штраф 400к
быстрофикс - самозанятый.
>получается, твой валидатор можно использовать только один раз. Чтобы проверить нового студента, надо создавать новый валидатор. Это так же нелогично, как покупать новую стиральную машину перед каждой стиркой.
Я не представляю что будет находиться у меня в переменных, если я запущу сервер и моя страница будет
запущена на двух отдельных устройствах и они будут посылать данные на мой сервер.
Т.е. как php-скрипт будет обрабатывать эти данные.
Одни данные перекроют другие? Для каждого браузера будет отдельный php-скрипт, у которого будут полностью чистые
переменные и которые будут очищаться после того как php-скрипт отработает?
>непонятно, зачем тебе метод carryOutVerification
Этот метод запускает отдельные проверятели-правил.
Мне посоветовали в прошлом треде сделать главный класс и классы-проверятели (проверка на пустоту, на количество и т.д.).
>Ведь все данные уже есть в конструкторе и ты мог бы там сразу сделать проверку.
Не понимаю тебя.
>Логичнее было бы, если бы метод для проверки возвращал список ошибок
Я храню текста ошибок в виде объекта, где его свойства - name, surname etc.
>ты передаешь в конструктор студента для проверки
Я отдал его в конструктор, потому что думал, что если для работы Валидатора нужен объект другого класса,
то я должен добавлять зависимости через конструктор, а не отдавать объекты в методы.
>Ты достаешь предудущую ошибку, склеиваешь с ней новую и записываешь обратно.
Я так сделал, потому что иначе бы один текст ошибки перекраивал другой.
Ну, да ладно.
>Только он знает, как хранить и склеивать ошибки
Ну...Мой класс, связанный с ошибками, просто хранит данные.
Он как студент, только вместо имени он принимает текст ошибки.
А чтобы понимать в каком поле пользователь совершил ошибку, я классу, который содержит текст ошибок,
дал поля, которые называются как поля студента (кроме тех, где пользователь не вводит, а выбирает вариант из списка).
Ну, ладно. Я понял. Я должен переписать класс с текстом ошибок.
Добавить в него метод, чтобы он...сам клал значения ошибок и склеивал их.
Окей.
>PHP позволяет указывать типа параметров функций, тип результата и тип полей класса.
Да, я узнал об этом в прошлом треде.
Нет пока что привычки добавлять все это, потому что я учился по урокам, когда указывать типы нельзя было(?).
Да и код я не пишу.
Как не обращусь с вопросом, так сразу неправильно у меня все.
Начинаю вместо кода сидеть думать как правильно строить классы.
В итоге просто бездарно проведенное время, ведь мне не хватает знаний и опыта, чтобы понять и применить с умом то,
о чем говорят в тредах опытные аноны.
>получается, твой валидатор можно использовать только один раз. Чтобы проверить нового студента, надо создавать новый валидатор. Это так же нелогично, как покупать новую стиральную машину перед каждой стиркой.
Я не представляю что будет находиться у меня в переменных, если я запущу сервер и моя страница будет
запущена на двух отдельных устройствах и они будут посылать данные на мой сервер.
Т.е. как php-скрипт будет обрабатывать эти данные.
Одни данные перекроют другие? Для каждого браузера будет отдельный php-скрипт, у которого будут полностью чистые
переменные и которые будут очищаться после того как php-скрипт отработает?
>непонятно, зачем тебе метод carryOutVerification
Этот метод запускает отдельные проверятели-правил.
Мне посоветовали в прошлом треде сделать главный класс и классы-проверятели (проверка на пустоту, на количество и т.д.).
>Ведь все данные уже есть в конструкторе и ты мог бы там сразу сделать проверку.
Не понимаю тебя.
>Логичнее было бы, если бы метод для проверки возвращал список ошибок
Я храню текста ошибок в виде объекта, где его свойства - name, surname etc.
>ты передаешь в конструктор студента для проверки
Я отдал его в конструктор, потому что думал, что если для работы Валидатора нужен объект другого класса,
то я должен добавлять зависимости через конструктор, а не отдавать объекты в методы.
>Ты достаешь предудущую ошибку, склеиваешь с ней новую и записываешь обратно.
Я так сделал, потому что иначе бы один текст ошибки перекраивал другой.
Ну, да ладно.
>Только он знает, как хранить и склеивать ошибки
Ну...Мой класс, связанный с ошибками, просто хранит данные.
Он как студент, только вместо имени он принимает текст ошибки.
А чтобы понимать в каком поле пользователь совершил ошибку, я классу, который содержит текст ошибок,
дал поля, которые называются как поля студента (кроме тех, где пользователь не вводит, а выбирает вариант из списка).
Ну, ладно. Я понял. Я должен переписать класс с текстом ошибок.
Добавить в него метод, чтобы он...сам клал значения ошибок и склеивал их.
Окей.
>PHP позволяет указывать типа параметров функций, тип результата и тип полей класса.
Да, я узнал об этом в прошлом треде.
Нет пока что привычки добавлять все это, потому что я учился по урокам, когда указывать типы нельзя было(?).
Да и код я не пишу.
Как не обращусь с вопросом, так сразу неправильно у меня все.
Начинаю вместо кода сидеть думать как правильно строить классы.
В итоге просто бездарно проведенное время, ведь мне не хватает знаний и опыта, чтобы понять и применить с умом то,
о чем говорят в тредах опытные аноны.
Ну и что, зато теперь же ведомства обязаны инфу передавать в ёнкомат. То есть ты самозарядный и ёнкомат узнает из налоговой, что ты самозарядный и щас дело не в том что принесут повистку, а в том что её тебе в госуслуги закинули, и ты по факту обязан. То есть знал ты или нет - поксуй, не пришел - с тебя штраф 10-30к
Хорошо. Я понимаю.
Хорошо, спасибо, я понял.
Окей.
>посредственный код, но чтобы у меня получился хотя бы внешне работающий продукт
тогда просто пиши, как получится, забудь пока про эти ооп, паттерны и прочая. Выпиши требования задачи на бумажке или в текстовик (важно - только требования, без комментариев, советов, рекомендаций и т.д.) . Дальше вся работа должна идти в порядке "хочу сделать это > гуглю, если не понятно как сделать > делаю > думаю, что дальше надо делать".
Потом, когда напишешь, прочитаешь ту же шапку снова, сравнишь описываемый "правильный" подход со своим, начнёшь понимать, зачем вообще эти всякие ООПы нужны и как они бы помогли при написании кода. Тогда и дальнейшее изучение пойдёт попроще - это уже будет не абстрактная "философия", а практический инструмент.
>важно - только требования
Даже не знаю.
Прям только-только требования?
Даже без раздела о подводных камнях от автора задачи (ну или человека, который разместил ее в гитхабе)?
Если я буду гуглить, не будет ли это нечто схожее, чем как если бы я спрашивал тут, у анонов, или читал советы по задаче?
И то, мб, это будет хуже.
Потому что тут живые люди, у которых можно переспрашивать.
А если я не смогу нагуглить, я также буду затупливаться, сидеть, думОть, но не кодить.
Да и не бросать же мне те советы, которые уже писали аноны?
Все-таки он(и) дело говорят.
>для класса с данными, моделирующего какую-то
Моделирует "модель". А у сущности другие свойства.
>Как противоположность классу-сервису, который не хранит данные
Сущность противопоставляется не сервису, а классу который полностью определяется его данными. Как адрес или дата.
>Что тут не так? Зачем для этого изучать DDD?
И пользователь и его адрес это модель, и пользователь и адрес хранятся в базе в отдельных таблицах. Только пользователь это сущность, а адрес нет.
Вот как одно от другого отличить и написано у Эванса.
> и ёнкомат узнает из налоговой,
Вообще похуй, нельзя будет открыть с нуля, но то что есть не закрывают.
> же ведомства обязаны инфу передавать в ёнкомат.
ГПХ/Самозанятые работает без этой хуйни. На работе про тебя все твой директор сдает и повестку тебе приносит, от которой уже неоткажешься.
> в том что её тебе в госуслуги закинули, и ты по факту обязан.
Реестра повесток нет, по слухам юристов не ранее 2025, да и должны обеспечить бесплатным инетом и компом ибо нет закона что я обязан все это иметь. Закон - пугалка.
Причина твоей тряски? Хочешь чтобы я на работу устроился или че?
Ну и пока повестка считается врученный когда на ней роспись поставил или заказное письмо пришло и ты его взял или пришел по извещению в почту и отказался. Другие варианты не предусмотрены законом.
Но если ты не запасник, а призывник у которого военника нет, то там свои законы и другая ответственность, более пиздецовая чем для запасников
>Прям только-только требования?
Даже без раздела о подводных камнях от автора задачи (ну или человека, который разместил ее в гитхабе)?
Да. Советы загружают мозг, заставляют думать о правильной структуре приложения, о всяких злых ООП, о том как бы всё сделать красиво и не обосраться, и т.д.
А когда сам пишешь, то всё просто - надо сделать приложение, которое будет иметь такой функционал. И всё. Это развязывает руки.
>Если я буду гуглить, не будет ли это нечто схожее, чем как если бы я спрашивал тут, у анонов, или читал советы по задаче?
Для того, чтобы получить нормальный ответ от гугла, надо уметь гуглить - сформулировать запрос, потыкать по ссылкам, проанализировать ответы, если не подходят, то изменить запрос и загуглить ещё раз, и т.д. Это практический навык, его надо развивать и он очень сильно упрощает дальнейшее обучение.
Для того, чтобы (в теории) получить ответ от анона, надо... уметь писать на русском? При этом можно написать максимально расплывчатый и неконкретный вопрос, вообще не шевеля мозгами и никак не вовлекаясь в процесс. Это не очень сильно помогает и занимает время. Даже если ответ на такой вопрос будет хорошим, то всё равно не будет понятно, где его применить.
>Потому что тут живые люди, у которых можно переспрашивать.
У гугла тоже можно переспрашивать. А ещё от анона ответы приходят за минуты и часы, а от гугла - за миллисекунды. И ещё гугл не устаёт, не бурчит, не спорит, и готов стерпеть самые глупые вопросы (которые даже в треде глупых вопросов бы не прошли).
>А если я не смогу нагуглить, я также буду затупливаться, сидеть, думОть, но не кодить.
Если не получается нагуглить, то гугли по-другому. Гугли руководства к действию: что сделать, чтобы сайт запомнил юзера; что сделать, чтобы из кода залезть в базу данных, что делать, чтобы ...
Результатом будут конкретные инструкции, которые можно попробовать и посмотреть, что получается. И дальше уже думать, как это можно использовать для решения проблемы.
>Да и не бросать же мне те советы, которые уже писали аноны?
Если в какой-то момент возникнет ощущение, что ты можешь применить какой-то совет, и при этом будет ясно, какие именно строчки кода нужно писать для реализации этого совета - то флаг в руки, пиши, пробуй. С другой стороны, если есть только унылое ощущение, что по этой теме что-то советовали, но как реализовать не понимаешь - то не надо.
>Прям только-только требования?
Даже без раздела о подводных камнях от автора задачи (ну или человека, который разместил ее в гитхабе)?
Да. Советы загружают мозг, заставляют думать о правильной структуре приложения, о всяких злых ООП, о том как бы всё сделать красиво и не обосраться, и т.д.
А когда сам пишешь, то всё просто - надо сделать приложение, которое будет иметь такой функционал. И всё. Это развязывает руки.
>Если я буду гуглить, не будет ли это нечто схожее, чем как если бы я спрашивал тут, у анонов, или читал советы по задаче?
Для того, чтобы получить нормальный ответ от гугла, надо уметь гуглить - сформулировать запрос, потыкать по ссылкам, проанализировать ответы, если не подходят, то изменить запрос и загуглить ещё раз, и т.д. Это практический навык, его надо развивать и он очень сильно упрощает дальнейшее обучение.
Для того, чтобы (в теории) получить ответ от анона, надо... уметь писать на русском? При этом можно написать максимально расплывчатый и неконкретный вопрос, вообще не шевеля мозгами и никак не вовлекаясь в процесс. Это не очень сильно помогает и занимает время. Даже если ответ на такой вопрос будет хорошим, то всё равно не будет понятно, где его применить.
>Потому что тут живые люди, у которых можно переспрашивать.
У гугла тоже можно переспрашивать. А ещё от анона ответы приходят за минуты и часы, а от гугла - за миллисекунды. И ещё гугл не устаёт, не бурчит, не спорит, и готов стерпеть самые глупые вопросы (которые даже в треде глупых вопросов бы не прошли).
>А если я не смогу нагуглить, я также буду затупливаться, сидеть, думОть, но не кодить.
Если не получается нагуглить, то гугли по-другому. Гугли руководства к действию: что сделать, чтобы сайт запомнил юзера; что сделать, чтобы из кода залезть в базу данных, что делать, чтобы ...
Результатом будут конкретные инструкции, которые можно попробовать и посмотреть, что получается. И дальше уже думать, как это можно использовать для решения проблемы.
>Да и не бросать же мне те советы, которые уже писали аноны?
Если в какой-то момент возникнет ощущение, что ты можешь применить какой-то совет, и при этом будет ясно, какие именно строчки кода нужно писать для реализации этого совета - то флаг в руки, пиши, пробуй. С другой стороны, если есть только унылое ощущение, что по этой теме что-то советовали, но как реализовать не понимаешь - то не надо.
Мало для чего? Что ты такого с симфони делать собрался что тебе не достаточно открыть доку?
1) Чтение исходиков.
2) xdebug. Деды всегда дебажили код, чтобы понять как он работает. В книжке каждую переменную не опишешь.
А так ты хочешь глотать пережеванную другими пищу.
А в "Мурзилке от создателя Симфони" вместо того как работать с фреймворком написано как хуй чесать?
Там мало написано, буквально шаг влево, шаг вправо и уже не понимаешь.
Может стоит Лару учить, по ней больше всего написано.
И что за шаги ты собрался делать вправо и влево? Тебе фреймворк нахуя? Делай как в доке написано и все.
> Я уже согласен написать посредственный код, но чтобы у меня получился хотя бы внешне работающий продукт.
Не надо так. Надо сидеть и разбираться.
> Философию ООП-кода не понимаю.
А вот в учебнике есть глава про ООП и задача про ООО Вектор, ты ее делал? Она вроде как должна помогать разобраться в ООП. Или можем какую-нибудь другую задачу на ООП-проектирование тебе дать.
> Я считаю, что пусть я напишу плохой код, но пройду дальше
Если ты не разберешься с основами (ООП), как ты пойдешь дальше? Это как пропустить основы арифметики и браться сразу за матрицы и векторы.
> Я не представляю что будет находиться у меня в переменных, если я запущу сервер и моя страница будет
запущена на двух отдельных устройствах и они будут посылать данные на мой сервер.
> Т.е. как 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);
Но твой стиль просто выглядит странно и нелогично. Непонятно, зачем вызывать функцию, в которую ничего не передается и которая ничего не возвращает.
>>непонятно, зачем тебе метод 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 варианта обычно используют в ООП. Такой подход очень часто встречается:
- создаем объект и в конструктор передаем параметры, настройки объекта, которые не меняются
- затем используем объект, передавая данные (сущности, числа, строки) в метод и возвращая из них результат
>>ты передаешь в конструктор студента для проверки
> Я отдал его в конструктор, потому что думал, что если для работы Валидатора нужен объект другого класса, то я должен добавлять зависимости через конструктор, а не отдавать объекты в методы.
Ну ок, значит я плохо написал статью про 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 именно потому, что по этим темам не смог ничего нормального найти. Если бы там были нормальные статьи, я бы просто давал на них ссылки, а не писал сам.
Ну например, попробуй погуглить куки и получишь статьи такого уровня: https://skillbox.ru/media/marketing/vsye-chto-nuzhno-znat-o-faylakh-cookie-v-odnoy-state/
А я ждал техническое объяснение: рисунок, на котором показано, как они передаются и хранятся; рисунок, где они показаны в HTTP-запросе и ответе; как их просмотреть в curl и в браузере; какие у кук есть атрибуты; лучшие практики по безопасности; функции для работы с куками.
И чтобы это было понятно начинающему и на русском. Вот такие статьи я хочу в Гугле видеть на первой странице.
Или другой пример, гуглим MVC и вверху выдачи переоптимизированный мусор от хекслета, который тут рекламируют каждый тред: https://ru.hexlet.io/blog/posts/chto-takoe-mvc-rasskazyvaem-prostymi-slovami
В статье написано:
> На сервере Хекслета ваш запрос обрабатывается. Программа достаёт из базы данных все последние тексты из рубрики «Истории успеха», чтобы показать список. Это можно сравнить с кухней и поварами из примера с сэндвичем. Это модель.
А затем в примерах кода модель у них состоит из одной сущности:
> Модель отвечает за данные, которые хранятся и обрабатываются на сервере.
> User: { userName: { firstName, lastName }, friends }
ну вот такого качества статьи заполняют первые страницы Гугла. Прежде чем отправлять в Гугл, проверь результат сам. Или пиши волшебный запрос, который дает нормальные статьи.
У меня одна просьба ко всем: прежде чем отправлять в Гугл, воспользуйся им сам.
Проблема статей по программированию в Интернете в том, что они:
- часто недостаточно технические и не рассказывают нужные подробности и детали, а просто дают совет в стиле "скопируйте этот код"
- иногда предполагают обширные знания и не рассчитаны на новичков
- часто неточные, некачественные, устаревшие (особенно по PHP таких много)
- используют плохие подходы
- часто не на русском
Ну например, я написал статьи на codedokode/pasta именно потому, что по этим темам не смог ничего нормального найти. Если бы там были нормальные статьи, я бы просто давал на них ссылки, а не писал сам.
Ну например, попробуй погуглить куки и получишь статьи такого уровня: https://skillbox.ru/media/marketing/vsye-chto-nuzhno-znat-o-faylakh-cookie-v-odnoy-state/
А я ждал техническое объяснение: рисунок, на котором показано, как они передаются и хранятся; рисунок, где они показаны в HTTP-запросе и ответе; как их просмотреть в curl и в браузере; какие у кук есть атрибуты; лучшие практики по безопасности; функции для работы с куками.
И чтобы это было понятно начинающему и на русском. Вот такие статьи я хочу в Гугле видеть на первой странице.
Или другой пример, гуглим MVC и вверху выдачи переоптимизированный мусор от хекслета, который тут рекламируют каждый тред: https://ru.hexlet.io/blog/posts/chto-takoe-mvc-rasskazyvaem-prostymi-slovami
В статье написано:
> На сервере Хекслета ваш запрос обрабатывается. Программа достаёт из базы данных все последние тексты из рубрики «Истории успеха», чтобы показать список. Это можно сравнить с кухней и поварами из примера с сэндвичем. Это модель.
А затем в примерах кода модель у них состоит из одной сущности:
> Модель отвечает за данные, которые хранятся и обрабатываются на сервере.
> User: { userName: { firstName, lastName }, friends }
ну вот такого качества статьи заполняют первые страницы Гугла. Прежде чем отправлять в Гугл, проверь результат сам. Или пиши волшебный запрос, который дает нормальные статьи.
Так читай документацию, вот примеры ссылок по разным копонентам:
- конфиги https://symfony.com/doc/current/configuration.html
- контейнер https://symfony.com/doc/current/service_container.html
- контроллеры https://symfony.com/doc/current/controller.html
и тд
>задача про ООО Вектор, ты ее делал?
Да.
>Она вроде как должна помогать разобраться в ООП.
Когда я впервые читал урок из мануала про ООП, для меня понятия "свойство", "метод", "тайп-хинт",
"модификаторы доступа" и т.д. воспринимались чем-то абстрактным, я не мог усвоить их как само собой разумеющееся.
Раздел с тайп-хинтом я вообще пробежал глазами и решил забыть о нем, потому что тогда ничего не понял, но сейчас уже понимаю.
Так что думаю, прогресс есть.
>Если ты не разберешься с основами (ООП), как ты пойдешь дальше? Это как пропустить основы арифметики и браться сразу за матрицы и векторы.
Здесь я имел ввиду то, что, мне кажется, что я занимаюсь перфекционизмом в решении этой задачи.
Чего я должен добиться этим? Сейчас сесть и с нуля научиться проектировать системы так, как это делают люди с опытом?
Мне кажется, что я много на себя пытаюсь взять. Я понимаю, что от джуна будут требовать понимание основ ООП.
Но ведь от него не будут требовать идеального проектирования системы? Просто мне кажется, что я ухожу не туда.
Мне просто нужно усвоить основы. А что делаю я? Учусь на проектировщика систем?
Я думаю, что мне нужно получить некий базис, который позволит мне кодить.
А потом одни знания лягут на другие и те знания, которые были в тумане, усвоятся.
Окей, спасибо за ответ.
>>2945232
>Зачем здесь вызов carryOutVerification? Ты мог бы его сразу вписать в конструктор, чтобы он вызывался автоматически.
Действительно. Я в таком ключе не думал.
>$errors = $validator->validate($enrollee);
Не понимаю эту строку.
Что из себя представляет $errors и что в таком случае будет возвращать $validator->validate($enrollee)?
И если errors - это объект, то какое у него будет содержание?
Хорошо, я понял.
>Радуйся, тебя учат и дают новые знания, причем бесплатно.
Если ты думаешь, что я негодую от того, что мои решение (лучше сказать - попытки реализовать то, что тут советуют) считают неправильными, то это не так. Я всегда прихожу в тред с пониманием, что я неправ. Мб мои формулировки звучат нагло, но ими я пытаюсь донести какое представление о предмете находится у меня в голове. Я никогда не пытаюсь ничего доказать.
Я очень благодарен всем, кто давал мне толковый совет или объяснение.
>есть domain model
Что значит "есть"? Есть подход "domain model development", который основан на выделении "domain" - "предметной области" и моделировании её. Делается это определенным образом с соблюдением определенных правил. Сущность это часть понятийного аппарата DDD. Термин вырван из контекста (лол) и используется как баззворд.
>есть model в MVC - это не только сущности, но и вообще весь код с бизнес-логикой.
Ты опять описал domain model.
>Я пробовал поискать определение, но гуглить такие темы бесперспективная задача.
А спорить в интернете - перспективная.
В смаллтолк MVC это observer паттерн, который отслеживает изменения в объектах представления. Джаваскрипт, который ловит события типа нажатий на кнопки это оно.
Ко временам джанго и рельс модель из MVC путем баззвординга и непонимания концепта превратилась в "данные между веб запросами". А определение, которое ты считаешь "лучше" совсем бессмыслица, потому что слишком широкое. Согласно этому определению роутинг - часть модели. Потому что содержит логику, валидирует ввод и доступ. При этом роутинг по определению происходит перед контроллером. Чушь и бессмыслица.
>Поэтому я стараюсь этот термин не использовать вообще, так как он 100% вызовет у начинающего путаницу.
Поэтому ты используешь вместо этого термины с другим значением и определением, из других областей и подходов. И никакой путаницы нет, все четко. Ну тут не доебешься, продолжай. Всем все понятно.
>классы с данными без DI и классы с операциями, как правило, использующие DI (вопреки идее ООП, что данные и операции должны быть объединены в одном классе).
Даже я хуй пойму что тут написано. Опять все смешалось.
1) Наличие "данных" ничего не говорит о том является ли объект сущностью. Что является чем диктует исключительно предметная область. Логика работы сервиса вполне может храниться в базе, но сущностью сервис от этого не станет. Так же и адрес не станет сущностью если добавить ему идентификатор. Как только улицу переименуют, все объекты с этой улицей придется изменить одинаковым образом и похуй сколько там идентификаторов.
2) "Объекты с данными" вполне себе могут использовать DI. У них точно так же есть конструктор, и точно так же можно передать зависимость в вызываемый метод. Да, инъекция через метод это тоже DI
Попробуй курс Елисеева навернуть, symfonycast'ы, нагуглить курс отуса мб слили уже
есть вариант создать дефолтную пхп.ини?
(не копируя из инета)
Ну ладно, я хуйню написал и пхп.ини-дев - это норма, пишут что ее просто переименовывают в .ини и не ебут мозги себе
Но вообще, вы все из исходников собираете? Или не страдаете хуйней такой
Чет у меня то расширения при сборке не ставятся, то десять каких-то либ нехватает. А мануала норм свежего не нашел. В итоге 8.1 поставил через апт,лол
Ини, хуини. Все это уже сто лет как ставится через докер. У меня на работе проектов двадцать, и у каждого какая-то особая версия и либы. Какой еблан будет с этим вручную морочиться?
>1к+ вакансий на БУС
>меньше 100 откликов на каждой
>зарплаты ниже "трушных фрейворков" всего на 20-30%
>вкатыши продолжают катиться в ларабэль (200-500 откликов на каждой, зп не сильно выше, требований больше, много где требуют vue)
Ебало? Имагине? Вкатышей?
Вкатыши на ларабель жрут яблоки и получают не сильно выше зп.
Битриксоиды жрут кал и получают не сильно меньше зп.
Ебало? Имагине?
Будешь и там и там рыться и мучительно разбираться что и как. Из плюсов битрикса: больше работы, много интересных проектов, много смежных технологий (nodejs, laravel).
>Из плюсов битрикса: больше работы, много интересных проектов
Это типа как сметана на говне?
Ты пробовал с этой парашей работать?
Есть свойство класса, которое, как я знаю, может быть в определённом интервале. Ну, допустим, 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 значение, которое не проходит валидацию, нехуй. Если возникла такая ситуация, значит, валидация кривая и нужно её редактировать.
Короче, хуй пойми. Как делать, чтобы сеньоры говорили малаца?
Погуглив и подумав, начал склоняться к варианту, в котором проверка значения выполняется внутри сеттера. Ещё такой аргумент есть - а нафиг тогда сеттер вообще нужен? Если всё, что он делает и я как бы ожидаю, что будет делать всегда - возвращает значение, то почему бы не сделать свойство public и не редактировать напрямую? Сеттер затем и нужен, что подразумевает возможность добавить валидацию или ещё что-нибудь.
В свойство может прилететь некорректное значение и из-за ошибки в коде программы. Если оно изменяется только через сеттер, то в сеттере можно делать проверку входящего значения и сразу выдавать исключение, если оно некорректное.
Геттеры, сеттеры какие-то. Ты ооп код пишешь или че? Объектами надо мыслить и типами. https://3v4l.org/WksJS#v8.2.13
Это троллинг, или я должен упороться и под каждое валидируемое свойство класса добавлять по ещё 3 класса?
Не "класса", а типа. И в чем проблема? Тебе все равно эти функции писать придется. Если ты пишешь ооп код, так и пиши его в виде взаимодействия объектов. А если пишешь процедурную дрисню, так нахуя тебе тогда все эти di заморочки, модели какие-то.
https://3v4l.org/7ldLa
Сравни количество кода. Это что, в тру-ООП скалярные типы вообще идут нахуй?
>Это что, в тру-ООП скалярные типы вообще идут нахуй?
Ты хотел в нормальном ооп языке? >>2936382
>Сравни количество кода.
Какой же кал, пиздец.
1) Метод setAge не знает каким образом происходит валидация, но с какого-то хуя формирует текст ошибки.
2) Метод ageIsValid не имеет никакого отношения к Yoba. Вынеси его отдельно и он будет точно так же работать.
3) Какого хуя вообще можно поменять возраст? Ты блядь не можешь себе поменять возраст и купить пива, это не так работает.
4) Даже допустим возраст можно менять. Кто это блядь такой умный что этот возраст меняет? Кто вызывает setAge?
>1) Метод setAge не знает каким образом происходит валидация, но с какого-то хуя формирует текст ошибки.
Это для примера. На деле, там будет скорее throw new ValidationFailedException, который уже формирует текст.
>Метод ageIsValid не имеет никакого отношения к Yoba. Вынеси его отдельно и он будет точно так же работать.
И? Это плохо? Предпочтительней было бы переписать это как-то так? https://3v4l.org/GCRfZ
>3) Какого хуя вообще можно поменять возраст? Ты блядь не можешь себе поменять возраст и купить пива, это не так работает.
>4) Даже допустим возраст можно менять. Кто это блядь такой умный что этот возраст меняет? Кто вызывает setAge?
Пикрелейтед.
Микрофикс, ageValidate должен тогда void возвращать, забыл исправить. Ну, это ничего не меняет фактически.
>переписать это как-то так?
Теперь этим методом можно провалидировать только возраст Yob'ы, а возраст Zalup'ы нельзя. И залупе придется писать свой валидатор.
>Пикрелейтед.
Ты либо пишешь "модель", которая должна соответствоавть моделируемуму, любо просто какой-то код и тогда вообще похуй как оно там написано.
Точно так же, ты либо пишешь ооп код и тогда ты соблюдаешь все эти инкапсуляции сингл респонсибилити итд, либо катаешься ебалом по клавиатуре и тебе все это не надо.
Это ведь ты выбрал писать "модель" в ооп стиле. Ну так пиши. Не хочешь - не пиши.
Никакого отношения к DDD то что ты описал не имеет.
иди
Нет
PHP более веб ориентированный чем питон, так шо мне больше нравицо. А так похуй один хуй две скриптовые залупы
Для веба на пхп вроде больше вакансий чем на питоне.
На питоне не малая часть вакансий это ии, дата саенс, машин ленин, но это для математиков, а не макак
> Здесь я имел ввиду то, что, мне кажется, что я занимаюсь перфекционизмом в решении этой задачи.
> Чего я должен добиться этим? Сейчас сесть и с нуля научиться проектировать системы так, как это делают люди с опытом?
Задача учит использованию ООП, работе с базой данных, формами, генерации HTML-страниц через шаблоны. Все это нужно в реальной работе.
Я думаю, что джун должен сразу писать правильный ООП-код. Или ты предполагаешь, что джун может писать как попало, а потом сеньор будет сидеть за него все исправлять? Никто джуна обучать заново не будет, его просто уволят с испытательного и скажут идти читать книжки.
> >$errors = $validator->validate($enrollee);
> Не понимаю эту строку.
> Что из себя представляет $errors и что в таком случае будет возвращать $validator->validate($enrollee)?
> И если errors - это объект, то какое у него будет содержание?
О, хороший вопрос и по делу.
Раз у нас ООП то $errors это объект, который представляет (и хранит в себе) список ошибок валидации студента. Но почему только ошибки валидации студента? Давай обобщим, $errorList это объект класса ErrorList, который способен хранить список ошибок валидации любого объекта.
У ErrorList есть такие методы:
- добавить ошибку к заданному полю (например: добавить ошибку "использованы недопустимые символы" к полю "имя")
- получить список всех ошибок для поля
- узнать, пуст ли список ошибок или в нем есть ошибки
Вот как он используется:
$errorList = new ErrorList(); // создали пустой список
Что касается ошибки, то одну ошибку мы могли бы представить строкой:
$errorList->add('name', 'использованы недопустимые символы');
Метод add тогда выглядит так:
add(string $field, string $error): void
Это допустимо, но тут недостаточно ООП. Чтобы его было больше, будет представлять ошибки тоже объектом:
$errorList->add('name', new Error('использованы недопустимые символы'));
add(string $field, Error $error): void
Плюс использования класса Error такой:
- мы можем делать не просто текстовые ошибки, а ошибки с подробностями, например, с подсказками или еще какими-то дополнительными свойствами
- в тайп-хинте функций очевидно, что они принимают именно ошибку, а не любую строку
> >$errors = $validator->validate($enrollee);
> Не понимаю эту строку.
> Что из себя представляет $errors и что в таком случае будет возвращать $validator->validate($enrollee)?
> И если errors - это объект, то какое у него будет содержание?
О, хороший вопрос и по делу.
Раз у нас ООП то $errors это объект, который представляет (и хранит в себе) список ошибок валидации студента. Но почему только ошибки валидации студента? Давай обобщим, $errorList это объект класса ErrorList, который способен хранить список ошибок валидации любого объекта.
У ErrorList есть такие методы:
- добавить ошибку к заданному полю (например: добавить ошибку "использованы недопустимые символы" к полю "имя")
- получить список всех ошибок для поля
- узнать, пуст ли список ошибок или в нем есть ошибки
Вот как он используется:
$errorList = new ErrorList(); // создали пустой список
Что касается ошибки, то одну ошибку мы могли бы представить строкой:
$errorList->add('name', 'использованы недопустимые символы');
Метод add тогда выглядит так:
add(string $field, string $error): void
Это допустимо, но тут недостаточно ООП. Чтобы его было больше, будет представлять ошибки тоже объектом:
$errorList->add('name', new Error('использованы недопустимые символы'));
add(string $field, Error $error): void
Плюс использования класса Error такой:
- мы можем делать не просто текстовые ошибки, а ошибки с подробностями, например, с подсказками или еще какими-то дополнительными свойствами
- в тайп-хинте функций очевидно, что они принимают именно ошибку, а не любую строку
Я тебе хотел просто сказать, что слово "модель" ассоциируется с несколькими терминами и сбивает начинающих, если они пытаются его гуглить. Поэтому я не хочу его использовать.
Ну то есть ты хочешь называть сущности "модель", но "модель" более широкое понятие и включает в себя и сервисы для обработки сущностей.
Также, судя по датам, понятие domain model старше чем DDD: оно упоминается в книге Фаулера от 2003 года. И MVC старше, чем DDD.
> А определение, которое ты считаешь "лучше" совсем бессмыслица, потому что слишком широкое. Согласно этому определению роутинг - часть модели. Потому что содержит логику, валидирует ввод и доступ. При этом роутинг по определению происходит перед контроллером. Чушь и бессмыслица.
Модель содержит бизнес-логику приложения. Роутинг это не бизнес-логика, а техническая штука.
Хорошо, предложи свои, "правильные" термины для обозначения классов, которые представляют какую-то сущность, и классов с операциями.
Есть 2 подхода:
1) в объекте не может быть невалидных значений ни при каких условиях. В этой ситуации мы ставим проверку в сеттер и при нарушении правил выкидываем исключение и роняем всю программу. Зато мы знаем, что в объекте правильные данные.
Тут надо учесть, что при создании объекта в нем изначально должны быть правильные данные. Это может потребовать ставить значения по умолчанию или передавать значения в конструктор.
2) в объекте могут быть невалидные значения, например, потому что они пришли от пользователя. Тогда мы принимаем все, но перед использованием объекта или записью в БД проверяем валидатором.
То есть, либо мы проверяем значения в момент записи, либо позже, перед использованием. Минус второго способа в том, что можно забыть сделать проверку, а в первом способе у нас просто не существует неправильных объектов. Минус первого способа в том, что он плохо сочетается с формами, так как в форму можно вводить неправильные значения.
Как не надо делать: при передаче неправильного значения просто не сохранять его и ничего не делать. Это самый плохой подход, так как ошибки в коде будут оставаться незамеченными.
> Если всё, что он делает и я как бы ожидаю, что будет делать всегда - возвращает значение, то почему бы не сделать свойство public и не редактировать напрямую? Сеттер затем и нужен, что подразумевает возможность добавить валидацию или ещё что-нибудь.
Да. Даже если тебе не нужна сегодня проверка, то она может понадобиться завтра и с сеттером тебе не придется переписывать пол-программы из-за этого.
Но есть и другой подход, например, в Питоне: там используют свойство напрямую, без сеттера. А если нужно добавить какую-то логику, то там используют магию и добавляют функцию, которая автоматически вызывается при изменении поля. Ну то есть по сути это тот же сеттер, только он вызывается при записи в поле. Просто синтаксис другой, а суть та же.
Вот ты предлагаешь интересные вещи, но ничего не поясняешь,а здесь ведь начинающие сидят.
>>2947152
Это паттерн 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.
Вот ты предлагаешь интересные вещи, но ничего не поясняешь,а здесь ведь начинающие сидят.
>>2947152
Это паттерн 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 местах.
Данные могут меняться. В случае с возрастом - абитуриент мог ошибиться при вводе и хочет исправить это.
Прочти книгу по DDD может быть? Ну или выясни, какой архитектурный паттерн используется и прочти документацию по нему.
Есть шанс что ты сломаешься и в итоге уйдешь на битрикс как большинство вкатунов
Ну я просто изначально в бекенд метил, но сильно перекосило в сторону нюхта и вуе. Единственное, что меня удерживает от вакансий чистого фронта - это ебануштейший отбор и собеседования
В смысле? Ты же эксперт уже. Давно работаешь?
>Ну то есть ты хочешь называть сущности "модель", но "модель" более широкое понятие и включает в себя и сервисы для обработки сущностей.
А я тебе сказал что тебе в твоей терминологии не нужен термин "сущность". Ты все равно используешь его неправильно. Достаточно просто различать классы по их названию и назначению. С тем же успехом ты мог бы говорить: вот этот объект кирпичик, а вот этот сервис, не перепутай. А если перепутаешь, то что? Да ничего. От того что он кирпичик тебе ни жарко ни холодно.
>Также, судя по датам
>гуглить такие темы бесперспективная задача
>Модель содержит бизнес-логику приложения. Роутинг это не бизнес-логика, а техническая штука.
Да ну? Права доступа это не бизнес логика? Параметры ссылки это не бизнес логика?
>>2948083
>термины для обозначения классов, которые представляют какую-то сущность
Классы не представляют никакой сущности. У тебя есть "модель" реального мира, именно по параметру "моделирования" мы и отличаем этот набор объектов от любого другого. Отличать их между собой никакого практического смысла нет. В DDD они отличаются именно из-за способа моделирования. Нет способа - нет отличий.
>>2948113
>Это паттерн ValueObject.
Это паттерн Object. Ну знаешь, из ооп. Ты инкапсулируешь поведение, а система типов гарантирует правильность программы. Оо "ебать его в сраку" п.
>будут еще бакалавры и магистры и у них тоже есть Возраст, но с другими ограничениями. Твой подход не сработает.
И что же мне помешает держать одни ограничения в одном месте, а другие в другом? Тупость?
>Также, зачем ты ставишь скобки после class не по PSR. Ставь нормально и не сбивай людей с толку.
Я делаю это чтобы листинг уместился на один экран. И вообще в 2к23 форматирование это ебля того кому не нравится формат. Отформатируй и перепости - помоги людям не сбиваться с толку. Делов-то - ctrl alt L нажать.
>Данные могут меняться. В случае с возрастом - абитуриент мог ошибиться при вводе и хочет исправить это.
Модель мутабельна когда мы моделируем смену возраста и иммутабельна когда мы моделируем как студень чешет нос. Легко и просто. Железная гарантия что при прикосновению к носу яйца не отвалятся.
Короче все. Завязываем бессмысленный срач. Никакой полезной нагрузки он не несет. "Учиться" чему-то в таком формате невозможно, а писать простыни текста мне впадлу.
>Ну то есть ты хочешь называть сущности "модель", но "модель" более широкое понятие и включает в себя и сервисы для обработки сущностей.
А я тебе сказал что тебе в твоей терминологии не нужен термин "сущность". Ты все равно используешь его неправильно. Достаточно просто различать классы по их названию и назначению. С тем же успехом ты мог бы говорить: вот этот объект кирпичик, а вот этот сервис, не перепутай. А если перепутаешь, то что? Да ничего. От того что он кирпичик тебе ни жарко ни холодно.
>Также, судя по датам
>гуглить такие темы бесперспективная задача
>Модель содержит бизнес-логику приложения. Роутинг это не бизнес-логика, а техническая штука.
Да ну? Права доступа это не бизнес логика? Параметры ссылки это не бизнес логика?
>>2948083
>термины для обозначения классов, которые представляют какую-то сущность
Классы не представляют никакой сущности. У тебя есть "модель" реального мира, именно по параметру "моделирования" мы и отличаем этот набор объектов от любого другого. Отличать их между собой никакого практического смысла нет. В DDD они отличаются именно из-за способа моделирования. Нет способа - нет отличий.
>>2948113
>Это паттерн ValueObject.
Это паттерн Object. Ну знаешь, из ооп. Ты инкапсулируешь поведение, а система типов гарантирует правильность программы. Оо "ебать его в сраку" п.
>будут еще бакалавры и магистры и у них тоже есть Возраст, но с другими ограничениями. Твой подход не сработает.
И что же мне помешает держать одни ограничения в одном месте, а другие в другом? Тупость?
>Также, зачем ты ставишь скобки после class не по PSR. Ставь нормально и не сбивай людей с толку.
Я делаю это чтобы листинг уместился на один экран. И вообще в 2к23 форматирование это ебля того кому не нравится формат. Отформатируй и перепости - помоги людям не сбиваться с толку. Делов-то - ctrl alt L нажать.
>Данные могут меняться. В случае с возрастом - абитуриент мог ошибиться при вводе и хочет исправить это.
Модель мутабельна когда мы моделируем смену возраста и иммутабельна когда мы моделируем как студень чешет нос. Легко и просто. Железная гарантия что при прикосновению к носу яйца не отвалятся.
Короче все. Завязываем бессмысленный срач. Никакой полезной нагрузки он не несет. "Учиться" чему-то в таком формате невозможно, а писать простыни текста мне впадлу.
>Я думаю, что джун должен сразу писать правильный ООП-код
Я с тобой полностью согласен.
Я имел ввиду глубину знаний.
Я себе представляю так, что от джуна требуется определенная глубина знаний в каждом из вопросов.
Просто из этой задачи я ушел куда-то в чтение проектирование систем. Тут еще советуют какое-то DDD. Короче. У меня сложилось впечатление, что я пытаюсь копать так глубо, сколько не требуется пока что от джуна. Я думаю, что знания должны накладываться слоями. От простого к сложному. И для того, чтобы усвоить определенные знания, человек должен быть готов.
Он должен обладать такими знаниями, которые позволят ему понять новое знание.
Окей.
Я это понял так:
Класс errorList состоит из:
Свойств (например, name, surname)
Трех методов, 2 из которых работают с отдельными полями, а один - со всеми.
Что в таком случае класс Error?
Судя по инструкции создания объекта, в конструктор передается строка с описанием ошибки, значит у него есть какое-то поле, которое хранит ошибку.
А что если поле не прошло валидацию сразу по нескольким правилам? В конструкторе должен быть метод, которые берет текст ошибки (которую отдали в параметры конструктора) и склеивает его с другими текстами ошибок, а потом кладет в значение поля?
Спасибо, что отвечаешь мне.
Я тут еще подумал.
Вернемся немного назад.
Мы обсуждали метод класса 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. В каком файле я буду описывать регистрацию сервисов? Это лишь пример, чтобы показать, что я пока что не понимаю где какая будет инструкция.
Не знаю, поймешь ли ты меня, я столько каши понаписал.
Проспавшись и пробздевшись, я, кажется, начал понимать, о чём ты говоришь. >>2948113 Этот помидор подтвердил мои догадки. В таком случае, я бы завёл
class Age
И там внутри уже пердолил всякие проверки и т. д. В данном же случае, Yoba - условно не делим. При большом желании можно, конечно, но зачем? На практике, там не число, а строка определённого формата. И, собственно, класс, в моём примере обозначенный Yoba, и нужен лишь для того, чтобы хранить эту строку определённого формата и не давать его нарушать.
То есть, твои ValidAge NotValidAge ValidateAge - более заёбная правильная наверное реализация того, чем является класс Yoba. А твой User, это, по сути, не мой Yoba, а тот, одно из свойств которого - объект Yoba.
Если следовать твоему пути, то должно получиться вообще как-то так, насколько я понимаю: https://3v4l.org/pJbBI#v8.3.0
>>2948116
Не понял, что у меня не PSR?
>>2948119
Прикольно вы придумали, что я задался этим вопросом в процессе решения какой-то задачи про студентов.
Только ещё вопрос. Я нуб и как-то вообще пропустил момент, это что? Область видимости для аргументов метода? Как оно работает? По каким словам гуглить?
Я тут еще подумал.
Да. Я сейчас понял, что я снова забыл, что Студент и Список ошибок - не зависимости.
Значит нужно выкинуть их из полей Validator?
Я думаю, строку $error = $validator->validate($enrollee)
Нужно понимать так:
error - переменная, которая будет хранить ссылку на объект.
validate возвращает объект.
Зависимостью называется не любой класс, а только класс из другого модуля. Если оба класса часть "модели" то это не зависимость. Модель не может зависеть сама от себя.
Список ошибок не надо передавать в валидатор. Валидатор сам создаст его и заполнит по итогам проверки.
Ну подумай сам: мы вызываем валидатор, даем ему студента, он нам взамен возвращает список найденных ошибок. Зачем список ошибок передавать в валидатор? Зачем он нужен валидатору? Наоборот, логично: пока мы не передадим студента, мы не получим список ошибок. Нет другого способа его получить (разве что создать и заполнить самому, увы, в PHP это запретить пока что нельзя).
Ты наверно это придумал после чтения статьи про DI. Но в DI мы передаем в конструктор классы-сервисы, настройки, опции. Список ошибок это просто данные, и валидатор его создает сам. У нас также нет нужны подменять список ошибок на что-то другое, и передавать его в конструктор нам не нужно.
Что касается устройства списка, не надо делать в нем никакие поля. Сделай просто массив для хранения ошибок. Тогда у тебя получится универсальный список ошибок, который не только для студентов годится, а для любых других объектов.
>из другого модуля
Что такое модуль?
Что такое модель?
Можешь привести примеры в контексте этой задачи?
Все. Архитектура, производительность, коммьюенити, проекты
450x360, 0:58
Так какого хуя ты лезешь в архитектуру если определений не знаешь?
Если ты не умеешь писать код. Не знаешь БАЗУ. То какого хуя ты лезешь туда, куда не каждый мидл полезет?
Я и не собирался лезть в архитектуру, когда начинал решать эту задачу.
Но написав код, я понял, что у меня получается хуйня. И мало того, что хуйня, так мне стало трудно ориентироваться в этой хуйне.
С этого момента и начались эти темы про проектирование, архитектуру.
А вопросы о том, что такое модель и модуль я задал для того, чтобы сообщение, которое написал мне анон, имело для меня практический смысл. Зачем писать что-то человеку так, как он не поймет?
У всех хуйня и всем трудно. Начинать надо сначала.
1) Ты не умеешь писать код, никакой, не только ооп.
2) Ты не знаешь базовых определений.
3) Вместо того чтобы нормально учиться ты впитываешь шизоидный поток сознания на двачах.
Конкретно, какой кусок ты не понял?
>Ты не умеешь писать код, никакой, не только ооп.
Да.
И я учусь.
И все эти тупые вопросы, которые я задаю в треде - часть обучения.
>Ты не знаешь базовых определений.
Да, не знаю.
>Вместо того чтобы нормально учиться ты впитываешь шизоидный поток сознания на двачах
Что такое шизоидный поток сознания на двачах?
Что такое нормально учиться?
>И все эти тупые вопросы, которые я задаю в треде - часть обучения.
Если ты получаешь правильные ответы или вообще получаешь вменяемые ответы. Если ты понимаешь что в этих ответах сказано.
>Что такое шизоидный поток сознания на двачах?
Тред, в котором неколько человек одновременно спорят друг с другом в твит формате. Да еще и с рассинхроном больше чем в сутки. Даже человеку, который и так все знает, нужно прилагать большие усилия чтобы просто понять с кем и о чем он пытается говорить.
>Что такое нормально учиться?
Иметь нормальный, последовательный план обучения. Ты в универе учился? Или хотя бы в шараге?
>Ты в универе учился? Или хотя бы в шараге?
По программированию - нет.
>Иметь нормальный, последовательный план обучения.
Какой бы ты список тем для меня составил?
Звучит нагло, но если у тебя есть желание поделиться опытом - можешь написать. Я приму его к сведению и попробую сопоставить с моей организацией обучения.
Сейчас я занимаюсь так:
4 часа на задачу Список Студента (из-за того, что я не решил все вопросы с одним аноном из треда я занимаюсь тем же, чем занимаюсь следующие 4 часа)
4 часа на чтение материала по роад карте.
Вот такое обучение...
Еще я начал читать книгу СИКП.
>По программированию - нет.
Нет никакой разницы.
>Какой бы ты список тем для меня составил?
И как ты себе это представляешь? За несколько минут составить нормальную программу обучения для незнакомого человека, который несколько лет хуй пойми чем занимался. Так не бывает.
>Сейчас я занимаюсь так:
>4 часа на задачу Список Студента
Ты занимаешься хуйней. Если бы ты по четыре часа в сутки писал код всю последнюю неделю, то у тебя была бы сотня вариантов одной только валидации.
>И как ты себе это представляешь? За несколько минут составить нормальную программу обучения для незнакомого человека, который несколько лет хуй пойми чем занимался. Так не бывает.
Да нет, хотя бы набросать список направлений куда смотреть.
Ну да, я виноват, использовал слово "темы".
Ты пишешь "ты не умеешь писать код". Окей. Я согласен с тобой.
В каком направлении мне смотреть, чтобы исправить это?
>Ты занимаешься хуйней. Если бы ты по четыре часа в сутки писал код всю последнюю неделю, то у тебя была бы сотня вариантов одной только валидации.
Ну, мб и хуйней.
В эти 4-ре часа входят не только написание кода, но и разбор ответов с двача, касаемо этой задачи (Да-да, иногда мозги так не работают, что приходится читать-перечитывать), разбор чужого кода (который кидают в треде), чтение статей (связанных с вещами, о которых я должен знать, чтобы решить задачу). Ты можешь сказать, что и это хуйня. Ну, мне и похуй. Я занимаюсь так, как могу.
>Я занимаюсь так, как могу.
Ты просто делаешь вид что занимаешься, вот и все. Это такой способ развлечься на двачах. Только вот анонам ты не рассказал что просто убиваешь время и они всерьез пытаются тебе что-то объяснить. Я тебе предлагал заняться обучением всерьез, но ты сказал "пук среньк".
>Ты просто делаешь вид что занимаешься
Считай так.
Я не преподаватель, чтобы уметь грамотно организовывать учебу.
Если нет варианта лучше чем тот, что уже есть - я остаюсь заниматься по старому методу. Этим я руководствуюсь, когда планирую свое обучение.
>Я тебе предлагал заняться обучением всерьез, но ты сказал "пук среньк".
Если голосом будешь говорить один ты, то, окей, я согласен. Мне голосом говорить нечего. Мои бе-ме вряд-ли тебе хочется слушать. В тексте можно подумать, выбрать формулировку, а в голосе?
>Тогда у тебя получится универсальный список ошибок, который не только для студентов годится, а для любых других объектов.
Почему бы тогда не сделать интерфейс для класса ErrorList?
Или наследование.
>Что касается устройства списка, не надо делать в нем никакие поля. Сделай просто массив для хранения ошибок.
Я не понимаю что это означает.
С тем аноном у нас два класса, связанные с ошибками - ErrorList и Error.
У ErrorList не будет полей name, surname и т.д., а будет одно поле-массив, где ключ - название поля, а элемент - ссылка на объект Error?
Или же ErrorList вообще не класс, а массив?
Вот, посмотри, я дописал классы, которые мы с тобой обсуждали.
Это черновой вариант.
Я не оттачивал его.
Потому что у меня не варит голова.
Да и не все я уточнил у тебя.
https://3v4l.org/ElabY
Ты можешь написать мне, в том ли направлении я тебя понимаю или нет.
Если не в том, то что конкретно исправить и почему.
Если где-то забыл тайп-хинт или перепутал что-то в цикле, то извиняюсь, я хотел лишь в общих чертах набросать то, что мы с тобой обсуждали.
Проверять и исправлять все то, что я там написал, я планирую завтра, на свежую голову.
Сап. Работаю бэк разрабом. Есть задание, при определенных действиях удалить все бонусные карты пользователя и создать для него новую. Сперва я хотел создать запрос на удаление всех бонусных карт. Старший разраб сказал, что не все так просто из-за обмена. Из-за этого мы поднимемся на этаж выше, устроим созвон с 1сниками. Я не совсем понял, что за проблема может быть с обменом? Что это значит?
ты тупой, насколько рынок перегрет пхпшниками, например во фронте макак куча
Посадят тебя на пикрелейтед стул, и начнут спрашивать что такое модель и модуль. Ответишь неправильно - отдадут тебя 1сникам.
И в чем я не прав?
В прошлых 2-3 тредах было погугли. Если не найдешь, иди на главный торрент-трекер страны, там в комментариях на последних страницах описан способ
public function foo () {
echo $this->editable;
}
Свойство $editable у каждого дочернего класса имеет своё значение, у AAA его, по сути, быть не должно, в этом нет смысла. При этом, значение не изменяется и задаётся в коде класса.
Вопрос - как это правильно сделать? По сути, мне нужна абстрактная константа. Чтобы я в AAA её объявил, а в детях задал её значения. Но абстрактных констант в PHP нет.
Или факт того, что я в AAA объявляю метод, который рассчитан на "$this->editable", который в AAA вообще не имеет смысла, а смысл имеет лишь в его детях, в принципе неадекватен? И правильнее скопипастить этот одинаковый метод в каждом из детей?
А зачем делать отдельные поля?
Забываешь добавить Bar::TEST, скрипт не показывает никаких ошибок. Чисто технически, мне полностью подходит объявление в родительском-абстрактном классе абстрактного метода типа:
abstract public function returnConst (): string;
А у детей:
public function returnConst (): string {
return "const-value-1";
}
И т. д. Если забудешь у ребёнка объявить returnConst(), интерпретатор тебе на это моментально пожалуется. Но это отдаёт какими-то костылями, я использую метод там, где по сути нужна константа. Это наталкивает меня на мысль о том, что я вообще херню какую-то горожу, раз нормальными средствами это не делается. Или нет, и ситуация, в которой родительский класс оперирует какими-то значением, которое будет определено в его дочерних классах, это вполне нормально?
https://3v4l.org/Uo2fh#v8.3.0
Вот, гляньте. Оно работает ровно так, как мне надо. Но с семантической точки зрения как будто пиздец, гвоздь забиваю микроскопом. Или всё нормально?
Ну и что ты хочешь услышать? Да, технически это работает. Есть ли какие-то другие варианты? А как блядь об этом узнать если ты не рассказываешь что ты пытаешься сделать, а постишь какие-то невнятные иксы и игреки.
Хочу услышать:
1. Выглядит ли код, который я скинул, как хтонический пиздец, или ничего особо криминального в нём нет.
2. Насколько адекватна ситуация, когда в коде РОДИТЕЛЬСКОГО абстрактного класса используется какое-то значение, которого в этом классе нигде нет. И весь расчёт на то, что оно должно быть объявлено в НАСЛЕДНИКЕ этого класса.
Говорю же, меня напрягает отсутствие абстрактных свойств в PHP. Какие причины для их отсутствия? Просто не завезли, или указание на то, что я хочу чего-то неадекватного?
если не забуду скину
А я тебе ответил. Что если у тебя вариантов нет, то какя хуй разница как оно выглядит, вариантов то нет. А понять какие у тебя варианты можно только если ты объяснишь что пытаешься сделать.
По сути ты показал код и спросил: "как мне сделать? так? или угадай как?"
>Говорю же, меня напрягает отсутствие абстрактных свойств в PHP.
А где они есть? В шарпе? А еще?
У меня есть как минимум два варианта.
1. Забить гвоздь микроскопом. Иди сделать всё чётко. Хуй знает, ты не оцениваешь адекватность моего варианта.
https://3v4l.org/CRNMZ#v8.3.0
2. В родителе объявить функцию как абстрактную, и потом реализовать её для каждого ребёнка. Получится копипаст, потому что содержание этой функции у разных детей отличается натурально одним параметром.
https://3v4l.org/t2mUm#v8.3.0
Ты нахуя используешь абстрактный класс вообще? Чтобы вынести в него повторяющийся код. В одном примере повторяющегося кода нет, в другом есть. Вот и все.
Однако если поведение хотя бы в одном наследнике хотя бы немного начнет отличаться, то придется заново выделять повторяющуюся часть и делать рефакторинг.
И если бы ты не вел себя как баран и привел реальный пример, то можно было бы определить возможные подводные камни. А твои высосанные из пальца синтетические умозрительные конструкции специально подстроены под конкретную ситуацию и ни о чем не говорят. "на 100% одинаков" ага, верю.
мимо фуллстек
Ты чё выёбываешься?
> В одном примере повторяющегося кода нет, в другом есть
Ага, 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));
}
}
Минус - здесь все сервисы захардкожены в классе. Плюс - простота и хорошая поддержка типизации (у каждого метода указан тип возврата).
> Просто у меня есть такое представление на задворках ума, что 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));
}
}
Минус - здесь все сервисы захардкожены в классе. Плюс - простота и хорошая поддержка типизации (у каждого метода указан тип возврата).
> В каком файле я буду описывать регистрацию сервисов?
Для варианта с PSR-11 контейнером- можно в bootstrap-файле или в подключаемом из него файле. Для варианта 2 - прямо в классе-контейнере.
У него идея, что для представления возраста используется не 1, а сразу 2 класса: ValidAge с гарантированно валидным содержимым и NotValidAge с невалидным. И ты можешь с помощью тайм-хинтов просто запретить передавать невалидный возраст в функцию.
Хотя мне нравится использование типов для защиты от ошибок, но мне кажется, тут не очень удачно это придумано.
Вот кстати, если тебе неохота, как ты выражешься, писать "простыни текста", то можно же скидывать сюда ссылки на голосовые сообщения через какой-нибудь сервис (только без регистрации плиз и чтобы запись не исчезала через день). Я без проблем их прослушаю, и анон, который учится, думаю, тоже.
Потому, что у нас всего одна реализация и больше не планируется.
Например, что касается классов-правил валидации, у нас их много и можно дописывать новые. Нам надо как-то объяснить людям, как добавлять эти правила. Тут явно нужен интерфейс.
А список ошибок у нас один, и другие нам не понадобятся. Поэтому для него не нужен интерфейс или наследование.
Будет поле типа: array<string, list<Error>>. Для каждого поля мы храним массив объектов Error.
Если тебе непонятен синтакис, то можно посмотреть тут: https://psalm.dev/docs/annotating_code/type_syntax/array_types/
Пчел. Просто создай канал в дискорде и не еби мозги. За пол часа можно обсудить больше чем тут за две недели написано. Записывай, хоть обзаписвайся если тебе это надо.
- добавляй тайп-хинты везде, где можно, чтобы я мог быстрее читать и понимать твой код и все другие, кто его увидит, тоже.
- так как PHP не позволяет описывать структуру массива в тайп-хинте, будет здорово, если ты научишься описывать ее через phpDoc и расширенные аннотации отсюда https://psalm.dev/docs/annotating_code/type_syntax/array_types/
> Код этой функции тоже черновой, потому что я пока что не думал куда всунуть строку "$errorList = new ErrorList;"
С этой строкой все нормально, не надо ничего в ней менять, разве что скобки добавить для конструктора, так как в PSR это требуется.
> foreach ($this->rules as $key => $value)
Надо использовать нормальные имена, а не key/value
> if (empty($result)==false) {
empty очень общая штука, лучше писать более конкретную проверку, вроде $result !== null
> $errorList->add($key);
$errorList->add($key, $error);
> use Getter;
use принято писать в начале описания класса
ErrorList я бы сделал массивом, так как ты к полям обращаешься только через $this->$field, то есть ты с ними работаешь, как с массивом. Но твой вариант конечно тоже можно оставить, только тогда правильнее класс назвать EnrolleeErrorList, то есть не универсальный список ошибок, а список ошибок только для студентов.
> //Я сделал эту функцию приватной, потому что подумал, что она нужна только для работы errorListIsEmpty()
Тебе нужна еще функция для получения списка ошибок для вывода их в форме. Мы же добавляем ошибки, чтобы их выводить потом.
- добавляй тайп-хинты везде, где можно, чтобы я мог быстрее читать и понимать твой код и все другие, кто его увидит, тоже.
- так как PHP не позволяет описывать структуру массива в тайп-хинте, будет здорово, если ты научишься описывать ее через phpDoc и расширенные аннотации отсюда https://psalm.dev/docs/annotating_code/type_syntax/array_types/
> Код этой функции тоже черновой, потому что я пока что не думал куда всунуть строку "$errorList = new ErrorList;"
С этой строкой все нормально, не надо ничего в ней менять, разве что скобки добавить для конструктора, так как в PSR это требуется.
> foreach ($this->rules as $key => $value)
Надо использовать нормальные имена, а не key/value
> if (empty($result)==false) {
empty очень общая штука, лучше писать более конкретную проверку, вроде $result !== null
> $errorList->add($key);
$errorList->add($key, $error);
> use Getter;
use принято писать в начале описания класса
ErrorList я бы сделал массивом, так как ты к полям обращаешься только через $this->$field, то есть ты с ними работаешь, как с массивом. Но твой вариант конечно тоже можно оставить, только тогда правильнее класс назвать EnrolleeErrorList, то есть не универсальный список ошибок, а список ошибок только для студентов.
> //Я сделал эту функцию приватной, потому что подумал, что она нужна только для работы errorListIsEmpty()
Тебе нужна еще функция для получения списка ошибок для вывода их в форме. Мы же добавляем ошибки, чтобы их выводить потом.
Обмен это значит данные из вашей БД копируются в 1С и обратно. Может там к этим картам привязан бухучет (сколько денег потрачено на бонусы), ты их удалишь и у них баланс не сойдется. Глупо же.
Обычно такие данные не удаляют, а лишь скрывают.
Тебе не нужны поля. В базовом классе делай метод
abstract public function foo(): bool;
В наследнике делай метод
public function foo(): bool { return true; }
Вот и все.
Надо использовать абстрактный метод и не пытаться изобрести "абстрактные поля" (что?) или "абстрактные константы".
Также, базовый класс не имеет права обращаться к полям или методам, которых в нем нету, и которые будут добавлены только в наследниках.
> 2. Насколько адекватна ситуация, когда в коде РОДИТЕЛЬСКОГО абстрактного класса используется какое-то значение, которого в этом классе нигде нет. И весь расчёт на то, что оно должно быть объявлено в НАСЛЕДНИКЕ этого класса.
Выглядит как полное непонимание наследования. Так делать нельзя.
> напрягает отсутствие абстрактных свойств в PHP.
А в чем смысл абстрактного свойства. Ты говоришь: хочу, чтобы в каждом наследнике было свойство int $x. Ну так объяви его в базовом классе, зачем его копипастить в каждом наследнике?
А вот абстрактные методы нужны, так как они могут быть разными в каждом классе. Их и надо тут использовать.
Во-первых, тред и ответы в нем видны всем, он даже архивируется.
Во-вторых, в тред я прихожу когда удобно и никого не жду.
В-третьих, в реальном времени начинающему трудно все понять. Ему надо 10 раз погуглить, написать 3 неработающих варианта кода, прежде чем до него дойдет.
Просто если кому-то неохота писать, а удобнее голосом сказать, можно оставить голосовое сообщение.
>Во-первых, тред и ответы в нем видны всем, он даже архивируется.
Старая песня. Дискорд тоже видно всем и там тоже все архивируется. Создай свой канал и прилепи в шапку. Дело не в том кому что ты хочешь показать. Я тебе уже сказал - записывай и пости куда хочешь. Дело в соотношении "сигнал/мусор". У тебя треть поста состоит из цитат, треть ты не договариваешь потому что надо в абзац уложиться, а последнюю треть у тебя переспрашивают.
Средняя статья только по одной из тем в этом треде занимает несколько экранов текста и представляет из себя бедную выдержку из целой книги или даже нескольких. И это еще и в фрагментарном формате. Когда мы просто вываливаем на человека кусочек нашей картины мира, а он без понятия как эта картина сложилась. А потом он спрашивает как мне заинжектить сущность в сервис с помощью di. Совершенно не одупляя ни что такое сущность, ни что такое di, ни зачем вообще что-то куда-то инжектить.
Да чего далеко ходить. Прямо в предыдущем посте ты не понял смысл абстрактного свойства, то ли перепутав его с шизой про "только в наследнике" то ли еще почему. Хотя очевидно что было бы удобно иметь свойство объявленное в базовом классе и которое нужно обязательно переопределять в наследнике. Сколько времени пройдет пока вы просто поймете что хотели друг другу сказать в начале разговора? День? Два?
>Во-вторых, в тред я прихожу когда удобно и никого не жду.
Очень удобно. А главное столько времени сэкономил. Мне интересно сколько тредов эпопея с валидатором займет. Месяц он с ним сидит точно, может больше. Через пол года может выдаст что-то вменяемое. Такая-то учоба.
>В-третьих, в реальном времени начинающему трудно все понять.
Точно так же как и не в реальном. Только в реальном времени есть человек, который сразу уточнит, напишет сниппет, тыкнет пальцем в книгу, приведет пример. И не придется сидеть ждать до завтра чтобы еще раз переспросить, чтобы ты переспросил в ответ.
>Ему надо 10 раз погуглить, написать 3 неработающих варианта кода, прежде чем до него дойдет.
Опять же пусть сидит и гуглит при тебе, пока не дойдет как это правильно делать. Можешь ради интереса поспрашивать что по теме поста нагуглил "валидатор". Гарантирую - фейспалмом ебло разобъешь. То же самое с кодом. Ничего они не пишут, а мгновенно впадают в ступор. Нужно буквально за руку вести, пока они кода бояться перестанут. И нужно обязательно убедиться что он именно зашел в логический тупик, действительно столкнулся с проблемой. понял почему этот вариант не работает. А потом объяснить как этой проблемы избежать. Удачи сделать это в треде, особенно когда тебе даже реальный код не показывают, лол.
Говорил что не буду простыни писать, а в итоге опять насрал. В общем если ты настолько глупенький, что тебе все это нужно объяснять по пять раз, то все еще печальнее. В лучшем случае учеба будет проходить где-то параллельно с тредом. Имаджинировать лицо человека, который реально "учился" в этом потоке сознания лично я не могу.
>Во-первых, тред и ответы в нем видны всем, он даже архивируется.
Старая песня. Дискорд тоже видно всем и там тоже все архивируется. Создай свой канал и прилепи в шапку. Дело не в том кому что ты хочешь показать. Я тебе уже сказал - записывай и пости куда хочешь. Дело в соотношении "сигнал/мусор". У тебя треть поста состоит из цитат, треть ты не договариваешь потому что надо в абзац уложиться, а последнюю треть у тебя переспрашивают.
Средняя статья только по одной из тем в этом треде занимает несколько экранов текста и представляет из себя бедную выдержку из целой книги или даже нескольких. И это еще и в фрагментарном формате. Когда мы просто вываливаем на человека кусочек нашей картины мира, а он без понятия как эта картина сложилась. А потом он спрашивает как мне заинжектить сущность в сервис с помощью di. Совершенно не одупляя ни что такое сущность, ни что такое di, ни зачем вообще что-то куда-то инжектить.
Да чего далеко ходить. Прямо в предыдущем посте ты не понял смысл абстрактного свойства, то ли перепутав его с шизой про "только в наследнике" то ли еще почему. Хотя очевидно что было бы удобно иметь свойство объявленное в базовом классе и которое нужно обязательно переопределять в наследнике. Сколько времени пройдет пока вы просто поймете что хотели друг другу сказать в начале разговора? День? Два?
>Во-вторых, в тред я прихожу когда удобно и никого не жду.
Очень удобно. А главное столько времени сэкономил. Мне интересно сколько тредов эпопея с валидатором займет. Месяц он с ним сидит точно, может больше. Через пол года может выдаст что-то вменяемое. Такая-то учоба.
>В-третьих, в реальном времени начинающему трудно все понять.
Точно так же как и не в реальном. Только в реальном времени есть человек, который сразу уточнит, напишет сниппет, тыкнет пальцем в книгу, приведет пример. И не придется сидеть ждать до завтра чтобы еще раз переспросить, чтобы ты переспросил в ответ.
>Ему надо 10 раз погуглить, написать 3 неработающих варианта кода, прежде чем до него дойдет.
Опять же пусть сидит и гуглит при тебе, пока не дойдет как это правильно делать. Можешь ради интереса поспрашивать что по теме поста нагуглил "валидатор". Гарантирую - фейспалмом ебло разобъешь. То же самое с кодом. Ничего они не пишут, а мгновенно впадают в ступор. Нужно буквально за руку вести, пока они кода бояться перестанут. И нужно обязательно убедиться что он именно зашел в логический тупик, действительно столкнулся с проблемой. понял почему этот вариант не работает. А потом объяснить как этой проблемы избежать. Удачи сделать это в треде, особенно когда тебе даже реальный код не показывают, лол.
Говорил что не буду простыни писать, а в итоге опять насрал. В общем если ты настолько глупенький, что тебе все это нужно объяснять по пять раз, то все еще печальнее. В лучшем случае учеба будет проходить где-то параллельно с тредом. Имаджинировать лицо человека, который реально "учился" в этом потоке сознания лично я не могу.
Не мучайся, братишка. На - попустись.
Типичный битриксойд!
А если в 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, ты чё, охуел. С методами так можно, со свойствами - нет.
> Ну так объяви его в базовом классе, зачем его копипастить в каждом наследнике?
Затем, что его значение фиксировано, как у константы. И для каждого класса-наследника оно отличается. Повторюсь, по смыслу это константа. Не переменная, в которую я кладу значение, извлекаю, меняю и т. д. А какое-то статичное значение. Уникальное для каждого из классов-наследников.
А если в 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, а не тернарному оператору?
Неужели стоит так много учитывать?
Я планировал просто проверять каждый символ строки со списком разрешенных значений.
Например, для имени - только a-z A-Z а-я А-Я, пробел, апостроф, дефис.
Даже если отбросить SQL-инъекцию (потому что это задача уже плейсхолдеров, а не валидации), то имя, написанное как SQL-запрос выглядит тупо.
Теперь я понимаю, что побуквенно проверять - глупая идея.
Но все равно.
Чего так сложно все с подбором регулярок для валидации?
Какое обсуждение не откроешь - множество мнений.
С английским беда. Написано же "sanitize".
Тебе придется согласиться что любой вводимый пользователем текст это юникод. Иначе даже казахские братушки отпадают. То есть в имени может быть литерали: ⚰️⚰️🪦🏳️🌈. И ничего страшного в этом нет. Потому что самое хуевое что можно сделать - это отказать в обслуживании кому-то потому что у него казахское ебало.
А от "страшного" избавляется именно санитайзер. В каждом фреймворке есть подобный функционал. Санитайзер автоматически изменяет данные. Например преобразует в число, удаляет пробелы в начале строки или удаляет определенные html теги.
Ты хочешь сказать, что мне нужно делать упор не на то, чтобы уметь отбрасывать некорректные имена, а принимать их и заменять в коде некорректные символы?
Не: какие вариации имен/фамилий существуют
А: какие вариации имен/фамилий готов принять мой сайт
Я ведь, да и другие аноны не кривят лицом, когда их просят в документах написать ФИО не кириллицей, а латиницей.
Нет, я хочу сказать что охуенно посрал. А что там получилось что-то про санитайзер? Перепутал, не туда запостил.
Посрал охуенно, и хуем до потолка стрельнул.
Почему IP-протокол находится в разделе о протоколах транспортного уровня, если он, вроде как, является протоколом сетевого уровня?
Это сахарок ака Null Coalescing Operator, с труъ тернарными оператором получилось бы так:
return is_null($values[$number]) ? (is_null($defaultValue) ? 'value not found' : $defaultValue) : $values[$number];
Спрашивают. Посмотри например те же реальные собеседования от канала с areaweb.
мимо фулстек
Идея вообще была в том, чтобы учиться по статьям, а в треде уточнять непонятные моменты и проверять задания. Статью можно написать один раз, а прочтет ее много человек и это лучше, чем каждому объяснять лично.
Так аргумент можно вынести в абстрактную функцию:
public function foo () {
someCode();
someCode2();
someCode3();
bar($this->getEditable());
someCodeA();
SomeCodeB()
}
abstract private function getEditable(): string;
Не понимаю, в чем проблема.
> Вот меня что-то и напрягало. Ситуацию не спасает то, что класс-родитель - абстрактный? То есть, реализован объектом он быть не может.
> Как ты прокомментируешь этот код: https://3v4l.org/obOn0#v8.3.0 ?
Ну это плохой код, и ты сам расписал, почему. Я не понимаю, что тебе мешает использовать абстрактный метод, который для таких случаев и придуман.
> И это копипасть каждому классу-наследнику ради изменения одного string-аргумента, передаваемого в функцию?
Ну вот тут https://3v4l.org/CRNMZ#v8.3.0 никакой копипасты нет же.
И кстати этой твой пример я бы сделал вообще так: https://3v4l.org/BsCvr#v8.3.0
Просто не привык. Иногда удобнее тернарник, иногда удобнее if написать, главное, чтобы код был понятным.
>>2951830
Там статья про имена во всем мире, которые, действительно, могут писаться самыми разными способами. Но мы можем ограничить себя только допустимыми в России (или в твоей стране, если ты не из России) именами. И для них можно попробовать найти официальные списки допустимых символов.
Официального списка символов я не нашел, но вот, какие символы разрешает использовать налоговая при обмене данными: https://www.consultant.ru/document/cons_doc_LAW_410788/763145d8bce1267a0501117c28ebd122104c1b8e/
Вот пример того, как люди мучаются из-за невозможности передать данные в ГНИВЦ: https://forum.gnivc.ru/index.php?/topic/2176-zabrakovany-soobscheniia-znaki-prepinaniia-v-polia/
Вот пример, как кредитные организации мучаются при передаче данных в кредитное бюро: https://asros.ru/upload/iblock/190/2y1xw5anyssa7scmdh5xfxcmklo9wxa7/output-_14_.pdf
Видимо, в госорганах тоже нет единого стандарта, и они добавляют разрешенные символы в ответ на обращения.
Это плохой совет. sanitize значит, что ты принимаешь от пользователя любые данные, но по-тихому удаляешь из них запрещенные символы, вместо того, чтобы указать на ошибку.
Не надо так делать.
> в имени может быть литерали: ⚰️⚰️🪦🏳️🌈.
Не может.
> Санитайзер автоматически изменяет данные.
ну и глупость. Получается, ты в базу запишешь не то, что ввел пользователь, то есть неверные данные.
Мне кажется, что IP протокол сетевого, а TCP - транспортного уровня. Но тут, конечно, лучше бы смотреть авторитетный источник. То есть определения уровней OSI и проверять, каким уровням он соответствует.
Надо понимать, что OSI это скорее абстракция, как можно разбивать протоколы на уровни. В реальности они могут не соответствовать OSI, хотя в общем, эта модель довольно близка к реальности.
>Не надо так делать.
Не надо кому?
В реальном мире за то что кто-то вообще попал на твою страницу заплачены деньги. Если этот кто-то развернется и уйдет из-за того что у него буква некрасивая в email'е хуже только тебе.
На самом деле вариантов всего два: экранирование и санитайзинг.
Ну так надо использовать экранирование. А санитайзинг не надо вообще нигде использовать, кроме технических вещей (типа номера страницы в URL, который пользователя руками не вводит).
И проверка валидности есть. Не знаю, как сейчас, а в магазинах типа DNS номер телефона не только проверяется на формат, но и проверяется через СМС. У тебя только стационарный телефон или вообще нет телефона? Иди тогда отсюда.
То же самое касается телеграмов, соцсетей, доставки, госуслуг. Они все проверяют и если ты вводишь что-то не то, то тебе отказывают в получении услугу.
Так что не рассказывай мне про реальный мир.
>Ну так надо использовать экранирование.
Сколько раз? Надо ли экранировать экранирующие символы?
Если ты никогда в реальном проекте не работал, то нахуй ты это пишешь? Кто реально пытался текст экранировать глядя на пикрелейтед пример нервно икает.
Гений, можешь хотя бы предположить почему так получилось?
Допустим что стэк у меня Нджинкс и пхп-фпм, как мне сделать чтоб ссылка не содержала расширений пхп?
Экранирование делается в том месте, где данные используются, а не в начале обрабочика. Если ты хочешь вставить данные в SQL-запрос, ты используешь подготовленные запросы. Если ты хочешь их вывести в HTML, ты используешь шаблонизатор с поддержкой экранирования.
И все символы отображаются и сохраняются корректно, и ничего не ломается.
А ты со свои санитайзеры из какого-то древнего неграмотного учебника достал скорее всего.
Как я уже писал выше, очистку можно применять для технических данных, которые пользователь не вводит вручную: номера страниц в URL, id комментария и тд.
Вроде напал на нужный след в гугле
Но если есть хороший человек готовый написать где это в конфиге сразу,то тысяча поцелуев ему
Ссылка на что?
Есть твой сервер с файлами, в твоем случае php скриптами и php-fpm для их выполнения.
Есть промежуточное звено nginx, которое получает ссылку, читает её и решает какой из файлов на твоем сервере был запрошен.
Не нравятся прямые ссылки на файлы - добавляешь в nginx настройку согласно которой любая ссылка со словом "пидар" будет направлена на pidar.php
Очевидно что прописывать каждый вариант руками и настолько сильно зависеть от настроек ngix не выгодно. Поэтому обычно прописывают одно правило: все ссылки с твоим доменом ведут на index.php на твоем сервере, а уже index.php смотрит на параметры ссылки и решает какой скрипт выполнить.
>И все символы отображаются и сохраняются корректно, и ничего не ломается.
Ты слепой или тупой? Я тебе привел пример как все сломалось. Потому что оно постоянно будет ломаться.
Я потому и сказал что ты ни дня в реальном проекте не работал. Хранить у себя sql инъекции и xss скрипты в надежде что ты все правильно сэкранировал. Это каким же ебланом надо быть.
>доменом ведут на index.php на твоем
Спасибо дядя, тоже сейчас в ходе поисков наткнулся на эту инфу,
поцеловал в пенис
Наверное я должен показывать код, да?
Код Validator: https://3v4l.org/AC3p8
Код ValidationError: https://3v4l.org/u0bd4
Код ErrorList: https://3v4l.org/HPpGT
Код, который проверяет качество символов по регулярному выражению: https://3v4l.org/Z3pIi
Код, который проверяет минимальное количество символов:
https://3v4l.org/eThug
Код, который проверяет максимальное количество символов: https://3v4l.org/UIZpF
Код, который проверят пустое значение или нет: https://3v4l.org/CequU
Минимальное и максимальное количество символов для каждого поля:
Имя: 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
Класса, который проверяет уникальность емайл - пока что нет, потому что я не добрался до написания класса работы с БД.
Я решил, что сначала напишу его, а потом допишу проверку.
Наверное я должен показывать код, да?
Код Validator: https://3v4l.org/AC3p8
Код ValidationError: https://3v4l.org/u0bd4
Код ErrorList: https://3v4l.org/HPpGT
Код, который проверяет качество символов по регулярному выражению: https://3v4l.org/Z3pIi
Код, который проверяет минимальное количество символов:
https://3v4l.org/eThug
Код, который проверяет максимальное количество символов: https://3v4l.org/UIZpF
Код, который проверят пустое значение или нет: https://3v4l.org/CequU
Минимальное и максимальное количество символов для каждого поля:
Имя: 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
Класса, который проверяет уникальность емайл - пока что нет, потому что я не добрался до написания класса работы с БД.
Я решил, что сначала напишу его, а потом допишу проверку.
Так я так и сделал, ёпт! Глянь >>2950316
Просто начал сомневаться на тему того, адекватно ли так делать, или я забиваю гвоздь микроскопом. Метод, который по сути ведёт себя как константа, возвращая всегда одно и то же неизменяемое значение.
>>2952116
Ну нет, это вообще мимо. На практике же дочерние классы своими внутренностями отличаются сильнее. В одном такие-то дополнительные методы, в другом иные.
В общем, всем спасибо, остановлюсь на абстрактном методе, потому что чисто технически это работает именно так, как мне надо. А из обратной связи я получил, в сущности, следующее:
1. Твой код полная хуйня, а мать - шлюха.
2. Делай абстрактный метод.
Первое не слишком конструктивно, остаётся второе.
Или всё таки уже норм распространен подход с отдельным жс кодом на вью\реакте?
Я тебе сразу указал что один вариант исключает дублирование кода, а другой нет.
А когда спросил какая у тебя цель - ты полез в залупу.
Очевидно что если цель "избавится от дублирования кода", то нужно выбирать вариант без дублирования. Но ты-то блядь свою цель так и не озвучил.
>Вот код. Он работает так как мне надо?
>А как тебе надо? Что ты делаешь вообще?
>Секрет.
А теперь ты пишешь: "это работает именно так, как мне надо". В натуре не очень конструктивно. Закидывать долбоебу в черный ящик какой-то код, чтобы услышать так ли он с его точки зрения работает. С первоклассником конструктивнее общаться, чем с шизами с двачей.
Пока я решал свои вкатунские задачи, я пользовался переменными, массивами...И не чувствовал ощущения, что мне чего-то не хватает.
Пробежался по гуглу.
Узнал, что есть всякие стеки, очереди.
Но так и не понял зачем они нужны.
Вот я решаю задачу.
Как понять, что стоит представить данные в виде определенной
структуры данных?
Насколько вообще часто программист имеет дело с чем-то кроме массива?
Ты пхп макака, твой код выполняется в виртуальной среде у тебя даже массивов нет, только хеш мапы и объекты.
Стек он стек именно потому что при такой структуре нужно меньше действий процессора и памяти. Ну напишешь ты стек на пхп, и чего? Прироста нет, пользы нет, лишняя абстракция.
>Как понять, что стоит представить данные в виде определенной
структуры данных?
Никак. Структуры данных выбираются исходя из приоритетов в расходовании производительности/памяти, пикрелейтед. А написать что-то быстрее пхпшных "массивов" ты физически не можешь, потому что они написаны на C.
У пхп есть своя сфера низкоуровневых оптимизаций. Называется "опкоды" https://php.watch/articles/php-dump-opcodes Это то во что транслируется твой пхп код. И можно написать код, который будет транслироваться в меньшее количество действий и будет немного производительнее. На практике таким никто не занимается, потому что для веба пхп достаточно быстрый и если не творить хуйню типа регулярных выражений в цикле, то сам пхп никогда бутылочным горлышком не будет.
Если посмотришь на трассировку симфони, то твой код это та маленькая синенькая пиздюлька перед твигом, а любой запрос в базу, даже SELECT 1; будет выполняться минимум 10ms, то есть в двое дольше.
Анон, ты вроде шаришь, подскажи если смысл лезть вкатуну в пхп в 2024? И почему/зачем ты сидишь на пхп?
Сижу потому что слезать = становиться опять джуном, со всеми вытекающими.
Если с полного нуля, то нет. Единственное преимущество пхп - меньше ебланов с улицы ломятся в джуны. Больше преимуществ я не вижу.
Если нужны быстрые бабки, то есть вротэнд.
Если нужна карьера, то любой энтерпрайз язык. Изучишь один - считай изучил все. А изучив пхп ты изучил только микромир пхп.
Для души и хобби тоже нахуй не надо. Пхп многосложный, косноязычный, забитый мусором. Красиво на нем не сделаешь.
Тебе зачем пхп вообще?
> if (!$result===null) {
надо писать !== , или можно просто if ($result), это работает так как if преобразует значение в тип bool как описано в мануале https://www.php.net/manual/ru/language.types.boolean.php#language.types.boolean.casting
> $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:
NotBlankValidator, LengthValidator, RegexValidator, и другие названия можно увидеть тут: https://symfony.com/doc/current/reference/constraints.html
> Это поле может содержать следующие символы - %s. Проверьте, не использовали ли Вы символ, который не входит в этот список.
А почему тупая железяка не может проверить сама и написать, какой символ неправильный, а не загадывать ребусы человеку?
> public function __invoke (mixed $value): ?string
лучше возвращать ?ValidationError, а не ?string, как я написал выше
> iconv_strlen($value);
Обычно используют mb_strlen... но твой вариант тоже годится.
В регулярке для email проблема: в доменах может быть и минус, и цифры по типу
Некоторые рекомендуют просто проверять, что :
- в email есть @ и хотя бы 1 точка справа от @
- в email нет пробелов и других запрещенных символов (каких, подскажет интуиция)
То есть, проверка email нужна в первую очередь для защиты от опечаток, когда люди забывают ставить собачку либо ставят пробел вместо точки.
Но можно просто доработать твою регулярку.
Проблема с очень сложными регулярками в том, что их сложно читать, проверять на правильность, и трудно сообщать ошибки. Например, твоя регулярка требует писать имя с большой буквы. Если я напишу с маленькой, она мне об этом скажет, или напишет, что я использовал запрещенный символ? Лучше было сделать 2 проверки, одна на запрещенные символы, другая на большую букву в начале (а еще можно автоматически делать первую букву большой и не мучать пользователя).
> год рождения:
желательно проверять, что он не меньше 1900 и не больше текущего - 12.
> if (!$result===null) {
надо писать !== , или можно просто if ($result), это работает так как if преобразует значение в тип bool как описано в мануале https://www.php.net/manual/ru/language.types.boolean.php#language.types.boolean.casting
> $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:
NotBlankValidator, LengthValidator, RegexValidator, и другие названия можно увидеть тут: https://symfony.com/doc/current/reference/constraints.html
> Это поле может содержать следующие символы - %s. Проверьте, не использовали ли Вы символ, который не входит в этот список.
А почему тупая железяка не может проверить сама и написать, какой символ неправильный, а не загадывать ребусы человеку?
> public function __invoke (mixed $value): ?string
лучше возвращать ?ValidationError, а не ?string, как я написал выше
> iconv_strlen($value);
Обычно используют mb_strlen... но твой вариант тоже годится.
В регулярке для email проблема: в доменах может быть и минус, и цифры по типу
Некоторые рекомендуют просто проверять, что :
- в email есть @ и хотя бы 1 точка справа от @
- в email нет пробелов и других запрещенных символов (каких, подскажет интуиция)
То есть, проверка email нужна в первую очередь для защиты от опечаток, когда люди забывают ставить собачку либо ставят пробел вместо точки.
Но можно просто доработать твою регулярку.
Проблема с очень сложными регулярками в том, что их сложно читать, проверять на правильность, и трудно сообщать ошибки. Например, твоя регулярка требует писать имя с большой буквы. Если я напишу с маленькой, она мне об этом скажет, или напишет, что я использовал запрещенный символ? Лучше было сделать 2 проверки, одна на запрещенные символы, другая на большую букву в начале (а еще можно автоматически делать первую букву большой и не мучать пользователя).
> год рождения:
желательно проверять, что он не меньше 1900 и не больше текущего - 12.
> Метод, который по сути ведёт себя как константа, возвращая всегда одно и то же неизменяемое значение.
Да, это правильно, так как методы возвращают разные значения в разных классах.
Структуры данных, как правило, неразрывно связаны с алгоритмами и служат целям:
- хранить данные так, чтобы с ними было удобнее работать
- хранить данные так, чтобы быстро их находить или менять
- хранить данные так, чтобы код был понятнее
В PHP есть структуры данных (хотя не скажу, что идеально реализованные), смотри:
- расширение Ds: https://www.php.net/manual/ru/book.ds.php
- встроенное расширение SPL: https://www.php.net/manual/ru/book.spl.php
Ну например, ты хранишь в массиве черный список номеров телефонов (очень много): ['9701234567', '9701234568', ...]. И тебе надо проверять, есть ли номер телефона в черном списке или нет.
Если ты используешь массив, то это не очень хорошо:
- проверка будет медленной и неоптимизированной на больших объемах
- допустим, тебе надо составить черный список. Тебе неочевидно: должны телефоны идти в определенном порядке или нет? какие ключи должны быть в массиве?
А если ты используешь множество, то все будет проще:
- проверка наличия во множестве быстрая
- порядок телефонов во множестве не важен, ключей в нем нету, и читатель кода не задает лишних вопросов
Главная проблема конечно не оптимизация, а читабельность кода. В PHP массив используют вместо списка, неизменяемого списка, множества, хеш-таблицы и при чтении кода приходится догадываться, какой именно массив нужен в этом месте кода.
> у тебя даже массивов нет, только хеш мапы и объекты.
Не пиши, что не знаешь. В расширениях SPL и DS кое-что есть.
> Ну напишешь ты стек на пхп, и чего? Прироста нет, пользы нет, лишняя абстракция.
Код понятнее.
> Если посмотришь на трассировку симфони
ты бы для начала включил режим prod, а не в dev-окружении мерял, оптимизатор. Приходят такие неграмотные в тред и потом мифы про Симфони распространяют, а дураки им верят.
Также, у тебя на профиле нет ли одного запроса на 10ms, и еще у тебя используются подзапросы, что не рекомендуется, но ты похоже не читаешь рекомендации.
>Не пиши, что не знаешь. В расширениях 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 быстрым, а бутылочным горлышком запросы в базу.
Зашивайся, знаток.
Казалось, лишь теперь он начал понимать, почему я прятался в руинах.
https://hh.ru/vacancy/84045097?from=vacancy_search_list&hhtmFrom=vacancy_search_list&query=php
https://hh.ru/vacancy/90004977?from=vacancy_search_list&hhtmFrom=vacancy_search_list&query=php
Побочные эффекты производят другие программисты. Они решают свои задачи с помощью твоего кода и данных, которые нужны тебе.
Если назначение кода не очевидно, неоднозначно, нужное тебе поведение не изолированно а данные не гарантируют свою целостность.
Вот это поворот, все эти дебильные правила, солиды хуелиды нужны не для приручения злых данных, которые сами вдруг меняются. А для улучшения взаимопонимания и взаимодействия между людьми.
Не реально. Поезд уехал, встретимся в 2024.
Бест практисы какие?
В инете нашел такой вариант:
Создавать стайлс.пхп, там указывать заголовок текст\цсс и описывать стили, линком подключить
Почему-то также написали что просто подключать цсс и настроить нджинкс на передачу стилей - хуета
А,я даун, всё без танцев подключилось легко по линку (как обычно)
Для сборки стилей и js скриптов из разных модулей и папок используются gulp и webpack https://doka.guide/tools/gulp/#ispolzovanie
Спасибо за ответ.
Вот исправленный код:
Класс Validator: https://3v4l.org/g8uRs
Класс ErrorList: https://3v4l.org/5cntA
Зачем я сделал это:
>throw new ArrayNotExistsException("Массива под таким ключом не существует. ");
Потому что дальше идет цикл foreach, если result это null, то что он будет делить на ключ и значение?
Класс ValidationError: https://3v4l.org/R6Dtg
Интерфейс 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
Т.е. заглавные и строчные латинские буквы, цифры, нижнее подчеркивание, точка, минус, собака.
Я это "украл" у майл.ру, от балды вбивал символы и ждал когда будет сообщение об ошибке валидации и просто внес в свою регулярку то, что они считают разрешенным.
Спасибо за ответ.
Вот исправленный код:
Класс Validator: https://3v4l.org/g8uRs
Класс ErrorList: https://3v4l.org/5cntA
Зачем я сделал это:
>throw new ArrayNotExistsException("Массива под таким ключом не существует. ");
Потому что дальше идет цикл foreach, если result это null, то что он будет делить на ключ и значение?
Класс ValidationError: https://3v4l.org/R6Dtg
Интерфейс 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
Т.е. заглавные и строчные латинские буквы, цифры, нижнее подчеркивание, точка, минус, собака.
Я это "украл" у майл.ру, от балды вбивал символы и ждал когда будет сообщение об ошибке валидации и просто внес в свою регулярку то, что они считают разрешенным.
Можешь ответить еще на несколько вопросов?
Ты предлагал мне заменить несколько свойств в классе ErrorList на одно свойство.
Мне понравилось это решение, потому что код стал меньше и универсальнее.
Но я не понимаю какая логика лежит за этим решением.
Как понять, смотря на совокупность свойств класса, что лучше их представить в виде одного свойства-массива?
И еще вопрос.
У меня есть две переменные. Когда лучше иметь две отдельные переменные, а когда лучше иметь массив с двумя отдельными элементами?
Например, у меня есть две переменные - $patternForSymbols и $patternForString.
Как мне понять, им лучше быть по одиночке или быть элементами одного массива?
>В сигнатуре метода интерфейса указано, что $value должно быть mixed, но, проверяя год, мы же не можем указать mixed, ведь нам конкретно нужен int (года же ведь хранятся в виде int, да? а то я туплю что-то в этом), поэтому я оставил mixed для того, чтобы сигнатуры методов совпадали, но добавил проверку типа в самом классе.
Хуй знает как это исправлять.
Есть такая идея:
У меня есть класс Enrollee с поля name, surname и т.д.
Мб сделать так, чтобы поля year и email хранили не int и string соответственно, а объекты классов Year и Email?
Это не решает проблему с интерфейсом.
Но решает другую проблему.
Если я уберу строку, что класс, который проверяет адекватность года, реализует интерфейс, уберу проверку типа и сделаю тип аргумента int, то функция invoke сможет принимать любое число.
Год имеет тип числа, да. Но ведь в таком случае функция сможет принимать любое число. А нам нужен именно год. Для этого можно представлять числа и емайлы отдельными типами.
Т.е.
Сначала сделать так:
//убрать то, что класс реализует интерфейс
Класс будет объявляться так:
Class ClassName {...}
//сигнатура метода с int
public function __invoke(int $value): ?ValidationError {...}
//сигнатура метода с объектом типа Year
public function __invoke(Year $value): ?ValidationError {...}
>представлять числа и емайлы отдельными типами.
*Представлять год и емайл отдельными типами.
Извиняюсь за опечатки.
Почему я завел речь про емайлы - потому что будет отдельный класс-проверятель на уникальность емайл.
И я думаю, он тоже не впишется в мой интерфейс.
Ну и кстати нашёл курс от webformyself длительностью 38 часов, кто-то проходил его? Норм? Походу в сети всего 5 курсов по битриксу: webformyself, Я-Кодер, гикбрейнс, Михаил базаров и офф. курсы битрикса.
Да просто на собес сходи, спрашивают обычно по пхп, на петпроекты всем похуй. По jquery есть старый курс дмитрия лаврика на торрентах "Js для верстальщица". Еще из джса нужен обязательно ajax. В битриксе есть своя всратая библиотека BX она процентов на 80% копирует jquery - ее изучать самому бессмыслено документации толком нет, натаскаешься на РАБоте.
Я сейчас понял чем чревата такая идея.
Сейчас я обращаюсь к свойству Enrollee так:
$enrollee->get("название свойства").
А для года и емайла придется так:
$enrollee->get("название свойства")->get("название свойства");
Вторым способом нужно обращаться только к двум полям, получается, что я теряю универсальность в коде.
Я сейчас сонный, поэтому рассуждения туманные, я написал это, чтобы ты ответил с учетом этого момента.
Генерация файла это не побочный эффект. Жаль, что ты не пишешь, какой именно файл тебе нужен и мне придется придумывать это за тебя.
Допустим, тебе надо отдавать Эксель-файл с доходами за последний месяц. Формирование данных делается в модели (так как бизнес-логика располагается в модели). Генерация файла - в модели либо в контроллере. Отдача файла - в контроллере.
>Жаль, что ты не пишешь, какой именно файл тебе нужен и мне придется придумывать это за тебя.
На запрос пользователя, надо разбить видео на сегменты, сохранить их на диски и отдать плейлист на клиент.
Начну с главного: ни в коем случае не помещай все стили и скрипты в один файл. Это приводит к тому, что там получается свалка, в которой невозможно разобраться и из которой нельзя ничего удалить потом.
Используй разные файлы. Для маленького сайта я могу предложить такую схему:
- файл global.css, который подключается на всех страницах
- файлы для каждого раздела типа blog.css, catalog.css, forum.css
И, например, в блоге ты подключаешь global.css и blog.css.
Для большого сайта можно разбивать код еще сильнее. Например, ты сделал форму обратной связи, стили для нее кладешь в отдельный файл. Сделал календарик, тоже стили кладешь в отдельный файл.
Для маленького сайта можно не заморачиваться со сборкой и не тратить на это время. В эпоху HTTP/2 и при маленьком количестве стилей сборка ничего особо не дает.
Для большого сайта можно настроить склеивание мелких файлов в 2 файла: сайт глобальных стилей и сайт раздела.
Отсутствие сборки дает много плюсов:
- разработчику не надо ничего устанавливать, запускать, не нужна нода и npm, не нужно ждать, пока стили соберутся (это часто занимает минуту и больше при холодном старте)
- код в инструментах разработчика выглядит как в оригинале
- браузер может задействовать кеширование, если один файл используется на нескольких страницах
- при этом в HTTP/2 особого выигрыша от сборки нескольких файлов в один не должно быть
Но сборка может понадобиться, если:
- ты хочешь использовать нестандартные языки вроде SCSS, Typescript
- ты хочешь автоматически добавлять CSS-префиксы для CSS-файлов
- ты хочешь транспилировать новый JS в старый для лучшей поддержки старыми браузерами
Начну с главного: ни в коем случае не помещай все стили и скрипты в один файл. Это приводит к тому, что там получается свалка, в которой невозможно разобраться и из которой нельзя ничего удалить потом.
Используй разные файлы. Для маленького сайта я могу предложить такую схему:
- файл 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)
Здесь желательно удалить повторяющиеся символы, чтобы не было "вы использовали неправильные символы: &, &, &, &".
> return ($invalidSymbols) ? $invalidSymbols : null;
А зачем ты превращаешь пустой массив в null? Возвращай пустой массив, если все ок.
> 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)
Здесь желательно удалить повторяющиеся символы, чтобы не было "вы использовали неправильные символы: &, &, &, &".
> return ($invalidSymbols) ? $invalidSymbols : null;
А зачем ты превращаешь пустой массив в null? Возвращай пустой массив, если все ок.
Да даже если и устарел. Замени название на любимый таск менеджер твоего протыка. По сути ничего не изменится. Есть файлы стилей, есть папка public, в которую их надо высрать.
> Я подумал, что клепать 10 свойств - тупо и решил сделать одно - $reviewResources, в котором буду хранить все, что нужно для этой валидации.
Это плохая идея, заменить свойства на элементы массива, так как:
- код становится длинее, ты пишешь $this->props['someProp'] вместо $this->someProp
- код становится непонятнее, так как у тебя не описано, какие свойства могут быть в массиве, каких они типов
- для свойств проверяются имена и типы данных, которые в них записываются, а в массив можно записать что угодно и ошибки не будет
Я не вижу плюсов. Если бы тебе надо было работать с набором свойств как с массивом, применять функции для работы с массивами, тогда еще можно было бы подумать. Но тут я вижу только недостатки и никаких плюсов. В твоем посте я тоже аргументов в пользу такого решения не вижу.
> Ведь именно для этой валидации мне понадобилось так много свойств...
4 это не много. Много это 50 и больше. Но даже в этом случае надо не заменять свойства на массив, а искать другие идеи (например, разбить класс на несколько классов)
> Проверить каждый символ на паттерн, в котором перечислены корректные символы, если символ не проходит совпадение = он некорректный
> Проверить каждое значение на то, что его структуру +- нормальная, чтобы не было всяких И,в,а,н,о,в,ы,х,
Ну вот это кстати 2 разных задачи. Для их решения лучше сделать 2 разных правила. Ты смешиваешь 2 задачи в одном классе и усложняешь код. Тут надо сделать 2 отдельных класса и все проблемы с кодом решатся сами собой.
> Я не понимаю когда стоит представлять что-то отдельным, а когда массивом
Когда тебе надо что-то хранить, и заранее известны названия и типы данных, используем свойства. Если заранее не известны названия, или элементов может быть сколько угодно, используем массив.
Массив обычно используется для списков каких-то вещей или для "соответствий"/маппингов (например, соответствие номер месяца - название).
Например: у любого студента есть имя, email, дата рождения. Используем свойства. У любого товара есть цена, название, вес. Используем свойства для них.
Другой пример: надо хранить список цветов, в которых доступен товар. У товара может быть сколько угодно цветов. Используем массив (при этом цвета могут быть элементами перечисления Color), по типу:
$this->colors = [Color::RED, Color::BLACK, Color::WHITE];
Пример еще сложнее. Допустим, у товара могут быть разные цвета, и они стоят по-разному. Тут опять нужен массив такого типа:
$this->colors = [
['color' => Color::RED, 'price' => 10000],
['color' => Color::BLACK, 'price' => 20000]
];
Или более красиво, используя объект:
$this->variants = [
new ProductColor(Color::RED, 10000),
new ProductColor(Color::BLACK, 20000),
];
Еще пример. Надо преобразовать номер месяца в название. Используем массив [ 1 => 'Январь', 2 => 'Февраль'...].
Третий пример. Мы загружаем список валют и их курсы. Используем массив, так как валют может быть сколько угодно.
> Проверить каждый символ на паттерн, в котором перечислены корректные символы, если символ не проходит совпадение = он некорректный
> Проверить каждое значение на то, что его структуру +- нормальная, чтобы не было всяких И,в,а,н,о,в,ы,х,
Ну вот это кстати 2 разных задачи. Для их решения лучше сделать 2 разных правила. Ты смешиваешь 2 задачи в одном классе и усложняешь код. Тут надо сделать 2 отдельных класса и все проблемы с кодом решатся сами собой.
> Я не понимаю когда стоит представлять что-то отдельным, а когда массивом
Когда тебе надо что-то хранить, и заранее известны названия и типы данных, используем свойства. Если заранее не известны названия, или элементов может быть сколько угодно, используем массив.
Массив обычно используется для списков каких-то вещей или для "соответствий"/маппингов (например, соответствие номер месяца - название).
Например: у любого студента есть имя, email, дата рождения. Используем свойства. У любого товара есть цена, название, вес. Используем свойства для них.
Другой пример: надо хранить список цветов, в которых доступен товар. У товара может быть сколько угодно цветов. Используем массив (при этом цвета могут быть элементами перечисления Color), по типу:
$this->colors = [Color::RED, Color::BLACK, Color::WHITE];
Пример еще сложнее. Допустим, у товара могут быть разные цвета, и они стоят по-разному. Тут опять нужен массив такого типа:
$this->colors = [
['color' => Color::RED, 'price' => 10000],
['color' => Color::BLACK, 'price' => 20000]
];
Или более красиво, используя объект:
$this->variants = [
new ProductColor(Color::RED, 10000),
new ProductColor(Color::BLACK, 20000),
];
Еще пример. Надо преобразовать номер месяца в название. Используем массив [ 1 => 'Январь', 2 => 'Февраль'...].
Третий пример. Мы загружаем список валют и их курсы. Используем массив, так как валют может быть сколько угодно.
> В сигнатуре метода интерфейса указано, что $value должно быть mixed, но, проверяя год, мы же не можем указать mixed, ведь нам конкретно нужен int (года же ведь хранятся в виде int, да? а то я туплю что-то в этом),
Вот ты и открыл одну из проблем ООП. В ООП есть "принцип Барбары Лисков". Он говорит следующее:
- если код рассчитан на работу с объектом класса A, то в него можно передать объект класса-наследника B и код не должен сломаться.
То есть, при наследовании мы должны сохранять совместимость. Именно по этой причине ты не можешь при наследовании удалить метод - так как в этом случае код, который думает, что в классе есть такой метод, сломается.
Ты можешь расширить список принимаемых значений, но не можешь сузить с mixed до int.
Увы, в нынешней реализации ООП никакого простого способа решить эту проблему нету.
> поэтому я оставил mixed для того, чтобы сигнатуры методов совпадали, но добавил проверку типа в самом классе.
Ок, это допустимо.
> YearIsValid
Вообще, вместо "год правильный" было бы лучше сделать облее общее правило "число лежит в диапазоне". Чтобы его можно было и с баллами использовать, например. Баллы это же тоже число.
> Максимальный год - 2023
Ты 1-летнего ребенка в студенты принимать собрался? Минимум надо лет 10-12 ставить.
> Минимальный год я выбрал - 1923
То есть дискриминируешь 102-летних? Ставь с запасом, чтобы не засудили.
> @\S*.
Звездочка допускает 0 символов
> В сигнатуре метода интерфейса указано, что $value должно быть mixed, но, проверяя год, мы же не можем указать mixed, ведь нам конкретно нужен int (года же ведь хранятся в виде int, да? а то я туплю что-то в этом),
Вот ты и открыл одну из проблем ООП. В ООП есть "принцип Барбары Лисков". Он говорит следующее:
- если код рассчитан на работу с объектом класса A, то в него можно передать объект класса-наследника B и код не должен сломаться.
То есть, при наследовании мы должны сохранять совместимость. Именно по этой причине ты не можешь при наследовании удалить метод - так как в этом случае код, который думает, что в классе есть такой метод, сломается.
Ты можешь расширить список принимаемых значений, но не можешь сузить с mixed до int.
Увы, в нынешней реализации ООП никакого простого способа решить эту проблему нету.
> поэтому я оставил mixed для того, чтобы сигнатуры методов совпадали, но добавил проверку типа в самом классе.
Ок, это допустимо.
> YearIsValid
Вообще, вместо "год правильный" было бы лучше сделать облее общее правило "число лежит в диапазоне". Чтобы его можно было и с баллами использовать, например. Баллы это же тоже число.
> Максимальный год - 2023
Ты 1-летнего ребенка в студенты принимать собрался? Минимум надо лет 10-12 ставить.
> Минимальный год я выбрал - 1923
То есть дискриминируешь 102-летних? Ставь с запасом, чтобы не засудили.
> @\S*.
Звездочка допускает 0 символов
> Но я не понимаю какая логика лежит за этим решением.
Логика такая. Мы делаем класс "список ошибок", который хранит поле и список ошибок в нем. Мы делаем универсальный список ошибок, который может работать с любой формой с любыми полями. Мы заранее не знаем, где он будет использоваться, для каких форм, как в них будут называться поля. У нас тут вариантов, кроме массива, не остается.
Если бы ты делал список ошибок только для студента, то можно было бы и поля использовать, и массив. Но лучше делать более обобщенные классы, более универсальные.
Почему? Потому, что завтра тебя могут попросить добавить к списку студентов список преподавателей, список лекций и еще кучу всего. Если у тебя универсальный валидатор и универсальный список ошибок, то ты сможешь их использовать и там. Если же ты делаешь валидтор только для студентов и список ошибок только для студентов, то ты не сможешь их повторно использовать.
Поэтому надо стараться делать классы более обобщенными, но не переходя в крайности. Не валидатор года, а валидатор целых чисел.
> Например, у меня есть две переменные - $patternForSymbols и $patternForString.
> Как мне понять, им лучше быть по одиночке или быть элементами одного массива?
По одиночке, так как название и тип тут заранее известны.
Это плохая идея. Посмотри, как сделаны валидаторы в Симфони.
Там есть 2 класса - класс, который описывает ограничение (например, Length), и класс, который проверяет ограничение (например, LengthValidator).
Код:
- https://github.com/symfony/validator/blob/7.0/Constraints/Length.php
- https://github.com/symfony/validator/blob/7.0/Constraints/LengthValidator.php
Пример использования:
// задаем ограничение, что длина строки от 5 до 10 символов
$constraint = new Length(min: 5, max: 10);
Пример проверки:
$validator = new LengthValidator();
$validator->initialize($context); // это не важно
// проверка
$validator->validate('aaaaa', $constraint);
У них та же проблема с наследованием. Интерфейс выглядит так: https://github.com/symfony/validator/blob/7.0/ConstraintValidatorInterface.php
> public function validate(mixed $value, Constraint $constraint);
И они в валидаторе с помощью if делают проверки.
Это плохая идея. Посмотри, как сделаны валидаторы в Симфони.
Там есть 2 класса - класс, который описывает ограничение (например, Length), и класс, который проверяет ограничение (например, LengthValidator).
Код:
- https://github.com/symfony/validator/blob/7.0/Constraints/Length.php
- https://github.com/symfony/validator/blob/7.0/Constraints/LengthValidator.php
Пример использования:
// задаем ограничение, что длина строки от 5 до 10 символов
$constraint = new Length(min: 5, max: 10);
Пример проверки:
$validator = new LengthValidator();
$validator->initialize($context); // это не важно
// проверка
$validator->validate('aaaaa', $constraint);
У них та же проблема с наследованием. Интерфейс выглядит так: https://github.com/symfony/validator/blob/7.0/ConstraintValidatorInterface.php
> public function validate(mixed $value, Constraint $constraint);
И они в валидаторе с помощью if делают проверки.
Вся бизнес-логика в модели, генерация плейлиста в модели, в контроллере только извлечение параметров запроса, координация и отдача файла.
Пожалуйста
Спасибо. Но на собес пока не пойду, не хочу тратить попытку впустую. Но идею ты подкинул вери гуд - посмотрю на ютубе собесы https://www.youtube.com/watch?v=qJJBd7YiTK4
Да, читать. Если хочешь ускорить процесс, то есть варианты:
- сделать конфиг в формате php-файла и закешировать его с помощью preload
- кешировать данные конфига в APCu
>Здесь хорошо бы добавлять phpDoc-комментарий с описанием структуры массива. А то при чтении кода непонятно, что в него можно передавать. Я давал выше ссылку, как это описывать (добавить phpDoc-тег @var).
Я раньше не читал про документирование кода.
Погуглив, я понял следующее:
phpDoc - приложение для автоматической генерации документации на основе docBlock.
docBlock - это комментарий в коде, который описывает что-то с помощью краткого описания, более детального и тегов.
Когда берусь за написание такого комментария - встаю в ступор.
Как я понял, опираясь на это: https://docs.phpdoc.org/latest/guide/references/phpdoc/types.html#arrays
Свойство $errors класса ErrorList можно описать так (я не буду писать краткое и детальное описание, а только затрону теги)
/ @var array[] */
Насколько я понял, за тегом идет название типа, который имеют элементы массива.
Но мой массив двумерный и я не знаю как его описывать.
По ссылке, которую ты изначально кинул, я ничего не понял.
Да-да, прости. Да. Туплю.
Спасибо за ответ.
Проверка на структуру значения: https://3v4l.org/GbBpm
Проверка на валидные символы: https://3v4l.org/FgUuP
Проверка на то, что число находится в диапазоне: https://3v4l.org/IZjC3
Проверка на отсутствие пустоты: https://3v4l.org/ugNK6
Класс ErrorList: https://3v4l.org/Uhu5V
А то смотрю вакухи и там блядь js. А я ебал в жс ебанутую экосистему влезать с node_modules
Да там пишут список фреймврков иии vue / react и прочее.
Если просто вбить в вакуху на хх название фреймворка то там везде хуйню из жс дописывают.
Ты на Laravel работу ищешь? У этих даунов часто вместе слеплено, потому что они больные и не лечатся.
Да вообще.
И пост для всех, кто знает - ответьте если не трудно.
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 и прочее - что из этого нужно знать?
Yii3 ты хотел сказать?
Ты оверпреппишься. Чаще всего тебя на собесе спрорят 5-10 вопросов по php.
5-10 вопросов по базам данных
5-10 вопросов по паттернам и всяким солид хуелид
5-10 вопросов про веб. Ну типа http(как работает протокол), как в вебе делается аутентификация, че такое cookie, че такое сессия.
Ну и все.
Судя по тому что ты высрал, ты уже блять готов. Иди уже на собес бля епта. Все что ты написал это реально пиздатый роадмап, но это не необходимо чтобы найти первую работу(и вторую тоже, лол).
Так шо я советую идти и не ссать. Если не ответишь, узнаешь где не ответил, ответишь потом на другом
Маам блядь и тут ты меня нашла, хватит меня гнать на работу
Насколько хорошо надо знать фронт, чтобы взяли? Есть вариант вообще без фронта?
>Есть вариант вообще без фронта?
Есть. Знать надо на уровне серверного рендеринга, уметь таблицы наверстать с бутстрапом. Изи короче. Реакт не надо
Ты откуда столько слов знаешь
Хорошая, а где стрим будет?
А, увидел, на твиче
Та роад мапа, которая указана в шапке, это минимум знаний, которыми должен обладать человек, чтобы идти на собес?
Нет. Каждое собеседование уникально
Пока я жду ответа насчет кода валидации, я решил взяться за написание остальных классов.
Посмотри, пожалуйста, эти классы:
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;
Спасибо за то, что отвечал мне.
Извини, если я много тебя напрягаю.
Мой код очень сильно преобразился благодаря твоим ответам.
Я стал чувствовать себя более увереннее.
Пока я жду ответа насчет кода валидации, я решил взяться за написание остальных классов.
Посмотри, пожалуйста, эти классы:
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;
Спасибо за то, что отвечал мне.
Извини, если я много тебя напрягаю.
Мой код очень сильно преобразился благодаря твоим ответам.
Я стал чувствовать себя более увереннее.
Т.е. они как бы есть в классе, но нет возможности к ним обратиться? Или их нет в классе, который наследует?
Я думаю, что, мб, для того, чтобы Cookie не обращался к $_COOKIE, нужно сделать класс, который будет хранить куки?
Контроллер будет создавать объект такого класса с куки, а Cookie извлекать информацию о куке из него?
>Еще я также подумал о том, что раз у меня и так универсальные геттер и сеттер, то почему бы не сделать их магическими?
Наверное это плохое решение, если окажется, что я где-нибудь забуду инициализировать свойство.
Таким образом Cookie разделился бы на два класса:
https://3v4l.org/hPJgF
https://3v4l.org/qE5QO
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, а с помощью нестандартных расширений ты можешь описать структуру массива, что делает код понятнее.
> private string $format =
Это можно сделать константой, если не планируется никогда менять.
В остальном, нормально.
В методе ErrorList#getListOfFieldErrors возможно, стоило бы возвращать не строки, а объекты, но строки тоже можно.
Думаю, что нет. В Симфони делают так:
- есть класс XBuilder, в котором можно менять значения
- у него есть метод build(), который возвращает объект класса X с неизменяемыми значениями
То есть, делают 2 класса: X и XBuilder. Иногда с помощью трюков (добавлятся поле "isReadonly") их совмещают в один.
Класс Cookie правильнее назвать типа CookieManager, так как он не представляет одну куку, а класс для управления куками.
Вообще, в ООП-фреймворках обычно запрос (с GET, POST, куками) представляют как объект Request, а ответ (с установленными куками, заголовками, телом) как объект Response. И куки добавляют в Response. И никто не лезет в GET/POST и тд напрямую.
Можешь глянуть, как это сделано в Симфони: https://symfony.com/doc/current/components/http_foundation.html
Также, для объектов запроса/ответа есть рекомендация PSR с интерфейсами для них: https://www.php-fig.org/psr/psr-7/
Если делать свое, то лучше по PSR. Но, возможно, для этой задачи это уже перебор.
> class Authorization
Тут стоит сделать такие публичные методы: залогинить человека, разлогинить, определить текущего пользователя. Ты можешь сделать, чтобы все функции работали с объектом Enrollee, либо чтобы в них передавался только токен(пароль). Но чтобы было везде одинаково, а не в одной функции пользователь, а в другой только токен.
> Что такое класс Cookie? Часть Модели MVC?
Вспомогательный класс, который будет вызываться из контроллера наверно. Я бы лично его в 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, а вот получаемые куки по моему просто идут как строка, а не объект.
https://symfony.com/doc/current/components/http_foundation.html#setting-cookies
И к чему ты это высрал, долбаеб?
Пиздец, с какими же даунами приходится сидеть в одном треде.
пиши через ларавель, с сидерами и фабриками, сразу покрывая тестами. Какие-то нативные запросы делаются на этапе оптимизации хайлоада, для получения каких-то особо выёбистых данных или в целом для ускорения сложных выборок. До этого даже не заморачивайся
На практике используют и то, и то.
Если тебе надо получить 20-50 объектов, берем ORM. Если нужен заковыристый запрос с группировками, суммированием, пишем на SQL, иначе глупо будет выбирать из базы >1000 объектов только чтобы циклом сложить пару полей в них.
Ну то есть, надо смотреть по ситуации, какие именно данные тебе нужны.
В ларавеле есть sum, и прочие стандартные арифметические плюшки типа модуля от числа, экстремумы, среднее арифметическое и т.д. И со строками он тоже нормально работает, и даже с чем-то более сложным типа json и датой. Так что я бы посоветовал пользовать функционал ларавеля до победного, пока не упрешься в действительно большие просадки по запросам
Спасибо за ответ.
Я решил отложить пока что написание классов в сторону.
Потому что нужно прояснить общую картину.
Я хотел бы, чтобы ты помог мне с этим.
Я чувствую, что я не понимаю как устроен мой проект и как его составляющие (файлы) взаимодействуют между собой.
Напишу, что у меня есть сейчас в проекте, а ты, мб, сможешь помочь мне прояснить ситуацию.
У меня есть папка 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.
Это, конечно, в силу возможности. Я понимаю, что всех деталей моего проекта ты не знаешь.
Спасибо за ответ.
Я решил отложить пока что написание классов в сторону.
Потому что нужно прояснить общую картину.
Я хотел бы, чтобы ты помог мне с этим.
Я чувствую, что я не понимаю как устроен мой проект и как его составляющие (файлы) взаимодействуют между собой.
Напишу, что у меня есть сейчас в проекте, а ты, мб, сможешь помочь мне прояснить ситуацию.
У меня есть папка 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, куда должен положить свои классы?
Вообще, это сложный вопрос: как раскладывать классы по папкам. Вот ты гуглил? Я не вижу учебников, которые бы этому учили. Симфони использует такую структуру: https://symfony.com/doc/current/best_practices.html#use-the-default-directory-structure
Они группируют классы по типу: все контроллеры в одну папку (можно с поддиректориями если их много), все сущности в другую, все формы в третью итд, все сервисы в четвертую, все исключения в пятую.
Но проблема в том, что обычно в больших приложениях больше всего классов-сервисов и в Симфони они все отправляются в одну и ту же папку, хоть и отвечают за разные вещи. Ты можешь создавать подпапки, но у тебя функционал получается раскидан по разным папкам. Например: сущности для форума лежат в Entity/Forum, сервисы в Service/Forum, формы в Form/Forum.
Вот другой подход, движок для создания интернет-магазинов разбивает код на модули, каждый модуль в своей папке: https://github.com/magento/magento2/tree/2.4-develop/app/code/Magento
То, что я чаще вижу (и в других языках) это разбить проект на компоненты по назначению. А компоненты в свою очередь на более мелкие компоненты так, что классы, делающие похожую задачу, лежат вместе. Например, если у тебя есть форум, блог, новости, то ты делаешь для каждого свою папку. А также папку или папки для общих сервисов, которые нужны везде, вроде загрузки картинок, валидации:
Controllers/
Blog/ все для блога
.....Post/ Посты
.....Comment/ комментарии
.....Rating/ голосование и расчет рейтинга
Forum/
.....Posts/ посты
.....Polls/ функционал опросов
.....Topics/ темы
News/ новости
Users/ регистрация, авторизация
....Profile/ профиль
Shared/ общие сервисы
.....Ads/ реклама
.....Images/ обработка картинок
.....Validation/ валидация
.....Metrics/ сбор данных о посещаемости, просмотрах страниц
В твоем случае классов не очень много и можно их поделить на такие компоненты:
- роутер
- контроллеры (этот компонент отвечает за прием и обработку HTTP-запросов пользователя)
- валидация (универсальные классы, без привязки к студенту)
- студент (модель студента, TDG для него, валидатор студента)
- авторизация
- утилиты (работа с куками и другие вспомогательные классы)
> У меня нет пока что шаблонов.
Можно сделать папку templates
> У меня нет CSS-файлов.
Они должны раздаваться наружу, поэтому можно сделать папку public/css/
> Я должен выделить в ней отдельную папку Controllers, чтобы положить туда два своих контроллера (я решил назвать их homePage.php и form.php)?
Если это классы, то названия файлов должны быть с большой буквы. Если не классы, то не обязательно. Название form.php не очень удачное, так как форм в будущем может быть много. Лучше назвать studentForm.php (или enrolleeForm.php).
> Я должен завести папку Models, куда должен положить свои классы?
Вообще, это сложный вопрос: как раскладывать классы по папкам. Вот ты гуглил? Я не вижу учебников, которые бы этому учили. Симфони использует такую структуру: https://symfony.com/doc/current/best_practices.html#use-the-default-directory-structure
Они группируют классы по типу: все контроллеры в одну папку (можно с поддиректориями если их много), все сущности в другую, все формы в третью итд, все сервисы в четвертую, все исключения в пятую.
Но проблема в том, что обычно в больших приложениях больше всего классов-сервисов и в Симфони они все отправляются в одну и ту же папку, хоть и отвечают за разные вещи. Ты можешь создавать подпапки, но у тебя функционал получается раскидан по разным папкам. Например: сущности для форума лежат в Entity/Forum, сервисы в Service/Forum, формы в Form/Forum.
Вот другой подход, движок для создания интернет-магазинов разбивает код на модули, каждый модуль в своей папке: https://github.com/magento/magento2/tree/2.4-develop/app/code/Magento
То, что я чаще вижу (и в других языках) это разбить проект на компоненты по назначению. А компоненты в свою очередь на более мелкие компоненты так, что классы, делающие похожую задачу, лежат вместе. Например, если у тебя есть форум, блог, новости, то ты делаешь для каждого свою папку. А также папку или папки для общих сервисов, которые нужны везде, вроде загрузки картинок, валидации:
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 классов в одной папке.
По неймспейсам, кстати, эту статью помжешь прочесть, если не читал: https://github.com/codedokode/pasta/blob/master/php/autoload.md#%D0%BD%D0%B5%D0%B9%D0%BC%D1%81%D0%BF%D0%B5%D0%B9%D1%81%D1%8B-%D0%BF%D1%80%D0%BE%D1%81%D1%82%D1%80%D0%B0%D0%BD%D1%81%D1%82%D0%B2%D0%B0-%D0%B8%D0%BC%D0%B5%D0%BD
> наверное стоит написать "файлы", ведь класс это файл, который содержит объявление класса,
Нет, файл это файл, и он содержит описание класса. А "класс" это какая-то абстрактная штука, которая появляется в памяти после того, как PHP прочтет файл. Или, с другой стороны, класс - это "вид" объекта.
> composer подключает только файлы-классы или любые файлы, в которых namespace Acme и они находятся в папке src?
Он подключает только те файлы, которые соответствуют правилам автозагрузки в composer.json и рекомендации PSR-4. Подключает он их в момент первого обращения к классу, используя механизм автозагрузки. То есть если в коде встречается $x = new X(); то срабатывает автозагрузка и вызывается композер для поиска файла с классом X.
> Сейчас, чтобы попасть на страницу localhost (ага, да)/form,
> должна осуществиться следующая цепочка:
Примерно так и есть. При поступлении HTTP-запроса выполняется public/index.php и его задача выдать какой-то ответ на запрос и умереть.
Неэффективно вытягивать тысячи записей через ORM, чтобы просто посчитать сумму и выкинуть эти записи. Эффективнее посчитать сумму на стороне БД.
Почему так? Почему в первом случае он добавляет
> and defined in <file>:<line>
А во втором
> in <file>:<line>
Куда делось and defined? Там какая-то дофига хитрая проверка под капотом, которая определяет, вывел ли я место вызова в сообщении?
Вопрос, конечно, имеет чисто академический характер. Мне от этого поведения наоборот удобнее, но было неожиданно, что в зависимости от того, что я пищу в сообщении, он меняет автоматически добавляемую часть.
Добрый вечер, ларавелевские аггрегаторы транслируются во вполне адекватные запросы, не выдумывай.
Просто судя по вакансиям - 250к ещё получать реально, а за 300 придётся работать клоуном у пидорасов с жопой в мыле.
Алсо, есть тут те кто на фрилансе подрабатывает? Насколько реально без головной боли в месяц 50к поднимать?
Спасибо за ответ.
Я немного застрял на роутинге.
Я поступил очень просто:
https://3v4l.org/rVZCl
Это не готовое решение.
Оценивать его составляющие - бессмысленно.
Акцент только на общем принципе работы.
Хоть это решение неплохо смотрится на моем проекте, который состоит из 2-3 страниц, но, наверное стоит сделать это иначе?
Возможно на ООП?
Я попробовал читать Symfony, это дало мне почти ничего.
Это вообще редко мне что-то дает.
Читал другую статью, в которой автор создавал два класса Route (представляет собой маршрут) и Router (умеет работать с маршрутами). Сначала было понятно, а потом он ушел в какую-то кашу, которую я не понял.
Как ты считаешь, если действительно, стоит сделать маршрутизатор в ООП виде, то как мне стоит это реализовывать?
Извини, если это нагло звучит.
У меня часто так получается, что люди искаженно понимают настроение, с которым я пишу им сообщение.
Если писать откровенно, то я не знаю где грань в нашем диалоге.
Наверное это неправильно, что я засыпаю и засыпаю тебя вопросами.
Но я также понимаю, что мне нужна обратная связь.
Что я многое не знаю.
Не знаю даже как прогугливать определенные темы.
Я стараюсь сначала разобраться в теме сам.
Вообще, если бы не ты, я бы так и сидел, пытаясь делать валидацию.
Только благодаря тебе я смог закончить этот этап создания проекта.
Ну или я бы сидел с той кашей, которую написал, когда принимался за эту задачу.
Насчет роутинга, я вижу 2 варианта:
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, и передаст в контроллер.
Что касается роутинга в Симфони, там как раз сделан ООП-роутер, довольно навороченный. Ты описываешь список роутов либо в конфиге в формате 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, и передаст в контроллер.
Что касается вопросов, то этот тред специально сделан для начинающих и их вопросов. Лучше ты тут будешь вопросы задавать, чем потом сеньоров отвлекать.
Не знаю, для меня выбить 150к - это уже что-то на грани невозможного. Но я забугром сейчас, ищу удаленку.
Вообще, фирмам по большому счету уже плевать сколько платить, для бизнеса это вопрос десятой важности. Всё равно сколько бы ты айтишке не плати, расходы на разработку программного продукта в процентном соотношении к общей выручке минимальные. Сейчас мода наоборот идёт на то, чтобы выцепить из потока айтишной массы самых лучших, как начинающих, так и сеньоров пиздаболов. Я вот например живая посредственность, у меня хоть и есть относительно большой опыт и какие-то знания, меня никуда не берут, потому что нет нужных софт скилов и таланта прыгать выше головы, не чувствуется амбициозность. А знакомый Вася Пупкин, знающий столько же сколько и я, но имеющий себя продать, имеет оффер в 250к
> Не знаю, для меня выбить 150к - это уже что-то на грани невозможного. Но я забугром сейчас, ищу удаленку.
Сколько лет опыта у тебя? Учился ли новому после трудоустройства или хуи пинал?
>>2962510
Кажется, ответ найден в исходном коде функции Exception#__toString (она на языке Си и входит в состав PHP): https://github.com/php/php-src/blob/master/Zend/zend_exceptions.c#L683
https://3v4l.org/hdZqa#v8.3.0
Если класс ошибки TypeError или ArgumentCountErrror и в тексте есть фраза ", called in ", то к сообщению дописывается defined in.
Похоже на хак какой-то.
Ну, собственно, очередная причина почему пхп говноязык.
>Error is the base class for all internal PHP errors.
Вот бы можно было сделать свой класс приватным, чтобы всякие ебланы не использовали его не по назначению и не удивлялись чей-то он работает не так как им надо.
Да не хуйня какая-то. Мама учила делиться.
Советую пройти курс по русскому языку и курс оператора эвм.
Гениям, которые могут два слова нормально напечатать, сразу лычку мидла дают. А когда научишься знаки препинания ставить - переведут в сеньоры и дадут почетную грамоту "Не еблан".
Дерзай.
laravel
Забавно, что когда речь заходит о зарплате - в треде/чатах сразу наступает молчок. Пыхари, вы там за идею работаете, просто вам стыдно в этом признаться?
Спасибо за ответ.
Класс Route: https://3v4l.org/BIkla
Класс Router: https://3v4l.org/PmVCS
index.php: https://3v4l.org/XjAUB
>Обрати внимание, если роут не найден, нужно отдавать страницу ошибки с кодом 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 могло
лучше подсказывать что есть в коде)?
Стоит ли мне, новичку, лезть в эту тему? Не рано ли мне это? Просто я еще не понимаю зачем мне все это нужно, я даже
комментарии не пишу в коде, а с момента когда я узнал про тайп-хинты, ведение документации выглядит в моих глазах как нечто
лишнее.
Если мне действительно стоит начать заниматься документированием своего кода, то с чего мне начать?
Что вообще гуглить?
Я пробовал гуглить, смотреть на ютубе.
Но либо я не умею гуглить (что вполне может соответствовать истине), либо я еще не готов к такой информации.
Еще у меня сложилось впечатление, что атрибуты/аннотации это не просто комментарий, текст, а будто что-то большее.
Если это так, то, что это?
Если я добавлю атрибут или аннотацию в код, что-то изменится?
У меня начнет иначе работать код?
Спасибо за ответ.
Класс Route: https://3v4l.org/BIkla
Класс Router: https://3v4l.org/PmVCS
index.php: https://3v4l.org/XjAUB
>Обрати внимание, если роут не найден, нужно отдавать страницу ошибки с кодом 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 могло
лучше подсказывать что есть в коде)?
Стоит ли мне, новичку, лезть в эту тему? Не рано ли мне это? Просто я еще не понимаю зачем мне все это нужно, я даже
комментарии не пишу в коде, а с момента когда я узнал про тайп-хинты, ведение документации выглядит в моих глазах как нечто
лишнее.
Если мне действительно стоит начать заниматься документированием своего кода, то с чего мне начать?
Что вообще гуглить?
Я пробовал гуглить, смотреть на ютубе.
Но либо я не умею гуглить (что вполне может соответствовать истине), либо я еще не готов к такой информации.
Еще у меня сложилось впечатление, что атрибуты/аннотации это не просто комментарий, текст, а будто что-то большее.
Если это так, то, что это?
Если я добавлю атрибут или аннотацию в код, что-то изменится?
У меня начнет иначе работать код?
>чтобы всякие ебланы не использовали его не по назначению
ГУГЛИШЬ - А ЕСТЬ ЛИ В PHP ТИПИЗИРОВАННЫЕ МАССИВЫ?
@
ИХ НЕТ
@
НАХОДИШЬ ОБСУЖДЕНИЕ ПРЕДЛОЖЕНИЯ RFC С ЭТОЙ ФИЧЕЙ
@
ИЗ ОБСУЖДЕНИЯ УЗНАЕШЬ, ЧТО ТЫ - ХУЙ, ТВОЯ МАТЬ - ШЛЮХА, А ИХ ОТСУТСТВИЕ - БАЗА. ЭТО БУДЕТ ОЧЕНЬ ПРОБЛЕМНО РЕАЛИЗОВАТЬ, ПОТОМУ ЧТО ЧИТАТЬ ДАЛЕЕ
@
НО НЕ ПЛАЧЬ, МАКАКА. ВСЕГДА МОЖНО СДЕЛАТЬ ДЛЯ ЭТИХ ЦЕЛЕЙ КЛАСС-ОБЁРТКУ С МЕТОДОМ PUSH(), КОТОРЫЙ БУДЕТ КИДАТЬ TYPEERROR, ЕСЛИ ПЕРЕДАННЫЙ В НЕГО ЭЛЕМЕНТ НЕ ЯВЛЯЕТСЯ INSTANCEOF ОЖИДАЕМОГО ТОБОЮ КЛАССА
@
НО ВООБЩЕ, КЛАСС TYPEERROR ДЛЯ ВНУТРЕННЕГО ИСПОЛЬЗОВАНИЯ, ПОШЁЛ НАХУЙ
>В твоем случае классов не очень много и можно их поделить на такие компоненты:
Кстати, у меня есть 2 трейта и интерфейс.
Куда мне стоит их отнести в файловой структуре?
Раз интерфейс относится к валидации (интерфейс для классов-проверятелей), то его стоит отнести в папку Validation?
А трейты в Utility?
Также:
>>2958095
>class Authorization
>Тут стоит сделать такие публичные методы: залогинить человека, разлогинить, определить текущего пользователя.
Я тебя понял так:
Залогинить человека = определить зарегистрированный пользователь или нет.
Определить текущего пользователя = отличить одного зарегистрированного пользователя от другого.
Как я планирую это делать:
Как отличить зарегистрированного пользователя = посмотреть $_COOKIE, если массив не пустой, то пользователь зарегистрирован (потому что при регистрации в куки сохранится пароль).
Да, я знаю о том, что пользователь может потерять куки.
Но мы же в решении задачи опускаем этот момент?
Как отличить одного зарегистрированного пользователя от другого:
Тут я два варианта придумал:
1) При регистрации пользователя создается же новая запись в БД, тогда нужно брать id новой записи и класть его в куки пользователя.
2) Взять пароль из куки пользователя, получить его соленый хеш, сравнить с солеными хешами в БД, если есть совпадение = отдать id.
Есть еще один момент, который для меня туманен.
Я не могу четко сказать который класс Модель, а который, эм, просто класс?
Эта проблема показала себя, когда мы заговорили про класс, работающий с куки.
Я себе троицу Модель-Контроллер-Представление представлял так, что всю работу делает Модель, а контроллер только говорит что именно делать.
Сейчас думаю о классах Регистрация и Авторизация.
По логике выше, Регистрация и Авторизация это части Модели, потому что они выполняют работу - регистрируют и авторизовывают. В то время как Контроллер может указывать Модели, чтобы она зарегистрировала или авторизовала.
Так...А что насчет класса CookieManager?...
Немного есть сомнение насчет того, что этот класс должен существовать. Потому что там сейчас только 2 метода - установить и получить куки.
Но ведь все это можно сделать и во внешнем коде, без всякого класса(?)
Если только контроллер может лазать в суперглобальные переменные, то, наверное он и должен работать с куки?
И если регистрации или авторизации нужны куки, то отдавать их должен контроллер? Так наверное...
Да, короче, одна каша получается.
Извини, если чтение моего поста вызвало у тебя фейспалм.
В index.php
есть такая строка: require_once($route->get('controllerName'));
Скорее всего нужно будет переписать ее.
Ведь контроллеры будут храниться в отдельной папке
src/Controllers/someController.php
Все пхпшники в какой-то мере пидоры, не только битриксойды
<?
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/header.php");
$APPLICATION->SetTitle("Главная");
?>
<?require($_SERVER["DOCUMENT_ROOT"]."/bitrix/footer.php");?>
В браузере это отображается как пикрил. То есть просто содержимое index.php котрый в корне сайта. При этом если выполнить тоже самое на работающей машине, там дохуя всего и собственно сам сайт погружается. В логах сервака ошибок не видно никаких. Есть какие-то идеи у местных php гуру и богов Битрикса? С меня как обычно нихуя
До меня такой ебанат на проекте такого наворотил и съебал в закат. Баг баги баги баги. Пишет модель в базу, через 400 строк они всегда срут прямо в контроллерах пихает метод где ещё в 400 опять достаются модели, перезаписываются частично и опять сохраняются. Туда-сюда, дрочила ты контуженный, а меня потом просят выяснить почему тормозит. Да потому, что код вам обезьяна с битриксом писала.
Правишь код в одном месте - вообще в другом баги лезут.
Как же у меня от этих макак пригорает.
>всю работу делает Модель
Модель отвечает только за данные. Всю работу должны делать сервисы - это отдельный слой.
>Регистрация и Авторизация
Это методы контроллера. К модели они никакого отношения не имеют.
А что делает модель?
Что значит "отвечает за данные"?
Конкретные действия напиши.
Что такое сервис?
Я думал класс, состоящий из методов, это сервис?
И если сервис это не Модель, то где в MVC он находится?
>Это методы контроллера
Зачем контроллеру регистрировать и логинить пользователя, если можно попросить Модель (вызвав какой-нибудь метод authorize()) и она все сделает?
>Версии mysql и php аналогичные на другой машине.
А настройки у них аналогичные? Сравни что выводит phpinfo().
Ну плюс минус да, понятно что не всё переменные. Но я не думаю, что там это как-то влияет. Нигде не пишут что надо какие-то особые пляски с бубном устраивать. Да и другие сайты на php на этом серваке работают, на вордпрессе например. Так, что я думаю дело в чём-то другом.
Но это не из-за настроек, потому что тебе впадлу их проверять?
Этот тред это какой-то магнит для дегенератов.
>Конкретные действия напиши
Конкретные задачи слоя модели ты сам загуглить можешь.
>И если сервис это не Модель, то где в MVC он находится?
А он и не в MVC. Это слой бизнес-логики.
Пишешь, например, сервис для работы со скидками где есть метод расчета скидки для проперженных стульев только сычей и потом ты его можешь вызвать как из контроллера, так и из cli консольной команды своего приложения. При этом когда ты его пишешь, то отлаживаешь именно через консоль, а не дергая браузер за писюн постоянно. Для него же юнит-тесты можно завезти. Потом просто вызываещь этот метод в контроллере с нужными параметрами из запроса и получаешь скидку для сыча по прореженному стулу красного цвета из осины.
Сервисы вообще база разработки.
Чуть позже никто тебе не мешает в контроллере получать запрос и перекидывать его данные в планировщик и уже из планировщика вызывать этот сервис точно так же. Это называется асинхронная обработка запроса. Хотя теб етакое рановато еще - тут про хайлоад.
Т.е. логика бизнеса у тебя инкапсулирована отдельно, а не в mvc и ты с ней отдельно работаешь.
Да ты прав, я тут напрасно быканул, мои извинения. Перекинул полностью конфиг php завелось, вопрос только не легли ли другие сайты...чувствую надо это говно по контейнерам распихать и не ебаться.
Тут соглы - хуярим цикл в цикле, внутри делаем запрос в базу. Особенно пикантно, когда это делается в "компоненте" между html и js-ом в куске пхп-кода
https://3v4l.org/5AsvT
Вот. Я бы мог, конечно, обращаться напрямую к $this->arr, но что это за говнина, зачем геттеры и сеттеры вообще тогда нужны? Вдруг в них какие-то дополнительные проверки, модификации и т. д. Без переменной $tmp тут никак, получается?
Ну давай разберем по частям тобой написанное.
1) Можешь ли ты зафорсить использование геттера снаружи объекта? Да можешь. Если геттер это единственный способ получения данных из объекта у тебя есть гарантия что логика геттера будет выполнена.
2) Можешь ли ты зафорсить использование геттера внутри класса? Нет не можешь. Всегда есть прямой доступ к переменной.
Если нет гарантий что внутри объекта будет вызван геттер, то тупо на это закладываться. А значит в геттер нельзя пихать логику для внутренней работы объекта, а только логику для доступа извне.
Звучит логично, но я не уверен. Сейчас у меня геттер просто возвращает значение, сеттер проверяет каждый элемент переданного массива на соответствие типу. Из твоих слов следует, что я должен нигде внутри не использовать сеттер, а копипастить код переборки и проверки на соответствие типа?
Даже если так. Допустим, внутри сеттеры и геттеры использовать не следует. А если у меня дальше должно упасть какое-то исключение?
https://3v4l.org/44QFC
Понимаю, что пример ущербный, если исключение кидать ПЕРЕД сплайсом, то всё будет нормально. Но я так моментально не могу придумать более реалистичный.
Так или иначе, мне кажется, что алгоритм должен быть примерно такой:
1. Текущее значение списка сохраняем в какую-то промежуточную переменную. Использую я здесь геттер, или напрямую обращаюсь к свойству - вопрос вторичный.
2. Делаем всякое.
3. Если в процессе всякого ошибок не возникло, говняка не вылезло, то сохраняем в $this->arr значение нашей промежуточной переменной.
То есть, получается, промежуточную переменную в любом случае стоит использовать. А будет она в себе хранит результат вызова геттера, или являться результатом прямого обращения к $this->arr - вопрос второй.
То что ты тут пытаешься написать это типичный функциональный код. Есть некоторая структура данных, есть отдельная функция, которая с этими данными работает (у тебя это функция которая использует результат getArr()), а чтобы эту структуру изменить и ничего не сломать мы делаем её иммутабельной и изменяем копию. Классика. Детсадовский пример из хаскеля какого-нибудь.
В чем по сути твоя проблема? В том что в твой массив попали данные, которые туда не должны попасть. Почему? Потому что это блядь массив, в который можно запихнуть любую хуйню.
Если ты пишешь ООП код, то у тебя в место массива должен быть объект в который нельзя поместить хуйню. Потому что объект, в отличии от функциональной тупой структуры данных имеет поведение в виде тех самых инкапсулированных правил, которые просто физически не дадут ему перейти в неправильное состояние.
И как итог у тебя из-за этих ебучих геттеров и сеттеров поведение размазано по коду в нескольких местах. В функциональных языках методы хоть и отдельно от структур данных, но хотя бы в одном и том же модуле лежат. А у тебя будет лютый пиздец, вся обработка данных снаружи. По каким правилам она происходит - пиздуй ищи. Да не забудь перебрать все места где эти функции вызывались.
>Если ты пишешь ООП код, то у тебя в место массива должен быть объект в который нельзя поместить хуйню
Так а что этот объект представляет из себя, если не свойство-массив и всякие методы вроде push, remove и т. д, которые позволяют менять содержимое этого массива, и не дают добавить в него говняк?
https://3v4l.org/5UORL#v8.3.0
Как ты заебал. Ну если ты пишешь в функциональном стиле, так сделай блядь его иммутабельным и не еби мозги ни себе ни людям https://3v4l.org/0jCsX
И все все вопросы отпали нахуй сами собой. Не надо никаких блядских геттеров никаких ебаных сеттеров. И копию надо делать ОБЯЗАТЕЛЬНО. Только к ооп такой код никакого отношения не имеет. Да тебе и похуй на то ооп было.
Я советую для иммутабельных классов дополнительно писать у полей readonly, чтобы уж точно не поменять было. В index.php надо вызывать контроллер 404 страницы. Контроллеры не надо класть в public.
> Что будет делать контроллер, который отвечает за 404-страницу?
Ограничено лишь глубиной твоей фантазии, но в минимальной версии надо отдать код состояния HTTP 404 и показать хотя бы надпись, что страница не найдена.
> Symfony разделяет понятия "форма" и "шаблон"?
Формы там это классы, в которых описано, какие в форме поля, какого они типа, и тд, вот пример: https://symfony.com/doc/current/forms.html#creating-form-classes
А шаблон это файл с HTML-кодом + конструкции twig.
> Представление в MVC, в частности в моем проекте, это же просто HTML-страницы
Да
> Что такое аннотация?
> Что такое атрибут?
Это штука, которая позволяет добавить к классу, полю, аргументу функции или методу дополнительную информацию. Какую? Да какую хочешь, атрибут это объект любого класса. Ты создаешь класс-атрибут, а затем указываешь его имя и параметры для конструктора и объект-атрибут создается и прикрепляется к классу или методу:
#[MyClass('xyz')]
public int $x;
Дальше ты можешь скриптом получить атрибуты класса, поля итд. Погугли где-нибудь статью про это.
Это используется напрмер, так:
- можно указывать правила валидации поля с помощью атрибутов, валидатор читает атрибуты и применяет к полю указанные в них правила. Так сделано в валидаторе Доктрины.
- можно указывать информацию для ORM, то есть какой колонке соответствует данное поле, какой таблице соответствует данный класс, и ORM будет это использовать.
Раньше атрибутов не было, а добавлять информацию хотелось, поэтому в комментариях писали аннотации типа @var или @ORM\Column. Для PHP это был ничего не значащий комментарий. Были библиотеки, которые парсили комментарии и извлекали из них аннотации в виде объектов. В PHP8 появились атрибуты, которые стали частью языка. Теперь аннотации используются только для phpDoc (так как он не использует классы), а все остальное надо делать атрибутами.
Хочешь - сделай атрибуты для маппинга в базу. Чтобы у тебя не был где-то прописан список полей, которые надо сохранять, а чтобы у полей класса был атрибут, говорящий, в какую колонку таблицы его надо сохранить. Или хочешь, добавь студенту правила валидации через атрибуты. Заодно изучишь их.
Я советую для иммутабельных классов дополнительно писать у полей readonly, чтобы уж точно не поменять было. В index.php надо вызывать контроллер 404 страницы. Контроллеры не надо класть в public.
> Что будет делать контроллер, который отвечает за 404-страницу?
Ограничено лишь глубиной твоей фантазии, но в минимальной версии надо отдать код состояния HTTP 404 и показать хотя бы надпись, что страница не найдена.
> Symfony разделяет понятия "форма" и "шаблон"?
Формы там это классы, в которых описано, какие в форме поля, какого они типа, и тд, вот пример: https://symfony.com/doc/current/forms.html#creating-form-classes
А шаблон это файл с HTML-кодом + конструкции twig.
> Представление в MVC, в частности в моем проекте, это же просто HTML-страницы
Да
> Что такое аннотация?
> Что такое атрибут?
Это штука, которая позволяет добавить к классу, полю, аргументу функции или методу дополнительную информацию. Какую? Да какую хочешь, атрибут это объект любого класса. Ты создаешь класс-атрибут, а затем указываешь его имя и параметры для конструктора и объект-атрибут создается и прикрепляется к классу или методу:
#[MyClass('xyz')]
public int $x;
Дальше ты можешь скриптом получить атрибуты класса, поля итд. Погугли где-нибудь статью про это.
Это используется напрмер, так:
- можно указывать правила валидации поля с помощью атрибутов, валидатор читает атрибуты и применяет к полю указанные в них правила. Так сделано в валидаторе Доктрины.
- можно указывать информацию для ORM, то есть какой колонке соответствует данное поле, какой таблице соответствует данный класс, и ORM будет это использовать.
Раньше атрибутов не было, а добавлять информацию хотелось, поэтому в комментариях писали аннотации типа @var или @ORM\Column. Для PHP это был ничего не значащий комментарий. Были библиотеки, которые парсили комментарии и извлекали из них аннотации в виде объектов. В PHP8 появились атрибуты, которые стали частью языка. Теперь аннотации используются только для phpDoc (так как он не использует классы), а все остальное надо делать атрибутами.
Хочешь - сделай атрибуты для маппинга в базу. Чтобы у тебя не был где-то прописан список полей, которые надо сохранять, а чтобы у полей класса был атрибут, говорящий, в какую колонку таблицы его надо сохранить. Или хочешь, добавь студенту правила валидации через атрибуты. Заодно изучишь их.
> Если мне действительно стоит начать заниматься документированием своего кода, то с чего мне начать?
С README: что это за проект, как его развернуть. Это первое, что нужно.
А так, надо ставить phpDoc-типы там, где не хватает нативных типов. Например, чтобы указать структуру массива.
Все комментировать не надо, комментировать надо не-очевидные вещи. Приватные методы обычно не комментируют, публичные комментируют. Свойства комментируют. Для полей, методов, переменных надо использовать понятные названия.
Приведу пример:
- если функция называется getSudentCount, не надо комментировать, что она считает количество студентов, это понятно из названия. А вот если она считает количество только подтвержденных аккаунтов студентов, то это не очевидно из названия и надо указать.
- если функция saveStudent может добавить нового или обновить существующего студента, это не очевидно
Обычно в названии все отразить невозможно (оно будет слишком длинным) и комментарии все-же нужны.
Поищи статью про правильное написание комментариев.
> Еще у меня сложилось впечатление, что атрибуты/аннотации это не просто комментарий, текст, а будто что-то большее.
Атрибут это способ добавления произвольной информации к элементам кода.
> Если я добавлю атрибут или аннотацию в код, что-то изменится?
> У меня начнет иначе работать код?
Нет, чтобы он начал работать по-другому, ты сам должен искать атрибуты в коде и менять поведение. PHP безразличны твои атрибуты.
Типизированных массивов не будет, пока нет обощенных типов. Как они иначе могут взяться.
>>2964013
> Раз интерфейс относится к валидации (интерфейс для классов-проверятелей), то его стоит отнести в папку Validation?
Если ты следуешь принципу "класть вместе классы, делающие одно дело", то да. Если ты следуешь принципу "класть классы по типу в разные папки", то интерфейс идет в папку interfaces (не советую).
> А трейты в Utility?
Если они вспомогательные, то да.
> Залогинить человека = определить зарегистрированный пользователь или нет.
Это значит выставить правильные куки, чтобы он был залогиненным.
> Определить текущего пользователя = отличить одного зарегистрированного пользователя от другого.
Проверить куки на правильность и вернуть либо null, либо текущего залогиненного студента как объект.
> 1) При регистрации пользователя создается же новая запись в БД, тогда нужно брать id новой записи и класть его в куки пользователя.
Тогда хакер может положить себе в куки чужой id и взломать твою систему.
> 2) Взять пароль из куки пользователя, получить его соленый хеш, сравнить с солеными хешами в БД, если есть совпадение = отдать id.
Это уже лучше. Только в куке не надо хранить пароль в открытом виде, а тот самый соленый хеш.
Есть еще вариант 3 - при регистрации генерировать длинный случайный токен, и класть его в базу и в куки пользователю. Хакер его точно не подберет.
Типизированных массивов не будет, пока нет обощенных типов. Как они иначе могут взяться.
>>2964013
> Раз интерфейс относится к валидации (интерфейс для классов-проверятелей), то его стоит отнести в папку Validation?
Если ты следуешь принципу "класть вместе классы, делающие одно дело", то да. Если ты следуешь принципу "класть классы по типу в разные папки", то интерфейс идет в папку interfaces (не советую).
> А трейты в Utility?
Если они вспомогательные, то да.
> Залогинить человека = определить зарегистрированный пользователь или нет.
Это значит выставить правильные куки, чтобы он был залогиненным.
> Определить текущего пользователя = отличить одного зарегистрированного пользователя от другого.
Проверить куки на правильность и вернуть либо null, либо текущего залогиненного студента как объект.
> 1) При регистрации пользователя создается же новая запись в БД, тогда нужно брать id новой записи и класть его в куки пользователя.
Тогда хакер может положить себе в куки чужой id и взломать твою систему.
> 2) Взять пароль из куки пользователя, получить его соленый хеш, сравнить с солеными хешами в БД, если есть совпадение = отдать id.
Это уже лучше. Только в куке не надо хранить пароль в открытом виде, а тот самый соленый хеш.
Есть еще вариант 3 - при регистрации генерировать длинный случайный токен, и класть его в базу и в куки пользователю. Хакер его точно не подберет.
Проблема со словом Модель, что его используют в 2 смыслах:
1) модель как часть MVC - весь код с бизнес-логикой
2) модель как класс, который представляет какую-то сущность типа Студент, Заказ, Товар
Из-за этого путаница.
> А что насчет класса CookieManager?...
Может быть, это просто вспомогательный класс.
> И если регистрации или авторизации нужны куки, то отдавать их должен контроллер? Так наверное...
Желательно, да. Либо просить кого-то отдать куки.
У тебя не включена поддержка коротких тегов <?, а только <?php в настройках php.ini. А битрикс похоже использует эти древние короткие теги.
Не согласен, в MVC сервисы это часть M(model). Никакого "отдельного слоя сервисов" в MVC нету.
Слой сервисов - это другой взгляд на код, как тут: https://martinfowler.com/eaaCatalog/serviceLayer.html
Давай ссылку либо точный запрос, по которому будет статья. А то если гуглить, то выпадает куча SEo-мусора. Ты и не гуглил сам наверно, и не проверял.
> , я должен использовать какую-то промежуточную переменную, если в качестве аргумента для передачи в функцию используется результат выполнения какого-то метода?
А как иначе? Ведь по ссылке ты должен передать "место", в которое можно что-то записать. Число или строка не подходят, подойдет переменная.
> Я бы мог, конечно, обращаться напрямую к $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() = получить имя атрибута
Как я понял по-вкатунски, раньше люди писали описание в виде комментариев, а теперь можно писать описание и представлять его объектом.
Спасибо за ответ.
Прочитав несколько раз твое объяснение, прочитав мануал по 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() = получить имя атрибута
Как я понял по-вкатунски, раньше люди писали описание в виде комментариев, а теперь можно писать описание и представлять его объектом.
Чисто по вкатунски: аттрибуты нужны не для описания и не для комментариев. А за ReflectionClass в коде тебе фанеру пробьют и отправят толчки чистить.
>аттрибуты нужны не для описания и не для комментариев
Окей, для чего они нужны?
>А за ReflectionClass в коде
Почему? И если не так получать атрибуты, то как?
>Окей, для чего они нужны?
Тебе выше написали. Чтобы добавить метаинформацию. Например как свойство класса называется в базе.
>Почему? И если не так получать атрибуты, то как?
Получить их можно только через рефлексию. Почему рефлексия кал наберешь в гугле, это не только к пхп относится.
>Чтобы добавить метаинформацию
Да? А я думал, что описание/комментарии это и есть метаинформация.
Разве метаданные это не данные о данных?
А чем комментарий о том, какой тип у переменной, не метаинформация?
>И категорически не рекомендуется использовать в рабочем коде проекта, т.к. это ещё и не безопасно.
Почему использовать Reflection API не безопасно?
>А чем комментарий о том, какой тип у переменной, не метаинформация?
Метаинформация. Поэтому информацию о типах давным давно перенесли прямо в код и такие комментарии не нужны.
Это вопрос?
>Это значит выставить правильные куки, чтобы он был залогиненным.
Я не понимаю что это значит.
Мне словосочетание "выставить правильные куки" ничего не говорит.
Мой код должен уметь две вещи, относительно запоминания пользователя:
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;
Во-первых, про 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;
> А за ReflectionClass в коде тебе фанеру пробьют
Не пиши чушь. Если ты пишешь свою библиотеку валидации или ORM, то Reflection и атрибуты как раз можно использовать. Да и во многих других случаях.
Потому что дурак статью писал. Если бы это было небезопасно, он бы показал пример и написал, почему. А если не может, то значит слышал звон, да не знает, где он.
Давай начнем со строгих определений:
> Идентификация — это процесс определения личности пользователя, например, по имени или его адресу электронной почты.
> Аутентификация — это сам процесс проверки подлинности пользователя, чтобы убедиться, что он является тем, за кого себя выдает. Для аутентификации пользователи могут вводить пароль, биометрические данные, СМС-код или другие секретные данные.
> Авторизация — это процесс проверки прав доступа пользователя к определенным ресурсам или функциям. Он проводится после успешной аутентификации. В результате авторизации пользователь может получить доступ к определенным файлам, программам и т. д.
Допустим, пользователь на странице банка вводит логин и пароль. Банк проверяет логин - это "идентификация", определение, кто пытается войти. Затем пароль - это "аутентификация", проверка, что пытается войти именно пользователь, а не хакер. Далее пользователь пытается посмотреть историю операций и банк делает "авторизацию" - проверку, что пользователь A имеет право на просмотр истории операций по счету X.
Заметь, что в случае с вебом, идентификация и авторизация происходят не только на форме логина, но и на каждой странице. Обычно послу успешного логина сайт выставляет куку с каким-то секретом (токеном). Браузер отправляет эту куку при заходе на страницу, и сайт идентифицирует и аутентифицирует пользователя по куке.
Кука используется, чтобы можно было войти один раз, и не надо было вводить пароль на каждой странице снова.
Эти 3 этапа есть всегда, даже если регистрации как таковой нет. В задаче про студентов предлагается такой подход: при вводе своих данных сайт генерирует токен и выставляет куку с ним. Когда пользователь заходит с этой кукой на любую страницу, сайт идентифицирует и аутентифицирует пользователя по этой куке.
>> Это значит выставить правильные куки, чтобы он был залогиненным.
> Я не понимаю что это значит.
"залогинить" - установить куки, по которым сайт сможет идентифицировать и аутентифицировать пользователя. "разлогинить" - удалить эти куки.
Давай начнем со строгих определений:
> Идентификация — это процесс определения личности пользователя, например, по имени или его адресу электронной почты.
> Аутентификация — это сам процесс проверки подлинности пользователя, чтобы убедиться, что он является тем, за кого себя выдает. Для аутентификации пользователи могут вводить пароль, биометрические данные, СМС-код или другие секретные данные.
> Авторизация — это процесс проверки прав доступа пользователя к определенным ресурсам или функциям. Он проводится после успешной аутентификации. В результате авторизации пользователь может получить доступ к определенным файлам, программам и т. д.
Допустим, пользователь на странице банка вводит логин и пароль. Банк проверяет логин - это "идентификация", определение, кто пытается войти. Затем пароль - это "аутентификация", проверка, что пытается войти именно пользователь, а не хакер. Далее пользователь пытается посмотреть историю операций и банк делает "авторизацию" - проверку, что пользователь A имеет право на просмотр истории операций по счету X.
Заметь, что в случае с вебом, идентификация и авторизация происходят не только на форме логина, но и на каждой странице. Обычно послу успешного логина сайт выставляет куку с каким-то секретом (токеном). Браузер отправляет эту куку при заходе на страницу, и сайт идентифицирует и аутентифицирует пользователя по куке.
Кука используется, чтобы можно было войти один раз, и не надо было вводить пароль на каждой странице снова.
Эти 3 этапа есть всегда, даже если регистрации как таковой нет. В задаче про студентов предлагается такой подход: при вводе своих данных сайт генерирует токен и выставляет куку с ним. Когда пользователь заходит с этой кукой на любую страницу, сайт идентифицирует и аутентифицирует пользователя по этой куке.
>> Это значит выставить правильные куки, чтобы он был залогиненным.
> Я не понимаю что это значит.
"залогинить" - установить куки, по которым сайт сможет идентифицировать и аутентифицировать пользователя. "разлогинить" - удалить эти куки.
> Токен и соленый хеш от пароля чем-то отличаются?
Токен генерируется из случайных символов, а хеш из пароля.
Спасибо за ответ.
Я не понимаю зачем мне нужен ReflectionAttribute#newInstance
Разве я не могу получить массив атрибутов для чего-то конкретного в коде и вызывать getName()/getArguments(), чтобы оперировать
значениями названия класса-атрибута и его параметров?
Как, например, тут: https://3v4l.org/qMWIE
Зачем мне промежуточная стадия в виде вызова newInstance()?
Спасибо за подробный ответ.
Потому, что мы используем ООП. Зачем нам выковыривать параметры из массивов каких-то, когда у нас есть объекты с удобными методами.
Также, при вызове getInstance проверяется, что класс-атрибут реально существует и аргументы правильные. А иначе не проводится никаких проверок.
К тому же параметр может быть записан несколькими способами, например: MyAttr('xyz') или MyAttr(name: 'xyz') и ты будешь получать разные форматы массивов.
Нет, это битриксоида на Ларавел пустили. По моему опыту они всегда как программисты слабые. Скриптописатели скорее.
velvss
А не подахуел ли твой тимлид? Тимлидит он, зарплату получает он. А на вопросы должны аноны с двачей отвечать? Вот пусть этот хуйлуша и отвечает, нихуя он четко устроился, на анонах воду возить, черт ебаный.
Окей.
В этой строке:
#[NotBlank('Имя заполнять кто будет?')]
private string $name;
Объект какого класса создается? NotBlank или ReflectionAttribute?
Как я понял по var_dump, в этой строке:
$reflProp->getAttributes()
создается массив из объектов класса ReflectionAttribute
И только у объектов этого класса есть методы: getName(), getArguments().
Если я сделаю так:
$reflProp->getAttributes()[0]->newInstance()
создается объект уже класса NotBlank, но NotBlack не обладает методами getName(), getArguments(), а его свойство является
приватным, как мне добывать информацию из атрибута?
Решение, которое я придумал - добавить в класс NotBlank геттер.
Так приемлимо?
>Чтобы у тебя не был где-то прописан список полей, которые надо сохранять, а чтобы у полей класса был атрибут, говорящий, в какую колонку таблицы его надо сохранить
Поля, которые нужно сохранять в БД, - это поля класса Enrollee.
Следуя этому, я делаю так:
Класс-атрибут: https://3v4l.org/o1LDQ
Класс Enrollee: https://3v4l.org/hV5Yd
Дальше я пишу класс 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 получить легко список полей и дальше найти среди них поля с атрибутом. Вот рефлекшен тебе и пригодился.
>Дальше, я бы советовал сделать имя колонки необязательным, чтобы его можно было не указывать, в этом случае класс работы с БД просто возьмет название поля. Зачем 2 раза писать одно и то же? То есть, чтобы было
Что ты имеешь ввиду?
Ничего не передавать в конструктор объекту-атрибуту?
Что вообще будет обозначать атрибут?
Его наличие для кода будет означать, что это поле нужно добавлять в БД? Т.е. если у поля есть атрибут Column, то его нужно добавлять в БД? А если нет - то класс работы с БД игнорирует его?
>Ты можешь с помощью ReflectionClass получить легко список полей и дальше найти среди них поля с атрибутом. Вот рефлекшен тебе и пригодился
>Окей, я попробую.
Спасибо за ответ.
Допустим есть сайт, к примеру ВК, чтобы получить разную инфу нужно обращаться к API.
Как делать?
Вот юзер открывает мой сайт и я сразу отправляю запрос, далее жду ответ, потом обрабатываю и так последовательно? Т.е. это анальная зависимость от работы чужого сервера? Так делают?
Нет. Есть секретная техника чтобы делать эти запросы мгновенно и без зависимости от серверов ВК.
Или vs код?
Как его добыть? Так неохота вирусню скачивать пиздец на лицензионную винду. Хотя есть вариант под линукс скачать и на виртуал боксе запускать.
В общем основной вопрос - вы все на работах на пхпшторме?
>на работе
>лицензионную винду
Ты баба срака из бухгалтерии что-ли? На работе линукс или мак онли.
Не ну правда, почитали бы документацию по Симфони и не изобретали свои примитивные велосипеды. В очередной раз убеждаюсь, что в PHP все лучше, чем в мире фронтендщиков и других языков.
Просто хочу разобраться, а какую библиотеку используют пхп-чиркаши для валидации json'а?
Да я ебал его с ноута сносить, лицензионный. На виртуалке есть
Злюка :p
Я еще давно написал DIContainer по твоему уроку:
https://3v4l.org/cJOHj
Извини, если я неправильно написал аннотации.
Сегодня смотрел видео по psalm, узнал что это анализатор кода.
Ага! Мне не очень хочется пока что разбираться в утилитах по php. Прости, пожалуйста, да, я очень плохой человек, мне очень стыдно. Просто ты из поста в пост пишешь, что мне следует этому научиться, а я ленюсь.
Ну и как раз вопрос (раз затронулась тема комментирования кода), мб мне нужно описывать структуру массива с помощью атрибутов? Я не знаю еще как.
И, возможно, с многомерными массива будет проблема.
Сегодня я вернулся к DIContainer и решил реализовать в нем интерфейс ArrayAccess.
Если почитать мануал php и посмотреть на эту сигнатуру:
public offsetSet(mixed $offset, mixed $value): void
Я точно знаю, что ключами своего контейнера хочу видеть строки (строками, мб, и правильнее, я читал текст по ссылке, которую ты скинул про PSR-11, ну и да, там был метод has(), на который я забил, это настолько критично?), но тут указывается mixed тип, должен ли я делать проверку (на тип) в начале метода и выкидывать исключение?
Это первый момент, а вот второй:
Интерфейс обязывает иметь этот метод:
public offsetExists(mixed $offset): bool
Как мне следует его реализовывать, держа в уме, что у меня 2 свойства-массива - для зарегистрированных сервисов и для созданных объектов?
Собственно вот какой код DIContainer у меня получился:
https://3v4l.org/Yhuue
Если что это черновик.
Я старался добиться только нужной мне функциональности.
Я еще давно написал DIContainer по твоему уроку:
https://3v4l.org/cJOHj
Извини, если я неправильно написал аннотации.
Сегодня смотрел видео по psalm, узнал что это анализатор кода.
Ага! Мне не очень хочется пока что разбираться в утилитах по php. Прости, пожалуйста, да, я очень плохой человек, мне очень стыдно. Просто ты из поста в пост пишешь, что мне следует этому научиться, а я ленюсь.
Ну и как раз вопрос (раз затронулась тема комментирования кода), мб мне нужно описывать структуру массива с помощью атрибутов? Я не знаю еще как.
И, возможно, с многомерными массива будет проблема.
Сегодня я вернулся к DIContainer и решил реализовать в нем интерфейс ArrayAccess.
Если почитать мануал php и посмотреть на эту сигнатуру:
public offsetSet(mixed $offset, mixed $value): void
Я точно знаю, что ключами своего контейнера хочу видеть строки (строками, мб, и правильнее, я читал текст по ссылке, которую ты скинул про PSR-11, ну и да, там был метод has(), на который я забил, это настолько критично?), но тут указывается mixed тип, должен ли я делать проверку (на тип) в начале метода и выкидывать исключение?
Это первый момент, а вот второй:
Интерфейс обязывает иметь этот метод:
public offsetExists(mixed $offset): bool
Как мне следует его реализовывать, держа в уме, что у меня 2 свойства-массива - для зарегистрированных сервисов и для созданных объектов?
Собственно вот какой код DIContainer у меня получился:
https://3v4l.org/Yhuue
Если что это черновик.
Я старался добиться только нужной мне функциональности.
>Сегодня я вернулся к своему велосипеду и решил добавить в него гребной винт и якорь.
>я читал текст по ссылке, которую ты скинул про сборку велосипедов, там была цепь, на которую я забил, это настолько критично?
>но упоминается руль, должен ли я делать рулевое колесо с гидроусилителем или без?
>Если что это черновик.
>Я старался добиться только нужной мне функциональности.
Я читал мануал по 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'
));
сразу все заработало, как написать тоже самое, но только без массива?
Ага, спасибо, анонче.
Оказывается вот так нужно было писать: httponly: true
А я пытался как $httponly=true
Значения хранятся в объектах.
Не хочу в html прописывать логику получения значения из объекта.
Хочу сделать класс ViewHelper, который будет преобразовывать объект так, чтобы в html его можно было вывести просто как переменную или элемент массива.
Дополнение: чтобы получить значение из объекта, мне нужно писать разные инструкции для каждого объекта, поэтому и возник вопрос.
Какой вариант лучше (мб анон предложит свой вариант)?
Первый вариант:
Сделать 2 метода в одном классе: преобразоватьОбъект1, преобразоватьОбъект2.
Мне не нравится этот вариант, потому что теряется универсальность класса.
Второй вариант:
Сделать 2 класса ViewHelper: для Объекта1, для Объекта2 с соответ. методами.
Мне не нравится этот вариант, потому что метод-то один, как-то тупо создавать класс для одного метода?
Третий вариант:
Сделать просто 2 функции, не методы.
Никто ничего ни спрашивает, ни обсуждает, ни спорит, ни отвечает.
Потому что мы в великой России, а не на загнивающем западе. Здесь работы с битриксом намного больше чем по вордпрессу
И что будет?
Все равно никто не пишет.
Я думаю, что перекат стоит делать на посте ~1000.
Так же можно? А то я не сижу на ваших двачах
Не знаю. Я вкатун. Я даже фреймворк никогда не трогал.
>И что будет?
Тред сейчас на дне находится, после переката может люди хоть увидят что пхп-тред существует и начнут заходить сюда
Ну да, мб и так.
Я могу подготовить пикчи для шапки.
Сам я перекатывать не очень хочу, никогда такое не делал, вдруг облажаюсь :)
Ну и да, я хуй знает как тут перекат работает, перекатывает определенный человек или тот, кто хочет и может.
Ну так айти всё, вкатуны не нужны, мидлы трясутся
Кому хочется и можется тот и перекатывает, главное чтобы это не навредило самому тредику и его обитателям
Не надо пока что, давай я на выходных перекачу. Мы тут обычно не перекатываем сразу после 500-го поста, раньше и по 1000 постов было иногда.
Ты наверно ставил через create-project или что-то подобное, а можно же просто вручную нужные пакеты поставить через композер.
А так вообще все эти заготовки проектов и установщики дурацко сделаны. Всегда надо ставить минимум всего, а не включать фичи, которые могут не понадобиться.
Ну кстати я слышал, у фронтендщиков еще хуже, там при установке какого-нибудь vue тебе предлагают кучу ненужной фигни заодно уставовить, прямо как мейл ру в свое время.
Для PHP есть библиотеки проверка по JSON Schema, но никто в здравом уме не будет использовать это для бизнес-логики. JSON Schema придумана, чтобы проверить, что данные имеют нужную структуру, а не то, что при заполнении заявки на кредит ты ввел имена и телефоны не менее 5 близких родственников.
DIContainer норм, но ты бы мог сделать его соответствующим PSR-11, это несложно. Для callable можно было бы указать в аннотации тип аргумента.
> Ну и как раз вопрос (раз затронулась тема комментирования кода), мб мне нужно описывать структуру массива с помощью атрибутов? Я не знаю еще как.
Ну я же давал где-то выше ссылку, ты пишешь так:
@var array{ x: int, y: int, s: array<string, int> }
> Сегодня я вернулся к DIContainer и решил реализовать в нем интерфейс ArrayAccess.
Почему бы и нет.
> но тут указывается mixed тип, должен ли я делать проверку (на тип) в начале метода и выкидывать исключение?
Можно, но не должен.
> public offsetExists(mixed $offset): bool
> Как мне следует его реализовывать, держа в уме, что у меня 2 свойства-массива - для зарегистрированных сервисов и для созданных объектов?
ArrayAccess позволяет получать и задавать значения по ключу. Ты должен сам подумать, что у тебя тут будет ключом и значением. Можно, например, получать сервисы по имени, тогда offsetExists проверяет, зарегистрирован ли сервис, но тогда надо запретить их менять через ArrayAccess.
Вообще этот интерфейс придуман для классов, которые реализуют какую-то коллекцию, хранилище чего-то.
> Собственно вот какой код DIContainer у меня получился:
Я тут вижу некоторую нелогичность, что ты записываешь в контейнер функции, а получаешь из него по тому же ключу объекты. Мне кажется, это будет запутывать при чтении кода.
DIContainer норм, но ты бы мог сделать его соответствующим PSR-11, это несложно. Для callable можно было бы указать в аннотации тип аргумента.
> Ну и как раз вопрос (раз затронулась тема комментирования кода), мб мне нужно описывать структуру массива с помощью атрибутов? Я не знаю еще как.
Ну я же давал где-то выше ссылку, ты пишешь так:
@var array{ x: int, y: int, s: array<string, int> }
> Сегодня я вернулся к DIContainer и решил реализовать в нем интерфейс ArrayAccess.
Почему бы и нет.
> но тут указывается mixed тип, должен ли я делать проверку (на тип) в начале метода и выкидывать исключение?
Можно, но не должен.
> public offsetExists(mixed $offset): bool
> Как мне следует его реализовывать, держа в уме, что у меня 2 свойства-массива - для зарегистрированных сервисов и для созданных объектов?
ArrayAccess позволяет получать и задавать значения по ключу. Ты должен сам подумать, что у тебя тут будет ключом и значением. Можно, например, получать сервисы по имени, тогда offsetExists проверяет, зарегистрирован ли сервис, но тогда надо запретить их менять через ArrayAccess.
Вообще этот интерфейс придуман для классов, которые реализуют какую-то коллекцию, хранилище чего-то.
> Собственно вот какой код DIContainer у меня получился:
Я тут вижу некоторую нелогичность, что ты записываешь в контейнер функции, а получаешь из него по тому же ключу объекты. Мне кажется, это будет запутывать при чтении кода.
Кстати, подниму вопрос этого анона, самому интересно. Я пока ещё не работаю, только учусь, а в качестве редактора кода использую SublimeText. Однако понимаю что в будущем придётся накатить PHPStorm, но не знаю как это сделать. У самого тоже винда. С меня нихуя тонны нефти и цистерна чая, если кто скинет гайдик какой.
А что, я задавал сюда вопросы периодически, причём вполне адекватные. Меня игнорили.
Мб ты не дожидался ответа?
Тут регулярно отвечали только два человека.
Один из них ОП. Он обычно всем отвечает, но раз в какой-то промежуток времени.
Спасибо за ответ.
Как считаешь, должен ли я считать ReflectionClass зависимостью, если без него не работает мой класс? Почти не работает. Ну, конечно, можно было бы обойтись и без рефлексии, если бы я прописал где-то в классе список полей.
>вручную нужные пакеты поставить через композер
Я так не умею. Тем более надо знать какие пакеты он использует, а я только изучаю фреймворк.
Я на прошлой работе делал сайт на вордпрессе для местной думы
Или я действительно должен сделать свой класс, реализующий интерфейс ArrayAccess, и в методе offsetSet() падать, если переданный ключ не является [0 ... n] с шагом +1? А встроенного SplList или чего-то типа нет?
Не считается.
>>2974959
Либо написать, либо найти готовую библиотеку, не ты первый с этим сталкиваешься. Есть расширение Ds, например: https://www.php.net/manual/ru/class.ds-sequence.php
Рефлексия оставлена для тех случаев, когда геттер и сеттер использовать нельзя или неправильно. Например: отладчик, показывающий содержимое объектов. Библиотека, которая добавляет к объектам новый фукнционал.
Тебе почти никогда не надо ее использовать.
Например, она используется в ORM Doctrine для создания объектов и установки им свойств.
Если не умеешь, то значит надо учиться, а не жаловаться.
Список пакетов можно подсмотреть тут: https://github.com/symfony/skeleton/blob/6.3/composer.json
Также, ты можешь просто установить симфони в новой папке и руками удалить папку .git, и будет Симфони без гита.
Скоро, дай на вопросы ответить сначала.
>>2973486
Нет, ReflectionClass мы не будем считать зависимостью. Тут конечно получается некоторая путаница, но в конструктор обычно передают:
- классы-сервисы
- классы с какими-то настройками, которые можно менять (поэтому их передают снаружи, чтобы мы могли менять эти настройки)
- классы, которые можно подменить на другие
ReflectionClass под эти критерии не подходит, так как это встроенный в PHP класс и вряд ли мы будем заменять его на другой, и у него нет каких-то настроек, которые можно менять.
Вижу, что объяснение в статье про DI не очень понятное и логичное ... но пока не знаю, как его можно переписать.
Ты мой вопрос поднял. Бесплатный аналог - нетбинс. Но сам сижу с вскодом + обмазанный плагинами удалённого доступа, докер, php, мускл
Зачем шапку менять?
Там вирус
Как-то по коллежу делал свой мини говнофреймворк mvc, там маршрутизация работала. Но по принципу {method} /[controller]/path
И затем есть методы контроллеров, которые имеют аттрибуи HttpGet и так далее, а в них указан path соответствующий
Там магия рефлексии юзалась)
Можешь подумать об этом
Это копия, сохраненная 10 марта в 20:54.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.