Вы видите копию треда, сохраненную 27 апреля 2019 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.
В нашем треде отвечают почти на все вопросы, только бампайте каждые 5 дней. И не разводите флуд, если вам скучно, лучше сходите промочите ноги на улице, например.
Это тред и для начинающих. Слово "классы" у тебя ассоциируется только со школой, а в аттестате тройка по математике? Ты наш человек.
Предыдущий тред был тут: >>1331378 (OP) . Остальные треды есть в архиве: https://phpclub.tech/ (там есть поиск, так что можно легко найти обсуждение какой-то задачи или ответы на свой старый пост) или ищутся в гугле по словам "клуб изучающих php" и в архиваче.
Мейлач лежит? Есть запасной тред на доброчане: /s/res/23225.xhtml#i46467
Форматируй свой код, если хочешь, чтобы его читали (как, написано во втором посте).
Правила: ведем себя воспитанно, помогаем новичкам, читаем учебники, решаем задачки, постим ссылки на решения, ОП их проверяет и дает советы и замечания. ОП заходит редко, где-то раз в 2-3 дня, у него мало времени, не жди его, решай задачки дальше. ОП отвечает на все вопросы по его задачкам и учебнику, а вот насчет каких-то других вещей - только если останется время. Но в треде немало анонимных экспертов разного уровня, так что вряд ли вопрос останется без ответа.
С чего начать
У нас есть свои уроки по основам PHP, они собраны и выложены по адресу http://codedokode.github.io/phpbook (вас отредиректит на другой домен, не читайте, не сохраняйте, не запоминайте его, он временный). Это учебник для изучающих с нуля, то есть если ты вообще ничего не знаешь, то можно начать с него. Он простой и понятный. Там есть задачи, их нужно решать (чтобы стать программистом, надо писать код — иначе никак). Пости ссылки на решения в тред, мы их проверим, напишем замечания и дадим советы по улучшению. С другой стороны, если этот учебник тебе не нравится, можно читать любой другой. Или официальный мануал. Или все сразу.
Устанавливать пока что ничего не требуется, разве что редактор кода вроде Sublime Text 3, Notepad++, Visual Studio Code, Netbeans PHP или PhpStorm (с ним будет удобнее).
Если не знаешь как решать, запости код, напиши в каком месте остановился и попроси подсказку.
Ты прошел весь учебник? Молодец, но это были лишь основы языка PHP, этого недостаточно. Вот что в идеале надо изучить еще: ООП, как работает веб-сервер, HTML/CSS, SQL, PDO, работа с таблицами в БД, работа с формами, MVC, git, composer, JS, фреймворки, автоматизированное тестирование.
Надо переходить к более серьезным задачкам, которые научат тебя всему этому.
- для начала прочти урок https://github.com/codedokode/pasta/blob/master/soft/web-server.md
- установи Апач + PHP (советы выше и ниже) и читай туториал http://php.net/manual/ru/tutorial.php
- Учи HTML/CSS и SQL, PDO, хотя бы основы
- Далее простая, но полезная задача сделать список студентов, в ней много полезных советов: https://github.com/codedokode/pasta/blob/master/student-list.md
- Более сложная задача сделать файлообменник на микрофреймворке Slim: https://gist.github.com/codedokode/9424217
- Еще более сложная и долгая задача на Yii/Symfony: https://gist.github.com/codedokode/8733007
- После нее можно изучать автоматизированное тестирование https://gist.github.com/codedokode/a455bde7d0748c0a351a
- Если ты все решил, переходи к Symfony 3/Doctrine 2
- Почитать про паттерны http://designpatternsphp.readthedocs.org/ru/latest/README.html (если ты не изучил ни одного фреймворка, то это будет рановато), тут с примерами кода http://designpatternsphp.readthedocs.org/ru/latest/README.html . Имей в виду что без примеров использования их учить бесполезно - не поймешь, хочешь увидеть примеры использования паттернов - ковыряй исходники Симфони, например Symfony Forms. Не заучивай паттерны - смотри код и думай, зачем тут они использованы.
Чтобы делать эти задания, тебе надо установить Апач + PHP (можно заодно сразу и MySQL) на компьютер. Вот полезные инструкции:
https://github.com/codedokode/pasta/blob/master/soft/php-install.md
https://github.com/codedokode/pasta/blob/master/soft/apache-install.md
Может тебе понадобится пользоваться командной строкой, вот гайд https://github.com/codedokode/pasta/blob/master/soft/cli.md
Решения задач лучше показать мне, особенно на ООП,так как сам ты вряд ли увидишь все ошибки. Пости свой код на гитхаб и вкидывай ссылку в тред по мере решения. Я прокомментирую и укажу на ошибки.
Параллельно стоит подучивать английский, на первых порах можно без него, но по мере развития придется все чаще сталкиваться с англоязычными статьями, так что лучше не откладывать. Читать можно news.ycombinator.com - это что-то вроде их хабра. Также можно начинать смотреть фильмы и видео на английском.
Также, у нас есть задачи которые позволят тебе изучить или подтянуть до нормального уровня знания JS/HTML/CSS/SQL. Решай их параллельно с задачами выше.
- HTML/CSS: https://github.com/codedokode/pasta/blob/master/html/html.md
- 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/
- MySQL: https://github.com/codedokode/pasta/blob/master/db/databases.md
Что почитать
- Мануал по PHP — http://www.php.net/manual/ru/langref.php
- Сайт phptherightway (перевод на русский: http://getjump.me/ru-php-the-right-way/ )
- По PHP: Профессиональное программирование на PHP Джордж Шлосснейгл
- По PHP: Мэтт Зандстра — PHP: Объекты, шаблоны, методики программирования
- JS: learn.javascript.ru
- Про Git: https://git-scm.com/book/ru/v1
- Новости IT на англ. https://news.ycombinator.com/
- какой-то древний, устаревший, но большой и на русском справочник по веб-разработке, посоветованный аноном: https://starcat.dp.ua/doc/wdh/
Оформляй код аккуратно!!! — например пропусти через phpformatter.com . Также, если ты пользуешься IDE вроде PhpStorm, Netbeans, Eclipse, то в них эта опция встроена, подробнее: https://gist.github.com/codedokode/8759492
У ОПа нет аккаунтов и групп вконтакте, в фейсбуке, в твиттере, все "пхп-треды" там поддельные.
Платиновые вопросы
- Почему PHP? Потому что вакансий море, и учить легко.
- Сайт опять упал!!!!! — Не паникуй, а открой http://rghost.ru/6bfCY9lfl и получи личную немного устаревшую оффлайновую копию сайта (можно читать хоть на андроиде без интернета)
- Что надо знать чтобы найти работу - разработчику: PHP, SQL, HTML/CSS, JS, ООП, Git, композер, MVC, фреймворк. Верстальщику - HTML/CSS, JS, jQuery. У нас в треде были люди, которые практически с нуля учились и смогли найти работу.
- Что будут спрашивать на собеседовании если 0 опыта - гонять по теории, по официальному мануалу PHP, давать дурацкие задачки на переворачивание строк, гонять по SQL (транзакции, внешние ключи, напиши запрос), по JS (как сделать анимацию при нажатии кнопки), ну погугли, не ленись
- Можно подробнее про поиск работы, собеседования - нет, ОП писать не будет, но может кто из анонов захочет рассказать. Поищите тред перезвонивших, а также раздел /wrk/
- Сколько времени надо изучать все это? - все зависит от тебя, но не меньше 6-8 месяцев
- Нужен ли ООП, фреймворки, MVC, git, composer? — Да, однозначно. Посмотри любую вакансию.
Если тебе лень выравнивать код руками, закачай его на http://beta.phpformatter.com/ и нажми «format». Робот исправит выравнивание и отступы в мгновение ока (да, прогресс не стоит на месте). Если ты используешь мощную IDE вроде PhpStorm, там тоже есть функция форматирования кода.
Горячие клавиши для форматирования кода в разных IDE: https://gist.github.com/codedokode/8759492
Вообще, в PHP долгое время не было единого стандарта оформления кода, все писали как попало и было много бардака, но сейчас дело лучше — есть стандарты PSR-1 и 2. Вот как надо оформлять код:
- переменные и функции пишутся с маленькой буквы, подчеркивание не используется, используется camelCase, пример: $x, $numberOfPeople, printResults()
- Название функции начинается с глагола, в стиле «сделайЧтоТо»
- не знаешь английский? Не беда, в 21 веке есть решение этой проблемы. Не пиши транслитом, открой лучше Гугл Транслейт или slovari.yandex.ru и найди название для переменной там
- в именах классов используется CamelCase, первая буква большая, «_» может использоваться
- мы предпочитаем подстановку переменных вместо конкатенации строк: "I am $age years old" — хорошо, 'I am ' . $age . ' years old' — плохо из-за обилия точек и кавычек
- мы используем для отступов 4 пробела (можно настроить редактор, чтобы при нажатии Tab он вставлял 4 пробела)
Вот ссылка на стандарты, где все это описано подробнее и даны примеры оформления:
PSR-1: https://github.com/samdark/fig-standards-ru/blob/master/accepted/ru/PSR-1-basic-coding-standard.md
PSR-2: https://github.com/samdark/fig-standards-ru/blob/master/accepted/ru/PSR-2-coding-style-guide.md
Нет, сори. Я умею ток взламывать вк.
Подумайте об этом.
Смотри через Network, что сервер тебе присылал. Если присылал уже со звездочками, то ничего.
бля, а ведь он прав
Количество вакансий и фирм глянь, где используется. Пхп очень популярен, прост в осовении и гибок, инструменты все современные есть, по скорости щас тоже отдалили. С жабой имеет смысл начинать, если есть огромный энтерпрайз, где кучи сервисов друг с другом коммуницируют. Жаба в крайне малом количестве стартапов сейчас используется, проще и быстрее все на пхп бывает написать и инвесторам результаты показать, чтобы получить дальнейшее финансирование.
Облом. Приходит со звездюльками( Спасиб всеж.
Проще и быстрее писать на рельсах, если ты очередной манястартапер. Если же хочешь в норм контору - то тут уже юзается питчон, а если прям охуеваешь от своей тормознутости - присыпается голанг для убер маня хайлода.
>чтобы получить дальнейший говнокод.
Подправил.
Как ни грустно это осозновать, но двачую.
Какие плюсы у Питона? Это же по сути и по синтаксису почти тот же Яваскрипт, нет даже тайп-хинтов. В Руби та же ерунда. В PHP больше возможностей статической типизации, есть фреймворки, ORM. И разработчиков найти проще.
И, конечно, непонятно, как просто использование Питона или Руби повышает качество кода. С чего бы вдруг? Что, менеджер по продажам, прошедший экспресс-курсы по Питону, будет писать более качественный код, чем после курсов по PHP? Вы, товарищи, просто распространяете очень далекие от реальности стереотипы.
>В PHP больше возможностей статической типизации, есть фреймворки, ORM. И разработчиков найти проще.
Это все нивелируется с существование жабы. Если бы ее не было, возможно пхп был бы еще уебищней.
>>53831
Качественный код может написать любой человек который умеет читать доки.
Какую-то хуйню не аргументированную спизданул.
>Это все нивелируется с существование жабы.
Ну так и есть, пхп второй после жабы, пикрелейтед. Где жабу поднимать геморно, но удобство и функциональность жабы нужна, поднимают пхп.
Бочку
есть два скрипта. В одном весь код поделен на методы вплоть до абсурда, например, метод hasProperty имеет всего лишь одну строчку кода return isset(prop); и методов очень много, сами понимаете, код раздут, код ради кода. В другом все написано в одном методе. Какой из этих скриптов отработает быстрее?
А ты это не читал? https://github.com/codedokode/pasta/blob/master/arch/di.md Там нет ответа?
Помогите изменить скрипт, пожалуйста.
Логика такая: при загрузке страницы срабатывает скрипт, считывает значение поля amount, отправляет его аяксом в requestUrl.php, принимает ответ и ждёт, пока человек не нажмёт кнопку payButtonId. Нажал - данные улетают в https://pay.realexpayments.com/pay
Проблема в том, что кроме amount нужны дополнительные поля, но пользователь заполняет их уже на странице. То есть, нужно, чтобы он сначала заполнил их, и только потом срабатывала их отправка в requestUrl.php
У меня не получается так сделать. Даже если ввести ещё одну кнопку и повесить на неё скрипт отправки в requestUrl.php, а на вторую отправку в платёжку, то отправка в платёжку не срабатывает. Работает только по $(document).ready
Вот код:
https://jsfiddle.net/tw6f7y2b/
Да. Если бы я был бы тобой и только закончил бы опа таски - то я бы сменил род деятельности ( в програмаче ). Явно не жаба, там слишком много писать. Пошел бы в питон, если бы ничего больше не знал. А если бы я делал выбор сейчас - то остался бы в мобилках. Тут языки не нужно придумывать. Все стандартно. Но для бека я бы выбрал голанг или рельсы.
а если в системное програмирование?
>как-год-перекатился-в-божественный-руби
А я вот выбирал между гошкой и руби. Не понравилась мне динамика. Поэтому делаю внутрипроекты на гошке.
Смысл сервис-контейнера в том, что он может создавать сервисы с зависимостями, не заставляя нас делать это вручную. Принцип DI (внедрение зависимостей) обычно требует передавать зависимости в сервис снаружи. Вручную это выглядит так. Допустим, нам нужен сервис C, который зависит от A и B:
$a = new A;
$b = new B;
$c = new C($a, $b);
С контейнером:
$c = $container->get('C'); // Или $app->make('C') в Laravel
Если что, DI и контейнеры я попытался, как мог, описать в своем уроке: https://github.com/codedokode/pasta/blob/master/arch/di.md - без теории по DI трудно понять, зачем нужен контейнер.
Второй плюс DI контейнера в том, что он умеет сохранять ранее созданный объект и возвращать его, а не создавать новый, при повторном вызове. Это в Ларавель называют "синглтоном".
Далее, ты упомянул "сервис-провайдеры". Это немного другая штука. Они используются, чтобы пачкой зарегистрировать несколько сервисов. Ты мог бы обойтись и без провайдера, и просто зарегистрировать сервисы в контейнере руками, но если ты, например, делаешь библиотеку, то логично объединить предоставляемые ей сервисы в провайдер, чтобы можно было зарегистрировать их одним действием.
> Заодно вопрос какая структура папок должна быть для контрактов и их реализации.
Не знаю, в документации Ларавел не описано?
> Если бороться с жирными контроллерами через сервисный слой то какую выбрать структуру папок чтоб можно было вернуться к проекту через месяц?
А ты разобрался, что такое сервисы? Проблема "толстых контроллеров" в том, что код в них нельзя повторно использовать. Ты описал, например, код регистрации пользователя в контроллере и после этого ты не можешь создать пользователя программно из другого места кода. Сервисы решают эту проблему.
Если у тебя маленький проект, то ты просто кладешь сервисы в одну папку. В простейшем случае можно для каждой сущности сделать свой сервис (сервис для работы с постами, с комментариями, с пользователями). Если большой - то делаешь, например, подпапки, а сами сервисы разделяешь по задачам: сервисы приема платежей, сервисы для подсчета статистики, итд.
Смысл сервис-контейнера в том, что он может создавать сервисы с зависимостями, не заставляя нас делать это вручную. Принцип DI (внедрение зависимостей) обычно требует передавать зависимости в сервис снаружи. Вручную это выглядит так. Допустим, нам нужен сервис C, который зависит от A и B:
$a = new A;
$b = new B;
$c = new C($a, $b);
С контейнером:
$c = $container->get('C'); // Или $app->make('C') в Laravel
Если что, DI и контейнеры я попытался, как мог, описать в своем уроке: https://github.com/codedokode/pasta/blob/master/arch/di.md - без теории по DI трудно понять, зачем нужен контейнер.
Второй плюс DI контейнера в том, что он умеет сохранять ранее созданный объект и возвращать его, а не создавать новый, при повторном вызове. Это в Ларавель называют "синглтоном".
Далее, ты упомянул "сервис-провайдеры". Это немного другая штука. Они используются, чтобы пачкой зарегистрировать несколько сервисов. Ты мог бы обойтись и без провайдера, и просто зарегистрировать сервисы в контейнере руками, но если ты, например, делаешь библиотеку, то логично объединить предоставляемые ей сервисы в провайдер, чтобы можно было зарегистрировать их одним действием.
> Заодно вопрос какая структура папок должна быть для контрактов и их реализации.
Не знаю, в документации Ларавел не описано?
> Если бороться с жирными контроллерами через сервисный слой то какую выбрать структуру папок чтоб можно было вернуться к проекту через месяц?
А ты разобрался, что такое сервисы? Проблема "толстых контроллеров" в том, что код в них нельзя повторно использовать. Ты описал, например, код регистрации пользователя в контроллере и после этого ты не можешь создать пользователя программно из другого места кода. Сервисы решают эту проблему.
Если у тебя маленький проект, то ты просто кладешь сервисы в одну папку. В простейшем случае можно для каждой сущности сделать свой сервис (сервис для работы с постами, с комментариями, с пользователями). Если большой - то делаешь, например, подпапки, а сами сервисы разделяешь по задачам: сервисы приема платежей, сервисы для подсчета статистики, итд.
Это правильно называется "скрейперы", и как правило, абсолютно ничего сложного там нет, все элементарно:
- загружаем HTML код
- выделяем куски страницы с помощью какой-нибудь DOM-библиотеки, по имени CSS-класса, по тексту, по еще каким-то HTML-атрибутам
- выводим собранные данные в каком-нибудь формате
Если ты знаешь HTML и DOM, то это обычно элементарная задача.
Со сложными алгоритмами никто не заморачивается, так как обычно написанный вручную "скрейпер" стоит очень дешево, зачастую как 1 час работы профессионального разработчика.
Еще можно, как Телеграм, объявить конкурс и выбрать лучшее решение: https://habr.com/ru/post/438926/
Еще есть способы, когда ты берешь страницу, каким-то расширением для браузера помечаешь на ней нужные места, и оно автоматически генерирует код для скрейпинга (или, например, xpath- или CSS-выражения). Это, мне кажется, интересная идея, так как позволяет привлекать не-программистов (за меньшие деньги) к созданию скрейперов.
Но те, кому нужно заниматься скрейпингом массово, например, поисковые системы, иногда пытаются что-то придумывать: микроразметку, автоматическое выделение содержимого, итд. Простейший способ автоматического анализа - взять 2 разных страницы с сайта, сравнить и таким образом увидеть различающиеся и общие (не содержащие полезной информации) части. Я когда-то давно пробовал писать такую штуку, но там оказался огромный объем работы и я забил на нее.
http://xgu.ru/wiki/HTML_scraping
>>54305
Какой смысл выучить один язык, забросить, и учить другой? Никакого.
Это, конечно, не значит, что не надо смотреть другие языки - полезно для расширения кругозора, или повышения своих навыков.
Но учить язык только потому, что анонимные эксперты с двача называют его "боежственным", довольно глупо. Есть такие люди, которым в принципе работать не интересно, они весь день сидят, пишут код на Си, PHP, C# или Яве, страдают, а тут рассуждают про "божественные" языки (которые точно так же им наскучат через неделю, если попросить их поучаствовать в большом, реальном проекте на них в команде, с реальным кодом, а не задачками на 500 строк).
Это правильно называется "скрейперы", и как правило, абсолютно ничего сложного там нет, все элементарно:
- загружаем HTML код
- выделяем куски страницы с помощью какой-нибудь DOM-библиотеки, по имени CSS-класса, по тексту, по еще каким-то HTML-атрибутам
- выводим собранные данные в каком-нибудь формате
Если ты знаешь HTML и DOM, то это обычно элементарная задача.
Со сложными алгоритмами никто не заморачивается, так как обычно написанный вручную "скрейпер" стоит очень дешево, зачастую как 1 час работы профессионального разработчика.
Еще можно, как Телеграм, объявить конкурс и выбрать лучшее решение: https://habr.com/ru/post/438926/
Еще есть способы, когда ты берешь страницу, каким-то расширением для браузера помечаешь на ней нужные места, и оно автоматически генерирует код для скрейпинга (или, например, xpath- или CSS-выражения). Это, мне кажется, интересная идея, так как позволяет привлекать не-программистов (за меньшие деньги) к созданию скрейперов.
Но те, кому нужно заниматься скрейпингом массово, например, поисковые системы, иногда пытаются что-то придумывать: микроразметку, автоматическое выделение содержимого, итд. Простейший способ автоматического анализа - взять 2 разных страницы с сайта, сравнить и таким образом увидеть различающиеся и общие (не содержащие полезной информации) части. Я когда-то давно пробовал писать такую штуку, но там оказался огромный объем работы и я забил на нее.
http://xgu.ru/wiki/HTML_scraping
>>54305
Какой смысл выучить один язык, забросить, и учить другой? Никакого.
Это, конечно, не значит, что не надо смотреть другие языки - полезно для расширения кругозора, или повышения своих навыков.
Но учить язык только потому, что анонимные эксперты с двача называют его "боежственным", довольно глупо. Есть такие люди, которым в принципе работать не интересно, они весь день сидят, пишут код на Си, PHP, C# или Яве, страдают, а тут рассуждают про "божественные" языки (которые точно так же им наскучат через неделю, если попросить их поучаствовать в большом, реальном проекте на них в команде, с реальным кодом, а не задачками на 500 строк).
В Питоне нет тайп-хинтов, приватных полей. Там везде костыли вроде virtualenv (так как Питон и его окружение по умолчанию пытается устанавливать пакеты в системные директории, что не всегда хорошо). Несколько менеджеров пакетов, причем часто один менеджер используется только для установки другого. Ужасные сокращения, часто нелогичные (например: в matplotlib есть метод set_xlim, а не set_x_limit, но при этом есть set_xscale). Вообще, если брать библиотеки вроде numpy или matplotlib, то нелогичность названий и интерфейса там вполне сопоставима с таковой же в стандартной библиотеке PHP. matplotlib это вообще жесть. Хотя богатство возможностей, конечно, радует.
Расширять библиотеки часто сложно. Например, попробуйте прикрутить DNS-кеш или подменить DNS-резолвер в стандартной библиотеке urllib. Придется поломать голову.
Часто используются глобальные переменные, глобальное состояние, или статические методы. Например: в urllib3 для настройки логгирования вместо DI (создать логгер и передать в urllib3) предлагают использовать код:
logging.getLogger("urllib3").setLevel(logging.WARNING)
А для замены компонента SSL предлагают:
urllib3.contrib.pyopenssl.inject_into_urllib3()
Ну не жесть ли, везде глобальные переменные и кривые костыли вместо DI? Кстати, обратите внимание на разный стиль написания имен функций и бессмысленные части неймспейса вроде contrib.
То же самое в urllib.requests:
> urllib.request.install_opener(opener)
> Install an OpenerDirector instance as the default global opener.
Это функция, которая меняет глобальное состояние библиотеки, и я подозреваю, кому-то надо было иметь возможность подменять компоненты и он, не заморачиваясь с архитектурой и рефакторингом, просто влепил костыль по-быстрому.
Обратите внимание на стиль написания функций:
> urllib.request.build_opener()
> urllib.request.getproxies()
Я не уверен, есть ли бесплатные IDE с анализом кода, показом определений для Питон. Ну и вакансий вроде меньше.
В Питоне нет тайп-хинтов, приватных полей. Там везде костыли вроде virtualenv (так как Питон и его окружение по умолчанию пытается устанавливать пакеты в системные директории, что не всегда хорошо). Несколько менеджеров пакетов, причем часто один менеджер используется только для установки другого. Ужасные сокращения, часто нелогичные (например: в matplotlib есть метод set_xlim, а не set_x_limit, но при этом есть set_xscale). Вообще, если брать библиотеки вроде numpy или matplotlib, то нелогичность названий и интерфейса там вполне сопоставима с таковой же в стандартной библиотеке PHP. matplotlib это вообще жесть. Хотя богатство возможностей, конечно, радует.
Расширять библиотеки часто сложно. Например, попробуйте прикрутить DNS-кеш или подменить DNS-резолвер в стандартной библиотеке urllib. Придется поломать голову.
Часто используются глобальные переменные, глобальное состояние, или статические методы. Например: в urllib3 для настройки логгирования вместо DI (создать логгер и передать в urllib3) предлагают использовать код:
logging.getLogger("urllib3").setLevel(logging.WARNING)
А для замены компонента SSL предлагают:
urllib3.contrib.pyopenssl.inject_into_urllib3()
Ну не жесть ли, везде глобальные переменные и кривые костыли вместо DI? Кстати, обратите внимание на разный стиль написания имен функций и бессмысленные части неймспейса вроде contrib.
То же самое в urllib.requests:
> urllib.request.install_opener(opener)
> Install an OpenerDirector instance as the default global opener.
Это функция, которая меняет глобальное состояние библиотеки, и я подозреваю, кому-то надо было иметь возможность подменять компоненты и он, не заморачиваясь с архитектурой и рефакторингом, просто влепил костыль по-быстрому.
Обратите внимание на стиль написания функций:
> urllib.request.build_opener()
> urllib.request.getproxies()
Я не уверен, есть ли бесплатные IDE с анализом кода, показом определений для Питон. Ну и вакансий вроде меньше.
как пересесть с PHP на иглу мужского одобрения?
Ну так померяй, например, на миллионе вызовов. Желательно сделать время измерения большим, минуты или более, чтобы нивелировать разные прерывания работы программы.
Должен предупредить, обычно тормоза в программах возникают совсем не из-за этого и ты просто зря потеряешь время.
>>53992
Человек, обзывающий других дегенератом, хорошего впечатления не производит.
>>53942
А что ты хочешь делать? Устроиться на работу, сделать свой проект, сдать сессию, какая у тебя цель?
Если на работу - то с задачами про студентов и файлообменник, навыками верстки, SQL, JS вполне может быть достаточно знаний. Можно, конечно, дополнительно освоить какой-нибудь фреймворк вроде Симфони или Ларавель, или посмотреть какую-то популярную CMS. Вроде как у нас были люди, которые послет студентов или файлообменника устраивались на работу и дальше задачи не делали.
> Качественный код может написать любой человек который умеет читать доки.
Неверно. Вот, допустим, человек прочитал документацию по Питону - станет ли он писать качественный код? Да не факт, Питон очень провоцирует городить списки из словарей, называть переменные непонятно итд. Будет ли этот человек знать MVC, ООП, паттерны? Вряд ли.
Вот посмотри библиотеки matplotlib или urllib. Их, наверно, писали люди, читавшие доки, но качество кода очень страдает, мягко говоря. Они даже функции назвать единообразно не смогли, не говоря уж про такие вещи, как DI. Я не вижу особого различия в качестве между PHP- и Питон-библиотеками.
>>53745
Тогда что ты, болезный, забыл в нашем треде? Выкатывайся в свой тред и нахваливайте друг друга петухами и кукушками. У нас тут учебный тред, а не тред для самоубеждения, что у нас самый лучший язык.
854x480, 0:17
>...использование Питона или Руби повышает качество кода
день 00:20 вот-вот начался, а ты уже сделал его
Реестр обычно это глобальное хранилище и он может быть только один. А если ты сделаешь Реестр на нестатических методах как объект, то он от DI контейнера будет не отличим.
>А что ты хочешь делать? Устроиться на работу, сделать свой проект, сдать сессию, какая у тебя цель? Если на работу - то с задачами про студентов и файлообменник, навыками верстки, SQL, JS вполне может быть достаточно знаний. Можно, конечно, дополнительно освоить какой-нибудь фреймворк вроде Симфони или Ларавель, или посмотреть какую-то популярную CMS. Вроде как у нас были люди, которые послет студентов или файлообменника устраивались на работу и дальше задачи не делали.
Я хочу устроиться на работу, но не абы куда, студии мимо, хочу заняться серьезным серверным программированием. Мне в вузе очень нравилось проектировать базы данных, вследствие этого за мной тянется фундаментальный багаж знаний о реляционных и нереляционных баз данных. Здесь я только пхп учил. Смотрел, кто из знакомых по вузу где зацепился, большинство не работает по профессии, некоторые админят (тоже не по профессии), некоторые пашут на мобилках, делают приложения, один ушел в 1С. Говно, в общем. Что делать, куда идти? Может свой проект сначала написать какой-то, чтобы крупные компании на меня обратили внимание?
То есть они могут вместе существовать?
А вот тут
https://designpatternsphp.readthedocs.io/ru/latest/Structural/Registry/README.html
пишут, что лучше использовать DI вместо Registry.
Жепой
В стартап либо компашку поменьше иди джуном. На собесе задавай вопросы, что они там пилят, если какой свой проект и начинают про архитектуру рассказывать, то круто, если про 100500 клиентов и как они им один и тот же фреймворк или цмс фигачат, то не очень. Можно в принципе и в студии свой уровень подтянуть, если по ходу дела книгу вроде зандстры осваивать и кусками паттерны и нормальный код стараться организовывать, но это дольше. Свой проект на гитхабе конечно лучше сразу, если еще и ООП нормальный там будет, то на собеседовании тебя уже будут нормально воспринимать, и будет о чем рассказать.
резюмируя - в сервис контейнер я пихаю только если у меня присутствует необходимость в зависимости? если у меня просто вспомогательный класс (сервисного слоя) без зависимостей для контроллера я его просто делаю use и использую.
>Не знаю, в документации Ларавел не описано?
контракты в /app/Contracts а реализации их неизвестено где
В общем у меня есть скрипт пхп вывода значений из mysql, как его засунуть в таблицу на сайте хтмл?
>Это правильно называется "скрейперы"
Да нет, проблем с написанием его нет. Меня интересует как и на каких технологиях разворачивают крупные скрейперы а-ля momondo или другие агрегаторы. Вот допустим есть агрегатор который должен содержать актуальную информацию, то-есть нужен не только первоначальный сбор инфы с сайта но и постоянный перепарсинг сайта (не учитываем тут защиту сайтов, считаем что её нет и принимаем работу что не все сайты нужно парсить, у некоторых есть удобное API).
Можно ли использовать Apache Beam для таких целей или что-нибудь подобное? Или как вообще они устроены? Докеризированные контейнеры которые разворачиваются on purpose? Меня не сам процесс парсинга интересует (тут я имею немного опыта, писал как PHP-парсеры так и мультипоточные парсеры на selenium-js) а вот та инфраструктура которая позволяет поддерживать беспрерывную работу парсеров под большой нагрузкой и в автоматическом режиме.
Джава, если объект-ориентированный php освоил, то будет минимум отличий и без усилий перейдешь, все довольно похоже. К тому же вакансий на ней поболее и платят там лучше, ну и в джаве более серьезно к качеству кода относятся, чем в питонах.
Тханкс. По пых я всю доку от корки до корки перечитал, долго работал пыхокодером. Хотет перекатиться во чтото для взрослых людей, чтобы быть уважаемым господином.
Т.е. хочу выбрать язык которому я мог бы посвятить жизнь без всяких сомнений, изучать каждый пук. Удачным ли выбором для таких целей является джава?
У нас в шараге под подобное были докеры на амазоне, запускались по таймеру из дженкинс, результаты потом дампом БД пересылались в основную базу. Платили только за время работы парсеров. Но наверняка есть лучшие методы.
То-есть в докере генерились дампы, не было прямого взаимодействия с бд? Был какой-то микросервис отдельный который принимал дампы на импорт? Немного подробнее пожалуйста этот момент объясни.
Питон по синтаксису на мой взгляд посасывает современному PHP, нет неймспейсов как у PHP, там интерпретатор ищет файлы сперва в локальной области видимости, потом в PYTHON_PATH и тд, есть конечно импорт директорий но это на мой взгляд не так удобно как с нейспейсами в PHP, нет тайпхинтов. Есть генераторы списков (удобная хрень) и есть многопоточность
мимо начал изучать python параллельно с основной работой на PHP
Для начала реализовал сервис авторизации. Можно сказать, что это выполненная задача Студентов https://github.com/codedokode/pasta/blob/master/student-list.md выполненная на JavaScript, с Server Side Rendering и SPA, поэтому если новичкам интересно они могут что-то подсмотреть.
Если у новичков есть какие-то вопросы - чувствуйте себя свободно задавать их.
Всё сделано "как по учебнику" и интересного здесь мало чего, за исключением пару вещей.
1. При первом заходе на сайт, пользователя встречает форма Приветствия, в которую нужно заполнить email и, в зависимости от того зарегистрирован ли пользователь или нет, перенаправляет на страницу регистрации или логина, а соответствующий импут email'а автозаполняется.
Это реализовано с помощью пары строчек, которые добавляют для определенного роута поле data с этим email'ом:
https://github.com/someApprentice/Crypter/blob/master/src/app/auth/welcome/welcome.component.ts#L46-L49
https://github.com/someApprentice/Crypter/blob/master/src/app/auth/registration/registration.component.ts#L75-L77
https://github.com/someApprentice/Crypter/blob/master/src/app/auth/login/login.component.ts#L35-L37
Но получив некий опыт на Ангуляре, я понял что более шаблонным и следовательно простым для понимания было бы обернуть все эти компоненты в отдельный компонент AuthComponent и в нём уже сделать свойство email (а лучше объект user/form) и передавать это значение с помощью "Взаимодействий Компонентов" https://angular.io/guide/component-interaction.
2. Поскольку приложение пререндерится на сервере а так же работает в браузере, ему нужен общий "источник информации" о пользовательском хранилище. Так как, для сервера используются лучшие пользовательское хранилище это Кукисы по сути кукисы не на что не влияют кроме как условий отображения пререндеренной страницы, а для браузера localStorage, то для обоих этих хранилищ написан сервис-обёртка.
https://github.com/someApprentice/Crypter/blob/master/src/app/storage.service.ts#L18-L20
https://github.com/someApprentice/Crypter/blob/master/src/app/storage.service.ts#L22-L26
https://github.com/someApprentice/Crypter/blob/master/src/app/models/StorageWrapper.ts
Небольшие вопросы, которые не критичны, но всё же знать их следует чтобы довести до идеала:
https://github.com/someApprentice/Crypter/blob/master/api/api.ts#L31
Позволяет ли Bearer token защититься от XSRF?
Я детально изучил эту уязвимость и касаемо этого вопроса, критический момент зависит от того, что может ли злоумышленник "обмануть" браузер отправить автоматически запрос с этим токеном. Он может, например, сделать XMLHttpRequests с ним (со своего сайта), но для начала ему нужно узнать этот токен, что практически тоже самое что добавлять csrf-token, чтобы бороться с этой уязвимостью.
Способ с Bearer token'ом технически идентичен с методом Cookie-to-header token ( https://en.wikipedia.org/wiki/Cross-site_request_forgery#Cookie-to-header_token ) за исключением семантики заголовка X-Csrf-Token заменённого на Bearer token.
И в моём случае, токен хранится в Кукисах, которые защищены с помощью HttpOnly и Secure флагов и в localStorage.
Приходит ли вам на ум какая-нибудь слабая точка которую можно эксплуатировать?
https://github.com/someApprentice/Crypter/blob/master/src/app/storage.service.spec.ts#L32-L33
Достаточно ли это строгая проверка на то является ли сущность экземпляром объекта localStorage?
https://github.com/someApprentice/Crypter/blob/master/src/app/models/StorageWrapper.ts
В правильной ли директории находится эта обёртка? Это не совсем сущность, например как User, но места по лучше я не могу придумать для неё. Куда следует помещать обёртки?
https://github.com/someApprentice/Crypter/blob/master/src/app/auth/registration/registration.component.spec.ts#L67
https://github.com/someApprentice/Crypter/blob/master/src/app/auth/login/login.component.spec.ts#L66
Нужно ли делать проверку на каждую валидацию? Например на встроенные в Ангуляр валидаторы или на валидацию по регулярному выражению? https://github.com/someApprentice/Crypter/blob/master/src/app/auth/registration/registration.component.ts#L21-L24
Следующий шаг написания сервиса сообщений.
Он будет реализован с помощь протокола WAMP и на платформе от https://crossbar.io/ , которая написана на Питоне, и для которой для аутентификации клиентов нужно тоже написать код на нём.
https://github.com/crossbario/crossbar-examples/blob/master/authentication/ticket/dynamic/authenticator.py
Поэтому я сейчас буду изучать его, и возможно у меня появиться небольшие вопросы по нему. Могу я задать их в этом треде?
Заодно и реактивное программирование подучу.
Буду признателен за проверку моего завершающего задания на JavaScript.
Это очень много значит для меня. Большое спасибо.
Для начала реализовал сервис авторизации. Можно сказать, что это выполненная задача Студентов https://github.com/codedokode/pasta/blob/master/student-list.md выполненная на JavaScript, с Server Side Rendering и SPA, поэтому если новичкам интересно они могут что-то подсмотреть.
Если у новичков есть какие-то вопросы - чувствуйте себя свободно задавать их.
Всё сделано "как по учебнику" и интересного здесь мало чего, за исключением пару вещей.
1. При первом заходе на сайт, пользователя встречает форма Приветствия, в которую нужно заполнить email и, в зависимости от того зарегистрирован ли пользователь или нет, перенаправляет на страницу регистрации или логина, а соответствующий импут email'а автозаполняется.
Это реализовано с помощью пары строчек, которые добавляют для определенного роута поле data с этим email'ом:
https://github.com/someApprentice/Crypter/blob/master/src/app/auth/welcome/welcome.component.ts#L46-L49
https://github.com/someApprentice/Crypter/blob/master/src/app/auth/registration/registration.component.ts#L75-L77
https://github.com/someApprentice/Crypter/blob/master/src/app/auth/login/login.component.ts#L35-L37
Но получив некий опыт на Ангуляре, я понял что более шаблонным и следовательно простым для понимания было бы обернуть все эти компоненты в отдельный компонент AuthComponent и в нём уже сделать свойство email (а лучше объект user/form) и передавать это значение с помощью "Взаимодействий Компонентов" https://angular.io/guide/component-interaction.
2. Поскольку приложение пререндерится на сервере а так же работает в браузере, ему нужен общий "источник информации" о пользовательском хранилище. Так как, для сервера используются лучшие пользовательское хранилище это Кукисы по сути кукисы не на что не влияют кроме как условий отображения пререндеренной страницы, а для браузера localStorage, то для обоих этих хранилищ написан сервис-обёртка.
https://github.com/someApprentice/Crypter/blob/master/src/app/storage.service.ts#L18-L20
https://github.com/someApprentice/Crypter/blob/master/src/app/storage.service.ts#L22-L26
https://github.com/someApprentice/Crypter/blob/master/src/app/models/StorageWrapper.ts
Небольшие вопросы, которые не критичны, но всё же знать их следует чтобы довести до идеала:
https://github.com/someApprentice/Crypter/blob/master/api/api.ts#L31
Позволяет ли Bearer token защититься от XSRF?
Я детально изучил эту уязвимость и касаемо этого вопроса, критический момент зависит от того, что может ли злоумышленник "обмануть" браузер отправить автоматически запрос с этим токеном. Он может, например, сделать XMLHttpRequests с ним (со своего сайта), но для начала ему нужно узнать этот токен, что практически тоже самое что добавлять csrf-token, чтобы бороться с этой уязвимостью.
Способ с Bearer token'ом технически идентичен с методом Cookie-to-header token ( https://en.wikipedia.org/wiki/Cross-site_request_forgery#Cookie-to-header_token ) за исключением семантики заголовка X-Csrf-Token заменённого на Bearer token.
И в моём случае, токен хранится в Кукисах, которые защищены с помощью HttpOnly и Secure флагов и в localStorage.
Приходит ли вам на ум какая-нибудь слабая точка которую можно эксплуатировать?
https://github.com/someApprentice/Crypter/blob/master/src/app/storage.service.spec.ts#L32-L33
Достаточно ли это строгая проверка на то является ли сущность экземпляром объекта localStorage?
https://github.com/someApprentice/Crypter/blob/master/src/app/models/StorageWrapper.ts
В правильной ли директории находится эта обёртка? Это не совсем сущность, например как User, но места по лучше я не могу придумать для неё. Куда следует помещать обёртки?
https://github.com/someApprentice/Crypter/blob/master/src/app/auth/registration/registration.component.spec.ts#L67
https://github.com/someApprentice/Crypter/blob/master/src/app/auth/login/login.component.spec.ts#L66
Нужно ли делать проверку на каждую валидацию? Например на встроенные в Ангуляр валидаторы или на валидацию по регулярному выражению? https://github.com/someApprentice/Crypter/blob/master/src/app/auth/registration/registration.component.ts#L21-L24
Следующий шаг написания сервиса сообщений.
Он будет реализован с помощь протокола WAMP и на платформе от https://crossbar.io/ , которая написана на Питоне, и для которой для аутентификации клиентов нужно тоже написать код на нём.
https://github.com/crossbario/crossbar-examples/blob/master/authentication/ticket/dynamic/authenticator.py
Поэтому я сейчас буду изучать его, и возможно у меня появиться небольшие вопросы по нему. Могу я задать их в этом треде?
Заодно и реактивное программирование подучу.
Буду признателен за проверку моего завершающего задания на JavaScript.
Это очень много значит для меня. Большое спасибо.
Меня интересует возможность удаленного заработка в пределах 40к. Сейчас начал изучать HTMl 5.
И появилась парочка вопросов:
1) Реально ли работая на фрилансе 8-10 часов в день поднимать 40к для среднестатистического человека?
2) Не покажется ли такая работа скучной, через чур сидячей или все зависит от организации раб. места?
Если это реально, то с такой зарплатой в 40к, в своем м-сраснке с населением 150к я буду полубогом.
В принципе, верстать нравится, сейчас пилю нубский сайт по Бри Ларсон.
Да, докер ставился на паузу через дженкинс с внешнего сервера, так что процессорное время не тратилось, когда там ничего не обрабатывалось. Парсеры забирали данные с внешних сайтов, у себя обрабатывали, во время работы на amazon aurora все промежуточные результаты хранили. В конце дамп с авроры делался и копировался на продакшн сервак и микросервис там висел, чтобы внешние парсеры запускать импорт дампов могли. Единственные сложности были с ключами в БД, которые между собой конфликтовали, но мы там потом все составные сделали основной ключ + номер парсера.
На одном html сложновато на фрилансе поднять, обычно клиенты хотят сразу и верстку и фронтенд js и бэкенд на php, за фуллстэк работы в основном платят. Если еще в английский можешь, то 40к самый минимум, обычно больше. Делай задания из шапки, там все есть чтобы на нужный уровень выйти.
Спасибо анон. Я за верстку и считал html, css, js. Только про php узнал сейчас.
И сколько в среднем стоит обычный проект для новичка? И за какое время, например ты сам в начале пути делал верстку
Благодарю, буду разбираться, ну хоть направление есть теперь
Не могу понять, как работает "поддержка" авторизации на сайтах. Взять тот же ВК. Просмотр страниц доступен только после входа.
Но как потом страницы определяют, что пользователь авторизирован?
Сессии? А в сессии сохранён параметр входа, типа AUTH = TRUE? Что будет, если я скопирую себе COOKIE с id сессии у другого пользователя? Получается, так я могу получить доступ к чужому аккаунту?
Хранение хэшей паролей и проверка на каждой странице мне кажется бредовым решением.
Подскажите, опытные аноны.
640x744, 0:11
ого, эти треды еще живы
года 2 назад вкатился благодаря им и опу, за что ему мое увожение
ну а чо пилить то
надоело говно вилкой чистить за 18к, вот и решил в ваше айти вкатиться
почти год после работы читал/писал и взяли меня на галеру
поработал там 2 года, понял что на этой галере больше ничему не научиться и теперь пора следующий шаг делать
Фига ты успешный. Чем занимался до этого? Тяжело вкатываться? Сам из м-сранска, зп 15-20к.
Может netbeans поставить, а? Может там все из коробки нормально, как думаете?
>Сессии? А в сессии сохранён параметр входа, типа AUTH = TRUE? Что будет, если я скопирую себе COOKIE с id сессии у другого пользователя? Получается, так я могу получить доступ к чужому аккаунту?
Концептуально так и есть, но если взять пример ВК, то там, возможно, есть какие то Cookie с хэшем которые высчитываются из ip-адреса, поэтому просто своровать их чтобы получить доступ к чужому аккаунту, скорее всего не получиться.
Попробуй другой порт например. (я по привычке ставлю 9005)
Если у тебя хост на виртуалке - remote_host будет не 127.0.0.1 а 192.168.1.Х (открываешь свойства сети на компе и смотришь свой IP в локалке)
profiler_enable - тебе вряд ли нужен, но будет дико замедлять работу скриптов (будет писаться файловый лог работы скрипта), лучше поставить чтобы по GET-параметру XDEBUG_PROFILER=1 срабатывал профайлер.
Алсо, если у тебя php-fpm не забывай и его перезагружать после изменений в php.ini
Що ты кажешь? Я наоборот скинул анону, чтобы он увидел как у меня настроено и все работает
А, извини, но я не говорил, что у тебя неправильно настроено, я просто уточнил по некоторым пунктам для него из твоего скрина.
ахаха, ну ты мочишь чувак, пошути про программирование на html, плз
https://www.youtube.com/watch?v=k1CN4YVcbHo
Спасибо, бро. Но я скачал нетбинс и охуел от его аскетичности чтоли:
- нет дерева файлов слева, не понял нихуя, наверное, я тупой, предлагает зачем-то импортировать из zip. А не из zip???
- предлагает создать проект html файл. Нахуя мне html? Я же скачал специально php edition...
- старые плагины 2014 года выпуска, например, Emmet вообще нет, он называется по-старому - Zen coding
Чет мне кажется, я говна поем с этой IDE.
Оууукей. скачал Codelobster - там конечно полный фарш по поддержке фреймворков и cms, смотрите сами, НО! Сука дебагер не пашет на php7, хотя вроде поддержка есть. Ладно, пока попишу на php5 свою херню, хотя странно.
Бля как же сложно, я просто хочу писать и отлаживать удобно код, как раньше в дельфи, неужели я многого прошу?
Алсо видео стронгли рилейтед.
шо? В нетбинсе все это есть. Это раз.
Два. Это вторая по популярности ИДЕ после пхпшторма. Три - коделобстер - кусок говна по функционалу, даже если сравнить с обычным блокнотом
Я его пробовал несколько лет назад, поэтому точно не помню. Помню что жопа горела дико.
Там какая-то перда была с тем что внутренние окна меняли свой размер, он лагал. Там ты ничего толком не настроишь, он платный. Я уже не помню есть там поддержка композера, гита и пр штук? Терминал имеется? Возможность работать с сервером удаленно?
Терминала нету. А что ты запускаешь в терминале? Artisan?
А что значит - поддерка компоузера?
Удаленный сервер - всё это есть, конечно.
Вы еще где-то есть? Не охота с телегой ебаться.
1. Не понимаю в чем профит сервис контейнера? Уже пересмотрел много видео и статей и примеры. Но я не понимаю через сервис провайдеры мы как бе можем подменять реализации интерфейсов того что будет храниться в сервис контейнере, но мы как бы можем сделать это и без него в чем профит использовать именно его? Да реализация через него синглтона довольна удобно и ясна, но она не так часто нужна, так для чего еще?. Так что вопрос зачем?
2. Заодно вопрос какая структура папок должна быть для контрактов и их реализации.
3. Если бороться с жирными контроллерами через сервисный слой то какую выбрать структуру папок чтоб можно было вернуться к проекту через месяц?
У меня дежа вю от твоего поста.
Добавьте меня @PsychoSion
>Но получив некий опыт на Ангуляре, я понял что более шаблонным и следовательно простым для понимания было бы обернуть все эти компоненты в отдельный компонент AuthComponent и в нём уже сделать свойство email (а лучше объект user/form) и передавать это значение с помощью "Взаимодействий Компонентов" https://angular.io/guide/component-interaction.
Здесь не получится реализовать "Взаимодействие Компонентов" потому что, всё равно, придется размещать не сами компоненты, а <router-outlet>.
816x592, 1:03
Допустим, массив всех строк из файла ложится в $key и я хочу организовать поиск по нему.
Дальше такой код: https://ideone.com/PKbkZz
1. Почему у меня выводит только один результат, хотя их десяток в файле?
2. Как мне вывестив результате номер строки именно найденного результата?
Спасибо вам.
В папке service лежат контракты
В папке useCases реализация
Пример тут https://github.com/ElisDN/laravel-demo-board/tree/master/app
Напиши пожалуйста название конфы
Strpos возвращает первое вхождение. Он может тебе вернуть 0 т.к нашел с индекса ноль а у тебя true стоит может быть поэтому у тебя не сходится. Мне кажется тебе надо.жестко проверить strpos() !== false
1280x720, 0:27
За студентов не скажу, потому что не помню, а вот файлообменник кинул на гитхаб как пример того чо могу(ничего). Ну там еще было задание после файлообменника, но его я не делал. К слову по жс я не умел вообще ничего, даже json запрос написать не мог. Ну и вообще, то что я там пыхтел год можно реально выучить за месяц максимум на реальных проектах, так что чем скорее попадете на вашу первую ламповую галеру за еду, тем быстрее пойдет скилл.
Задачка про вопросы из раздела про ООП.
Ты поля из вне задаешь. Я против такого подхода потому что создается не валидный обьект. Ты через new его создаешь а потом туда пихаешь значения.
Я как клиент твоего класса должен знать , что после new надо в поля записать. А я не хочу)
На работе появилась задача, которую мне нужно по итогу реализовать, сроки совсем не сжатые, но работу веду по факту ежедневно. Думаю что мой MVC шаблон и станет основой будещего приложения.
Суть приложения проста - сотрудники заходят на сервер и получают перечень доступных им запросов в базу, которые могут использовать.
Какие проблемы для себя я вижу:
1. Как сделать авторизацию в MVC?
2. Как делать права доступа? Т.е. сотруднику №1 доступны все отчеты, а сотруднику №2 только один.
sql-отчетов гипотетически будет много разных.
Делаю это для удобства всех вообще, потому как на данный момент отчеты я ручками пилю при необходимости.
Такой вот учебно-практичный проект возникает.
Где почитать годноты, котаны?
1. В бд есть таблица с логинами и паролями (хешами с солью). Показываешь страницу логина с полями для входа. Если пароль не подходит - посылаешь нахер. Если подходит - в сесию пишешь токен (очень длинную случайную строку). С этим токеном пользователь может лазить по сайту. Без токена - посылаешь на страницу логина. Токен проверяешь в каждом контроллере.
2. В бд есть таблица с правами пользователей. В контроллере проверяешь права - в зависимости от прав выводишь нужную инфу, или вообще посылаешь нахер на первую страницу если пользователь ломиться куда не положено.
Вообще лучше фрейморк какой-то взять (Yii2 или Laravel) - там эта вся хуйня сделана изначально (по крайней мере в Yii2).
$q1 = new ChoiceQuestion("Какая планета располагается четвертой по счету от Солнца?", 'b');
$q1->options = array('a' => 'Венера', 'b' => 'Марс', 'c' => 'Юпитер', 'd' => 'Меркурий');
$q2 = new NumericQuestion("Чему равна скорость света в км/с?", 300000, 20);
$q2->deviation = 20000;
Поля $q1->options и $q2->deviation = 20000 ты все так же задаешь из вне.
И еще мне кажется что делать echo в методе не совсем верно, лучше возвращать строки и пусть кто вызывал твой метод разбирается что с ней делать.
echo " {$key}. {$option}\n"; заменить на return " {$key}.{$option}";
>>57057
Переделал вот: https://ideone.com/Lb6iG1.
Если я правильно понял, то переопределить метод изменив количество параметров не получится. Поэтому я в каждый дочерний класс запихнул конструктор. Верно или я ошибаюсь?
Да норм. Конструктор часть конкретного класса. Я бы не рассматривал его как часть интерфейса
Ого. Я тоже сделал только файлобменник. Учу шаблоны, дабы приступить к изучению Laravel.
Предлагаешь с такими околонулевыми знаниями уже подыскивать галеру?
Двачую. Тут люди с одним файлообменником на работу устраивались.
Хз, пробуй. Если в дс то изи, если нет, то не очень. Ну от бомжа и не ждут, что он процесс от потока отличает и знает чтото про уровни изоляции транзакций.
>ого, эти треды еще живы
Не просто живы, а ещё у них появился свой сайт https://phpclub.tech/
Его код находиться на ГитХабе https://github.com/richBlueElephant/phpClub и там есть issues, которые хорошо бы исправить https://github.com/richBlueElephant/phpClub/issues
Быстро мне накидал CMS с плугами под нормальные языки!
Знаю что напсавший уже и забыл о том что писал, ведь в школе каждый день кипит событиями
Да просто не знают ответа ибо только учатся или посрать пришли
Это что бы изменение 1 вьюшки были отображены в другой?например я обычно общие куски делаю в partial и их уже где надо подключаю. Какой результвт ты хотел бы получить?
Господа анончики, может кто проверить задачку на айфон в кредит?
https://ideone.com/ZsjeOK
пхп макак
Что значит проверить?
Хорошо. Лучше чем большинство делает в этот ITT треде, обычно пытаются все логические действия впихнуть в одну строку и косячат. Учись дальше.
Есть вопрос по "Вектору".
Правильно ли я понимаю что создание всех этих объектов работников надо как то автоматизировать? Или втупую ручками создавать каждый объект?
Ну я когда делал, то парсил из текста с твоей картинки, только там тогда были буквы из латиницы и из кириллицы намешаны, что добавило анальных болей, не знаю было ли это исправлено.
Нет. Делай нормальные конструкторы. По факту там и ватоматизировать мало чего. В циклах создаешь объекты и все. Инициализации все равно много будет.
Я просто еще с фреймворками не работал, так что хз, как оно там везде работает. Есть вот шаблон, куда я подключаю вид, считай, вызываю функцию getView(), которая подключает текущий вид. Например, это список товаров. А еще у меня в каждом виде меняется, к примеру, шапка. Там title разный на каждой вьюхе. И как мне его там менять? Инклюдить всю шапку как виджет в каждый вид, туда передавать параметры для title и потом выводить все вместе или еще как-то можно?
Напрмиер, в шаблоне также как подключаешь вью с товарами, также подключашь нужную шапку. Виджет норм идея, если сможешь сделать его более-менее универсальным (тоесть можно будет использовать больше чем в одном проекте). А если придется допиливать под каждый конкретный проект то лучше всетаки как отдельное вью или типа того оформить.
Понял, благодарю.
Ребятки, проверьте "Вектор", пожалуйста : https://repl.it/@PhilPhil/GrandUrbanProjections
Для начала сделай нормальную автозагрузку
Почему \n работает только на сайте ideone com, а человеческий '<br>', который работает везде, сайт тупо принимает за текст?
Чому тайп хинтинг не используешь? Захардкоженные названия профессий тоже не оч хорошо
Потому что там вывод скрипта используется как простой текст, а не html разметка.
>>57310
Вкатился на python галеру после задачи с файлообмненником, работаю уже два месяца. Самое забавное, что набирают людей с куда меньшими чем у меня знаниями, так что вкатывайтесь чем раньше, тем лучше. Кстати, огромное спасибо опу за задачки, я, правда, учился в основном, не на его уроках, но все-равно материалы из этого треда были очень полезны.
>>57424
Я бы добавил константы и в свитчах использовал бы их
switch($this->profession) {
case self::MANAGER итд
Насколько будет проблемно найти работку в МСК пхп кодеру?
Имеется опыт работы 3+ года, знаю ООП, фреймворки и пр. Хуйов в верстке еще немношк знаю джаву и планирую в нее перекатиться.
Будут ли меня обссыкать за то что я шокаю и гакаю?
Потому что количество элементов массива 4 а последний индекс 3. Происходит обращение к несуществующему элементу.
И правда. Спасибо, теперь все норм.
(При вызове функции посередине)
хмм, походу на то.
Но тогда получается у меня $data - объект, а не в коим случае не объект?
В связи с этим, правильно ли я думаю, что если считывать json файл где всё сосредоточено в
{
//что то
}
то это объект, а если
[
// что то
]
то это массив?
https://3v4l.org/NSVsb - задача про зарплату
https://3v4l.org/4GGth - задача про вопросы
https://3v4l.org/3avqA - задача про Вектор
Ну если ты обращаешься к данным через ->, то это признак того, что ты обращаешься к объекту.
В javascript можно обращаться к пустому объекту, просто создав его через {}. В PHP так делать нельзя, объект нужно создать через new, поэтому твой json (ты ведь его получаешь?) сначала нужно распарсить (превратить в массив) и потом уже только обращаться к нему.
В смысле не ковыряние какой-то ублюдской параши с ключевыми словами "битрикс джумла вордпресс", а создание каких-то систем с нуля там, на фреймворках?
Нет. Это фантастика, сынок. А фреймворки это так, хуйня. Фабьен с Тейлором их по приколу напейсали. Вкатывайся лучше в руби
Работа есть, но только как ты выразился ковыряние cmsок в студиях, потому что дешево и быстро.
Есть у меня к примеру экшен, который вызывается один раз по умолчанию, когда юзверь заходит на страниц. Потом, когда программа юзверя запомнила, она будет вызывать другой экшен, при заходе на эту же страницу по умолчанию.
Можно ли сделать вот так?
методПоУмолчанию {
if (тутБылЮзверь) {
методПослеПосещения()
}
"Юзверь, зарегистрируйся!"
}
методПослеПосещения {
выводимИнфу()
}
То есть я понимаю, что чисто физически я могу вызвать один метод в другом, но можно ли так делать в MVC шаблоне?
Почему нет? Только если у тебя фрейморк какой-то то там скорее всего лучше не напрямую вызывать другой экшен, а сделать редирект.
Ага, спасибо. Почему нет, я не знаю, поэтому и спрашиваю, лол. С фреймами пока не работал, на вольных хлебах пока что. Что хочу, то и ворочу.
Решил написать простенький калькулятор, но после проверки, является ли введенные значения целыми числами, но жидко обсераюсь и ловлю сообщение о том, что там не числа (хотя там числа).
Посмотрите, пожалуйста, где я не прав.
https://ideone.com/gwTPUt
Там не числа. Из консоли всегда приходят строки.
http://php.net/manual/ru/function.readline.php
Почитай про преобразование типов в PHP.
Спасибо. Сейчас разберемся.
Работа тебе нужна только 1.И на руби можно найти ее
Мне кажется, хуевый план. А если мне нужно будет этих юзеров рассортировать, пропустить их через валидатор, то где мне это делать? Явно не в контроллере. Опчик, ты ведь знаешь ответ? Взываю к тебе!
что тебя смущает я не понимаю.
class Controller
{
private $search;
public function __construct(Search $search)
{
$this->search = $search;
}
public function index()
{
$result = $this->search->search();
}
}
bump
test($a);
public function test($a) :int
{
return $a;
}
вернет ли в данном случае int или же string?
Зависит от strict_types. Или сделает приведение типов и вернет число, или при strict_types=1 будет ошибка
да, уже потестил в песочницах.
Но в проекте просто втихую возвращает стринг и никаких ворнингов, ошибок, поэтому и пришел с этим вопросом.
нвм, я дегенерат, дампил значение до его возвращения, а приведение типов работает во время возвращения.
Всем плевать на вышку если ты сильный кандидат (немного похуже в Украине, у них странноватый рынок труда в IT). Больше интересует что бы в армию не ушел прямо с работы или в запой.
Вообще, это неудобно. Что, если пользователь нажал кнопку, а данные еще не пришли?
Сначала лучше сделать так: пользователь заполняет все поля и жмет кнопку. Отправляются по очереди все нужные запросы.
А потом уже можно прикрутить оптимизацию, отправлять первый запрос сразу после заполнения поля amount, итд.
Если у тебя нет понимания, как это делать, то скорее всего ты пока плохо знаешь Яваскрипт. Пройди учебник http://learn.javascript.ru/ и обрати особое внимание на события, анонимные функции (коллбеки), почитай про промисы.
>>54526
Часто, чтобы попасть в "серьезную" компанию, надо сначала где-то приобрести опыт, возможно, что в веб-студии, в аутсорсере или где-то еще, где берут джуниоров. Ты можешь сам посмотреть вакансии компании, которая тебе нравится, на hh.ru и оценить требования.
Например, если ты любишь проектировать БД, то тебе подойдет какая-нибудь компания, которая делает софт на заказ, всякий софт для банков, телекомов, госорганов - там точно будут сложные базы данных.
>>54531
Реестр - это паттерн, который описывает объект, в который можно класть и получать данные ( https://martinfowler.com/eaaCatalog/registry.html ). Часто он реализуется на статических методах:
ServiceRegistry::set(ServiceA::class, new ServiceA);
...
$srvA = ServiceRegistry::get(ServiceA::class);
Но это не обязательно. Как я помню, в той же Симфони есть такие реестры:
- реестр кастомных (добавленных пользователем) типов полей Доктрины
- реестр типов элементов форм
Там, где Реестр противопоставляется DI контейнеру - противопоставляется, наверно, статическая версия реестра. То есть подход
ServiceRegistry::get('ServiceA')
и
$container->get('ServiceA');
У Реестра и Контейнера могут быть такие отличия:
- Реестр часто бывает статический, а контейнер - нет
- Реестр не создает объекты: он хранит то, что в него положат. DI контейнер может создавать объекты, если ему дать описание, как это делается (например, описать, что надо передавать в конструктор, или позволить ему самому это понять анализом аргументов в конструкторе).
- Реестр может хранить что угодно, например, числа какие-нибудь или настройки. DI контейнер, как правило, хранит объекты-сервисы. Хотя в Симфони он хранит и настройки.
Вообще, это неудобно. Что, если пользователь нажал кнопку, а данные еще не пришли?
Сначала лучше сделать так: пользователь заполняет все поля и жмет кнопку. Отправляются по очереди все нужные запросы.
А потом уже можно прикрутить оптимизацию, отправлять первый запрос сразу после заполнения поля amount, итд.
Если у тебя нет понимания, как это делать, то скорее всего ты пока плохо знаешь Яваскрипт. Пройди учебник http://learn.javascript.ru/ и обрати особое внимание на события, анонимные функции (коллбеки), почитай про промисы.
>>54526
Часто, чтобы попасть в "серьезную" компанию, надо сначала где-то приобрести опыт, возможно, что в веб-студии, в аутсорсере или где-то еще, где берут джуниоров. Ты можешь сам посмотреть вакансии компании, которая тебе нравится, на hh.ru и оценить требования.
Например, если ты любишь проектировать БД, то тебе подойдет какая-нибудь компания, которая делает софт на заказ, всякий софт для банков, телекомов, госорганов - там точно будут сложные базы данных.
>>54531
Реестр - это паттерн, который описывает объект, в который можно класть и получать данные ( https://martinfowler.com/eaaCatalog/registry.html ). Часто он реализуется на статических методах:
ServiceRegistry::set(ServiceA::class, new ServiceA);
...
$srvA = ServiceRegistry::get(ServiceA::class);
Но это не обязательно. Как я помню, в той же Симфони есть такие реестры:
- реестр кастомных (добавленных пользователем) типов полей Доктрины
- реестр типов элементов форм
Там, где Реестр противопоставляется DI контейнеру - противопоставляется, наверно, статическая версия реестра. То есть подход
ServiceRegistry::get('ServiceA')
и
$container->get('ServiceA');
У Реестра и Контейнера могут быть такие отличия:
- Реестр часто бывает статический, а контейнер - нет
- Реестр не создает объекты: он хранит то, что в него положат. DI контейнер может создавать объекты, если ему дать описание, как это делается (например, описать, что надо передавать в конструктор, или позволить ему самому это понять анализом аргументов в конструкторе).
- Реестр может хранить что угодно, например, числа какие-нибудь или настройки. DI контейнер, как правило, хранит объекты-сервисы. Хотя в Симфони он хранит и настройки.
У меня просто времени иногда нет, последние пару недель адски загружен. Как станет полегче - разберу накопившиеся вопросы. А так, я всем стараюсь отвечать.
>>55207
> Нужно ли делать ячейку DI-контейнера одиночкой, если она нуждается в этом?
Ни в коем случае. Идея DI контейнера - IoC - предполагает, что жизнью объекта (когда его создать, когда уничтожить, что передать в зависимости) управляют снаружи, что это не его зона ответственности. Это не дело объекта-сервиса знать, сколько экземпляров сейчас создано. Дело сервиса - указать свои зависимости, например, в конструкторе или в методах.
За поддержку единственного экземпляра отвечает контейнер. Да, пользователь может создать новый объект в обход контейнера, ну, значит, он так решил. Но скорее всего это будет сложно, так как нужно где-то взять зависимости объекта, настройки конфига, и это выльется в огромный объем кода. Ну и на код-ревью такой разработчик получит по шапке за велосипедостроение.
Простой пример, когда нам не нужен синглтон - это тесты. Мы хотим, чтобы тесты выполнялись в изоляции друг от друга, и можем для каждого теста создавать одноразовый экземпляр DI контейнера. DI это позволяет, а твой подход с синглтонами - нет.
Потому синглтон - это почти всегда антипаттерн. Не стоит его использовать.
>>55140
Я думаю, сервис провайдеры и сервисы хранятся в отдельных папках. По поводу организации структуры кода, если ты используешь фреймворк, то стоит делать, как там принято. Если не используешь, то такие варианты:
- если классов мало (< 10), их можно класть в одну папку
- можно делать папки по типам классов: папка для сервисов, для контроллеров, для представлений, для моделек, для классов работы с БД, для вспомогательных утилит. Этот подход начинает фейлиться, когда у тебя становится огромное приложение с сотнями серисов и моделей.
- можно взять предыдущий подход и добавлять подпапки внутри папок. Ну например, папка модели, а в ней: папка пользователи, папка товары, папка статистика итд.
- а можно поступить по-другому, и разбить приложение на "части". Для каждой "части" создаем свою папку, а в ней подпапки: сервисы, модели, контроллеры, утилиты и тд. То есть будет папка "пользователи", а в ней подпапки с моделями пользователей, контроллерами раздела работы с пользователями итд.
Симфони, например, использует что-то отдаленно напоминающее последний подход, где можно делать разные "части" - бандлы. У каждого бандла могут быть свои конфиги. Вообще, последний подход, если делать бандлы небольшими, хорошо работает в огромных приложениях. Так как часто у нас разработка идет в стиле "создаем новый раздел сайта, дорабатываем и потом почти не трогаем". И мы сидим в основном внутри одного небольшого бандла и не трогаем остальной код.
То есть для огромного магазина, например, можно сделать так:
CommonBundle - общий код, который используется всеми бандлами (не контроллеры или модели, а например, сервисы, утилиты, расширения фреймворка итд)
UsersBundle - управление пользователями, регистрация, личный кабинет
StoreBundle - витрина, вывод товаров, оформление заказа
FinanceBundle - оплата, бонусы
SupportBundle - раздел техподдержки
ForumBundle - форум
AnalyticBundle - аналитика
AdminBundle - админка
У меня просто времени иногда нет, последние пару недель адски загружен. Как станет полегче - разберу накопившиеся вопросы. А так, я всем стараюсь отвечать.
>>55207
> Нужно ли делать ячейку DI-контейнера одиночкой, если она нуждается в этом?
Ни в коем случае. Идея DI контейнера - IoC - предполагает, что жизнью объекта (когда его создать, когда уничтожить, что передать в зависимости) управляют снаружи, что это не его зона ответственности. Это не дело объекта-сервиса знать, сколько экземпляров сейчас создано. Дело сервиса - указать свои зависимости, например, в конструкторе или в методах.
За поддержку единственного экземпляра отвечает контейнер. Да, пользователь может создать новый объект в обход контейнера, ну, значит, он так решил. Но скорее всего это будет сложно, так как нужно где-то взять зависимости объекта, настройки конфига, и это выльется в огромный объем кода. Ну и на код-ревью такой разработчик получит по шапке за велосипедостроение.
Простой пример, когда нам не нужен синглтон - это тесты. Мы хотим, чтобы тесты выполнялись в изоляции друг от друга, и можем для каждого теста создавать одноразовый экземпляр DI контейнера. DI это позволяет, а твой подход с синглтонами - нет.
Потому синглтон - это почти всегда антипаттерн. Не стоит его использовать.
>>55140
Я думаю, сервис провайдеры и сервисы хранятся в отдельных папках. По поводу организации структуры кода, если ты используешь фреймворк, то стоит делать, как там принято. Если не используешь, то такие варианты:
- если классов мало (< 10), их можно класть в одну папку
- можно делать папки по типам классов: папка для сервисов, для контроллеров, для представлений, для моделек, для классов работы с БД, для вспомогательных утилит. Этот подход начинает фейлиться, когда у тебя становится огромное приложение с сотнями серисов и моделей.
- можно взять предыдущий подход и добавлять подпапки внутри папок. Ну например, папка модели, а в ней: папка пользователи, папка товары, папка статистика итд.
- а можно поступить по-другому, и разбить приложение на "части". Для каждой "части" создаем свою папку, а в ней подпапки: сервисы, модели, контроллеры, утилиты и тд. То есть будет папка "пользователи", а в ней подпапки с моделями пользователей, контроллерами раздела работы с пользователями итд.
Симфони, например, использует что-то отдаленно напоминающее последний подход, где можно делать разные "части" - бандлы. У каждого бандла могут быть свои конфиги. Вообще, последний подход, если делать бандлы небольшими, хорошо работает в огромных приложениях. Так как часто у нас разработка идет в стиле "создаем новый раздел сайта, дорабатываем и потом почти не трогаем". И мы сидим в основном внутри одного небольшого бандла и не трогаем остальной код.
То есть для огромного магазина, например, можно сделать так:
CommonBundle - общий код, который используется всеми бандлами (не контроллеры или модели, а например, сервисы, утилиты, расширения фреймворка итд)
UsersBundle - управление пользователями, регистрация, личный кабинет
StoreBundle - витрина, вывод товаров, оформление заказа
FinanceBundle - оплата, бонусы
SupportBundle - раздел техподдержки
ForumBundle - форум
AnalyticBundle - аналитика
AdminBundle - админка
> что без шаблонизатора мы не можем создать полноценную MVC на пхп
В теории - можем. Например, если мы делаем сервер API, который отдает не HTML-страницы, а данные в формате JSON, то шаблоны нам не нужны. Ну и в теории, опять же, мы можем без шаблонов сделать вывод HTML кода кучей операторов echo.
То есть, шаблон - это лишь один из вариантов реализации View.
Я думаю, ты хотел спросить, "можно ли вместо стороннего шаблонизатора использовать встроенный в PHP?" - да, можно, но неудобно на больших проектах.
Урок про шаблонизаторы: https://github.com/codedokode/pasta/blob/master/php/templates.md
>>54575
Обычно все сервисы создают через контейнер. Сегодня у сервиса нет зависимостей, а завтра они появятся - и тебе придется обходить весь код и заменять создание сервиса на получение через контейнер.
Также, контейнер может поддерживать существование одного экземпляра сервиса.
> если у меня просто вспомогательный класс (сервисного слоя) без зависимостей для контроллера я его просто делаю use и использую.
Для вспомогательных классов можно использовать паттерн Utility Class (класс со статичесикими методами).
> контракты в /app/Contracts а реализации их неизвестено где
Их реализации там, где уместно. Естественно, не в папке Contracts. Вообще, ты можешь посмотреть, как код организован в самом Laravel или стандартных библиотеках к нему.
Если у тебя мало сервисов, можно просто сделать папку App/Services. Если много - то думать, как дробить на части. Можно как-то по смыслу их группировать, по разделу сайта, по компоненту приложения.
>>54604
По идее так: http://php.net/manual/ru/language.basic-syntax.phpmode.php
Но это даст низкокачественный код, потому читай https://github.com/codedokode/pasta/blob/master/php/templates.md
> что без шаблонизатора мы не можем создать полноценную MVC на пхп
В теории - можем. Например, если мы делаем сервер API, который отдает не HTML-страницы, а данные в формате JSON, то шаблоны нам не нужны. Ну и в теории, опять же, мы можем без шаблонов сделать вывод HTML кода кучей операторов echo.
То есть, шаблон - это лишь один из вариантов реализации View.
Я думаю, ты хотел спросить, "можно ли вместо стороннего шаблонизатора использовать встроенный в PHP?" - да, можно, но неудобно на больших проектах.
Урок про шаблонизаторы: https://github.com/codedokode/pasta/blob/master/php/templates.md
>>54575
Обычно все сервисы создают через контейнер. Сегодня у сервиса нет зависимостей, а завтра они появятся - и тебе придется обходить весь код и заменять создание сервиса на получение через контейнер.
Также, контейнер может поддерживать существование одного экземпляра сервиса.
> если у меня просто вспомогательный класс (сервисного слоя) без зависимостей для контроллера я его просто делаю use и использую.
Для вспомогательных классов можно использовать паттерн Utility Class (класс со статичесикими методами).
> контракты в /app/Contracts а реализации их неизвестено где
Их реализации там, где уместно. Естественно, не в папке Contracts. Вообще, ты можешь посмотреть, как код организован в самом Laravel или стандартных библиотеках к нему.
Если у тебя мало сервисов, можно просто сделать папку App/Services. Если много - то думать, как дробить на части. Можно как-то по смыслу их группировать, по разделу сайта, по компоненту приложения.
>>54604
По идее так: http://php.net/manual/ru/language.basic-syntax.phpmode.php
Но это даст низкокачественный код, потому читай https://github.com/codedokode/pasta/blob/master/php/templates.md
Он зато лаконичный. И там есть IPython Notebook - отличнейшая штука. Но отсутствие тайп-хинтов, конечно, убивает.
>>59625
Вернет в любом случае то, что указано в тайп-хинте, в нестрогом режиме при этом допускает автоматическую конвертацию в int.
>>59071
Можно как 3 функции, можно как 3 класса, можно как 3 метода в одном классе. Метод может принимать экземпляр Компании и делать с ней какие-то операции.
>>59022
Зависит от того, как сделана работа с БД. Если у тебя используется дата маппер, то в нем и будет метод для загрузки моделек из БД.
> Какой слой модели вызывает методы этой сущности и возвращает результат контроллеру? Или может контроллер сам это делает?
Вызывать метод загрузки данных может контроллер. Либо сервис, который загружает данные из БД и делает какие-то еще преобразования.
Зависит от ситуации. Контроллер должен быть тонким, то есть в основном в нем вызовы разных сервисов (тех же мапперов) для получения разных данных и передача их во View. Для валидации можно сделать отдельный класс-валидатор. Для сортировки - если это что-то простое, нужно только для вывода и не повторно используемое, то делаем в контроллере. В задаче про студентов сортировку логично сделать аргументом в методе для получения студентов:
findStudents($searchBy, $orderBy, $orderDir, $offset, $limit): iterable
Этот метод может быть в маппере для работы с БД.
В таком случае мы можем получать отсортированный список студентов из любого места кода.
Контроллер отвечает за управление процессом обработки запроса, за интерпретацию команд пользователя. То есть контроллер как-то (например, из $_GET) определяет, какая сортировка нужна пользователю, и вызывает метод для загрузки студентов с сортировкой по данному полю. А затем передает найденных студентов во View и отдает пользователю сгенерированную HTML страницу.
>>58638
Обычно это делают так:
class Controller
{
public function indexAction(Request $req)
{
if (!$this->isLoggedIn($req)) {
return $this->redirectTo(...);
}
...
}
}
То есть перед выполнением нужных действий проверяем наличие логина. При отсутствии - редирект на форму логина с передачей текущего URL (и проверкой, что он с нашего сайта). На форме логина пишется: "Чтобы XXX, вам надо зарегистрироваться, или ввести логин и пароль, если вы уже зарегистрированы". При желании можно как-то заморочиться и например, сделать проверку на уровне контроллера (то есть закрыть доступ ко всем методам этого контроллера сразу). Это удобно в админке, например.
В твоем варианте пользователь может попробовать обойти авторизацию, вызвав напрямую метод "методПослеПосещения".
Зависит от ситуации. Контроллер должен быть тонким, то есть в основном в нем вызовы разных сервисов (тех же мапперов) для получения разных данных и передача их во View. Для валидации можно сделать отдельный класс-валидатор. Для сортировки - если это что-то простое, нужно только для вывода и не повторно используемое, то делаем в контроллере. В задаче про студентов сортировку логично сделать аргументом в методе для получения студентов:
findStudents($searchBy, $orderBy, $orderDir, $offset, $limit): iterable
Этот метод может быть в маппере для работы с БД.
В таком случае мы можем получать отсортированный список студентов из любого места кода.
Контроллер отвечает за управление процессом обработки запроса, за интерпретацию команд пользователя. То есть контроллер как-то (например, из $_GET) определяет, какая сортировка нужна пользователю, и вызывает метод для загрузки студентов с сортировкой по данному полю. А затем передает найденных студентов во View и отдает пользователю сгенерированную HTML страницу.
>>58638
Обычно это делают так:
class Controller
{
public function indexAction(Request $req)
{
if (!$this->isLoggedIn($req)) {
return $this->redirectTo(...);
}
...
}
}
То есть перед выполнением нужных действий проверяем наличие логина. При отсутствии - редирект на форму логина с передачей текущего URL (и проверкой, что он с нашего сайта). На форме логина пишется: "Чтобы XXX, вам надо зарегистрироваться, или ввести логин и пароль, если вы уже зарегистрированы". При желании можно как-то заморочиться и например, сделать проверку на уровне контроллера (то есть закрыть доступ ко всем методам этого контроллера сразу). Это удобно в админке, например.
В твоем варианте пользователь может попробовать обойти авторизацию, вызвав напрямую метод "методПослеПосещения".
Ты передаешь массив, а пытаешься обращаться к его элементам через стрелку. Кто тебе сказал, что так можно? Это не JS, тут с массивами работают с помощью квадратных скобок, а стрелка для полей и методов объектов.
>>58542
Прочти также целиком мануал по json_decode. Там написано, когда получается объект класса stdClass, а когда массив.
>>58506
Не знаю. Где-то на хабре давно была статья с такой историей, можно ее поискать.
>>58393
Не думал, что у файлообменника такая сила. Сейчас, вроде начинающие разработчики делают сокращатели ссылок, но это уже неприлично просто.
>>58316
<br> это тег языка HTML. Он работает только там, где данные интерпретируются как HTML-код (в браузере). А \n браузер воспринимает просто как разделитель слов и непереносит строку.
На ideone и в консоли данные просто выводятся как текст. Если там будут теги - они выведутся как есть.
Это не единственное отличие. Например, & lt ; в браузере выводится как знак "меньше", а в консоли - как есть.
То есть различай "просто текст" и "HTML текст с тегами".
Если добавить в начало header("Content-Type: text/plain; charset=utf-8"); то браузер перестанет воспринимать данные как HTML и \n начнет работать.
Ты передаешь массив, а пытаешься обращаться к его элементам через стрелку. Кто тебе сказал, что так можно? Это не JS, тут с массивами работают с помощью квадратных скобок, а стрелка для полей и методов объектов.
>>58542
Прочти также целиком мануал по json_decode. Там написано, когда получается объект класса stdClass, а когда массив.
>>58506
Не знаю. Где-то на хабре давно была статья с такой историей, можно ее поискать.
>>58393
Не думал, что у файлообменника такая сила. Сейчас, вроде начинающие разработчики делают сокращатели ссылок, но это уже неприлично просто.
>>58316
<br> это тег языка HTML. Он работает только там, где данные интерпретируются как HTML-код (в браузере). А \n браузер воспринимает просто как разделитель слов и непереносит строку.
На ideone и в консоли данные просто выводятся как текст. Если там будут теги - они выведутся как есть.
Это не единственное отличие. Например, & lt ; в браузере выводится как знак "меньше", а в консоли - как есть.
То есть различай "просто текст" и "HTML текст с тегами".
Если добавить в начало header("Content-Type: text/plain; charset=utf-8"); то браузер перестанет воспринимать данные как HTML и \n начнет работать.
Если они совсем однотипные то можно, но проще наверно через new.
>>57918
Обычно делают отдельно шаблон для "лейаута" (шапка/подвал) и "контента". Лейаутов может быть несколько - например, один для морды, другой для админки. Как передавать параметры для лейаута? Тут есть варианты:
- руками копипастить их в каждый метод каждого контроллера (боль)
- наследовать контроллеры от базового класса и в нем формировать данные. У базового класса может быть метод вроде "поменять тайтл текущей страницы". Плюс - инкапсуляция, трудно передать что-то неправильное, например, не-строку.
- передать эти данные во вью до или после выполнения контроллера, без использования наследования. Или передать в контроллер.
- сделать "контроллер для лейаута" и в нем формировать данные для лейаута. У "контроллера лейаута" могут быть вспомогательные методы вроде "поменять тайтл текущей страницы".
В фреймворках готового решения нет. Каждый изобретает сам.
>>57536
Не очень понятно, как это. Вызывать из одного шаблона другие шаблоны можно.
>>57016
В сессию токен писать нет смысла. Данные сессии хранятся на сервере и недоступны пользователю, потому можно просто писать туда userId = 123. Это безопасно.
Токен нужен, если используется не сессия, а куки. Тогда писать userId = 123 нельзя, так как пользователь может их подделать. Также, некоторые шифруют и/или подписывают куки для защиты от чтения и/или модификации их пользователем. Плюс: сессии не требуется хранить.
В твоем случае проще всего сделать либо связь многие-ко-многим между запросами и пользователями, либо сделать роли (бухгалтер, продажник, админ), для запроса указать требуемую роль, и каждому пользователю присвоить одну или несколько ролей. Последнее называется RBAC, почитай, прежде чем изобретать велосипед.
Советую реализовать RBAC, в общем.
Статья в википедии про RBAC трудночитаема.
> 1. Как сделать авторизацию в MVC?
В твоем случае:
if (!$this->hasRole(Roles::ROLE_BUH)) {
редиректим на форму логина, либо показываем ошибку нехватки прав;
}
Junior не вводи
Клонирую объект в свойстве которого лежит массив объектов. Правильно я понимаю, что объекты в массиве тож надо клонировать с помощью __clone()? Если да, то правильной ли будет вот такая функция:
function __clone()
{
foreach ($this->arrayWithObjects as $key => $object)
{
$this->arrayWithObjects[$key] = clone $object;
}
}
Потом мне нужно работать с этими объектами в массиве и изменять их, но так чтобы оригинальные не поменялись.
Так сделал, вроде работает.
Или можно было как- то короче?
Тут я сравнивал не по половинам, а целую строку. Потому что я тупой и не придумал, как сравнить одну половину со второй.
Ты в тот же самый массив записываешь тот же самый объект с тем же ключом. Какой смысл? Массив у тебя как был один, так и будет. Или ты все-таки в другой клонируешь?
Одна из моих проблем заключается в том, что я не могу в базовую терминологию, поэтому часто приходится изъяснятся как ретард. Подскажите, как можно обозначить вот эти элементы страницы любого саба на Reddit (pic related)?
Хочу построить простой сайт (на локальном сервере, просто ради практики), который будет что-то листить. Людей, бананы - без разницы. Я просто селебрити выбрал. Сейчас сделал простую html форму для добавления новых записей в базу данных, на той же странице написал PHP скрипт, который пользуется переменными из $_GET для составления SQL INSERT INTO. Всё ок, только теперь не могу понять, каким образом написать скрипт для генерации html файла для каждого добавления в базу данных. То есть, у каждого селеба должен генерероваться свой профиль на основе страницы-шаблона.
Затем будет стоять задача построить механизм фильтрации по атрибутам (first_name, last_name, occupation, location, etc.) при поиске на сайте. Пока не могу даже в гугле толком вопрос сформировать, чтобы найти, что нужно. То есть, как реализовать фильтрацию по базе данных на странице html?
Подскажите, пожалуйста.
В тот же массив я записываю клоны объектов, чтобы потом их изменять, без изменения оригинальных объектов.
> теперь не могу понять
1. Пишешь скрипт который просто генерирует статический профиль.
2. Находишь в профиле всю инфу которая должна тянуться с базы.
3. Выделяешь эту инфу в переменные. Тоесть при генерации профиля инфа не захаркоджена а берется из переменных. Переменные ты инициализируешь перед скриптом генерации.
4. Придумываешь способ заполнить эти переменные инфой из бд.
5. .....
6. профит
Ты берешь и перезаписываешь оригинал его клонированной копией. Так понятно?
function __clone()
{
foreach (твойМассив как $ключ => $значение) {
твойМассив[$ключ] (текущий ключ форыча, - прим. автора) = clone $объект
}
}
Это равноценно такому
$arr = [0 => 'a', 1 => 'b'];
//первая итерация
foreach ($arr as 0 => 'a') {
$arr[0] = 'a';
}
То есть форыч берет и перезаписывает значение на то же самое. Чтобы работать с копиями, тебе необходимо положить их в новый массив.
>Реестр - это паттерн, который описыв
Спасибо, исчерпывающий ответ. Написал бы ты всеобъемлющую книгу по всем этим аспектам. Они ведь не устаревают. Будет пользоваться популярностью.
>>59946
>У меня просто времени иногда нет, последние пару недель адски загружен.
А можно узнать, чем ты занимаешься? В студии трудишься или в каком-нибудь банке софт ваяешь?
>Ни в коем случае. Идея DI контейнера - IoC - предполагает
Хорошо, проблему синглтона контейнер решает.
А вот еще вопрос. Создал я класс подключения к мускулу, сделал для него сервис-провайдер, где он создается, а потом внедряю его, куда нужно.
А вот, скажем, мне нужен не один объект, а нужно итеративно и с условиями получить много, тогда что? Нужно будет в контейнере хранить не объект, а класс, а потом внедрять этот класс и там создавать через new?
>Я думаю, сервис провайдеры и сервисы хранятся в отдельных папках.
Спасибо, теперь все понятно.
>>59948
Если у тебя используется дата маппер, то в нем и будет метод для загрузки моделек из БД.
То есть вообще всем управляет маппер, а сущности это такие пассивные ячейки памяти, к которым маппер обращается, и сущность сама по себе ничего делать не должна? И в контроллере правильно будет вызывать именно методы маппера?
>>59949
> В задаче про студентов сортировку логично сделать аргументом в методе для получения студентов:
Спасибо за разъяснение особенно за последний абзац и за метод, ты сэкономил мне кучу времени.
>То есть перед выполнением нужных действий проверяем наличие логина. При отсутствии - редирект на форму логина с передачей текущего URL
А можно такую проверку делать в конструкторах? Например, в общий абстрактный контроллер засунуть. А то не делать же ее в каждом методе (боль, лол). И если юзверь не залогинен, то просто будет редирект на контроллер залогинивания, который этот конструктор не наследует.
>В твоем варианте пользователь может попробовать обойти авторизацию, вызвав напрямую метод "методПослеПосещения".
Но у него не получится, если я объявлю этот метод закрытым. Можно вообще все методы сделать закрытыми и вызывать их через indexAction, в котором сформировать настройки и всякие проверки. Сигнатуру формировать по маршруту. Получится эдакий фронт контроллер, только не физический файл, а метод. Но это что-то идиотское, да? Лол.
>Не думал, что у файлообменника такая сила. Сейчас, вроде начинающие разработчики делают сокращатели ссылок, но это уже неприлично просто.
А это разве не проще, чем файлообменник? Там ведь просто идет редирект на реальный URI при обращении к твоей ссылке. За вечер-другой можно сделать.
>>59951
>Обычно делают отдельно шаблон для "лейаута" (шапка/подвал) и "контента". Лейаутов может быть несколько - например, один для морды, другой для админки. Как передавать параметры для лейаута? Тут есть варианты:
Я придумал вот что. Я в контроллере общем создал контейнер обычный массив data, куда записываю данные со всех уголков контроллеров и приходящие данные из моделей. В контроллерах-родителях задаю общие данные, в контроллерах специфичные, в методах контроллеров данные еще специфичнее. Данные записываются не через переопределение свойств, а через методы суперкласса.
Потом этот контейнер передаю в вид и обращаюсь к нему из шаблонов типа $data['title'], foreach($data['posts'] as $key => $post) {}, ну и все в таком духе. То есть "лейаут" и "контент" получаются данные из одного глобального массива data, переданного в вид. Так нормально?
>Не очень понятно, как это. Вызывать из одного шаблона другие шаблоны можно.
Ну я имел ввиду, можно ли несколько "контентов" подключать в "лейаут".
Спасибо за ответы, ты будто чистое знание вливаешь мне прямо в мозг. Я, бывает, что-то очень долго ищу, бывает, несколько дней занимает поиск ответа на какой-то вопрос, иногда бывает сам додумываюсь до ответа, но проблема в том, что я не знаю, правильный он или нет. Но вот так очень круто зайти сюда и прочитать ответы на все, что я задавал. Пасиба.
>Реестр - это паттерн, который описыв
Спасибо, исчерпывающий ответ. Написал бы ты всеобъемлющую книгу по всем этим аспектам. Они ведь не устаревают. Будет пользоваться популярностью.
>>59946
>У меня просто времени иногда нет, последние пару недель адски загружен.
А можно узнать, чем ты занимаешься? В студии трудишься или в каком-нибудь банке софт ваяешь?
>Ни в коем случае. Идея DI контейнера - IoC - предполагает
Хорошо, проблему синглтона контейнер решает.
А вот еще вопрос. Создал я класс подключения к мускулу, сделал для него сервис-провайдер, где он создается, а потом внедряю его, куда нужно.
А вот, скажем, мне нужен не один объект, а нужно итеративно и с условиями получить много, тогда что? Нужно будет в контейнере хранить не объект, а класс, а потом внедрять этот класс и там создавать через new?
>Я думаю, сервис провайдеры и сервисы хранятся в отдельных папках.
Спасибо, теперь все понятно.
>>59948
Если у тебя используется дата маппер, то в нем и будет метод для загрузки моделек из БД.
То есть вообще всем управляет маппер, а сущности это такие пассивные ячейки памяти, к которым маппер обращается, и сущность сама по себе ничего делать не должна? И в контроллере правильно будет вызывать именно методы маппера?
>>59949
> В задаче про студентов сортировку логично сделать аргументом в методе для получения студентов:
Спасибо за разъяснение особенно за последний абзац и за метод, ты сэкономил мне кучу времени.
>То есть перед выполнением нужных действий проверяем наличие логина. При отсутствии - редирект на форму логина с передачей текущего URL
А можно такую проверку делать в конструкторах? Например, в общий абстрактный контроллер засунуть. А то не делать же ее в каждом методе (боль, лол). И если юзверь не залогинен, то просто будет редирект на контроллер залогинивания, который этот конструктор не наследует.
>В твоем варианте пользователь может попробовать обойти авторизацию, вызвав напрямую метод "методПослеПосещения".
Но у него не получится, если я объявлю этот метод закрытым. Можно вообще все методы сделать закрытыми и вызывать их через indexAction, в котором сформировать настройки и всякие проверки. Сигнатуру формировать по маршруту. Получится эдакий фронт контроллер, только не физический файл, а метод. Но это что-то идиотское, да? Лол.
>Не думал, что у файлообменника такая сила. Сейчас, вроде начинающие разработчики делают сокращатели ссылок, но это уже неприлично просто.
А это разве не проще, чем файлообменник? Там ведь просто идет редирект на реальный URI при обращении к твоей ссылке. За вечер-другой можно сделать.
>>59951
>Обычно делают отдельно шаблон для "лейаута" (шапка/подвал) и "контента". Лейаутов может быть несколько - например, один для морды, другой для админки. Как передавать параметры для лейаута? Тут есть варианты:
Я придумал вот что. Я в контроллере общем создал контейнер обычный массив data, куда записываю данные со всех уголков контроллеров и приходящие данные из моделей. В контроллерах-родителях задаю общие данные, в контроллерах специфичные, в методах контроллеров данные еще специфичнее. Данные записываются не через переопределение свойств, а через методы суперкласса.
Потом этот контейнер передаю в вид и обращаюсь к нему из шаблонов типа $data['title'], foreach($data['posts'] as $key => $post) {}, ну и все в таком духе. То есть "лейаут" и "контент" получаются данные из одного глобального массива data, переданного в вид. Так нормально?
>Не очень понятно, как это. Вызывать из одного шаблона другие шаблоны можно.
Ну я имел ввиду, можно ли несколько "контентов" подключать в "лейаут".
Спасибо за ответы, ты будто чистое знание вливаешь мне прямо в мозг. Я, бывает, что-то очень долго ищу, бывает, несколько дней занимает поиск ответа на какой-то вопрос, иногда бывает сам додумываюсь до ответа, но проблема в том, что я не знаю, правильный он или нет. Но вот так очень круто зайти сюда и прочитать ответы на все, что я задавал. Пасиба.
>а нужно итеративно и с условиями получить много, тогда что? Нужно будет в контейнере хранить не объект, а класс, а потом внедрять этот класс и там создавать через new?
Хранить в контейнере factory, через нее создавать объекты.
Еще до фабрик не дошел, сейчас самое время, спасибо, анонче.
WINAMPP лучше всех.
Да, ты правильно понял. По умолчанию при клонировании поля объекта копируются их оригинала в клон. Если в поле массив - то создается новая копия массива, но объекты внутри массива не клонируются - копируются просто указатели на них (ты можешь это увидеть с помощью var_dump, который выводит уникальный номер объекта).
>>60214
Смысл в том, что когда мы клонируем объект и у него есть поле с массивом объектов, мы хотим сделать независимые копии этих объектов.
>>60386
Тут есть 2 подхода. Статический и динамический.
При статическом подходе для каждой знаменитости у нас будет отдельный HTML файл, а также будет файл-оглавление со списком знаменитостей.
Их можно генерировать 2 способами:
- сделать скрипт, который выбирает все записи из БД, обходит их циклом, для каждой генерирует страницу, а в конце генерирует оглавление. Такой скрипт удобнее запускать из ком. строки, так как он может долго работать.
- можно при добавлении знаменитости генерировать страницу с ней и класть в папку, а также перегенерировать оглавление. Это должно быть недолго, и это можно сделать в скрипте обновления БД, только вынеси это в отдельные функции.
Тут правильнее будет вынести код генерации страницы знаменитости и оглавления в функции, чтобы их можно было использовать в обоих сценариях - и для инкрементального обновления, и для полной перегенерации сайта.
Есть и другой подход - динамический. При нем мы не храним HTML на диске вообще. Мы генерируем HTML-страницу знаменитости или оглавление на лету, когда пользователь ее попросит, и не сохраняем ее никуда. То есть когда пользователь заходит по адресу /celeb.php?id=123 (адрес можно будет поменять), то запускается скрипт, берет информацию о знаменитости 123, подставляет ее в шаблон и отдает получившийся HTML пользователю. Аналогично делается оглавление.
Плюс динамического сайта - то, что не надо ничего обновлять при добавлении или изменении информации, и что изменения в БД мгновенно отображаются на сайте. Плюс статического сайта - скорость работы, в десятки-сотни раз выше, чем у динамического. Минус статического сайта - по мере роста объема информации обновление занимает больше времени. Представь, что у тебя оглавление содержит 20 знаменитостей на страницу, а всего знаменитостей миллион. При добавлении новой тебе придется обновлять все 50 000 страниц оглавления.
Почитать про шаблоны можно тут: https://github.com/codedokode/pasta/blob/master/php/templates.md
Почитать про архитектуру MVC (для динамической генерации страниц) можно тут: https://github.com/codedokode/pasta/blob/master/arch/mvc.md
Наконец, у нас есть задача сделать список студентов - и в ней есть очень много комментариев, которые подойдут и тебе: https://github.com/codedokode/pasta/blob/master/student-list.md
> Подскажите, как можно обозначить вот эти элементы страницы любого саба на Reddit (pic related)?
Посты, записи.
> на той же странице написал PHP скрипт, который пользуется переменными из $_GET для составления SQL INSERT INTO.
Прочитай обязательно про инъекции: https://github.com/codedokode/pasta/blob/master/security/sql-injection.md
> Затем будет стоять задача построить механизм фильтрации по атрибутам (first_name, last_name, occupation, location, etc.) при поиске на сайте.
Это надо делать динамически, то есть при получении запроса из формы поиска ты должен сформулировать SQL запрос с условиями, выбрать знаменитостей из БД, сформировать HTML страницу и отправить пользователю.
Со статическим подходом это сделать вряд ли получится, так как фильтры дают огромное количество комбинаций и ты замучаешься генерировать заранее HTML страницу под каждую комбинацию.
Да, ты правильно понял. По умолчанию при клонировании поля объекта копируются их оригинала в клон. Если в поле массив - то создается новая копия массива, но объекты внутри массива не клонируются - копируются просто указатели на них (ты можешь это увидеть с помощью var_dump, который выводит уникальный номер объекта).
>>60214
Смысл в том, что когда мы клонируем объект и у него есть поле с массивом объектов, мы хотим сделать независимые копии этих объектов.
>>60386
Тут есть 2 подхода. Статический и динамический.
При статическом подходе для каждой знаменитости у нас будет отдельный HTML файл, а также будет файл-оглавление со списком знаменитостей.
Их можно генерировать 2 способами:
- сделать скрипт, который выбирает все записи из БД, обходит их циклом, для каждой генерирует страницу, а в конце генерирует оглавление. Такой скрипт удобнее запускать из ком. строки, так как он может долго работать.
- можно при добавлении знаменитости генерировать страницу с ней и класть в папку, а также перегенерировать оглавление. Это должно быть недолго, и это можно сделать в скрипте обновления БД, только вынеси это в отдельные функции.
Тут правильнее будет вынести код генерации страницы знаменитости и оглавления в функции, чтобы их можно было использовать в обоих сценариях - и для инкрементального обновления, и для полной перегенерации сайта.
Есть и другой подход - динамический. При нем мы не храним HTML на диске вообще. Мы генерируем HTML-страницу знаменитости или оглавление на лету, когда пользователь ее попросит, и не сохраняем ее никуда. То есть когда пользователь заходит по адресу /celeb.php?id=123 (адрес можно будет поменять), то запускается скрипт, берет информацию о знаменитости 123, подставляет ее в шаблон и отдает получившийся HTML пользователю. Аналогично делается оглавление.
Плюс динамического сайта - то, что не надо ничего обновлять при добавлении или изменении информации, и что изменения в БД мгновенно отображаются на сайте. Плюс статического сайта - скорость работы, в десятки-сотни раз выше, чем у динамического. Минус статического сайта - по мере роста объема информации обновление занимает больше времени. Представь, что у тебя оглавление содержит 20 знаменитостей на страницу, а всего знаменитостей миллион. При добавлении новой тебе придется обновлять все 50 000 страниц оглавления.
Почитать про шаблоны можно тут: https://github.com/codedokode/pasta/blob/master/php/templates.md
Почитать про архитектуру MVC (для динамической генерации страниц) можно тут: https://github.com/codedokode/pasta/blob/master/arch/mvc.md
Наконец, у нас есть задача сделать список студентов - и в ней есть очень много комментариев, которые подойдут и тебе: https://github.com/codedokode/pasta/blob/master/student-list.md
> Подскажите, как можно обозначить вот эти элементы страницы любого саба на Reddit (pic related)?
Посты, записи.
> на той же странице написал PHP скрипт, который пользуется переменными из $_GET для составления SQL INSERT INTO.
Прочитай обязательно про инъекции: https://github.com/codedokode/pasta/blob/master/security/sql-injection.md
> Затем будет стоять задача построить механизм фильтрации по атрибутам (first_name, last_name, occupation, location, etc.) при поиске на сайте.
Это надо делать динамически, то есть при получении запроса из формы поиска ты должен сформулировать SQL запрос с условиями, выбрать знаменитостей из БД, сформировать HTML страницу и отправить пользователю.
Со статическим подходом это сделать вряд ли получится, так как фильтры дают огромное количество комбинаций и ты замучаешься генерировать заранее HTML страницу под каждую комбинацию.
Наконец, есть еще третий подход, который нельзя отнести ни к статическому, ни к динамическому. Это генерация страниц на клиенте (в браузере). Дело в том, что в HTML страницу можно встраивать программу на языке Яваскрипт. Ты можешь сделать сайт из единственной HTML-страницы. При загрузке в браузер программа на JS обратится на сервер, запросит список знаменитостей, оформит с помощью шаблона и отобразит его на странице. Когда пользователь нажмет на знаменитость, JS скрипт запросит данные о ней (или возьмет информацию из ранее запрошенных данных) и сгенерует страницу этой знаменитости.
То есть HTML код будет генерироваться прямо в браузере, а сервер по сути будет просто предоставлять данные о знаменитостях в виде того же JSON.
Более того, если знаменитостей ограниченное кол-во (не более нескольких тысяч), можно просто информацию о них сложить в файл JSON (или CSV, XML) на сервере, программа на JS будет скачивать этот файл, и использовать информацию из него. В таком случае нам не придется писать PHP-код, который будет выдавать информацию о знаменитостях.
Этот подход самый сложный, но он дает огромные возможности, например, ты можешь скачать все данные о знаменитостях в браузер, и после этого сайт может работать даже без доступа к Интернету. Думаю, это может быть удобно для разработки сайтов для смартфонов.
На практике, правда, когда пытаются реализовать этот подход, получается тормозной, глючный, плохо работающий, дергающийся сайт. Потому что этот подход сложный и не всем по плечу. Люди бездумно используют готовые библиотеки и подходы, даже не понимая толком, как они работают, какие у них ограничения.
MySQL знаю хорошо, сильны ли различия?
Где что можно посмотреть/почитать на русском, дабы обуздать PostgreSQL для людей знакомых с мускулем?
Смысла по-моему не особо, mysql по уши хватает на задачи реляционных БД. Зато nosql решения часто в добавок к mysql на фирмах требуются, так что полезнее будет их поизучать, mongodb c редисами, elasticsearch.
с редиской знаком, а монго давно как-то мельком юзал. Есть чето на русском годное и исчерпывающее?
И всетаки интересно именно по postgre
Ого! Thanks for guidance. Буду разбираться.
Есть официальный качественный мануал на русском: https://postgrespro.ru/docs/postgresql/11/index.html
Изучить наверно стоит, так как в ней гораздо больше фич, чем в MySQL.
Почему в экосистеме PHP такая чудовищно всратая ситуация с поднятием локального сервера?
Вот есть Нода. Спокойно запускает процесс сервера из любого каталога, лишь бы там были нужные файлы и всех пакетов хватало.
То же самое у Питона, но только надо с виртуальной средой чуть заморочиться.
ASP.Net? Да тоже в общем-то обычным процессом запускается, ещё и с exe-шников.
Да что там, даже Хаскель компилится в нормально исполняемый из любого каталога сервер.
И тут я прихожу в мир PHP. И наблюдаю удивительнейшие истории когда для запуска проекта нужно зачем-то пихать его в какую-то папку апача\нгинкса и смотреть как это чудовище пытается запускаться на 80м порту. При этом ещё по дефолту будет как-то странно всё отображать. Ещё и лагать может только в путь.
Блять, ну почему осталось именно так? Почему в 2к19 приходится наблюдать эти 90е?
Я прекрасно понимаю насколько это удачный подход относительно продакшн-сервера уже на готовом хосте, но для локальной работы, ещё и с несколькими проектами это какой-то ужас.
Ты бы для начала изучил PHP получше: http://php.net/manual/ru/features.commandline.webserver.php
Сервер там поднять в разы проще, чем на ноде.
>>60865
Надо же, не думал что буду так благодарен за струю мочи в мою сторону.
Работает прекраснейшим образом, без лагов и даже настраивать ничего не понадобилось.
Но почему тогда мне отовсюду XAMPP, LAMP, WAMP советуют ставить чтобы хоть что-то запустить?
Я в своё время по сути только из-за этого php и не трогал, что он целенаправленно MySQL пропагандирует с которым я не в ладах. А тут, получается, можно и без этого тоже чтоли?
> XAMPP, LAMP, WAMP советуют
Они желают тебе зла. Если собираешься серьезно погружаться в тему, то только php_fpm + nginx, а по мелочам и встроенного хватит.
>php и не трогал, что он целенаправленно MySQL пропагандирует
Ты шутишь?
>Если собираешься серьезно погружаться в тему, то только php_fpm + nginx, а по мелочам и встроенного хватит.
Что-то с nginx по-моему ебли и допотопности было не сильно меньше чем с Апачем.
>Ты шутишь?
Да не особо. По тем же хостингам если из БД предлагают только MySQL, то уже ясно что хостинг заточен на php. Те же стандартные стаки и почти все гайды - отовсюду предлагают именно MySQL.
Если мне нужно будет найти определенные буквы, то я могу написать:
[а-жк-у] (от А до Ж, от К до У), то код не слепит вместе Ж и К?
Есть ли какой- то символ, который можно ставить, чтоб чисто визуально видеть, что они (Ж и К) будут врознь?
Не слепит
Вопрос:"Есть ли тут место php?"
Ну да. А что тебя смущает?
api platform
Знаю, код очень корявый, но я хочу стать лучше, потому мне нужны ваши ответа
Можно ли сделать плавный переход по якорям без JS?
Можно ли задать плавное всплывание картинок по нажатию на какую-либо из кнопок?
Копирайт всегда должен быть в футере, можно ли их разделить и будет ли это правильно? Я разделил, потому что так в PSD было. С шапкой также сделал.
Подписи к заголовкам обрамлять в <h> на пункт меньше или <p>?
Почему "margin: 0;" в body не работал и мне приходилось подписывать его к каждому элементу?
А, и что еще со значками соц.сетей мне делать нужно было? Я фигурку внутри кружка скачал, а она битая получилась, но я вставил все равно.
Вот что мне в ноде кажется неудобным - так это то, что каждый в Node.JS организует код как хочет. Вот, например, код: bodyParser.json() - непонятно, почему он сделан в виде вызова функции? Если он есть в единственном экземпляре, можно просто было бы использовать свойство bodyParser.jsonParser. Если там каждый раз надо создавать новый объект, можно было бы экспортировать класс: new bodyParser.JsonParser. А тут надо вызвать функцию, причем в ее названии нет ни малейшего намека на то, что это функция-конструктор (хоть бы createJsonParser ее назвали). Понятно, что это сторонняя библиотека, но все же не привычно.
А еще я открыл для себя, что в Express функция get() делает 2 разных вещи, в зависимости от числа аргументов: http://expressjs.com/ru/api.html#app . Кажется, разработчики Ноды решили пройтись по тем же граблям, которые проходили разработчики PHP.
Но в общем-то, я довольно плохо знаком с Нодой. Потому перейдем к коду.
Комментариев, конечно мало. Наверно стоило бы хотя бы в начале файла писать, что это за модуль и для чего.
Тут ссылка на отсутствующий в репозитории файл - это нормально? Этот файл ведь из чего-то генерируется, и нельзя ли тут подключать исходный файл. Или хотя бы комментарий написать:
https://github.com/someApprentice/Crypter/blob/master/server.ts#L30
> const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main.js');
Это место очень трудно понять. Мне пришлось открыть вопрос по ссылке из комментария и прочесть его:
> const asyncHandler = fn => (req, res, next) => {
И проблема тут в плохом названии и отсутствии комментариев. Нужно было назвать функцию, например, promiseToCallback, handlePromise или adaptPromise и снабдить комментарием (я еще убрал стрелки так как на мой личный вкус, они плохо читаемы):
// Комментарий
function wrapPromise(asyncFn) {
return function(req, res, next) {
return Promise....;
}
}
Еще я не очень понял:
1) зачем там нужен Promise.resolve(), если асинхронная функция при вызове и так возвращает нам промис.
2) зачем использовать return
Я бы тогда написал так:
// Принимает на вход асинхронный обработчик, и возвращает
// новый обработчик, который отлавливает выброшенные
// в нем асинхронно исключения, передавая их в next().
//
// ссылка на SO
function adaptPromise(asyncFn) {
return function (req, res, next) {
asyncFn(req, res, next).catch(next);
}
}
> Does Bearer token provide XSRF protection?
Я думаю, да, тут XSRF не пройдет, так как браузер автоматически добавляет к отправляемым запросам куки или заголовок Authorization для традиционной авторизации по логину-паролю (на чем и основана уязвимость), но не добавляет его для типа Bearer.
> if (err) throw new err;
А не throw err? err - это функция-констуктор или объект исключения?
> res.cookie('uuid', uuid, ...
> res.cookie('email', email, ...
> res.cookie('name', name, ...
А зачем тут столько кук? Они же уже содержатся внутри токена. А в твоем варианте пользователь может их подменить, и если ты доверяешь им на сервере, то получается уязвимость. Ну и вообще, нужно ли ставить куки в SPA? Это же серверное API, оно наверно не должно ставить куки. А должно отдавать JSON с токеном.
> json(<U> { uuid, email, name, jwt });
Не очень понятно, зачем здесь влеплен Type Assertion? Функции json() он не нужен, это для проверки, что объект соответствует интерфейсу User?
Еще, кстати, вопрос: а при регистрации проверяется уникальность email? Также, я подозреваю, что удобнее может быть сделать отдельную функцию для валидации, чем использовать только встроенные возможности sequelize.
По поводу JWT - я с ним не работал (но с интересом прочитал про него), но у меня уже есть сомнения: почему при подписании токена мы передаем все значения из модели? Получается, что токен перестанет работать в следующих ситуациях:
- пользователь меняет имя
- пользователь меняет email
- пользователь меняет пароль
- мы добавили новые поля в модель пользователя
На мой взгляд, токен должен переставать работать только при смене пароля (на случай, если пользователь боится, что у него украли токен и меняет пароль, старый токен должен перестать действовать). Соответственно, в подписи должны участвовать только uuid и хеш пароля. Или я ошибаюсь?
И, кстати, а почему был выбран JWT? Какие-то еще варианты рассматривались?
Также, я не уверен, а правильно ли токен создавать прямо в обработчике? Не логичнее ли сделать функцию такого вида:
async function createToken(u: User): Promise<string>
Чтобы мы могли бы создавать токены в любом месте API?
> let user = new User({ email });
> await user.validate({ skip: difference(Object.keys(User.rawAttributes), ['email']) });
Это, конечно, выглядит немного корявым способом проверить email. Наверно, можно как-то получить функцию валидации отдельного поля?
> router.get('/email/:email'
Это не очень соответствует REST, так как URL в нем это указатель на "ресурс" (какую-то сущность), и при отсутствии ресурса ты должен отдавать 404. А ты всегда отдаешь 200. Я бы сделал тогда просто /email-exists?email=....
Не очень понятно, зачем ты тут вызвал валидацию и не использовал результат.
И еще важный момент. У тебя нет документации по API, а это плохо. Если фронтенд-разработчик хочет использовать твое API, где он прочтет документацию? Код что ли разбирать?
В наши дни наверно самый популярный формат документирования - это OpenAPI 3.0 (бывший Swagger). Ты описываешь свое API в виде YAML файла, и получаешь на выходе такую красивую штуку: https://petstore.swagger.io/ или такую: https://docs.discourse.org/
Естественно, логично не писать YAML руками, а генерировать его из комментариев в коде. Так ты получишь и комментированный код, и документацию для разработчиков.
Еще, кстати, мне интересно, можно ли как-то использовать описания моделей (например, User) для автоматической генерации документации о формате JSON-ответа, и можно ли использовать описания для автоматического тестирования (тест берет Swagger-описание, проходится по всем методам и пробует их вызывать и проверяет, что ответ соответствует описанию).
Вот что мне в ноде кажется неудобным - так это то, что каждый в Node.JS организует код как хочет. Вот, например, код: bodyParser.json() - непонятно, почему он сделан в виде вызова функции? Если он есть в единственном экземпляре, можно просто было бы использовать свойство bodyParser.jsonParser. Если там каждый раз надо создавать новый объект, можно было бы экспортировать класс: new bodyParser.JsonParser. А тут надо вызвать функцию, причем в ее названии нет ни малейшего намека на то, что это функция-конструктор (хоть бы createJsonParser ее назвали). Понятно, что это сторонняя библиотека, но все же не привычно.
А еще я открыл для себя, что в Express функция get() делает 2 разных вещи, в зависимости от числа аргументов: http://expressjs.com/ru/api.html#app . Кажется, разработчики Ноды решили пройтись по тем же граблям, которые проходили разработчики PHP.
Но в общем-то, я довольно плохо знаком с Нодой. Потому перейдем к коду.
Комментариев, конечно мало. Наверно стоило бы хотя бы в начале файла писать, что это за модуль и для чего.
Тут ссылка на отсутствующий в репозитории файл - это нормально? Этот файл ведь из чего-то генерируется, и нельзя ли тут подключать исходный файл. Или хотя бы комментарий написать:
https://github.com/someApprentice/Crypter/blob/master/server.ts#L30
> const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main.js');
Это место очень трудно понять. Мне пришлось открыть вопрос по ссылке из комментария и прочесть его:
> const asyncHandler = fn => (req, res, next) => {
И проблема тут в плохом названии и отсутствии комментариев. Нужно было назвать функцию, например, promiseToCallback, handlePromise или adaptPromise и снабдить комментарием (я еще убрал стрелки так как на мой личный вкус, они плохо читаемы):
// Комментарий
function wrapPromise(asyncFn) {
return function(req, res, next) {
return Promise....;
}
}
Еще я не очень понял:
1) зачем там нужен Promise.resolve(), если асинхронная функция при вызове и так возвращает нам промис.
2) зачем использовать return
Я бы тогда написал так:
// Принимает на вход асинхронный обработчик, и возвращает
// новый обработчик, который отлавливает выброшенные
// в нем асинхронно исключения, передавая их в next().
//
// ссылка на SO
function adaptPromise(asyncFn) {
return function (req, res, next) {
asyncFn(req, res, next).catch(next);
}
}
> Does Bearer token provide XSRF protection?
Я думаю, да, тут XSRF не пройдет, так как браузер автоматически добавляет к отправляемым запросам куки или заголовок Authorization для традиционной авторизации по логину-паролю (на чем и основана уязвимость), но не добавляет его для типа Bearer.
> if (err) throw new err;
А не throw err? err - это функция-констуктор или объект исключения?
> res.cookie('uuid', uuid, ...
> res.cookie('email', email, ...
> res.cookie('name', name, ...
А зачем тут столько кук? Они же уже содержатся внутри токена. А в твоем варианте пользователь может их подменить, и если ты доверяешь им на сервере, то получается уязвимость. Ну и вообще, нужно ли ставить куки в SPA? Это же серверное API, оно наверно не должно ставить куки. А должно отдавать JSON с токеном.
> json(<U> { uuid, email, name, jwt });
Не очень понятно, зачем здесь влеплен Type Assertion? Функции json() он не нужен, это для проверки, что объект соответствует интерфейсу User?
Еще, кстати, вопрос: а при регистрации проверяется уникальность email? Также, я подозреваю, что удобнее может быть сделать отдельную функцию для валидации, чем использовать только встроенные возможности sequelize.
По поводу JWT - я с ним не работал (но с интересом прочитал про него), но у меня уже есть сомнения: почему при подписании токена мы передаем все значения из модели? Получается, что токен перестанет работать в следующих ситуациях:
- пользователь меняет имя
- пользователь меняет email
- пользователь меняет пароль
- мы добавили новые поля в модель пользователя
На мой взгляд, токен должен переставать работать только при смене пароля (на случай, если пользователь боится, что у него украли токен и меняет пароль, старый токен должен перестать действовать). Соответственно, в подписи должны участвовать только uuid и хеш пароля. Или я ошибаюсь?
И, кстати, а почему был выбран JWT? Какие-то еще варианты рассматривались?
Также, я не уверен, а правильно ли токен создавать прямо в обработчике? Не логичнее ли сделать функцию такого вида:
async function createToken(u: User): Promise<string>
Чтобы мы могли бы создавать токены в любом месте API?
> let user = new User({ email });
> await user.validate({ skip: difference(Object.keys(User.rawAttributes), ['email']) });
Это, конечно, выглядит немного корявым способом проверить email. Наверно, можно как-то получить функцию валидации отдельного поля?
> router.get('/email/:email'
Это не очень соответствует REST, так как URL в нем это указатель на "ресурс" (какую-то сущность), и при отсутствии ресурса ты должен отдавать 404. А ты всегда отдаешь 200. Я бы сделал тогда просто /email-exists?email=....
Не очень понятно, зачем ты тут вызвал валидацию и не использовал результат.
И еще важный момент. У тебя нет документации по API, а это плохо. Если фронтенд-разработчик хочет использовать твое API, где он прочтет документацию? Код что ли разбирать?
В наши дни наверно самый популярный формат документирования - это OpenAPI 3.0 (бывший Swagger). Ты описываешь свое API в виде YAML файла, и получаешь на выходе такую красивую штуку: https://petstore.swagger.io/ или такую: https://docs.discourse.org/
Естественно, логично не писать YAML руками, а генерировать его из комментариев в коде. Так ты получишь и комментированный код, и документацию для разработчиков.
Еще, кстати, мне интересно, можно ли как-то использовать описания моделей (например, User) для автоматической генерации документации о формате JSON-ответа, и можно ли использовать описания для автоматического тестирования (тест берет Swagger-описание, проходится по всем методам и пробует их вызывать и проверяет, что ответ соответствует описанию).
По тесту:
https://github.com/someApprentice/Crypter/blob/master/api/api.test.ts#L31
Тут код теста почти повторяет код обработчика. Это не годится, так как ты можешь сделать одинаковые ошибки и там, и там. Если мы тестируем функцию вычисления корня, то ее проверяют возведением в квадрат, а не аналогичным вычислением.
В твоем случае надо сформулировать требования к функции регистрации. На мой взгляд, они такие:
- она должна создавать аккаунт на сервере, под которым можно залогиниться
- она должна возвращать токен, с которым можно использовать защищенное API
- нельзя дважды зарегистрироваться с одинаковым email
- нельзя регистрироваться без указания обязательных аргументов
- и, может, еще какие-то требования по валидации. Например, что при ошибке возвращается объект определенного вида
Для каждого требования мы придумываем свой способ проверки. Создание аккаунта на сервере можно проверить, попробовав залогиниться в него. Токен можно проверить, попробовав вызвать защищенное API, например "получить сведения о себе".
В общем, тесты пока сделаны неправильно.
Так как ты тестируешь код, использующий БД, тебе надо предусмотреть, чтобы база была перед тестом в каком-то определенном состоянии (полностью пустая, например, или с какими-то тестовыми данными). Один из вариантов - в самом начале почистить БД/загрузить дамп, а каждый тест обернуть в транзакцию, которая откатывается. Это может быть быстрее, чем загружать дамп каждый раз.
На практике, может быть полезно разместить тестовую БД на ramfs, чтобы избежать вообще обращений к диску и ускорить прогон тестов.
Можно также попробовать добавить тест для обработчика ошибок (вызвать специальный URL, который выбрасывает исключение). Иначе ты можешь его отключить/сломать и не заметить.
> Is there any way to declare all classes at once for a webpack output file?
Я думаю, это не имеет отношения к webpack, так как ты должен в sequelize передать список классов. А для этого их надо импортировать. И вебпак увидит импорты.
По клиентской стороне:
Ты в API при логине ставишь куки вручную, но разве это правильно? По идее, это должно работать так:
- пользователь заходит на HTML-версию и его браузер отправляет запрос на сервер
- на сервере создается "песочница", в которой выполняется Angular-приложение и рендерит страницу
- оно добавляет записи в StorageService
- серверный код видит эти изменения и формирует из них куки
Если ты сделал куки аналогом localStorage для серверного рендеринга, то, наверно, логично абстрагировать это от приложения. Оно работает с StorageService, а куда потом сохраняются данные - не его забота.
Тут конечно мы упираемся в проблему, что объем кук ограничен, в то время как в браузере мы можем кучу информации сохранить в localStorage. Решением наверно тут будет как можно меньше сохранять в storage, когда мы выполняемся в режиме рендера на сервере. В идеале - хранить там только JWT.
Реализовать разную логику для работы на клиенте и на сервере можно, например, через интерфейс:
interface Storage {
getJwtToken(): Promise<string>
setJwtToken(token)
getUserData(): Promise<User>
setUserData()
}
И 2 реализации: клиентская берет данные из localStorage, а серверная - берет из БД или кук. Но это, конечно, требует писать два варианта кода.
Есть еще альтернатива, но она довольно сложная. Мы могли бы сделать что-то вроде "сессий" и делать для каждой "сессии" свой серверный аналог localStorage. То есть пользователь заходит на сайт, на сервере создается некий serverStorage, который не очищается между запросами. И приложение Angular, работая на сервере, работает с ним, как будто это localStorage. Ключом сессии может быть токен JWT. Данные serverStorage могут храниться в памяти Node-приложения или сбрасываться в HASH внутри редис.
То есть, когда приложение работает на сервере, в итоге данные пишутся в redis с ключом на основе JWT. Там конечно еще можно подумать над оптимальным по производительности алгоримом. И опять же, стараться при работе на сервере меньше сохранять данные в serverStorage, если они есть в БД.
https://github.com/someApprentice/Crypter/blob/master/src/app/app.component.html
Тут мне не кажется 100% надежной проверка залогиненности. Тут же нет проверки, что это валидный токен.
И еще, я не очень понял, как будет работать форма регистрации в режиме серверного рендеринга. Как и куда она будет отправлять свои данные?
> let route = this.router.config.find(r => r.path === redirect);
> route.data['email'] = email;
А это передает email только один раз, или ты этот email навсегда в конфиг роутинга вписал? По моем, не очень красиво получается. Разве нельзя передавать email внутри route parameters: https://angular.io/guide/router#route-parameters
Тогда можно сделать так: router.navigate(['/register', { email: email }])
Причем, я бы хранил email не в path, а в query: /register?email=
> Позволяет ли Bearer token защититься от XSRF?
> Приходит ли вам на ум какая-нибудь слабая точка которую можно эксплуатировать?
Да. У тебя используются куки, и в них тоже есть токен. Предположим, у нас есть HTML версия, работающая без ангулара. Не получится ли, что злоумышленник сделает форму, которая будет имитировать отправку запроса этой HTML версией, и возникнет XSRF?
> Достаточно ли это строгая проверка на то является ли сущность экземпляром объекта localStorage?
Проверка достаточная, но непонятен ее смысл. Тест по идее должен проверять, что данные сохраняются и загружаются, а не какой именно класс там испольуется для хранения.
> В правильной ли директории находится эта обёртка? Это не совсем сущность, например как User, но места по лучше я не могу придумать для неё. Куда следует помещать обёртки?
Это сервис. Помещать - не знаю, в папку для сервисов? Но у меня есть ощущение, что в клиентском приложении файлы и так довольно хаотично разбросаны.
> Нужно ли делать проверку на каждую валидацию? Например на встроенные в Ангуляр валидаторы или на валидацию по регулярному выражению?
Нет смысла тестировать протестированное. Валидатор если и тестировать, то юнит-тестами, и они уже есть в сторонней библиотеке. Но надо протестировать, что:
- эти валидаторы действительно вызываются и проверяют данные
- их результат работы отображается
Потому, я думаю, нужно проверять не каждую валидацию, а просто вариант, когда поле заполнено неправильно. Можно даже сделать тест, где все поля заполнены неправильно, и проверить, что каждое из них помечено как ошибочное. Ну и тест, что есть какой-то минимум полей, без заполнения которых нельзя пройти регистрацию.
> Следующий шаг написания сервиса сообщений.
> Он будет реализован с помощь протокола WAMP и на платформе от https://crossbar.io/ , которая написана на Питоне, и для которой для аутентификации клиентов нужно тоже написать код на нём.
А чем Node не годится, кстати? Или ты решил изучить Питон и микросервисы? Ок, но на практике такой маленький проект может быть невыгодно разбивать на микросервисы - у них ведь есть и накладные расходы.
> Поэтому я сейчас буду изучать его, и возможно у меня появиться небольшие вопросы по нему. Могу я задать их в этом треде?
> Заодно и реактивное программирование подучу.
Конечно. Только пиши тогда, что именно ты добавил в проект и на что стоит посмотреть, а то он большой и с ходу это будет не очевидно.
Я пока все не успел проверить, увы, давай сначала с тем, что я написал, разберемся, а потом остальное глянем.
По тесту:
https://github.com/someApprentice/Crypter/blob/master/api/api.test.ts#L31
Тут код теста почти повторяет код обработчика. Это не годится, так как ты можешь сделать одинаковые ошибки и там, и там. Если мы тестируем функцию вычисления корня, то ее проверяют возведением в квадрат, а не аналогичным вычислением.
В твоем случае надо сформулировать требования к функции регистрации. На мой взгляд, они такие:
- она должна создавать аккаунт на сервере, под которым можно залогиниться
- она должна возвращать токен, с которым можно использовать защищенное API
- нельзя дважды зарегистрироваться с одинаковым email
- нельзя регистрироваться без указания обязательных аргументов
- и, может, еще какие-то требования по валидации. Например, что при ошибке возвращается объект определенного вида
Для каждого требования мы придумываем свой способ проверки. Создание аккаунта на сервере можно проверить, попробовав залогиниться в него. Токен можно проверить, попробовав вызвать защищенное API, например "получить сведения о себе".
В общем, тесты пока сделаны неправильно.
Так как ты тестируешь код, использующий БД, тебе надо предусмотреть, чтобы база была перед тестом в каком-то определенном состоянии (полностью пустая, например, или с какими-то тестовыми данными). Один из вариантов - в самом начале почистить БД/загрузить дамп, а каждый тест обернуть в транзакцию, которая откатывается. Это может быть быстрее, чем загружать дамп каждый раз.
На практике, может быть полезно разместить тестовую БД на ramfs, чтобы избежать вообще обращений к диску и ускорить прогон тестов.
Можно также попробовать добавить тест для обработчика ошибок (вызвать специальный URL, который выбрасывает исключение). Иначе ты можешь его отключить/сломать и не заметить.
> Is there any way to declare all classes at once for a webpack output file?
Я думаю, это не имеет отношения к webpack, так как ты должен в sequelize передать список классов. А для этого их надо импортировать. И вебпак увидит импорты.
По клиентской стороне:
Ты в API при логине ставишь куки вручную, но разве это правильно? По идее, это должно работать так:
- пользователь заходит на HTML-версию и его браузер отправляет запрос на сервер
- на сервере создается "песочница", в которой выполняется Angular-приложение и рендерит страницу
- оно добавляет записи в StorageService
- серверный код видит эти изменения и формирует из них куки
Если ты сделал куки аналогом localStorage для серверного рендеринга, то, наверно, логично абстрагировать это от приложения. Оно работает с StorageService, а куда потом сохраняются данные - не его забота.
Тут конечно мы упираемся в проблему, что объем кук ограничен, в то время как в браузере мы можем кучу информации сохранить в localStorage. Решением наверно тут будет как можно меньше сохранять в storage, когда мы выполняемся в режиме рендера на сервере. В идеале - хранить там только JWT.
Реализовать разную логику для работы на клиенте и на сервере можно, например, через интерфейс:
interface Storage {
getJwtToken(): Promise<string>
setJwtToken(token)
getUserData(): Promise<User>
setUserData()
}
И 2 реализации: клиентская берет данные из localStorage, а серверная - берет из БД или кук. Но это, конечно, требует писать два варианта кода.
Есть еще альтернатива, но она довольно сложная. Мы могли бы сделать что-то вроде "сессий" и делать для каждой "сессии" свой серверный аналог localStorage. То есть пользователь заходит на сайт, на сервере создается некий serverStorage, который не очищается между запросами. И приложение Angular, работая на сервере, работает с ним, как будто это localStorage. Ключом сессии может быть токен JWT. Данные serverStorage могут храниться в памяти Node-приложения или сбрасываться в HASH внутри редис.
То есть, когда приложение работает на сервере, в итоге данные пишутся в redis с ключом на основе JWT. Там конечно еще можно подумать над оптимальным по производительности алгоримом. И опять же, стараться при работе на сервере меньше сохранять данные в serverStorage, если они есть в БД.
https://github.com/someApprentice/Crypter/blob/master/src/app/app.component.html
Тут мне не кажется 100% надежной проверка залогиненности. Тут же нет проверки, что это валидный токен.
И еще, я не очень понял, как будет работать форма регистрации в режиме серверного рендеринга. Как и куда она будет отправлять свои данные?
> let route = this.router.config.find(r => r.path === redirect);
> route.data['email'] = email;
А это передает email только один раз, или ты этот email навсегда в конфиг роутинга вписал? По моем, не очень красиво получается. Разве нельзя передавать email внутри route parameters: https://angular.io/guide/router#route-parameters
Тогда можно сделать так: router.navigate(['/register', { email: email }])
Причем, я бы хранил email не в path, а в query: /register?email=
> Позволяет ли Bearer token защититься от XSRF?
> Приходит ли вам на ум какая-нибудь слабая точка которую можно эксплуатировать?
Да. У тебя используются куки, и в них тоже есть токен. Предположим, у нас есть HTML версия, работающая без ангулара. Не получится ли, что злоумышленник сделает форму, которая будет имитировать отправку запроса этой HTML версией, и возникнет XSRF?
> Достаточно ли это строгая проверка на то является ли сущность экземпляром объекта localStorage?
Проверка достаточная, но непонятен ее смысл. Тест по идее должен проверять, что данные сохраняются и загружаются, а не какой именно класс там испольуется для хранения.
> В правильной ли директории находится эта обёртка? Это не совсем сущность, например как User, но места по лучше я не могу придумать для неё. Куда следует помещать обёртки?
Это сервис. Помещать - не знаю, в папку для сервисов? Но у меня есть ощущение, что в клиентском приложении файлы и так довольно хаотично разбросаны.
> Нужно ли делать проверку на каждую валидацию? Например на встроенные в Ангуляр валидаторы или на валидацию по регулярному выражению?
Нет смысла тестировать протестированное. Валидатор если и тестировать, то юнит-тестами, и они уже есть в сторонней библиотеке. Но надо протестировать, что:
- эти валидаторы действительно вызываются и проверяют данные
- их результат работы отображается
Потому, я думаю, нужно проверять не каждую валидацию, а просто вариант, когда поле заполнено неправильно. Можно даже сделать тест, где все поля заполнены неправильно, и проверить, что каждое из них помечено как ошибочное. Ну и тест, что есть какой-то минимум полей, без заполнения которых нельзя пройти регистрацию.
> Следующий шаг написания сервиса сообщений.
> Он будет реализован с помощь протокола WAMP и на платформе от https://crossbar.io/ , которая написана на Питоне, и для которой для аутентификации клиентов нужно тоже написать код на нём.
А чем Node не годится, кстати? Или ты решил изучить Питон и микросервисы? Ок, но на практике такой маленький проект может быть невыгодно разбивать на микросервисы - у них ведь есть и накладные расходы.
> Поэтому я сейчас буду изучать его, и возможно у меня появиться небольшие вопросы по нему. Могу я задать их в этом треде?
> Заодно и реактивное программирование подучу.
Конечно. Только пиши тогда, что именно ты добавил в проект и на что стоит посмотреть, а то он большой и с ходу это будет не очевидно.
Я пока все не успел проверить, увы, давай сначала с тем, что я написал, разберемся, а потом остальное глянем.
Чтобы скопировать чужие куки, тебе надо получить доступ к чужому компьютеру. А если он у тебя есть, ты можешь просто установить кейлоггер и перехватить пароль (или прочитать сохраненный в браузере пароль, если он сохранен). То есть новой уязвимости не открывается.
Дополнительно да, сайт может смотреть, с какого IP ты зашел, и если он сильно поменялся, то попросить подтвердить вход ссылкой на email или SMS.
ТестХаб или задачу на SPA, но ты от нее умрешь, ее пока никто не решил. Можешь попробовать сделать SPA, но попроще, чем описанный в ней, например: сайт, показывающий новости (наподобие мобильной версии Хабра). Можно смотреть список новостей, можно смотреть новости из какого-то хаба, можно читать новость, лайкать и добавлять в закладки. Все это в виде SPA, с умным кешированием, автоматическим обновлением и способностью временно работать без Интернета на кешированных данных. То есть Интернета нет, ты жмешь лайк, он запоминается и при появлении связи отправляется. Или ты жмешь новость, и если она есть в кеше, она открывается. Обновления отслеживаются, если появились новые новости, то ввеху появляется плашка с предложением обновить список и загрузить их. Число лайков обновляется если кто-то жмет лайк. Как тебе идея?
> А можно узнать, чем ты занимаешься?
Просто обычная удаленная работа.
> А вот, скажем, мне нужен не один объект, а нужно итеративно и с условиями получить много, тогда что? Нужно будет в контейнере хранить не объект, а класс, а потом внедрять этот класс и там создавать через new?
Можно хранить фабрику (класс, создающий нужные объекты), можно в контейнере сделать метод, который позволяет создавать объекты с параметрами. Но вообще, надо подумать, а нужны ли в таких объектах зависимости? Обычно, если это объект типа "Пользователь", у них нет зависимостей и их можно просто создавать через new. То есть такие "сущности" обычно в контейнер не кладут.
> То есть вообще всем управляет маппер, а сущности это такие пассивные ячейки памяти, к которым маппер обращается, и сущность сама по себе ничего делать не должна? И в контроллере правильно будет вызывать именно методы маппера?
Если ты читал мой урок https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md то знаешь, что есть ACtiveRecord, где сущности умеют сами себя сохранять в БД и Data Mapper, где они не умеют.
Второй подход имеет тот плюс, что ты в принципе можешь где-то (например, для теста) создавать сущности, не имея базы данных и использовать их. То есть код работы с БД отделен от кода сущностей и от кода, который использует эти сущности. Ты можешь в теории создать просто массив этих сущностей и весь остальной код даже не заметит, что у тебя не подключена БД (только в теории, на практике часто это не работает, так как код все равно пытается что-то искать и загружать из БД).
> А можно такую проверку делать в конструкторах?
Плохая идея, из конструктора нельзя возвращать значения, да и он не для этого. Но ты можешь сделать метод вроде beforeRequest() и обязательно его вызывать из фронт-контроллера.
> Я придумал вот что. Я в контроллере общем создал контейнер обычный массив data, куда записываю данные со всех уголков контроллеров и приходящие данные из моделей. В контроллерах-родителях задаю общие данные, в контроллерах специфичные, в методах контроллеров данные еще специфичнее. Данные записываются не через переопределение свойств, а через методы суперкласса.
Это плохо для чтения кода, так как чтобы понять, что передается в шаблон, надо изучать все эти места кода. Удобнее, когда передаваемые данные формируются в одном месте одним массивом.
> А можно узнать, чем ты занимаешься?
Просто обычная удаленная работа.
> А вот, скажем, мне нужен не один объект, а нужно итеративно и с условиями получить много, тогда что? Нужно будет в контейнере хранить не объект, а класс, а потом внедрять этот класс и там создавать через new?
Можно хранить фабрику (класс, создающий нужные объекты), можно в контейнере сделать метод, который позволяет создавать объекты с параметрами. Но вообще, надо подумать, а нужны ли в таких объектах зависимости? Обычно, если это объект типа "Пользователь", у них нет зависимостей и их можно просто создавать через new. То есть такие "сущности" обычно в контейнер не кладут.
> То есть вообще всем управляет маппер, а сущности это такие пассивные ячейки памяти, к которым маппер обращается, и сущность сама по себе ничего делать не должна? И в контроллере правильно будет вызывать именно методы маппера?
Если ты читал мой урок https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md то знаешь, что есть ACtiveRecord, где сущности умеют сами себя сохранять в БД и Data Mapper, где они не умеют.
Второй подход имеет тот плюс, что ты в принципе можешь где-то (например, для теста) создавать сущности, не имея базы данных и использовать их. То есть код работы с БД отделен от кода сущностей и от кода, который использует эти сущности. Ты можешь в теории создать просто массив этих сущностей и весь остальной код даже не заметит, что у тебя не подключена БД (только в теории, на практике часто это не работает, так как код все равно пытается что-то искать и загружать из БД).
> А можно такую проверку делать в конструкторах?
Плохая идея, из конструктора нельзя возвращать значения, да и он не для этого. Но ты можешь сделать метод вроде beforeRequest() и обязательно его вызывать из фронт-контроллера.
> Я придумал вот что. Я в контроллере общем создал контейнер обычный массив data, куда записываю данные со всех уголков контроллеров и приходящие данные из моделей. В контроллерах-родителях задаю общие данные, в контроллерах специфичные, в методах контроллеров данные еще специфичнее. Данные записываются не через переопределение свойств, а через методы суперкласса.
Это плохо для чтения кода, так как чтобы понять, что передается в шаблон, надо изучать все эти места кода. Удобнее, когда передаваемые данные формируются в одном месте одним массивом.
К чему ты это высрал?
>Почему "margin: 0;"
Выбирай не через body, а через .
{margin: 0; padding: 0;}
>Подписи к заголовкам
В <p>. Our Featured Works и Get in Touch это будет h2.
>Копирайт всегда
@ 2013 Webpaint. All Rights Reserved. - у тебя в футере будет, а область выше это секция (section).
>со значками соц.сетей
У тебя на фото 5 значков, полностью их вырезай из макета, прямо с кружком. Вставляешь каждую по очереди и делаешь их ссылкой. Помести их в блок div.soc-but или типа того, сделай его display:flex и justify-content: space-between.
>плавное всплывание
webref.ru/css/transition
>плавный переход
webref.ru/css/scroll-behavior
из того в чем разбираюсь норм это sql, pdo, html/css. А куда дальше ковырять не понимаю.
ООП раскрывается на больших проектах которые разрабатываются большими группами людей.
Самому понять трудно нахуя вообще нужны интерфейсы, и зачем по десять раз писать сначала обьвление в интерфейсе методов, потом их реализация. И другие вещи связанные в ООП.
Но на больших проектах оказывается что это наоборот экономит время и позволяет не повторять функционал по сто раз, передавать экземпляры класса как аргумент итд.
вот, и я нихуя не понимаю как мне это учить блждат.
Или ты хочешь сказать забить на ооп и только в теории понимание этого достаточно, пока не началась работа в сфере.
Насколько нормально держать сайт и базу данных на одном сервере, при условии, что у бд указан локалхост вместо интерфейса
Во-первых, если хочешь начать с самых-самых основ, то глянь учебник из ОП-поста, в нем есть глава про ООП, и там есть задачка про "Вектор". Стоит попробовать решить. Плюс в том, что там рассматриваются совсем основы ООП, которые ты может быть пропустил.
Если хочешь что-то посложнее, то к твоим услугам задача про ООП-гостинцу и ООП-будильник тут:
- ООП-Будильник: https://phpclub.tech/pr/res/1232710.html#1263399
- ООП-Гостиница: https://phpclub.tech/pr/res/1082507.html#1097078
Дальше, если ты все это сделал или все идеально знаешь, гугли и изучай инкапсуляцию, SOLID, DI. Кстати, у меня есть понятный урок по DI: https://github.com/codedokode/pasta/blob/master/arch/di.md
А еще по интерфейсам: https://github.com/codedokode/pasta/blob/master/php/interfaces.md
Ну и если есть желание, почитай другие уроки тут: https://github.com/codedokode/pasta/ - там есть и про MVC и про другие трендовые слова.
Ну и заодно почитай статью про выпечку хлеба: https://habr.com/ru/post/153225/
Если захочется увидеть применение ООП на практике, изучи библиотеку Symfony forms, разбери ее код и нарисуй диаграмму классов и связей между ними (кто кого наследует, кто кого создает).
Если ты все это прочтешь и изучишь, то ты будешь намного лучше разбираться в ООП и увереннее себя чувствовать. Я готов ответить на уточняющие вопросы, а также подсказать по задачкам или проверить решения.
Изучать ООП надо с основ. Если ты их не освоил, а берешься за паттерны, фреймворки, то конечно ты будешь путаться.
о, спасибо большое анон, отлично все расписал. Займусь сегодня, благодарю, буду вкидывать решения. Еще раз спасибо.
не могу вывести названия элементов из дб в список в php файл
http://beta.phpformatter.com#.XIex79kY6AQ.link
выдает ошибку
Warning: mysqli_fetch_assoc() expects parameter 1 to be mysqli_result, null given in мой путь до файла
файл у меня на сайте на локалхосте через апач (опенсервер)
Кстати про симфони, в объектах-наследниках Constraint открытые публичные свойства. Получается бывают ситуации, когда инкапсуляцией можно пренебречь?
https://github.com/symfony/symfony/blob/4.2/src/Symfony/Component/Validator/Constraints/Length.php
я дебич
короче вот на пиках
первый пик подключение к дб
второй пик это я так пытался вывести тайтлы из дб в столбик
У тебя регулярка неправильная, [ ] задают список возможных символов, там не работают всякие | и &. Если тебе нужно условие "+7 или 8", то будет `(\+7|8)`.
И ещё, в классе работы с БД очевидно нужна функция поиска студентов. Например $studentMapper->find($searchQuery, $orderBy, $orderDir, $offset, $limit);
Что если в find() передавать не кучу параметров, а объект с параметрами класса SearchOptions? Узнал о таком способе решения тут https://www.refactoring.com/catalog/introduceParameterObject.html
Вот такая функция:
[code lang="php"] %
public function func1()
{
$x = 0;
try {
///
}
catch(SomeException $e) {
switch ($e->getCode()) {
case 1:
throw new SomeException("message_1", 11, $e);
break;
case 2:
throw new SomeException("message_2", 22, $e);
break;
case 3:
try {
///
}
catch (SomeException $g) {
// brainfuck
$g->previous = $e;
throw new SomeException('message_5', 55, $g);
}
throw new SomeException('message_3', 33, $e);
break;
case 4:
///
break;
}
}
return $x;
}
% [/code]
Сложность в блоке с brainfuck. Так как написано - не получится хотя бы потому что и поля previous нету. Создать копию $g вроде как нельзя - перенесётся message и code, но не trace и остальное.
Как же сцепить исключения таким образом чтобы все данные нормально сохранились? Может тут какая-то стилистическая или структурная ошибка?
Вот такая функция:
[code lang="php"] %
public function func1()
{
$x = 0;
try {
///
}
catch(SomeException $e) {
switch ($e->getCode()) {
case 1:
throw new SomeException("message_1", 11, $e);
break;
case 2:
throw new SomeException("message_2", 22, $e);
break;
case 3:
try {
///
}
catch (SomeException $g) {
// brainfuck
$g->previous = $e;
throw new SomeException('message_5', 55, $g);
}
throw new SomeException('message_3', 33, $e);
break;
case 4:
///
break;
}
}
return $x;
}
% [/code]
Сложность в блоке с brainfuck. Так как написано - не получится хотя бы потому что и поля previous нету. Создать копию $g вроде как нельзя - перенесётся message и code, но не trace и остальное.
Как же сцепить исключения таким образом чтобы все данные нормально сохранились? Может тут какая-то стилистическая или структурная ошибка?
Код довольно большой, и не очень понятно, что ты хочешь сделать.
Связь исключений используется. чтобы показать, что одно исключение было вызвано другим. Ну например, чтобы показать, что исключение DataLoadException вызвано исключнием HTTPConnectException (мы не смогли загрузить данные, так как в ходе их получения не удалось соединиться с удаленным сервером). Пример:
function getData($x)
{
try {
$a = loadDataFrom('https://a.example.com/1');
$b = loadDataFrom('https://b.example.com/1');
return $a + $b;
} catch (HTTPConnectException $e) {
throw new DataLoadException("Не удалось загрузить данные из-за проблем связи с сервером", 0, $e);
} catch (AccessDeniedException $e) {
throw new DataLoadException("Не удалось загрузить данные из-за того, что доступ к ним запрещен", 0, $e);
}
Ты же в коде ловишь одно исключение. и создаешь на его основе исключение того же класса. Это выглядит как минимум странно.
Я пробую:
([0-9]{10}) ([\w]{0,}) - цифры и знаки в разных скобках.
([0-9]{10} [\w]{0,}) - цифры и знаки в одних скобках.
[0-9 [\w]{0,} ]{10} - знаки внутри скобок с цифрами.
Как я понял, то, что внутри (этих) скобок считается отдельным блоком, куда можно накидать кучу параметров, написанных [тут].
Но почему- то у меня не работает это никак.
Объясните, как, блять, тут делать, чтоб нормально было.
Читать нормальные статьи где объясняют регулярки.
>>53705 (OP)
Вы все еще так же сидите на РПР? Это же каким дегенератом нужно быть.
всё мне пизда пора спать идти
Подскажите, блять, наконец уже, как сделать поиск символов между цифр.
Есть охуенная идея:
Накопипастить ([\W]{0,})([0-9]) десять раз, но это же пиздец как тупо. Уверен, есть другой способ, просто я слишком туп, чтоб его найти.
Подскажите, блэт.
У программиста была проблема которую он предпочел решить регулярными выражениями.
Теперь у него две проблемы.
- напиши выражение, которое находит ровно 1 цифру и любое число доп. символов за ней
- возьми его в круглые скобки и поставь после них {x}
Чтоб не тратить время на изучение регулярок?
Чот вообще не то. И неверные номера тоже зацепило.
Мы развиваемся и эволюционируем. Осталось понять, как искать символы между цифрами.
Чот я прикола не понял.
Чому он пропускает некоторые номера? Не могу понять, по какому признаку он их выбирает.
В чем причина моих мозгах, аноны?
Смотри внимательнее на проблемные номера. В одном из них в начале идет пробел, но твоя регулярка это не предусмотрела. Попробуй мысленно сопоставить регулярку с текстом и увидеть, где она не соответствует номеру.
Также, вместо {0,} можно писать просто звездочку.
Также, вместо \W лучше писать конкретные символы - минусы, скобки, пробелы, а то он что угодно под это выражение подгонит.
Спасибо тебе, помог очень сильно. Долго думал, как быть с пробелом между + и 7, но догадался(!) сделать там просто пробел.
> Также, вместо {0,} можно писать просто звездочку.
> Также, вместо \W лучше писать конкретные символы - минусы, скобки, пробелы, а то он что угодно под это выражение подгонит.
За вот эти советы отдельное спасибо, буду знать и применять.
Сейчас я очевидно горожу свои костыльные недо-велосипеды.
Может стоит взять какой нибудь фреймворк за основу? Сразу начинать с большого, я думаю о laravel. Или все таки какой то мини-фреймворк? Я правда о таких не знаю.
Основная задача все таки - посмотреть как это должно быть реализованно.
MVC для меня сейчас обязательно, так как я с начала изучения пишу классами на MVC.
Рекомендую 2й уровень курса профит, там как раз про mvc. Очень доступно объясняет.
Могу предложить простой урок про основы MVC: https://github.com/codedokode/pasta/blob/master/arch/mvc.md
Затем можно посмотреть какой-нибудь фреймворк вроде Yii2 (попроще) или Laravel, Symfony (посложнее). Это как раз подходит для варианта "посмотреть, как реализовано".
Для ролей обычно принято использовать RBAC.
Бля, анон, как же хочется помочь тебе, но мне пиздец как впадлу даже твой скрин смотреть(
Гугли уроки по регуляркам от WebForMySelf
мимо бог регулярок
> WebForMySelf
И на этом спасибо. Чем больше материалов, тем лучше. Где- то одни фичи рассказывают, где- то - другие.
Там все рассказывают по регуляркам, больше тебе нихуя не надо
Технически никакой и на скорость выполнения не влияет, просто синтаксически наглядней, особенно если будет огромный запрос, то сразу видно будет какие таблицы юзаются, а в условии будут примитивные конструкции
Выборка из двух таблиц === inner join.
Есть ещё outer join (с пустыми клеточками) и редко бывает нужен self join (поиск совпадений в таблицах).
Алсо, не забываем о том, что в джойнах есть ключевое слово "ON", с ним код легче читать.
Я сделал как ^(\+?(8|7)\d{10})$ Но как сделать так, чтобы между цифрами могли быть пробелы, или другие символы?
(\+?7|8)([()\-\s]\d[()\-\s]){10}
([()\-\s]\d[()\-\s]){10}
Обязательно одно число от 0 до 9 (\d). Вокруг него может быть, а может и не быть любое количество пробелов, тире, скобок.
Помог как смог.
Я въехал в регулярку, спасибо!, как сам не допер до этого...
Спасибо, вкатился.
>Могу предложить простой урок про основы MVC: https://github.com/codedokode/pasta/blob/master/arch/mvc.md
К сожалению, там упущен один очень важный момент, а именно по поводу тонкого контроллера и толстой модели.
Все установил через консоль, как описано здесь: https://howtoubuntu.org/how-to-install-lamp-on-ubuntu
Однако когда закидываю свой test.php в /var/www/html, то в результате браузер открывает пустую страницу. Смотрел туториалы рандомных индусов - у них всё отображается.
HTML файлы отображаются без проблем.
В чём может быть проблема? Выручьте идеями, пожалуйста.
Вроде теорию прочитал - понял, но на практике не могу применять, что касаемо DDD.
Спасибо за вариант! Изменил файл, проблема осталась, к сожалению.
Выходит абсолютно пустая страница.
Что ты изменил :) ?
Какая версия пхп у тебя под апачем работает?
Покажи свой test.php
Что ты в адресной строке набираешь?
Не заставляй ванговать
Еще ты забыл сделать sudo service apache2 restart
Окей, вот порядок моих действий:
1) sudo vim /etc/php/7.2/apache2/php.ini
2) нашёл строчку display_errors = Off, поменял на On (:wq, конечно же);
3) sudo service apache2 restart
Скрипт простой:
<?php
echo "This is supposed to be displayed!";
?>
Просто без задней мысли вбиваю:
file:///var/www/html/test.php
Я просто очень большой нуб, поэтому я просто создал HTML файл в /var/www/html
Назвал его test.html, положил туда простой контент и кликнул дважды. Затем поменял test.html в строке на test.php и ничего не получил.
Почему я так сделал? Потому что на винде localhost/myfolder выводит список файлов, которые без проблем запускаются по нажатию. Тут такое не работает.
Моя логика действий само собой вызывает улыбку, но вот как-то так.
Анон, большое тебе спасибо. Я не перестаю удивляться, как этот тред умудряется привлекать столько отзывчивых анонов.
Есть переменная (массив) $_REQUEST
Довольно быстро. Но буден непривычно выделять переменные и ставить ; в конце команд.
А так - такой же простой язык, как и питхон.
поначалу орал люто, потому что вместо echo писал print().
Какую функцию использовать чтобы в zakaz.php отображался свой элемент из моей бд для каждого элемента цикла?
Следующие советы подразумевают что ты хочешь поебаться с ООП.
Рефакторишь своих животных чтобы они наследовались от общего базового класса. В базовом классе реализешь генерацию id, а также метод getYeld() - этот метод возвращает массив с дневными профитами с животного, в твоем случае это будет что-то типа ['milk' => 8] с коровы и ['egg' => 1] с курицы. В массиве можно возвращать несколько разных продуктов (если ты хардкорный фермер можно еще навоз например добавить - это важный ресурс). Ключи массива сделать костантами.
В классе фермы делаешь массив для животных. Для добавления животных делаешь метод addAnimal($class, $count) - указываешь класс животного и количество, ферма добавляет нужное количество в массив животных. Делаешь массив для хранения ресурсов. Добавляешь к ферме метод getYeld() который проходит по массиву животных, вызывает у каждого животного getYeld(), полученые результаты суммирует с массивом с ресурсами - тут нужно внимательно проследить чтобы результаты для каждого ключа суммировались с теми что уже есть в массиве, а не перезаписывались новыми.
пиши в sql запрос SELECT*FROM stor WHERE 'столбец цена' AS price AND 'столбец количество' AS quan и т.д , все отсально кроме запроса вроде корректное
Слушать Бугаенко - антипаттерн
Там нет геттеров и сеттеров.
сделай index.php
помогите пожайлуста
Аноы, чет туплю.
Есть у меня класс Klass, у этого класса есть статический метод Start(){...}
создаю новый объект $obj = new Klass();
И вызываю статический метод из объекта не статическим способом:
$obj->Start(); и так работает
Какой жопой я читал документацию? Разве так можно?
Объявление свойств и методов класса статическими позволяет обращаться к ним без создания экземпляра класса. Свойство класса, объявленное как статическое, не может быть доступно посредством экземпляра класса (но статический метод может быть вызван).
http://php.net/manual/ru/language.oop5.static.php
Ок, понял, спасибо.
Снкачала убедись что file_get_contents правильно читает файл, потом разбивай джон на части пока не найдешь строчку с проблемой
Хз что сказать.
Имей ввиду что йава ждуниоров фильтруют сложностью тестового.
Это ёба задание на 3-х листах английского текста на неделю, где специально выбирают ёба технологии про которые никто не знает (сразу отваливаются вкатальщики по роликам с ютуба и остаются тру пацаны которые умеют читать доки по английски и гуглить в стековерфлоу)
Если у тебя есть зелёный гит и что нить в сети на пхп то ты уже молодец.
>Вот что мне в ноде кажется неудобным - так это то, что каждый в Node.JS организует код как хочет. Вот, например, код: bodyParser.json() - непонятно, почему он сделан в виде вызова функции? Если он есть в единственном экземпляре, можно просто было бы использовать свойство bodyParser.jsonParser. Если там каждый раз надо создавать новый объект, можно было бы экспортировать класс: new bodyParser.JsonParser. А тут надо вызвать функцию, причем в ее названии нет ни малейшего намека на то, что это функция-конструктор (хоть бы createJsonParser ее назвали). Понятно, что это сторонняя библиотека, но все же не привычно.
А ещё с TypeScript некоторые возможности теряются https://github.com/someApprentice/Crypter/blob/master/api/api.ts#L8-L9
Моё мнение что на JS не должно быть никакого кода, кроме браузерного, и максимум выдаваться SSR страницы. А серверный код должен написан на языках получше, например PHP.
>Комментариев, конечно мало. Наверно стоило бы хотя бы в начале файла писать, что это за модуль и для чего.
Ой-ой. Ошибка. Нужно было предупредить, что я комментарии оставлял "для себя" чтобы понимать что это и откуда взялось, и что это скорее всего нужно исправить.
К сожалению, инструменты для написания кода на JS ещё не совершены, и не справляются с тривиальными задачами, по моему приземлённому мнению.
Я перенёс эти проблемы в issues задачи https://github.com/someApprentice/Crypter/issues проверять это не нужно, но если у вас есть подсказка, то я с радостью её приму
Исправлю эту ошибку написав хорошие комментарии. Только, я не понимаю зачем, по-моему только сервису-обёртке хранилища можно, потому что это весьма не типичный подход. В остальном, разве это не очевидно что чем занимается?
У меня плохо с английским и написание комментариев утяжелит и замедлил разработку. Насколько важно их писать?
>Тут ссылка на отсутствующий в репозитории файл - это нормально? Этот файл ведь из чего-то генерируется, и нельзя ли тут подключать исходный файл. Или хотя бы комментарий написать:
>
>https://github.com/someApprentice/Crypter/blob/master/server.ts#L30
>> const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main.js');
>
Да, эта папка генерируется после сборки приложения npm run build. Думаю тут тоже можно добавить комментарий.
>И проблема тут в плохом названии и отсутствии комментариев. Нужно было назвать функцию, например, promiseToCallback, handlePromise или adaptPromise и снабдить комментарием
Насчет комментария это да, их стоит добавить. Но я не понимаю почему вы пишите, что эта функция для того чтобы обрабатывать промисы, это не так. Эта функция нужна, чтобы обработать асинхронный обработчик запросов, который Эксрпресс не поддерживает (пока что?).
// Это не сработает
express.get('/route', async (...) => {
await promise = ...
});
https://github.com/someApprentice/Crypter/issues/12
>> if (err) throw new err;
>
>А не throw err? err - это функция-констуктор или объект исключения?
Объект исключения. Исправлю.
>> res.cookie('uuid', uuid, ...
>> res.cookie('email', email, ...
>> res.cookie('name', name, ...
>
>А зачем тут столько кук? Они же уже содержатся внутри токена. А в твоем варианте пользователь может их подменить, и если ты доверяешь им на сервере, то получается уязвимость. Ну и вообще, нужно ли ставить куки в SPA? Это же серверное API, оно наверно не должно ставить куки. А должно отдавать JSON с токеном.
Они нужны для отрисовки данных при серверном рендеренге. Как я уже писал, куки нужны только для условий отображений https://github.com/someApprentice/Crypter/blob/master/src/app/main/main.component.html#L3 . Возможно uuid можно убрать, но почему нет?
Здесь нет уязвимости, т.к. сервер (API) не использует куки.
Сделать приложение работающие без поддержки JS - я отказываюсь от этой идеи.
>> json(<U> { uuid, email, name, jwt });
>
>Не очень понятно, зачем здесь влеплен Type Assertion? Функции json() он не нужен, это для проверки, что объект соответствует интерфейсу User?
Да, именно так. Это лишнее?
>Еще, кстати, вопрос: а при регистрации проверяется уникальность email?
Нет, я забыл её сделать. Исправлю. И наверно нужно ещё это в схему БД добавить.
>Также, я подозреваю, что удобнее может быть сделать отдельную функцию для валидации, чем использовать только встроенные возможности sequelize.
Почему нужно отдельную функцию валидации? Всё же и так в одну строчку делается. https://github.com/someApprentice/Crypter/blob/master/api/api.ts#L50
>По поводу JWT - я с ним не работал (но с интересом прочитал про него), но у меня уже есть сомнения: почему при подписании токена мы передаем все значения из модели? Получается, что токен перестанет работать в следующих ситуациях:
>
>- пользователь меняет имя
>- пользователь меняет email
>- пользователь меняет пароль
>- мы добавили новые поля в модель пользователя
>
>На мой взгляд, токен должен переставать работать только при смене пароля (на случай, если пользователь боится, что у него украли токен и меняет пароль, старый токен должен перестать действовать). Соответственно, в подписи должны участвовать только uuid и хеш пароля. Или я ошибаюсь?
>
>И, кстати, а почему был выбран JWT? Какие-то еще варианты рассматривались?
>
>Также, я не уверен, а правильно ли токен создавать прямо в обработчике? Не логичнее ли сделать функцию такого вида:
>
>async function createToken(u: User): Promise<string>
>
>Чтобы мы могли бы создавать токены в любом месте API?
>почему при подписании токена мы передаем все значения из модели?
Да, я понимаю этот вопрос. Я думал, что можно сделать так, получать из Bearer token этот токен, и получать из него всё что нужно (имейл/что-нибудь ещё), но это, скорее всего нигде не пригодиться, так что это ошибка. Я согласен что достаточно хранить в нём только uuid и хэш.
>И, кстати, а почему был выбран JWT? Какие-то еще варианты рассматривались?
Он популярен. Чем популярнее технология, тем больше решений вопросов связанной с ней. В прочитанных мной статьях по JS приводят примеры именно с ним.
>Также, я не уверен, а правильно ли токен создавать прямо в обработчике? Не логичнее ли сделать функцию такого вида:
>
>async function createToken(u: User): Promise<string>
>
>Чтобы мы могли бы создавать токены в любом месте API?
Я скажу на чистоту мой взгляд на это - Я не понимаю зачем нужно выносить такие функции в отдельные. Это функция понятна любому разработчику и он с первого взгляда поймет что происходит в ней. Почему нужно делать обёртку ради человекопонятного названия функции?
Я правда не понимаю, вот как эта функция будет выглядеть (она кстати не работает на промисах):
function createToken(u: User, cb) {
token.sign(u.dataValues, JWT_SECRET, cb(...))
}
Но мы и так передаём в неё пользователя и коллбэк, и больше ничего не делаем. Почему нужно её выносить?
>> let user = new User({ email });
>> await user.validate({ skip: difference(Object.keys(User.rawAttributes), ['email']) });
>
>Это, конечно, выглядит немного корявым способом проверить email. Наверно, можно как-то получить функцию валидации отдельного поля?
Это баг библиотеки sequelize-typescript. Чтобы получить валидацию отдельного поля нужно вместо опции skip добавить fields.
>> router.get('/email/:email'
>
>Это не очень соответствует REST, так как URL в нем это указатель на "ресурс" (какую-то сущность), и при отсутствии ресурса ты должен отдавать 404. А ты всегда отдаешь 200. Я бы сделал тогда просто /email-exists?email=....
>Я бы сделал тогда просто /email-exists?email=....
Всё исправлю. Здесь тоже в случае отсутствия нужно отдавать 404?
>Не очень понятно, зачем ты тут вызвал валидацию и не использовал результат.
Хм, возможно валидацию и не стоило вызывать. Почему нет?
>и не использовал результат
Результат валидации либо вбрасывает ошибку, либо возвращает null(?). Эта ошибка обрабатывается в обработчике ошибок https://github.com/someApprentice/Crypter/blob/master/api/errorHandler.ts#L8-L10
>И еще важный момент. У тебя нет документации по API, а это плохо. Если фронтенд-разработчик хочет использовать твое API, где он прочтет документацию? Код что ли разбирать?
>
>В наши дни наверно самый популярный формат документирования - это OpenAPI 3.0 (бывший Swagger). Ты описываешь свое API в виде YAML файла, и получаешь на выходе такую красивую штуку: https://petstore.swagger.io/ или такую: https://docs.discourse.org/
>
>Естественно, логично не писать YAML руками, а генерировать его из комментариев в коде. Так ты получишь и комментированный код, и документацию для разработчиков.
Понял, буду работать над этим. Написать комментарии для документации API мне кажется более понятной задачей, чем писать комментарии для бушующих разроботчиков.
Так вы имели ввиду комментраии для модулей API, а не фронтенд приложения?
>Вот что мне в ноде кажется неудобным - так это то, что каждый в Node.JS организует код как хочет. Вот, например, код: bodyParser.json() - непонятно, почему он сделан в виде вызова функции? Если он есть в единственном экземпляре, можно просто было бы использовать свойство bodyParser.jsonParser. Если там каждый раз надо создавать новый объект, можно было бы экспортировать класс: new bodyParser.JsonParser. А тут надо вызвать функцию, причем в ее названии нет ни малейшего намека на то, что это функция-конструктор (хоть бы createJsonParser ее назвали). Понятно, что это сторонняя библиотека, но все же не привычно.
А ещё с TypeScript некоторые возможности теряются https://github.com/someApprentice/Crypter/blob/master/api/api.ts#L8-L9
Моё мнение что на JS не должно быть никакого кода, кроме браузерного, и максимум выдаваться SSR страницы. А серверный код должен написан на языках получше, например PHP.
>Комментариев, конечно мало. Наверно стоило бы хотя бы в начале файла писать, что это за модуль и для чего.
Ой-ой. Ошибка. Нужно было предупредить, что я комментарии оставлял "для себя" чтобы понимать что это и откуда взялось, и что это скорее всего нужно исправить.
К сожалению, инструменты для написания кода на JS ещё не совершены, и не справляются с тривиальными задачами, по моему приземлённому мнению.
Я перенёс эти проблемы в issues задачи https://github.com/someApprentice/Crypter/issues проверять это не нужно, но если у вас есть подсказка, то я с радостью её приму
Исправлю эту ошибку написав хорошие комментарии. Только, я не понимаю зачем, по-моему только сервису-обёртке хранилища можно, потому что это весьма не типичный подход. В остальном, разве это не очевидно что чем занимается?
У меня плохо с английским и написание комментариев утяжелит и замедлил разработку. Насколько важно их писать?
>Тут ссылка на отсутствующий в репозитории файл - это нормально? Этот файл ведь из чего-то генерируется, и нельзя ли тут подключать исходный файл. Или хотя бы комментарий написать:
>
>https://github.com/someApprentice/Crypter/blob/master/server.ts#L30
>> const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main.js');
>
Да, эта папка генерируется после сборки приложения npm run build. Думаю тут тоже можно добавить комментарий.
>И проблема тут в плохом названии и отсутствии комментариев. Нужно было назвать функцию, например, promiseToCallback, handlePromise или adaptPromise и снабдить комментарием
Насчет комментария это да, их стоит добавить. Но я не понимаю почему вы пишите, что эта функция для того чтобы обрабатывать промисы, это не так. Эта функция нужна, чтобы обработать асинхронный обработчик запросов, который Эксрпресс не поддерживает (пока что?).
// Это не сработает
express.get('/route', async (...) => {
await promise = ...
});
https://github.com/someApprentice/Crypter/issues/12
>> if (err) throw new err;
>
>А не throw err? err - это функция-констуктор или объект исключения?
Объект исключения. Исправлю.
>> res.cookie('uuid', uuid, ...
>> res.cookie('email', email, ...
>> res.cookie('name', name, ...
>
>А зачем тут столько кук? Они же уже содержатся внутри токена. А в твоем варианте пользователь может их подменить, и если ты доверяешь им на сервере, то получается уязвимость. Ну и вообще, нужно ли ставить куки в SPA? Это же серверное API, оно наверно не должно ставить куки. А должно отдавать JSON с токеном.
Они нужны для отрисовки данных при серверном рендеренге. Как я уже писал, куки нужны только для условий отображений https://github.com/someApprentice/Crypter/blob/master/src/app/main/main.component.html#L3 . Возможно uuid можно убрать, но почему нет?
Здесь нет уязвимости, т.к. сервер (API) не использует куки.
Сделать приложение работающие без поддержки JS - я отказываюсь от этой идеи.
>> json(<U> { uuid, email, name, jwt });
>
>Не очень понятно, зачем здесь влеплен Type Assertion? Функции json() он не нужен, это для проверки, что объект соответствует интерфейсу User?
Да, именно так. Это лишнее?
>Еще, кстати, вопрос: а при регистрации проверяется уникальность email?
Нет, я забыл её сделать. Исправлю. И наверно нужно ещё это в схему БД добавить.
>Также, я подозреваю, что удобнее может быть сделать отдельную функцию для валидации, чем использовать только встроенные возможности sequelize.
Почему нужно отдельную функцию валидации? Всё же и так в одну строчку делается. https://github.com/someApprentice/Crypter/blob/master/api/api.ts#L50
>По поводу JWT - я с ним не работал (но с интересом прочитал про него), но у меня уже есть сомнения: почему при подписании токена мы передаем все значения из модели? Получается, что токен перестанет работать в следующих ситуациях:
>
>- пользователь меняет имя
>- пользователь меняет email
>- пользователь меняет пароль
>- мы добавили новые поля в модель пользователя
>
>На мой взгляд, токен должен переставать работать только при смене пароля (на случай, если пользователь боится, что у него украли токен и меняет пароль, старый токен должен перестать действовать). Соответственно, в подписи должны участвовать только uuid и хеш пароля. Или я ошибаюсь?
>
>И, кстати, а почему был выбран JWT? Какие-то еще варианты рассматривались?
>
>Также, я не уверен, а правильно ли токен создавать прямо в обработчике? Не логичнее ли сделать функцию такого вида:
>
>async function createToken(u: User): Promise<string>
>
>Чтобы мы могли бы создавать токены в любом месте API?
>почему при подписании токена мы передаем все значения из модели?
Да, я понимаю этот вопрос. Я думал, что можно сделать так, получать из Bearer token этот токен, и получать из него всё что нужно (имейл/что-нибудь ещё), но это, скорее всего нигде не пригодиться, так что это ошибка. Я согласен что достаточно хранить в нём только uuid и хэш.
>И, кстати, а почему был выбран JWT? Какие-то еще варианты рассматривались?
Он популярен. Чем популярнее технология, тем больше решений вопросов связанной с ней. В прочитанных мной статьях по JS приводят примеры именно с ним.
>Также, я не уверен, а правильно ли токен создавать прямо в обработчике? Не логичнее ли сделать функцию такого вида:
>
>async function createToken(u: User): Promise<string>
>
>Чтобы мы могли бы создавать токены в любом месте API?
Я скажу на чистоту мой взгляд на это - Я не понимаю зачем нужно выносить такие функции в отдельные. Это функция понятна любому разработчику и он с первого взгляда поймет что происходит в ней. Почему нужно делать обёртку ради человекопонятного названия функции?
Я правда не понимаю, вот как эта функция будет выглядеть (она кстати не работает на промисах):
function createToken(u: User, cb) {
token.sign(u.dataValues, JWT_SECRET, cb(...))
}
Но мы и так передаём в неё пользователя и коллбэк, и больше ничего не делаем. Почему нужно её выносить?
>> let user = new User({ email });
>> await user.validate({ skip: difference(Object.keys(User.rawAttributes), ['email']) });
>
>Это, конечно, выглядит немного корявым способом проверить email. Наверно, можно как-то получить функцию валидации отдельного поля?
Это баг библиотеки sequelize-typescript. Чтобы получить валидацию отдельного поля нужно вместо опции skip добавить fields.
>> router.get('/email/:email'
>
>Это не очень соответствует REST, так как URL в нем это указатель на "ресурс" (какую-то сущность), и при отсутствии ресурса ты должен отдавать 404. А ты всегда отдаешь 200. Я бы сделал тогда просто /email-exists?email=....
>Я бы сделал тогда просто /email-exists?email=....
Всё исправлю. Здесь тоже в случае отсутствия нужно отдавать 404?
>Не очень понятно, зачем ты тут вызвал валидацию и не использовал результат.
Хм, возможно валидацию и не стоило вызывать. Почему нет?
>и не использовал результат
Результат валидации либо вбрасывает ошибку, либо возвращает null(?). Эта ошибка обрабатывается в обработчике ошибок https://github.com/someApprentice/Crypter/blob/master/api/errorHandler.ts#L8-L10
>И еще важный момент. У тебя нет документации по API, а это плохо. Если фронтенд-разработчик хочет использовать твое API, где он прочтет документацию? Код что ли разбирать?
>
>В наши дни наверно самый популярный формат документирования - это OpenAPI 3.0 (бывший Swagger). Ты описываешь свое API в виде YAML файла, и получаешь на выходе такую красивую штуку: https://petstore.swagger.io/ или такую: https://docs.discourse.org/
>
>Естественно, логично не писать YAML руками, а генерировать его из комментариев в коде. Так ты получишь и комментированный код, и документацию для разработчиков.
Понял, буду работать над этим. Написать комментарии для документации API мне кажется более понятной задачей, чем писать комментарии для бушующих разроботчиков.
Так вы имели ввиду комментраии для модулей API, а не фронтенд приложения?
>Еще, кстати, мне интересно, можно ли как-то использовать описания моделей (например, User) для автоматической генерации документации о формате JSON-ответа, и можно ли использовать описания для автоматического тестирования (тест берет Swagger-описание, проходится по всем методам и пробует их вызывать и проверяет, что ответ соответствует описанию).
Я ничего из этого не знаю, но постараюсь разобраться.
>>61815
>Тут код теста почти повторяет код обработчика. Это не годится, так как ты можешь сделать одинаковые ошибки и там, и там.
А где у меня код повторяется? То что я токен генерирую? А как я узнаю какой токен будет иначе?
>Если мы тестируем функцию вычисления корня, то ее проверяют возведением в квадрат, а не аналогичным вычислением.
Не понимаю сравнение.
var n = 5
expect(sqrt(n^2)).toEqual(n)
Я же тоже "возвожу" данные пользователя в токен и проверяю совпадает ли возвращённый ответ, а именно кукисы, с ним.
var user = { ... }
token(user, (jwt) => {
expect(cookies['jwt']).toEqual(jwt)
});
Ну да, теперь вижу причину. Нужно проверить что пришедший токен в дешифрованном виде равен данным пользователя?
>В твоем случае надо сформулировать требования к функции регистрации. На мой взгляд, они такие:
>
>- она должна создавать аккаунт на сервере, под которым можно залогиниться
Я забыл сделать проверку, что пользователь добавился в БД и данные правильные. Это достаточное условие? Залогинивание проверяется в предназначенном для неё тесте, и там как раз сначала заполняется запись в БД.
>- она должна возвращать токен, с которым можно использовать защищенное API
Нужно сделать проверку как я писал выше, что пришедший токен в дешифрованном виде равен данным пользователя или действительно нужно проверять каким-то методом API?
>- нельзя дважды зарегистрироваться с одинаковым email
Сделаю. то нужно сделать прописав ассоциацию Sequelize на уникальность emai'а. Тогда при повтороном имейле будет вбрасываться ошибка и тест провалится.
>- нельзя регистрироваться без указания обязательных аргументов
Сделаю.
>- и, может, еще какие-то требования по валидации. Например, что при ошибке возвращается объект определенного вида
Сделаю.
>Для каждого требования мы придумываем свой способ проверки. Создание аккаунта на сервере можно проверить, попробовав залогиниться в него. Токен можно проверить, попробовав вызвать защищенное API, например "получить сведения о себе".
>Создание аккаунта на сервере можно проверить, попробовав залогиниться в него.
>Токен можно проверить, попробовав вызвать защищенное API, например "получить сведения о себе".
Почему? Разве не достаточно проверить что запись добавилась в базу? Разве не залогинивание не должно проверяться в своём собственном тесте?
А токен не достаточно проверить просто дешифровав его (как раз метод и называется verify(token))?
>Так как ты тестируешь код, использующий БД, тебе надо предусмотреть, чтобы база была перед тестом в каком-то определенном состоянии (полностью пустая, например, или с какими-то тестовыми данными). Один из вариантов - в самом начале почистить БД/загрузить дамп, а каждый тест обернуть в транзакцию, которая откатывается. Это может быть быстрее, чем загружать дамп каждый раз.
В данный момент для каждого теста БД должна быть в чистом состоянии и после каждого теста база очищается это можно улучшить добавив перед каждым тестом очистку (методо beforeEach()).
>На практике, может быть полезно разместить тестовую БД на ramfs, чтобы избежать вообще обращений к диску и ускорить прогон тестов.
Тяжело понять. Я с таким не сталкивался. Вижу что это как бы "эмулирует" жесткий диск в оперативной памяти. Как для этого настроить psql? Как нужно будет для это настраивать найстройки соеденения к БД в Ноде? Слишком много вопросов для такой маленькой задачи. Возможно позже, с ростом приложения это будет актуально. Если что взял этот подход на вооружение и вернусь к нему, когда тесты будут делаться медленно.
>Можно также попробовать добавить тест для обработчика ошибок (вызвать специальный URL, который выбрасывает исключение). Иначе ты можешь его отключить/сломать и не заметить.
Сделаю.
>> Is there any way to declare all classes at once for a webpack output file?
>
>Я думаю, это не имеет отношения к webpack, так как ты должен в sequelize передать список классов. А для этого их надо импортировать. И вебпак увидит импорты.
В sequlize-typescript можно указать директорию с моделями, но webpack транспилирует всё в один файл, и это ломается.
https://www.npmjs.com/package/sequelize-typescript#configuration
https://github.com/someApprentice/Crypter/issues/7
>По клиентской стороне:
>
>Ты в API при логине ставишь куки вручную, но разве это правильно? По идее, это должно работать так:
Ставлю куки вручную в клиентской стороне? Нет, у меня такого нет. В клиентской стороне куки только берутся из реквеста, чтобы пререндерить страницу с залогиненым пользователем или нет. Для этого они и нужны. localStorage ставиться в ручную(?) в клиентской стороне при залогинивании https://github.com/someApprentice/Crypter/blob/master/src/app/auth/login/login.component.ts#L48-L51 . Не могли бы вы уточнить строку где я это делаю, я не могу понять последующие описание проблемы.
>https://github.com/someApprentice/Crypter/blob/master/src/app/app.component.html
>
>Тут мне не кажется 100% надежной проверка залогиненности. Тут же нет проверки, что это валидный токен.
Так это же отображение. Если что-то не так, то при запросе к API выдастся соответствующая ошибка. Вряд ли, пользователь сможет что-то сломать не лазия в хранилище.
>И еще, я не очень понял, как будет работать форма регистрации в режиме серверного рендеринга. Как и куда она будет отправлять свои данные?
>
>> let route = this.router.config.find(r => r.path === redirect);
>> route.data['email'] = email;
Имеете ввиду версию страницы работающей с отключенным JS? Я решил отказаться от этой идеи. Она бесполезная, по крайней мере для части чата, потому что сообщения будут шифроваться на стороне клиента. Может быть позже можно что-то с этим придумать, но пока этого не будет. Я считаю, что это вопрос доверия пользователя, оно есть в каких-то границах, и эти границы могут как и широки так и малы, т.е. я пытаюсь сказать что зависит от пользователя и его доверия. Я думаю, что для приложения с открытом кодом можно даже задуматься и о вайтлисте js.
>А это передает email только один раз, или ты этот email навсегда в конфиг роутинга вписал? По моем, не очень красиво получается. Разве нельзя передавать email внутри route parameters: https://angular.io/guide/router#route-parameters
>
>Тогда можно сделать так: router.navigate(['/register', { email: email }])
>
>Причем, я бы хранил email не в path, а в query: /register?email=
x2JUyz ANUSe xamplePUNCTUMco5&Bm
Можно, но мне наоборот кажется это не очень красивым, когда в URL что-то появляется. Хочу чтобы было так.
Этот email удаляется из роутинга после уничтожения связанного компонента:
https://github.com/someApprentice/Crypter/blob/master/src/app/auth/login/login.component.ts#L63-L68
>> Позволяет ли Bearer token защититься от XSRF?
>> Приходит ли вам на ум какая-нибудь слабая точка которую можно эксплуатировать?
>
>Да. У тебя используются куки, и в них тоже есть токен. Предположим, у нас есть HTML версия, работающая без ангулара. Не получится ли, что злоумышленник сделает форму, которая будет имитировать отправку запроса этой HTML версией, и возникнет XSRF?
Как я говорил ранее, я принял решение отказаться от версии без ангуляра, что значит что не будет такого обработчика запроса, который будет работать с куками повторюсь в очередной раз, что они нужны только для условий в отоброжении (токен наверно и удалить из кук)
>> Достаточно ли это строгая проверка на то является ли сущность экземпляром объекта localStorage?
>
>Проверка достаточная, но непонятен ее смысл. Тест по идее должен проверять, что данные сохраняются и загружаются, а не какой именно класс там испольуется для хранения.
Проверить... что, например, вот для серверной части есть проверка, что вернулся StorageWrapper и всё работает, а для браузерной тоже аналогично. Это не нужно?
>Еще, кстати, мне интересно, можно ли как-то использовать описания моделей (например, User) для автоматической генерации документации о формате JSON-ответа, и можно ли использовать описания для автоматического тестирования (тест берет Swagger-описание, проходится по всем методам и пробует их вызывать и проверяет, что ответ соответствует описанию).
Я ничего из этого не знаю, но постараюсь разобраться.
>>61815
>Тут код теста почти повторяет код обработчика. Это не годится, так как ты можешь сделать одинаковые ошибки и там, и там.
А где у меня код повторяется? То что я токен генерирую? А как я узнаю какой токен будет иначе?
>Если мы тестируем функцию вычисления корня, то ее проверяют возведением в квадрат, а не аналогичным вычислением.
Не понимаю сравнение.
var n = 5
expect(sqrt(n^2)).toEqual(n)
Я же тоже "возвожу" данные пользователя в токен и проверяю совпадает ли возвращённый ответ, а именно кукисы, с ним.
var user = { ... }
token(user, (jwt) => {
expect(cookies['jwt']).toEqual(jwt)
});
Ну да, теперь вижу причину. Нужно проверить что пришедший токен в дешифрованном виде равен данным пользователя?
>В твоем случае надо сформулировать требования к функции регистрации. На мой взгляд, они такие:
>
>- она должна создавать аккаунт на сервере, под которым можно залогиниться
Я забыл сделать проверку, что пользователь добавился в БД и данные правильные. Это достаточное условие? Залогинивание проверяется в предназначенном для неё тесте, и там как раз сначала заполняется запись в БД.
>- она должна возвращать токен, с которым можно использовать защищенное API
Нужно сделать проверку как я писал выше, что пришедший токен в дешифрованном виде равен данным пользователя или действительно нужно проверять каким-то методом API?
>- нельзя дважды зарегистрироваться с одинаковым email
Сделаю. то нужно сделать прописав ассоциацию Sequelize на уникальность emai'а. Тогда при повтороном имейле будет вбрасываться ошибка и тест провалится.
>- нельзя регистрироваться без указания обязательных аргументов
Сделаю.
>- и, может, еще какие-то требования по валидации. Например, что при ошибке возвращается объект определенного вида
Сделаю.
>Для каждого требования мы придумываем свой способ проверки. Создание аккаунта на сервере можно проверить, попробовав залогиниться в него. Токен можно проверить, попробовав вызвать защищенное API, например "получить сведения о себе".
>Создание аккаунта на сервере можно проверить, попробовав залогиниться в него.
>Токен можно проверить, попробовав вызвать защищенное API, например "получить сведения о себе".
Почему? Разве не достаточно проверить что запись добавилась в базу? Разве не залогинивание не должно проверяться в своём собственном тесте?
А токен не достаточно проверить просто дешифровав его (как раз метод и называется verify(token))?
>Так как ты тестируешь код, использующий БД, тебе надо предусмотреть, чтобы база была перед тестом в каком-то определенном состоянии (полностью пустая, например, или с какими-то тестовыми данными). Один из вариантов - в самом начале почистить БД/загрузить дамп, а каждый тест обернуть в транзакцию, которая откатывается. Это может быть быстрее, чем загружать дамп каждый раз.
В данный момент для каждого теста БД должна быть в чистом состоянии и после каждого теста база очищается это можно улучшить добавив перед каждым тестом очистку (методо beforeEach()).
>На практике, может быть полезно разместить тестовую БД на ramfs, чтобы избежать вообще обращений к диску и ускорить прогон тестов.
Тяжело понять. Я с таким не сталкивался. Вижу что это как бы "эмулирует" жесткий диск в оперативной памяти. Как для этого настроить psql? Как нужно будет для это настраивать найстройки соеденения к БД в Ноде? Слишком много вопросов для такой маленькой задачи. Возможно позже, с ростом приложения это будет актуально. Если что взял этот подход на вооружение и вернусь к нему, когда тесты будут делаться медленно.
>Можно также попробовать добавить тест для обработчика ошибок (вызвать специальный URL, который выбрасывает исключение). Иначе ты можешь его отключить/сломать и не заметить.
Сделаю.
>> Is there any way to declare all classes at once for a webpack output file?
>
>Я думаю, это не имеет отношения к webpack, так как ты должен в sequelize передать список классов. А для этого их надо импортировать. И вебпак увидит импорты.
В sequlize-typescript можно указать директорию с моделями, но webpack транспилирует всё в один файл, и это ломается.
https://www.npmjs.com/package/sequelize-typescript#configuration
https://github.com/someApprentice/Crypter/issues/7
>По клиентской стороне:
>
>Ты в API при логине ставишь куки вручную, но разве это правильно? По идее, это должно работать так:
Ставлю куки вручную в клиентской стороне? Нет, у меня такого нет. В клиентской стороне куки только берутся из реквеста, чтобы пререндерить страницу с залогиненым пользователем или нет. Для этого они и нужны. localStorage ставиться в ручную(?) в клиентской стороне при залогинивании https://github.com/someApprentice/Crypter/blob/master/src/app/auth/login/login.component.ts#L48-L51 . Не могли бы вы уточнить строку где я это делаю, я не могу понять последующие описание проблемы.
>https://github.com/someApprentice/Crypter/blob/master/src/app/app.component.html
>
>Тут мне не кажется 100% надежной проверка залогиненности. Тут же нет проверки, что это валидный токен.
Так это же отображение. Если что-то не так, то при запросе к API выдастся соответствующая ошибка. Вряд ли, пользователь сможет что-то сломать не лазия в хранилище.
>И еще, я не очень понял, как будет работать форма регистрации в режиме серверного рендеринга. Как и куда она будет отправлять свои данные?
>
>> let route = this.router.config.find(r => r.path === redirect);
>> route.data['email'] = email;
Имеете ввиду версию страницы работающей с отключенным JS? Я решил отказаться от этой идеи. Она бесполезная, по крайней мере для части чата, потому что сообщения будут шифроваться на стороне клиента. Может быть позже можно что-то с этим придумать, но пока этого не будет. Я считаю, что это вопрос доверия пользователя, оно есть в каких-то границах, и эти границы могут как и широки так и малы, т.е. я пытаюсь сказать что зависит от пользователя и его доверия. Я думаю, что для приложения с открытом кодом можно даже задуматься и о вайтлисте js.
>А это передает email только один раз, или ты этот email навсегда в конфиг роутинга вписал? По моем, не очень красиво получается. Разве нельзя передавать email внутри route parameters: https://angular.io/guide/router#route-parameters
>
>Тогда можно сделать так: router.navigate(['/register', { email: email }])
>
>Причем, я бы хранил email не в path, а в query: /register?email=
x2JUyz ANUSe xamplePUNCTUMco5&Bm
Можно, но мне наоборот кажется это не очень красивым, когда в URL что-то появляется. Хочу чтобы было так.
Этот email удаляется из роутинга после уничтожения связанного компонента:
https://github.com/someApprentice/Crypter/blob/master/src/app/auth/login/login.component.ts#L63-L68
>> Позволяет ли Bearer token защититься от XSRF?
>> Приходит ли вам на ум какая-нибудь слабая точка которую можно эксплуатировать?
>
>Да. У тебя используются куки, и в них тоже есть токен. Предположим, у нас есть HTML версия, работающая без ангулара. Не получится ли, что злоумышленник сделает форму, которая будет имитировать отправку запроса этой HTML версией, и возникнет XSRF?
Как я говорил ранее, я принял решение отказаться от версии без ангуляра, что значит что не будет такого обработчика запроса, который будет работать с куками повторюсь в очередной раз, что они нужны только для условий в отоброжении (токен наверно и удалить из кук)
>> Достаточно ли это строгая проверка на то является ли сущность экземпляром объекта localStorage?
>
>Проверка достаточная, но непонятен ее смысл. Тест по идее должен проверять, что данные сохраняются и загружаются, а не какой именно класс там испольуется для хранения.
Проверить... что, например, вот для серверной части есть проверка, что вернулся StorageWrapper и всё работает, а для браузерной тоже аналогично. Это не нужно?
>> Следующий шаг написания сервиса сообщений.
>
>> Он будет реализован с помощь протокола WAMP и на платформе от https://crossbar.io/ , которая написана на Питоне, и для которой для аутентификации клиентов нужно тоже написать код на нём.
>
>А чем Node не годится, кстати? Или ты решил изучить Питон и микросервисы? Ок, но на практике такой маленький проект может быть невыгодно разбивать на микросервисы - у них ведь есть и накладные расходы.
У роутеров WAMP'а на Node, нету возможностей для аутентифекации и авторизации соединения, и документации. Вышеупомянутая платформа развита лучше других.
>> Поэтому я сейчас буду изучать его, и возможно у меня появиться небольшие вопросы по нему. Могу я задать их в этом треде?
>
>> Заодно и реактивное программирование подучу.
>
>Конечно. Только пиши тогда, что именно ты добавил в проект и на что стоит посмотреть, а то он большой и с ходу это будет не очевидно.
Хорошо, я буду всё подробно описывать.
>Я пока все не успел проверить, увы, давай сначала с тем, что я написал, разберемся, а потом остальное глянем.
Хорошо, спасибо большое! Я не успеваю ответить в тот же день. Не понимаю почему - занимаюсь каждый день, и всё больше и больше нужно изучить. Я так хочу просто писать код. Я никогда не знал что разработка на JS может быть такой сложной, и приложений вообще.
Я ещё серьёзно задумываюсь о приложениях для устройств, а там либо медленный и кривой JS, либо изучать Java/Swift.
Я так хочу хотя бы браузернное приложение к лету закончить, а приложения к осени.
Спасибо большое, у меня нет слов как мне всё это важно.
>валидация реквеста намного больше понравилась чем формы в симфони
Тебе никто не запрещает запилить аргумент ресолвер в симфе и тоже валидировать реквест.
в чём проблема использовать лару с доктриной ?
Вот инглиш ща взялся поттягивать. С пыхой на "ты", опыт работы и гитхаб имеется.
С пыхи вообще укатиться сложно? Насколько это будет плюсом при приеме на работу? Или меня джависты будут обоссывать?
Вопросы по задаче про список студентов
1)Где нужно создавать базу данны=х? В командной строке(КС), в самом PHP или в PHPadmin?
Если использовать КС или админку, то как сохранять ... код на Git?
2) Так же не понял с, что делать с организацией доступа пользователей. Мне нужно через GRANT создать пользователя и или можно предоставить привилегированный доступ (root)? Я просто не понял, как люди проверяют работу через Git. Им же все равно нужно выгрузить весь код на свой компьютер и потом запустить у себя? Значит не нужно создавать пользователя или все таки нужно?
3) Зачем в этом задании использовать классы? Разве нельзя обойтись без них? Особенно не понял, зачем нужен "класс-помощник". Раз у него отсутствуют свойства, почему бы не использовать обычные функции?
4)Также вопрос про экранирование. В уроке про шаблоны говорилось, что "данные лучше форматировать не в php-скрипте, а в шаблоне. Причина тому в том, что форматов вывода данных может быть много, а php-скрипт, получающий данные из базы, как правило один общий". Да, вроде бы логично, но неужели не правильней будет дать базе данных уже экранированную строку, а не повторять эти действия вновь и вновь в шаблоне?
1) В mysql или какой-то другой БД. У mysql есть удобный интерфейс phpmyadmin, также можно работать через КС.
2) Короче говоря, ты ставишь регистрирующемуся студенту куки которые записываются в том числе в базу данных.
Потом каждого пользователя подключающегося к нашему приложению проверяем на наличие кук. Если они есть, проверяем их на сущестование в БД. При положительном результате производим нужные нам действия, например вывод ФИО данного студента.
3.) Так надо
мимо сам делаю эту задачу
1. Хочу больше денех;
2. Хочу глубоко изучать язык, жить этим, хотелось бы выбрать что-то более серьезное, на что не жалко тратить жизнь.
>Насколько это будет плюсом при приеме на работу? Или меня джависты будут обоссывать?
Ты идёшь на ждуна, но ты с опытом чувак, это большой плюс!
1)Ну про mysql понятно. А как мне создать БД, чтобы записать код в Git?
2)Тоже не то. Есть команда GRANT, управляет доступом БД. Когда я читал, про эту команду, говорилось, что лучше создать через эту команду пользователя(одного), чтобы не входить в БД через root. Вот я и спрашиваю, а оно надо? Как это будет работать, если код будет запущен на другом компе?
3)Ну тут можно было бы сказать, что это сделано для удобства. Но создавать массив объектов Student не затратно по памяти?
>1)Ну про mysql понятно. А как мне создать БД, чтобы записать код в Git?
Может сделать дамп базы (только структура, без данных студентов), например students_list.sql и добавить его в репозиторий? Учитель кажется писал об этом в конце задания.
2.) Понял тебя, довольно интересный вопрос. Я работаю со своей БД через root и даже не задумывался об этом, будем ждать что скажет ОП или другие опытные ребята.
3.) Нужно посмотреть как делают в фреймворках и сделать выводы.
Ты можешь создавать у себя БД любым способом, но я бы советовал попробовать описать структуру руками в формате SQL (CREATE TABLE ...). Это поможет тебе вспомнить этот язык. Затем сделай дамп БД в формате .sql и положи в репозиторий, чтобы другие люди могли бы себе развернуть этот дамп. Не включай в дамп команды создания самой БД или пользователя, только команды создания таблиц и вставки данных. Так как у людей могут быть другие названия БД и имя пользователя.
Пользователя люди создадут сами, просто опиши в README, в какой файл надо прописать параметры соединения с БД и где лежит дамп.
Классы можно и не использовать, но я думал, с ними будет удобнее. Ты можешь попробовать сделать и без классов.
>>66516
С экранированной строкой неудобно работать. Допустим, тебе надо проверить длину строки. Тебе придется ее разэкранировать для этого. То же самое, если ты захочешь ее сдампить в файл или отдать через API - придется сначала разэкранировать, чтобы восстановить исходные данные.
А это создает сложности. Где должны экранироваться пришедшие от пользователя данные - в контроллере или в коде сохранения в БД? Функция валидации данных пользователя - что она принимает на вход, экранированные или неэкранированные данные? Функция, ищущая пользователя по id - в каком виде она вернет результат? Тебе надо по всему коду как-то расставлять комментарии, и тщательно проверять, что везде данные правильно преобразуются. Если где-то программист ошибся, то программа будет либо корежить данные, либо в ней появится уязвимость XSS.
Вторая проблема - если мы посмотрим на шаблон, то там нет команд экранирования. Как понять, что тут нет уязвимости? Изучать весь код и все пути, по которым могут передаваться данные? А если завтра новый разработчик добавит новый код, и забудет там поставить экранирование? Глядя на шаблон, мы это не увидим.
Хотя, эти проблемы мы могли бы решить с помощью ООП. Мы бы могли сделать объект SafeString, и у него несколько методов: getRaw(), getHtmlEscaped(), итд, возвращающих данные в нужном формате (и несколько статических конструкторов, позволяющий создать объект из обычной или экранированной строки). Это защитило бы нас от ошибок, так как мы сразу бы видели, какие данные получаются. Но это получается усложнение кода.
Ты можешь создавать у себя БД любым способом, но я бы советовал попробовать описать структуру руками в формате SQL (CREATE TABLE ...). Это поможет тебе вспомнить этот язык. Затем сделай дамп БД в формате .sql и положи в репозиторий, чтобы другие люди могли бы себе развернуть этот дамп. Не включай в дамп команды создания самой БД или пользователя, только команды создания таблиц и вставки данных. Так как у людей могут быть другие названия БД и имя пользователя.
Пользователя люди создадут сами, просто опиши в README, в какой файл надо прописать параметры соединения с БД и где лежит дамп.
Классы можно и не использовать, но я думал, с ними будет удобнее. Ты можешь попробовать сделать и без классов.
>>66516
С экранированной строкой неудобно работать. Допустим, тебе надо проверить длину строки. Тебе придется ее разэкранировать для этого. То же самое, если ты захочешь ее сдампить в файл или отдать через API - придется сначала разэкранировать, чтобы восстановить исходные данные.
А это создает сложности. Где должны экранироваться пришедшие от пользователя данные - в контроллере или в коде сохранения в БД? Функция валидации данных пользователя - что она принимает на вход, экранированные или неэкранированные данные? Функция, ищущая пользователя по id - в каком виде она вернет результат? Тебе надо по всему коду как-то расставлять комментарии, и тщательно проверять, что везде данные правильно преобразуются. Если где-то программист ошибся, то программа будет либо корежить данные, либо в ней появится уязвимость XSS.
Вторая проблема - если мы посмотрим на шаблон, то там нет команд экранирования. Как понять, что тут нет уязвимости? Изучать весь код и все пути, по которым могут передаваться данные? А если завтра новый разработчик добавит новый код, и забудет там поставить экранирование? Глядя на шаблон, мы это не увидим.
Хотя, эти проблемы мы могли бы решить с помощью ООП. Мы бы могли сделать объект SafeString, и у него несколько методов: getRaw(), getHtmlEscaped(), итд, возвращающих данные в нужном формате (и несколько статических конструкторов, позволяющий создать объект из обычной или экранированной строки). Это защитило бы нас от ошибок, так как мы сразу бы видели, какие данные получаются. Но это получается усложнение кода.
плиз ответь на вопрос,
т.к я обычный вкатывальщик, меня интересует 1 вопрос, твои ожидания по увеличению зп с переходом пхп на яву, на сколько %.
Вообще, если ты очень хорошо разбираешься в Ларавеле, ты наверно, можешь найти способ сделать пакетную вставку. Но даже если и нет, я думаю, это не очень принципиально. Ведь обновление тестов делается не так часто и не создает большой нагрузки. Единственное, стоит делать его в рамках транзакции, чтобы при ошибке в БД не было наполовину созданного теста. Доктрина делает обновление в рамках транзакции, а вот как это в Ларавеле сделано, я не знаю.
То есть я советую сначала беспокоиться о корректности кода, а потом уже о производительности. Ну и вставка одной транзакцией иногда работает быстрее.
В твоем варианте, вместо ручной расстановки startTranscation/commit лучше использовать готовый метод: https://laravel.com/docs/5.8/database#database-transactions
Также, ты зря делаешь получение и валидацию внутри транзакции. По моему, лучше сначала подготовить граф объектов (а в идеале еще и валидировать), а только потом внутри транзакции выполнить сохранение в БД.
В твоем коде, тут 'body' => $question['body'] нет проверки, что $question это массив и там есть такой элемент. Не лучше ли использовать получение элемента по пути, как описано тут: https://laravel.com/docs/5.8/requests#retrieving-input или сделать проверку на наличие ключей?
> Помню в доктрине можно было банально через сеттер сначала сохранить всё как ты хочешь, а если еще и cascade: persist стоит то вообще можно не напрягаться
persist() ничего не сохраняет. Она лишь передает сущность под управление Доктрины. Сохранение происходит по flush(): Доктрина сравнивает данные в управляемых сущностях с теми, что были загружены из БД, и формирует запросы INSERT/UPDATE/DELETE, выполняя их в одной транзакции.
Вообще, я бы тебе советовал детально изучить ORM в Laravel, и в коде немного поковыряться, чтобы ты мог сравнивать Доктрину vs Eloquent и видеть плюсы/минусы.
А напомни пожалуйста, зачем ты решил делать server-side rendering, если ты все равно поддерживаешь только браузеры с JS? Для изучения SSR?
Просто обычно SSR используется либо для поддержки поисковых ботов, либо для пользователей без JS или для создания "облегченной" версии. У тебя же ситуация усложняется тем, что ты пытаешься на сервере имитировать localStorage. Если бы у тебя был сайт вроде инстаграма или твиттера, то там можно было бы сделать SSR для поисковиков, и не было бы никаких сложностей. А для страниц с авторизацией делать SSR чуть сложнее.
То есть, я хочу понять, зачем и как используется SSR и что в этом случае можно сделать с хранилищем для сервера. Идея передавать кучу данных в куках мне не особо нравится.
> Исправлю эту ошибку написав хорошие комментарии. Только, я не понимаю зачем, по-моему только сервису-обёртке хранилища можно, потому что это весьма не типичный подход. В остальном, разве это не очевидно что чем занимается?
Да, комментарии нужны там, где назначение модуля не очевидно.
Тут я кстати обратил внимание, что у тебя в стектрейсе обфусцированные имена функций. Лучше было бы в dev использовать неминифицированные файлы, с исходными названиями функций, а то сложно разбираться в функции с именем t._e(). Не знаю, где это настраивается, может быть, в angular.json (но я не уверен).
Вообще, по моему мнению, в dev удобнее не склеивать файлы, а подгружать динамически (через что-нибудь вроде require.js или загрузчик для модулей ES6). Тогда в dev tools и в стек трейсах ты видишь файлы с теми же именами, как в оригинале, а не огромный main.js. Но я, увы, не знаю, легко ли это сделать в твоем случае.
> У меня плохо с английским и написание комментариев утяжелит и замедлил разработку. Насколько важно их писать?
Там, где я работаю, проблему решили написанием комментариев на русском языке. А так, я думаю, что важно. Пока ты работаешь один, ты многое можешь держать в голове. Но что, если придет новый человек, и ему надо разобраться с кодом? Тут важно не количество комментариев, а насколько легко разобраться в проекте.
У тебя в README даже не описана общая архитектура, как все связано. Я не очень хорошо знаком со всеми этими компиляторами и средствами сборки, и я путаюсь от того, что там в корне лежит куча разных конфигов. Если бы в README было описано, как происходит процесс сборки и какой конфиг за что отвечает, думаю, было бы понятнее.
> Эта функция нужна, чтобы обработать асинхронный обработчик запросов, который Эксрпресс не поддерживает (пока что?).
Я имею в виду, что когда смотришь на код, абсолютно непонятно, зачем эта функция и что за магию она делает. Я не знал, поддерживает ли express возврат промисов из обработчиков (теперь знаю, что нет). А если бы там был комментарий - было бы понятнее. Ну и мне кажется, функцию, которая адаптирует асинх. функцию к использованию в express вполне можно называть адаптером.
> Как я уже писал, куки нужны только для условий отображений
По идее, эти данные на сервере проще получить из БД по токену авторизации. Значит, куки можно было бы не делать. То есть тут получается избыточность, как мне кажется.
>> Функции json() он не нужен, это для проверки, что объект соответствует интерфейсу User?
> Да, именно так. Это лишнее?
Нет, я просто хотел спросить, зачем так сделано.
> Почему нужно отдельную функцию валидации? Всё же и так в одну строчку делается. https://github.com/someApprentice/Crypter/blob/master/api/api.ts#L50
Пока это не требуется, но если ты захочешь сделать проверку уникальности или другую проверку, требующую обращения к БД или сервисам, то из модели ты не сможешь это сделать, так как у нее нет ссылок на эти сервисы. И тебе понадобится внешняя функция валидации.
> Я скажу на чистоту мой взгляд на это - Я не понимаю зачем нужно выносить такие функции в отдельные. Это функция понятна любому разработчику и он с первого взгляда поймет что происходит в ней. Почему нужно делать обёртку ради человекопонятного названия функции?
Чтобы избежать дублирования кода. Чтобы не надо было в нескольких местах править одинаковый код. Ну и чтобы не писать код длинной портянкой, а разделять на отдельные части.
> Я правда не понимаю, вот как эта функция будет выглядеть
function createToken(u: User): Promise<string> - наверно так?
> Всё исправлю. Здесь тоже в случае отсутствия нужно отдавать 404?
По идее, да, можно так, если ты хочешь следовать идеям из REST. Либо можно возвращать JSON с true/false, если не хочешь.
В идеологии REST (да и в стандартах HTTP что-то может быть), как я помню, URL соответствуют ресурсам, по типу:
/user/123 - пользователь 123
/book/some-book - какая-то книга
И ты можешь слать к этим ресурсам запросы GET/POST/PUT/DELETE. Такая схема очень хорошо работает с кешем - ты можешь просто использовать URL ресурса в качестве ключа кеша. У REST могут быть и минусы, например, может потребоваться делать много запросов для получения всех данных (хотя это зависит от того, как спроектировано API).
Потому не все используют REST. Например, у Ютуба в API для получения списка видео и одного видео используется один и тот же эндпойнт, просто с разными параметрами: https://developers.google.com/youtube/v3/docs/videos/list
> Результат валидации либо вбрасывает ошибку, либо возвращает null(?). Эта ошибка обрабатывается в обработчике ошибок
Там теряются подробности ошибки:
if (err instanceof ValidationError) {
return res.sendStatus(400);
}
Ну и довольно неочевидная штука, я бы не додумался лезть в обработчик ошибок смотреть код.
А напомни пожалуйста, зачем ты решил делать server-side rendering, если ты все равно поддерживаешь только браузеры с JS? Для изучения SSR?
Просто обычно SSR используется либо для поддержки поисковых ботов, либо для пользователей без JS или для создания "облегченной" версии. У тебя же ситуация усложняется тем, что ты пытаешься на сервере имитировать localStorage. Если бы у тебя был сайт вроде инстаграма или твиттера, то там можно было бы сделать SSR для поисковиков, и не было бы никаких сложностей. А для страниц с авторизацией делать SSR чуть сложнее.
То есть, я хочу понять, зачем и как используется SSR и что в этом случае можно сделать с хранилищем для сервера. Идея передавать кучу данных в куках мне не особо нравится.
> Исправлю эту ошибку написав хорошие комментарии. Только, я не понимаю зачем, по-моему только сервису-обёртке хранилища можно, потому что это весьма не типичный подход. В остальном, разве это не очевидно что чем занимается?
Да, комментарии нужны там, где назначение модуля не очевидно.
Тут я кстати обратил внимание, что у тебя в стектрейсе обфусцированные имена функций. Лучше было бы в dev использовать неминифицированные файлы, с исходными названиями функций, а то сложно разбираться в функции с именем t._e(). Не знаю, где это настраивается, может быть, в angular.json (но я не уверен).
Вообще, по моему мнению, в dev удобнее не склеивать файлы, а подгружать динамически (через что-нибудь вроде require.js или загрузчик для модулей ES6). Тогда в dev tools и в стек трейсах ты видишь файлы с теми же именами, как в оригинале, а не огромный main.js. Но я, увы, не знаю, легко ли это сделать в твоем случае.
> У меня плохо с английским и написание комментариев утяжелит и замедлил разработку. Насколько важно их писать?
Там, где я работаю, проблему решили написанием комментариев на русском языке. А так, я думаю, что важно. Пока ты работаешь один, ты многое можешь держать в голове. Но что, если придет новый человек, и ему надо разобраться с кодом? Тут важно не количество комментариев, а насколько легко разобраться в проекте.
У тебя в README даже не описана общая архитектура, как все связано. Я не очень хорошо знаком со всеми этими компиляторами и средствами сборки, и я путаюсь от того, что там в корне лежит куча разных конфигов. Если бы в README было описано, как происходит процесс сборки и какой конфиг за что отвечает, думаю, было бы понятнее.
> Эта функция нужна, чтобы обработать асинхронный обработчик запросов, который Эксрпресс не поддерживает (пока что?).
Я имею в виду, что когда смотришь на код, абсолютно непонятно, зачем эта функция и что за магию она делает. Я не знал, поддерживает ли express возврат промисов из обработчиков (теперь знаю, что нет). А если бы там был комментарий - было бы понятнее. Ну и мне кажется, функцию, которая адаптирует асинх. функцию к использованию в express вполне можно называть адаптером.
> Как я уже писал, куки нужны только для условий отображений
По идее, эти данные на сервере проще получить из БД по токену авторизации. Значит, куки можно было бы не делать. То есть тут получается избыточность, как мне кажется.
>> Функции json() он не нужен, это для проверки, что объект соответствует интерфейсу User?
> Да, именно так. Это лишнее?
Нет, я просто хотел спросить, зачем так сделано.
> Почему нужно отдельную функцию валидации? Всё же и так в одну строчку делается. https://github.com/someApprentice/Crypter/blob/master/api/api.ts#L50
Пока это не требуется, но если ты захочешь сделать проверку уникальности или другую проверку, требующую обращения к БД или сервисам, то из модели ты не сможешь это сделать, так как у нее нет ссылок на эти сервисы. И тебе понадобится внешняя функция валидации.
> Я скажу на чистоту мой взгляд на это - Я не понимаю зачем нужно выносить такие функции в отдельные. Это функция понятна любому разработчику и он с первого взгляда поймет что происходит в ней. Почему нужно делать обёртку ради человекопонятного названия функции?
Чтобы избежать дублирования кода. Чтобы не надо было в нескольких местах править одинаковый код. Ну и чтобы не писать код длинной портянкой, а разделять на отдельные части.
> Я правда не понимаю, вот как эта функция будет выглядеть
function createToken(u: User): Promise<string> - наверно так?
> Всё исправлю. Здесь тоже в случае отсутствия нужно отдавать 404?
По идее, да, можно так, если ты хочешь следовать идеям из REST. Либо можно возвращать JSON с true/false, если не хочешь.
В идеологии REST (да и в стандартах HTTP что-то может быть), как я помню, URL соответствуют ресурсам, по типу:
/user/123 - пользователь 123
/book/some-book - какая-то книга
И ты можешь слать к этим ресурсам запросы GET/POST/PUT/DELETE. Такая схема очень хорошо работает с кешем - ты можешь просто использовать URL ресурса в качестве ключа кеша. У REST могут быть и минусы, например, может потребоваться делать много запросов для получения всех данных (хотя это зависит от того, как спроектировано API).
Потому не все используют REST. Например, у Ютуба в API для получения списка видео и одного видео используется один и тот же эндпойнт, просто с разными параметрами: https://developers.google.com/youtube/v3/docs/videos/list
> Результат валидации либо вбрасывает ошибку, либо возвращает null(?). Эта ошибка обрабатывается в обработчике ошибок
Там теряются подробности ошибки:
if (err instanceof ValidationError) {
return res.sendStatus(400);
}
Ну и довольно неочевидная штука, я бы не додумался лезть в обработчик ошибок смотреть код.
> Так вы имели ввиду комментраии для модулей API, а не фронтенд приложения?
Главное, чтобы в коде было проще разбираться.
> Я же тоже "возвожу" данные пользователя в токен и проверяю совпадает ли возвращённый ответ, а именно кукисы, с ним.
Ты в тесте просто скопировал код из серверного API. Если ты его неправильно написал, то ты мог сделать одинаковую ошибку и в серверном коде, и в тесте. Потому проверять работу функции нужно другим кодом, другим алгоритмом, а не тем же самым.
Плюс, второй недостаток теста - ты в нем используешь знание о том, как устроена функция регистрации, как она генерирует токен. Если в алгоритме генерации токена что-то поменяется, тебе придется преределывать тест.
Да и это нелогично, получатель токена ведь не должен знать, как он устроен внутри. Ему достаточно того, что этот токен дает доступк API, а как он устроен - не его дело.
В твоем случае, если ты вызываешь API регистрации, логичнее всего проверить, что полученный токен можно использовать для доступа к защищенному API. Или хотя бы вызвать функцию проверки токена.
Тесты проверяют требования (ТЗ) для API. У тебя в требованиях (если их сформулировать и записать пиьменно) вряд ли сказано, что "функция регистрации возвращает токен JWT такой-то структуры, зашифрованный таким-то ключом". Она возвращает просто токен, неважно какой. Знание о том, как он устроен, должно быть сконцентрировано в модуле для генерации и проверки токена, а остальным знать это не важно. Потому я и советовал вынести функции генерации и проверки токена в отдельный модуль авторизации.
Принцип разделения ответственности здесь нарушается. Это легко видеть по тому, что код теста использует секрет, который нужен для генерации токена. Тесты ведь имитируют поведение пользователя API. А у него нет этого секрета.
> Ну да, теперь вижу причину. Нужно проверить что пришедший токен в дешифрованном виде равен данным пользователя?
Мне кажется, это будет плохая идея. Получатель токена не должен знать, как он устроен.
> Я забыл сделать проверку, что пользователь добавился в БД и данные правильные. Это достаточное условие? Залогинивание проверяется в предназначенном для неё тесте, и там как раз сначала заполняется запись в БД.
Я думаю, что удобнее тестировать API снаружи, не имея доступа к БД, а используя только само API. Это позволяет тесту не знать о том, как и где хранятся данные, и нам не придется переделывать тест при каких-то изменениях в БД. Это экономит наше время.
Но на практике, иногда для удобства добавляют специальный тестовые методы в API. Например, ты мог бы сделать тестовый метод проверки наличия пользователя, или проверки токена. Но если можно обойтись без этого, то проще не использовать их, так как это лишний код.
> Тяжело понять. Я с таким не сталкивался. Вижу что это как бы "эмулирует" жесткий диск в оперативной памяти. Как для этого настроить psql?
Я почитал тут:
- https://ru.wikipedia.org/wiki/Tmpfs
- http://www.k-max.name/linux/ramdisk-ramfs-tmpfs-in-linux/
Лучше использовать tmpfs. По идее, ты создаешь диск и монтируешь его в папку одной командой (могут понадобиться права рута, но можно прописать этот диск в /etc/fstab с опцией монтирования пользователем). tmpfs не тратит память, если на виртуальном диске нет данных, более того, она по моему не создает структуру каталогов и служебные области, а как-то хранит файлы напрямую в памяти.
После этого ты как-то настраиваешь psql, чтобы данные для тестовой базы хранились бы в папке с tmpfs. Я не знаю, как это сделать, но гугление выдает статьи вроде такой https://www.manniwood.com/postgresql_94_in_ram которая описывает создание отдельного инстанса Postgres с данными в tmpfs.
> В sequlize-typescript можно указать директорию с моделями, но webpack транспилирует всё в один файл, и это ломается.
Можно придумать какой-то костыль, например, читать список файлов и создавать файл "models.js", импортирующий все эти модели. Пока моделей мало, проще просто вручную перечислить.
> Я считаю, что это вопрос доверия пользователя, оно есть в каких-то границах, и эти границы могут как и широки так и малы, т.е. я пытаюсь сказать что зависит от пользователя и его доверия. Я думаю, что для приложения с открытом кодом можно даже задуматься и о вайтлисте js.
Проблему можно решить, выложив клиентский код в открытый доступ и собрав из него electron-приложение. Если мы сами собрали его из проверенных исходников, то никто не сможет подменить код клиента.
> Можно, но мне наоборот кажется это не очень красивым, когда в URL что-то появляется. Хочу чтобы было так.
Так это не сохраняет состояние. Если мы обновим страницу, или используем навигацию в браузере (вперед/назад), то email потеряется. Потому аргументы и хранят в URL.
Тут тогда логичнее было просто сделать единую страницу регистрации/логина на одном роуте с двумя формами и скрывать одну из них. А email хранить в обычной переменной. И не понадобятся такие явно ненадежные костыли.
> Проверить... что, например, вот для серверной части есть проверка, что вернулся StorageWrapper и всё работает, а для браузерной тоже аналогично. Это не нужно?
Проверять это логичнее, пытаясь что-то туда сохранить, а не проверкой, к какому классу относится объект.
> У роутеров WAMP'а на Node, нету возможностей для аутентифекации и авторизации соединения, и документации. Вышеупомянутая платформа развита лучше других.
Да, но это можно добавить. А в случае с Питоном, ты не сможешь вызывать функции из приложения Node, тебе придется возможно дублировать их, или настраивать взаимодействие. Я не против такого варианта, но готовься к сложностям. И надо правильно спроектировать взаимодействие между питоновским и нодовским приложениями. Это уже почти микросервисы получаются со всеми их плюсами и минусами.
> Так вы имели ввиду комментраии для модулей API, а не фронтенд приложения?
Главное, чтобы в коде было проще разбираться.
> Я же тоже "возвожу" данные пользователя в токен и проверяю совпадает ли возвращённый ответ, а именно кукисы, с ним.
Ты в тесте просто скопировал код из серверного API. Если ты его неправильно написал, то ты мог сделать одинаковую ошибку и в серверном коде, и в тесте. Потому проверять работу функции нужно другим кодом, другим алгоритмом, а не тем же самым.
Плюс, второй недостаток теста - ты в нем используешь знание о том, как устроена функция регистрации, как она генерирует токен. Если в алгоритме генерации токена что-то поменяется, тебе придется преределывать тест.
Да и это нелогично, получатель токена ведь не должен знать, как он устроен внутри. Ему достаточно того, что этот токен дает доступк API, а как он устроен - не его дело.
В твоем случае, если ты вызываешь API регистрации, логичнее всего проверить, что полученный токен можно использовать для доступа к защищенному API. Или хотя бы вызвать функцию проверки токена.
Тесты проверяют требования (ТЗ) для API. У тебя в требованиях (если их сформулировать и записать пиьменно) вряд ли сказано, что "функция регистрации возвращает токен JWT такой-то структуры, зашифрованный таким-то ключом". Она возвращает просто токен, неважно какой. Знание о том, как он устроен, должно быть сконцентрировано в модуле для генерации и проверки токена, а остальным знать это не важно. Потому я и советовал вынести функции генерации и проверки токена в отдельный модуль авторизации.
Принцип разделения ответственности здесь нарушается. Это легко видеть по тому, что код теста использует секрет, который нужен для генерации токена. Тесты ведь имитируют поведение пользователя API. А у него нет этого секрета.
> Ну да, теперь вижу причину. Нужно проверить что пришедший токен в дешифрованном виде равен данным пользователя?
Мне кажется, это будет плохая идея. Получатель токена не должен знать, как он устроен.
> Я забыл сделать проверку, что пользователь добавился в БД и данные правильные. Это достаточное условие? Залогинивание проверяется в предназначенном для неё тесте, и там как раз сначала заполняется запись в БД.
Я думаю, что удобнее тестировать API снаружи, не имея доступа к БД, а используя только само API. Это позволяет тесту не знать о том, как и где хранятся данные, и нам не придется переделывать тест при каких-то изменениях в БД. Это экономит наше время.
Но на практике, иногда для удобства добавляют специальный тестовые методы в API. Например, ты мог бы сделать тестовый метод проверки наличия пользователя, или проверки токена. Но если можно обойтись без этого, то проще не использовать их, так как это лишний код.
> Тяжело понять. Я с таким не сталкивался. Вижу что это как бы "эмулирует" жесткий диск в оперативной памяти. Как для этого настроить psql?
Я почитал тут:
- https://ru.wikipedia.org/wiki/Tmpfs
- http://www.k-max.name/linux/ramdisk-ramfs-tmpfs-in-linux/
Лучше использовать tmpfs. По идее, ты создаешь диск и монтируешь его в папку одной командой (могут понадобиться права рута, но можно прописать этот диск в /etc/fstab с опцией монтирования пользователем). tmpfs не тратит память, если на виртуальном диске нет данных, более того, она по моему не создает структуру каталогов и служебные области, а как-то хранит файлы напрямую в памяти.
После этого ты как-то настраиваешь psql, чтобы данные для тестовой базы хранились бы в папке с tmpfs. Я не знаю, как это сделать, но гугление выдает статьи вроде такой https://www.manniwood.com/postgresql_94_in_ram которая описывает создание отдельного инстанса Postgres с данными в tmpfs.
> В sequlize-typescript можно указать директорию с моделями, но webpack транспилирует всё в один файл, и это ломается.
Можно придумать какой-то костыль, например, читать список файлов и создавать файл "models.js", импортирующий все эти модели. Пока моделей мало, проще просто вручную перечислить.
> Я считаю, что это вопрос доверия пользователя, оно есть в каких-то границах, и эти границы могут как и широки так и малы, т.е. я пытаюсь сказать что зависит от пользователя и его доверия. Я думаю, что для приложения с открытом кодом можно даже задуматься и о вайтлисте js.
Проблему можно решить, выложив клиентский код в открытый доступ и собрав из него electron-приложение. Если мы сами собрали его из проверенных исходников, то никто не сможет подменить код клиента.
> Можно, но мне наоборот кажется это не очень красивым, когда в URL что-то появляется. Хочу чтобы было так.
Так это не сохраняет состояние. Если мы обновим страницу, или используем навигацию в браузере (вперед/назад), то email потеряется. Потому аргументы и хранят в URL.
Тут тогда логичнее было просто сделать единую страницу регистрации/логина на одном роуте с двумя формами и скрывать одну из них. А email хранить в обычной переменной. И не понадобятся такие явно ненадежные костыли.
> Проверить... что, например, вот для серверной части есть проверка, что вернулся StorageWrapper и всё работает, а для браузерной тоже аналогично. Это не нужно?
Проверять это логичнее, пытаясь что-то туда сохранить, а не проверкой, к какому классу относится объект.
> У роутеров WAMP'а на Node, нету возможностей для аутентифекации и авторизации соединения, и документации. Вышеупомянутая платформа развита лучше других.
Да, но это можно добавить. А в случае с Питоном, ты не сможешь вызывать функции из приложения Node, тебе придется возможно дублировать их, или настраивать взаимодействие. Я не против такого варианта, но готовься к сложностям. И надо правильно спроектировать взаимодействие между питоновским и нодовским приложениями. Это уже почти микросервисы получаются со всеми их плюсами и минусами.
Привет ОП. В ларавеле я пока не особо хорошо разбираюсь, складывается ощущение что всё почти идеально пока у тебя какая-то типичная веб-задача, немного навязывается стиль написания кода, но мне как бы и норм, пока что хз какие бест практис и можно доверять фреймворку. Готовый метод транзакции кстати делает абсолютно тоже самое что и у меня, только через анонимную функцию. Не раз замечаю что на одну функцию может идти несколько алиасов, просто чтобы было чуть более интуитивно. А граф объектов у меня уже провалидирован, на том скрине не видно, но это не обычный Request, а ларкин FormRequest, обёртка, которая при создании валидирует входные данные на правила, как на скрине, и если валидация фейлится то в контроллер даже не пускает. Я её еще не доделал правда.
Я сделал объект Attempt, который представляет собой попытку прохождения теста, у него может быть два глобальных состояния, в процессе прохождения и завершен. Так вот я пока что не уверен что делать, когда клиент пытается завершить прохождение теста, но при этом присылает неправильные данные, например входной массив будет иметь вид [ключ_вопроса => ключ_варианта_ответа], клиент присылает свои варианты, но оказывается что указанный вопрос не имеет такого ответа, он относится или к другому вопросу, или его вообще не существует, айди ответа какой-то левый. Мне в таких случаях отправлять обратно что-то в стиле "Вы там у себя разберитесь, а потом перезвоните" и на этом всё или сразу завершать прохождение, помечать как зафейленное, при счете баллов можно например просто притвориться что пользователь банально не ответил на вопрос? То же относится и к отношению Тест -> вопросы в принципе.
Покажи для начала, что ты уже сделал. Потому что твой скрин говорит о том, что ты ленивая хуйлуша.
https://ideone.com/2ro2xS
Антоны, мне нужно написать веб-морду для управления кое-каким сервисом. Основная функциональность - это внесение правок в конфиги, которые лежат на сервере, перезапуск этого сервиса ну и снятие парочки метрик с него.
Сейчас это делают написанные мной скрипты на баше, но хочу чего-то более юзер-френдли для других юзеров.
Ну и с авторизацией и разграничением прав, конечно (почти наверняка это будет в виде ldap учеток)
С программизмом я никак не связан, писал только скрипты на баше. Питон не знаю, да. Посоветуйте какую-нибудь книжку (а лучше видеокурс), чтобы быстро вкатиться в какой-нибудь простой пэхапэ-фреймворк. Думаю, что идеально было бы, если б учили сразу пилить какую-нибудь веб-морду/cms.
>>66549
На линуксе обычно проекты кладут в /var/www/название. На windows - можно сделать отдельную папку на каком-нибудь диске. Например, c:\projects\. В apache/htdocs обычно лежит тестовая страница самого апача.
>>66879
Если посмотреть на твою регулярку, то она написана примерно так:
- сначала идет 8
- затем 1 или больше скобок
- затем 1 или больше пробелов
- затем цифра
Таким образом, между 8 и следующей цифрой может быть последовательность вида "скобка, пробел". Тебе лучше исправить регулярку, чтобы там между цифрами могли бы идти дополнительные символы в любом порядке.
Чтобы написать "любой символ из указанных", можно использовать квадратные скобки, а потом добавить к ним звездочку.
>>66855
> Так вот я пока что не уверен что делать, когда клиент пытается завершить прохождение теста, но при этом присылает неправильные данные, например входной массив будет иметь вид [ключ_вопроса => ключ_варианта_ответа], клиент присылает свои варианты, но оказывается что указанный вопрос не имеет такого ответа, он относится или к другому вопросу, или его вообще не существует,
Проще всего приравнять это к отсутствию ответа (отстутствию ключа в массиве). Можно еще в лог варнинг писать, вдруг это фронтенд-специалист накосячил и не те данные шлет.
Вообще, проще наверно было бы совместить Constraint и ConstraintValidator. Не очень понятно, зачем делать 2 отдельных класса. В Симфони одна из причин - это использование аннотаций, Constraint это аннотация, потому оно сделано отдельным классом. В более простой системе разделять здесь код на 2 класса может быть, нет необходимости.
Код в классе Constraint в конструкторе немного усложнен. Не очень понятно, зачем присваивать свойствам значения из массива, если значения по умолчанию можно просто прописать при объявлении свойства.
> Кстати про симфони, в объектах-наследниках Constraint открытые публичные свойства. Получается бывают ситуации, когда инкапсуляцией можно пренебречь?
Там скорее всего аннотации, которые никто руками не создает, а которые создаются при разборе кода. А так, иногда делают объекты из одних публичных свойств. Видимо, решили, что инкапсуляция там не требуется.
>>63410
Наверно можно переименовать.
> Что если в find() передавать не кучу параметров, а объект с параметрами класса SearchOptions?
Вполне хорошая идея. Я думаю, что этот объект может пригодиться и в других местах, например, при генерации ссылок для пагинации.
>>64961
Я не знаю, есть ли там DDD или нет, но есть Magento - это e-commerce проект.
У тебя в регулярке написано [0-9]{10}, а это значит "10 идущих подряд цифр", без каких-то символов между ними.
> Как я понял, то, что внутри (этих) скобок считается отдельным блоком, куда можно накидать кучу параметров, написанных [тут].
Квадратные скобки [abc] - обозначают "ровно один любой символ из набора a, b или c"
Круглые скобки () используются для группировки, и позволяют применять квантификаторы ко всей группе. Сравни: abc+ - здесь плюс применяется только к букве c и регулярка ищет строки abc, abccc, abcccc. a(bc)+ - здесь плюс применяется к группе (bc) и регулярка ищет строки abc, abcbc, abcbcbcbc.
>>63352
Да. Ты разбиваешь данные на N блоков, а затем просто запускаешь несколько PHP скриптов параллельно, передавая им границы диапазоны. Ну например:
php script.php 1 199 &
php script.php 200 399 &
php script.php 400 1000 &
wait
Здесь & - это синтаксис линуксовой оболочки bash, который запускает команду в фоновом режиме. А команда wait ждет завершения всех фоновых программ.
Но здесь может быть проблема. Если ты используешь транзакции (а их стоит использовать) , то они блокируют данные, с которыми работают, и если нескольким скриптам надо работать с одними и теми же данными, то может так получиться, что работать будет только один скрипт, а другие будут ждать освобождения блокировки.
В случае докера, данные обычно не помещают внутрь образа. Докер часто используют только для того, чтобы засунуть программу + ее конфиг + зависимости (библиотеки) внутрь контейнера. То есть у тебя будет контейнер с nginx, с php-fpm, с mysql и docker-compose, чтобы их всех одновременно запускать. Но картинки и файлы таблиц базы данных не хранятся в этих контейнерах.
Более того, если у тебя есть dev сервер, тебе не требуется на нем держать все картинки с продакшена. Зачем? Пусть они на продакшене и лежат.
Обычно деплоят только код. Изменения в БД деплоят как миграции. Если у тебя докер, то наверно придется заморочиться со сборкой и выгрузкой образов, но если их содержимое не изменилось, то они будут быстро создаваться на основе закешированных данных.
По поводу деплоя. Самый простой вариант, для простых сайтов - это деплой через rsync (команда копирования файлов) или git. То есть ты просто обновляешь скриптом файлы на сервере. Минус - в процессе деплоя есть момент, когда часть файлов уже обновлена, а часть еще нет и в этот момент сайт может выдавать ошибки.
Сине-зеленый деплой ( https://habr.com/ru/post/309832/ ) - это когда у тебя 2 папки для сайта. Сайт работает из первой папки, а деплой ты делаешь во вторую папку, и после завершения переключаешь настройку на веб-сервере, чтобы сайт отдавался теперь из второй папки. А следующий деплой делаешь в первую папку.
В случае с докерами и сине-зеленым деплоем, ты можешь сделать деплой кода в новую папку. Поднять там все докеры, используя другие номера портов (php-fpm, mysql). И затем переключить нгинкс на использование кода из новой папки. А старые докеры остановить.
Это дает бесшовные обновления без даунтайма. Если в момент переключения работали скрипты из старой папки, то они спокойно завершают свою работу используя старый код, а все новые запросы идут в новую папку.
Единственная проблема может быть с долгоживущими cli-скриптами (за время их жизни код в папке, из которой они запущены, мог обновиться), хотя на практике это маловероятно.
В случае докера, данные обычно не помещают внутрь образа. Докер часто используют только для того, чтобы засунуть программу + ее конфиг + зависимости (библиотеки) внутрь контейнера. То есть у тебя будет контейнер с nginx, с php-fpm, с mysql и docker-compose, чтобы их всех одновременно запускать. Но картинки и файлы таблиц базы данных не хранятся в этих контейнерах.
Более того, если у тебя есть dev сервер, тебе не требуется на нем держать все картинки с продакшена. Зачем? Пусть они на продакшене и лежат.
Обычно деплоят только код. Изменения в БД деплоят как миграции. Если у тебя докер, то наверно придется заморочиться со сборкой и выгрузкой образов, но если их содержимое не изменилось, то они будут быстро создаваться на основе закешированных данных.
По поводу деплоя. Самый простой вариант, для простых сайтов - это деплой через rsync (команда копирования файлов) или git. То есть ты просто обновляешь скриптом файлы на сервере. Минус - в процессе деплоя есть момент, когда часть файлов уже обновлена, а часть еще нет и в этот момент сайт может выдавать ошибки.
Сине-зеленый деплой ( https://habr.com/ru/post/309832/ ) - это когда у тебя 2 папки для сайта. Сайт работает из первой папки, а деплой ты делаешь во вторую папку, и после завершения переключаешь настройку на веб-сервере, чтобы сайт отдавался теперь из второй папки. А следующий деплой делаешь в первую папку.
В случае с докерами и сине-зеленым деплоем, ты можешь сделать деплой кода в новую папку. Поднять там все докеры, используя другие номера портов (php-fpm, mysql). И затем переключить нгинкс на использование кода из новой папки. А старые докеры остановить.
Это дает бесшовные обновления без даунтайма. Если в момент переключения работали скрипты из старой папки, то они спокойно завершают свою работу используя старый код, а все новые запросы идут в новую папку.
Единственная проблема может быть с долгоживущими cli-скриптами (за время их жизни код в папке, из которой они запущены, мог обновиться), хотя на практике это маловероятно.
Скорее всего ошибка при выборке данных, например в SQL запросе. Ты должен после каждой команды работы с mysqli проверять, что она не вернула null или false. Или же настроить выброс исключений при ошибке и тогда проверять не придется.
Также, почитай про шаблоны и про XSS:
- https://github.com/codedokode/pasta/blob/master/php/templates.md
- https://github.com/codedokode/pasta/blob/master/security/xss.md
>>63056
[+7|8] значит "ровно один любой символ из набора: +, 7, | или 8"
>>62965
Если ресурсов сервера хватает, то можно держать на одном сервере. Разносят, чтобы например можно было запустить несколько отдельных php-серверов, и они бы не отъедали CPU у базы данных.
Слушать всем, кто сомневается брать ли PHP сейчас, полезно послушать.
Кирюх, ты?
$text = "Жырный тролль сдесь.зделал координально срач а модеры?смотрят но всем плевать";
$regexp = '/([.,;:!?](\\S))|жы|шы|координально|сдесь|зделал|([^,] (а|но) )/ui';
$match = [];
if (preg_match_all($regexp, $text, $match) > 0) {
foreach ($match[0] as $number => $mistake) {
$number++;
echo "{$number}-я ашипка - {$mistake}\n";}}
выводит
1-я ашипка - Жы
2-я ашипка - сдесь
3-я ашипка - .з
4-я ашипка - координально
5-я ашипка - ч а
6-я ашипка - ?с
7-я ашипка - т но
Объясните мне, почему не видит слово "зделал"?
Удаленка, полностью свободный график, минимум контроля, отсутствие корпошизы (образование, внешний вид, пол и прочее тоже пофиг). Можно делать быстро и ебланить большую часть дня (не перегружаю работой), можно делать дольше, если что-то не умеешь, подождем.
Языки программирования: PHP (на сервере 7.2), JS, HTML/CSS, SQL (на сервере MySQL или MariaDB).
Фреймворки: Yii, jQuery.
Технологии и инструменты: scss(sass), gulp, git, PHPStorm.
Нужен опыт работы над крупными yii-проектами (крупнее лаба10.пхп и сокращателя ссылок).
ЗП 40-50 в месяц (на руки). Если вы крутой и с ходу хотите больше (более-менее адекватно предложению) - говорите, сколько.
Задачи: доработка существующего продукта, который, по идее, написан неплохо (MVC, вся фигня), с нынешним разработчиком познакомлю, введем в курс дела.
Вряд ли, проект уже имеющийся на yii написан, там куча строк кода, и его надо развивать, дорабатывать, фреймворк активно используется. Если ты сможешь быстро вкатиться в йиишные фичи (всякие там актив рекордс, актив форм, гридвью, контроль доступа, миграции) - то в принципе вариант.
*Начинающий
Возможно будет нужен через пару месяцев, пока ищу основного разраба в проект.
Я, блядь, полночи бился, выискивая ошибку в каждой букве, только чтобы обнаружить, что на phptester.net или на runphponline.com он прекрасно работает! Какого хрена?! Ну и проверьте, что ли, как я вообще решил эту задачу про палиндромы. Нормальный код?
>А напомни пожалуйста, зачем ты решил делать server-side rendering, если ты все равно поддерживаешь только браузеры с JS? Для изучения SSR?
>
>Просто обычно SSR используется либо для поддержки поисковых ботов, либо для пользователей без JS или для создания "облегченной" версии. У тебя же ситуация усложняется тем, что ты пытаешься на сервере имитировать localStorage. Если бы у тебя был сайт вроде инстаграма или твиттера, то там можно было бы сделать SSR для поисковиков, и не было бы никаких сложностей. А для страниц с авторизацией делать SSR чуть сложнее.
>
>То есть, я хочу понять, зачем и как используется SSR и что в этом случае можно сделать с хранилищем для сервера. Идея передавать кучу данных в куках мне не особо нравится.
>А напомни пожалуйста, зачем ты решил делать server-side rendering, если ты все равно поддерживаешь только браузеры с JS? Для изучения SSR?
Для ускорения загрузки приложения. И для поддержки поисковых ботов, конечно.
>То есть, я хочу понять, зачем и как используется SSR и что в этом случае можно сделать с хранилищем для сервера. Идея передавать кучу данных в куках мне не особо нравится.
Почему не нравится? Это же тоже самое что запрос к серверной части приложения и его шаблонизатору, для которого нужны куки для отображения, той или иной информации относительно отдельного пользователя.
>
>Тут я кстати обратил внимание, что у тебя в стектрейсе обфусцированные имена функций. Лучше было бы в dev использовать неминифицированные файлы, с исходными названиями функций, а то сложно разбираться в функции с именем t._e(). Не знаю, где это настраивается, может быть, в angular.json (но я не уверен).
>
>Вообще, по моему мнению, в dev удобнее не склеивать файлы, а подгружать динамически (через что-нибудь вроде require.js или загрузчик для модулей ES6). Тогда в dev tools и в стек трейсах ты видишь файлы с теми же именами, как в оригинале, а не огромный main.js. Но я, увы, не знаю, легко ли это сделать в твоем случае.
У меня сейчас получилось сгенерировать не минифицированные файлы, убрав просто флаг --prod из команды сборки ng build --prod && ng run Crypter:server && webpack --config webpack.server.config.js
Добавил новый стактрейс: https://github.com/someApprentice/Crypter/issues/1#issuecomment-474728547
>Тогда в dev tools и в стек трейсах ты видишь файлы с теми же именами, как в оригинале, а не огромный main.js.
Разве и так в dev tools нельзя посмотреть все файлы даже после компиляции webpack'ом?pic-1
>> У меня плохо с английским и написание комментариев утяжелит и замедлил разработку. Насколько важно их писать?
>
>Там, где я работаю, проблему решили написанием комментариев на русском языке. А так, я думаю, что важно. Пока ты работаешь один, ты многое можешь держать в голове. Но что, если придет новый человек, и ему надо разобраться с кодом? Тут важно не количество комментариев, а насколько легко разобраться в проекте.
>
>У тебя в README даже не описана общая архитектура, как все связано. Я не очень хорошо знаком со всеми этими компиляторами и средствами сборки, и я путаюсь от того, что там в корне лежит куча разных конфигов. Если бы в README было описано, как происходит процесс сборки и какой конфиг за что отвечает, думаю, было бы понятнее.
Если честно, я сам путаюсь от такого количества конфигов, и я сам первый раз сталкиваюсь с этими всеми компиляторами и средствами сборки. Это бесконечная яма если я буду и это ещё изучать во всех тонкостях. Мне нужно изучить Python, мне нужно изучить crossbar.io (сегодня собираюсь писать действительный код), мне нужно изучить реактивное программирование, мне нужно написать само приложение, и на каждом шажке всплывает ещё 1000 подводных камней, которые нужно изучать. Я так никогда не закончу эту приложение.
Простите, что противлюсь вашим наставлениям - я исправлю это после всего... А пока, всё что нужно знать о сборке, тому кто хочет протестировать приложение, нужно всего лишь набрать npm run build.
Комментарии в коде для обёртки я обязательно добавлю.
>> Эта функция нужна, чтобы обработать асинхронный обработчик запросов, который Эксрпресс не поддерживает (пока что?).
>
>Я имею в виду, что когда смотришь на код, абсолютно непонятно, зачем эта функция и что за магию она делает. Я не знал, поддерживает ли express возврат промисов из обработчиков (теперь знаю, что нет). А если бы там был комментарий - было бы понятнее. Ну и мне кажется, функцию, которая адаптирует асинх. функцию к использованию в express вполне можно называть адаптером.
Хорошо, я переменную функцию и добавлю к ней более вразумительный комментарий.
>> Как я уже писал, куки нужны только для условий отображений
>
>По идее, эти данные на сервере проще получить из БД по токену авторизации. Значит, куки можно было бы не делать. То есть тут получается избыточность, как мне кажется.
Да, можно написать и серверный код в Angular-приложении за счет проверки на isPlatformServer(), но для этого нужно будет... хм, внутри этой проверки делать отдельные импорты библиотек (sequelize, dotenv, весь серверный код) чтобы они не тянулись в браузерной части. Мне не нравится этот стиль написания кода, так ещё не понятней будет, и к тому же нужно будет писать другую абстракцию - ничего от этого не выигрывается и только теряется время на написание дополнительных комментариев, увеличения кода, роста количества файлов, и что значит сложности в изучении проекта.
Я вижу вашу мысль об избыточности и понимаю, что это связанно с тем что есть и localStorage и кукисы, но почему бы и нет? Это нормально использовать и кукисы и localStorage. Что мешает этому? Это же машинное хранилище, и им нужно пользоваться.
Можно посмотреть на это так - сервер рендерит страницу с помощью кук, а js приложение подхватывает полученную страницу и уже работает с localStorage. То с чем и должен работать js, не получая доступ к кукам. Разве это не плохо что js имеет доступ к кукам?
>> Почему нужно отдельную функцию валидации? Всё же и так в одну строчку делается. https://github.com/someApprentice/Crypter/blob/master/api/api.ts#L50
>
>Пока это не требуется, но если ты захочешь сделать проверку уникальности или другую проверку, требующую обращения к БД или сервисам, то из модели ты не сможешь это сделать, так как у нее нет ссылок на эти сервисы. И тебе понадобится внешняя функция валидации.
А, хорошо. Конечно, для таких случаев я бы написал сервис валидации.
>Чтобы избежать дублирования кода. Чтобы не надо было в нескольких местах править одинаковый код. Ну и чтобы не писать код длинной портянкой, а разделять на отдельные части.
В данный момент тогда лучше сделать сервис с методом .login(...), потому что, пока что, код дублируется в двух местах и нужен как раз для залогинивания. Думаю, всё таки нужно написать сервис Authorizer.
>> Я правда не понимаю, вот как эта функция будет выглядеть
>
>function createToken(u: User): Promise<string> - наверно так?
Я выше написал, что для этой функции нужно передать коллбэк и в итоге получится тоже самое, либо нужно делать отдельную функцию для каждого случая createTokenForLogin(u: User) Promise<string>, createTokenForSomething(smth: any) Promise<string>... Может правда лучше разбить сам код на функции, и там пользоваться функцией создания токена как есть?
>> Результат валидации либо вбрасывает ошибку, либо возвращает null(?). Эта ошибка обрабатывается в обработчике ошибок
>
>Там теряются подробности ошибки:
>
>if (err instanceof ValidationError) {
>return res.sendStatus(400);
>}
>
>Ну и довольно неочевидная штука, я бы не додумался лезть в обработчик ошибок смотреть код.
А как иначе нужно делать? Делать блоки try/catch каждый раз и каждый раз повторять один и тот же код? Разве в PHP на Slim'е не была такая логика обработки ошибки?
>А напомни пожалуйста, зачем ты решил делать server-side rendering, если ты все равно поддерживаешь только браузеры с JS? Для изучения SSR?
>
>Просто обычно SSR используется либо для поддержки поисковых ботов, либо для пользователей без JS или для создания "облегченной" версии. У тебя же ситуация усложняется тем, что ты пытаешься на сервере имитировать localStorage. Если бы у тебя был сайт вроде инстаграма или твиттера, то там можно было бы сделать SSR для поисковиков, и не было бы никаких сложностей. А для страниц с авторизацией делать SSR чуть сложнее.
>
>То есть, я хочу понять, зачем и как используется SSR и что в этом случае можно сделать с хранилищем для сервера. Идея передавать кучу данных в куках мне не особо нравится.
>А напомни пожалуйста, зачем ты решил делать server-side rendering, если ты все равно поддерживаешь только браузеры с JS? Для изучения SSR?
Для ускорения загрузки приложения. И для поддержки поисковых ботов, конечно.
>То есть, я хочу понять, зачем и как используется SSR и что в этом случае можно сделать с хранилищем для сервера. Идея передавать кучу данных в куках мне не особо нравится.
Почему не нравится? Это же тоже самое что запрос к серверной части приложения и его шаблонизатору, для которого нужны куки для отображения, той или иной информации относительно отдельного пользователя.
>
>Тут я кстати обратил внимание, что у тебя в стектрейсе обфусцированные имена функций. Лучше было бы в dev использовать неминифицированные файлы, с исходными названиями функций, а то сложно разбираться в функции с именем t._e(). Не знаю, где это настраивается, может быть, в angular.json (но я не уверен).
>
>Вообще, по моему мнению, в dev удобнее не склеивать файлы, а подгружать динамически (через что-нибудь вроде require.js или загрузчик для модулей ES6). Тогда в dev tools и в стек трейсах ты видишь файлы с теми же именами, как в оригинале, а не огромный main.js. Но я, увы, не знаю, легко ли это сделать в твоем случае.
У меня сейчас получилось сгенерировать не минифицированные файлы, убрав просто флаг --prod из команды сборки ng build --prod && ng run Crypter:server && webpack --config webpack.server.config.js
Добавил новый стактрейс: https://github.com/someApprentice/Crypter/issues/1#issuecomment-474728547
>Тогда в dev tools и в стек трейсах ты видишь файлы с теми же именами, как в оригинале, а не огромный main.js.
Разве и так в dev tools нельзя посмотреть все файлы даже после компиляции webpack'ом?pic-1
>> У меня плохо с английским и написание комментариев утяжелит и замедлил разработку. Насколько важно их писать?
>
>Там, где я работаю, проблему решили написанием комментариев на русском языке. А так, я думаю, что важно. Пока ты работаешь один, ты многое можешь держать в голове. Но что, если придет новый человек, и ему надо разобраться с кодом? Тут важно не количество комментариев, а насколько легко разобраться в проекте.
>
>У тебя в README даже не описана общая архитектура, как все связано. Я не очень хорошо знаком со всеми этими компиляторами и средствами сборки, и я путаюсь от того, что там в корне лежит куча разных конфигов. Если бы в README было описано, как происходит процесс сборки и какой конфиг за что отвечает, думаю, было бы понятнее.
Если честно, я сам путаюсь от такого количества конфигов, и я сам первый раз сталкиваюсь с этими всеми компиляторами и средствами сборки. Это бесконечная яма если я буду и это ещё изучать во всех тонкостях. Мне нужно изучить Python, мне нужно изучить crossbar.io (сегодня собираюсь писать действительный код), мне нужно изучить реактивное программирование, мне нужно написать само приложение, и на каждом шажке всплывает ещё 1000 подводных камней, которые нужно изучать. Я так никогда не закончу эту приложение.
Простите, что противлюсь вашим наставлениям - я исправлю это после всего... А пока, всё что нужно знать о сборке, тому кто хочет протестировать приложение, нужно всего лишь набрать npm run build.
Комментарии в коде для обёртки я обязательно добавлю.
>> Эта функция нужна, чтобы обработать асинхронный обработчик запросов, который Эксрпресс не поддерживает (пока что?).
>
>Я имею в виду, что когда смотришь на код, абсолютно непонятно, зачем эта функция и что за магию она делает. Я не знал, поддерживает ли express возврат промисов из обработчиков (теперь знаю, что нет). А если бы там был комментарий - было бы понятнее. Ну и мне кажется, функцию, которая адаптирует асинх. функцию к использованию в express вполне можно называть адаптером.
Хорошо, я переменную функцию и добавлю к ней более вразумительный комментарий.
>> Как я уже писал, куки нужны только для условий отображений
>
>По идее, эти данные на сервере проще получить из БД по токену авторизации. Значит, куки можно было бы не делать. То есть тут получается избыточность, как мне кажется.
Да, можно написать и серверный код в Angular-приложении за счет проверки на isPlatformServer(), но для этого нужно будет... хм, внутри этой проверки делать отдельные импорты библиотек (sequelize, dotenv, весь серверный код) чтобы они не тянулись в браузерной части. Мне не нравится этот стиль написания кода, так ещё не понятней будет, и к тому же нужно будет писать другую абстракцию - ничего от этого не выигрывается и только теряется время на написание дополнительных комментариев, увеличения кода, роста количества файлов, и что значит сложности в изучении проекта.
Я вижу вашу мысль об избыточности и понимаю, что это связанно с тем что есть и localStorage и кукисы, но почему бы и нет? Это нормально использовать и кукисы и localStorage. Что мешает этому? Это же машинное хранилище, и им нужно пользоваться.
Можно посмотреть на это так - сервер рендерит страницу с помощью кук, а js приложение подхватывает полученную страницу и уже работает с localStorage. То с чем и должен работать js, не получая доступ к кукам. Разве это не плохо что js имеет доступ к кукам?
>> Почему нужно отдельную функцию валидации? Всё же и так в одну строчку делается. https://github.com/someApprentice/Crypter/blob/master/api/api.ts#L50
>
>Пока это не требуется, но если ты захочешь сделать проверку уникальности или другую проверку, требующую обращения к БД или сервисам, то из модели ты не сможешь это сделать, так как у нее нет ссылок на эти сервисы. И тебе понадобится внешняя функция валидации.
А, хорошо. Конечно, для таких случаев я бы написал сервис валидации.
>Чтобы избежать дублирования кода. Чтобы не надо было в нескольких местах править одинаковый код. Ну и чтобы не писать код длинной портянкой, а разделять на отдельные части.
В данный момент тогда лучше сделать сервис с методом .login(...), потому что, пока что, код дублируется в двух местах и нужен как раз для залогинивания. Думаю, всё таки нужно написать сервис Authorizer.
>> Я правда не понимаю, вот как эта функция будет выглядеть
>
>function createToken(u: User): Promise<string> - наверно так?
Я выше написал, что для этой функции нужно передать коллбэк и в итоге получится тоже самое, либо нужно делать отдельную функцию для каждого случая createTokenForLogin(u: User) Promise<string>, createTokenForSomething(smth: any) Promise<string>... Может правда лучше разбить сам код на функции, и там пользоваться функцией создания токена как есть?
>> Результат валидации либо вбрасывает ошибку, либо возвращает null(?). Эта ошибка обрабатывается в обработчике ошибок
>
>Там теряются подробности ошибки:
>
>if (err instanceof ValidationError) {
>return res.sendStatus(400);
>}
>
>Ну и довольно неочевидная штука, я бы не додумался лезть в обработчик ошибок смотреть код.
А как иначе нужно делать? Делать блоки try/catch каждый раз и каждый раз повторять один и тот же код? Разве в PHP на Slim'е не была такая логика обработки ошибки?
>> Я же тоже "возвожу" данные пользователя в токен и проверяю совпадает ли возвращённый ответ, а именно кукисы, с ним.
>
>Ты в тесте просто скопировал код из серверного API. Если ты его неправильно написал, то ты мог сделать одинаковую ошибку и в серверном коде, и в тесте. Потому проверять работу функции нужно другим кодом, другим алгоритмом, а не тем же самым.
>
>Плюс, второй недостаток теста - ты в нем используешь знание о том, как устроена функция регистрации, как она генерирует токен. Если в алгоритме генерации токена что-то поменяется, тебе придется преределывать тест.
>
>Да и это нелогично, получатель токена ведь не должен знать, как он устроен внутри. Ему достаточно того, что этот токен дает доступк API, а как он устроен - не его дело.
>
>В твоем случае, если ты вызываешь API регистрации, логичнее всего проверить, что полученный токен можно использовать для доступа к защищенному API. Или хотя бы вызвать функцию проверки токена.
>
>Тесты проверяют требования (ТЗ) для API. У тебя в требованиях (если их сформулировать и записать пиьменно) вряд ли сказано, что "функция регистрации возвращает токен JWT такой-то структуры, зашифрованный таким-то ключом". Она возвращает просто токен, неважно какой. Знание о том, как он устроен, должно быть сконцентрировано в модуле для генерации и проверки токена, а остальным знать это не важно. Потому я и советовал вынести функции генерации и проверки токена в отдельный модуль авторизации.
>
>Принцип разделения ответственности здесь нарушается. Это легко видеть по тому, что код теста использует секрет, который нужен для генерации токена. Тесты ведь имитируют поведение пользователя API. А у него нет этого секрета.
>Плюс, второй недостаток теста - ты в нем используешь знание о том, как устроена функция регистрации, как она генерирует токен. Если в алгоритме генерации токена что-то поменяется, тебе придется преределывать тест.
У меня в тесте с логином, я сначала добавляю пользователя в БД, а потом проверяю само залогиневание. То есть я здесь тоже использую знание о том как устроенна функция. А что если алгоритм сохранения пользователя тоже поменяется? Здесь тоже ошибка? Как тогда быть? Нужно писать какую-то абстракцию над БД во время тестов? Значит, нужно сначала вызвать функцию регистрации?
>Принцип разделения ответственности здесь нарушается. Это легко видеть по тому, что код теста использует секрет, который нужен для генерации токена. Тесты ведь имитируют поведение пользователя API. А у него нет этого секрета.
Мне тяжело понять лежащий глубокий смысл в таких принципах как SOLID. Я похоже нарушил один из его принципов. Напишу здесь чтобы не забыть - что мне нужно детальнее ознакомиться с примерами этих принципов чего я не делал.
В целом, я понял свою ошибку, что я тестировал код не то как это сделал бы человек, а проверки на то что вернёт машина. Я понял, что нужно писать код теста так, чтобы с сымитировать тестирование функционала живым человек на действительном сайте.
Всё исправлю.
>Но на практике, иногда для удобства добавляют специальный тестовые методы в API. Например, ты мог бы сделать тестовый метод проверки наличия пользователя, или проверки токена. Но если можно обойтись без этого, то проще не использовать их, так как это лишний код.
Можно проверить можно ли залогиниться/разлогиниться после этих методов.
>> В sequlize-typescript можно указать директорию с моделями, но webpack транспилирует всё в один файл, и это ломается.
>
>Можно придумать какой-то костыль, например, читать список файлов и создавать файл "models.js", импортирующий все эти модели. Пока моделей мало, проще просто вручную перечислить.
Я плохо разбираюсь как настраивать webpack. В проблемах этой библиотеке, мне сказали, что нужно написать плагин для этого.
Буду пока вручную перечислять, хоть и знаю что это плохо.
>> Я считаю, что это вопрос доверия пользователя, оно есть в каких-то границах, и эти границы могут как и широки так и малы, т.е. я пытаюсь сказать что зависит от пользователя и его доверия. Я думаю, что для приложения с открытом кодом можно даже задуматься и о вайтлисте js.
>
>Проблему можно решить, выложив клиентский код в открытый доступ и собрав из него electron-приложение. Если мы сами собрали его из проверенных исходников, то никто не сможет подменить код клиента.
Вот именно. Код и так будет в открытом доступе. Только, к сожалению, не каждый пользователь сможет сделать это самостоятельно. Даже я думаю, что это не так просто, как вы говорите.
Что вы знаете о других инструментах созданий приложений? Я знаю о таком инструменте как React Native и нагуглить NativeScript, но учитывая свой печальный опыт с React'ом, я боюсь что с ним тоже будет не всё так гладко. Ну конечно же, это ни секрет что JS медленный.
Вы знаете что-нибудь об их этих инструментах и как легко с ними будет работать? После ваших слов, создаётся ощущение, что в случае с Electorn'ом это действительно будет достаточно склонировать репозитирий и собрать из него десктопное приложение - и мне интересно, будет ли здесь всё так же просто?
>> Можно, но мне наоборот кажется это не очень красивым, когда в URL что-то появляется. Хочу чтобы было так.
>
>Так это не сохраняет состояние. Если мы обновим страницу, или используем навигацию в браузере (вперед/назад), то email потеряется. Потому аргументы и хранят в URL.
>
>Тут тогда логичнее было просто сделать единую страницу регистрации/логина на одном роуте с двумя формами и скрывать одну из них. А email хранить в обычной переменной. И не понадобятся такие явно ненадежные костыли.
Мне кажется так удобно будет для каждого пользователя. Например, мы же не можем знать заранее зарегистрирован человек или только хочет зарегистрироваться, и всё что ему нужно это просто ввести свой email и дальше система определит, что он хочет сделать. Я такое видел только в моей бывшей любимой соц.сети - tumblr'е, но сейчас они убрали эту функцию. Я считаю это очень удобным и типо приветливым.
>> Проверить... что, например, вот для серверной части есть проверка, что вернулся StorageWrapper и всё работает, а для браузерной тоже аналогично. Это не нужно?
>
>Проверять это логичнее, пытаясь что-то туда сохранить, а не проверкой, к какому классу относится объект.
Понимаю, исправлю.
>> У роутеров WAMP'а на Node, нету возможностей для аутентифекации и авторизации соединения, и документации. Вышеупомянутая платформа развита лучше других.
>
>Да, но это можно добавить. А в случае с Питоном, ты не сможешь вызывать функции из приложения Node, тебе придется возможно дублировать их, или настраивать взаимодействие. Я не против такого варианта, но готовься к сложностям. И надо правильно спроектировать взаимодействие между питоновским и нодовским приложениями. Это уже почти микросервисы получаются со всеми их плюсами и минусами.
Можно вызывать функции приложения из Node с помощью Remote Procedure Calls паттерна реализуемым WAMP.
Я не знаю, о каких функциях приложения Node вы говорили, но у crossbar.io есть возможность сделать компонент(?) и на другом языке https://crossbar.io/docs/Guest-Configuration/?highlight=node.js
>> Я же тоже "возвожу" данные пользователя в токен и проверяю совпадает ли возвращённый ответ, а именно кукисы, с ним.
>
>Ты в тесте просто скопировал код из серверного API. Если ты его неправильно написал, то ты мог сделать одинаковую ошибку и в серверном коде, и в тесте. Потому проверять работу функции нужно другим кодом, другим алгоритмом, а не тем же самым.
>
>Плюс, второй недостаток теста - ты в нем используешь знание о том, как устроена функция регистрации, как она генерирует токен. Если в алгоритме генерации токена что-то поменяется, тебе придется преределывать тест.
>
>Да и это нелогично, получатель токена ведь не должен знать, как он устроен внутри. Ему достаточно того, что этот токен дает доступк API, а как он устроен - не его дело.
>
>В твоем случае, если ты вызываешь API регистрации, логичнее всего проверить, что полученный токен можно использовать для доступа к защищенному API. Или хотя бы вызвать функцию проверки токена.
>
>Тесты проверяют требования (ТЗ) для API. У тебя в требованиях (если их сформулировать и записать пиьменно) вряд ли сказано, что "функция регистрации возвращает токен JWT такой-то структуры, зашифрованный таким-то ключом". Она возвращает просто токен, неважно какой. Знание о том, как он устроен, должно быть сконцентрировано в модуле для генерации и проверки токена, а остальным знать это не важно. Потому я и советовал вынести функции генерации и проверки токена в отдельный модуль авторизации.
>
>Принцип разделения ответственности здесь нарушается. Это легко видеть по тому, что код теста использует секрет, который нужен для генерации токена. Тесты ведь имитируют поведение пользователя API. А у него нет этого секрета.
>Плюс, второй недостаток теста - ты в нем используешь знание о том, как устроена функция регистрации, как она генерирует токен. Если в алгоритме генерации токена что-то поменяется, тебе придется преределывать тест.
У меня в тесте с логином, я сначала добавляю пользователя в БД, а потом проверяю само залогиневание. То есть я здесь тоже использую знание о том как устроенна функция. А что если алгоритм сохранения пользователя тоже поменяется? Здесь тоже ошибка? Как тогда быть? Нужно писать какую-то абстракцию над БД во время тестов? Значит, нужно сначала вызвать функцию регистрации?
>Принцип разделения ответственности здесь нарушается. Это легко видеть по тому, что код теста использует секрет, который нужен для генерации токена. Тесты ведь имитируют поведение пользователя API. А у него нет этого секрета.
Мне тяжело понять лежащий глубокий смысл в таких принципах как SOLID. Я похоже нарушил один из его принципов. Напишу здесь чтобы не забыть - что мне нужно детальнее ознакомиться с примерами этих принципов чего я не делал.
В целом, я понял свою ошибку, что я тестировал код не то как это сделал бы человек, а проверки на то что вернёт машина. Я понял, что нужно писать код теста так, чтобы с сымитировать тестирование функционала живым человек на действительном сайте.
Всё исправлю.
>Но на практике, иногда для удобства добавляют специальный тестовые методы в API. Например, ты мог бы сделать тестовый метод проверки наличия пользователя, или проверки токена. Но если можно обойтись без этого, то проще не использовать их, так как это лишний код.
Можно проверить можно ли залогиниться/разлогиниться после этих методов.
>> В sequlize-typescript можно указать директорию с моделями, но webpack транспилирует всё в один файл, и это ломается.
>
>Можно придумать какой-то костыль, например, читать список файлов и создавать файл "models.js", импортирующий все эти модели. Пока моделей мало, проще просто вручную перечислить.
Я плохо разбираюсь как настраивать webpack. В проблемах этой библиотеке, мне сказали, что нужно написать плагин для этого.
Буду пока вручную перечислять, хоть и знаю что это плохо.
>> Я считаю, что это вопрос доверия пользователя, оно есть в каких-то границах, и эти границы могут как и широки так и малы, т.е. я пытаюсь сказать что зависит от пользователя и его доверия. Я думаю, что для приложения с открытом кодом можно даже задуматься и о вайтлисте js.
>
>Проблему можно решить, выложив клиентский код в открытый доступ и собрав из него electron-приложение. Если мы сами собрали его из проверенных исходников, то никто не сможет подменить код клиента.
Вот именно. Код и так будет в открытом доступе. Только, к сожалению, не каждый пользователь сможет сделать это самостоятельно. Даже я думаю, что это не так просто, как вы говорите.
Что вы знаете о других инструментах созданий приложений? Я знаю о таком инструменте как React Native и нагуглить NativeScript, но учитывая свой печальный опыт с React'ом, я боюсь что с ним тоже будет не всё так гладко. Ну конечно же, это ни секрет что JS медленный.
Вы знаете что-нибудь об их этих инструментах и как легко с ними будет работать? После ваших слов, создаётся ощущение, что в случае с Electorn'ом это действительно будет достаточно склонировать репозитирий и собрать из него десктопное приложение - и мне интересно, будет ли здесь всё так же просто?
>> Можно, но мне наоборот кажется это не очень красивым, когда в URL что-то появляется. Хочу чтобы было так.
>
>Так это не сохраняет состояние. Если мы обновим страницу, или используем навигацию в браузере (вперед/назад), то email потеряется. Потому аргументы и хранят в URL.
>
>Тут тогда логичнее было просто сделать единую страницу регистрации/логина на одном роуте с двумя формами и скрывать одну из них. А email хранить в обычной переменной. И не понадобятся такие явно ненадежные костыли.
Мне кажется так удобно будет для каждого пользователя. Например, мы же не можем знать заранее зарегистрирован человек или только хочет зарегистрироваться, и всё что ему нужно это просто ввести свой email и дальше система определит, что он хочет сделать. Я такое видел только в моей бывшей любимой соц.сети - tumblr'е, но сейчас они убрали эту функцию. Я считаю это очень удобным и типо приветливым.
>> Проверить... что, например, вот для серверной части есть проверка, что вернулся StorageWrapper и всё работает, а для браузерной тоже аналогично. Это не нужно?
>
>Проверять это логичнее, пытаясь что-то туда сохранить, а не проверкой, к какому классу относится объект.
Понимаю, исправлю.
>> У роутеров WAMP'а на Node, нету возможностей для аутентифекации и авторизации соединения, и документации. Вышеупомянутая платформа развита лучше других.
>
>Да, но это можно добавить. А в случае с Питоном, ты не сможешь вызывать функции из приложения Node, тебе придется возможно дублировать их, или настраивать взаимодействие. Я не против такого варианта, но готовься к сложностям. И надо правильно спроектировать взаимодействие между питоновским и нодовским приложениями. Это уже почти микросервисы получаются со всеми их плюсами и минусами.
Можно вызывать функции приложения из Node с помощью Remote Procedure Calls паттерна реализуемым WAMP.
Я не знаю, о каких функциях приложения Node вы говорили, но у crossbar.io есть возможность сделать компонент(?) и на другом языке https://crossbar.io/docs/Guest-Configuration/?highlight=node.js
Что ты там выискивал, если тебе прямым текстом говорится, в чем ошибка? Похоже, что у них похапе без расширения ext-mbstring
Не работают там функции с приставкой mb_ , твёрдо и чётко.
PHP состоит из "ядра" и "расширений", которые добавляют в него дополнительные возможности. Часто расширения поставляются в комплекте с PHP и ты можешь даже не заметить, что ты их используешь.
Увидеть список основных расширений, и какие в них есть функции, можно в мануале: http://php.net/manual/ru/funcref.php
Кроме них, бывают еще сторонние расширения.
Про установку расширений можно почитать тут: http://php.net/manual/ru/install.pecl.php Под Windows обычно ты просто скачиваешь расширение, кладешь в папку и указываешь его в конфиге.
Народ по полгода вкатывается, а тут Васян пришёл - быстренько подайте ему супер-курс по двухдневному вкату.
Прост нахуй иди.
Неожиданно нашел решение, но мне кажется оно какое-то... Ну не то оно какое-то. Пните, скажите как надо было сделать красивее.
https://ideone.com/N2Zr7R
Благодарю! Пока что эта больше всего времени заняла, хотя решалась в одно действие, но тем приятнее все же было победить.
>Посоветуйте какую-нибудь книжку (а лучше видеокурс), чтобы быстро вкатиться в какой-нибудь простой пэхапэ-фреймворк
Документация к Laravel\Symphony\etc.
Следующий.
>супер-курс по двухдневному вкату
Кто тебе сказал, что по двухдневному?
>Народ по полгода вкатывается
Полгода вкатывается во что? В запись файлов на пхп? Соболезную
Как тебя в пр занесло, ты же элементарно логически мыслить не можешь.
>>67662
>Документация к Laravel\Symphony\etc.
Документация == видеокурс? Похоже, у тебя с восприятием еще хуже, чем у антона выше, лол или ты просто хреново умеешь читать представляю, какой у тебя код
>>67655
Тосто и тупо тут, видимо, все аноны такие же тупые?
Ага, я понял, поэтому сменил тему разговора со своего реквеста на обсуждение тупости местных анонов
>Давай дальше развлекай.
Хорошо
>запись файлов на пхп
А ты в своих проектах на пхп запись в файл, видимо, не используешь, дегрод? Но я не удивлюсь, если ты еще и чтение из файла не осилил.
>видеокурс
Нейрофизиологи давно доказали, что обучение через восприятие визуального контента более эффективно. Тем более, нормальные люди смотрят уроки/лекции на скорости х2 и выше. Но тут надо, конечно, обладать достаточно развитым абстрактным мышлением.
>называет кого-то тупым
Всегда приятно тупого назвать тупым, ведь он от этого начинает бомбить. А нормальному человеку всегда приятно, когда страдает тупой дегенерат. Да и как еще можно назвать человека, который думает, что любой курс для быстрого вката это именно "супер-курс по двухдневному вкату".
Пиши, если тебя что-нибудь еще заинтересует
Парни, подгоните этому гению 15 часовой курс лекций по конкатенации строк в пыхе, у кого есть.
А ещё тут где-то анон ходил, с 9 часовым курсом по регуляркам.
>Парни, подгоните этому гению 15 часовой курс лекций по конкатенации строк в пыхе, у кого есть.
Опять сказал какую-то херню и даже не понял, что сказал херню
Держу пари, ты этот самый фантазер >>67653
>А ещё тут где-то анон ходил, с 9 часовым курсом по регуляркам.
Снова нерелевантная хуйня. Я бы на твоем месте прошел курс по мат.логике от мфти
Ты смотри, пацан, такая манера общения быстро прилипает. Сам не заметишь как в реале хамить людям начнёшь, а там в щи быстро прилетит административно или физически.
>Сам не заметишь как в реале хамить людям начнёшь, а там в щи быстро прилетит административно или физически.
В реале я с такими мудаками обычно не сталкиваюсь. А если сталкиваюсь, то они никогда не входят в круг моего постоянного общения, очевидно же. Ну а незнакомым людям на улице я и не хамлю без причины, так что за щи не беспокойся
>вообще токсичное чмо
Проиграл с адеквата:
>токсичное чмо
>>67655 >>67653 >>67662
>Проиграл с адеквата:
Это разные люди, токсичный шизик. Ты сам не понимаешь чего несёшь. Не позорься и замолкни лучше.
>Это разные люди, токсичный шизик.
А где я сказал о том, что это один и тот же человек? Я намекнул на то, что токсичная мразь не я, а они.
>Ты сам не понимаешь чего несёшь.
Нет, я прекрасно понимаю.
>Не позорься и замолкни лучше.
Я пока еще ни разу не опозорился, никаких противоречий в моих словах нет, это твои маняпроекции.
>токсичная мразь не я, а они
Так-то тебе вполне нормально посоветовали. Хочешь по-быстрому, то читай доки. Не поймёшь 80% так как у тебя базы нет так как ты с программизмом никак не связан, но ты сам просил "чтобы быстро вкатиться", а не нормально - значит страдай.
По курсам от "Профита" не скажу - не смотрел.
Да, название дурацкое, но это самый понятный курс по mvc из всех что я посмотрел.
https://pr-of-it.ru/courses/php-2.html
$regexp = '/([.,!?])( )([а-я])/u';
$text = preg_replace($regexp, '$1$2mb_strtoupper($3)', $text);
Однако нихера, программа вместо перевода $3 в верхний регистр просто выводит mb_strtoupper буквально. Как сделать так, чтобы прога воспринимала это как команду?
У тебя функция в кавычках - конпелятор её как строку воспринимает.
1. функция в строке работать не будет;
2. $3 - эту хуйню в строке ждет функция preg_replace, для mb_strtoupper - она нихуя не значит, поэтому сделать так, как ты задумал нельзя. Другие способы ищи
Нужно использовать preg_replace_callback. Ты создаешь функцию, передаешь ее и preg_replace_callback вызывает ее на каждое совпадение с регуляркой и спрашивает, на что заменить текст.
P.s это задачка.
>Ведите на экран только те числа из массива, которые начинаются на цифру 1, 2 или 5.
>$elem[0], это индекс массива или что?
Да, индекс массива.
Но в твоём примере цикл foreach разбирает массив на элементы, возвращая их уже без индекса. То есть у $elem нет индекса, это тупо переменная со значением.
А если интересно, то у каждого элемента твоего массива, значения проставляются автоматически по порядку добавления. У первого добавленного элемента будет 0, у второго 1 и т.д.
Вот как оно выглядит
$arr = [0 => 10, 1 => 20, 2 => 30, 3 => 50, 4 => 235, 5 => 3000];
И да, не нужно числа оборачивать в кавычки '' из-за этого они становятся строками.
Огромное спасибо, я довольна дотошный, за это отдельное спасибо.
>И да, не нужно числа оборачивать в кавычки '' из-за этого они становятся строками.
P/s
Просто не смог найти больше подробностей, по поводу
>Но в твоём примере цикл foreach разбирает массив на элементы, возвращая их уже без индекса. То есть у $elem нет индекса, это тупо переменная со значением.
Намекну. Чтобы отрезать первую цифру от цельного числа, можешь использовать функцию substr($number, 0, 1).
Помог как смог.
>substr
Спасибо, но она со строками работает, а я сделал ошибку, анон выше правильно заметил, я числа в строку превратил, зачем? да хуй знаетдумал всегда надо оборачивать в ковычки, любые данные.
>Просто не смог найти больше подробностей, по поводу
https://ideone.com/WAO6Ts
Примерно так оно работает, короче с опытом придёт понимание.
>Спасибо, но она со строками работает
Соображаешь, но она может приводить числа к строке и успешно их считать. Хотя при строгом сравнении === это не прокатывает.
>Примерно так оно работает, короче с опытом придёт понимание.
Надеюсь, я понял как происходит индексация элементов массивавсегда с 0, но можно это переделать за счет удаления , я просто не мог понять, как скрипт понимает, что надо выдергивать определенные элементы из массива, еще и с определенными условиями.
P/s.
https://ideone.com/WAO6Ts
Я посмотрел, да красиво и элегантно непонятно мне еще учиться и учиться до PHP_EOL (string) .
Спасибо еще раз за подробное объяснение, у меня проблемы с циклами, вот я и решаю с разных сайтов задачки, что могу решить, хочу довести до автомата это понимание.
>я просто не мог понять, как скрипт понимает, что надо выдергивать определенные элементы из массива, еще и с определенными условиями.
$массив = [ключ => значение];
$значение = $массив[ключ];
$array = [4, 20, 34];
То же самое, что и:
$array = [0 => 4, 1 => 20, 2 => 34];
$array[0] вернет число 4;
Понятно, теперь все по полочкам в моей голове.
Первый передается в конструктор и там уже берется зависимость. Контейнер не передается в конструктор, он передает зависимость аргументом извне.
https://github.com/medbrat13/students-list - код
http://w91118rv.beget.tech/ - сайт на бесплатном хостинге
20 дней в сумме. Бывало, что над какой-то мелочью думал целый день, а бывало, что целый компонент мог написать за пару часиков. Но это нормально в программировании, как я понял.
В фаерфоксе таблица не на всю ширину открывается, какие-то баги.
Анон, как занести в массив несколько совпадений, а не только первое?
Нашел PREG_SET_ORDER.
Но оно выводит массив, который в массиве, который в массиве.
Как просто вывести совпадения?
Понял, спасибо.
Да и так его использую, но он выводит тогда "МассивМассив".
Когда и вызываю [$i[0]], то он выдает пустоту.
Когда вызываю просто [$i], то получаю массив, в котором массив, в котором массив.
Как фиксить?
>Когда и вызываю [$i[0]], то он выдает пустоту.
Ты пытаешься получить значение нулевого ключа в переменной $i, которая не является массивом и вообще содержит число.
Вбрось код с ideone, посмотрю.
Я думал, что $i тоже массив, так как при использовании echo мне выводит, что $mails[$i] это ArrayArray.
Вот так можно. Нулевой индекс содержит все совпадения (в твоем случае, все индексы содержат все совпадения), берешь и достаешь оттуда мыла.
https://ideone.com/n9kudx
preg_match находит только первое совпадение, тебе нужен preg_match_all. Придется раскурить мануал, и поработать с var_dump, чтобы понять, в каком формате он возвращает данные.
Спасибо тебе, я понял свою ошибку.
Я выводил [ 0 [0] ],то есть, внутри одних скобок, писал другие.
Надо было просто рядом поставить, блять.
Да ничего, я в начале пути еще не так ошибался, лул.
Да то же самое решение, просто он вместо вайла взял форыч, и регулярку прям в функцию вставил.
>Autoloader.php
Почему ты не реализовал загрузчик классов как на пике, а написал целый класс? Мне думается, что громоздко вышло.
>init.php
Почему ты его решил в конфиг положить? Я такое обычно прямо в корень кладу - инициализация приложения вроде как.
Или ты просто не заморачивался на этот счёт?
Намотал себе на ус несколько твоих приёмов.
Не задумывался каким образом будешь показывать страницу ошибки, если пользователь захочет зайти на несуществующую страницу или где-то выбросится исключение?
Мне кажется лучшим вариантом будет перехватывать исключения во фронт-контроллере и выводить соответсвующие ошибкам страницы, попутно записывая их в лог.
мимо другой анон, делающий студетов
Я лично постоянно забываю, что означают все эти классы буквенные в регулярках. Намного нагляднее писать вместо \S - [a-z0-9.+_-], сразу видно, что ищем, читается проще, хоть и длиннее. + можно отредактировать тут же, если какой-то символ не понадобится.
>каким образом будешь показывать страницу ошибки
Если маршрут не найден, то маршрутизатор возвращает заранее определённый адрес страницы ошибки. Потом всё это дело идёт на выполнение.
Но я про автозагрузку классов спрашивал, а не роутинг. Это немного разное.
>Почему ты не реализовал загрузчик классов как на пике, а написал целый класс? Мне думается, что громоздко вышло.
Видел бы ты, сколько у композеровского загрузчика классов, вроде 5-8, если мне не изменяет память.
Я размышлял так: автозагрузчик это ведь тоже некая сущность, а для каждой сущности нужен свой класс.
>Почему ты его решил в конфиг положить?
Потому что это конфигурационный файл. Он подключается во фронт-контроллер и отрабатывается. Константы рассасываются по коду, сервисы создаются, остальные классы технические типа автозагрузки подключаются. Можно его и в корень, но я не люблю, когда файлы просто так валяются. Если два файла имеют что-то общее, для них создается каталог. Если файл один, и его некуда запихнуть, то это, как правило, какая-то точка входа или особняком стоящий класс по моему скромному опыту.
>Намотал себе на ус несколько твоих приёмов.
Поднял мне чсв, лол. Только не копипасть, не думая. Я даже если копипащу что-то откуда-то, то всегда переписываю другим способом, чтобы понять, как это работает.
>автозагрузчик это ведь тоже некая сущность
Но получается так, что при добавлении нового неймспейса тебе приходится лезть в этот класс и править его исходник, дописывая пути.
Алсо, обрати внимание на этот диалог >>68140 так как твой DI это Сервис-Локатор, а не Контейнер Зависимостей. Если я не ошибаюсь, то у тебя он целиком в конструкторы передаётся, что есть бад-практис, так как в норме сторонние классы не должны знать поимённо сервисы в контейнере зависимостей - внутри они их по своему даже могут называть. Потом просто при тестировании проблемы будут, и при изменении имён сервисов тоже возни много - по всему коду лазать, вместо правки в месте создании объекта.
Забыл про расширение на затемнение всех сайтов, подумал, пиздатый ты дизайн сделал, въеби мб так приммерно или это мне уже глаза выжигает белый с непривычки, если отключить расширение?
>Но получается так, что при добавлении нового неймспейса тебе приходится лезть в этот класс и править его исходник, дописывая пути.
Щас уже не вспомню, почему я так сделал. Была какая-то причина вроде той, что этот анон пишет. >>68228
>Алсо, обрати внимание на этот диалог >>68140 так как твой DI это Сервис-Локатор, а не Контейнер Зависимостей.
Это я и отвечал.
> у тебя он целиком в конструкторы передаётся
Он целиком передается только в контроллер, чтобы не передавать все зависимости вручную и в класс запуска приложения. Это этап загрузки приложения, так что тут не стоит говорить плохой практике.
>в норме сторонние классы не должны знать поимённо сервисы в контейнере зависимостей
А они и не знают. Сервисы это ведь не сами классы, а способ их создания. В шаблоне Service Locator контейнер передается в конструктор класса и уже там извлекаются зависимости, а в моих сервисах передаются только сами зависимости.
>>68243
Я дизайн на коленке сваял, а цветовую схему взял с картинки какой-то в стиле 80х. Мне не режет ничего. Да и не дизайнер я, чтобы глаза твои беречь, лол.
ОП, в правильном ли направлении я думаю?
Покопался щас в своем коде. Да, действительно, в описании Connection я напрямую передаю контейнер в конструктор класса. Это ошибка, которую я не исправил. Я в самом начале написания программы где-то прочитал, как нужно готовить DI-контейнер и это оказалось брехней. Человек спутал локатор с контейнером. Но я обязательно исправлю.
>Человек спутал локатор с контейнером.
Частое явление, как я смотрю - тоже встречал такое пару раз. Подводные камни как есть.
Материалов нет нормальных на русском. А что есть - либо какая-то общая информация из переведенных надмозгом книжек, либо неверное понимание шаблонов. Почти везде одна теория. А практика если и есть, то очень сферическая в вакууме. Нет кода из реальных проектов, пусть даже учебных.
Про платную информацию не говорю, не знаю, может, и есть что-то.
>Материалов нет нормальных на русском.
Ну, кроме ОПовских. Но часто ли его ссылка мелькает в выдаче гугла? Хотя это хорошо, с другой стороны, конкуренция слабее.
Вроде бы и флаг верный, но работает только с первым массивом, со вторым - второй пикрил.
Может я чего-то не понял, но прег_сет_ордер должен раскидывать по массивам совпадения, но при вызове второго массива я получаю хуй.
1) В статье про архитектуру MVC в программе сервиса для управления студентов есть строчки типа:
/
@var Post[] Список объявлений
/
что это значит? Зачем нужна собачка?
2) И еще, если кто сможет объяснить, там есть дерево файлов. Можете объяснить всю схему:
Есть 2 папки public и view. На этом уровне есть файлы bootstrap и Post. А где PostService? Он просто странно расположен.
Статья про MVC
https://github.com/codedokode/pasta/blob/master/arch/mvc.md
Ой, там звездочки были в первом примере. Две звездочки после /, одна звездочка перед @var и одна звездочка перед /
Попробую обойти вакабу ари помощи комментов, но если что в статье можно найти
>/
> @var Post[] Список объявлений
> /
Так это многострочный комментарий, почитай в гугле, что такое phpdoc.
>А где PostService? Он просто странно расположен
Где угодно. MVC-шаблон не обязывает хранить модели, контроллеры или представления только в определённых папках. Можно даже просто в корень всё бросать, правда это не очень удобно.
Я в таких маленьких приложениях классы в папку lib кладу. Вроде как бибилиотека классов.
Та ты заебал со своими регулярками. Я тебе скинул уроки - смотри, там все есть, доступно и понятно
^ - вот этот символ означает начало строки. То биш в твоей регулярке всё что не является первым словом не соответствует регулярному выражению.
А вjобще удобней проверять регулярки если сразу наглядно видно как она работает. Можешь посмотреть как она работает на таких сайтах как regexr или regex101.
https://regexr.com/
https://regex101.com/
Будет нужно сделать что-то подобное?
https://ideone.com/iF8vXE
>Стоит ли пилить подобный класс в списке студентов?
Думаю, что можно и без него, но с ним будет лучше - по PSR.
>Я так понимаю это нужно для тестирования?
Нет. Это абстракция скрывающая ненужное, и выделяющая существенное. Чтобы поворачивать на авто удобно руль использовать. При этом не нужно знать что под ним, как он крепится, и как управляет механизмами поворота колес. Request работает по такому же принципу. Поэтому дело тут отнюдь не в тестировании.
Конечно, используй. Никакого требования использовать Апач нету. Только вместе с ним тебе скорее всего придется освоить php-fpm (менеджер процессов PHP и FastCGI сервер).
А пригодится это вообще в работе дальнейшей? Не то чтобы я пекусь о том, что зря, а что нет, просто у меня срок сделать все задачи из оп-поста до сентября, хочу все успеть.
Нужен толковый чувак, веб-разработчик широкого профиля (фуллстак).
Удаленка, полностью свободный график, минимум контроля, отсутствие корпошизы (образование, внешний вид, пол и прочее тоже пофиг). Можно делать быстро и ебланить большую часть дня (не перегружаю работой), можно делать дольше, если что-то не умеешь, подождем.
Языки программирования: PHP (на сервере 7.2), JS, HTML/CSS, SQL (на сервере MySQL или MariaDB).
Фреймворки: Yii, jQuery.
Технологии и инструменты: scss(sass), gulp, git, PHPStorm.
Нужен опыт работы над крупными yii-проектами (крупнее лаба10.пхп и сокращателя ссылок).
ЗП 40-50 в месяц (на руки). Если вы крутой и с ходу хотите больше (более-менее адекватно предложению) - говорите, сколько.
Задачи: доработка существующего продукта, который, по идее, написан неплохо (MVC, вся фигня), с нынешним разработчиком познакомлю, введем в курс дела.
Обязательно пригодится. Нужно понимать разницу между ними. Понимать какие задачи они лучше или хуже выполняют.
Понял, благодарю за ответ.
>>68674
Ну, недельку могу подождать. Но там же всякие особенности типа актив рекорд с актив форм, гридвью, миграциями (хотя эти вроде везде есть), все дела. Мне главное, чтобы не вышло ситуации, когда кодер не справится, свалит через месяц-два, а кодовая база к тому времени будет представлять собой адовую лапшу без mvc и psr, за которую уже никто адекватный точно не возьмется. Могу какое-то тестовое задание дать или предложить перепилить ваш ларавель-симфони проект (крупнее сокращалки ссылок) на йии с его фишками, показать код текущему разрабу, если ему будет ок, возьму с радостью.
Я вас наверное заебу, простите.
На циклах я опять сломался, логика никогда не была моей сильной стороной, но всё-таки осилил.
Пожалуйста, напишите замечания, можно ли было сделать лучше и подскажите возможно ли менее заёбно работать с процентами?
https://ideone.com/RCwvSU
>возможно ли менее заёбно работать с процентами?
Потому тебе и заёбно, что у тебя неправильно. Проценты в теле цикла считаются, а не в объявлении.
Хорошо, но какая будет разница, поменяй я местами слагаемые ?
Я к тому что я не могу написать "+15%", приходится делить на сто и у множать на процент. Можно это как-то сократить?
Окей, спасибо.
>возможно ли менее заёбно работать с процентами?
Типа вот так:
$rubles = $rubles * $percent, где $percent = 0.1
ты имеешь ввиду?
>>68691
Не надо здесь ставить скобки: ($age++); Можно писать просто $age++;
Точка с запятой завершает команду и скобки тут ни на что не влияют.
Лестницы, светофоры, витрины, автобусы, такси. Гугл нейронку-водителя на нас обучает что-ли?
Мосты точно были.
Превед интернед
Иногда всплывают, такое ощущение, будто им нужны веб-макаки для простой (реально простой) работы.
Может я не понимаю чего- то.
Или они набирают нуфагов, чтоб заточить их под работу в своей конторе?
> Или они набирают нуфагов, чтоб заточить их под работу в своей конторе?
Возможно, это же стажировка. Набирают чернорабочих за еду. А может просто так написали, а на собеседовании ебут всякой хуйней и хотят найти какого-то омежку стронг-джуна за еду.
Тут это где? Просто несложный, документированный фреймворк. На чем прошлый разраб писал, на том и писали, на самом деле, просьба только была, чтоб распространенное что-то, а симфони, ларавель или йии это будет - неважно. Почему нет?
Вообще, самый простой вариант авторизации, который приходит в голову - это при регистрации сгенерировать каждому пользователю униакльный длинный токен и сохранить БД. Когда пользователь логинится, мы ставим ему в куки токен. Когда он заходит на защищенную страницу, мы по токену находим данные пользователя в БД и проверяем, что у него есть доступ.
Сессии - это чуть усложненная версия. Сессия это индивидуальное для каждого пользователя хранилище данных на сервере (в простейшем случае просто файл). Можно создавать сессию и в нее писать id пользователя. Но сессии обычно удаляют, если их не использовали некоторое время, и пользователя будет разлогинивать. Мы можем продлить время жизни сессии, но тогда наш диск будет забиваться старыми сессиями.
>>56794
> if($this->correctAnswer == $answer) {
> return true;
Так как оператор == уже возвращает true/false, то можно писать просто return $this->correctAnswer == $answer;
> if (($this->correctAnswer == $answer) || (($answer <= $plusDeviation) && ($answer >= $minusDeviation))
Первое сравнение на равенство лишнее.
А так, решено правильно.
Вот эта версия с конструкторами >>57141 еще лучше, так как не позволяет создать объекты без указания их свойств.
>>57720
Хорошо, ответ верный.
> https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/students.sql#L24
> `hash` varchar(100) NOT NULL,
Здесь стоит добавить комментарий (COMMENT ...) с пояснением, что в этой колонке.
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/bootstrap.php
Ты сделал DI так, что для контроллера он возвращает функцию-фабрику, которую надо вызвать, чтобы получить сам контроллер. Но лучше было бы решить это на уровне самого DI контейнера, например, сделать отдельный метод bindFactory, чтобы указать, что этот объект создается функцией. А получатель просто вызывает $di->get() и не задумывается о том, как именно создан объект.
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/helpers/DIContainer.php#L37
Нет смысла писать throw и catch рядом. Ты можешь заменить их на обычный if, и код будет проще читать.
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/exceptions/DIContainerException.php
Тут ошибка. Исключение - это объект, хранящий информацию об ошибке. Но в нем нет обработчика, который решает, что делать в случае ее возникновения. Если ты хочешь при ошибке показывать страницу, то проще всего ловить ошибку где-то в роутере, и выводить страницу ошибки. Не забудь также логгировать исключение, например, с помощью error_log(). В твоем варианте разработчик даже не узнает про ошибку.
Урок про исключения: https://github.com/codedokode/pasta/blob/master/php/exceptions.md
В роутере мне не нравится, что у тебя у одной страницы может быть много URL, например: /registration, /registration/xyz. Это плохо для поисковой оптимизации.
Сообщения о коммитах плохие: https://github.com/asdasdasdasddasasdasdas/StudentList/commits/ads
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/controller/MainController.php#L30
> $countStudents = $_GET['search'] !== '' ?
Здесь при отсутствии ключа search в массиве генерируется notice. Странно, что ты его не заметил - может, у тебя отключен их показ?
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/model/StudentTableGateway.php
Здесь стоило использовать DI для передачи соединения с БД снаружи. Класс DBConnector тогда становится не нужен.
В коде очень мало комментариев. Понятно, что многие места очевидны, но перед некоторыми классами вроде Paginator и некоторыми публичными методами стоит их добавлять. Перед методами контроллеров удобно писать, за какую страницу они отвечают. Заодно почитай про phpDoc.
Стоит добавлять тайп-хинты (string, int) в аргументы функций. Это защищает от ошибок и документирует код.
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/model/StudentTableGateway.php#L70
Здесь в getStudents(int $offset, int $limit) в SQL запросе ты не указал способ сортировки. Это значит, что БД может возвращать любые записи, например, одни и те же записи для любой страницы. То есть конструкция OFFSET без сортировки не имеет особого смысла.
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/model/StudentTableGateway.php#L143
Проверка сделана некорректно тут:
> } elseif ($result['id'] == $id && $result['email'] == $email) {
Может быть такое, что найдется 2 записи, но первой вернется запись с нужным id, а вторую ты не проверяешь.
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/controller/ProfileController.php#L83
тут можно было сделать вспомогательную функцию, чтобы не копипастить длинные выражения.
> $student = $this->auth->getAuthUser($this->auth->getHash());
Странно, что мы должны передавать хеш, хотя $this->auth его и так знает.
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/helpers/Authorization.php#L22
> public function IsLoggedIn()
> {
> return isset($_COOKIE['hash']) ? true : false;
Этой проверки недостаточно, чтобы считать пользователя залогиненным. Что, если он сам себе поставит куку hash=1 ?
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/view/partials/form.php
Тут стоит добавить атрибуты для HTML5 валидации до отправки формы.
> <?= $student->gender == 'f' ? 'checked=checked' : null; ?>
echo выводит только числа и строки, неправильно передавать на вывод null
Радует, что ты прочитал про SQL-инъекции и XSS. А есть ли у тебя защита от XSRF?
> if ($this->paginator->getPreviousPage() !== null
Красивее писать if ($this->paginator->hasPreviousPage()), а еще лучше $paginator->hasPreviousPage()
При ошибке 404 надо отдавать соотв. HTTP-код. То же самое касается страницы ошибки в приложении.
Сессии наверно не нужны в index.php?
Вообще, пока впечатление по коду хорошее.
> https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/students.sql#L24
> `hash` varchar(100) NOT NULL,
Здесь стоит добавить комментарий (COMMENT ...) с пояснением, что в этой колонке.
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/bootstrap.php
Ты сделал DI так, что для контроллера он возвращает функцию-фабрику, которую надо вызвать, чтобы получить сам контроллер. Но лучше было бы решить это на уровне самого DI контейнера, например, сделать отдельный метод bindFactory, чтобы указать, что этот объект создается функцией. А получатель просто вызывает $di->get() и не задумывается о том, как именно создан объект.
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/helpers/DIContainer.php#L37
Нет смысла писать throw и catch рядом. Ты можешь заменить их на обычный if, и код будет проще читать.
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/exceptions/DIContainerException.php
Тут ошибка. Исключение - это объект, хранящий информацию об ошибке. Но в нем нет обработчика, который решает, что делать в случае ее возникновения. Если ты хочешь при ошибке показывать страницу, то проще всего ловить ошибку где-то в роутере, и выводить страницу ошибки. Не забудь также логгировать исключение, например, с помощью error_log(). В твоем варианте разработчик даже не узнает про ошибку.
Урок про исключения: https://github.com/codedokode/pasta/blob/master/php/exceptions.md
В роутере мне не нравится, что у тебя у одной страницы может быть много URL, например: /registration, /registration/xyz. Это плохо для поисковой оптимизации.
Сообщения о коммитах плохие: https://github.com/asdasdasdasddasasdasdas/StudentList/commits/ads
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/controller/MainController.php#L30
> $countStudents = $_GET['search'] !== '' ?
Здесь при отсутствии ключа search в массиве генерируется notice. Странно, что ты его не заметил - может, у тебя отключен их показ?
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/model/StudentTableGateway.php
Здесь стоило использовать DI для передачи соединения с БД снаружи. Класс DBConnector тогда становится не нужен.
В коде очень мало комментариев. Понятно, что многие места очевидны, но перед некоторыми классами вроде Paginator и некоторыми публичными методами стоит их добавлять. Перед методами контроллеров удобно писать, за какую страницу они отвечают. Заодно почитай про phpDoc.
Стоит добавлять тайп-хинты (string, int) в аргументы функций. Это защищает от ошибок и документирует код.
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/model/StudentTableGateway.php#L70
Здесь в getStudents(int $offset, int $limit) в SQL запросе ты не указал способ сортировки. Это значит, что БД может возвращать любые записи, например, одни и те же записи для любой страницы. То есть конструкция OFFSET без сортировки не имеет особого смысла.
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/model/StudentTableGateway.php#L143
Проверка сделана некорректно тут:
> } elseif ($result['id'] == $id && $result['email'] == $email) {
Может быть такое, что найдется 2 записи, но первой вернется запись с нужным id, а вторую ты не проверяешь.
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/controller/ProfileController.php#L83
тут можно было сделать вспомогательную функцию, чтобы не копипастить длинные выражения.
> $student = $this->auth->getAuthUser($this->auth->getHash());
Странно, что мы должны передавать хеш, хотя $this->auth его и так знает.
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/helpers/Authorization.php#L22
> public function IsLoggedIn()
> {
> return isset($_COOKIE['hash']) ? true : false;
Этой проверки недостаточно, чтобы считать пользователя залогиненным. Что, если он сам себе поставит куку hash=1 ?
https://github.com/asdasdasdasddasasdasdas/StudentList/blob/ads/app/view/partials/form.php
Тут стоит добавить атрибуты для HTML5 валидации до отправки формы.
> <?= $student->gender == 'f' ? 'checked=checked' : null; ?>
echo выводит только числа и строки, неправильно передавать на вывод null
Радует, что ты прочитал про SQL-инъекции и XSS. А есть ли у тебя защита от XSRF?
> if ($this->paginator->getPreviousPage() !== null
Красивее писать if ($this->paginator->hasPreviousPage()), а еще лучше $paginator->hasPreviousPage()
При ошибке 404 надо отдавать соотв. HTTP-код. То же самое касается страницы ошибки в приложении.
Сессии наверно не нужны в index.php?
Вообще, пока впечатление по коду хорошее.
Есть небольшой недостаток, что нельзя добавлять новые профессии, не трогая существующий код. А так, пока неплохо, давай посмотрим, что будет с антикризисными мерами.
>>58554
> https://3v4l.org/NSVsb - задача про зарплату
overtime должен вычисляться автоматически. Все, что больше 40 часов в неделю - переработка.
> https://3v4l.org/4GGth - задача про вопросы
Тут можно было ставить тайп-хинт на возвращаемое значение:
> public function checkAnswer($answer): bool
А так, все верно.
> https://3v4l.org/3avqA - задача про Вектор
В качестве названий профессий можно было использовать константы-имена классов вроде Manager::class: http://php.net/manual/en/language.oop5.constants.php
addEmployees лучше бы вынести наружу из Департамента, так как это не позволяет создавать новые профессии, не трогая существующий код. Ну например, если твои классы будут оформлены в виде библиотеки, которую делает один человек, то другой человек, который ей пользуется, не сможет добавлять свои профессии. Случай, конечно, немного придуманный, но надеюсь, смысл передает. У тебя класс Department сейчас по сути содержит захардкоженный список возможных профессиий. А это не его зона ответственности - знать, какие профессии есть.
> public function getInformation(): array {
Мне кажется, было бы удобнее сделать несколько отдельных методов, но можно и так. Но name и employeesCount точно надо вынести отдельно. Так как неудобно будет их получать и никакой экономии от помещения их в этот метод мы не получаем.
Если доводить до абсурда, то мы тогда можем сделать в любом классе 2 метода setInformation и getInformation и работать со старыми добрыми массивами. Это не очень удобно, и в плане получения данных (надо извлекать их из массива), и в плане кода (все собрано в одном огромном методе).
Класс Table правильнее назвать TablePrinter, TableFormatter, так как он не представляет информацию о таблице.
А вообще хорошо, меня радует, что и у нас в треде, да и вообще в программистском сообществе стали лучше понимать ООП. А не как раньше, когда все сидели на CMS и городили жуткие конструкции на многомерных массивах.
Есть небольшой недостаток, что нельзя добавлять новые профессии, не трогая существующий код. А так, пока неплохо, давай посмотрим, что будет с антикризисными мерами.
>>58554
> https://3v4l.org/NSVsb - задача про зарплату
overtime должен вычисляться автоматически. Все, что больше 40 часов в неделю - переработка.
> https://3v4l.org/4GGth - задача про вопросы
Тут можно было ставить тайп-хинт на возвращаемое значение:
> public function checkAnswer($answer): bool
А так, все верно.
> https://3v4l.org/3avqA - задача про Вектор
В качестве названий профессий можно было использовать константы-имена классов вроде Manager::class: http://php.net/manual/en/language.oop5.constants.php
addEmployees лучше бы вынести наружу из Департамента, так как это не позволяет создавать новые профессии, не трогая существующий код. Ну например, если твои классы будут оформлены в виде библиотеки, которую делает один человек, то другой человек, который ей пользуется, не сможет добавлять свои профессии. Случай, конечно, немного придуманный, но надеюсь, смысл передает. У тебя класс Department сейчас по сути содержит захардкоженный список возможных профессиий. А это не его зона ответственности - знать, какие профессии есть.
> public function getInformation(): array {
Мне кажется, было бы удобнее сделать несколько отдельных методов, но можно и так. Но name и employeesCount точно надо вынести отдельно. Так как неудобно будет их получать и никакой экономии от помещения их в этот метод мы не получаем.
Если доводить до абсурда, то мы тогда можем сделать в любом классе 2 метода setInformation и getInformation и работать со старыми добрыми массивами. Это не очень удобно, и в плане получения данных (надо извлекать их из массива), и в плане кода (все собрано в одном огромном методе).
Класс Table правильнее назвать TablePrinter, TableFormatter, так как он не представляет информацию о таблице.
А вообще хорошо, меня радует, что и у нас в треде, да и вообще в программистском сообществе стали лучше понимать ООП. А не как раньше, когда все сидели на CMS и городили жуткие конструкции на многомерных массивах.
Я приведу пример про числа и строки:
$a = 123; // это число. Его можно складывать, вычитать итд.
$b = "123"; // это строка. Это набор букв. Вычитать строки нельзя, но PHP
// автоматически преобразует строку в число, если ты попытаешься
// это сделать
Когда ты читаешь что-то из файла, из консоли, ты всегда получаешь строку. Есть функции intval, floatval, strval, которые делают преобразования между типами. Увидеть тип значения можно с помощью var_dump.
Вместо is_int ты можешь использовать is_numeric.
>>60327
Сделано неправильно. Ты сравниваешь только одну пару букв (самую первую и самую последнюю), и после сравнения завершаешь программу, а надо сравнивать все.
>>60712
Postgres интересная БД, у нее много фич, по ней есть хорошая актуальная документация на русском, стоит прочесть. Например, там много интересных видов индексов.
>>60810
Читай постепенно. Если ты не любишь читать документацию, то развиваться будет трудно.
>>60904
Сегодня я выучил новую поговорку.
Я приведу пример про числа и строки:
$a = 123; // это число. Его можно складывать, вычитать итд.
$b = "123"; // это строка. Это набор букв. Вычитать строки нельзя, но PHP
// автоматически преобразует строку в число, если ты попытаешься
// это сделать
Когда ты читаешь что-то из файла, из консоли, ты всегда получаешь строку. Есть функции intval, floatval, strval, которые делают преобразования между типами. Увидеть тип значения можно с помощью var_dump.
Вместо is_int ты можешь использовать is_numeric.
>>60327
Сделано неправильно. Ты сравниваешь только одну пару букв (самую первую и самую последнюю), и после сравнения завершаешь программу, а надо сравнивать все.
>>60712
Postgres интересная БД, у нее много фич, по ней есть хорошая актуальная документация на русском, стоит прочесть. Например, там много интересных видов индексов.
>>60810
Читай постепенно. Если ты не любишь читать документацию, то развиваться будет трудно.
>>60904
Сегодня я выучил новую поговорку.
Я имел ввиду что-то типа число * 10%
Ты можешь посмотреть, как сделано в Doctrine Extensions если тебя интересует именно реализация расширений: https://github.com/Atlantic18/DoctrineExtensions
Там используются события Доктрины.
Для простых случаев можно использовать просто трейты.
Также, на мой взгляд, если эти расширения сложно реализуются, то лучше не заморачиваться и сделать по-простому.
Плюс, надо помнить о производительности. Если тебе надо получить число лайков, то быстрее всего иметь поле с этим числом. А ставить лайк SQL-запросом (если хайлоад - то через очередь в редисе и групповые обновления). Да, не универсально, зато просто и быстро. Ну и в рамках Доктрины эффективные групповые обновления сделать будет трудно.
Вы обещали что с тройкой по математике можно!
Почему это неправильно? Что вообще нужно сделать? У меня уже опускаются руки.
https://ideone.com/QIFtDz
У тебя получился какой-то сложный код. Ну например, ты в первый раз всегда платишь 5000, независимо от суммы кредита. Вряд ли рациональный человек будет так делать.
Ты зря скопировал код, который был дан, так как в нем была специально сделана ошибка.
Давай попробуем составить более простой алгоритм.
Попробуй переписать код внутри цикла примерно так:
- прибавляем проценты и комиссию к остатку долга (!не вычитаем ничего пока!)
- если остаток маленький, выплачиваем сколько осталось и уходим
- иначе платим 5000
«Платим» здесь значит уменьшаем долг и увеличиваем общую сумму выплаченного.
И еще, вот здесь очень странный код:
if ($creditBalance < $monthlyPayment){
$monthlyPayment = $creditBalance; / Почему это не работает? /
echo "С меня хватит!\n";
break;
/ Почему это не работает? /
}
Что он должен делать? break выходит из цикла, а так как после цикла ничего больше нет, то на этом выполнение программы заканчивается.
как в твоем понимании должна была "работать" строка $monthlyPayment = $creditBalance; ? Она копирует число из одной переменной в другую. Но так как дальше идет break и программа завершается, то это уже ни на что не влияет.
Ни в коем случае не пытайся писать код наугад. Ты должен понимать, что ты хочешь сделать, что будет делать каждая строчка, что будет происходить, и только потом писать код, а не переставлять команды наугад в надежде, что оно как-то заработает. Даже если оно заработает, это не даст никаких навыков.
>Читай постепенно. Если ты не любишь читать документацию, то развиваться будет трудно.
Не в этом дело, просто гора чего еще в планах выучить/прочитать и postgres не в приоритете. Если бы для ознакомления надо было уделить немного времени то ок, а так придется отложить в долгий ящик
Имею опыт работы, пишу в ООП. Но меня отовсюду постоянно заебуют знакомые, что мол необходимо паттерны знать.
Несколько раз брался их изучить (читал/смотрел видеоуроки) но так и не понял нахуя они нужны?
Я понимаю например MVC - понятна суть такой структуры, но когда смотрю какуюнибудь фабрику, то не пойму о чем речь. Фабрика как я понял - это идея о порождении объектов другим объектом? Это и вся суть? Я постоянно пишу подобные структуры и не понимаю, зачем мне надо знать, что это называется фабрикой? Или я наделяю паттерны большим значением, чем они имеют на самом деле?
Увидел вот это - https://github.com/cztomczak/phpdesktop - и хочу спросить мнения анона по данной идее.
Большинство задач. с которыми часто приходится сталкиваться программистам. уже давным-давно решены другими членами сообщества. С помощью проектных шаблонов просто выделяют общие задачи, определяют проверенные решения и
описывают вероятные результаты. Задачи имеют свойство повторяться, и веб-программистам приходится решать их снова и снова. Со временем мы находим более или менее изящные ответы на эти вопросы и создаем неформальный набор методик, которые затем снова и снова используем в своих проектах. Эти методики - и есть проектные шаблоны.
Что касается знаний названий шаблонов, то наименование шаблона уже само по себе очень ценно; таким образом создается
что-то вроде общего словаря. Вот сравни две ситуации.
На совещании тебе дали задание, что в проекте имеет смысл сделал интерфейс для создания семейств взаимозависимых объектов, не специфицируя их конкретных классов. Это всё должно быть реализовано созданием абстрактного класса, который представляет собой интерфейс для создания компонентов системы. Далее должны быть классы, реализующие этот интерфейс."
Или просто сказали, что система должна создавать каждый набор с помощью шаблона Abstract Factory.
Зандстра, Мэтт.
РНР: объекты, шаблоны и методики программирования
>Зандстра, Мэтт.
>РНР: объекты, шаблоны и методики программирования
То читал. Технически понятно все, но зачем все то выделяется в отдельную дисциплину - не понятно.
>На совещании тебе дали задание, что в проекте имеет смысл сделал интерфейс для создания семейств взаимозависимых объектов, не специфицируя их конкретных классов. Это всё должно быть реализовано созданием абстрактного класса, который представляет собой интерфейс для создания компонентов системы. Далее должны быть классы, реализующие этот интерфейс.
Вот я и буду реализовывать то, что сказали. Зачем мне для этого знать, то, что кто-то это уже реализовывал и дал этому название?
>зачем все то выделяется в отдельную дисциплину
Шаблон описывает лишь общее решение. Не обязательно им слепо следовать и пихать куда не попадя. Если задачу решить без шаблонов проще и удобней, решай без шаблонов. Шаблон не самоцель, просто некий вариант решения.
>Зачем мне для этого знать, то, что кто-то это уже реализовывал и дал этому название?
Если у тебя возник такой вопрос, то видимо не зачем. Но если будешь часто сталкиваться с такой ситуацией, то рано или поздно запомнишь названия шаблонов. Считай это профессиональной лексикой.
https://ideone.com/IKruQg
То есть у меня список Id в одном массиве вида: 3,10,12,25
А другом значения.
Id выводит верный - итерируется, а значение - нет.
https://ideone.com/dCvwSr
Не брезгуй залезть в документацию по языку, когда работаешь с каким-то типом данных чтобы не приходилось писать уже существующие функции.
читай комменты
https://ideone.com/YBE0sH
без встоенных функций:
https://ideone.com/ZrcuSE
а лучше как анон >>69646 сделал.
на amazon
Могу ли я прихуячить js файл в php и он будет нормально работать?
Смогу ли я брать данные из sql и рендерить их при помощи js в пхп?
Сильно ли отличается PHP Bootstrap от ванильного?
https://ideone.com/Gow8Pf
как добавить первичный индекс цифру каждого из них к значению, в начало то есть перед всем остальным subarr[]? Что я делаю не так?
>Что я делаю не так?
Ты не изучил работу с массивами и не потренировался с ними.
Если ты взялся за реальную задачу, не имея скиллов, то земля тебе пухом, братишка. Тут тебя тащить никто не будет.
Потомучто ларавель разрабатывают программисты, а не двачеры или вкатывальщики с каргокультом
>>69119
Спасибо за подсказку, но всё равно не получается.
По моей логике если долг меньше ежемесячного платежа, то ежемесячный платеж нужно сравнять с остатком долга, соответственно заплатить его, и должен получиться "0". Но почему ноль не получается? Я не могу понять.
https://ideone.com/hcLusb
Ты в последний месяц (в твоем if) не уменьшаешь $creditBalance и не изменяешь $paymentTotal и делаешь break; из цикла. Поэтому выводится баланс и т.д. за предпоследний месяц.
В нормальных языках (C#/Java) нет никаких трейтов, люди следуют SOLID и пишут нормальный код. Ларавель пишут говнокодеры, ломающие обратную совместимость в минорных версиях ( https://github.com/laravel/framework/issues/27949 ), создающие трейты на каждый чих, порождая хрупкий код, вместо того, чтобы подумать и нормально разделить ответственности. Одно дело когда в модели переиспользуемые поведения, совсем другое - когда вся архитектура на запутанном месиве из трейтов и магических методов, которые нативно не поддерживаются IDE и статическими анализаторами. Частично помогают лишь сторонние костыли и заплатки. Не пишу на ларавеле больше года и очень рад, что больше не имею с ним дел.
Ну неужели у меня получилось, наконец-то!
Понял свою ошибку в понимании построения кода теперь скажи, это сильно пиздец и стоит попробовать как-то оптимизировать код и количество команд, или что-то вроде этого и должно было получиться?
https://ideone.com/CPJLAo
Что думаешь о Yii?
>Спустя 4 часа пердолинга
Поначалу - обычное дело. Вообще привыкай учиться сразу потом мозги сами нормально встанут.
Хорошо. Решай дальше.
- Понимаю MVC есть свой - вечнонедоделанный и нубский микрофрейм
- Работал с APIделал обёртки, встраивал, разрабатывал - разбирался
- Знаю пару библиотек с Гитхаба. И вообще очень ленивый - мне проще у кого модуль скачать и встроить главное чтобы без лютого говнокода и с доками, чем с нуля собирать.
- Знаю штук пять-шесть Структур Данных
- Предпочитаю писать на ООП - как-то легче на нём мне пишется. Из фреймворков только пока Слим пробовал
- Ну и всякое по мелочи.
Можно уже ходить по собесам?
С твоими знаниями я бы еще немного хтмл/ксс и js подучил и вкатился бы на собеседование.
Подскажите дурачку какой-нибудь простой финт ушами на чистом пхп (хотя можно какую-то библиотеку, главное чтоб юзать было просто) который якобы защитит меня от sql инъекций (нужно для диплома, просто показать, в гугле какие-то сложные вещи)
Начал пользоваться кодигнайтером для этих целей, но у меня стили не подгружаются, хотя гуглил и делал все как тут https://stackoverflow.com/questions/11581636/fatal-error-call-to-undefined-function-base-url-in-c-wamp-www-test-ci-applic (возможно это из-за галповских scss, хуй знает как все что на пике вообще в фреймворком подгрузить)
Начни со статьи про инъекции: https://github.com/codedokode/pasta/blob/master/security/sql-injection.md
Чем бэкенд-разработчик занимается по 8 часов в день? Как часто с базами данных работает?
справедливости ради стоит вспомнить, что по их схеме версионирования это мажорный апдейт
Пагни, использую symfony4. Нужно чтобы определённый пхп скрипт выполнялся перед загрузкой каждой страницы. В скрипте происходит обращение к удалённому сервису, который возвращает некоторое дерьмо. Это дерьмо должно быть доступно после в любом контроллере для последующей передачи во вьюхи.
Как это правильно реализовать?
Привет. Я бекенд-разработчик. Я пишу бекенд для сайтов, сервисов и всякие разные скрипты автоматизации. Но чаще нихуя не делаю. В среднем я работаю с базами данных по 146 раз в день.
>Но чаще нихуя не делаю
У меня друг сеньор-помидор с $3700 на руки. Говорит что если 4.5 часа за день кодировал и на митинге орал, то это был ппц напряженный день.
>>70358
https://symfony.com/doc/current/event_dispatcher/before_after_filters.html
>обращение к удалённому сервису, который возвращает некоторое дерьмо
Я бы подумал о выдергивании некоторого дерьма в кэш или бд скриптом по крону например, если время жизни некоторого дерьма позволяет.
Это плохая идея и приведет к багам и тормозам на сайте. Лучше от нее отказаться и реализовать по-другому.
>https://symfony.com/doc/current/event_dispatcher/before_after_filters.html
Спасибо за ответ по делу.
Устанавливаю зависимости через composer. Есть одна либа в приватном репозитории, у которой нет package.json и вообще файлов с метаинформацией.
Добавляю я её так
>{
> "repositories": [
> {
> "type": "package",
> "package": {
> "name": "xxx/package_name",
> "version": "1",
> "source": {
> "url": "
",
> "type": "git",
> "reference": "master"
> }
> }
> }
> ]
>}
Устанавливается она нормально. В папке vendor вижу склонированную репу. Но если она обновляется в удалённом репозитории и я делаю composer update - либа в vendor остаётся старой версии. Пробовал делать composer clear-cache - без изменений. Помогает только ручное удаление директории либы в vendor и запуск после composer update - только после этого скачивается новая версия из репозитория. Почему так происходит и как сделать так, чтобы composer скачивал ту версию либы, которая на данный момент находится в удалённом репозитории?
>хтмл/ксс
Вкатывался через вёрстку - могу делать небольшие правки. Даже пара лендосов собственноручно свёрстанных где-то есть, не то, чтобы очень хороших. Немного бутстрап пробовал, было дело и на вордпресс натягивал.
>js
Тоже об этом думаю. Ещё не трогал его.
Иногда стоит смотреть мануал. Вот, что тут написано про тип package: https://getcomposer.org/doc/05-repositories.md#package-2
> Composer will not update the package unless you change the version field.
То есть это для случая, когда "пакет" это просто зип-архив с фиксированной версией.
Тебе стоит попробовать тип VCS: https://getcomposer.org/doc/05-repositories.md#vcs
Версии пакетов автоматически определяются из меток (тегов) в репозитории.
Также, я сам не разбираюсь в композере, но тебе советовал бы полистать мануал. Например:
- https://getcomposer.org/doc/02-libraries.md
- https://getcomposer.org/doc/articles/aliases.md
> по их схеме версионирования
Я в курсе. Это значит, что разработчикам не хватает профессионализма, чтобы релизить новый функционал, не меняя код, на который завязались люди в своих проектах. Все серьёзные фреймворки умеют в deprecation warnings:
- Spring: https://beginnersbook.com/2014/07/deprecated-annotation-in-java/
- ReactJS: https://reactjs.org/blog/2017/04/07/react-v15.5.0.html#new-deprecation-warnings
- Symfony: https://symfony.com/doc/current/contributing/code/conventions.html#deprecations
В симфони так вообще есть Backward Compatibility Promise, в котором точно описано, что и в каком случае может поменятся: https://symfony.com/doc/current/contributing/code/bc.html
Думаешь им хочется с этим всем заморачиваться? Вряд ли, просто они понимают, что проекты на Symfony как правило большие, сложные и долгоживущие и ломать что-то в минорных версиях недопустимо. Да и глупо не следовать семантическому версионироваю в 2019-м году, когда любой более-менее популярный фреймворк ему следует.
В пхп должен быть подключен модуль mb_ из которого эта функция берется. Уебки ideone этого уже который год не делают. Используй другой сервис для пхп онлайн.
в идеоне нет поддержки многобайтовой кодировки, это означает что функций mb не будут там работать, добро пожаловать в пиайчпи (используй сендбокс)
я автор вопроса.
1. Нагуглил статьи и повторил.
2. Аналогично на ютубчике
Элементарно могу после этого тестировать классы/методы/исключения, так использовать анотации, создавать структуру файлов и прочее.
Но открыл доку и там яяяяябать сколько всего. Казалось бы тесты, но пиздец как лень читать
Благодарю
https://habr.com/ru/company/oleg-bunin/blog/444508/
https://habr.com/ru/company/oleg-bunin/blog/445512/
>>71053
У меня еще есть обзорный урок по тестам, если кому интересно: https://gist.github.com/codedokode/a455bde7d0748c0a351a
Все прочитал что есть, статьи огонь!
У меня такая проблема, вот я беру что-то учить и учу от корки и до корки.
Взяля за phpunit, все видосы на ютубе пересмотрел, твою статью еще давно прочел и другие из гугла. А сейчас доку читаю. Уже тупо не лезет инфа, потому-что я заебался учить, т.к. не вижу денег от своего труда, а мозг меня нахуй посылает из-за холостой работы.
И тот же yii2 учу, все видосы на ютубе (проматывал то чего знаю), статьи + вся дока для создания индексов в голове и чтобы знать что он может. Осталось добить RBAC, REST и прочее
А тестирование влезло как раз в yii2 и я уже надолго забросил yii и сейчас учу его, точнее все делаю кроме того чтобы учить, то видос обычный посмотрю, то в инете мемы посмотрю и тд, уже просто отвращение пошло, планировал весной выйти на работу добив yii так вот хуй.
бляя, глянул на список чего сам себе составил учить, там пиздец сколько всего(
справедливости ради стоит отметить, что я не пытался таким образом их оправдать
Я уверен, что это обязательно окупится. Поначалу, да, море информации кажется безбрежным, но когда ты, например, начнешь учить какой-то другой фреймворк, ты поразишься, сколько много там общего. Ну и на собеседовании, если ты начинающий, то тебя будут гонять по теории, и это тебе поможет произвести хорошее впечатление, чем кандидаты. которые что-то поверхностно знают, но не могут толком ничего рассказать.
Так что считай это своим плюсом. Учиться вообще тебе придется постоянно. На досуге можешь почитывать статьи про собеседования от яндекса. Когда ты сможешь там решить все задачи, то значит, знаний ты набрал достаточно.
В phpunit пишут что устарело
https://phpunit.readthedocs.io/ru/latest/database.html
И вообще, этого достаточно или еще надо
phpunit, codeception (там же и BDD подход), TDD
Этого хватит для middle?
1. Записи выводятся по 10 штук (пока) на страницу. Записи просматриваю с помощью fetch(). Поскольку подразумевается, что записей может быть много, делать fetchAll() неправильно. Соответственно, сначала мы считываем первые десять записей в массив и отображаем. А если нам сразу нужно будет отобразить записи с 30 по 40? Также в цикле делать fetch и просто пропускать записи? Как-то принудительно можно начать читать с определенной записи?
2. Вопрос вытекает из предыдущего - пагинация. Я правильно понимаю, кто кнопки пагинации содержат в своих гиперрсылках информацию о текущей странице, то есть на каждой странице ссылки разные вида, например, ?first_page=10&last_page=20 и потом мы это получаем в переменной $_GET? Я как бы сам до этого допер, но правильный ли это вообще подход? Может подскажете, где можно почитать про перенаправление и вот это вот все. Пагинацию тоже наверное имеет смысл сделать объектом?
3. Уже собственно про twig. Страница шаблона должна быть в одном файле или можно их как-то объединять несколько? Может ту же пагинацию в отдельном файле хранить? И еще, передача данных в шаблон передается массивом, что насчет быстродействия при передаче больших объемов информации?
4. Создание объекта PDO делаю так:
$db = new StudentsTable(new PDO('sqlite:db/students.db'));
Я почему-то из уроков по студентам сначала подумал, что привязка в объекту PDO будет в самом классе, но потом как-бы понял, что это свойство может быть присуще именно объекту. Поправьте меня, может я все же что-то не допонял.
Сорян за нубские вопросы, но немножко тупички небольшие возникли
Был тут с вами два года назад, принимайте пополнение, пикрелейтед
да хуй его знает, всякие моки хуеки, вот есть AR модель, как-то че-то пошуршать и заебенить, по пацанский четко хуяк хуяк ну ты понял, да?
>Поскольку подразумевается, что записей может быть много
В базе - много, а на руки запрашивай ровно столько, сколько надо - одну страницу, и разбирай всё что пришло. Пагинация получается - это смещение по твоим страницам.
>кнопки пагинации содержат в своих гиперрсылках информацию о текущей странице
Объект Пагинатор должен знать сколько у тебя всего записей, сколько выводится на одну страницу и текущее смещение. Этого достаточно чтобы получить общее количество страниц и текущую.
Я ему ещё иногда хтмл-шаблон для вывода всей ссылки отдаю - чтобы сразу оформленные блоки с номерами выдавал, а не тупо цифры. Удобно, но не везде любят такое.
>$db = new StudentsTable(new PDO('sqlite:db/students.db'));
А если с двумями таблицами будешь работать в приложении - вторую переменную создашь с ещё одним PDO? А если мне надо будет заменить объект PDO для тестов, а он у тебя гвоздями при создании вкручен.
1) Неправильно выбирать все записи, а потом читать только часть.
Чтобы выбрать только часть записей есть конструкция LIMIT/OFFSET: https://www.sqlite.org/lang_select.html
Обрати внимание, что LIMIT/OFFSET не входит в стандарт языка SQL и является дополнением, которое поддерживается только в некоторых СУБД вроде MySQL или SQlite.
Описание на русском есть тут на примере Postgres: https://postgrespro.ru/docs/postgresql/11/queries-limit (прочти внимательно).
Стандартные способы постраничной выборки данных есть в Википедии: https://en.wikipedia.org/wiki/Select_(SQL)#Limiting_result_rows
2) Я правильно понимаю, кто кнопки пагинации содержат в своих гиперрсылках информацию о текущей странице
Да, в в соответствие с буквой S (stateless) из REST.Сервер не "запоминает", какие параметры сортировки выбирал пользователь.
Ссылки неудобно собирать руками, лучше сделать фукнцию, которая их будет генерировать.
3) Страница шаблона должна быть в одном файле или можно их как-то объединять несколько?
Обычно делают "лейаут" - это шапка и подвал, общие для всех страниц. Потом делают страницы, каждая в своем файле. Отдельные компоненты можно и нужно выносить в отдельные шаблоны. Например, пагинацию можно вынести так, что ее можно будет подключить на любой странице сайта.
> И еще, передача данных в шаблон передается массивом, что насчет быстродействия при передаче больших объемов информации?
Никаких проблем нет, в PHP это сделано суперэффективно и занимает меньше микросекунды (в реальности ничего не копируется, просто передается ссылка на существующий в памяти массив). Можешь проверить, сделав большой массив, копируя его в цикле в другую переменную миллион раз и замеряя время в сравнении с пустым циклом.
Учись делать измерения с помощью microtime(true).
4) Ты интуитивно сделал правильное решение. Класс StudentsTable не должен думать, где взять "соединение" (на самом деле не соединение, а просто объект для связи) с БД, какие у него параметры. Он должен получать его снаружи через конструктор. Это называется DI и у меня есть урок про это на гитхабе.
1) Неправильно выбирать все записи, а потом читать только часть.
Чтобы выбрать только часть записей есть конструкция LIMIT/OFFSET: https://www.sqlite.org/lang_select.html
Обрати внимание, что LIMIT/OFFSET не входит в стандарт языка SQL и является дополнением, которое поддерживается только в некоторых СУБД вроде MySQL или SQlite.
Описание на русском есть тут на примере Postgres: https://postgrespro.ru/docs/postgresql/11/queries-limit (прочти внимательно).
Стандартные способы постраничной выборки данных есть в Википедии: https://en.wikipedia.org/wiki/Select_(SQL)#Limiting_result_rows
2) Я правильно понимаю, кто кнопки пагинации содержат в своих гиперрсылках информацию о текущей странице
Да, в в соответствие с буквой S (stateless) из REST.Сервер не "запоминает", какие параметры сортировки выбирал пользователь.
Ссылки неудобно собирать руками, лучше сделать фукнцию, которая их будет генерировать.
3) Страница шаблона должна быть в одном файле или можно их как-то объединять несколько?
Обычно делают "лейаут" - это шапка и подвал, общие для всех страниц. Потом делают страницы, каждая в своем файле. Отдельные компоненты можно и нужно выносить в отдельные шаблоны. Например, пагинацию можно вынести так, что ее можно будет подключить на любой странице сайта.
> И еще, передача данных в шаблон передается массивом, что насчет быстродействия при передаче больших объемов информации?
Никаких проблем нет, в PHP это сделано суперэффективно и занимает меньше микросекунды (в реальности ничего не копируется, просто передается ссылка на существующий в памяти массив). Можешь проверить, сделав большой массив, копируя его в цикле в другую переменную миллион раз и замеряя время в сравнении с пустым циклом.
Учись делать измерения с помощью microtime(true).
4) Ты интуитивно сделал правильное решение. Класс StudentsTable не должен думать, где взять "соединение" (на самом деле не соединение, а просто объект для связи) с БД, какие у него параметры. Он должен получать его снаружи через конструктор. Это называется DI и у меня есть урок про это на гитхабе.
> Я ему ещё иногда хтмл-шаблон для вывода всей ссылки отдаю
Это плохая идея, так как разные ссылки могут оформляться по-разному, например, первая ссылка может быть выделена. Лучше сделать отдельный шаблон для пагинации.
Здесь на примере Postgres на русском описан FETCH FIRST из стандарта SQL, читай и это тоже: https://postgrespro.ru/docs/postgresql/11/sql-select#SQL-LIMIT
Саму БД тестировать не надо - ее тестируют ее разработчики. Ты наверно хотел тестировать код, который работает с БД?
Тут есть пара вариантов:
1) разделить код на тот, которому точно нужна БД и тот, который только подготавливает данные. Тот, который подготавливает данные, тестировать, подсунув ему заглушку вместо реальной БД.
2) код, который работает непосредственно с БД можно тестировать, используя тестовую БД. Для соблюдения требования об изоляции тестов друг от друга мы должны перед каждым тестом воссоздавать одинаковое состояние БД. Например, загружать в БД дамп. Но тут 2 проблемы:
- это медленно
- если у нас 1000 тестов и мы данные для каждого добавим в дамп, то он будет большой и непонятно, какие данные кому нужны
Потому обычно делают так:
- перед тестами один раз тестовая БД очищается и в ней создаются структуры таблиц (пустые), и может какие-то неизменные справочники. Это можно делать миграциями или загрузкой заготовленного дампа (что может быть быстрее).
- перед каждым тестом мы начинаем транзакцию, и откатываем ее после теста, отменяя все изменения
- если тесту нужно наличие каких-то записей в БД, он сам их создает
Вот как-то так. Если СУБД поддерживает создание таблиц в памяти, то стоит это использовать для ускорения. SQlite умеет такое, например.
Допустим, ты тестируешь репозиторий Пользователей, который умет создавать пользователей в БД, искать их по id, удалять. Тест создания пользователя может выглядеть так:
- создать объект с параметрами пользователя
- вызвать у репозитория метод сохранения этого объекта в БД
- получить id
- запросить у репозитория данные пользователя с таким id
- проверить, что он нашелся и это тот же пользователь
Саму БД тестировать не надо - ее тестируют ее разработчики. Ты наверно хотел тестировать код, который работает с БД?
Тут есть пара вариантов:
1) разделить код на тот, которому точно нужна БД и тот, который только подготавливает данные. Тот, который подготавливает данные, тестировать, подсунув ему заглушку вместо реальной БД.
2) код, который работает непосредственно с БД можно тестировать, используя тестовую БД. Для соблюдения требования об изоляции тестов друг от друга мы должны перед каждым тестом воссоздавать одинаковое состояние БД. Например, загружать в БД дамп. Но тут 2 проблемы:
- это медленно
- если у нас 1000 тестов и мы данные для каждого добавим в дамп, то он будет большой и непонятно, какие данные кому нужны
Потому обычно делают так:
- перед тестами один раз тестовая БД очищается и в ней создаются структуры таблиц (пустые), и может какие-то неизменные справочники. Это можно делать миграциями или загрузкой заготовленного дампа (что может быть быстрее).
- перед каждым тестом мы начинаем транзакцию, и откатываем ее после теста, отменяя все изменения
- если тесту нужно наличие каких-то записей в БД, он сам их создает
Вот как-то так. Если СУБД поддерживает создание таблиц в памяти, то стоит это использовать для ускорения. SQlite умеет такое, например.
Допустим, ты тестируешь репозиторий Пользователей, который умет создавать пользователей в БД, искать их по id, удалять. Тест создания пользователя может выглядеть так:
- создать объект с параметрами пользователя
- вызвать у репозитория метод сохранения этого объекта в БД
- получить id
- запросить у репозитория данные пользователя с таким id
- проверить, что он нашелся и это тот же пользователь
У меня есть вопрос, поможешь мне?
В пхп я (как и везде) почти ноль. Есть сайт на котором по api показывался блок погоды. После переезда сайта на новый хостинг данные перестали выводится, хотя код я не менял. Ключ АПИ так же работает и при открытии напрямую все данные есть. На сайте же невыводится ничего. В чем может быть проблема? В настройках хостинга, версии пхп или в чем?
Я реально тупенький и не понимаю в чем может быть проблема,
Код типа такой:
<?
$url = "мой ключ апи";
$contents = file_get_contents($url);
$clima = json_decode($contents);
$temp = $clima->main->temp;
$temp_max = $clima->main->temp_max;
$temp_min = $clima->main->temp_min;
$desc = $clima->weather[0]->main;
?>
<? echo "". $temp ."° ". $desc .""; ?>
Тю блять. Невнимательность спасибо
Вроде получилось, но я 2 момента не понимаю.
http://sandbox.onlinephpfunctions.com/code/804b14ca336e4c0eed19d07f4a905cf61e897acf
1. Я выдираю из противоположной стороны фразы. Если для этого я использую аргумент -$i, то после первой буквы а идет еще одна а. это легко проверяется по эху. Почему так происходит, как я понял это из за того, что при 0 аргументе первая буква всегда первая в предложении 1ца дает 2ю букву в одном направлении и 0ю(в данном случае первую с обратной стороны) так?
2. Как мне сделать так, что бы если слово не было палиндромом ехо после цикла не выводилось?
Попробовал - не пашет, сейчас переделал и все равно не пашет. https://ideone.com/ucPQkR Вроде и комментарии сам себе расписал, почему-то пишет про неопределенный аргумент, но я его определил до начала цикла
Не совсем понял про переменные.
Переменные хукают данные из JSON (см. пик) и выводят их далее через echo.
Когда я со своим нулем знаний конструировал это, все работало. Все работало ровно до ебаного переезда хостинга.
спс
Попробуй просто в echo строку вывести без переменных,если все норм,то и хостинг не виноват. Мб какой xdebug поставь и проследи, что происходит, плюс в браузере в network посмотри, какие ответы на запросы приходят, какой код ответа. Я сам вкатывальщик, особо не шарю, но ты мало инфы дал анонам.
Для начала, почему ты гадаешь наугад? Так можно еще 100 лет гадать. Изучи ситуацию. Сделай следующее:
- убедись, что на сервере ведутся логи ошибок PHP или веб-сервера, найди их. Что логгирование не запрещено настройками PHP или хостинга.
- проверь, есть ли там сообщения, относящиеся к этому файлу с кодом - вдруг там происходит ошибка
Если ошибок нет, возьми код и после каждой команды поставь var_dump с выводом, что она возвращает. запусти код и посмотри - все ли команды возвращают то, что ожидается?
Проверь - что именно вернула file_get_contents, какую страницу, что на ней?
Не используй echo, как тебе советуют, так как она выводит только строки и числа. Используй var_dump, которая выводит все типы данных. Подробности в мануале.
Можно ещё на локалке скрипт свой проверить,опенсервер какой быстро поставить и все.
>>71666
У тебя в условии значение никогда не становится меньше или равно 0 и, соответственно, условие функции не доходит до ложного значения, и, соответственно ничего не возвращается.
>https://ideone.com/oO5WuX меня интересует откуда в последней строке ошибка.
Потому что ты задаешь функцию, которая уже создана. С помощью ключевого слова function, а тебе нужно её вызвать просто написав имяФункции().
Спасибо анону >>71973 полез искать логи и все стало ясно нет:
file_get_contents(): failed to open stream: no suitable wrapper could be found in on line 90
file_get_contents(): http:// wrapper is disabled in the server configuration by allow_url_fopen=0 in *
Теперь дело за малым, понять что это и как фиксить. Спасибо, анонче.
Как мне сделать так, что бы если слово не было палиндромом ехо после цикла не выводилось?
Используй дополнительную переменную как флаг.
http://sandbox.onlinephpfunctions.com/code/cae9f2c8f9e6ae0f1332e88c1a5336eb867b716a
Cпасибо. В тех уроках явно не хватает небольшой инфы о флагах. Уж очень задача упрощается.
Лил погуглил и нашел урок под это на другом сайте.
https://ideone.com/LYrtEq
$overallPaid=0;
переменная находится внутри цикла. И обнуляется каждый раз.
В остальном всё работает. Но проценты и комиссия банка абсурдны. Анон не успевает расплачиваться с кредитом. 100 витков цикла проходят до того как он расплатится. Потому ты не видишь вывода на экран.
Можешь отслеживать состояние переменных внутри программы командами echo или var_dump.
Ага, спасиб щас дампами обмажусь
Как надоели эти детские ошибк, ну йобана.
1. https://codepen.io/codecoshauni/pen/bZQKyv
2. https://codepen.io/codecoshauni/pen/QoJBGz?editors=1100
3. https://codepen.io/codecoshauni/pen/bZzzWE
4. https://codepen.io/codecoshauni/pen/YgBgMV
5. https://codepen.io/codecoshauni/pen/ZPwZEz
6. https://codepen.io/codecoshauni/pen/MxLRzy
7. https://codepen.io/codecoshauni/pen/PLVvZM
8. https://codepen.io/codecoshauni/pen/YgBbVg
9. https://codepen.io/codecoshauni/pen/draExb
10. https://codepen.io/codecoshauni/pen/QoPLjW
11. https://codepen.io/codecoshauni/pen/ZPdyeK
12. https://codepen.io/codecoshauni/pen/oVKxbL
Вот набросок кода: https://repl.it/repls/ConsciousBlondElectricity (можно не разбираться в том что он делает, меня интересует синтаксис)
Вот примеры синтаксиса, которые работают как ожидаются:
https://repl.it/repls/GiantSeveralControlpanel
https://repl.it/repls/FixedAnnualRuntime
Но в рабочем коде, в первом случае, выдаётся ошибка
>if (details is None) or (not 'Bearer token' in details['authextra']):
>builtins.TypeError: argument of type 'NoneType' is not iterable
И, во втором случае,
>if (details is None) :
> principal[u'extra'] = {
> u'error': u"Access denied: No Bearer token in authexta"
> }
> return principal
>
>
> token = details['authextra']['Bearer token'];
return не выполняется и код продолжает выполнятся
>token = details['authextra']['Bearer token'];
>builtins.TypeError: 'NoneType' object is not subscriptable
И вот ещё 3-ий пример: https://repl.it/repls/DependentUnselfishDevelopment
но в рабочем коде
> try:
> payload = jwt.decode(token, JWT_SECRET)
> except Exception as e:
> principal[u'extra'] = {
> u'error': e
> }
> return principal
>
> ...
return снова не выполняется и вбрасывается ошибка: "WAMP message serialization error: Object of type 'InvalidSignatureError' is not JSON serializable"
https://pyjwt.readthedocs.io/en/latest/api.html#jwt.exceptions.InvalidSignatureError
Почему так может происходить? Версии Питона 3.6.x что на Repl.it, что и на машине, и примеры синтаксиса проверялись и у себя на машине в консоли с помощью команды python.
Вот набросок кода: https://repl.it/repls/ConsciousBlondElectricity (можно не разбираться в том что он делает, меня интересует синтаксис)
Вот примеры синтаксиса, которые работают как ожидаются:
https://repl.it/repls/GiantSeveralControlpanel
https://repl.it/repls/FixedAnnualRuntime
Но в рабочем коде, в первом случае, выдаётся ошибка
>if (details is None) or (not 'Bearer token' in details['authextra']):
>builtins.TypeError: argument of type 'NoneType' is not iterable
И, во втором случае,
>if (details is None) :
> principal[u'extra'] = {
> u'error': u"Access denied: No Bearer token in authexta"
> }
> return principal
>
>
> token = details['authextra']['Bearer token'];
return не выполняется и код продолжает выполнятся
>token = details['authextra']['Bearer token'];
>builtins.TypeError: 'NoneType' object is not subscriptable
И вот ещё 3-ий пример: https://repl.it/repls/DependentUnselfishDevelopment
но в рабочем коде
> try:
> payload = jwt.decode(token, JWT_SECRET)
> except Exception as e:
> principal[u'extra'] = {
> u'error': e
> }
> return principal
>
> ...
return снова не выполняется и вбрасывается ошибка: "WAMP message serialization error: Object of type 'InvalidSignatureError' is not JSON serializable"
https://pyjwt.readthedocs.io/en/latest/api.html#jwt.exceptions.InvalidSignatureError
Почему так может происходить? Версии Питона 3.6.x что на Repl.it, что и на машине, и примеры синтаксиса проверялись и у себя на машине в консоли с помощью команды python.
Потому что это php тред
Мануал читал, честно, так и не дошло.
https://ideone.com/UYAeWZ
http://sandbox.onlinephpfunctions.com/code/6ec7a8b63f62394bf9896b3a58c4d3e6bb628b0d
И не выключай отображение ошибок
Работает - значит норм. Но вообще через array_rand лучше тк тут массив проиндексирован. Если бы он не был индексирован( удали ключи) то у тебя отсекся вариант да тк он был бы 0м. И 6го тупо не было. Представь, что массивы с разным числом значений, которые потом еще и меняться могут . В итоге не высчитывать же тебе все числа? Если через эту функцию то гораздо проще https://ideone.com/Nx5ew5
Не в этом дело, я сейчас переписал на регексе с нуля уже другую задачу и все заработало. Странная хрень
Возникли некоторые вопросы по объекту реализующему ResposeInterface из PSR-7.
Вы писали:
>Он представляет ответ на HTTP-запрос. В Симфони контроллер получает на вход Request и выдает на выходе Response, а фреймворк уже выводит его содержимое. Это удобно для тестирования, мы можем вызвать контроллер и смотреть, что он там сгенерировал.
Этот объект нужен для того, чтобы содержать в себе html-код? При тестировании каким-то образом проверяется его содержимое (html)? Не могу понять.
>Обычно это выглядит так:
public function indexAction(Request $request): Response
{
,....
return $this->render('template.twig', ['x' => 1, 'y' => 2]);
}
Получается метод $this->render(), встроенный в контроллер, подставляет наши значения в шаблон. Затем полученный html-код вставляется в поле $body конструктора объекта Response, который мы возвращаем как результат работы контроллера.
В каком месте выводить полученный результат?
Достаточно написать во фронт-контроллере echo $response->getBody(); ?
Например, пусть примерно так у нас выглядит файл init.php, который подгружается в index.php
$request = ServerRequest::fromGlobals();
$app = new App($request);
$response = $app->run();
echo $response->getBody();
Возникли некоторые вопросы по объекту реализующему ResposeInterface из PSR-7.
Вы писали:
>Он представляет ответ на HTTP-запрос. В Симфони контроллер получает на вход Request и выдает на выходе Response, а фреймворк уже выводит его содержимое. Это удобно для тестирования, мы можем вызвать контроллер и смотреть, что он там сгенерировал.
Этот объект нужен для того, чтобы содержать в себе html-код? При тестировании каким-то образом проверяется его содержимое (html)? Не могу понять.
>Обычно это выглядит так:
public function indexAction(Request $request): Response
{
,....
return $this->render('template.twig', ['x' => 1, 'y' => 2]);
}
Получается метод $this->render(), встроенный в контроллер, подставляет наши значения в шаблон. Затем полученный html-код вставляется в поле $body конструктора объекта Response, который мы возвращаем как результат работы контроллера.
В каком месте выводить полученный результат?
Достаточно написать во фронт-контроллере echo $response->getBody(); ?
Например, пусть примерно так у нас выглядит файл init.php, который подгружается в index.php
$request = ServerRequest::fromGlobals();
$app = new App($request);
$response = $app->run();
echo $response->getBody();
самое главное мозги ебать не надо, надо постоянно учить, основы типа переменные, массивы, циклы, функций, операторы и т.д это 5% из всего что надо знать. Теория те же 5% от знаний, применил, уже 10%, чтобы более менее что-то знать надо постоянно кодить. По мере опыта уже сам будешь разбираться и таких вопросов не возникнет
Как вывести вне цикла полную фразу, а не ее последнюю часть?
Это я ошибся, не обращай внимания
Спасибо тебе, а то я бы еще долго думал и бомбил.
https://www.youtube.com/user/TheLukesky1/playlists
В таком порядке:
PHP Start Теория
PHP Start Практика
PHP UP Теория
PHP UP Практика
Мимо 3-курс ПГС, но хочеться в ойти... вот вкатываюсь потихоньку...
Выше в треде смотри
Вот две строки, заменяю:
preg_replace( '/{([a-z]+):([^\}]+)}/ ', '(<\1>\2)' , $str); //вариант из учебного пособия
preg_replace( '/{([a-z]+):([^\}]+)}/' , '(<$1>$2)' , $str); // мой вариант
В чем отличие этих двух вариантов подстановки? А еще в том учебном фреймворке если я из '(<\1>\2)' или '(<$1>$2)' убираю первую "переменную" подстановки: <\1> или <$1> - то все ломается. Указание "переменной" в скобках <> - указывает на то что не нужно использовать данные из этой переменной, вроде. Но зачем тогда ее вообще указывать?
Не то что бы фикс - изначально в строке пособия было указано так:
preg_replace('/{([a-z]+) : ([^\}]+)}/', '(?P<$1>$2)', $route);
- я подумал что "?P" - ничего не значит. Однако ток сейчас прочел такое: "Указать обратную ссылку на именованную подмаску можно с помощью (?P=name)".
Но в варианте пособия нет "=";
Чет я запутался.
У ЖС-макак свой манямирок, не спорь с ним. Через 3 года то же самое говорить будет. Только названия фреймворков поменяются.
Такое требуют обычно только в днищеконторах, где ты будешь и за папу и за маму, и за админа, и за бухгалтера. И платить тебе будут как эникею.
я живу не в СНГ. У меня все тренды , в том числе и айти, немножко раньше проявляются.
Руби умир. Нода нахрен никому нинужна.
У пхп из альтернатив разве что питон, но он как пишут помедленнее. Но что важнее - вакансии.
Нет, просто ты только по ЖС ситуации на рынке видишь. ПХПшники же больше по ПХП видят. Зачем вообще лезть с советами, особенно в шапку, если и без тебя разберутся?
>делать ставку на пхп
Мне вообще как-то насрать. Надо будет на питон или ещё куда перееду - примерно те же яйца. Просто на ПХП хайп уже прошёл, а у питона сейчас идёт.
Дваждую. Предметная область и опыт решают больше чем тупо синтаксис %%который +\- схож.
Так я фулстак, и сейчас таких вакансий большинство и у вас так будет через год два
Их без тебя тысячи фрилансеров сделают за 5 рублей
>и у вас так будет через год два
Рыночек по регионам несколько отличается, как по фреймворкам, так и по наборам требуемых скиллов.
Причем тут это вообще, я лишь сказал, что сейчас без фронта никуда. Но вы можете продолжать обучатся вебу по канонам 2013 года. Я уверен шапка не менялась с того времени
Толкните хоть в нужное русло.
Как сделать так, чтобы случайное слово выбиралось из пяти разных массивов?
Да просто перемешивай массивы каждый проход и всё.
https://www.php.net/manual/ru/function.shuffle.php
$word1[array_rand($word1)]
https://ideone.com/vwBdVw
На 7 строке ошибка - палиндром пишется через "а".
На 4 строке он функцию не знает. Как так случилось и почему? Я в душе не ебу что ты за говносборку PHP там поставил.
Вместо $textLower[$i] != $textLower[$lenghtText-$i] должно быть
$textLower[$i] != $textLower[$lenghtText-$i-1]
>На 7 строке ошибка - палиндром пишется через "а".
аха ха -ха, теперь то заработает нет =(
> Я в душе не ебу что ты за говносборку PHP там поставил.
на ideone php7.1.0, как я ее могу поставить?
Пробовал через консоль на винде открыть (7.3.3php) - не выходит видимо что то не так делаю,
>>73474
Действительно, спасибо за замечание. Но проблема в том что анон выше написал. Даже без цикла переменные выводить не хочет.
Затестил в другой песочнице - заработало. Правда заданный текст пришлось на транслите писать.
Всем спасибо
Я уже с кирилицей тебе сделал
http://sandbox.onlinephpfunctions.com/code/0a983d62fd8270da652bab28afb5242a22b94b1a
Есть таблица complited_test(список тестов которые прошёл пользователь) и она ссылается на test. Но как бы можно обойтись без этой связи.
complited_test ссылается на student_answers(список вариантов ответов пользователя). Варианты ответов ссылаются на вопрос, которому они принадлежат. Вопросы ссылаются собственно на тест из которого взяты. Для того что б получить строку данных из complited_test и test нужно составить сложный запрос для всей этой цепочки.
Собственно вопрос. Какой путь решения будет верным. Делать сложный запрос по всей цепочке. Или оставить лишнюю связь и получать ответы простым запросом.
Сэнкс. А зачем разделять строку на массив символов? Разве это не одно и то же?
нет, не одно
это нужно для того, чтобы решить ахуительную задачку на переворачивание строки на собеседовании и кучу других
Ну на латинице вроде работало все, почитаю об этом
>>73541
Я правильно понимаю, что основная библиотека php это лишь малая часть того, что должен знать разработчик? И на собеседованиях на джуна будут гонять по алгоритмам? Нужно ли заучивать функции с гайда для нубов, ведь если с ними работать они со временем запомнятся, и достаточно знать что такие существуют?
>что основная библиотека php это лишь малая часть того
Ты про SPL? Так-то это классика, это знать надо. Какой же ты погроммист без знаний структур данных?
Ты в консоли вместо Subl печатаешь Slub, о чем тебе и говорит сообщение.
Для проверки можно попробовать распечатать файл командой type c:\Subl\... и убедиться, что ты пишешь путь неправильно.
При наборе команды стоит использовать клавишу Tab для автодополнения имен папок и файлов.
Повторю снова свой пост, может кто поможет:
Котаны, помогите немного по регулярным выражениям. Не пойму как работают группировки.
Вот две строки, заменяю:
preg_replace( '/{([a-z]+):([^\}]+)}/ ', '(<\1>\2)' , $str); //вариант из учебного пособия
preg_replace( '/{([a-z]+):([^\}]+)}/' , '(<$1>$2)' , $str); // мой вариант
В чем отличие этих двух вариантов подстановки? А еще в том учебном фреймворке если я из '(<\1>\2)' или '(<$1>$2)' убираю первую "переменную" подстановки: <\1> или <$1> - то все ломается. Указание "переменной" в скобках <> - указывает на то что не нужно использовать данные из этой переменной, вроде. Но зачем тогда ее вообще указывать?
Не то что бы фикс - изначально в строке пособия было указано так:
preg_replace('/{([a-z]+) : ([^\}]+)}/', '(?P<$1>$2)', $route);
- я подумал что "?P" - ничего не значит. Однако ток сейчас прочел такое: "Указать обратную ссылку на именованную подмаску можно с помощью (?P=name)".
Но в варианте пособия нет "=";
Чет я запутался.
>Чет я запутался.
Просто мозги ебёшь.
Одно дело, когда у тебя загововки улетают в вывод и ты не понимаешь где как у меня недавно, и другое дело дрочить какое-то пособие до основания и спрашивать у левых людей почему там именно так написано.
То есть, нужно, чтобы у него были GET/POST параметры, куки, вся хуйня как обычно, но фактически такой запрос не выполняя.
Не тупо в консоли, а именно будто пришел пользовательский запрос.
Так из браузера и запрашивай. На локалхост положи его и вызывай.
Сам так недавно делал чтобы API проверить.
твое игнорирование пособия сразило меня наповал. зачем ты вообще его читаешь в таком случае.
в пособии тебя судя по всему учат делать регулярки из регулярок.
в таком ключе разумеется все что написано имеет значение.
ты список вида {name:group} превращаешь в регулярки вида
(?P<name>group) которые потом можно опять использовать в функциях preg
(?P<name>(...)) и (?P=name(...)) разные способы сделать одно и тоже в разных регулярных синтаксисах, я в пхп использую ?P<name> - другой вариант по моему не работает
эта фишка потом позволит тебе брать уже что-то следующее, парсить и получать массив с именноваными ключами для групп. это скорее всего какой то туториал как сделать роутер по типу "fast route"
>>73756
Разобрался. В пыхе же есть встроенный веб-сервер (и похоже этот ублюдок охуеть какой быстрый).
Можно, когда нужно, оперативно поднять сервер на левом порту и в нужной папке.
Узрите же великую магию: https://pastebin.com/QXzR8sy4
Да именно это роутер.
>>твое игнорирование пособия сразило меня наповал
Дело в том, что именно этот момент и не разъяснен. Просто дан как данность.
Спасибо.
P.S. - теперь я рили понял. В комментах к туториалу неверно эту строку истолковали. И я перерыв документацию по регуляркам - не нашел того о чем они писали.
Впрочем плюс в том что регулярных я до этого косяка не знал. А так волей-не волей пришлось плотнее ознакомиться.
Спасибо еще раз.
Я понимаю, что лезть руками в базу и вручную добавлять новые поля в таблицу это неправильный способ работы с доктриной, но чисто в учебных целях как сделать следующее: допустим в существующую таблицу добавлено новое поле напрямую, без участия доктрины. Как мне дать знать об этом доктрине и перегенерировать entity? В доке https://symfony.com/doc/current/doctrine.html есть следующий рецепт:
If you prefer to add new properties manually, the make:entity command can generate the getter & setter methods for you:
php bin/console make:entity --regenerate
If you make some changes and want to regenerate all getter/setter methods, also pass --overwrite.
Но после запуска этой команды entity файл на меняется. ЧЯДНТ?
Что тебе мешает руками дописать нужные поля и аннотации в entity?
> Но после запуска этой команды entity файл на меняется. ЧЯДНТ?
А ты добавил overwrite? Плюс, я подозреваю, эта штука перезапишет файл и может стереть какие-то методы, которые ты туда добавлял.
Вообще, Доктрина очень универсальная и поддерживает 2 модели:
1) источник правды - база данных. Ты делаешь миграцию БД, а потом добавляешь в сущности нужные поля.
2) источник правды - модели. Ты добавляешь нужные поля и аннотации и из них генерируешь миграции, которые вносят нужные изменения в БД.
https://regex101.com/r/qF7vT8/192
Нужна помощь с регуляркой. Как учитывать общее количество символов в группе если получается разброс от одного до трех символов в каждой группе
https://codecoshauni.github.io - сама страница.
https://github.com/codecoshauni/codecoshauni.github.io - ну и код.
Нужно сделать редактируемую таблицу, а введенные в таблицу данные отправлять в базу.
Я так понимаю саму таблицу и отправку данных только с помощью js можно сделать?
Вопрос по HTML и CSS...
Задача: сделать вывод текста и текстареи - в две колонки,
как тут: https://css-live.ru/articles/css-gridy-css-kolonki-♥.html
но так, чтобы при изменении размера текстареи, изменялся и размер line-height,
и чтоб не ровно на половину страницы разделялось, а с небольшим отступом.
Вопрос - как правильно сделать?
>Что тебе мешает руками дописать нужные поля и аннотации в entity?
Лень конечно же. Думал доктрина сделает это за меня при regenerate
>А ты добавил overwrite?
Да
>Плюс, я подозреваю, эта штука перезапишет файл и может стереть какие-то методы, которые ты туда добавлял.
Да, знаю, но я ничего в файл не добавлял, поэтому значения не имеет
Раз ты помог мне, подскажи еще с регулярками. Меня эта тема выносит уже.
Смотри
Я выполняю этот код на ideone
$str = 'admin/delete/{id:\w+}';
$newstr = preg_replace( '/{([a-z]+):([^\}]+)}/' , '(?P<\1>\2)' , $str);
echo $newstr;
результат : admin/delete/(?P<id>\w+)
Все как ты и объяснил. Все логично.
Я выполняю эти же строки на своем сервере и в результате получаю в результате: admin/delete/(?P\w+). Куда девается '<id>' ?
Что за дела?
Сервак OpenServer, версия PHP 7.2.10 Выполняю в отдельном файле. То есть ничего на этот код не влияет.
Есть интеграционные тесты, нужно передать такую инфу, как логин/пароль.
У меня была такая же проблема. Ты пытался написать код разделяя на очень много промежутков, которые могут быть при написании телефона. Твой код:
^\s?(\+\s?7|8)\s?[\s-]?\(?\s?\d{3,4}\s?\)?(([-\s]){0,3}(\d{1,3})){0,}$
Есть в коде лишние скобки и тут например вместо ([-\s]){0,3} можно было бы написать [-\s]{1,3} ну или вообще [-\s]+ ну и тд, упрощать и упрощать. В общем мой код для данной задачи такой:
^([ +]+7|\s?8)([() -]?\d){10}$. Тут есть начало кода ^([ +]+7|\s?8) потом середина, которая либо есть, либо её нету [() -]? и конец это.
Помогите понять как решать задачу по регулярке:
https://ideone.com/yxFIpH
Я никак не могу понять почему он выдаёт 892754-54305 и не считывает дефис посередине
\d)$.
В чем может быть проблема?
Хотелось бы верить, что это дерьмо заработает :d
ctrl+f5 пробовал?
первое что приходит на ум - ты выдал результат в браузер, который съел <id> как тег
Что делать?
Обычно так не делают. Ведь когда твои тесты будет запускать CI сервер, он дописывать пароли не будет. Но есть варианты:
- сделать кастомный конфиг, в него вписать пароли. Он не комитится в гит, а туда коммитится лишь образец (config.ini.example) и каждый разработчик из образца делает себе конфиг со своими паролями
- передавать логины и пароли через переменные окружения:
APP_XYZ_LOGIN=123 phpunit
Тогда есть еще вариант переместить куда-то файл и попросить Доктрину сгенерировать его заново. Там наверняка есть опции для генерации только одного файла.
Вообще, я тут пару минут погуглил и понял, что ты все перепутал. make - это команда из MakerBundle, она, как я понимаю, годится только для создания файлов с нуля.
Но у Доктрины есть инструменты для синхронизации кода и БД. Набери php bin/console list | grep doctrine (показать все команды и оставить только имеющие слово doctrine) и ты увидишь кучу полезных команд, например:
doctrine:mapping:import Imports mapping information from an existing database
doctrine:migrations:diff Generate a migration by comparing your current database to your mapping information.
doctrine:schema:update Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata
Изучи их и посмотри, нет ли там чего-то нужного.
Браузер воспринимает результат вывода скрипта как HTML а <id> - как тег.
Решение - используй var_dump вместо echo или ставь правильные заголовки:
header("Content-Type: text/plain; charset=utf-8");
Это заставит браузер воспринимать то, что выводит твоя программа, как обычный текст, а не HTML.
Ах, да. Ошибки и предупреждения нужно каким-то образом превращать в исключения.
Мой вариант решения это проблемы, что скажете?
Допустим у нас есть фронт-контроллер, который расположен в public/index.php
Объявляем обработчик ошибок set_error_handler, в нём превращаем предупреждения/ошибки в исключения.
Затем оборачиваем исполняемый код в try catch
try {
// Код выполняемого приложения
} catch (Throwable $e) {
// Запись в лог
// Загрузка страницы ошибки
}
Не могу разобраться с рутингом
хочу чтобы при нажатии на линк (пик 1) у меня открывалась форма поста в той же секции, где на линку нажали, а не в начале страницы
что для этого нужно сделать?
сама форма (пик 2)
вопрос скорее по верстке, но может кто подскажет
нужно, создать
соответствующую структура базы данных MySQL и импортировать данные в нее.
PHP скрипт должен выбрать 10 последних заголовков из базы данных
статьи, предполагая, что каждый заголовок должен исходить из разных
категорий (категории в списке из 10 названий не могут повторяться).
Полученные записи должны отображаться на странице в формате
"категория - название - дата".
Можно и так, вполне хороший вариант. Если что, у меня это описано в уроке: https://github.com/codedokode/pasta/blob/master/php/exceptions.md
Открой инструменты разработчика в браузере (Ctrl + Shift + I), вкладку "Сеть" и перезагрузи страницу. Посмотри, нет ли ошибок загрузки каких-то ресурсов. Если потребуется, почитай про инструменты разработчика.
>>74792
Header в начале кода бы тоже помог. Ну хоть освоил функцию htmlspecialchars().
>>74757
Если ты хочешь, чтобы форма появлялась на той же странице без перезагрузки, то тебе и нужна одна страница, а не две. Также нужен яваскрипт или продвинутый CSS (псевдоселектор :target либо невидимый input type="checkbox" ).
Например:
- можно сделать форму скрытой и открывать яваскриптом при нажатии на кнопку (а ссылку убрать)
- попробовать использовать тег HTML5 details, который делает примерно то, что нужно
- можно сделать в кнопке невидимый input type="checkbox" и показывать форму при галочке в нем. Примеры похожих штук, где что-то показывается/скрывается: https://webref.ru/layout/pseudo-class-checked/tabs https://habr.com/ru/post/174029/
Я в курсе об этой команде, но неужели мне нужно будет это в каждый фаил подключать?
Правда на строки mb_string не проверял, ещё этому компилятору инет нужен.
Я так сделал, но кажется что не совсем правильно
http://sandbox.onlinephpfunctions.com/code/f444a95132632b1a6ee1253a93e8cad13a903b53
Твое не отлавливает скобки и с таким подходом можно каждую цифру оборачивать в скобки и она пройдет
Бамп вопросу. Все чаще симфони на слуху стал быть, да и yii опять чето мелькать начал.
Стоит ли вкатиться в эти фреймворки или лары достаточно и потратить силы на изучение чего-то другого?
ты опять делаешь слишком длинный код. Вот посмотри наглядный пример рабочего кода
https://regex101.com/r/qF7vT8/196
Посдскажи, не могу понять (я другой анон). В твоем коде проверка символов идет так (после 7или 8) скобка, тире пробел затем цифра. А если человек дебил и скажем поставил скобку после последней цифры? Например так 8 (888)(8888888) теоретически это неправильно и должно отсеяться тк последняя скобка не проверяется. Добавив проверку на знаки после цифры можно обойти это. Может я просто переусложняю без дела? И второй момент, при поиске первых цифр можно написать так ^(\s7или\s8) хз как горизонтальную линию с телефона ставить. почему ты ищешь [ 7]* в таком виде?
у каждого сайта есть какие-то элементы, общие для всех страниц - это, например, шапка и футер. Если данные для шапки и футера подтягиваются из базы логично сделать так, чтобы эти данные были доступны в любой шаблоне сразу или любом контроллере, чтобы дальше передать их в шаблон. Как этот сделать без дублирования кода в каждом из контроллеров? Нужно добавить эти данные как глобальную переменную? Или как-то инжектить в каждый контроллер?
>Изучи их и посмотри, нет ли там чего-то нужного.
Спасибо, анончик. Очень полезно! Справился с задачей.
Тут всё проще. Сначала надо выкинуть весь мусор - то есть всё, что не является цифрами
Потом проверить длину строки и если больше 11 то забраковать
Если 11, то проверить какая цифра первая - если не 7 или 8 то забраковать
Если 7 то поменять на 8
А почему не должен? 11 цифр есть. И если ввод никак не ограничивается на стороне клиента то может быть и такое - клавиша залипла, например, тачскрин заглючил.
Sphinx или ElasticSearch
Понял, спасибо
>$symbol2= mb_substr( $text, -$i-1,1);
Неплохо, я бы даже сказал элегантно. А я доп. переменную вводил сразу, не мог допереть как с конца нормально индексировать.
>2. Как мне сделать так, что бы если слово не было палиндромом ехо после цикла не выводилось?
Можно вызовом exit() сделать:
if($first!=$last){
echo "Not palindrom\n";
exit();
}
Спасибо, гляну.
https://ideone.com/WeE8Hi
Вот веб-студия. Допустим, я пойду туда устраиваться. Попрошу 80к. Оформят наполовину в серую, итого для конторы я обойдусь в 100к в месяц. Допустим, я чистый фронтендер. За две недели могу слепить сайт из шаблона от дизайнера. Сам шаблон дизайнер будет рисовать те же условные 2 недели. и бек-энд будет делать отдельный человек за 2 недели. Плюс ПМ, он же менеджер по работе с клиентами, он же притаскивает новые заказы и тому подобное. Итого - 4 человека будут работать 2 недели ради одного сайта. Если каждый получит 100к (до налогов), то за 2 недели заказчику это всё обойдётся в 200к. Плюс пренда офиса, уборщица-бухгалтер-печенки-кофемашина. Плюс хоть какая-то прибыль для владельца. Итого, обычный сайт будет стоить от 300к рублей.
Я не понимаю этого. Неужели кто-то платит такие деньги? Или там берут количеством и дизайнер только слегка перерисовывает готовые шаблоны, фронт натягивает очередной шаблон на вордпресс, а бек пилит мелкие свистелки? По паре сайтов в день. С утра ты пилишь сайт-визитку на вордпрессе, после обеда магазин на опенкарте, назавтра нужно запустить три лендинга на шаблонах, так что ли?
http://docs.php.net/manual/ru/control-structures.break.php
>break прерывает выполнение текущей структуры for, foreach, while, do-while или switch
можешь использовать exit()
лучше переписать скрипт без подобных вещей.
Думаю если дизайн пилит дизайнер, фронтенд - фронтендер, а бэк - бэкэндер, то продук определённо годным будет.
Во-первых, это может быть шаблон на продажу.
Во-вторых, это может быть сайт для больших дядей.
В-третьих, это может быть тупа аутсорс на забугор.
В-четвёртых, есть разные системы и проекты, и это даже необязательно может быть обычный сайт - какая-нить система автоматизации и прочий софт с годным дизайном, фронтендом и бэком.
Ну и прочие фантазии.
Спасибо, поясни как это нормально сделать. Мы же проверяем функцией if, а если не соответствует прекращаем проверку на одном из этапов
Переписал с exit. Теперь ошибка в строке https://ideone.com/m02OaI. Упростил вырезав плюс, вроде получилось.https://ideone.com/xMO55G
Давай я тебя верну на землю. Вот, допустим есть веб-контора. В ней программисту платят 30-40 к в месяц, с налогами пусть это 60. В итоге 3 000 р расходов за рабочий день, который мы естественно удлиняем как можем. Менеджер тратит 4 часа своего времени на переговоры и обсуждения. Покупается готовый шаблон долларов за 20. Берется CMS и программист, пыхтя, за день натягивает на нее шаблон. Затраты ты можешь оценить. Там, правда, еще деньги нужны на рекламу.
Ну то есть существуют разные сегменты. Есть студии, где бизнес-аналитики будут чертить флоучарты, а ведущие разработчики проектировать бекенд на современном фреймворке и изобретать штуки, которых вообще в паблике нету, а есть бюджетный сектор, где все делается максимально экономно.
Я помню, как-то у меня спрашивали, сколько стоит создание сайта, а потом сказали, что есть компания, которая делает сайт за 3000. А что, почему бы и нет.
Что касается стоимости, то на высококонкурентном рынке маржа получается минимальной, и получается, значительная часть денег заказчика идет на расходы, а не в карман владельцу.
Если заказчику нужно то, что можно сделать за день - он идет в бюджетную студию. Если у него высокие запросы - вполне возможно, что с него возьмут эти 200к или больше. Если у него работы на человекогоды - он сам начинает нанимать разработчиков. Да и 200к не большие деньги же, особенно для организации.
Но тут конечно есть свои нюансы. Например, широко практикуется апселлинг - клиент хотел сайт, а мы продаем ему еще и продвижение по завышенной цене.
Если тебя интересует, как живут именно студии верхнего сегмента, то читай описание проектов Студии Лебедева ( https://www.artlebedev.ru/everything/ ), они очень интересно все описывают, Советы у Бюро Горбунова ( https://bureau.ru/bb/soviet/ ) ну и можно посмотреть, сайты каких-то студий из топа ( http://www.ruward.ru/index-ruward/united-web-rating-2018/ ) - вдруг они что интересное выкладывают.
Я должен предупредить, работать в студиях нижнего сегмента не очень интересно и не очень легко. Перфекционист там вообще умрет в первый же день. Не стоит наверно туда особо стремиться.
Давай я тебя верну на землю. Вот, допустим есть веб-контора. В ней программисту платят 30-40 к в месяц, с налогами пусть это 60. В итоге 3 000 р расходов за рабочий день, который мы естественно удлиняем как можем. Менеджер тратит 4 часа своего времени на переговоры и обсуждения. Покупается готовый шаблон долларов за 20. Берется CMS и программист, пыхтя, за день натягивает на нее шаблон. Затраты ты можешь оценить. Там, правда, еще деньги нужны на рекламу.
Ну то есть существуют разные сегменты. Есть студии, где бизнес-аналитики будут чертить флоучарты, а ведущие разработчики проектировать бекенд на современном фреймворке и изобретать штуки, которых вообще в паблике нету, а есть бюджетный сектор, где все делается максимально экономно.
Я помню, как-то у меня спрашивали, сколько стоит создание сайта, а потом сказали, что есть компания, которая делает сайт за 3000. А что, почему бы и нет.
Что касается стоимости, то на высококонкурентном рынке маржа получается минимальной, и получается, значительная часть денег заказчика идет на расходы, а не в карман владельцу.
Если заказчику нужно то, что можно сделать за день - он идет в бюджетную студию. Если у него высокие запросы - вполне возможно, что с него возьмут эти 200к или больше. Если у него работы на человекогоды - он сам начинает нанимать разработчиков. Да и 200к не большие деньги же, особенно для организации.
Но тут конечно есть свои нюансы. Например, широко практикуется апселлинг - клиент хотел сайт, а мы продаем ему еще и продвижение по завышенной цене.
Если тебя интересует, как живут именно студии верхнего сегмента, то читай описание проектов Студии Лебедева ( https://www.artlebedev.ru/everything/ ), они очень интересно все описывают, Советы у Бюро Горбунова ( https://bureau.ru/bb/soviet/ ) ну и можно посмотреть, сайты каких-то студий из топа ( http://www.ruward.ru/index-ruward/united-web-rating-2018/ ) - вдруг они что интересное выкладывают.
Я должен предупредить, работать в студиях нижнего сегмента не очень интересно и не очень легко. Перфекционист там вообще умрет в первый же день. Не стоит наверно туда особо стремиться.
Скопипащу ответ из старого поста: https://phpclub.tech/pr/res/1353705.html#1359951
Обычно делают отдельно шаблон для "лейаута" (шапка/подвал) и "контента". Лейаутов может быть несколько - например, один для морды, другой для админки. Как передавать параметры для лейаута? Тут есть варианты:
- руками копипастить их в каждый метод каждого контроллера (боль)
- наследовать контроллеры от базового класса и в нем формировать данные. У базового класса может быть метод вроде "поменять тайтл текущей страницы". Плюс - инкапсуляция, трудно передать что-то неправильное, например, не-строку.
- передать эти данные во вью до или после выполнения контроллера, без использования наследования. Или передать в контроллер.
- сделать "контроллер для лейаута" и в нем формировать данные для лейаута. У "контроллера лейаута" могут быть вспомогательные методы вроде "поменять тайтл текущей страницы".
В фреймворках готового решения нет. Каждый изобретает сам.
Добавлю, что стоит держать в уме, что лейаутов может быть несколько (сайт, админка) и у них могут быть разные наборы параметров. Идея Симфони с прописыванием глобальных переменных в конфиге мне не очень нравится, но какой-то хелпер так передать вполне допустимо.
Раз уж такая тема, то тоже спрошу:
вот у меня есть простой самописный фрейм, который роутит роуты, передаёт зависимости, имеет базовые Реквест и Респонс. Ну на коленке собираю для изучения темы.
Не могу решить как там представление реализовать. Есть вариант через класс Представления обрабатывать лайеры с шаблонами и контентом, и отдавать это всё строкой в Респонс несколько вычурно и нелепо получается, конструкция вида $Response->body = $View->reder(бла-бла-бла), или лучше вообще его не использовать и тупо выводить рендером в любом месте, правда мне не очень это по нраву - я хотел бы чтобы вывод был в одном месте, в конце приложения.
Есть какие-то намёки как это половчее сделать?
Спасибо за ссылки, посмотрю.
>Если у него высокие запросы - вполне возможно, что с него возьмут эти 200к или больше.
Но верхушка - это процентв 10-20 рынка, не более, так ведь? Почему тогда так дико популярны фреймворки? Сделать сайт на вордпрессе можно за вечер, покликав мышкой и не оскверняя мозги киким-то там пхп и жаба-скриптом вообще. Сделать то же самое на условном Ларавеле займёт гораздо больше времени даже у профи. Если ент спроса, то людей, работающих с фреймворками (а тем более с машин лёрн, биг дата, блок чейн, пит буль) должно быть мало. Однако, когда я открываю хх, то у вебов почти все вакансии обязывают знать хоть один фреймворк.
>Сделать сайт на вордпрессе можно за вечер
Так там нормальных интерактивных сайтов почти и нет - вукомерс, который всё равно кодить придётся, и лэндосы, которые суть - один жаваскрипт.
Так на вордпрессе, что не делай, получится блог. CMS - это для случаев, когда тебе нужен именно ее функционал, плюс может какие-то готовые плагины + готовая или своя тема + немного написанных тобой кастомных плагинов. CMS - это в первую очередь настройка готового решения через админку. Если же твою задачу вордпресс не решает, и нужно писать много кастомного кода, то писать будет выгоднее на фреймворке, так как это удобнее, быстрее и надежнее. Я сомневаюсь, что ты на вордпрессе сделаешь что-то сложное и нестандартное, а даже если и сделаешь, то будешь постоянно плеваться.
Ну банальный пример: в Laravel есть ORM на основе ActiveRecord, в Symfony - Data Mapper. Что насчет вордпресса? Там этого банально нет. Там даже миграций БД по моему нет. Нет тестов, нет нормального деплоя.
То есть CMS заточены под создание решений по определенному шаблону, где ты можешь только менять отдельные настройки. Фреймворки - для произвольных кастомных, нестандартных решений.
Что касается магазинов, то я работал над ними, и могу сказать, что все крупные магазины используют самописный код. Им нужен уникальный функционал, которого нет в коробочной CMS, и делать его на CMS просто менее удобно, чем на фреймворке. Ну и оптимизировать свой код проще, например, чем пытаться оптимизировать CMS. Например, коробочные CMS часто плохо работают с большим кол-вом товаров и большой нагрузкой. Так как в них главное не производительность, а возможность настройки через админку. Но крупному магазину нужно не это.
> Сделать сайт на вордпрессе можно за вечер, покликав мышкой и не оскверняя мозги киким-то там пхп и жаба-скриптом вообще. Сделать то же самое на условном Ларавеле займёт гораздо больше времени даже у профи.
В такой ситуации естественно, выгоднее, делать на вордпрессе. Но большой сложный магазин ты так не сделаешь. И Яндекс-такси не сделаешь. И инстаграм. И альфа-банк. И госуслуги.
> Однако, когда я открываю хх, то у вебов почти все вакансии обязывают знать хоть один фреймворк.
Значит, они решают задачу, которую средствами CMS не решить. Ты бы посмотрел, чем компания занимается, какие проекты делает.
Так на вордпрессе, что не делай, получится блог. CMS - это для случаев, когда тебе нужен именно ее функционал, плюс может какие-то готовые плагины + готовая или своя тема + немного написанных тобой кастомных плагинов. CMS - это в первую очередь настройка готового решения через админку. Если же твою задачу вордпресс не решает, и нужно писать много кастомного кода, то писать будет выгоднее на фреймворке, так как это удобнее, быстрее и надежнее. Я сомневаюсь, что ты на вордпрессе сделаешь что-то сложное и нестандартное, а даже если и сделаешь, то будешь постоянно плеваться.
Ну банальный пример: в Laravel есть ORM на основе ActiveRecord, в Symfony - Data Mapper. Что насчет вордпресса? Там этого банально нет. Там даже миграций БД по моему нет. Нет тестов, нет нормального деплоя.
То есть CMS заточены под создание решений по определенному шаблону, где ты можешь только менять отдельные настройки. Фреймворки - для произвольных кастомных, нестандартных решений.
Что касается магазинов, то я работал над ними, и могу сказать, что все крупные магазины используют самописный код. Им нужен уникальный функционал, которого нет в коробочной CMS, и делать его на CMS просто менее удобно, чем на фреймворке. Ну и оптимизировать свой код проще, например, чем пытаться оптимизировать CMS. Например, коробочные CMS часто плохо работают с большим кол-вом товаров и большой нагрузкой. Так как в них главное не производительность, а возможность настройки через админку. Но крупному магазину нужно не это.
> Сделать сайт на вордпрессе можно за вечер, покликав мышкой и не оскверняя мозги киким-то там пхп и жаба-скриптом вообще. Сделать то же самое на условном Ларавеле займёт гораздо больше времени даже у профи.
В такой ситуации естественно, выгоднее, делать на вордпрессе. Но большой сложный магазин ты так не сделаешь. И Яндекс-такси не сделаешь. И инстаграм. И альфа-банк. И госуслуги.
> Однако, когда я открываю хх, то у вебов почти все вакансии обязывают знать хоть один фреймворк.
Значит, они решают задачу, которую средствами CMS не решить. Ты бы посмотрел, чем компания занимается, какие проекты делает.
не могу в yii 2
не открывается даже стартовая страница-пример
хотя все требования на сервере есть
An Error occurred while handling another error:
exception 'yii\web\HeadersAlreadySentException' with message 'Headers already sent in
An Error occurred while handling another error:
exception 'yii\web\HeadersAlreadySentException' with message 'Headers already sent in E:\openserv\OSPanel\domains\ex\config\web.php on line 1.' in E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\web\Response.php:366
Stack trace:
#0 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\web\Response.php(339): yii\web\Response->sendHeaders()
#1 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\web\ErrorHandler.php(135): yii\web\Response->send()
#2 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\ErrorHandler.php(111): yii\web\ErrorHandler->renderException(Object(yii\base\ErrorException))
#3 [internal function]: yii\base\ErrorHandler->handleException(Object(yii\base\ErrorException))
#4 {main}
Previous exception:
exception 'yii\base\ErrorException' with message 'session_start(): Cannot send session cookie - headers already sent by (output started at E:\openserv\OSPanel\domains\ex\config\web.php:1)' in E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\web\Session.php:137
Stack trace:
#0 [internal function]: yii\base\ErrorHandler->handleError(2, 'session_start()...', 'E:\\openserv\\OSP...', 137, Array)
#1 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\web\Session.php(137): session_start()
#2 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\web\Session.php(612): yii\web\Session->open()
#3 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\web\Session.php(751): yii\web\Session->get('__flash', Array)
#4 E:\openserv\OSPanel\domains\ex\widgets\Alert.php(53): yii\web\Session->getAllFlashes()
#5 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\Widget.php(140): app\widgets\Alert->run()
#6 E:\openserv\OSPanel\domains\ex\views\layouts\main.php(65): yii\base\Widget::widget()
#7 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\View.php(348): require('E:\\openserv\\OSP...')
#8 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\View.php(257): yii\base\View->renderPhpFile('E:\\openserv\\OSP...', Array)
#9 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\Controller.php(399): yii\base\View->renderFile('E:\\openserv\\OSP...', Array, Object(app\controllers\SiteController))
#10 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\Controller.php(385): yii\base\Controller->renderContent('<div class="sit...')
#11 E:\openserv\OSPanel\domains\ex\controllers\SiteController.php(64): yii\base\Controller->render('index')
#12 [internal function]: app\controllers\SiteController->actionIndex()
#13 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\InlineAction.php(57): call_user_func_array(Array, Array)
#14 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\Controller.php(157): yii\base\InlineAction->runWithParams(Array)
#15 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\Module.php(528): yii\base\Controller->runAction('', Array)
#16 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\web\Application.php(103): yii\base\Module->runAction('', Array)
#17 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\Application.php(386): yii\web\Application->handleRequest(Object(yii\web\Request))
#18 E:\openserv\OSPanel\domains\ex\web\index.php(12): yii\base\Application->run()
#19 {main}
не могу в yii 2
не открывается даже стартовая страница-пример
хотя все требования на сервере есть
An Error occurred while handling another error:
exception 'yii\web\HeadersAlreadySentException' with message 'Headers already sent in
An Error occurred while handling another error:
exception 'yii\web\HeadersAlreadySentException' with message 'Headers already sent in E:\openserv\OSPanel\domains\ex\config\web.php on line 1.' in E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\web\Response.php:366
Stack trace:
#0 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\web\Response.php(339): yii\web\Response->sendHeaders()
#1 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\web\ErrorHandler.php(135): yii\web\Response->send()
#2 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\ErrorHandler.php(111): yii\web\ErrorHandler->renderException(Object(yii\base\ErrorException))
#3 [internal function]: yii\base\ErrorHandler->handleException(Object(yii\base\ErrorException))
#4 {main}
Previous exception:
exception 'yii\base\ErrorException' with message 'session_start(): Cannot send session cookie - headers already sent by (output started at E:\openserv\OSPanel\domains\ex\config\web.php:1)' in E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\web\Session.php:137
Stack trace:
#0 [internal function]: yii\base\ErrorHandler->handleError(2, 'session_start()...', 'E:\\openserv\\OSP...', 137, Array)
#1 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\web\Session.php(137): session_start()
#2 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\web\Session.php(612): yii\web\Session->open()
#3 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\web\Session.php(751): yii\web\Session->get('__flash', Array)
#4 E:\openserv\OSPanel\domains\ex\widgets\Alert.php(53): yii\web\Session->getAllFlashes()
#5 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\Widget.php(140): app\widgets\Alert->run()
#6 E:\openserv\OSPanel\domains\ex\views\layouts\main.php(65): yii\base\Widget::widget()
#7 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\View.php(348): require('E:\\openserv\\OSP...')
#8 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\View.php(257): yii\base\View->renderPhpFile('E:\\openserv\\OSP...', Array)
#9 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\Controller.php(399): yii\base\View->renderFile('E:\\openserv\\OSP...', Array, Object(app\controllers\SiteController))
#10 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\Controller.php(385): yii\base\Controller->renderContent('<div class="sit...')
#11 E:\openserv\OSPanel\domains\ex\controllers\SiteController.php(64): yii\base\Controller->render('index')
#12 [internal function]: app\controllers\SiteController->actionIndex()
#13 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\InlineAction.php(57): call_user_func_array(Array, Array)
#14 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\Controller.php(157): yii\base\InlineAction->runWithParams(Array)
#15 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\Module.php(528): yii\base\Controller->runAction('', Array)
#16 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\web\Application.php(103): yii\base\Module->runAction('', Array)
#17 E:\openserv\OSPanel\domains\ex\vendor\yiisoft\yii2\base\Application.php(386): yii\web\Application->handleRequest(Object(yii\web\Request))
#18 E:\openserv\OSPanel\domains\ex\web\index.php(12): yii\base\Application->run()
#19 {main}
Можно ли из хелпера обращаться к глобальным переменным? Или он как часть модели ничего не должен знать о пользовательских данных, а получать их только снаружи?
Что правильнее?
class Controller
{
function indexAction()
{
if (AuthHelper::isAuthenticated()) {
$this->view->render('шаблон1');
} else {
$this->view->render('шаблон2');
}
}
}
Или
class Controller
{
function indexAction()
{
if (AuthHelper::isAuthenticated($_COOKIE['student'])) {
$this->view->render('шаблон1');
} else {
$this->view->render('шаблон2');
}
}
}
Можно ли из хелпера обращаться к глобальным переменным? Или он как часть модели ничего не должен знать о пользовательских данных, а получать их только снаружи?
Что правильнее?
class Controller
{
function indexAction()
{
if (AuthHelper::isAuthenticated()) {
$this->view->render('шаблон1');
} else {
$this->view->render('шаблон2');
}
}
}
Или
class Controller
{
function indexAction()
{
if (AuthHelper::isAuthenticated($_COOKIE['student'])) {
$this->view->render('шаблон1');
} else {
$this->view->render('шаблон2');
}
}
}
https://ideone.com/t2sO1Y
Написал
Я подумал: "Бля, а внатуре".
Теперь, когда надо проверить какую-то хуйню, тупо ебашу тест, там вывожу и заодно делаю какую-нибудь проверку побыстроляну.
Итого, я проверил что хотел - и у меня лишний ствол тест появился.
Это и называется test-driven development
Распаковка аргументов штука прикольная, можно задавать массив объектов последним параметром.
Но только 1 раз за метод и только последним.
Возвращаемого тайпхинта массива объектов в пыхе нет.
Кстати, я тот самый чел, что 2 треда рассирался про дженерики в пыхе и как их можно сделать.
В одном проекте даже реализовал.
Но понял, что это потребляет больше времени, чем экономит.
Теперь тупо делаю это через phpdoc.
Автокомпликт работает, это самое главное.
Ну а то, что фактической проверки типа возвращаемых значений и аргументов нет - ну что же, приходится забить на это хуй в угоду быстроте разработки.
>Это и называется test-driven development
Блять, вообще это был вопрос.
Это и называется test-driven development?
Как говорил мой дед: "Сначала пишешь тест, потом пишешь код, пока он не пройдет твой тест" Но меня такой подход ебет, потому что я почти всегда не знаю что хочу, и пишу сразу тесты для контроллера, типа при таких то параметрах вернуть то и то, а при таких ошибку высрать.
$regexp = '/\s{2,}/u';
$text = preg_replace($regexp, '',$text);
$regepx = '/([.?!;:,])(.)/ui';
$text = preg_replace($regexp, '$1 $2',$text);
return $text;
Первая регулярка проходит, вторая - нет. Ощущение что я ее не правильно написал, но тут все ок.https://regex101.com/r/H2Cwq3/1
разобрался, я просто дебил слепой
Задача:
>Сделать RESTful API телефонного справочника c использованием фреймворка Laravel.
https://github.com/tsubaku/phonebook
Если у кого-то есть свободное время, может посмотрите? Что там плохо?
Разрешите доебаться. В названии грится RESTful, а у тебя аутентификация походу через куки и сессии, и вообще не понятно нужно ли для этого задания использовать что-то другое. Можно через гварды указывать для каких методов нужна авторизация и получается что не особо много смысла иметь два Number контроллера в разных неймспейсах (это было бы не так важно если бы ты руты замапил без перфикса admin, ну это уже холиварная тема сама по себе). Для джсон ответов кстати очень удобно юзать ларавельские Resources. Контроллер search в неймспейса Ajax опять же не особо нужен, не проще в Number@index ( / url) добавить возможность фильтровать по запросу, например /?name=123&number=456 и возвращать отфильтрованные звонки. Еще не совсем понятно зачем сортировать в пхп а не в бд, и вообще про методы all() лучше забыть.
Из такого тестового хз какой вывод можно сделать, там и задания то внятного не было с требованиями, просто ЗДЕЛОЙТЕ, ну ты и сделал
В Postgresql есть "табличные пространства": https://postgrespro.ru/docs/postgresql/11/manage-ag-tablespaces (русс.)
Они позволяют хранить часть таблиц в определенном месте файловой системы. Соответственно, логика получается такая:
- делаем раздел tmpfs, например, в /var/test-database/, даем на него права пользователю postgres. Чтобы он монтировался при старте системы, пишем его в /etc/fstab, если он будет пустой, то память все равно не тратит.
- даем пользователю postgres, под которым гоняются тесты, права создавать табличные пространства
- создаем в postgres табличное пространство в этой папке
- создаем в postgres новую БД под тесты и указываем ей это табличное пространство (CREATE DATABASE ... TABLESPACE = ...)
- очищаем тестовую БД, загружаем в неё дамп
- прогоняем тесты
- дропаем тестовую БД
Кстати, для выкладывания сайта можно использовать github sites, если ты готов разобраться с гитом. Это будет удобнее для отладки и проверки сайта, так как там не будет постороннего кода. Можно, например, оценить его вес и скорость загрузки.
> Можно ли сделать плавный переход по якорям без JS?
В новых браузерах, как пишет анон, есть scroll-behaviour.
> Можно ли задать плавное всплывание картинок по нажатию на какую-либо из кнопок?
Изучи CSS-переходы (transition) и анимации, и посмотри, есть ли что-то подходящее. Тут трюк в том, что смена некоторых свойств отключает переход или анимацию. Например, смена display с block на hidden. И приходится скрывать элементы каким-то другим способом.
> Копирайт всегда должен быть в футере, можно ли их разделить и будет ли это правильно? Я разделил, потому что так в PSD было. С шапкой также сделал.
В принципе, можно.
> Подписи к заголовкам обрамлять в <h> на пункт меньше или <p>?
Если ты про Consectetur, Tritiquet - то там наверно <p>. Если ты про "We are webpaint", то можно сделать h2 или div. <p> - это абзац текста, тут явно не абзац текста.
> Почему "margin: 0;" в body не работал и мне приходилось подписывать его к каждому элементу?
Если ты про поля по краям окна, то они могут быть заданы не на body, а на html, и не в виде маргина, а в виде паддинга. Стандарта тут нет. Потому надо обнулять и маргин, и паддинг на обоих элементах.
Пункты меню стоило слегка подсвечивать при наведении или как-то еще выделять. Также, расскажу хитрость, так как они далеко друг от друга и маленькие, то стоит добавить им за счет паддинга небольшую активную зону вокруг надписи. То есть если даже пользователь ткнул не в саму надпись, а близко к ней, то клик засчитывается. Это облегчает попадание в пункты меню. Иначе надо точно в буквы попадать.
Кнопки соцсетей должны реагировать на наведение, например, бледнеть, становиться ярче, еще как-то меняться, ты можешь поискать готовые CSS эффекты. Также, картинки на кнопках очень плохого качества: https://imgur.com/a/WuWquKP в FF66
Надо извлечь из PSD более качественные картинки. Я проверил, значки с кнопок можно извлечь даже бесплатным гимпом, без покупки фотошопа. Увы, гимп извлекает их только в расторовой форме.
Скорее всего ты потерял полупрозрачность. Там иконки полупрозрачные, то есть там есть частично прозрачные пиксели с альфа-каналом. У тебя же альфа-канала в картинке нет и из-за этого иконки стали с угловатыми краями, а не плавными. Почитай про формат PNG, полупрозрачность и альфа-канал.
Для адреса и телефона надо использовать иконки из файла, а не посторонние.
Адаптивность под разные разрешения экрана сделана слабо, для галочки. Вот косяки:
- если мы уменьшаем ширину окна, то надпись "digital & branding agency based ..." криво переносится - она разбивается на 3 строчки разной ширины. Это из-за наличия там br, который надо либо отключать, либо заменить на что-то получше.
- на ширине 700 px меню упирается в правый край окна без отступов, а надпись ниже We Are Webpaint смотрится непропорционально большой. Я бы уменьшил шрифт заголовков до 44 px / 30 px на такой ширине, слегка убавил бы вертикальные паддинги в шапке. Попробуй сам на глаз подобрать размеры, чтобы надписи были бы не слишком большие, и не слишком маленькие, а в самый раз, и чтобы шапка смотрелась пропорционально.
- на ширине в 700px блоки Consectetur, Tristiquet разваливаются и выглядят криво
- на ширине в 540 px меню проваливается вниз
- на ширине 430 px надпись digital & branding agency упирается в края окна, что некрасиво. Вертикальное меню, наверно, тоже не самое удачное решение тут. Лучше наверно сделать его в 2 колонки и сделать расстояние между пунктами больше, чтобы пальцем не промазать. Также, можно сделать кнопку "меню" и спрятать меню за ней, хотя это снижает discoverability пунктов в нем - пользователь о них не узнает.
- на маленькой ширине блок Consectetur лучше было бы выстроить вертикально, в 1 колонку, вынеся картинку влево или убрав ее вообще.
- на маленькой ширине картинки в портфолио очень мелкие. Удобно ли их разглядывать? Хотя, мне нравится компактность и возможность их всех увидеть на одном экране
- На маленькой ширине иконка перед адресом вываливается на отдельную строку
- на маленькой ширине иконки соцсетей и телефон слишком близко и пальцем можно нажать не туда. Нужно предусмотреть отступ от телефона до иконок, сам телефон, кстати можно сделать крупнее. Я для эксперимента поставил ему размер 24px, отступы по 20px, выглядит не так и плохо.
Надо более тщательно проработать адаптивность. Пока я вижу, что ты просто в паре мест уменьшил размеры блоков и шрифта. Этого недостаточно. Попробуй подумать, как пользователю на маленьком экране будет удобнее пользоваться твоим сайтом.
По коду. Иконки принято делать не img, а фоновыми картинками. img используется для картинках в контенте - статьях, например. Тут img подходит для картинок портфолио, может быть для блока Consectetur, но не для остальных мест.
Названия css классов должны быть сделаны в одинаковом стиле, а вразнобой. У тебя то минус, то подчеркивания использованы.
> title__of__service
Так длинно, пишут просто service-title. Плюс, тут этот класс вообще не нужен, так как ты можешь использовать стиль .service h3
Завязываться в CSS на тег nav не очень хорошо, так как завтра могут добавить какую-то вторую навигацию и твои стили будут ей мешать, придется переделывать. Это плохо.
Вместо i.font__light проще было бы выделить жирную часть просто тегом strong.
> .service{
>display: inline-block;
Стоит тут указывать vertical-align.
Когда ты используешь transition, стоит явно перечислять анимируемые свойства, чтобы не анимировалось ничего лишнего.
Далее, тестировал ли ты кроссбраузерность? Знаешь, где можно генерировать скриншоты? Надо проверить поддержку в разных браузерах. Вот примеры сервисов:
- https://developer.microsoft.com/en-us/microsoft-edge/tools/screenshots/ для IE (Не отображает сайты с jsbin)
- http://browsershots.org/https://jsbin.com/pofasoviqa/1/edit?output для FF/Chrome (плохо отображают сайты на jsbin)
- есть еще browserstack, он они хотят регистрацию, деньги и тд
Также, если что, для FF/Chromium можно найти портабельные версии, которые можно себе установить. С ИЕ так не получится и ради него придется делать виртуалки (по одной на каждую версию - спасибо Майкрософт). Хотя для этой задачи хватит и скриншотов, но для более серьезной работы удобнее иметь сами браузеры.
Я бы советовал поделить браузеры на 2 категории: новые и не-новые. В новых браузерах макет соответствует дизайну. В не-новых - может отступать от дизайна, если там нет нужной фичи, но оставаться читабельным. Например, если в браузере нет скругления уголков, то в таком браузере можно их сделать квадратными. ИЕ ниже 8 поддерживать не требуется, как и браузеры старше 10 лет.
Картинки в портфолио сделаны в PNG. Посмотри, можно ли их сжать в JPEG без заметных артефактов, чтобы получить меньший объем? Это умеет делать даже бесплатный GIMP.
Задавай вопросы, если что-то непонятно.
Кстати, для выкладывания сайта можно использовать github sites, если ты готов разобраться с гитом. Это будет удобнее для отладки и проверки сайта, так как там не будет постороннего кода. Можно, например, оценить его вес и скорость загрузки.
> Можно ли сделать плавный переход по якорям без JS?
В новых браузерах, как пишет анон, есть scroll-behaviour.
> Можно ли задать плавное всплывание картинок по нажатию на какую-либо из кнопок?
Изучи CSS-переходы (transition) и анимации, и посмотри, есть ли что-то подходящее. Тут трюк в том, что смена некоторых свойств отключает переход или анимацию. Например, смена display с block на hidden. И приходится скрывать элементы каким-то другим способом.
> Копирайт всегда должен быть в футере, можно ли их разделить и будет ли это правильно? Я разделил, потому что так в PSD было. С шапкой также сделал.
В принципе, можно.
> Подписи к заголовкам обрамлять в <h> на пункт меньше или <p>?
Если ты про Consectetur, Tritiquet - то там наверно <p>. Если ты про "We are webpaint", то можно сделать h2 или div. <p> - это абзац текста, тут явно не абзац текста.
> Почему "margin: 0;" в body не работал и мне приходилось подписывать его к каждому элементу?
Если ты про поля по краям окна, то они могут быть заданы не на body, а на html, и не в виде маргина, а в виде паддинга. Стандарта тут нет. Потому надо обнулять и маргин, и паддинг на обоих элементах.
Пункты меню стоило слегка подсвечивать при наведении или как-то еще выделять. Также, расскажу хитрость, так как они далеко друг от друга и маленькие, то стоит добавить им за счет паддинга небольшую активную зону вокруг надписи. То есть если даже пользователь ткнул не в саму надпись, а близко к ней, то клик засчитывается. Это облегчает попадание в пункты меню. Иначе надо точно в буквы попадать.
Кнопки соцсетей должны реагировать на наведение, например, бледнеть, становиться ярче, еще как-то меняться, ты можешь поискать готовые CSS эффекты. Также, картинки на кнопках очень плохого качества: https://imgur.com/a/WuWquKP в FF66
Надо извлечь из PSD более качественные картинки. Я проверил, значки с кнопок можно извлечь даже бесплатным гимпом, без покупки фотошопа. Увы, гимп извлекает их только в расторовой форме.
Скорее всего ты потерял полупрозрачность. Там иконки полупрозрачные, то есть там есть частично прозрачные пиксели с альфа-каналом. У тебя же альфа-канала в картинке нет и из-за этого иконки стали с угловатыми краями, а не плавными. Почитай про формат PNG, полупрозрачность и альфа-канал.
Для адреса и телефона надо использовать иконки из файла, а не посторонние.
Адаптивность под разные разрешения экрана сделана слабо, для галочки. Вот косяки:
- если мы уменьшаем ширину окна, то надпись "digital & branding agency based ..." криво переносится - она разбивается на 3 строчки разной ширины. Это из-за наличия там br, который надо либо отключать, либо заменить на что-то получше.
- на ширине 700 px меню упирается в правый край окна без отступов, а надпись ниже We Are Webpaint смотрится непропорционально большой. Я бы уменьшил шрифт заголовков до 44 px / 30 px на такой ширине, слегка убавил бы вертикальные паддинги в шапке. Попробуй сам на глаз подобрать размеры, чтобы надписи были бы не слишком большие, и не слишком маленькие, а в самый раз, и чтобы шапка смотрелась пропорционально.
- на ширине в 700px блоки Consectetur, Tristiquet разваливаются и выглядят криво
- на ширине в 540 px меню проваливается вниз
- на ширине 430 px надпись digital & branding agency упирается в края окна, что некрасиво. Вертикальное меню, наверно, тоже не самое удачное решение тут. Лучше наверно сделать его в 2 колонки и сделать расстояние между пунктами больше, чтобы пальцем не промазать. Также, можно сделать кнопку "меню" и спрятать меню за ней, хотя это снижает discoverability пунктов в нем - пользователь о них не узнает.
- на маленькой ширине блок Consectetur лучше было бы выстроить вертикально, в 1 колонку, вынеся картинку влево или убрав ее вообще.
- на маленькой ширине картинки в портфолио очень мелкие. Удобно ли их разглядывать? Хотя, мне нравится компактность и возможность их всех увидеть на одном экране
- На маленькой ширине иконка перед адресом вываливается на отдельную строку
- на маленькой ширине иконки соцсетей и телефон слишком близко и пальцем можно нажать не туда. Нужно предусмотреть отступ от телефона до иконок, сам телефон, кстати можно сделать крупнее. Я для эксперимента поставил ему размер 24px, отступы по 20px, выглядит не так и плохо.
Надо более тщательно проработать адаптивность. Пока я вижу, что ты просто в паре мест уменьшил размеры блоков и шрифта. Этого недостаточно. Попробуй подумать, как пользователю на маленьком экране будет удобнее пользоваться твоим сайтом.
По коду. Иконки принято делать не img, а фоновыми картинками. img используется для картинках в контенте - статьях, например. Тут img подходит для картинок портфолио, может быть для блока Consectetur, но не для остальных мест.
Названия css классов должны быть сделаны в одинаковом стиле, а вразнобой. У тебя то минус, то подчеркивания использованы.
> title__of__service
Так длинно, пишут просто service-title. Плюс, тут этот класс вообще не нужен, так как ты можешь использовать стиль .service h3
Завязываться в CSS на тег nav не очень хорошо, так как завтра могут добавить какую-то вторую навигацию и твои стили будут ей мешать, придется переделывать. Это плохо.
Вместо i.font__light проще было бы выделить жирную часть просто тегом strong.
> .service{
>display: inline-block;
Стоит тут указывать vertical-align.
Когда ты используешь transition, стоит явно перечислять анимируемые свойства, чтобы не анимировалось ничего лишнего.
Далее, тестировал ли ты кроссбраузерность? Знаешь, где можно генерировать скриншоты? Надо проверить поддержку в разных браузерах. Вот примеры сервисов:
- https://developer.microsoft.com/en-us/microsoft-edge/tools/screenshots/ для IE (Не отображает сайты с jsbin)
- http://browsershots.org/https://jsbin.com/pofasoviqa/1/edit?output для FF/Chrome (плохо отображают сайты на jsbin)
- есть еще browserstack, он они хотят регистрацию, деньги и тд
Также, если что, для FF/Chromium можно найти портабельные версии, которые можно себе установить. С ИЕ так не получится и ради него придется делать виртуалки (по одной на каждую версию - спасибо Майкрософт). Хотя для этой задачи хватит и скриншотов, но для более серьезной работы удобнее иметь сами браузеры.
Я бы советовал поделить браузеры на 2 категории: новые и не-новые. В новых браузерах макет соответствует дизайну. В не-новых - может отступать от дизайна, если там нет нужной фичи, но оставаться читабельным. Например, если в браузере нет скругления уголков, то в таком браузере можно их сделать квадратными. ИЕ ниже 8 поддерживать не требуется, как и браузеры старше 10 лет.
Картинки в портфолио сделаны в PNG. Посмотри, можно ли их сжать в JPEG без заметных артефактов, чтобы получить меньший объем? Это умеет делать даже бесплатный GIMP.
Задавай вопросы, если что-то непонятно.
Вообще, мне кажется, что не совсем. TDD - это когда ты получаешь задачу (надо добавить страницу, на которой пользователь может подписаться или отписаться от рассылки), формулируешь требования (пользователь должен иметь возможность подписаться, пользователь должен иметь возможность отписаться, нельзя 2 раза подписаться на одну рассылку итд). Пишешь тесты, проверяющие эти требования. Пишешь код.
В отличие от обычного подхода, когда ты сначала пишешь код, а потом перечитываешь задачу и пишешь тесты.
У тебя же другой подход: ты подгоняешь тесты под то, что вернет код. Значит, это скорее всего не очень хорошие тесты. Возможно, например, при твоем подходе у тебя в тестах захардкожены какие-нибудь массивы, которые возвращает функция, вместо проверки требований к ней.
Не забывай, что тесты проверяют именно выполнение требований.
Фраза про var_dump, думаю, относится к случаю, когда ты тестируешь написанный код, расставляя в нем вар-дампы и смотря глазами, что они выведут. Это действительно неудачный подход. Но если ты, например, исправляешь баг, и хочешь что-то сдампить, то тут тест вместо этого писать не надо.
>>76041
Если тебя волнует проверка типов, можно по-старинке, циклом foreach проверить. А подробности про типы указывать в phpDoc.
> Но понял, что это потребляет больше времени, чем экономит.
Получил ценный опыт.
>>75943
>> Он представляет ответ на HTTP-запрос. В Симфони контроллер получает на вход Request и выдает на выходе Response, а фреймворк уже выводит его содержимое. Это удобно для тестирования, мы можем вызвать контроллер и смотреть, что он там сгенерировал.
> Этот объект нужен для того, чтобы содержать в себе html-код? При тестировании каким-то образом проверяется его содержимое (html)? Не могу понять.
Да, содержит. И упрощает тестирование не только этим. В чистом PHP у программы нет явно определенных входных и выходных данных. Данные запроса она берет из глобальных переменных, а результат выводит куда-то наружу через echo. И это неудобно и при разработке, и при тестировании. Вот, допустим, мы хотим написать функцию, которая что-то определяет по входным данным. Как описать, что ей нужны эти данные? Тут-то нам и поможет реквест:
function getPageName(Request $req): string {}
Теперь явно видно, что использует функция. И это удобно при тестах, мы просто создаем временный объект Request, передаем ей, и это никак не влияет на остальной код. А если бы мы использовали глобальные переменные вроде $_GET, то не было бы видно, что функция зависит от входных данных, а при тесте изменения в $_GET либо пришлось бы как-то откатывать, либо бы наш тест мог бы повлиять на другие. Разве это удобно?
Наконец, в случае с объектом Request мы можем вызвать функцию на несуществующих данных. Если мы хотим проверить, "в случае, если пользователь пришлет такой запрос, что вернет функция?" - мы просто создаем временный объект Request, и вызвыаем функцию, а что делать при использовании глобальных переменных? Придется городить костыли.
Что касается Response, то он позволяет нам решать, что делать с ответом. В чистом PHP код отдает заголовки через header, отдает контент через echo и мы не можем их перехватить и что-то с ними сделать. В случае с Response, нам возвращают Response и мы с ним делаем, что хотим. То есть теперь мы решаем, что делать с ответом. Это полезно в тестах, и не только.
Например, мы хотим сделать автоматическое шифрование кук - при отдаче шифруем, при получении расшифровываем. В случае с Request/Response мы это можем сделать, не меняя сам код приложения. Просто пишем функцию, которая принимает на вход Response с открытыми куками и возвращает Response с зашифрованными (и тестировать такую функцию несложно). А вот если приложение использует глобальные переменные и отдает куки через setcookie, то сделать это сложно - без костылей вроде runkit вызов функции не перехватить.
Наконец рассмотрим еще один пример. Допустим, мы используем какую-то другую модель сервера, когда PHP код не умирает, а обрабатывает много запросов в цикле. Естественно, для отдачи контента там будут другие функции, и без объекта Response код, использующий Header/echo, с такой моделью работать не будет. В случае наличия объекта код трогать не придется.
То есть разговор о Request/Response похож на разговор про недостатки глобальных переменных. Плюсы:
- мы контролируем входные/выходные данные для кода и решаем, что с ними делать. Можем как-то их модифицировать, подменять итд.
- мы явно описываем, что код получает на вход и дает на выходе
- мы не получаем проблем, типичных для глобальных переменных
Ну и как дополнительный плюс, у объекта Request/Response могут быть какие-то дополнительные возможности, например, готовый метод для установки имени скачиваемого файла, или срока кеширования, чтобы нам не надо было вспоминать, какие заголовки за это отвечают.
> В каком месте выводить полученный результат?
> Достаточно написать во фронт-контроллере echo $response->getBody(); ?
Достаточно. Но только там не только тело, но и еще код ответа и HTTP-заголовки. В симфони у респонса есть метод send(), который все это отправляет в браузер.
>>75943
>> Он представляет ответ на HTTP-запрос. В Симфони контроллер получает на вход Request и выдает на выходе Response, а фреймворк уже выводит его содержимое. Это удобно для тестирования, мы можем вызвать контроллер и смотреть, что он там сгенерировал.
> Этот объект нужен для того, чтобы содержать в себе html-код? При тестировании каким-то образом проверяется его содержимое (html)? Не могу понять.
Да, содержит. И упрощает тестирование не только этим. В чистом PHP у программы нет явно определенных входных и выходных данных. Данные запроса она берет из глобальных переменных, а результат выводит куда-то наружу через echo. И это неудобно и при разработке, и при тестировании. Вот, допустим, мы хотим написать функцию, которая что-то определяет по входным данным. Как описать, что ей нужны эти данные? Тут-то нам и поможет реквест:
function getPageName(Request $req): string {}
Теперь явно видно, что использует функция. И это удобно при тестах, мы просто создаем временный объект Request, передаем ей, и это никак не влияет на остальной код. А если бы мы использовали глобальные переменные вроде $_GET, то не было бы видно, что функция зависит от входных данных, а при тесте изменения в $_GET либо пришлось бы как-то откатывать, либо бы наш тест мог бы повлиять на другие. Разве это удобно?
Наконец, в случае с объектом Request мы можем вызвать функцию на несуществующих данных. Если мы хотим проверить, "в случае, если пользователь пришлет такой запрос, что вернет функция?" - мы просто создаем временный объект Request, и вызвыаем функцию, а что делать при использовании глобальных переменных? Придется городить костыли.
Что касается Response, то он позволяет нам решать, что делать с ответом. В чистом PHP код отдает заголовки через header, отдает контент через echo и мы не можем их перехватить и что-то с ними сделать. В случае с Response, нам возвращают Response и мы с ним делаем, что хотим. То есть теперь мы решаем, что делать с ответом. Это полезно в тестах, и не только.
Например, мы хотим сделать автоматическое шифрование кук - при отдаче шифруем, при получении расшифровываем. В случае с Request/Response мы это можем сделать, не меняя сам код приложения. Просто пишем функцию, которая принимает на вход Response с открытыми куками и возвращает Response с зашифрованными (и тестировать такую функцию несложно). А вот если приложение использует глобальные переменные и отдает куки через setcookie, то сделать это сложно - без костылей вроде runkit вызов функции не перехватить.
Наконец рассмотрим еще один пример. Допустим, мы используем какую-то другую модель сервера, когда PHP код не умирает, а обрабатывает много запросов в цикле. Естественно, для отдачи контента там будут другие функции, и без объекта Response код, использующий Header/echo, с такой моделью работать не будет. В случае наличия объекта код трогать не придется.
То есть разговор о Request/Response похож на разговор про недостатки глобальных переменных. Плюсы:
- мы контролируем входные/выходные данные для кода и решаем, что с ними делать. Можем как-то их модифицировать, подменять итд.
- мы явно описываем, что код получает на вход и дает на выходе
- мы не получаем проблем, типичных для глобальных переменных
Ну и как дополнительный плюс, у объекта Request/Response могут быть какие-то дополнительные возможности, например, готовый метод для установки имени скачиваемого файла, или срока кеширования, чтобы нам не надо было вспоминать, какие заголовки за это отвечают.
> В каком месте выводить полученный результат?
> Достаточно написать во фронт-контроллере echo $response->getBody(); ?
Достаточно. Но только там не только тело, но и еще код ответа и HTTP-заголовки. В симфони у респонса есть метод send(), который все это отправляет в браузер.
break можно использовать только в определенных случаях, в циклах, например. В твоем случае хватит просто if/else. exit не требуется. Повтори, как работает if, и напиши так:
if (номер соответствует выражению) {
делать что-то одно;
} else {
делать что-то другое;
}
if/else можно вкладывать друг в друга. Не забывай про отступы и форматирование.
Твой код пока что неверный, так как он признает верным телефон из 11 букв.
> ^[7]
Скобки около семерки тут не требуются.
Регулярку стоит писать примерно так:
- в начале +7 или 8
- затем 11 раз повторяется конструкция: (одна цифра и любое число дополнительных скобок, минусов, пробелов)
- затем конец строки
>>75846
Его обычно не включают ни в один компонент, он сбоку сам по себе. Если речь о хелперах вроде преобразования даты в текстовый формат. В твоем примере это может быть часть контроллера, так как работает с даными запроса, хотя кто-то отнесет ее к модели. Тут пограничный вариант.
Вообще, код не обязан строго относиться к M, V или C. MVC - это ключевые компоненты, которые участвуют в обработке запроса. Ну и MVC вообще был придуман для долгоживущих десктопных приложений с окошечками и кнопочками, и главной идеей было отделение кода UI от модели (так как до этого их могли смешивать). К веб-приложениям он немного притянут за уши, скажем так.
Ну например, крон-скрипт - это что? По расположению похож на контроллер. А почему он с пользователем не взаимодействует? А где его View?
> Можно ли из хелпера обращаться к глобальным переменным?
Обращаться к глобальным переменным плохо почти всегда, так что не стоит. Лучше, когда функция получает аргументы явно. А так, получается скрытая недокументированная зависимость: если ты не задал какую-то глобальную переменную, то функция не будет работать. Как об этом догадаться? Это не очевидно. А если функция меняет глобальную переменную, то это называется "побочный эффект".
В твоем случае, идеально, было бы, например, так:
$this->authHelper = new AuthHelper($request, $userDb);
...
if ($this->authHelper->isAuthorized()) ...
Или так (объект можно использовть многократно):
$this->authHelper = new AuthHelper($userDb);
...
if ($this->authHelper->isAuthorized($request))
Тут мы явно видим, от чего зависит класс. Но если у тебя нет класса Request, то можно работу с кукой сделать в хелпере, но оговориться, что с этой кукой никто больше не имеет право работать для соблюдения принципа единой ответственности.
break можно использовать только в определенных случаях, в циклах, например. В твоем случае хватит просто if/else. exit не требуется. Повтори, как работает if, и напиши так:
if (номер соответствует выражению) {
делать что-то одно;
} else {
делать что-то другое;
}
if/else можно вкладывать друг в друга. Не забывай про отступы и форматирование.
Твой код пока что неверный, так как он признает верным телефон из 11 букв.
> ^[7]
Скобки около семерки тут не требуются.
Регулярку стоит писать примерно так:
- в начале +7 или 8
- затем 11 раз повторяется конструкция: (одна цифра и любое число дополнительных скобок, минусов, пробелов)
- затем конец строки
>>75846
Его обычно не включают ни в один компонент, он сбоку сам по себе. Если речь о хелперах вроде преобразования даты в текстовый формат. В твоем примере это может быть часть контроллера, так как работает с даными запроса, хотя кто-то отнесет ее к модели. Тут пограничный вариант.
Вообще, код не обязан строго относиться к M, V или C. MVC - это ключевые компоненты, которые участвуют в обработке запроса. Ну и MVC вообще был придуман для долгоживущих десктопных приложений с окошечками и кнопочками, и главной идеей было отделение кода UI от модели (так как до этого их могли смешивать). К веб-приложениям он немного притянут за уши, скажем так.
Ну например, крон-скрипт - это что? По расположению похож на контроллер. А почему он с пользователем не взаимодействует? А где его View?
> Можно ли из хелпера обращаться к глобальным переменным?
Обращаться к глобальным переменным плохо почти всегда, так что не стоит. Лучше, когда функция получает аргументы явно. А так, получается скрытая недокументированная зависимость: если ты не задал какую-то глобальную переменную, то функция не будет работать. Как об этом догадаться? Это не очевидно. А если функция меняет глобальную переменную, то это называется "побочный эффект".
В твоем случае, идеально, было бы, например, так:
$this->authHelper = new AuthHelper($request, $userDb);
...
if ($this->authHelper->isAuthorized()) ...
Или так (объект можно использовть многократно):
$this->authHelper = new AuthHelper($userDb);
...
if ($this->authHelper->isAuthorized($request))
Тут мы явно видим, от чего зависит класс. Но если у тебя нет класса Request, то можно работу с кукой сделать в хелпере, но оговориться, что с этой кукой никто больше не имеет право работать для соблюдения принципа единой ответственности.
Проверь, нет ли в твоих файлах символа BOM. Если есть, сохраняй в utf-8 без BOM.
>>75786
Придется наверно писать руками. Можно попробовать new DateTime("tomorrow 12:00");
>>75710
> как там представление реализовать
Отдавать HTML и пусть тот, кому нужно, сам загоняет его в респонс. Добавить функцию, которая принимает респонс и загоняет HTML в него.
>>75354
Тут сложно определить момент, где заканчивается правильный номер и начинается неправильный. Потому для упрощения я предложил идею про наличие 11 цифр. Но ты можешь предложить более удачные правила, если хочешь.
Спасибо за предоставленный приём. Тесты на JS (а точнее на TypeScript) действительно проходят медленно, и иногда даже без асинхронных операций проверка запуска теста с единственной строчкой expect(true).toBeTrue() выдавал ошибку таймаута (???) или выполнялся спустя секунд 15. Представляю как серверная обработка запросов будет проходить так же медленно.
Всё больше убеждаюсь, что серверная часть должна быть написана на предназначенных для этого языках, например PHP. Передаю этот ценный опыт всем анонам и хочу чтобы они оценили преимущество этого языка в том, что он как раз предназначен для веб-технологий.
>Проверь, нет ли в твоих файлах символа BOM. Если есть, сохраняй в utf-8 без BOM.
да каждый файл проверил в нотпаде, там кодировка без бом
тем более это дефолтная страница от yii, все должно работать
может я просто с настройками (пик1) где-то проебался или ещё что-то
хотя тот же codeigniter у меня нормально работал
> Ну а то, что фактической проверки типа возвращаемых значений и аргументов нет - ну что же, приходится забить на это хуй в угоду быстроте разработки.
Проверки типов для каждого из элементов массива и не должно быть. В PHP проверки типов происходят в рантайме, а значит время проверки будет увеличиваться в зависимости от длины массива, такого ни в одном языке нет. Способы обхода проблемы:
1) Создавать коллекцию для каждого класса:
class UserCollection {
addUser(user: User) {}
removeUser(user: User) {}
}
Тогда UserCollection можно использовать в качестве тайп-хинта и проверка не будет зависеть от числа элементов в массиве. Generic-коллекцию с таким вариантом создать не получится, поэтому этим способом никто не пользуется.
2) Использовать статические анализаторы вроде PHPStan/Psalm. До языка TypeScript им, конечно же, далеко, но базовые потребности покрывают неплохо. Мы внедряли PHPStan с самым щадящим режимом (level 0) в CI, постепенно повышая уровень строгости, польза есть. Python, кстати, пошёл именно по этому пути, там типы в рантайме не проверяются: https://docs.python.org/3/library/typing.html
>>75887
Это костыльно (работает только если аргумент последний) и влияет на перформанс. Лучше попробуй статические анализаторы.
Дошел до задачи с калькулятором, не могу понять, как выполнить операцию из переменной $op над $result и $number
>- затем 11 раз повторяется конструкция: (одна цифра и любое число дополнительных скобок, минусов, пробелов)
Ебните меня хуем по лбу. Почему я до этого не допер. Никак не привыкну разбивать целое к частным.
>- в начале +7 или 8
>- затем 11 раз повторяется конструкция: (одна цифра и любое число дополнительных скобок, минусов, пробелов)
>- затем конец строки
Я над этим покумекал, а не будет ли выгоднее убрать плюс, но при этом оставить 11 цифр ( тут ошибку понял). А потом сделать проверку 7 или 8
>В названии грится RESTful, а у тебя аутентификация походу через куки и сессии
Делал через стандартную ларавелевскую. Думаешь, они имели в виду перепилить её на рестфулл? Возможно и так, но я не сообразил.
>Контроллер search в неймспейса Ajax опять же не особо нужен, не проще в Number@index ( / url) добавить возможность фильтровать по запросу, например /?name=123&number=456 и возвращать отфильтрованные звонки.
Аякс же. Там в дополнительных требованиях было обеспечить работу поиска без перезагрузки страницы (а ещё про то, что хорошо бы вообще весь фронт написать на реакте/ангуляре, но поскольку я в них не умею, то просто убрал лишнее из описания).
>Еще не совсем понятно зачем сортировать в пхп а не в бд, и вообще про методы all() лучше забыть.
Да, пожалуй. Надо будет мне переписать запросы.
Спасибо за ревью!
$text = preg_replace($regexp,mb_strtoupper('$0'),$text);
Ты сначала применяешь к строке '$0' функцию mb_strtoupper (от этого она не меняется), а потом передаешь результат в preg_replace.
Для твоей задачи нужна функция preg_replace_callback().
>Аякс же. Там в дополнительных требованиях было обеспечить работу поиска без перезагрузки страницы (а ещё про то, что хорошо бы вообще весь фронт написать на реакте/ангуляре, но поскольку я в них не умею, то просто убрал лишнее из описания).
Эти два метода делают одно и тоже, только формат ответа разный. Можно явно передавать ключ format=json/html где html будет дефолтным или смотреть например по Accept заголовку но я так не делал никогда и хз.
>Эти два метода делают одно и тоже, только формат ответа разный.
А и верно. Надо будет попробовать сделать поиск тем же контроллером, что и выдачу.
>Ты сначала применяешь к строке '$0'
Т.е я применяю функцию не к найденному тексту, а конкретно заданной строке '$0'? Соответственно возвращается это же строка, и только потом она считывается как "найденный по регулярке текст", и в итоге ничего не меняется.
Правильно ли я понимаю что preg_replace_callback принимает в себя только функции, а preg_replace функции не принимает?
С первого раза, кстати, вышло. Спасибо большое
http://sandbox.onlinephpfunctions.com/code/acc29ae465e387db405a8aacfcf5a583138c2c37
Ну кто делает треды с более 500-ми постов?
Тред сдох а весь браузер висит, а процессор в ноутбуке свестит и пердит.
А что если я прогружают с тилибона, где каждый килобайт трафика бабло насчитывает?
Зашёл ещё, как-то в arduino тред в /ra - так там вообще больше 1000 постов, ещё и с картинками, блядь.
Обычно на треды 500 постов до бамплимита, и перекат,
либо же - бесконечные треды с лимитом в 500 постов, в которых старые посты - обрезаются...
Пилите перекот!
Погоди еще 2-3 дня. Сейчас разберемся с анончиками, которые писали свои вопросы, и сделаем перекат. Про тормоза я знаю.
В связи с этим вопрос: зачем нужен язык, который становится джавой, когда есть джава?
Для начала идеон, нопомни эта сука не может в кириллицу.
NetBeans
Потому, что он заточен под свою задачу. ЯП, который и швец и жнец и в жопе холодец - не очень обычно получается.
Задачка по регулярке, на проверку текста на ошибки с последующим их исправлением. Я текст разбил на массив и каждый элемент проверяю отдельно. Вот не знаю как сделать так, чтобы значение в условии if (это $result=", {$result}";) сохранилось для значения в массиве $results.
Что за основы?
Ох, раза 3 перечитал, пока понял. Ты хочешь исправить все ошибки в словах и сделать текст со всеми исправлениями? Тогда лучше всего просто создать новый, пустой массив слов. На каждом шаге цикла класть в него либо исходное, либо исправленное слово. После окончания цикла собрать массив в строку с помощью implode(). Учти, что там могут быть сложности с пробелами между словами:
Были слова в массиве: да|но|это|не|точно
Стали после исправления: да|, но|это|не|точно
Если ты теперь склеишь это, добавляя пробел, то получится "да , но это..." с лишним пробелом. Если же склеивать, не добавляя пробелы, то их надо как-то добавлять вручную к каждому слову.
Тут можно добавлять запятую в другое слово:
да,|но|это|не|точно
Или же можно вообще не разбивать текст на слова, и искать ошибки регуляркой по всему тексту. Например, для пропущенной запятой можно написать выражение:
- сначала идет буква
- за ней любое число пробелов
- далее "а" или "но"
- далее граница слова (чтобы не срабатывало на любые слова, начинающиеся с "а")
И стоит называть переменные лучше. Не results, а, например, foreach ($words as $word).
>На каждом шаге цикла класть в него либо исходное, либо исправленное слово.
У меня на картинке есть массив, Там каждый индекс (ключ) из массива с его значением проверяется на регулярку и потом изменяется в условии if. По сути программа считает переменную $result внутри if (preg_match($regex3, $result)) как временную переменную. И я не понимаю, как изменения в $result передать для значения в массиве, чтобы он часть текста с ошибкой заменил на уже исправленный.
Во-первых, result - это не ключ, а значение элемента массива.
Во-вторых, если тебе надо изменить исходный массив $results, то ты можешь это сделать, записывая в него значение по ключу:
foreach ($words as $key => $word) {
$words[$key] = "hello, $word";
}
Но это усложняет понимание работы программы (ты на лету изменяешь массив, по которому идет цикл). Лучше не записывать изменения в исходный массив, а создать новый и записывать исправленные слова в него.
Господа, я тупой или не очень?
Для одного проекта мне надо поставить db* из pear
Pear мне говорит, что db депрекейтед в пользу mdb2
При этом, 1.9.3 (stable) released on 2018-12-05
https://pear.php.net/package/DB/download/
а mdb2: Status:2.5.0b5 (beta) released on 2012-10-29
пофиксил господи наконец-то
не юзайте говно мамонта типа пхп <7 как бы вам удобно это не казалось
Вопрос по организации архитектуры сайта.
Например я сделаю личный бложек (с помощью фреймворка или велосипеда). И тут понадобится расширить сайт чем-то массивным, например прирутить форум. Как поступить в этом случае? Делать форум на фундаменте текущего блога (то есть писать код прямо рядом с php-классами блога) или создавать отдельный 'репозиторий', в который перенаправлять запросы через index.php? А если я хочу сделать общую авторизацию то как быть?
$foo = 'n';
$request = '/bar/$foo/more';
Разобрался. Нужно юзать двойные кавычки.
tl;dr
Проблема решена
>>if (details is None) or (not 'Bearer token' in details['authextra']):
>>builtins.TypeError: argument of type 'NoneType' is not iterable
>
>И, во втором случае,
>
>>if (details is None) :
>> principal[u'extra'] = {
>> u'error': u"Access denied: No Bearer token in authexta"
>> }
>> return principal
>>
>>
>> token = details['authextra']['Bearer token'];
>
>return не выполняется и код продолжает выполнятся
>
>>token = details['authextra']['Bearer token'];
>>builtins.TypeError: 'NoneType' object is not subscriptable
Здесь нет проверки на существование свойства authextra.
>> try:
>> payload = jwt.decode(token, JWT_SECRET)
>> except Exception as e:
>> principal[u'extra'] = {
>> u'error': e
>> }
>> return principal
>>
>> ...
>
>return снова не выполняется и вбрасывается ошибка: "WAMP message serialization error: Object of type 'InvalidSignatureError' is not JSON serializable"
>https://pyjwt.readthedocs.io/en/latest/api.html#jwt.exceptions.InvalidSignatureError
Питон сам по себе не сериализирует исключение в строку.
Решено в связи с https://forum.crossbar.io/t/python-code-does-not-work-as-expected/1495
tl;dr
Проблема решена
>>if (details is None) or (not 'Bearer token' in details['authextra']):
>>builtins.TypeError: argument of type 'NoneType' is not iterable
>
>И, во втором случае,
>
>>if (details is None) :
>> principal[u'extra'] = {
>> u'error': u"Access denied: No Bearer token in authexta"
>> }
>> return principal
>>
>>
>> token = details['authextra']['Bearer token'];
>
>return не выполняется и код продолжает выполнятся
>
>>token = details['authextra']['Bearer token'];
>>builtins.TypeError: 'NoneType' object is not subscriptable
Здесь нет проверки на существование свойства authextra.
>> try:
>> payload = jwt.decode(token, JWT_SECRET)
>> except Exception as e:
>> principal[u'extra'] = {
>> u'error': e
>> }
>> return principal
>>
>> ...
>
>return снова не выполняется и вбрасывается ошибка: "WAMP message serialization error: Object of type 'InvalidSignatureError' is not JSON serializable"
>https://pyjwt.readthedocs.io/en/latest/api.html#jwt.exceptions.InvalidSignatureError
Питон сам по себе не сериализирует исключение в строку.
Решено в связи с https://forum.crossbar.io/t/python-code-does-not-work-as-expected/1495
>Делать форум на фундаменте текущего блога (то есть писать код прямо рядом с php-классами блога)
Конечно так. Тебе нужно чтобы у тебя были отдельно файлы например blog.php и forum.php. Почитай ещё про архитектуру MVC, станет очень много понятно.
https://github.com/codedokode/pasta/blob/master/arch/mvc.md
Чего мне читать, и как стать все-таки сеньером помидором? Если что js я почти не трогаю, текущий фреймворк Symfony 4. Комьюнити PHP в моем городе почти не существует, судьба забросила, увы.
Ох уж эта ебля с настройками, не могу направлять запросы через ФронтКонтроллер. Если на прямую к нему обращаться, то создает роутер и выполняет функцию из класса роутера. Чего я только не делал. И закидывал в стандартный путь, и новый адресс делал, и разные htaccess создавал, и переустанавливал ос. Уже не понимаю куда смотреть.
php7.2 Апач2.4 на виртуалке ubuntu 18.04.
Это в .htaccess:
"AddDefaultCharset utf-8
RewriteEngine on
RewriteBase /
RewriteRUle ^(.*)$ index.php"
Я не анончик проверяющий регулярки, сам только начал. Но кое-чем подсобить могу. С ideone лучше перекатывайся, у меня там тоже много чего не работало.
http://sandbox.onlinephpfunctions.com/code/134052c9924b5d9c6751b620f4774cb5533fec30
Вот, скрипт выполняется, но он все равно не правильный. Как минимум из за этой регулярки '/\D/' в 4 строке. Тебе же по заданию нужно сначала проверить верный ли номер. Если я напишу "+7892()326 ()-()2_032 сап Двач", то все кроме чисел выкинется, "+" тоже выкинется, а по заданию нам нужна проверка стоит ли там +7 или 8 (+8 и 7 - номер неверный) и т.д
Еще не забывай при выводе переменной, если сразу за ней идет текст заключать ее в {}.
Попробуй сделать так
RewriteEngine On
RewriteRule ^(.*)$ index.php
Если получится, желательно ещё отключить переадресацию для публичных файлов.
А в чем проблема? В том, что не вызывается index.php?
Для начала проверь, включено ли в конфиге Апача выполнение htaccess:
- https://httpd.apache.org/docs/2.4/mod/core.html#allowoverride
- https://www.linode.com/docs/web-servers/apache/how-to-set-up-htaccess-on-apache/
Не забудь, что после изменения конфига надо перезапустить Апач.
Далее, глянь в лог ошибок веб-сервера (/var/log/apache2 или /var/log/httpd). Посомтри, нет ли там чего странного.
>>77929
Там в начале может быть либо +7, либо 8. Если просто 7 или +8 - то это ошибка. Также, выкидывать лучше не все, а только пробелы, скобки, минусы. Ну а еще лучше все же написать полноценную регулярку для проверки:
- в начале идет 8 или +7
- затем выражение (одна цифра, за ней любое число скобок, минусов, пробелов), которое повторяется 11 раз
Далее, у тебя ошибки в коде:
> if (mb_substr ($clearNum,0,1)=='/7|8/'){
Знак "равно" просто делает посимвольное сравнение, он не интерпретирует выражение как регулярку. Тебе тут нужен preg_match.
Сеньор - это в каждой компании разное понятие. Общее там то, что у сеньора обычно порядка 6 лет опыта, высокий уровень знаний в своей области, развитый кругозор, способность самостоятельно решать сложные задачи, проектировать систему или ее компоненты. Высокий уровень ответственности (он не может выгрузить обновление на прод и уйти домой, не убедившись, что все ок и никаких ошибок нет).
Но сам по себе уровень разный - сеньор в подвальной веб-студии может не пройти собеседование в Яндексе на джуниора. В какой-то компании "сеньор" может просто значить "человек с 6 годами опыта".
Сеньор способен передавать свой опыт и знания младшим разработчикам, способен руководить ими.
3 года - это скорее мидл, если только ты не какой-нибудь вундеркинд с выдающимися способностями.
Вот, первая попавшаяся статьи из гугла, довольно адекватная: https://dataart.ru/news/junior-middle-senior-v-chem-raznifa-i-kuda-dalshe/
Так что ответ: развивайся, чтобы соответствовать написанному выше представлению. Изучай внимательно технологии, с которыми работаешь, интересуйся другими технологиями, изучай теорию, читай статьи, смотри доклады. Читай и решай задачи с собеседований в Яндекс. Вот сегодня, например, ты изучил что-то новое?
Что касается того, что ты живешь в маленьком городе, то во-первых, можно попробовать найти работу удаленно, во-вторых, сейчас же не крепостное право, тебя там никто цепями не держит.
Я также могу добавить, что один из лучших способов развиваться - это попасть в компанию с сложными проектами. Туда, где ты еле-еле можешь пройти собеседование, и то не факт, что примут. Где есть куча людей умнее тебя. Если ты можешь такую компанию найти и внедриться туда, то ты будешь учиться, получая за это деньги. Что может быть лучше?
>>77697
Удобнее всего будет сделать форму на отдельном домене (forum.example.com) или URL (http://example.com/forum/), взяв готовый движок. При необходимости интегрировать с сайтом (сделать сквозной логин, например).
Жаль, что-то у меня руки не дошли проверить код. Но я бы тебе посоветовал:
- использовать объекты вместо массивов для ключевых типов данных, которые используются во многих функциях в коде
- не городить такие массивы, когда можно обойтись без них. Тут вместо сложного массива principal можно было использовать внутри функции просто переменные вроде error, role, и при необходимости перед возвратом результата собирать из них массив. Хотя, тут можно просто возвращать кортеж (role, error), как мне кажется.
- разбивать стену кода на отдельные функции. Ты и токены проверяешь, и к БД подсоединяешься, и что только не делаешь.
- в try/except указывать конкрентные классы исключений, которые тебя интересуют, а не ловить все подряд
>>77564
Возможно, что последние изменения - это багфиксы или исправления безопасности, а никакого нового функционала не добавляется.
>>77517
Чтобы не нарушать семантику, можно еще использовать display: table вместо настоящих таблиц. А так, да, вертикальное центрирование - это table или flexbox.
>>77063
Sublime Text, Netbeans for PHP, Eclipse PDT, если ты богат (или если можешь выпросить лицензию как студент) и у тебя быстрый проц, то PHPStorm. VS Code неплоха, но не у всех быстро работает.
preg_replace принимает текстовое выражение
preg_replace_callback принимает функцию для замены
Твой код склеивает слова вместе, если между ними 2 или более пробелов.
$count; - эта строка ничего не делает, ее надо убрать.
[а-яё]|[a-z] - это равносильно [а-яёa-z]
<a href="<?php echo Utils::generateLink($i, 'query'); ?>"><?php echo $i ?></a>
<?php endfor; ?>
Можно ли так смешивать php и html?
Благодарю, помогло.
Вообще програмировать начал класса так с 6 или 7, какие-то основы паскаля и на олимпиадки ходил(правда там нифига не смог решить кроме первых задач ибо разрыв когда в школе тебя учат ифам, а на олимпиаде поиск минимального пути на графе просто выносит нахуй). На работу за бабло пошел через 3 месяца, как стукнуло 18, когда понял что найти работу джуном на яве - анриал, и пошел задротить PHP. Щас мне 22.
>>78170
Я и не планирую в яндекс на джуна идти. Что бы пройти туда собеседования не плохо надо знать Computer Science и матан, не то, что бы что-то плохое, но сейчас не в приоритете.
Ну я и сказал, что я крепкий мидл середнячек. Я нашел работук и даже здесь, я просто переехал в родное село пол года назад. Местная продуктовая компания.
Яв основном так и вывозил и быстренько подрос по карьере. Чё. Оп-кун, короче спасибо тебе, я тут где-то с 18 или 20 треда сидел, хахахахахах.
Мда, а меня пхнула бабка на инженегра. Окончил бакалавра и 2 годика проработал. Сейчас понял, что это не мое, хотя к пк всегда душа лежала. Вот переучиваюсь сызнова.
Пагни, поясните по структуре проекта на симфони 4. В /src проекта есть директории Entity и Repository. С Entity более менее понятно - там хранятся файлы-модели таблиц БД. А для чего нужна директория Repository?
Так в чём ошибка была ты понял? Расскажи.
Задача
Каждый пользователь может написать другому пользователю. При первой отправке сообщения создаётся диалог. Сообщения могут быть текстовые, могут быть голосовые и могут быть видео. Каждое сообщение может удаляться либо у себя лично, либо у обоих пользователей. Диалоги так же могут удаляться у каждого пользователя лично (но не у обоих, т.е. каждый пользователь имеет только ссылку на диалог).
К каждому текстовому сообщению могут прикрепляться данные: изображение, аудио, видео, файл, пересылка сообщения и ответ на конкретное сообщение.
Прикреплений может быть несколько за сообщений.
Диалоги могут быть приватные (тет-а-тет) а могут быть публичные (конференции).
Соответственно архитектура будет такая:
пользователь:
- ...
текстовое_сообщение:
-uuid
-пользователь (автор сообщения)
-сообщение
-дата
-прочитано (булевый)
-...
аудио_сообщение:
-uuid
-пользователь (автор сообщения)
-звукозапись (путь/к/аудиофайлу)
-дата
-прочитано (булевый)
-прослушано (булевый)
-...
видео_сообщение:
-uuid
-пользователь (автор сообщения)
-видеозапись (путь/к/видеофайлу)
-дата
-прочитано (булевый)
-просмотрено (булевый)
-...
текстовое_сообщения: (хранит в себя ссылки на сообщения для каждого отдельного пользователя)
-uuid
-пользователь (кому принадлежит ссылка)
-диалог (диалог в котором сообщение находится)
-сообщение
-...
аудио_сообщения:
-...
видео_сообщения:
-...
прикрепление_изображения:
-uuid
-сообщение
-изображение
-...
прикрепление_аудиозаписи:
-...
и т.д. за исключением что у каждого прикрепления своя ссылка на источник.
диалог:
-uuid
-приватный (булевый)
-...
участники: (для приватных диалогов всегда будет два участника)
-uuid
-диалог
-пользователь
...
диалоги: (ссылки на диалоги для каждого пользователя)
-uuid
-пользователь
-диалог
...
Такова вся архитектура. База данных будет psql.
Название таблиц в боевой базе данных будет идентично т.е. будет и сообщение и сообщения. Это же не создаёт затруднения для понимания?
Разделение приложений к сообщению на отдельные таблицы для каждого типа, а не хранения всех видов файлов в одной, позволяет в будущем расширять приложение, если появятся новые типы данных.
>>78171
>Жаль, что-то у меня руки не дошли проверить код. Но я бы тебе посоветовал:
>
>- использовать объекты вместо массивов для ключевых типов данных, которые используются во многих функциях в коде
>- не городить такие массивы, когда можно обойтись без них. Тут вместо сложного массива principal можно было использовать внутри функции просто переменные вроде error, role, и при необходимости перед возвратом результата собирать из них массив. Хотя, тут можно просто возвращать кортеж (role, error), как мне кажется.
>- разбивать стену кода на отдельные функции. Ты и токены проверяешь, и к БД подсоединяешься, и что только не делаешь.
>- в try/except указывать конкрентные классы исключений, которые тебя интересуют, а не ловить все подряд
>Жаль, что-то у меня руки не дошли проверить код.
Всё хорошо, у меня было время чем другим заняться. Я рад, что сэкономил ваше время.
>- использовать объекты вместо массивов для ключевых типов данных, которые используются во многих функциях в коде
>объекты
Я правильно понимаю, что в Питоне объект, это instance класса, как в PHP, а не как в JS? Для каких ключевых типов данных вы имеете ввиду?
>- не городить такие массивы, когда можно обойтись без них. Тут вместо сложного массива principal можно было использовать внутри функции просто переменные вроде error, role, и при необходимости перед возвратом результата собирать из них массив. Хотя, тут можно просто возвращать кортеж (role, error), как мне кажется.
>Тут вместо сложного массива principal можно было использовать внутри функции просто переменные вроде error, role, и при необходимости перед возвратом результата собирать из них массив.
Разве это не создаст дополнительные проверки и не раздует код? Т.е. во время ответа писать: if error is not None: principals['extra']['error'] = error; Почему нельзя собирать массив "на ходу"?
>Хотя, тут можно просто возвращать кортеж (role, error), как мне кажется.
Возвращать кортеж из функции авторизации, вы это имели ввиду? Это не правильно, потому что principals сериализуется в json и возвращается клиенту.
>- разбивать стену кода на отдельные функции. Ты и токены проверяешь, и к БД подсоединяешься, и что только не делаешь.
Конечно. Это только черновик где я тестирую чтобы посмотреть как работает код.
У меня будет вопрос по архитектуре, когда я его сформулирую.
>- в try/except указывать конкрентные классы исключений, которые тебя интересуют, а не ловить все подряд
Понял.
Задача
Каждый пользователь может написать другому пользователю. При первой отправке сообщения создаётся диалог. Сообщения могут быть текстовые, могут быть голосовые и могут быть видео. Каждое сообщение может удаляться либо у себя лично, либо у обоих пользователей. Диалоги так же могут удаляться у каждого пользователя лично (но не у обоих, т.е. каждый пользователь имеет только ссылку на диалог).
К каждому текстовому сообщению могут прикрепляться данные: изображение, аудио, видео, файл, пересылка сообщения и ответ на конкретное сообщение.
Прикреплений может быть несколько за сообщений.
Диалоги могут быть приватные (тет-а-тет) а могут быть публичные (конференции).
Соответственно архитектура будет такая:
пользователь:
- ...
текстовое_сообщение:
-uuid
-пользователь (автор сообщения)
-сообщение
-дата
-прочитано (булевый)
-...
аудио_сообщение:
-uuid
-пользователь (автор сообщения)
-звукозапись (путь/к/аудиофайлу)
-дата
-прочитано (булевый)
-прослушано (булевый)
-...
видео_сообщение:
-uuid
-пользователь (автор сообщения)
-видеозапись (путь/к/видеофайлу)
-дата
-прочитано (булевый)
-просмотрено (булевый)
-...
текстовое_сообщения: (хранит в себя ссылки на сообщения для каждого отдельного пользователя)
-uuid
-пользователь (кому принадлежит ссылка)
-диалог (диалог в котором сообщение находится)
-сообщение
-...
аудио_сообщения:
-...
видео_сообщения:
-...
прикрепление_изображения:
-uuid
-сообщение
-изображение
-...
прикрепление_аудиозаписи:
-...
и т.д. за исключением что у каждого прикрепления своя ссылка на источник.
диалог:
-uuid
-приватный (булевый)
-...
участники: (для приватных диалогов всегда будет два участника)
-uuid
-диалог
-пользователь
...
диалоги: (ссылки на диалоги для каждого пользователя)
-uuid
-пользователь
-диалог
...
Такова вся архитектура. База данных будет psql.
Название таблиц в боевой базе данных будет идентично т.е. будет и сообщение и сообщения. Это же не создаёт затруднения для понимания?
Разделение приложений к сообщению на отдельные таблицы для каждого типа, а не хранения всех видов файлов в одной, позволяет в будущем расширять приложение, если появятся новые типы данных.
>>78171
>Жаль, что-то у меня руки не дошли проверить код. Но я бы тебе посоветовал:
>
>- использовать объекты вместо массивов для ключевых типов данных, которые используются во многих функциях в коде
>- не городить такие массивы, когда можно обойтись без них. Тут вместо сложного массива principal можно было использовать внутри функции просто переменные вроде error, role, и при необходимости перед возвратом результата собирать из них массив. Хотя, тут можно просто возвращать кортеж (role, error), как мне кажется.
>- разбивать стену кода на отдельные функции. Ты и токены проверяешь, и к БД подсоединяешься, и что только не делаешь.
>- в try/except указывать конкрентные классы исключений, которые тебя интересуют, а не ловить все подряд
>Жаль, что-то у меня руки не дошли проверить код.
Всё хорошо, у меня было время чем другим заняться. Я рад, что сэкономил ваше время.
>- использовать объекты вместо массивов для ключевых типов данных, которые используются во многих функциях в коде
>объекты
Я правильно понимаю, что в Питоне объект, это instance класса, как в PHP, а не как в JS? Для каких ключевых типов данных вы имеете ввиду?
>- не городить такие массивы, когда можно обойтись без них. Тут вместо сложного массива principal можно было использовать внутри функции просто переменные вроде error, role, и при необходимости перед возвратом результата собирать из них массив. Хотя, тут можно просто возвращать кортеж (role, error), как мне кажется.
>Тут вместо сложного массива principal можно было использовать внутри функции просто переменные вроде error, role, и при необходимости перед возвратом результата собирать из них массив.
Разве это не создаст дополнительные проверки и не раздует код? Т.е. во время ответа писать: if error is not None: principals['extra']['error'] = error; Почему нельзя собирать массив "на ходу"?
>Хотя, тут можно просто возвращать кортеж (role, error), как мне кажется.
Возвращать кортеж из функции авторизации, вы это имели ввиду? Это не правильно, потому что principals сериализуется в json и возвращается клиенту.
>- разбивать стену кода на отдельные функции. Ты и токены проверяешь, и к БД подсоединяешься, и что только не делаешь.
Конечно. Это только черновик где я тестирую чтобы посмотреть как работает код.
У меня будет вопрос по архитектуре, когда я его сформулирую.
>- в try/except указывать конкрентные классы исключений, которые тебя интересуют, а не ловить все подряд
Понял.
Ну я на математика учился. Мехмат. Любил математику, но понял, что хочется жрат и денех. Бросил где-то на втором курсе, хотя жалею сейчас немного.
Допустим юзер отправляет через форму какие-то данные. Сразу, как эти данные приходят в контроллер мне нужно сделать с ними некоторую работу, которая требует времени. Результат этой работы возвращать юзеру не нужно. Но нужно вернуть ему сообщение, типа "мы вас услышали. спасибо. идите нахуй". Как мне это сделать, не дожидаясь, пока вот та времязатратная работа завершится?
Ну, надеюсь теперь норм. Хотя я не понимаю, почему не могу выбить буквы. http://sandbox.onlinephpfunctions.com/code/abbfee8d43d3a7f0868c9a58441b1f02e5a19b1e
Буквы-флаги. В начали урока по регуляркам. Вместо /[a-zA-Z]/
можно написать /[a-z]/i
Если используешь русские буквы в выражении - ставь флаг 'u'.
/[а-яё]/iu
Так же у тебя по условию нужно проверить является ли строка номером. У тебя она является номером, хотя содержит "позвать Люсю".
Окей понял. За флаги спасибо, а то только через 101 проверил, а там автоматом проверяется. Перепсал с первичной проверкой любого текста. Но теперь почему-то выражение принимается в любом случае вне зависимости от того есть в нем буквы или нет (я писал только русские). http://sandbox.onlinephpfunctions.com/code/cce3879a8929314360111bd939069d09b7020410
Есть несколько моментов:
-в PHP есть функция preg_match($regexp, $text, $match). Она принимает на вход регулярку, текст и пустой массив. Она проверяет, есть ли в тексте подстрока, соответствующая данному шаблону и возвращает 0, если нет, или 1, если она есть.
If (preg_match('/[a-z]/',$text)), если функция найдет что нибудь по регулярному выражению - вернет 1. В обратном же случае вернет 0.
if (1) - Правда, идем дальше
if (0) - Ложь, переходим к Else.
-https://regex101.com/r/qF7vT8/3
Видишь, твоё регулярное выражение не очень то и работает. Должны светиться только ВСЕ правильные номера.
-Читай внимательнее уроки и задачки.
-Сделай сначала первую часть задания, где программа должна из массива номеров выдавать только правильные, а только потом преступай к их форматированию
>должна из массива номеров выдавать только правильные
Вот в чем цимес. а я получается пытался один насильно привести к формату.
http://sandbox.onlinephpfunctions.com/code/c0635168d02a3291ef7885de84e3891b2615f6e5
Буквы не пропускает, знаки удаляет, 7 на 8 меняет
А теперь сделай чтобы скрипт массив номеров проверял. Исходник массивов в задании есть.
Выбранные ответы вроде не-верные.
ПХП разрабы. Вот вы риал учите такую хуйню? Сука, ониб еще спросили каким цветом кнопка по созданию бэкапа блядь.
У пыхи огроменная экосистема - вордпресы, битриксы, друпалы, хуялы, ларавели, мускули, посгресйли, мариидб, докеры, композеры, паттерны, хуятерны, мемкешы, хуешы блядь. Этот список можно на 100500 страниц составить.
Это все выучить невозможно физически, а учитывая, что все оно периодически обновляется/меняется - то и поддерживать знания на актуальном уровне.
Вы реально все это учите?
Я работаю и когда мне приходится иметь дело с новой цмс - я просто тыкаю куда попало, лишь бы работало, потомучто я не собираюсь тратить свою жизнь на знание ебучих говно-цмс, которые через год будут работать по другому.
(11|12|13|14|15|16|17|18|19)
Где истина?
Тут цимес не в учении, один хер забудешь половину, но когда столкнешься снова уже не будешь орать в ужасе и более менее соориентируешься куда копать.
Аа точняк ща.
Спасибо
Сделал, глянь, https://ideone.com/v2XnwF. Дуболомно, но пашет. Я сейчас кстати сам с себя проиграл, половина номеров повторяются, а я думал, что один и тотже в цикле идет
Согласен, но со скобками пришлось так сделать т к я изначально накосячил с ними и подбивал куда нужно, спасибо тем не менее
Всё сразу знать и не надо. Обычно у кодера есть какой-то свой стек полюбившихся\прибыльных технологий.
Ну понятно, что всегда есть какая-то база кодерская, которую знать просто необходимо для понимания как оно вообще работает и куда смотреть.
if (file_exists(__DIR__ . '/' . 'dbconfig.ini')) {
$config = parse_ini_file(__DIR__ . '/' . 'dbconfig.ini');
} else {
throw new Exception('Database config file missing');
}
Так это должно выглядеть?
Ты этот урок читал? Там разве нет ответа? https://github.com/codedokode/pasta/blob/master/php/exceptions.md
Тебе нужно сделать свой кастомный класс исключений, если ты хочешь его ловить и обрабатывать. Хотя я не очень понимаю, что ты собрался делать, если нет файла конфига.
Если не собираешься, то можно использовать стандартное исключение.
Прочитал сейчас второй раз и уже понял более менее. В самом простом случае можно просто везде, где могут быть косяки выбрасывать исключения, а в фронт контроллере зарегать set_error_handler и там уже сохранять в лог, для всех исключений выводить в http ответ 503 и страницу с этой ошибкой для пользователя.
Что значит "во время разработки выбрасываешь"? В продакшене специально условие писать, чтобы не выбрасывать?
Тут каких-то различий между продакшеном и dev не требуется, разве что в dev можно подробности выводить.
По идее перед деплоем ты всё равно запускаешь тесты, и если конфиг с подключением к бд куда-то пропал то ты об этом узнаешь, я не понимаю как такое можно обработать чтобы приложение не упало
>>79543
>в http ответ 503
Почему не 500?
Никого, кстати, не смущает что код ответа http часто совмещают с ответом самого приложения? Например мы сделали форму на сайте, клиент отправляет запрос с данными, мы видим там ошибку, часто ставят ответ 422, хотя по сути ответ же 200, сервер всё прочитал и понял, хттп на транспортном уровне сделал своё дело, а то что внутри запроса данные не такие это уже наше дело, нет?
Оп писал,что пустая страница с ответом 200 при выбросе исключения - это плохо. Там что-то про гугл и яндекс, которые индексируют такие страницы, а 503 они не трогают.
После выброса исключения невозможно продолжить работу.
Возможно, Макконелл имел в виду "продолжить работу" с точки зрения пользователя, например, показать страницу ошибки и дать возможность повторно отправить запрос.
Вообще, коды ошибки предназначены для программ и роботов. Человек их не видит. Они должны передавать роботам информацию о результате выполнения запроса.
Ты наверно имел в виду HTTP API.Там часто код используют для сигнализирования об ошибке на уровне логики (пользователь с таким емайл уже существует). Почему бы и нет? Кто-то использует код ошибки, кто-то возвращает 200 и информацию об ошибке в JSON. Оба подхода имеют право на жизнь, лишь бы они были задокументированы.
Я недавно думал на эту тему, какие есть плюсы, и подумал, что код ошибки в API позволяет быстро понять результат запроса не заглядывая в содержимое.
В случае же HTML-страниц это обычно не используется. Если ты ввел существующий email в форму регистрации, то ты получаешь страницу с кодом 200. Хотя с другой стороны, коды 404 или 403 используются.
В принципе, можно возвращать 422 и в ответ на форму регистрации, если ты гарантируешь, что браузеры вместо нее не покажут стандартную страницу ошибки.
Не путай язык и задачи.
echo <<< END
хуй
залупа
END; - видимо не работает в 7 версии - так как по-другому вывести несколько строк?
А, бля, я все понял
Надеюсь я тебя правильно понял.
Если пользователь выбрал сортировку в таблице, то значит у нас на стороне пхп должны быть гет-параметры. Соответственно нужно вставить их во все ссылки при генерации.
$orderBy = $_GET['orderBy'];
$orderDir = $_GET['orderDir'];
LinkHelper::generateLink($page, $searchQuery, $orderBy, $orderDir);
Должно получиться, что-то подобное
/?page=7&orderBy="name"&orderDir="DESC"
Вот моя функция на пике, которая решает эту проблему.
Идея в том, что гет-параметры сохраняются из предыдущего запроса и просто подставляются в новую ссылку. Функция передаётся как замыкание в шаблон и через неё формируются ссылки на пагинацию и т.д.
Если у кого есть идея лучше - прошу озвучить, так как я писал от балды.
Числа в строки: http://sandbox.onlinephpfunctions.com/code/8f78a64f577b9d3eb6c00dfb5df5cba88483bd29
Простой калькулятор:
http://sandbox.onlinephpfunctions.com/code/5a4761d4e56d1d933fa1dbfe2f80e6feebe2f89a
Аноны, нужно ли устанавливать библиотеки функций для php?
У меня и консоль и localhost жалуются:
Call to undefined function mb_... on line ...
Обосрался насчет substr
Каков наглец
Это я и имел ввиду. Может не так выразился.
НУ и нахуй так писать, не говоря, что за ошибка и как ты её исправил? Может кому-то полезно будет. И вообще это хороший тон отвечать с конкретикой на свой вопрос если удалось самому справиться с проблемой.
Я построчно из одного файла, где сплошной текст без переносов читаю, и пишу в другой файл, но так, что бы каждая новая часть была с переносом строки.
Что характерно в новом файле переносы работают, но '\r\n' так же отображаются. Как так то?
Код:
$handle = fopen($path, "r");
$handle1 = fopen($path1, 'a');
while(!feof($handle)){>>80236
$str = fgets($handle, 50);
$str = $str.'\r\n';
fwrite($handle1, $str."\r\n");
}
fclose($handle);
fclose($handle1);
Бля, ссылка чет лишняя в коде. Я криволапый.
Бля,все ясно, я не отвечайте мне. Я просто косолапый в квадрате.
Если тебе это неинтересно, то ты можешь придумать какую-то другую задачу аналогичной сложности на более интересную тему. Если непонятно, то это задача на фреймворки. Возьми какой-нибудь фреймворк (Laravel, Symfony), почитай туториалы к нему и станет понятнее.
Ну или задавай вопросы, если есть какие-то конкретные вопросы.
>>79893
Да, там не все идеально, если будет возможность, то надо будет исправить. Насчет "вырезает" - имелось в виду, естественно, возвращает. Разница между substr и mb_substr в том, что первая работает только с 8-битными кодировками, а вторая - с той, что задана в опции default_encoding в php.ini или функцией mb_default_encoding().
>>79860
> Числа в строки: http://sandbox.onlinephpfunctions.com/code/8f78a64f577b9d3eb6c00dfb5df5cba88483bd29
> if(preg_match('/(11|12|13|14|15|16|17|18|19)/',$lastTwo))
Тут можно просто написать что lastTwo больше/равно 11 и меньше/равно 19. Числа все же правильнее сравнивать с помощью математических операций.
В if/else надо использовать фигурные скобки, так как иначе легко допустить ошибку при чтении или правке кода. Это же не Питон, где все оступами определяется.
В функции inclideWord if/else можно было написать в один уровень, без вложенности.
> $count = strlen($number);
> if ($count == 1){
Тут лучше использовать математику, например условие что number меньше 10.
Функция smallNumberToText переусложнена и в ней много копипасты. Нужно ее переделать. Например, так:
- если в числе есть сотни, добавить слово для сотен
- если число оканчивается на 11-19, добавить соотв. слово
- если в числе есть десятки, то добавить слово для них
- если в числе есть единицы, то добавить слово для них (с учетом пола)
Смотри, всего 4 условия.
То же касается функции numberToText(), ее надо упростить и избавить от копипасты.
> Аноны, нужно ли устанавливать библиотеки функций для php?
> У меня и консоль и localhost жалуются:
> Call to undefined function mb_... on line ...
Если ты под виндой, то скорее всего она просто не включена в php.ini директивой extension = .... Если под линуксом, то там обычно расширения ставятся отдельно (в Убунте/дебиане через apt-get install).
> Простой калькулятор:
http://sandbox.onlinephpfunctions.com/code/5a4761d4e56d1d933fa1dbfe2f80e6feebe2f89a
> if ($operation == ''){
> ...
> if ($operation == '+')
Здесь лучше было использовать elseif, так как у нас либо есть заданная ранее операция, либо нет.
А так, верно.
Если тебе это неинтересно, то ты можешь придумать какую-то другую задачу аналогичной сложности на более интересную тему. Если непонятно, то это задача на фреймворки. Возьми какой-нибудь фреймворк (Laravel, Symfony), почитай туториалы к нему и станет понятнее.
Ну или задавай вопросы, если есть какие-то конкретные вопросы.
>>79893
Да, там не все идеально, если будет возможность, то надо будет исправить. Насчет "вырезает" - имелось в виду, естественно, возвращает. Разница между substr и mb_substr в том, что первая работает только с 8-битными кодировками, а вторая - с той, что задана в опции default_encoding в php.ini или функцией mb_default_encoding().
>>79860
> Числа в строки: http://sandbox.onlinephpfunctions.com/code/8f78a64f577b9d3eb6c00dfb5df5cba88483bd29
> if(preg_match('/(11|12|13|14|15|16|17|18|19)/',$lastTwo))
Тут можно просто написать что lastTwo больше/равно 11 и меньше/равно 19. Числа все же правильнее сравнивать с помощью математических операций.
В if/else надо использовать фигурные скобки, так как иначе легко допустить ошибку при чтении или правке кода. Это же не Питон, где все оступами определяется.
В функции inclideWord if/else можно было написать в один уровень, без вложенности.
> $count = strlen($number);
> if ($count == 1){
Тут лучше использовать математику, например условие что number меньше 10.
Функция smallNumberToText переусложнена и в ней много копипасты. Нужно ее переделать. Например, так:
- если в числе есть сотни, добавить слово для сотен
- если число оканчивается на 11-19, добавить соотв. слово
- если в числе есть десятки, то добавить слово для них
- если в числе есть единицы, то добавить слово для них (с учетом пола)
Смотри, всего 4 условия.
То же касается функции numberToText(), ее надо упростить и избавить от копипасты.
> Аноны, нужно ли устанавливать библиотеки функций для php?
> У меня и консоль и localhost жалуются:
> Call to undefined function mb_... on line ...
Если ты под виндой, то скорее всего она просто не включена в php.ini директивой extension = .... Если под линуксом, то там обычно расширения ставятся отдельно (в Убунте/дебиане через apt-get install).
> Простой калькулятор:
http://sandbox.onlinephpfunctions.com/code/5a4761d4e56d1d933fa1dbfe2f80e6feebe2f89a
> if ($operation == ''){
> ...
> if ($operation == '+')
Здесь лучше было использовать elseif, так как у нас либо есть заданная ранее операция, либо нет.
А так, верно.
У тебя как-то странно сделано, что параметры сохраняются в $this->get. Это значит, что если ты с помощью функции linkMaker() создашь 2 анонимных функции, то одна будет влиять на другую (параметры, переданные в одну, отобразятся в другой). Это неправильно. Лучше сделать так:
function makeLinkGenerator(array $basicParams): callable {
return function (array $override) { ... };
}
Использование:
$linkGen = makeLinkGenerator(['sort' => 'name', 'page' => 3]);
echo $linkGen(['page' => 2]); // Меняем страницу
echo $linkGen(['sort' => 'score', 'page' => '']); // при смене сортировки сбрасываем пагинацию
Можно сделать аналогичную штуку с помощью объекта:
$linkGen = new LinkGenerator([....]);
$linkGen->makeLink([....]);
Можно сделать специализированные функции для разных видов ссылок:
$linkGen = new LinkGenerator([....]);
$linkGen->makePaginationLink(3);
$linkGen->makeSortLink('-name');
Ты же зачем-то смешал оба подхода. Если ты используешь анонимную функцию, то не надо перезаписывать поля объекта.
Можно вместо массива с базовыми параметрами использовать объект ViewParams/ViewFilter.
Также стоит предусмотреть такие вещи:
- если номер страницы равен 1, то его не надо добавлять в ссылку
- если мы меняем сортировку, то надо сбрасывать пагинацию
>>79741
Возможно, у тебя там отступ (пробелы) перед END или какие-то лишние символы после.
У тебя как-то странно сделано, что параметры сохраняются в $this->get. Это значит, что если ты с помощью функции linkMaker() создашь 2 анонимных функции, то одна будет влиять на другую (параметры, переданные в одну, отобразятся в другой). Это неправильно. Лучше сделать так:
function makeLinkGenerator(array $basicParams): callable {
return function (array $override) { ... };
}
Использование:
$linkGen = makeLinkGenerator(['sort' => 'name', 'page' => 3]);
echo $linkGen(['page' => 2]); // Меняем страницу
echo $linkGen(['sort' => 'score', 'page' => '']); // при смене сортировки сбрасываем пагинацию
Можно сделать аналогичную штуку с помощью объекта:
$linkGen = new LinkGenerator([....]);
$linkGen->makeLink([....]);
Можно сделать специализированные функции для разных видов ссылок:
$linkGen = new LinkGenerator([....]);
$linkGen->makePaginationLink(3);
$linkGen->makeSortLink('-name');
Ты же зачем-то смешал оба подхода. Если ты используешь анонимную функцию, то не надо перезаписывать поля объекта.
Можно вместо массива с базовыми параметрами использовать объект ViewParams/ViewFilter.
Также стоит предусмотреть такие вещи:
- если номер страницы равен 1, то его не надо добавлять в ссылку
- если мы меняем сортировку, то надо сбрасывать пагинацию
>>79741
Возможно, у тебя там отступ (пробелы) перед END или какие-то лишние символы после.
Да, HTTP-код предназначен для роботов и программ, и они его используют.
>>79411
Сейчас оба регекспа работают. Надо учесть, что на regex101 немного другие флаги (в PHP нет флага global), и могут быть немного другие правила использования бекслешей (в PHP сначала обрабатываются бекслеши как часть синаксиса записи строк, а потом в том, что останется, обрабатываются бекслеши как часть регулярки).
>>79090
Этот тест проверяет знание Битрикса. Если ты с ним каждый день работаешь, то ты и так знаешь ответы на эти вопросы.
ООП и паттерны запоминаются, если не заучивать их наизусть, а попытаться разобраться и понять, зачем это нужно и как бы ты сам бы решил такюу задачу.
Гордиться низким уровнем знаний и нежеланием развиваться довольно странно.
>>79314
Решено верно, но.
> if (preg_match('/^(\+7)/',$checkNum)) {
> $checkNum=preg_replace('/^(\+7)/',8,$checkNum);
Тут сразу можно делать preg_replace без предварительной проверки.
И по тестированию: лучше не просто писать, что номер верный/неверный, а сразу добавлять, правильно ли определила его программа или нет. Так как сейчас у тебя надо вручную читать то, что вывели программа, чтобы проверить, что она правильно определяет номера.
> Но теперь почему-то выражение принимается в любом случае вне зависимости от того есть в нем буквы или нет (я писал только русские).
У тебя стоит звездочка, которая значит, что предыдущий символ может встретиться 0 или более раз. Даже если в строке нет букв, это соответствует условию "0 раз". Под твою регулярку вообще любая строка подходит, даже пустая строка.
Надо убрать зведочку либо заменить ее на плюс.
>>78542
Чтобы регулярка корректно работала с нелатинскими символами в кодировке utf-8 (а у тебя она), надо ставить флаг u. Иначе регулярка думает, что перед ней текст в 1-байтовой кодировке, а русская буква - это 2 отдельных символа (так как она кодируется 2 байтами в utf-8). Описано тут: https://github.com/codedokode/pasta/blob/master/php/strings-utf8.md
>>79192
const появился в PHP7, а книга наверно написана раньше. Лучше использовать const.
>>78611
Это способ записи больших строк. Можно использовать кавычки, а можно heredoc. Ты бы документацию вместо книги почитал бы, например:
- https://www.php.net/manual/ru/language.types.string.php
- https://ru.wikipedia.org/wiki/Heredoc-синтаксис
Главный плюс в том, что в нем можно использовать кавычки и/или спецсимволы без экранирования, что удобно. К тегу <pre> он отношения не имеет.
>>78531
Есть очереди задач для этого.
> Но теперь почему-то выражение принимается в любом случае вне зависимости от того есть в нем буквы или нет (я писал только русские).
У тебя стоит звездочка, которая значит, что предыдущий символ может встретиться 0 или более раз. Даже если в строке нет букв, это соответствует условию "0 раз". Под твою регулярку вообще любая строка подходит, даже пустая строка.
Надо убрать зведочку либо заменить ее на плюс.
>>78542
Чтобы регулярка корректно работала с нелатинскими символами в кодировке utf-8 (а у тебя она), надо ставить флаг u. Иначе регулярка думает, что перед ней текст в 1-байтовой кодировке, а русская буква - это 2 отдельных символа (так как она кодируется 2 байтами в utf-8). Описано тут: https://github.com/codedokode/pasta/blob/master/php/strings-utf8.md
>>79192
const появился в PHP7, а книга наверно написана раньше. Лучше использовать const.
>>78611
Это способ записи больших строк. Можно использовать кавычки, а можно heredoc. Ты бы документацию вместо книги почитал бы, например:
- https://www.php.net/manual/ru/language.types.string.php
- https://ru.wikipedia.org/wiki/Heredoc-синтаксис
Главный плюс в том, что в нем можно использовать кавычки и/или спецсимволы без экранирования, что удобно. К тегу <pre> он отношения не имеет.
>>78531
Есть очереди задач для этого.
Использование таблиц с похожими именами будет однозначно вызывать путаницу. Более того, я пока сам не понял, в чем разница между "сообщением" и "сообщениями" или "диалог" и "диалоги".
Если ты хочешь делать для каждого пользователя свою копию сообщения, то не проще ли просто сделать таблицу "диалог", и таблицу "сообщения", где в сообщении стоит ссылка на диалог и ссылка на получателя, кому оно предназначено? То есть, так:
диалог:
- uuid
участники диалога:
- id пользователя
- id диалога
сообщения: (копия для каждого получателя)
- uuid
- id, объединяющий копии одного сообщения
- id диалога
- id отправителя
- id получателя этой копии
- признак прочтения
- текст
Кстати, в больших чатах невыгодно будет делать для каждого свою копию сообщения. Выгоднее делать всего одну копию для всех.
Далее, у тебя есть наследование таблиц (для текстовых, аудио, видео сообщений сделаны отдельные таблицы). Ты использовал Concrete Table Inheritance. Не очень удобно делать их разными таблицами. Представь, что тебе надо получить последние 10 сообщений в диалоге. Тебе надо будет запрашивать данные из каждой таблицы (из текстовых сообщений, аудио сообщений, видео сообщений) и объединять. Как ты себе представляешь эффективную пагинацию по ним?
Логичнее было бы использовать другой паттерн наследования, например, Single Table Inheritance или Class Table Inheritance. В случае STI мы делаем единую таблицу для всех видов сообщений. В случае CTI мы делаем одну таблицу для полей, которые есть в каждом виде сообщения (отправитель, время отправки, получено), и отдельные таблицы для доп. полей (которые есть только у опр. вида сообщения):
- http://design-pattern.ru/patterns/class-table-inheritance.html
- http://design-pattern.ru/patterns/single-table-inheritance.html
В обоих случаях мы можем легко получить последние N сообщений или сделать пагинацию без использования OFFSET.
Также, возможно, имеет смысл свести любые сообщения к текстовым сообщениям с приложениями. Тогда аудиосообщение будет просто текстовым сообщением без текста с приложенным аудио, с пометкой, что аудиосообщение тут главное. Тут надо взвесить плюсы и минусы. Плюс в упрощении структуры таблиц.
Примерно то же относится к вложениям. Возможно, проще просто сделать одну таблицу для любых вложений.
Наконец, тут надо быть готовым к денормализации или кешированию. Допустим, мы хотим показывать пользователю число непрочитанных сообщений в диалоге. Делать это через подсчет сообщений без признака прочтения неэффективно. Эффективнее иметь готовую циферку. Или, допустим, мы хотим вывести верхние 50 диалогов. Эффективнее одним простым запросом получить список диалогов, циферки непрочтенных сообщений, чем выбирать эти данные через джойны и группировки.
Потому вначале надо придумать правильную нормализованную схему, а потом составить список типичных запросов, которые мы будем делать, и при необходимости реализовать индексы, денормализацию либо кеширование. В мессенджере чаще будут идти запросы на чтение, и они должны выполняться быстро, и с использованием индексов. Запросы также можно разделить на часто и редко выполняемые (например, получение списка последних диалогов выполняется часто, а получение истории месячной давности - редко) и учитывать это.
Поэтому, думаю, схему надо доработать.
> Разделение приложений к сообщению на отдельные таблицы для каждого типа, а не хранения всех видов файлов в одной, позволяет в будущем расширять приложение, если появятся новые типы данных.
Но оно не дает нам эффективно делать пагинацию по сообщениям, что сводит плюсы на нет.
> Я правильно понимаю, что в Питоне объект, это instance класса, как в PHP, а не как в JS? Для каких ключевых типов данных вы имеете ввиду?
Да, как в PHP. Я имел в виду, что если у тебя в коде есть какой-то массив, который много где используется, передается и возвращается разными функциями, то правильнее будет сделать его объектом. Но, как я понял, там был просто тестовый код, и тогда это не важно.
> Разве это не создаст дополнительные проверки и не раздует код? Т.е. во время ответа писать: if error is not None: principals['extra']['error'] = error; Почему нельзя собирать массив "на ходу"?
Потому, что это разные задачи: получение данных и формирование ответа на JSON-запрос. И скорее всего, лучше чтобы отдельно была функция проверки авторизации, а отдельно функция, которая формирует JSON-ответ. А не совмещать это все в одной функции. Но так как это тестовый код, то это не принципиально.
Использование таблиц с похожими именами будет однозначно вызывать путаницу. Более того, я пока сам не понял, в чем разница между "сообщением" и "сообщениями" или "диалог" и "диалоги".
Если ты хочешь делать для каждого пользователя свою копию сообщения, то не проще ли просто сделать таблицу "диалог", и таблицу "сообщения", где в сообщении стоит ссылка на диалог и ссылка на получателя, кому оно предназначено? То есть, так:
диалог:
- uuid
участники диалога:
- id пользователя
- id диалога
сообщения: (копия для каждого получателя)
- uuid
- id, объединяющий копии одного сообщения
- id диалога
- id отправителя
- id получателя этой копии
- признак прочтения
- текст
Кстати, в больших чатах невыгодно будет делать для каждого свою копию сообщения. Выгоднее делать всего одну копию для всех.
Далее, у тебя есть наследование таблиц (для текстовых, аудио, видео сообщений сделаны отдельные таблицы). Ты использовал Concrete Table Inheritance. Не очень удобно делать их разными таблицами. Представь, что тебе надо получить последние 10 сообщений в диалоге. Тебе надо будет запрашивать данные из каждой таблицы (из текстовых сообщений, аудио сообщений, видео сообщений) и объединять. Как ты себе представляешь эффективную пагинацию по ним?
Логичнее было бы использовать другой паттерн наследования, например, Single Table Inheritance или Class Table Inheritance. В случае STI мы делаем единую таблицу для всех видов сообщений. В случае CTI мы делаем одну таблицу для полей, которые есть в каждом виде сообщения (отправитель, время отправки, получено), и отдельные таблицы для доп. полей (которые есть только у опр. вида сообщения):
- http://design-pattern.ru/patterns/class-table-inheritance.html
- http://design-pattern.ru/patterns/single-table-inheritance.html
В обоих случаях мы можем легко получить последние N сообщений или сделать пагинацию без использования OFFSET.
Также, возможно, имеет смысл свести любые сообщения к текстовым сообщениям с приложениями. Тогда аудиосообщение будет просто текстовым сообщением без текста с приложенным аудио, с пометкой, что аудиосообщение тут главное. Тут надо взвесить плюсы и минусы. Плюс в упрощении структуры таблиц.
Примерно то же относится к вложениям. Возможно, проще просто сделать одну таблицу для любых вложений.
Наконец, тут надо быть готовым к денормализации или кешированию. Допустим, мы хотим показывать пользователю число непрочитанных сообщений в диалоге. Делать это через подсчет сообщений без признака прочтения неэффективно. Эффективнее иметь готовую циферку. Или, допустим, мы хотим вывести верхние 50 диалогов. Эффективнее одним простым запросом получить список диалогов, циферки непрочтенных сообщений, чем выбирать эти данные через джойны и группировки.
Потому вначале надо придумать правильную нормализованную схему, а потом составить список типичных запросов, которые мы будем делать, и при необходимости реализовать индексы, денормализацию либо кеширование. В мессенджере чаще будут идти запросы на чтение, и они должны выполняться быстро, и с использованием индексов. Запросы также можно разделить на часто и редко выполняемые (например, получение списка последних диалогов выполняется часто, а получение истории месячной давности - редко) и учитывать это.
Поэтому, думаю, схему надо доработать.
> Разделение приложений к сообщению на отдельные таблицы для каждого типа, а не хранения всех видов файлов в одной, позволяет в будущем расширять приложение, если появятся новые типы данных.
Но оно не дает нам эффективно делать пагинацию по сообщениям, что сводит плюсы на нет.
> Я правильно понимаю, что в Питоне объект, это instance класса, как в PHP, а не как в JS? Для каких ключевых типов данных вы имеете ввиду?
Да, как в PHP. Я имел в виду, что если у тебя в коде есть какой-то массив, который много где используется, передается и возвращается разными функциями, то правильнее будет сделать его объектом. Но, как я понял, там был просто тестовый код, и тогда это не важно.
> Разве это не создаст дополнительные проверки и не раздует код? Т.е. во время ответа писать: if error is not None: principals['extra']['error'] = error; Почему нельзя собирать массив "на ходу"?
Потому, что это разные задачи: получение данных и формирование ответа на JSON-запрос. И скорее всего, лучше чтобы отдельно была функция проверки авторизации, а отдельно функция, которая формирует JSON-ответ. А не совмещать это все в одной функции. Но так как это тестовый код, то это не принципиально.
Repository это паттерн, про который можно прочесть тут:
- https://www.google.com/search?client=firefox-b-d&q=паттерн+repository
- http://design-pattern.ru/patterns/repository.html
- https://martinfowler.com/eaaCatalog/repository.html
Репозиторий изображает "хранилище", в котором хранятся сущности, откуда их можно получать, добавлять, удалять. При этом пользователь Репозитория не знает, где они хранятся: в БД, в массиве в памяти или где-то еще. В Доктрине репозиторий реализует такие интерфейсы, из которых видно, что он может делать:
- https://github.com/doctrine/persistence/blob/master/lib/Doctrine/Persistence/ObjectRepository.php
- https://github.com/doctrine/collections/blob/master/lib/Doctrine/Common/Collections/Selectable.php
Видно, что там в основном получение данных. Доктрина предоставляет готовый общий репозиторий:
- https://github.com/doctrine/orm/blob/master/lib/Doctrine/ORM/EntityRepository.php
Но ты можешь сделать свой репозиторий для какой-то сущности на основе доктриновского, и добавить туда дополнительные методы поиска данных.
В общем: если ты изучаешь Симфони, то изучи Доктрину. А если ты ее изучаешь, то изучи связанные с ней паттерны, такие, как Entity Map, Unit Of Work, Lazy Load: http://design-pattern.ru/index.html . Посмотри, как они реализованы в Доктрине.
Доктрину надо изучать. Я постоянно вижу, что люди ее не знают, и думают, например, что persist сохраняет объект в базу данных. Почему им лень даже документацию прочесть?
>>378182
Вместо echo лучше использовать <?= , и надо экранировать спецсимволы в ссылках при выводе.
>>80244
В одиночных кавычках \n это просто бекслеш и буква n.
>>80297
А я посто везде \n использую. Пишется короче и код читабельнее. Блокнотом Windows я все равно не пользуюсь.
Repository это паттерн, про который можно прочесть тут:
- https://www.google.com/search?client=firefox-b-d&q=паттерн+repository
- http://design-pattern.ru/patterns/repository.html
- https://martinfowler.com/eaaCatalog/repository.html
Репозиторий изображает "хранилище", в котором хранятся сущности, откуда их можно получать, добавлять, удалять. При этом пользователь Репозитория не знает, где они хранятся: в БД, в массиве в памяти или где-то еще. В Доктрине репозиторий реализует такие интерфейсы, из которых видно, что он может делать:
- https://github.com/doctrine/persistence/blob/master/lib/Doctrine/Persistence/ObjectRepository.php
- https://github.com/doctrine/collections/blob/master/lib/Doctrine/Common/Collections/Selectable.php
Видно, что там в основном получение данных. Доктрина предоставляет готовый общий репозиторий:
- https://github.com/doctrine/orm/blob/master/lib/Doctrine/ORM/EntityRepository.php
Но ты можешь сделать свой репозиторий для какой-то сущности на основе доктриновского, и добавить туда дополнительные методы поиска данных.
В общем: если ты изучаешь Симфони, то изучи Доктрину. А если ты ее изучаешь, то изучи связанные с ней паттерны, такие, как Entity Map, Unit Of Work, Lazy Load: http://design-pattern.ru/index.html . Посмотри, как они реализованы в Доктрине.
Доктрину надо изучать. Я постоянно вижу, что люди ее не знают, и думают, например, что persist сохраняет объект в базу данных. Почему им лень даже документацию прочесть?
>>378182
Вместо echo лучше использовать <?= , и надо экранировать спецсимволы в ссылках при выводе.
>>80244
В одиночных кавычках \n это просто бекслеш и буква n.
>>80297
А я посто везде \n использую. Пишется короче и код читабельнее. Блокнотом Windows я все равно не пользуюсь.
Я постараюсь тут в ближайшие пару дней проверить оставшиеся посты, но для надежности вы можете напомнить о них в новом треде.
>Использование таблиц с похожими именами будет однозначно вызывать путаницу. Более того, я пока сам не понял, в чем разница между "сообщением" и "сообщениями" или "диалог" и "диалоги".
Сообщение - это конкретная запись сообщения имеющая все данные о нём, от содержания до автора, id, даты публикации и т.д., а сообщения - это запись какие сообщения принадлежат какому пользователю и/или диалогу. Т.е. "сообщения" хранит ссылки для пользователя на id конкретного сообщения.
Как можно понятней назвать таблицы чтобы не вызвать путаницу?
Такая схема была предложена вами при обсуждении моей задачи чата на чистом JS:
https://phpclub.tech/pr/res/1019301.html#1028550
>чаты и диалоги лучше делать одинаковым способом, то есть представить диалог 2 пользователей как чат из 2 участников.
>Первый вариант - сделать у каждого пользователя списки входящих и исходящих сообщений (по аналогии с почтовым ящиком), и помещать копии сообщения как отправителю, так и получателю. При этом каждый может независимо управлять своей историей сообщений, например, очищать ее. Для чатов сообщение придется копировать многократно.
>
>При этом, чтобы связать сообщения, можно сделать какой-нибудь добавочный id. Можно например сделать таблицу сообщений (где и будет этот id), и отдельно таблицу, которая хранит копию сообщения для конкретного получателя.
Мне хочется казать, что я слегка её отшлифовал, убрав многократное копирование сообщений заменив его "ссылками" на сообщения.
>Если ты хочешь делать для каждого пользователя свою копию сообщения, то не проще ли просто сделать таблицу "диалог", и таблицу "сообщения", где в сообщении стоит ссылка на диалог и ссылка на получателя, кому оно предназначено? То есть, так:
>
>диалог:
>- uuid
>
>участники диалога:
>- id пользователя
>- id диалога
>
>сообщения: (копия для каждого получателя)
>- uuid
>- id, объединяющий копии одного сообщения
>- id диалога
>- id отправителя
>- id получателя этой копии
>- признак прочтения
>- текст
>
>Кстати, в больших чатах невыгодно будет делать для каждого свою копию сообщения. Выгоднее делать всего одну копию для всех.
>- id, объединяющий копии одного сообщения
Это получается что объединяющее копия одного сообщения это отдельная сущность/таблица? Я не понимаю тогда, это получается тоже самое что и я предложил, только колонки в разных таблицах. Ну и контент сообщения в моём предложении находиться только оригинальном, а не дублируется.
Повторюсь, что в ссылке на сообщение находиться id на сообщение, а не его содержимое:
>текстовое_сообщения: (хранит в себя ссылки на сообщения для каждого отдельного пользователя)
>-uuid
>-пользователь (кому принадлежит ссылка)
>-диалог (диалог в котором сообщение находится)
>-uuid сообщения
>-...
>Далее, у тебя есть наследование таблиц (для текстовых, аудио, видео сообщений сделаны отдельные таблицы). Ты использовал Concrete Table Inheritance. Не очень удобно делать их разными таблицами. Представь, что тебе надо получить последние 10 сообщений в диалоге. Тебе надо будет запрашивать данные из каждой таблицы (из текстовых сообщений, аудио сообщений, видео сообщений) и объединять. Как ты себе представляешь эффективную пагинацию по ним?
>
>Логичнее было бы использовать другой паттерн наследования, например, Single Table Inheritance или Class Table Inheritance. В случае STI мы делаем единую таблицу для всех видов сообщений. В случае CTI мы делаем одну таблицу для полей, которые есть в каждом виде сообщения (отправитель, время отправки, получено), и отдельные таблицы для доп. полей (которые есть только у опр. вида сообщения):
>
>- http://design-pattern.ru/patterns/class-table-inheritance.html
>- http://design-pattern.ru/patterns/single-table-inheritance.html
>
>В обоих случаях мы можем легко получить последние N сообщений или сделать пагинацию без использования OFFSET.
Я думал об этом и предполагал, что у psql есть встроенная поддержка этого, и как оказалось, действительно есть https://postgrespro.ru/docs/postgresql/11/ddl-inherit
>Также, возможно, имеет смысл свести любые сообщения к текстовым сообщениям с приложениями. Тогда аудиосообщение будет просто текстовым сообщением без текста с приложенным аудио, с пометкой, что аудиосообщение тут главное. Тут надо взвесить плюсы и минусы. Плюс в упрощении структуры таблиц.
Не согласен. Аудиосообщение по сути это аудиозапись, и в прикрепление тоже могут прикладывать аудиозапись, и поэтому будет конфликт, что есть голосовое сообщение, а что аудиофайл. Для этого нужно будет наследовать отдельный тип прикрепления, что является тем же самым что и унаследовать от сообщений. Я считаю, что голосовые сообщения должны быть отдельным типом сообщения, а не прикрепления.
>Наконец, тут надо быть готовым к денормализации или кешированию. Допустим, мы хотим показывать пользователю число непрочитанных сообщений в диалоге. Делать это через подсчет сообщений без признака прочтения неэффективно. Эффективнее иметь готовую циферку. Или, допустим, мы хотим вывести верхние 50 диалогов. Эффективнее одним простым запросом получить список диалогов, циферки непрочтенных сообщений, чем выбирать эти данные через джойны и группировки.
Да, я думаю, что можно хранить количество непрочитанных сообщений и дату последнего сообщения прямо в таблице диалога.
>>80460
>> Я правильно понимаю, что в Питоне объект, это instance класса, как в PHP, а не как в JS? Для каких ключевых типов данных вы имеете ввиду?
>
>Да, как в PHP. Я имел в виду, что если у тебя в коде есть какой-то массив, который много где используется, передается и возвращается разными функциями, то правильнее будет сделать его объектом. Но, как я понял, там был просто тестовый код, и тогда это не важно.
Этот массив был бы объектом с одним свойством этого самого массива?
>Использование таблиц с похожими именами будет однозначно вызывать путаницу. Более того, я пока сам не понял, в чем разница между "сообщением" и "сообщениями" или "диалог" и "диалоги".
Сообщение - это конкретная запись сообщения имеющая все данные о нём, от содержания до автора, id, даты публикации и т.д., а сообщения - это запись какие сообщения принадлежат какому пользователю и/или диалогу. Т.е. "сообщения" хранит ссылки для пользователя на id конкретного сообщения.
Как можно понятней назвать таблицы чтобы не вызвать путаницу?
Такая схема была предложена вами при обсуждении моей задачи чата на чистом JS:
https://phpclub.tech/pr/res/1019301.html#1028550
>чаты и диалоги лучше делать одинаковым способом, то есть представить диалог 2 пользователей как чат из 2 участников.
>Первый вариант - сделать у каждого пользователя списки входящих и исходящих сообщений (по аналогии с почтовым ящиком), и помещать копии сообщения как отправителю, так и получателю. При этом каждый может независимо управлять своей историей сообщений, например, очищать ее. Для чатов сообщение придется копировать многократно.
>
>При этом, чтобы связать сообщения, можно сделать какой-нибудь добавочный id. Можно например сделать таблицу сообщений (где и будет этот id), и отдельно таблицу, которая хранит копию сообщения для конкретного получателя.
Мне хочется казать, что я слегка её отшлифовал, убрав многократное копирование сообщений заменив его "ссылками" на сообщения.
>Если ты хочешь делать для каждого пользователя свою копию сообщения, то не проще ли просто сделать таблицу "диалог", и таблицу "сообщения", где в сообщении стоит ссылка на диалог и ссылка на получателя, кому оно предназначено? То есть, так:
>
>диалог:
>- uuid
>
>участники диалога:
>- id пользователя
>- id диалога
>
>сообщения: (копия для каждого получателя)
>- uuid
>- id, объединяющий копии одного сообщения
>- id диалога
>- id отправителя
>- id получателя этой копии
>- признак прочтения
>- текст
>
>Кстати, в больших чатах невыгодно будет делать для каждого свою копию сообщения. Выгоднее делать всего одну копию для всех.
>- id, объединяющий копии одного сообщения
Это получается что объединяющее копия одного сообщения это отдельная сущность/таблица? Я не понимаю тогда, это получается тоже самое что и я предложил, только колонки в разных таблицах. Ну и контент сообщения в моём предложении находиться только оригинальном, а не дублируется.
Повторюсь, что в ссылке на сообщение находиться id на сообщение, а не его содержимое:
>текстовое_сообщения: (хранит в себя ссылки на сообщения для каждого отдельного пользователя)
>-uuid
>-пользователь (кому принадлежит ссылка)
>-диалог (диалог в котором сообщение находится)
>-uuid сообщения
>-...
>Далее, у тебя есть наследование таблиц (для текстовых, аудио, видео сообщений сделаны отдельные таблицы). Ты использовал Concrete Table Inheritance. Не очень удобно делать их разными таблицами. Представь, что тебе надо получить последние 10 сообщений в диалоге. Тебе надо будет запрашивать данные из каждой таблицы (из текстовых сообщений, аудио сообщений, видео сообщений) и объединять. Как ты себе представляешь эффективную пагинацию по ним?
>
>Логичнее было бы использовать другой паттерн наследования, например, Single Table Inheritance или Class Table Inheritance. В случае STI мы делаем единую таблицу для всех видов сообщений. В случае CTI мы делаем одну таблицу для полей, которые есть в каждом виде сообщения (отправитель, время отправки, получено), и отдельные таблицы для доп. полей (которые есть только у опр. вида сообщения):
>
>- http://design-pattern.ru/patterns/class-table-inheritance.html
>- http://design-pattern.ru/patterns/single-table-inheritance.html
>
>В обоих случаях мы можем легко получить последние N сообщений или сделать пагинацию без использования OFFSET.
Я думал об этом и предполагал, что у psql есть встроенная поддержка этого, и как оказалось, действительно есть https://postgrespro.ru/docs/postgresql/11/ddl-inherit
>Также, возможно, имеет смысл свести любые сообщения к текстовым сообщениям с приложениями. Тогда аудиосообщение будет просто текстовым сообщением без текста с приложенным аудио, с пометкой, что аудиосообщение тут главное. Тут надо взвесить плюсы и минусы. Плюс в упрощении структуры таблиц.
Не согласен. Аудиосообщение по сути это аудиозапись, и в прикрепление тоже могут прикладывать аудиозапись, и поэтому будет конфликт, что есть голосовое сообщение, а что аудиофайл. Для этого нужно будет наследовать отдельный тип прикрепления, что является тем же самым что и унаследовать от сообщений. Я считаю, что голосовые сообщения должны быть отдельным типом сообщения, а не прикрепления.
>Наконец, тут надо быть готовым к денормализации или кешированию. Допустим, мы хотим показывать пользователю число непрочитанных сообщений в диалоге. Делать это через подсчет сообщений без признака прочтения неэффективно. Эффективнее иметь готовую циферку. Или, допустим, мы хотим вывести верхние 50 диалогов. Эффективнее одним простым запросом получить список диалогов, циферки непрочтенных сообщений, чем выбирать эти данные через джойны и группировки.
Да, я думаю, что можно хранить количество непрочитанных сообщений и дату последнего сообщения прямо в таблице диалога.
>>80460
>> Я правильно понимаю, что в Питоне объект, это instance класса, как в PHP, а не как в JS? Для каких ключевых типов данных вы имеете ввиду?
>
>Да, как в PHP. Я имел в виду, что если у тебя в коде есть какой-то массив, который много где используется, передается и возвращается разными функциями, то правильнее будет сделать его объектом. Но, как я понял, там был просто тестовый код, и тогда это не важно.
Этот массив был бы объектом с одним свойством этого самого массива?
>У тебя как-то странно сделано, что параметры сохраняются в $this->get. Это значит, что если ты с помощью функции linkMaker() создашь 2 анонимных функции, то одна будет влиять на другую (параметры, переданные в одну, отобразятся в другой). Это неправильно.
>$this->get
Так ведь там весь массив гет из реквеста, а не просто место для сохранения. Параметры не столько сохраняются, сколько апдейтятся - меняется только один, за который отвечает ссылка, и она пересобирается с новым значением.
Ну и разные функции нет смысла создавать - всегда один гет-массив приходит в запросе и с ним работаем.
Вообще, задумка была такой, что замыкание передаётся в шаблон под каким-то именем, где просто вызывается как на пикрелейтед. Меняются только запрошенные параметры, остальные не остаются как были. Ну и пустой писанины чтоб поменьше.
Я тут не подумал сразу и вкинул эту функцию в тред, без комментов, без пояснений нормальных. Потом только дошло, что она не оформлена никак у меня.
>если мы меняем сортировку, то надо сбрасывать пагинацию
Зачем? Количество записей ведь не изменилось. Я вот со своей этой функцией поигрался - вполне юзабельно вроде. Страницы жмёшь - меняет страницы, сортировку меняешь - сортировка только и меняется, что нажали - то и выводит.
null
ConstraintViolation {#1123
-message: "This value should not be blank."
-messageTemplate: "This value should not be blank."
-parameters: []
-plural: null
-root: Form {#594 }
-propertyPath: "data.password"
-invalidValue: null
-constraint: NotBlank {#905 …}
-code: "c1051bb4-d103-4f74-8988-acbcafc7fdc3"
-cause: null
}
помогите пж нубасу)
Во-первых, иди в новый тред, этот утонул, >>1380485 (OP) Вот-вторых, у тебя же в ограничении стоит NotBlank, то есть поле обязательно к заполнению.
>>> Я правильно понимаю, что в Питоне объект, это instance класса, как в PHP, а не как в JS? Для каких ключевых типов данных вы имеете ввиду?
>>
>>Да, как в PHP. Я имел в виду, что если у тебя в коде есть какой-то массив, который много где используется, передается и возвращается разными функциями, то правильнее будет сделать его объектом. Но, как я понял, там был просто тестовый код, и тогда это не важно.
>Этот массив был бы объектом с одним свойством этого самого массива?
Или я подумал, что это может быть объект со свойствами которые содержатся в этом массиве, и сделать этому объекту метод toArray().
>Есть очереди задач для этого.
Ты имеешь в виду какой-нибудь rabbitmq? Из пушки по воробьям же. Мне нужно просто отправить юзеру view сразу, до того как долгий таск завершится. Я до этого писал всякую шляпу на fat free framework и там есть функция abort, которая делает нужное
https://fatfreeframework.com/3.6/base#abort
Я вполне способен залезть в исходники и посмотреть, что она там делает и перенести это в симфони, но ведь скорее всего это уже там есть и мой велосипед всяко будет хуже.
'/s|^. - это либо символ начала строки либо ограничивающий пробел.
\.\S - условие замены
.\s|.*\. - ограничение по пробелу или концу строк
Я хочу что бы сделав дамп 0го массива вылезли все ошибки. Но почему - то даже правильные последовательности тоже выдаются. Где я перемудрил? По возможности намекните т к хочу допереть сам
>Как можно понятней назвать таблицы чтобы не вызвать путаницу?
Также, можно добавлять комментарии к таблицам. Разработчики часто их читают?
https://postgrespro.ru/docs/postgrespro/11/sql-comment
>>83892
>>Использование таблиц с похожими именами будет однозначно вызывать путаницу. Более того, я пока сам не понял, в чем разница между "сообщением" и "сообщениями" или "диалог" и "диалоги".
>Сообщение - это конкретная запись сообщения имеющая все данные о нём, от содержания до автора, id, даты публикации и т.д., а сообщения - это запись какие сообщения принадлежат какому пользователю и/или диалогу. Т.е. "сообщения" хранит ссылки для пользователя на id конкретного сообщения.
>
>Как можно понятней назвать таблицы чтобы не вызвать путаницу?
Можно вместо создания бесконечного количества ссылок создать поле с типом ARRAY и помещать в него все ссылки (UUID) на необходимые данные.
https://postgrespro.ru/docs/postgresql/11/arrays
https://postgrespro.ru/docs/postgresql/11/functions-array
Соответственно, все операции будет выполнятся легко и ловко.
Прим.
Message:
-uuid
...
Dialog:
-uuid
-private BOOLEAN
-participants UUID[] //массив из uuid получателей
Participant:
-uuid
-user
-messages UUID[] //массив из uuid сообщений
И все операции выполняются очень легко:
// Пользователь удаляет свою копию сообщения
UPDATE Partcipant SET messages = array_remove(messages, uuid_of_message) WHERE uuid = uuid_of_participant;
// Проверить существует ли приватный диалог,
// не смотря на то что один из пользователей когда-то покинул диалог
SELECT * FROM Dialog WHERE private = true AND (uuid_of_alice = ANY(participants) OR uuid_of_bob = ANY(participants));
>>83892
>>Использование таблиц с похожими именами будет однозначно вызывать путаницу. Более того, я пока сам не понял, в чем разница между "сообщением" и "сообщениями" или "диалог" и "диалоги".
>Сообщение - это конкретная запись сообщения имеющая все данные о нём, от содержания до автора, id, даты публикации и т.д., а сообщения - это запись какие сообщения принадлежат какому пользователю и/или диалогу. Т.е. "сообщения" хранит ссылки для пользователя на id конкретного сообщения.
>
>Как можно понятней назвать таблицы чтобы не вызвать путаницу?
Можно вместо создания бесконечного количества ссылок создать поле с типом ARRAY и помещать в него все ссылки (UUID) на необходимые данные.
https://postgrespro.ru/docs/postgresql/11/arrays
https://postgrespro.ru/docs/postgresql/11/functions-array
Соответственно, все операции будет выполнятся легко и ловко.
Прим.
Message:
-uuid
...
Dialog:
-uuid
-private BOOLEAN
-participants UUID[] //массив из uuid получателей
Participant:
-uuid
-user
-messages UUID[] //массив из uuid сообщений
И все операции выполняются очень легко:
// Пользователь удаляет свою копию сообщения
UPDATE Partcipant SET messages = array_remove(messages, uuid_of_message) WHERE uuid = uuid_of_participant;
// Проверить существует ли приватный диалог,
// не смотря на то что один из пользователей когда-то покинул диалог
SELECT * FROM Dialog WHERE private = true AND (uuid_of_alice = ANY(participants) OR uuid_of_bob = ANY(participants));
public function do() {
//...
this->calculate($someVar);
}
private function calculate($someVar) {
//..
$this->supaVar = $someVar 42;
}
ИЛИ
public function do() {
//...
$this->supaVar = this->calculate($someVar);
}
private function calculate($someVar) {
//..
return $someVar 42;
}
Говорю на своем опыте. я перекатился в израиль и тут нет вакансий. нода в 3-4 раза востребованей. самые топовые языки тут это с#, питон и джава
Вы видите копию треда, сохраненную 27 апреля 2019 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.