Вы видите копию треда, сохраненную 15 января 2020 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.
Это не чат! Пожалуйста не флудите, а старайтесь постить только вопросы, решения и ответы. Сколько лет вы не можете найти работу никому не интересно. Высказывайтесь одним большим постом а не цепочкой мелких
Это тред для начинающих. Не написал за свою жизнь ни одной программы? Ты наш человек.
Устанавливать пока что ничего не требуется, разве что редактор кода вроде Sublime Text 3, Notepad++, Netbeans PHP или PhpStorm (с ним будет удобнее).
Предыдущий тред был тут: >>715010 (OP)
Что самое главное для программиста? Умение аккуратно оформлять код (читай второй пост прежде чем писать код).
Почему PHP? Потому что фейсбук и википедия на нем написаны, и вакансий море, и учить легко.
Правила: ведем себя воспитанно, помогаем новичкам, постим ссылки на решения задачек, ОП их проверяет и дает советы и замечания. ОП заходит редко, где-то раз в 2-3 дня, у него мало времени, не жди его, решай задачки дальше. ОП отвечает на все вопросы по его задачкам и учебнику, а вот насчет каких-то других вещей - только если останется время. Но в треде немало анонимных экспертов разного уровня, так что вряд ли вопрос останется без ответа.
У нас есть уроки по основам PHP, они собраны и выложены по адресу http://archive-ipq-co.narod.ru/ Это учебник для изучающих с нуля, то есть если ты вообще ничего не знаешь, то надо начать с него. Он простой и понятный (по крайней мере в начале). Там есть задачи, их надо решать обязательно (чтобы стать программистом, надо писать код — иначе никак). Пости ссылки на решения в тред, мы их проверим, напишем замечания и дадим советы по улучшению.
Если не знаешь как решать, запости код, напиши в каком месте остановился и попроси подсказку.
Ты прошел весь учебник? Молодец, но это были лишь основы языка 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/Yii2: https://gist.github.com/codedokode/8733007
- После нее можно изучать автоматизированное тестирование
- Если ты все решил, переходи к Symfony 2/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
Решения задач лучше показать мне, особенно на ООП,так как сам ты вряд ли увидишь все ошибки. Пости свой код на гитхаб и вкидывай ссылку в тред по мере решения. Я прокомментирую и укажу на ошибки.
Также, у нас есть задачи которые позволят тебе изучить или подтянуть до нормального уровня знания 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://gist.github.com/codedokode/10539213
Что почитать
- Мануал по 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
Нужен ли ООП, фреймворки, MVC, git, composer? — Да, однозначно. Посмотри любую вакансию.
Сайт опять упал!!!!! — Не паникуй, а открой http://rghost.ru/6bfCY9lfl и получи личную немного устаревшую оффлайновую копию сайта (можно читать хоть на андроиде без интернета)
Оформляй код аккуратно!!! — например пропусти через phpformatter.com . Также, если ты пользуешься IDE вроде PhpStorm, Netbeans, Eclipse, то в них эта опция встроена, подробнее: https://gist.github.com/codedokode/8759492
ОП, сделай за меня мою работу или домашнее задание? — Это конечно, хорошая идея, но нет.
Подскажи сайты для поиска работы, я не умею гуглить? — hh.ru, geekjob.ru, moikrug.ru (склеен с brainstorage.me), fl.ru, upwork.com (бывший одеск). Имей в виду, что кроме фриланса есть еще постоянная удаленная работа (remote job) когда тебе не надо тратить время на поиск заказов и переговоры с неадекватными заказчиками.
Если тебе лень выравнивать код руками, закачай его на 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/php-fig/fig-standards/blob/master/accepted/ru/PSR-1-basic-coding-standard.md
PSR-2: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-2-coding-style-guide.md
------------------
Итак, ты зашел в тред и решил помочь какому-то анону, дав ему совет или подсказку. Спасибо! Но прочти сначала эти напоминания, чтобы твоя помощь действительно была полезной.
Давай удочку, а не рыбу
Лучше не давать готовое решение проблемы, а рассказать как его искать. Может дать ключевые слова для гугла или ссылку. Но помогай, а не пытайся показать превосходство. Если даешь ссылки на нерусскоязычные статьи, упомяни об этом.
Будь доброжелателен
Не годится: «Ты мануал хоть раз в жизни открывал, обезьяна?»
Не годится: «В гугле забанили?»
Не годится: «Твой код плохой»
Хорошо: «Вот, как можно улучшить этот код: ...»
Хорошо: «Ты неправильно используешь функцию abc(). Вот ее описание: ссылка, и как видишь ей надо передать строку, а не массив»
Не придирайся к знанию английского языка.
Объясняй
Не очень хорошо: «сделай как в этом коде»
Хорошо: «если ты вставляешь текст от пользователя в SQL запрос, то получается SQl-инъекция, которая позволяет взломать твой сервер (ссылки). Чтобы этого избежать, надо вставлять данные с помощью плейсхолдеров (ссылки)»
Хорошо: «Помни, что код пишется для людей. Если писать такие большие функции, то в них становится трудно разобраться...»
Не проповедуй
Мы учим использованию самых распространненных подходов, стандартов, библиотеки фреймворков. Если ты не любишь ООП, пробелы в коде, jQuery, сам PHP, то рассказать об этом стоит в каком-нибудь другом треде.
Не придирайся к знанию английского языка, анон пишет как умеет.
Ах да. Если тебе кажется, что что-то в учебнике или задачах можно сделать лучше — пиши, обратная связь всегда очень полезна.
Если тебе лень выравнивать код руками, закачай его на 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/php-fig/fig-standards/blob/master/accepted/ru/PSR-1-basic-coding-standard.md
PSR-2: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-2-coding-style-guide.md
------------------
Итак, ты зашел в тред и решил помочь какому-то анону, дав ему совет или подсказку. Спасибо! Но прочти сначала эти напоминания, чтобы твоя помощь действительно была полезной.
Давай удочку, а не рыбу
Лучше не давать готовое решение проблемы, а рассказать как его искать. Может дать ключевые слова для гугла или ссылку. Но помогай, а не пытайся показать превосходство. Если даешь ссылки на нерусскоязычные статьи, упомяни об этом.
Будь доброжелателен
Не годится: «Ты мануал хоть раз в жизни открывал, обезьяна?»
Не годится: «В гугле забанили?»
Не годится: «Твой код плохой»
Хорошо: «Вот, как можно улучшить этот код: ...»
Хорошо: «Ты неправильно используешь функцию abc(). Вот ее описание: ссылка, и как видишь ей надо передать строку, а не массив»
Не придирайся к знанию английского языка.
Объясняй
Не очень хорошо: «сделай как в этом коде»
Хорошо: «если ты вставляешь текст от пользователя в SQL запрос, то получается SQl-инъекция, которая позволяет взломать твой сервер (ссылки). Чтобы этого избежать, надо вставлять данные с помощью плейсхолдеров (ссылки)»
Хорошо: «Помни, что код пишется для людей. Если писать такие большие функции, то в них становится трудно разобраться...»
Не проповедуй
Мы учим использованию самых распространненных подходов, стандартов, библиотеки фреймворков. Если ты не любишь ООП, пробелы в коде, jQuery, сам PHP, то рассказать об этом стоит в каком-нибудь другом треде.
Не придирайся к знанию английского языка, анон пишет как умеет.
Ах да. Если тебе кажется, что что-то в учебнике или задачах можно сделать лучше — пиши, обратная связь всегда очень полезна.
Так ведь $department это и есть объекты. Сам массив клонируется вместе с компанией, ведь это не объект, он копируется сразу.
>I ran into the same problem of an array of objects inside of an object
public function __clone() {
foreach ($this->varName as &$a) {
foreach ($a as &$b) {
$b = clone $b;
}
}
}
Вот ещё нашел в мануале, типа для клонирования массивов объектов. Но тоже не работает.
Пишу сейчас простенькую систему парольной аутентификации.
Возник небольшой вопрос по сессиям пользователей.
Когда пользователь вводит в форму правильную пару логин\пароль, ему выдается кука с SESSID (просто набор рандомных символов).
Этот SESSID так же прописывается в БД.
Потом при посещении личного кабинета эта SESSID кука сравнивается с SESSID в БД. Если данные совпадают то разрешаем пользоваться личным кабинетом.
Собственно сам вопрос:
Что если потенциальные хацкеры напишут программу, которая будет пытаться подобрать куку с SESSID.
Спасибо
https://gist.github.com/codedokode/9576319
>>29508
залей им какашку на сайт, прекинь как у них бабахнет)))0
По всей видимости, Вы, к сожалению, не поняли сути моего вопроса.
В статье, которую Вы привели, говорится о хранении паролей в БД.
Для хранения я использую функцию:
password_hash($_REQUEST["pass"], PASSWORD_DEFAULT);
Меня интересует как проверять перебирают ли хацкеры кукисы в которых хранится SESSID.
Привязывать в БД sessid к ip пользователя? Но если ip у пользователя сменится то под этой кукой ему уже не войти.
Делать ограничение на количество попыток с одного ip войти под разными кукисами?
>Но если ip у пользователя сменится то под этой кукой ему уже не войти.
Перечислите, пожалуйста, случаи, когда это может произойти вне зависимости от волеизъявления пользователя.
Например, он переходит из кафе в кафе, пользуясь бесплатным Wi-Fi, либо едет на поезде (летит на самолёте и т.п.), переключаясь между доступными мобильными сетями.
На этом моя фантазия, увы, иссякла.
Да, у пользователей редко меняется ip.
Но задача стоит в том чтобы пользователь мог войти при смене ip.
Возможно я ошибаюсь, но так в основном на всех сайтах и происходит
Тогда в зависимости от частоты смены IP в промежуток времени, наверное. Если часто меняется - непорядок.
В одном из тредов вроде бы ОП советовал проверять, залогинен ли юзер в социальных сетях. То есть если он залогинен в одной сети, но IP меняется - плохо. Если ни в какой не залогинен и IP меняется - того хуже.
Соцсети, почта, поисковики, вот это всё.
Живой человек где-то да залогинен. Купи пасскод, азаза!
Неплохое решение от ОПа (вроде бы), только кучу всего надо изучать, наверное, я пока теоретик.
Ну а если зарегистрирован на самом сайте, то и шут бы со всем этим, если не нарушает какие-либо правила.
>>29535
У меня такой.
Так он не меняется в обычной ситуации, когда не отключаешь роутер. А если часто включаешь-выключаешь и находишься на сайте - тоже не всё чисто.
Оговорюсь: тогда речь шла о простом доступе к сайту, без регистрации на нём, говорили об анонимных лайках, как сейчас помнится.
На данный момент, я уже решил больше половины задач по сосновам: все до дополнительных задач, одну дополнительную (про банкомат), 2 из 4 про ООП.
И в прошлом треде за 27ое число три задачи без ответов, просил напомнить:
Числа прописью https://2ch.hk/pr/res/715010.html#728416 (М)
Объекты в PHP, часть 2 https://2ch.hk/pr/res/715010.html#728419 (М)
Задача про банкомат https://2ch.hk/pr/res/715010.html#728599 (М)
Верно, массив копируется при клонировании. Но в копии содержатся ссылки на старые объекты. Если тебе будет так проще понять, представь что у нас все объекты пронумерованы и переменные хранят просто их номера.
Когда ты делаешь
$x = clone $x;
Ты делаешь клон объекта и помещаешь его идентификатор в $x, но в массиве-то остаются старые идентифиаторы.
> foreach ($this->varName as &$a) {
А ты изучил мануал по ссылкам? Изучи сначала тогда этот раздел от кроки до корки, а не копируй бездумно код.
>>29503
> Что если потенциальные хацкеры напишут программу, которая будет пытаться подобрать куку с SESSID.
Надо сделать код достаточно длинным, чтобы было очень много вариантов кода и перебрать их было нереально. Например если брать 32 цифры или латинских буквы (с учетом регистра) то получается 62 в 32 степени вариантов, и это очень много: https://www.google.ru/search?q=62^32=&newwindow=1&gbv=1&sei=lHciV7CVO-md6ATQnreIAw
> Этот SESSID так же прописывается в БД.
Ты учел что можно залогиниться с нескольких браузеров на разных компьютерах?
Вообще, можно не генерировать куку, а взять например хеш от пароля с солью - он и так есть в Бд, и при смене пароля старые авторизации перестанут работать (это кстати важно, при смене пароля надо разлогинивать все остальные сессии).
>>29508
Сообщить разработчикам сайта
>>29516
Он хотел тебе сказать про число вариантов для перебора я думаю
>>29524
На мобильных устройствах при пропаже сигнала или переходе от сот к соте теоретически может происхоlить разрыв связи и выдача нового IP. да и у некоторых домашних провайдеров (если они используют какой-нибудь PPP) это бывает.
>>29613
Дойди в моем учебнике до ООП и можешь параллельно начинать читать.
Верно, массив копируется при клонировании. Но в копии содержатся ссылки на старые объекты. Если тебе будет так проще понять, представь что у нас все объекты пронумерованы и переменные хранят просто их номера.
Когда ты делаешь
$x = clone $x;
Ты делаешь клон объекта и помещаешь его идентификатор в $x, но в массиве-то остаются старые идентифиаторы.
> foreach ($this->varName as &$a) {
А ты изучил мануал по ссылкам? Изучи сначала тогда этот раздел от кроки до корки, а не копируй бездумно код.
>>29503
> Что если потенциальные хацкеры напишут программу, которая будет пытаться подобрать куку с SESSID.
Надо сделать код достаточно длинным, чтобы было очень много вариантов кода и перебрать их было нереально. Например если брать 32 цифры или латинских буквы (с учетом регистра) то получается 62 в 32 степени вариантов, и это очень много: https://www.google.ru/search?q=62^32=&newwindow=1&gbv=1&sei=lHciV7CVO-md6ATQnreIAw
> Этот SESSID так же прописывается в БД.
Ты учел что можно залогиниться с нескольких браузеров на разных компьютерах?
Вообще, можно не генерировать куку, а взять например хеш от пароля с солью - он и так есть в Бд, и при смене пароля старые авторизации перестанут работать (это кстати важно, при смене пароля надо разлогинивать все остальные сессии).
>>29508
Сообщить разработчикам сайта
>>29516
Он хотел тебе сказать про число вариантов для перебора я думаю
>>29524
На мобильных устройствах при пропаже сигнала или переходе от сот к соте теоретически может происхоlить разрыв связи и выдача нового IP. да и у некоторых домашних провайдеров (если они используют какой-нибудь PPP) это бывает.
>>29613
Дойди в моем учебнике до ООП и можешь параллельно начинать читать.
https://m.habrahabr.ru/post/282674/
https://m.habrahabr.ru/post/282644/
#
LoadModule php5_module "c:/php/php5apache2.dll"
AddHandler application/x-httpd-php .php
# конфигурирование пути к php.ini
PHPIniDir "C:/php"
Просто скопировать в любое место?
>$validate = [
>'validateName' => 'getName',
>....
>];
А разве ключом не должно быть название поля? Как тогда обозначить ключ ошибки?
>>729418
>
>В качестве значения по умолчанию нельзя использовать выражение. И по моему ты плохо понял что написал. Когда ты пишешь
>
>$this->x = func();
>
>ты не сохраняешь функцию в поле. ты сохраняешь то,что она вернула - число например.
Нет нет, я понял что здесь подвох. Я просто не знал про функцию call_user_func.
Задача разработка проекта на php, постоянно этим не занимаюсь поэтому особо в дебри лезть каких-то крутых иде не хочется. Что-то бесплатное чем можно комфортно выполнить задачу, пример из мира винды xampp + notepad++ например.
foreach ($this->departaments as $departament) {
$quantity = $departament->countEmployees();
$result[$departament->name] = $quantity;
}
Советовал бы ставить всё по отдельности.
Не повторяй моих ошибок.
Apache, PHP, MySQL. И дальше IDE, которые советует анон.
А в чем могут быть проблемы? Я пару лет назад делал задачу на винде и хамп отлично справился.
1. Если проект потом переносить на сервер, то могут быть проблемы из-за настроек.
2. РНР там какой-нибудь 5 версии и не самой последней.
3. Perl Там на хрен не нужен тебе.
4. MariaDB тоже на хрен не нужна, Мускул таки лучше и ТРУЪ.
А эти отдельные пакеты я потом смогу удалить из системы? Задумался, может просто поставить это на виртуалку все...
Ребзи, с помощью чего на PHP можно замутить систему лобби ? Хватит ли одного Аякса ?
На пхп такое делать будет неудобно. Я бы смотрел в сторону модного жс-фреймворка на фронт, и ноду\пайтон на бэкэнд.
Я чето слабо себе представляю как вообще такое реализовывать, даже с аяксом. Вот люди, сидящие на странице, отправляют аяксом пост запросы о своём присутствии в лобби, но что сервер должен с ними делать? Не в базу же заносить? Как их передать другим участникам лобби?
Так а может какие-то сокеты посоветуешь ? Или на чистом аяксе можно всё сделать ?
Ну пускай они в лобби сидят, как на отдельной генерируемой страничке. Или не прокатит ?
>Ну пускай они в лобби сидят, как на отдельной генерируемой страничке
Wat? Во первых это как, а во вторых вопрос о передаче юзерам инфы друг о друге всё еще в силе.
На вебсокетах такие лобби делают обычно. Проблема в том что на пхп есть один вариант, это reactphp. И не важно нормальный он или нет, тебе все равно нужно будет его использовать, если хочешь реализовать такое на пхп. Я думаю проще было бы взять ноду и сделать все на ней.
Ну вот зашли они по факту на отдельную страницу, созданую для них двух, и Аякс там манипулирует ДОМ"ом и в реальном времени меняет то что нужно. Или так нельзя ?
Блеа. но мне не нужна нода, мне нужен php ...
дело в том, что на PHP уже многое сделано. Нужно уже добить.
Отдельная страница или нет, и что там аякс с домом делает это второстепенные вопросы. Главная проблема передать друг другу какую-то инфу, будь то сообщение в чате, или профиль.
Погугли что такое веб-сокеты и как они работают, потом возьми библиотеку для PHP которая реализует эти вебсокеты. Фронт на жсе, бэкэнд на пхп, и не нужно никаких аяксов, ноды и пайтона.
Для примера вот Ratchet http://socketo.me/ вроде как вполне норм.
>>30684
А ты подумал как в таком случае сделать обновление в реальном времени? Отправлять на сервер запросы каждые n секунд? В таком случае не слишком ли большая нагрузка будет для простого лобби с чатом?
Окей. Спасибо. Доверюсь тебе.
Ой, ну объясни нормально, анон, что ты сразу.
Fuck you.
Поясните по хардроку, как вообще это функционирует на сайтах.
1. Вот у меня файл .php со скриптом, там ООП, классы, вот это всё, ему нужны экземпляры класса создавать - как-то надо подключать их, ставить на место созданий экземпляра include с файлом, уже содержащим созданные классы? Я правильно понимаю логику? Или это всё возможно через БД - оттуда брать инфу и вставлять в скрипт?
2. Соответстветственно, ещё какой-нибудь скрипт отвечает за заполнение этого скрипта с экземплярами класса или БД с ними. Например, это студенты - скрипт собирает с регистрацией набивает БД студентами как экземплярами класса Student, а ты потом их вставляешь хитроумным способом в свой первый скрипт с ООП и вертишь там по-всякому.
Я верно это представляю себе?
http://ideone.com/asjGPa
ООП - это такой стиль программирования, который придумали чтоб совсем не ахуеть с мильенами строк кода в разрастающихся приложениях. Оно типа упрощает все, структурирует.
Объекты эта такая штука, которая позволяет создавать тебе что-то вроде собственных типов данных, такие, как например уже знакомые тебе массивы. Очень гибкая и удобная.
Когда ты описываешь конструкцию class MyClass{}; ты типа вводишь новый тип данных в окружение и после этого можешь создавать экземпляры этого типа инструкцией new MyClass;. Я особо не шарю, но вроде и обычные массивы тоже можно создавать в таком стиле - $array = new ArrayObject;
Касательно где создавать экземпляры - там же где и массивы, если бы ты использовал их вместо специально созданного тобой класса студента.
>ставить на место созданий экземпляра include с файлом
Нет, юзай композер.
>Или это всё возможно через БД
БД вообще тут не при чем.
>ещё какой-нибудь скрипт отвечает за заполнение этого скрипта с экземплярами класса или БД с ними
Экземпляры в коде создаются ручками, вместо инклюдов композер автозагрузку делает автоматически.
>Я верно это представляю себе?
Данные из БД тащит класс работы с ней, потом в коде создается нужный экземпляр класса с использованием полученных данных, данные просто передаются в конструктор.
Классы обычно делятся на 2 вида (это не официальные названия, а придуманные мной) - модели и сервисы. Модель - это класс который представляет какой-то реальный объект и хранит информацию о нем (например класс Студент хранит информацию о зарегистрированном студенте). Может быть много экземпляров моделей - 10 студентов это 10 объектов. А сервис - это класс который обычно существует в 1 экземпляре и делает какие-то действия, в том числе над моделями. Ну например Валидатор в котром есть метод валидации. Он берет на вход модель студента, проверяет ее и выдает на выходе список ошибок или признак что наоборот, вся информация соответствует заданному формату. Или TableDataGateway - это сервис, который умеет сохранять модели студентов в базу данных и доставать из нее обратно.
В MVC как ты наверно знаешь, приложение делят на 3 части - модель (не та модель что описана выше, а модель как 1/3 часть приложения), контроллер и вид. Ну вот, собственно в коде обычно ты в начале создаешь всякие нужные сервисы, контроллер и запускаешь его. А контроллер например просит у TDG взять из базы 10 студентов, и передает этих студентов в вид, который отображает их на HTML странице. Как-то так.
Читай замечания к задаче про студентов, все там подробно расписано. И конечно читай урок про ООП очень внимтельно и решай задачи там.
Разумеется разделение на модели и сервисы не очень четкое. Вот например что такое контроллер? С одной стороны это не модель так как он не соответствует никаким реальным объектам и не хранит информацию о них. С другой стороны это не совсем сервис, так как мы часто создаем новый контроллер на каждый новый запрос от пользователя. Хотя есть фреймворки который считают что контроллер это сервис. А некоторые считают что это такой "одноразовый сервис", который создается, обрабатывает 1 запрос пользователя и выбрасывается в корзину.
>это не совсем сервис, так как мы часто создаем новый контроллер на каждый новый запрос
Но ведь сервисы тоже создаются на каждый новый запрос
Ты подразумеаешь стндартную модель работы php когда скрипт обрабаывает запрос и завершается:
- создать все нужные сервисы
- проанализировать запрос
- сгенерировать ответ
- выйти
А вещи вроде взаимодействия с браузером по HTTP реализуют веб-сервер и ядро php.
Но PHP можно использовать и в другом сценарии:
- создать все нужные сервисы
- открыть порт и ждать входящий HTTP запрос
- бесконечно повторять {
-- принять запрос
-- проанализировать запрос
-- сгенерировать ответ
}
Эта модель имеет как преимущества так и недостатки:
+ инициализация делается 1 раз а не на каждый запрос
+ можно сохранять какие-то данные между запросами в памяти в переменных (напрмиер какие-то часто используемые данные можно загрузить в начале из БД в массив и дальше брать из него с огромной скоростью. Это хорошо работает только для read-only данных вроде всяких справочников которые не меняются в процессе работы)
+ соответственно не нужны кеши байткода вроде APC, opcache так как скрипты компилируются в момент запуска приложения а не при каждом запросе
- требуется большая внимательность. В стандартном подходе мы не боимся утечек памяти так как после заверщения скрипта она очищается, а тут может получиться такая ситацция что расход памяти будет расти, она закончится и наше приложение упадет
- опять же при какой-то фатальной ошибке падает все приложение а не обработчик одного запроса. Это обычно решают автоматическим перезапуском приложения при падении
В таком сценарии сервис может пережить много запросов, а например контроллер будет на каждый запрос создаваться новый (так как это проще чем очищать состояние). В других языках вроде явы именно такая модель используется и там сервисы долгоживущие.
Ты подразумеаешь стндартную модель работы php когда скрипт обрабаывает запрос и завершается:
- создать все нужные сервисы
- проанализировать запрос
- сгенерировать ответ
- выйти
А вещи вроде взаимодействия с браузером по HTTP реализуют веб-сервер и ядро php.
Но PHP можно использовать и в другом сценарии:
- создать все нужные сервисы
- открыть порт и ждать входящий HTTP запрос
- бесконечно повторять {
-- принять запрос
-- проанализировать запрос
-- сгенерировать ответ
}
Эта модель имеет как преимущества так и недостатки:
+ инициализация делается 1 раз а не на каждый запрос
+ можно сохранять какие-то данные между запросами в памяти в переменных (напрмиер какие-то часто используемые данные можно загрузить в начале из БД в массив и дальше брать из него с огромной скоростью. Это хорошо работает только для read-only данных вроде всяких справочников которые не меняются в процессе работы)
+ соответственно не нужны кеши байткода вроде APC, opcache так как скрипты компилируются в момент запуска приложения а не при каждом запросе
- требуется большая внимательность. В стандартном подходе мы не боимся утечек памяти так как после заверщения скрипта она очищается, а тут может получиться такая ситацция что расход памяти будет расти, она закончится и наше приложение упадет
- опять же при какой-то фатальной ошибке падает все приложение а не обработчик одного запроса. Это обычно решают автоматическим перезапуском приложения при падении
В таком сценарии сервис может пережить много запросов, а например контроллер будет на каждый запрос создаваться новый (так как это проще чем очищать состояние). В других языках вроде явы именно такая модель используется и там сервисы долгоживущие.
Модель - это не обязательно сущность. Только в небольших приложениях позволительно использовать ORM. Сразу видно, что ты новичок и никогда не работал с реальными проектами.
простой пример как получить учтечку памяти: допустим начинающий программист решил сделать для себя лог SQL запросов и складывает их в массив. Этот массив будет только расти, никогда не очищаясь, и потредление памяти тоже будет толоько расти, рано или поздно она закончится.
в случае умирающего подхода эта проблема решается сама собой, а вот в долгоживущем приложении ты еще замучаешся ее искать. В некоторых языках вроде Явы или C# (и даже в Хроме в инструментах разработчика) есть профайлеры памяти, позволяющие узнать кто сколько отъел, а вот в php вроде простого способа нет.
Я слышал что некоторые рубисты (там используется долгоживущее приложение) и разработчики на ноде не смогли найти причины утечек и решали свою проблему прибиванием и перезапуском приложения по расписанию. Ну то есть расписываются в своем же бессилии.
Если ты будешь делать логгер SQL запросов или чего угодно в массив - ограничивай его размер.
>> Я не вижу такого решения с помощью цикла, только прописывать всё в ручную
>Почему цикл не годится? ты можешь обраьиться к полю по имени в переменной, такой конструкцией:
>
>$fieldName = 'year';
>$this->{$fieldName} = 1990;
>
>И соответственно можно использовать цикл по массиву разрешенных имен полей:
>
>$fields = ['name' ,surname'];
>foreach ($fields as $field) {
>....
>$this->{$field} = ....;
>}
Цикл не годиться потому что в массиве могут придти любые данные.
>Могут быть какие-то поля (например статус модератора) которые не должны заполняться из пришедших данных.
Единственный способ с циклом которые я вижу, это использовать в методе массив с белом листом полей которых можно заполнить
function fillFromArray(array $data)
{
$allowed = ['name', 'surname', ...];
foreach ($allowed as $value) {
$this->$value = $data[$value];
}
}
И прокомментируй пожалуйста вот этот вопрос, мне действительно непонятно почему мы должны ограничиваться только массивом:
>А почему именно из массива? Вот например у меня раньше брались данные из $_POST, и я подумать не мог что они будут браться откуда-то еще. Если мы собрались расширять гибкость кода, то давайте расширять до конца. Например, мы можем получать данные и из объекта.
>> Я не вижу такого решения с помощью цикла, только прописывать всё в ручную
>Почему цикл не годится? ты можешь обраьиться к полю по имени в переменной, такой конструкцией:
>
>$fieldName = 'year';
>$this->{$fieldName} = 1990;
>
>И соответственно можно использовать цикл по массиву разрешенных имен полей:
>
>$fields = ['name' ,surname'];
>foreach ($fields as $field) {
>....
>$this->{$field} = ....;
>}
Цикл не годиться потому что в массиве могут придти любые данные.
>Могут быть какие-то поля (например статус модератора) которые не должны заполняться из пришедших данных.
Единственный способ с циклом которые я вижу, это использовать в методе массив с белом листом полей которых можно заполнить
function fillFromArray(array $data)
{
$allowed = ['name', 'surname', ...];
foreach ($allowed as $value) {
$this->$value = $data[$value];
}
}
И прокомментируй пожалуйста вот этот вопрос, мне действительно непонятно почему мы должны ограничиваться только массивом:
>А почему именно из массива? Вот например у меня раньше брались данные из $_POST, и я подумать не мог что они будут браться откуда-то еще. Если мы собрались расширять гибкость кода, то давайте расширять до конца. Например, мы можем получать данные и из объекта.
1) Модели можно объявить через PHP-классы, XML и YAML? Вторые два способа редко используются или как?
2) Где в модели указываются типы значений, внешние ключи и т.д.? Вижу в туторе комментарии над полями моделей, это оно что ли?
3) Миграции из коробки есть или надо модуль устанавливать?
> Модели можно объявить через PHP-классы, XML и YAML?
Ты странные термины используешь. Модели - это твои классы и ты их создаешь без всякой доктрины. Для доктрины ты просто добавляешь ним правила маппинга, то есть описываешь как именно ты хочешь их сохранять в БД. Это можно делать с помощью аннотаций или отдельных файлов, на мой взгляд аннотации удобнее так как когда ты например удаляешь поле, то сразу же и удаляешь аннотацию к нему. Хотя есть и те кто предпочитает файлы
> Где в модели указываются типы значений, внешние ключи и т.д.? Вижу в туторе комментарии над полями моделей, это оно что ли?
Это называется аннотации. Каждая аннотация представлена классом, и там даже поддерживаются неймспейсы то есть когда ты пишешь
use .... as ORM;
@ORM\Table(name=....)
То ты ссылаешься на класс Table из внутренностей доктрины: https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/Mapping/Table.php у которого как ты видишь есть свойство name.
Сам модуль который отвечает за чтение и анализ аннотаций, вынесен в отдельный проект, так что ты можешь создавать свои аннотации и использовать для каких-то своих целей:
https://github.com/doctrine/annotations
https://github.com/doctrine/annotationshttps://github.com/doctrine/annotations
> Миграции из коробки есть или надо модуль устанавливать?
есть https://github.com/doctrine/migrations но я им не пользовался. Попробуй, можешь рассказать нам потом о впечатлениях от исопльзования.
>>31041
> Единственный способ с циклом которые я вижу, это использовать в методе массив с белом листом полей которых можно заполнить
Да я это и имел в виду только еще проверяй что в массиве есть такое поле. И для верности можно еще проверять что оно есть в оьъекте на случай если программист переименовал поле а в белом списке забыл поменять.
>И прокомментируй пожалуйста вот этот вопрос, мне действительно непонятно почему мы должны ограничиваться только массивом:
>>А почему именно из массива? Вот например у меня раньше брались данные из $_POST, и я подумать не мог что они будут браться откуда-то еще. Если мы собрались расширять гибкость кода, то давайте расширять до конца. Например, мы можем получать данные и из объекта.
Во-первых, явное лучше неявного. Лучше когда мы явно передаем данные чем когда функция сама их берет непонятно откуда. Мне вообще не нравятся эти суперглобальные переменные в пхп там как они видны везде, и мы не можем ограничить доступ к ним только теми кому он нужен.
Во-вторых, про объект. Массив это набор произвольных элементов который может иметь любые ключи. Но у объектов поля жестко заданы и даже если они называются одинаково это не значит что они связаны. Значит мы можем копировать поля не из любых объектов, а только имеющих отношение студенту - например других студентов, наследников, предков или классов реализующих тот же интерфейс. У тебя в программе есть такие классы? Ты впрочем можешь сделать копирование данных из другого студента. Разумеется отдельной функцией так как желательно определить тип аргументов функции и одна функция не может принимать в данной ситуаци и на вход разные типы данных.
В-третьих, я не вижу выгоды. Массив более универсальная штука для передачи данных и ты можешь просто экспортировать данные из объекта в массив и уже из массива заполнить поля. То есть добавлять другие типы данных мне кажется избыточно. Ты можешь сделать это, если тебе надо, но ведь внутри скорее всего будет преобразование в массив и далее заполнение полей из масива.
То есть я не вижу смысла, но если ты хочешь, можешь сделать функцию берущую данные из другого источника кроме массива.
> Модели можно объявить через PHP-классы, XML и YAML?
Ты странные термины используешь. Модели - это твои классы и ты их создаешь без всякой доктрины. Для доктрины ты просто добавляешь ним правила маппинга, то есть описываешь как именно ты хочешь их сохранять в БД. Это можно делать с помощью аннотаций или отдельных файлов, на мой взгляд аннотации удобнее так как когда ты например удаляешь поле, то сразу же и удаляешь аннотацию к нему. Хотя есть и те кто предпочитает файлы
> Где в модели указываются типы значений, внешние ключи и т.д.? Вижу в туторе комментарии над полями моделей, это оно что ли?
Это называется аннотации. Каждая аннотация представлена классом, и там даже поддерживаются неймспейсы то есть когда ты пишешь
use .... as ORM;
@ORM\Table(name=....)
То ты ссылаешься на класс Table из внутренностей доктрины: https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/Mapping/Table.php у которого как ты видишь есть свойство name.
Сам модуль который отвечает за чтение и анализ аннотаций, вынесен в отдельный проект, так что ты можешь создавать свои аннотации и использовать для каких-то своих целей:
https://github.com/doctrine/annotations
https://github.com/doctrine/annotationshttps://github.com/doctrine/annotations
> Миграции из коробки есть или надо модуль устанавливать?
есть https://github.com/doctrine/migrations но я им не пользовался. Попробуй, можешь рассказать нам потом о впечатлениях от исопльзования.
>>31041
> Единственный способ с циклом которые я вижу, это использовать в методе массив с белом листом полей которых можно заполнить
Да я это и имел в виду только еще проверяй что в массиве есть такое поле. И для верности можно еще проверять что оно есть в оьъекте на случай если программист переименовал поле а в белом списке забыл поменять.
>И прокомментируй пожалуйста вот этот вопрос, мне действительно непонятно почему мы должны ограничиваться только массивом:
>>А почему именно из массива? Вот например у меня раньше брались данные из $_POST, и я подумать не мог что они будут браться откуда-то еще. Если мы собрались расширять гибкость кода, то давайте расширять до конца. Например, мы можем получать данные и из объекта.
Во-первых, явное лучше неявного. Лучше когда мы явно передаем данные чем когда функция сама их берет непонятно откуда. Мне вообще не нравятся эти суперглобальные переменные в пхп там как они видны везде, и мы не можем ограничить доступ к ним только теми кому он нужен.
Во-вторых, про объект. Массив это набор произвольных элементов который может иметь любые ключи. Но у объектов поля жестко заданы и даже если они называются одинаково это не значит что они связаны. Значит мы можем копировать поля не из любых объектов, а только имеющих отношение студенту - например других студентов, наследников, предков или классов реализующих тот же интерфейс. У тебя в программе есть такие классы? Ты впрочем можешь сделать копирование данных из другого студента. Разумеется отдельной функцией так как желательно определить тип аргументов функции и одна функция не может принимать в данной ситуаци и на вход разные типы данных.
В-третьих, я не вижу выгоды. Массив более универсальная штука для передачи данных и ты можешь просто экспортировать данные из объекта в массив и уже из массива заполнить поля. То есть добавлять другие типы данных мне кажется избыточно. Ты можешь сделать это, если тебе надо, но ведь внутри скорее всего будет преобразование в массив и далее заполнение полей из масива.
То есть я не вижу смысла, но если ты хочешь, можешь сделать функцию берущую данные из другого источника кроме массива.
>>30967
Это я в общих чертах понял. Но просто мы до ООП делаем так, что всё находится в одном скрипте, в одном файле. Мне интересен сейчас сам механизм - правильно ли я его себе представляю. Что надо подключать разные файлы к скрипту, создавать скрипты, которые отвечают за заполнение этих файлов. Либо не файлы, а строки в БД - оттуда как-то вставлять.
Я пока лишь разбираюсь в ООП - просто на будущее.
>>30969
>Экземпляры в коде создаются ручками, вместо инклюдов композер автозагрузку делает автоматически.
А как быть, когда уже созданы экземпляры? Они же где-то хранятся? В файлах или в БД лучше?
Композер пока и не смотрел - мне еще рановато.
>Данные из БД тащит класс работы с ней, потом в коде создается нужный экземпляр класса с использованием полученных данных, данные просто передаются в конструктор.
Вот это ответ на мои вопросы, спасибо, это как раз интересовало больше всего.
>>30973
Спасибо, неплохо расписано!
>контроллер например просит у TDG взять из базы 10 студентов, и передает этих студентов в вид, который отображает их на HTML странице. Как-то так.
Вот то, что хотел узнать. Отлично, я придвигаюсь к сути понемногу.
>>30978
>мы часто создаем новый контроллер на каждый новый запрос от пользователя.
Хм, сложновато для меня пока.
Но что-то уже формируется, займусь-ка ООП.
Тут и происходит. Просто новичков сейчас мало. Наверно куличи пекут. Кидай вопросы и код не забудь.
>Если ты будешь делать логгер SQL запросов или чего угодно в массив - ограничивай его размер.
А если нужны прежние данные для чего либо (для статистики, например)?
Можно ли как-то настроить, чтобы раз в какой-либо промежуток времени этот массив становился равным массиву $period? Каждый раз разный, конечно, название - по первому моменту и по последнему моменту. Например, $mon2may2016_sun8may2016, что-то такое.
Так, стало интересно.
По поводу Доктрины:
Миграции автоматически анализируют аннотации полей и создают поля нужногл типа и связью в БД? Вообще странный подход в комментариях указывать такие важные вещи. В Джанго ОРМ и Алхимии, просто как параметр в конструкторе каждого поля передаётся.
> А как быть, когда уже созданы экземпляры? Они же где-то хранятся? В файлах или в БД лучше?
Экземпляр класса (объект) нельзя хранить в бд или файле, он хранится в памяти, единственные способы его создать - new или clone, нельзя сохранить произвольный объект в файл и восстановить (есть сериализация но она не работает с любыми классами).
Да и подумай, если бы мы вдруг хранили объекты в бд, нам нужен бы был объект который их оттуда достает, это проблема курицы и яйца.
Что касается кода то код хранится в файлах. Более удобного способа никто не придумал пока.
http://ideone.com/85fwQ6
Вот такое дерьмо написал по задаче с кубиками. Все время пишет что выиграл комп. Где я обосрался помимо того что родился?
Заранее благодарю.
Можно генерировать SQL код из моделей. Вроде там есть команда для этого (а также противоположное - создавать осову для моделей по схеме БД). Можно писать руками. Мне всегда было интереснее и удобнее писать SQL самому, и доктрина тем и хороша что позволяет мапить любые модели на любую базу, она не обязывает тебя создаваьть таблицы через нее.
> PHP Notice: Undefined variable: anonDIce2 in /home/ruUGkD/prog.php on line 13
пишет что нет такой переменной. Ты там большую букву с маленькой не перепутал?
Благодарю, Антоний, я и правда с регистром одной буквы обосрался.
Я в правильном направлении?
Ну, я имел в виду, что не в готовом виде хранится, а в отдельных соответствующих полях.
$student1 = new Student();
$student1->number = 321;
$student1->name = "Иван";
$student1->surname = "Иванов";
$student1->team = 2;
Вот эти "Иван", "Иванов" и 2 ведь хранятся в БД, а потом используются при создании экземпляра класса, когда надо вывести это всё или произвести иные действия?
Я не имею в виду, что вот такое создание экземпляра хранится в БД, а только эти элементы для создания.
Иначе как быстро доставать студента из файла? Ведь должно быть проще достать его из БД?
Я, наверное, неправильно сделал, что полез со своими вопросами - всё равно еще пока ООП не рассмотрел, ты уж извини, анончик.
Ну просто захотелось всё себе представить.
>Значит мы можем копировать поля не из любых объектов, а только имеющих отношение студенту - например других студентов, наследников, предков или классов реализующих тот же интерфейс. У тебя в программе есть такие классы?
Да, есть, у меня класс Формы наследуется от класса студента, следовательно даже будет лучше, если у меня будет метод которые будет принимать данные и из массива и из объекта. Однако, этот метод всё равно придется переопределять в классе Формы потому что класс Формы содержит пароль и повторныйПароль, а класс Студента содержит хэш.
Так же, этот метод был бы весьма полезен, потому что мы можем заполнять Студента из массива получаемого из ДБ и из объекта Формы, но тут мы сталкиваемся с тем же самым подвохом, при получение данных из Формы мы получаем чистый пароль и преобразовываем его в хэш, а при получении данных из ДБ мы получаем хэш.
Немного подумав мне приходит мысль, что универсальный метод конечно хорошо, но проще и очевидней сделать просто два разных метода.
Еще немного подумав у меня возникает вопрос с той же самой проблемой получения паролей и хэшей в классе Студента:
Если у меня будет в Студенте метод получения данных, то чтобы решить ту проблему мне придется писать два разных метода. Из-за этого класс "раздувается" и к тому же всё это наследуется в класс Формы где хэши не используются. Я зря беспокоюсь по этому поводу и мне стоит привыкать к таким условностям или это вполне резонно?
>>31147
>Но у объектов поля жестко заданы и даже если они называются одинаково это не значит что они связаны.
Элементы массива тоже могут быть не связанны.
Помню, анон расписывал всё чётко, когда кто-то задавал такой вопрос, только я забыл, в чём была суть, лол.
Вроде Yii2 советовал, потому как первый вариант уже устарел чутка.
Я и есть один из разработчиков, мне нужно понять как работает xss уязвимость и чего я добьюсь тем что в инет. Магазине подменю свои кукизы чужими и зайду под чужим акком
А ты быстрый! Обновления в основном "в ядре", теперь PHP стал более пригоден для хайлоада (но все равно еще на порядок медленнее чем остальные языки). Ну и по мелочи сделали возможность объявлять скалярные типы, объявлять какой тип вернет функция, анонимные классы и многое другое. Полный список новых вещей почитать можно тут http://php.net/manual/en/migration70.new-features.php
Автоматизировать процесс, так сказать.
Желательно, халявный или чтобы раз вносить оплату.
раньше мог посоветовать hostinger, но эта параша теперь требует смс-валидацию через смс(которая стоит почти 2 бакса).На данный момент нашел только один нормальный - 2freehosting.com Косяк там есть, но не знаю ошибка сайта или ограничение фри-версии акка - не делает бекап.
ну я уже год работаю на этом хостинге, брат жив.Иногда сайт падает, но это незначительный минус.
За сто рублей можно впску взять с 256 оперативки и 10 гигами места. Это более чем достаточно для бложиков до 10к трафа и своих маня проектиков. Заодно придрочишь скилы в настройке сервера и работы по ссх. Оно тебе нужно будет.
Что-то я не видел таких впсок по 100 рублей. Везде минимальная цена 400 - 600 за самый дешевый тариф.
Сто рублей это без админ панели или как она там называется. Ее можно прикупить, тогда будет рублей 400/мес, да.
Вот например: firstvds.ru/products/vds_vps_cheap
Вот еще одна, сам ей пользуюсь - x5x.ru. В чикаго/денвере не советую брать, пинг в консольке будет пиздецовый и дороже немного. Разве что для впн.
Рефочка http://x5x.ru/?aff=4349
Так я сделаю заказы, менеджеры свяжутся с клиентом в любом случае и очевидно что отменят заказ, даже если не отменят - он приедет не мне, а если я укажу удобный мне адрес - там либо предоплата по карте либо наложенный платеж, в самом аккаунте инфы о карте очевидно что нет, в итоге профита особого и нет как мне кажется.
А как на 2freehosting.com по FTP коннектнуться? Не работает что-то нихуя, как я не изголялся. Анон, что нужно-то? Прописываю порт, айпишник, имя, пароль от cPanel и сосу с "невозможно подключиться ко-ко-ко".
https://ideone.com/TZL4rs
Поправь ошибки. У тебя там куча нотисов
> Вот эти "Иван", "Иванов" и 2 ведь хранятся в БД, а потом используются при создании экземпляра класса, когда надо вывести это всё или произвести иные действия?
Да. Нам надо иметь возможность сохранять данные из модели в БД и наоборот, создавать модели по данным из БД. Это называется ORM - object to relational database mapping и есть 2 подхода, active record и data mapper. В комментариях к задаче есть ссылка на урок по этой теме.
Если тебе это интересно, читай комментарии к задаче про студентов и уроки по ссылкам, которые там приведены.
>>31305
> Да, есть, у меня класс Формы наследуется от класса студента
Я считаю это неправильно. Форма это не разновидность Студента и вряд ли может от него наследоваться. даже если формально выполняется правило Лисков (что объект формы можно использовать в коде везде вместо объекта студента) мне кажется тут оно неприменимо.
Вот статья по теме:
https://tproger.ru/translations/inheritance-and-composition-in-java/
http://sergeyteplyakov.blogspot.ru/2012/12/vs-vs.html
Мне кажется отношение между формой и студентом правильнее было бы реализовать через композицию.
Вот еще тут написано: https://msdn.microsoft.com/ru-ru/library/27db6csx(v=vs.90).aspx
> Наследование лучше использовать в следующих случаях.
> Иерархия наследования представляет собой отношения тождественности (is-a), а не отношения включения (has-a).
Вот попробуй ответить, какое утверждение звучит логичнее:
- "Форма Регистрации является (разновидностью или улучшенной версией) Студента"
- "Форма Регистрации содержит в себе модель Студента"
> Вот эти "Иван", "Иванов" и 2 ведь хранятся в БД, а потом используются при создании экземпляра класса, когда надо вывести это всё или произвести иные действия?
Да. Нам надо иметь возможность сохранять данные из модели в БД и наоборот, создавать модели по данным из БД. Это называется ORM - object to relational database mapping и есть 2 подхода, active record и data mapper. В комментариях к задаче есть ссылка на урок по этой теме.
Если тебе это интересно, читай комментарии к задаче про студентов и уроки по ссылкам, которые там приведены.
>>31305
> Да, есть, у меня класс Формы наследуется от класса студента
Я считаю это неправильно. Форма это не разновидность Студента и вряд ли может от него наследоваться. даже если формально выполняется правило Лисков (что объект формы можно использовать в коде везде вместо объекта студента) мне кажется тут оно неприменимо.
Вот статья по теме:
https://tproger.ru/translations/inheritance-and-composition-in-java/
http://sergeyteplyakov.blogspot.ru/2012/12/vs-vs.html
Мне кажется отношение между формой и студентом правильнее было бы реализовать через композицию.
Вот еще тут написано: https://msdn.microsoft.com/ru-ru/library/27db6csx(v=vs.90).aspx
> Наследование лучше использовать в следующих случаях.
> Иерархия наследования представляет собой отношения тождественности (is-a), а не отношения включения (has-a).
Вот попробуй ответить, какое утверждение звучит логичнее:
- "Форма Регистрации является (разновидностью или улучшенной версией) Студента"
- "Форма Регистрации содержит в себе модель Студента"
> Так же, этот метод был бы весьма полезен, потому что мы можем заполнять Студента из массива получаемого из ДБ и из объекта Формы, но тут мы сталкиваемся с тем же самым подвохом, при получение данных из Формы мы получаем чистый пароль и преобразовываем его в хэш, а при получении данных из ДБ мы получаем хэш.
Ты еще не учел, что если в форме 2 поля для ввода пароля то эти пароли могут не совпадать и непонятно как от них брать хеш. Плюс, в студенте могут быть поля (токен например) которые проставляет не форма. Полностью заполненного студента из формы возможно получить не получится, только частично заполненного.
> Если у меня будет в Студенте метод получения данных, то чтобы решить ту проблему мне придется писать два разных метода. Из-за этого класс "раздувается" и к тому же всё это наследуется в класс Формы где хэши не используются. Я зря беспокоюсь по этому поводу и мне стоит привыкать к таким условностям или это вполне резонно?
Это из-за того что ты считаешь Студента и Форму однотипной сущностью. Если использовать композицию, то есть сделать чтобы Форма содержала в себе Студента и записывала данные в него, эта проблема может решиться сама собой: в студенте будет поле для хеша, в форме поля для воода пароля. И кстати преобразовывать пароль в хеш форма не обязана самостоятельно - этим может например заниматься сервис авторизации в методе setStudentPassword($student, $password).
> Элементы массива тоже могут быть не связанны.
могут быть не связаны, могут быть связаны, но если пользователь передает его в метод простановки значений, то наверно они все же имеют отношение к студенту.
Если ты действительно разработчик то тебе надо ее закрыть, а не изучать что с ней можно сделать. Урок есть: https://github.com/codedokode/pasta/blob/master/security/xss.md
XSS похзволяет делать хакеру любые вещи от имени пользователя сайта и выводить на нем любую информацию. Как именно, я думаю, не принципиально.
>>31320
> (но все равно еще на порядок медленнее чем остальные языки)
Это же ложь. Динамические языки, такие как Руби и Питон медленнее пхп. И в php например за счет настоящих нерасширяемых классов и тайп-хинтов больше возможностей для оптимизации во время компиляции или выполнения (смотри HHVM). А на Си++ ты будешь сидеть в отладчике с утра до вечера в то время как php разработчики сделают несколько сайтов за это время.
>>31341
Не надо ничего изменять. Если ты не хочешь что-то выводить на странице то просто не выводи, а не лепи костыли.
>>31350
include в шаблоне
>>31356
Ищи статьи "обзор бесплатных хостингов". Западные бесплатные хостинги иногда даже не ставят на твой сайт рекламу, по-настоящему бесплатны.
>>31440
Бывают бесплатные без рекламы. Но разумеется никакой официальной техподдержки и все надо делать самому.
>>31535
> взять с 256 оперативки
Оперативка бывает разная. На openVZ (он часто используется в дешевых решениях) считается не реально используемая память, а потенциально используемая, и по факту ты получаешь где-то в полтора-два раза меньше (разумеется владельцы хостинга специально так делают чтобы больше серверов запихнуть в одну машину). Также твоя память может быть вытеснена в своп на диске.
Я помню давно клиенту впарили openvz с гигом памяти, от небольшой нагрузки там апач выходил за ограничения по памяти, его прибивало, мне пришлось заниматься всякими оптимизациями типа уменьшения возмодного размера стека итд.
В то же время виртуалка на технллогии Xen например дает честную память, если ты что-то держишь в памяти то оно там и находится, а не в свопе. Ты можешь управлять свопом, и вообще возможностей больше. Такой тип виртуализации есть например в облаке селектела.
Если ты действительно разработчик то тебе надо ее закрыть, а не изучать что с ней можно сделать. Урок есть: https://github.com/codedokode/pasta/blob/master/security/xss.md
XSS похзволяет делать хакеру любые вещи от имени пользователя сайта и выводить на нем любую информацию. Как именно, я думаю, не принципиально.
>>31320
> (но все равно еще на порядок медленнее чем остальные языки)
Это же ложь. Динамические языки, такие как Руби и Питон медленнее пхп. И в php например за счет настоящих нерасширяемых классов и тайп-хинтов больше возможностей для оптимизации во время компиляции или выполнения (смотри HHVM). А на Си++ ты будешь сидеть в отладчике с утра до вечера в то время как php разработчики сделают несколько сайтов за это время.
>>31341
Не надо ничего изменять. Если ты не хочешь что-то выводить на странице то просто не выводи, а не лепи костыли.
>>31350
include в шаблоне
>>31356
Ищи статьи "обзор бесплатных хостингов". Западные бесплатные хостинги иногда даже не ставят на твой сайт рекламу, по-настоящему бесплатны.
>>31440
Бывают бесплатные без рекламы. Но разумеется никакой официальной техподдержки и все надо делать самому.
>>31535
> взять с 256 оперативки
Оперативка бывает разная. На openVZ (он часто используется в дешевых решениях) считается не реально используемая память, а потенциально используемая, и по факту ты получаешь где-то в полтора-два раза меньше (разумеется владельцы хостинга специально так делают чтобы больше серверов запихнуть в одну машину). Также твоя память может быть вытеснена в своп на диске.
Я помню давно клиенту впарили openvz с гигом памяти, от небольшой нагрузки там апач выходил за ограничения по памяти, его прибивало, мне пришлось заниматься всякими оптимизациями типа уменьшения возмодного размера стека итд.
В то же время виртуалка на технллогии Xen например дает честную память, если ты что-то держишь в памяти то оно там и находится, а не в свопе. Ты можешь управлять свопом, и вообще возможностей больше. Такой тип виртуализации есть например в облаке селектела.
> OVZ
Имейте в виду что в openvz 256 мегабайт значит не то что вам даются реальные 256 мегабайт памяти, а виртуальные и условно говоря на сервере с реальными 256 Мб (или в виртуалке с Xen) вы сможете запустить больше программ чем тут. openvz позволяет оверселлить, то есть продавать больше памяти чем ее реально есть на сервере.
Скорее всего изменен тип окончаний строк. То есть в исходном файле например был формат окончаний строк windows (\r\n) а после изменений стал unix (\n) или наоборот. Или табы заменены на пробелы.
Как узнать реальную причину?
Для начала надо извлечь старую и новую версию файла из гита (например командой git checkout) и сохранить в отдельную папку.
Затем надо скачать hex редактор https://ru.wikipedia.org/wiki/Hex-редактор
Например https://en.wikipedia.org/wiki/HxD или бесплатный http://www.hexedit.com/ подойдет
Этот редактор покажет байты из которых состоит файл. Сравни файлы и сделай выводы.
Там даже самые первые коммиты (т.е. только отправленные в пустой репрозиторий) такие почти везде. Наверное у меня редактор сразу ставит не те переносы/табы?
Пикрелейтед 1 коммитился нормально и в гуи, и в консоле. А вот второй пикрелейтед нет. Я не понимаю причину по байтам в этом редакторе.
Не туда вставил скрин успешного коммита, скрин гитхаба слева должен быть у пикрелейтед2 и наоборот.
Посмотри внимательно на байты на первом скрипте что идут после <?php
Слева там 0D0D (\r\r), справа 0D0A0D0A (\r\n\r\n). Это разные способы обозначения конца строки, видимо ты в редакторе поменял формат конца строки.
Он у меня уже несколько месяцев в голове висел, наконец-то написал. Он научит вас особенностям написания клиентских MVC приложений на примере игры "Сапер". То есть мы пишем простую игру, но при этом изучаем принципы которые используются в разработке полноценных сложных приложений (и может даже пригодятся в нашем легендарном задании на SPA).
Упоминаются knockout, angular, react.
Пожалуйста, посмотрите, напишите свое мнение. Даже если вы пока плохо яваскрипт знаете.
Хорошо. Давно хотел переделать, да всё как-то руки не доходят, да и слабо представлял MVC на жсе.
Спасибо, антош. Всё необходимое в одном месте собрал, мне нравится.
echo "$sasamba";
echo "{$sasamba}";
А де у тебя среднее соотношение тугр./стр по всем департаментам?
Очень много сложноты в коде, мне кажется, гораздо проще многое возможно.
Давно хотел спросить: ты где-то учился кодингу? Чувствуется по подходу к решению задач - не совсем новичок.
> А де у тебя среднее соотношение тугр./стр по всем департаментам?
Вроде 3.28, строка "Среднее", последний столбец.
Или ты про последнюю строку, последний столбец? Там не совсем понятно что считать...
> Очень много сложноты в коде, мне кажется, гораздо проще многое возможно.
Да возможно, а что например?
>Давно хотел спросить: ты где-то учился кодингу? Чувствуется по подходу к решению задач - не совсем новичок.
В школе написал пару простых флеш игр, хардкорная процедурка :) Потом 10 лет принимал тяжелые наркотики и работал дизайнером. Сейчас наверстываю.
По задаче вопрос, для чего тут имеет смысл использовать фабрику?
>>731911 числа прописью, вектор
>>731912 калькулятор, банкомат
>>731913 симфони, вордпресс
>>731915 https://github.com/fidnex/student
Приветствую вас, уважаемые. Мой мозг опять решил включить тупого, и я не могу понять, где я снова обосрался, при попытке решить задачу про депозит из стартового туториала в оппосте.
http://ideone.com/qvdLfh
Вот код. Заранее благодарю за помощь
>PHP Parse error: syntax error, unexpected ';' in /home/dZCMd4/prog.php on line 3
Почему не читаешь уведомления об ошибках?
>x*10%
Поехавший чертяка. Прочитай начало самое, где рассказывается об операторах. Знак "%" нужен для того, чтобы остаток числа получить, ты не получишь так проценты.
Да. Увидел это сейчас в phpstorm. Спасибо!
У тебя там дофига точек с запятыми внутри for. Их должно быть три. Одинаковые вещи отделяются запятыми. Синтаксис такой:
for(Инициализация счетчика 1, инициализация счетчика 2... и тд ; условие 1, Условие 2... и т.д; увеличение счетчика 1, увеличение счетчика2... и т.д)
Для того чтобы выдергивать ВСЕ урлы из текста.
Если есть класс, с бд всех доменов чтобы четко детектить урлы типа ololo.moscow и отличать их от от шлака типа web.hui будет вообще круто.
Строка для примера:
asdf asdf http://asdads1.ru asdf asdf asdf a11sffdads2.ru a
Алсо, пробелы в начале и конце строки придется обрезать через какой-нибудь trim, или используй группу захвата ().
Привет, ОП. У меня студенты на ООП ̶b̶a̶d̶u̶m̶-̶t̶s̶s̶. Вообщем вот мой репозиторий (третий по счету) https://github.com/greenTea242/Student_List, но теперь в нем все коммиты отображаются. В него вошли два старых коммита из прошлого репозиторий и мои последние изменения.
> Тут уже возникает вопрос почему эти функции отдельные и какой смысл ставить одну куку, не поставив другую?
Установка кук для токена существует отдельно потому что в register.php проверяется защита на СSRF во время отправки формы, т.е. до момента регистрации студента и выставления оных.
>>725118
Ты мне советовал для выделения слов при поиске использовать preg_replace_callback. У меня не получилось ее правильно использовать, потому что она сразу меняет параллельно все совпадения по массиву регулярок и нигде нельзя посмотреть процесс изменения текста, а мне нужно сделать так, что если это совпадение уже в тегах <mark>, мне это не нужно делать. Данный результат мне удалось получить через дополнительный foreach. Результат по пикрелейтеду вроде правильный.
Хорошая традиция.
Спасибо тебе большое.
>В сумме 60 бит энтропии. Для их записи хватит 10 символов (если каждый принимает 64 значения = 6 бит), а не 32.
Я, если честно, не совсем понимаю что это и зачем все оно нужно. Проблема в том что в моем коде неоправданно много ресурсов тратится на генерацию токена, да?
$i = 32; $hash = '';
while($i--) $hash .= chr(rand(40,126));
return $hash;
Сойдет такое? Меня возможная точка с запятой немного смущает.
mysql> GRANT ALL ON menagerie.* TO your_mysql_name;
>your mysql name
Скорее всего под юзернеймом root сидишь
Создаешь пользователя, типа vasya@localhost, а потом даешь ему права на бд GRANT ALL ON menagerie.* TO vasya@localhost
Спасибо, браток
2) Как правильно сообщать об ошибках на разных уровнях: бд, валидатора, модели?
3) Где проверять логические ошибки? То есть, когда нам нужно сначала получить запись из бд и посмотреть, наш ли это комментарий, например. Пока получается нагромождение if'ов в модели.
Спасибо.
Не могу найти что и где задаёт цвет верхней части в википедии, на скрине белым. Можете помочь понять где там её цвет менять?
Всё нашёл, там просто не было этого параметра!
Давай я поясню более простым примером. Допустим ты генерируешь токен как
md5(rand(0, 9))
с виду кажется что это надежный токен из 32 символов но на самом деле там ровно 10 возожных вариантов, то есть по уровню защищеннности он равносилен
rand(0, 9)
И тогда возникает вопрос, а зачем там md5? Я не знаю почему она там, скорее всего программист сам нпе понял что написал. А каждая функция должна быть поставлена осмысленно и с какой-то целью.
md5 не увеличивает число возможных вариантов токена, значит она никакой пользы не несет и не нужна.
> while($i--) $hash .= chr(rand(40,126));
сойдет, только я бы код оформил чуть получше, с фигурными скобками и циклом for вместо while так как в этом случае лучше будет видно что мы делаем ровно 32 итерации.
> Меня возможная точка с запятой немного смущает.
а что с ней не так? setcookie вроде бы делает urlencode для спецсимволов.
1) коды ошибок сделать константами, сообщения можно хранить где-нибудь в массиве или где-то еще. Но вообще я не думаю что тут так уж нужны коды, почему бы при проверке сразу не возвращать сообщения? Коды ведь не позволят сделать такое:
- поле "имя" должно содержать не более 50 символов, вы ввели 100
2) через return, например возвращать массив ошибок или объект ErrorsCollection содержащий список ошибок
3) сделать функцию валидации которая все проверяет. Подумай, что функия получает на вход? Наверно комментарий. А что дает на выходе? Список ошибок
> То есть, когда нам нужно сначала получить запись из бд и посмотреть, наш ли это комментарий, например.
ПРава доступа проверяют в контроллере, либо можно сделать функцию для из проверки (это даже лучше)
> получается нагромождение if'ов в модели.
неудобно делать валидацию в самой модели. Например не получится сделать проверку если надо обращаться к БД
Ты читал комментарии к задаче про студентов? Там вроде упомянуто это. Делается отдельный класс для валидации.
И даже вложенные md5 не помогут? Пожалуй стоит почитать чего на тему.
>а что с ней не так? setcookie вроде бы делает urlencode для спецсимволов.
Тогда с 33 по 126 брать, довольно солидно выглядит.
бумп
Олимпиадные задачи и написание реальных приложений - разные вещи. Насчет учебника, если ты знаешь циклы и ифы - пропускай их, переходи сразу к массивам (если ты не изучал раньше php то ты вряд ли их знаешь так как массивы в пхп и массивы в Си это разные вещи), регуляркам, ООП. Если ты все это знаешь, можешь браться за студентов, но я бы советовал минимум решить задачи на регулярки и на ООП.
Блог на wordpress
Хм, вообще вот эта часть рассчитана под такое:
>(((http(s)?):\/\/)?(www\.)?)?
А, нет, вот так надо:
>([a-zа-яё0-9\/]+)?
То есть после домена могут и не быть никакие символы.
Я с планшета, так бы давно сам проверил, соррян, делал наугад и для развлечения.
Приветствую, ананасики. Решил вот вкатиться в пхп, до этого изучал паскаль. Оцените решение задачки про рост.
>у тебя все странно выглядит, я бы мог написать замечания к каждой строчке но некогда.
ОП, ну зачем ты так? Уже 5 дней ничего не пишу и только гадаю что у меня не так в КАЖДОЙ строчке. Напиши пожалуйста что не так.
>Ну например почему у тебя всюду статические методы
Половина статических методов - это методы Laravel. Мои методы тоже не требуют создания объектов, классы существуют просто для группировки функций..
>или что за странный класс table (и погчему он с маленькой буквы?).
Это метод. Насчет странности я не понимаю о чем речь, взял тут https://laravel.com/docs/5.2/queries#retrieving-results
>Почему имя класса GetLinks начинается с глагола?
Уже переправил на PageProcessor. Как еще я мог назвать класс, который только и делает что достает ссылки из страницы? LinksExtractor?
>Тебе надо разобраться с ООП, MVC, прежде чем браться писать такие приложения. В ОП посте есть задача про студентов с комментариями, почитай. Про ООП есть в моем учебнике в ОП посте.
Пожалуй прислушаюсь, а то у меня классы все как модули для хранения функций выглядят со статикой во всех полях.
>> . Это правильно? Или надо передавать массив параметров в модель чтобы она меняла свое состояние сама?
>А за что по твоему отвечает модель? Чем она отличается от контроллера?
Решил передавать массив параметров напрямую в модель для разбора. Правильно или нет (логика разбора параметров в голове не укладывается когда их много)? Модель отвечает за выработку состояния компонента или приложения в зависимости от настроек, которые лежат в сессиях, бд и передаются вместе с запросами. Модель отвечает за работу с БД (или это бизнес-логика делает уже - я пока не вкурил). Контроллер отвечает за выбор необходимой модели и передачу ей параметров запроса, а также получает массив данных из модели и возвращает вид, заполненный данными из этого массива.
>> Делать запрос в модель для получения массива состояния вида, возвращать обновленный вид - это задача контроллера или модели?
>А почему массив а не объект?
В Laravel view работает с массивами, но может и объект blade'y передать как элемент в массиве.
Модель не должна знать ничего про view и потому возвращать его не может.
Разобрался. Это должен делать контроллер.
>у тебя все странно выглядит, я бы мог написать замечания к каждой строчке но некогда.
ОП, ну зачем ты так? Уже 5 дней ничего не пишу и только гадаю что у меня не так в КАЖДОЙ строчке. Напиши пожалуйста что не так.
>Ну например почему у тебя всюду статические методы
Половина статических методов - это методы Laravel. Мои методы тоже не требуют создания объектов, классы существуют просто для группировки функций..
>или что за странный класс table (и погчему он с маленькой буквы?).
Это метод. Насчет странности я не понимаю о чем речь, взял тут https://laravel.com/docs/5.2/queries#retrieving-results
>Почему имя класса GetLinks начинается с глагола?
Уже переправил на PageProcessor. Как еще я мог назвать класс, который только и делает что достает ссылки из страницы? LinksExtractor?
>Тебе надо разобраться с ООП, MVC, прежде чем браться писать такие приложения. В ОП посте есть задача про студентов с комментариями, почитай. Про ООП есть в моем учебнике в ОП посте.
Пожалуй прислушаюсь, а то у меня классы все как модули для хранения функций выглядят со статикой во всех полях.
>> . Это правильно? Или надо передавать массив параметров в модель чтобы она меняла свое состояние сама?
>А за что по твоему отвечает модель? Чем она отличается от контроллера?
Решил передавать массив параметров напрямую в модель для разбора. Правильно или нет (логика разбора параметров в голове не укладывается когда их много)? Модель отвечает за выработку состояния компонента или приложения в зависимости от настроек, которые лежат в сессиях, бд и передаются вместе с запросами. Модель отвечает за работу с БД (или это бизнес-логика делает уже - я пока не вкурил). Контроллер отвечает за выбор необходимой модели и передачу ей параметров запроса, а также получает массив данных из модели и возвращает вид, заполненный данными из этого массива.
>> Делать запрос в модель для получения массива состояния вида, возвращать обновленный вид - это задача контроллера или модели?
>А почему массив а не объект?
В Laravel view работает с массивами, но может и объект blade'y передать как элемент в массиве.
Модель не должна знать ничего про view и потому возвращать его не может.
Разобрался. Это должен делать контроллер.
http://ideone.com/qXKUpj
Задача про банкомат.
http://ideone.com/q63wGt
Резюме пилите на:
Как-то так тогда.
\s(((http(s)?):\/\/)?(www\.)?)?[a-zа-яё]+\.[a-zа-яё]{2,6}([a-zа-яё0-9\/]+)?
Другой анон
$anonDice1 = mt_rand(1,6);
$anonDice2 = mt_rand(1,6);
$compDice1 = mt_rand(1,6);
$compDice2 = mt_rand(1,6);
echo "У Анона выпало {$anonDice1} и {$anonDice2}\nУ Компьютера {$compDice1} и {$compDice2}\n";
$anonSum = ($anonDice1 + $anonDice2);
$compSum = ($compDice1 + $compDice2);
if (($anonDice1 == $anonDice2) && ($compDice1 == $compDice2)) {
echo "2 дабла - тебя ждет удача\n";
exit ();
}
Не понял вопроса.
У тебя в условии стоит если выпавшее у человека на первом кубике равно выпавшему на втором кубике, а выпавшее у компьютера на первом так же равно выпавшему на втором, то у них два дабла - выводим это и выходим из скрипта.
>
>Дошёл до ООП.
>Поясните по хардроку, как вообще это функционирует на сайтах.
>1. Вот у меня файл .php со скриптом, там ООП, классы, вот это всё, ему нужны экземпляры класса создавать - как-то надо подключать их, ставить на место созданий экземпляра include с файлом, уже содержащим созданные классы? Я правильно понимаю логику? Или это всё возможно через БД - оттуда брать инфу и вставлять в скрипт?
>2. Соответстветственно, ещё какой-нибудь скрипт отвечает за заполнение этого скрипта с экземплярами класса или БД с ними. Например, это студенты - скрипт собирает с регистрацией набивает БД студентами как экземплярами класса Student, а ты потом их вставляешь хитроумным способом в свой первый скрипт с ООП и вертишь там по-всякому.
>Я верно это представляю себе?
Кто-нибудь может перевести на русский язык то что он написал?
Что тебе непонятно?
Интересно, как всё взаимосвязано на сайте.
Мы со всеми начальными задачами делаем так, что весь скрипт со всеми данными расположен на одной странице, в одном файле.
Но ведь на сайтах всё по-другому.
Что-то вводится пользователем, что-то берется из БД.
Я не понимаю концепцию MVC. Вот сейчас пытаюсь ковырять yii2 и сделать на нём приложение. Мне нужна стартовая страница, где была бы простая форма, куда бы я добавил адрес треда на дваче и после нажатия на кнопку подтверждения данные треда (тема оп-поста, текст оп-поста, адрес треда и прочее) добавлялись бы в таблицу. Может кто-то на пальцах пояснить как это на yii2 реализовать?
бамп реквесту
Поэтому, пока не наделал глупостей, вопрос по роутингу: хочу создать класс Router, который будет парсить, разбивать запрос из адресной строки, а потом вызывать соответствующий контроллер и его метод. Каким образом лучше получать запрос из адресной строки? Один из вариантов - это указать в public/.htaccess правило вроде RewriteRule ^(.*)$ index.php?url=$1, чтобы все запросы вида controller/method преобразовывались в index.php?url=controller/method. Роутер проверит, если есть $_GET['url'], тогда нужно функцией explode() по слешу по слешу разбить строку, первый элемент массива на контроллер, второй на метод, остальные на параметры, а потом всё вызывать фунцией call_user_func_array(), для того, чтобы вызвать соответствующий контроллер и метод.
Второй вариант - использовать $_GET['REQUEST_URI'], но он указывает путь относительно локалхоста, то есть добавляет лишнее (пик). Вот как можно обойти: http://stackoverflow.com/a/13040861
Не слишком костыльно? Или лучше виртуальные хосты использовать?
> Второй вариант - использовать $_GET['REQUEST_URI'], но он указывает путь относительно локалхоста, то есть добавляет лишнее (пик).
Лучше бы конечно REQUEST_URI. если испольщуешь реврайтинг надо добавлять флаг QSA иначе все GET-параметры теряются. Ну и надо помнить что сервера вроде nginx не поддерживают htaccess и не будут передавать параметр url, а REQUEST_URI должен работать везде.
> хочу создать класс Router, который будет парсить, разбивать запрос из адресной строки, а потом вызывать соответствующий контроллер и его метод.
Если он вызывает контроллер то это уже формально Front Controller а не просто роутер. Роутер никого не вызывает.
> по слешу по слешу разбить строку, первый элемент массива на контроллер, второй на метод, остальные на параметры
Это годится только для самых простых случаев. В реальном приложении такая схема будет доставлять неудобства. Ну например с ней одной странице соответствует бесконечное число URL вида
/some/page/x/1
/some/page/x/2
/some/page/x/3
что негативно может сказать на индексации поисковиками.
Как альтернатива можно использовать регулярки:
if (preg_match...) {
вызываем конроллер X;
}
>Что тебе непонятно?
Половину написанного.
>>30857
>Вот у меня файл .php со скриптом, там ООП, классы, вот это всё, ему нужны экземпляры класса создавать - как-то надо подключать их, ставить на место созданий экземпляра include с файлом, уже содержащим созданные классы?
Это у тебя вопросительное или утвердительное предложение? 34 слова в предложении - не многовато ли?
>Дошёл до ООП.
>Поясните по хардроку, как вообще это функционирует на сайтах.
>1. Вот у меня файл .php со скриптом, там ООП, классы, вот это всё,
Зачем ты это пишешь?
>ему нужны экземпляры класса создавать
Кому "ему"? Классу ничего не нужно. Объекты нужны тебе.
>как-то надо подключать их
Что подключать? Что? Объекты? Что значит "подключать"? Инициализировать может?
>ставить на место созданий экземпляра include с файлом, уже содержащим созданные классы?
"Созданий"? "Создания" может? Что за "include с файлом"? Пиши "include". Какие созданные "классы" у тебя в инклюде?
>>30969
>Нет, юзай композер.
Ты шутишь, да?
>БД вообще тут не при чем.
Как ни при чём если объекты заполняют с помощью массивов и таблиц БД, или в процессе работы данные узнают.
>>30857
>2. Соответстветственно, ещё какой-нибудь скрипт отвечает за заполнение этого скрипта с экземплярами класса или БД с ними. Например, это студенты - скрипт собирает с регистрацией набивает БД студентами как экземплярами класса Student, а ты потом их вставляешь хитроумным способом в свой первый скрипт с ООП и вертишь там по-всякому.
>Я верно это представляю себе?
Что тут вообще написано?
По моему ты занимаешься глупостью. Человек задал вопрос и получил на него какой-то ответ. Если он тебе непонятен, то возможно ты пока чего-то не знаешь. Перестань придираться к словам и тем более к опечаткам и смени тон разговора.
>По моему ты занимаешься глупостью.
Смени тон разговора. У человека с мышлением и рассудком не все в порядке. Мне интересно очень в каком порядке должны замкнуться нейроны чтобы вышел этот опус.
1280x720
Я опять выхожу на связь. Тащемта, запилил отдельные классы под каждую профессию и проверку названия профессии константой класса, например. Пока кидаю без антикризисных мер, но уже делаю их. http://ideone.com/Ery6OY
А программирование значит для тебя не лингвистика? И декодирование сбитой логики уже не входит в рамки анализа постановки задачи?
Ест правило: класс-предок ничего не знает о своих потомках. Так как классы-помтоки например могут писаться позже и заранее их наличие угадать невозможно.
У тебя это правило не соблюдается:
> if ($position == self::POSITION_MANAGER||$position == self::POSITION_MARKETER||
> $position == self::POSITION_ANALYST||$position == self::POSITION_ENGINEER)
{
$this->position = $position;
}else{
echo 'Професия введена неверно.'
у тебя нельзя добавить новую профессию, написав новый класс. А в хорошем ООП-дизайне такое было бы возможно.
По этой же причине нельзя сделать константы для профессий в базовом классе. Эти константы и поле position вообще не нужны так как у тебя используются разные класса и их имена сами по себе константы.
Если тебе где-то надо передать идентификатор профессии, можно использовать такую конструкцию:
Marketter::class
это встроенная в пхп константа, возвращающая имя класса.
> foreach ($employees as $employee)
> {
> $this->quantity[] = $employee->quantity;
> $this->position[] = $employee->position;
Это тоже непрвильно. У тебя есть объект-сотрудник, и в нем хранится его должность, ранг. и тд. Зачем ты эти данные копируешь и храишь где-то еще? Это ведь тольео добавляет проблем. Что если у сотрудника поменяется ранг - он обновится в департаменте? Что если мы наймем или уволим сотрудника?
Не надо дублировать данные. В департаменте надо хрнить массив объектв-сотрудников а не копию информации о них которая может оказаться неактуальной.
> }elseif ($this->position[$x] == 'Marketer')
> {
> $coffe = ((new Marketer($this->rank[$x], > $this->boss[$x]))->getCoffe())*$this->quantity[$x] + $coffe;
Тут жестко заложен список профессий и это не позволяет добавлять новые.
> class Company
>{
>public $total = 'Всего';
Это такое свойство компании? ЧТо оно обозначает? Какие еще значения у него могут быть?
>public $totalPersonelAmount;
>public $totalSalary;
Если я повышу сотруднику зарплату, она автоматически обновится в этом поле?
> class Employee
> {
>public $quantity;
У тебя класс называется "сотрудник" (не "группа сотрудников"), тогда почему там есть поле "количество"? И это плохая идея, так как непонятно, если у тебя один объект обозначает группу сотрудников, как например повысить ранг только одному из этой группы?
Ест правило: класс-предок ничего не знает о своих потомках. Так как классы-помтоки например могут писаться позже и заранее их наличие угадать невозможно.
У тебя это правило не соблюдается:
> if ($position == self::POSITION_MANAGER||$position == self::POSITION_MARKETER||
> $position == self::POSITION_ANALYST||$position == self::POSITION_ENGINEER)
{
$this->position = $position;
}else{
echo 'Професия введена неверно.'
у тебя нельзя добавить новую профессию, написав новый класс. А в хорошем ООП-дизайне такое было бы возможно.
По этой же причине нельзя сделать константы для профессий в базовом классе. Эти константы и поле position вообще не нужны так как у тебя используются разные класса и их имена сами по себе константы.
Если тебе где-то надо передать идентификатор профессии, можно использовать такую конструкцию:
Marketter::class
это встроенная в пхп константа, возвращающая имя класса.
> foreach ($employees as $employee)
> {
> $this->quantity[] = $employee->quantity;
> $this->position[] = $employee->position;
Это тоже непрвильно. У тебя есть объект-сотрудник, и в нем хранится его должность, ранг. и тд. Зачем ты эти данные копируешь и храишь где-то еще? Это ведь тольео добавляет проблем. Что если у сотрудника поменяется ранг - он обновится в департаменте? Что если мы наймем или уволим сотрудника?
Не надо дублировать данные. В департаменте надо хрнить массив объектв-сотрудников а не копию информации о них которая может оказаться неактуальной.
> }elseif ($this->position[$x] == 'Marketer')
> {
> $coffe = ((new Marketer($this->rank[$x], > $this->boss[$x]))->getCoffe())*$this->quantity[$x] + $coffe;
Тут жестко заложен список профессий и это не позволяет добавлять новые.
> class Company
>{
>public $total = 'Всего';
Это такое свойство компании? ЧТо оно обозначает? Какие еще значения у него могут быть?
>public $totalPersonelAmount;
>public $totalSalary;
Если я повышу сотруднику зарплату, она автоматически обновится в этом поле?
> class Employee
> {
>public $quantity;
У тебя класс называется "сотрудник" (не "группа сотрудников"), тогда почему там есть поле "количество"? И это плохая идея, так как непонятно, если у тебя один объект обозначает группу сотрудников, как например повысить ранг только одному из этой группы?
Я имел ввиду,я должен сравнить суммы $anonSum и $compSum, если сумма у анона и кампуктера ровна, то объявляется ничья, итд итп.
if ($anonSum == $compSum) {
echo "Нечья\n";
} elseif ($anonSum > $compSum) {
echo "Человек победил.\n";
} elseif ($anonSum < $aiComp) {
echo "ИИ победил.\n";
}
Верно?
>Половину написанного.
Согласен. Там поток сознания, писал с планшета и слегка засыпая.
Отредактирую сейчас именно тот пост:
"Дошёл до главы "Объектно-ориентированное программирование" в учебнике ОПа.
Прошу вас объяснить без лишних слов и затрагивая самую суть, каким именно образом скрипты, созданные с помощью ООП, функционируют на динамических сайтах.
1. Я создал файл с расширением .php, в нём создал скрипт на РНР, написанный с помощью ООП - там инициализированы классы, имеющие свои свойства и методы. Но ведь нам нужно каким-то образом динамически создавать экземпляры этих классов? Нам нужно как-то подключать их из сторонних файлов или базы данных? Обычно мы создавали экземпляры классов прямо в самих скриптах, а в случае с динамическими сайтами как должны поступать? В том месте скрипта, где необходимо оперировать уже созданным экземпляром класса, мы должны каким-то образом подключать сторонний файл, уже содержащий созданные экземпляры класса? Либо каким-то образом подключать информацию из базы данных? Правильно ли я понимаю логику взаимодействия разрозненных файлов в динамическом сайте?
2. Соответственно, нам могут потребоваться дополнительные скрипты, отвечающие за связь с базой данных и создание необходимых экземпляров класса? Своё предположение могу продемонстрировать на примере динамического списка студентов. Как мне кажется, для его функционирования необходим отдельный скрипт с регистрацией самих студентов (он отвечает за заполнение базы данных либо отдельного файла информацией, которая понадобится при создании экземпляров классов, необходимых для полноценной работы этого динамического списка студентов). Например, мы имеем класс Student, а в базе данных либо отдельном файле имеем информацию, которая каким-то образом должна использоваться при создании экземпляров этого класса Student. После чего мы получаем возможность тем или иным образом оперировать как с отдельными экземплярами, так и с их совокупностью.
Могу ли я считать, что мои первоначальные представления о функционировании динамического сайта верны хотя бы отчасти?"
Так лучше?
>>33018
Да, в целом я уже разобрался, спасибо братишкам. Я и хотел просто понять, верно ли я себе представляю динамику именно на самих сайтах.
>Половину написанного.
Согласен. Там поток сознания, писал с планшета и слегка засыпая.
Отредактирую сейчас именно тот пост:
"Дошёл до главы "Объектно-ориентированное программирование" в учебнике ОПа.
Прошу вас объяснить без лишних слов и затрагивая самую суть, каким именно образом скрипты, созданные с помощью ООП, функционируют на динамических сайтах.
1. Я создал файл с расширением .php, в нём создал скрипт на РНР, написанный с помощью ООП - там инициализированы классы, имеющие свои свойства и методы. Но ведь нам нужно каким-то образом динамически создавать экземпляры этих классов? Нам нужно как-то подключать их из сторонних файлов или базы данных? Обычно мы создавали экземпляры классов прямо в самих скриптах, а в случае с динамическими сайтами как должны поступать? В том месте скрипта, где необходимо оперировать уже созданным экземпляром класса, мы должны каким-то образом подключать сторонний файл, уже содержащий созданные экземпляры класса? Либо каким-то образом подключать информацию из базы данных? Правильно ли я понимаю логику взаимодействия разрозненных файлов в динамическом сайте?
2. Соответственно, нам могут потребоваться дополнительные скрипты, отвечающие за связь с базой данных и создание необходимых экземпляров класса? Своё предположение могу продемонстрировать на примере динамического списка студентов. Как мне кажется, для его функционирования необходим отдельный скрипт с регистрацией самих студентов (он отвечает за заполнение базы данных либо отдельного файла информацией, которая понадобится при создании экземпляров классов, необходимых для полноценной работы этого динамического списка студентов). Например, мы имеем класс Student, а в базе данных либо отдельном файле имеем информацию, которая каким-то образом должна использоваться при создании экземпляров этого класса Student. После чего мы получаем возможность тем или иным образом оперировать как с отдельными экземплярами, так и с их совокупностью.
Могу ли я считать, что мои первоначальные представления о функционировании динамического сайта верны хотя бы отчасти?"
Так лучше?
>>33018
Да, в целом я уже разобрался, спасибо братишкам. Я и хотел просто понять, верно ли я себе представляю динамику именно на самих сайтах.
Какие нейроны? У человека просто напрочь отсутствуют представления о предмете разговора, поэтому он несет такую хуиту.
Например если бы я или ты, или другой человек совершенно незнакомый с генетикой попытался бы изложить свое представление
об этом вопросе, тоже получилась бы шизофазия вида
"ну там всякие рибосомы через митохондрии че-то там передают генетический код, код это короче типа молекула из атомов. скажите я правильно понимаю ООП генетику?"
А студенты медики такие "омг, фейспалм, ти вапщи с кокой плонети???77".
Короче ты какой-то хуйней занимаешься. Если это вброс, то максимум что можно выжать это вялый флейм.
>>33023
>Смени тон разговора.
А ты соси хуй и не забывай где находишься. Или съеби в хабр/жж.
Отстань.
>У человека просто напрочь отсутствуют представления о предмете разговора, поэтому он несет такую хуиту.
Да, всё верно.
>>33084
>У тебя вопросы получились не про ООП, а про порядок исполнения многофайлового приложения.
Да, я просто задумался об этом всём именно при изучении ООП.
>Он себе на все вопросы практически ответил.
Да, действительно.
Суть же в том, что это всё было моим предположением, я просил всего лишь объяснить этот механизм и подтвердить, верно ли я предполагаю это взаимодействие файлов.
Ну, спасибо, друзья, но не хотелось бы, чтобы это всё продолжалось - выяснение какое-то чего-то.
Поиск пути: http://ideone.com/33ROxP
Текст по кругу: http://ideone.com/2q1F4N (в браузере красивее выглядел)
На вашем счету n рублей: http://ideone.com/JXvVHa
ООО Вектор: http://ideone.com/MbL6Pb (в XAMPP ошибки нет, не пойму чем ему int не нравится)
> public function delEmployee(int $i){
В пятом php нет тайпхинтов для скаляров.
>XAMPP
Говно из жопы.
А, всё, понял в чём ошибка. Просто у меня модно-новый php7, там так можно
/api/x/2
А хотелось бы:
/api/x/2/y/1/z
Как быть?
Ты мне лучшую практику дай. Зачем я буду велосипеды городить?
> ООП. Два типа вопроса
Ок, решено верно.
> И в прошлом треде за 27ое число три задачи без ответов,
На них вроде дал ответы в старом треде.
>>30277
В конфиг сервера, в винде он в папке conf Апача, в линуксе обычно в /etc/apache2/apache.conf или как-то так.
> Просто скопировать в любое место?
ну можно в конец например. Не знаю, важно ли расположение.
>>30285
Может быть слишком старая версия языка или не хватает какого-то расширения. Напиши что за ошибки и в какой строке.
>>30337
> А разве ключом не должно быть название поля? Как тогда обозначить ключ ошибки?
действительно. Ну значит массив должен быть какой-то другой структуры.
>>30350
На мак вроде через homebrew ставят пхп и апач.
>>30533
> MariaDB тоже на хрен не нужна, Мускул таки лучше и ТРУЪ.
Там вроде различия не очень большие и есть фишечки вроде мониторинга использования индексов, которые, впрочем, на локальном сервере не особо нужны.
>>30537
Если через homebrew ставить то оно вроде в отдельные папки ставится.
>>30666
Может быть, не знаю.
>>30763
Не знаю, спроси в Питон треде.
>>30943
Да. Причем можно было записать еще короче, без промежуточных переменных.
> ООП. Два типа вопроса
Ок, решено верно.
> И в прошлом треде за 27ое число три задачи без ответов,
На них вроде дал ответы в старом треде.
>>30277
В конфиг сервера, в винде он в папке conf Апача, в линуксе обычно в /etc/apache2/apache.conf или как-то так.
> Просто скопировать в любое место?
ну можно в конец например. Не знаю, важно ли расположение.
>>30285
Может быть слишком старая версия языка или не хватает какого-то расширения. Напиши что за ошибки и в какой строке.
>>30337
> А разве ключом не должно быть название поля? Как тогда обозначить ключ ошибки?
действительно. Ну значит массив должен быть какой-то другой структуры.
>>30350
На мак вроде через homebrew ставят пхп и апач.
>>30533
> MariaDB тоже на хрен не нужна, Мускул таки лучше и ТРУЪ.
Там вроде различия не очень большие и есть фишечки вроде мониторинга использования индексов, которые, впрочем, на локальном сервере не особо нужны.
>>30537
Если через homebrew ставить то оно вроде в отдельные папки ставится.
>>30666
Может быть, не знаю.
>>30763
Не знаю, спроси в Питон треде.
>>30943
Да. Причем можно было записать еще короче, без промежуточных переменных.
> Я особо не шарю, но вроде и обычные массивы тоже можно создавать в таком стиле - $array = new ArrayObject;
Нет, это не массив. Ты например не можешь передать его в функцию sort().
>>31213
ты что-то путаешь. Речь шла не о хранении записей в базе данных или в файле, а в памяти в массиве. И ограничивать его длину - значит проверять размер и при превышении отрезать лишние элементы.
>>31223
Чем джанго лучше? У нас ты берешь любой класс и добавляешь аннотации, а там обязан наследоваться и создавать поля нестандартным образом.
>>31255
Не совсем. Вот смотри:
> if ($number < 100) {
> $number = $spelling[($number % 100) - ($number % 10)].$spelling[$number % 10];
Если число меньше 100 то у него может и не быть единиц, например в числе "80"
Исправлять их костылями через регулярки неправильно, надо изначально туда ноль не добавлять.
> if (($a < 5) and ($a != 1) and ($a != 0)) {
> return "$textNumber ($number) рубля";
Это надо вынести в функицю, иначе тебе придется копипастить ифы и для тысяч и миллионов.
Ну и ошибку исправить:
> PHP Warning: Missing argument 2 for smallNumberToText(), called in /home/15mtSB/prog.php on line 66 and defined in /home/15mtSB/prog.php on line 18
>>31308
Можно
>>31604
Ты не можешь предугадать как уязвимостью воспользуются. Например с помощью XSS и социнженерии можно убедить сотрудника техподдержки что-то сделать что нужно взломщику.
> Я особо не шарю, но вроде и обычные массивы тоже можно создавать в таком стиле - $array = new ArrayObject;
Нет, это не массив. Ты например не можешь передать его в функцию sort().
>>31213
ты что-то путаешь. Речь шла не о хранении записей в базе данных или в файле, а в памяти в массиве. И ограничивать его длину - значит проверять размер и при превышении отрезать лишние элементы.
>>31223
Чем джанго лучше? У нас ты берешь любой класс и добавляешь аннотации, а там обязан наследоваться и создавать поля нестандартным образом.
>>31255
Не совсем. Вот смотри:
> if ($number < 100) {
> $number = $spelling[($number % 100) - ($number % 10)].$spelling[$number % 10];
Если число меньше 100 то у него может и не быть единиц, например в числе "80"
Исправлять их костылями через регулярки неправильно, надо изначально туда ноль не добавлять.
> if (($a < 5) and ($a != 1) and ($a != 0)) {
> return "$textNumber ($number) рубля";
Это надо вынести в функицю, иначе тебе придется копипастить ифы и для тысяч и миллионов.
Ну и ошибку исправить:
> PHP Warning: Missing argument 2 for smallNumberToText(), called in /home/15mtSB/prog.php on line 66 and defined in /home/15mtSB/prog.php on line 18
>>31308
Можно
>>31604
Ты не можешь предугадать как уязвимостью воспользуются. Например с помощью XSS и социнженерии можно убедить сотрудника техподдержки что-то сделать что нужно взломщику.
Есть модальное окошко-элемент с бутстрапа, хочу изменить в нем цвет тени вокруг элемента и никак не могу врубиться как это запилить, вроде и написано что через модал контент, но конкретный идентификатор элемента не представлен.
>Это годится только для самых простых случаев.
Из этих соображений и исходил. Насчёт регулярок в роутере и бесконечное число URL. Разбивать не explode(), а регулярками? Но в таком случае удобнее воспользоваться parse_url(). Не понял.
Разбор URL и вызов соответствующих контроллеров/экшенов, получение конфига для доступа к БД пока вынес в класс App. Теперь понимаю, что это плохая идея, так как слишком много обязанностей различного характера на один класс.
И ещё, вот у меня есть StudentController c экшенами all(), one($id), create(). Модель студента содержит только свойства. За поиск студента по id, возврат всех студентов из базы отвечает StudentMapper. Конструкции на пикрелейтед допустимы? Просто я во фреймворках использовал схему обратиться к модели -> сохранить в переменную -> переменную направить во view. Почему DataMapper, а не, к примеру, наследование модели Student от класса Model, которая соединяется с БД, предоставляет методы для поиска/сохранения? Отнаследуется уйма ненужных методов?
Почему я не могу произвести вычитание в конструкции echo если используется конкатенация строк?
А девтулс в браузере для кого?
Разобрался, причина оказалась в приоритетах операторов.
Тот случай, когда прошёл урок по ООП, решил несколько задач (кроме кошек-мышек), а ОП переделал урок и теперь надо заново его проходить.
>Почему DataMapper, а не, к примеру, наследование модели Student от класса Model
Вот мне тут не понятно немного, разъясните за эти мапперы. Почему класс студента называют моделью, если он не работает с бд? Как в мвс приложениях с этими датамапперами работать? Это ведь то самое ОРМ?
Увы, появившаяся в последнее время мода делать тонкие или невидимые полоски прокрутки (взятые с тач интерфейсов) лишает возможности оценить объем документа.
Полоски убирают из стремления к минимализму чтобы вместо них добавить бесполезный счетчик слов.
>>34054
> Почему класс студента называют моделью, если он не работает с бд?
Потому что модель студента (domain model, модель предметной области) - это объект соответствующий реальному студенту и описывающий его свойства. Почему он обязан работать с БД? Если ты где-то прочел "модель это то что работает с БД" то это неправильтно. Наверно там имели в виду Model из MVC которая отвечает за хранение данных в приложении, и включает в себя и модель студента, и маппер для сохранения ее в БД.
> Как в мвс приложениях с этими датамапперами работать? Это ведь то самое ОРМ?
да, разновидность ОРМ. Ты читал мой урок про паттерны работы с БД? active record, data mapper , table data gateway?
А, там модель из орм, а тут модель из мвс. Разные вещи с одинаковым названием. Пока понятно, спасибо.
>Ты читал мой урок про паттерны работы с БД?
Ага, раз десять. Еще разок почитаю.
>В мое время оценить объем статьи можно было по размеру двигающегося прямоугольника (забыл как он наызвается) на полосе прокрутки
Бегунок или движок. Стрелочки по бокам - кнопки прокрутки.
Сейчас на многих сайтах реализована бесконечная полоса прокрутки - перемещаешься в них экрана - а там подгружается новый контент. Так на любых социальных сетях, так даже на этой имиджборде, когда находишься в любом разделе и скроллишь в низ страницы - подгружаются новые треды.
Но, по сути, раньше это и было минимализмом - оценивал документ по этому бегунку на полосе прокрутки, а сейчас - увы - не всегда это возможно.
*в низ экрана
Раньше это было минимализмом - как структурированные сайты без CSS.
А сейчас - кроме как таким вот счётчиком - сложно оценить объём контента.
А когда под статьёй появляется добрая сотня комментариев и она раздвигается - тем более.
Так что, в целом, указание на примерный объём статьи - неплохая опора, ящитаю, возьму это на заметку. Встречал такое только на разных обучающих сайтах: Лингвалео, Гикбрейнс и т.п.
Модель в ORM не обязана взаимодействовать с базой данных.
Это ты наверное зашкварился об YII с ее AR, поэтому сложился такой стереотип, что модель это типа хрень работающая с базой данных.
То что идет в Yii это вообще не ORM, они это так назвали чисто в рекламных целях.
Я ток вкатываюсь.
И если не модель, то как это все называется? Как мне папку назвать со всеми этими мапперами и гейтвеями?
>стало модно добавлять счетчик времени на чтение
Можно пример какой-нибудь посмотреть как это сделано?
Ну почему? Как я понимаю AR как раз одна из возможных реализаций ORM
>>34257
Не надо делать папки Controller, View, Model вообще. Разделение в MVC условное. Просто сделай все на 1 уровне:
Controller
Model
Mapper
...
>>34268
Считается число букв, слов или слогов (например по гласным) и делится на экспериментально подобранное число.
>>34285
нет, только методы. По идее свойство переопределять особого смысла нет так как там можно поменять разве что значение по умолчанию, а вот в случае метода мы меняем логику работы.
Если тебе надо "абстрактное свойство" то лучше сделать абстрактный метод, возвращающий нужное значение.
>Считается число букв, слов или слогов (например по гласным) и делится на экспериментально подобранное число.
А где посмотреть то реализацию можно? И не проще считать умножая среднее время чтения на количество слов?
на медиуме например https://uncouthuncouth.com/when-is-it-okay-to-call-a-woman-a-whore-2a3dc9970cbb?source=reading_list---------5-3&gi=c6f33a38c508
или у Варламова
Не увидел ничего подобного у Варламова.
Ну а на первом блоге если бы была куча комментариев, то указанное примерное время помогло бы сразу сориентироваться, не глядя на полосу прокрутки, которая вводила бы в заблуждение относительно реального объёма статьи.
Ну вы же разрушите мой манямирок сразу, я вас знаю.
Спасибо, я уже забыл про эту фичу.
Говно.
Вот написал для сравнения: http://ideone.com/gW2O0v
Алсо вылезают +1/-1 из-за уродской нумерации начиная с 1.
Покажи пример процедурного кода.
Какая же пыха говнище из-за знака "$" .
Пока писал забывал его через раз, выбесило к хуям.
АЖТРИСЕТ
Ребята. Пишу сайтик на ПХП. В процедурном стиле. Есть файл индекс.пхп с которого производятся практически все манимуляции с сессиями и тд. Появилась потребность выполнить JS скрипт на стороне сервера.
Поясните пожалуйста, как это сделать ?
Я не так давно вкатился, освоил поверхностно html и css (сейчас прохожу курсы на htmlacademy.ru для закрепления), но в какую сторону двигаться дальше?
Цель: начать фрилансить.
Я для себя наметил несколько направлений, но уже, если честно, сломал всю голову, поэтому спрашиваю у вас, не ругайтесь только, в какой последовательности лучше изучать?
То, что собираюсь выучить: JavaScript, PHP, MySQL, WP и Joomla. Хочу поскорее брать заказики на weblancer.net и остальное доучивать уже на практике, так я гораздо лучше усваиваю. А уже через полгода-год подтяну ангельский и перекачусь на upwork. Помогите лишь определиться с последовательностью? Я у кого ни спрошу, кто советует с КМСок начать, кто с ЖС, кто с пыха.
PHP c основами MySQL -> какой-нибудь популярный php-фреймворк (на yii2 много заказов и работы) -> JavaScript.
>WP и Joomla
Забудь про всё это говнище. Там как бы учить нечего, если знаешь php и ооп на примере хорошего фреймворка.
Если ты хочешь быть полезным, сделай что-нибудь что интересно и нужно тебе самому, что может пригодиться потом и другим. Зачем писать CMS? алсо, это оффтопик получается, с этим наверно лучше в другой тред.
Я сейчас опять сижу на очень странной стадии изучения программирования. Знаю 17 управляющих конструкций, 8 типов, понимаю зачем нужен try-catch, немношко могу пояснить за историю ПХП, с английским особо не дружу, так что гугл для меня не особо полезен. Мне интересно писать код чтобы изучать код, я хочу задачи решать (хотелось бы выйти на уровень богоподобных олимпиадников), и так много их придумываю что жизни не хватит все написать. Если брать что-то сложное то смысла нет, поэтому беру простое и думаю над числом вариаций построения велосипедов. На ПХП можно написать все что угодно, но почему то даже для нужных вещей отсутствует документация, опять отгоняю от себя мысли залогиниться в группу документации и описать хоть одно расширение. Хочется все таки быть опытным. Верю что все впереди.
Извиняюсь. Это фича браузера оказалась.
[code lang="php"]
$this->view('index', [
'students' => $allStudents,
'pageTitle' => 'Студенты'
]);
[/code]
или
[code lang="php"]
$this->view->Page('index')->with([
'students' => $allStudents,
])->pageTitle('Студенты')
->render();
[/code]
2. https://designpatternsphp.readthedocs.io/en/latest/Structural/DataMapper/README.html
Почему в мане по паттерну DataMapper не используют __set() и __get()? Из-за того, что объясняется суть паттерна, а магия/сахар PHP не уместны? Или использовать __get() и __set() плохо? Мне магические геттеры/сеттеры помогли код сократить.
3. Нужно ли юзать DI-контейнеры? В процессе написания студентов понял, что мне не хватает глобальных переменных, так как первый раз пишу многостраничное приложение. Мне из контроллера нужно иметь доступ к объекту StudentMapper. Этот объект создаётся с передачей конфига для PDO. Инстанциирование StudentMapper(PDO $pdo) в контроллере выглядит уродливо, поэтому я в конструкторе контроллера получаю уже готовый объект StudentMapper, как мне кажется, так менее уродливей. Но не передавать же мне всё через конструктор? Или передавать?
Значение этого атрибута будет одинаково во всех инстансах класса в каждом из запущенных скриптов например, один скрипт запустили дважды.
Или только для всех инстансов класса внутри одного запущенного скрипта?
Дополнение к пункту 1. В первом случае кода меньше, но смешиваются переменные. Первый случай под капотом просто require'ит путь APP_ROOT . "/views/" . первый_параметр . ".php"
Для второго случая нужно будет создавать класс и объект view.
Для того, чтобы листинги были более понятны, нужно поставить: https://github.com/ololoepepe/MakabaCode/
Пилю веб-приложуху. В данный момент идёт реализация системы лобби. тоесть есть по факту таблица, каждая строка которой - это отдельное лобби. Как на пике.
По нажатию на кнопку "создать лобби", появляется форма, заполняя которую создаётся лобби в этой таблице. Я прилепил на JS(jQuery) событие, что мол при заполнении полей и нажатии на кнопку "создать", в таблицу добавляется строка, с введёнными данными, но вот беда, она появляется до перезагрузки страницы. Мне же нужно, чтобы пункт в таблице вмонтировался в страницу, чтобы его видели другие пользователи и чтобы оно не пропадало после перезагрузки ...
В JS треде сказали выполнить скрипт на стороне сервера, но я уже по-ходу понял, что они имели Ноду ... А мне на ПХП надо.
Точнее добавляемый через jQuery элемент существует только на компьютере создавшего пользователя, да и то, пока тот не перезагрузит страницу. Мне же нужно, чтобы оно встроилось в страницу и было видно всем пользоателям.
Здесь есть тред идей. Зайди туда и выбирай что понравится.
Отправляешь форму на сервер. Чтобы страница не перегружалась, используй ajax
В задаче про Вектор и департаменты.
Если мы создаем абстрактный класс Профессия, в нем указываем свойства
ставка = 100;
потребление кофе = 10;
производительность = 20;
И наследуем от него классы профессии (Менеджер, Инженер и тд.), в каждом из которых переопределяем эти свойства.
Но что если, другой программист создаст новую профессию, как быть уверенным, что он переопределит значения по умолчанию?
Тебе же ответили выше про веб-сокеты (это же ты был?).
Можно ещё long-polling попробовать, но, я так понимаю, там задержки больше будут.
Братишки, а чем вы код в текстовых редакторах (не IDE) дополняете?
Ты имеешь в виду класс Сотрудник? Или помимо него ещё и класс Профессия?
Можно сделать абстрактный класс Сотрудник, а от него наследовать уже любую профессию. Ну или как ты хочешь.
Если другой программист добавляет другую профессию, то он по-любому наследует её от Сотрудника (или Профессии - в твоём случае). Он же должен посмотреть на те свойства, которые идут по умолчанию в них, естественно, что они их переопределит в соответствии с новой профессией.
>>35085
Плагины имеешь в виду или что?
>>35087
Интересная задача, но, наверное, это нереально без огромного списка исключений.
Хотя надо бы прикинуть.
А что на входе - начальная форма в Именительном падеже уже дана? "Синяя ночь", "синий день"? Довольно интересная же задачка, действительно. Уровня чисел прописью, а может даже выше.
>Плагины имеешь в виду или что?
Движки анализа/дополнения кода, что-то вроде Tern, Racer, Jedi, но если нет ничего подобного, то хоть что-то. Допустим, пользую я свой любимый Vim, какие у меня есть опции, кроме https://github.com/shawncplus/phpcomplete.vim, что мне YCM / Neocomplete скормить можно?
Зачем тебе его экранировать?
>любимый Vim
Его лучше любить, сидя на IDE. С Vim режимом. Для PHPStorm можно даже создать файл .ideavim и добавлять туда простейшие команды вроде map jj <Esc>
А так могу посоветовать Practical Vim и Practical Tmux заодно, всё из серии The Pragmatic Bookshelf. Там рассказано как с помощью ctags писать самому всякие find by usage и autocomplete плагины.
>Его лучше любить, сидя на IDE.
Нет, в Vim мне комфортно, осталось только понять, можно ли сделать дополнение чуточку интеллектуальнее.
>Это нормально, если я передаю в метод аргументом метод из другого класса? Хочу проверять существование мыла в классе-валидаторе.
>Это нормально, если я передаю в метод аргументом метод из другого класса?
Вполне нормально, если такие методы ты делаешь универсальными. Универсальными - в том смысле чтобы можно было использовать где-нибудь еще, а не строить новый велосипед.
SELECT DISTINCT filters.filter_id FROM (SELECT ta.filter_id, pa.product_id from filters_to_products ta JOIN all_products pa ON (pa.product_id = ta.product_id) JOIN store_depot_items sb ON (pa.product_id = sb.product_id) JOIN store_depots sdepot ON (sb.depot_id = sdepot.depot_id) WHERE sb.stock = 1 AND sdepot.store_id = 136 AND ta.tag_id = 25 AND ta.tag_id = 3) filters
Проблема в том, что запрос занимает по 4 секунды, поскольку продуктов около миллиона, а пользователь не готов столько ждать обновления фильтров. Можно делать все это как-то быстрее? В каком направлении копать? Сейчас я делаю этот запрос к SQL асинхронно из аякса, но фильтры все равно долго обновляются по нескольку секунд, заказчиков не устраивает. Видел шопы, где это мгновенно происходит, как такое делается?
SELECT DISTINCT filters.filter_id FROM (SELECT ta.filter_id, pa.product_id from filters_to_products ta JOIN all_products pa ON (pa.product_id = ta.product_id) JOIN store_depot_items sb ON (pa.product_id = sb.product_id) JOIN store_depots sdepot ON (sb.depot_id = sdepot.depot_id) WHERE sb.stock = 1 AND sdepot.store_id = 136 AND ta.tag_id = 25 AND ta.tag_id = 3) filters
Проблема в том, что запрос занимает по 4 секунды, поскольку продуктов около миллиона, а пользователь не готов столько ждать обновления фильтров. Можно делать все это как-то быстрее? В каком направлении копать? Сейчас я делаю этот запрос к SQL асинхронно из аякса, но фильтры все равно долго обновляются по нескольку секунд, заказчиков не устраивает. Видел шопы, где это мгновенно происходит, как такое делается?
>как такое делается?
Оптимизацией БД (нормализация, индексы) и вменяемыми запросами, попробуй учебник какой-нибудь почитать на эту тему.
Ничем не дополняю. Фреймворк которым пользуюсь и тем более свой код я знаю наизусть, набираю быстро.
Индексы есть, таблицы нормализованные. Думал, может кто делал уже такое и есть какие-то готовые рецепты, как правильно ненужные фильтры убирать, используя список отфильтрованных продуктов.
Почему бы не сделать просто SELECT * FROM products WHERE filter_param = filter_value AND ... ?
Чет не получается блин.
У меня есть класс валидации, у него есть метод, который принимает функцию и через нее прогоняет значение. Хочу передать в этот метод метод существующего экземпляра класса, не статического. Такое возможно вообще? Может pimple как-то умеет?
>У меня есть класс валидации, у него есть метод, который принимает функцию и через нее прогоняет значение.
Ты наркоман?
Какой фреймворк? Не хочешь ли перейти на какой-нибудь другой по каким-нибудь причинам или прям всем тебя устраивает?
Для себя или заказчиков?
В products нет фильтров, там только продукты, они в filters_to_products. Каждому продукту соответствует несколько фильтров, например так:
product_id tag_id
34 7
34 136
34 80
Плюс продукты нужно искать, какие только в наличии в конкретном магазине, а это джойнить еще 2 таблицы со складами и магазинами.
Нормальный прием. Двойная деспетчеризация, все дела, но зачем она ему не совсем ясно.
Также вопрос - ускорится ли все это дело, если вместо джойнов использовать массивы в PHP? Если айдишники продуктов под фильтры например собрать в PHP уже обычным селектом, и передавать их оттуда в выборку новых фильтров через WHERE filters.product_id IN (id1, id2, id3 ...)
Проверить пришедшее с формы мыло на существование. Передавать маппер в конструктор ради одной проверки, чет как из пушки по воробьям. Наверное.
Продукт имеет свойства.
Фильтр - набор свойств и их значений. Можно, например, его передавать в uri как ?param1=value1¶m2=value2&...
Зачем тут еще что-то, кроме WHERE param1=value1 AND ... ?
Ну а что касается необходимых джоинов для денормализации - без них никак. Поможет кэширование запросов. Но нужно учитывать объем памяти.
Фильтры отображать надо на сайте же, а чтобы фильтры отображать нужно сначала выбрать доступные фильтры для отфильтрованных продуктов. С одним where ничего не получится. Кэш невозможен, поскольку фильтры каждый раз разные, а продукты обновляются. С секунды на секунду красная рубашка Adidas для девочек может закончиться в магазе (stock=0 станет), а с ней и сразу пара фильтров накроется.
>>Интересная задача, но, наверное, это нереально без огромного списка исключений.
Хотя надо бы прикинуть.
А что на входе - начальная форма в Именительном падеже уже дана? "Синяя ночь", "синий день"? Довольно интересная же задачка, действительно. Уровня чисел прописью, а может даже выше.
Есть к примеру категории: Женская/Мужская и есть товар: блузка, пальто, футболки
Итого получим на выходе:
Женская блузка, женское пальто, женские футболки
и Мужское пальто, мужские футболки и тд.
Я откопал какие то словари http://phpmorphy.sourceforge.net/dokuwiki/download и библиотеку, но не совсем понял как именно мне поступать с существительным (как разбивать на корень, префиксы и суфиксы + окончания) чтобы потом искать это в словаре и определять по нему падеж, чтобы потом просклонять прилагательное относительно падежа существительного по тому же словарю. К сожалению документации к самому словарю я не нашел
Ты не понимаешь, что такое фильтр. Это просто параметры и значения: color=blue, size=34.
Показываешь все фильтры, пользователь выбирает нужные и делает запрос.
А запрос к бд получается:
SELECT FROM
(SELECT FROM all_products WHERE color=blue AND size=34) p # этот подзапрос всегда в кэше
INNER JOIN {тут твои таблицы с наличием на складе} ...
WHERE stock = 1;
Весь запрос тоже будет закэширован до изменения таблиц с наличием.
То есть ты получишь выборку товаров, которые удовлетворяют условиям и есть в наличии. А скрыть бренды, у которых 0 продуктов в этой выборке, легко на самом клиенте.
Как такое кэширование делать? SQL сам умеет? Или memcached какой-то подключать? В кэшировании я вообще плаваю, у нас его нет.
>>35297
Скрыть бренды на клиенте не получится. Смотри - клиент получает только 200 товаров, но по фильтрам там доступно еще тысяч 20-500, которые подгружаются если клиент дальше жмет. Если ты будешь на клиенте скрывать по имеющимся товарам, то он у тебя скроет все, что в эти 200 не входит, а в большей выборке может быть больше фильтров доступно.
Здесь дальше - имеется в виду, что клиент жмет "показать следующую страницу", чтобы ему больше товаров по фильтрам подгрузилось. Тогда следующий SQL запрос делается, чтобы больше товаров поулучить. А вот ненужные фильтры нужно отключить уже на самом первом.
https://designpatternsphp.readthedocs.io/en/latest/Structural/DataMapper/README.html
Я сначала не понял, зачем они в UserMapper.php запихали метод mapObject(array $row). Кратко, принцип работы с БД там такой: Мы делаем запрос к базе, получаем ответ в виде массива $row. Массив мапится на объект c помощью mapObject, оттуда возвращается объект класса User. А если бы я просто использовал ActiveRecord, то через FETCH_CLASS возвращал бы данные в виде нужного объекта. Почему в случае DataMapper нельзя воспользоваться FETCH_CLASS? Да потому, что конструктор модели User принимает 3 параметра, которые нужно ставить в null (см. содержимое ссылки). PDO фетчит данные в виде объекта, но когда этот объект создаётся, то все данные заполненные раннее PDO, теперь обращаются в null.
UPD: Нагуглил в PDO опцию FETCH_PROPS_LATE, всё работает: http://stackoverflow.com/a/2862563
А вот FETCH_INTO не работает, забавная ошибка: object must be an object.
В общем, я по-прежнему жалею, что не взялся сразу за ActiveRecord, столько бы времени сэкономил.
>>34963
>>33728
>>32988
-хуй
помогите пожалуйста разобраться, почему мой скрипт http://pastebin.com/agNbgazH неправильно отображает мой ip-адрес, он должен быть 127.0.0.1, а этот код отображает его как "::1", объясните пожалуйста почему. Сам php-файл называется tuna.php, я запускаю его через Apache как localhost/tuna.php, и если я пишу echo "$ip", то мне выводится "::1", причем если запускать его как 127.0.0.1/tuna.php, то всё выводится верно.
> Он же должен посмотреть на те свойства, которые идут по умолчанию в них, естественно, что они их переопределит в соответствии с новой профессией.
Вообще. не факт. Если ты хочешь обязать класс-наследник определить какой-то параметр, надо использовать абстрактный метод (например getBaseSalary()) - PHP не позволит создать конкретный (не-абстрактный) класс без реализации всех абстрактных методов.
http://newstar.rinet.ru/~goga/sicp/sicp.pdf
Всякие предисловия которых там 20 страниц, можно проматывать и переходить к делу. Для того чтобы выполнять код из него, вам нужен интерпретатор Scheme (не бойтесь, это язык который учится за 5 минут), который можно либо использовать онлайн:
https://repl.it/languages/scheme
либо скачать себе на компьютер один из интепретаторов
http://stackoverflow.com/questions/2521477/what-is-the-best-scheme-interpreter-or-compiler
Просто прочел сегодня новость что в MIT перестали читать курс по этому учебнику, ну думаю, американцам он больше не нужен, а нам может еще пригодиться.
-----
И кстати на repl.it есть интепретатор и для PHP: https://repl.it/languages/php
Просто пишете слева определения функций, а справа - выражения, ну например
$x = 1;
function a () { return 100; }
a() * 2; // выведет 200
Только точку с запятой ставить не забывайте.
Правда там по моему периодически коннект к серверу отваливается при фатальной ошибке в коде.
http://newstar.rinet.ru/~goga/sicp/sicp.pdf
Всякие предисловия которых там 20 страниц, можно проматывать и переходить к делу. Для того чтобы выполнять код из него, вам нужен интерпретатор Scheme (не бойтесь, это язык который учится за 5 минут), который можно либо использовать онлайн:
https://repl.it/languages/scheme
либо скачать себе на компьютер один из интепретаторов
http://stackoverflow.com/questions/2521477/what-is-the-best-scheme-interpreter-or-compiler
Просто прочел сегодня новость что в MIT перестали читать курс по этому учебнику, ну думаю, американцам он больше не нужен, а нам может еще пригодиться.
-----
И кстати на repl.it есть интепретатор и для PHP: https://repl.it/languages/php
Просто пишете слева определения функций, а справа - выражения, ну например
$x = 1;
function a () { return 100; }
a() * 2; // выведет 200
Только точку с запятой ставить не забывайте.
Правда там по моему периодически коннект к серверу отваливается при фатальной ошибке в коде.
Понятно, что ты хочешь. Пиши тогда хранимую процедуру, которая:
1) Выбирает все отфильтрованные продукты во временную таблицу
2) Делает по ней селект с GROUP BY по брендам.
Это вернет таблицу брендов.
3) Делает по ней селект с LIMIT X, Y.
Это вернет страницу товаров.
Будет разы быстрее, чем есть сейчас.
Сейчас он выбирает сначала продукты, потом опять их же выбирает и делает по ним джоины. Плюс запрос не оптимизирован.
Сомневаюсь. Твое решение гораздо более громоздкое. Например ты выбираешь все товары в таблицу в то время как нам могут быть нужны только первые N. То есть ты выбираешь намного больше данных чем надо, да еще и таблицы создаешь. И зачем процедуры? Мы не в 80-х.
И вообще я сомневаюсь что тут оптимизация поможет, если индексы проставлены и все равно тормозит то значит надо искать другие пути (кеш, сторонний поисковый движок). Ну и конечно не нужны там подзапросы, лучше джойны.
Представляю смесь бугурта и любви к Apple у того музыканта.
>Сам он смог восстановить оригинальные файлы из резервной копии, но сочувствует тем, кто не делает бэкапа.
Всю коллекцию собственных работ, например, вот так взять - и потерять.
>Тотальный контроль Apple над файлами пользователя напоминает их известную рекламу «1984», которая воплотилась в жизнь в зеркальном отражении, в печальной и гнетущей иронии, пишет Пинкстоун.
Яблочник прозрел, спешите видеть! С самого начала было понятно, что это именно Apple всегда и последовательно выступает как Большой Брат - с самого начала нельзя было назвать их демократичными. И люди, которые были в теме в то время, выкладывали тогда кирпичи от того, что таким вот образом и стал работать Большой Брат.
Но вместо этого он сразу рисует мне див высотой в 1000, тип мне похуй что у тебя там цикл.
Как правильно возвращать нужное значение? Или мб есть какие-нибудь более изящные варианты?
>тип мне похуй что у тебя там цикл
Как раз таки оно и выполняет цикл до конца, и возвращает значение функции в height().
Не вижу причин использовать цикл, тебе нужно по клику увеличить "текущую" высоту на 10, не более.
Ты мог бы также использовать "замыкания"
Тебе стоит сделать упор на изучение самого яваскрипта, а не только изучать jQuery. без этого ничего не выйдет.
У тебя по-прежнему код странный так как 2 обработчика вложено друг в друга и ты их многократно навешиваешь. Не, тебе надо начать с правильного яваскрипта.
Если у тебя нет времени, то тебе стоит заняться чем-то другим. Ибо на исправление багов и ошибок ты потратишь в сумме гораздо больше времени и заказчики/работодатели вряд ли будут счастливы.
Моя ближайшая перспектива - верстка, а не весь фронтенд. Может, ты и считаешь целесообразным сидя дома без штанов въебать полгода на JS, чтоб потом хуярить инфографику пизженными плагинами, но вот я думаю немного иначе.
Спасибо, попробую с временными таблицами.
>>35738
Да, может быть медленее, я сейчас товары как раз поэтому ограничиваю до 300-500, потому что иначе много времени занимает, и клиент не готов столько ждать. Думаю, можно создать не временные, а постоянные таблицы для популярных фильтров, где будут лежать наборы доступных для них фильтров. И обновлять их по cronjobу, который запускать раз в пару минут. Будет правда проблема, что этот cronjob кучу времени занимает, комбинаций фильтров же много.
>Ну и конечно не нужны там подзапросы, лучше джойны.
Для выборки продуктов подзапросов нет, но она все равно тормозит на миллионе продуктов, там 6 joinов и group by. Возможно в этом направлении надо крутить, попробую избавиться от лишних joinов для cronjobа.
Так все товары как раз и нужны, чтобы не показывать отсутствующие бренды. Только те, которые в них найдутся. Походу ты не понял задачи.
>искать другие пути (кеш
По кешам посмотрел, что в Mysql есть встроенный Mysql Query Cache. Он для подзапросов действует? Для SELECT xxx FROM (SELECT xxx from xxx) будет ли действовать для второго SELECT? Или он только для joinов?
Да, нужны все товары, фильтров может быть много (штук 7 их всего). При выборе любой комбинации из 7 фильтров, нужно пройтись по всем фильтрам и отключить более недоступные фильтры в каждом из 7. А для этого нужно пройтись по всем товарам, которые попали под эти 7 фильтров. Чем больше фильтров включено, тем доступных товаров по ним меньше (а значит быстрее по этим товарам проходить). Самый долгий обход, если включен только 1 фильтр, тогда может быть до полумиллиона-миллиона товаров под него. Самый быстрый соответственно, если все 7 фильтров включены, там может быть и 3 товара всего.
А в учебники написано нужен Лисп
> Для описания процессов нам нужен подходящий язык, и с этой целью мы используем
язык программирования Лисп.
Или Scheme это интерпретатор Лиспа?
Также, от кеша, как я понял, мне немного толку, поскольку каждый раз, как будет обновляться таблица с наличием товаров в магазинах, он будет очищаться и соответственно все закешированные запросы надо заново делать. Таблица наличия товаров обновляется каждые пару секунд-минут, в зависимости от подвозов-покупок.
Ну и дичь...
Сколько всего ещё дополнительно нужно изучить после учебника ОПа, чтобы въехать во всё это? Там адова бездна, просто адова бездна.
Не знаю насчет иии(уайии?), но сейчас читаю доки по симфони, вроде все понятно, хотя это вроде как самый сложный фреймворк.
Вроде не так уж и много, все основное после учебника Опа ты уже будешь если не знать, то ориентироваться.
Алсо, ты весь учебник опа прошел? Вплоть до задачи с yii2?
>Слово Yii (произносится как Йи [ji:]) в китайском языке означает «простой и эволюционирующий». Также Yii может расшифровываться как акроним Yes It Is!
>сейчас читаю доки по симфони, вроде все понятно, хотя это вроде как самый сложный фреймворк.
Ну а ты Студентов и ФО сделал? Я пока только ООП осилил с трудом, но Кошек-мышек пока не трогал - хочу на выходных попробовать.
>>36074
>ты весь учебник опа прошел?
Только ООП заканчиваю. До этого сделал все задачи на HTML+CSS, ещё до смены последовательности уроков.
Я бы не хотел сейчас притрагиваться к Студентам и ФО: слишком много времени уйдёт на это, я думаю.
У меня в планах MySQL на следующей неделе, а до конца мая надо разобраться в Yii2 на базовом уровне, чтобы уметь подключать модули, виджеты и подобное - как минимум.
Какого хуя верстальщик должен учить 2 языка программирования, html и css?
Думаю выучить за 2 года html и получать 100 000 рублей в месяц (так написано в рекламе видеокурса, они же не будут врать в отличее от неудачников с двача).
Ну, надо сказать, что в SASS есть функции, переменные и циклы. Т.е. какой нибудь верстак, пишущий CSS на SASS, вполне себе программирует.
>размещение стилей в футере приведет к тому что может отобразиться старница без стилей на какое-то время. размещение скриптов в футере чревато тем, что при использовании <button onсlick="fn()"> до загрузки скрипта мы получаем неработающую кнопку.
Гугл ругает за стили/js в хедере, а сеодебилы за это переживают. В остальном все вродь поправил. Посмотри пожалуйста: github.com/fidnex/students
Целеустремленность - это великолепно, но с таким подходом ты действительно соберешь все возможные неровности на пути, испортишь себе репутацию и потеряешь массу времени.
Хорошо.
У меня есть месяц, чтоб довершить свои безумные умения в верстке. В день я уделяю в среднем 10-11 часов. кроме воскресенья и половины субботы
Остался JQ/JS, и я действительно чувствую, что я тону и насасываю, ибо привык брать памятью, а здесь эта хуйня не прокатывает. Разве что запоминать какие-то куски под нужную задачу.
Я могу уделить 10 дней качественному подходу к теории и 10 практике JS/JQ, но смогу ли я так получить больше, чем просто штурмуя всё подряд? Я не знаю.
Терять время я не могу.
>В день я уделяю в среднем 10-11 часов
Что-то мне слабо верится в это. С таким темпом работы учебник Ильи Кантора ты пройдешь за неделю.
К нам такая крутая московская сеоконтора тикеты бросала. Задачки были уровня выводить 404 страницу на все возможные страницы с гет параметрами
site.ru/ - главная страница
site.ru/?foo1 - 404
site.ru/?foo2=bar - 404
А то дубли страниц, ранжирование-хуирование плохое будет. Или опять же дублирующие страницы - товар лежит в site.ru/category1/tovar и в site.ru/category2/tovar. rel canonical ставить они не рекомендуют, потому нужно переписать кучу кода в тысяче местах и привести все ссылки в магазине к виду site.ru/product/tovar. И это крутая московская сео контора, которая выставляла нашему клиенту неебические ценники за такие вот аудиты. Как-то в общем не очень к ним отношение сложилось.
Учебник по яваскрипту у нас один - learn.javascript.ru. Если тебе хочется еще задачки, то они есть в ОП посте. Включая даже изучение MVC на примере игры в сапера.
>Задачки были уровня выводить 404 страницу на все возможные страницы с гет параметрами
Правильно делать редирект на каноническую страницу. 404 ошибка - это сигнал поисковикам, что куча битых ссылок, куча ошибок - будет сайт терять свои позиции и по другим запросам.
>нужно переписать кучу кода в тысяче местах и привести все ссылки в магазине к виду site.ru/product/tovar
Думаю, что-то надо мутить в .htaccess, чтобы редиректило любую шнягу именно на каноничный URL. Регуляркой или чем-то проверять URL, приводить к какому-то стандарту, а потом вести на каноникал именно через .htaccess.
Раздался пронзительный голос со стороны SEO
Вообще насчет GET параметров - неправильный совет. При переходе с некоторых рекламных сетей например в URL добавляются какие-то параметры. И у вас страница просто не откроется. Да и поисковики я думаю, не такие глупые и прекрасно знают что параметры это необязательная штука. Да и на всех сайтах эти параметры можно дописывать. Другой вопрос что у вас на сайте должны внутренние ссылки стоять нормальные, без параметров.
Эта проблема бывает на каких-то формуах, которым нужна сессия, и они при отстутвии кук делают ее через параметр в URL вида ?sessid=многобукв (что кстати еще и небезопасно, используйте только куки), и потом такие ссылки попадают в гугл.
А насчет дублирующих страниц вам все правильно сказали - Гуглобот будет тратить выделенное вам время на обход копий одной и той же страницы.
> потому нужно переписать кучу кода в тысяче местах
Это сеоконтора виновата что вы DRY нарушаете и ссылки как попало генерируете, да еще небось и с копипастой?
Не надо через htaccess.
- дублируется функционал роутера, с вероятностью близкой к 100% будут баги
- при переносе на нгинкс придется руками все правила переписывать
Не учи людей делать их работу, особенно если ты в ней понимаешь ничего - делай хорошо свою.
>есть месяц
Это заранее провальное мероприятие, ты даже простенький макет вряд ли сверстать сможешь через месяц, не говоря уже о хоть каком-то программировании.
Мне кажется гет параметры редиректить это совсем маразм
>>36216
>Это сеоконтора виновата что вы DRY нарушаете и ссылки как попало генерируете, да еще небось и с копипастой?
Это ок, но чем им rel canonical не устроил? Магазину много лет, тысячи товаров, тысячи ссылок на них по всему интернету, а им вот приперлось вид ссылок поменять.
>>36218
Извини, не хотел никого обидеть.
>> А разве ключом не должно быть название поля? Как тогда обозначить ключ ошибки?
>действительно. Ну значит массив должен быть какой-то другой структуры.
А можно подсказку какой?
Напомню: Нужно сделать для сервиса валидации массив который содержит в себе белый список имен полей и функцию валидации к ним. Проблема заключается в том что неизвестно как передавать аргументы в функцию валидации. Очевидным решением мне показалось передавать их двумерным массивом, но функция call_user_func принимает аргументы только по отдельности - а что если для функции валидации нужно будет передать несколько аргументов?
$validations = [
'name' = [
'validator' => 'validateName',
'parameters' => ['getName']
],
...
]
foreach($validations as $field => $validator) {
...
$error[$field] = call_user_func($this, $validator['validator'], $validator['parameters']);
...
}
https://ideone.com/9cuKjL
HTML/CSS уже относительно пройдены LESS не затрагивал пока+не пробовал резиновую верстку, только адаптивную.
>>36207
>>36169
Ну не верь. Кстати, спасибо за подкинутый учебник может и его возьму, если пойдет
>>36207
Да, начал читать, но как-то не особо легко частям заходит. Нахуя мне в самом начале знать регулярные выражения, если в большинстве случаев всё ограничивается их копипастом?
Но
> он один
Поэтому делать нечего.
Спасибо.
>Сколько всего ещё дополнительно нужно изучить после учебника ОПа, чтобы въехать во всё это?
Скажу по своему опыту: на фреймворках можно писать, вообще не думая. Эти избыточные абстракции и созданы для того, чтобы можно было при минимальном понимании ООП и MVC быстро воплотить задуманное в жизнь. Та же задачка на студентов требует какого-никакого проектирования. Пишу 3-й день подряд и не знаю, сколько ещё буду писать, так как каждый день обнаруживаю огромные пробелы в знаниях, а вот на фреймворке я её меньше, чем за час написал. Так что будь уверен, после студентов и файлообменника никакие монструозные фреймворки вроде Yii тебе не будут страшны.
>Мне кажется отношение между формой и студентом правильнее было бы реализовать через композицию.
А как пользоваться композицией в нашем случае?
Вот в Студенте у нас есть поля которые есть в Форме, и в Форме есть поля которых нету в Студенте. Далее, мы хотим чтобы эти поля заполнялись все сразу с помощью одной функции/проходили валидацию/итд, все этих методы используют цикл, но мы не можем заставить цикл проходиться по двум объектам по очереди, тем более в классе Студента есть лишние поля связные с хэшем. Нужно опять составлять массив со свойствами которые нам нужны?
<div style="left : 0 px"> Ффф </div>
<script>
var ch=document.getElementsByTagName('div')[0];
ch.style.left="100px";
</script>
Вроде position: absolute нужен. Заливай в следующий раз на jsfiddle.net, аноны охотнее помогут.
Адаптивную верстку нельзя сделать без умения верстать "резиново". Ну и LESS ты мог бы вообще не упоминать - это просто препроцессор который может за несколько часов осваивается.
В моем понимании, если не уметь верстать "резиново" то это значит что человек в принципе верстать не научился.
Кстати, в ОП посте есть задания на HTML.
Если ты серьезно готов потратить месяц, каждый день уделяя много времени учебе, то вполне реально и HTML подтянуть и JS подучить и наверно немного DOM успеть изучить с jQuery. Задачи по всем этим темам, если что - в ОП посте.
>>36406
Композиция это когда один объект включает в себя другой. Тут по моему вариантов особых нет: в форме должно быть поле student в котором хранится студент.
> , но мы не можем заставить цикл проходиться по двум объектам по очереди
Сделай 2 цикла. Или сделай универсальные методы вроде get($fieldname) которые понимают по имени поля, откуда его надо брать.
Это уже мелочи реализации. Тут главный принципиальный вопрос - должны ли мы хранить в форме студента или должны дублировать все его поля. Что удобнее, какие есть недостатки и преимущества. Как я уже написал, в фреймворках вроед Симфони у формы своя отдельная копия данных, но у нас простое приложение и нам наверно проще будет редактировать данные прямо в студенте чем делать копию. Хотя, кто знает.
>>36391
Можно писать не думая только пока ты делаешь задачи аналогичные тем что в туториале. Как только надо сделать что-то нестандартное, если ты не понимаешь архитектуру фреймворка, ты будешь лепить костыли. Да и ты говоришь что просто, но мы не видели твой код и не знаем насколько он правильный.
>>Ну значит массив должен быть какой-то другой структуры.
> А можно подсказку какой?
Давай поставим вопрос по другому: а какие тебе данные нужны для валидации одного поля? (например, имя поля, имя функции-валидатора, еще что-то?) Такие и надо класть в массив.
> Проблема заключается в том что неизвестно как передавать аргументы в функцию валидации.
Несколько аргументов можно передать через call_user_func_array. Она принимает массив аргументов.
Но я не очень понимаю, зачем это. Не проще ли сделать стандартизованный вид для функции валидации, чтобы они все принимали один и тот же набор аргументов?
Старайся не усложнять код без необходимости. Вот давай для начала попробуем сформулировать задачу:
- есть объект с N полями (или N методами-геттерами), и N функций для валидации этих полей. На выходе мы хотели бы получить массив или объект с ошибками.
Как должна выглядеть функия валидации? Что ей нужно? Ну как минимум ей нужно дать значение поля. Надо ли что-то еще? не знаю. Что она должна вернуть? Как минимум сообщение об ошибке или признак что все ок. В идеале это вообще хорошо бы возвращать как 2 переменных но можно и одной.
Глядя на твой код, я например, не понимаю, зачем ты передаешь в функцию валидации 'getName'. Зачем ей имя геттера? Оно как-то помогает проверить правильность заполнения поля?
Дальше, как мы будем хранить информацию об ошибках? Вообще, тут в принципе для этого можно даже объект задействовать. А можно и массивом обойтись.
Попробуй начать с таких рассуждений и определить как должны выглядеть эти функции, что они получают, что они возвращают, как хранить ошибки.
> но чем им rel canonical не устроил?
ну как минимум робот будет тратить заходы на индексацию одной и той же страницы. Все же вместо canonical лучше ставить 301.
Я думаю вы сами виноваты что не следовали правилам написания хорошего кода и то что его тяжело переделывать это прямое следствие вашего подхода.
Ну и вы всегда можете поменять СЕО контору или даже заниматься оптимизацией сами.
>>36212
редиректить надо на уровне приложения.
>>36157
Увы, не знаю, погугли. Если кратко то O-нотация показывает как время выполнения или например объем потребляемой памяти увеличиваются при увеличении числа данных.
O(1) - алогритм работает одинаковое время независимо от объекма данных
O(N) - пропорционально объему данных, если данных в 10 раз больше то и работать будет в 10 раз дольше
O(log2(N)) - пропорционально логарифму, в 10 раз больше данных, примерно раза в 3 дольше работать, в 1000 раз больше - примерно в 10 раз дольше (не в 27, а в 10)
O(N^2) - пропорционально квадрату объема данных, в 10 раз больше данных - в 100 раз дольше их обрабатывать
и тд.
O(2N) это то же самое что O(N) и потому так не пишут.
Эта нотация показывает только относительный рост, а не абсолютные числа. Нельзя сказать, какой алгоритм быстрее при N = 100: тот что O(1) или O(N)
> но чем им rel canonical не устроил?
ну как минимум робот будет тратить заходы на индексацию одной и той же страницы. Все же вместо canonical лучше ставить 301.
Я думаю вы сами виноваты что не следовали правилам написания хорошего кода и то что его тяжело переделывать это прямое следствие вашего подхода.
Ну и вы всегда можете поменять СЕО контору или даже заниматься оптимизацией сами.
>>36212
редиректить надо на уровне приложения.
>>36157
Увы, не знаю, погугли. Если кратко то O-нотация показывает как время выполнения или например объем потребляемой памяти увеличиваются при увеличении числа данных.
O(1) - алогритм работает одинаковое время независимо от объекма данных
O(N) - пропорционально объему данных, если данных в 10 раз больше то и работать будет в 10 раз дольше
O(log2(N)) - пропорционально логарифму, в 10 раз больше данных, примерно раза в 3 дольше работать, в 1000 раз больше - примерно в 10 раз дольше (не в 27, а в 10)
O(N^2) - пропорционально квадрату объема данных, в 10 раз больше данных - в 100 раз дольше их обрабатывать
и тд.
O(2N) это то же самое что O(N) и потому так не пишут.
Эта нотация показывает только относительный рост, а не абсолютные числа. Нельзя сказать, какой алгоритм быстрее при N = 100: тот что O(1) или O(N)
Ты проскакиваешь темы . Между ООП и фреймворком Юи очень много тем, которые надо изучить - архитектура веб-приложений, DI, MVC, работа с БД, формами - это все изучается в студентах.
>>36017
Тут можно конечно оптимизировать базу, но скорее всего нужен поисковый движок с фасетным поиском для этой задачи. Базы данных для такого плохо подходят.
Как полумера можно в части случаев обойтись заранее сгенерированным кешем. Ну к примеру, у нас есть кеш-таблица вида:
id рубрики - список брендов в ней
id рубрики - список цветов товаров в ней
+ комбинации из 2 значений каждого фильтра
В редисе например есть удобный тип hash для этого
Как альтернатива, можно сделать кеш-таблицы с кучей индексов в отдельной БД (чтобы если что перенести ее на отдельный сервер и не грузить основную). Кеш-таблицы такого вида:
рубрика, бренд, цвет
Такая таблица позволяет ответить на вопросы:
- какие бренды есть в рубрике
- какие цвета есть в рубрике
- какие цвета есть в рубрике + бренде
Имей в виду что для эффективной выборки нужно несколько индексов.
Кеш поможет отобразить допустимые значения фильтров для тех случаев когда товаров много и искать их через баз долго. А для случаев когда выбрано уже 3-4 фильтра, и товаров которые им соответствуют, мало, можно считать знаения напримую в БД.
Имей в виду, это общие рассуждения, так как у тебя не приведено достаточно данных: какие есть измерения для поиска, какие их сочетания возможны и какие нет.
Если ты приведешь больше данных, может быть какой-нибудь анон захочет поразбираться и предложит что-то оптимальное.
База не будет быстро работать когда у тебя есть связи вида много-ко-многим (то есть у товара может быть несколько рубрик и несколько цветов) и надо искать по их сочетаниям. Если связи вида один-ко-многим (товар входит только в одну рубрику и имеет только один цвет) тут еще что-то можно сделать составлными индексами по парам критериев, но вставки и обновления такой таблицы могут быть более тяжелыми.
И опять же если у тебя например дана рубрика, ей соответствует 500 000 товаров и ты хочешь найти все бренды, цвета этих товаров то это тоже быстро никак работать не может так как базе придется обходить всю эту кучу товаров.
А и еще. Все это я пишу исходя из того что у вас оптимизированы настройки БД (в общем, используемые индексы должны целиком в нее помещаться + быть место для горячих данных) и расставлены правильно индексы. Если вы даже этого не смогли правильно сделать - это задача не вашего уровня, наймите профессионала.
БД вообще не оптимизированы под такой сценарий. Нужен либо специальный движок для фасетного поиска либо лепить временные костыли на кеше или денормализованных данных, которые помогут на какое-то время, пока у вас не станет больше товаров. В принципе, решения для таких случаев известны, велосипеды тут придумывать не требуется.
Сейчас даже в сфинксе есть что-то для фасетного поиска, но он по моему для этого не идеально подходит. Надо тестировать.
Ты проскакиваешь темы . Между ООП и фреймворком Юи очень много тем, которые надо изучить - архитектура веб-приложений, DI, MVC, работа с БД, формами - это все изучается в студентах.
>>36017
Тут можно конечно оптимизировать базу, но скорее всего нужен поисковый движок с фасетным поиском для этой задачи. Базы данных для такого плохо подходят.
Как полумера можно в части случаев обойтись заранее сгенерированным кешем. Ну к примеру, у нас есть кеш-таблица вида:
id рубрики - список брендов в ней
id рубрики - список цветов товаров в ней
+ комбинации из 2 значений каждого фильтра
В редисе например есть удобный тип hash для этого
Как альтернатива, можно сделать кеш-таблицы с кучей индексов в отдельной БД (чтобы если что перенести ее на отдельный сервер и не грузить основную). Кеш-таблицы такого вида:
рубрика, бренд, цвет
Такая таблица позволяет ответить на вопросы:
- какие бренды есть в рубрике
- какие цвета есть в рубрике
- какие цвета есть в рубрике + бренде
Имей в виду что для эффективной выборки нужно несколько индексов.
Кеш поможет отобразить допустимые значения фильтров для тех случаев когда товаров много и искать их через баз долго. А для случаев когда выбрано уже 3-4 фильтра, и товаров которые им соответствуют, мало, можно считать знаения напримую в БД.
Имей в виду, это общие рассуждения, так как у тебя не приведено достаточно данных: какие есть измерения для поиска, какие их сочетания возможны и какие нет.
Если ты приведешь больше данных, может быть какой-нибудь анон захочет поразбираться и предложит что-то оптимальное.
База не будет быстро работать когда у тебя есть связи вида много-ко-многим (то есть у товара может быть несколько рубрик и несколько цветов) и надо искать по их сочетаниям. Если связи вида один-ко-многим (товар входит только в одну рубрику и имеет только один цвет) тут еще что-то можно сделать составлными индексами по парам критериев, но вставки и обновления такой таблицы могут быть более тяжелыми.
И опять же если у тебя например дана рубрика, ей соответствует 500 000 товаров и ты хочешь найти все бренды, цвета этих товаров то это тоже быстро никак работать не может так как базе придется обходить всю эту кучу товаров.
А и еще. Все это я пишу исходя из того что у вас оптимизированы настройки БД (в общем, используемые индексы должны целиком в нее помещаться + быть место для горячих данных) и расставлены правильно индексы. Если вы даже этого не смогли правильно сделать - это задача не вашего уровня, наймите профессионала.
БД вообще не оптимизированы под такой сценарий. Нужен либо специальный движок для фасетного поиска либо лепить временные костыли на кеше или денормализованных данных, которые помогут на какое-то время, пока у вас не станет больше товаров. В принципе, решения для таких случаев известны, велосипеды тут придумывать не требуется.
Сейчас даже в сфинксе есть что-то для фасетного поиска, но он по моему для этого не идеально подходит. Надо тестировать.
Можно не очищать кеш, а продумать механизм его обновления. Если у вас обновление каждые пару минут то надо инкрементально обновлять данные в кеше. Ну или не использовать его.
>>36012
Он не поможет так как мала вероятность что будут одни и те же запросы. Он сбрасывается при любой записи в таблицу. Ну и это все описано в офиц документации, гугли mysql query cache
>>36004
То что там анон предложил с временныим таблицами - это бред. Ты серьезно собрался копировать полмиллиона товаров во временную таблицу чтобы посчитать сколько там брендов? Анон то ли что-то путает то ли вообще не понимает в оптимизации.
>>35709
Зависит от структуры БД. Если отношение товар-бренд и товар-рубрика это 1 ко многим то можно сделать индекс и получать ответ "какие бренды есть в рубрике" довольно быстро. Вариант с созданием новой временной таблицы по моему заведомо самый медленный. Не веришь - попробуй сам сделать это на досуге и померяй.
>>35503
Используется Ipv6 а не IPv4, почитай википедию. Там другой формат адресов и ::1 это просто сокращение адреса где куча нулей и единица в конце.
> Я сначала не понял, зачем они в UserMapper.php запихали метод mapObject(array $row).
Чтобы преобразовать массив данных в модель.
> А если бы я просто использовал ActiveRecord, то через FETCH_CLASS возвращал бы данные в виде нужного объекта.
- имена полей объекта и БД могут не совпадать (удобнее когда совпадают конечно)
- FETCH_CLASS проставляет поля в обход сеттеров. Может у тебя в модели поля должны запоняться через setName и там есть какая-то логика, FETCH_CLASS ее игнорирует. Хуже, он сначала заполняет поля а потом вызывает конструктор. Это вообще пример глупо спроектированной фичи.
- могут требоваться преобразования типов данных. Например в БД дата хранится строкой а в PHP объектом DateTime. Также, могут быть специальные классы для других типов данных: например денег, каких-то сложных структур данных
Те же проблемы есть и в AR. Раз ты знаешь про эти 2 подхода ты наверно знаешь и главный недостаток AR и мне не надо его рассказывать?
Наконец исопьзование или не использование FETCH_CLASS вообще не имеет отношения к DataMapper или AR. Оба этих подхода могут использовать или не использовать эту фичу. Ты по моему что-то путаешь думая что AR это кодда мы используем FETCH_CLASS. Не так. AR это когда модель и код работы с БД объединены (перемешаны) в 1 классе, DM это когда модель ничего не знает про БД и логика работы с ней вынесена в отдельный класс (класcы).
Простой пример. Допустим у тебя есть код который работает с моделью. В случае AR он не может работать без БД, а в случае DM ты можешь брать модели хоть откуда, например создать вручную, и код будет работать точно так же, без базы.
> Почему в случае DataMapper нельзя воспользоваться FETCH_CLASS? Да потому, что конструктор модели User принимает 3 параметра, которые нужно ставить в null
То что конструктор требует параметры вообще не имеет никакого отношения к выбору между AR и DM.
> PDO фетчит данные в виде объекта, но когда этот объект создаётся, то все данные заполненные раннее PDO, теперь обращаются в null.
Это ошибка в PDO. Там есть параметр http://stackoverflow.com/questions/14336726/pdofetch-props-late-and-construct-call-using-fetched-data специально для исправления этого бага. Просто инвалиды которые делали PDO плохо разбираются в ООП. Лучше не полагаться на такие фичи, а написать свой код маппинга.
> Я сначала не понял, зачем они в UserMapper.php запихали метод mapObject(array $row).
Чтобы преобразовать массив данных в модель.
> А если бы я просто использовал ActiveRecord, то через FETCH_CLASS возвращал бы данные в виде нужного объекта.
- имена полей объекта и БД могут не совпадать (удобнее когда совпадают конечно)
- FETCH_CLASS проставляет поля в обход сеттеров. Может у тебя в модели поля должны запоняться через setName и там есть какая-то логика, FETCH_CLASS ее игнорирует. Хуже, он сначала заполняет поля а потом вызывает конструктор. Это вообще пример глупо спроектированной фичи.
- могут требоваться преобразования типов данных. Например в БД дата хранится строкой а в PHP объектом DateTime. Также, могут быть специальные классы для других типов данных: например денег, каких-то сложных структур данных
Те же проблемы есть и в AR. Раз ты знаешь про эти 2 подхода ты наверно знаешь и главный недостаток AR и мне не надо его рассказывать?
Наконец исопьзование или не использование FETCH_CLASS вообще не имеет отношения к DataMapper или AR. Оба этих подхода могут использовать или не использовать эту фичу. Ты по моему что-то путаешь думая что AR это кодда мы используем FETCH_CLASS. Не так. AR это когда модель и код работы с БД объединены (перемешаны) в 1 классе, DM это когда модель ничего не знает про БД и логика работы с ней вынесена в отдельный класс (класcы).
Простой пример. Допустим у тебя есть код который работает с моделью. В случае AR он не может работать без БД, а в случае DM ты можешь брать модели хоть откуда, например создать вручную, и код будет работать точно так же, без базы.
> Почему в случае DataMapper нельзя воспользоваться FETCH_CLASS? Да потому, что конструктор модели User принимает 3 параметра, которые нужно ставить в null
То что конструктор требует параметры вообще не имеет никакого отношения к выбору между AR и DM.
> PDO фетчит данные в виде объекта, но когда этот объект создаётся, то все данные заполненные раннее PDO, теперь обращаются в null.
Это ошибка в PDO. Там есть параметр http://stackoverflow.com/questions/14336726/pdofetch-props-late-and-construct-call-using-fetched-data специально для исправления этого бага. Просто инвалиды которые делали PDO плохо разбираются в ООП. Лучше не полагаться на такие фичи, а написать свой код маппинга.
Да phpmorphy мог бы помочь тут.
>>35239
Ты что-то путаешь
"джойны для денормализации" - это что-то странное.
>>35236
Размазывать функционал одного класса по всему коду - вот что неправильно. Каждый должен иметь свою зону ответственности. Валидацией занимается валидатор, а не кто попало.
>>35235
Нет, выборка 500 000 товаров в PHP скорее всего приведет к перерасходу памяти и будет намного медленнее.
>>35233
Что значит "диспетчеризация"? Сишник? У нас в PHP это не имеет никакого знаения, не применяй термины из одного языка к другому.
у нас это называется функции высшего порядка.
>>35230
> Плюс продукты нужно искать, какие только в наличии в конкретном магазине, а это джойнить еще 2 таблицы со складами и магазинами.
Это скорее плюс так как снижает число просматриваемых записей.
>>35226
Можно, в виде [$obj, 'method']: http://php.net/manual/ru/language.types.callable.php
Но непонятно зачем.
>>35224
Там могут быть связи многие-ко-многим
>>35223
Бред.
>>35218
Кеш как временное решение или движок с поддержкой фасетного поиска.
Да phpmorphy мог бы помочь тут.
>>35239
Ты что-то путаешь
"джойны для денормализации" - это что-то странное.
>>35236
Размазывать функционал одного класса по всему коду - вот что неправильно. Каждый должен иметь свою зону ответственности. Валидацией занимается валидатор, а не кто попало.
>>35235
Нет, выборка 500 000 товаров в PHP скорее всего приведет к перерасходу памяти и будет намного медленнее.
>>35233
Что значит "диспетчеризация"? Сишник? У нас в PHP это не имеет никакого знаения, не применяй термины из одного языка к другому.
у нас это называется функции высшего порядка.
>>35230
> Плюс продукты нужно искать, какие только в наличии в конкретном магазине, а это джойнить еще 2 таблицы со складами и магазинами.
Это скорее плюс так как снижает число просматриваемых записей.
>>35226
Можно, в виде [$obj, 'method']: http://php.net/manual/ru/language.types.callable.php
Но непонятно зачем.
>>35224
Там могут быть связи многие-ко-многим
>>35223
Бред.
>>35218
Кеш как временное решение или движок с поддержкой фасетного поиска.
> для фильтров таблица filters_to_products, где задается соответствие product_id - filter_id
Они там все многие-ко-многим что ли? А, понял, вы все фильтры держите в одной таблице.
> AND ta.tag_id = 25 AND ta.tag_id = 3
Тут явно ошибка - это не выберет записей так как условия противоречат - наверно таблицу джойнят 2 раза?
> Со второй начались проблемы - тут нужен вложенный подзапрос.
Не понимаю. Допустим надо найти бренды, предлагающие красную одежду для девочек. Тут не нужен подзапрос. Хватит джойнов.
Подзапрос всегда хуже так как не дает оптимизатору менять план запроса.
> Видел шопы, где это мгновенно происходит, как такое делается?
специализированный движок для фасетного поиска.
Памяти у вас достаточно на сервере для mysql?
>>35151
> Там рассказано как с помощью ctags писать самому всякие find by usage и autocomplete плагины.
костыли же которые в ИДЕ из коробки. Для ctags есть инкрементальное обновление по мере набора или сохранения файлов? В Саблайме например индекс функций обновляется сам собой.
>>34982
Данные надо сохранять на сервере
>>34968
На практике везде применяют первое. Я не вижу выгоды писать примитивные однотипные классы для каждого шаблона.
>>34968
Это дело вкуса. Я например не ставлю браузерные расширения. Есть риск снижения производительности (особенно на больших страницах), есть риск вредоносного функционала. Код лучше раскрашивать на сервере перед публикацией.
>>34967
Внутри одного скрипта. Выполнение 2 скриптов абсолютно независимо друг от друга и никаких общих данных у них нет.
>>34963
1) первое
2) код должен огбъяснять паттерн а не отвлекаться на лишние подробности. Да и мне тоже не нравятся эти магические методы, они затрудняют понимание кода, требуют больше времени чтобы разобраться (надо прочесть и анализировать их код, непонятно что там может быть написано). Для свойств должен быть специальный синтаксис вроде property name { ... } а мегические методы это уродливые костыли.
3) можно. Если объектов мало, можно просто в bootstrap.php создать их, но когда объектов становится больше, то вручную отслеживать кто от кого зависит становится утомительно.
> Мне из контроллера нужно иметь доступ к объекту StudentMapper. Этот объект создаётся с передачей конфига для PDO.
Это неправильно и нарушает прицип DI. StudentMapper не должен создавать объект PDO. Перечитай урок по DI - идея в том что мы передаем ему готовый PDO а не нагружаем его не своей работой.
Также не путай DI контейнеры и сам DI. DI можно использовать и без контейнеров, создавая объекты вручную.
> Но не передавать же мне всё через конструктор?
Если есть контейнер, в контроллер можно передать его. Если нет - отдельные объекты. А что делать? не копипастить же код создания мапперов.
Памяти у вас достаточно на сервере для mysql?
>>35151
> Там рассказано как с помощью ctags писать самому всякие find by usage и autocomplete плагины.
костыли же которые в ИДЕ из коробки. Для ctags есть инкрементальное обновление по мере набора или сохранения файлов? В Саблайме например индекс функций обновляется сам собой.
>>34982
Данные надо сохранять на сервере
>>34968
На практике везде применяют первое. Я не вижу выгоды писать примитивные однотипные классы для каждого шаблона.
>>34968
Это дело вкуса. Я например не ставлю браузерные расширения. Есть риск снижения производительности (особенно на больших страницах), есть риск вредоносного функционала. Код лучше раскрашивать на сервере перед публикацией.
>>34967
Внутри одного скрипта. Выполнение 2 скриптов абсолютно независимо друг от друга и никаких общих данных у них нет.
>>34963
1) первое
2) код должен огбъяснять паттерн а не отвлекаться на лишние подробности. Да и мне тоже не нравятся эти магические методы, они затрудняют понимание кода, требуют больше времени чтобы разобраться (надо прочесть и анализировать их код, непонятно что там может быть написано). Для свойств должен быть специальный синтаксис вроде property name { ... } а мегические методы это уродливые костыли.
3) можно. Если объектов мало, можно просто в bootstrap.php создать их, но когда объектов становится больше, то вручную отслеживать кто от кого зависит становится утомительно.
> Мне из контроллера нужно иметь доступ к объекту StudentMapper. Этот объект создаётся с передачей конфига для PDO.
Это неправильно и нарушает прицип DI. StudentMapper не должен создавать объект PDO. Перечитай урок по DI - идея в том что мы передаем ему готовый PDO а не нагружаем его не своей работой.
Также не путай DI контейнеры и сам DI. DI можно использовать и без контейнеров, создавая объекты вручную.
> Но не передавать же мне всё через конструктор?
Если есть контейнер, в контроллер можно передать его. Если нет - отдельные объекты. А что делать? не копипастить же код создания мапперов.
Точно,заработало. Забыл про это свойство. Спасибо,добрый анон!
Задачка на проверку: https://ideone.com/7gHlP7
Задачка на исправление: https://ideone.com/6TJwXr
Чекните, плез, на наличие велосипедов и ошибок.
>Для ctags есть инкрементальное обновление по мере набора или сохранения файлов?
https://github.com/ludovicchabant/vim-gutentags
Как минимум для JS, PHP и Python нормально настроенный Vim точно не хуже тех же Eclipse или Netbeans.
Там любят представлять все в виде файлов. Вот и значения некоторых системных параметров передаются из ядра в пользовательское пространство через псевдо-файлы. Ну например, есть такой псевдо-файл /proc/sys/kernel/pid_max который возвращает максимальный id процесса:
$ cat /proc/sys/kernel/pid_max
32768
выглядит он как обычный файл, хотя на самом деле при чтении просто ядро возвращает текстовое представление числа. А теперь попробуем узнать длину этого псевдо-файла:
$ stat -c '%s' /proc/sys/kernel/pid_max
0
Упс. Вот и первая дырка в абстракции. А я покажу вам еще одну. Попробуем читать этот псевдо-файл по 1 байту за раз:
$ dd if=/proc/sys/kernel/pid_max bs=1 count=20
31+0 records in
Прочиталась только первая цифра. Попробуем сделать размер блока равным 3 байтам:
$ dd if=/proc/sys/kernel/pid_max bs=3 count=20
3271+0 records in
Прочитались только цифры "327". (1+0 records in - это статистика).
Это уже серьезный баг. Ядро возвращает только столько данных, сколько поместится в буфер, а на вторую и далее попытки чтения возвращает признак конца файла. Если вы читаете блоками меньше определенного размера, вы получаете неверные данные.
На мой взгляд это непраивльно. Если они не могут обеспечить чтение в несколько приемов, пусть хотя бы проверяют размер буфера и возвращают ошибку если он недостаточен. А не молча притворяются что все в порядке.
Я еще проверил, другие псевдофайлы вроде /proc/cmdline или /proc/uptime читаются нормально. Видимо у них как-то избирательно все это реализовано.
Там любят представлять все в виде файлов. Вот и значения некоторых системных параметров передаются из ядра в пользовательское пространство через псевдо-файлы. Ну например, есть такой псевдо-файл /proc/sys/kernel/pid_max который возвращает максимальный id процесса:
$ cat /proc/sys/kernel/pid_max
32768
выглядит он как обычный файл, хотя на самом деле при чтении просто ядро возвращает текстовое представление числа. А теперь попробуем узнать длину этого псевдо-файла:
$ stat -c '%s' /proc/sys/kernel/pid_max
0
Упс. Вот и первая дырка в абстракции. А я покажу вам еще одну. Попробуем читать этот псевдо-файл по 1 байту за раз:
$ dd if=/proc/sys/kernel/pid_max bs=1 count=20
31+0 records in
Прочиталась только первая цифра. Попробуем сделать размер блока равным 3 байтам:
$ dd if=/proc/sys/kernel/pid_max bs=3 count=20
3271+0 records in
Прочитались только цифры "327". (1+0 records in - это статистика).
Это уже серьезный баг. Ядро возвращает только столько данных, сколько поместится в буфер, а на вторую и далее попытки чтения возвращает признак конца файла. Если вы читаете блоками меньше определенного размера, вы получаете неверные данные.
На мой взгляд это непраивльно. Если они не могут обеспечить чтение в несколько приемов, пусть хотя бы проверяют размер буфера и возвращают ошибку если он недостаточен. А не молча притворяются что все в порядке.
Я еще проверил, другие псевдофайлы вроде /proc/cmdline или /proc/uptime читаются нормально. Видимо у них как-то избирательно все это реализовано.
Обезумев от безделья, дохода, беспросветности и собственной никчемности он решил превозмочь, и наконец изучить хоть что нибудь полезное.
Вопросов не задаю, сижу тихо, мануалю манулов и курю бамбук.
Всем добра в вашем чатике!
Спасибо, наконец что-то прояснилось.
>скорее всего нужен поисковый движок с фасетным поиском для этой задачи
Какие порекомендуешь для новичков, чтобы не увязли совсем в этом фасетном поиске? В фирме одни джуны-миддлы, профессионалов баз данных нет, так что изучать и внедрять их с нуля придется.
>Кеш-таблицы такого вида:
>рубрика, бренд, цвет
>для эффективной выборки нужно несколько индексов
Хорошая идея, но что делать, когда меняется stock для одного из товаров. Т.е. наличие товара добавляется или удаляется. Генерировать тут же все таблицы с кешами заново? Их, как я понял, много будет.
>у тебя не приведено достаточно данных: какие есть измерения для поиска, какие их сочетания возможны и какие нет.
Возможны все сочетания для которых есть товары, фильтров всего от 7 до 13 штук (сейчас 7 используется, но до 13 подключаем), фильтры в одной таблице filters_to_product, часть фильтров по отдельным таблицам, например category_to_product, special_filter_to_product. Отношение везде много-ко-многим, т.е. множеству фильтров соответствует множество продуктов.
>База не будет быстро работать когда у тебя есть связи вида много-ко-многим (то есть у товара может быть несколько рубрик и несколько цветов) и надо искать по их сочетаниям.
У товара может быть только одна рубрика, бренд или цвет, и остальные фильтры все только по одному на товар.
>Если связи вида один-ко-многим (товар входит только в одну рубрику и имеет только один цвет) тут еще что-то можно сделать составлными индексами по парам критериев, но вставки и обновления такой таблицы могут быть более тяжелыми.
Постоянные изменения идут только в таблицу наличия товаров, которая джойнится. Все остальные таблицы только читаются, обновления списка товаров происходят не так часто и ими можно пренебречь.
>Все это я пишу исходя из того что у вас оптимизированы настройки БД (в общем, используемые индексы должны целиком в нее помещаться + быть место для горячих данных) и расставлены правильно индексы.
Индексы все есть, на это нас хватило, про настройки БД возможно нет, до задачи с убиранием фильтров на все скорости хватало в пределах 1 секунды, что устраивало. Есть где про эти настройки руководство почитать?
>Если вы даже этого не смогли правильно сделать - это задача не вашего уровня, наймите профессионала.
Тут все глухо, профессионалы не хотят у нас работать, это фирма для джунов-миддлов. К тому же у нас нет задач для профессионалов, эта с фильтрами считай единственная, и других такого уровня больше не предвидится. Все остальные задачи в рамках знаний джунов спокойно решаются.
>или денормализованных данных, которые помогут на какое-то время, пока у вас не станет больше товаров
Товаров больше не станет, около миллиона товаров всегда будет.
>В принципе, решения для таких случаев известны, велосипеды тут придумывать не требуется.
Это уже хорошо, что велосипедов не надо. Как я понял из твоих объяснений, из известных решений только кэш-таблицы и фасетный поиск? Или еще какие есть?
>>36637
>Можно не очищать кеш, а продумать механизм его обновления. Если у вас обновление каждые пару минут то надо инкрементально обновлять данные в кеше. Ну или не использовать его.
Это как? Можешь схему примерно объяснить? Не очень представляю.
>>36644
>Они там все многие-ко-многим что ли? А, понял, вы все фильтры держите в одной таблице.
Да, все в одной таблице id продукта - id фильтра. Ну и пара фильтров в отдельных таблицах, вроде category_to_products, где схема такая же.
>Тут явно ошибка - это не выберет записей так как условия противоречат - наверно таблицу джойнят 2 раза?
Хм, да, там OR было, а не AND. Выбираются все фильтры с тэгами 25 или 3.
>Не понимаю. Допустим надо найти бренды, предлагающие красную одежду для девочек. Тут не нужен подзапрос. Хватит джойнов.
Внешний select выбирает фильтры из полученной таблицы доступных продуктов по этим фильтрам. SELECT DISTINCT filters.filter_id FROM (select товары по фильтрам которые в наличии, плюс фильтры к ним через join filters_to_products)
Спасибо, наконец что-то прояснилось.
>скорее всего нужен поисковый движок с фасетным поиском для этой задачи
Какие порекомендуешь для новичков, чтобы не увязли совсем в этом фасетном поиске? В фирме одни джуны-миддлы, профессионалов баз данных нет, так что изучать и внедрять их с нуля придется.
>Кеш-таблицы такого вида:
>рубрика, бренд, цвет
>для эффективной выборки нужно несколько индексов
Хорошая идея, но что делать, когда меняется stock для одного из товаров. Т.е. наличие товара добавляется или удаляется. Генерировать тут же все таблицы с кешами заново? Их, как я понял, много будет.
>у тебя не приведено достаточно данных: какие есть измерения для поиска, какие их сочетания возможны и какие нет.
Возможны все сочетания для которых есть товары, фильтров всего от 7 до 13 штук (сейчас 7 используется, но до 13 подключаем), фильтры в одной таблице filters_to_product, часть фильтров по отдельным таблицам, например category_to_product, special_filter_to_product. Отношение везде много-ко-многим, т.е. множеству фильтров соответствует множество продуктов.
>База не будет быстро работать когда у тебя есть связи вида много-ко-многим (то есть у товара может быть несколько рубрик и несколько цветов) и надо искать по их сочетаниям.
У товара может быть только одна рубрика, бренд или цвет, и остальные фильтры все только по одному на товар.
>Если связи вида один-ко-многим (товар входит только в одну рубрику и имеет только один цвет) тут еще что-то можно сделать составлными индексами по парам критериев, но вставки и обновления такой таблицы могут быть более тяжелыми.
Постоянные изменения идут только в таблицу наличия товаров, которая джойнится. Все остальные таблицы только читаются, обновления списка товаров происходят не так часто и ими можно пренебречь.
>Все это я пишу исходя из того что у вас оптимизированы настройки БД (в общем, используемые индексы должны целиком в нее помещаться + быть место для горячих данных) и расставлены правильно индексы.
Индексы все есть, на это нас хватило, про настройки БД возможно нет, до задачи с убиранием фильтров на все скорости хватало в пределах 1 секунды, что устраивало. Есть где про эти настройки руководство почитать?
>Если вы даже этого не смогли правильно сделать - это задача не вашего уровня, наймите профессионала.
Тут все глухо, профессионалы не хотят у нас работать, это фирма для джунов-миддлов. К тому же у нас нет задач для профессионалов, эта с фильтрами считай единственная, и других такого уровня больше не предвидится. Все остальные задачи в рамках знаний джунов спокойно решаются.
>или денормализованных данных, которые помогут на какое-то время, пока у вас не станет больше товаров
Товаров больше не станет, около миллиона товаров всегда будет.
>В принципе, решения для таких случаев известны, велосипеды тут придумывать не требуется.
Это уже хорошо, что велосипедов не надо. Как я понял из твоих объяснений, из известных решений только кэш-таблицы и фасетный поиск? Или еще какие есть?
>>36637
>Можно не очищать кеш, а продумать механизм его обновления. Если у вас обновление каждые пару минут то надо инкрементально обновлять данные в кеше. Ну или не использовать его.
Это как? Можешь схему примерно объяснить? Не очень представляю.
>>36644
>Они там все многие-ко-многим что ли? А, понял, вы все фильтры держите в одной таблице.
Да, все в одной таблице id продукта - id фильтра. Ну и пара фильтров в отдельных таблицах, вроде category_to_products, где схема такая же.
>Тут явно ошибка - это не выберет записей так как условия противоречат - наверно таблицу джойнят 2 раза?
Хм, да, там OR было, а не AND. Выбираются все фильтры с тэгами 25 или 3.
>Не понимаю. Допустим надо найти бренды, предлагающие красную одежду для девочек. Тут не нужен подзапрос. Хватит джойнов.
Внешний select выбирает фильтры из полученной таблицы доступных продуктов по этим фильтрам. SELECT DISTINCT filters.filter_id FROM (select товары по фильтрам которые в наличии, плюс фильтры к ним через join filters_to_products)
По поводу обноления кеша, идея такая. Допустим у нас в кеше хранится информация, что для рубрики "Футболки", цвет "Красный", бренд "Adidas" есть 3 товара.
Допустим теперь товар Футболка красного цвета бренда адидас стал недоступен. Значит мы можем уменьшить соответствующую цифру в кеш-таблице на 1. Тем самым обновив информацию в кеше.
Риск конечно есть всегда что из-за ошибок значения в кеше начинают расходиться с реальными. Этим и плох такой подход.
Я позже наверно напишу подробнее что думаю по этому поводу, сейчас просто некогда.
Пока можешь попробовать запрос с DISTINCT и подзапросом который ты писал выше, переписать на чистые джойны без подзапроса и посмотреть скорость.
>теперь товар Футболка красного цвета бренда адидас стал недоступен. Значит мы можем уменьшить соответствующую цифру в кеш-таблице на 1
Думаю, не можем. Потому что может быть еще другой товар, тоже футболка красного цвета бренда адидас. Т.е. чтобы уменьшить цифру в кеш-таблице, нам сначало надо пройтись опять по всем товарам в магазине, посмотреть есть ли другие товары, которые футболка красного цвета и бренда адидас, и только если их нет, можно из кэш таблицы удалить эту запись. Также видимо возникнет проблема - при исчезновении товара, какие таблицы обновлять. Если всего 13 фильтров, кеш-таблицы мы видимо создавать будем только по 3-4 фильтрам. Для 13 фильтров таких таблиц будет довольно много, значит при изменении наличия товара, нужно обойти всю эту кучу таблиц и для каждой обойти заново все товары в магазине, проверяя что нет товаров с такой комбинацией 3-4 фильтров. Хмм, кажется все это будет занимать много времени.
>>36993
Ок, попробую.
Также, для 13 фильтров, комбинаций из 3 фильтров будет 2197, это 2197 кеш-таблиц каждый раз проверять при исчезновении товара. Что-то много выходит.
Спасибо большое за ответы!
>Ты по моему что-то путаешь думая что AR это кодда мы используем FETCH_CLASS.
Я думал, что DataMapper подразумевает работу без FETCH_CLASS.
>Наконец исопьзование или не использование FETCH_CLASS вообще не имеет отношения к DataMapper или AR.
Дошло, я слишком буквально воспринимал тот сайт с паттернами, хотя мог бы догадаться по аббревиатуре DBAL в коде.
>Это неправильно и нарушает прицип DI. StudentMapper не должен создавать объект PDO.
Он и не создаёт, я немного запутанно объяснил здесь:
>поэтому я в конструкторе контроллера получаю уже готовый объект StudentMapper, как мне кажется, так менее уродливей.
Примеры на пике.
Вопросы:
1) Если упростить, в DataMapper для манипуляции данными в БД нужно первым делом найти модель по id, результат-объект в переменную; теперь для изменения/добавления данных нужно обращатся только к этому объекту; дальше у маппера вызывается метод save(), куда передаётся переменная-объект. Правильно понимаю? У меня сейчас именно так всё и работает.
2) Может ли хелпер обращаться к мапперу? Объясню зачем: есть хелпер Auth. Можно проверить зарегистрирован ли пользователь: <?php if (\App\Helper\Auth::check()): ?>
А вот Auth::user() возвращает $_SESSION['user'], то есть только строчку с ником.
Хотелось бы, что Auth::user() возвращал уже объект зарегистрированного пользователя, чтобы делать такие штуки Auth::user()->id или Auth::user()->email
Для этого из хелпера нужен доступ к мапперу.
3) Я гуглил Front Controller и ничего не понял. Сейчас у меня есть класс App, который запускается в index.php через $app = new App($config)
Этот класс App одновременно соединяется с БД, разбирает URI, определяет какой контроллер и экшн вызывать, заполняет контейнер объектами, вызывает контроллер и экшн. В объекте App чуть меньше 100 строк, но он слишком много разных вещей делает. Это норма?
А, сори, ступил, хеш-таблиц будет 455, а не 2197, ведь порядок полей не важен. Но все равно как-то много, если для каждой все товары сканировать.
RewriteRule ^ index.php [QSA,L]
В итоге отрабатывает только $app->get('/'), какой бы юрл я не вводил. Непонятная чертовщина.
Разобрался, все хорошо.
На сайте http://htmlbook.ru есть два самоучителя. по 4-й и по 5-й версии. Скажите нужно начать изучение с сразу с пятой, или сначала четверка, а потом пятерка?
Сразу с пятой.
Если ближайшие два года ты будешь просто учиться, то можешь сразу седьмую постигать какими-то путями.
тот, что по 5-ой не про html в целом, а про нововведения, так что начинай с четвертой, там о том, как делать ссылки, таблицы и прочее..
webref.ru от автора htmlbook но о современных технологиях, там и курсы новые добавили
Ну давай, начни.
Задачка о фасетном поиске, какой выше обсуждали, явно не помешала бы. Популярная же штука, почти в каждом магазе встречается, судя по данным гугла.
>Тут главный принципиальный вопрос - должны ли мы хранить в форме студента или должны дублировать все его поля. Что удобнее, какие есть недостатки и преимущества. Как я уже написал, в фреймворках вроед Симфони у формы своя отдельная копия данных, но у нас простое приложение и нам наверно проще будет редактировать данные прямо в студенте чем делать копию. Хотя, кто знает.
Будем хранить студента в форме. Делать копию было бы слишком просто и всегда хорошо научится чему-то новому.
>>36623
>Глядя на твой код, я например, не понимаю, зачем ты передаешь в функцию валидации 'getName'. Зачем ей имя геттера? Оно как-то помогает проверить правильность заполнения поля?
Ну да, функция валидации должна получить что-то в аргументы. Например, есть методы которым нужно только одно поле чтобы проверить, а есть метод проверки пароля и повторного пароля, для которого нужно получить два свойства. Во втором случае не получится применить стандартизированный вид функции валидации.
>>36623
>Дальше, как мы будем хранить информацию об ошибках? Вообще, тут в принципе для этого можно даже объект задействовать. А можно и массивом обойтись.
Я объектами делаю - на каждую сущность свой объект ошибок
class Student
{
...
}
class ErrorList
{
...
}
class StudentErrorList extends ErrorList
{
...
}
Я знаю что это лишнее усложнение, но это поможет мне лучше запомнить принцип, да и кажется весьма логичным чтобы у каждой сущности был свой класс, потому что помимо этого у каждой сущности у меня есть свой класс валидации, который имеет свой массив с белым списком полей и валидации к ним. Так просто будет наглядней, я думаю.
И еще у меня вопрос про паттерны. Немого погуглив и прочитал на википедии что паттерн это всего лишь пример решения проблемы/задачи, и связи с этим у меня возникло несколько вопросов:
Как (мне) новичку узнать какой паттерн применить лучше? Выучить их все и отталкиваться от собственной базы или есть какой-то способ узнать какой паттерн применить? Или же их вовсе не обязательно знать все? Например, когда я изучал код SymfonyForms я сразу обратил внимание что названиях классов содержится слова Builder и Factory и сразу смекнул что это какие-то паттерны, потому что видел что-то такое в твоих ссылках на Design Patterns. Выходит ли что паттерны это всего лишь некая договорённость правильного написания кода, чтобы проще было понимать что написано. Ведь для любой проблемы или задачи всегда можно найти какое-то собственное решение, которое будет не факт что понятно остальным.
>Тут главный принципиальный вопрос - должны ли мы хранить в форме студента или должны дублировать все его поля. Что удобнее, какие есть недостатки и преимущества. Как я уже написал, в фреймворках вроед Симфони у формы своя отдельная копия данных, но у нас простое приложение и нам наверно проще будет редактировать данные прямо в студенте чем делать копию. Хотя, кто знает.
Будем хранить студента в форме. Делать копию было бы слишком просто и всегда хорошо научится чему-то новому.
>>36623
>Глядя на твой код, я например, не понимаю, зачем ты передаешь в функцию валидации 'getName'. Зачем ей имя геттера? Оно как-то помогает проверить правильность заполнения поля?
Ну да, функция валидации должна получить что-то в аргументы. Например, есть методы которым нужно только одно поле чтобы проверить, а есть метод проверки пароля и повторного пароля, для которого нужно получить два свойства. Во втором случае не получится применить стандартизированный вид функции валидации.
>>36623
>Дальше, как мы будем хранить информацию об ошибках? Вообще, тут в принципе для этого можно даже объект задействовать. А можно и массивом обойтись.
Я объектами делаю - на каждую сущность свой объект ошибок
class Student
{
...
}
class ErrorList
{
...
}
class StudentErrorList extends ErrorList
{
...
}
Я знаю что это лишнее усложнение, но это поможет мне лучше запомнить принцип, да и кажется весьма логичным чтобы у каждой сущности был свой класс, потому что помимо этого у каждой сущности у меня есть свой класс валидации, который имеет свой массив с белым списком полей и валидации к ним. Так просто будет наглядней, я думаю.
И еще у меня вопрос про паттерны. Немого погуглив и прочитал на википедии что паттерн это всего лишь пример решения проблемы/задачи, и связи с этим у меня возникло несколько вопросов:
Как (мне) новичку узнать какой паттерн применить лучше? Выучить их все и отталкиваться от собственной базы или есть какой-то способ узнать какой паттерн применить? Или же их вовсе не обязательно знать все? Например, когда я изучал код SymfonyForms я сразу обратил внимание что названиях классов содержится слова Builder и Factory и сразу смекнул что это какие-то паттерны, потому что видел что-то такое в твоих ссылках на Design Patterns. Выходит ли что паттерны это всего лишь некая договорённость правильного написания кода, чтобы проще было понимать что написано. Ведь для любой проблемы или задачи всегда можно найти какое-то собственное решение, которое будет не факт что понятно остальным.
Реквестирую на проектирование бд.
Потому что пока там только лайки и кинотеатр, это несерьезно.
Особенно часто возникают проблемы с джойнами, группировкой/having, правильная расстановка индексов и т.д.
Только не мути очередной громадный квест, который выполнять полгода. Нужны короткие и красивые как шахматные этюды задания.
В базу конечно. Парсинг файлов медленное и неоптимальное дело, база же специально под такие задачи заточена.
> Например, есть методы которым нужно только одно поле чтобы проверить, а есть метод проверки пароля и повторного пароля, для которого нужно получить два свойства.
Тогда можно сначала в цикле вызвать стандартные функции валидации, а потом уже вручную, нестандартные, которые работают с формой целиком, а не с отдельными полями.
Кстати, валидацию наверно стоит разбить на 2 функции: валидация модели Студента и валидация формы, которая может вызывать первую.
> да и кажется весьма логичным чтобы у каждой сущности был свой класс
Да не уверен. Чем StudentErrorList отличается от любого другого ErrorList? если ничем то не стоит и отдельный класс делать, я думаю.
> Как (мне) новичку узнать какой паттерн применить лучше?
паттерны надо изучать на примере конкретной ситуации или конкретного кода. Вот надо тебе сделать наследование таблиц в БД - почитай про соответствующие паттерны. Изучаешь код библиотеки Doctrine или компоненты Симфони - посмотри какие паттерны там используются. Так, абстрактно, ты можешь про них почитать, ну например тут:
http://design-pattern.ru/ (это краткая выжимка из книги Фаулера "Паттерны/Шаблоны Проектирования Корпоративных приложений", можешь ее поискать).
Но вряд ли ты сразу их все поймешь, если ты не сталкивался с описанными там ситуациями сам. Но книгу можешь найти и почитать, если интересно, в книге все более-менее подробно расписано.
Ну например паттерн TableDataGateway ты наверно использовал.
Также, есть примеры тут с кодом, но судя по отзывам других анонов, они менее понятные и в общем похуже чем книга: http://designpatternsphp.readthedocs.io/ru/latest/README.html
> Например, когда я изучал код SymfonyForms я сразу обратил внимание что названиях классов содержится слова Builder и Factory и сразу смекнул что это какие-то паттерны,
Верно. Builder например это класс который создает и настраивает другой класс, в случае когда этот процесс сложный и вручную можно забыть что-то нужное указать.
> Выходит ли что паттерны это всего лишь некая договорённость правильного написания кода, чтобы проще было понимать что написано.
Ну да, паттерн проектирования можно перевести как "шаблон". То есть шаблон решения для той или иной задачи. Вообще, если ты будешь решать ее, не зная паттернов, возможно что в итоге ты придешь к тому же самому, только это займет больше времени, решение получится не до конца идеальным. А в случае паттерна достаточно сказать "давайте используем тут паттерн N" - и все понимают что ты хотел сказать.
> Например, есть методы которым нужно только одно поле чтобы проверить, а есть метод проверки пароля и повторного пароля, для которого нужно получить два свойства.
Тогда можно сначала в цикле вызвать стандартные функции валидации, а потом уже вручную, нестандартные, которые работают с формой целиком, а не с отдельными полями.
Кстати, валидацию наверно стоит разбить на 2 функции: валидация модели Студента и валидация формы, которая может вызывать первую.
> да и кажется весьма логичным чтобы у каждой сущности был свой класс
Да не уверен. Чем StudentErrorList отличается от любого другого ErrorList? если ничем то не стоит и отдельный класс делать, я думаю.
> Как (мне) новичку узнать какой паттерн применить лучше?
паттерны надо изучать на примере конкретной ситуации или конкретного кода. Вот надо тебе сделать наследование таблиц в БД - почитай про соответствующие паттерны. Изучаешь код библиотеки Doctrine или компоненты Симфони - посмотри какие паттерны там используются. Так, абстрактно, ты можешь про них почитать, ну например тут:
http://design-pattern.ru/ (это краткая выжимка из книги Фаулера "Паттерны/Шаблоны Проектирования Корпоративных приложений", можешь ее поискать).
Но вряд ли ты сразу их все поймешь, если ты не сталкивался с описанными там ситуациями сам. Но книгу можешь найти и почитать, если интересно, в книге все более-менее подробно расписано.
Ну например паттерн TableDataGateway ты наверно использовал.
Также, есть примеры тут с кодом, но судя по отзывам других анонов, они менее понятные и в общем похуже чем книга: http://designpatternsphp.readthedocs.io/ru/latest/README.html
> Например, когда я изучал код SymfonyForms я сразу обратил внимание что названиях классов содержится слова Builder и Factory и сразу смекнул что это какие-то паттерны,
Верно. Builder например это класс который создает и настраивает другой класс, в случае когда этот процесс сложный и вручную можно забыть что-то нужное указать.
> Выходит ли что паттерны это всего лишь некая договорённость правильного написания кода, чтобы проще было понимать что написано.
Ну да, паттерн проектирования можно перевести как "шаблон". То есть шаблон решения для той или иной задачи. Вообще, если ты будешь решать ее, не зная паттернов, возможно что в итоге ты придешь к тому же самому, только это займет больше времени, решение получится не до конца идеальным. А в случае паттерна достаточно сказать "давайте используем тут паттерн N" - и все понимают что ты хотел сказать.
>>32572
Ок, по коду.
- неправильные названия функций. имена функций начинаются с глагола, "сделайЧтоТо()" и не надо использовать нечитаемые сокращения в них вроде dbst(). parser() это не глагол. pageName это непонятно что.
Попробуй сам прочитать:
GetLinks::pageName()
ПолучитьСсылки::названиеСтраницы()
Как правильно: ПоисковикСсылок::получитьНазваниеСтраницы()
- в Queue::enqueue зачем-то передается двухмерный массив. зачем такая сложная структура? Лучше 1-мерный, а еще лучше - объект Node.
- имена классов - это существительные. GetLinks -> LinksGetter
- к Queue явно надо дописать что именно это за очередь
- нарушение инкапсуляции и зоны ответственности. За работу с очередью отвечает класс Queue. Почему мы узнаем размер очереди не из него напрямую, а через обращение к какой-то таблице?
Много статических вызовов и просто функций: Input::get, view(), Queue::enqueue(). По моему это не очень здорово, чем плохи статические методы? Тем что это по сути не ООП. Это ближе к использованию глобальных переменных или функций.
- Например, если какая-то функция обращается к Input::get, то получается она берет входные данные не из переданных ей аргументов, а откуда-то еще (побочные эффекты: https://github.com/codedokode/pasta/blob/master/good-code.md#Избегай-побочных-эффектов ), мы их не передаем явно, не можем подменить на другие. И это не видно из заголовка функции, что она к Input будет обращаться. То есть эта зависимость не видна, пока ты не изучишь весь код.
Если бы мы использовали не-статический класс Input то нам надо было бы передавать его объект явно в функцию. Было бы явно видно что ей нужны эти данные. Можно было бы их подменить.
- Опять же, из-за этого нет разграничения доступа: любая функция может обратиться к Input::get, в то время как с обычным объектом к нему могут получить доступ только те, кому его передали. Это автоматически защищает от ошибок вроде обращения к Input::get в модели (хотя конечно упорные люди найдут пути обхода).
- Нельзя создать несколько экземпляров объекта с разными данными (это не всегда нужно, но иногда бывает нужно, например при тестировании или каком-то нестандартном коде). Ну например у тебя нельзя создать и обрабатывать 2 Queue параллельно.
- Нельзя переопределить поведение объекта через наследование или альтернативную реализацию. Имя класса жестко задано. Ну например мы не можем сделать свой класс-наследник Input с дополнительными или работающими по-другому методами и передать его в функцию, так как она строго привязана к классу Input. (это тоже наверно редко нужно, в случае с Input, но с другими классами может быть полезно. Например вместо обычного объекта PDO мы могли бы передать в отладочном режиме наследник, который логгирует все выполняющиеся запросы).
А вот если бы мы передавали объект Input, мы бы могли передать вместо него альтернативный совместимый с ним класс.
То есть используя статические методы ты фактически отказываешься от ООП подхода и возвращаешься к процедурному коду из функций. Конечно надо смотреть по ситуации, имеет это какие-то недостатки или нет. Например в контроллере большинство из описанного выше не особо нужно и от обращения через статические методы вреда много нет.
Вот еще урок который отчасти связан с этой темой: https://gist.github.com/codedokode/e1d31a31b37d5f635057
Вот как бы мог выглядеть код на объектах (для простоты все объекты я создаю руками без DI-контейенра):
$queueTableGateway = ...
$queue = new Queue($queueTableGateway);
$linksGetter = new LinksGetter();
$pageName = $linksGetter->getPageName();
$node = new Node($pageName, ...);
$queue->enqueue($node);
Чем это лучше? Ну тем, что тут нет побочных эффектов, явно видно кто от кого зависит, и что откуда берется и куда передается. То есть у нас тут виден весь путь преобразования данных. Видно кто от кого зависит. Мы можем взять любой класс и использовать его в другом месте как-то по другому и это никак не повлияет на текущий код. Можем сделать несколько Queue.
Ну вот посмотрим например на такую штуку:
\DB::setFetchMode(...)
Это как раз пример побочных эффектов. Функция parserResults() меняет глобальное состояние класса DB (который используется и другим кодом) после своего вызова. Допустим, какая-то другая функция использует другой fetchMode, и вызывает parserResults(). Та меняет fetchMode и исходная функция после этого перестает работать. Причем это абсолютно неочевидно, казалось бы, какая связь между ними?
Даже хуже, функция parserResults меняет это трежим не для себя, а видимо для какого-то другого класса (Queue? GetLinks?)
Как было бы лучше: ставить fetchMode не глобально, а только для запросов которые ты делаешь. И не в контроллере, а там, где эти запросы делаются.
В общем, если ты хочешь лучше изучить все эти вещи, придется начинать с основ. У нас в учебнике из Оп поста в последней главе (ООП и пасты) изучается ООП и есть задачки - если ты хочешь разобраться, надо их все решить. Затем было бы полезно почитать про DI, может даже посмотреть задачу про студентов.
Ты же начал изучать фреймворк, пропустив много важных вещей и потому ты пишешь так называемую "лапшу", то есть запутанный код, ты явно не знаешь то, что я написал выше и просто ставишь инструкции наугад чтобы оно хоть как-то работало. Да и Laravel как мы сейчас видим, за счет злоупотребления статическими методами учит плохому.
> Как еще я мог назвать класс, который только и делает что достает ссылки из страницы? LinksExtractor?
Почему нет? Кстати, из-за того что он у тебя статический непонятно откуда он вообще берется, чем заполняется и тд.
> Решил передавать массив параметров напрямую в модель для разбора. Правильно или нет
Скорее всего нет, так как массив параметров это данные формы, а с формами работает контроллер. Модель не должна ничего знать про формы.
>Модель отвечает за работу с БД (или это бизнес-логика делает уже - я пока не вкурил).
В общем, понимаешь ты плохо. Модель это и есть бизнес-логика, то есть сама суть приложения, оторванная от интерфейсов с внешним миром (этим занимаются контроллер и вью).
>>32572
Ок, по коду.
- неправильные названия функций. имена функций начинаются с глагола, "сделайЧтоТо()" и не надо использовать нечитаемые сокращения в них вроде dbst(). parser() это не глагол. pageName это непонятно что.
Попробуй сам прочитать:
GetLinks::pageName()
ПолучитьСсылки::названиеСтраницы()
Как правильно: ПоисковикСсылок::получитьНазваниеСтраницы()
- в Queue::enqueue зачем-то передается двухмерный массив. зачем такая сложная структура? Лучше 1-мерный, а еще лучше - объект Node.
- имена классов - это существительные. GetLinks -> LinksGetter
- к Queue явно надо дописать что именно это за очередь
- нарушение инкапсуляции и зоны ответственности. За работу с очередью отвечает класс Queue. Почему мы узнаем размер очереди не из него напрямую, а через обращение к какой-то таблице?
Много статических вызовов и просто функций: Input::get, view(), Queue::enqueue(). По моему это не очень здорово, чем плохи статические методы? Тем что это по сути не ООП. Это ближе к использованию глобальных переменных или функций.
- Например, если какая-то функция обращается к Input::get, то получается она берет входные данные не из переданных ей аргументов, а откуда-то еще (побочные эффекты: https://github.com/codedokode/pasta/blob/master/good-code.md#Избегай-побочных-эффектов ), мы их не передаем явно, не можем подменить на другие. И это не видно из заголовка функции, что она к Input будет обращаться. То есть эта зависимость не видна, пока ты не изучишь весь код.
Если бы мы использовали не-статический класс Input то нам надо было бы передавать его объект явно в функцию. Было бы явно видно что ей нужны эти данные. Можно было бы их подменить.
- Опять же, из-за этого нет разграничения доступа: любая функция может обратиться к Input::get, в то время как с обычным объектом к нему могут получить доступ только те, кому его передали. Это автоматически защищает от ошибок вроде обращения к Input::get в модели (хотя конечно упорные люди найдут пути обхода).
- Нельзя создать несколько экземпляров объекта с разными данными (это не всегда нужно, но иногда бывает нужно, например при тестировании или каком-то нестандартном коде). Ну например у тебя нельзя создать и обрабатывать 2 Queue параллельно.
- Нельзя переопределить поведение объекта через наследование или альтернативную реализацию. Имя класса жестко задано. Ну например мы не можем сделать свой класс-наследник Input с дополнительными или работающими по-другому методами и передать его в функцию, так как она строго привязана к классу Input. (это тоже наверно редко нужно, в случае с Input, но с другими классами может быть полезно. Например вместо обычного объекта PDO мы могли бы передать в отладочном режиме наследник, который логгирует все выполняющиеся запросы).
А вот если бы мы передавали объект Input, мы бы могли передать вместо него альтернативный совместимый с ним класс.
То есть используя статические методы ты фактически отказываешься от ООП подхода и возвращаешься к процедурному коду из функций. Конечно надо смотреть по ситуации, имеет это какие-то недостатки или нет. Например в контроллере большинство из описанного выше не особо нужно и от обращения через статические методы вреда много нет.
Вот еще урок который отчасти связан с этой темой: https://gist.github.com/codedokode/e1d31a31b37d5f635057
Вот как бы мог выглядеть код на объектах (для простоты все объекты я создаю руками без DI-контейенра):
$queueTableGateway = ...
$queue = new Queue($queueTableGateway);
$linksGetter = new LinksGetter();
$pageName = $linksGetter->getPageName();
$node = new Node($pageName, ...);
$queue->enqueue($node);
Чем это лучше? Ну тем, что тут нет побочных эффектов, явно видно кто от кого зависит, и что откуда берется и куда передается. То есть у нас тут виден весь путь преобразования данных. Видно кто от кого зависит. Мы можем взять любой класс и использовать его в другом месте как-то по другому и это никак не повлияет на текущий код. Можем сделать несколько Queue.
Ну вот посмотрим например на такую штуку:
\DB::setFetchMode(...)
Это как раз пример побочных эффектов. Функция parserResults() меняет глобальное состояние класса DB (который используется и другим кодом) после своего вызова. Допустим, какая-то другая функция использует другой fetchMode, и вызывает parserResults(). Та меняет fetchMode и исходная функция после этого перестает работать. Причем это абсолютно неочевидно, казалось бы, какая связь между ними?
Даже хуже, функция parserResults меняет это трежим не для себя, а видимо для какого-то другого класса (Queue? GetLinks?)
Как было бы лучше: ставить fetchMode не глобально, а только для запросов которые ты делаешь. И не в контроллере, а там, где эти запросы делаются.
В общем, если ты хочешь лучше изучить все эти вещи, придется начинать с основ. У нас в учебнике из Оп поста в последней главе (ООП и пасты) изучается ООП и есть задачки - если ты хочешь разобраться, надо их все решить. Затем было бы полезно почитать про DI, может даже посмотреть задачу про студентов.
Ты же начал изучать фреймворк, пропустив много важных вещей и потому ты пишешь так называемую "лапшу", то есть запутанный код, ты явно не знаешь то, что я написал выше и просто ставишь инструкции наугад чтобы оно хоть как-то работало. Да и Laravel как мы сейчас видим, за счет злоупотребления статическими методами учит плохому.
> Как еще я мог назвать класс, который только и делает что достает ссылки из страницы? LinksExtractor?
Почему нет? Кстати, из-за того что он у тебя статический непонятно откуда он вообще берется, чем заполняется и тд.
> Решил передавать массив параметров напрямую в модель для разбора. Правильно или нет
Скорее всего нет, так как массив параметров это данные формы, а с формами работает контроллер. Модель не должна ничего знать про формы.
>Модель отвечает за работу с БД (или это бизнес-логика делает уже - я пока не вкурил).
В общем, понимаешь ты плохо. Модель это и есть бизнес-логика, то есть сама суть приложения, оторванная от интерфейсов с внешним миром (этим занимаются контроллер и вью).
Скорее всего для FTP нужен отдельный логи и пароль, может он в письме, может в панели управления.
>>31619
> Круг строится, но выглядит как дерьмо.
Идеально он наверно и не будет выглядеть так как буквы ставятся не как угодно, а по сетке. Ну и коэффициент у тебя маловат: 1.1105 - там должно быть что-то в районе 1.4 ... 2. У тебя круг сплющенный из-за этого.
По коду: оформлено все плохо. Тебе надо работать над тем, чтобы делать код более понятным и лаконичным. Ну например код вывода данных на экран переусложнен.
Для вывода достаточно сделать 2-мерный массив, то есть массив строк, где каждая строка представлена как массив символов:
$field = [
[' ', ' ', ' '],
[' ', ' ', ' '],
[' ', ' ', ' ']
];
Тогда установка буквы в определенную точку делается так:
$field[$y][$x] = 'А';
А вывод можно сделать например через цикл по строкам + implode для вывода строки.
Ты же выбрал другой способ хранения и как следствие получился запутанный код. Ты еще добавил сюда неудачные названия переменных ($xlast вместо $lastX или $xLast) и не смог разбить сложный код на отдельные простые функции. Ну давай посмотрим например на это:
for ($z=0;$z<$xlast;$z++) {
$echoTemp =$echoTemp.' ';
}
Это явно стоит вынести в отдельную функцию, генерирующую заданное число пробелов. Более того, такая функция уже есть - погугли str_repeat.
> $radius = ceil($leng/4);
Ты не предусмотрел промежутки между буквами, а также не учел что буквы не являются идеальными квадратами, а имеют прямоугольную форму. Ну как минимум тут стоит поиграть с коэффициентом и найти такой, для которого будут промежутки между буквами.
Для разбиения строки на массив букв есть хак - preg_split("//u", $text, null, PREG_SPLIT_NO_EMPTY), хотя твой способ тоже интересный, если ты сам до него додумался.
> 'y' => round(cos(deg2rad($angle $i)) $radius),
Тут надо еще добавлять координату центра, а то будут получаться отрицательные значения
В общем, советую переделать.
Скорее всего для FTP нужен отдельный логи и пароль, может он в письме, может в панели управления.
>>31619
> Круг строится, но выглядит как дерьмо.
Идеально он наверно и не будет выглядеть так как буквы ставятся не как угодно, а по сетке. Ну и коэффициент у тебя маловат: 1.1105 - там должно быть что-то в районе 1.4 ... 2. У тебя круг сплющенный из-за этого.
По коду: оформлено все плохо. Тебе надо работать над тем, чтобы делать код более понятным и лаконичным. Ну например код вывода данных на экран переусложнен.
Для вывода достаточно сделать 2-мерный массив, то есть массив строк, где каждая строка представлена как массив символов:
$field = [
[' ', ' ', ' '],
[' ', ' ', ' '],
[' ', ' ', ' ']
];
Тогда установка буквы в определенную точку делается так:
$field[$y][$x] = 'А';
А вывод можно сделать например через цикл по строкам + implode для вывода строки.
Ты же выбрал другой способ хранения и как следствие получился запутанный код. Ты еще добавил сюда неудачные названия переменных ($xlast вместо $lastX или $xLast) и не смог разбить сложный код на отдельные простые функции. Ну давай посмотрим например на это:
for ($z=0;$z<$xlast;$z++) {
$echoTemp =$echoTemp.' ';
}
Это явно стоит вынести в отдельную функцию, генерирующую заданное число пробелов. Более того, такая функция уже есть - погугли str_repeat.
> $radius = ceil($leng/4);
Ты не предусмотрел промежутки между буквами, а также не учел что буквы не являются идеальными квадратами, а имеют прямоугольную форму. Ну как минимум тут стоит поиграть с коэффициентом и найти такой, для которого будут промежутки между буквами.
Для разбиения строки на массив букв есть хак - preg_split("//u", $text, null, PREG_SPLIT_NO_EMPTY), хотя твой способ тоже интересный, если ты сам до него додумался.
> 'y' => round(cos(deg2rad($angle $i)) $radius),
Тут надо еще добавлять координату центра, а то будут получаться отрицательные значения
В общем, советую переделать.
О, интересное реешние где профессия сделана отдельным объектом.
> public function setProfession ($profession) {
> public function setEmployee ($employee){
тут нужен тайп-хинт. Также по PSR вроде фигурная скобка ставится на новой строке. И лучше не set, а add.
Для классов профессий - ты переопределяешь поля с зарплатой, названием. Но тут есть недостатки:
- если кто-то хочет сделать новую профессию. не очевидно что надо переопределить поля (ок, тут класс маленький и в принципе можно догадаться, но в более сложных классах где много полей и методов - нельзя)
- если кто-то забудет переопределить поле, это никак не обнаружится.
Не хочешь попробовать решить обе проблемы за счет абстрактных методов?
> public $departaments = array(); // департаменты
Лучше наверно будет сделать приватным чтобы снаружи нельзя было что попало туда запихнуть или что-то сделать.
> public function getAvrSalary (){
average сокращается как avg, а еще лучше было бы не сокращать вообще. Если ты еще не сделал это, поставь себе удобный редактор или IDE.
> $avrSalary = round($this->getTotalSalary () / count($this->departaments), 2)
Вообще, окргление лучше делать при выводе, а тут давать точные данные. Кто знает как они испоьзоваться будут.
кстати, я подумал, а ведь Департамент не существует вне Компании, может имеет смысл тут применить композицию вместо агрегации? Хотя можно и так оставить.
> function creationEmployees (
Функции начинаются с глагола, создатьСотрудника, а не создание.
> for ($id = 1; $id <= $vacancyQuantity; $id++) {
> $employees[$id] = new Employee($id);
id уникален только в пределах Департамента - что делать при переводе сотрудника? Лучше либо сделать полностью уникальные id либо отказаться так как объект сам по себе уникален и может сам себя отличать от других.
> $employees[$id]->setProfession($professions[$profession]);
> $employees[$id]->setRank($rank);
Лучше делать это на переменной, а только потом добавлять в массив - меньше скобок будет.
> $departament = $vector->departaments[$depName];
Вот тут явно стоило бы вместо прямого обращения использовать метод поиска по названию. А еще лучще - сделать чтобы функция работала с одним департаментом за раз и он передавался в нее объектом.
> public function padLeft($string, $length) {
зачем этой функции быть публичной? Она же вроде для внутренненго использования?
Конструктор стоит писать первым в списке методов. Вообще, порядок такой: константы, публичные поля, непубличные, конструкторы, публичные методы, непубличные. Чтобы читать было удобнее.
> $this->str .= "\n" . $this->padRight($str, $this->length);
Логичнее хранить исходные загловки, а форматировать только при выводе.
> if ($caption==1) {
> // Добаляем горизонтальное поддчеркивание снизу, длина колонки * кол-во колонок
Логичнее черту по моему выводить в методе таблицы.
Вместо public $str лучше сделать метод getAsString() или render().
> unset($strs); // обнуляем массив
$strs = []; (что за название странное?)
В общем, неплохо, давай теперь еще антикризисные меры.
О, интересное реешние где профессия сделана отдельным объектом.
> public function setProfession ($profession) {
> public function setEmployee ($employee){
тут нужен тайп-хинт. Также по PSR вроде фигурная скобка ставится на новой строке. И лучше не set, а add.
Для классов профессий - ты переопределяешь поля с зарплатой, названием. Но тут есть недостатки:
- если кто-то хочет сделать новую профессию. не очевидно что надо переопределить поля (ок, тут класс маленький и в принципе можно догадаться, но в более сложных классах где много полей и методов - нельзя)
- если кто-то забудет переопределить поле, это никак не обнаружится.
Не хочешь попробовать решить обе проблемы за счет абстрактных методов?
> public $departaments = array(); // департаменты
Лучше наверно будет сделать приватным чтобы снаружи нельзя было что попало туда запихнуть или что-то сделать.
> public function getAvrSalary (){
average сокращается как avg, а еще лучше было бы не сокращать вообще. Если ты еще не сделал это, поставь себе удобный редактор или IDE.
> $avrSalary = round($this->getTotalSalary () / count($this->departaments), 2)
Вообще, окргление лучше делать при выводе, а тут давать точные данные. Кто знает как они испоьзоваться будут.
кстати, я подумал, а ведь Департамент не существует вне Компании, может имеет смысл тут применить композицию вместо агрегации? Хотя можно и так оставить.
> function creationEmployees (
Функции начинаются с глагола, создатьСотрудника, а не создание.
> for ($id = 1; $id <= $vacancyQuantity; $id++) {
> $employees[$id] = new Employee($id);
id уникален только в пределах Департамента - что делать при переводе сотрудника? Лучше либо сделать полностью уникальные id либо отказаться так как объект сам по себе уникален и может сам себя отличать от других.
> $employees[$id]->setProfession($professions[$profession]);
> $employees[$id]->setRank($rank);
Лучше делать это на переменной, а только потом добавлять в массив - меньше скобок будет.
> $departament = $vector->departaments[$depName];
Вот тут явно стоило бы вместо прямого обращения использовать метод поиска по названию. А еще лучще - сделать чтобы функция работала с одним департаментом за раз и он передавался в нее объектом.
> public function padLeft($string, $length) {
зачем этой функции быть публичной? Она же вроде для внутренненго использования?
Конструктор стоит писать первым в списке методов. Вообще, порядок такой: константы, публичные поля, непубличные, конструкторы, публичные методы, непубличные. Чтобы читать было удобнее.
> $this->str .= "\n" . $this->padRight($str, $this->length);
Логичнее хранить исходные загловки, а форматировать только при выводе.
> if ($caption==1) {
> // Добаляем горизонтальное поддчеркивание снизу, длина колонки * кол-во колонок
Логичнее черту по моему выводить в методе таблицы.
Вместо public $str лучше сделать метод getAsString() или render().
> unset($strs); // обнуляем массив
$strs = []; (что за название странное?)
В общем, неплохо, давай теперь еще антикризисные меры.
Дополнение к вопросу 2: возможность получать доступ из хелпера к мапперу возникла из-за того, что в форме для редактирования профиля нужно откуда-то брать значения, которые будут передаваться в value="" как дефолтные значения для инпутов.
Как сейчас это работает, если убрать лишнее:
- если пользователь зарегистрирован, получаю email этого пользователя
- обращаюсь к мапперу и получаю модель этого пользователя
- данные модели передаю во view, а во view-файле распихиваю переданные данные по value=""
Быть может, стоит при логине записывать в сессию все данные о пользователе, которые можно редактировать? Из сессии проще достать их потом и распихать по input'ам, через каждый раз запрос в БД делать.
2. Почитал я про DI у тебя в гистах и понял, что у меня в классе App подобие Registry и сделать из этого DI никак не получается. Service Locator подошёл лучше, невзирая на его недостатки; подключение к БД теперь вне App (пик).
>>32572
>Насчет странности я не понимаю о чем речь, взял тут https://laravel.com/docs/5.2/queries#retrieving-results
Если в Laravel можно делать X несколькими способами, то не стоит брать самые неочевидные. Query Builder следует использовать, когда Eloquent ORM недостаточно, а для подсчёта значений в таблице этой ORM достаточно. А вместо непонятно откуда берущегося Input обычно используют объект класса Request, которые передаётся в контроллер параметром.
PHP и MySQL Создание интернет-магазинов Ларри Ульман.
Господа знатоки, но не забудьте и мне про блоги что-нибудь посоветовать.
Пик отвалился. Даже показывать немного стыдно. Это тот класс, который мастер на все руки.
>Из сессии проще достать их потом и распихать по input'ам, через каждый раз запрос в БД делать.
*через каждый раз запрос в БД делать.
Безуспешно пытаюсь не плодить посты.
>>37434
>Книгу про интернет-магазины я нашёл, а про блоги нет.
Вот посмотри, сможешь ли ты реализовать такой примитивный блог: http://www.blogovo.ru/testovoe-zadanie-programmistu-php-junior
Если да, то тебе осталось добавить аутентификацию и комментарии. Если с этим проблемы, то задачка на список студентов их решит, так как суть везде одна и та же. И не нужны тебе целые книги, посвящённые созданию блогов.
Что скажете насчёт опечаточника? http://ideone.com/yhglMW
А, нет, это я аутист.
>- если кто-то хочет сделать новую профессию. не очевидно что надо переопределить поля (ок, тут класс маленький и в принципе можно догадаться, но в более сложных классах где много полей и методов - нельзя)
>- если кто-то забудет переопределить поле, это никак не обнаружится.
>
>Не хочешь попробовать решить обе проблемы за счет абстрактных методов?
Хочу, только я не понимаю как это сделать. Можно пример?
Я рассуждаю так:
Если я создаю абстрактный метод setSalaryRate, в дочернем классе он должен быть переопределен в обязательном порядке. Тогда программист, который решил создать новый дочерний класс (новую профессию), должен будет его описать. Но, если в данной реализации, программист может пропустить переопределение свойств, то что ему мешает скопипастить этот метод с класса профессии, которая уже существует, и в которой я описал этот метод. Тогда метод должен не просто переопределять свойство setSalaryRate, но и запрашивать значение при создании экземпляра? Тогда, логично описать этот метод в конструкторе. (Значит метод конструктор в классе предке будет абстрактным). И получится примерно такой функционал конструктора: при создании экземпляра класса, конструктор принимает аргументом значение setSalaryRate.
Но опять же, что если он не скопирует этот метод, и не будет описывать вообще какой либо функционал. Т.е. метод уже не абстрактный, но при этом ничего не делает?
Значит, реализация которую я представляю, не защитит от этой ошибки. Как сформулировать этот вопрос гуглгу?
К слову я уже задавал этот вопрос в этом треде >>35032,
думал, что мне ответил ОП >>35111
Опечаточник в порядке.
А в задаче с проверкой на ошибки можно сделать ассоциативный массив с регулярками в ключах и описаниями ошибок в значениях - это если без функции.
['/жы/ui' => 'Жи/ши пиши с буквой \"И\"', '/сдесь/ui' => 'Правильно писать \"Здесь\"', '/координально/ui' => 'Правильно \"кардинально\"'];
Дальше цикл foreach, который при соответствии найденным ошибкам (регулярки в ключах) будет выдавать описание ошибки (описания в значениях массива).
А так прилагай код всегда, так быстрее и точнее тебе ответят.
>>37607
Если символа между цифрами два и более - не захватывает весь номер. Это исправь.
Главное изменение - внедрил класс Компания.
Вот твой разбор в прошлом треде: >>731915
Кое-что прояснело уже после того, как я это решение сделал, как обычно.
Получается, нам нужно создавать всё-таки кучу экземпляров класса Сотрудник, а потом приписывать их экземпляру класса Департамент? Ну и после этого экземпляры Департамента приписывать классу Компания?
Твой разбор сохранил, позже попробую именно по тем рекомендациям всё сделать.
Нереально ты помогаешь, братишка, куда бы я без тебя...
я сколько пытался чего-то менять, но никак ничего не получается. Ты не мог бы конкретно указать, что на что менять, а то я без сил -_-
А где у тебя первый вариант? Он был ближе к тому, что нужно.
Когда уже доходит до проверки чисел ЗА +7 и 8, то нужна регулярка "любое количество скобок, минусов, пробелов в любом порядке либо вообще ни одного", а дальше цифры.
Тебе понадобятся квадратные скобки, перечисление внутри них разных знаков, которые не могут быть или не быть, за скобками знак "любое количество указанного или полное отсутствие" - и всё. Дальше сами цифры от 0 до 9 повторить 10 раз.
Так немного понятнее?
Гуглишь Simple HTML DOM, читаешь доку: http://simplehtmldom.sourceforge.net/manual.htm
Ты массив сможешь в БД вставить foreach'eм? Так вот, контент сразу парсишь в массив массивов массивов . Пробегаешься по массиву поэлементно, каждый элемент массива суёшь в базу запросами вида "INSERT INTO ..."
Для "вкатиться" тебе этого с головой хватит.
>>37634
Гуглишь что-то вроде "how learn X properly", получаешь гайд вроде http://javascriptissexy.com/learn-node-js-completely-and-with-confidence/
Не отвлекайте ОПа тем, что на первых станицах гугла можете легко найти сами.
все верные номера стали верными, а далее я никак, прошу помощи, черт подери
http://ideone.com/vw5P0b
Тут ты не даёшь понять, что все символы должны быть тесно связаны с цифрами.
Попробуй объединить круглыми скобками все знаки и цифру - и уже этому задай количество повторений - 10.
>>37736
А вот тут ты исправляешься, но забываешь про то, что в квадратных скобках символы меняют своё значение и большинство из них экранировать не надо. Убери несколько обратных слэшей из квадратных скобок.
Насколько я понимаю могут быть три случая неправильного расположения знака препинания и один правильный:
1)скачать , но
2)скачать ,но
3)скачать,но
4)скачать, но (правильный). Но неужели можно подобрать такую регулярку, чтобы она одновременно находила первые три и не находила четверую?
Аа, ты ещё не определяешь конкретно, чтобы проверка была именно этого номера, а не отдельных частей текста.
Вспомни про циркумфлекс и знак доллара, там было это в начале или середине урока.
>[+7|8]
Как ты это себе представляешь? Не имеет смысла ставить в квадратные скобки сравнение, потому что квадратные скобки - это "один из указанных символов в случайном порядке".
Тем более, что есть номера, которые начинаются с "+ 7" - как ты такие захватишь?
Давай-давай, постигай основы.
Это достаточно непростая задача для новичка, но надо самому дойти до всего.
Можно.
Условий всего два:
1. Перед запятой не должно быть пробела.
2. После запятой не должна сразу идти буква.
ОП, посмотри задачи, пожалуйста. Ты меня пропустил
Нет, идея такая:
abstract class A
{
abstract protected function getBaseSalary();
}
class B extends A
{
protected function getBaseSalary()
{
return 500;
}
}
то есть мы отказываемся от свойства и используем метод вместо него.
> Но, если в данной реализации, программист может пропустить переопределение свойств, то что ему мешает скопипастить этот метод с класса профессии, которая уже существует
Если он копирует код не думая, то, конечно, ничто ему не поможет. Идея другая.
- когда мы используем абс. методы, достаточно посмотреть на список абс. методов и понятно как именно сделать класс-наследника
- когда мы их не используем, то надо полностью анализиовать код класса и думать что надо переопределить. Обычно это дольше. У тебя конечно код классов-профессий очень простой и можно обойтись без абс. методов, но если бы у тебя были более сложные классы, то без абс. методов было бы тяжело.
Ищем такие последовательности:
(любое число пробелов в том числе 0) (знак препинания) (любое число пробелов в том числе 0)
Так как квантификаторы по умолчанию жадные то такая регулярка захватит максимально возможное число пробелов. И заменяем на
(знак препинания) (1 пробел)
>- в модели студента есть метод хеширования пароля, но не лучше ли если бы он был в хелпере отвечающем за авторизацию? Если посмотреть, этот метод вообще к $this не обращается и непонятно что он там делает в классе студента.
>- методы генерации соли и токена - не лучше ли разместить в классе авторизации?
Хелпер отвечающий за авторизацию и класс авторизации это те же самые вещи?
>>717443
>Вот представь что мы хотим взять модель студента и заполнить форму его данными. Сколько для этого кода надо? А хотелось бы чтобы это делалось в 2-3 строчки:
>
>$form->setStudent($sudent);
С использованием композиции, я лишился надобности заполнять данными Студента после валидации (класс формы заполняет его автоматически), и перед добавлением студента в дб остается только поменять в нём пароль. У меня появляются некоторые сомнения насчет того что это должен делать контроллер, а не класс формы. Вот небольшой отрывок из контроллера: https://ideone.com/yIPuZ6
Мы вместо двух строчек можем получить одну
//код метода Формы
function setStudentPassword()
{
$this->student->setPassword($this->password));
}
//код в контроллере
if (!$errors->hasErrors()) {
$registerStudentForm->setStudentPassword();
...
}
>>717443
>Насчет логина по имени - странная идея. Разве имена уникальны? Нужно тогда как минимум проверять что имя уникально и если нет, не разрешать логиниться через него. Также, надо иметь не 3 поля, а одно, в которое можно ввести что угодно. Это же неудобно, когда в форме лишние поля.
Логика такова, что мало вероятно что имя и пароль будут совпадать у разных людей.
>>717443
>Наконец обрати внимание еще на это место:
>
>> https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L42
>Чтобы залогиниться у тебя надо написать сложный код. Почему бы не сделать проще, напрмиер так:
>
>if ($authService->isValidPassword($student, $password)) {
>$authService->login($student);
>....
>}
$authService это все тот же класс авторизации о котором я спрашивал выше?
>>717443
>Для авторизации не стоит исплоьзовать сессию. Она же устаревает и удаляется через 20-30 минут неактивности. Лучше просто использовать куку с id и хешем пароля.
А разве я не должен спросить у пользователя хочет ли он оставаться залогиненым?
>>717446 >>717241
>>Я захотел сделать чтобы для залогинивания можно было использовать и почту, и фамилию, и имя, я подошел к этому таким способом:
>>https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L40
>>Это можно как-нибудь сократить или это нужно вынести в отдельную функцию чтобы сделать контроллер 'тонким'? Если выносить в отдельную функцию, то кто этим должен заниматься? Контроллер или хелпер?
>Можно, но я думаю что не имеет смысла так как этот код нигде никогда больше не понадобится. Сам код цикла содержит много ошибок.
Стыдно спрашивать, но можно подсказку в каких местах ошибки?
Нет ничего же плохого в том чтобы залогиниваться через резные типы данных?
>>717446
>> Что будет если сделать редирект перед остановкой цикла?
>Не знаю, а что-то дложно быть? Редирект это просто выдача заголовка. после него код выполнять особого смысла нет.
Перефразирую вопрос: Что становится с кодом после ридеректа? Выполняется функция die()?
>>717446
>Значит, надо сделать такой метод:
>...->ispasswordValid($student, $password)
>Почему контроллер должен считать какие-то хеши? Почему он вообще должен знать что у нас исплоьзуются хеши? Это ответственность хелпера (сервиса) авторизации, знать как она устроена. Контроллер просто просит сервис проверить пароль на правильность. Или залогнинить студента. Или разлогинить. Не вдаваясь в детали, как он устроено.
>Это ответственность хелпера (сервиса) авторизации, знать как она устроена.
А почему не ответственность валидатора? В названии функции присутствует слово Valid, значит это как-то относится к валидации. И если, мы отнесем его к валидации то как не запутаться между этим методом и методом проверки на правильность написания пароля, которая нужна при регистрации?
>- в модели студента есть метод хеширования пароля, но не лучше ли если бы он был в хелпере отвечающем за авторизацию? Если посмотреть, этот метод вообще к $this не обращается и непонятно что он там делает в классе студента.
>- методы генерации соли и токена - не лучше ли разместить в классе авторизации?
Хелпер отвечающий за авторизацию и класс авторизации это те же самые вещи?
>>717443
>Вот представь что мы хотим взять модель студента и заполнить форму его данными. Сколько для этого кода надо? А хотелось бы чтобы это делалось в 2-3 строчки:
>
>$form->setStudent($sudent);
С использованием композиции, я лишился надобности заполнять данными Студента после валидации (класс формы заполняет его автоматически), и перед добавлением студента в дб остается только поменять в нём пароль. У меня появляются некоторые сомнения насчет того что это должен делать контроллер, а не класс формы. Вот небольшой отрывок из контроллера: https://ideone.com/yIPuZ6
Мы вместо двух строчек можем получить одну
//код метода Формы
function setStudentPassword()
{
$this->student->setPassword($this->password));
}
//код в контроллере
if (!$errors->hasErrors()) {
$registerStudentForm->setStudentPassword();
...
}
>>717443
>Насчет логина по имени - странная идея. Разве имена уникальны? Нужно тогда как минимум проверять что имя уникально и если нет, не разрешать логиниться через него. Также, надо иметь не 3 поля, а одно, в которое можно ввести что угодно. Это же неудобно, когда в форме лишние поля.
Логика такова, что мало вероятно что имя и пароль будут совпадать у разных людей.
>>717443
>Наконец обрати внимание еще на это место:
>
>> https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L42
>Чтобы залогиниться у тебя надо написать сложный код. Почему бы не сделать проще, напрмиер так:
>
>if ($authService->isValidPassword($student, $password)) {
>$authService->login($student);
>....
>}
$authService это все тот же класс авторизации о котором я спрашивал выше?
>>717443
>Для авторизации не стоит исплоьзовать сессию. Она же устаревает и удаляется через 20-30 минут неактивности. Лучше просто использовать куку с id и хешем пароля.
А разве я не должен спросить у пользователя хочет ли он оставаться залогиненым?
>>717446 >>717241
>>Я захотел сделать чтобы для залогинивания можно было использовать и почту, и фамилию, и имя, я подошел к этому таким способом:
>>https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L40
>>Это можно как-нибудь сократить или это нужно вынести в отдельную функцию чтобы сделать контроллер 'тонким'? Если выносить в отдельную функцию, то кто этим должен заниматься? Контроллер или хелпер?
>Можно, но я думаю что не имеет смысла так как этот код нигде никогда больше не понадобится. Сам код цикла содержит много ошибок.
Стыдно спрашивать, но можно подсказку в каких местах ошибки?
Нет ничего же плохого в том чтобы залогиниваться через резные типы данных?
>>717446
>> Что будет если сделать редирект перед остановкой цикла?
>Не знаю, а что-то дложно быть? Редирект это просто выдача заголовка. после него код выполнять особого смысла нет.
Перефразирую вопрос: Что становится с кодом после ридеректа? Выполняется функция die()?
>>717446
>Значит, надо сделать такой метод:
>...->ispasswordValid($student, $password)
>Почему контроллер должен считать какие-то хеши? Почему он вообще должен знать что у нас исплоьзуются хеши? Это ответственность хелпера (сервиса) авторизации, знать как она устроена. Контроллер просто просит сервис проверить пароль на правильность. Или залогнинить студента. Или разлогинить. Не вдаваясь в детали, как он устроено.
>Это ответственность хелпера (сервиса) авторизации, знать как она устроена.
А почему не ответственность валидатора? В названии функции присутствует слово Valid, значит это как-то относится к валидации. И если, мы отнесем его к валидации то как не запутаться между этим методом и методом проверки на правильность написания пароля, которая нужна при регистрации?
Вот и молодец.
Ну а вот мой вариант, который сделал, чтобы показать тебе, если совсем не пойдёт: http://ideone.com/bRC90j
То же самое, только кое-что местами поменяно.
Регулярные выражения вообще интересные штуки, мощная тема, надо бы что-нибудь дополнительно изучить по ним, я всё хотел.
Вообще неправильный подход вроде.
Там чуть выше ОП расписал алгоритм, попробуй его буквально воссоздать в регулярке, базарю, ещё захочешь, поцене 38 рублей.
pascal
питончик няшный и джанго там попроще какого нибудь zend'a будет
Выбирай то на чём хочешь зарабатывать деньги, если конечная цель именно это.
спасибо большое
Да и вроде Java скорее больше для мобильных приложений, и для программ для ПК, чем для веб. Безусловно на Java можно и бэкенд сделать, но вакансий и фриланс работ не так уж и много по этому направлению как для PHP, JS.
> Хелпер отвечающий за авторизацию и класс авторизации это те же самые вещи?
да
> Мы вместо двух строчек можем получить одну
Тогда наверно стоит сделать чтобы и пароль автоматически проставлялся.
> Логика такова, что мало вероятно что имя и пароль будут совпадать у разных людей.
Имя в принципе не уникально так как Иванов много. Не надо полагаться на это вот "маловероятно".
> $authService это все тот же класс авторизации о котором я спрашивал выше?
Наверно да
> А разве я не должен спросить у пользователя хочет ли он оставаться залогиненым?
не знаю. Можешь спрашивать, я не против. Но и в этом случае сессия не нужна.
> Стыдно спрашивать, но можно подсказку в каких местах ошибки?
код если честно создает ощущение что автор не понимает ни как работает цикл ни как надо использовать переменные.
> foreach ($student as $key => $value) {
> if (!count(array_filter($student)))
зачем нужна эта строка? она проверяет не ткеущий элеиент массива что еще было бы логично, а весь массив. Весь массив ты мог бы проверить и до цикла а не во время.
> unset($student[$key]);
Зачем во время цикла удалять элементы из массива? Точнее зачем их вообще удалять?
> $student = $value;
Зачем ты заменяешь массив студентов на одного студента? как читать твой код когда по ходу цикла переменная заменяется на другую и тот же самый код начинает обозначать совсем другое?
> $_SESSION['id'] = $student->getId();
> $_SESSION['name'] = $student->getName();
> $_SESSION['surname'] = $student->getSurname();
> $_SESSION['token'] = $student->getToken();
Зачем класть так много данных? В чем преимущество сессии перед куками? Как ты сделашь залогинивание дольше чем на полчаса?
> $loginStudentForm->getError('login', "Incorrect username or password")
Непонятно почему этот код внутри цикла. Ты хочешь несколько раз ошибку добавить?
> Перефразирую вопрос: Что становится с кодом после ридеректа?
Можно die можно return, так даже аккуратнее мне кажется.
> А почему не ответственность валидатора?
Потому что проверка пароля обычно подразумевает получение его хеша. Если ты это поместишь в валидатор то получается чтобы поставить студенту пароль надо вызывать валидатор даже если мы не хотим ничего проверять? это же нелогично.
И как в твоем сценарии проверить праивльность логина и пароля не имея формы? Ведь пароль можно и не только в форму логина ввести, и вообще не в форму. А например в консоли или через API. Ты сможешь в этих случаях его проверить?
Если ты думаешь что редактирование данных того же студента делается только через форму то это уже ошибка. В MVC мы разделяем ответственность: один компонент (контроллер) у нас принимает данные от пользователя через форму, другой (модель) изменяет состояние приложения и Бд по команде от контроллера. Если у тебя поменять пароль можно только через форму, то это значит принцип MVC у тебя не соблюдается, а ты его пока до конца не понял.
В MVC для смены или проверки пароля форма не обязательна. И кстати проверить данные в модели студента тоже должно быть можно без формы.
Потому выгоднее может быть сделать что валидатор лишь проверяет соответсвие данных опредленному формату, а за проверку пароля отвечает не он. Но можешь делать как хочешь, если сделаешь неправильно я все равно увижу ошибку.
> Хелпер отвечающий за авторизацию и класс авторизации это те же самые вещи?
да
> Мы вместо двух строчек можем получить одну
Тогда наверно стоит сделать чтобы и пароль автоматически проставлялся.
> Логика такова, что мало вероятно что имя и пароль будут совпадать у разных людей.
Имя в принципе не уникально так как Иванов много. Не надо полагаться на это вот "маловероятно".
> $authService это все тот же класс авторизации о котором я спрашивал выше?
Наверно да
> А разве я не должен спросить у пользователя хочет ли он оставаться залогиненым?
не знаю. Можешь спрашивать, я не против. Но и в этом случае сессия не нужна.
> Стыдно спрашивать, но можно подсказку в каких местах ошибки?
код если честно создает ощущение что автор не понимает ни как работает цикл ни как надо использовать переменные.
> foreach ($student as $key => $value) {
> if (!count(array_filter($student)))
зачем нужна эта строка? она проверяет не ткеущий элеиент массива что еще было бы логично, а весь массив. Весь массив ты мог бы проверить и до цикла а не во время.
> unset($student[$key]);
Зачем во время цикла удалять элементы из массива? Точнее зачем их вообще удалять?
> $student = $value;
Зачем ты заменяешь массив студентов на одного студента? как читать твой код когда по ходу цикла переменная заменяется на другую и тот же самый код начинает обозначать совсем другое?
> $_SESSION['id'] = $student->getId();
> $_SESSION['name'] = $student->getName();
> $_SESSION['surname'] = $student->getSurname();
> $_SESSION['token'] = $student->getToken();
Зачем класть так много данных? В чем преимущество сессии перед куками? Как ты сделашь залогинивание дольше чем на полчаса?
> $loginStudentForm->getError('login', "Incorrect username or password")
Непонятно почему этот код внутри цикла. Ты хочешь несколько раз ошибку добавить?
> Перефразирую вопрос: Что становится с кодом после ридеректа?
Можно die можно return, так даже аккуратнее мне кажется.
> А почему не ответственность валидатора?
Потому что проверка пароля обычно подразумевает получение его хеша. Если ты это поместишь в валидатор то получается чтобы поставить студенту пароль надо вызывать валидатор даже если мы не хотим ничего проверять? это же нелогично.
И как в твоем сценарии проверить праивльность логина и пароля не имея формы? Ведь пароль можно и не только в форму логина ввести, и вообще не в форму. А например в консоли или через API. Ты сможешь в этих случаях его проверить?
Если ты думаешь что редактирование данных того же студента делается только через форму то это уже ошибка. В MVC мы разделяем ответственность: один компонент (контроллер) у нас принимает данные от пользователя через форму, другой (модель) изменяет состояние приложения и Бд по команде от контроллера. Если у тебя поменять пароль можно только через форму, то это значит принцип MVC у тебя не соблюдается, а ты его пока до конца не понял.
В MVC для смены или проверки пароля форма не обязательна. И кстати проверить данные в модели студента тоже должно быть можно без формы.
Потому выгоднее может быть сделать что валидатор лишь проверяет соответсвие данных опредленному формату, а за проверку пароля отвечает не он. Но можешь делать как хочешь, если сделаешь неправильно я все равно увижу ошибку.
> Получается, нам нужно создавать всё-таки кучу экземпляров класса Сотрудник, а потом приписывать их экземпляру класса Департамент? Ну и после этого экземпляры Департамента приписывать классу Компания?
Да
И судя по тому что в Компании есть поля вроде public $allSalary = 0; ты еще не все исправил.
По моему это глупость так как в JS даже классов нормальных нет. Ни тайп хинтов. Ни многопоточности. Ни фреймворков нормальных. Как ты на нем приложения писать собрался?
Ну если тебе интересно, изучай ноду, express, что еще там есть. Довольно много придется изучать включая линукс, линукс-сокеты, сетевое программирование, асинхронную модель. Потом изучай как это прикручивать к базе данных, формам и тд.
Так же как раз есть 2 задачи имеющих некоторое отноешние к яваскрипту:
Сапер с MVC https://github.com/codedokode/pasta/blob/master/js/minesweeper-mvc.md
SPA приложение https://github.com/codedokode/pasta/blob/master/js/spa.md
Можешь глянуть.
Он не нужен если ты разбираешься в протоколе HTTP, знаешь библиотеку-HTTP-клиент, DOM, сами базы данных - тогда тебе все и так очевидно. Вот по этим темам уроки может быть были бы полезны, но считается что они относительно легкие.
Блог лучше делать на фреймворке или микрофреймворке. Задача на студентов только для изучения основ а не пример как надо писать приложения так как в ней слишком много "велосипедов" которые ты пишешь сам хотя лучше использовать готовое.
> Пик
new Session это уже странно. Зачем ты создаешь объхект который сразу же удаляется? Может проще его не создавать тогда?
Алсо что за нездоровая традиция первым делом создавать сессию? Видеокурсы?
Непонятно зачем ты указываешь путь к конфигу в методе setStudentMapper - зачем он там?
НЕпонятно почему в базовом классе контроллера есть публичные статические поля и тем более предок не дложен знать ничего о своих наследниках.
Картинка демонстрирует непонимание принципов ООП автором.
Также видно нездоровое увлечение цепочечными вызовами методв что на мой взгляд скорее дурнгая привычка так как замусоривает код нелогичными return $this. Если в языке нет цепоченчных вызовов то ничего не поделать, пишите в столбик.
непонятно почему в пути к шаблону пропущено расширение. Не то чтобы это ошибка, просто странно выглядит. Вроде как путь к файлу, а вроде и нет.
Не хватает пагинации и сортировки.
на пике я вижу очень странное слово self:: . Это точно ООП?
> 1) Если упростить, в DataMapper для манипуляции данными в БД нужно первым делом найти модель по id, результат-объект в переменную; теперь для изменения/добавления данных нужно обращатся только к этому объекту; дальше у маппера вызывается метод save(), куда передаётся переменная-объект.
вообще в навороченных мапперах в save не надо передавать модель. Они сами умеют находить изменения в любом числе моделей и сохранять их в БД. Более простые не умеют и там надо все вручную передавать.
> Правильно понимаю? У меня сейчас именно так всё и работает.
Не совсем. Ты описал выше не прицип, а просто пересказал один из примеров реализации. Прицип маппера в том что модель никак не связана с базой и ничего о ней не знает. Сохранением и загрузкой мдели занимается сторонний класс. Теоретически ты можешь брать модель и не из базы, вообще убрать базу и создать модель вручную, и остальой код будет раотать как ни в чем не бывало.
Маппер противопоставляется active record так как в AR модель умеет сама работать с базой. бизнес-логика и работа с БД тесно смешаны в одном классе. Главная разница в этом.
> 2) Может ли хелпер обращаться к мапперу?
Да так как это более высокоуровневый слой.
> <?php if (\App\Helper\Auth::check()): ?>
У тебя странный код. Ты хочешь проверить залогинен ли пользователь но ничего не передаешь в функицю. Откуда она интересно поймет что проверять? Это как функция сложения в которую не передаются слагаемые.
> 3) Я гуглил Front Controller и ничего не понял.
FC это подход когда все запросы первоначально попадают в этот самый FC и он дальше решает что с ними делать. Это вообще не обязан быть класс, суть в том что все запросы проходят сначала через один общий обработчик.
> В объекте App чуть меньше 100 строк, но он слишком много разных вещей делает. Это норма?
не знаю.
> что в форме для редактирования профиля нужно откуда-то брать значения, которые будут передаваться в value="" как дефолтные значения для инпутов.
А ты читал урок про обработку форм? Там же вроде написано откуда.
> Быть может, стоит при логине записывать в сессию все данные о пользователе, которые можно редактировать? Из сессии проще достать их потом и распихать по input'ам, через каждый раз запрос в БД делать.
Вижу что не читал. Иди и перечитай.
Подскажите, я вообще в правильном направлении иду, а то откуда-то возникает такое чувство, что я что-то упустил, и там может вообще двумя строчками все решается, а я тут горожу велосипеды через циклы.
На Java пишутся массивные проекты, которые фрилансерам отдавать не будут, ровно поэтому нет разработки, но разного рода доработки и фиксы периодически заказывают. Btw, новичкам там делать нечего совершенно.
>И судя по тому что в Компании есть поля вроде public $allSalary = 0; ты еще не все исправил.
Ох щи, что-то забыл убрать.
Я сначала прописал то, что просто должно там считаться, в этом классе, а потом забыл убрать.
В принципе, там у нас всё то, что высчитывается из другого, поэтому нам там никакие поля не нужны. Я помню, ты говорил кому-то, что только обязательные свойства там надо указывать, а остальное всё получать с помощью методов.
http://ideone.com/AVe2d4 - так и не удаётся добиться того, чтобы работало с УРЛом в начале текста. Пробел ставишь - всё как нужно.
Чего не хватает? Как указать, что если другие буквы есть перед УРЛом, то он не нужен?
Туплю, помогите.
Дайте, пожалуйста, ссыль на гитхаб с задачей студента сделанной идеально на ООП. Буду сидеть и копаться в коде.
>new Session это уже странно. Зачем ты создаешь объхект который сразу же удаляется? Может проще его не создавать тогда?
Обёртка над session_start(). Проще не создавать.
>Алсо что за нездоровая традиция первым делом создавать сессию?
Так эта функция должна вызываться до любого выхлопа на страницу. Однако её и вправду стоит засунуть туда, где успешно произошла аутентификация.
>Непонятно зачем ты указываешь путь к конфигу в методе setStudentMapper - зачем он там?
С этим проблема, я долго пытался отвязать конфиг от маппера. Сейчас маппер получает уже объект PDO вот так: new StudentMapper(Config::getPDOconnection);
Так лучше? Это максимум, до чего я смог додуматься.
>НЕпонятно почему в базовом классе контроллера есть публичные статические поля
Куда положить дефолтные controller и action? В методы setController и setAction?
>нездоровое увлечение цепочечными вызовами методв что на мой взгляд скорее дурнгая привычка так как замусоривает код нелогичными return $this.
С return $this можно использовать $obj->foo()->bar()->baz() вместо $obj->foo(); $obj->bar(); $obj->baz()
Хорошо, я постараюсь убрать цепочные вызовы там, где они лишние.
>непонятно почему в пути к шаблону пропущено расширение.
Ну так DRY, во всех аж 2-х фреймворках, на которых я крудил, расширение не указывается. В моём случае все шаблоны имеют расширение .php и можно в методе view один раз конкатенировать первый параметр с ".php", вместо того, чтобы в каждом вызове view() дописывать php.
>> <?php if (\App\Helper\Auth::check()): ?>
Ты хочешь проверить залогинен ли пользователь но ничего не передаешь в функицю.
Этот метод проверяет наличие $_SESSION['email']. Что передавать в метод, который проверяет, залогинен ли пользователь?
>> что в форме для редактирования профиля нужно откуда-то брать значения, которые будут передаваться в value="" как дефолтные значения для инпутов.
>А ты читал урок про обработку форм? Там же вроде написано откуда.
Там написано, что после отправления формы нужно заполнить values тем, что пришло (если ошибок нет). А вот если я уже зарегистрирован и хочу отредактировать свой профиль, то при переходе на страницу редактирования профиля я никаких форм не отправляю. Но в input'ах должен получить уже заполненные значения для моего профиля. Сейчас в контроллере алгоритм такой: [/i]если пользователь залогинен -> получить модель пользователя в переменную -> переменную отправить во view -> заполнить values тем, что передано во view[/i]
Вопросы:
1. Что должна представлять из себя обёртка над Location для редиректа? Часть класса, хелпер, как назвать, какие ещё функции можно туда добавить?
2. Статические классы. У меня статические - Validator, Auth (хелпер для регистрации/логина), Session, Config. Они ведь все работают только с какой-то одной сущностью, у которой не может быть несколько инстансов. Можно их статическими оставить?
У валидатора один публичный метод make, который принимает 2 массива (пик). Мне иногда кажется, что вместо классов Config и Validator было бы проще создать по одной функции.
>new Session это уже странно. Зачем ты создаешь объхект который сразу же удаляется? Может проще его не создавать тогда?
Обёртка над session_start(). Проще не создавать.
>Алсо что за нездоровая традиция первым делом создавать сессию?
Так эта функция должна вызываться до любого выхлопа на страницу. Однако её и вправду стоит засунуть туда, где успешно произошла аутентификация.
>Непонятно зачем ты указываешь путь к конфигу в методе setStudentMapper - зачем он там?
С этим проблема, я долго пытался отвязать конфиг от маппера. Сейчас маппер получает уже объект PDO вот так: new StudentMapper(Config::getPDOconnection);
Так лучше? Это максимум, до чего я смог додуматься.
>НЕпонятно почему в базовом классе контроллера есть публичные статические поля
Куда положить дефолтные controller и action? В методы setController и setAction?
>нездоровое увлечение цепочечными вызовами методв что на мой взгляд скорее дурнгая привычка так как замусоривает код нелогичными return $this.
С return $this можно использовать $obj->foo()->bar()->baz() вместо $obj->foo(); $obj->bar(); $obj->baz()
Хорошо, я постараюсь убрать цепочные вызовы там, где они лишние.
>непонятно почему в пути к шаблону пропущено расширение.
Ну так DRY, во всех аж 2-х фреймворках, на которых я крудил, расширение не указывается. В моём случае все шаблоны имеют расширение .php и можно в методе view один раз конкатенировать первый параметр с ".php", вместо того, чтобы в каждом вызове view() дописывать php.
>> <?php if (\App\Helper\Auth::check()): ?>
Ты хочешь проверить залогинен ли пользователь но ничего не передаешь в функицю.
Этот метод проверяет наличие $_SESSION['email']. Что передавать в метод, который проверяет, залогинен ли пользователь?
>> что в форме для редактирования профиля нужно откуда-то брать значения, которые будут передаваться в value="" как дефолтные значения для инпутов.
>А ты читал урок про обработку форм? Там же вроде написано откуда.
Там написано, что после отправления формы нужно заполнить values тем, что пришло (если ошибок нет). А вот если я уже зарегистрирован и хочу отредактировать свой профиль, то при переходе на страницу редактирования профиля я никаких форм не отправляю. Но в input'ах должен получить уже заполненные значения для моего профиля. Сейчас в контроллере алгоритм такой: [/i]если пользователь залогинен -> получить модель пользователя в переменную -> переменную отправить во view -> заполнить values тем, что передано во view[/i]
Вопросы:
1. Что должна представлять из себя обёртка над Location для редиректа? Часть класса, хелпер, как назвать, какие ещё функции можно туда добавить?
2. Статические классы. У меня статические - Validator, Auth (хелпер для регистрации/логина), Session, Config. Они ведь все работают только с какой-то одной сущностью, у которой не может быть несколько инстансов. Можно их статическими оставить?
У валидатора один публичный метод make, который принимает 2 массива (пик). Мне иногда кажется, что вместо классов Config и Validator было бы проще создать по одной функции.
>>38202
Мне очень хорошо помог вкатиться Learning SQL Алана Бьюли, как-то так.
Сидел я тут раньше с вами, сделал студентов и почти доделал файлообменник https://github.com/V3N0m21 может кто помнит, потом нашел работу Magento developer'ом. И вот пришел спросить совет.
Все началось с того что пришло предложение сделать тестовое задание на Magento, нужно было сделать модуль отзывов, я еще тогда подумал что тестовое задание было сложновато как для джуна, хоть я и сказал что никогда с magento не работал, но могу попробовать разобраться. В общем за неделю гугления и чтения очень скупой документации я решил делать модуль для magento 2, которая только-только вышла я задание таки сделал и отправил, меня пригласили на собеседование которое проводили CEO и CMO конторы пацаны 23х лет, контора находится в съемной квартире которая дала тестовое задание . Я сказал что хоть я и написал тестовое задание, для меня это было достаточно сложно, и мне обязательно нужен наставник, или хотя бы просвященный человек которому можно задавать вопросы и пару месяцев времени чтоб нормально разобраться, они сказали без проблем, у нас есть люди с большим опытом. По технической части меня вообще ничего не спрашивали, просто сказали что можешь выходить в понедельник. Так как я работал на другой работе я сказал что мне нужно две недели. Написал заявление на работе, в тот же день из этой конторы мне перезвонили, сказали что нужно готовиться, завтра у меня собеседование с заграничным заказчиком и скинули мне мое резюме, чтоб я ознакомился, в котором было указано что-то около 4х лет опыта разработки magento и год разработки magento 2. Я спросил как так, я же говорил что я джун и для того чтоб нормально разобраться в теме мне нужно хотя бы месяца два-три, они ответили "не ссы, вопросы будут не сложные, тем более ты же говоришь по английски", короче всю ночь я готовился к этому собеседованию, на следующий день пришел к ним, созвонились с заказчиком по скайпу. Пообщался с заказчиком, поотвечал на вопросы, не на все, но все прошло более-менее, они меня взяли
tl;dr В общем мне надоело печатать в подробностях, поэтому коротко: я работаю на проекте зарубежного заказчика, среди моих коллег все адские нубы, плюс на зарубежного заказчика работаю только я, и только я работаю на magento2, все остальные на локальных проектах и на magento и wordpress'e, и помощи вообще нет никакой, во всем абсолютно нужно разбираться самому. Заказчик думает что я мидло-синиор, кидает тикеты которые я с адовым трудом и кучей замечаний закрываю. Так я проработал вот уже два месяца, деньги платят как договаривались $500, но силенок работать в таком режиме и таком стрессе у меня уже нет, с ужасом думаю что завтра на работу и там тикет который я скорее всего сделать не смогу. И так вопрос - что делать дальше? Превозмогать и ждать когда я уже досконально в этом всем разберусь, или уходить и искать что-то более адекватное с прошаренными коллегами и параллельно нормально доучить фронтенд и JS с фреймворками? Из плюсов того что есть сейчас, я очень-очень сильно подтянул свой уровень за эти два месяца. Где-то как за 8 месяцев обучения в обычном режиме. ОП, дай свой мудрый совет что делать, а то я запутался и адекватно ситуацию оценить не могу.
Сидел я тут раньше с вами, сделал студентов и почти доделал файлообменник https://github.com/V3N0m21 может кто помнит, потом нашел работу Magento developer'ом. И вот пришел спросить совет.
Все началось с того что пришло предложение сделать тестовое задание на Magento, нужно было сделать модуль отзывов, я еще тогда подумал что тестовое задание было сложновато как для джуна, хоть я и сказал что никогда с magento не работал, но могу попробовать разобраться. В общем за неделю гугления и чтения очень скупой документации я решил делать модуль для magento 2, которая только-только вышла я задание таки сделал и отправил, меня пригласили на собеседование которое проводили CEO и CMO конторы пацаны 23х лет, контора находится в съемной квартире которая дала тестовое задание . Я сказал что хоть я и написал тестовое задание, для меня это было достаточно сложно, и мне обязательно нужен наставник, или хотя бы просвященный человек которому можно задавать вопросы и пару месяцев времени чтоб нормально разобраться, они сказали без проблем, у нас есть люди с большим опытом. По технической части меня вообще ничего не спрашивали, просто сказали что можешь выходить в понедельник. Так как я работал на другой работе я сказал что мне нужно две недели. Написал заявление на работе, в тот же день из этой конторы мне перезвонили, сказали что нужно готовиться, завтра у меня собеседование с заграничным заказчиком и скинули мне мое резюме, чтоб я ознакомился, в котором было указано что-то около 4х лет опыта разработки magento и год разработки magento 2. Я спросил как так, я же говорил что я джун и для того чтоб нормально разобраться в теме мне нужно хотя бы месяца два-три, они ответили "не ссы, вопросы будут не сложные, тем более ты же говоришь по английски", короче всю ночь я готовился к этому собеседованию, на следующий день пришел к ним, созвонились с заказчиком по скайпу. Пообщался с заказчиком, поотвечал на вопросы, не на все, но все прошло более-менее, они меня взяли
tl;dr В общем мне надоело печатать в подробностях, поэтому коротко: я работаю на проекте зарубежного заказчика, среди моих коллег все адские нубы, плюс на зарубежного заказчика работаю только я, и только я работаю на magento2, все остальные на локальных проектах и на magento и wordpress'e, и помощи вообще нет никакой, во всем абсолютно нужно разбираться самому. Заказчик думает что я мидло-синиор, кидает тикеты которые я с адовым трудом и кучей замечаний закрываю. Так я проработал вот уже два месяца, деньги платят как договаривались $500, но силенок работать в таком режиме и таком стрессе у меня уже нет, с ужасом думаю что завтра на работу и там тикет который я скорее всего сделать не смогу. И так вопрос - что делать дальше? Превозмогать и ждать когда я уже досконально в этом всем разберусь, или уходить и искать что-то более адекватное с прошаренными коллегами и параллельно нормально доучить фронтенд и JS с фреймворками? Из плюсов того что есть сейчас, я очень-очень сильно подтянул свой уровень за эти два месяца. Где-то как за 8 месяцев обучения в обычном режиме. ОП, дай свой мудрый совет что делать, а то я запутался и адекватно ситуацию оценить не могу.
>Все началось с того что пришло предложение сделать тестовое задание на Magento, нужно было сделать модуль отзывов
Охуеть, мне такое же задание давали. Но я его проебал.
Лол, нет. Вообще мимо. Видимо всем такое дают.
Оп наверняка скажет "надрывайся-превозмогай".
Бросать сейчас конечно глупо, но что мешает параллельно искать другую работу, с более комфортными условиями?
Я когда устраивался, выложил во-первых резюме, во-вторых разослал по ~50 вакансиям. Ответили 9, плюс еще 5 сами отозвались на резюме, так что выбирать было из чего.
Не нужно хвататься за первую попавшуюся возможность при таком спросе.
>>38412
В Киеве легко можно найти стажировку с адекватной нагрузкой и обучением (~300$).
Проблема только в том что я не в Киеве и переехать туда в ближайшие год-полтора я точно не смогу, в Киеве и правда выбор широкий. Ну а вообще в принципе мне на джинне нормально предложений приходило, хотя во всех хотели Symfony или Zend Framework. Я именно поэтому и думал посидеть дома, подтянуть это все плюс верстку с JS.
Хотя по большому счету мне и Magento 2 нравится, просто вот этот вот адовый стресс это что-то невыносимое.
Обсуди этот вопрос с начальством (если в той шараге таковое имеется), а не на дваче.
Мол так и так, не справляюсь с нагрузкой/не хватает опыта-квалификации, раскидайте тикеты другим членам команды.
В конце концов ты сам можешь проявить лидерские качества и напрячь кого-то из джунов/верстальщиков, чтобы они помогли
хотя бы с черновой работой (небезвозмездно естественно).
Заказчик судя по всему неплохой, это нужно ценить. Кол-во работы будет только расти.
Есть смысл обсудить с начальством и предложить взять еще одного человека, даже ценой снижения твоей з.п., если тебе так
хреново тоже вариант.
Короче твоя проблема решается обсуждением в коллективе. Говори с начальством, ищи напарника, делай бочку.
А вы еврей, вопросом на вопрос отвечать?
Свободно общаюсь с сильным акцентом. Никак специально не учил, много читал книжки и интернеты, смотрел кинцо и сериалы без перевода, общался когда появлялась возможность с иностранцами.
>но силенок работать в таком режиме и таком стрессе у меня уже нет
Велкам ту программирование, года через три будет немного полегче, а спустя еще два всё просто заебет до невозможности.
В Украине очень влияет, у них своих продуктов нет, поэтому вся Украина это рынок дешевой рабочей силы для белых людей.
>в тот же день из этой конторы мне перезвонили, сказали что нужно готовиться, завтра у меня собеседование с заграничным заказчиком и скинули мне мое резюме, чтоб я ознакомился, в котором было указано что-то около 4х лет опыта разработки magento и год разработки magento 2.
Хуже всего то, что так почти всегда бывает.
И на фрилансах, и в реальной жизни - везде привирают.
Это какое-то днище для заказчика или того, кто нанимает на работу.
Это я как заказчик говорю.
Ну а с другой стороны, платить мидл-сениору 500 баксов - это сурово, очень сурово.
Но, походу, все тупо в курсе, не дураки ведь.
Хоть я еще и не джуниор, но английский по идее должен сильно возвышать тебя. Открывается весь мир клиентов из стран, в которых зп в 10 раз больше, чем в рашке.
Имхо его только и наняли из-за английского. Софт скиллс, лел.
Байтослесарь за 16к?
поаскай на улице или попроси у анонов. Я как-то на домен себе 150 рублей напопрошайничал.
Да мне нужно сейчас сайт закинуть для дипломной работы. Это необязательно, но желательно.
>>36701
>>36833
Настрочил еще две задачи из раздела "Повторим?". Проверьте, плез, кому не сложно. Хотелось бы знать ваше мнение по поводу решения.
https://ideone.com/n2jYia
https://ideone.com/07D9j9
> $array = [];
Название ничего не значит. Массив чего?
> function spellSmallNumber($number, $number1, $female = null){
Вот это странный заголовок функции. Что такое number? чем оно отличается от number1? Непонятно. Ну и это нелогично, это функция которая преобразует небольшое число в строку, зачем передавать ей 2 числа?
Вот когда ты видишь что-то такое "нелогичное", скорее всего это говорит о том что код сделан неправильно.
Я вижу, оно используется в проверке не ноль, но это значит что эта проверка просто должна быть не в функции. Это не ее задача. Если у тебя 0 миллионов, то надо просто не вызвать эту функцию. Надо переделать код так, чтобы передавалось только одно число.
> function zeroinNumbers(
Логичнее было назвать addIfNotZero. Название функции начинается с глагола. сам код можно было написать проще, if ( ...) { ... } без else.
Использование ссылок это в общем плохо. Вот посмотри:
> zeroinNumbers($hundreds, $numbertoWords, $array); //Сотни
тут совсем неочевидно что делает эта строка. Вроде как она ничего не возвращает, и можно подумать, что она ничего не меняет. Да и еще название ничего не говорит. Лучше было сделать так:
Если (в числе есть сотни) {
добавить слово для сотен;
}
Так сразу видно что тут делается. Или хотя бы вместо передачи по ссылке возвращать из функции измененный массив.
> 11 => 'одиннадцать ',
Вот это плохая идея, так добавлять пробелы. Во-первых, не очень заметно что они тут есть, во-вторых, в конце строки появляется лишний пробел. Лучше хранить числа как есть, а пробелы добавлять напритмер в implode. Ну и по моему добавить один пробел в implode быстрее чем добавить по пробелу в каждое слово.
> }
> elseif($lastDigit == 1){
Это идет в одну строку: } elseif (...) {
> if($millions == 0 && $thousands != 0){
> return $thousandstoWords . $unitstoWords;
> }
> elseif($thousands == 0 && $millions != 0){
> return $millionstoWords . $unitstoWords;
Тут лучше было применить прием с добавлением в масив. А то если мы добавим еще миллиарды, то число комбинаций возрастет.
>>31882
> По задаче вопрос, для чего тут имеет смысл использовать фабрику?
А я такое советовал? Вообще, тут фабрику можно использовать для создания работников по названию и рангу. В данном случае я фабрикой назвал функцию которая производит объекты, а не тот сложный паттерн с кучей классов и интерфейсов.
> $array = [];
Название ничего не значит. Массив чего?
> function spellSmallNumber($number, $number1, $female = null){
Вот это странный заголовок функции. Что такое number? чем оно отличается от number1? Непонятно. Ну и это нелогично, это функция которая преобразует небольшое число в строку, зачем передавать ей 2 числа?
Вот когда ты видишь что-то такое "нелогичное", скорее всего это говорит о том что код сделан неправильно.
Я вижу, оно используется в проверке не ноль, но это значит что эта проверка просто должна быть не в функции. Это не ее задача. Если у тебя 0 миллионов, то надо просто не вызвать эту функцию. Надо переделать код так, чтобы передавалось только одно число.
> function zeroinNumbers(
Логичнее было назвать addIfNotZero. Название функции начинается с глагола. сам код можно было написать проще, if ( ...) { ... } без else.
Использование ссылок это в общем плохо. Вот посмотри:
> zeroinNumbers($hundreds, $numbertoWords, $array); //Сотни
тут совсем неочевидно что делает эта строка. Вроде как она ничего не возвращает, и можно подумать, что она ничего не меняет. Да и еще название ничего не говорит. Лучше было сделать так:
Если (в числе есть сотни) {
добавить слово для сотен;
}
Так сразу видно что тут делается. Или хотя бы вместо передачи по ссылке возвращать из функции измененный массив.
> 11 => 'одиннадцать ',
Вот это плохая идея, так добавлять пробелы. Во-первых, не очень заметно что они тут есть, во-вторых, в конце строки появляется лишний пробел. Лучше хранить числа как есть, а пробелы добавлять напритмер в implode. Ну и по моему добавить один пробел в implode быстрее чем добавить по пробелу в каждое слово.
> }
> elseif($lastDigit == 1){
Это идет в одну строку: } elseif (...) {
> if($millions == 0 && $thousands != 0){
> return $thousandstoWords . $unitstoWords;
> }
> elseif($thousands == 0 && $millions != 0){
> return $millionstoWords . $unitstoWords;
Тут лучше было применить прием с добавлением в масив. А то если мы добавим еще миллиарды, то число комбинаций возрастет.
>>31882
> По задаче вопрос, для чего тут имеет смысл использовать фабрику?
А я такое советовал? Вообще, тут фабрику можно использовать для создания работников по названию и рангу. В данном случае я фабрикой назвал функцию которая производит объекты, а не тот сложный паттерн с кучей классов и интерфейсов.
Норм все у тебя, так программистами и становятся. Подумай вот о чем - еще несколько месяцев таких мучений, и ты и правда миддлом станешь, потом тебе везде дороги открыты. А вот как ты хотел, чтобы учили и показывали, так редко бывает, да и не научишься ничему особо. Если тебя держат, и кое-как справляешься, то так оно и должно быть. В свободное время почитывай книги по программированию, задавай вопросы на форумах, юзай stackoverflow.
Еще, забыл упомянуть - тебе это в кайф начнет становиться со временем. Стресс пройдет с увеличением скилла. Чем активнее читаешь и пробуешь, больше тикетов делаешь - тем тебе все это больше нравиться начинает. Потом будешь на джунов и их проблемы еще с усмешкой посматривать, разве же это проблемы.
2)Как сказать апачу что индекс.php у меня в папке public и ссылаться надо на него? Сейчас в htacess прописано:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
Платят там наверняка тыщи 2-3, а до анона доходит ток 500.
Помню тебя, я примерно в то же время делал студентов (года 1.5 назад) и тоже нашел работу. Но у меня ситуация другая: есть люди которые имеют опыт и которые отвечают на вопросы и могут помочь но зп низкое (250$) на данный момент (для моей мухосрани норм). Сперва были задачи по джумле/вордпресс, сейчас онли интернет-магазины. Верстку тоже пробовал на паре сайтов. В свободное время изучаю js. атмосфера в коллективе прекрасная. Стрессы тоже есть, куда без этого но есть и поддержка. Работаю почти год.
Если они ограничивают то наверно не просто так. Соцсеть не хочет с каждым делиться информацией которую можно продать.
И вообще, это тред про программирование а не про спам и SMM (что в моем понимании одно и то же).
Это хипстерские фреймворки. О них говорят, чтобы быть сойти за продвинутого в последней моде.
Документацию почему не читаешь?
http://vk.com/dev/groups.getMembers
Максимальное значение получаемых айдишников - 1000. В запросе можно указать offset. Вот так и получаешь - выбрал 1000, увеличил оффсет на 1000, выбрал вторую тысячу и т.д.
Одним циклом можно все сделать.
Ну что ты вот это сразу, не пользуюсь я им, просто по работе нужно было.
Нет, тут дело в том, что мне нужна информация о конкретном количестве подписчиков одного сообщества, которые после рекламы стали подписчиками моего. И подписчиках моего, которые подписались на сообщество другого человека. После взаимного репоста записей в наши сообщества. Я бы тогда смог отслеживать эффективность рекламы как в моем, так и в других сообществах и искать наиболее подходящих.
>еще несколько месяцев таких мучений, и ты и правда миддлом станешь
У него времени на это становление нет - ad-hoc влепил что-то более-менее с натяжкой, а тут следующий тикет меж булочек лезет. Но можно взять перерыв на год через несколько месяцев и подтянуть свой уровень на основе приобретенного опыта.
>>38707
>>38671
Спасибо аноны за поддержку.
>А вот как ты хотел, чтобы учили и показывали, так редко бывает, да и не научишься ничему особо.
На самом деле я забыл упомянуть что самая главная проблема именно в том что по magento 2 практически вообще еще нет инфы, есть две базовые книжки и документация, все это я уже перечитал вдоль и поперек, а больше инфы нету практически, ни на стаковерфлоу ни в гугле. Только чтение и изучение кода ядра, только хардкор
Это татарский.
1. Устроиться в любую около-веб шарагу в своем 700к мухосранске. Зп 35-40 к.
Плюсы - достаточно легко найти работу. Возможно получится немного откладывать 5-10 к.
Минусы - зп будет хватать только на аренду + еда + учеба + активити вроде тренажерного зала. Отсутствие профессионального и карьерного роста. Предполагаю, что не будет времени или сил для прокачки чего-либо кроме кмс.
2. Попытаться устроиться в сириоус контору, вроде крока или ланнита, имеющую филиалы в моем городе.
Плюсы - такие-же как и в первом пункте, + профессиональный и карьерный рост. Постепенное увеличение оклада. Плюс для дальнейшего трудоустройства, т.к. будет опыт работы в серьезной конторе. Слышал, что Ланнит точно проводит обучение своих сотрудников.
Минусы - оче мало вакансий, серьезный отбор, и сравнительно большая конкуренция среди соискателей. Т.е. есть вероятность, что с моим медскилзом не возьмут.
3. Фриланс.
Плюсы - есть время на прокачку. Довольно широкий спектр задач, которые придется решать (он же минус).
Минусы - не постоянный доход, вероятно, я не смогу снимать квартиру первое время, а может и вообще не смогу, т.к. придется жестко самоорганизовываться, чтобы постоянно обеспечивать себя новыми заказами.
4. Удаленная работа. Тут я не совсем в теме, как найти, какие будут условия. Очевидно если работать на СНГ, то зп будет как в п.1 или даже ниже. Если на буржуев, то возможно и выше.
Плюсы - постоянный доход, не обязательно носить штаны на работе.
5. Радикальный. Понаехать в ДС и искать работу там.
Плюсы - относительно высокая зп. Смогу больше откладывать.
Минусы - помимо того, что работа будет фул тайм, и не известно будет ли обучение, у меня нет на данный момент суммы, которой бы хватило переехать, и снять жилье на первое время, пока ищу работу. Не известно в какую контору, на какую должность и зп я смогу рассчитывать первое время. Сыкотоно.
6. Сразу искать работодателя за бугром. Я думаю, что мне, для начала, нужно что-то, что я пока сам не знаю. В Канаду и Австралию, можно эмигрировать официально. И искать работу уже на месте, судя по форумам, этот вариант проще. Но нужно год потратить на бюрократию, и накопить достаточную сумму, чтобы не умереть с голоду, пока буду искать там работу.
Какой стул выбрать, если моя цель завести трактор, отложить 5-10k$ на эмиграцию, мне 28 и я заканчиваю первый курс заочки? Еще, я бы хотел заниматься, чем-то вроде анализа больших данных, или систем на основе машинного обучения или стрелять лазером из глаз.
1. Устроиться в любую около-веб шарагу в своем 700к мухосранске. Зп 35-40 к.
Плюсы - достаточно легко найти работу. Возможно получится немного откладывать 5-10 к.
Минусы - зп будет хватать только на аренду + еда + учеба + активити вроде тренажерного зала. Отсутствие профессионального и карьерного роста. Предполагаю, что не будет времени или сил для прокачки чего-либо кроме кмс.
2. Попытаться устроиться в сириоус контору, вроде крока или ланнита, имеющую филиалы в моем городе.
Плюсы - такие-же как и в первом пункте, + профессиональный и карьерный рост. Постепенное увеличение оклада. Плюс для дальнейшего трудоустройства, т.к. будет опыт работы в серьезной конторе. Слышал, что Ланнит точно проводит обучение своих сотрудников.
Минусы - оче мало вакансий, серьезный отбор, и сравнительно большая конкуренция среди соискателей. Т.е. есть вероятность, что с моим медскилзом не возьмут.
3. Фриланс.
Плюсы - есть время на прокачку. Довольно широкий спектр задач, которые придется решать (он же минус).
Минусы - не постоянный доход, вероятно, я не смогу снимать квартиру первое время, а может и вообще не смогу, т.к. придется жестко самоорганизовываться, чтобы постоянно обеспечивать себя новыми заказами.
4. Удаленная работа. Тут я не совсем в теме, как найти, какие будут условия. Очевидно если работать на СНГ, то зп будет как в п.1 или даже ниже. Если на буржуев, то возможно и выше.
Плюсы - постоянный доход, не обязательно носить штаны на работе.
5. Радикальный. Понаехать в ДС и искать работу там.
Плюсы - относительно высокая зп. Смогу больше откладывать.
Минусы - помимо того, что работа будет фул тайм, и не известно будет ли обучение, у меня нет на данный момент суммы, которой бы хватило переехать, и снять жилье на первое время, пока ищу работу. Не известно в какую контору, на какую должность и зп я смогу рассчитывать первое время. Сыкотоно.
6. Сразу искать работодателя за бугром. Я думаю, что мне, для начала, нужно что-то, что я пока сам не знаю. В Канаду и Австралию, можно эмигрировать официально. И искать работу уже на месте, судя по форумам, этот вариант проще. Но нужно год потратить на бюрократию, и накопить достаточную сумму, чтобы не умереть с голоду, пока буду искать там работу.
Какой стул выбрать, если моя цель завести трактор, отложить 5-10k$ на эмиграцию, мне 28 и я заканчиваю первый курс заочки? Еще, я бы хотел заниматься, чем-то вроде анализа больших данных, или систем на основе машинного обучения или стрелять лазером из глаз.
https://ideone.com/5RnMlV
https://github.com/greenTea242/Student_List
> https://github.com/greenTea242/Student_List/blob/master/public/index.php#L30
> if ($pager->checkPossiblePages($myPage)) {
> $abiturients = $gateway->getAbiturientsInPage($recordsPerPage, $pager->getOffsetForDB($myPage), $sort, $search, $order);
> }
А что если if не сработает? Чему будет равна $abiturients? Это очень подозрительно, когда переменная создается внутри ифа, а используется снаружи.
> require_once "../src/ini.php";
Не стоит подключать файлы с относильным путем, надежнее указывать полный путь через __DIR__, так как правила по которым php ищет файлы с относительным путем, очень мутные.
> /Массив свойств для заполнения модели/
> $properties = [
> "token",
Зачем разрешать перезаписывать токен? Пользователь разве может его редактировать?
https://github.com/greenTea242/Student_List/blob/master/public/register.php#L29
> if (!empty($_POST[$property])) {
А если пользователь при редактировании захочет очистить какое-то поле, эта проверка ведь его не пропустит?
https://github.com/greenTea242/Student_List/blob/master/public/inc/login.php
Почему этот файл в публичной папке?
> if ($gateway->isAbiturientExist($abiturientID, $token)) {
> $abiturient = $authorizator->getStudent($abiturientID);
> $authorizator->logIn($abiturientID, $token);
Это странный код: если куки устанвлены и они правильные, установим их еще раз. Зачем?
> $authorizator->setToken($token);
> $abiturient = new Abiturient();
не очень понятно зачем для незалогиненного студента ставить токен, и создавать объект.
Библиотеку bootstrap не надо раскидывать по файлам. Надо положить ее целиком в отдельную папку и не перемешивать с другим файлами. Иначе непонятно где твой код, а где нет.
> https://github.com/greenTea242/Student_List/blob/master/src/Authorization.php#L12
Этот метод странный. Вообще, класс авторизации у тебя умеет ставить куки, но не умеет проверять. Мне кажется логично применить тут принцип инкапсуляции и поместить всю работу с авторизационными куками в класс авторизации. Чтобы внешний код не знал как сделана авторизация и какие куки используются.
А сейчас у тебя авторизация размазана по нескольким местам кода.
> https://github.com/greenTea242/Student_List/blob/master/src/AbiturientDataGateway.php#L210
> if ($counter == 1) {;
Безопаснее писать > 0, а не равно 1. Точка с запятой после открывающей скобки не ставится.
https://github.com/greenTea242/Student_List/blob/master/src/AbiturientValidator.php
тут мне не нравится что трудно понять как пользоваться этим классом. Почему нельзя сделать один метод валидации, куда мы даем студента и получаем список ошибок, а не гадать в каком порядке надо вызывать методы?
https://github.com/greenTea242/Student_List/blob/master/src/AbiturientValidator.php#L151
Букву ё надо указывать отдельно.
https://github.com/greenTea242/Student_List/blob/master/src/TokenHelper.php
Насчет этого класса. Опять же, я думаю, надо инкапсулировать работу с CSRF кукой внутри него. А сейчас у тебя за проверку и генерацию токенов отвечает внешний код.
Насчет подсветки слов: принцип такой что мы можем делать замену только 1 раз. Ибо на входе мы имеем просто текст, а на выходе - HTML с HTML-мнемониками и тегами. И по нему проходиться уже нельзя. Потому замену надо сделать в 1 проход.
У тебя этот принцип не выполняется. И то, что ты там делаешь проверку на слово "mark" (<> воспринимаются как ограничители в регулярке) это костыль.
Я думаю, замену стоит делать так: сформировать регулярку вида:
длинноеслово1|среднееслово2|слово3
И уже ей заменять за 1 раз все вхождения на окруженные тегами. Причем в этом случае мы можем даже не разбивать текст на слова.
> $searchWords = preg_split("/[^\\w]+/ui", $search);
Кстати тут принуип разбиения не совпадает с тем что используется в формировании шаблона для LIKE. Ты нарушил принцип DRY, продублировал код и вот последствия.
https://github.com/greenTea242/Student_List/blob/master/src/config.php
В конфиг стоит класть то, что можно менять и желательно в более простом виде. DSN сложный и в нем нельзя поменять тип БД так как твой код вряд ли заточен на любые базы данных. Потому стоит в конфиге просто сделать хост, порт, имя, пароль, название БД. Ну и количество записей.
https://github.com/greenTea242/Student_List/blob/master/templates/index.html#L3
> <?php require_once("/inc/head.html"); ?>
Не будет же работать, это абсолютный путь от корня диска.
Тут в файлах скопипащена часть тегов, например body и head:
https://github.com/greenTea242/Student_List/blob/master/templates/register.html
https://github.com/greenTea242/Student_List/blob/master/templates/index.html
Копипаста - плохо.
> <p>Показаны только абитуриенты, найденные по запросу "<?=$search?>".</p>
Где защита от XSS? Перечитай урок.
> <?php if ($order == "asc"): ?>
> <?php else: ?>
Тут ведь явно почти одно и то же. Нельзя ли это упростить за счет функций?
Вообще, подумай как упростить код формирования заголовка таблицы.
> <?php if ($search): ?>
> <td><?=ViewHelper::paintFound($abiturient->getName(), $search)?></td>
> <?php else: ?>
> <td><?=htmlspecialchars($abiturient->getName(), ENT_QUOTES)?></td>
Тоже по сути копипаста. Хорошо бы объединить paintFound и htmlspecialchars в одну функцию.
> <p><a href="<?=$_SERVER['PHP_SELF']?>"
Нехорошо что шаблон лезет в SERVER. Пусть ему URL передают снаружи.
> <?php for ($pageNum = 1; $pageNum <= $pager->getTotalPages(); $pageNum++): ?>
> <?php if ($pageNum == $myPage): ?>
> <?php elseif ($pageNum == $myPage + 1 ||
> $pageNum == $myPage - 1 ||
Эта логика по выбору номеров смотрится очень неуклюже. Нелзя ли в пейджер ее перенести и например сделать чтобы он возвращал список номеров страниц для отображения?
> <?php if(!empty($errorList['name'])): ?>
> <div class="control-group has-error">
> <?php endif ?>
Вот это плохо. ты целый див добавляешь при ошибке который может влиять на верстку. Мне кажется, добавялять надо только один класс.
> pattern="<?=$validator->getHTML5RegExpForName()?>"
Спецсимволы стоит экранировать
> " - "(дефис)
Точка с запятой забыта после quot, почитай про html мнемоники
https://github.com/greenTea242/Student_List/blob/master/templates/register.html#L120
Тут инпуты выбора пола скопипащены 3 раза. Избавься от копипасты.
https://github.com/greenTea242/Student_List
Тут разметка в ридми кривая.
> Установка кук для токена существует отдельно потому что в register.php проверяется защита на СSRF во время отправки формы, т.е. до момента регистрации студента и выставления оных.
Наверно лучше использовать разные токены, один для авторизации, другой для CSRF. И кстати я подумал, авторизационный токен - он ведь уникальный, и id студента в куки можно не класть.
> Ты мне советовал для выделения слов при поиске использовать preg_replace_callback. У меня не получилось ее правильно использовать, потому что она сразу меняет параллельно все совпадения по массиву регулярок и нигде нельзя посмотреть процесс изменения текста, а мне нужно сделать так, что если это совпадение уже в тегах <mark>, мне это не нужно делать.
Нет, прочитай выше, замену надо делать в один проход. preg_replace_callback это бы позволил сделать, как впрочем и другой вариант, что я предложил.
https://github.com/greenTea242/Student_List
> https://github.com/greenTea242/Student_List/blob/master/public/index.php#L30
> if ($pager->checkPossiblePages($myPage)) {
> $abiturients = $gateway->getAbiturientsInPage($recordsPerPage, $pager->getOffsetForDB($myPage), $sort, $search, $order);
> }
А что если if не сработает? Чему будет равна $abiturients? Это очень подозрительно, когда переменная создается внутри ифа, а используется снаружи.
> require_once "../src/ini.php";
Не стоит подключать файлы с относильным путем, надежнее указывать полный путь через __DIR__, так как правила по которым php ищет файлы с относительным путем, очень мутные.
> /Массив свойств для заполнения модели/
> $properties = [
> "token",
Зачем разрешать перезаписывать токен? Пользователь разве может его редактировать?
https://github.com/greenTea242/Student_List/blob/master/public/register.php#L29
> if (!empty($_POST[$property])) {
А если пользователь при редактировании захочет очистить какое-то поле, эта проверка ведь его не пропустит?
https://github.com/greenTea242/Student_List/blob/master/public/inc/login.php
Почему этот файл в публичной папке?
> if ($gateway->isAbiturientExist($abiturientID, $token)) {
> $abiturient = $authorizator->getStudent($abiturientID);
> $authorizator->logIn($abiturientID, $token);
Это странный код: если куки устанвлены и они правильные, установим их еще раз. Зачем?
> $authorizator->setToken($token);
> $abiturient = new Abiturient();
не очень понятно зачем для незалогиненного студента ставить токен, и создавать объект.
Библиотеку bootstrap не надо раскидывать по файлам. Надо положить ее целиком в отдельную папку и не перемешивать с другим файлами. Иначе непонятно где твой код, а где нет.
> https://github.com/greenTea242/Student_List/blob/master/src/Authorization.php#L12
Этот метод странный. Вообще, класс авторизации у тебя умеет ставить куки, но не умеет проверять. Мне кажется логично применить тут принцип инкапсуляции и поместить всю работу с авторизационными куками в класс авторизации. Чтобы внешний код не знал как сделана авторизация и какие куки используются.
А сейчас у тебя авторизация размазана по нескольким местам кода.
> https://github.com/greenTea242/Student_List/blob/master/src/AbiturientDataGateway.php#L210
> if ($counter == 1) {;
Безопаснее писать > 0, а не равно 1. Точка с запятой после открывающей скобки не ставится.
https://github.com/greenTea242/Student_List/blob/master/src/AbiturientValidator.php
тут мне не нравится что трудно понять как пользоваться этим классом. Почему нельзя сделать один метод валидации, куда мы даем студента и получаем список ошибок, а не гадать в каком порядке надо вызывать методы?
https://github.com/greenTea242/Student_List/blob/master/src/AbiturientValidator.php#L151
Букву ё надо указывать отдельно.
https://github.com/greenTea242/Student_List/blob/master/src/TokenHelper.php
Насчет этого класса. Опять же, я думаю, надо инкапсулировать работу с CSRF кукой внутри него. А сейчас у тебя за проверку и генерацию токенов отвечает внешний код.
Насчет подсветки слов: принцип такой что мы можем делать замену только 1 раз. Ибо на входе мы имеем просто текст, а на выходе - HTML с HTML-мнемониками и тегами. И по нему проходиться уже нельзя. Потому замену надо сделать в 1 проход.
У тебя этот принцип не выполняется. И то, что ты там делаешь проверку на слово "mark" (<> воспринимаются как ограничители в регулярке) это костыль.
Я думаю, замену стоит делать так: сформировать регулярку вида:
длинноеслово1|среднееслово2|слово3
И уже ей заменять за 1 раз все вхождения на окруженные тегами. Причем в этом случае мы можем даже не разбивать текст на слова.
> $searchWords = preg_split("/[^\\w]+/ui", $search);
Кстати тут принуип разбиения не совпадает с тем что используется в формировании шаблона для LIKE. Ты нарушил принцип DRY, продублировал код и вот последствия.
https://github.com/greenTea242/Student_List/blob/master/src/config.php
В конфиг стоит класть то, что можно менять и желательно в более простом виде. DSN сложный и в нем нельзя поменять тип БД так как твой код вряд ли заточен на любые базы данных. Потому стоит в конфиге просто сделать хост, порт, имя, пароль, название БД. Ну и количество записей.
https://github.com/greenTea242/Student_List/blob/master/templates/index.html#L3
> <?php require_once("/inc/head.html"); ?>
Не будет же работать, это абсолютный путь от корня диска.
Тут в файлах скопипащена часть тегов, например body и head:
https://github.com/greenTea242/Student_List/blob/master/templates/register.html
https://github.com/greenTea242/Student_List/blob/master/templates/index.html
Копипаста - плохо.
> <p>Показаны только абитуриенты, найденные по запросу "<?=$search?>".</p>
Где защита от XSS? Перечитай урок.
> <?php if ($order == "asc"): ?>
> <?php else: ?>
Тут ведь явно почти одно и то же. Нельзя ли это упростить за счет функций?
Вообще, подумай как упростить код формирования заголовка таблицы.
> <?php if ($search): ?>
> <td><?=ViewHelper::paintFound($abiturient->getName(), $search)?></td>
> <?php else: ?>
> <td><?=htmlspecialchars($abiturient->getName(), ENT_QUOTES)?></td>
Тоже по сути копипаста. Хорошо бы объединить paintFound и htmlspecialchars в одну функцию.
> <p><a href="<?=$_SERVER['PHP_SELF']?>"
Нехорошо что шаблон лезет в SERVER. Пусть ему URL передают снаружи.
> <?php for ($pageNum = 1; $pageNum <= $pager->getTotalPages(); $pageNum++): ?>
> <?php if ($pageNum == $myPage): ?>
> <?php elseif ($pageNum == $myPage + 1 ||
> $pageNum == $myPage - 1 ||
Эта логика по выбору номеров смотрится очень неуклюже. Нелзя ли в пейджер ее перенести и например сделать чтобы он возвращал список номеров страниц для отображения?
> <?php if(!empty($errorList['name'])): ?>
> <div class="control-group has-error">
> <?php endif ?>
Вот это плохо. ты целый див добавляешь при ошибке который может влиять на верстку. Мне кажется, добавялять надо только один класс.
> pattern="<?=$validator->getHTML5RegExpForName()?>"
Спецсимволы стоит экранировать
> " - "(дефис)
Точка с запятой забыта после quot, почитай про html мнемоники
https://github.com/greenTea242/Student_List/blob/master/templates/register.html#L120
Тут инпуты выбора пола скопипащены 3 раза. Избавься от копипасты.
https://github.com/greenTea242/Student_List
Тут разметка в ридми кривая.
> Установка кук для токена существует отдельно потому что в register.php проверяется защита на СSRF во время отправки формы, т.е. до момента регистрации студента и выставления оных.
Наверно лучше использовать разные токены, один для авторизации, другой для CSRF. И кстати я подумал, авторизационный токен - он ведь уникальный, и id студента в куки можно не класть.
> Ты мне советовал для выделения слов при поиске использовать preg_replace_callback. У меня не получилось ее правильно использовать, потому что она сразу меняет параллельно все совпадения по массиву регулярок и нигде нельзя посмотреть процесс изменения текста, а мне нужно сделать так, что если это совпадение уже в тегах <mark>, мне это не нужно делать.
Нет, прочитай выше, замену надо делать в один проход. preg_replace_callback это бы позволил сделать, как впрочем и другой вариант, что я предложил.
не помогут так как они не увеличивают число вариантов. md5 ведь не создает новые коды, для одних и тех же данных на входе будет одно и то же на выходе.
> Тогда с 33 по 126 брать, довольно солидно выглядит.
ну ок
>>32272
Принимаешь логи и пароль, если правильные, выдаешь куку с токеном, который хранится в БД, для каждого пользователя свой, меняется при смене пароля. Позже по этой куке можно проверить что пользователь залогинен и кто он такой.
В базе хранишь не пароли, а только соленые (не обычные) хеши от них.
>>32331
Тут далеко не день. Тебе минимум пару месяцев изучать сначала теорию придется.
>>32421
Выглядит очень переусложеннным. И сам алгоритм, и то как ты пишешь код.
> $temp1
Это название ничего не значит, а название должно соответсвтовать тому что хранится внутри.
> $numberOfPupils = 10;
Число учеников надо не считать вручную а получить из массива имен. Или они не связаны?
> $classList[$i]['name'] = $names[$index];
> $classList[$i]['height'] = 150 + mt_rand(0,50);
тут наверно проще было сделать массив вида имя => рост.
> echo "{$classList[$i]['name']} {$classList[$i]['height']} {$classList[$i]['isAnone']} <br>";
Тут много раз повторяется $classList[$i], тебя это не беспокоит? Вообще, это повторяется очень много раз в коде и это плохо.
То же касается $classList[$maxIndex]
> for ($i=1; $i<=$numberOfPupils; $i++) {
> if ($maxHeight < $classList[$i]['height']) {
тут надо использовать foreach, он лучше годится для обхода массива.
> if ($i == $temp1) {
> $classList[$i]['isAnone'] = 'Это анон';
Это поле не нужно так как всегда можно проверить индекс.
В общем, надо переделывать.
не помогут так как они не увеличивают число вариантов. md5 ведь не создает новые коды, для одних и тех же данных на входе будет одно и то же на выходе.
> Тогда с 33 по 126 брать, довольно солидно выглядит.
ну ок
>>32272
Принимаешь логи и пароль, если правильные, выдаешь куку с токеном, который хранится в БД, для каждого пользователя свой, меняется при смене пароля. Позже по этой куке можно проверить что пользователь залогинен и кто он такой.
В базе хранишь не пароли, а только соленые (не обычные) хеши от них.
>>32331
Тут далеко не день. Тебе минимум пару месяцев изучать сначала теорию придется.
>>32421
Выглядит очень переусложеннным. И сам алгоритм, и то как ты пишешь код.
> $temp1
Это название ничего не значит, а название должно соответсвтовать тому что хранится внутри.
> $numberOfPupils = 10;
Число учеников надо не считать вручную а получить из массива имен. Или они не связаны?
> $classList[$i]['name'] = $names[$index];
> $classList[$i]['height'] = 150 + mt_rand(0,50);
тут наверно проще было сделать массив вида имя => рост.
> echo "{$classList[$i]['name']} {$classList[$i]['height']} {$classList[$i]['isAnone']} <br>";
Тут много раз повторяется $classList[$i], тебя это не беспокоит? Вообще, это повторяется очень много раз в коде и это плохо.
То же касается $classList[$maxIndex]
> for ($i=1; $i<=$numberOfPupils; $i++) {
> if ($maxHeight < $classList[$i]['height']) {
тут надо использовать foreach, он лучше годится для обхода массива.
> if ($i == $temp1) {
> $classList[$i]['isAnone'] = 'Это анон';
Это поле не нужно так как всегда можно проверить индекс.
В общем, надо переделывать.
можно
>>32654
> задачу про калькулятор.
> function abc(&$op, &$char, &$number, &$result){
> $op = $char;
Ой, как много ссылок. Зачем? Вообще ссылки это плохо так как удобнее когда функция возвращает результат явно. да еще и половина из них не используется.
И что за бредовое название? Названия функции начинаются с глагола, сделайЧтоТо.
Тут вообще эта функция не нужна, код в ней можно поставить под блоком if/else
В остальном верно.
> Пока не доходит до меня как ввести поддержку дробных чисел.
Можно сделать переменную-флаг, показывающую встретили мы уже точку (или запятую) или нет, и если встретили, то добавлять цифры по другому приницпу (делим число на 10 в степени N и прибавляем). Ну и проверки добавить, что в числе нет двух точек например.
> Задача про банкомат.
> echo "Выдача возможна, число купюр:\n";
не говори "гоп", пока не перепрыгнешь: http://ideone.com/3kTQeP - твой банкомат зажал 100 тугриков.
> $abc =
переменным надо давать осмысленные имена
>>33136
Что значат URL? Ты имеешь в виду каждый раз ли разрешается имя домена в IP адрес? Да, каждый, но результат может кешироваться, благо сам протокол DNS требует указать время в течении которого ответ можно использовать без повторных запросов.
можно
>>32654
> задачу про калькулятор.
> function abc(&$op, &$char, &$number, &$result){
> $op = $char;
Ой, как много ссылок. Зачем? Вообще ссылки это плохо так как удобнее когда функция возвращает результат явно. да еще и половина из них не используется.
И что за бредовое название? Названия функции начинаются с глагола, сделайЧтоТо.
Тут вообще эта функция не нужна, код в ней можно поставить под блоком if/else
В остальном верно.
> Пока не доходит до меня как ввести поддержку дробных чисел.
Можно сделать переменную-флаг, показывающую встретили мы уже точку (или запятую) или нет, и если встретили, то добавлять цифры по другому приницпу (делим число на 10 в степени N и прибавляем). Ну и проверки добавить, что в числе нет двух точек например.
> Задача про банкомат.
> echo "Выдача возможна, число купюр:\n";
не говори "гоп", пока не перепрыгнешь: http://ideone.com/3kTQeP - твой банкомат зажал 100 тугриков.
> $abc =
переменным надо давать осмысленные имена
>>33136
Что значат URL? Ты имеешь в виду каждый раз ли разрешается имя домена в IP адрес? Да, каждый, но результат может кешироваться, благо сам протокол DNS требует указать время в течении которого ответ можно использовать без повторных запросов.
Может быть много причин. Может, уязвимость, может среди разработчиков есть "крот". Может кто-то сохранил пароль от FTP или SSH, а вирус его украл.
Причину можно попробовать найти в логах веб- и FTP-сервера, там же можно увидеть кто и когда обращется к скрипту.
Надо как минимум менять все пароли и переустанаваливать сайт заново, очистив папку и залив исходные незараженные файлы. Чтобы например бывшие разрабочтики потеряли бы доступы.
Ну и смотрите какие вы там библиотеки и плагины используете, гуглите, может они устарели уже и надо обновить.
Начинать карьеру с обмана плохо. Если все будут обманывать заказчиков, постепенно число заказов от них будет снижаться так как они будут искать разработчиков в других странах.
Конечно заказчик и сам немного виноват что толком не проверил тебя.
Лучшее что ты можешь сделать это поствятить все свои силы изучению магенто, благо там ООП везде, так что думаю разобраться реально.
И заведи себе привычку перед сдачей задачи перечитать требования и проверить что все что надо, сделано.
> или уходить и искать что-то более адекватное с прошаренными коллегами
Если есть варианты то можно попробовать. Но не окажется ли там то же самое? Насколько я знаю, то, что тебе нужно есть в крупных организациях-аутсорсерах, у них там и сеньоры настоящие есть, и всякие современные штуки используются.
У тебя в цикле нет никаких действий, кроме echo.
Ещё:
>PHP Notice: Undefined variable: b in /home/YImoVw/prog.php on line 3
В начале ты объявил $a = ( $b * $b );, но ведь ты не объявил, чему равна $b. Всегда читай, что пишется в заметках - это помогает увидеть ошибки, для того они и нужны.
Тебе сейчас нужно определить переменные и поставить то выражение в тело цикла.
Оп, как устроиться в "в крупную организацию-аутсорсер"? Я понимаю, что ответ: открыть hh и посмотреть требования в вакансиях. Но ведь там часто пишут больше, чем реально требуется. И берут с меньшим количеством навыков и знаний. Или нет? У тебя есть опыт, возможно ты знаешь саксес стори коллег по цеху или твоих учеников. Какой реальный порог входа для подобных организаций?
> в которых зп в 10 раз больше, чем в рашке.
Давай не будем преувеличивать, далеко не в 10. В Европе программисты вообще не сильно больше среднего европейца получают, а самые большие зарплаты только в Калифорнии и в штате Нью-Йорк. И то, там условия жизни не сладкие, жилье дорогущее, транспорт плохой, пробки, постоянная нервотрепка. И внезапно в США ты сам платишь со своей зарплаты налоги, да еще и немаленькие. ЧТо-то я не уверен что в 10 раз больше. В странах СНГ хорошим разработчикам могут платить до 2000 -3000 долл.
И еще у них там много всякого разводилова, например опционы (право на покупку акций), которые можно реализовать не раньше и не позже определенного срока, за акции надо платить реальными деньгами, надо заплатить налоги и при этом там может быть условие что ты не можешь продать эти акции до IPO. То есть вроде как и акции, а посмотришь внимательно и видно что все сделано, чтобы ты не мог на них заработать.
Ну и сейчас конечно ситуация не самая хорошая в экономике, раньше разрыв по моему был даже меньше.
"хорошо там, где нас нет" в общем.
Мне вспоминается история про тян из техподдержки которой там еду не на что покупать было: https://vc.ru/p/yelp-fired
Конечно в США можно жить хорошо - если ты из богатой семьи или если ты смог развить успешный бизнес. Но не всем так везет.
Опыта нет. Порог может быть высокий, пока не попробуешь не узнаешь. Если ты джун то больше будут по всяким теориям спрашивать, я думаю, теория баз данных (нормальные формы, внешние ключи, транзакции), ООП, паттерны.
> Но ведь там часто пишут больше, чем реально требуется. И берут с меньшим количеством навыков и знаний
Не знаю. Но кто тебе мешает это изучить?
Спасибо добрый человек.
Удивительно, после стольких лет на двощах увидел тред в котором исключительно адекватные люди помогают другим, без агрессии, злобы и высокомерства. Прямо на душе теплее стало
1. Будет какое-нибудь говно устаревшее и говнокод. Увязнешь.
2. Норм вариант. Возьмут или не возьмут, зависит от того, кого набирают в данный момент. Иногда и сосем джунов берут. В любом случае, если возьмут, то многому научишься.
3. Время на прокачку тебе никто на фрилансе давать не будет. Везде хотят быстро, и чтобы вчера сделано. Естественно один говнокод с такими условиями. Клиенты будут кидать регулярно, пока базу не наберешь.
4. Норм вариант, но найти сложно. Из минусов, что удаленщиков вся остальная фирма за халявщиков держит. Коммуникации осложнены. Все заслуги будут офисным доставаться, а тебя как малоценного работника держать будут.
5. Откладывать вряд ли сможешь сильно больше, жилье дорогое же и все дорогое в ДС. Зато сможешь работу за неделю сменить, если надоест, их полным-полно, и берут охотно.
6. Без законченного высшего будут проблемы с визой, придется закончить сначала. А так, да, лучший вариант, и зарплаты самые высокие. Ну и инглиш конечно выучить надо.
Работаю в европке. Джунам платят 2500 евро брутто, сеньерам 5-7 штук. С работой проблем нет, много ее.
https://github.com/nsdvw/TestHub
По поводу прав на логи/кеш, перепробовал уже все комбинации. Давал любые права,
в том числе три семерки (как знаменитый портвейн), добавлял ввв-рута себе в друзяшки,
и сам к нему добавился.
Все равно какая-то фигня, там в standard-edition в композере какие-то пост-установочные
скрипты (от Distribution bundle), в том числе они должны чистить кеш. Но очистить кеш у него
не получается, говорит нет прав и вдобавок откатывает composer.json, сволочь.
То есть я даже бутстрап установить не мог, он откатывал изменения.
Помогло только заклинание, которое дают в документации по установке.
Что тут вообще написано?
$ HTTPDUSER=`ps axo user,comm | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\ -f1`
$ sudo setfacl -R -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX var
$ sudo setfacl -dR -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX var
Да, и почему оно работает только в dev режиме? В prod 404 на любой запрос.
А, нет, уже работает. Но стили похерились.
>Что тут вообще написано?
Вроде бы ищет апач в списке процессов, потом отсеивает рутовые апачи, и выдирает юзера из апачей, которые не под рутом. Потом ставит права этому юзеру на папку var.
Точнее двум юзерам права дает на папку var и все ее подпапки. Тебе и тому, на кого апач найденный запущен.
Какие есть способы сделать так, чтобы текст статьи не наезжал под меню? Я так понял, что мне нужно растянуть меню до нижней границы экрана. Как это можно сделать? Если выставить свойство height: 100% ничего не происходит. Получилось выставить лишь точное значение.
Исходники прилагаю: https://github.com/codedokode/pasta/blob/master/html/html.md
Ребятки, помогите плиз.
В файле .htaccess эти строчки:
RewriteRule ^.$ [NC,L]
RewriteRule ^.$ index.php [NC,L]
Какого-то хуя убивают страничку, на которой расположен простенький ajax чатик. Всё остальное работает отлично. Отличие этого ajax чатика, от всех остальных страниц в том, что в нём инклудятся другие соседние файлы. С чем сввязано, как поправить ?
Точнее не убивают, а скорее заставляют сайтик игнорировать скрипты, которые должны выполняться.
Проблема с правами действительно есть. В общем случае у нас есть пользователь, от которого работает PHP (www-root или apache) и N пользователей-разработчиков которые могут править код, запускать скрипты.
Апач намеренно работает от другого пользователя, чтобы он не мог например иметь доступ к другим файлам пользователя.
Однако проблема в том, что PHP скрипт может быть запущен как Апачом, так и разработчиками (или кроном), в этом случае файлы, которые он создает, будут принадлежать разным пользователям, и из-за этого постоянно проблемы с их удалением, сменой прав и тд, так как ты не можешь менять права на чужой файл.
Вдобавок есть еще так называемый umask. Он определяет какие биты будут сброшены в разрешениях на создаваемые файлы и папки. Ты можешь увидеть свой umask командой umask, и в php есть опция для его указания.
По умолчанию это 0022, то есть в создаваемых файлах будут сброшены биты записи для группы и для всех. Таким образом, даже если ты объединишь разработчиков и Апач в группу, они получат доступ только на чтение.
Вот какие я способы решения видел:
- создается отдельный пользователь для сайта, например, testhub, и php-fpm запускается от его имени. Для разработчиков добавляется право делать sudo в testhub через sudoers. Соответственно cli скрипты тоже запускаются от того же пользователя. Все файлы также принадлежат этому пользователю.
Чтобы разработчик не пытался создавать файлы или запускать скрипты от своего имени, можно выставить права так, что он имеет только право на чтение.
Минус в том что уменьшается разделение пользователей, так как все делается от имени одного и того же пользователя.
Решение из мануала Симфони - это использование ACL, системы которая позволяет давать допольнительно права любому числу групп и пользователей.
http://linux.die.net/man/5/acl
http://linux.die.net/man/1/setfacl
Можешь немного поизучать эту тему и подумать, как лучше все это делать.
> Какие есть способы сделать так, чтобы текст статьи не наезжал под меню?
А ты внимательно изучил особенности флоатов в учебнике на softwaremaniacs? Их надо знать наизусть.
Способ решения который подойдет здесь и во многих других случаях, такой: делаем меню флоатом, а контент - обычным блоком с паддингом слева. Чтобы загнать меню на паддинг, используем отрицатеьный маргин, мини-урок: https://gist.github.com/codedokode/3f6063edf0a2227eb313
> Я так понял, что мне нужно растянуть меню до нижней границы экрана. Как это можно сделать?
Только используя display:table-cell (или флексбокс который пока не везде работает), то есть заставить блоки меню и контента вести себя как ячейки таблицы. Но в данном случае с флоатами наверно удобнее.
О! Вот тебе задание. Изучи особенности работы display: table, display: table-row, table-cell. Сравни варианты верстки сайта из нескольких колонок флоатами и таблицей.
То есть у нас есть сайт из центральной колонки и одной или нескольких колонок сбоку. Довольно типичная ситуация.
Сравни преимущества и недостатки каждого способа. ну например:
- позволяет ли способ залить колонку фоновым цветом целиком
- позволяет ли способ размещать колонки в коде в другом порядке по сравнению с отображаемым. Это может быть нужно для адаптивной верстки, чтобы на узких экранах мы перешли от горизонтального расположения колонок к вертикальному (то есть контент сверху, меню под ним или над ним).
- позволяет ли способ определять ширину колонок по содержимому (а не жестко задавать ее)
- позволяет ли способ выравнивать содержимое колонки вертикально по центру или прибивать к низу
- позволяет ли способ жестко ограничить ширину колонки даже если контент в ней шире чем сама колонка (например там встретилось длинное слово)
Это мини-исследование поможет тебе лучше понять принципы расстановки и позиционирования блоков в CSS. Уверен что потраченное на него время окупится сторицей, благодаря тому что ты будешь лучше разбираться в CSS и быстрее решать какие-то проблемы в верстке.
> Какие есть способы сделать так, чтобы текст статьи не наезжал под меню?
А ты внимательно изучил особенности флоатов в учебнике на softwaremaniacs? Их надо знать наизусть.
Способ решения который подойдет здесь и во многих других случаях, такой: делаем меню флоатом, а контент - обычным блоком с паддингом слева. Чтобы загнать меню на паддинг, используем отрицатеьный маргин, мини-урок: https://gist.github.com/codedokode/3f6063edf0a2227eb313
> Я так понял, что мне нужно растянуть меню до нижней границы экрана. Как это можно сделать?
Только используя display:table-cell (или флексбокс который пока не везде работает), то есть заставить блоки меню и контента вести себя как ячейки таблицы. Но в данном случае с флоатами наверно удобнее.
О! Вот тебе задание. Изучи особенности работы display: table, display: table-row, table-cell. Сравни варианты верстки сайта из нескольких колонок флоатами и таблицей.
То есть у нас есть сайт из центральной колонки и одной или нескольких колонок сбоку. Довольно типичная ситуация.
Сравни преимущества и недостатки каждого способа. ну например:
- позволяет ли способ залить колонку фоновым цветом целиком
- позволяет ли способ размещать колонки в коде в другом порядке по сравнению с отображаемым. Это может быть нужно для адаптивной верстки, чтобы на узких экранах мы перешли от горизонтального расположения колонок к вертикальному (то есть контент сверху, меню под ним или над ним).
- позволяет ли способ определять ширину колонок по содержимому (а не жестко задавать ее)
- позволяет ли способ выравнивать содержимое колонки вертикально по центру или прибивать к низу
- позволяет ли способ жестко ограничить ширину колонки даже если контент в ней шире чем сама колонка (например там встретилось длинное слово)
Это мини-исследование поможет тебе лучше понять принципы расстановки и позиционирования блоков в CSS. Уверен что потраченное на него время окупится сторицей, благодаря тому что ты будешь лучше разбираться в CSS и быстрее решать какие-то проблемы в верстке.
Застрял на задаче про полином из стартового курса.
Вот ссылка: http://ideone.com/1PghDS Все время говорит что фраза не полином, хотя она полином, сука. Подскажите, где я обосрался?
Заранее спасибо за помощь.
По дизайну:
- список тестов и тегов идет равномерной стеной и не очень очевидно какие теги к какому тесту относятся. Я бы предложил прижать теги ближе к заголовку теста или добавить серые бледные полосочки-разделители
- мне не нравится современная тенденция делать неподчеркнутые ссылки (речь о заголовках тестов). Хуже, часто их делают еще и темно-синим цветом который тяжело отличить от черного, а посеещенные ссылки - черным.
Конечно, подчеркивание, особенно когда его много, сильно отвлекает внимание и оно бывает "тяжелым" на надписям крупного размера. Но для этого есть способы борьбы - например подчеркивание можно делать через border-bottom на inline-элементе, сделав его тонким и бледненьким.
https://www.artlebedev.ru/kovodstvo/sections/171/
Кнопка "создать тест" слишком близко к тексту (сравни с расстоянием между абзацами) и такое ощущение что ее не мешало бы сдвинуть на пару пикселей левее.
Логотип норм.
https://github.com/nsdvw/TestHub/blob/master/tests/AppBundle/Controller/DefaultControllerTest.php
Тест стоит удалить как и сам default controller.
А, еще, какую БД ты используешь? Если mysql то советую вместо нее попробовать постгрес. Она использует тот же SQL так что на 80% там все то же, но у нее есть много других возможностей:
- строгий режим включен по умолчанию
- соответствие стандартам ANSI SQL
- поддерживается CHECK на колонках и таблицах (погугли что это)
- индексы по выражениям
- географические типы данных и индексы (вроде найти точки в радиусе R от данной)
- полнотекстовые индексы
- поддержка JSON с индексацией
mysql ты и так наверно знаешь. Полезно изучить и постгрес еще. Вполне возможно, что в перспективе она обойдет mysql по использованию. mysql сильно тормозит тот факт что она оракловская (оракл не хочет развивать конкурента дорогущей Oracle DB) и в некоторых случаях довольно-таки легкомысленно обращается с данными.
Плюс, не знаю, важно ли это, но сейчас есть некоторое движение по замене Oracle/MSSQL на открытые БД во всяких крупных организациях, госорганизациях и обычно как замена используется именно постгрес. Хотя это конечно происходит не с такой скоростью, как хотелось бы.
Насчет генерации схемы из моделей. Это конечно красиво выглядит в теории, но на практике этого будет недостаточно, так как тебе захочется добавлять разные SQL опции в таблицы. Так что настраивайся на то, что надо будет переходить на SQL дампы и миграции.
По дизайну:
- список тестов и тегов идет равномерной стеной и не очень очевидно какие теги к какому тесту относятся. Я бы предложил прижать теги ближе к заголовку теста или добавить серые бледные полосочки-разделители
- мне не нравится современная тенденция делать неподчеркнутые ссылки (речь о заголовках тестов). Хуже, часто их делают еще и темно-синим цветом который тяжело отличить от черного, а посеещенные ссылки - черным.
Конечно, подчеркивание, особенно когда его много, сильно отвлекает внимание и оно бывает "тяжелым" на надписям крупного размера. Но для этого есть способы борьбы - например подчеркивание можно делать через border-bottom на inline-элементе, сделав его тонким и бледненьким.
https://www.artlebedev.ru/kovodstvo/sections/171/
Кнопка "создать тест" слишком близко к тексту (сравни с расстоянием между абзацами) и такое ощущение что ее не мешало бы сдвинуть на пару пикселей левее.
Логотип норм.
https://github.com/nsdvw/TestHub/blob/master/tests/AppBundle/Controller/DefaultControllerTest.php
Тест стоит удалить как и сам default controller.
А, еще, какую БД ты используешь? Если mysql то советую вместо нее попробовать постгрес. Она использует тот же SQL так что на 80% там все то же, но у нее есть много других возможностей:
- строгий режим включен по умолчанию
- соответствие стандартам ANSI SQL
- поддерживается CHECK на колонках и таблицах (погугли что это)
- индексы по выражениям
- географические типы данных и индексы (вроде найти точки в радиусе R от данной)
- полнотекстовые индексы
- поддержка JSON с индексацией
mysql ты и так наверно знаешь. Полезно изучить и постгрес еще. Вполне возможно, что в перспективе она обойдет mysql по использованию. mysql сильно тормозит тот факт что она оракловская (оракл не хочет развивать конкурента дорогущей Oracle DB) и в некоторых случаях довольно-таки легкомысленно обращается с данными.
Плюс, не знаю, важно ли это, но сейчас есть некоторое движение по замене Oracle/MSSQL на открытые БД во всяких крупных организациях, госорганизациях и обычно как замена используется именно постгрес. Хотя это конечно происходит не с такой скоростью, как хотелось бы.
Насчет генерации схемы из моделей. Это конечно красиво выглядит в теории, но на практике этого будет недостаточно, так как тебе захочется добавлять разные SQL опции в таблицы. Так что настраивайся на то, что надо будет переходить на SQL дампы и миграции.
Вообще команды от Симфони плохие. Они непонятные и там нет защиты от ошибок. Что если Апач не запущен? Что если запущен, но для других целей? Мне они не нравятся.
В ридми ты забыл упомянуть что после правки конфига надо почистить кеш симфони (cache:clear --env=-prod), также надо сделать assetic:dump для prod так как composer install почему-то делает его для dev.
И конечно там какой-то ад с этими конфигами, их много.
Перед этой строчкой
if ($text[$i] == $text[$x]){
поставь echo или var_dump который выведет, чему равны переменные, и какие буквы сравниваются.
И кстати, с чего ты взял что $text[$i] это i-я буква? Это i-й байт, в utf-8 буква состоиит из нескоьких байт и это не работает, урок https://gist.github.com/codedokode/ff99e357e9860ea169b8
Wordpress -> wooCommerce .
Кроме падения, процесс вкладки мог быть завершен принудительно - например из-за долгой работы скрипта появился диалог и пользователь выбрал принудительное завершение.
Я не знаю, собирает ли кто-то такие данные, но какая разница. Мы как перфекционисты хотели бы иметь статистику падений и принудительных завершений. Чтобы например увидеть что в определенном браузере или на определенной странице есть проблемы.
Предложите ваши варианты реализации системы сбора такой информации.
>полином
>где я обосрался?
Уже на этом месте, например.
>$text = mb_strtolower ($text);
>$text[$i]
Ты путаешь строки и массивы.
Предыдущие задачи точно решил? Покажи парочку (или ссылки на них в треде дай, если уже показывал).
Допустим, постом передаём логин и пасс скрипту, делаем проверку с БД и если всё забеись, делаем такое $_SESSION['logged_id'] = 1;
Теперь, для того, чтобы отобразить закрытую страницу, достаточно просто чекнуть
if (isset($_SESSION['logged_id']) {# some shit}
так?
Вопрос в том, может ли сторона юзера это как-то подделать? Сессии же по идее на серваке храняться, так что видимо, не может?
Прочел новость про то что Тесла продает "программный" апгрейд аккумулятора: https://m.geektimes.ru/post/275516/
В комментариях разумеется собрались одни глупцы: одни пытаются объяснить как это всем выгодно, другие говорят что без этого машина стоило бы дороже. Мол делать две линии и производить 2 модели аккумуляторов дорого.
Это все конечно чушь. Цена на S70 полностью покрывает стоимость более мощного аккумулятора. Реальная причина - сегментация рынка с целью заработать больше денег. Про этот принцип хорошо написал Джоэл: http://russian.joelonsoftware.com/Articles/CamelsandRubberDuckies.html (очень интересная статья)
Логика производителя очень простая. Есть те кто побогаче и те, кто победнее. Хорошо иметь возможность брать за один и тот же товар с богатых больше. Поскольку просто так ты сделать это не можешь - приходится придумывать искуственные ограничения.
Только Тесла тут поступила еще умнее: вместо того чтобы продавать только 2 модели (точнее одну и ту же модель по разным ценам), она продает еще и апгрейд с первой на вторую - чтобы "нищеброды", которые не могут сразу купить более дорогую модель, могли подкопить денег и купить апгрейд позже.
Разумеется все это Тесле с рук не сойдет. Никто не любит когда ему пытаются впарить по большей цене точно такую же модель (S75) как и дешевая (S70). Если бы я выбирал машину, это было бы фактором против теслы.
А плохо станет, когда выбора уже не останется.
http://codepad.org/525kYmga
А тебе зачем?
>11 месяцев спустя, долг = 4138.1720657184, выплачено 55000
Смотри, у тебя долг на 11-ом месяце 4138 с копейками. Далее тебе надо ещё раз прибавить к этому 3% и комиссию в 1000 рублей. Это происходит у тебя, но остаётся 262 рубля.
После этого твой скрипт просто прибавляет их к окончательной сумме выплат и всё.
А ведь он должен снова прибавить уже к 262-м рублям 3% и 1000 рублей комиссии.
Правильный ответ - 61270 рублей со многими копейками.
После того, как добьёшься выплаты верной суммы, протестируй на сумме кредита в 1000 рублей - итоговая сумма выплат в этом случае составит 2030 рублей.
Установил MySQL, вроде всё нормально (всё по настройкам из коробки, я же не разбираюсь пока).
Читаю по советам ОПа вот это: http://phpclub.ru/mysql/doc/connecting-disconnecting.html
Ну и там сразу идёт:
"3.1. Подсоединение к серверу и отсоединение от него
При подключении к серверу с помощью mysql обычно нужно ввести имя пользователя MySQL и, в большинстве случаев, пароль. Если сервер запущен не на том компьютере, с которого вы вошли в систему, необходимо также указать имя хоста. Параметры соединения (а именно - соответствующее имя хоста, пользователя и пароль) вы сможете узнать у администратора. Получив соответствующие параметры, подсоединиться к серверу можно следующим образом:
shell> mysql -h host -u user -p
Enter password:"
Это всё через консоль, что ли делать надо?
Ни черта же непонятно, где что.
Мускул стоит - что вообще дальше-то?
Может, есть какие не древние видеоуроки или что-то опять же более-менее современное в текстовом виде?
У меня СТУПОР, короче.
Урок по командной строке есть в ОП посте. Прочти его.
Тебе надо запустить программу с названием mysql, которая является клиентом, то есть позволяет отправлять запросы на сервер. Если она у тебя в PATH то команда пишется как
mysql ...
Если нет то надо писать полный путь
c:\mysql\bin\mysql.exe ....
Где взять параметры подключения?
хост = localhost, то есть твой компьютер
порт = стандартный 3306, указывать не надо
пользователь = root
пароль = задается при установке mysql, если ты не задавал то наверно пустой
Узнать подробности о клиенте mysql можно набрав
mysql --help
(если он не в PATH то с полным указанием пути)
Будь внимателен к пробелам, точкам, слешам и не перепутай ничего.
Я же над дизайном и не заморачивался, сделал по ваерфреймам gomockingbird.
Если есть макет, без проблем сверстаю.
>мне не нравится современная тенденция делать неподчеркнутые ссылки (речь о заголовках тестов)
Так то не ссылки, смотри свой прототип. Там должны быть отдельные ссылки-кнопки для запуска тестов, заголовки делать ссылкой как-то не хотеть.
>Логотип норм.
Мне тоже понравился, с намеком на тестирование ПО.
Позаимствован с shutterstock (в фотошопе замазал ватермарку).
Что можешь сказать по теме защиты петушиных авторских прав? Я имею ввиду, как устанавливается идентичность изображений, если я подкорректирую оригинал?
>Тест стоит удалить как и сам default controller.
Тест понятно, это шло из коробки. А контроллер-то зачем? Я удалил то что там было, пишу в него свои экшены.
На постгрес потом потихоньку перейдем, тем более что уже используется доктрина, так что это не будет проблемой.
>И конечно там какой-то ад с этими конфигами, их много.
Увы, пока не нашел подробного описания, за что отвечает каждая настройка. В мануале пишут: "расслабьтесь посоны, оно само придет с опытом)))00"
>For now, don't worry about the specific configuration options in each section. The configuration file ships with sensible defaults. As you read more and explore each part of Symfony, you'll learn about the specific configuration options of each feature.
Так и живем.
>Насчет генерации схемы из моделей. Это конечно красиво выглядит в теории, но на практике этого будет недостаточно, так как тебе захочется добавлять разные SQL опции в таблицы.
Да, уже заметил, что некоторые фишки в аннотациях особо и не укажешь.
Но в оф.доках к migrations bundle советуют как раз таки генерировать миграции автоматически на основе аннотаций.
>The moral of the story is this: after each change you make to your Doctrine mapping information, run the doctrine:migrations:diff command to automatically generate your migration classes.
Впрочем, есть подозрение, что symfony book рассчитан на умственно отсталых даунов с синдромом дауна, поэтому они так небрежно описывают подобные моменты.
Использовать подход с миграциями на основе аннотаций может быть имеет смысл с точки зрения согласованности, то есть если писать отдельно аннотации и отдельно миграции, неизбежны разночтения.
Ну ладно, я пока освою "ручные" миграции, как минимум в учебных целях пригодится.
Я же над дизайном и не заморачивался, сделал по ваерфреймам gomockingbird.
Если есть макет, без проблем сверстаю.
>мне не нравится современная тенденция делать неподчеркнутые ссылки (речь о заголовках тестов)
Так то не ссылки, смотри свой прототип. Там должны быть отдельные ссылки-кнопки для запуска тестов, заголовки делать ссылкой как-то не хотеть.
>Логотип норм.
Мне тоже понравился, с намеком на тестирование ПО.
Позаимствован с shutterstock (в фотошопе замазал ватермарку).
Что можешь сказать по теме защиты петушиных авторских прав? Я имею ввиду, как устанавливается идентичность изображений, если я подкорректирую оригинал?
>Тест стоит удалить как и сам default controller.
Тест понятно, это шло из коробки. А контроллер-то зачем? Я удалил то что там было, пишу в него свои экшены.
На постгрес потом потихоньку перейдем, тем более что уже используется доктрина, так что это не будет проблемой.
>И конечно там какой-то ад с этими конфигами, их много.
Увы, пока не нашел подробного описания, за что отвечает каждая настройка. В мануале пишут: "расслабьтесь посоны, оно само придет с опытом)))00"
>For now, don't worry about the specific configuration options in each section. The configuration file ships with sensible defaults. As you read more and explore each part of Symfony, you'll learn about the specific configuration options of each feature.
Так и живем.
>Насчет генерации схемы из моделей. Это конечно красиво выглядит в теории, но на практике этого будет недостаточно, так как тебе захочется добавлять разные SQL опции в таблицы.
Да, уже заметил, что некоторые фишки в аннотациях особо и не укажешь.
Но в оф.доках к migrations bundle советуют как раз таки генерировать миграции автоматически на основе аннотаций.
>The moral of the story is this: after each change you make to your Doctrine mapping information, run the doctrine:migrations:diff command to automatically generate your migration classes.
Впрочем, есть подозрение, что symfony book рассчитан на умственно отсталых даунов с синдромом дауна, поэтому они так небрежно описывают подобные моменты.
Использовать подход с миграциями на основе аннотаций может быть имеет смысл с точки зрения согласованности, то есть если писать отдельно аннотации и отдельно миграции, неизбежны разночтения.
Ну ладно, я пока освою "ручные" миграции, как минимум в учебных целях пригодится.
Давайте тогда мы, как разработчики веб-сайтов, требовать пожизненный процент от прибыли с каждого сделанного говносайта.
Ладно еще автор контента, но всякие посредники и прочие бездельники достойны только кала и мочи в качестве "отчислений".
> Что можешь сказать по теме защиты петушиных авторских прав? Я имею ввиду, как устанавливается идентичность изображений, если я подкорректирую оригинал?
В авторском праве есть понятие "производная работа". Ты не имеешь права просто так взять и "подправить" чужую работу, и распространять.
> как устанавливается идентичность изображений,
А какая разница? Правонарушение происходит в тот момент когда ты берешь чужую работу, обнаружат это потом или нет - ничего не меняет.
У нас конечно есть некий недостаток правовой культуры и люди осознанно нарушают законы, не только что касается авторского права, а например что касается правил парковки, выгула собак, поведения в обществнных местах. В этой плане конечно наша страна находится далеко в темном прошлом.
На Западе все по-другому. За нарушение авторских прав там есть наказание и нешуточное. Однажды кто-то из наших поставил на сайт западного заказчика первое попавшееся фото из интернета. Владелец фото узнал об этом и написал заказчику. Заказчик перепугался, к счастью ему удалось отделаться извинениями.
А у нас люди почему-то думаю, что если картинка лежит в интернете то можно взять ее и использовать. Некоторые даже на полном серьезе думают что плата провайдеру автоматически покрывает доступ к любому виду контента.
Надо стремиться не копировать пиратское ПО, а развивать свободное.
> Увы, пока не нашел подробного описания, за что отвечает каждая настройка
Можно найти класс который их читает. Обычно в Симфони разбором конфигурации занимается конкретный бандл которому она нужна. Он задает правила и ограничения для нужных ему полей конфига и читает их значения.
Задавая ограничения, надо помнить, что применяться они будут не при каждом вызове скрипта, а только при "компиляции" бандлов.
Ну например, возьмем DoctrineBundle которая интегрирует доктрину в Симфони
Открываем https://github.com/doctrine/DoctrineBundle/blob/master/DoctrineBundle.php#L62
Там метод build отвечает за "компиляцию" бандла, которая вроде бы делается при прогреве кеша, а вот boot() вызывается при каждом запуске приложения.
Вот здесь https://github.com/doctrine/DoctrineBundle/blob/master/DependencyInjection/Configuration.php задаются ограничения для значений конфига.
Здесь они разбираются: https://github.com/doctrine/DoctrineBundle/blob/master/DependencyInjection/DoctrineExtension.php
Назначение этих файлов описано в мануале по бандлам, я плохо помню подробности, поищи их тут:
http://symfony.com/doc/current/cookbook/bundles/configuration.html
http://symfony.com/doc/current/book/bundles.html
http://symfony.com/doc/current/components/config/definition.html
http://symfony.com/doc/current/cookbook/bundles/extension.html
Также, там упоминается doctrine-bridge. В нем есть https://github.com/symfony/doctrine-bridge/blob/master/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php который ищет в конфигурации DI контейнера у сервисов теги вроде doctrine.event_listener и ставит эти сервисы обработчиками событий доктрины. Это делаетя на этапе "компиляции" контейнера, надеюсь ты знаешь что в целях оптимизации в продакшене DI контейнер компилируется из кучи конфигов (каждый бандл может что-то добавить в него от себя) в один большой файл и кладется в кеш.
Вот это я разобрал как найти в коде место где описаны и читаются параметры конфигурации одного бандла. Остальные бандлы устроены примерно так же.
> Что можешь сказать по теме защиты петушиных авторских прав? Я имею ввиду, как устанавливается идентичность изображений, если я подкорректирую оригинал?
В авторском праве есть понятие "производная работа". Ты не имеешь права просто так взять и "подправить" чужую работу, и распространять.
> как устанавливается идентичность изображений,
А какая разница? Правонарушение происходит в тот момент когда ты берешь чужую работу, обнаружат это потом или нет - ничего не меняет.
У нас конечно есть некий недостаток правовой культуры и люди осознанно нарушают законы, не только что касается авторского права, а например что касается правил парковки, выгула собак, поведения в обществнных местах. В этой плане конечно наша страна находится далеко в темном прошлом.
На Западе все по-другому. За нарушение авторских прав там есть наказание и нешуточное. Однажды кто-то из наших поставил на сайт западного заказчика первое попавшееся фото из интернета. Владелец фото узнал об этом и написал заказчику. Заказчик перепугался, к счастью ему удалось отделаться извинениями.
А у нас люди почему-то думаю, что если картинка лежит в интернете то можно взять ее и использовать. Некоторые даже на полном серьезе думают что плата провайдеру автоматически покрывает доступ к любому виду контента.
Надо стремиться не копировать пиратское ПО, а развивать свободное.
> Увы, пока не нашел подробного описания, за что отвечает каждая настройка
Можно найти класс который их читает. Обычно в Симфони разбором конфигурации занимается конкретный бандл которому она нужна. Он задает правила и ограничения для нужных ему полей конфига и читает их значения.
Задавая ограничения, надо помнить, что применяться они будут не при каждом вызове скрипта, а только при "компиляции" бандлов.
Ну например, возьмем DoctrineBundle которая интегрирует доктрину в Симфони
Открываем https://github.com/doctrine/DoctrineBundle/blob/master/DoctrineBundle.php#L62
Там метод build отвечает за "компиляцию" бандла, которая вроде бы делается при прогреве кеша, а вот boot() вызывается при каждом запуске приложения.
Вот здесь https://github.com/doctrine/DoctrineBundle/blob/master/DependencyInjection/Configuration.php задаются ограничения для значений конфига.
Здесь они разбираются: https://github.com/doctrine/DoctrineBundle/blob/master/DependencyInjection/DoctrineExtension.php
Назначение этих файлов описано в мануале по бандлам, я плохо помню подробности, поищи их тут:
http://symfony.com/doc/current/cookbook/bundles/configuration.html
http://symfony.com/doc/current/book/bundles.html
http://symfony.com/doc/current/components/config/definition.html
http://symfony.com/doc/current/cookbook/bundles/extension.html
Также, там упоминается doctrine-bridge. В нем есть https://github.com/symfony/doctrine-bridge/blob/master/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php который ищет в конфигурации DI контейнера у сервисов теги вроде doctrine.event_listener и ставит эти сервисы обработчиками событий доктрины. Это делаетя на этапе "компиляции" контейнера, надеюсь ты знаешь что в целях оптимизации в продакшене DI контейнер компилируется из кучи конфигов (каждый бандл может что-то добавить в него от себя) в один большой файл и кладется в кеш.
Вот это я разобрал как найти в коде место где описаны и читаются параметры конфигурации одного бандла. Остальные бандлы устроены примерно так же.
У меня подозрение что автоматические миграции могут быть рассчитаны на случаи, когда особой оптимизации БД не требуется, может на этапе разработки прототипа. Но чем доктрина и хороша, что не заставляет тебя их использовать и разрешает тебе саму управлять базой. Тем более что в Doctrine DBAL есть средства для ручных миграций.
>>40456
> Давайте тогда мы, как разработчики веб-сайтов, требовать пожизненный процент
А кто тебе запрещает? Подпиши договор и требуй. И аналогия не очень удачная - дизайнер логотипа ведь не требует себе пожизненных отчислений, он в данном случае ничем от программиста не отличается.
Насчет авторского права, мне кажется тут во всем виновато то, что ты можешь что-то взять не сталкиваясь с автором. Представь, если бы программу можно было получить только напрямую из рук автора. Вряд ли бы было много желающих подойти к автору и попросить дать программу бесплатно.
Хотя конечно у авторского права есть свои проблемы - например малопопулярные книги или аудиозаписи могут со временем исчезать, так как переиздавать их нерентаьельно, а бесплатно их не отдадут. Теряется чей-то интеллектуальный труд. Надеюсь, в будущем придумают какое-нибудь решение, какую-нибудь библиотеку, хранящую копии всех старых произведений.
Надо разбить задачу на составные части. Там вроде в уроке написано, напомню еще раз:
- функция spellSmallNumber(), принимает на вход число от 0 до 999 и возвращает его текстовую форму: spellSmallNumber(123) → 'сто двадцать три'
- функция выбора формы слова, принимает на вход число и 3 формы слова и возвращает подходящую: getWordForm(23, 'кот', 'кота', 'котов') → 'кота'. С ее помощью склоняются слова вроде "тысяч".
- основная функция spellNumber(), которая принимает на вход большое число, разбивает его на части по 3 цифры и вызывая указанные выше функции, получает число прописью.
Первую и вторую функцию можно сделать отдельно, проверить, и если все ок, делать третью функцию.
Что именно тебе непонятно? На чем застрял?
Итак, если я хочу использовать DataMapper в контроллере, то есть такие варианты:
1. Захардкодить в контроллере $studentMapper = new StudentMapper. Плохо, мне нужно будет лезть в класс контроллера, если нужно поменять маппер + здесь ещё нужно объект PDO передавать в StudentMapper.
2. Передавать готовый StudentMapper в конструктор контроллера. Лучше, но контроллер по-прежнему знает, что ему передаётся не просто маппер, а StudentMapper.
3. Использовать IoC, конструктор контроллера будет выглядеть примерно так: __construct(IMapper $mapper), а для всех мапперов указать implements IMapper, чтобы у них были одинаковые методы. Контроллер не знает, что за маппер ему передали, классы стали менее связаны.
А теперь если я хочу передать несколько мапперов в контроллер? Нужны ServiceLocator или DI-контейнер. Но как будет выглядеть тело конструктора контроллера теперь? Примерно так:
$this->studentMapper = $mapperLocator->getMapper('Student');
Это плохо?
Начал решение задачи с написания третьей функции и сразу же застопорился на моменте с разбиением: на пике процесс разбиения если число имеет длину от 7 до 9 знаков, то есть для миллионов. Только подходит от не для всех чисел. Если например число будет иметь вид 900000000 то выведет 0 0 900 вместо 000 000 900. И я вот не могу уловить, как потом это учитывать в функции перевода в текстовую форму.
> то выведет 0 0 900 вместо 000 000 900
А чем отличается 000 от 0? Это просто неправильная форма записи нуля. В PHP, как и в математике, число не может быть равно 000.
в php возможна строка "000" (обрати внимание на кавычки), но в данной задаче надо все решать через числа.
И я не понимаю, как это тебе мешает.
Отвечая на твой вопрос: ничего с этим делать не надо, числа бывают и меньше 3 разрядов и это нормально.
На память может кто-нибудь помнит, как сменить драйвер в доктрине?
Для mysql это pdo_mysql, а для постгрес?
pdo_postrgres? postgres? postgresql?
Миграции через doctrine:migrations:diff генерируют оказывается голый sql, ну в смысле
$this->addSql('ALTER TABLE ...');
Диалекты-то сильно отличаются?
Причины, по которой мне не очень охота писать миграции вручную, во-первых это кол-во букв
(мне вот сейчас нужно создать около 8 таблиц, в каждой в среднем 5-7 колонок, куча индексов и ключей, я заколебаюсь писать
$table = $schema->createTable('first'); $table->addColumn('abc', 'string', [options])),
во-вторых опять отсутствие внятной документации.
Вот мне нужно сделать nullable колонку, ну в смысле чтобы она могла принимать значение null.
Пишу $table->addColumn('abc', 'string', ['nullable'=>true]) как в аннотациях, все равно генерирует NOT NULL.
Синтаксис другой походу, не как в аннотациях.
Вместо GeneratedValue нужно писать
'autoincrement' => true
Откуда я об этом узнаю? Из какого-то левого примера
http://docs.doctrine-project.org/projects/doctrine-migrations/en/latest/reference/generating_migrations.html#without-the-orm
А все остальные опции? Что, опять придется как лох читать исходный код?
Я сегодня смотрел
http://stackoverflow.com/questions/10588646/how-to-change-a-database-to-postgresql-with-symfony-2-0
> database_driver: pdo_pgsql
Где-то в конфиге или parameters.yml и перегенерировать кеш.
> (мне вот сейчас нужно создать около 8 таблиц, в каждой в среднем 5-7 колонок, куча индексов и ключей, я заколебаюсь писать
Ну не смеши, по 2 минуты на таблицу = 16 минут.
Как вариант можно генерировать и потом править SQL вручную.
> Пишу $table->addColumn('abc', 'string', ['nullable'=>true]) как в аннотациях, все равно генерирует NOT NULL.
Видимо такой опции нет или ты что-то напутал.
> Синтаксис другой походу, не как в аннотациях.
Видимо
Доки: http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/schema-representation.html
Ссылка на них стоит в самом начале страницы http://docs.doctrine-project.org/projects/doctrine-migrations/en/latest/reference/generating_migrations.html
Так драйвер же не получится сменить.
Я не знаю postgres, но наверное там есть отличия в диалекте. Например только что нагуглил вместо auto_increment используется какой-то serial.
Квери билдер это абстракция.
По 2 минуты на таблицу это когда знаешь наизусть апи, а я пока даже не знаю где это само апи искать.
Буду надеяться, что тут все есть
http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/schema-representation.html
Я думал за сегодня успею полностью написать кусок с прохождением тестов, ты тут со своими миграциями и постгресом.
Спасибо, пойду думать
Я сегодня разворачивал твой проект на c9.io, и после замены конфига он мне сгенеирировал нужный SQL для постгрес, который тот с радостью принял.
> Например только что нагуглил вместо auto_increment используется какой-то serial.
Вообще, в постгресе генерация id отвязана от таблиц.Там есть отдельный объект - SEQUENCE который умеет генерировать последовательно возрастающие id. Ты можешь использовать его как хочешь. Тип SERIAL просто создает sequence и привязывает ее к полю таблицы.
Почему не захуярить логику работы с данными в модели?
Допустим есть класс Student
private $mapper;
В нем конструктор
public function __construct()
{
$this->mapper = new StudentMapper();
}
и дальше уже дохерачить методы save, delete, load
ну?
Зачем делать привязку контроллеров к определенному классу? Почему бы просто не передавать контейнер в конструктор, а потом уже извлекать из него все что тебе нужно? Это конечно не очень хороший способ, потому что в больших приложениях ты можешь передавать 200+ элементов в контроллер, когда тебе нужен только один, но в студентах это вполне подойдет, наверное.
>>40817
Это называется Active Record. В данном случае код работы с базой и модель в одном классе, хотя модель вполне может существовать сама по себе. Решением этой проблемы является как раз Data Mapper который хочет использовать анон.
Так суть DataMapper (насколько я понял) в том, что мы разделяем слой модели (которая теперь является точной проекцией содержимого строки таблицы) от слоя, который этой моделью манипулирует.
Это в ActiveRecord модель может сама себя удалять и записывать в БД.
Вспомнил и прилепил забавную картинку.
Так ведь можно вынести код работы именно с базой в StudentMapper таким образом полностью отделив саму базу от модели. Допустим в классе маппера тоже будут, save только он уже принимает данные из модели и сейвает их в базу.
Сорян, я сам то ни разу не очень опытен в проектировании, но по мне это логично и удобно
Здесь суть в coffee.drink() и сoffee.refill()
Кофе пьёт и кофе наполняет.
Язык может быть Java или C#, для этих языков нужно при инициализации указывать тип данных переменной. Тип данных в данном случае - Сoffee.
Это методы
Явно не по PSR, скобки перенесены после условия на новую строку, название свойства с большой буквы, методы с большой буквы.
Есть два стиля форматирования кода основных, лол, в одном из них скобки по разному после условия
В PSR один стиль.
Ага, потому, что это ебучий php стандарт, но кроме его существует еще хуевая туча различных стилей под различные цмс и фреймворки, пошел нахуй.
И кроме того, в различных языках в стандартах прослеживаются вот именно 2 стиля.
Это C#, у Java методы с маленькой буквы пишут.
> Итак, если я хочу использовать DataMapper в контроллере, то есть такие варианты:
- передать DM через конструктор
- передать DI контейнер через конструктор
> . Захардкодить в контроллере $studentMapper = new StudentMapper. Плохо, мне нужно будет лезть в класс контроллера, если нужно поменять маппер + здесь ещё нужно объект PDO передавать в StudentMapper.
Плохо тем что если DM нужен где-то еще, придется копипастить код его создания. Плюс, нет гарантий что DM существует в одном экземпляре. Да и вообще, создание сервисов лучше сделать где-то в одном месте.
> Передавать готовый StudentMapper в конструктор контроллера. Лучше, но контроллер по-прежнему знает, что ему передаётся не просто маппер, а StudentMapper.
Норм
> 3. Использовать IoC, конструктор контроллера будет выглядеть примерно так: __construct(IMapper $mapper), а для всех мапперов указать implements IMapper
Неправильно. Если ты пишешь IMapper то это значит что контроллер готов работать с любым маппером, но ведь это не так, верно? Если ты решил сделать внедрение зависимостей через интерфейсы, то надо писать IStudentMapper тогда.
> А теперь если я хочу передать несколько мапперов в контроллер?
Либо передать их по отдельности либо передать DI контейнер.
>>40817
Потому что класс Student отвечает за хранение данных о студенте. Работать с БД не его обязанность. Ты предлагаешь смешать 2 обязанности в одном классе.
Ну и когда студент отвязан от Бд, это делает код более универсальным. Например, у нас есть функция которая что-то делает со студентом:
function doSomething(Student $student)
для тестов мы можем создавать просто объект студента, не привязанный к БД (через new) и передавать в функцию. БД этой функции вообще не нужна.
Подход когда код работы с БД засунут в модель, называется active Record - это вроде было в моем уроке по паттернам работы с БД.
> Итак, если я хочу использовать DataMapper в контроллере, то есть такие варианты:
- передать DM через конструктор
- передать DI контейнер через конструктор
> . Захардкодить в контроллере $studentMapper = new StudentMapper. Плохо, мне нужно будет лезть в класс контроллера, если нужно поменять маппер + здесь ещё нужно объект PDO передавать в StudentMapper.
Плохо тем что если DM нужен где-то еще, придется копипастить код его создания. Плюс, нет гарантий что DM существует в одном экземпляре. Да и вообще, создание сервисов лучше сделать где-то в одном месте.
> Передавать готовый StudentMapper в конструктор контроллера. Лучше, но контроллер по-прежнему знает, что ему передаётся не просто маппер, а StudentMapper.
Норм
> 3. Использовать IoC, конструктор контроллера будет выглядеть примерно так: __construct(IMapper $mapper), а для всех мапперов указать implements IMapper
Неправильно. Если ты пишешь IMapper то это значит что контроллер готов работать с любым маппером, но ведь это не так, верно? Если ты решил сделать внедрение зависимостей через интерфейсы, то надо писать IStudentMapper тогда.
> А теперь если я хочу передать несколько мапперов в контроллер?
Либо передать их по отдельности либо передать DI контейнер.
>>40817
Потому что класс Student отвечает за хранение данных о студенте. Работать с БД не его обязанность. Ты предлагаешь смешать 2 обязанности в одном классе.
Ну и когда студент отвязан от Бд, это делает код более универсальным. Например, у нас есть функция которая что-то делает со студентом:
function doSomething(Student $student)
для тестов мы можем создавать просто объект студента, не привязанный к БД (через new) и передавать в функцию. БД этой функции вообще не нужна.
Подход когда код работы с БД засунут в модель, называется active Record - это вроде было в моем уроке по паттернам работы с БД.
Также, ты заметил что в твоем коде явно проблемы с DI? Ты создаешь маппер, а где ты берешь объект PDO для него?
>>40830
> Зачем делать привязку контроллеров к определенному классу?
Это как раз логично так как контроллеру конкретный класс и нужен. Явно обозначить зависимость, что ему нужны классы X и Y - это хорошо.
> Это конечно не очень хороший способ, потому что в больших приложениях ты можешь передавать 200+ элементов в контроллер, когда тебе нужен только один,
Тут есть разные точки зрения. Некоторые считают что контроллер это полноценный сервис и должен использовать DI, другие что контроллер это одноразовый, не повторно используемый код и не надо заморачиваться, можно просто передать контейнер.
>>40832
Код на картинке сомнительный. Кофе само себя выпило? Само себя наполнило?
Логичнее было бы
human.drink(coffee);
или если нам не важно кто его выпил то
coffee.decreaseVolume(10);
>>40846
Ява (анон ниже пишет до диез, ну да ладно). Там перед переменными не ставится доллар и пишется тип переменной. Coffee coffee значит переменная coffee являющаяся объектом класса Coffee. Точка там значит обращение к полю или методу, то есть аналог стрелочки в php. Теперь ты можешь читать и ява-код тоже.
>>40854
Метод в каком-то смысле функция в классе.
>>40895
А вот в C# как я помню стандарта оформления нет и каждый пишет как хочет.
> И кроме того, в различных языках в стандартах прослеживаются вот именно 2 стиля.
Только там где нет единого стандарта, там и бардак.
Также, ты заметил что в твоем коде явно проблемы с DI? Ты создаешь маппер, а где ты берешь объект PDO для него?
>>40830
> Зачем делать привязку контроллеров к определенному классу?
Это как раз логично так как контроллеру конкретный класс и нужен. Явно обозначить зависимость, что ему нужны классы X и Y - это хорошо.
> Это конечно не очень хороший способ, потому что в больших приложениях ты можешь передавать 200+ элементов в контроллер, когда тебе нужен только один,
Тут есть разные точки зрения. Некоторые считают что контроллер это полноценный сервис и должен использовать DI, другие что контроллер это одноразовый, не повторно используемый код и не надо заморачиваться, можно просто передать контейнер.
>>40832
Код на картинке сомнительный. Кофе само себя выпило? Само себя наполнило?
Логичнее было бы
human.drink(coffee);
или если нам не важно кто его выпил то
coffee.decreaseVolume(10);
>>40846
Ява (анон ниже пишет до диез, ну да ладно). Там перед переменными не ставится доллар и пишется тип переменной. Coffee coffee значит переменная coffee являющаяся объектом класса Coffee. Точка там значит обращение к полю или методу, то есть аналог стрелочки в php. Теперь ты можешь читать и ява-код тоже.
>>40854
Метод в каком-то смысле функция в классе.
>>40895
А вот в C# как я помню стандарта оформления нет и каждый пишет как хочет.
> И кроме того, в различных языках в стандартах прослеживаются вот именно 2 стиля.
Только там где нет единого стандарта, там и бардак.
Оп-кун, я предлагаю не совсем Active Record. Да и я знаю, что такое ActiveRecord только я уже здесь почти не учусь, а больше года работаю ;)
Я предлагал что-то в духе Magento.
Student - модель, а StudentMapper ресурс для работы с базой данных, вот к примеру метод в Magento:
/
Load object data
@param integer $id
@return Mage_Core_Model_Abstract
*/
public function load($id, $field=null)
{
$this->_beforeLoad($id, $field);
$this->_getResource()->load($this, $id, $field);
$this->_afterLoad();
$this->setOrigData();
$this->_hasDataChanges = false;
return $this;
}
Что значит "не совсем active record". Если модель сама себя загружает из базы - это Active Record. Это я не сам придумал, названия и описания паттернов я взял у Фаулера.
Пример который ты привел это Active Record. Причем еще и с каким-то нездоровым количеством подчеркиваний. Им PHP5 все еще не завезли что ли?
Там можно определять функцию внутри функции/возвращать функцию из вызова функции? Интересно посмотреть пример
Ты о Magento не слышал? ;) Это наверное лучшая e-commerce cms.
Это не классический ActiveRecord, а все потому, что работы с базой в модели нет, вся связь с базой вынесенна в ресурсные модели
https://github.com/TheSidSpears/Students
1. Правильно ли я реализовал MVC?
2. Слышал о подходе, когда в классе все свойства закрытые, а обращаются к ним/изменяют через getter'ы/setter'ы. Нужно ли это?
3. Критикуйте полностью. Пока у меня нет уверенности, что я на верном пути, поэтому пока не оценят, дальше делать боюсь
Важно же, с ресурсными моделями нивелируется недостаток абстрактных источников данных, ресурсные модели могут работать с любыми абстракциями будь то json,csv,db,api
ммм, магия.
Бамп
Возможно больше абстракции
>хекслет
>годнота
Скрины сделаны тогда, когда я проходил там один из тестов.
А ОП упоминал, что данные из инпутов нужно trim'ить, это азы. На хекслете об этом не знают.
Забавно, бредово.
Но вообще пока присматриваюсь к Хекслету и Гикбрейнс, к бесплатным курсам.
Так вот Гикбрейнс - просто плохо, всё просто ужасно с бесплатными курсами. Настолько плохо, что платные даже не хочется смотреть. А на Хекслете вроде получше всё.
Другой аноним
Ну а ты не задавался вопросами, откуда ребята из видеоуроков знают то, что знают? Наверняка они не по видеоурокам учились. Читали доки, маны, книги, скорее всего на инглише, экспериментировали, пробовали. Но точно не безучастно смотрели за тем, как кто-то на видео, с трудом связывая слова и запинаясь в объяснениях, пишет код.
Рахим в своём курсе по ОС так примерно так и говорит: "Этот курс это выжимка из Таненбаума. Хотите лучше что-то понять - читайте его".
Проблема в том, что надо с чего-то начинать.
ОП даёт хорошую базу знания синтаксиса, всяких массивов и регулярок, а вот после ООП (не считаю HTML+CSS) начинается мрак и тьма, пришедшая со Средиземного моря.
В какой-то момент важно не просто получить огромный список того, что нужно знать, а хотя бы посмотреть и пощупать небольшой рабочий проект, пусть даже простую гостевую книгу или небольшой блог с минимумом функционала.
Для углубления - разумеется, читать нужно много.
Но для старта что-то пошаговое в создании гостевой либо блога было бы неплохим подспорьем для того, кто идёт с нуля.
Я вот сейчас прорабатываю Роберта Никсона "Создаём динамические сайты на PHP..." (>>37470), хотя ОП и не одобряет эту книгу.
Но можно делать то, что делает Никсон, и анализировать это с подходом ОПа - никто же не запрещает.
Я пока очень доволен всем, потому как там объяснения уровня начальных страниц учебника ОПа или даже выше.
Но вот хочется посмотреть и на то, как в нескольких уроках по 10-15 минут от начала до конца создают какой-нибудь динамический сайт.
> Поиск пути: http://ideone.com/33ROxP
> function show_shortest_way($paths,$from,$where,$time,$way,$pointNames,$transportName){
time и way всегда одинаковые, зачем их передавать?
> $GLOBALS['ways']
Что еще за использование глобальных переменных? Тут можно было бы использовать return например вместо этого.
То есть вызов функции всегда возвращает 1 путь. Там, где ты в цикле перебираешь соседние точки, ты собираешь массив путей, выбираешь из них наилучший и возвращаешь его:
....
для каждой соседней точки:
- пути[] = проложить путь через точку
лучшийПуть = выбратьЛучшийИз(пути)
вернуть лучшийПуть
И вообще, глобальные переменные это плохо так как они добавляют функции побочные эффекты. Где гарантия что переменная, установленная при первом вызове функции, не повлияет на второй? Что эту переменную не поменяют где-нибудь еще?
Также, плохо что ты используешь многомерные массивы сложной структуры. Трудно из-за этого разбираться в коде.
> if (isset($GLOBALS['ways']))
> return ($GLOBALS['ways']);
А если переменная не существует, что вернет функция? null? Почему она разные типы данных возвращает?
Ну и конечно рекурсивный алгоритм довольно неэффективный. Лучше было бы использовать например алгоритм Дейкстры или что-то еще. В Википедии есть статья по алгоритмам поиска пути.
> Текст по кругу: http://ideone.com/2q1F4N
> $sina=
надо писать кемелкейсом, например $sinA
> rad2deg(sin($alpha));
Это непраивльно. Синус возвращает не угол, а число от -1 до 1. И переводить его бессмысленно.
> for($x=0;$x<80;$x++){
> echo $screen[$y][$x];
Тут можно использовать implode
> На вашем счету n рублей: http://ideone.com/JXvVHa
> if(($number%100)<=20) //если два посл. числа <=20, то сравниваем их
> $n=$number%100;
надо ставить фигурные скобки в ифе
> $n=$number%100;
> $n=$number%10;
Это усложняет код, когда ты одну переменную используешь для разных целей. Надо было просто сделать что если число от 11 до 19 то возвращаем определенный вариант - было бы проще.
> return $text." ";
Это нелогично. Функция inclineWord выбирает подходящую форму слова. С какой стати она должна добавлять к нему пробелы? Это не ее задача, а того кто ее вызвал.
> $n[1]=$number;
Выглядит странно так как начальное значение переменной $n нигде не присваивается.
> if ($number==0) //при нуле сразу пишем результат
> $text.=$spelling[0]." ";
тут логичнее сразу было ставить return
Сам алгоритм в общем верный, но код оформлен неаккуратно.
> ООО Вектор: http://ideone.com/MbL6Pb
> if(!in_array($department,$this->departments)){
Почитай описание in_array в мануале. Ты исопльзуешь неточное сравнение, которое сравнивает не идентичность объектов, а содержимое их полей. Это в общем плохо, в ООП в 99% случаев надо проверять именно идентичность, то есть хранят ли 2 переменных ссылку на один и тот же или разные объекты.
> public function countEmployees(){
Кстати эту функцию можно попробовать записать через array_reduce и анонимную функцию, может выйдет чуть короче. То есть у тебя верно сделано, но если тебе интересно попробовать другой вариант, то можно попробовать.
> $empls
Не стоит так сокращать. Либо $employees, либо $count, либо $c.
> foreach($this->departments as $k=>$v){
> $k=>$v
Это плохой выбор названий так как $k и $v вообще ничего не значат (как и key/value). Тем более, ты еще и не используешь $k, зачем ее вообще писать тогда?
> public function newEmployee(
Названия функций начинаются с глагола, сделайЧтоТО(), например addEmployee
> public function delEmployee(int $i){
Неправильно выбран аргумент. Вот я хочу удалить сотрудника. Как я узнаю его ключ? Это внутренняя деталь реализации, которая спрятана в классе Department, которую я не знаю и не хочу знать. Надо передавать не индекс, а сам объект-сотрудника.
> public $employees=array(); //сотрудники класса Employee
Тут наверно стоит закрыть доступ, чтобы нельзя было делать с массивом что угодно и добавлять туда что угодно.
> public static $rate; //int
Ой, статические публичные поля. Я думаю, это плохая идея так как они например не позволят индивидуально регулировать зарплату. Ну или например, представь что в разных корпорациях разные базовые ставки. Из-за статических полей у тебя все сотрудники намертво связаны общей ставкой хотя они могут работать в разных компаниях.
Плюс, там легко ошибиться, использовав self:: вместо static::, и трудно обнаружить такую ошибку. Это "позднее статическое связывание" - какая-то очень мутная штука и я бы избегал его исопльзования вообще. Если тебе надо переопределять что-то в наследнике, для этого подойдут обычные поля.
В качестве решения можно:
- сделать ставку обычным полем
- сделать отдельный объект, знающий про зарплаты (тогда впрочем ставку нельзя задать индивидуально)
И еще кое-что. Сейчас у тебя в коде никак не указано что при создании новой профессии надо переопределить для нее ставку. И если программист забудет это сделать, нет никакой проверки. Исправить оба этих недостатка можно с помощью абстрактных методов - попробуй это сделать.
> for($i=0;$i<3;$i++){
> $employee[0][]=new Manager(2,false);
> }
> for($i=0;$i<2;$i++){
> $employee[0][]=new Manager(3,false);
тут идет копипаста, нельзя ли это упростить, положив например информацию о сотрудниках в массив:
$people = [
[$marketing, Manager::class, 3, false],
[$marketing, Engineer::class, 5, false],
...
И потом по массиву насоздавать работников.
Антикризисные меры надо поместить в класс, а не писать код стеной.
> $vector=clone $vector_orig; //возвратили исходные данные
А вот и нет. Департаменты при таком подходе не копируются. а копируется ссылка на тот же самый департамент. надо использовать магический метод __clone.
> foreach($depClass->employees as $empNum=>$empClass){
> if(get_class($empClass)=="Engineer"){
лучше сделать в департаменте метод отбора по каким-то критериям
> if(get_class($empClass)=="Engineer"){
> $engArr["id"]=$empNum;
> $engArr["range"]=$empClass->range;
Это неправильно. У тебя есть объект-работник со всей нужной информацией, а ты зачем-то городишь массивы. Ты может быть ООП не любишь?
> foreach($engineers as $depNum=>$depEmpls){
Ужасные названия переменных. Возьми себе какую-нибудь IDE с автодополнением.
> array_multisort($range, SORT_ASC,$engineers[$depNum]);
Слишком сложно. Лучше выбрать обычный 1-мерный массив инженеров и отсортировать их через usort. Взять 40% можно через array_slice.
> Analyst::$rate = 1100;
Этим ты меняешь ставку во всех копиях компании, а надо только в одной
В общем, код надо рефакторить.
> Поиск пути: http://ideone.com/33ROxP
> function show_shortest_way($paths,$from,$where,$time,$way,$pointNames,$transportName){
time и way всегда одинаковые, зачем их передавать?
> $GLOBALS['ways']
Что еще за использование глобальных переменных? Тут можно было бы использовать return например вместо этого.
То есть вызов функции всегда возвращает 1 путь. Там, где ты в цикле перебираешь соседние точки, ты собираешь массив путей, выбираешь из них наилучший и возвращаешь его:
....
для каждой соседней точки:
- пути[] = проложить путь через точку
лучшийПуть = выбратьЛучшийИз(пути)
вернуть лучшийПуть
И вообще, глобальные переменные это плохо так как они добавляют функции побочные эффекты. Где гарантия что переменная, установленная при первом вызове функции, не повлияет на второй? Что эту переменную не поменяют где-нибудь еще?
Также, плохо что ты используешь многомерные массивы сложной структуры. Трудно из-за этого разбираться в коде.
> if (isset($GLOBALS['ways']))
> return ($GLOBALS['ways']);
А если переменная не существует, что вернет функция? null? Почему она разные типы данных возвращает?
Ну и конечно рекурсивный алгоритм довольно неэффективный. Лучше было бы использовать например алгоритм Дейкстры или что-то еще. В Википедии есть статья по алгоритмам поиска пути.
> Текст по кругу: http://ideone.com/2q1F4N
> $sina=
надо писать кемелкейсом, например $sinA
> rad2deg(sin($alpha));
Это непраивльно. Синус возвращает не угол, а число от -1 до 1. И переводить его бессмысленно.
> for($x=0;$x<80;$x++){
> echo $screen[$y][$x];
Тут можно использовать implode
> На вашем счету n рублей: http://ideone.com/JXvVHa
> if(($number%100)<=20) //если два посл. числа <=20, то сравниваем их
> $n=$number%100;
надо ставить фигурные скобки в ифе
> $n=$number%100;
> $n=$number%10;
Это усложняет код, когда ты одну переменную используешь для разных целей. Надо было просто сделать что если число от 11 до 19 то возвращаем определенный вариант - было бы проще.
> return $text." ";
Это нелогично. Функция inclineWord выбирает подходящую форму слова. С какой стати она должна добавлять к нему пробелы? Это не ее задача, а того кто ее вызвал.
> $n[1]=$number;
Выглядит странно так как начальное значение переменной $n нигде не присваивается.
> if ($number==0) //при нуле сразу пишем результат
> $text.=$spelling[0]." ";
тут логичнее сразу было ставить return
Сам алгоритм в общем верный, но код оформлен неаккуратно.
> ООО Вектор: http://ideone.com/MbL6Pb
> if(!in_array($department,$this->departments)){
Почитай описание in_array в мануале. Ты исопльзуешь неточное сравнение, которое сравнивает не идентичность объектов, а содержимое их полей. Это в общем плохо, в ООП в 99% случаев надо проверять именно идентичность, то есть хранят ли 2 переменных ссылку на один и тот же или разные объекты.
> public function countEmployees(){
Кстати эту функцию можно попробовать записать через array_reduce и анонимную функцию, может выйдет чуть короче. То есть у тебя верно сделано, но если тебе интересно попробовать другой вариант, то можно попробовать.
> $empls
Не стоит так сокращать. Либо $employees, либо $count, либо $c.
> foreach($this->departments as $k=>$v){
> $k=>$v
Это плохой выбор названий так как $k и $v вообще ничего не значат (как и key/value). Тем более, ты еще и не используешь $k, зачем ее вообще писать тогда?
> public function newEmployee(
Названия функций начинаются с глагола, сделайЧтоТО(), например addEmployee
> public function delEmployee(int $i){
Неправильно выбран аргумент. Вот я хочу удалить сотрудника. Как я узнаю его ключ? Это внутренняя деталь реализации, которая спрятана в классе Department, которую я не знаю и не хочу знать. Надо передавать не индекс, а сам объект-сотрудника.
> public $employees=array(); //сотрудники класса Employee
Тут наверно стоит закрыть доступ, чтобы нельзя было делать с массивом что угодно и добавлять туда что угодно.
> public static $rate; //int
Ой, статические публичные поля. Я думаю, это плохая идея так как они например не позволят индивидуально регулировать зарплату. Ну или например, представь что в разных корпорациях разные базовые ставки. Из-за статических полей у тебя все сотрудники намертво связаны общей ставкой хотя они могут работать в разных компаниях.
Плюс, там легко ошибиться, использовав self:: вместо static::, и трудно обнаружить такую ошибку. Это "позднее статическое связывание" - какая-то очень мутная штука и я бы избегал его исопльзования вообще. Если тебе надо переопределять что-то в наследнике, для этого подойдут обычные поля.
В качестве решения можно:
- сделать ставку обычным полем
- сделать отдельный объект, знающий про зарплаты (тогда впрочем ставку нельзя задать индивидуально)
И еще кое-что. Сейчас у тебя в коде никак не указано что при создании новой профессии надо переопределить для нее ставку. И если программист забудет это сделать, нет никакой проверки. Исправить оба этих недостатка можно с помощью абстрактных методов - попробуй это сделать.
> for($i=0;$i<3;$i++){
> $employee[0][]=new Manager(2,false);
> }
> for($i=0;$i<2;$i++){
> $employee[0][]=new Manager(3,false);
тут идет копипаста, нельзя ли это упростить, положив например информацию о сотрудниках в массив:
$people = [
[$marketing, Manager::class, 3, false],
[$marketing, Engineer::class, 5, false],
...
И потом по массиву насоздавать работников.
Антикризисные меры надо поместить в класс, а не писать код стеной.
> $vector=clone $vector_orig; //возвратили исходные данные
А вот и нет. Департаменты при таком подходе не копируются. а копируется ссылка на тот же самый департамент. надо использовать магический метод __clone.
> foreach($depClass->employees as $empNum=>$empClass){
> if(get_class($empClass)=="Engineer"){
лучше сделать в департаменте метод отбора по каким-то критериям
> if(get_class($empClass)=="Engineer"){
> $engArr["id"]=$empNum;
> $engArr["range"]=$empClass->range;
Это неправильно. У тебя есть объект-работник со всей нужной информацией, а ты зачем-то городишь массивы. Ты может быть ООП не любишь?
> foreach($engineers as $depNum=>$depEmpls){
Ужасные названия переменных. Возьми себе какую-нибудь IDE с автодополнением.
> array_multisort($range, SORT_ASC,$engineers[$depNum]);
Слишком сложно. Лучше выбрать обычный 1-мерный массив инженеров и отсортировать их через usort. Взять 40% можно через array_slice.
> Analyst::$rate = 1100;
Этим ты меняешь ставку во всех копиях компании, а надо только в одной
В общем, код надо рефакторить.
1) есть недоделанный урок https://github.com/codedokode/pasta/blob/master/soft/web-server.md
2) есть туториал в php мануале
3) есть задача на студентов
Ты конечно можешь почитать книгу, но потом посмори задачу на студентов и увидишь насколько в той книге все отсталое.
640x360
Спасибо, я опять пропустил этот урок среди прочих паст...
О, понадеемся что удача выразится в том что ты сможешь решить все задачи.
>>33712
надо инструментом разработчика посмотреть какое правило за нее отвечает и переопределить.
>>33728
> Разбивать не explode(), а регулярками? Но в таком случае удобнее воспользоваться parse_url(). Не понял.
Не разбивать, а анализировать. Вроде того:
если URL соответствует шаблону /news/\d+ то ...
если URL соответствует шаблону /news/by-year/\d+ то ...
Обычно конечно это не пишут руками, а используют конфиг где описываются шаблоны путей. А роутер читает этот конфиг и проверяет УРЛ на соответсвие шаблонам.
> Конструкции на пикрелейтед допустимы?
да
> Почему DataMapper, а не, к примеру, наследование модели Student от класса Model, которая соединяется с БД
это называется active record, недостаток в том что в одном классе смешивается 2 задачи: хранение информации о студенте и работа с БД. Ну и как следствие студент не может существовать без БД что нелогично.
>>33885
Вроде из задач там ничего особо не добавилось, добавилась только теория.
О, понадеемся что удача выразится в том что ты сможешь решить все задачи.
>>33712
надо инструментом разработчика посмотреть какое правило за нее отвечает и переопределить.
>>33728
> Разбивать не explode(), а регулярками? Но в таком случае удобнее воспользоваться parse_url(). Не понял.
Не разбивать, а анализировать. Вроде того:
если URL соответствует шаблону /news/\d+ то ...
если URL соответствует шаблону /news/by-year/\d+ то ...
Обычно конечно это не пишут руками, а используют конфиг где описываются шаблоны путей. А роутер читает этот конфиг и проверяет УРЛ на соответсвие шаблонам.
> Конструкции на пикрелейтед допустимы?
да
> Почему DataMapper, а не, к примеру, наследование модели Student от класса Model, которая соединяется с БД
это называется active record, недостаток в том что в одном классе смешивается 2 задачи: хранение информации о студенте и работа с БД. Ну и как следствие студент не может существовать без БД что нелогично.
>>33885
Вроде из задач там ничего особо не добавилось, добавилась только теория.
Тем что быстро превращается в трудночитаемую и трудноподдерживаемую лапшу. Плюс, если ты используешь массивы вместо объектов то в коде трудно разобраться так как неизвестно какие поля есть в этом массиве. В классах методы и поля сгруппированы вместе, в процедурном коде функции и массивы разбросаны как придется. Глядя на класс, понятно что с ним можно сделать, а вот имея массив непонятно какие функции с ним работают.
>>35032
Чтобы быть уверенным, надо использовать абстрактные методы либо интерфейс.
>>35210
копать в сторону фасетного поиска. В сфинксе есть что-то для этого, в Elastic Search есть.
По твоей задаче, еще одна мысль. Хотя у тебя наличие товаров и обнволяется постоянно, может быть можно не учитывать это при построении фильтров. Условно говоря, если у нас вдруг закончились красные футболки, мы можем еще какое-то время показывать в фильтре цвета красный цвет - просто при его выборе будет написано, что таких товаров нет. Это приемлемо если обновлять фильтры в реальном времени слишком сложно.
>>35262
Вообще, мне кажется, если товар закончился, а в фильтре пункт остался - это не так и страшно.
>>35297
Нет, не легко если там сотни тысяч товаров.
Огромнейшее спасибо за такой развёрнутый ответ! Завтра буду исправлять
>Вообще, мне кажется, если товар закончился, а в фильтре пункт остался - это не так и страшно.
Страшно. Фильтр вываливает 1000 брэндов, пользователь начинает их по очереди тыкать, и везде вываливается - продуктов такого брэнда не найдено. Еще хуже, если комбинация из фильтров, например в каждом из 1000 брэндов может не быть одежды для девочек или маек красного цвета. Через 3-4 тычка пользователь уходит. Поэтому и нужны динамически обновляемые фильтры.
Так-с авторы самого сайта с разработчиками платформы не корелируют чуть мения чем никак, тем более, что курс то по SICP, что таки годнота.
https://github.com/someApprentice/Students/blob/master/app/Model/Helper/RegistrationHelper.php
Я решил что не только ПомощникРегистрации должен заниматься ридеректом, и решил вынести функции редиректа в отдельный родительский класс https://github.com/someApprentice/Students/blob/master/app/Model/Helper/Helper.php
Теперь сам класс ПомощникаРегистрации стал совершенно не нужен, и ридеректом теперь занимается вовсе класс аутентификации https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L42
Не портит ли это модель?
https://github.com/someApprentice/Students/blob/master/app/Model/Entity/Student.php#L113
https://github.com/someApprentice/Students/blob/master/app/Model/Entity/RegisterStudentForm.php#L34
https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L36
Не допустил ли я здесь ошибку с передачей авторайзера? Не должен ли он содержаться в классе студента изначально? И правильно ли я назвал эту переменную? Ведь авторизация, в отличие от аутентификации, отвечает за предоставление прав к данным, а не за вспомогательные функции к ней.
https://github.com/someApprentice/Students/blob/master/app/Model/Entity/LoginStudentForm.php#L9
https://github.com/someApprentice/Students/blob/master/app/Model/Entity/Student.php#L20
https://github.com/someApprentice/Students/blob/master/app/Model/Entity/RegisterStudentForm.php#L18
Здесь присутствует копипаста метода. Будет ли лучше вынести этот метод в отдельный класс Entity (https://github.com/someApprentice/Students/blob/master/app/Model/Entity/Entity.php), а переменную содержащую разрешенные поля ($allowed) вынести в отдельное свойство, и затем наследовать этот класс? Не портит ли вынесенное свойство $allowed сущность Студента?
>>38044
>> Логика такова, что мало вероятно что имя и пароль будут совпадать у разных людей.
>Имя в принципе не уникально так как Иванов много. Не надо полагаться на это вот "маловероятно".
Тогда фамилии и пароль тоже могут совпасть. Похоже все таки это плохая идея, но может в рамках условности мы не будем обращать на это внимание? Мне могут помочь в будущем знания о том как сделать авторизацию с помощью разных данных. Ведь за место имени/фамилии мог бы быть обычный логин или номер телефона.
>>38044
>> А разве я не должен спросить у пользователя хочет ли он оставаться залогиненым?
>не знаю. Можешь спрашивать, я не против. Но и в этом случае сессия не нужна.
Сессиями больше не пользуются?
>>38044
>> $_SESSION['id'] = $student->getId();
>> $_SESSION['name'] = $student->getName();
>> $_SESSION['surname'] = $student->getSurname();
>> $_SESSION['token'] = $student->getToken();
>Зачем класть так много данных?
Если бы я пользовался куками, данные, такие как Имя, Фамилия, Возраст и так далее, тоже были бы лишними?
>>38044
>> $loginStudentForm->getError('login', "Incorrect username or password")
>Непонятно почему этот код внутри цикла. Ты хочешь несколько раз ошибку добавить?
Потому что когда все возможные студенты, после неудачной проверки на пароль, закончатся, нужно будет сообщить что соответствий не найдено. А как это сделать вне цикла? Ведь проверка на правильный пароль находится внутри него.
С этим вообще у меня возникает много проблем. Даже после того как я переработал код мне приходиться несколько раз перезадавать значение ошибки.
https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L71
https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L77
https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L63
>>38044
>> Перефразирую вопрос: Что становится с кодом после ридеректа?
>Можно die можно return, так даже аккуратнее мне кажется.
Так это нужно вызывать в ручную? Я собираюсь прямо по среди цикла делать ридерект, даже если я пропишу break или die после этого, ридерект не сломает мой код?
>>38044
>Если ты это поместишь в валидатор то получается чтобы поставить студенту пароль надо вызывать валидатор даже если мы не хотим ничего проверять?
Тогда придется вызывать авторайзер, всё равно придётся что-то вызывать.
>И как в твоем сценарии проверить праивльность логина и пароля не имея формы? Ведь пароль можно и не только в форму логина ввести, и вообще не в форму. А например в консоли или через API. Ты сможешь в этих случаях его проверить?
В консоли или через API обычно передают $_POST? Если да, то смогу - контроллер всегда вызывает валидатор если есть $_POST. Если нет, то мне придется переделывать контроллер чтобы он принимал в аргументы логин и пароль или еще что-то поменять. Ведь контролер же занимается пришедшими данными из вне?
>Потому выгоднее может быть сделать что валидатор лишь проверяет соответсвие данных опредленному формату, а за проверку пароля отвечает не он. Но можешь делать как хочешь, если сделаешь неправильно я все равно увижу ошибку.
Да, теперь я вижу чем этот метод отличается от обычных методов валидации. Просто было не понятно, ведь по сути этот метод отвечает за проверку, да и в контроллере вызывается как и класс авторизации так и класс валидатора.
https://github.com/someApprentice/Students/blob/master/app/Model/Helper/RegistrationHelper.php
Я решил что не только ПомощникРегистрации должен заниматься ридеректом, и решил вынести функции редиректа в отдельный родительский класс https://github.com/someApprentice/Students/blob/master/app/Model/Helper/Helper.php
Теперь сам класс ПомощникаРегистрации стал совершенно не нужен, и ридеректом теперь занимается вовсе класс аутентификации https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L42
Не портит ли это модель?
https://github.com/someApprentice/Students/blob/master/app/Model/Entity/Student.php#L113
https://github.com/someApprentice/Students/blob/master/app/Model/Entity/RegisterStudentForm.php#L34
https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L36
Не допустил ли я здесь ошибку с передачей авторайзера? Не должен ли он содержаться в классе студента изначально? И правильно ли я назвал эту переменную? Ведь авторизация, в отличие от аутентификации, отвечает за предоставление прав к данным, а не за вспомогательные функции к ней.
https://github.com/someApprentice/Students/blob/master/app/Model/Entity/LoginStudentForm.php#L9
https://github.com/someApprentice/Students/blob/master/app/Model/Entity/Student.php#L20
https://github.com/someApprentice/Students/blob/master/app/Model/Entity/RegisterStudentForm.php#L18
Здесь присутствует копипаста метода. Будет ли лучше вынести этот метод в отдельный класс Entity (https://github.com/someApprentice/Students/blob/master/app/Model/Entity/Entity.php), а переменную содержащую разрешенные поля ($allowed) вынести в отдельное свойство, и затем наследовать этот класс? Не портит ли вынесенное свойство $allowed сущность Студента?
>>38044
>> Логика такова, что мало вероятно что имя и пароль будут совпадать у разных людей.
>Имя в принципе не уникально так как Иванов много. Не надо полагаться на это вот "маловероятно".
Тогда фамилии и пароль тоже могут совпасть. Похоже все таки это плохая идея, но может в рамках условности мы не будем обращать на это внимание? Мне могут помочь в будущем знания о том как сделать авторизацию с помощью разных данных. Ведь за место имени/фамилии мог бы быть обычный логин или номер телефона.
>>38044
>> А разве я не должен спросить у пользователя хочет ли он оставаться залогиненым?
>не знаю. Можешь спрашивать, я не против. Но и в этом случае сессия не нужна.
Сессиями больше не пользуются?
>>38044
>> $_SESSION['id'] = $student->getId();
>> $_SESSION['name'] = $student->getName();
>> $_SESSION['surname'] = $student->getSurname();
>> $_SESSION['token'] = $student->getToken();
>Зачем класть так много данных?
Если бы я пользовался куками, данные, такие как Имя, Фамилия, Возраст и так далее, тоже были бы лишними?
>>38044
>> $loginStudentForm->getError('login', "Incorrect username or password")
>Непонятно почему этот код внутри цикла. Ты хочешь несколько раз ошибку добавить?
Потому что когда все возможные студенты, после неудачной проверки на пароль, закончатся, нужно будет сообщить что соответствий не найдено. А как это сделать вне цикла? Ведь проверка на правильный пароль находится внутри него.
С этим вообще у меня возникает много проблем. Даже после того как я переработал код мне приходиться несколько раз перезадавать значение ошибки.
https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L71
https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L77
https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L63
>>38044
>> Перефразирую вопрос: Что становится с кодом после ридеректа?
>Можно die можно return, так даже аккуратнее мне кажется.
Так это нужно вызывать в ручную? Я собираюсь прямо по среди цикла делать ридерект, даже если я пропишу break или die после этого, ридерект не сломает мой код?
>>38044
>Если ты это поместишь в валидатор то получается чтобы поставить студенту пароль надо вызывать валидатор даже если мы не хотим ничего проверять?
Тогда придется вызывать авторайзер, всё равно придётся что-то вызывать.
>И как в твоем сценарии проверить праивльность логина и пароля не имея формы? Ведь пароль можно и не только в форму логина ввести, и вообще не в форму. А например в консоли или через API. Ты сможешь в этих случаях его проверить?
В консоли или через API обычно передают $_POST? Если да, то смогу - контроллер всегда вызывает валидатор если есть $_POST. Если нет, то мне придется переделывать контроллер чтобы он принимал в аргументы логин и пароль или еще что-то поменять. Ведь контролер же занимается пришедшими данными из вне?
>Потому выгоднее может быть сделать что валидатор лишь проверяет соответсвие данных опредленному формату, а за проверку пароля отвечает не он. Но можешь делать как хочешь, если сделаешь неправильно я все равно увижу ошибку.
Да, теперь я вижу чем этот метод отличается от обычных методов валидации. Просто было не понятно, ведь по сути этот метод отвечает за проверку, да и в контроллере вызывается как и класс авторизации так и класс валидатора.
Или если без поиска getAll()->orderBy($column)->order($order)->get()
Маппер же не может делать такие запросы? А формировать голый SQL в контроллере не выход.
$mapper->build([
'search' => $search,
'orderBy' => $orderBy,
'order' => $order,
]);
1. User. (пользователь и действия)
2. Mail (Почта)
3. Trigger (событие в ответ на действие User, использует Mail для отправки) тут не совсем уверен, может это больше к User относится
Фабрика для создания писем, письмо, юзер принимается на фабрику и там происходит магия. Отправление в контроллерах
Не плоди лишних, заебешься кодить
У меня есть одна ссылка, при переходе на которую мне открывается массив. Как его записать в переменную?
Я тот кун который насчет ВК АПИ пару дней назад спрашивал.
Там можно сгенерировать ссылку и получить по ней некоторые данные.
https://api.vk.com/method/groups.getById?group_id=22751485&fields=members_count
Вот например я хочу достать отсюда количество подписчиков паблика двача.
Тебе нужно декодировать json-ответ. Парсить ничего не нужно, есть готовые функции.
Вот пример: http://www.tutorialspoint.com/json/json_php_example.htm
Кто с ним работает, не знаете, сейчас обращения возможны только через приложение?
А то пытаюсь по ссылке, которую они в документации дают, обращаться.
Например то же.
https://api.vk.com/method/groups.getById?group_id=22751485&fields=members_count
В браузере вижу результат, но если попытаюсь использовать file_get_contents то пишет что failed to open stream: Connection refused in...
Или я рукожоп где-то ошибся в коде?
Насколько я знаю file_get_contents не работает с https. Тебе нужен CURL.
>Кто с ним работает, не знаете, сейчас обращения возможны только через приложение?
Есть список открытых и закрытых методов (закрытые - те которым нужен токен авторизации чтобы работать). Открытые методы это те которым такой токен не нужен, но это только основные, вроде запроса для получения основной информации о пользователе. Токен можно получить через OAuth, полный список методов можешь найти тут http://vk.com/dev/methods
Там же написано какие из них закрыте, какие открытые.
Запущен PHP в интерактивном режиме из консоли. Введена команда. Внизу нужный тебе результат - количество подписчиков группы.
Братишки-php'шки. Помогите пожалуйста. Вот вопросик на stackoverflow, там не много кодика.
http://ru.stackoverflow.com/questions/523122
Вот вам няша
Вроде было много вопросов, но уже ничего не помню, сплю до воскресенья.
https://github.com/nsdvw/TestHub
Один геморрой с этими миграциями. Мне всего лишь нужно изменить некоторые первичные ключи, с integer на bigint.
Но ведь это потянет за собой изменение всех внешних ключей. Значит я должен удалить внешний ключ, изменить первичный, после чего опять повесить внешний.
Проблема в том, что сразу не дал нормальные имена внешним ключам, а оно (doctrine migrations) сгенерировало какой-то хеш вместо имени, так еще и отказывается
принимать это имя, говорит нет такого ключа и все.
Я же тебе говорил уже, билдер это абстракция, если уж мы используем доктрину, то везде нужно помнить, что базу может понадобиться сменить.
Диалекты ведь отличаются, я не могу захардкодить в классе миграции sql для конкретного диалекта.
>>40754
>после замены конфига он мне сгенеирировал нужный SQL для постгрес, который тот с радостью принял
Так то ты генерировал наверное через doctrine:scheme:update.
Да, эта команда создает налету sql под текущий драйвер, указанный в конфиге.
Мы ведь уже говорим про миграции, файлы которые мне нужно хранить под системой контроля версий, они не должны быть завязаны на конкретную платформу.
Рано куда-то разворачивать, там же почти ничего нет. Я выкладываю частями, тебе же вроде удобнее проверять отдельные патчи чем сразу весь проект.
>>43223
Попробовал удалить руками через консоль, говорит
Cannot drop index 'IDX_D87F7E0CA76ED395': needed in a foreign key constraint
Это на другой стороне тоже нужно что-то удалить?
Короче я близок к тому, чтобы написать миграцию, которая дропает все таблицы, а потом еще одну, которая пересоздает нормальную структуру.
Но это конечно зашквар, нужно разобраться как правильно обновлять первичный и внешний ключ.
> Мы ведь уже говорим про миграции, файлы которые мне нужно хранить под системой контроля версий, они не должны быть завязаны на конкретную платформу.
На практике это малореалистично. Если ты хочешь сделать проект независимым от БД то тебе надо пользоваться минимальным подмножеством SQL которое поддерживается везде. Соответственно использовать например преимущества postgres не получится.
> Cannot drop index 'IDX_D87F7E0CA76ED395': needed in a foreign key constraint
Сначала надо удалить внешний ключ
Я не люблю билдеры так как сам SQL лаконичнее и выразительнее и не требует горы кавычек, скобок и стрелок.
Более того, в некоторых Бд нет LIMIT. Например в Oracle. Там вместо этого используется отбор по ROW_NUMBER() < ?.
Даже строковые функции или функции работы с датой разные в разных БД:
https://en.wikibooks.org/wiki/SQL_Dialects_Reference/Functions_and_expressions/String_functions
https://en.wikibooks.org/wiki/SQL_Dialects_Reference/Functions_and_expressions/Date_and_time_functions
Ты конечно можешь попробовать писать унивресальный код, но боюсь даже только для поддержки mysql + postgres уже уйдет много труда. И при этом ты не сможешь пользоваться разными оптимизациями, которые предлагают эти БД.
>уйдет много труда
Убедил.
Я думал что билдер это такая штука, которой пишешь ->сделатьЧтоТо(), и оно само разрулит для всех возможных ситуаций. Оказывается создатели доктрины и симфони не совсем боги и не все продумали. А жаль.
Тогда буду писать миграции на sql. Тем более что есть инструмент doctrine:migrations:diff, который автоматически их генерирует из аннотаций, может чуть-чуть руками только подправить.
Тогда вопрос, что делать с теми миграциями что уже есть, где билдер? А ладно, пусть остаются, вроде никому не мешают.
Ну хорошо, разберусь как-нибудь, хотя геморрой еще тот. Чтобы написать элементарное изменение уровня "сменить тип первичного ключа" нужны такие пляски с бубном.
Хотя если бы я знал как это делается, ушло бы 3 минуты.
В общем, первые впечатления от симфони: долго запрягаешь - быстро едешь. То есть нужно потратить уйму времени, чтобы разобраться в тонкостях, но уж если разберешься, получаешь йоба-инструмент, который делает разработку супер-быстрой и гибкой.
Например вчера 2 часа долбился, как сделать кастомный шаблон вместо того, что генерируют стандартные функции для форм. Такой код
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
генерирует label перед input, а мне нужно, чтобы input был вложен.
Сидел вардампил объект view, перечитал кучу ответов на stackoverflow, научился переопределять стандартные "темы" симфони.
Потом оказалось, что начиная с 2.8 (или 2.6?) в комплекте идет тема под бутстрап.
Так что моя проблема, над которой долбился 2 часа, решалась 2 строчками в конфиге
twig:
form_themes:
- 'bootstrap_3_layout.html.twig'
То есть долго разбираться, но если разберешься и запомнишь решение, значительно облегчает и ускоряет разработку.
Ну ладно, не буду отвлекать, в принципе я уже дошел до той стадии, когда могу найти сам все ответы в доках/гугле/исходном коде.
Проверяй как будет время, я пока отдыхаю, подумаю как сделать ту странную форму создания новых тестов/вопросов/вариантов/тегов, завтра попробую успеть запилить.
>уйдет много труда
Убедил.
Я думал что билдер это такая штука, которой пишешь ->сделатьЧтоТо(), и оно само разрулит для всех возможных ситуаций. Оказывается создатели доктрины и симфони не совсем боги и не все продумали. А жаль.
Тогда буду писать миграции на sql. Тем более что есть инструмент doctrine:migrations:diff, который автоматически их генерирует из аннотаций, может чуть-чуть руками только подправить.
Тогда вопрос, что делать с теми миграциями что уже есть, где билдер? А ладно, пусть остаются, вроде никому не мешают.
Ну хорошо, разберусь как-нибудь, хотя геморрой еще тот. Чтобы написать элементарное изменение уровня "сменить тип первичного ключа" нужны такие пляски с бубном.
Хотя если бы я знал как это делается, ушло бы 3 минуты.
В общем, первые впечатления от симфони: долго запрягаешь - быстро едешь. То есть нужно потратить уйму времени, чтобы разобраться в тонкостях, но уж если разберешься, получаешь йоба-инструмент, который делает разработку супер-быстрой и гибкой.
Например вчера 2 часа долбился, как сделать кастомный шаблон вместо того, что генерируют стандартные функции для форм. Такой код
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
генерирует label перед input, а мне нужно, чтобы input был вложен.
Сидел вардампил объект view, перечитал кучу ответов на stackoverflow, научился переопределять стандартные "темы" симфони.
Потом оказалось, что начиная с 2.8 (или 2.6?) в комплекте идет тема под бутстрап.
Так что моя проблема, над которой долбился 2 часа, решалась 2 строчками в конфиге
twig:
form_themes:
- 'bootstrap_3_layout.html.twig'
То есть долго разбираться, но если разберешься и запомнишь решение, значительно облегчает и ускоряет разработку.
Ну ладно, не буду отвлекать, в принципе я уже дошел до той стадии, когда могу найти сам все ответы в доках/гугле/исходном коде.
Проверяй как будет время, я пока отдыхаю, подумаю как сделать ту странную форму создания новых тестов/вопросов/вариантов/тегов, завтра попробую успеть запилить.
http://archive-ipq-co.narod.ru/l1/strings.html
http://codepad.org/5XEkSKHI
Что там доделывать?
оп его знает
Может дополнить список кодов для замены, чтобы заменяло все буквы
http://lurkmore.to/_/2368#mws_PnElI97
может сделать обратный перевод.
Да, сделай обратный перевод. Разумеется, не трогая массив и не добавляя второго.
854x480
Пишет или "Отказано в доступе", или "Не является внутренней или внешней командой, исполняемой программой или установленным файлом".
У меня вообще в папке с MySQL нет никакого bin - что я сделал не так? Как просто взять и запустить в Windows 8 MySQL?
>shell> mysql -h host -u user -p
Вообще не предлагает ввести пароль, а сразу пишет "Отказано в доступе".
shell> mysql -h host -u user -p хххх - то же самое.
ЧТО Я ДЕЛАЮ НЕ ТАК??7
Нет, ничего не выходит.
Я вообще правильно набираю команду?
От администратора делаю - вот этот результат.
А не от администратора было "Отказано в доступе".
Ну вот путь до mysql.exe - на скриншоте.
Пытаюсь прописать его - нет, бесполезно сие занятие.
Я в тупике, короче.
Как всё было понятно с селекторами и прочим в книжке, а запустить сам Мускул не могу, вот лалка, сам себя затроллел.
Короче, просто нажимаешь Shift на папке bin, выбираешь "Открыть окно команд", там уже будет прописан верный путь до содержимого папки. Дописываешь "mysql.exe -u root -p", а дальше вводишь пароль.
Вроде получилось, но не знаю, как пойдёт дальше.
Буду держать всех в курсе и дальше, это очень важно.
shell> писать не надо, это подсказка которая выводится
Писать надо начиная с mysql
Также, в ОП посте есть гайд по командной строке - изучи его внимательно.
Возможно что у тебя клиент mysql будет не в одной из папок в PATH. В этом случае надо будет писать полный путь, например c:\mysql\bin\mysql.exe либо добавить нужную папку в PATH.
>>43834
Если ты пишешь полный путь то его надо начинать с буквы диска, то есть "c:\Program Files"
У тебя относительный путь, который отсчитывается от текущей папки (она написана в приглашении)
>В этом случае надо будет писать полный путь, например c:\mysql\bin\mysql.exe
>Если ты пишешь полный путь то его надо начинать с буквы диска, то есть "c:\Program Files"
А как его можно поменять, если по дефолту у меня там было с WINDOWS\system32 и подобное?
Ну вот щелчок ПКМ по папке bin и "Открыть окно команд" помогли запустить командную строку с уже прописанным путём, а дальше всё сработало.
Сейчас играюсь с селекторами и прочим, создаю индексы, красота.
Спасибо за помощь!
> А как его можно поменять,
Почитай урок по командной строке - есть команда cd. Также, можно создаять ярлык указывающий на cmd.exe где указана в качестве текущей нужная папка. Но удобнее добавить папку с mysql в PATH.
Спасибо!
Мужчины, а сильно ли ударит по производительности, если создавать новые таблицы - тысячами в день а потом когда они не нужны - удалять их ?
>Формы регистрации и редактирования очень похожи. Не стоит множить сущности, стоит использовать для них общий код.
Как не множить сущности, если в контроллере должны быть разные методы работы с БД?
Насколько я понял, нужно для форм регистрации и редактирования профиля общий код использовать.
В конфиг стоит класть то, что может менять пользователь. тип БД (mysql) он вряд ли может поменять, его можно прописать и где-то в коде (тут вроде у же прописано: https://github.com/fidnex/students/blob/master/app/init.php - знаичт надо просто убрать это из конфига)
https://github.com/fidnex/students/blob/master/dump.sql
Тут стоит добавить уникальный ключ по значениям которые не должны повторяться (например email)
https://github.com/fidnex/students/blob/master/app/Controller/Main.php#L45
> $this->view->render('header');
> $this->view->render('main/index');
> $this->view->render('footer');
Тут лучше подключать только один шаблон, а хедер и футер подключать из него
https://github.com/fidnex/students/blob/master/app/Controller/Form.php#L35
Вместо длинной стены кода лучше наверно было сделать отдельный класс для этой формы и там все правила задать.
https://github.com/fidnex/students/blob/master/app/Lib/View.php#L9
> private function _output($str) {
Название не отрадает смысл функции, и зачем подчеркивание? Не надо его ставить, у нас не Питон.
https://github.com/fidnex/students/blob/master/app/Lib/Form.php#L5
> private $_currentItem = null;
не надо ставить подчеркивание. private и так показывает что оно приватное
В форме наверно лучше было бы разбить обработку формы на 2 этапа: подготовку формы и обработку и проверку данных POST. Тогда мы могли бы сделать отдельный класс ФормаРедактированияСтудента с уже настроенными правилами.
Также, как я понимаю, у тебя функции валидации могут проверять только одно поле.А иногда надо проверять сочетание нескольких полей.
Также, у тебя валидация завязана на форму. А что если мы просто хотим проверить модель студента на правильность? Наверно лучше было бы делать валидацию для модели.
И еще мне не нравится что форма сама обращается к POST, было бы лучше если бы контроллер явно передавал этот массив например в submit.
То есть хорошо что ты попытался сделать построитель форм, но на мой взгляд, тут несколько проблем с архитектурой.
> public function post($field) {
> public function val($method, $arg = null) {
имя функции должно начинаться с глагола
https://github.com/fidnex/students/blob/master/app/Lib/Form.php#L61
Тут нужна проверка что в модели есть такое поле - для защиты от опечаток
https://github.com/fidnex/students/blob/master/app/Lib/Form.php#L57
Плохо что функция возвращает либо массив либо объект. Лучше было сделать 2 разных функции.
https://github.com/fidnex/students/blob/master/app/Lib/Validation.php#L5
Зачем тут магический метод? PHP и так выдаст ошибку при обращении к несуществующему методу.
https://github.com/fidnex/students/blob/master/app/Lib/Form/ValStudent.php#L9
> public function __construct($helper) {
Нужен тайп хинт
> https://github.com/fidnex/students/blob/master/app/Lib/Form/ValStudent.php#L13
Лучше сделать название функции вроде validateMaxLength()
> if (strlen($data) > $length) {
Это считает не число символов, а число байт. Почитай урок https://gist.github.com/codedokode/ff99e357e9860ea169b8
https://github.com/fidnex/students/blob/master/app/Lib/Form/ValStudent.php#L33
> return 'Значение содержит недопустимые символы.';
Тут стоит предусмотреть возможность указать какие именно символы допустимые (в идеале еще и писать какой именно недопустим)
https://github.com/fidnex/students/blob/master/app/Helper/AuthHelper.php#L31
> $expire = intval(time() + 9460800000);
Это число лучше писать как 3600*24*356 - намного понятнее
Также, в AuthHelper логично бы добавить и метод разлогинивания.
https://github.com/fidnex/students/blob/master/app/Helper/TableHelper.php#L5
> public $page, $sortColumn, $sortType, $search;
Не стоит так оьбъявлять поля, лучше каждое по отдельности
https://github.com/fidnex/students/blob/master/app/Helper/TableHelper.php#L31
Не уверен что здесь стоит ставить htmlspecialchars так как это лучше делать в шаблоне и непонятно что делать если нам нужна неэкранированная ссылка.
https://github.com/fidnex/students/blob/master/app/Mapper/StudentTDG.php#L99
Тут надо проверять значения подставляемых переменных $sortColumn $sortTypeпо белому списку, а то получается SQL инъекция
https://github.com/fidnex/students/blob/master/app/View/footer.php#L2
Из-за того что ты ставишь CSS в конец, возможно такое, что при медленной загрузке браузер сначала отобразит страницу без стилей, а только потом применит стили. Я знаю, что некоторые советуют ставить статические файлы в конец, но на мой взгляд тут есть плюсы и минусы и стили в конец точно ставить не стоит.
И вообще мне не нравятся такие "советчики". Если им так хочется оптимизации, почему бы не сделать по-другому? Например вырезать из гигантского бутстрапа все неиспользуемые правила и остаивть только нужные? Это конечно сложнее чем переставить файл в конец, зато как оптимизирует.
В общем, в случае с оптимизациями, не стоит слепо следовать советам, а стоит разобраться как это работает, какие есть преимущества и недостатки.
https://github.com/fidnex/students/blob/master/app/View/form/index.php#L2
> <?php if(isset($this->errors['name'])) echo 'has-error';?>
не применяй echo в шаблонах. Тут лучше использовать <?= isset(...) ? ' has-error' : '' ?>
> ->val('regexp', '/[a-zа-яё0-9\-\'\"]/i')
Ты не используешь флаг u в регулярке, она будет работать некорретно с кириллицей
> ->val('regexp', '/.@./')
Тут должны стоять не зведочки, а плюсы
https://github.com/fidnex/students/blob/master/app/View/main/index.php#L13
Тут наверно не требуется крестик
https://github.com/fidnex/students/blob/master/app/Config.php
Я бы вынес загрузку конфига из файла в отдельный метод, а то у тебя нельзя создать конфиг без файла. Было бы чуть универсальнее.
https://github.com/fidnex/students/blob/master/app/Config.php#L25
В сообщении об ошибке стоит указывать название параметра.
Так, в общем, неплохо.
В конфиг стоит класть то, что может менять пользователь. тип БД (mysql) он вряд ли может поменять, его можно прописать и где-то в коде (тут вроде у же прописано: https://github.com/fidnex/students/blob/master/app/init.php - знаичт надо просто убрать это из конфига)
https://github.com/fidnex/students/blob/master/dump.sql
Тут стоит добавить уникальный ключ по значениям которые не должны повторяться (например email)
https://github.com/fidnex/students/blob/master/app/Controller/Main.php#L45
> $this->view->render('header');
> $this->view->render('main/index');
> $this->view->render('footer');
Тут лучше подключать только один шаблон, а хедер и футер подключать из него
https://github.com/fidnex/students/blob/master/app/Controller/Form.php#L35
Вместо длинной стены кода лучше наверно было сделать отдельный класс для этой формы и там все правила задать.
https://github.com/fidnex/students/blob/master/app/Lib/View.php#L9
> private function _output($str) {
Название не отрадает смысл функции, и зачем подчеркивание? Не надо его ставить, у нас не Питон.
https://github.com/fidnex/students/blob/master/app/Lib/Form.php#L5
> private $_currentItem = null;
не надо ставить подчеркивание. private и так показывает что оно приватное
В форме наверно лучше было бы разбить обработку формы на 2 этапа: подготовку формы и обработку и проверку данных POST. Тогда мы могли бы сделать отдельный класс ФормаРедактированияСтудента с уже настроенными правилами.
Также, как я понимаю, у тебя функции валидации могут проверять только одно поле.А иногда надо проверять сочетание нескольких полей.
Также, у тебя валидация завязана на форму. А что если мы просто хотим проверить модель студента на правильность? Наверно лучше было бы делать валидацию для модели.
И еще мне не нравится что форма сама обращается к POST, было бы лучше если бы контроллер явно передавал этот массив например в submit.
То есть хорошо что ты попытался сделать построитель форм, но на мой взгляд, тут несколько проблем с архитектурой.
> public function post($field) {
> public function val($method, $arg = null) {
имя функции должно начинаться с глагола
https://github.com/fidnex/students/blob/master/app/Lib/Form.php#L61
Тут нужна проверка что в модели есть такое поле - для защиты от опечаток
https://github.com/fidnex/students/blob/master/app/Lib/Form.php#L57
Плохо что функция возвращает либо массив либо объект. Лучше было сделать 2 разных функции.
https://github.com/fidnex/students/blob/master/app/Lib/Validation.php#L5
Зачем тут магический метод? PHP и так выдаст ошибку при обращении к несуществующему методу.
https://github.com/fidnex/students/blob/master/app/Lib/Form/ValStudent.php#L9
> public function __construct($helper) {
Нужен тайп хинт
> https://github.com/fidnex/students/blob/master/app/Lib/Form/ValStudent.php#L13
Лучше сделать название функции вроде validateMaxLength()
> if (strlen($data) > $length) {
Это считает не число символов, а число байт. Почитай урок https://gist.github.com/codedokode/ff99e357e9860ea169b8
https://github.com/fidnex/students/blob/master/app/Lib/Form/ValStudent.php#L33
> return 'Значение содержит недопустимые символы.';
Тут стоит предусмотреть возможность указать какие именно символы допустимые (в идеале еще и писать какой именно недопустим)
https://github.com/fidnex/students/blob/master/app/Helper/AuthHelper.php#L31
> $expire = intval(time() + 9460800000);
Это число лучше писать как 3600*24*356 - намного понятнее
Также, в AuthHelper логично бы добавить и метод разлогинивания.
https://github.com/fidnex/students/blob/master/app/Helper/TableHelper.php#L5
> public $page, $sortColumn, $sortType, $search;
Не стоит так оьбъявлять поля, лучше каждое по отдельности
https://github.com/fidnex/students/blob/master/app/Helper/TableHelper.php#L31
Не уверен что здесь стоит ставить htmlspecialchars так как это лучше делать в шаблоне и непонятно что делать если нам нужна неэкранированная ссылка.
https://github.com/fidnex/students/blob/master/app/Mapper/StudentTDG.php#L99
Тут надо проверять значения подставляемых переменных $sortColumn $sortTypeпо белому списку, а то получается SQL инъекция
https://github.com/fidnex/students/blob/master/app/View/footer.php#L2
Из-за того что ты ставишь CSS в конец, возможно такое, что при медленной загрузке браузер сначала отобразит страницу без стилей, а только потом применит стили. Я знаю, что некоторые советуют ставить статические файлы в конец, но на мой взгляд тут есть плюсы и минусы и стили в конец точно ставить не стоит.
И вообще мне не нравятся такие "советчики". Если им так хочется оптимизации, почему бы не сделать по-другому? Например вырезать из гигантского бутстрапа все неиспользуемые правила и остаивть только нужные? Это конечно сложнее чем переставить файл в конец, зато как оптимизирует.
В общем, в случае с оптимизациями, не стоит слепо следовать советам, а стоит разобраться как это работает, какие есть преимущества и недостатки.
https://github.com/fidnex/students/blob/master/app/View/form/index.php#L2
> <?php if(isset($this->errors['name'])) echo 'has-error';?>
не применяй echo в шаблонах. Тут лучше использовать <?= isset(...) ? ' has-error' : '' ?>
> ->val('regexp', '/[a-zа-яё0-9\-\'\"]/i')
Ты не используешь флаг u в регулярке, она будет работать некорретно с кириллицей
> ->val('regexp', '/.@./')
Тут должны стоять не зведочки, а плюсы
https://github.com/fidnex/students/blob/master/app/View/main/index.php#L13
Тут наверно не требуется крестик
https://github.com/fidnex/students/blob/master/app/Config.php
Я бы вынес загрузку конфига из файла в отдельный метод, а то у тебя нельзя создать конфиг без файла. Было бы чуть универсальнее.
https://github.com/fidnex/students/blob/master/app/Config.php#L25
В сообщении об ошибке стоит указывать название параметра.
Так, в общем, неплохо.
Спасибо.
>Тут лучше подключать только один шаблон, а хедер и футер подключать из него
А если некий шаблон выводят разные контроллеры и им нужен свой хедер или вообще его отсутствие? Админка, хедер для печати
>В форме наверно лучше было бы разбить обработку формы на 2 этапа: подготовку формы и обработку и проверку данных POST. Тогда мы могли бы сделать отдельный класс ФормаРедактированияСтудента с уже настроенными правилами.
А если добавлю метод валидации другим классом? Отдам ему данные с формы и получу от него ошибки. На пике накидал пример.
>Тут надо проверять значения подставляемых переменных $sortColumn $sortTypeпо белому списку, а то получается SQL инъекция
Они проверяются в TableHelper и только после этого передаются в модель. Но да, логичней там держать.
Сумма прописью - http://ideone.com/HphHsa
Калькулятор - http://ideone.com/XjzcED
http://ideone.com/nO4ZHk - если в начале текста, то не захватывает ошибки.
В регулярке точка и количество символов не вполне годится для захвата текста вокруг ошибки.
Ну вот например если взять эту борду. Каждый тредик, это по факту новая таблица ? А в телеграмме ? Каждый чат рум, это тоже новая таблица ?
>Можно возвращаться к предыдущим вопросам. Можно не отвечать на вопрос.
По поводу "возвращаться". Представь, что в тесте over 100 вопросов, и я пропустил скажем четвертый. На 99-ом спохватился, а не вернуться ли мне назад?
Ссылка на предыдущий наверное уже не катит.
Предлагаю сделать отдельный экшен, типа "предрезультат" (придумай имя, у меня не хватает словарного запаса).
После того как пользователь ответит (или пропустит) последний по списку вопрос, его отредиректит на этот экшен, где будет сообщение уровня
Вы ответили не на все вопросы (пропущено {{ attempt.skippedCount }}).
У вас еще есть время ответить на них (осталось {{ attempt.timeLeft }}).
Ответить на пропущенные вопросы или завершить тест?
Кнопки "вернуться" и "завершить".
Тогда куда должна вести ссылка "вернуться"? На тот же url что и обычные вопросы? (/test/{testId}/question)
Но как тогда узнать, что это "повторное" прохождение? Ведь у "первого прохождения" другая логика выдергивания вопросов из базы (ищет в таблице ответов последний ответ к данному тесту и выдает следующий).
Передать гет-переменную и в зависимости от нее лепить ветку условий? Там и так уже бардак, не хочется.
Может сделать отдельный экшен /test/{testId}/review?
Нет. Ты плохо понимаешь идеи SQL. Треды - это сущности одного типа и они все хранятся в одной таблице, каждый под своим идентификатром. Таблицы обычно создает разработчик явно, сами они не создаются.
Вообще, "вернуться" имелось в виду именно вернуться назад на 1 вопрос. Но есть и другие варианты навигации - например один анон делал список номеров - как тут https://ege.yandex.ru/mathematics/1/ при клике на "1 из 20".
> Но как тогда узнать, что это "повторное" прохождение? Ведь у "первого прохождения" другая логика выдергивания вопросов из базы (ищет в таблице ответов последний ответ к данному тесту и выдает следующий).
А зачем узнавать? У тебя где-то (в БД или во временном хранилище) хранится список данных ответов. Пользователь переходит например на 5-й вопрос. Ты смотришь - если он на него отвечал, то помечаешь выбранный им ранее вариант.
Если ты используешь GET то можно сделать для перехода URL вида /test/:tid/question/:qid, если форму с POST то можно передавать номер вопроса для перехода скрытым полем (а если он не передан, переходить на следующий), также есть еще вариант вообще загрузить все вопросы на клиент и переключать яваскриптом (то есть у нас как бы форма из 100 вопросов из которых виден только один).
Вообще, ссылка тут не очень годится. Она ведь не сохранит выбранный ранее ответ (если только ты как-то не используешь JS). Если тебе нужен вариант без JS то надо использовать форму, чтобы на сервер передался ответ на текущий вопрос. Например, при клике "назад" либо заполняется скрытое поле либо сервер определяет факт перехода по имени кнопки.
если используешь JS то конечно возможны варианты, сделать большую форму и показывать по частям либо хранить введенные ответы в переменной.
> (ищет в таблице ответов последний ответ к данному тесту и выдает следующий).
Можно просто передавать на сервер номер текущего вопроса, на который дается ответ. И уже по этому номеру определять следующий. Так по моему надежнее. И как я уже сказал, можно на сервере определять какая кнопка нажата по имени кнопки либо яваскриптом перед отправкой формы заполнять скрытое поле указывающее направление перехода.
>Не разбивать, а анализировать. Вроде того:
>если URL соответствует шаблону /news/\d+ то ...
>если URL соответствует шаблону /news/by-year/\d+ то ...
ОП, попробовал реализовать примитивный роутер на регулярках. Ньюфаг ньюфагом, буду очень признателен за ревью: https://github.com/applejacky/tmp
- в роутере жестко прописано что URL надо брать из SERVER. Это нелогично. Нарушается принцип разделения обяхзанностей - это не задача роутера определять откуда брать URL
- чтобы метод getController() что-нибудь вернул, надо сначала вызвать другой метод. Как об этом догадаться? По моему это намеренное запутывание пользователя. должно быть проще: на вход даем URL (или Request), на выходе получаем нужные параметры в массиве или объекте.
- не надо использовать die, надо использовать исключения. die не пишется в логи и ты не узнаешь об ошибке, хуже того, ты пользователю показываешь непонятные и ненужные ему надписи
> $controllerAction = explode(' ', $routeMapValue)[1];
Что такое 1? Что за странный формат хранения информации об URL? Лучше наверно хранить их в распакованном виде, например в виде объектов или массива. Так, вообще, понять формат сложно, глядя на код.
> [a-zA-Z0-9]
Очень ограниченный набор символов
> :num
Других плейсхолдеров нет?
> $controllerAction = explode(' ', $methodControllerAction)[1];
Что если второго элемента нет? И кстати, формат записи по моему неудобный. Удобнее было бы так:
GET /url Controller@ACtion
GET+POST /url Controller@Action
или как-то так.
Ну и наконец, советую посмотреть (в том числе на код) симфониевского роутера. Он мощный, имеет много разных возможностей, и содеждит разные оптимизации. Например, он способен компилировать набор правил в класс вида
if (preg_match(...)) { ... }
if (preg_match(...)) { ... }
то есть без массивов и циклов. Также, он там пытается группировать URL по префиксу, чтобы, если URL не начинается с определенного слова, не проверять соотв. маршруты.
http://symfony.com/doc/current/components/routing/introduction.html
https://github.com/symfony/routing
- в роутере жестко прописано что URL надо брать из SERVER. Это нелогично. Нарушается принцип разделения обяхзанностей - это не задача роутера определять откуда брать URL
- чтобы метод getController() что-нибудь вернул, надо сначала вызвать другой метод. Как об этом догадаться? По моему это намеренное запутывание пользователя. должно быть проще: на вход даем URL (или Request), на выходе получаем нужные параметры в массиве или объекте.
- не надо использовать die, надо использовать исключения. die не пишется в логи и ты не узнаешь об ошибке, хуже того, ты пользователю показываешь непонятные и ненужные ему надписи
> $controllerAction = explode(' ', $routeMapValue)[1];
Что такое 1? Что за странный формат хранения информации об URL? Лучше наверно хранить их в распакованном виде, например в виде объектов или массива. Так, вообще, понять формат сложно, глядя на код.
> [a-zA-Z0-9]
Очень ограниченный набор символов
> :num
Других плейсхолдеров нет?
> $controllerAction = explode(' ', $methodControllerAction)[1];
Что если второго элемента нет? И кстати, формат записи по моему неудобный. Удобнее было бы так:
GET /url Controller@ACtion
GET+POST /url Controller@Action
или как-то так.
Ну и наконец, советую посмотреть (в том числе на код) симфониевского роутера. Он мощный, имеет много разных возможностей, и содеждит разные оптимизации. Например, он способен компилировать набор правил в класс вида
if (preg_match(...)) { ... }
if (preg_match(...)) { ... }
то есть без массивов и циклов. Также, он там пытается группировать URL по префиксу, чтобы, если URL не начинается с определенного слова, не проверять соотв. маршруты.
http://symfony.com/doc/current/components/routing/introduction.html
https://github.com/symfony/routing
Зачем у тебя там call_user_func_array, если число аргументов известно. Он не для этого - читай Зандстру в шапке.
Пустой конструктор - не нужен.
setControllerAction - у тебя от порядка вызова методов зависит использование класса. Что, если клиент забудет вызвать этот метод? Роутер не сработает, так, как ждет клиент. Класс сам себя должен настраивать через конструктор, а не завивесть от порядка вызываемых методов.
die в методах класса - плохо, exception юзай
Docblocks - почему не на всех функциях и не соответствуют содержимому? @throws RouterException нет, там у тебя die, @return на возвращающих значение функциях не указан.
parseRequest() - работает напрямую с $_REQUEST, это не задача роутера. Передавай запрос через dependency injection, лучше даже отдельным классом, который реализует публичный интерфейс.
private $requestPath = ''; - зачем ты инициируешь переменные в пустую строку? Чтобы код лишний раз засорить?
Двойной вызов функции:
if ($this->getRouteMapValues()) {
$routeMapValues = $this->getRouteMapValues();
Можно одним заменить, зачем 2 раза один и тот же код выполнять?
setControllerAction - сбивающее с толку имя метода, неясно сразу что делает. Может ты setControllerAndAction имел в виду? Так и пиши тогда.
Запись - строка в таблице posts, треды - строка в таблице threads. Дальше делается соответствие между этими таблицами по primary key, чтобы можно было найти к какому треду какой пост относится. Чтобы вывести весь тред - ищешь в таблице threads этот тред, выводишь что есть, ищешь посты в таблице posts по ключу треда из найденного в threads, выводишь.
То есть все треды и посты находятся всего в двух таблицах ? Но ведь тогда эти таблицы просто огроменные.
А как тогда хранятся беседы в телеграмме ? Это же огромное к-во связей нужно проводить. Так и запутаться просто. А вот если отдельная таблица - это отдельный тред/беседа, то как-то и разобраться в них проще. Или я опять ошибаюсь ?
Ну типу, я мыслю таким образом, что яблоки в ящике с надписью "яблоки", а груши в ящике "груши" - лучше, чем один огромный ящик с надписью"фрукты".
Базы данных для этого и есть, чтобы огроменные таблицы держать. Таблицы запросто могут быть по нескольку гигабайт и состоять из нескольких миллионов строк. Каждая строка и будет постом. Чтобы быстро искать по такому количеству строк, в таблице существуют индексы. Это просто сортированные значения ключей. По сортированному можно быстро искать, так же как ты можешь легко искать записи в записной книжке по названию буквы.
>А вот если отдельная таблица - это отдельный тред/беседа, то как-то и разобраться в них проще.
Так тоже можно делать, но у тебя будут сложнее запросы и скорость меньше. Например ты захотел добавить в каждый пост в каждом треде что-то, или провести поиск по постам во всех тредах. Если у тебя гора таблиц, тебе придется обходить по очереди каждую, а это отдельный запрос на каждую таблицу, в то время как если у тебя все посты в одной таблице, ты просто проходишься быстро по всем строкам и смотришь/меняешь значения в полях.
Спасибо. Вот теперь понял. Доступно объяснил.
На самом деле треды это тоже посты (оп-посты), а все остальные посты треда - комментарии к оп-посту.
https://2ch.hk/pr/res/729430.json (М)
Так что и "треды" (оп-посты) и комментарии к ним хранятся в одной таблице.
Оп-посты выделяются отдельной колонкой (parent=0), у остальных постов в parent ссылка на оп-пост. Номер оп-поста считается номером треда.
У опа кстати где-то есть задача на проектирование бд для сосача и написание запроса типа
"выбрать первые 10 тредов (оп-постов) + по три последних комментария к ним", как на главной.
А теперь представь длину запроса с JOINами всех таблиц, чтобы что-то найти в этих тредах.
> Передавай запрос через dependency injection, лучше даже отдельным классом, который реализует публичный интерфейс.
запрос это не dependency а аргумент функции parse. Так как мы можем захотеть разроутить несколько разных запросов.
> @throws RouterException нет
Да это наверно не обязательно писать, в реаьности почти каждый метод что-нибудь выбрасывает, это надо как-то автоматически определять.
> зачем ты инициируешь переменные в пустую строку? Чтобы код лишний раз засорить?
Почему нет? Чтобы в ней всегда было знаечние строкового типа.
>>44720
В ОП посте есть задачи по mysql, там есть ссылка на туториал "SQL для начинающих" - изучай.
>>44733
С горой таблиц вообще невозможно работать. Попробуй выведи последние N постов или тредов с доски например.
>>44729
Принципы проектирования баз данных давно изучены. Гугли "нормализация баз данных" - там есть статьи, написанные простым языком, которые объясняют как правильно проектирвоать БД. И читай наши задачи на mysql.
> Задачка на проверку: https://ideone.com/7gHlP7
> "/шы/ui" => "ши",
> "/жы/ui" => "жи",
Можно объединить в одно правило за счет скобок и $1
> "/[,;!?:](?!\s)/ui" => "пропущен пробел",
правило сработает, если в самом конце строки точка и за ней ничего нет: https://ideone.com/S6XfDG
> "/[^,](\sно\b|\sа\b)/ui" => "пропущена запятая"
сработает на Но в начале предложения: https://ideone.com/S6XfDG
Также, не обнаруживает отстувие пробела после точки: https://ideone.com/BxS1eW
> Задачка на исправление: https://ideone.com/6TJwXr
> "/шы/ui" => "ши",
> "/жы/ui" => "жи",
Можно объединить
Не видит отстутвие пробела после точки, добавляет лишнюю запятую: https://ideone.com/pykbgl
>>36833
Ты ищешь все латинские буквы. Но надо искать слова, содержащие буквы разных алфавитов. Чтобы он на слово "hello" не реагировал.
>>36950
Наверно, не знаю.
> Задачка на проверку: https://ideone.com/7gHlP7
> "/шы/ui" => "ши",
> "/жы/ui" => "жи",
Можно объединить в одно правило за счет скобок и $1
> "/[,;!?:](?!\s)/ui" => "пропущен пробел",
правило сработает, если в самом конце строки точка и за ней ничего нет: https://ideone.com/S6XfDG
> "/[^,](\sно\b|\sа\b)/ui" => "пропущена запятая"
сработает на Но в начале предложения: https://ideone.com/S6XfDG
Также, не обнаруживает отстувие пробела после точки: https://ideone.com/BxS1eW
> Задачка на исправление: https://ideone.com/6TJwXr
> "/шы/ui" => "ши",
> "/жы/ui" => "жи",
Можно объединить
Не видит отстутвие пробела после точки, добавляет лишнюю запятую: https://ideone.com/pykbgl
>>36833
Ты ищешь все латинские буквы. Но надо искать слова, содержащие буквы разных алфавитов. Чтобы он на слово "hello" не реагировал.
>>36950
Наверно, не знаю.
Посмотри такие движки:
- sphinx
- elastic search
- apache lucene
В них есть возможности для фасетного поиска, но не знаю, подойдут ли они тебе. Надо сделать тестовую базу с большим числом записей, и быстренько набросать простой код который будет индексировать эту базу, а затем делать запросы и тестировать время их выполнения. И далее смотреть, то или не то.
Также, можно конечно попробовать вариант с велосипедом на кеш-таблицах (или ключах в редисе) и инкрементальным обновлением, но я вижу тут проблемы: 1) сложность реализации, легко допустить баг 2) данные будут постепенно расходиться с реальностью 3) может быть тяжело их обновлять при поступлении/расходе товара 4) может получиться что мы изобретаем то что уже есть в том же elastic search
Да и сам поиск в специализированном движке скорее всего будет быстрее при условии правильной настройки индексов. Единтсвенное, что я не знаю, насколько тяжело будет обновлять данные в реальном времени - часто поисковые движки оптимизированы на редкое обновление.
Идея с кешем такая. Когда выбрано несколько условий, у нас в выборке мало товаров и кеш нам не нужен - мы можем посчитать фасеты по данным из БД. А когда у нас выбрано 0 или 1-2 условия, мы используем кеш для быстрого получения данных по фасетам.
Соответственно нужны ключи вида
цвет:красный -> X товаров
цвет:синий -> Y товаров
бренд:adidas -> Z товаров
Этот кеш позволит получать фасеты когда не выбрано ни 1 условие. Аналогично можно сделать кеш или таблицу для подсчета фасетов при выборе 1 условия. В ней придется перебрать все возможные сочетания 2 фильтров, и записей может получиться много:
цвет:красный,бренд:adidas -> X
цвет:синий,бренд:adidas -> Y
....
предыдущую таблицу можно объединить с этой, добавив NULL (или например 0) в качестве разрешенного значения второго фильтра:
цвет:синий,NULL -> X
цвет:красный,NULL -> Y
Инкрементальное обновление делается так: когда у нас появляется или исчезает товар, мы увеличиваем или уменьшаем число во всех строках кеш-таблицы где есть характеристики этого товара.
Не могу гарантировать что эта схема будет работать хорошо, надо делать тесты. Я сейчас подумал и схема кажется мне громоздкой и не очень удачной. Лучше наверно разобраться с одним из поисковых движков.
Ссылки в помощь:
- (англ) https://lucene.apache.org/core/4_0_0/facet/org/apache/lucene/facet/doc-files/userguide.html
- (англ) http://sphinxsearch.com/blog/2014/05/22/the-facet-feature/
- (англ) https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html
Имей в виду, что эти поисковые движки это не такая штука, которую можно просто установить и все само заработает. Придется разбираться как именно они работают.
> Хорошая идея, но что делать, когда меняется stock для одного из товаров. Т.е. наличие товара добавляется или удаляется. Генерировать тут же все таблицы с кешами заново?
Инкрементально обновлять только строчки относящиеся к данному товару.
> про настройки БД возможно нет, до задачи с убиранием фильтров на все скорости хватало в пределах 1 секунды, что устраивало. Есть где про эти настройки руководство почитать?
Если вы используете InnoDB то надо чтобы памяти в innodb pool было не меньше чем размер всех используемых индексов, чтобы они все в нее помещались. Увидеть размер индексов можно командой SHOW TABLES STATUS по моему.
> Как я понял из твоих объяснений, из известных решений только кэш-таблицы и фасетный поиск? Или еще какие есть?
То, что тебе нужно, называется фасетный поиск, надо либо использовать готовый движок с их поддержкой либо, что хуже, сделать велосипед с такими возможностями.
> Внешний select выбирает фильтры из полученной таблицы доступных продуктов по этим фильтрам. SELECT DISTINCT filters.filter_id FROM (select товары по фильтрам которые в наличии, плюс фильтры к ним через join filters_to_products)
Не нужны тут подзапросы. Если у тебя есть запрос выбирающий все товары попадающие под фильтры, то чтобы найти какие еще фильтры доступны, тебе надо сделать SELECT filetr_id, COUNT(DISTINCT product_id) ... GROUP BY filter_id то есть сгруппировать найденные записи по id фильтра и получим таблицу вида
filter_id | COUNT
Из которой уже можно определить какие еще фиьтры доступны и сколько в них товаров. Без подзапросов и за 1 проход по таблице.
Ну например у нас применено 2 фильтра (цвет=красный, бренд=adidas). Запрос на выборку товаров:
SELECT DISTINCT product_id
FROM products_present ...
JOIN product_filter pf1 ON ...
JOIN product_filter pf2 ON ...
WHERE pf1.filter_id = ? AND pf2.filter_id = ?
LIMIT ?
Либо как альтернатива с 1 джойном (наверно так даже быстрее будет)
SELECT product_id
FROM products_present ...
JOIN product_filter pf ON ...
GROUP BY product_id
HAVING SUM(pf.filter_id = ?) > 0 AND SUM(pf.filter_id = ?) > 0
LIMIT ?
Соответстенно чтобы отсюда получить данные по фасетам, надо приджойнить таблицу еще раз и сгруппировать:
SELECT pf3.filter_id, COUNT(DISTINCT product_id)
FROM products_present ...
JOIN product_filter pf1 ON ...
JOIN product_filter pf2 ON ...
JOIN product_filter pf3 ON ...
WHERE pf1.filter_id = ? AND pf2.filter_id = ?
GROUP BY pf3.filter_id
Запросы я не проверял, так что перепроверь их сам. Но вряд ли лни будут очень быстро работать так как ту несколько джойнов. Но я думаю, будет лучше чем с подзапросом. Таблица products_present по задумке джойнится для проверки наличия товара.
>>36999
> Думаю, не можем. Потому что может быть еще другой товар, тоже футболка красного цвета бренда адидас.
Тогда там в кеш-таблице будет цифра 2, то есть она показывает не число футболок одного вида, а число видов товаров которые соответствуют услвоиям.
> Т.е. чтобы уменьшить цифру в кеш-таблице, нам сначало надо пройтись опять по всем товарам в магазине, посмотреть есть ли другие товары, которые футболка красного цвета и бренда адидас, и только если их нет, можно из кэш таблицы удалить эту запись.
Нет, только если какой-то вид товара полностью вышел из продажи (то есть остаток на складе стал равен нулю), мы уменьшаем число видов на 1.
> Если всего 13 фильтров, кеш-таблицы мы видимо создавать будем только по 3-4 фильтрам.
Для 3-4 фильтров будет слишком много комбинаций наверно. Потому что число записей в таблице = число сочетаний фильтров, если 1000 фильтров то комбинаций из 3 фильтров будет миллиард.
>>37007
Нет, мы рассмтриваем все виды фильтров как однородный список, то есть фильтр по цвету и по бренду идет на одном уровне. Представь что фильтров нет, а есть просто теги у каждого товара вроде "цвет:красный", "бренд:adidas". Вроде у вас так уже и сделано.
Кеш-таблица будет одна.
Посмотри такие движки:
- sphinx
- elastic search
- apache lucene
В них есть возможности для фасетного поиска, но не знаю, подойдут ли они тебе. Надо сделать тестовую базу с большим числом записей, и быстренько набросать простой код который будет индексировать эту базу, а затем делать запросы и тестировать время их выполнения. И далее смотреть, то или не то.
Также, можно конечно попробовать вариант с велосипедом на кеш-таблицах (или ключах в редисе) и инкрементальным обновлением, но я вижу тут проблемы: 1) сложность реализации, легко допустить баг 2) данные будут постепенно расходиться с реальностью 3) может быть тяжело их обновлять при поступлении/расходе товара 4) может получиться что мы изобретаем то что уже есть в том же elastic search
Да и сам поиск в специализированном движке скорее всего будет быстрее при условии правильной настройки индексов. Единтсвенное, что я не знаю, насколько тяжело будет обновлять данные в реальном времени - часто поисковые движки оптимизированы на редкое обновление.
Идея с кешем такая. Когда выбрано несколько условий, у нас в выборке мало товаров и кеш нам не нужен - мы можем посчитать фасеты по данным из БД. А когда у нас выбрано 0 или 1-2 условия, мы используем кеш для быстрого получения данных по фасетам.
Соответственно нужны ключи вида
цвет:красный -> X товаров
цвет:синий -> Y товаров
бренд:adidas -> Z товаров
Этот кеш позволит получать фасеты когда не выбрано ни 1 условие. Аналогично можно сделать кеш или таблицу для подсчета фасетов при выборе 1 условия. В ней придется перебрать все возможные сочетания 2 фильтров, и записей может получиться много:
цвет:красный,бренд:adidas -> X
цвет:синий,бренд:adidas -> Y
....
предыдущую таблицу можно объединить с этой, добавив NULL (или например 0) в качестве разрешенного значения второго фильтра:
цвет:синий,NULL -> X
цвет:красный,NULL -> Y
Инкрементальное обновление делается так: когда у нас появляется или исчезает товар, мы увеличиваем или уменьшаем число во всех строках кеш-таблицы где есть характеристики этого товара.
Не могу гарантировать что эта схема будет работать хорошо, надо делать тесты. Я сейчас подумал и схема кажется мне громоздкой и не очень удачной. Лучше наверно разобраться с одним из поисковых движков.
Ссылки в помощь:
- (англ) https://lucene.apache.org/core/4_0_0/facet/org/apache/lucene/facet/doc-files/userguide.html
- (англ) http://sphinxsearch.com/blog/2014/05/22/the-facet-feature/
- (англ) https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html
Имей в виду, что эти поисковые движки это не такая штука, которую можно просто установить и все само заработает. Придется разбираться как именно они работают.
> Хорошая идея, но что делать, когда меняется stock для одного из товаров. Т.е. наличие товара добавляется или удаляется. Генерировать тут же все таблицы с кешами заново?
Инкрементально обновлять только строчки относящиеся к данному товару.
> про настройки БД возможно нет, до задачи с убиранием фильтров на все скорости хватало в пределах 1 секунды, что устраивало. Есть где про эти настройки руководство почитать?
Если вы используете InnoDB то надо чтобы памяти в innodb pool было не меньше чем размер всех используемых индексов, чтобы они все в нее помещались. Увидеть размер индексов можно командой SHOW TABLES STATUS по моему.
> Как я понял из твоих объяснений, из известных решений только кэш-таблицы и фасетный поиск? Или еще какие есть?
То, что тебе нужно, называется фасетный поиск, надо либо использовать готовый движок с их поддержкой либо, что хуже, сделать велосипед с такими возможностями.
> Внешний select выбирает фильтры из полученной таблицы доступных продуктов по этим фильтрам. SELECT DISTINCT filters.filter_id FROM (select товары по фильтрам которые в наличии, плюс фильтры к ним через join filters_to_products)
Не нужны тут подзапросы. Если у тебя есть запрос выбирающий все товары попадающие под фильтры, то чтобы найти какие еще фильтры доступны, тебе надо сделать SELECT filetr_id, COUNT(DISTINCT product_id) ... GROUP BY filter_id то есть сгруппировать найденные записи по id фильтра и получим таблицу вида
filter_id | COUNT
Из которой уже можно определить какие еще фиьтры доступны и сколько в них товаров. Без подзапросов и за 1 проход по таблице.
Ну например у нас применено 2 фильтра (цвет=красный, бренд=adidas). Запрос на выборку товаров:
SELECT DISTINCT product_id
FROM products_present ...
JOIN product_filter pf1 ON ...
JOIN product_filter pf2 ON ...
WHERE pf1.filter_id = ? AND pf2.filter_id = ?
LIMIT ?
Либо как альтернатива с 1 джойном (наверно так даже быстрее будет)
SELECT product_id
FROM products_present ...
JOIN product_filter pf ON ...
GROUP BY product_id
HAVING SUM(pf.filter_id = ?) > 0 AND SUM(pf.filter_id = ?) > 0
LIMIT ?
Соответстенно чтобы отсюда получить данные по фасетам, надо приджойнить таблицу еще раз и сгруппировать:
SELECT pf3.filter_id, COUNT(DISTINCT product_id)
FROM products_present ...
JOIN product_filter pf1 ON ...
JOIN product_filter pf2 ON ...
JOIN product_filter pf3 ON ...
WHERE pf1.filter_id = ? AND pf2.filter_id = ?
GROUP BY pf3.filter_id
Запросы я не проверял, так что перепроверь их сам. Но вряд ли лни будут очень быстро работать так как ту несколько джойнов. Но я думаю, будет лучше чем с подзапросом. Таблица products_present по задумке джойнится для проверки наличия товара.
>>36999
> Думаю, не можем. Потому что может быть еще другой товар, тоже футболка красного цвета бренда адидас.
Тогда там в кеш-таблице будет цифра 2, то есть она показывает не число футболок одного вида, а число видов товаров которые соответствуют услвоиям.
> Т.е. чтобы уменьшить цифру в кеш-таблице, нам сначало надо пройтись опять по всем товарам в магазине, посмотреть есть ли другие товары, которые футболка красного цвета и бренда адидас, и только если их нет, можно из кэш таблицы удалить эту запись.
Нет, только если какой-то вид товара полностью вышел из продажи (то есть остаток на складе стал равен нулю), мы уменьшаем число видов на 1.
> Если всего 13 фильтров, кеш-таблицы мы видимо создавать будем только по 3-4 фильтрам.
Для 3-4 фильтров будет слишком много комбинаций наверно. Потому что число записей в таблице = число сочетаний фильтров, если 1000 фильтров то комбинаций из 3 фильтров будет миллиард.
>>37007
Нет, мы рассмтриваем все виды фильтров как однородный список, то есть фильтр по цвету и по бренду идет на одном уровне. Представь что фильтров нет, а есть просто теги у каждого товара вроде "цвет:красный", "бренд:adidas". Вроде у вас так уже и сделано.
Кеш-таблица будет одна.
задача на ошибки решается несколькими регулярками, по одной на правило
опечаточник не воспринимает английские слова: http://ideone.com/7fGiyC
>>37657
Тебя не проверяем пока? Ты ведь вроде не все еще исправил?
>>38055
Ставишь приложение в автозапуск под контролем какого-нибудь супервизора. можно написанного на той же самой ноде.
Без прав root поставить ноду нельзя, нужен VPS/VDS/свой сервер.
>>38059
> разбитие текста по символу переноса строки -> разбитие строки по пробелам
\s в регулярке соответствует тому и другому сразу
> проверка слова на специфичные символы свойственные языку (типа "qui или йцф"), если специфичных символов нет, то подсчет количества символов английского и русского алфавита, и в зависимости от того каких символов больше, в ту сторону и переводить через preg_replace с массивами схожих символов
Сложно. Проще просто искать сочетания вида
- латинская буква, за ней русская
и наоборот.
Язык слова можно определять по первой букве или по тому, каких букв в слове больше. И выделять все не соответствующие языку.
>>38132
Как минимум проблема в том, что ты хочешь чтобы перед урл обязательно шел пробел.
задача на ошибки решается несколькими регулярками, по одной на правило
опечаточник не воспринимает английские слова: http://ideone.com/7fGiyC
>>37657
Тебя не проверяем пока? Ты ведь вроде не все еще исправил?
>>38055
Ставишь приложение в автозапуск под контролем какого-нибудь супервизора. можно написанного на той же самой ноде.
Без прав root поставить ноду нельзя, нужен VPS/VDS/свой сервер.
>>38059
> разбитие текста по символу переноса строки -> разбитие строки по пробелам
\s в регулярке соответствует тому и другому сразу
> проверка слова на специфичные символы свойственные языку (типа "qui или йцф"), если специфичных символов нет, то подсчет количества символов английского и русского алфавита, и в зависимости от того каких символов больше, в ту сторону и переводить через preg_replace с массивами схожих символов
Сложно. Проще просто искать сочетания вида
- латинская буква, за ней русская
и наоборот.
Язык слова можно определять по первой букве или по тому, каких букв в слове больше. И выделять все не соответствующие языку.
>>38132
Как минимум проблема в том, что ты хочешь чтобы перед урл обязательно шел пробел.
Не поможет тебе готовое решение. Чтобы понять ООП надо сначала решить задачи про Вектор и Кошки-Мышки из учебника. также, разобраться с mysql, внимательно прочесть примечания к задаче про студентов.
На готовый код можно смотреть только после того как ты сделаешь свой вариант решения. так как до этого ты много чего просто не поймешь.
Программирование это не работа на конвейере. Здесь нельзя один раз посмотреть как делают другие и повторять за ними. Надо понимать, что ты делаешь, каждую строчку которую ты пишешь.
Если ты на чем-то застрял, покажи код и попроси подсказку. Наверняка кто-нибудь поможет.
>>38377
> Обёртка над session_start(). Проще не создавать.
С точки зрения ООП это нелогично. Если ты хочешь ООП версию сессии, ты должен и дальше к ней через тот же объект обращаться, хотя это получается плохая абстракция так как создав второй объект, ты получишь не вторую сессию, а ту же самую.
> Сейчас маппер получает уже объект PDO вот так: new StudentMapper(Config::getPDOconnection);
> Так лучше? Это максимум, до чего я смог додуматься.
да, так и надо. Только вот мне кажется класс конфиг должен отвечать за хранение конфига, а не создаение объектов PDO. И он не должен использовать статические методы, и вообще статические методы это не ООП.
> Куда положить дефолтные controller и action? В методы setController и setAction?
Просто в переменную кукю-нибудь
> Этот метод проверяет наличие $_SESSION['email']. Что передавать в метод, который проверяет, залогинен ли пользователь?
Например, не использовать статический метод, а использовать нормальный метод. Тогда выглядеть будет логично и понятно что исплоьзуется ранее сохраненная в объекте информация.
Что-то у тебя плоховато с ООП. Ты Вектор или кошки-мышки решал? Статические методы это процедурный код, а не ООП.
> 1. Что должна представлять из себя обёртка над Location для редиректа? Часть класса, хелпер, как назвать, какие ещё функции можно туда добавить?
Можно сделать метод в базовом контроллере, можно во вью если это у тебя объект. Вообще, в Симфони сделано та: контроллер должен вернуть объект REsponse, а этот объект содердит заголовки и тело запроса и в базовом контроллере есть меод создающий респонс с нужным заголовком.
> 2. Статические классы. У меня статические - Validator, Auth (хелпер для регистрации/логина), Session, Config. Они ведь все работают только с какой-то одной сущностью, у которой не может быть несколько инстансов. Можно их статическими оставить?
Не вижу логики. Что мешает нам создать несколько разных объектов-конфигов? Что мешает создать несколько валидаторов? Сессий? То, что в твоем конкретном случае нужен только один конфиг, тут особого значения не играет.
Также, ты читал урок про DI? Исплоьзование статических классов не дает полноценно использовать DI (что делает код хуже, так как все классы статновятся спутаны друг с другом намертво) и возможности ООП. Тестировать классы по отдельности тоже становится невозможно. Их зависимости явно не обозначены в конструкторе. По моему так получается не ООП код, а лапша.
Статические методы используются очень редко: в тех случаях. когда у нас нет объекта как такового, либо когда метод общий для любого числа объектов и не исплоьзует $this. Ну например, метод, который переводит число байт в строку вроде "3 Мб" можно сделать статическим (паттерн utility class).
То что в твоей программе валидатор нужен только один значения не имеет. Это объект? Объект. Значит, надо делать нормальный объект с нормальными методами.
> У валидатора один публичный метод make, который принимает 2 массива (пик).
Валидатор в ООп логичнее сделать так: ты создаешь валидатор, задаешь правила, а потом проверяешь им данные. И кстати очень странно что ты проверяешь не модель, а массив. Чем тебе модель не нравится? Зачем для студента делать альтернативное представление в виде массива? Массив ведь хуже объекта.
>>38387
Сессия это надстройка над куками. Что мешает их использовать напрямую?
Сессии будут создаватьяс в больших количествах и забивать твой диск когда к тебе зайдет например робот гугла. Я не вижу зачем она тут нужна если то же самое делается через куки и у кук можно выставить любой срок жизни. Какие ты видишь преимущества?
Недостатки сессий:
- короткий срок жизни
- забивают диск
- если у тебя несколько серверов, приходится переностить их в редис или базу
> Куки же менее безопасны, чем сессии.
Если в них хранить длинный токен то это вполне безопасно
> Или возможность не разлогинивать пользователя спустя час бездействия важнее безопасности?
В чем проявляется "опасность" кук? Напиши пример уязвимости в авторизации через куки? Помни при этом что сессия тоже использует куки для хранения идентификатора.
Не поможет тебе готовое решение. Чтобы понять ООП надо сначала решить задачи про Вектор и Кошки-Мышки из учебника. также, разобраться с mysql, внимательно прочесть примечания к задаче про студентов.
На готовый код можно смотреть только после того как ты сделаешь свой вариант решения. так как до этого ты много чего просто не поймешь.
Программирование это не работа на конвейере. Здесь нельзя один раз посмотреть как делают другие и повторять за ними. Надо понимать, что ты делаешь, каждую строчку которую ты пишешь.
Если ты на чем-то застрял, покажи код и попроси подсказку. Наверняка кто-нибудь поможет.
>>38377
> Обёртка над session_start(). Проще не создавать.
С точки зрения ООП это нелогично. Если ты хочешь ООП версию сессии, ты должен и дальше к ней через тот же объект обращаться, хотя это получается плохая абстракция так как создав второй объект, ты получишь не вторую сессию, а ту же самую.
> Сейчас маппер получает уже объект PDO вот так: new StudentMapper(Config::getPDOconnection);
> Так лучше? Это максимум, до чего я смог додуматься.
да, так и надо. Только вот мне кажется класс конфиг должен отвечать за хранение конфига, а не создаение объектов PDO. И он не должен использовать статические методы, и вообще статические методы это не ООП.
> Куда положить дефолтные controller и action? В методы setController и setAction?
Просто в переменную кукю-нибудь
> Этот метод проверяет наличие $_SESSION['email']. Что передавать в метод, который проверяет, залогинен ли пользователь?
Например, не использовать статический метод, а использовать нормальный метод. Тогда выглядеть будет логично и понятно что исплоьзуется ранее сохраненная в объекте информация.
Что-то у тебя плоховато с ООП. Ты Вектор или кошки-мышки решал? Статические методы это процедурный код, а не ООП.
> 1. Что должна представлять из себя обёртка над Location для редиректа? Часть класса, хелпер, как назвать, какие ещё функции можно туда добавить?
Можно сделать метод в базовом контроллере, можно во вью если это у тебя объект. Вообще, в Симфони сделано та: контроллер должен вернуть объект REsponse, а этот объект содердит заголовки и тело запроса и в базовом контроллере есть меод создающий респонс с нужным заголовком.
> 2. Статические классы. У меня статические - Validator, Auth (хелпер для регистрации/логина), Session, Config. Они ведь все работают только с какой-то одной сущностью, у которой не может быть несколько инстансов. Можно их статическими оставить?
Не вижу логики. Что мешает нам создать несколько разных объектов-конфигов? Что мешает создать несколько валидаторов? Сессий? То, что в твоем конкретном случае нужен только один конфиг, тут особого значения не играет.
Также, ты читал урок про DI? Исплоьзование статических классов не дает полноценно использовать DI (что делает код хуже, так как все классы статновятся спутаны друг с другом намертво) и возможности ООП. Тестировать классы по отдельности тоже становится невозможно. Их зависимости явно не обозначены в конструкторе. По моему так получается не ООП код, а лапша.
Статические методы используются очень редко: в тех случаях. когда у нас нет объекта как такового, либо когда метод общий для любого числа объектов и не исплоьзует $this. Ну например, метод, который переводит число байт в строку вроде "3 Мб" можно сделать статическим (паттерн utility class).
То что в твоей программе валидатор нужен только один значения не имеет. Это объект? Объект. Значит, надо делать нормальный объект с нормальными методами.
> У валидатора один публичный метод make, который принимает 2 массива (пик).
Валидатор в ООп логичнее сделать так: ты создаешь валидатор, задаешь правила, а потом проверяешь им данные. И кстати очень странно что ты проверяешь не модель, а массив. Чем тебе модель не нравится? Зачем для студента делать альтернативное представление в виде массива? Массив ведь хуже объекта.
>>38387
Сессия это надстройка над куками. Что мешает их использовать напрямую?
Сессии будут создаватьяс в больших количествах и забивать твой диск когда к тебе зайдет например робот гугла. Я не вижу зачем она тут нужна если то же самое делается через куки и у кук можно выставить любой срок жизни. Какие ты видишь преимущества?
Недостатки сессий:
- короткий срок жизни
- забивают диск
- если у тебя несколько серверов, приходится переностить их в редис или базу
> Куки же менее безопасны, чем сессии.
Если в них хранить длинный токен то это вполне безопасно
> Или возможность не разлогинивать пользователя спустя час бездействия важнее безопасности?
В чем проявляется "опасность" кук? Напиши пример уязвимости в авторизации через куки? Помни при этом что сессия тоже использует куки для хранения идентификатора.
Ну давай разберём по порядку всё тобой написанное.
Разделов на имиджборде где-то 100 (примерно).
В каждом разделе ну никак не больше 100 тредов (может, и намного меньше).
Итого имеем максимум 10 тысяч таблиц.
И это не так уж и много.
>>44746
Так поиска нет по всем разделам сразу, поиск и есть только конкретному разделу из 100 тредов (может, я чего упустил - давно не был на главной, несколько лет уже). А перечисление ста таблиц в запросе с JOINом - это нормально.
>С горой таблиц вообще невозможно работать. Попробуй выведи последние N постов или тредов с доски например.
А где такое на имиджбордах? Впервые слышу.
Мне кажется, это сильное усложнение функционала. Так и во многих задачах из твоего учебника, мне кажется, хотя это очень помогает разобраться во всём.
Есть треды, которые бампаются, есть простой поиск по сообщениям в разделе.
Всё остальное - от лукавого ОПа-перфекциониста.
Задавай конкретные вопросы, прикладывай своё решение - хотя бы начало.
Чужой код лучше смотри только у тех задач, которые уже сам решил.
> public function getSalaryRate()
> {
> return $salaryRate = 1100;
Зачем тут переменная? return и = это 2 отдкльных команды которые стоит писать на отдельной строке, или же, что лучше, убрать переменную вообще.
>;extension=php_mbstring.dll
dll это вообще виндовое вроде, не? Как оно делается по человечески?
Допустим, есть говносайтики на cms, которые раз в пару месяцев нужно обновить, поправить перекосившиеся после обновления костыли, может прикрутить какой-то новый костыль. У владельца говносайтика нет денег на то чтобы содержать своих кодеров, поэтому либо лезет изредка на фриланс биржу и поручает это задание местным васянам, либо дрессирует своих контент-манагеров, чтобы могли сами обновить/пофиксить cms. Или делает сам.
Уровень ответственности: хуяк-хуяк -> в продакшн.
Темпы разработки: должно быть готово вчера.
Командная работа: на нуле, все делает один индус (причем каждый раз новый)
Другой случай, когда такой говносайт идет вверх, растет посещаемость, по-любому нужны чуваки уже хотя бы среднего уровня компетенции для рефакторинга/оптимизации.
Хозяин говносайта либо находит ответственного фрилансера (что редкость), либо контору, которая за определенный срок и вознаграждение приводит сайт к более-менее человеческому виду.
Ответственность: выше средней. Есть слабое подобие код-ревью, можно в случае затруднений попросить начальство перераспределить нагрузку.
Темпы: средние.
Командная работа: есть, но заставляет желать лучшего. Командная работа на уровне когда два-три слабо разбирающихся чувака пытаются что-то там обсуждать и как-то помогать друг другу.
Наконец есть крупные богатые сайты организаций, которые либо держат свой отдел, либо постоянно сотрудничают с хорошей конторой. Возможно контора ведет этот сайт с самого создания, возможно своя cms. Ну или легаси от другой хай-левел конторы.
Ответственность: экстремальная (к GN: какого писюна слово экстрим пишется через И, а экстремальная через Е?), есть код-ревью, непременные тесты. Много уровней в планировании и обсуждении (всякие scrum, agile).
Темпы: средние.
Команда: отлично налаженное взаимодействие.
Правильно представляю классификацию?
И как попасть именно в контору третьего типа, где есть шикарный карьерный рост?
Сколько ни смотрел вакансии, джунов ищут только первые и вторые. А как я стану мидлом, если буду работать в дно-конторе? Какой-то замкнутый круг.
inb4: решить все задачки опа про калькулятор и устраиваться в сеньором в гугл
Допустим, есть говносайтики на cms, которые раз в пару месяцев нужно обновить, поправить перекосившиеся после обновления костыли, может прикрутить какой-то новый костыль. У владельца говносайтика нет денег на то чтобы содержать своих кодеров, поэтому либо лезет изредка на фриланс биржу и поручает это задание местным васянам, либо дрессирует своих контент-манагеров, чтобы могли сами обновить/пофиксить cms. Или делает сам.
Уровень ответственности: хуяк-хуяк -> в продакшн.
Темпы разработки: должно быть готово вчера.
Командная работа: на нуле, все делает один индус (причем каждый раз новый)
Другой случай, когда такой говносайт идет вверх, растет посещаемость, по-любому нужны чуваки уже хотя бы среднего уровня компетенции для рефакторинга/оптимизации.
Хозяин говносайта либо находит ответственного фрилансера (что редкость), либо контору, которая за определенный срок и вознаграждение приводит сайт к более-менее человеческому виду.
Ответственность: выше средней. Есть слабое подобие код-ревью, можно в случае затруднений попросить начальство перераспределить нагрузку.
Темпы: средние.
Командная работа: есть, но заставляет желать лучшего. Командная работа на уровне когда два-три слабо разбирающихся чувака пытаются что-то там обсуждать и как-то помогать друг другу.
Наконец есть крупные богатые сайты организаций, которые либо держат свой отдел, либо постоянно сотрудничают с хорошей конторой. Возможно контора ведет этот сайт с самого создания, возможно своя cms. Ну или легаси от другой хай-левел конторы.
Ответственность: экстремальная (к GN: какого писюна слово экстрим пишется через И, а экстремальная через Е?), есть код-ревью, непременные тесты. Много уровней в планировании и обсуждении (всякие scrum, agile).
Темпы: средние.
Команда: отлично налаженное взаимодействие.
Правильно представляю классификацию?
И как попасть именно в контору третьего типа, где есть шикарный карьерный рост?
Сколько ни смотрел вакансии, джунов ищут только первые и вторые. А как я стану мидлом, если буду работать в дно-конторе? Какой-то замкнутый круг.
inb4: решить все задачки опа про калькулятор и устраиваться в сеньором в гугл
Сперва клепаешь говносайты чтобы было на что жить, в свободное время учишь что-то ещё, в итоге опыт + знания дают возможность перебраться на уровень выше.
Дебагер нужно покупать отдельно? Во взрослом IDE есть встроенные, этого хватит?
На реддите советуют PHPStorm и Netbeans. У меня стоит Komodo, там вроде тоже есть, но что-то не работает.
В PHPStorm нужно накофигурить xdebug, в принципе даже в саблайме это можно сделать.
> Насколько он упрощает / уменьшает время на поиск и исправление ошибок в коде?
Очень помогает, не надо забивать голову и гадать как у тебя в определенном месте алгоритм отработал. Меньше вардампов будет в разы
Кулстори?
Быдлошарага без помощи со стороны коллег, или наоборот крутая контора, которую не потянул по уровню?
>Командная работа: есть, но заставляет желать лучшего. Командная работа на уровне когда два-три слабо разбирающихся чувака пытаются что-то там обсуждать и как-то помогать друг другу.
На основе чего ты сделал такой вывод? Все всегда по разному и зависит от конторы и людей которые в ней работают.
>Команда: отлично налаженное взаимодействие.
Хорошее взаимодействие может быть и в маленькой конторе, из двух с половиной специалистов.
>И как попасть именно в контору третьего типа, где есть шикарный карьерный рост?
Ты хочешь попасть в контору с хорошим взаимодействием? Тогда откликайся на любые вакансии, ходи на собеседования, знакомься с командой, пробуй выполнять тестовые задания, т.е. взаимодействуй с ними. Если захочешь присоединиться к ним, тебе будет понятно как это сделать, что почитать, что подтянуть.
Или ты хочешь в крупную компанию с карьерным ростом? Совет аналогичный, отличие в том, что поиск сужается списком топ-100 ИТ компаний. Не все работают с рекрутерами. У многих вакансии и контакты отдела кадров есть на сайте. Например у яндекса на сайте, есть даже небольшие тестовые задания. Учи, выполняй.
>А как я стану мидлом, если буду работать в дно-конторе? Какой-то замкнутый круг.
Если ты вообще нуб, то даже опыт работы в днище конторе поднимет твои навыки. Но долго там не задерживайся. Как тут уже советовали, поработай, возьми отпуск на пол года и учи еще. Затем повторяй весь цикл сначала.
конкатинация, текст из переменной дописать до текста которые вернет функция
Вариант с переопределением в шаблоне
{{ form_start(form, {'name': 'new_form_name'})
не годится, потому что имена инпутов по-прежнему будут браться неизвестно откуда
old_form_name[input_name], а мне нужно new_form_name[input_name]
Вариант с createNamedBuilder тоже не годится.
Я создаю форму через createForm(MyForm::class, $data) контроллера, передавая ему имя класса формы.
В своем классе определяю метод
public function buildForm(FormBuilderInterface $builder, array $options)
который требуется интерфейсом FormTypeInterface
То есть там уже используется готовый билдер, созданный видимо через createBuilder. В нем как-то можно переопределить имя формы?
Это просто пиздец, у мну нет слов, чтобы изменить одну букву сидеть гуглить по двачаса.
Помогло только расковыривание исходного кода.
Оказывается нужно было в классе формы переопределить метод AbstractType::getBlockPrefix()
(возможно я зря начал с 3-ей версии, под нее даже stackoverflow недостаточно загажен ответами)
Ну ок, +1exp в копилку. Еще 10k и стану архимагом.
Вот есть таблица attempts (попытки пройти тест).
id | test_id | user_id
Таблица answers (ответы) выглядит так
id | attempt_id | question_id | answer
Нужно узнать, является ли тест пройденным (попытка завершенной) - то есть на все ли вопросы теста пользователь дал ответы (на некоторые вопросы можно дать несколько ответов, варианты с чекбоксами).
То есть нужно сравнить кол-во вопросов к тесту с кол-вом уникальных (distinct) ответов.
Чистый sql для получения кол-ва уникальных ответов
select count(distinct question_id) from answers where attempt_id = :id
Собственно вопрос где этот код писать? В модели (например Attempt), или в ее репозитории (AttemptRepository, использую доктрину). Или может запилить какой сервис?
Удобно было бы в модели, вроде это логично, что Попытка должна знать о кол-ве ответов и т.д., и в контроллере код красивый типа
if ($attempt->isCompleted()) {...}
Но это вообще по-ормному, что модель обращается к базе данных?
С другой стороны, когда мы пишем $attempt->answers->count(), там тоже втихаря идет обращение к бд. Только с вытягиванием из бд всех связанных записей, созданием их объектов (ну или прокси), и только потом у объекта ArrayCollection вызывается метод count, который возвращает кол-во записей.
Тут конечно нужно писать чистый sql/dql.
Да нет, там же даже нет возможности получить entityManager, так что в модели наверное не нужно писать такой код.
Сервис или репозиторий? Если чет, пишу сервис, нечет репозиторий. На дабл иду играть в игры.
> Хорошо написанная функция получает все нужные ей данные из аругментов (метод может еще использовать данные из полей объекта). Побочные эффекты - это случаи, когда функция или метод ведут себя не очевидно. Например:
функция или метод работает правильно только если перед ней был вызван какой-то другой метод.
Это что, вообще не рекомендуется использовать другие методы внутри функции? Эти методы нужно использовать при передачи аргумента?
По ровну. Сначала была быдло шарага, где никто не помогал, дальше выдрочился и стали объяснять не нубские вещи.
А потом охуел от наших менеджеров, когда они говорят "a little bit" и это часть ТЗ
Дрочи вопросы к собеседованиям, пиши резюме покруче и иди в другую. Преимущество работы программистом, что увольнение вообще на тебя никак не влияет, желающих нанять программистов шараг очень много. Увольнения вообще тебя заботить не должны, только набор опыта. Используй время между увольнениями, чтобы его еще поднабраться, делай тестовые проекты или open source.
Так и будет, но вообще я думаю на фриланс съебать на несколько месяцев и парт-тайм в другой конторе, где друзяшки работают. Просто не очень приятно, колектив хороший, уволили потому, что работки нихрена нет, и все бывают сидели без тасков по неделю-две
>>44717
Благодарю всех за дельные советы!
Учёл замечания, избавился от массивов массивов и необходимости их проверять трёхэтажными циклами.
https://github.com/applejacky/tmp
die() временно использую, чтобы сократить код для проверяющих. Exception'ы всегда можно добавить потом, мне гораздо важнее разобраться с архитектурой. Да, мне следовало об это сразу предупредить, чтобы вы на это не отвлекались.
>> [a-zA-Z0-9]
>Очень ограниченный набор символов
Добавил дефис и нижнее подчёркивание.
>> :num
>Других плейсхолдеров нет?
Пока нет. Их добавлять-то не сложно, мне кажется у меня гораздо больше проблем с архитектурой.
Поизучал роутинг в Symfony (сложно). Однако на нём и основывался, распределяя ответственности.
А если есть возможность поедать мамкины борщи, качая скиллы программирования, то обязательно ли идти в быдлошараги? Я пробыл пару недель в одной студии: сидел и разгребал кривые модули для WP и Джумлы, а гендир без наушников смотрел дом-2. Поэтому я сейчас качаюсь в сторону ООП и фреймворков, чтобы никогда больше за процедурный код не садиться.
Шараги в своем cv потом можно поднести как опыт работы в реальных проектах, а те что ты дома делаешь это такое себе.
Если ты готов относиться у обучению как к работе, то есть всерьез часов по 6 в день учиться то конечно учиться выгоднее. Ведь на работе надо работать, учиться некогда. Проблема в том что люди просто обычно забивают на это дело и бездельничают по 6 часов в день.
В тех проектах во вьюшках были SQL-запросы, в таблицах БД были не просто названия товаров, а названия товаров вперемешку с вёрсткой. GET-переменные шли прямиком в SQL-запрос без какой-либо фильтрации. Шторм треть функций на странице зачёркивал, так как они сильно устарели. Я хотел PHP бросить. До сих не верю, что кто-то может платить за аккуратный и грамотный код, а не за процедурные портянки, которые через задницу, но работают.
>>45445
Я достаточно отбитый, чтобы сутками аутировать за кодом.
У тебя стереотипы какие-то. Ты думаешь, на других языках лучше? JS - это язык верстальщиков, то есть тех кто не осилил программирование, но все равно хочет вкатиться в IT. Экосистема JS, все эти модные фреймворки это адские велосипеды заточенные под какую-то одну цель. В самом языке даже нет нормального синтаксиса для классов.
Многие разработчики на JS не знают JS и умеют только копипастить куски кода на джейкери со stackoverflow. Толпы хелловордщиков, которые умеют написать хелло ворлд на модном фреймворке при наличии туториала.
Руби - ну тут все понятно, его пиарили как простой язык с низким порогом входа, и туда поналез кто попало. Сам руби он рейлс - это фреймворк наподобие Юи, со своими странностями. Библиотеки в руби патчат друг друга и могут из-за этого даже быть несовместимы.
На ютубе видел видеособеседование рубиста, который работал полгода в команде одного проекта. Он не смог написать код переворота строки (я надеюсь все аноны которые у нас учатся, это могут. Если нет - срочно садитесь и пишите).
Си, Си++ - у них нет даже единого стандарта оформления кода. В Си нет даже колллекций и когда проекту нужен динамический массив или хеш-таблица, они изобретают велосипед заново. В Си++ есть стандартная библиотека коллекций, но многие говорят что она неудобная, неуклюжая, динамически выделяет память, тормозит, и потому надо написать свою реализацию. Часть людей умеет только копипастиь куски кода на Qt. Нет менеджера пакетов, сборка и компиляция очень медленные из-за непродуманности процесса.
Большую часть ты будешь проводить не за написанием программы, а за отладкой кода, поиском утечек памяти и нарушений синхронизации при многопоточном доступе. задачи скучные, неинтересные, программирование допотопных 8-битных контроллеров на госпредприятии со всеми вытекающими. Разумеется, люди, котором жалко денег на нормальный контроллер, будут экономить и на твоей зарплате.
Java - серьезный, взрослый язык с хорошей экосистемой, но многословный и скучноватый.
C# - серьезный, взрослый язык с хорошей экосистемой, но очень уж заточен на продукты МС.
Го - язык клоунов, придуманный чтобы было чем занять скучающих сотрудников Гугл. В коде будут перемешаны функции с маленькой и большой буквы, так как первая буква у них обозначает public/private и ты будешь постоянно их путать. Нет единого менеджера пакетов, нету классов, нету абстрактных типов. Нет никаких правил рапсределения кода по файлам потому все сваливают в одинфайл. Экосистемы нет, многие библиотеки на гитхабе выглядят так будето их писал умственно отатсалый, про вещи вроде разделения ответсвтенности никто и не слышал (ну, это термин из ООП, а ООП в Го не рады). Впрочем, лучше, чем Си.
Ты думаешь в других языках лучше, Да ничуть. Точно такие же толпы быдлокодеров везде. И точно также среди них выделяются те, кто подходит к изучению материала всерьез, расширяет свой кругозор, знает большое число технологий.
У тебя стереотипы какие-то. Ты думаешь, на других языках лучше? JS - это язык верстальщиков, то есть тех кто не осилил программирование, но все равно хочет вкатиться в IT. Экосистема JS, все эти модные фреймворки это адские велосипеды заточенные под какую-то одну цель. В самом языке даже нет нормального синтаксиса для классов.
Многие разработчики на JS не знают JS и умеют только копипастить куски кода на джейкери со stackoverflow. Толпы хелловордщиков, которые умеют написать хелло ворлд на модном фреймворке при наличии туториала.
Руби - ну тут все понятно, его пиарили как простой язык с низким порогом входа, и туда поналез кто попало. Сам руби он рейлс - это фреймворк наподобие Юи, со своими странностями. Библиотеки в руби патчат друг друга и могут из-за этого даже быть несовместимы.
На ютубе видел видеособеседование рубиста, который работал полгода в команде одного проекта. Он не смог написать код переворота строки (я надеюсь все аноны которые у нас учатся, это могут. Если нет - срочно садитесь и пишите).
Си, Си++ - у них нет даже единого стандарта оформления кода. В Си нет даже колллекций и когда проекту нужен динамический массив или хеш-таблица, они изобретают велосипед заново. В Си++ есть стандартная библиотека коллекций, но многие говорят что она неудобная, неуклюжая, динамически выделяет память, тормозит, и потому надо написать свою реализацию. Часть людей умеет только копипастиь куски кода на Qt. Нет менеджера пакетов, сборка и компиляция очень медленные из-за непродуманности процесса.
Большую часть ты будешь проводить не за написанием программы, а за отладкой кода, поиском утечек памяти и нарушений синхронизации при многопоточном доступе. задачи скучные, неинтересные, программирование допотопных 8-битных контроллеров на госпредприятии со всеми вытекающими. Разумеется, люди, котором жалко денег на нормальный контроллер, будут экономить и на твоей зарплате.
Java - серьезный, взрослый язык с хорошей экосистемой, но многословный и скучноватый.
C# - серьезный, взрослый язык с хорошей экосистемой, но очень уж заточен на продукты МС.
Го - язык клоунов, придуманный чтобы было чем занять скучающих сотрудников Гугл. В коде будут перемешаны функции с маленькой и большой буквы, так как первая буква у них обозначает public/private и ты будешь постоянно их путать. Нет единого менеджера пакетов, нету классов, нету абстрактных типов. Нет никаких правил рапсределения кода по файлам потому все сваливают в одинфайл. Экосистемы нет, многие библиотеки на гитхабе выглядят так будето их писал умственно отатсалый, про вещи вроде разделения ответсвтенности никто и не слышал (ну, это термин из ООП, а ООП в Го не рады). Впрочем, лучше, чем Си.
Ты думаешь в других языках лучше, Да ничуть. Точно такие же толпы быдлокодеров везде. И точно также среди них выделяются те, кто подходит к изучению материала всерьез, расширяет свой кругозор, знает большое число технологий.
Я был в шараге, где не знают что такое ide. Копируют контент при помощи мышки из исходного кода страницы, потом руками исправляют ссылки в блокноте. (какой линукс, какой вгет, какой курл, ты шо, вася)
Особо одаренные в ворде.
Это я пошел на неоплачиваемую стажировку. Свалил в ужасе на второй день.
Чтобы вы представляли, как обстоит дело в мухосранях, а то совсем зажрались в своих москвах. IDE за сто баксов им видите ли на халяву не раздают.
Не стоит соглашаться на неоплачиваемые стажировки (ты наверно это у же понял, а я еще для других анонов напишу). Если вы будете дома сидеть и решать например наши задачки, пользы будет скорее всего даже больше.
>>45490
Ребят, никто мне IDE не оплачивал, просто есть такая штука как студенческая лицензия. Отправляете фото студака на оф сайте и получаете лицензию на год. В конце концов, можно сидеть на триалке месяц, а перед удалением шторма скопировать конфиги куда-нибудь (продвинутые пакетные менеджеры вроде Pacman умеют удалять пакеты, не трогая конфиги). При новой установке шторма указывайте, чтобы он использовал старые конфиги. Занимает пару минут, хватает на месяц, никакого пиратства.
Около 1,5 года. Сидел в основном на magento, иногда делал что-нибудь на микрофреймворках и один заказик был на wp, а его я знал еще раньше.
Ух ты, мадженто, прям как я, правда я на вторую сразу попал, я там выше свою историю написал.
Как вообще оцениваешь свой уровень именно в мадженто? Разобрался во всех тонкостях за полтора года?
Я правильно классы и логику задачи?
// Класс геймсет
// Размер поля
// Содержит массив Мышей
// Массив Кошек
// Конструктор (размер поля, кол-во Мышей, кол-во Кошек)
// Метод просчета игрового хода
// Цикл 20 ходов
// Запуск хода мышыей
// Запуск хода кошек
// Запус поедания Мышки Кошкой
// Вывод состояния текущего хода
// Метод вывода состояния текущего хода
// Метод поедания Мышки Кошкой
// Класс Мышка
// Координата Х
// Координата Y
// Метод генерации всех возможных ВариантовХодов (Размер поля)
// Метод оценки ВариантовХодов (ВариантХода, Координаты Кошек)
// Возвращает вес
// Выбор наилучшего ВариантХода (массив ВариантовХодов)
// Меняет координаты Мышки
// Клас Кошка
// Координата Х
// Координата Y
// Счетчик ходов
// Метод генерации всех возможных ВариантовХодов (Размер поля)
// Метод оценки ВариантовХодов (ВариантХода, Координаты Мышек)
// Возвращает вес
// Выбор наилучшего ВариантХода (массив ВариантовХодов)
// Меняет координаты Кошки
// Класс ВариантХода
// Координаты
Я правильно классы и логику задачи?
// Класс геймсет
// Размер поля
// Содержит массив Мышей
// Массив Кошек
// Конструктор (размер поля, кол-во Мышей, кол-во Кошек)
// Метод просчета игрового хода
// Цикл 20 ходов
// Запуск хода мышыей
// Запуск хода кошек
// Запус поедания Мышки Кошкой
// Вывод состояния текущего хода
// Метод вывода состояния текущего хода
// Метод поедания Мышки Кошкой
// Класс Мышка
// Координата Х
// Координата Y
// Метод генерации всех возможных ВариантовХодов (Размер поля)
// Метод оценки ВариантовХодов (ВариантХода, Координаты Кошек)
// Возвращает вес
// Выбор наилучшего ВариантХода (массив ВариантовХодов)
// Меняет координаты Мышки
// Клас Кошка
// Координата Х
// Координата Y
// Счетчик ходов
// Метод генерации всех возможных ВариантовХодов (Размер поля)
// Метод оценки ВариантовХодов (ВариантХода, Координаты Мышек)
// Возвращает вес
// Выбор наилучшего ВариантХода (массив ВариантовХодов)
// Меняет координаты Кошки
// Класс ВариантХода
// Координаты
В Москве есть несколько компаний, набирающих новичков-стажеров в PHP, за еду, естественно. QSOFT, amoCRM, еще там кто-то. Что это за компании, я знаю, про текучку, отношения знаю. Но вот стоит ли туда пойти ради практики, чтоб через полгода-год этого ада устроиться на норм работу? Там вообще можно рассчитывать на стабильные 25к?
Вот, если че: https://career.ru/vacancy/16926757
Просто я после того, как доделаю задачу про студентов, буду даже ой как крут для них. А вот изучение фреймворков, мне кажется, это еще 4 месяца изучение дома, без работы и денег (лол)
Тонкостях? Не смеши, я всех атрибутов к блокам в layout-е не помню, не говоря уже о кучи различных фишек, хотя основы крепко вбиты в голову. Полюбил ее кстати. Вот с EAV по эксперементировать, ну а так готов написать, что угодно, правда по времени будут различия.
Вторую надо будет подучить сейчас, выделею себе на это день другой.
Пиздос, скажи, что ты еще во Львове, так я вообще ебанусь. Аноны из Львова хуярят под Magento2
https://github.com/babyba/html_path
Я имел в виду не все атрибуты на память, а вообще тонкости работы, там все эти квоты, передачи аттрибутов, филдсеты, обсерверы и вся эта хрень. Потому что я уже два с половиной месяца сижу на это второй магенте, и чем дальше, тем больше всяких непонятных и неочевидных вещей
>>45938
Не Львов, но 250км до негоб так что рядом
Кто пользуется, подскажите, тут есть окно навигации, в котором видны все классы-свойства-методы, функции активного документа?
В Komodo IDE такой есть, вроде дерева папок, как в проводнике. Оч удобно, тут найти не могу.
Зы, для блокнота с подсветкой кушать пол гига памяти... хм. И тормозить сильнее чем Illustrator и Photoshop вместе взятые. Наверное это такой хитрый маркетинговый ход. Но цветовая схема Draacula приятная.
Спасибо, сам нашел, вкладка Structure
>шлет запросы на сервак, на каждую букву
Может стоит какой-то таймаут использовать? Типа если инпут в фокусе, то проверять время с его последнего изменения, и если больше например секунды двух слать запрос.
И если первая часть строки не изменилась, то можно фильтровать результаты прошлого поиска на стороне клиента, а не создавать новый, по сути уточняющий запрос.
С дивана
Не должно быть долго выполняющихся запросов в вебе. Либо переделывайте принцип работы, либо настраивйте индексы, либо подключайте поисковый движок либо отключайте автодополнение.
Также, если вариантов слов там небольшое количество, выгоднее может быть реализовать локальное автодополнение без сервера.
>>46285
Для новичка, ничего не умеющего делать, это даже много. Если тебе не нравятся такие условия, ты всегда можешь пойти работать на государственное предприятие или в крошку-картошку. Там особые навыки не требуются.
>>45940
> https://github.com/babyba/html_path/blob/master/les1/index.html
Нет doctype (а это значит браузер будет отступать от стандартов при отображении), 2 раза стоит тег html, нет тега meta charset.
Само задание сделано верно.
> https://github.com/babyba/html_path/blob/master/les2/index.html
Тут максимальная ширина у тебя 622px а не 600 получается. width ведь задает внутреннюю ширину области контента.
Цвет фона очень темный -у меня черные буквы на нем еле видны. Надо было взять бледно-зеленый цвет с картинки.
Принцип решения верный.
> задача 3
Вместо цифровых кодов лучше использовать названия, например < - они лучше читаются, а коды вряд ли кто наизусть запомнит.
Решено верно.
> задача 4
Между блоками больше 10px по горизонтали - к ним добавляется пробел. Это легко увидеть если выставить горизонтальные margin в ноль.
В комментариях к задаче есть ссылка на статью с обяснениями.
Цвета не точно соответствуют картинке.
> задача 5
В общем верно, если не считать того что цвета на картинке более светлые.
> задача 6
> font-family: "Trebuchet MS";
Надо всегда в конец списка добавлять один из встроенных шрифтов вроде sans-serif.
Верстка сделана ненадежно. Попробуй сделать больше текста справа- текст начинает обтекать меню и заходить под него снизу: https://jsfiddle.net/nc2nLv3f/1/
> задача 7
между кнопкой и инпутом больше 10px по горизонтали. Также, для свойства box-sizing надо добавлять версии с префикасми (вроде -moz-box-sizing) для старых браузеров.
> задача 8
Текст выровнен странно.
Верстка сделана неприавльно. Ты заключил отдельные абзацы в тег article. С какой стати? Это что, 2 отдельных не связанных статьи? Это абзацы одного текста. Верстка должна быть максимально простая, в таком стиле:
<p>абзац</p>
<div class="note">примечание</div>
<h2>заголовок</h2>
<p>абзац</p>
> width: calc(100% - 130px);
Это вообще кошмар. calc мало где работает, не стоит пока его использловать. Также, ты написал правило для абзацев, а что делать с заголовками, таблицами, списками, картинками, которые могут встретиться в тексте?
Эта задача решена неправильно.
> задача 9
> src="/home/vasil/Desktop/HTML/les9/images/img.gif"
Ты так поставил ссылки (указав полный путь к файлу) что они будут работать только на твоем компьютере. Почитай про относительные ссылки: https://github.com/codedokode/pasta/blob/master/network/urls.md
> display: inline-flex;
Это мало где поддерживается. И для такой простой задачи не требуется флексбокс. И к тому же непонятно почему inline-flex, а не flex, объясни, почему? хуже того, ты прописал контейнеру inline-flex, но сами возможности флексбокса не используешь. Или ты хотел написать inline-block? Это тоже неправильно.
Блоки идут вертикально друг над другом и растягиваются на всю ширину родителя. По моему, для такого случая есть только один вариант позиционирования.
> margin: 20px;
> width: 90%;
Неправильно прописаны свойства. Надо прописывать либо ширину и 1 маргин, либо только 2 маргина, а ширину в auto. У тебя в сумме (90% + 20px + 20px) может не получиться 100%.
Если текста будет много, он будет затекать под картинку снизу.
Эта задача пока решена неправильно.
> задача 10
> width: 98.5%;
> padding-left: 5px;
Они в сумме не дадут 100%. Ты подбирал значения под конкретную картинку, а надо код который будет работать с картинкой любой ширины.
Ну и паддинг там не 5, а вроде 10px.
> bottom: 4px;
Ага, ты не разобрался почему под картинкой есть отступ в 4px и начал городить костыли. Надо разобраться, посмотри нет ли информации в подсказках к задаче. Я могу сказать, почему - картинка это inline элемент по умолчанию и она выравнивается не по низу блока, а по базовой линии текста, которая находится выше на эти 4 пикслея (зависит от размера шрифта, твое решение подобрано под конкретную высоту букв). В блоке где находится картинка (figure), текста, конечно нет, а вот базовая линия для него - есть.
Также, попробуй перезашрузить страницу зажав ctrl + f5. Пока картинка не загрузилась, блок с ней имеет нулевую ширину и выглядит странно. Надо задать какой-то минимальный размер для него.
Не реализована подгонка картинки под ширину страницы. Уменьши ширину body с 500 до 200 пикселей. Картинка должна сузиться до этиз 200 пикс, а она не сужается, а выводится в полную ширину.
Также, если вариантов слов там небольшое количество, выгоднее может быть реализовать локальное автодополнение без сервера.
>>46285
Для новичка, ничего не умеющего делать, это даже много. Если тебе не нравятся такие условия, ты всегда можешь пойти работать на государственное предприятие или в крошку-картошку. Там особые навыки не требуются.
>>45940
> https://github.com/babyba/html_path/blob/master/les1/index.html
Нет doctype (а это значит браузер будет отступать от стандартов при отображении), 2 раза стоит тег html, нет тега meta charset.
Само задание сделано верно.
> https://github.com/babyba/html_path/blob/master/les2/index.html
Тут максимальная ширина у тебя 622px а не 600 получается. width ведь задает внутреннюю ширину области контента.
Цвет фона очень темный -у меня черные буквы на нем еле видны. Надо было взять бледно-зеленый цвет с картинки.
Принцип решения верный.
> задача 3
Вместо цифровых кодов лучше использовать названия, например < - они лучше читаются, а коды вряд ли кто наизусть запомнит.
Решено верно.
> задача 4
Между блоками больше 10px по горизонтали - к ним добавляется пробел. Это легко увидеть если выставить горизонтальные margin в ноль.
В комментариях к задаче есть ссылка на статью с обяснениями.
Цвета не точно соответствуют картинке.
> задача 5
В общем верно, если не считать того что цвета на картинке более светлые.
> задача 6
> font-family: "Trebuchet MS";
Надо всегда в конец списка добавлять один из встроенных шрифтов вроде sans-serif.
Верстка сделана ненадежно. Попробуй сделать больше текста справа- текст начинает обтекать меню и заходить под него снизу: https://jsfiddle.net/nc2nLv3f/1/
> задача 7
между кнопкой и инпутом больше 10px по горизонтали. Также, для свойства box-sizing надо добавлять версии с префикасми (вроде -moz-box-sizing) для старых браузеров.
> задача 8
Текст выровнен странно.
Верстка сделана неприавльно. Ты заключил отдельные абзацы в тег article. С какой стати? Это что, 2 отдельных не связанных статьи? Это абзацы одного текста. Верстка должна быть максимально простая, в таком стиле:
<p>абзац</p>
<div class="note">примечание</div>
<h2>заголовок</h2>
<p>абзац</p>
> width: calc(100% - 130px);
Это вообще кошмар. calc мало где работает, не стоит пока его использловать. Также, ты написал правило для абзацев, а что делать с заголовками, таблицами, списками, картинками, которые могут встретиться в тексте?
Эта задача решена неправильно.
> задача 9
> src="/home/vasil/Desktop/HTML/les9/images/img.gif"
Ты так поставил ссылки (указав полный путь к файлу) что они будут работать только на твоем компьютере. Почитай про относительные ссылки: https://github.com/codedokode/pasta/blob/master/network/urls.md
> display: inline-flex;
Это мало где поддерживается. И для такой простой задачи не требуется флексбокс. И к тому же непонятно почему inline-flex, а не flex, объясни, почему? хуже того, ты прописал контейнеру inline-flex, но сами возможности флексбокса не используешь. Или ты хотел написать inline-block? Это тоже неправильно.
Блоки идут вертикально друг над другом и растягиваются на всю ширину родителя. По моему, для такого случая есть только один вариант позиционирования.
> margin: 20px;
> width: 90%;
Неправильно прописаны свойства. Надо прописывать либо ширину и 1 маргин, либо только 2 маргина, а ширину в auto. У тебя в сумме (90% + 20px + 20px) может не получиться 100%.
Если текста будет много, он будет затекать под картинку снизу.
Эта задача пока решена неправильно.
> задача 10
> width: 98.5%;
> padding-left: 5px;
Они в сумме не дадут 100%. Ты подбирал значения под конкретную картинку, а надо код который будет работать с картинкой любой ширины.
Ну и паддинг там не 5, а вроде 10px.
> bottom: 4px;
Ага, ты не разобрался почему под картинкой есть отступ в 4px и начал городить костыли. Надо разобраться, посмотри нет ли информации в подсказках к задаче. Я могу сказать, почему - картинка это inline элемент по умолчанию и она выравнивается не по низу блока, а по базовой линии текста, которая находится выше на эти 4 пикслея (зависит от размера шрифта, твое решение подобрано под конкретную высоту букв). В блоке где находится картинка (figure), текста, конечно нет, а вот базовая линия для него - есть.
Также, попробуй перезашрузить страницу зажав ctrl + f5. Пока картинка не загрузилась, блок с ней имеет нулевую ширину и выглядит странно. Надо задать какой-то минимальный размер для него.
Не реализована подгонка картинки под ширину страницы. Уменьши ширину body с 500 до 200 пикселей. Картинка должна сузиться до этиз 200 пикс, а она не сужается, а выводится в полную ширину.
> // Конструктор (размер поля, кол-во Мышей, кол-во Кошек)
Неудобно с самого начала задавать число животных. Лучше сделать методы позволяющие добавлять и убирать их.
Так, в общем, выглядит нормально.
>>45823
Для новых браузеров - кроссдоменный аякс, для старых можно JSONP использовать.Если у тебя iframe то вообще проблем никаких нет и обычный аякс будет работать.
Алсо надо приложить усилия чтоюы не сломать что-то на сайте или не вызвать конфликт добавлением каких-то бибилиотек. Вообще, если ты не уверен то лучше делать ифреймом так как он изолирут виджет и сайт.
>>45411
> https://github.com/applejacky/tmp/blob/master/app/Routing/RouteCollection.php#L17
Абсолютно не понятно, что делает проверка валидности роута в классе коллекции. Надо делать ее либо в самом роуте либо в классе-валидаторе. Думаю, тут логичнее в самом роуте.
> $pathRegex = "~^{$route->getPath()}$~";
Стоит исплоьзовать preg_quote так как в path могут быть спецсимволы.
Вообще, если подумать, метод проверки на соответсвие УРЛ логично поместить в сам роут. Хотя ... можно конечно и снаружи это делать, вопрос тут как ты рассматриваешь класс - как хранилище информации о роуте или как активный объект, который и сам умеет что-то делать.
Так, в общем, норм. Кстати, я подуал, кроме симфони есть и другие реализации роутеров - наприер в фреймворке Slim есть свои роуты и они попроще (но может не так хорошо сделаны).
> Добавил дефис и нижнее подчёркивание.
Все равно маловато ведь. А как насчет восклицательных знаков? Насчет шапочки? Скобок? Запятой?
> // Конструктор (размер поля, кол-во Мышей, кол-во Кошек)
Неудобно с самого начала задавать число животных. Лучше сделать методы позволяющие добавлять и убирать их.
Так, в общем, выглядит нормально.
>>45823
Для новых браузеров - кроссдоменный аякс, для старых можно JSONP использовать.Если у тебя iframe то вообще проблем никаких нет и обычный аякс будет работать.
Алсо надо приложить усилия чтоюы не сломать что-то на сайте или не вызвать конфликт добавлением каких-то бибилиотек. Вообще, если ты не уверен то лучше делать ифреймом так как он изолирут виджет и сайт.
>>45411
> https://github.com/applejacky/tmp/blob/master/app/Routing/RouteCollection.php#L17
Абсолютно не понятно, что делает проверка валидности роута в классе коллекции. Надо делать ее либо в самом роуте либо в классе-валидаторе. Думаю, тут логичнее в самом роуте.
> $pathRegex = "~^{$route->getPath()}$~";
Стоит исплоьзовать preg_quote так как в path могут быть спецсимволы.
Вообще, если подумать, метод проверки на соответсвие УРЛ логично поместить в сам роут. Хотя ... можно конечно и снаружи это делать, вопрос тут как ты рассматриваешь класс - как хранилище информации о роуте или как активный объект, который и сам умеет что-то делать.
Так, в общем, норм. Кстати, я подуал, кроме симфони есть и другие реализации роутеров - наприер в фреймворке Slim есть свои роуты и они попроще (но может не так хорошо сделаны).
> Добавил дефис и нижнее подчёркивание.
Все равно маловато ведь. А как насчет восклицательных знаков? Насчет шапочки? Скобок? Запятой?
Если функция ничего не возвращает то писать return null бессмысленно. Разумеется, ты не должен пытаться сохранять результат функции если она всегда возвращает null. Это тоже бессмысленно.
Если же функция иногла возвращает значение, а иногда null, то лучше прописать это в return явно.
>>45363
> Это что, вообще не рекомендуется использовать другие методы внутри функции? Эти методы нужно использовать при передачи аргумента?
Нет, можно. Ты смотрел примеры "плохого" кода? Там суть либо в том что надо вызвать перед одним методом другой, чтобы первый правильно работал (что неочевидно и легко забыть), либо что функция обращается к глобальным переменным, и опять же в том месте где она вызывается это неочевидно, что она данные откуда-то еще берет, кроме аргументов.
>>45309
> Таблица answers (ответы) выглядит так
> id | attempt_id | question_id | answer
Вообще, учитывая что бывают разные типы вопросов, получается что бывают и разные типы ответов на эти вопросы. Не забывай про это.
> Нужно узнать, является ли тест пройденным (попытка завершенной) - то есть на все ли вопросы теста пользователь дал ответы (на некоторые вопросы можно дать несколько ответов, варианты с чекбоксами).
> То есть нужно сравнить кол-во вопросов к тесту с кол-вом уникальных (distinct) ответов.
Лучше сравнить множество id вопросов и множество id на которые даны ответы. И найти разницу между ними.
> Собственно вопрос где этот код писать? В модели (например Attempt), или в ее репозитории (AttemptRepository, использую доктрину). Или может запилить какой сервис?
Модель не может обращаться к БД явно, так что если ты не можешь это сделать там, придется в репозитории или сервисе. Помни что репозиторий отвечает прежде всего за работу с хранилищем данных, а не за сложные высокоуровневые функции. Ну и если тебе надо обращаться к нескольким репозиториям то нужен сервис.
> Но это вообще по-ормному, что модель обращается к базе данных?
К базе она обратиться не может так как в ней нет ссылки на entity manager. Обращаться к связанным сущностям она может. Представь что у тебя нет базы вообще и ты модели создаешь руками в начале скрипта. Будет ли твой код работать корректно? Если да то этот код можно поместить и в модель. В Data Mapper модель полностью отделена от базы, ничего о ней не знает и способна работать без нее.
> С другой стороны, когда мы пишем $attempt->answers->count(), там тоже втихаря идет обращение к бд
Не факт. Мы могли бы просто создать коллекцию ответов руками и засунуть в модель (например для теста). Откуда ты знаешь что она из базы? Более того, именно так и работают новосозданные модели до их сохранения в БД - мы добавляем в них связанные сущности вручную.
> Тут конечно нужно писать чистый sql/dql.
Тогда в репозиторий.
> Если чет, пишу сервис, нечет репозиторий
Ну вот ты и перечеркнул все мои многолетние усилия сделать из тебя программиста.
Если функция ничего не возвращает то писать return null бессмысленно. Разумеется, ты не должен пытаться сохранять результат функции если она всегда возвращает null. Это тоже бессмысленно.
Если же функция иногла возвращает значение, а иногда null, то лучше прописать это в return явно.
>>45363
> Это что, вообще не рекомендуется использовать другие методы внутри функции? Эти методы нужно использовать при передачи аргумента?
Нет, можно. Ты смотрел примеры "плохого" кода? Там суть либо в том что надо вызвать перед одним методом другой, чтобы первый правильно работал (что неочевидно и легко забыть), либо что функция обращается к глобальным переменным, и опять же в том месте где она вызывается это неочевидно, что она данные откуда-то еще берет, кроме аргументов.
>>45309
> Таблица answers (ответы) выглядит так
> id | attempt_id | question_id | answer
Вообще, учитывая что бывают разные типы вопросов, получается что бывают и разные типы ответов на эти вопросы. Не забывай про это.
> Нужно узнать, является ли тест пройденным (попытка завершенной) - то есть на все ли вопросы теста пользователь дал ответы (на некоторые вопросы можно дать несколько ответов, варианты с чекбоксами).
> То есть нужно сравнить кол-во вопросов к тесту с кол-вом уникальных (distinct) ответов.
Лучше сравнить множество id вопросов и множество id на которые даны ответы. И найти разницу между ними.
> Собственно вопрос где этот код писать? В модели (например Attempt), или в ее репозитории (AttemptRepository, использую доктрину). Или может запилить какой сервис?
Модель не может обращаться к БД явно, так что если ты не можешь это сделать там, придется в репозитории или сервисе. Помни что репозиторий отвечает прежде всего за работу с хранилищем данных, а не за сложные высокоуровневые функции. Ну и если тебе надо обращаться к нескольким репозиториям то нужен сервис.
> Но это вообще по-ормному, что модель обращается к базе данных?
К базе она обратиться не может так как в ней нет ссылки на entity manager. Обращаться к связанным сущностям она может. Представь что у тебя нет базы вообще и ты модели создаешь руками в начале скрипта. Будет ли твой код работать корректно? Если да то этот код можно поместить и в модель. В Data Mapper модель полностью отделена от базы, ничего о ней не знает и способна работать без нее.
> С другой стороны, когда мы пишем $attempt->answers->count(), там тоже втихаря идет обращение к бд
Не факт. Мы могли бы просто создать коллекцию ответов руками и засунуть в модель (например для теста). Откуда ты знаешь что она из базы? Более того, именно так и работают новосозданные модели до их сохранения в БД - мы добавляем в них связанные сущности вручную.
> Тут конечно нужно писать чистый sql/dql.
Тогда в репозиторий.
> Если чет, пишу сервис, нечет репозиторий
Ну вот ты и перечеркнул все мои многолетние усилия сделать из тебя программиста.
Это очень странно что тебе надо переопределять префикс имен полей формы. Ты что-то делаешь не так с вероятностью 99%
>>45200
> Как в симфони сменить имя формы?
А зачем?
> у мну нет слов, чтобы изменить одну букву сидеть гуглить по двачаса.
Ты еще с друпал не работал наверно. Там то же самое только без ООП, на коде из тысяч функций. Симфони очень универальная и многоцелевая штука и потому сложная.
>>45169
склеивание строк
>>44943
>>44975
> static public function getXRankSalary ($rank)
Почему статические методы? Не вижу убедительных причин. Ну например может есть другая компания и в ней другие расценки оплаты труда. Вообще, в ООП редко нужны статические методы.
> $xRankSalary
Не стоит добавлять странные префиксы. Я например не понял что значит x тут.
> 1 => '1',
Странно, почему ты коэффициент сделал строкой, а не числом. Это ведь число.
> public function getProfessionName ()
Не избыточная ли функция? Не лучше ли использовать getProfession()->getName()?
Вообще, я не уверен что нужно отдельно класс Coefficients и отдельно Profession. Мне тут вспоминается паттерн "Стратегия". Он используется когда нужно вынести вычисление чего-то из класса и сделать возможность замены алгоритма вычисления.
В данном случае мы можем сделать класс СтратегияРасчетаЗарплатыИДругихПараметровИнженера, в нем метод посчитатьзарплату(), посчитатьКофе(), в которые передаем профессию и ранг. Или самого работника. Или же совместить этот класс с Профессией. То есть, возможны варианты, я что-то сейчас сам не соображу как правильнее.
> public $name = ""; // название профессии
> abstract public function getSalaryRate();
А почему название полем? Можно его не задавать?
> / Класс аналитик
> class NewAnalyst extends Profession
А вот это явно неправильно. Ты собрался при каждом повышении зарплаты создавать новую профейссию? Профессия не поменялась, поменялась базовая ставка отдельно взятого сотрудника.
Я думаю, это можно решить так. Сделать в сотруднике поле "базовая ставка" и по умолчанию брать его из профессии. При смене профессии - сбрасывать. Или же сделать поле "переопределение базовой ставки". В общем, тут тебе надо подумать, как реализовать индивидуальное изменение ставки.
> if ($employee->getProfessionName() == $profession) {
мне кажется название профессии - плохой идентификатор. Хороший - это само имя класса. Его можно получить как константу: SomeClasss::class
> // Если сотрудник не соответствует профессии
> if ($employee->getProfessionName() == $profession) {
Комментарий противоречит коду
> if ($profession == "") {
> $employeesNumber = count($this->employees);
лушче тогда null использовать, а еще лучше сделать для этого отдельный метод
> // параметр может принимать значение 'all', параметр будет игнорироваться
Нужно использовать константу для таких случаев. Или тот же null.
> public function findEmployee ($profession, $rank, $chief)
Вообще этот метод странный. Почему он возвращает одного работника если под услови могут попадать несколько?
> $employeeKey = array_search($employee, $this->employees);
Там нужен дополнительный флаг, задающий строгий поиск, а то он може уволить не того. Прочитай в чем разница между == и === для объектов.
> // Поиск департамента по названию, аргумент "all" возращает все департааменты
Это 2 разных функции - найти один департамент и найти несколько.
> // Ничего не возвращам, мы обиделись
> die ("Департамент не найден");
Надо использовать исключения в таких случаях. они лучше и у меня есть по ним урок.
> static public function dismissEngineers($company)
Что-то мне не нравится тут статический метод. Почему не обычный?
Также, эта функция делает слишком много задач:
- клонирует компанию
- применяет антикризисную меру
- выводит таблицу результатов
Эти задачи надо разделить отдельно. У тебя сейчас нельзя применить антикризисные меры не печатая таблицы. не вижу логики.
Насчет выбора работников, все переусложнено. Выбирать лучше так:
- взять всех работников определенной профессии и рангов
- исключить из списка босса
- отсортировать по порядку увольнения
- взять первые N% через array_slice
- уволить
Так будет и проще и логичнее.
> // Находим аналитика самого высокого ранга
Это определенно стоит вынести в отдельную функцию. Разбей-ка слишком длинные функции на части.
> $rank = mb_substr ($vacancyName, 2, 1);
Лучше было парсить обозначение сотрудника регуляркой
>>44859
>>44858
Если ты не троллишь, то ты ошибаешься. Погугли "нормализация баз данных", "проектирование баз данных".
Это очень странно что тебе надо переопределять префикс имен полей формы. Ты что-то делаешь не так с вероятностью 99%
>>45200
> Как в симфони сменить имя формы?
А зачем?
> у мну нет слов, чтобы изменить одну букву сидеть гуглить по двачаса.
Ты еще с друпал не работал наверно. Там то же самое только без ООП, на коде из тысяч функций. Симфони очень универальная и многоцелевая штука и потому сложная.
>>45169
склеивание строк
>>44943
>>44975
> static public function getXRankSalary ($rank)
Почему статические методы? Не вижу убедительных причин. Ну например может есть другая компания и в ней другие расценки оплаты труда. Вообще, в ООП редко нужны статические методы.
> $xRankSalary
Не стоит добавлять странные префиксы. Я например не понял что значит x тут.
> 1 => '1',
Странно, почему ты коэффициент сделал строкой, а не числом. Это ведь число.
> public function getProfessionName ()
Не избыточная ли функция? Не лучше ли использовать getProfession()->getName()?
Вообще, я не уверен что нужно отдельно класс Coefficients и отдельно Profession. Мне тут вспоминается паттерн "Стратегия". Он используется когда нужно вынести вычисление чего-то из класса и сделать возможность замены алгоритма вычисления.
В данном случае мы можем сделать класс СтратегияРасчетаЗарплатыИДругихПараметровИнженера, в нем метод посчитатьзарплату(), посчитатьКофе(), в которые передаем профессию и ранг. Или самого работника. Или же совместить этот класс с Профессией. То есть, возможны варианты, я что-то сейчас сам не соображу как правильнее.
> public $name = ""; // название профессии
> abstract public function getSalaryRate();
А почему название полем? Можно его не задавать?
> / Класс аналитик
> class NewAnalyst extends Profession
А вот это явно неправильно. Ты собрался при каждом повышении зарплаты создавать новую профейссию? Профессия не поменялась, поменялась базовая ставка отдельно взятого сотрудника.
Я думаю, это можно решить так. Сделать в сотруднике поле "базовая ставка" и по умолчанию брать его из профессии. При смене профессии - сбрасывать. Или же сделать поле "переопределение базовой ставки". В общем, тут тебе надо подумать, как реализовать индивидуальное изменение ставки.
> if ($employee->getProfessionName() == $profession) {
мне кажется название профессии - плохой идентификатор. Хороший - это само имя класса. Его можно получить как константу: SomeClasss::class
> // Если сотрудник не соответствует профессии
> if ($employee->getProfessionName() == $profession) {
Комментарий противоречит коду
> if ($profession == "") {
> $employeesNumber = count($this->employees);
лушче тогда null использовать, а еще лучше сделать для этого отдельный метод
> // параметр может принимать значение 'all', параметр будет игнорироваться
Нужно использовать константу для таких случаев. Или тот же null.
> public function findEmployee ($profession, $rank, $chief)
Вообще этот метод странный. Почему он возвращает одного работника если под услови могут попадать несколько?
> $employeeKey = array_search($employee, $this->employees);
Там нужен дополнительный флаг, задающий строгий поиск, а то он може уволить не того. Прочитай в чем разница между == и === для объектов.
> // Поиск департамента по названию, аргумент "all" возращает все департааменты
Это 2 разных функции - найти один департамент и найти несколько.
> // Ничего не возвращам, мы обиделись
> die ("Департамент не найден");
Надо использовать исключения в таких случаях. они лучше и у меня есть по ним урок.
> static public function dismissEngineers($company)
Что-то мне не нравится тут статический метод. Почему не обычный?
Также, эта функция делает слишком много задач:
- клонирует компанию
- применяет антикризисную меру
- выводит таблицу результатов
Эти задачи надо разделить отдельно. У тебя сейчас нельзя применить антикризисные меры не печатая таблицы. не вижу логики.
Насчет выбора работников, все переусложнено. Выбирать лучше так:
- взять всех работников определенной профессии и рангов
- исключить из списка босса
- отсортировать по порядку увольнения
- взять первые N% через array_slice
- уволить
Так будет и проще и логичнее.
> // Находим аналитика самого высокого ранга
Это определенно стоит вынести в отдельную функцию. Разбей-ка слишком длинные функции на части.
> $rank = mb_substr ($vacancyName, 2, 1);
Лучше было парсить обозначение сотрудника регуляркой
>>44859
>>44858
Если ты не троллишь, то ты ошибаешься. Погугли "нормализация баз данных", "проектирование баз данных".
Спасибо за комментарии по делу, большинство понятны, буду исправлять.
Некоторые хочу уточнить:
>> static public function getXRankSalary ($rank)
> Почему статические методы? Не вижу убедительных причин. Ну например может есть другая компания и в ней
> другие расценки
> оплаты труда. Вообще, в ООП редко нужны статические методы.
Сделал статическим, чтобы иметь доступ к этому методу из любого места кода, не создавая экземпляра класса. Расчет на то, что коэффициенты могут меняться, и удобнее менять их в одном месте в коде. Хотя можно прописать их в формулу расчета зарплаты/кофе в классе Employee. И избавиться от отдельного класса.
А можно еще как-то получать доступ к коэффициентам из класса Employee?
> Вообще, я не уверен что нужно отдельно класс Coefficients и отдельно Profession.
Насчет класса Coefficients - вынес отдельно, чтобы они не болтались в классе Employee. Когда смотрел дамп компании увидел, что они копируются в каждом работнике. Там 1000 символов, в среднем по 2 бита на символ / 8 = 250 байт * 101(сотрудник) = 24 кБ, если я правильно посчитал. Решил вынести их из класса, для экономии (лет 30 назад, наверное было актуальнее).
Может быть логичнее хранить их в компании?
Но как получать к ним доступ из класса Employee? Обращаться к методу класса Company->getCoefficients из класса Employee не правильно?
У меня был вариант, но он мне показался не надежным, т.к. рассчет зп можно было запускать только из контекста класса Company, и передавать коэффициенты как аргумент вызова метода по цепочке Company > Department > Employee. Ведь так тоже не очень?
Паттерн "Стратегия" для изменения алгоритма расчета, а ведь тут меняется не алгоритм, а только коэффициенты?
Класс Profession отдельно, для того чтобы можно было поменять профессию у работника (Employee), и создавать новые классы профессий по шаблону родительского класса Profession. Идея была в том, что сторонний программист, создавая новый класс профессии должен переопределить обязательные методы для класса новой профессии. Хотя его можно убрать из кода, программа будет работать и без него.
>> static public function dismissEngineers($company)
>Что-то мне не нравится тут статический метод. Почему не обычный?
Решил не создавать объект класса АтикризисныеМеры, а напрямую обратиться к его методу. Думал статические методы именно для этого.
Спасибо за комментарии по делу, большинство понятны, буду исправлять.
Некоторые хочу уточнить:
>> static public function getXRankSalary ($rank)
> Почему статические методы? Не вижу убедительных причин. Ну например может есть другая компания и в ней
> другие расценки
> оплаты труда. Вообще, в ООП редко нужны статические методы.
Сделал статическим, чтобы иметь доступ к этому методу из любого места кода, не создавая экземпляра класса. Расчет на то, что коэффициенты могут меняться, и удобнее менять их в одном месте в коде. Хотя можно прописать их в формулу расчета зарплаты/кофе в классе Employee. И избавиться от отдельного класса.
А можно еще как-то получать доступ к коэффициентам из класса Employee?
> Вообще, я не уверен что нужно отдельно класс Coefficients и отдельно Profession.
Насчет класса Coefficients - вынес отдельно, чтобы они не болтались в классе Employee. Когда смотрел дамп компании увидел, что они копируются в каждом работнике. Там 1000 символов, в среднем по 2 бита на символ / 8 = 250 байт * 101(сотрудник) = 24 кБ, если я правильно посчитал. Решил вынести их из класса, для экономии (лет 30 назад, наверное было актуальнее).
Может быть логичнее хранить их в компании?
Но как получать к ним доступ из класса Employee? Обращаться к методу класса Company->getCoefficients из класса Employee не правильно?
У меня был вариант, но он мне показался не надежным, т.к. рассчет зп можно было запускать только из контекста класса Company, и передавать коэффициенты как аргумент вызова метода по цепочке Company > Department > Employee. Ведь так тоже не очень?
Паттерн "Стратегия" для изменения алгоритма расчета, а ведь тут меняется не алгоритм, а только коэффициенты?
Класс Profession отдельно, для того чтобы можно было поменять профессию у работника (Employee), и создавать новые классы профессий по шаблону родительского класса Profession. Идея была в том, что сторонний программист, создавая новый класс профессии должен переопределить обязательные методы для класса новой профессии. Хотя его можно убрать из кода, программа будет работать и без него.
>> static public function dismissEngineers($company)
>Что-то мне не нравится тут статический метод. Почему не обычный?
Решил не создавать объект класса АтикризисныеМеры, а напрямую обратиться к его методу. Думал статические методы именно для этого.
https://jsfiddle.net/uo6mnvxy/
Выполнил с использованием label с атрибутом for. Как можно обратится к label для установки фона когда внутри него находится input, с которого мы узнаём нажата кнопка или нет?
Оп, привет Студенты у меня https://github.com/greenTea242/Student_List
> А если пользователь при редактировании захочет очистить какое-то поле, эта проверка ведь его не пропустит?
Можно удалять поля полностью и добавлять их в бд? Я про это не знал. Мои регулярки не пропускают пустые значения. Менять? Пользователь что, вообще может не заполнять ничего?
> Тут ведь явно почти одно и то же. Нельзя ли это упростить за счет функций?
Я не нарушаю логику отображения перенося ее из шаблона в методы из вспомогательных классов как ты советовал? В борьбе с копипастом создал их(методов) несколько.
> Это странный код: если куки устанвлены и они правильные, установим их еще раз. Зачем?
Продление кук.
Это нормально что я несколько раз отклонился от общего для кода стиля(камелкейс) для имени переменной и имени метода и использовал вариант с нижним подчеркиванием ("CSRF_token")? Мне показалось, что не очень красиво если будет "CSRFToken". Стоило ли тогда тоже самое делать с authToken для, не знаю как сказать, симметрии чтоли.
> Зачем разрешать перезаписывать токен? Пользователь разве может его редактировать?
Ну он у меня в бд попадал через модель. Сейчас после манипуляций с двумя токенами я его добавляю отдельно https://github.com/greenTea242/Student_List/blob/master/public/register.php#L31 . Ок?
Алсо везде подбавлял абсолютных путей, сделал два токена. И еще вопрос, почему на пикрелейтед2 вокруг котика летает __DIR__? Помню я увидел пикчу в старых тредах и почему-то осталось в памяти что __DIR__ не есть хорошо.
Котика нарисовал не ОП, так что не знаю. Я думаю, автор пытался обратить внимание на нездоровое количество подчеркиваний, но это он еще Питон не видел.
>>46699
И еще забыл.
> тут мне не нравится что трудно понять как пользоваться этим классом. Почему нельзя сделать один метод валидации, куда мы даем студента и получаем список ошибок, а не гадать в каком порядке надо вызывать методы?
Нужно было перенести функционал метода addAbiturient в createErrorList или сделать как я сделал(методы теперь получают отдельно объект, без добавления его в свойства класса)?
Низкий порог вхождения, из-за этого пишется очень много говнокода, поэтому у языка такая репутация. ЖС дрисня или Руби ничем не лучше пхп (а в некоторых местах даже хуже), но это сейчас модно и прогрессивно, поэтому пхп в англоязычных комьюнити хейтят в основном всякие хипстеры.
> Как в симфони сменить имя формы?
> А зачем?
Затем что у меня есть форма для ответов. Ответы хранятся в STI таблице. Соответственно в доктринке разные классы для ответов.
TextAnswer, DecimalAnswer, VariantAnswer и т.д.
Я беру вопрос, смотрю его тип, после чего создаю форму под тип ответа.
TextAnswerForm, VariantAnswerForm, поля ведь отличаются, там текстовый инпут, там чекбоксы или радиокнопки.
Формы ведь тоже отдельный класс, имена классов разные.
Функция start_form подставляет в name именно из имени класса.
Тогда как я не зная имя формы буду брать данные из запроса?
$formData = $request->request->get('имя_формы?');
Поэтому я унифицирую, чтобы вместо хуиты типа 'text_answer_form' или 'variant_answer_form' всегда была 'answer_form' вне зависимости от типа вопроса и можно было знать по какому имени к ней обращаться.
(впрочем сейчас дошло, что к данные из формы можно получить не из реквеста, а из ее объекта, пропустив через $form->handle($request), и оно уже само разгребет какие данные ему нужны).
>Ну вот ты и перечеркнул все мои многолетние усилия сделать из тебя программиста.
Пошутить нельзя.
Я делаю не бездумно, просто когда нет возможности взвесить преимущества и недостатки какого-то подхода, легче уже выбрать произвольный и реализовать, тогда будут видны
его недостатки. Ну и вообще мне нравится писать код, а не сидеть как даун и "ломать голову".
Хочется видеть материальное выражение того что сделал за день.
Три дня думал над алгоритмом прохождения теста, особенно возможность возвращаться и отвечать на пропущенные вопросы оказалась непростой в реализации.
Несколько раз переписывал, наконец придумал такую схему, теперь точно получится.
/test/{id}/question/{num}
1. Если нет такого testId, выдать notFound.
2. Если у пользователя нет кук с токеном доступа, зарегистрировать его в фоновом режиме.
3. Найти у пользователя последнюю попытку пройти данный тест.
3.1. Если такой попытки нет, или она есть и ее статус 'completed', то создать новую.
3.2. Если такая попытка есть, и ее статус 'active' (по-умолчанию), то проверить
оставшееся время.
3.2.1. Если время истекло, редирект на /test/result/{attempt}, где выставить
статус в 'completed'.
3.2.2. Если время не истекло, проверить остались ли неотвеченные вопросы.
3.2.2.1. Если есть неотвеченные вопросы, взять id этой попытки.
3.2.2.2. Иначе редирект на /test/result/{attempt}, где выставить статус.
4. Если {num} не передан, /test/{id}/question методом GET выдать первый из
неотвеченных вопросов.
5. При запросе /test/{id}/question методом POST выдать первый из неотвеченных
вопросов, сохранить результат и сделать редирект на /test/{id}/question/{num}
где {num} это номер по порядку следующего неотвеченного вопроса.
6. При запросе /test/{id}/question/{num} метдом GET выдать вопрос {num}.
7. ... методом POST сохранить ответ и редирект /test/{id}/question/{num}.
8. Если нет следующего вопроса, проверить, есть ли у попытки пропущенные вопросы.
8.1. Если есть пропущенные, редирект на /test/{id}/confirm, где спросить
подтверждение завершения теста либо ссылку на /test/{id}/question (без параметра
{num}, cмотри пункт 4).
8.2. Если нет пропущенных, редирект на /test/result/{attempt}.
> Как в симфони сменить имя формы?
> А зачем?
Затем что у меня есть форма для ответов. Ответы хранятся в STI таблице. Соответственно в доктринке разные классы для ответов.
TextAnswer, DecimalAnswer, VariantAnswer и т.д.
Я беру вопрос, смотрю его тип, после чего создаю форму под тип ответа.
TextAnswerForm, VariantAnswerForm, поля ведь отличаются, там текстовый инпут, там чекбоксы или радиокнопки.
Формы ведь тоже отдельный класс, имена классов разные.
Функция start_form подставляет в name именно из имени класса.
Тогда как я не зная имя формы буду брать данные из запроса?
$formData = $request->request->get('имя_формы?');
Поэтому я унифицирую, чтобы вместо хуиты типа 'text_answer_form' или 'variant_answer_form' всегда была 'answer_form' вне зависимости от типа вопроса и можно было знать по какому имени к ней обращаться.
(впрочем сейчас дошло, что к данные из формы можно получить не из реквеста, а из ее объекта, пропустив через $form->handle($request), и оно уже само разгребет какие данные ему нужны).
>Ну вот ты и перечеркнул все мои многолетние усилия сделать из тебя программиста.
Пошутить нельзя.
Я делаю не бездумно, просто когда нет возможности взвесить преимущества и недостатки какого-то подхода, легче уже выбрать произвольный и реализовать, тогда будут видны
его недостатки. Ну и вообще мне нравится писать код, а не сидеть как даун и "ломать голову".
Хочется видеть материальное выражение того что сделал за день.
Три дня думал над алгоритмом прохождения теста, особенно возможность возвращаться и отвечать на пропущенные вопросы оказалась непростой в реализации.
Несколько раз переписывал, наконец придумал такую схему, теперь точно получится.
/test/{id}/question/{num}
1. Если нет такого testId, выдать notFound.
2. Если у пользователя нет кук с токеном доступа, зарегистрировать его в фоновом режиме.
3. Найти у пользователя последнюю попытку пройти данный тест.
3.1. Если такой попытки нет, или она есть и ее статус 'completed', то создать новую.
3.2. Если такая попытка есть, и ее статус 'active' (по-умолчанию), то проверить
оставшееся время.
3.2.1. Если время истекло, редирект на /test/result/{attempt}, где выставить
статус в 'completed'.
3.2.2. Если время не истекло, проверить остались ли неотвеченные вопросы.
3.2.2.1. Если есть неотвеченные вопросы, взять id этой попытки.
3.2.2.2. Иначе редирект на /test/result/{attempt}, где выставить статус.
4. Если {num} не передан, /test/{id}/question методом GET выдать первый из
неотвеченных вопросов.
5. При запросе /test/{id}/question методом POST выдать первый из неотвеченных
вопросов, сохранить результат и сделать редирект на /test/{id}/question/{num}
где {num} это номер по порядку следующего неотвеченного вопроса.
6. При запросе /test/{id}/question/{num} метдом GET выдать вопрос {num}.
7. ... методом POST сохранить ответ и редирект /test/{id}/question/{num}.
8. Если нет следующего вопроса, проверить, есть ли у попытки пропущенные вопросы.
8.1. Если есть пропущенные, редирект на /test/{id}/confirm, где спросить
подтверждение завершения теста либо ссылку на /test/{id}/question (без параметра
{num}, cмотри пункт 4).
8.2. Если нет пропущенных, редирект на /test/result/{attempt}.
Если мы размещаем <label>asfa</label><input type="radio">
То проблему решают соседние селекторы, загугли
изменил, теперь при нажатии на label радиобаттон не устанавливается в положение checked
https://jsfiddle.net/g3333rfb/1/
$query = " SELECT t1.,t2.,t3.* FROM auctions_start t1 LEFT JOIN auctions_img t2 ON t1.auc_id = t2.a LEFT JOIN auctions t3 ON t1.auc_id = t3.id WHERE t3.category=$cat_id LIMIT $start,$size";
пиздец просто и за такое говно кто-то платит.
так у меня изначально и были лейблы с атрибутом for, но в задании требуется их не использовать
90% внутренностей интернета выглядит именно так. Радуйся тому, что хотя бы контроллеры и модели есть.
>>46693
Ты забыл дописать "быстро!".
>>46699
Без __DIR__ могут возникнуть проблемы при переносе кода между различными ОС, не вижу ничего плохого в нём.
>Мне показалось, что не очень красиво если будет "CSRFToken".
Пиши CsrfToken и не парься.
http://baron-fon-kolt.github.io
Двачик, а как с помощью PHP определить, на какой именно странице сайта находится пользователь ?
Дописал, теперь все работает как нужно, но код выглядит как земля с многоэтажными ифами и редиректами.
Нужны советы по рефакторингу.
https://github.com/nsdvw/TestHub
Теперь в тесте можно пропускать вопросы, возвращаться к пропущенным, причем оно само перекидывает именно на те вопросы, которые остались без ответа, пользователю не нужно сто раз нажимать ссылку вперед/назад, чтобы добраться до вопроса, как предлагалось в изначальном задании.
Вариант с js мне не нравится, хотя бы потому что сайт должен работать и без этой безусловно очень полезной технологии. Потом сверху обмажу "красотами", хотя как по мне js вообще не нужен.
Дня 3 только обдумывал как сделать, переписывал несколько раз. Как вообще по времени в нормальной конторе?
Потому что я работал только в шарагах, где продуктивность труда оценивалась в кол-ве набранных на клавиатуре букв.
Короче, у меня трудности в проектировании архитектуры приложения, тогда как технологии осваиваю быстро (доки симфони открыл первый раз недели 2 назад, сейчас сносно ориентируюсь, и вообще этот фреймворк мне нравится гораздо больше чем корявый юи), четкие и понятные задачи тоже реализую с удовольствием и без проблем.
Но я все-таки думаю, что в хорошей конторе действительно будет хотя бы командная работа, то есть как минимум проблемы архитектуры и оптимизации должны обсуждаться командой, плюс в конце концов опытные старшие руководители должны типа руководить, иначе нафиг они нужны вообще.
Займусь пока изучением юнит-тестов, потому что эта байда меня вконец вымотала.
>90% внутренностей интернета выглядит именно так
Но это же кошмар какой-то, мы тут сидим выдрачиваем стройную логику МВК, читаем про паттерны работы с базой данной и все равно без работы сидим, а они хуяк-хуяк и гребут бабки лопатой за такое вот неработающее говно, которое потом хуй кто разберет.
А можно ли отправлять такие запросы ?
$db->query("SELECT * FROM lobby WHERE link = 'substr($_SERVER[REQUEST_URI], 1)' ");
Я верю в то, что по-настоящему квалифицированный специалист будет обязательно востребованным. И такой специалист будет решать сложные задачи, используя паттерны, MVC и прочие непростые в освоении, но мощные штуки, а "кодеры" так и будут клепать однотипные лендинги на CMS/Самописном процедурном корявом движке до конца своих дней и разгребать говно за другими. В то время как ты сможешь без проблем раскурить сложный код другого специалиста, легко его расширить, ведь ты знаешь как, а гибкая архитектура этого приложения позволяет это сделать.
Ребята, кто вам просто так доверит это всё - вы хотя бы об этом подумайте.
Специалист-шмециалист.
Во всяком деле, касающемся производства, либо создания интернет-магазина, либо создания просто портала под игори, надо смотреть на целевую аудиторию и сам спрос.
Штука-то в том, что спрос среди кодеров на РНР как раз и состоит в том, что надо CMS/Самопис поковырять вилкой, лендинг-шмендинг состряпать и так далее.
Это тщета и суета сует.
НО!
Пока вы сами не начнёте что-то делать для себя, пока не поднимите свой проект какой-нибудь.
Свободного или малоконкурентного и по сей день полно.
Например, на каком-то корявом самописе сделан сайт vsezverushki.ru (как-то его упоминал как раз в подобном контексте десяток тредов назад). Там посещаемость-то всего 2к в сутки, но доход - несколько тысяч рублей в сутки же. Сам сайт продавался на Телдери за несколько миллионов (ОП тогда сказал, что в итоге не купили его, но предлагали-то почти 2 миллиона в несколько ставок).
Вот к подобному надо стремиться, а не к тому, что придёт дядя, оценит, даст работу.
Кстати, дядя охотнее оценит и даст нормальную работу именно при наличии каких-либо проектов - это общее место уже.
Согласен с этим. Мужик правду говорит.
>Кстати, дядя охотнее оценит и даст нормальную работу именно при наличии каких-либо проектов - это общее место уже.
Работодатель отдаст предпочтение конченному аутисту, что двух слов связать на собеседовании не смог, чем амбициозному челу с каким-то там проектами в интернетах. Первый будет горбатиться за копейки и ссать прибавку к зп попросить, второй отработает годок и свалит на лучший офер в свой наебизнес
С аутистом тоже риски есть, что он уйдет в свой внутренний мир и ничего полезного по работе не сделает.
У меня пара коллег в офисе из таких вот, там другая проблема. Они если чего не знают или не поймут что им сказали, то будут весь день ебать гугл, вместо того чтоб спросить/уточнить и тем самым заваливают все сроки. И ничего тут не поделать, если к нему подойти и попытаться как-то помочь, тот будет потеть фонтаном и кивать головой, мол я все понял ток оставь меня в покое.
Код у них хороший. Один с 2010, другой с 2007 работает. Платят им на уровне джуниоров, обильно штрафуя по всякой хуйне, на что те и не против совсем.
http://php.net/manual/ru/language.operators.precedence.php
У операторов сравнения приоритет больше, чем у логических.
Если в Yii2 базовые знания есть, то пойдет, я думаю.
И правильно будет Портфолио Junior разработчика.
if ($a && $b > 0)
Ты хотел написать "если обе а и б больше 0"? Язык программирования это не человеческий язык и компьютер понимает это выражение совсем по-другому:
if (($a) && ($b > 0 ))
"если а не равно нулю И б больше нуля"
То что тебе надо, пишется как ($a > 0) && ($b > 0)
дело в том что > это логический оператор. Он сравнивает числа и возвращает true или false. И то что ты написал, понимается совсем по-другому. И кстати && это тоже оператор который принимает 2 логических значения false или true и возвращает true только если справа и слева от него стоит true.
http://php.net/manual/ru/language.types.boolean.php
https://ru.wikipedia.org/wiki/Логический_тип
http://php.net/manual/ru/language.operators.logical.php
https://github.com/foobar1643/filehosting
Все прошлые замечания исправил, пофиксил проблему с выводом несуществующих результатов, прикрутил опциональный x-sendfile (и x-accel для nginx), так же исправил проблему с относительными путями.
Вот этот костыль https://github.com/foobar1643/filehosting/blob/master/app/Model/File.php#L110 так и остался, я просто не понимаю как это сделать без цикла. Нужно из id файла получить целое число, которое будет именем папки, в которой этот файл хранится (100, 200, 300 и т.д.).
Еще ОП писал что video.js слишком тяжелый и нужно бы взять другой плеер, я погуглил и нормальных легких плееров не нашел. Почти все бесплатные плееры занимают места от 200кб.
Дальше планирую переделать загрузку файлов с поддержкой перетаскивания, и фоллбэки для пользователей с отключенным жс в комментариях.
>Нужно из id файла получить целое число, которое будет именем папки, в которой этот файл хранится (100, 200, 300 и т.д.).
>public function getFolder()
>{
>for($i = 100; $i < 100000000; $i += 100) {
>if(($this->id / $i) <= 1) return $i;
>}
>return false;
>}
Больной ублюдок.
> Один с 2010, другой с 2007 работает
> Платят им на уровне джуниоров
Пиздец! Омега, он и в программировании останется омегой
Анон, как сделать совпадения в рандомном порядке?
Нужно чтобы скобки, тире и пробелы выделялись независимо от того в каком порядке они стоят по отношению друг к другу.
$obj = $repo->find($id);
Если нужно обратиться к какому-то свойству или методу объекта, делать вложенные ифы для проверки на null, или корректно будет записать в
одном условии типа:
if ($obj === null or $obj->getSmth() === 100) {/.../}
Я проверял, если первый операнд слева от OR вернет true, то второй никогда не выполнится, то есть здесь формально ошибки нет, но выглядит как-то не очень. Правильно так писать, или может сделать вложенный иф?
$base = array("orange", "banana", "apple", "raspberry");
$replacements = array(0 => "pineapple", 4 => "cherry");
$replacements2 = array(0 => "grape");
$basket = array_replace($base, $replacements, replacements2);
echo $basket;
В последнее время все чаще вижу, когда нехилый кусок логики приложения живет в комментариях, например аннотации в доктрине/симфони/юниттестах.
Было бы логично уже сделать оф.поддержку таких конструкций, если все ими пользуются.
>>45160
>На основе чего ты сделал такой вывод?
Личный опыт, плюс одна бабка на дваче сказала.
Вот еще кулстори от бабки
>>47393
про команду из заторможенных и взаимодействие с менеджерами уровня "если что-то будет непонятно спрашивайте)))))".
Не знаю, может я живу в каком-то маня-идеалистичном мире, но мне видится хорошее командное взаимодействие приблизительно так.
Каждый день тим собирается на совещание, минимум час. Пока все проснутся, перекусят, подтянутся опоздуны.
На совещании рассматривается: очередь и приоритеты новых задач, обсуждаются способы реализации конкретных глобальных задач, распределяются мелкие тикеты,
каждый в обязательном порядке отчитывается за прошедший день, может (и должен) выставить на рассмотрение свою проблему/затруднение, если на чем-то завис.
В конце двухнедельного срока большое совещание, 3-4 часа. Ни одна мелочь или проблема не должна быть упущена или оставлена на самотек. Каждый член команды
должен четко представлять, чего от него хотят, и как это релизовать.
Короче я вижу идеальной схему команды с опытным руководителем, в обязанности которого внезапно входит в первую очередь контроль над работой команды, распределение задач, контроль за исполнением, принятие главных решений в архитектуре (например спроектировать бд и набросать uml-схему основных классов), а также важные переговоры с бизнесом.
То есть тимлид (или как это называется), который выполняет функции диспетчера, но почти не пишет код, и команда ответственных исполнителей, которые с удовольствием реализуют грамотно поставленное тимлидом тз.
Не должно быть такого, что сидит группа аутистов, и манагеры им кидают бестолковые тз от неграмотного заказчика уровня "сайт не работает, сделайте чтобы работал".
Я был подобной шараге, приходилось с боем выбивать контакты заказчиков, чтобы самостоятельно черт побери выяснять подробности.
Несколько неплохих заказчиков кстати осталось с того времени, подкидывают изредка немножко работы в обход той шараги.
В последнее время все чаще вижу, когда нехилый кусок логики приложения живет в комментариях, например аннотации в доктрине/симфони/юниттестах.
Было бы логично уже сделать оф.поддержку таких конструкций, если все ими пользуются.
>>45160
>На основе чего ты сделал такой вывод?
Личный опыт, плюс одна бабка на дваче сказала.
Вот еще кулстори от бабки
>>47393
про команду из заторможенных и взаимодействие с менеджерами уровня "если что-то будет непонятно спрашивайте)))))".
Не знаю, может я живу в каком-то маня-идеалистичном мире, но мне видится хорошее командное взаимодействие приблизительно так.
Каждый день тим собирается на совещание, минимум час. Пока все проснутся, перекусят, подтянутся опоздуны.
На совещании рассматривается: очередь и приоритеты новых задач, обсуждаются способы реализации конкретных глобальных задач, распределяются мелкие тикеты,
каждый в обязательном порядке отчитывается за прошедший день, может (и должен) выставить на рассмотрение свою проблему/затруднение, если на чем-то завис.
В конце двухнедельного срока большое совещание, 3-4 часа. Ни одна мелочь или проблема не должна быть упущена или оставлена на самотек. Каждый член команды
должен четко представлять, чего от него хотят, и как это релизовать.
Короче я вижу идеальной схему команды с опытным руководителем, в обязанности которого внезапно входит в первую очередь контроль над работой команды, распределение задач, контроль за исполнением, принятие главных решений в архитектуре (например спроектировать бд и набросать uml-схему основных классов), а также важные переговоры с бизнесом.
То есть тимлид (или как это называется), который выполняет функции диспетчера, но почти не пишет код, и команда ответственных исполнителей, которые с удовольствием реализуют грамотно поставленное тимлидом тз.
Не должно быть такого, что сидит группа аутистов, и манагеры им кидают бестолковые тз от неграмотного заказчика уровня "сайт не работает, сделайте чтобы работал".
Я был подобной шараге, приходилось с боем выбивать контакты заказчиков, чтобы самостоятельно черт побери выяснять подробности.
Несколько неплохих заказчиков кстати осталось с того времени, подкидывают изредка немножко работы в обход той шараги.
>Каждый день тим собирается на совещание, минимум час
Предлагаю собираться не один раз в день, а восемь. И увеличить время совещаний минимум в полтора раза. При таком раскладе командное взаимодействие станет еще более лучшим.
Если просишь помощи, нужно указать полную информацию.
Сделай скриншот своего кода (или выложи ссылку на ideone.com), скриншот сообщения об ошибке.
Желательно еще версию php (и сборки).
print_r пишется с маленькой буквы (регистр названий функций в php не имеет значения, но так принято)
>>47695
Согласен. В течение всего остального рабочего дня тоже можно и нужно обращаться за помощью, а не сидеть молча каждый в своем углу.
Опчек (если это ты), а почему ты против?
Разве тебе самому не профитно было бы занять место такого руководителя-организатора, от которого только и требуется, что раздавать задачи, проверять говнокод и отвечать на вопросы умственно отсталых (ты же этим в треде занимаешься, прикинь еще большие деньги будут платить, а самому не нужно заниматься рутинной работой типа чистки горячо любимого друпаля).
Хотя возможно бизнесу такая схема невыгодна, не знаю.
>array_replace
> (PHP 5 >= 5.3.0, PHP 7)
http://php.net/manual/ru/function.array-replace.php
Эта функция доступная только с версии 5.3
Альтернативы не знаю, нужно свою писать
http://stackoverflow.com/questions/6198679/array-replace-alternatives-for-earlier-version-of-php
Ну и как бы обновиться пора. PHP 7 уже на полном ходу, работает раза в 2 быстрее чем пятерка.
Постом ниже чего? После поста саркастичного господина тоже мой потс.
Я думал то оп бомбанул от предложения час в день тратить на детальный разбор полетов, типа он говорил что ему и так времени на приготовить пожрать даже не хватает.
Так увеличение рабочего дня коснется только джунов, они должны быть только за, я бы например не против 9-часового дня, лишь бы была взаимопомощь и возможность учиться у опытных коллег.
Как раз для начальства рабочий день может быть и ограничится совещанием, а потом в свободном режиме руководитель может заняться код-ревью джуниоров, подумать над сложным вопросом архитектуры, который не по силенкам для джунов. Это можно сделать и дома например. Да и конференцию можно проводить по скайпу, ради бога.
Я к тому, что опытный квалифицированный чел должен быть избавлен от рутинной работы, ему поручить сложные общие вопросы типа архитектуры, алгоритма работы какой-то нестандартной фичи, а также код-ревью.
Джуны в свою очередь должны быть избавлены от мозгоебли теми проблемами, которые им реально не по силам, или занимают слишком много времени.
Само собой они должны вникать в детали текущего проекта и изучать его, таким образом прокачивать скиллы, но делать это в фоновом режиме, а не в авральном, когда давит ответственность, а ты нихуя не понимаешь что делать.
>Ты в тимлиды метишь?
Не, руководитель это по определению чувак с многолетним опытом, прошедший через все круги ада.
У меня реального опыта меньше полгода в мухосранских веб-студиях. Плюс еще год в треде (что принесло намного больше опыта и знаний).
var_dump() сделай, есть ли там вообще что-то.
Надо использовать не метод тыка, а логи. Если php запущен под апачом то смотреть логи ошибок апача. Ты зря тратишь время, и хорошо, если свое, а не работодателя.
>>47723
5.2 безнадежное старье
>>47709
Это не я. На работу где не надо ничего не делать, меня вряд ли кто пустит. А типичный старший программист обычно кроме руководства, сам кодит в поте лица. С Друпалом не работал уже давно, хотя он довольно простой, с ним работа это скорее как отдых будет.
>>47671
> Не планируют ли вводить официальную поддержку синтаксических конструкций в комментариях?
Это с Явы взято, там аннотации это часть языка. В PHP (пока?) нет.
Каждый день тратить час всей команды на митинг расточительно и дорого. Лучше 15-минутный стендап раз в неделю. Новички могут общаться с надсмотрщиками и вне митинга.
>>47612
Выглядит не очень. Лучше бы if ($obj && $obj->...) или вложенный иф.
>>47458
Квадратные скобки
>>47456
Может у них просто другие интересы. Хотя если развиваться, мне кажется за 9 лет можно стать суперсеньором, нет?
>>47436
С такими комментариями лучше в /b - там твои братья обитают.
Надо использовать не метод тыка, а логи. Если php запущен под апачом то смотреть логи ошибок апача. Ты зря тратишь время, и хорошо, если свое, а не работодателя.
>>47723
5.2 безнадежное старье
>>47709
Это не я. На работу где не надо ничего не делать, меня вряд ли кто пустит. А типичный старший программист обычно кроме руководства, сам кодит в поте лица. С Друпалом не работал уже давно, хотя он довольно простой, с ним работа это скорее как отдых будет.
>>47671
> Не планируют ли вводить официальную поддержку синтаксических конструкций в комментариях?
Это с Явы взято, там аннотации это часть языка. В PHP (пока?) нет.
Каждый день тратить час всей команды на митинг расточительно и дорого. Лучше 15-минутный стендап раз в неделю. Новички могут общаться с надсмотрщиками и вне митинга.
>>47612
Выглядит не очень. Лучше бы if ($obj && $obj->...) или вложенный иф.
>>47458
Квадратные скобки
>>47456
Может у них просто другие интересы. Хотя если развиваться, мне кажется за 9 лет можно стать суперсеньором, нет?
>>47436
С такими комментариями лучше в /b - там твои братья обитают.
> я просто не понимаю как это сделать без цикла. Нужно из id файла получить целое число, которое будет именем папки, в которой этот файл хранится (100, 200, 300 и т.д.).
Поделить id на 100?
Не сталкивался близко, его вообще где-то за пределом университетов и селектела используют?
>>47269
Делать свои проекты конечно можно но гарантий что ты хоть что-то заработаешь нету. Сайт который ты приводишь в пример, ничего не значит - одно дело если ьы его реально купили за какие-то деньги, другое дело это оценка которую робот берет с потолка и ставка, возможно от самого же владельца сайта чтобы поднять цену.
И вообще, насколько я знаю, сайт это обычно дополнение и инструмент для какого-то бизнеса, а не бизнес сам по себе. Ну например сайт авиасейлз это лишь витрина конторы по продаже билетов, сам понимаешь, что кроме программирования там очень много того, что не видно: реклама, продвижение и тд.
Конечно можно какую-то незанятую нишу найти но сидеть в ней ты будешь ровно до того момента как кто-то возьмется за нее всерьез.
Ад какой-то. Кто тебя научил подставлять данные, да еще и пришедшие от пользователя, напрямую в SQL запрос? Иди читай про плейсхолдеры.
И странно что у тебя смешан код работы с базой и работа с внешними переменными.
То он жаловался, что ему прислали чистить такой код.
Я еще и не такое видел. В первом yii иногда принято сохранять куски php-кода в базу например.
Я вяло занимался контекстной рекламой несколько лет, продал 5 сайтов за довольно неплохие деньги. Могу оценить суть сайта, его примерный доход от реального размещения рекламы. На тех зверюшках 200р или около того в сутки стоит разместить объявление о продаже. Объявление появляется только на главной, сами зверята стоят во много раз больше, это выгодно питомникам/заводчикам: 4к заплатил за сутки за всю главную - купили щенка за 25к - к примеру, всё окупилось в несколько раз.
Сайт знаком заводчикам и питомникам, тут ты прав, без этого многое не возможно было бы.
Но делали его простые энтузиасты - потому что на безрыбье и рак рыба.
Я не призывал эксплуатировать ниши, но только делать годные проекты для себя, людей и, в том числе, для работодателей, чтобы они заметили тебя.
http://www.telderi.ru/ru/viewsite/198901 - вот этот лот на Телдери. Там ставки делают реальные люди, это не система оценивает, было около 5 ставок, насколько вспоминаю сейчас, шаг в 200к рублей или около того. Система такое оценить не сможет - реальные люди платят за свои объявления, это не контекстная реклама, от кликов по которой вебмастеру перепадают крохи.
И суть в том, что сайт этот только на Москву рассчитан. Остальные города, наверное, на Авито и аналогах как-то рекламируются.
Просто в качестве примера это всё приводил.
Так в оф.доках рекомендуют делать
http://www.yiiframework.com/doc/guide/1.1/ru/topics.auth#sec-9
Я имею ввиду использование "бизнес-правил".
Второй юи вроде хорошо почистили.
>>47853
Что вы все тут такие хомяки непуганные?
У меня негативный опыт планерок. Они отнимают время у тех кто работает. Для менеджера проекта есть СРМ с текущими задачами всех исполнителей. В образовательных целях стоит проводить семинары или лекции, в свободное от работы время.
Идеальная планерка по моему мнению, выглядит так: для каждого человека достаточно 2-5 минуты, что бы он отчитался по сделанной работе, обозначил свои цели на ближайшее время, и ответил на вопросы. 5-7 активных участников не больше. И того от 10 до 35 минут. Проводится 29 февраля. Служит целям сплочения коллектива.
>Что вы все тут такие хомяки непуганные?
Веди себя прилично. На анонимной борде оценивается характер сообщения. И вообще веди себя культурно в культурном обществе.
Мать твою ебал.
Расскажи про сайты, которые ты продал? Какая тематика? Где брал контент, как раскручивал? Где продал?
У меня тож было пару сайтов, японская косметика, % с продаж, до обвала рубля приносили стабильный профит, но продать у меня не получилось не один из них, даже ставок не было. А MFA на торговой площадке (возможно талбери даже) расходились как горячие прижки. Партнер купил один из таких сайтов, через пару недель обвалилась посещаемость, а чуть позже его забанил адсенс.
Почему ты пишешь как ОП отступая одну строку после ответа? Ты можешь ввести в заблуждение новичков в нашем треде.
Лол, здесь много кто так пишет.
Мне кажется погромистам нужно не на статейники смотреть, а писать сервисы или модули для популярных кмсок.
У меня есть идея для сервиса,
сделать такой сайт, в который ты вводишь номер своего поста, а он проверяет тред каждые 5 минут, и если тебе ответили на сообщение, высылает на почту фото котика или смс в скайп, еще до конца на определился.
Бывает зарегистрируешься на форуме, чтобы только один вопрос задать, а потом забудешь.
Даже название придумал - ЕНОТ НАПОМИНУН
Я уверен такое расширение уже есть, но найти не могу, смотрел по запросам "wakaba", "answer cheker" и "post checker". Как еще можно поискать?
>Квадратные скобки
Не понял тебя.
Квадратные скобки не помогли:
https://regex101.com/r/tL6gZ3/4
Зато переделал с квантификатором после группы и получилось:
https://regex101.com/r/tL6gZ3/5
Храню в классе Student инфу по каждому студенту: имя, фамилия и и.т.д.
Мне нужно запилить регистрацию и авторизацию. Для этого мне нужно хранить для каждого студента хеш. Один экземпляр в БД, другой у студента в куках.
1. Логично ли добавить столбец hash в таблицу students? Или сделать отдельную таблицу user со столбцами student_id и hash?
2. Соответственно, хранить ли свойство hash в классе Student или создать новый класс User?
Самая разнообразная тематика, но всё связано с реальным производством: бетонные изделия, металлический прокат, сантехника, авто - куча всего.
Сайты на простом WP все до единого.
Контент заказывал на text.ru и ещё у пары знакомых анонов, которых нашёл в /wrk (там тред про копирайтинг).
Не раскручивал никак, только статьи делал, много статей под семантическое ядро.
В среднем сайты продавал по 30к рублей (при этом на создание тратил тысячи 3 от силы), один продал за 100к, о чём пожалел через полгода, потому что у сайта посещалка выросла в три раза, хотя новый владелец ничего не делал ради этого (насколько я мог судить, проверяя бэклинки и кол-во страниц, просто сам сайт).
Спалить урлы не могу - всё это есть на Телдери, не хочу подставлять купивших, там все АДсенсом монетизируют и прямыми рекламодателями.
МФА - когда это годные статейники по Пузату, Фсео и так далее из инфобизнесменов - на Телдери разлетаются как пирожки даже за блиц. Я тоже по их технологии делал (ну, куча структурированных статей, картиночки, видосики, перелинковка, уникальный шаблон, даже маскота делал для одного сайта).
>Партнер купил один из таких сайтов, через пару недель обвалилась посещаемость, а чуть позже его забанил адсенс.
Накручивают, гады, очень много накручивают. Я думал, гораздо меньше будут накручивать. Я когда на лоты смотреееел...
По многим признакам можно определить, что накрутка есть. Часто несуразное соотношение траф/доход. Часто из гугла 90% трафа, а из яндекса всего 5% - или наоборот.
Проблема в том, что в рунете контекстка отмирает: как доллар взлетел, так доходы снизились, кризис коснулся мелкого бизнеса, они перестали сливать кучу денег на рекламу.
Вообще значение рекламы крайне преувеличено - мои наблюдения на одном сайте, на котором у меня прямые рекламодатели.
Там заходят по сочным коммерческим запросам, а выхлоп почти нулевой для реклов. Так, из-за имиджа только и платят мне за площадку, даже стыдно.
Надоело всё это - до жути.
>>47902
Неистово двачую!
Поэтому я и здесь.
Самая разнообразная тематика, но всё связано с реальным производством: бетонные изделия, металлический прокат, сантехника, авто - куча всего.
Сайты на простом WP все до единого.
Контент заказывал на text.ru и ещё у пары знакомых анонов, которых нашёл в /wrk (там тред про копирайтинг).
Не раскручивал никак, только статьи делал, много статей под семантическое ядро.
В среднем сайты продавал по 30к рублей (при этом на создание тратил тысячи 3 от силы), один продал за 100к, о чём пожалел через полгода, потому что у сайта посещалка выросла в три раза, хотя новый владелец ничего не делал ради этого (насколько я мог судить, проверяя бэклинки и кол-во страниц, просто сам сайт).
Спалить урлы не могу - всё это есть на Телдери, не хочу подставлять купивших, там все АДсенсом монетизируют и прямыми рекламодателями.
МФА - когда это годные статейники по Пузату, Фсео и так далее из инфобизнесменов - на Телдери разлетаются как пирожки даже за блиц. Я тоже по их технологии делал (ну, куча структурированных статей, картиночки, видосики, перелинковка, уникальный шаблон, даже маскота делал для одного сайта).
>Партнер купил один из таких сайтов, через пару недель обвалилась посещаемость, а чуть позже его забанил адсенс.
Накручивают, гады, очень много накручивают. Я думал, гораздо меньше будут накручивать. Я когда на лоты смотреееел...
По многим признакам можно определить, что накрутка есть. Часто несуразное соотношение траф/доход. Часто из гугла 90% трафа, а из яндекса всего 5% - или наоборот.
Проблема в том, что в рунете контекстка отмирает: как доллар взлетел, так доходы снизились, кризис коснулся мелкого бизнеса, они перестали сливать кучу денег на рекламу.
Вообще значение рекламы крайне преувеличено - мои наблюдения на одном сайте, на котором у меня прямые рекламодатели.
Там заходят по сочным коммерческим запросам, а выхлоп почти нулевой для реклов. Так, из-за имиджа только и платят мне за площадку, даже стыдно.
Надоело всё это - до жути.
>>47902
Неистово двачую!
Поэтому я и здесь.
Предыдущие задачи вроде проверил
Задача про исправление ошибок
> foreach ($sentence as $key => $sent) {
Лучше было назвать $sentences и $sentence
Так, решено верно.
Йода
> return($result);
Скобки не нужны так как return это оператор, а не функция. В остальном, все верно.
>>38695
> 1)В каких случаях в MVC стоит создавать класс представления вместо обычного шаблона?
Практически ни в каких
> 2)Как сказать апачу что индекс.php у меня в папке public и ссылаться надо на него?
Ты не совсем так делаешь. В htaccess менять ничего не требуется. Тебе надо указать что корень сайта в папке public. Это надо делать в конфиге, в виндоуз это conf/httpd.conf в папке апача, в линукс это файлы в /etc/apache2. Там должна быть опция DocumentRoot, либо в самом конфиге, либо внутри VirtualHost. В ней надо прописать путь к папке, например:
<VirtualHost .... >
ServerName neko.local
DocumentRoot c:/web/example/public/
...
>>39718
Я не думаю что надо специально искать гайд по разработке ИМ, тебе нужны уроки по разработке веб приложений вообще. ООП, архитектура, работа с формами, базой данных. Ну или как альтернатива, ты можешь изучить какую-нибудь CMS, где магазины можно делать почти без программирования, но как только ты захочешь что-то нестандартное, начнутся сложности.
>>40120
> RewriteRule ^.$ [NC,L]
А что делает это правило?
Ну и слишком мало информации. Ты почему-то думаешь что дело в htaccess но оно модет быть и в чем то другом. На инклюды он никак повлиять не может.
Предыдущие задачи вроде проверил
Задача про исправление ошибок
> foreach ($sentence as $key => $sent) {
Лучше было назвать $sentences и $sentence
Так, решено верно.
Йода
> return($result);
Скобки не нужны так как return это оператор, а не функция. В остальном, все верно.
>>38695
> 1)В каких случаях в MVC стоит создавать класс представления вместо обычного шаблона?
Практически ни в каких
> 2)Как сказать апачу что индекс.php у меня в папке public и ссылаться надо на него?
Ты не совсем так делаешь. В htaccess менять ничего не требуется. Тебе надо указать что корень сайта в папке public. Это надо делать в конфиге, в виндоуз это conf/httpd.conf в папке апача, в линукс это файлы в /etc/apache2. Там должна быть опция DocumentRoot, либо в самом конфиге, либо внутри VirtualHost. В ней надо прописать путь к папке, например:
<VirtualHost .... >
ServerName neko.local
DocumentRoot c:/web/example/public/
...
>>39718
Я не думаю что надо специально искать гайд по разработке ИМ, тебе нужны уроки по разработке веб приложений вообще. ООП, архитектура, работа с формами, базой данных. Ну или как альтернатива, ты можешь изучить какую-нибудь CMS, где магазины можно делать почти без программирования, но как только ты захочешь что-то нестандартное, начнутся сложности.
>>40120
> RewriteRule ^.$ [NC,L]
А что делает это правило?
Ну и слишком мало информации. Ты почему-то думаешь что дело в htaccess но оно модет быть и в чем то другом. На инклюды он никак повлиять не может.
Читай документацию и исходный код. Принцип там примерно такой: ты даешь слово, он возвращает возможные варианты форм слова и признаки (в каком числе, падеже слово). Ну например на слово "мыла" он вернет:
- глагол "мыть", в форме "мама мыла раму"
- существительное "мыло" в форме "мыла больше нет"
- сущестительное "мыло" во множ. числе: "разные мыла"
Как видишь тут по слову сказать в каком оно числе, и существительное ли оно, нельзя. Надо либо как-то мириться с этим, либо анализировать связи между словами (делать синтаксический анализ, это отдельная сложная задача) и по ним определять какой тут вариант.
>>40213
Не может. Но ты должен помнить что сессии умирают через минут 20-30 неактивности так что это не лучшая идея использовать их для логина.
>>40441
Насчет миграций, я делаю так: написанные вручную на SQL миграции и аннотации в классах. Если я добавляю поле в таблицу, я делаю миграцию и добавляю поле с аннотацией в класс.
> Но в оф.доках к migrations bundle советуют как раз таки генерировать миграции автоматически на основе аннотаций.
Я сомневаюсь что это работает только в самых простых случаях. Не, SQL код, мне кажется, придется писать вручную.
>>41208
https://ideone.com/bUy2YN
> $creditSum -= $payout;
Это повторяется 2 раза, можно вынести наружу за цикл.
> if ($creditSum == 0) {
надежнее писать <= 0 на случай если мы ошибемся и поставим результат чуть меньше нуля.
Так, в общем, верно
https://ideone.com/PIkUhh
Нет привязки к концу строки: https://ideone.com/TIn4zU
https://ideone.com/UXs3Jd
Для знокаов вроде минусов и скобок лучше было использовать квадратные скобки, а у тебя заложен определенный порядок, например что минус идет после скобки. В остальном верно.
Читай документацию и исходный код. Принцип там примерно такой: ты даешь слово, он возвращает возможные варианты форм слова и признаки (в каком числе, падеже слово). Ну например на слово "мыла" он вернет:
- глагол "мыть", в форме "мама мыла раму"
- существительное "мыло" в форме "мыла больше нет"
- сущестительное "мыло" во множ. числе: "разные мыла"
Как видишь тут по слову сказать в каком оно числе, и существительное ли оно, нельзя. Надо либо как-то мириться с этим, либо анализировать связи между словами (делать синтаксический анализ, это отдельная сложная задача) и по ним определять какой тут вариант.
>>40213
Не может. Но ты должен помнить что сессии умирают через минут 20-30 неактивности так что это не лучшая идея использовать их для логина.
>>40441
Насчет миграций, я делаю так: написанные вручную на SQL миграции и аннотации в классах. Если я добавляю поле в таблицу, я делаю миграцию и добавляю поле с аннотацией в класс.
> Но в оф.доках к migrations bundle советуют как раз таки генерировать миграции автоматически на основе аннотаций.
Я сомневаюсь что это работает только в самых простых случаях. Не, SQL код, мне кажется, придется писать вручную.
>>41208
https://ideone.com/bUy2YN
> $creditSum -= $payout;
Это повторяется 2 раза, можно вынести наружу за цикл.
> if ($creditSum == 0) {
надежнее писать <= 0 на случай если мы ошибемся и поставим результат чуть меньше нуля.
Так, в общем, верно
https://ideone.com/PIkUhh
Нет привязки к концу строки: https://ideone.com/TIn4zU
https://ideone.com/UXs3Jd
Для знокаов вроде минусов и скобок лучше было использовать квадратные скобки, а у тебя заложен определенный порядок, например что минус идет после скобки. В остальном верно.
https://github.com/TheSidSpears/Students
> 2. Слышал о подходе, когда в классе все свойства закрытые, а обращаются к ним/изменяют через getter'ы/setter'ы. Нужно ли это?
Тут необязательно, а в больших приложениях може быть полезно, например ты можешь запретить менять некоторые поля или запретить записыват в них неправильные значения, поменяв лишь одну функцию.
https://github.com/TheSidSpears/Students/blob/master/config.json#L4
> "driver": "mysql",
В конфиг стоит выносить только то, что можно поменять, а тип базы данных вряд ли получится поменять, так как у разных БД немного разные диалекты SQL. Потому надо либо гарантировать что твое приложение работает с разными БД либо убрать тип БД из конфига.
> "tablename": "students"
Не советую это выносить в конфиг. Во-первых, в реальных приложениях таблиц много и конфиг разбухнет, во-вторых, это улсожняет код, там вместо имени таблицы будет стоять имя переменной, сложнее искать места где используется таблица.
Папку .idea надо убрать из репозитория, внести в gitignore и вернуть обратно (чтобы она не попала в репозиторий).
index.php - непонятно, зачем он, если он вне веб-папки и его вызвать нельзя.
> $config->db
Почему не массив? Это все равно не настоящий объект так как он не относится к какому-то классу. Лушче либо сделать правильный класс с нужными полями либо массив, так как для работы с массивом есть куча полезных функций.
> $token=md5(uniqid(rand(), true));
Что за нездоровое увлечение md5? Зачем он тут? Он не увеличивает число возможных вариантов хеша (даже чуть уменьшает). Не ставь функции наугад. uniqid испоьзует текущее время и потому плохо годится как источник случайных знаечний. Надо генерировать хеш из случайно выбранных символов.
> Попытка взлома. Без предупреждений перекидываем на главную
Не надо перекидывать. Надо либо вывести страницу ошибки с соответствующим HTTP кодом (например 403 или 400, поищи в википедии), либо выводить форму с заполненными значениями и сообщением об ошибке.
> foreach($form_data as $k=>&$s){
Эта переменная нигде не определена
https://github.com/TheSidSpears/Students/blob/master/public/register.php#L27
> $s=htmlspecialchars($s,ENT_QUOTES);
> $s=preg_replace("#((data|javascript)(://))#iu","",$s);
Это выглядит как код написанный наугад. Автор кода сам не понимает что он делает. Такой код я видел в некачественных учебниках и не стоит его копировать. Прочитай мой урок по XSS - там написано что htmlspecialchars пишется в шаблоне. Данные надо экранировать там, где они исопльзуются.
Вот например ты вырезаешь слово "яваскрипт". Но что, если у нас в форме есть поле "о себе" и пользователь хочет написать что он знаком с языком яваскрипт?
И еще кое-что. У меня есть такой урок и в нем задачка на экранирование: https://github.com/codedokode/pasta/blob/master/soft/web-server.md#Экранирование . Реши-ка ее.
> $check_student
Переменные пишутся кемелкейсом, checkStudent. Более того, они обычно не начинаются с глагола (кроме случаев типа $isValid).
> $check_student=new Student($form_data['name'],$form_data['sname'],$form_data['group_num'],$form_data['points'],$form_data['gender'],$form_data['email'],$form_data['b_year'],$form_data['is_resident']);
Слишком много аргументов. Невозможно пользоваться функцией у которой там много аргументов. Не больше 4-5 должно быть. В случае модели, там обычно вообще делают пучтой конструктор.
> $s=$check_student;
Название $s ничего не говорит.
> https://github.com/TheSidSpears/Students/blob/master/app/bootstrap.php
> Route::start();
Это не бутстрап. Скрипт инициализации должен только инициализировать приложение, подготовить нужные объекты, но не должен пытаться что-то роутить. То что у тебя называется front controller.
Кстати, тебе дополнительное задание. Сделай Cli скрипт который загружает по очереди всех студентов из БД, проверяет каждого на правильность и если есть ошибки, пишет id студента и подробности ошибок.
> function __autoload($className){
Это устаревшая функция. Надо переделать как описано в уроке https://github.com/codedokode/pasta/blob/master/php/autoload.md
> Route::start();
Тут явно не случай когда можно использовать статический метод. Статические методы используются редко, а именно:
- когда функции не нужен экземпляр объекта и $this. Когда функция работает одинаково для любого объекта.
- если это статический конструктор
Например, функции перевода сантиметров в дюймы очевидно объект не нужен, она всегда работает одинаково и потому делается статической. У тебя это не так. У тебя есть объект - роутер и есть метод который очевидно будет использовать $this.
> catch(Exception $e){
> echo "Исключение: ", $e->getMessage(),"\n";
Почитай про исключения https://github.com/codedokode/pasta/blob/master/php/exceptions.md
> $config=file('config.json',FILE_IGNORE_NEW_LINES);
> $config=implode('',$config);
Есть file_get_contents. Уже лет 10 как он появился.
>$config=json_decode($config);
Конфиг стоит декодировать в массив, а не в псевдообъект имитирующий массив.
> include('public/'.$controller.'.php');
Ты по моему не понял что такое папка public. перечитай примечания к задаче про студентов. Идея в том что мы делаем эту папку корнем сайта и соответственно все файлы снаружи нее недоступны напрямую. И наоборот, все, что положено в public, доступно пользователю.
Мне не нравится роутинг через гет параметры. Надо либо сделать либо такие URL:
- example.com/
- example.com/register.php
Либо например такие:
- example.com/register
https://github.com/TheSidSpears/Students/blob/master/app/class/Student.php
Класс сделан неудачно. Нельзя просто создать студента с незаполненными полями, надо передавать 10 аргументов, причем из порядок наизусть не запомнить.
> function __construct($db_params){
> //подключаемся к БД через PDO
Почитай урок про DI https://gist.github.com/codedokode/e1d31a31b37d5f635057
Соединяться с базой - это ответсвенность другого класса, а не gateway. gateway незачем иметь ссылку на конфиг так как разбирать конфиги - тоже не его задача. правильно когда каждый класс занимается своим делом, а у тебя код работы с конфигом например размазан по нескольким местам.
> $p=@$this->p; //для удобства
В наших задачах запрещается использовать @. Этот оператор не нужен. И я вообще не понимаю, зачем ты его тут поставил.
> function pdo_exec(
> function getStudents
Ты один делаешь проект и сам же не можешь выдержать единый стиль наименования функций.
> foreach($students as $k=>&$v){
Непонятно зачем тут & перед $v
https://github.com/TheSidSpears/Students/blob/master/app/class/StudentValidator.php#L32
В конструкторе не может быть return. Как, интересно, ты собрался получать результат вызова конструктора?
https://github.com/TheSidSpears/Students/blob/master/app/class/Route.php#L11
У тебя тут огромный try который содержит нескольког десятков строк, почти всю функицю. Это нехорошо, такие огромные конструкции, надо как-то переделать функцию чтобы он не был огромным.
Класс Route должен называться FrontController. Роутер только анализиурет URL и никого сам не вызывает.
https://github.com/TheSidSpears/Students
> 2. Слышал о подходе, когда в классе все свойства закрытые, а обращаются к ним/изменяют через getter'ы/setter'ы. Нужно ли это?
Тут необязательно, а в больших приложениях може быть полезно, например ты можешь запретить менять некоторые поля или запретить записыват в них неправильные значения, поменяв лишь одну функцию.
https://github.com/TheSidSpears/Students/blob/master/config.json#L4
> "driver": "mysql",
В конфиг стоит выносить только то, что можно поменять, а тип базы данных вряд ли получится поменять, так как у разных БД немного разные диалекты SQL. Потому надо либо гарантировать что твое приложение работает с разными БД либо убрать тип БД из конфига.
> "tablename": "students"
Не советую это выносить в конфиг. Во-первых, в реальных приложениях таблиц много и конфиг разбухнет, во-вторых, это улсожняет код, там вместо имени таблицы будет стоять имя переменной, сложнее искать места где используется таблица.
Папку .idea надо убрать из репозитория, внести в gitignore и вернуть обратно (чтобы она не попала в репозиторий).
index.php - непонятно, зачем он, если он вне веб-папки и его вызвать нельзя.
> $config->db
Почему не массив? Это все равно не настоящий объект так как он не относится к какому-то классу. Лушче либо сделать правильный класс с нужными полями либо массив, так как для работы с массивом есть куча полезных функций.
> $token=md5(uniqid(rand(), true));
Что за нездоровое увлечение md5? Зачем он тут? Он не увеличивает число возможных вариантов хеша (даже чуть уменьшает). Не ставь функции наугад. uniqid испоьзует текущее время и потому плохо годится как источник случайных знаечний. Надо генерировать хеш из случайно выбранных символов.
> Попытка взлома. Без предупреждений перекидываем на главную
Не надо перекидывать. Надо либо вывести страницу ошибки с соответствующим HTTP кодом (например 403 или 400, поищи в википедии), либо выводить форму с заполненными значениями и сообщением об ошибке.
> foreach($form_data as $k=>&$s){
Эта переменная нигде не определена
https://github.com/TheSidSpears/Students/blob/master/public/register.php#L27
> $s=htmlspecialchars($s,ENT_QUOTES);
> $s=preg_replace("#((data|javascript)(://))#iu","",$s);
Это выглядит как код написанный наугад. Автор кода сам не понимает что он делает. Такой код я видел в некачественных учебниках и не стоит его копировать. Прочитай мой урок по XSS - там написано что htmlspecialchars пишется в шаблоне. Данные надо экранировать там, где они исопльзуются.
Вот например ты вырезаешь слово "яваскрипт". Но что, если у нас в форме есть поле "о себе" и пользователь хочет написать что он знаком с языком яваскрипт?
И еще кое-что. У меня есть такой урок и в нем задачка на экранирование: https://github.com/codedokode/pasta/blob/master/soft/web-server.md#Экранирование . Реши-ка ее.
> $check_student
Переменные пишутся кемелкейсом, checkStudent. Более того, они обычно не начинаются с глагола (кроме случаев типа $isValid).
> $check_student=new Student($form_data['name'],$form_data['sname'],$form_data['group_num'],$form_data['points'],$form_data['gender'],$form_data['email'],$form_data['b_year'],$form_data['is_resident']);
Слишком много аргументов. Невозможно пользоваться функцией у которой там много аргументов. Не больше 4-5 должно быть. В случае модели, там обычно вообще делают пучтой конструктор.
> $s=$check_student;
Название $s ничего не говорит.
> https://github.com/TheSidSpears/Students/blob/master/app/bootstrap.php
> Route::start();
Это не бутстрап. Скрипт инициализации должен только инициализировать приложение, подготовить нужные объекты, но не должен пытаться что-то роутить. То что у тебя называется front controller.
Кстати, тебе дополнительное задание. Сделай Cli скрипт который загружает по очереди всех студентов из БД, проверяет каждого на правильность и если есть ошибки, пишет id студента и подробности ошибок.
> function __autoload($className){
Это устаревшая функция. Надо переделать как описано в уроке https://github.com/codedokode/pasta/blob/master/php/autoload.md
> Route::start();
Тут явно не случай когда можно использовать статический метод. Статические методы используются редко, а именно:
- когда функции не нужен экземпляр объекта и $this. Когда функция работает одинаково для любого объекта.
- если это статический конструктор
Например, функции перевода сантиметров в дюймы очевидно объект не нужен, она всегда работает одинаково и потому делается статической. У тебя это не так. У тебя есть объект - роутер и есть метод который очевидно будет использовать $this.
> catch(Exception $e){
> echo "Исключение: ", $e->getMessage(),"\n";
Почитай про исключения https://github.com/codedokode/pasta/blob/master/php/exceptions.md
> $config=file('config.json',FILE_IGNORE_NEW_LINES);
> $config=implode('',$config);
Есть file_get_contents. Уже лет 10 как он появился.
>$config=json_decode($config);
Конфиг стоит декодировать в массив, а не в псевдообъект имитирующий массив.
> include('public/'.$controller.'.php');
Ты по моему не понял что такое папка public. перечитай примечания к задаче про студентов. Идея в том что мы делаем эту папку корнем сайта и соответственно все файлы снаружи нее недоступны напрямую. И наоборот, все, что положено в public, доступно пользователю.
Мне не нравится роутинг через гет параметры. Надо либо сделать либо такие URL:
- example.com/
- example.com/register.php
Либо например такие:
- example.com/register
https://github.com/TheSidSpears/Students/blob/master/app/class/Student.php
Класс сделан неудачно. Нельзя просто создать студента с незаполненными полями, надо передавать 10 аргументов, причем из порядок наизусть не запомнить.
> function __construct($db_params){
> //подключаемся к БД через PDO
Почитай урок про DI https://gist.github.com/codedokode/e1d31a31b37d5f635057
Соединяться с базой - это ответсвенность другого класса, а не gateway. gateway незачем иметь ссылку на конфиг так как разбирать конфиги - тоже не его задача. правильно когда каждый класс занимается своим делом, а у тебя код работы с конфигом например размазан по нескольким местам.
> $p=@$this->p; //для удобства
В наших задачах запрещается использовать @. Этот оператор не нужен. И я вообще не понимаю, зачем ты его тут поставил.
> function pdo_exec(
> function getStudents
Ты один делаешь проект и сам же не можешь выдержать единый стиль наименования функций.
> foreach($students as $k=>&$v){
Непонятно зачем тут & перед $v
https://github.com/TheSidSpears/Students/blob/master/app/class/StudentValidator.php#L32
В конструкторе не может быть return. Как, интересно, ты собрался получать результат вызова конструктора?
https://github.com/TheSidSpears/Students/blob/master/app/class/Route.php#L11
У тебя тут огромный try который содержит нескольког десятков строк, почти всю функицю. Это нехорошо, такие огромные конструкции, надо как-то переделать функцию чтобы он не был огромным.
Класс Route должен называться FrontController. Роутер только анализиурет URL и никого сам не вызывает.
Что годного в SICP? Во-первых, если тебе интересен этот курс, можно взять оригинальный учебник с кодом на scheme: http://newstar.rinet.ru/~goga/sicp/sicp.pdf Я не очень понимаю, в чем выгода от замены scheme на PHP и урезания объема курса. Плюс, у тебя по моему просто нездоровая реакция на слово SICP. Увидев его, ты теряешь способность видеть содержание урока. Какой-то религиозный фанатизм.
Во-вторых, на хекслете курс больше по основам PHP. Там вроде бы нет уроков по работе с формами, по MVC, по исключениям, по особенностям ООП, всего того, что мы изучаем в студентах. Зато есть много всяких сторонних вещей вроде алгоритмов.
>>41605
Гикбрейнс использует грязные приемы. Они вешают на сайте рекламу вроде "вот как может выглядеть ваше резюме" с зарплатой 100 000 р при этом очевидно, что конечно прошедший их курсы может написать такую зарплату себе в резюме, но вряд ли кто-то из работодателей согласится на нее.
Их цель заманить такими обещаниями как можно больше людей, завысить им самооценку, взять с них деньги, а потом пусть мучаются как хотят.
Ну и другие трюки, когда складывается впечателние что достаточно пройти один курс, хоят на деле надо пройти минимум несколько.
Вот пример человека которому перезавысили самооценку https://www.youtube.com/watch?v=YrXJzD2E6NU
Это конечно хорошо что хекслет такое выкладывает, но это скорее против них играет. Самооценка это хорошо, но она должна соотноситься с реальным уровнем знаний, а там у человека не то что пробелы, а зияющие пустоты в знании основных технологий с которыми он (по его словам) работал.
ни в коем случае. Юзай подготовленные запросы с плей-холджераи
>>сессии умирают после 20-30 минут
Это поведение можно менять session_set_cookie_params
А что, по-твоему, было бы лучшей идеей для логина? Я пробую варинаты.
Гикбрейнс ужасен почти всем. Курсы никто не сопровождает - нет даже видимости обратной связи с автором. Не выкладываются исходники файлов в курсах - ну что за бред? Там - до кучи - какая-то жуткая система оповещений, сам сайт какой-то кривой.
Постоянно присылают какие-то заманухи про гуманитариев, ставших программистами, что-то такое, я не читаю.
После знакомства с парой бесплатных курсов создалось впечатление, что платные не лучше (постоянно там отзывы по ходу встречаются, что человек и на платном такое встречал, платные разочаровали и т.п.).
>>48106
А что там? Вроде в итоге справился с тестовым (я в самом конце только посмотрел, там вроде всё решено). Вроде как собеседование - надо же себя позиционировать кукареку! соответствующим образом.
Хренассе ты тролляка
Там ведь нет процедурщины, там по ООП всё!
Он там оговаривается в одном месте, что сначала разберём регистрацию и т.п. с точки зрения процедурщины, а потом пойдём по ООП, может, ты это увидел?
> $s=preg_replace("#((data|javascript)(://))#iu","",$s);
> что, если у нас в форме есть поле "о себе" и пользователь хочет написать что он знаком с языком яваскрипт?
Код удаляет "data://" и "javascript://", а вот строка "javascript" проходит на ура, что мне и требовалось
> Route::start();
> Это не бутстрап.
Мне тогда эту строку в public/index.php засунуть?
> Почитай про исключения
Ты про пользовательские классы исключений? Реализовал
> Класс сделан неудачно. Нельзя просто создать студента с незаполненными полями, надо передавать 10 аргументов, причем из порядок наизусть не запомнить.
Делаем пустой конструктор и фунцию addInfo($array). Так? Или много ф-ии типа addName, addGender...?
В общем я уже много чего исправил и еще доисправляю, и нового написал, можешь проверять: https://github.com/TheSidSpears/Students
Кстати, как правильно использовать гит? Типа внёс какое-то изменение в файл и сразу коммит пишешь, и так каждые 5 минут. А в конце push.
А то я часа 3 сижу пишу, а потом понимаю, что мне впадлу для каждого файла прописывать что я там менял, да и стоит ли?
>Ты не совсем так делаешь. В htaccess менять ничего не требуется. Тебе надо указать что корень сайта в папке public. Это надо делать в конфиге, в виндоуз это conf/httpd.conf в папке апача, в линукс это файлы в /etc/apache2. Там должна быть опция DocumentRoot, либо в самом конфиге, либо внутри VirtualHost. В ней надо прописать путь к папке, например:
Ок, получилось. Это делать каждый раз для разных приложений? Если у меня сейчас DocumentRoot "c:/Apache/htdocs/app1/public", то для другого проекта придется удалять это и писать свою папку?
> Ты про пользовательские классы исключений? Реализовал
нет, про то как надо делать страницу ошибки. Логгировать все, код правильный выдавать
> Кстати, как правильно использовать гит? Типа внёс какое-то изменение в файл и сразу коммит пишешь, и так каждые 5 минут. А в конце push.
зачем после каждого файла коммитить? Поменяй все что нужно и коммитить одним коммитом. Желательно чтобы все изменения относились например к исправлению одного и того же бага или добавлению новой фичи. Например добавил возможность регистрации - сделал коммит с соотв. сообщением.
> Делаем пустой конструктор и фунцию addInfo($array). Так?
Можно и так, но не забудь про белый список полей которые разрешено изменять.
> Мне тогда эту строку в public/index.php засунуть
Можно
> Код удаляет "data://" и "javascript://",
Хорошо, если пользователь хочет написать javascript:// ?
В апаче есть виртуальные хосты. Для нового проекта добавляется новый вирт. хост.
Тут чуть-чуть это упомянуто: https://github.com/codedokode/pasta/blob/master/soft/apache-install.md#q-Ок-Апач-работает-php-работает-Но-меня-напрягает-что-у-меня-только-один-хост--localhost-Я-бы-хотел-иметь-несколько-сайтов-на-компе-с-разными-адресами
Ну и погугли еще. Если что, задавай вопросы.
Ты тралируешь так?
Посмотри финальный проект книги. Там класическая процедурная лапша. Да даже она херово написана. Привет начало 2000-ных.
Я ещё не дошёл до конца, прост..
> Хорошо, если пользователь хочет написать javascript:// ?
> Если ты на сайте разрешаешь пользователям вводить ссылки (например, в разделе «О себе»), то надо проверять, что они начинаются с http:// или https:// . Иначе пользователь подсунет ссылку вида data://... или javascript:// .... при клике по которой выполнится код. Конечно, ему надо еще заманить других пользователей кликнуть по ней, но наверняка есть способы для этого. htmlspecialchars тут не спасет, но поможет проверка ссылки регулярным выражением на правильность.
Источник: https://github.com/codedokode/pasta/blob/master/security/xss.md Я это отсюда взял
Всё остальное понятно, исправляю постепенно, огромнейшее спасибо
Столько инфы, что каша в голове, и непонятно что собственно надо сделать.
Вот в случае этого TestHub, покрыть юнит-тестами только классы, которые отвечают за подсчет результатов теста и статистики прохождений?
Остальная логика содержится либо в контроллерах (надо бы отрефакторить и попытаться перетащить ее в сервисы), либо в сервисах, либо в репозиториях, и сводится к получению данных через entity manager sql запросом, dql или тупо $entity->getRelation()->getProperty(). Что тут собственно тестировать? Получение данных из бд и то что методы возвращают объект ожидаемого класса?
Допустим, методы сервисов и репозиториев не нужно покрывать тестами.
Возвращаясь к подсчету результатов и статистики: а эти методы должны брать dataSet (или как это называется) из базы данных, или можно подсунуть простой массив из dataProvider? Короче, тестировать здесь нужно именно логику подсчета результатов, или таки дополнительно проверить, что из бд возвращаются нужные данные?
Потому что когда в $char живет знак равенства в $op живет операнд умножения и потому последний elseif у тебя вообще не отрабатывает.
>я надеюсь все аноны которые у нас учатся, это могут. Если нет - срочно садитесь и пишите
Я немного опоздал конечно, но ради интереса решил попробовать.
http://ideone.com/xH6Eja Как мне показалось самый очевидный вариант.
Спасибо. Я как раз не сохранил это своё решение пару месяцев назад.
Все, я понял. Спасибо
В юникоде один символ может занимать больше одного байт, от 1 до 4, так что твой вариант работает только с латиницей (для которой существует strrev).
Правильное решение - через разбиение по пустому регулярному выражению с юникодным флажком.
Еще один интересный способ через конвертацию строки в кодировку с другим направлением байт, переворачиванием через strrev и обратная конвертация.
Вообще знать ответы на подобные вопросы нужно, но лучше избегать контор, где бездельники-эйчары изо всех сил стараются изобрести вопрос посложнее и побессмысленнее, чтобы унизить человека и самоутвердиться.
Скорее всего в такой конторе весь коллектив состоит из скучающих задротов, которые будут навязывать всякую новомодную хипсторскую поебень, вместо того чтобы просто хорошо делать свое дело.
На тему адекватных вопросов на собеседовании можно почитать Антона Шевчука http://anton.shevchuk.name/php/php-interview/
и даже книжка кажется есть со сборником вопросов https://www.kobzarev.com/wp-content/uploads/books/php/PHPbook.pdf
https://ideone.com/JQVunA
Поправил: там на 37 строке ошибка.
Но я делаю так: извлекаю всю инфу о пикче,
кладу ее в $image = mysqli_fetch_array($result);
дальше header('Content-type: image/jpeg; charset=utf-8');
и получение инфы из блоба
echo $image['image_data'];
Но нихуя не работает. Что локально на сервере, что на сайте (там та же фигня).
Это, анон, я имею ввиду, что я не ошибку поправил, а поправил указание на ошибку, я не разобрался с этой херней. Помогите, пожалуйста.
Короче, анончик, разобрался я.
У меня в файле с коннектом к базе данных был лишний вывод инфы, вроде "база данных подключена". Этот кал примешивался к выводу инфы из блоба и пикча ломалась.
>anton.shevchuk.name
Спасибо, но нет.
Это ж каким дятлом надо быть, чтобы на собственном домене сделать поддомен со своим именем?
> Каждое собеседование начинается с одного и того же вопроса:
> – Почему PHP?
> И, как ни странно, некоторые умудряются “засыпаться” уже на нем
Какой правильный ответ?
Я выбрал php потому что он самый популярный, на нем много заказов, и под него много библиотек и фреймворков.
Я люблю программирование, а язык только инструмент, и мне глубоко по-барабану на каком языке писать.
Было бы время, с удовольствием выучил бы новый, если это необходимо.
Не знаю, "правильный" ли это ответ, но я бы ответил так.
Часто важна не правильность ответа, а его адекватность.
Вот у тебя адекватный ответ.
А начнёшь дёргаться, говорить "похапе", "я хз" и подобное - будет не адекватный.
Это относится к тем данным которые в итоге оказываются внутри <a href="...">, img src=, iframe src=, то есть внутри определенных атрибутов тегов. Если данные выводятся как обычный текст, то проблемы нет - не забывай только про htmlspecialchars.
>>48421
Начнем с вопроса, зачем мы вообще пишем тесты? Очевидно, для того, чтобы автоматизировать процесс тестирования. Когда ты пишешь какую-то функцию, ты можешь захотеть проверить, что она работает корректно. Для этого ты скорее всего напишешь какой-то скрипт, который ее вызывает, выводит результаты и глазами смотришь, адекватные ли они. Юнит тест - это практически то же самое, только результаты проеряются автоматически. Автоматизация позволяет тебе экономить время и за счет этого увеличивать частоту тестирования. Например с автоматическими тестами ты можешь позволить себе запускать их после каждого коммита. Это позволяет обнаруживать ошибки как можно раньше.
Аналогично, если ты добавил на сайт новую страницу, ты можешь тестировать ее, открыв в браузере и смотря глазами, а можешь описать процесс тестирования с помощью функционального теста.
Может быть ты такого не видел, но в некоторых компаниях делают функциональные тесты вручную. Специально обученные люди каждую неделю тестируют приложение вручную по огромному списку со сценариями. Когда ты пишешь функциональные тесты, ты как бы программируешь работу этих людей.
Кстати самый простой и минимальный тест - это просто вызвать функцию или открыть страницу. даже такой тест позволит отлавливать определенные виды ошибок.
Также, тестами можно "защитить" определенный функционал от того, что его позже сломаешь ты или твой коллега. Некоторые люди идут еще дальше и предлагают рассматривать тесты как спецификацию (ТЗ) на функцию: ты сначала пишешь тесты, которыми определяешь требования к функции, а только потом саму функцию, подходящую под тесты. Это называется TDD.
Если проводить аналогию, допустим, приложения с ракетой. Юнит-тест это когда ты вынимаешь какой-нибудь клапан и тестируешь что он работает как надо. Интеграционный тест тестирует взаимодействие компонентов, это например когда мы ставим двигатель на стенд и тестируем его там. И функциональный тест проверяет что ракета может выполнять свои функции, то есть для ракеты функциональнй тест это запуск ее в космос.
Давай рассматривать все классы, которые у тебя есть в приложении, как некие сервисы. Вместе они предоставляют приложению что-то вроде внутреннего АПИ. Юнит-тестами ты проверяешь все функции этого АПИ. То есть в идеале юнит-тесты тестируют все публиные функции всех классов. Процент кода, выполняющийся в ходе тестов, называется coverage и его можно мерять.
Однако, написать тесты на все функции может потребовать много времени. Потому твоя задача расставить приоритеты и определить, как потратить свое время выгоднее, как с ограниченными затратами времени обеспечить максимальный coverage.
Потому стоит в первую очередь тестировать функции, содержающие какую-то сложную логику, функции, критичные для работы приложения. Примитивные функции типа сеттеров я считаю неприоритетными - там сломаться в принципе нечему. Также, часто не получится протестировать контроллеры юнит-тестами - но их хорошо покрывают функциональные тесты.
Так что начни с того, что посмотри на свои классы и функции и выбери наиболее важные и сложные.
Юнит-тесты это тесты, тестирующие одну функцию или класс отдельно от других. Они не используют базу данных (кстати здесь проявляется преимущество data mapper, мы можем использовать модели без базы данных). Файловую систему и тем более сеть тоже не стоит использовать, так как это все усложняет, делает тесты более хрупкими и увеличивает объем кода который мы тестируем.
Тестировать функции, использующие базу, можно так:
- юнит-тестами, используя моки вместо реального класса работы с БД. может быть трудоемко.
- интеграционными тестами, на специально подготовленной базе
- в рамках функциональных тестов
Вообще, тестирование, особенно пока ты начинающий, это отдельная задача, требующая творческого подхода. Не раз ты будешь задавать себе вопрос "а как это вообще можно протестировать?". Ты должен учитывать такие вещи:
- тесты должны быть 100% повторяемымыми. Избегай использования генератора случайных чисел или приводи его в заранее известное состояние
- тесты не должны быть хрупкими, то есть не должны давать случайных ошибок. Например тест испльзующий сеть, может давать ложные срабатывания из-за сетевых неполадок, тест использующий БД может падать если в ней нет нужных данных, или наоборот есть лишние. Потому для тестов надо делать максимально контролируемое окружение
- тест не должен завязыватсья на внутреннюю структуру кода. Например ты должен избегать обращений к приватным или защищенным полям, ты должен тестировать лишь конкретную функцию, так как при рефактоинге тест придется переделывать.
- отдельная проблема - привязка к строкам. Сегодня ты проверяешь наличие кнопки "зарегистрироваться", завтра ее переделывают на "присоединиться" и тест падает. Избегай привязки к строкам и сообщениям.
Также, читай статьи на хабре в блоге яндекса про тестирование - там много интересного.
Это относится к тем данным которые в итоге оказываются внутри <a href="...">, img src=, iframe src=, то есть внутри определенных атрибутов тегов. Если данные выводятся как обычный текст, то проблемы нет - не забывай только про htmlspecialchars.
>>48421
Начнем с вопроса, зачем мы вообще пишем тесты? Очевидно, для того, чтобы автоматизировать процесс тестирования. Когда ты пишешь какую-то функцию, ты можешь захотеть проверить, что она работает корректно. Для этого ты скорее всего напишешь какой-то скрипт, который ее вызывает, выводит результаты и глазами смотришь, адекватные ли они. Юнит тест - это практически то же самое, только результаты проеряются автоматически. Автоматизация позволяет тебе экономить время и за счет этого увеличивать частоту тестирования. Например с автоматическими тестами ты можешь позволить себе запускать их после каждого коммита. Это позволяет обнаруживать ошибки как можно раньше.
Аналогично, если ты добавил на сайт новую страницу, ты можешь тестировать ее, открыв в браузере и смотря глазами, а можешь описать процесс тестирования с помощью функционального теста.
Может быть ты такого не видел, но в некоторых компаниях делают функциональные тесты вручную. Специально обученные люди каждую неделю тестируют приложение вручную по огромному списку со сценариями. Когда ты пишешь функциональные тесты, ты как бы программируешь работу этих людей.
Кстати самый простой и минимальный тест - это просто вызвать функцию или открыть страницу. даже такой тест позволит отлавливать определенные виды ошибок.
Также, тестами можно "защитить" определенный функционал от того, что его позже сломаешь ты или твой коллега. Некоторые люди идут еще дальше и предлагают рассматривать тесты как спецификацию (ТЗ) на функцию: ты сначала пишешь тесты, которыми определяешь требования к функции, а только потом саму функцию, подходящую под тесты. Это называется TDD.
Если проводить аналогию, допустим, приложения с ракетой. Юнит-тест это когда ты вынимаешь какой-нибудь клапан и тестируешь что он работает как надо. Интеграционный тест тестирует взаимодействие компонентов, это например когда мы ставим двигатель на стенд и тестируем его там. И функциональный тест проверяет что ракета может выполнять свои функции, то есть для ракеты функциональнй тест это запуск ее в космос.
Давай рассматривать все классы, которые у тебя есть в приложении, как некие сервисы. Вместе они предоставляют приложению что-то вроде внутреннего АПИ. Юнит-тестами ты проверяешь все функции этого АПИ. То есть в идеале юнит-тесты тестируют все публиные функции всех классов. Процент кода, выполняющийся в ходе тестов, называется coverage и его можно мерять.
Однако, написать тесты на все функции может потребовать много времени. Потому твоя задача расставить приоритеты и определить, как потратить свое время выгоднее, как с ограниченными затратами времени обеспечить максимальный coverage.
Потому стоит в первую очередь тестировать функции, содержающие какую-то сложную логику, функции, критичные для работы приложения. Примитивные функции типа сеттеров я считаю неприоритетными - там сломаться в принципе нечему. Также, часто не получится протестировать контроллеры юнит-тестами - но их хорошо покрывают функциональные тесты.
Так что начни с того, что посмотри на свои классы и функции и выбери наиболее важные и сложные.
Юнит-тесты это тесты, тестирующие одну функцию или класс отдельно от других. Они не используют базу данных (кстати здесь проявляется преимущество data mapper, мы можем использовать модели без базы данных). Файловую систему и тем более сеть тоже не стоит использовать, так как это все усложняет, делает тесты более хрупкими и увеличивает объем кода который мы тестируем.
Тестировать функции, использующие базу, можно так:
- юнит-тестами, используя моки вместо реального класса работы с БД. может быть трудоемко.
- интеграционными тестами, на специально подготовленной базе
- в рамках функциональных тестов
Вообще, тестирование, особенно пока ты начинающий, это отдельная задача, требующая творческого подхода. Не раз ты будешь задавать себе вопрос "а как это вообще можно протестировать?". Ты должен учитывать такие вещи:
- тесты должны быть 100% повторяемымыми. Избегай использования генератора случайных чисел или приводи его в заранее известное состояние
- тесты не должны быть хрупкими, то есть не должны давать случайных ошибок. Например тест испльзующий сеть, может давать ложные срабатывания из-за сетевых неполадок, тест использующий БД может падать если в ней нет нужных данных, или наоборот есть лишние. Потому для тестов надо делать максимально контролируемое окружение
- тест не должен завязыватсья на внутреннюю структуру кода. Например ты должен избегать обращений к приватным или защищенным полям, ты должен тестировать лишь конкретную функцию, так как при рефактоинге тест придется переделывать.
- отдельная проблема - привязка к строкам. Сегодня ты проверяешь наличие кнопки "зарегистрироваться", завтра ее переделывают на "присоединиться" и тест падает. Избегай привязки к строкам и сообщениям.
Также, читай статьи на хабре в блоге яндекса про тестирование - там много интересного.
Ну давай пройдемся по коду и посмотрим что стоит тестировать юнит-тестами. Вот я открываю первый попавшийся файл: https://github.com/nsdvw/TestHub/blob/master/src/AppBundle/Helper/StringGenerator.php
Что мы тут видим? В глаза бросается бессмысленный вызов sha1, который только вхолостую тратит процессорные циклы и природный газ на электростанции, не выполняя никакой полезной функции. Ладно, сегодня мы пришли не за этим.
Мы видим 2 публичные функции, разницу между которыми я плохо понимаю:
- generateToken()
- generateString()
Так как они делают в принципе одно и тоже, то я выберу вторую функцию, ее имя мне кажется более подходящим тому, что она делает.
Итак, как протестировать generateString()? Надеюсь ты помнишь, что самый простой способ - это просто вызвать функцию. Этот способ отловит например программные ошибки внутри нее вроде бесконечного цикла или обращения к несущестсующей переменной. Пишем:
class StringGeneratorTest extends ...
public function testGenerateString()
{
StringGenerator::generateString();
}
Но конечно, раз уж мы потратили время на создание теста, давай протестируем ее чуть получше.
Попробуй ответить на вопросы: "как бы я тестировал функцию вручную?" и "а что делает, что возвращает эта функция? ". В данном случае, она возвращает строку из случайных символов. Вручную мы бы вызвали ее и проверили что она возаращает строку случанйых символов. Вот мы и придумали алгоритм:
- проверяем что возвращается строка
- проверяем что ее длина равна N символов
- хорошо бы проверить что символы случайные, но я плохо представляю как это сделать. В теории генератор случайных символов может выжать любую последовательность символов. Есть тесты генераторов случайных чисел (https://en.wikipedia.org/wiki/Randomness_tests), которые считают разные параметры статистики (вроде частоты выпадения символов и числа повторов), но им обычно требуются огромные объем данных, сотни мегабайт, и даже в этом случае мы тестируем нестолкьо саму нашу функцию сколько встроенный в php mt_rand. Потому забьем на это.
Ну или мы можем использовать упрощенный тест: сгенерировать 3-4 строки и проверить что среди них есть хотя бы 2 уникальных строки. Конечно генератор случайных чисел может выдать 4 одинаковых случайных 40-символьных строки, но вероятность этого сопоставима с вероятностью что завтра наша планета провалится в черную дыру. И если она провалится, то плохо работающий тест будет нас беспокоить меньше всего.
(кстати это как раз был пример творческого подхода к решению задачи. нам подходит не математически идеальное, но работающее решение)
Все ли хорошо? Нет, не все. Мы закладываем в тест длину генерируемой строки. Но что если завтра мы решим усилить защиту и повысим длину токена? тест упадет. Более того, функция называется generateString и она генерирует просто строки, а не токены. Хватит проверки, что строка не пустая.
А если взять функцию генерации токена, надо ли проверять что длина токена равна N? Давай подумаем, а какая вообще должна быть длина токена? Какие у нас к нему требования?
В нем не должно быть слишком мало символов (так как он тогда ничего не защищает) и не должно быть слишком много (так как у кук есть ограничение в 4 Кб на куки для одного домена). Вот и ответ. Для токена мы должны проверить, что:
- это строка
- что в 3-4 токенах есть различные строки
- она длиннее X символов
- она короче Y символов
-----
Ну, это было просто, давай поищем что-нибудь посложнее.
Ну давай пройдемся по коду и посмотрим что стоит тестировать юнит-тестами. Вот я открываю первый попавшийся файл: https://github.com/nsdvw/TestHub/blob/master/src/AppBundle/Helper/StringGenerator.php
Что мы тут видим? В глаза бросается бессмысленный вызов sha1, который только вхолостую тратит процессорные циклы и природный газ на электростанции, не выполняя никакой полезной функции. Ладно, сегодня мы пришли не за этим.
Мы видим 2 публичные функции, разницу между которыми я плохо понимаю:
- generateToken()
- generateString()
Так как они делают в принципе одно и тоже, то я выберу вторую функцию, ее имя мне кажется более подходящим тому, что она делает.
Итак, как протестировать generateString()? Надеюсь ты помнишь, что самый простой способ - это просто вызвать функцию. Этот способ отловит например программные ошибки внутри нее вроде бесконечного цикла или обращения к несущестсующей переменной. Пишем:
class StringGeneratorTest extends ...
public function testGenerateString()
{
StringGenerator::generateString();
}
Но конечно, раз уж мы потратили время на создание теста, давай протестируем ее чуть получше.
Попробуй ответить на вопросы: "как бы я тестировал функцию вручную?" и "а что делает, что возвращает эта функция? ". В данном случае, она возвращает строку из случайных символов. Вручную мы бы вызвали ее и проверили что она возаращает строку случанйых символов. Вот мы и придумали алгоритм:
- проверяем что возвращается строка
- проверяем что ее длина равна N символов
- хорошо бы проверить что символы случайные, но я плохо представляю как это сделать. В теории генератор случайных символов может выжать любую последовательность символов. Есть тесты генераторов случайных чисел (https://en.wikipedia.org/wiki/Randomness_tests), которые считают разные параметры статистики (вроде частоты выпадения символов и числа повторов), но им обычно требуются огромные объем данных, сотни мегабайт, и даже в этом случае мы тестируем нестолкьо саму нашу функцию сколько встроенный в php mt_rand. Потому забьем на это.
Ну или мы можем использовать упрощенный тест: сгенерировать 3-4 строки и проверить что среди них есть хотя бы 2 уникальных строки. Конечно генератор случайных чисел может выдать 4 одинаковых случайных 40-символьных строки, но вероятность этого сопоставима с вероятностью что завтра наша планета провалится в черную дыру. И если она провалится, то плохо работающий тест будет нас беспокоить меньше всего.
(кстати это как раз был пример творческого подхода к решению задачи. нам подходит не математически идеальное, но работающее решение)
Все ли хорошо? Нет, не все. Мы закладываем в тест длину генерируемой строки. Но что если завтра мы решим усилить защиту и повысим длину токена? тест упадет. Более того, функция называется generateString и она генерирует просто строки, а не токены. Хватит проверки, что строка не пустая.
А если взять функцию генерации токена, надо ли проверять что длина токена равна N? Давай подумаем, а какая вообще должна быть длина токена? Какие у нас к нему требования?
В нем не должно быть слишком мало символов (так как он тогда ничего не защищает) и не должно быть слишком много (так как у кук есть ограничение в 4 Кб на куки для одного домена). Вот и ответ. Для токена мы должны проверить, что:
- это строка
- что в 3-4 токенах есть различные строки
- она длиннее X символов
- она короче Y символов
-----
Ну, это было просто, давай поищем что-нибудь посложнее.
Вот ты накатал 2 полотна, буду до ночи читать только.
>Ну, это было просто, давай поищем что-нибудь посложнее.
Наверное, опоздал, и ты там выбрал уже что-то другое, но только что накатал большой метод для вычисления результатов теста.
Вот тут точно нужны тесты.
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Service/Calculator.php
далее я иду в папку сервисов и вижу:
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Service/Calculator.php
Ну, это элементарно. Создаем разные Attempt разных типов, даем на вход функции, проверяем что ответ совпаадет с ожидаемым. Не забудь тестировать как положительные сценарии (ответ на вопрос верный) так и негативные (ответ неправильный).
Теперь перейдем к классу поинтереснее: https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Service/TestService.php
Тут есть работа с базой. Прочитаем http://symfony.com/doc/current/cookbook/testing/database.html и понимаем что у нас такие варианты:
- делать моки для репозитория и инжектировать в класс для теста. Трудоемко
- делать интеграционные тесты, вставив в чистую базу заранее известные данные и вызывая методы. Может быть вполне приемломо
- тестировать в рамках функциональных тестов. Минимум трудозатрат.
Посмотрим функции:
- findLastAttempt(Test $test, User $user) - нет особого смысла тестировать так как элементарный код
- createNewAttempt(Test $test, User $user) - нет особого смысла тестировать, плюс она еще и persist + flush зачем-то делает, мешая пользователю управлять транзакцией
- getUnansweredCount(Attempt $attempt) - в принципе можно протестировать интеграционно, хотя функция сама несложная
- public function findByNum($sequenceNumber, $testId) - кода в ней почти нет, тестировать нечего. При желании можно тестировать интеграционно, вставить в БД вопрос и попробовать найти
- questionAlreadyHasAnswer(Attempt $attempt, Question $question) - только интеграционно
- getFirstUnanswered(Attempt $attempt) - только интеграционно
- getNextUnansweredNumber(Attempt $attempt, $num) - интеграционно
Для интеграционного тестирования нам надо будет написать класс-помощник с методами для быстрой генерации и вставки через доктрину теста, вопроса, и тд, чтобы мы могли создать в базе новый тест известной структуры и тестировать методы на нем.
Интеграционный тест лучше делать на отдельной базе расположенной в памяти, чтобы не тратить время на дисковые операции. Скрипт должен создать базу, залить в нее дампы или миграции, и только после запускать интеграционные тесты. Тесты надо писать изолированно, чтобы созданные одним тестом записи не ломали бы дргуие. Некоторые делают тест внутри транзакции, откатывая ее после теста. Некоторые после/перед тестом очищают таблицы в БД.
далее я иду в папку сервисов и вижу:
https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Service/Calculator.php
Ну, это элементарно. Создаем разные Attempt разных типов, даем на вход функции, проверяем что ответ совпаадет с ожидаемым. Не забудь тестировать как положительные сценарии (ответ на вопрос верный) так и негативные (ответ неправильный).
Теперь перейдем к классу поинтереснее: https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Service/TestService.php
Тут есть работа с базой. Прочитаем http://symfony.com/doc/current/cookbook/testing/database.html и понимаем что у нас такие варианты:
- делать моки для репозитория и инжектировать в класс для теста. Трудоемко
- делать интеграционные тесты, вставив в чистую базу заранее известные данные и вызывая методы. Может быть вполне приемломо
- тестировать в рамках функциональных тестов. Минимум трудозатрат.
Посмотрим функции:
- findLastAttempt(Test $test, User $user) - нет особого смысла тестировать так как элементарный код
- createNewAttempt(Test $test, User $user) - нет особого смысла тестировать, плюс она еще и persist + flush зачем-то делает, мешая пользователю управлять транзакцией
- getUnansweredCount(Attempt $attempt) - в принципе можно протестировать интеграционно, хотя функция сама несложная
- public function findByNum($sequenceNumber, $testId) - кода в ней почти нет, тестировать нечего. При желании можно тестировать интеграционно, вставить в БД вопрос и попробовать найти
- questionAlreadyHasAnswer(Attempt $attempt, Question $question) - только интеграционно
- getFirstUnanswered(Attempt $attempt) - только интеграционно
- getNextUnansweredNumber(Attempt $attempt, $num) - интеграционно
Для интеграционного тестирования нам надо будет написать класс-помощник с методами для быстрой генерации и вставки через доктрину теста, вопроса, и тд, чтобы мы могли создать в базе новый тест известной структуры и тестировать методы на нем.
Интеграционный тест лучше делать на отдельной базе расположенной в памяти, чтобы не тратить время на дисковые операции. Скрипт должен создать базу, залить в нее дампы или миграции, и только после запускать интеграционные тесты. Тесты надо писать изолированно, чтобы созданные одним тестом записи не ломали бы дргуие. Некоторые делают тест внутри транзакции, откатывая ее после теста. Некоторые после/перед тестом очищают таблицы в БД.
Наконец посмтрим еще сюда: https://github.com/nsdvw/TestHub/blob/master/src/TestHubBundle/Controller/TestController.php
Контроллеры обычно тестируются функциональным тестом, но в данном случае явно стоит еще подумать о рефакторинге. В контроллере много кода и длинные функции. Кстати, можно сделать тест до рефакторинга.
> Что тут собственно тестировать? Получение данных из бд и то что методы возвращают объект ожидаемого класса?
Если функция тривиальная то можно не тестировать.
> Короче, тестировать здесь нужно именно логику подсчета результатов, или таки дополнительно проверить, что из бд возвращаются нужные данные?
Юнит тесты не работают с БД, только если интеграционные.
Неэффективно хранить картинки в БД. Лучше хранить и отдавать их с диска, а в базе хранить только информацию о них. Так как с диска тот же нгинкс или апачих отдает быстрее и требуя меньше ресурсов.
> Content-type: image/jpeg; charset=utf-8
Кодировку имеет смысл указывать только для типов которые начинаются с text. У картинок бинарный формат, а не текстовый.
>>48633
А почему не ответить честно? Вообще, вопрос глупый так как ответ на него ничего не дает. Я бы никогда таких вопросов не спрашивал.
>а в базе хранить только информацию о них.
Давно хотел спросить а зачем это? Ну типа может там для каких-то картиночных хостингов актуально, а так например пользователь грузит аватарку зачем везде создают отдельную таблицу и хранят об этом инфу? Можно завести папку с айдишником пользователя и аватары переименовывать в avatar.jpg ну если надо будет найти и удалить.
Может для верстки надо знать ширину картинки. Ну и если загрузка картинки опциональная, можно с помощью запроса найти пользователей без аватарок.
Ну и ты мыслишь ограниченно. Большие веб-приложения часто работают на нескольких серверах и хранилище картинок может быть отдельно. Без базы проверить есть ли у пользователя картинка, никак нельзя (дикие варианты вроде сетевых файловых систем сразу отбросим).
Я честно и говорю, что выбрал php из-за популярности, комьюнити и рынка вакансий.
Просто непонятно в чем подвох, может они хотят отсеять вованов с мотивацией уровня "мне на сосаче сказали что в айти можно легко зашибать бабло, ничего не делая".
>>48670
Это все я уже читал и в твоей статье, и на хабре, все это общие высказывания, я больше стремлюсь понять как применять на рактике.
И да, тесты это круто (жаль что пока смутно понятно как их писать). Конечно хорошо, когда только закоммитишь, а травис тебе уже во всю трубит о баге.
Потому что часто такие ситуации, когда баг обнаруживается спустя много времени, код разросся, исправлять тяжело и неудобно.
Тесты это очень круто, и я буду всегда буду ими пользоваться, когда/если осилю.
>>48683
>В глаза бросается бессмысленный вызов sha1, ... не выполняя никакой полезной функции.
Этот класс задумывался как генератор для хеша соленого пароля в первую очередь. Потом я подумал, что наверное уже есть какой-то бандл для авторизации, типа FOS UserBundle. Но класс пригодился для тупой генерации токена доступа для неавторизованного пользователя.
Впрочем да, зачем его пропускать через функцию хеширования? Наверное удалю метод с хешированием, пусть останется только generateString, может она еще где-нибудь пригодится. (Опять таки наверняка где-то в недрах фреймворка есть класс для генерации рандомной строки, но я пока что-то не нашел, нагуглил какой-то Symfony\Component\Security\Core\Util\SecureRandom но это походу только во второй версии, я пользуюсь третьей).
Логику что тестировать в целом понял (по крайней мере для таких примитивных случаев), вот что получилось
http://ideone.com/UugEm1
Но даже здесь есть много вопросов
1. В симфони нет бандла для тестирования?
Если наследоваться от \PHPUnit_Framework_TestCase он говорит нет такого класса,
и не автодополняет. И сам phpunit я качаю phar архивом, наверняка оно там где-то
уже лежит в папке вендор, знать бы где.
2. Писать отдельный тест (метод в классе теста) под метод тестируемого класса?
Или можно написать несколько тестов для одного метода класса?
Например в случае StringGenerator::generateString нам нужно протестировать
что он возвращает строку,
что эта строка уникальна, и
что ее длина между X и Y (кстати сразу вопрос, 2.1.можно выставить левые значения
для X и Y, или таки эти значения нужно брать из какого-то конфига? подозреваю что
их можно засунуть в phpunit.xml, только не знаю куда именно)
Это отдельные тесты (методы класса теста), или можно в одном нафигачить кучу ассертов?
Походу отдельные, причем они зависят друг от друга, ну то есть можно скипать все
остальные, если первый тест провалился (где тип возвращаемого результата проверяется
на строку).
Какие имена им давать тогда, или по-барабану?
3. Насколько позволительно использовать в тестах постороннюю логику?
Вот я например хочу проверить уникальность генерируемой строки, создав несколько
(опять-таки, сколько? хардкодить число, или выносить непонятно куда?) строк,
положить в массив, и сделать
for($i = 0; $i < $iterations; $i++){$randoms[] = ...}
$this->assertEquals($iterations, count(array_unique($randoms)));
То есть тут две левые функции: count со вложенным array_unique. Я конечно не сомневаюсь
в том что они никогда не сломаются, просто логика может быть сложнее, наверное
таких ситуаций нужно избегать, а то не хватало еще тестировать тесты.
Я честно и говорю, что выбрал php из-за популярности, комьюнити и рынка вакансий.
Просто непонятно в чем подвох, может они хотят отсеять вованов с мотивацией уровня "мне на сосаче сказали что в айти можно легко зашибать бабло, ничего не делая".
>>48670
Это все я уже читал и в твоей статье, и на хабре, все это общие высказывания, я больше стремлюсь понять как применять на рактике.
И да, тесты это круто (жаль что пока смутно понятно как их писать). Конечно хорошо, когда только закоммитишь, а травис тебе уже во всю трубит о баге.
Потому что часто такие ситуации, когда баг обнаруживается спустя много времени, код разросся, исправлять тяжело и неудобно.
Тесты это очень круто, и я буду всегда буду ими пользоваться, когда/если осилю.
>>48683
>В глаза бросается бессмысленный вызов sha1, ... не выполняя никакой полезной функции.
Этот класс задумывался как генератор для хеша соленого пароля в первую очередь. Потом я подумал, что наверное уже есть какой-то бандл для авторизации, типа FOS UserBundle. Но класс пригодился для тупой генерации токена доступа для неавторизованного пользователя.
Впрочем да, зачем его пропускать через функцию хеширования? Наверное удалю метод с хешированием, пусть останется только generateString, может она еще где-нибудь пригодится. (Опять таки наверняка где-то в недрах фреймворка есть класс для генерации рандомной строки, но я пока что-то не нашел, нагуглил какой-то Symfony\Component\Security\Core\Util\SecureRandom но это походу только во второй версии, я пользуюсь третьей).
Логику что тестировать в целом понял (по крайней мере для таких примитивных случаев), вот что получилось
http://ideone.com/UugEm1
Но даже здесь есть много вопросов
1. В симфони нет бандла для тестирования?
Если наследоваться от \PHPUnit_Framework_TestCase он говорит нет такого класса,
и не автодополняет. И сам phpunit я качаю phar архивом, наверняка оно там где-то
уже лежит в папке вендор, знать бы где.
2. Писать отдельный тест (метод в классе теста) под метод тестируемого класса?
Или можно написать несколько тестов для одного метода класса?
Например в случае StringGenerator::generateString нам нужно протестировать
что он возвращает строку,
что эта строка уникальна, и
что ее длина между X и Y (кстати сразу вопрос, 2.1.можно выставить левые значения
для X и Y, или таки эти значения нужно брать из какого-то конфига? подозреваю что
их можно засунуть в phpunit.xml, только не знаю куда именно)
Это отдельные тесты (методы класса теста), или можно в одном нафигачить кучу ассертов?
Походу отдельные, причем они зависят друг от друга, ну то есть можно скипать все
остальные, если первый тест провалился (где тип возвращаемого результата проверяется
на строку).
Какие имена им давать тогда, или по-барабану?
3. Насколько позволительно использовать в тестах постороннюю логику?
Вот я например хочу проверить уникальность генерируемой строки, создав несколько
(опять-таки, сколько? хардкодить число, или выносить непонятно куда?) строк,
положить в массив, и сделать
for($i = 0; $i < $iterations; $i++){$randoms[] = ...}
$this->assertEquals($iterations, count(array_unique($randoms)));
То есть тут две левые функции: count со вложенным array_unique. Я конечно не сомневаюсь
в том что они никогда не сломаются, просто логика может быть сложнее, наверное
таких ситуаций нужно избегать, а то не хватало еще тестировать тесты.
Сап товарищи.
Такой вопрос. На сайт выводятся вопросы из базы данных в случайном порядке. Но вот вопрос, как можно сделать, чтобы запрос к базе обновлялся, переодически ?
Есть ли какой-то аналог setInterval(), только в PHP ?
> В симфони нет бандла для тестирования?
не знаю, погугли
> Писать отдельный тест (метод в классе теста) под метод тестируемого класса?
Можно на каждый метод класса писать свой тест. Можно одним тестом несколько связанных методов проверить. Можно на один метод несколько разных тестов написать.
> не автодополняет.
Можно в композере прописать phpunit в require-dev или установить его в какую-нибудь левую папку. Ну и вообще, в пхпсторме наверно можно как-то включить поддержку автодополнения
> Это отдельные тесты (методы класса теста), или можно в одном нафигачить кучу ассертов?
Они все свзяаны так что не вижу смысла раскидывать по разным методам. Какая от этого польза?
> кстати сразу вопрос, 2.1.можно выставить левые значения
для X и Y, или таки эти значения нужно брать из какого-то конфига?
Прописать как цифры в тесте. Не надо усложнять.
> Походу отдельные, причем они зависят друг от друга, ну то есть можно скипать все
> остальные, если первый тест провалился (где тип возвращаемого результата проверяется
> на строку).
Это как раз значит 1 тест
> Какие имена им давать тогда, или по-барабану?
В названии функции ты пишешь что хочешь протестировать
> . Насколько позволительно использовать в тестах постороннюю логику?
позволительно, в том числе можно делать support классы с вспомогательными функцями. Например для создания определенной структуры из объектов или записей в БД. Еще ты можешь делать свои базовые классы для тестов.
> То есть тут две левые функции: count со вложенным array_unique.
Их тестируют разработчики языка php и я думаю что они надежные.
Для проверки типа есть https://phpunit.de/manual/current/en/appendixes.assertions.html#appendixes.assertions.assertInternalType
> $this->assertEquals($iterations, count(array_unique($randoms)));
Я бы для надежности тестировал что там есть хотя бы 2 разных строки. А не что их ровно 5.
> mb_internal_encoding('utf8');
Это должно быть в bootstrap.php. Не надо менять глобальные натсройки посередине одного теста, влияя на все другие
В тесте не хватает пары комментариев, про проверку уникальности и про длину.
Ну, это была самая простая функция, давай что-ниьудь посложнее тестировать тоже.
> В симфони нет бандла для тестирования?
не знаю, погугли
> Писать отдельный тест (метод в классе теста) под метод тестируемого класса?
Можно на каждый метод класса писать свой тест. Можно одним тестом несколько связанных методов проверить. Можно на один метод несколько разных тестов написать.
> не автодополняет.
Можно в композере прописать phpunit в require-dev или установить его в какую-нибудь левую папку. Ну и вообще, в пхпсторме наверно можно как-то включить поддержку автодополнения
> Это отдельные тесты (методы класса теста), или можно в одном нафигачить кучу ассертов?
Они все свзяаны так что не вижу смысла раскидывать по разным методам. Какая от этого польза?
> кстати сразу вопрос, 2.1.можно выставить левые значения
для X и Y, или таки эти значения нужно брать из какого-то конфига?
Прописать как цифры в тесте. Не надо усложнять.
> Походу отдельные, причем они зависят друг от друга, ну то есть можно скипать все
> остальные, если первый тест провалился (где тип возвращаемого результата проверяется
> на строку).
Это как раз значит 1 тест
> Какие имена им давать тогда, или по-барабану?
В названии функции ты пишешь что хочешь протестировать
> . Насколько позволительно использовать в тестах постороннюю логику?
позволительно, в том числе можно делать support классы с вспомогательными функцями. Например для создания определенной структуры из объектов или записей в БД. Еще ты можешь делать свои базовые классы для тестов.
> То есть тут две левые функции: count со вложенным array_unique.
Их тестируют разработчики языка php и я думаю что они надежные.
Для проверки типа есть https://phpunit.de/manual/current/en/appendixes.assertions.html#appendixes.assertions.assertInternalType
> $this->assertEquals($iterations, count(array_unique($randoms)));
Я бы для надежности тестировал что там есть хотя бы 2 разных строки. А не что их ровно 5.
> mb_internal_encoding('utf8');
Это должно быть в bootstrap.php. Не надо менять глобальные натсройки посередине одного теста, влияя на все другие
В тесте не хватает пары комментариев, про проверку уникальности и про длину.
Ну, это была самая простая функция, давай что-ниьудь посложнее тестировать тоже.
https://github.com/someApprentice/Students/
> Я решил что не только ПомощникРегистрации должен заниматься ридеректом, и решил вынести функции редиректа в отдельный родительский класс
Тогда можно его и использовать, а ResgistrationHelper можно удалять так как в нем нет методов и непонятно зачем он вообще нужен.
https://github.com/someApprentice/Students/blob/master/app/Model/Helper/Helper.php
Странная функция. Если URL соответствует шаблону, она редиректит, а если нет - то нет. И что происходит в этом случае? Я бы просто редиректил на главную например.
> Не портит ли это модель?
Модель не занимается редиректами. Это относится к контроллеру или представлению.
> Не допустил ли я здесь ошибку с передачей авторайзера? Не должен ли он содержаться в классе студента изначально? И правильно ли я назвал эту переменную? Ведь авторизация, в отличие от аутентификации, отвечает за предоставление прав к данным, а не за вспомогательные функции к ней.
Именно так. authorizer должен отвечать за залогинивание и проверку авторизации. У тебя он как-то странно используется. Ну например:
$registerStudentForm->setStudentPassword($this->authorizer);
Непонятно зачем он вообще тут передается. Судя по названию в эту функцию должен передаваться пароль, а не непонятный объект. Если тебе в форме нужен сервис авторизации то логичнее его передавать через конструктор.
А зачем форме знать про класс авторизации? Как они связаны? Если тебе надо взять пароль из формы и проставить студенту через сторонний класс то можно так и сдеать:
пароль = форма->получитьПароль
сервисАвторизации->назначитьПароль(студент, пароль)
Если студент умеет сам ставить себе пароль, то можно сделать так:
пароль = форма->получитьПароль
студент->назначитьПароль(пароль)
Вот смотри, у меня универсальная функция назначения пароля которая ничего не знает о форме и в которую можно передавать пароль откуда угодно. А форма ничего не знает о сервисе авторизаиции.
Можно сделать чтобы и форма проставляла пароль, так иногда удобнее, но наверно в этом случае это должно происходить автоматически. Ты же не пишешь что форма должна проставить студенту имя и фамилию, а почему тогда про пароль пишешь?
> Здесь присутствует копипаста метода. Будет ли лучше вынести этот метод в отдельный класс Entity
Нет, так как форма не связана с Entity. В случае с формами, можно этот метод вынести в базовый класс формы. В модели пусть будет копипаста. Ну а что поделать, эти классы никак не связаны, и незачем тут радикально все усложнять из-за таких мелочей.
Также, этот метод явно неправильный:
> https://github.com/someApprentice/Students/blob/master/app/Model/Entity/Student.php#L40
> fillDataFromDB(array $result)
Модель лучше отвязать от базы данных, а метод перенести в класс работы с БД. Вместо того чтобы поместмть работу с БД в один класс, ты ее размазываешь по 2 классам. И я не вижу смысла делать 2 метода fillFrom ... Я бы сделал олин метод, который ставит любые поля, а проверку по белому списку сделал бы где-то выше, например в классе формы.
> Мне могут помочь в будущем знания о том как сделать авторизацию с помощью разных данных.
Я не против, но она должна быть сделана разумно. Ну например если ты используешь фамилию то ты должен отказываться принимать ее, если она не уникальна в базе. Или требовать имя + фамилию.
>>Но и в этом случае сессия не нужна.
> Сессиями больше не пользуются?
Тут просто кук же хватит, зачем сессия?
> Если бы я пользовался куками, данные, такие как Имя, Фамилия, Возраст и так далее, тоже были бы лишними?
Конечно, лишние. Достаточно иметь id, все данные студента есть в базе. Какая выгода от дублирования? От этого наоборот проблемы. Представь что студент залогинен с 2 разных устройств и имеет 2 сессии. Он меняет свою фамилию. Поменяется ли она в обоих сессиях? Очевидно нет.
А теперь объясни свою точку зрения. Почему надо использовать сессию вместо кук и почему надо в ней хранить имя и фамилию? Какая от этого выгода?
> С этим вообще у меня возникает много проблем. Даже после того как я переработал код мне приходиться несколько раз перезадавать значение ошибки.
по моему ты переусложнил алгоритм. Алгоритм там пишется так:
----
пусть у нас есть "логин", который может быть фамилией либо емайлом, и "пароль"
если (логин выглядит как емайл и (в базе есть студент с таким емайлом и паролем)) {
то это он
выходим
}
если (логин (соответствует фамилии ровно одного студента и пароль подходит)) то {
студент найден
выходим
}
иначе не удалось авторизоваться
----
Я тут не вижу циклов и сложных манипуляций массивами. Достаточно сделать 2 метода: авторизоватьсяПоФамилии и авторизоватьсяПоемайлу и вызвать их по очереди.
> Так это нужно вызывать в ручную? Я собираюсь прямо по среди цикла делать ридерект, даже если я пропишу break или die после этого, ридерект не сломает мой код?
Наверно нет. А как он сломает код?
>>И как в твоем сценарии проверить праивльность логина и пароля не имея формы? Ведь пароль можно и не только в форму логина ввести, и вообще не в форму. А например в консоли или через API. Ты сможешь в этих случаях его проверить?
> В консоли или через API обычно передают $_POST? Если да, то смогу - контроллер всегда вызывает валидатор если есть $_POST. Если нет, то мне придется переделывать контроллер чтобы он принимал в аргументы логин и пароль или еще что-то поменять. Ведь контролер же занимается пришедшими данными из вне?
В консоли нет никакого POST. POST это данные пришедшие в теле HTTP запроса который получил от браузера веб-сервер. В консоли нет ни браузера, ни запроса от него, ни веб-сервера и значит в POST ничего не будет.
> Если нет, то мне придется переделывать контроллер чтобы он принимал в аргументы логин и пароль или еще что-то поменять
Не надо переделывать. Контроллер предназначен только для обработки одного конкретного запроса. Для консоли мы напишем отдельный контроллер. И кстати по этйо причине контроллер стоит стараться делать небольшими, так как код в них нельзя повторно исопльзовать в другом месте и вызвать откуда-то.
Если что-то непонятно, задавай уточняющие вопросы. И старайся определить, за что отвечает каждый класс, как мы распределим обязанности. Это конечно не всегда просто, так что придется поломать голову.
https://github.com/someApprentice/Students/
> Я решил что не только ПомощникРегистрации должен заниматься ридеректом, и решил вынести функции редиректа в отдельный родительский класс
Тогда можно его и использовать, а ResgistrationHelper можно удалять так как в нем нет методов и непонятно зачем он вообще нужен.
https://github.com/someApprentice/Students/blob/master/app/Model/Helper/Helper.php
Странная функция. Если URL соответствует шаблону, она редиректит, а если нет - то нет. И что происходит в этом случае? Я бы просто редиректил на главную например.
> Не портит ли это модель?
Модель не занимается редиректами. Это относится к контроллеру или представлению.
> Не допустил ли я здесь ошибку с передачей авторайзера? Не должен ли он содержаться в классе студента изначально? И правильно ли я назвал эту переменную? Ведь авторизация, в отличие от аутентификации, отвечает за предоставление прав к данным, а не за вспомогательные функции к ней.
Именно так. authorizer должен отвечать за залогинивание и проверку авторизации. У тебя он как-то странно используется. Ну например:
$registerStudentForm->setStudentPassword($this->authorizer);
Непонятно зачем он вообще тут передается. Судя по названию в эту функцию должен передаваться пароль, а не непонятный объект. Если тебе в форме нужен сервис авторизации то логичнее его передавать через конструктор.
А зачем форме знать про класс авторизации? Как они связаны? Если тебе надо взять пароль из формы и проставить студенту через сторонний класс то можно так и сдеать:
пароль = форма->получитьПароль
сервисАвторизации->назначитьПароль(студент, пароль)
Если студент умеет сам ставить себе пароль, то можно сделать так:
пароль = форма->получитьПароль
студент->назначитьПароль(пароль)
Вот смотри, у меня универсальная функция назначения пароля которая ничего не знает о форме и в которую можно передавать пароль откуда угодно. А форма ничего не знает о сервисе авторизаиции.
Можно сделать чтобы и форма проставляла пароль, так иногда удобнее, но наверно в этом случае это должно происходить автоматически. Ты же не пишешь что форма должна проставить студенту имя и фамилию, а почему тогда про пароль пишешь?
> Здесь присутствует копипаста метода. Будет ли лучше вынести этот метод в отдельный класс Entity
Нет, так как форма не связана с Entity. В случае с формами, можно этот метод вынести в базовый класс формы. В модели пусть будет копипаста. Ну а что поделать, эти классы никак не связаны, и незачем тут радикально все усложнять из-за таких мелочей.
Также, этот метод явно неправильный:
> https://github.com/someApprentice/Students/blob/master/app/Model/Entity/Student.php#L40
> fillDataFromDB(array $result)
Модель лучше отвязать от базы данных, а метод перенести в класс работы с БД. Вместо того чтобы поместмть работу с БД в один класс, ты ее размазываешь по 2 классам. И я не вижу смысла делать 2 метода fillFrom ... Я бы сделал олин метод, который ставит любые поля, а проверку по белому списку сделал бы где-то выше, например в классе формы.
> Мне могут помочь в будущем знания о том как сделать авторизацию с помощью разных данных.
Я не против, но она должна быть сделана разумно. Ну например если ты используешь фамилию то ты должен отказываться принимать ее, если она не уникальна в базе. Или требовать имя + фамилию.
>>Но и в этом случае сессия не нужна.
> Сессиями больше не пользуются?
Тут просто кук же хватит, зачем сессия?
> Если бы я пользовался куками, данные, такие как Имя, Фамилия, Возраст и так далее, тоже были бы лишними?
Конечно, лишние. Достаточно иметь id, все данные студента есть в базе. Какая выгода от дублирования? От этого наоборот проблемы. Представь что студент залогинен с 2 разных устройств и имеет 2 сессии. Он меняет свою фамилию. Поменяется ли она в обоих сессиях? Очевидно нет.
А теперь объясни свою точку зрения. Почему надо использовать сессию вместо кук и почему надо в ней хранить имя и фамилию? Какая от этого выгода?
> С этим вообще у меня возникает много проблем. Даже после того как я переработал код мне приходиться несколько раз перезадавать значение ошибки.
по моему ты переусложнил алгоритм. Алгоритм там пишется так:
----
пусть у нас есть "логин", который может быть фамилией либо емайлом, и "пароль"
если (логин выглядит как емайл и (в базе есть студент с таким емайлом и паролем)) {
то это он
выходим
}
если (логин (соответствует фамилии ровно одного студента и пароль подходит)) то {
студент найден
выходим
}
иначе не удалось авторизоваться
----
Я тут не вижу циклов и сложных манипуляций массивами. Достаточно сделать 2 метода: авторизоватьсяПоФамилии и авторизоватьсяПоемайлу и вызвать их по очереди.
> Так это нужно вызывать в ручную? Я собираюсь прямо по среди цикла делать ридерект, даже если я пропишу break или die после этого, ридерект не сломает мой код?
Наверно нет. А как он сломает код?
>>И как в твоем сценарии проверить праивльность логина и пароля не имея формы? Ведь пароль можно и не только в форму логина ввести, и вообще не в форму. А например в консоли или через API. Ты сможешь в этих случаях его проверить?
> В консоли или через API обычно передают $_POST? Если да, то смогу - контроллер всегда вызывает валидатор если есть $_POST. Если нет, то мне придется переделывать контроллер чтобы он принимал в аргументы логин и пароль или еще что-то поменять. Ведь контролер же занимается пришедшими данными из вне?
В консоли нет никакого POST. POST это данные пришедшие в теле HTTP запроса который получил от браузера веб-сервер. В консоли нет ни браузера, ни запроса от него, ни веб-сервера и значит в POST ничего не будет.
> Если нет, то мне придется переделывать контроллер чтобы он принимал в аргументы логин и пароль или еще что-то поменять
Не надо переделывать. Контроллер предназначен только для обработки одного конкретного запроса. Для консоли мы напишем отдельный контроллер. И кстати по этйо причине контроллер стоит стараться делать небольшими, так как код в них нельзя повторно исопльзовать в другом месте и вызвать откуда-то.
Если что-то непонятно, задавай уточняющие вопросы. И старайся определить, за что отвечает каждый класс, как мы распределим обязанности. Это конечно не всегда просто, так что придется поломать голову.
Проще всего использовать ту же таблицу и тот же код для вывода результатов поиска. Просто при поиске мы выводим не всех студентов, а только часть, соответстующую условию.
> Просто я не знаю, как сделать второй случай без Query Builder.
Можно сделать у функции получения студентов параметр $search и если он равен null то поиск не проводится, возвращаются все студенты. Тут не нужен Query Builder я думаю.
>>42023
Модно передавать параметры функции массивом, можно по отдельности, только она должна называться не build а например getStudents
>>42260
Ты наверно переусложняешь. У тебя же этим будет заниматься крон-скрипт, который просто ищет в базе пользователей по определенным условиям. Тут можно сделать класс типа Trigger и User.
---
Вообще такие рассылки дрянь. Это по сути "псевдолегальный" спам, совсем нелегальный если он рассылается всем кто зарегистрировался например, без явного согласия. Я всегда такие письма отправляю в спам, так как не подписывался на них явно. И надеюсь что другие люди будут тоже так делать, потому что дауны маркетологи тратят время кучи людей на то чтобы пометить письмо как спам.
Хорошая рассылка это та, на которую человек подписался сам, явно, осознавая, что делает. Конечно в этом случае подписчиков почти не будет, но это лишь говорит о полезности таких рассылок.
Я знаю, зачем это делается. расылки делаются для накрутки поещаемости. Я плохо представляю, кто их читает и тем более переходит с них на сайт, наверно это люди у которых много свободного времени и нечем его занять (дети? домохозяйки? пенсионеры?). Любой нормальный человек помечает рассылки как спам, не открывая, так как они отвлекают его от чтения важных писем.
Один интернет-магазин, в котором я имел несчастье оставить свой емайл и который благополучно потерял мой заказ перед новым годом (и мои деньги в итоге получил юлмарт, а не они) агрессивно расылает мне спам, хотя я на него не подписывался. Думают, что лучше потратить деньги на зарплат маркетолога, чем на нормальную организацию продаж. Естественно, я ни одного письма от них открывать даже не собираюсь.
>>42569
Скорее всего никак. Ну или подготовить этот xls заранее, сохранить на диск и отдавать с диска.
Проще всего использовать ту же таблицу и тот же код для вывода результатов поиска. Просто при поиске мы выводим не всех студентов, а только часть, соответстующую условию.
> Просто я не знаю, как сделать второй случай без Query Builder.
Можно сделать у функции получения студентов параметр $search и если он равен null то поиск не проводится, возвращаются все студенты. Тут не нужен Query Builder я думаю.
>>42023
Модно передавать параметры функции массивом, можно по отдельности, только она должна называться не build а например getStudents
>>42260
Ты наверно переусложняешь. У тебя же этим будет заниматься крон-скрипт, который просто ищет в базе пользователей по определенным условиям. Тут можно сделать класс типа Trigger и User.
---
Вообще такие рассылки дрянь. Это по сути "псевдолегальный" спам, совсем нелегальный если он рассылается всем кто зарегистрировался например, без явного согласия. Я всегда такие письма отправляю в спам, так как не подписывался на них явно. И надеюсь что другие люди будут тоже так делать, потому что дауны маркетологи тратят время кучи людей на то чтобы пометить письмо как спам.
Хорошая рассылка это та, на которую человек подписался сам, явно, осознавая, что делает. Конечно в этом случае подписчиков почти не будет, но это лишь говорит о полезности таких рассылок.
Я знаю, зачем это делается. расылки делаются для накрутки поещаемости. Я плохо представляю, кто их читает и тем более переходит с них на сайт, наверно это люди у которых много свободного времени и нечем его занять (дети? домохозяйки? пенсионеры?). Любой нормальный человек помечает рассылки как спам, не открывая, так как они отвлекают его от чтения важных писем.
Один интернет-магазин, в котором я имел несчастье оставить свой емайл и который благополучно потерял мой заказ перед новым годом (и мои деньги в итоге получил юлмарт, а не они) агрессивно расылает мне спам, хотя я на него не подписывался. Думают, что лучше потратить деньги на зарплат маркетолога, чем на нормальную организацию продаж. Естественно, я ни одного письма от них открывать даже не собираюсь.
>>42569
Скорее всего никак. Ну или подготовить этот xls заранее, сохранить на диск и отдавать с диска.
Если ты запутался, запости то, что получилось написать, напиши на чем остановился, дадим совет или подсказку.
>>42860
Открывай developer tools и смотри что получается в переменной, что отправляется. Вообще, конечно в коде каждая вторая строка с ошибками, ад какой-то. Ну например, объясни почему ты не проверяешь при отправке запроса ситуацию, когда произолшла ошибка? Почему не блокируешь кнопку на время отправки? Почему у тебя $("#userArea") 2 раза встречается? Зачем инклудить ajaxLoad.php? Зачем замедлять загрузку страницы дополнительным аякс запросом?
Зачем у тебя слеши в конце одиночных тегов?
>>43295
В случае с формами, тебе надо изучить их получше, в том числе темизацию, хотя она там довольно сложная и неочевидная.
Для вывода формы достаточно писать {{ form(form) }}
>>43671
ну можно от себя пару букв добавить в массив например
>>43898
Верно.
>>44230
> А если некий шаблон выводят разные контроллеры и им нужен свой хедер или вообще его отсутствие?
Можно сделать переменную $headerTemplate и инклудить через нее. Или можно сделать файл layout.php который отвечает за шапку/подвал и подключает шаблон с содержимым.
> А если добавлю метод валидации другим классом? Отдам ему данные с формы и получу от него ошибки. На пике накидал пример.
Мне кажется что конструировать форму лучше в классе формы. а не контроллере. То есть сделат клас например StudentForm. Не уверен что стоит использовать формы для обработки параметров сортировки таблицы - это возможно, но мне кажется переусложнением. Хотя можно и так.
Если ты запутался, запости то, что получилось написать, напиши на чем остановился, дадим совет или подсказку.
>>42860
Открывай developer tools и смотри что получается в переменной, что отправляется. Вообще, конечно в коде каждая вторая строка с ошибками, ад какой-то. Ну например, объясни почему ты не проверяешь при отправке запроса ситуацию, когда произолшла ошибка? Почему не блокируешь кнопку на время отправки? Почему у тебя $("#userArea") 2 раза встречается? Зачем инклудить ajaxLoad.php? Зачем замедлять загрузку страницы дополнительным аякс запросом?
Зачем у тебя слеши в конце одиночных тегов?
>>43295
В случае с формами, тебе надо изучить их получше, в том числе темизацию, хотя она там довольно сложная и неочевидная.
Для вывода формы достаточно писать {{ form(form) }}
>>43671
ну можно от себя пару букв добавить в массив например
>>43898
Верно.
>>44230
> А если некий шаблон выводят разные контроллеры и им нужен свой хедер или вообще его отсутствие?
Можно сделать переменную $headerTemplate и инклудить через нее. Или можно сделать файл layout.php который отвечает за шапку/подвал и подключает шаблон с содержимым.
> А если добавлю метод валидации другим классом? Отдам ему данные с формы и получу от него ошибки. На пике накидал пример.
Мне кажется что конструировать форму лучше в классе формы. а не контроллере. То есть сделат клас например StudentForm. Не уверен что стоит использовать формы для обработки параметров сортировки таблицы - это возможно, но мне кажется переусложнением. Хотя можно и так.
> $endingNumber = $endingNumber % 100;
> } else {
> $endingNumber = $endingNumber % 10;
по моему это усложняет функцию. проще просто провеярть, если число от 11 до 19 то возвращать определенный вариант сразу.
> function smallNumberToarrayOfWords($number, $end)
тут по моему проще поместить массив $end внутрь функции либо сделать отдельную функцию возвращающую его
> echo "На вашем счету ноль рублей";
> exit;
Неправильно, если рублей ноль то функция завершает программу. А если я хочу несколько разных сумм вывести? И почему тут стоит echo, а кто тебе сказал что я хочу на экран этот текст выводить? Нужна универсальная функция которая просто возвращает строку, а не решает за пользователя что с ней делать.
> if ($discharge == 0 && $key == 2) {
> $numberOfWord++;
> $arrayOfWords[$numberOfWord] = "рублей";
Непонятно зачем тот случай выносить особо, можно его обрабатыать так же как и другие.
> $arrayOfWords[$numberOfWord]
Переменная $numberOfWord не нужна
> function writeThousand($key, $discharge, $spelling) {
Если смотреть только на эту функцию то непонятно что за переменная key. По моему так ее надо заменить на что-то более понятное. например переменную обозначающую род слова.
калькулятор
Тут мне не нравится алгоритм разбиения на части. Ты предполагаешь что в выражении всегда идут по очереди число и знак операции, и можно брать по очереди значения из 2 массивов, но в реальности может идти и 2 числа или 2 знака подряд. Алгоритм выглядит ненадежным. Надо разбивать строку на части один раз. Чтобы был один массив, где идут и числа, и знаки операций.
Это позволит вместо 3 регулярок обойтись одной.
Ну вот например ошибка: http://ideone.com/2UHnq2
Или вот: http://ideone.com/AcQSgh
> $endingNumber = $endingNumber % 100;
> } else {
> $endingNumber = $endingNumber % 10;
по моему это усложняет функцию. проще просто провеярть, если число от 11 до 19 то возвращать определенный вариант сразу.
> function smallNumberToarrayOfWords($number, $end)
тут по моему проще поместить массив $end внутрь функции либо сделать отдельную функцию возвращающую его
> echo "На вашем счету ноль рублей";
> exit;
Неправильно, если рублей ноль то функция завершает программу. А если я хочу несколько разных сумм вывести? И почему тут стоит echo, а кто тебе сказал что я хочу на экран этот текст выводить? Нужна универсальная функция которая просто возвращает строку, а не решает за пользователя что с ней делать.
> if ($discharge == 0 && $key == 2) {
> $numberOfWord++;
> $arrayOfWords[$numberOfWord] = "рублей";
Непонятно зачем тот случай выносить особо, можно его обрабатыать так же как и другие.
> $arrayOfWords[$numberOfWord]
Переменная $numberOfWord не нужна
> function writeThousand($key, $discharge, $spelling) {
Если смотреть только на эту функцию то непонятно что за переменная key. По моему так ее надо заменить на что-то более понятное. например переменную обозначающую род слова.
калькулятор
Тут мне не нравится алгоритм разбиения на части. Ты предполагаешь что в выражении всегда идут по очереди число и знак операции, и можно брать по очереди значения из 2 массивов, но в реальности может идти и 2 числа или 2 знака подряд. Алгоритм выглядит ненадежным. Надо разбивать строку на части один раз. Чтобы был один массив, где идут и числа, и знаки операций.
Это позволит вместо 3 регулярок обойтись одной.
Ну вот например ошибка: http://ideone.com/2UHnq2
Или вот: http://ideone.com/AcQSgh
> .{15}(ж|ш)ы.
получается перед ошибкой должно идти не менее 15 символов?
>>45309
> когда мы пишем $attempt->answers->count(), там тоже втихаря идет обращение к бд. Только с вытягиванием из бд всех связанных записей, созданием их объектов (ну или прокси), и только потом у объекта ArrayCollection вызывается метод count, который возвращает кол-во записей.
Тут конечно нужно писать чистый sql/dql.
Кстати в доктрине для такого есть сверхленивые коллекции - они вычисляют count не загружая сами записи.
>>46525
> Сделал статическим, чтобы иметь доступ к этому методу из любого места кода, не создавая экземпляра класса
Это не уважительная причина для использования статических методов. Это скорее плохо так как ты по сути делаешь тут глобальные переменные (единая глобальная зарплата для всех работников). Как я написал, это не позволит создать 2 компании с разными зарплатами. Хотя по условию это и не требуется.
> А можно еще как-то получать доступ к коэффициентам из класса Employee?
Можно хранить сетку зарплат в компании или департаменте, а в работнике ссылку на компанию. Можно вообще убрать этот класс и назначать зарплату индивидульно.
> Когда смотрел дамп компании увидел, что они копируются в каждом работнике. Там 1000 символов, в среднем по 2 бита на символ / 8 = 250 байт * 101(сотрудник) = 24 кБ, если я правильно посчитал
Ты неправильно посчитал. Ты посчитал объем исходного кода, Но в памяти данные и методы хранятся в другой форме. Более того, методы одинаковы для всех объектов одного класса и они хранятся в одном экземплаяре. Копируются только поля, и там ксати своя специфика - обычная переменная, хранящая число, может занимать 50 байт.
Но в данном случае это все не имеет значения. PHP применяет хитрые оптимизации, вроде отложенног окопирования массивов. Если ты сделаешь 1000 копий массива и не меняешь их, то php в реальности дердит в памяти только одну копию.
В общем, не стоит заниматься оптимизаиями, не зная осоюенностей внутреннего устройства php. Если интересно, гугли статьи на хабре и читай internals раздел в мануале:
https://www.gitbook.com/book/romka/php-internals-book-ru/details
https://secure.php.net/manual/ru/internals2.php
> Обращаться к методу класса Company->getCoefficients из класса Employee не правильно?
Можно и так
> т.к. рассчет зп можно было запускать только из контекста класса Company, и передавать коэффициенты как аргумент вызова метода по цепочке Company > Department > Employee. Ведь так тоже не очень?
сложновато. Вообще, я подумал, проще всего их прямо в employee и прописать.
> Паттерн "Стратегия" для изменения алгоритма расчета, а ведь тут меняется не алгоритм, а только коэффициенты?
верно
> Класс Profession отдельно, для того чтобы можно было поменять профессию у работника (Employee), и создавать новые классы профессий по шаблону родительского класса Profession. Идея была в том, что сторонний программист, создавая новый класс профессии должен переопределить обязательные методы для класса новой профессии. Хотя его можно убрать из кода, программа будет работать и без него.
Стоит оставить. Не надо убирать.
> Решил не создавать объект класса АтикризисныеМеры, а напрямую обратиться к его методу. Думал статические методы именно для этого.
Статические методы обычно используют в функциях, которым не нужен конкретный объект и $this, которые для всех экземпляров объекта работают одинково. Ну например фнкция перевода миль в километры. Тут лучше обычный объект и обычные методы. Так как Антикризисный КОмитет это вполне себе объект.
> .{15}(ж|ш)ы.
получается перед ошибкой должно идти не менее 15 символов?
>>45309
> когда мы пишем $attempt->answers->count(), там тоже втихаря идет обращение к бд. Только с вытягиванием из бд всех связанных записей, созданием их объектов (ну или прокси), и только потом у объекта ArrayCollection вызывается метод count, который возвращает кол-во записей.
Тут конечно нужно писать чистый sql/dql.
Кстати в доктрине для такого есть сверхленивые коллекции - они вычисляют count не загружая сами записи.
>>46525
> Сделал статическим, чтобы иметь доступ к этому методу из любого места кода, не создавая экземпляра класса
Это не уважительная причина для использования статических методов. Это скорее плохо так как ты по сути делаешь тут глобальные переменные (единая глобальная зарплата для всех работников). Как я написал, это не позволит создать 2 компании с разными зарплатами. Хотя по условию это и не требуется.
> А можно еще как-то получать доступ к коэффициентам из класса Employee?
Можно хранить сетку зарплат в компании или департаменте, а в работнике ссылку на компанию. Можно вообще убрать этот класс и назначать зарплату индивидульно.
> Когда смотрел дамп компании увидел, что они копируются в каждом работнике. Там 1000 символов, в среднем по 2 бита на символ / 8 = 250 байт * 101(сотрудник) = 24 кБ, если я правильно посчитал
Ты неправильно посчитал. Ты посчитал объем исходного кода, Но в памяти данные и методы хранятся в другой форме. Более того, методы одинаковы для всех объектов одного класса и они хранятся в одном экземплаяре. Копируются только поля, и там ксати своя специфика - обычная переменная, хранящая число, может занимать 50 байт.
Но в данном случае это все не имеет значения. PHP применяет хитрые оптимизации, вроде отложенног окопирования массивов. Если ты сделаешь 1000 копий массива и не меняешь их, то php в реальности дердит в памяти только одну копию.
В общем, не стоит заниматься оптимизаиями, не зная осоюенностей внутреннего устройства php. Если интересно, гугли статьи на хабре и читай internals раздел в мануале:
https://www.gitbook.com/book/romka/php-internals-book-ru/details
https://secure.php.net/manual/ru/internals2.php
> Обращаться к методу класса Company->getCoefficients из класса Employee не правильно?
Можно и так
> т.к. рассчет зп можно было запускать только из контекста класса Company, и передавать коэффициенты как аргумент вызова метода по цепочке Company > Department > Employee. Ведь так тоже не очень?
сложновато. Вообще, я подумал, проще всего их прямо в employee и прописать.
> Паттерн "Стратегия" для изменения алгоритма расчета, а ведь тут меняется не алгоритм, а только коэффициенты?
верно
> Класс Profession отдельно, для того чтобы можно было поменять профессию у работника (Employee), и создавать новые классы профессий по шаблону родительского класса Profession. Идея была в том, что сторонний программист, создавая новый класс профессии должен переопределить обязательные методы для класса новой профессии. Хотя его можно убрать из кода, программа будет работать и без него.
Стоит оставить. Не надо убирать.
> Решил не создавать объект класса АтикризисныеМеры, а напрямую обратиться к его методу. Думал статические методы именно для этого.
Статические методы обычно используют в функциях, которым не нужен конкретный объект и $this, которые для всех экземпляров объекта работают одинково. Ну например фнкция перевода миль в километры. Тут лучше обычный объект и обычные методы. Так как Антикризисный КОмитет это вполне себе объект.
> Выполнил с использованием label с атрибутом for. Как можно обратится к label для установки фона когда внутри него находится input, с которого мы узнаём нажата кнопка или нет?
Никак нельзя, но можно после input поместить например спан или див и менять его свойства.
for неудобен тем что надо генерировать уникальные id.
Клавиатурная навигация работает, хорошо.
> .rb-container label {
> width: 50px;
Ты подгоняешь ширину под конкретный текст в этой задаче, нужно решение поуниверсальнее.
> transition: 0.1s;
надо писать какие свойства участуют
> position: absolute;
> top: -1000px;
не лучше через opacity скрыть? 1000px не так и много, бьывают страницы выше.
Так, в общем, неплохо сделано.
>> Не портит ли это модель?
>Модель не занимается редиректами. Это относится к контроллеру или представлению.
Моя вина, я имел ввиду модель программирования. Ведь так же можно выразиться о всей нашей MVC?
Перефразирую вопрос: Не выходит ли за рамки MVC использование в контроллере регистрации функции редиректа через сервис авторизации, а не через свой собственный хелпер? Ведь собственный хелпер, по сути, будет содержать те же методы что и сервис авториазации.
https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L42
>>48987
>Можно сделать чтобы и форма проставляла пароль, так иногда удобнее, но наверно в этом случае это должно происходить автоматически. Ты же не пишешь что форма должна проставить студенту имя и фамилию, а почему тогда про пароль пишешь?
Потому что сначала нужно проверить пароль на валидность а потом уже хешировать его.
>>48987
>Если студент умеет сам ставить себе пароль, то можно сделать так:
>
>пароль = форма->получитьПароль
>студент->назначитьПароль(пароль)
Умение студента самому себе ставить пароль, это имеется ввиду собственная функция получения хэша?
Мне нравится больше такой подход, потому что тогда не придется писать отдельную функцию назначитьПарольДляСтудента(студент, пароль) в сервисте авторазации, но я не понимаю откуда мы будем получать хэш, в предложенном тобой методе, если форма\студент и класс авторизации никак не связанны?
И что такое связь между классами? Я могу сказать что форма и класс авторизации связанны тем, что форма содержит в себе сущность студента, которая содержит в себе метод работающий с классом авторизации, но ты, по всей видимости, не согласен с этим, поэтому, логично будет спросить, как определяется эта связь?
Является ли сервис авторизации аутентификацией и при этом не является самой авторизацией?
>>48987
>> https://github.com/someApprentice/Students/blob/master/app/Model/Entity/Student.php#L40
>> fillDataFromDB(array $result)
>Модель лучше отвязать от базы данных, а метод перенести в класс работы с БД. Вместо того чтобы поместмть работу с БД в один класс, ты ее размазываешь по 2 классам.
Но этот метод никак не работает с БД, он просто получает переданные данные оттуда. Почему этот метод относится больше к классу работы с БД, чем к классу студента, ведь он же с самой БД никак не взаимодействует, а только меняет данные в классе студента?
>И я не вижу смысла делать 2 метода fillFrom ... Я бы сделал олин метод, который ставит любые поля, а проверку по белому списку сделал бы где-то выше, например в классе формы.
Получается нужно белый список передавать в эту функцию fillFromArray($allowed, $array)? А как тогда передавать его в классе БД? Писать прямо по среди функции? Это же не его задача, почему класс с БД должен знать что есть какой-то белый список для студента?
public function getStudentByСolumn($column, $value)
{
...
$allowed = [...];
$student->fillDataFromArray($allowed, $result);
...
}
>>48987
>> Если бы я пользовался куками, данные, такие как Имя, Фамилия, Возраст и так далее, тоже были бы лишними?
>Конечно, лишние. Достаточно иметь id, все данные студента есть в базе.
Тогда при выводе данных нужно получать их из БД?
>>48987
>>>Но и в этом случае сессия не нужна.
>> Сессиями больше не пользуются?
>Тут просто кук же хватит, зачем сессия?
>А теперь объясни свою точку зрения. Почему надо использовать сессию вместо кук и почему надо в ней хранить имя и фамилию? Какая от этого выгода?
Если пользователь не захочет оставаться залогиненым, то можно использовать сессию, а не создавать кукисы на 30 минут. К тому же, пользователь не сможешь отредактировать сессии, в отличие от кук. Но я не настаиваю на сессиях. Просто не понятно, почему нет.
>и почему надо в ней хранить имя и фамилию? Какая от этого выгода?
Когда я решал эту задачу я отталкивался от нашей старой задачи на простую регистрацию, где мы делали её процедурно https://github.com/someApprentice/simpleRegistration
Там мы использовали кукисы и сессии для вывода данных:
https://github.com/someApprentice/simpleRegistration/blob/master/index.php#L7-L11
https://github.com/someApprentice/simpleRegistration/blob/master/Lib/functions.php#L65
Но, тем не менее, меня все же смутило что мы берем данные из кук, а не из БД, ведь куки можно отредактировать самостоятельно, не смотря даже на то что это не на что не повлияет, кроме как на личное отображение страницы.
>>48987
>Я тут не вижу циклов и сложных манипуляций массивами. Достаточно сделать 2 метода: авторизоватьсяПоФамилии и авторизоватьсяПоемайлу и вызвать их по очереди.
Эти методы реализуются в контроллере? Могу я обойтись без них, а просто реализовывать авторизацию прямо в условии, даже не смотря на то что в условиях будет копипаста? Хотя, если делать это в методах, то в них тоже будет та же самая копипаста.
если (логин выглядит как емайл и (в базе есть студент с таким емайлом и паролем)) {
то это он
создаемКукисы
ридеректим
выходим
}
если (логин (соответствует фамилии ровно одного студента и пароль подходит)) то {
студент найден
создаемКукисы
ридеректим
выходим
}
Или вовсе написать одно условие с выборкой(?):
если (
(логин выглядит как емайл и (в базе есть студент с таким емайлом и паролем))
или
(логин (соответствует фамилии ровно одного студента и пароль подходит))
) {
...
}
>> Не портит ли это модель?
>Модель не занимается редиректами. Это относится к контроллеру или представлению.
Моя вина, я имел ввиду модель программирования. Ведь так же можно выразиться о всей нашей MVC?
Перефразирую вопрос: Не выходит ли за рамки MVC использование в контроллере регистрации функции редиректа через сервис авторизации, а не через свой собственный хелпер? Ведь собственный хелпер, по сути, будет содержать те же методы что и сервис авториазации.
https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L42
>>48987
>Можно сделать чтобы и форма проставляла пароль, так иногда удобнее, но наверно в этом случае это должно происходить автоматически. Ты же не пишешь что форма должна проставить студенту имя и фамилию, а почему тогда про пароль пишешь?
Потому что сначала нужно проверить пароль на валидность а потом уже хешировать его.
>>48987
>Если студент умеет сам ставить себе пароль, то можно сделать так:
>
>пароль = форма->получитьПароль
>студент->назначитьПароль(пароль)
Умение студента самому себе ставить пароль, это имеется ввиду собственная функция получения хэша?
Мне нравится больше такой подход, потому что тогда не придется писать отдельную функцию назначитьПарольДляСтудента(студент, пароль) в сервисте авторазации, но я не понимаю откуда мы будем получать хэш, в предложенном тобой методе, если форма\студент и класс авторизации никак не связанны?
И что такое связь между классами? Я могу сказать что форма и класс авторизации связанны тем, что форма содержит в себе сущность студента, которая содержит в себе метод работающий с классом авторизации, но ты, по всей видимости, не согласен с этим, поэтому, логично будет спросить, как определяется эта связь?
Является ли сервис авторизации аутентификацией и при этом не является самой авторизацией?
>>48987
>> https://github.com/someApprentice/Students/blob/master/app/Model/Entity/Student.php#L40
>> fillDataFromDB(array $result)
>Модель лучше отвязать от базы данных, а метод перенести в класс работы с БД. Вместо того чтобы поместмть работу с БД в один класс, ты ее размазываешь по 2 классам.
Но этот метод никак не работает с БД, он просто получает переданные данные оттуда. Почему этот метод относится больше к классу работы с БД, чем к классу студента, ведь он же с самой БД никак не взаимодействует, а только меняет данные в классе студента?
>И я не вижу смысла делать 2 метода fillFrom ... Я бы сделал олин метод, который ставит любые поля, а проверку по белому списку сделал бы где-то выше, например в классе формы.
Получается нужно белый список передавать в эту функцию fillFromArray($allowed, $array)? А как тогда передавать его в классе БД? Писать прямо по среди функции? Это же не его задача, почему класс с БД должен знать что есть какой-то белый список для студента?
public function getStudentByСolumn($column, $value)
{
...
$allowed = [...];
$student->fillDataFromArray($allowed, $result);
...
}
>>48987
>> Если бы я пользовался куками, данные, такие как Имя, Фамилия, Возраст и так далее, тоже были бы лишними?
>Конечно, лишние. Достаточно иметь id, все данные студента есть в базе.
Тогда при выводе данных нужно получать их из БД?
>>48987
>>>Но и в этом случае сессия не нужна.
>> Сессиями больше не пользуются?
>Тут просто кук же хватит, зачем сессия?
>А теперь объясни свою точку зрения. Почему надо использовать сессию вместо кук и почему надо в ней хранить имя и фамилию? Какая от этого выгода?
Если пользователь не захочет оставаться залогиненым, то можно использовать сессию, а не создавать кукисы на 30 минут. К тому же, пользователь не сможешь отредактировать сессии, в отличие от кук. Но я не настаиваю на сессиях. Просто не понятно, почему нет.
>и почему надо в ней хранить имя и фамилию? Какая от этого выгода?
Когда я решал эту задачу я отталкивался от нашей старой задачи на простую регистрацию, где мы делали её процедурно https://github.com/someApprentice/simpleRegistration
Там мы использовали кукисы и сессии для вывода данных:
https://github.com/someApprentice/simpleRegistration/blob/master/index.php#L7-L11
https://github.com/someApprentice/simpleRegistration/blob/master/Lib/functions.php#L65
Но, тем не менее, меня все же смутило что мы берем данные из кук, а не из БД, ведь куки можно отредактировать самостоятельно, не смотря даже на то что это не на что не повлияет, кроме как на личное отображение страницы.
>>48987
>Я тут не вижу циклов и сложных манипуляций массивами. Достаточно сделать 2 метода: авторизоватьсяПоФамилии и авторизоватьсяПоемайлу и вызвать их по очереди.
Эти методы реализуются в контроллере? Могу я обойтись без них, а просто реализовывать авторизацию прямо в условии, даже не смотря на то что в условиях будет копипаста? Хотя, если делать это в методах, то в них тоже будет та же самая копипаста.
если (логин выглядит как емайл и (в базе есть студент с таким емайлом и паролем)) {
то это он
создаемКукисы
ридеректим
выходим
}
если (логин (соответствует фамилии ровно одного студента и пароль подходит)) то {
студент найден
создаемКукисы
ридеректим
выходим
}
Или вовсе написать одно условие с выборкой(?):
если (
(логин выглядит как емайл и (в базе есть студент с таким емайлом и паролем))
или
(логин (соответствует фамилии ровно одного студента и пароль подходит))
) {
...
}
git clone репа
git status
git add .
git commit -m ""
git log
git remote add origin репа
git push origin master
Про ветки знаю, пока не доводилось использовать
>Вообще, вопрос глупый так как ответ на него ничего не дает. Я бы никогда таких вопросов не спрашивал.
Вот поэтому ты и пограммист, а не йоба-мэн, принимающий на работу, оу йес.
Ответ на него даёт представление о человеке.
Глупо думать, что на собеседовании проверяют только знание каких-то моментов из программирования.
Работать-то в итоге с человеком, а не с набором знаний о РНР и прочем.
> Вот поэтому ты и пограммист, а не йоба-мэн, принимающий на работу, оу йес.
Нет, не потому. Вопрос бессмысленный так как человека нанимают чтобы писать код, и самое важное это умение писать код.
Но ты, я вижу, знаешь что-то, что обычные люди вроде нас не понимают. Объясни какие варианты ответа на этот вопрос возможны и как их надо интерпретировать.
400x226
Я просто сам принимал на работу несколько раз - вот и вся моя возможная необычность.
Инструкции от начальства были чёткие: без высшего образования даже не рассматривать (сфера такая), неадекватов не брать (внешний вид, поведение, интересы).
Поэтому куча вопросов была не по теме - просто чтобы узнать, что за человек.
Я уже давненько не принимал на работу (наоборот, увольнять приходилось из-за проклятого кризиса), а вот знакомого, который недавно принимал на работу, обязали ознакомиться с цифровым профилем соискателя - по возможности. Профили в соцсетях, просто инфа в Гугле, всё-такое.
>человека нанимают чтобы писать код, и самое важное это умение писать код.
Почти любого можно обучить писать код (сужу по себе, азаза, лалка, сам себя затролел). А на работу принимают, чтобы вместе решать какие-то задачи, тесно общаться и вообще быть бок о бок 8 часов 5 дней в неделю.
Дело не в интерпретации ответа, а в простой адекватности соискателя.
Ты задал тот вопрос про РНР, а он испугался, ручонки вспотели, ножки задрыгались, "М-м-м-м, а де моё тестовое? Я сюда пришёл задачки решать, а не лясы точить!.." - и прочее. Зрачки расширились, зубы оскалены.
И с таким человеком нужно будет бок о бок 5 дней в неделю?
Максимум естественности.
Ответ братишки выше - адекватный.
Остальное - не имеет значения.
Правильного ответа нет и быть не может.
Ты уже троллишь по моему. Написал такую телегу, а как понимать ответы на вопрос не написал. Потому что очевидно что на него ответят стандартным ожидемым ответом. Если научить можно любого, почему бы тебе и не брать любых людей и не обучать их за счет компании? Наверно потому, что ты мамкин фантазер.
> без высшего образования даже не рассматривать (сфера такая)
Не хочется в тысячный раз подымать донельзя заезженную тему, но почему нельзя без ВО, что за сфера такая? Я уже свою шарагу не выдерживаю.
>>49350
>Если научить можно любого, почему бы тебе и не брать любых людей и не обучать их за счет компании?
Он вполне внятно описал, почему нельзя брать кого попало, что "с таким человеком нужно будет бок о бок 5 дней в неделю".
Тут мы изучаем язык PHP (а также JS/CSS/HTML/SQL)
Существуют ли php рекрутеры? Если да, подскажите где они обитают и как им попадаться на глаза?
>как понимать ответы на вопрос не написал.
Всё ясно, у вас программирование головного мозга.
Спасибо, до свидания, мы вам перезвоним.
>Если научить можно любого, почему бы тебе и не брать любых людей и не обучать их за счет компании?
Зачем, когда куча готовых реальных специалистов и нужно просто выбрать наиболее - какого? какого? - адекватного.
"Брать любых людей", "обучать за счёт компании" - ну ты даёшь.
Хотя знакомый рассказывал, как получал стипендию от какого-то металлургического комбината, когда учился в техникуме, проходил там практику, - его хотели таким образом поддержать, так или иначе готовили своего работника. Если не ошибаюсь, такое повсеместно в Европе и Америке бывает.
>>49354
>Не хочется в тысячный раз подымать донельзя заезженную тему, но почему нельзя без ВО, что за сфера такая?
Высшее образование в общем и целом - это как лакмусовая бумажка. Человек после школы определился со сферой, поступил в вуз - и окончил его, что немаловажно. Это значит, что у человека есть какие-никакие цели в жизни, он их так или иначе добивается. Высшее образование так или иначе гарантирует, что человек на многое способен, обладает многими навыками.
Сфера работы - издательское дело, одно из крупнейших российских издательств.
Никто не будет рассматривать кандидатуру соискателя, не имеющего высшего образования, - любого высшего образования, гуманитарного или технического - без разницы.
И образование играет здесь роль именно лакмусовой бумажки - характеризует человека, а не обязательно требуется из-за специфики самой работы. Хотя приоритет отдаётся гуманитарному ( филологическому) образованию, конечно.
Однако и высшее образование не гарантирует, что человек стопроцентно получит работу.
А вдруг он полный неадекват, азаза?
>вакансия веб-программиста
>приоритет отдаётся гуманитарному ( филологическому) образованию, конечно.
>дружный коллектив, печеньки-корпоративы))))
Ну ясно в общем. Кто-то ищет профессионала, а кто-то члена в дружный коллектив, лишь бы человек был хороший)
Все зависит от конторы. В больших компаниях, где куча отделов, и программисту приходится общаться с левыми людьми типа менеджеров или сеошников, конечно в первую очередь важны социальные скиллы.
В маленьких конторах в первую очередь профессиональные навыки.
Хотя клинические психи или аутисты-тормоза естественно не нужны, но разве их так много?
>>49342
>Зрачки расширились, зубы оскалены.
И часто такие на практике встречаются? По-моему это либо клинический случай, либо 15-летние позеры из rf.
>webm
Шикарный демагог кстати, можно наравне с геббельсом заносить в учебники.
Ебучий сброд, по вашим вопросам делают оценку и по вам. Если кто-то после таких вопросов дальше хочет устроится в ваш быдлобиореактор, то вы набираете только максимум отбитых дегенератов. Надеюсь у вас устроют какой-нибудь теракт чтобы очистить генофонд человечества.
Семен семеныч.
>Шикарный демагог кстати, можно наравне с геббельсом заносить в учебники
Прошу заметить что поп говорит во втором лице не помню как правильно называется эта форма речи - "ты", "тебе". Не разу не сказал, например, "Мы живем в аду". Более того его выражения полны презрения к аудитории "харя", "сынок", "нажраться", "наколоться". Никакого глубокого психоанализа и не нужно чтобы понять каким лицемерием обладают служители рпц.
Ты прав, я только предполагаю, что в случае с вопросом о РНР - это проверка адекватности.
Но ты всё-таки утрируешь слегка: профессиональные навыки всё-таки в большом почёте, этого никто не отменял. Хотя они и не гарантия трудоустройства - а разве где-то бывает по-другому?
>В маленьких конторах в первую очередь профессиональные навыки
У меня был печальный опыт работы с довольно неплохим программистом, который кучу плагинов написал для WP, свои темы, разные приложения и т.п.
Просто адов кошмар - многое не принимает во внимание, многое интерпретирует совершенно непонятно, перескакивает с пункта на пункт в ТЗ.
В итоге ничего не вышло, хотя профессиональные навыки у него были вполне на высоте.
Кулстори, бро, короче.
>И часто такие на практике встречаются?
Отсеиваем на этапе собеседования же, тут сложно сказать.
Ко мне приходил парнишка с - как сказал бы Джером Дэвид Сэлинджер - обкусанными до мяса ногтями - я его быстренько так собеседовал и отпустил с Богом. Остальные несколько человек были вполне приемлемыми, с некоторыми до сих пор работаем.
>И часто такие на практике встречаются?
>>49390
>Шикарный демагог кстати, можно наравне с геббельсом заносить в учебники.
У них всё связано с тем, что человек осознаёт свою греховность (другие и не идут в церковь).
Христос спрашивал бросавших в гулящую женщину камни - кто без греха?
Принимаешь осознание греховности -> чувствуешь его правоту -> стремишься к избавлению от греха.
Не знаю, мне очень нравятся его слова, в какой-то момент буквально до слёз. Особенно, про мешающих спать соловьёв. Аз есмь грешен как чертяка потому что...
>>49576
>Не разу не сказал, например, "Мы живем в аду".
А почему он должен был это сказать?
Он живёт полной жизнью, так или иначе исцеляет души людей. В него верят, его слушают, ему известна какая-то Истина.
Он не находится в аду - по его терминологии.
>Прошу заметить что поп говорит во втором лице не помню как правильно называется эта форма речи
Обращение, наверное.
Он статью "ты" на лурачке прочитал прост, и решил потралить ссачеров)))00
На самом деле обычный прием демагогии под названием "перевод стрелок".
Правда действует только на впечатлительных маменькиных сынков на сосаче, или старух в церкви.
Допустим, журналист обращается к коррумпированному чиновнику с вопросом: "откуда были взяты средства на строительство храмов рпц? а на вот эти наручные часы?", следует ответ:
"а ты почему спрашиваешь? Наверное у тебя в жизни не все в порядке. Может тебе бабы не дают? У тебя кривые зубы. Жиртрест. У тебя нет отца? Да ты же ублюдок. Что такое? Ой, ты сейчас заплачешь)))"
В эпизоде 1501 южного парка похожая ситуация: Картман перед всеми хвалится своим новым айпадом, тут к нему подходит крейг и говорит:
"Том Зальцман сказал, что у тебя нет айпада, что ты приклеил наклейку на кусок пластика и понтуешься"
Пауза.
"Отец Тома Зальцмана алкаш, а еще его мать бьет!"
>>49599
>Принимаешь осознание греховности -> чувствуешь его правоту -> стремишься к избавлению от греха.
Те качества, которые эти люди объявят греховными, я таковыми не считаю, и не считаю себя должным с ними согласиться в этом плане.
Я считаю греховными их, за то что они одурачивают глупых и слабым духом людей, забирают их последние копейки на свои дорогие машины и храмы, а также сотрудничают с бандитскими олигархическими кланами в деле популизма и пропаганды.
Они тормозят развитие человечества, как в культурном, так и техническом плане.
И вообще судя по всему ты даже не программист, а какой-нибудь редактор или сеошник, как ты можешь проводить собеседования на программиста? Школьную задачку на вектор хоть дорешал?
Признай, что тебе просто скучно на работе, поэтому отсеиваешь необщительных чуваков, чтобы было с кем попиздеть.
Я тоже могу "проповедовать".
Оп-слоупок, ты перекат хоть к тысячному посту оформишь? Люди заждались, сейчас быстро до тысячи добьем флудом.
Нет, фабрика не для этого. Фабрика для случаев когда мы хотим иметь возможность на процесс создания объектов другим классом.
>>49599
> довольно неплохим программистом
> многое не принимает во внимание, многое интерпретирует совершенно непонятно, перескакивает с пункта на пункт в ТЗ.
Ты сам себе противоречишь
Для проверки "адекватности" (что это значит?) достаточно просто лично побеседовать с человеком, и дополнительные бессмысленные вопросы задавать не надо. Такие вопросы обычно любят задавать не-программисты, например, менеджеры или HR.
>>49370
Про образование забавно. Одно дело когда такие требования выдвигает серьезная крупная компания - их можно понять, им хочется как-то унифицировать требования и проще отсеять хорошего кандидата чем пропустить нехорошее - но совсем другое когда это требования для натягивателя шаблонов на вордпресс.
>>49370
> Сфера работы - издательское дело
Это не IT компания.
Способы описаны в интернетре, у каждого свои ограничения, например зависит от того известны ли ширина и высота объекта или нет, погугли и выбери тот который подойдет.
>>49373
Вообще-то он работает, ты наверно просто не знаешь в каких именно случаях. Я подскажу. VA работает либо на ячейках таблицы (с display table-cell) либо на инлайн-блоках и инлайн элементах в строчном конексте форматирования. Он не позволяет выровнять что угодно относительно чего угодно. Судя по твоим вопросам, у тебя есть непонимание некоторых вещей CSS и я бы советовал заполнять эти пробелы.
На эту тему целая книга есть: http://css-live.ru/articles/obzor-inlajnovyj-kontekst-formatirovaniya.html
Спасибо, и ещё вопрос: Factory Method решает какую реализуацию инстанциировать и возвращает её. Каким образом решает? В примерах используется switch/case, хорошая ли это практика?
> Не выходит ли за рамки MVC использование в контроллере регистрации функции редиректа через сервис авторизации, а не через свой собственный хелпер?
Немного странно что класс посвященный залогиниванию, занимается у тебя только редиректами. Может тогда правильнее его назвать RedirectHelper? За рамки MVC не выходит.
>>Можно сделать чтобы и форма проставляла пароль, так иногда удобнее, но наверно в этом случае это должно происходить автоматически. Ты же не пишешь что форма должна проставить студенту имя и фамилию, а почему тогда про пароль пишешь?
> Потому что сначала нужно проверить пароль на валидность а потом уже хешировать его.
Я имею в виду, у тебя у формы есть метод, который возвращает заполненную модель студента. Пусть он заодно и хеш пароля ему проставляет. ЛИбо пусть форма не связыается с паролями вообще.
Вообще, мне кажется, удобнее всего сделать в модели Студента метод "задать пароль". Для хеширования можно сделать статический метод в хелпере авторизациии. Можно даже в самом студенте конечно. Лишь бы он был в одном экземпляре, а не раскидан по коду несколько раз.
> Умение студента самому себе ставить пароль, это имеется ввиду собственная функция получения хэша?
Она может быть собственной, а может находиться в другом классе в виде статической функции.
> но я не понимаю откуда мы будем получать хэш, в предложенном тобой методе, если форма\студент и класс авторизации никак не связанны?
Можно сделать статический метод хеширования в любом из этих классов.
> И что такое связь между классами?
Это значит что например один класс в себе содержит ссылку на другой, или вызывает его статический метод. Ну то есть представь что 2 класса пишут 2 разных человека. Если они могут их написать не заглядывая в код друг друга, то классы полностью отделены и никак не связаны.
Ну например для того чтобы написать класс работы с бапзой данных, тебе надо знать про модуль студента, какие у нее поля и методы, верно? А вот про валидатор или класс автоизации тебе знать не надо, так как они тут никакого отношения не имеют.
В твоем случае, ты в форму передаешь как аргумент класс авторизации, потому форма о нем знает. Не то чтобы это плохо, но тогда лучше передавать его через конструктор, а еще лучше чтобы она о нем не знала.
> Является ли сервис авторизации аутентификацией и при этом не является самой авторизацией?
Давай считать что он занимается и тем и другим.
> Но этот метод никак не работает с БД, он просто получает переданные данные оттуда.
Вот опять. ВОт скажи, ты можешь написать моудль студента, не зная структуры базы данных? Нет, не можешь так как ты расчитываешь что тебе придут поля с определенными названиями. Получается, знание о базе данных у тебя вытекло из StudentGateway и затекло в Student, что плохо. Лучше когда за работу с БД отвечает только один класс, а не два.
> Получается нужно белый список передавать в эту функцию fillFromArra
Есть такие варианты:
- проставлять данные из базы в StudentGateway
- передавать список полей явно
- сделать метод, принимающий любые поля, но в контроллере отфильтровывать POST по списку полей
- сделать у функции опцию вроде "разрешить любые поля"
- сделать 2 функции, одна проверяет список полей, другая нет
> Не выходит ли за рамки MVC использование в контроллере регистрации функции редиректа через сервис авторизации, а не через свой собственный хелпер?
Немного странно что класс посвященный залогиниванию, занимается у тебя только редиректами. Может тогда правильнее его назвать RedirectHelper? За рамки MVC не выходит.
>>Можно сделать чтобы и форма проставляла пароль, так иногда удобнее, но наверно в этом случае это должно происходить автоматически. Ты же не пишешь что форма должна проставить студенту имя и фамилию, а почему тогда про пароль пишешь?
> Потому что сначала нужно проверить пароль на валидность а потом уже хешировать его.
Я имею в виду, у тебя у формы есть метод, который возвращает заполненную модель студента. Пусть он заодно и хеш пароля ему проставляет. ЛИбо пусть форма не связыается с паролями вообще.
Вообще, мне кажется, удобнее всего сделать в модели Студента метод "задать пароль". Для хеширования можно сделать статический метод в хелпере авторизациии. Можно даже в самом студенте конечно. Лишь бы он был в одном экземпляре, а не раскидан по коду несколько раз.
> Умение студента самому себе ставить пароль, это имеется ввиду собственная функция получения хэша?
Она может быть собственной, а может находиться в другом классе в виде статической функции.
> но я не понимаю откуда мы будем получать хэш, в предложенном тобой методе, если форма\студент и класс авторизации никак не связанны?
Можно сделать статический метод хеширования в любом из этих классов.
> И что такое связь между классами?
Это значит что например один класс в себе содержит ссылку на другой, или вызывает его статический метод. Ну то есть представь что 2 класса пишут 2 разных человека. Если они могут их написать не заглядывая в код друг друга, то классы полностью отделены и никак не связаны.
Ну например для того чтобы написать класс работы с бапзой данных, тебе надо знать про модуль студента, какие у нее поля и методы, верно? А вот про валидатор или класс автоизации тебе знать не надо, так как они тут никакого отношения не имеют.
В твоем случае, ты в форму передаешь как аргумент класс авторизации, потому форма о нем знает. Не то чтобы это плохо, но тогда лучше передавать его через конструктор, а еще лучше чтобы она о нем не знала.
> Является ли сервис авторизации аутентификацией и при этом не является самой авторизацией?
Давай считать что он занимается и тем и другим.
> Но этот метод никак не работает с БД, он просто получает переданные данные оттуда.
Вот опять. ВОт скажи, ты можешь написать моудль студента, не зная структуры базы данных? Нет, не можешь так как ты расчитываешь что тебе придут поля с определенными названиями. Получается, знание о базе данных у тебя вытекло из StudentGateway и затекло в Student, что плохо. Лучше когда за работу с БД отвечает только один класс, а не два.
> Получается нужно белый список передавать в эту функцию fillFromArra
Есть такие варианты:
- проставлять данные из базы в StudentGateway
- передавать список полей явно
- сделать метод, принимающий любые поля, но в контроллере отфильтровывать POST по списку полей
- сделать у функции опцию вроде "разрешить любые поля"
- сделать 2 функции, одна проверяет список полей, другая нет
> Это же не его задача, почему класс с БД должен знать что есть какой-то белый список для студента?
Он должен знать какие поля есть в БД. Меня правда беспокоит что у нас тут список полей дублируется многократно и при добавлении или переименовании поля придется в куче мест менять.
какие ты видишь варианты? нам надо и загружать студента из базы, и разрешить редактировать через форму.
> >Конечно, лишние. Достаточно иметь id, все данные студента есть в базе.
> Тогда при выводе данных нужно получать их из БД?
Да
> Если пользователь не захочет оставаться залогиненым, то можно использовать сессию, а не создавать кукисы на 30 минут.
Но можно и создать куки. Куки не требуют места на севрере например.
> К тому же, пользователь не сможешь отредактировать сессии, в отличие от кук
Это да, потому нужен токен для проверки подлинности пользователя, и надо его проверять по базе.
Просто мне кажется что разлогиниваться через полчаса довольно неудобно.
> Когда я решал эту задачу я отталкивался от нашей старой задачи на простую регистрацию, где мы делали её процедурно
ну, это было давно, сейчас мы можем и посложнее вещи делать.
> ведь куки можно отредактировать самостоятельно, не смотря даже на то что это не на что не повлияет, кроме как на личное отображение страницы.
Потмоу что не на что не повлияет, я думаю. Но лучше не хранить там имя вообще.
> если (логин выглядит как емайл и (в базе есть студент с таким емайлом и паролем)) {
> то это он
> создаемКукисы
> ридеректим
> выходим
> }
Не "создаемКукисы" а вызываем метод "залогинитьСтудента".
> Или вовсе написать одно условие с выборкой(?):
Скорее всего будет громоздко
> Это же не его задача, почему класс с БД должен знать что есть какой-то белый список для студента?
Он должен знать какие поля есть в БД. Меня правда беспокоит что у нас тут список полей дублируется многократно и при добавлении или переименовании поля придется в куче мест менять.
какие ты видишь варианты? нам надо и загружать студента из базы, и разрешить редактировать через форму.
> >Конечно, лишние. Достаточно иметь id, все данные студента есть в базе.
> Тогда при выводе данных нужно получать их из БД?
Да
> Если пользователь не захочет оставаться залогиненым, то можно использовать сессию, а не создавать кукисы на 30 минут.
Но можно и создать куки. Куки не требуют места на севрере например.
> К тому же, пользователь не сможешь отредактировать сессии, в отличие от кук
Это да, потому нужен токен для проверки подлинности пользователя, и надо его проверять по базе.
Просто мне кажется что разлогиниваться через полчаса довольно неудобно.
> Когда я решал эту задачу я отталкивался от нашей старой задачи на простую регистрацию, где мы делали её процедурно
ну, это было давно, сейчас мы можем и посложнее вещи делать.
> ведь куки можно отредактировать самостоятельно, не смотря даже на то что это не на что не повлияет, кроме как на личное отображение страницы.
Потмоу что не на что не повлияет, я думаю. Но лучше не хранить там имя вообще.
> если (логин выглядит как емайл и (в базе есть студент с таким емайлом и паролем)) {
> то это он
> создаемКукисы
> ридеректим
> выходим
> }
Не "создаемКукисы" а вызываем метод "залогинитьСтудента".
> Или вовсе написать одно условие с выборкой(?):
Скорее всего будет громоздко
>Те качества, которые эти люди объявят греховными, я таковыми не считаю, и не считаю себя должным с ними согласиться в этом плане.
Ты бы и не пошёл никогда в церковь, разве не так?
Проповедь-то обращена к прихожанам и к символическому греховному человеку, который всем вечно недоволен и т.п. Кто-то может узнать в этом человеке себя - только и всего.
>И вообще судя по всему ты даже не программист, а какой-нибудь редактор или сеошник, как ты можешь проводить собеседования на программиста?
Да я же не на программиста проводил собеседования, а просто описывал свой опыт со стороны рекрутера - для чего могут задаваться такие отвлечённые вопросы, как "Почему вы выбрали РНР?". SEO, простые сайты на WP и HTML - моё всё. Сейчас уже полгода варюсь с вами в жарких котлах этих тредов.
>Школьную задачку на вектор хоть дорешал?
Решил недавно с грехом (siс!) пополам. ОП не одобрил. Антикризисные меры как применить - я пока не представляю. Изучаю сейчас MySQL, наваял подобие Guestbook вчера.
>>49632
>Ты сам себе противоречишь
Дело в том, что у него собственное понимание того, каким продукт должен быть в итоге. А я - в силу того, что в то время вообще ничего не понимал, - не мог повлиять на его решение идти по ТЗ как попало. Он говорил мне, что так лучше, - а я и верил. Несколько различных модулей сделаны наполовину - при видимости готового решения.
А его собственные работающие приложения ведь никто не отменял, они есть, работают нормально.
Итого: отличные навыки программирования, но полное нежелание работать в команде (из двух человек, тандеме, так сказать).
>Для проверки "адекватности" (что это значит?) достаточно просто лично побеседовать с человеком, и дополнительные бессмысленные вопросы задавать не надо.
Не всегда, ну да ладно.
>Одно дело когда такие требования выдвигает серьезная крупная компания
Так и есть.
>Это не IT компания.
Я говорил, ну да ладно.
Зачем нужен Factory Method. Вот допустим кто-то написал класс Скачивальщик который отправляет HTTP запрос и возвраащет Ответ:
скачивальщик = new Скачивальщик;
запрос = Запрос::скачать('http://example.com/1.html');
ответ = скачивальщик->отправитьЗапрос(запрос);
теперь допустим ты хочешь расширить класс Ответ через наследование, добавив ему полезные методы или поменяв его поведение. Как ты заставишь Скачивальщик создавать объеты класса УлучшенныйОтвет вместо Ответ? Тут-то и поможет паттерн Factory Method, при условии что автор класса Скачивальщик вынес создание ответа в отдельный метод.
> Каким образом решает? В примерах используется switch/case, хорошая ли это практика?
Это вообще никакого значения не имеет тут. как хочешь так и проверяй. Ты по моему суть паттерна не понял, а спрашиваешь про мелкие детали реализации.
> Да я же не на программиста проводил собеседования, а просто описывал свой опыт со стороны рекрутера - для чего могут задаваться такие отвлечённые вопросы
Их задают потому что по теме сказать ничего не могут.
> что у него собственное понимание того, каким продукт должен быть в итоге.
Если он не старший разработчик то у него и не должно быть понимания - у него должно быть ТЗ или описание задачи.
> Несколько различных модулей сделаны наполовину - при видимости готового решения.
Скорее всего он просто не разобрался в силу нехватки знаний, опыта или времени. Вот когда на собеседовании задают не технические вопросы, а общие, таким людям как раз пролезть легче.
> Итого: отличные навыки программирования
нет
>Проповедь-то обращена к прихожанам и к символическому греховному человеку
Она явно обращена как к прихожанам, так и к оппонентам церкви и бандитской власти (обрати внимание, он упомянул слово "политика"), так как транслировалась по телевидению, возможно даже по национальным каналам.
Цели речи две: убедить прихожан в том, что атеисты и либералы это пидоры и неудачники, ну и заодно задеть самых неуверенных в себе слушателей этой речи со стороны оппозиции.
Если бы целью речи было убедить прихожан в том, что они должны любить и прощать близких и вообще всех окружающих, не осуждать, относиться с сочувствием, пытаться понять и помочь. Но нет, содержание речи противоположное: все кто не ходит в церковь и не голосует за путина - лузеры и плохие люди, так что ходите в церковь, платите десятину, голосуйте за путина.
Мой вывод: рпц это псевдохристианская организация, цель которой - оболванивание людей и пополнение легиона доверчивых, манипулируемых и глупых избирателей, которые никогда не будут иметь своего мнения, а спрашивать разрешения у красномордого жлоба-проповедника или царя-батюшки.
Вместо того, чтобы воспитывать умных людей, умеющих самостоятельно анализировать свою жизнь и проблемы, церковь оболванивает людей, хуже того навязывает ксенофобию, мракобесие, нетерпимость.
У любого человека, если он не безмозглый баран, должна существовать одна "совесть": не легко манипулируемое чувство вины, как у щенка написавшего в ботинок, а критическое мышление, основанное на логике и рассудке.
Да я помню тебя, ты тот симпатичный толстячок-редактор.
Надеюсь у тебя достаточно развито хоть какое-то мышление, чтобы не обижаться на резкие слова на дваче?
Все что здесь пишут, пишут с некоторыми целями, обычно ради провокаций или развлечения, и не нужно воспринимать всерьез.
Хотя сказка ложь, да в ней намек.
Смотрите какое интересное ТЗ, тут и постгре и архивы и сложная структура таблиц и Редис. Может кто-нибудь хочет сделать аналог чатика в вконтакте по такому ТЗ чисто в обучающих целях? Могли бы одновременно с кем-нить делать, чтобы был пример чужого кода.
https://www.fl.ru/projects/2797768/skript-lichnaya-perepiska-chat-po-funktsionalu-blizka-k-vkcom.html
Довольно бредовое. Если в обучающих целях, то я бы выкинул оттуда все кроме PHP и постгреса для начала и сделал нормализованную Бд.А только потом занимался бы оптимизациями.
>Аж палец заболел прокручивать.
Для этого есть ссылка "вниз" и "вверх" соответственно.
Из тз вообще ничего не понял, у меня по несколько вопросов на каждую строку этой шизофазии.
Я сейчас наверное накатаю вопросы, а оп или кто-нибудь пусть ответит. Получится практическое занятие на тему
"взаимопонимание заказчика и программиста-аутиста".
Решать само задание естественно не собираюсь, нет ни квалификации, ни желания.
> Можно сказать, что мы медиа сайт, т.к. у нас очень много всевозможных статей,
> 90% приходит из поиска Google.
> почти полгода мы разрабатывали Single Page Application версию
Дальше еще велесее:
> Из-за неравномерной нагрузки на сервера, и невозможности гибко оптимизировать страницы, было решено разделить сайт на backend (текущая версия на Drupal) и frontend (SPA на AngularJS).
> Из Drupal сделали REST сервер,
То есть нагрузка по идее должна была остаться примерно такая же.
Я бы советовал этим товарищам назвать статью "Ёжик в тумане". А, хотя жалко такое тянущее на отдельную книгу про современные методологии разработки приложений название использовать для одной статьи.
Объясните уже им кто-нибудь что бывают сайты, а бывают приложения и не надо использовать везде SPA потмоу что это слово часто пишут в твиттере.
Кстати, тем кто хочет увидеть реальный пример SPA, в ОП посте есть задачка на эту тему. Это вам не даунские туториалы по хелло ворлду на ангуларе или реакте, тут думать и разбираться в архитектуре надо.
>>49706
А еще кнопки Home/End.
>>49706
Во-первых, мне всегда хочется видеть хоть какую-то схему приложения, хоть какой-то вайрфрейм, каркас, или как это называется, а еще лучше макет.
Тогда достаточно было бы просто уточнить детали, а не сидеть ломать голову, что это вообще такое.
>скрипт личная переписка (чат) по функционалу близка к vk.com
Скрипт? Что они подразумевают под "скриптом"? Это должен быть один файл с процедурщиной, а не ооп-приложение на классах с полноценной архитектурой?
В скобках написано "чат". Это видимо человек хотел мне помочь понять, но на самом деле только запутал.
Личная переписка, это когда два человека общаются, и не обязательно в онлайне, почта это тоже переписка, там допустим таймаут. А чат - когда толпа людей строчит односложные бессмысленные сообщения и смайлы.
Так что им нужно? Рум на двух человек, или открытый чат?
В vk никогда не сидел, пару раз видел только стену.
Чат как на стене, или в диалогах? В диалогах там же 2 человека общаются, или есть возможность для конференции?
>1. users: 'id', кука 'uid'..etc
Что еще за кука 'uid'? Мы тут в треде для анонимного доступа используем токены доступа, возможно они это имели ввиду.
> user_message_dialog: hash (формируется как диалог между 2user), count_messge (кол-во сообщений в переписке), unread(user1), unread(user2)
Я в ауте.
По-моему должна быть отдельная таблица 'message' с полями 'id', 'from_user', 'to_user', 'content', 'status=read/unread', или как-то так.
Кол-во сообщений в диалоге естественно можно получать запросом, а не хранить в таблице, и как они его собрались хранить? Что за 'hash'? Хеш чего? Текста сообщения?
>также в этой базе еще аналогичная таблица user_msg_archive
Ну выставь колонку в таблице сообщений, типа 'active/archived/deleted'. Зачем еще одна таблица?
>сам функционал (на примере отдельного пользователя): redis пуст пользователь зашел на сайт, авторизовался, после авторизации заполняется redis данными из "user_message_dialog", только в части новых (непрочитанных сообщений), т.е. hash и unread (для этого пользователя).
Зачем тут вообще редис? Редис можно использовать как кеш, или временное хранилище, чтобы снизить нагрузку на базу.
Короче чушь несусветная, и я бы не справился, именно по причине непонимания.
Скажите, я очень много хочу, если смогу работать только в команде, где один специально обученный человек будет долго беседовать с заказчиком, чтобы выяснить все подробности, сформулирует качественное тз, а потом всем тимом сесть и обсудить, как это реализуется?
>>49725
Может он в метро с планшета? Тебе лень удалить букву m?
>>49706
Во-первых, мне всегда хочется видеть хоть какую-то схему приложения, хоть какой-то вайрфрейм, каркас, или как это называется, а еще лучше макет.
Тогда достаточно было бы просто уточнить детали, а не сидеть ломать голову, что это вообще такое.
>скрипт личная переписка (чат) по функционалу близка к vk.com
Скрипт? Что они подразумевают под "скриптом"? Это должен быть один файл с процедурщиной, а не ооп-приложение на классах с полноценной архитектурой?
В скобках написано "чат". Это видимо человек хотел мне помочь понять, но на самом деле только запутал.
Личная переписка, это когда два человека общаются, и не обязательно в онлайне, почта это тоже переписка, там допустим таймаут. А чат - когда толпа людей строчит односложные бессмысленные сообщения и смайлы.
Так что им нужно? Рум на двух человек, или открытый чат?
В vk никогда не сидел, пару раз видел только стену.
Чат как на стене, или в диалогах? В диалогах там же 2 человека общаются, или есть возможность для конференции?
>1. users: 'id', кука 'uid'..etc
Что еще за кука 'uid'? Мы тут в треде для анонимного доступа используем токены доступа, возможно они это имели ввиду.
> user_message_dialog: hash (формируется как диалог между 2user), count_messge (кол-во сообщений в переписке), unread(user1), unread(user2)
Я в ауте.
По-моему должна быть отдельная таблица 'message' с полями 'id', 'from_user', 'to_user', 'content', 'status=read/unread', или как-то так.
Кол-во сообщений в диалоге естественно можно получать запросом, а не хранить в таблице, и как они его собрались хранить? Что за 'hash'? Хеш чего? Текста сообщения?
>также в этой базе еще аналогичная таблица user_msg_archive
Ну выставь колонку в таблице сообщений, типа 'active/archived/deleted'. Зачем еще одна таблица?
>сам функционал (на примере отдельного пользователя): redis пуст пользователь зашел на сайт, авторизовался, после авторизации заполняется redis данными из "user_message_dialog", только в части новых (непрочитанных сообщений), т.е. hash и unread (для этого пользователя).
Зачем тут вообще редис? Редис можно использовать как кеш, или временное хранилище, чтобы снизить нагрузку на базу.
Короче чушь несусветная, и я бы не справился, именно по причине непонимания.
Скажите, я очень много хочу, если смогу работать только в команде, где один специально обученный человек будет долго беседовать с заказчиком, чтобы выяснить все подробности, сформулирует качественное тз, а потом всем тимом сесть и обсудить, как это реализуется?
>>49725
Может он в метро с планшета? Тебе лень удалить букву m?
Не знаю, я ее читаю. Мне она нравится тем, что там все очень компактно и главная просто выглядит как список ссылок, а на десктопном сайте главная перегружена текстами и картинками и я не могу ее воспринимать. Она по высоте занимает экранов 10 и у меня сил нет все это читать и прокручивать.
Видимо дело вкуса и привычки.
> мне всегда хочется видеть хоть какую-то схему приложения, хоть какой-то вайрфрейм
Можно попросить заказчика нарисовать.
> Что они подразумевают под "скриптом"?
Набор скриптов, миграций и работа по их установке на сервере
> Так что им нужно? Рум на двух человек, или открытый чат?
телепат во мне говорит что нужны ЛС на существующем сайте.
> Чат как на стене, или в диалогах? В диалогах там же 2 человека общаются, или есть возможность для конференции?
2 человека + можно добавлять участников, вкидывать смешные картинки, файлы и ссылки. Ну как в скайпе например
> По-моему должна быть отдельная таблица 'message' с полями 'id', 'from_user', 'to_user', 'content', 'status=read/unread', или как-то так.
Автор наверно думает что его вариант производительнее
> Что за 'hash'? Хеш чего?
2 идентификатора беседующих пользователей
> Зачем еще одна таблица?
> Зачем тут вообще редис?
Возможно автор думает что так будет производительнее
> Скажите, я очень много хочу, если смогу работать только в команде, где один специально обученный человек будет долго беседовать с заказчиком, чтобы выяснить все подробности
Вообще, многовато. Иногда других технических специалистов нет.
>Ты по моему суть паттерна не понял,
Запутался полностью.
Тут: https://www.sitepoint.com/understanding-the-factory-method-design-pattern/
Первый листинг.
А вот здесь это уже Static Factory: http://designpatternsphp.readthedocs.io/ru/latest/Creational/StaticFactory/README.html
Вот мне и нужно то, что в последнем примере, но ведь фабрика не для этого?
Я говорил про Factory Method а не static Factory. Насчет static factory как именно определяется имя класса, через иф, свитч, массив, функцию - не принципиально.
static factory это просто функция которая получает какие-то аргументы на вход и создает какой-то объект на выходе.
Да, это я >>49619 здесь не уточнил.
>static factory это просто функция которая получает какие-то аргументы на вход и создает какой-то объект на выходе.
Имеет ли смысл прятать в неё проверку на существование класса? Есть 2 похожих класса, принимающих один и тот же параметр в конструктор. Мне приходится в 2-х случаях проверять класс на существование.
Господа, ну подскажите же как мне сделать видео чат на пхп. Как с этим флешем работать??
https://www.fl.ru/projects/2797994/trebuetsya-programmist-dlya-optimizatsii-izobrajeniy.html
>Если он не старший разработчик то у него и не должно быть понимания - у него должно быть ТЗ или описание задачи.
Он программист-одиночка, немного работает на биржах фриланса.
Вряд он когда-нибудь сможет работать в команде. Но не потому, что не осилит техническую сторону, нет.
>Вот когда на собеседовании задают не технические вопросы, а общие, таким людям как раз пролезть легче.
Вообще да, ты прав, действительно, как мне кажется.
Но я изначально имел в виду просто обычные приёмы для проверки на адекватность, которые часто используют рекрутеры и которые я сам использовал, когда принимал на работу.
Это было важным дополнением, необходимо было оценить все стороны соискателя, ведь именно мне именно с ним потом придётся постоянно общаться, давать задачи, так или иначе отвечать за его работу.
>нет
Как минимум, очень хороший уровень именно программирования.
Хотя твоя оценка и моя оценка должны сильно различаться.
Я вот пока не чувствую себя готовым взглянуть на код того, что он наваял полгода назад, - я только подбираюсь к этому.
Возможно, поменяю своё мнение в скором времени.
>>49685
>Если бы целью речи было убедить прихожан в том, что они должны любить и прощать близких и вообще всех окружающих, не осуждать, относиться с сочувствием, пытаться понять и помочь.
Возможно - если ты сейчас серьёзен, - ты почему-то не почувствовал именно этот посыл в его речи. Он ведь и говорит, что недовольный всем окружающим (людьми, высшим светом, просто природой и даже своим отражением) греховный человек находится именно в аду. Из этого выход - к Богу. Ну а Бог находится в церкви, в которую ходят люди, стремящиеся очиститься от этого ада внутри и снаружи. Подобные проповеди обычно оканчиваются приглашением покаяться (вспомнить о грехах) и помолиться (собраться вместе в церкви).
А так нечестивых людей везде полно, церковь как организация - не исключение.
Спасибо за симпатичного толстячка, братишка.
Попробуй OpenMeetings, BigBlueButton или аналоги, например.
>О. помните тут кто-то уменьшал картинки через пхп, чувак ты не дашь ссыль на свою работу?
Тут, наверное, много кто делал, это же из учебника ОПа задачка. Вот, например
https://github.com/MindiMakridi/RESTFUL
>Цели речи две
Ты ошибаешься, никакой цели не было, это просто богатый толстый дядька с ученной степенью по ПГМ несет свой говноконтент своей целевой аудитории. Что-то вроде рок концерта.
>Вместо того, чтобы воспитывать умных людей, умеющих самостоятельно анализировать свою жизнь и проблемы, церковь оболванивает людей, хуже того навязывает ксенофобию, мракобесие, нетерпимость.
Опять же ошибаешься, церковь сама по себе ничего не делает, люди сам приходят туда, сами приводят своих детей. Воспитывать умных людей должны собственные родители, а не какая-то сторонняя организация.
Вообще никто не должен был провоцироваться на вбросы этого отбитого >>49342
Конторки с вопросами от дебилов для дебилов, любовь к изречениям, не постесняюсь этого слова, мудака из рпц. Еще и выставил мой максимально адекватный пост на их быдлоконтору как за не имеющий место, пытаясь смаконуть тем самым свою правоту. Все эти факторы только доказывают обратное, что все в его конторе и он сам максимально деграданты.
>>49599
>>Не разу не сказал, например, "Мы живем в аду".
>А почему он должен был это сказать?
>Он живёт полной жизнью, так или иначе исцеляет души людей. В него верят, его слушают, ему известна какая-то Истина.
>Он не находится в аду - по его терминологии.
Если бы он исцелял души людей, он бы не говорил об этом т.к. в "В аду" больше не было бы никого. Если бы он не справлялся с количеством людей "В аду", то значит он был бы "В аду" сам.
Еще одно логичное заключение с моей стороны, к которому тебе не дано прийти самостоятельно, и которое вновь доказывает что в таких конторах зачастую обитают только умственно отсталые.
Вы можете подумать что я срусь с ним потому что мне противна такая мразь, но это не по этому. Мой уровень бытья находится ваше этого - за исключением этого случая, я игнорирую посты даунов, и не доказываю им что они дауны. Я просто хочу чтобы в моем любимом треде новички случайно прочитавшие его посты знали, что есть жесткая, решительно настроенная и не знающая компромиссов оппозиция его праздника слабоумия.
Аноны пишущее как ОП отступая одну строку, будьте осторожны в написании своих постов, вы можете ввести в заблуждение новичков и выставить ОПа в дурном свете, подвергаясь на провокации других.
Очень сожалею что выражаюсь, может быть, в не корректной форме - я давно уже засыпаю.
>Цели речи две
Ты ошибаешься, никакой цели не было, это просто богатый толстый дядька с ученной степенью по ПГМ несет свой говноконтент своей целевой аудитории. Что-то вроде рок концерта.
>Вместо того, чтобы воспитывать умных людей, умеющих самостоятельно анализировать свою жизнь и проблемы, церковь оболванивает людей, хуже того навязывает ксенофобию, мракобесие, нетерпимость.
Опять же ошибаешься, церковь сама по себе ничего не делает, люди сам приходят туда, сами приводят своих детей. Воспитывать умных людей должны собственные родители, а не какая-то сторонняя организация.
Вообще никто не должен был провоцироваться на вбросы этого отбитого >>49342
Конторки с вопросами от дебилов для дебилов, любовь к изречениям, не постесняюсь этого слова, мудака из рпц. Еще и выставил мой максимально адекватный пост на их быдлоконтору как за не имеющий место, пытаясь смаконуть тем самым свою правоту. Все эти факторы только доказывают обратное, что все в его конторе и он сам максимально деграданты.
>>49599
>>Не разу не сказал, например, "Мы живем в аду".
>А почему он должен был это сказать?
>Он живёт полной жизнью, так или иначе исцеляет души людей. В него верят, его слушают, ему известна какая-то Истина.
>Он не находится в аду - по его терминологии.
Если бы он исцелял души людей, он бы не говорил об этом т.к. в "В аду" больше не было бы никого. Если бы он не справлялся с количеством людей "В аду", то значит он был бы "В аду" сам.
Еще одно логичное заключение с моей стороны, к которому тебе не дано прийти самостоятельно, и которое вновь доказывает что в таких конторах зачастую обитают только умственно отсталые.
Вы можете подумать что я срусь с ним потому что мне противна такая мразь, но это не по этому. Мой уровень бытья находится ваше этого - за исключением этого случая, я игнорирую посты даунов, и не доказываю им что они дауны. Я просто хочу чтобы в моем любимом треде новички случайно прочитавшие его посты знали, что есть жесткая, решительно настроенная и не знающая компромиссов оппозиция его праздника слабоумия.
Аноны пишущее как ОП отступая одну строку, будьте осторожны в написании своих постов, вы можете ввести в заблуждение новичков и выставить ОПа в дурном свете, подвергаясь на провокации других.
Очень сожалею что выражаюсь, может быть, в не корректной форме - я давно уже засыпаю.
>у меня по несколько вопросов на каждую строку этой шизофазии.
хаха, ну вот так и проиходит работа пхп-программиста, разгребать абсолютно шизофренические, непонятные задания.
>По-моему должна быть отдельная таблица 'message' с полями 'id', 'from_user', 'to_user', 'content', 'status=read/unread', или как-то так.
>Кол-во сообщений в диалоге естественно можно получать запросом, а не хранить в таблице, и как они его собрались хранить? Что за 'hash'? Хеш чего? Текста сообщения?
Вот тебе смешно, а мне приходится на такие шизо-заказы сабмитеться за кусок хлеба.
>это же из учебника ОПа задачка
А можешь ссылкой на само задание поделиться? И ещё кто-то говорил про задание на проектирование БД борды. Где это всё искать? Я вроде ковырялся в gist'ах ОПа.
Алсо делал подобное >>49803 на Bash'е с помощью ImageMagick, 5-6 строк вышло.
>>49837
- Тред можно добавить в избранное.
- Ссылка на текущий PHP-тред есть в шапке прикреплённого ньюфаг треда pr (но там частенько ссылка на устаревший тред)
- В конце концов можно написать js скрипт, работающий с 2ch.hk API, который находит самый новый PHP-тред и редиректит на него. Сам скрипт оформить в виде букмарклета, который можно добавить в закладки браузера. И у тебя будет закладка, которая постоянно ведёт тебя на текущий PHP-тред. Сам пользовался этим вариантом, до тех пор пока не обнаружил возможность добавлять тред в избранное.
>ImageMagick
о кстати да, я вспомнил этот либ в пхп, реально в одну строчку уменьшает картинки.
>Ссылка на текущий PHP-тред
битая ссылка
>избранное
я не хочу иметь сосач в перечне закладок, а то вдруг еще люди увидят такой срам.
>можно написать js скрипт
Почему бы макаке на своем сайте не написать скрипт, фильтрующий треды по пхп/ruby/java тегам, выбирающий последний по дате и не занести ссылку на него в переменную прикрепленной шапки? У него доступ к бд есть, а мне еще с парсерами ебаться и виртуальную машину поднимать по такому случаю.
>я не хочу иметь сосач в перечне закладок, а то вдруг еще люди увидят такой срам.
Ну измени название. И "Избранное" это фишка на сосаче, смотри пик.
>Почему бы макаке
Тебе дали API, просто пройдись по нему, получи первый occurrence фразы "Клуб изучающих" в названии треда, выдерни ссылку и перейди по ней. Какие виртуальные машины, скрипт будет в браузере отрабатывать.
В общем меня можно снова проверять. Большинство ошибок уже исправлено
>Для хеширования можно сделать статический метод в хелпере авторизациии.
А почему здесь не нарушается принцип DI?
>>49652
>> Является ли сервис авторизации аутентификацией и при этом не является самой авторизацией?
>Давай считать что он занимается и тем и другим.
А если бы у нас не было бы такой договоренности, то как бы было принято считать?
>>49652
>- сделать у функции опцию вроде "разрешить любые поля"
Мне нравится этот вариант
Я сделаю так
function fillDataFromArray($data, $allowed = array())
{
if (!empty($allowed)) {
$allowed = array_keys($data);
}
...
}
И в классе формы я напишу так
function fillDataFromArray(array $data)
{
$allowed = [
'name',
'surname',
'gender',
'grupNumber',
'email',
'satScores',
'yearOfBirth',
'location'
];
$this->student->fillDataFromArray($data, $allowed);
$allowed = [
'password',
'retryPassword',
];
foreach ($allowed as $value) {
if (isset($data[$value]) and is_scalar($data[$value]) and property_exists($this, $value)) {
$this->$value = trim($data[$value]);
}
}
}
И я подумал, что две переменные $allowed можно объединить в одну - всё равно лишние поля не заполнятся потому что есть условие которое проверяет на наличие таких свойств.
>>49663
>> Это же не его задача, почему класс с БД должен знать что есть какой-то белый список для студента?
>Он должен знать какие поля есть в БД. Меня правда беспокоит что у нас тут список полей дублируется многократно и при добавлении или переименовании поля придется в куче мест менять.
>
>какие ты видишь варианты? нам надо и загружать студента из базы, и разрешить редактировать через форму.
Варианты с тем чтобы не дублировать поля?
Я бы сделал одно свойство $data и заполнял его в соответствии с пришедшими данными из формы (проверяя при этом на разрешенные поля) или из БД, или лучше на оборот сделал чтобы форма и запросы из БД делались автоматически на основе свойств той или иной сущности, но чтобы это сделать нужно полностью переписывать код.
>Для хеширования можно сделать статический метод в хелпере авторизациии.
А почему здесь не нарушается принцип DI?
>>49652
>> Является ли сервис авторизации аутентификацией и при этом не является самой авторизацией?
>Давай считать что он занимается и тем и другим.
А если бы у нас не было бы такой договоренности, то как бы было принято считать?
>>49652
>- сделать у функции опцию вроде "разрешить любые поля"
Мне нравится этот вариант
Я сделаю так
function fillDataFromArray($data, $allowed = array())
{
if (!empty($allowed)) {
$allowed = array_keys($data);
}
...
}
И в классе формы я напишу так
function fillDataFromArray(array $data)
{
$allowed = [
'name',
'surname',
'gender',
'grupNumber',
'email',
'satScores',
'yearOfBirth',
'location'
];
$this->student->fillDataFromArray($data, $allowed);
$allowed = [
'password',
'retryPassword',
];
foreach ($allowed as $value) {
if (isset($data[$value]) and is_scalar($data[$value]) and property_exists($this, $value)) {
$this->$value = trim($data[$value]);
}
}
}
И я подумал, что две переменные $allowed можно объединить в одну - всё равно лишние поля не заполнятся потому что есть условие которое проверяет на наличие таких свойств.
>>49663
>> Это же не его задача, почему класс с БД должен знать что есть какой-то белый список для студента?
>Он должен знать какие поля есть в БД. Меня правда беспокоит что у нас тут список полей дублируется многократно и при добавлении или переименовании поля придется в куче мест менять.
>
>какие ты видишь варианты? нам надо и загружать студента из базы, и разрешить редактировать через форму.
Варианты с тем чтобы не дублировать поля?
Я бы сделал одно свойство $data и заполнял его в соответствии с пришедшими данными из формы (проверяя при этом на разрешенные поля) или из БД, или лучше на оборот сделал чтобы форма и запросы из БД делались автоматически на основе свойств той или иной сущности, но чтобы это сделать нужно полностью переписывать код.
>Не "создаемКукисы" а вызываем метод "залогинитьСтудента".
А что это за метод? Что он будет содержать в себе помимо создания кукисов? Да и не занимается ли сам контроллер залогиниванием студента?
Нашёл, можно примерно таким образом
RewriteEngine on
RewriteCond %{REQUEST_URI} !app/
RewriteRule (.*) /app/$1 [L]
https://httpd.apache.org/docs/current/rewrite/flags.html#flag_qsa
>With the [QSA] flag, a request for /pages/123?one=two will be mapped to /page.php?page=123&one=two. Without the [QSA] flag, that same request will be mapped to /page.php?page=123 - that is, the existing query string will be discarded.
И обычно корневой папку делают с помощью виртульных хостов.
Ни черта не понятно.
>Интеграционный тест лучше делать на отдельной базе расположенной в памяти
Где? Я не знаю, как создавать базу расположенную "в памяти". Знаю только таблицы типа memory в mysql, но там же нет ни внешних ключей, ничего.
>Скрипт должен создать базу
Как? Я знаю только способ создать базу через консольную команду doctrine:database:create -e test
Как это сделать через левый класс?
Аналогично миграции, знаю только консольные команды для миграций, не знаю как их запустить из своего кода.
Короче я пока создаю обычную базу на диске (других не знаю) для тестов через консольную команду, используя конфиги тестового окружения из config_test.yml.
Вручную накатываю миграции через консольную команду.
Наследуюсь от PHPUnit_Extensions_Database_TestCase и подгружаю dataSet из дампа.
https://phpunit.de/manual/current/en/database.html#database.yaml-dataset
> класс Attempt с кучей релейшенов
>Ну, это элементарно. Создаем разные Attempt разных типов
Нет, я заколебаюсь делать моку под него. Тут наверное тоже нужен интеграционный тест с участием бд.
Я тоже в деревне живу, ближайший город 700К, 10 php вакансий на hh. Но я собрался после сессии устраиваться на удаленку, вакансий много. Почему ты не попробуешь? Особенно если у тебя есть опыт.
а подробнее? Нет, я могу поставить куку isadmin=1 при авторихации и проверять её,
но любой вася может сделать то же самое на стороне юзера.
Соленый хеш пароля ставить в куку.
При аутентификации проверять, есть ли у пользователя куки для id и хеша, если есть извлечь из базы запись по id, сравнить хеш из бд с хешом из куки.
Я помню, какой-то братишка так спалился каким-то образом пару месяцев назад, когда выложил на Гитхабе проект с дампом базы.
Вообще не пойму, с чего начать.
Есть готовая Гостевая - пара файликов, хочу иметь первый проЭкт на Гитхабе.
Бро, как понять "захардкоденными"?
В файле конфига вообще не указывать пароль, может быть? Никто же не будет её тестировать, там строк 60 кода всего.
Или сами поставят пароль, если надумают тестировать.
Я в сомнениях и раздумьях, короче.
Ну ты пароль хранишь в переменной, переменную реквайришь из файла config.php
В весрию для гитхаба в этом файле пишешь
$passw = ''; # DB password
в чем проблема я не понимаю.
Указываешь пустой пароль в конфиге. Потом говоришь git'у не отслеживать изменения для данного файла командой:
git update-index --assume-unchanged <file>
Потом вставляешь свой пароль в файл, пушишь на GitHub.
На всякий случай, отслеживание изменений обратно включается командой: git update-index --no-assume-unchanged <file>
Съеби в лузер-тред.
>- делать моки для репозитория и инжектировать в класс для теста. Трудоемко
Более чем.
У меня же там куча связей у каждой модели, если бы была только пара свойств, я бы не возникал.
>- делать интеграционные тесты, вставив в чистую базу заранее известные данные и вызывая методы.
Не знаю как из класса теста обратиться к доктрине.
Функциональные тесты насколько я понял относятся к контроллерам, о них пока не вспоминаем.
В манах симфони ни слова не сказано про "интеграционные тесты".
В book как обычно даунский пример со сложением двух чисел, про работу с базой не сказано.
http://symfony.com/doc/current/book/testing.html
Затем сразу переключаются на функциональные тесты.
В рецептах сказано использовать моки.
http://symfony.com/doc/current/cookbook/testing/database.html
Это конечно хорошо, что они приводят в качестве примера офигенно сложную модель с двумя int свойствами
$employee->expects($this->once())
->method('getSalary')
->will($this->returnValue(1000));
$employee->expects($this->once())
->method('getBonus')
->will($this->returnValue(1100));
Только вот у меня в свойствах лежат коллекции объектов, густо переплетенные перекрестными связями.
Наконец есть доки phpunit
https://phpunit.de/manual/current/en/database.html
где предлагают возню с xml-ными конфигами и дампами.
Не понимаю, что делать.
Кладу пока на тесты и пишу другие части приложения.
>- делать моки для репозитория и инжектировать в класс для теста. Трудоемко
Более чем.
У меня же там куча связей у каждой модели, если бы была только пара свойств, я бы не возникал.
>- делать интеграционные тесты, вставив в чистую базу заранее известные данные и вызывая методы.
Не знаю как из класса теста обратиться к доктрине.
Функциональные тесты насколько я понял относятся к контроллерам, о них пока не вспоминаем.
В манах симфони ни слова не сказано про "интеграционные тесты".
В book как обычно даунский пример со сложением двух чисел, про работу с базой не сказано.
http://symfony.com/doc/current/book/testing.html
Затем сразу переключаются на функциональные тесты.
В рецептах сказано использовать моки.
http://symfony.com/doc/current/cookbook/testing/database.html
Это конечно хорошо, что они приводят в качестве примера офигенно сложную модель с двумя int свойствами
$employee->expects($this->once())
->method('getSalary')
->will($this->returnValue(1000));
$employee->expects($this->once())
->method('getBonus')
->will($this->returnValue(1100));
Только вот у меня в свойствах лежат коллекции объектов, густо переплетенные перекрестными связями.
Наконец есть доки phpunit
https://phpunit.de/manual/current/en/database.html
где предлагают возню с xml-ными конфигами и дампами.
Не понимаю, что делать.
Кладу пока на тесты и пишу другие части приложения.
> У меня же там куча связей у каждой модели, если бы была только пара свойств, я бы не возникал.
А вот и нет. Что тебе мешает сделать так:
$test = new Test;
$test->setName(...);
$q = new Question;
$test->addQuestion($q);
ну и так далее. Я думаю, это можно разбить на части, отдельно функция создания теста, отдельно функция вбивания вопросов в него. Поля все заполнять не требуется, только обязательные.
> Функциональные тесты насколько я понял относятся к контроллерам, о них пока не вспоминаем.
Они тестируют может ли приложение в целом выполнять свои функции. Там обычно используется эмулятор браузера.
> В манах симфони ни слова не сказано про "интеграционные тесты".
Это тесты, проверябщие как компоненты взаимодействуют друг с другом и работают вместе
https://ru.wikipedia.org/wiki/Интеграционное_тестирование
https://habrahabr.ru/post/146984/
В данном случае имеется в виду интеграция базы данных и кода работающего с ней. Вроде же соответствует определению.
> В рецептах сказано использовать моки.
я тебе советую держаться подальше от тестов, как в примере. Вот посмотри, что тестирует этот код? Что такая-то функция будет вызвана. Это применимо если ты например тестируешь систему событий и проверяешь вызовется ли твой обработчик, но в других случаях это неправильно так как ты в тест закладываешь знания о том как работает тестируемый код и что он вызывает. Это ненадежно, при первом же рефакторинге это сломается.
Ну и второй момент, что моком подменяют возвращаемые результаты. ну опять же, тут тест слишком завязывается на внутреннюю структуру кода, мне кажется.
> У меня же там куча связей у каждой модели, если бы была только пара свойств, я бы не возникал.
А вот и нет. Что тебе мешает сделать так:
$test = new Test;
$test->setName(...);
$q = new Question;
$test->addQuestion($q);
ну и так далее. Я думаю, это можно разбить на части, отдельно функция создания теста, отдельно функция вбивания вопросов в него. Поля все заполнять не требуется, только обязательные.
> Функциональные тесты насколько я понял относятся к контроллерам, о них пока не вспоминаем.
Они тестируют может ли приложение в целом выполнять свои функции. Там обычно используется эмулятор браузера.
> В манах симфони ни слова не сказано про "интеграционные тесты".
Это тесты, проверябщие как компоненты взаимодействуют друг с другом и работают вместе
https://ru.wikipedia.org/wiki/Интеграционное_тестирование
https://habrahabr.ru/post/146984/
В данном случае имеется в виду интеграция базы данных и кода работающего с ней. Вроде же соответствует определению.
> В рецептах сказано использовать моки.
я тебе советую держаться подальше от тестов, как в примере. Вот посмотри, что тестирует этот код? Что такая-то функция будет вызвана. Это применимо если ты например тестируешь систему событий и проверяешь вызовется ли твой обработчик, но в других случаях это неправильно так как ты в тест закладываешь знания о том как работает тестируемый код и что он вызывает. Это ненадежно, при первом же рефакторинге это сломается.
Ну и второй момент, что моком подменяют возвращаемые результаты. ну опять же, тут тест слишком завязывается на внутреннюю структуру кода, мне кажется.
>>50586
>>50547
Нет, наверно лучше не так. Надо добавить конфиг в gitignore, а вместо него в репозиторий поместить образец конфига, например config.ini.dist без паролей и имен.
Другой вариант - сделать базовый конфиг (config.ini) без паролей, и локальный конфиг, config.local.ini, который в гитигноре и который переопределяет пароль.
Ну и не забыть про это написать в ридми.
В любом случае в репозитории должен быть код, подходящий всем, там не должно быть твоих личных паролей.
>>50407
У sqlite есть такой режим, хранить данные в памяти, у postgresql вроде нет, печалька: http://stackoverflow.com/questions/7872693/running-postgresql-in-memory-only
У редиса можно легко поднять временный демон не сохраняющий никаких данных.
Ну в mysql оказывается тоже не все хорошо. У меня еще есть мысль, что можно примонтировать tmpfs (ФС хранящая файлы в памяти), поднять mysql с базой на ней, инициализировать, а после тестов выкинуть. Но это наверно сложно.
Раз так, можно просто сделать отдельную БД и очищать ее или определенные таблицы в ней перед тестами. То есть скрипт выглядит так:
- очистить БД
- накатить миграции и справочные данные
- прогнать тесты
Ну и в тесте дополнительно можно делать TRUNCATE некоторым таблицам например. Или делать тест в транзакции, которую откатывать после. Это все нужно для изоляции, чтобы вставленные одним тестом данные не влияли на другой.
> Как это сделать через левый класс?
Я имел в виду баш скрипт. Он подготавливает все что нужно и запускает phpunit.
>>50420
Ну ок.
> и подгружаю dataSet из дампа.
Тут есть подвох, что при изменениях в БД дамп не будет работать, надо решать проблему связей... не лчше ли сделать вспомогательный класс, который создает тесты и вопросы например через сервисы или доктрину?
>>50586
>>50547
Нет, наверно лучше не так. Надо добавить конфиг в gitignore, а вместо него в репозиторий поместить образец конфига, например config.ini.dist без паролей и имен.
Другой вариант - сделать базовый конфиг (config.ini) без паролей, и локальный конфиг, config.local.ini, который в гитигноре и который переопределяет пароль.
Ну и не забыть про это написать в ридми.
В любом случае в репозитории должен быть код, подходящий всем, там не должно быть твоих личных паролей.
>>50407
У sqlite есть такой режим, хранить данные в памяти, у postgresql вроде нет, печалька: http://stackoverflow.com/questions/7872693/running-postgresql-in-memory-only
У редиса можно легко поднять временный демон не сохраняющий никаких данных.
Ну в mysql оказывается тоже не все хорошо. У меня еще есть мысль, что можно примонтировать tmpfs (ФС хранящая файлы в памяти), поднять mysql с базой на ней, инициализировать, а после тестов выкинуть. Но это наверно сложно.
Раз так, можно просто сделать отдельную БД и очищать ее или определенные таблицы в ней перед тестами. То есть скрипт выглядит так:
- очистить БД
- накатить миграции и справочные данные
- прогнать тесты
Ну и в тесте дополнительно можно делать TRUNCATE некоторым таблицам например. Или делать тест в транзакции, которую откатывать после. Это все нужно для изоляции, чтобы вставленные одним тестом данные не влияли на другой.
> Как это сделать через левый класс?
Я имел в виду баш скрипт. Он подготавливает все что нужно и запускает phpunit.
>>50420
Ну ок.
> и подгружаю dataSet из дампа.
Тут есть подвох, что при изменениях в БД дамп не будет работать, надо решать проблему связей... не лчше ли сделать вспомогательный класс, который создает тесты и вопросы например через сервисы или доктрину?
Вот, для начала почитай-ка мой новый урок по DI: https://github.com/codedokode/pasta/blob/master/arch/di.md
Все эти принципы мы применяем не потому что кто-то умный так сказал, а чтобы бороться с той или иной проблемой. И тут всегда надо искать компромисс - вот например при строительстве моста мы можем ставить опоры чаще (но это будет дорож и затруднит судоходство), а можем реже (но тогда секции должны быть более прочными). Так же и с кодом, если мы его будем писать стеной, то в нем будет не разобраться, а если будем применять все возмодные паттерны и разобьем на 1000 классов, то тоже не очень хорошо будет.
>>Для хеширования можно сделать статический метод в хелпере авторизациии.
>А почему здесь не нарушается принцип DI?
Хеширование не обращается к полям класса, небольшое по объему, не имеет зависимостей, потому его можно сделать статическим методом.
>>Давай считать что он занимается и тем и другим.
> А если бы у нас не было бы такой договоренности, то как бы было принято считать?
Тут надо не считать, а взвесить плюсы и минусы. "Если мы сделаем авторизацию и аутентификацию в одном классе то...", "если мы сделаем это в 2 отдельных классах то....". Я не вижу никаких плюсов в 2 классах, по моему только сложнее будет.
> Мне нравится этот вариант
> Я сделаю так
> function fillDataFromArray($data, $allowed = array())
Ну тут по моему как раз закладывается мина замедленного действия. В моем понимании пустой массив значит что никакие поля не разрешены, а функция поступает ровно наоборот. Функции не должны вести себя неожиданно и неинтуитивно. Так как в этом случае кто-то сделает ошибку.
Насчет белого списка полей - что-то я затрудняюсь. Может для БД все же лучше сделать отдельную функцию, не проверяющую список? А то мы замучаемся поддерживать кучу списков полей при изменении структуры таблицы.
> Я бы сделал одно свойство $data
ну это не ООП. Ты по сути заменяешь хороший класс на плохой массив, в котором неизвестно какие поля могут быть. Если ты так сделаешь в реальном приложении, то один программист положит туда один набор полей, другой - другой и в итоге будет не понять что там должно быть, а что нет.
> тобы форма и запросы из БД делались автоматически на основе свойств той или иной сущности
Что интересно, в Симфони примерно так и делается, только там к полям пишутся комментарии - например какому типу в БД оно соответствует, также можно приписывать правила валидации (изучай доктрину, symfony Validation и symfony forms если интересно). Но это довольно сложная штука, ее писать с нуля наверно нет смысла, тем более когда есть готовая. У нас маленькое приложение, я думаю, один-два раза список полей можно и вручную вписать.
Вот, для начала почитай-ка мой новый урок по DI: https://github.com/codedokode/pasta/blob/master/arch/di.md
Все эти принципы мы применяем не потому что кто-то умный так сказал, а чтобы бороться с той или иной проблемой. И тут всегда надо искать компромисс - вот например при строительстве моста мы можем ставить опоры чаще (но это будет дорож и затруднит судоходство), а можем реже (но тогда секции должны быть более прочными). Так же и с кодом, если мы его будем писать стеной, то в нем будет не разобраться, а если будем применять все возмодные паттерны и разобьем на 1000 классов, то тоже не очень хорошо будет.
>>Для хеширования можно сделать статический метод в хелпере авторизациии.
>А почему здесь не нарушается принцип DI?
Хеширование не обращается к полям класса, небольшое по объему, не имеет зависимостей, потому его можно сделать статическим методом.
>>Давай считать что он занимается и тем и другим.
> А если бы у нас не было бы такой договоренности, то как бы было принято считать?
Тут надо не считать, а взвесить плюсы и минусы. "Если мы сделаем авторизацию и аутентификацию в одном классе то...", "если мы сделаем это в 2 отдельных классах то....". Я не вижу никаких плюсов в 2 классах, по моему только сложнее будет.
> Мне нравится этот вариант
> Я сделаю так
> function fillDataFromArray($data, $allowed = array())
Ну тут по моему как раз закладывается мина замедленного действия. В моем понимании пустой массив значит что никакие поля не разрешены, а функция поступает ровно наоборот. Функции не должны вести себя неожиданно и неинтуитивно. Так как в этом случае кто-то сделает ошибку.
Насчет белого списка полей - что-то я затрудняюсь. Может для БД все же лучше сделать отдельную функцию, не проверяющую список? А то мы замучаемся поддерживать кучу списков полей при изменении структуры таблицы.
> Я бы сделал одно свойство $data
ну это не ООП. Ты по сути заменяешь хороший класс на плохой массив, в котором неизвестно какие поля могут быть. Если ты так сделаешь в реальном приложении, то один программист положит туда один набор полей, другой - другой и в итоге будет не понять что там должно быть, а что нет.
> тобы форма и запросы из БД делались автоматически на основе свойств той или иной сущности
Что интересно, в Симфони примерно так и делается, только там к полям пишутся комментарии - например какому типу в БД оно соответствует, также можно приписывать правила валидации (изучай доктрину, symfony Validation и symfony forms если интересно). Но это довольно сложная штука, ее писать с нуля наверно нет смысла, тем более когда есть готовая. У нас маленькое приложение, я думаю, один-два раза список полей можно и вручную вписать.
только к этой строчке надо дописать проверку результата, сохранение/перенос файлов, сборку мусора, логгирование, обработку ошибок, таймаут. Получается не так и мало. Да и по моему средствами пхп делать это удобнее чем вызывая внешний процесс. И кстати imageMagick доступна как расширение пхп.
>>49850
> на Bash'е с помощью ImageMagick, 5-6 строк вышло.
А логгирование, проверку ошибок, таймауты - ты делал?
update.php отправляет запрос на обновления в sqlbd
!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<link rel="stylesheet" type="text/css" href="../style.css">
<title>Untitled Document</title>
</head>
<body>
<?php
require 'connect.php';
$id=$_REQUEST['id'];
$first_name=trim($_REQUEST['first_name']);
$fast_name=trim($_REQUEST['fast_name']);
$metod=trim($_REQUEST['metod']);
$update_sql = "UPDATE users SET first_name='$first_name', fast_name='$fast_name', metod='$metod', administr='$administr' WHERE id='$id'";
mysql_query($update_sql) or die("Ошибка вставки" . mysql_error());
echo '<p>Запись успешно обновлена!</p>';
?>
<a href="../info_form.html">Добавить пользователя</a><br/><br/>
<a href="../search_user.html">Вернуться к поиску</a><br/><br/>
<a href="../select_change.php">Вернуться к выбору записей для редактирования</a><br/><br/>
</body>
</html>
all_users.php здесь находятся данные, которые нужно обновить в sqlbd через поля в таблице.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<link rel="stylesheet" type="text/css" href="style.css">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<title>Информационно - образовательная среда</title>
</head>
<body>
<h1 align="center">Информационно - образовательная среда</h1>
<?php
$dom = new DOMDocument();
include('simple_html_dom.php');
require 'scripts/connect.php';
$sql_select = "SELECT FROM not_new_db";
$result = mysql_query($sql_select);
$result=mysql_query("SELECT id,first_name,fast_name,metod,administr FROM users ORDER BY id");
$row = mysql_fetch_array($result);
$dom = new DOMDocument();
$data = $dom->getElementById("metod");
do
{
/ printf("<p>Пользователь: " .$row['fast_name'] . " " .$row['first_name'] . " " .$row['third_name'] ."</p>
<p><i>Контактные данные</i></p><p>методы и средства: " .$row['metod'] . "</p><p>administr: " .$row['administr'] . "</p>---------<br/>"
); */
$n=mysql_num_rows($result);
echo "<table border=1>
<tr><th>ID</th><th>Имя</th><th>Предмет</th><th>Оценка</th></tr>";
//вывод построчно
for($i=0;$i<$n;$i++)
echo
"<tr><td contenteditable=true id=".mysql_result($result,$i,id).">".mysql_result($result,$i,id),
"</td><td contenteditable=true id=".mysql_result($result,$i,first_name).">".mysql_result($result,$i,first_name),
"</td><td contenteditable=true id=".mysql_result($result,$i,fast_name).">".mysql_result($result,$i,fast_name),
"</td><td contenteditable=true id=".mysql_result($result,$i,metod).">".mysql_result($result,$i,metod),
"</td></tr>";
echo "</table>";
}
while($row = mysql_fetch_array($result));
printf("<form action='scripts/update.php'>")
?>
<input id='submit' type='submit' value='Редактировать запись'>
<div id="heretext"></div>
<br/><br/>
</body>
</html>
На пике таблица из которой нужно обновить поле оценок в sql.
update.php отправляет запрос на обновления в sqlbd
!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<link rel="stylesheet" type="text/css" href="../style.css">
<title>Untitled Document</title>
</head>
<body>
<?php
require 'connect.php';
$id=$_REQUEST['id'];
$first_name=trim($_REQUEST['first_name']);
$fast_name=trim($_REQUEST['fast_name']);
$metod=trim($_REQUEST['metod']);
$update_sql = "UPDATE users SET first_name='$first_name', fast_name='$fast_name', metod='$metod', administr='$administr' WHERE id='$id'";
mysql_query($update_sql) or die("Ошибка вставки" . mysql_error());
echo '<p>Запись успешно обновлена!</p>';
?>
<a href="../info_form.html">Добавить пользователя</a><br/><br/>
<a href="../search_user.html">Вернуться к поиску</a><br/><br/>
<a href="../select_change.php">Вернуться к выбору записей для редактирования</a><br/><br/>
</body>
</html>
all_users.php здесь находятся данные, которые нужно обновить в sqlbd через поля в таблице.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<link rel="stylesheet" type="text/css" href="style.css">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<title>Информационно - образовательная среда</title>
</head>
<body>
<h1 align="center">Информационно - образовательная среда</h1>
<?php
$dom = new DOMDocument();
include('simple_html_dom.php');
require 'scripts/connect.php';
$sql_select = "SELECT FROM not_new_db";
$result = mysql_query($sql_select);
$result=mysql_query("SELECT id,first_name,fast_name,metod,administr FROM users ORDER BY id");
$row = mysql_fetch_array($result);
$dom = new DOMDocument();
$data = $dom->getElementById("metod");
do
{
/ printf("<p>Пользователь: " .$row['fast_name'] . " " .$row['first_name'] . " " .$row['third_name'] ."</p>
<p><i>Контактные данные</i></p><p>методы и средства: " .$row['metod'] . "</p><p>administr: " .$row['administr'] . "</p>---------<br/>"
); */
$n=mysql_num_rows($result);
echo "<table border=1>
<tr><th>ID</th><th>Имя</th><th>Предмет</th><th>Оценка</th></tr>";
//вывод построчно
for($i=0;$i<$n;$i++)
echo
"<tr><td contenteditable=true id=".mysql_result($result,$i,id).">".mysql_result($result,$i,id),
"</td><td contenteditable=true id=".mysql_result($result,$i,first_name).">".mysql_result($result,$i,first_name),
"</td><td contenteditable=true id=".mysql_result($result,$i,fast_name).">".mysql_result($result,$i,fast_name),
"</td><td contenteditable=true id=".mysql_result($result,$i,metod).">".mysql_result($result,$i,metod),
"</td></tr>";
echo "</table>";
}
while($row = mysql_fetch_array($result));
printf("<form action='scripts/update.php'>")
?>
<input id='submit' type='submit' value='Редактировать запись'>
<div id="heretext"></div>
<br/><br/>
</body>
</html>
На пике таблица из которой нужно обновить поле оценок в sql.
ПЕРЕКАТ https://2ch.hk/pr/res/751213.html (М)
ПЕРЕКАТ https://2ch.hk/pr/res/751213.html (М)
ПЕРЕКАТ https://2ch.hk/pr/res/751213.html (М)
ПЕРЕКАТ https://2ch.hk/pr/res/751213.html (М)
Да, ты типа привыкай.
Чтобы написать 2 строчки кода, нужно прочитать (и запомнить) трехтомную документацию. Если не запомнишь с первого раза, повторить цикл.
Для гита кстати коротенький мануал, всего 285 страниц.
Чаще всего ты ищешь себе клиентов, они тебе дают уже готовый говнокод к которому ты должен что-то добавить или исправить. Часто попадаются неадекваты и люди которые могут не заплатить. На апворке и других англоязычных сайтах будешь конкурировать с толпой индусов за пачку доширака. Вроде всё.
>Чаще всего ты ищешь себе клиентов, они тебе дают уже готовый говнокод к которому ты должен что-то добавить или исправить.
Это что-то вроде добавить галерею на сайт?
> Часто попадаются неадекваты и люди которые могут не заплатить.
И что в этом случае делать?
> На апворке и других англоязычных сайтах будешь конкурировать с толпой индусов за пачку доширака. Вроде всё.
Я рассматриваю фриланс как дополнителный доход к основной работе, напрямую не связанной с программированием. Денег мало, кушать охота.
>Это что-то вроде добавить галерею на сайт?
Что-то вроде этого, да. Но представь что сайт написали макаки-индусы без какого либо намека на ООП или разделение кода. Вся логика и отображение в одном файле, с странными названиями переменных. И вот к этой каше тебе нужно что-то добавить, и желательно сделать быстро, за пару часов, а то господин будет недоволен и заплатит тебе меньше, или вообще минус в репутацию поставит. Так же бывают задачи посерьезнее, на 1 - 4 дней работы, но для такого нужно иметь хорошую репутацию и communication skills.
>И что в этом случае делать?
От сайта зависит. Я бы на твоем месте лучше о неадекватах подумал, их куда больше.
>Я рассматриваю фриланс как дополнителный доход к основной работе, напрямую не связанной с программированием. Денег мало, кушать охота.
Денег будет хватать если будешь получать в долларах, но для этого нужно знать английский и уметь конкурировать с индусами.
>А вот и нет. Что тебе мешает сделать так
> написал 4 строчки
> ну и так далее
Мне мешает "и так далее", даже не потому что оно занимает больше 100 строк кода, а то что это кропотливейшая и медленная работа.
Набрать сто строчек не проблема, проблема нигде не ошибиться.
http://ideone.com/XUfvWb
Кода получилось немного, сам в итоге вижу что выглядит со стороны не страшно.
Но мне приходилось постоянно заглядывать в свои модели, сверяться какие у них свойства, проверять вызовы в тестируемом классе, в итоге заняло больше 2 часов времени написание одного несчастного теста.
Если это время мне будут давать на работе, то без проблем. Но меня терзают смутные сомнения, что кто-то даст столько времени на тесты.
Это надо держать целый отдел тестировщиков, чтобы они могли все свое время уделять не написанию кода, а его тестированию.
Кроме того, мне еще сеттеры для id пришлось добавлять в модели. Доктрина такого делать не рекомендует.
Короче, тестирование это конечно хорошо на бумаге, но реализация пока не очень.
В общем, у меня негативные впечатления от интеграционных тестов. Юнит тесты отдельных изолированных методов еще ничего.
Сейчас переключусь на функциональные, должно быть приятно благодаря краулеру и прочим шикарным библиотекам, и не нужно никаких селениумов.
Ну и если функциональные тесты требуют заполнять бд фейковыми данными, зачем я здесь занимался ручной сборкой десятков объектов?
Не лучше ли сразу залить фейковую бд, и на ней выполнять как интеграционные, так и функциональные тесты за один присест?
>То есть скрипт выглядит так:
> - очистить БД
> - накатить миграции и справочные данные
> - прогнать тесты
Только что нагуглил плагин для доктрины doctrine fixtures.
Имеешь что-то против нее, или не знал о ней/любишь велосипеды?
Несколько ссылок на тему (потому что у меня уже закладок не хватает, и может еще кому пригодится)
http://stackoverflow.com/questions/14752930/best-way-to-create-a-test-database-and-load-fixtures-on-symfony-2-webtestcase
http://plutov.by/post/data_fixtures
https://www.theodo.fr/blog/2011/09/symfony2-unit-database-tests/
И не надо никаких левых баш-скриптов.
Пиздец, не думал что столкнусь с точно такой же херней. Выполнил тестовое задание и попал на собеседование только чтобы отлететь на вопросе "с какой целью вы изучаете пхп?".
У меня на собеседовании спросили, девственник ли я, и не гей ли часом?
Ответил положительно на оба вопроса, сразу взяли, больше ничего не спрашивали, сказали завтра же приходить на работу.
Просили соблюдать дресс-код.
>Заметь что контейнер — внешняя вещь по отношению к сервису. Мы не передаем сам контейнер в конструктор (иначе это будет ServiceLocator)
А если ServiceLocator не передавать через конструктор, то это будет DI-контейнер? И как не передавать контейнер через конструктор тому же контроллеру, если в контроллере мне нужны TDG, хелпер для регистрации и т.д?
Как выполнить подобный запрос?
INSERT INTO adAccess (id_user,type)
VALUES (
SELECT DISTINCT
users.`id_user`
FROM users
INNER JOIN users_address ON users_address.id_user = users.id_user
, 3)
Вернее как правильно его написать
Убрал свою шлюху отсюда, тут пацаны делом занимаются, а ты отвлекаешь.
Закинь пожалуйста еще этих шлюх да выпей чаю
HomoCredit: 88692.768160878 руб.
SoftBank: 118952.9694837 руб.
StrawberryBank: 102547.63370281 руб.
>>52787
Переделал. Получается у гомо кредита самые выгодные условия.
http://ideone.com/v2Xbrk
<?php
error_reporting(-1);
function getFinalPrice ($credit, $pay, $loanRate, $comission){
$totalPay = 0;
while ($credit > 0):
$credit = $credit * (1 + $loanRate/100) + $comission - $pay;
$totalPay += $pay;
endwhile;
$totalPay += $credit;
return $totalPay;
}
$homoCreditTotal = getFinalPrice(39999, 5000, 4, 500);
$softBankTotal = getFinalPrice(39999, 5000, 3, 1000);
$strawberryBankTotal = getFinalPrice(47776, 5000, 2, 0);
echo "HomoCredit: $homoCreditTotal руб.\n";
echo "SoftBank: $softBankTotal руб.\n";
echo "StrawberryBank: $strawberryBankTotal руб.\n";
вот на этом сайте лежит бд, как мне к ней подключиться, чтобы дамп себе в mysql не качать? логин и пароль известен. в интенрете что-то непонятное пишут, типа надо заходить в пхпадмин и прописывать свой айпишник, как доверенный для подключения извне??
Доступ к панели управления хостингом есть? Там, как правило, есть ссылка на phpmyadmin.
Нужно распарсить строку вида "фигня, фигня, фигня", причем фигня может содержать запятую с пробелом, поэтому explode не подходит.
Регулярка /(?:(фигня), )?(фигня)/ матчит строку, но в первой подмаске значения затирают друг друга.
Как сохранить всю "фигню" из строки?
Много отзывов уже набрал? Сколько в среднем стоимость проекта, за которые ты берешься/тебя нанимают? Сколько выходит в час?
Почему именно flru? Есть еще weblancer.net там тож много около-веб-программирования и фрилансим.ру, который на хабре форсят. Про последний не в курсе, возможно заказчики более вменяемые, ит-тусовка.
Гугли preg_split. Эта функция работает как explode, только в качестве разделителя позволяет использовать не только символ, но и регулярку. В твоём случае это будет регулярка вроде /\s?,/
Ну и чем это будет отличаться от explode?
Повторюсь, фигня может содержать запятую с пробелом, и у фигни есть определенный синтаксис.
Нужно распарсить строку с ETag по RFC. Правильные ответы: "s"d, fsdf" "sdf, sd"f2" "dsf"sdf, sdf345".
И они правильно матчатся, но первая подмаска хранит только последнее значение. Как сохранить все значения?
>>53587
Если у тебя задача матчить списки вида a, b, c то одним применением регулярки сделать это не получится. Такие варианты:
- применять регулярку, используя preg_match_all, недостаток: если в строке есть мусор то он молча проигнорируется. Победить этот недостаток можно сделав preg_split и посмотрев что останется - должны быть только пустые строки с проблемами.
- применять регулярку несколько раз используя preg_match с указанием позиции
- парсить как-то по-другому, без регулярок, например исплоьзуя метод рекурсивного спуска.
>>53599
Да, аноны, перекатывайтесь туда, я хотел позавчера сделать перекат, но устал и уснул. А, и сейчас кстати мне тоже скоро надо будет уходить.
>>53002
Обычно у баз данных стоит ограничение, откуда к ним можно подключаться. Так что чтобы к ней подсоединиться тебе скорее всего нужен ssh-доступ, чтобы прокинуть туннель. Либо использовать phpmyadmin доступ к которому тебе дан.
>>52913
https://habrahabr.ru/post/230737/
Прочитай тут пункт 6
Не понимаю зачем изобретать шаблонизатор когда есть много существующих, например, твиг.
>>52791
> while ($credit > 0):
Версии с двоеточием для шаблонов. используй в коде версию с фигурными скобками.
Алгоритм конечно немного странный, что ты сначала уходишь в минус, а потом как-то костыльно возвращаешь лишние деньги, но ответ получается правильный.
>>53587
Если у тебя задача матчить списки вида a, b, c то одним применением регулярки сделать это не получится. Такие варианты:
- применять регулярку, используя preg_match_all, недостаток: если в строке есть мусор то он молча проигнорируется. Победить этот недостаток можно сделав preg_split и посмотрев что останется - должны быть только пустые строки с проблемами.
- применять регулярку несколько раз используя preg_match с указанием позиции
- парсить как-то по-другому, без регулярок, например исплоьзуя метод рекурсивного спуска.
>>53599
Да, аноны, перекатывайтесь туда, я хотел позавчера сделать перекат, но устал и уснул. А, и сейчас кстати мне тоже скоро надо будет уходить.
>>53002
Обычно у баз данных стоит ограничение, откуда к ним можно подключаться. Так что чтобы к ней подсоединиться тебе скорее всего нужен ssh-доступ, чтобы прокинуть туннель. Либо использовать phpmyadmin доступ к которому тебе дан.
>>52913
https://habrahabr.ru/post/230737/
Прочитай тут пункт 6
Не понимаю зачем изобретать шаблонизатор когда есть много существующих, например, твиг.
>>52791
> while ($credit > 0):
Версии с двоеточием для шаблонов. используй в коде версию с фигурными скобками.
Алгоритм конечно немного странный, что ты сначала уходишь в минус, а потом как-то костыльно возвращаешь лишние деньги, но ответ получается правильный.
Твой запрос неправильный. Посмотри сам на код:
VALUES(много значений, одно значение)
Это так не работает. Если ты используешь SELECT внутри VALUES то он должен вернуть одно значение.
Однако можно сделать так
INSERT INTO t SELECT x, 3 AS num FROM ...
>>52489
> А если ServiceLocator не передавать через конструктор, то это будет DI-контейнер?
Я бы сказал наоборот: ServiceLocator это когда контейнер передают в конструктор.
> И как не передавать контейнер через конструктор тому же контроллеру, если в контроллере мне нужны TDG, хелпер для регистрации и т.д?
Для контроллеров обычно делают исключение и переают контейнер. Либо можно передавать только необходимые сервисы.
>>52144
Не знаю, в чем тут штука, но за второй вопрос в западных странах можно и судебный иск получить.
>>51859
По моему баш скрипты как раз удобнее для каких-то подготовительных действий. Ими можно запустить какой-нибудь сервис, например временный инстанс редиса с тестовым конфигом. И прибить его по завершению теста.
То, что ты указал, это тоже вариант, хотя мне кажется на баш делать это просто удобнее.
Тут еще надо смотреть как бы тесты не стали слишком медленными. С одной стороны в идеале надо каждый тест запускать на идентичной БД, с другой стороны, это может занять много времени.
Может быть годится вариант сначала подготовить SQL дамп а потом перед каждм тестом его заливать. Хотя .... мне кажется что и так медленно.
Может у тебя какие-то идеи есть? Помни, в реальных приложениях часто под сотню таблиц, тысячи тестов, и надо иметь возможность выполнять их параллельно (знчит надо несколько баз).
Можешь почитать например про баду: https://habrahabr.ru/company/badoo/blog/181488/
Твой запрос неправильный. Посмотри сам на код:
VALUES(много значений, одно значение)
Это так не работает. Если ты используешь SELECT внутри VALUES то он должен вернуть одно значение.
Однако можно сделать так
INSERT INTO t SELECT x, 3 AS num FROM ...
>>52489
> А если ServiceLocator не передавать через конструктор, то это будет DI-контейнер?
Я бы сказал наоборот: ServiceLocator это когда контейнер передают в конструктор.
> И как не передавать контейнер через конструктор тому же контроллеру, если в контроллере мне нужны TDG, хелпер для регистрации и т.д?
Для контроллеров обычно делают исключение и переают контейнер. Либо можно передавать только необходимые сервисы.
>>52144
Не знаю, в чем тут штука, но за второй вопрос в западных странах можно и судебный иск получить.
>>51859
По моему баш скрипты как раз удобнее для каких-то подготовительных действий. Ими можно запустить какой-нибудь сервис, например временный инстанс редиса с тестовым конфигом. И прибить его по завершению теста.
То, что ты указал, это тоже вариант, хотя мне кажется на баш делать это просто удобнее.
Тут еще надо смотреть как бы тесты не стали слишком медленными. С одной стороны в идеале надо каждый тест запускать на идентичной БД, с другой стороны, это может занять много времени.
Может быть годится вариант сначала подготовить SQL дамп а потом перед каждм тестом его заливать. Хотя .... мне кажется что и так медленно.
Может у тебя какие-то идеи есть? Помни, в реальных приложениях часто под сотню таблиц, тысячи тестов, и надо иметь возможность выполнять их параллельно (знчит надо несколько баз).
Можешь почитать например про баду: https://habrahabr.ru/company/badoo/blog/181488/
Остановился на truncate таблиц во время setUp и заливки свеженьких данных. Да, времени много занимает, но оно же в фоне выполняется. Не говоря уже о трависе, который сделает это на автомате.
Тесты на то и автоматические, что роботу все равно, какая у них там производительность.
>Может быть годится вариант сначала подготовить SQL дамп
Не универсально. Вариант с классами fixtures лучше, потому что не нужно их переписывать при смене платформы.
Кстати doctrine fixtures во время заливки данных по-умолчанию делает delete from table, что неудобно, потому что я не могу знать id.
Для выполнения truncate пришлось писать костыль по рецепту
https://coderwall.com/p/staybw/workaround-for-1701-cannot-truncate-a-table-referenced-in-a-foreign-key-constraint-using-doctrine-fixtures-load-purge-with-truncate
Разбираюсь сейчас с функциональными тестами, отправка форм и работа с куками в том browser-kit, что используется в симфони.
Пока так
https://github.com/nsdvw/TestHub
> Мне мешает "и так далее", даже не потому что оно занимает больше 100 строк кода, а то что это кропотливейшая и медленная работа.
решение давно известно: надо делать отдельные функции, создать тест, добавить вопрос итд.
> Набрать сто строчек не проблема, проблема нигде не ошибиться.
Плохо что ты прямо в тест код вставляешь, я бы выносил отдельно создание теста так как это наверняка и в других тестах понадобится да и код у тебя сейчас нечитаемый. Стена кода.
> Но мне приходилось постоянно заглядывать в свои модели, сверяться какие у них свойства, проверять вызовы в тестируемом классе,
Вот это мне кажется неправильно. Надо избегать закладывать в тесты знания о внутреннем устройстве кода. Ты тестируешь АПИ как черный ящик.
> Это надо держать целый отдел тестировщиков, чтобы они могли все свое время уделять не написанию кода, а его тестированию.
Такое бывает. Некоторые делают тестовые сценарии в екселе, некоторые в текстовом виде, и в большом приложении такие сценарии могут быть по объему как книга. И потом отдел живых тестеров по ним проходится. А как ты хотел? Если не тестировать что качество продукта на выходе стремительно пойдет вниз, это к гадалке не ходи.
Впрочем есть и другие примеры. Например, соцсети часто тестируют так: разработчик смотрит новую фичу, менеджер смотрит новую фичу (на продакшене), выкатывают на часть пользователей, потом на всех. Если идут жалобы - откатывают назад. Экономия на тестерах, но для банковского или медичинского приложения такой подход недопустим. Хотя... все мы помним историю с провалом дорогущего американского портала госуслуг по здравоохранению.
> что кто-то даст столько времени на тесты.
Зависит от масштаба проекта. Праздник оплачивает заказчик или работодатель.
> Кроме того, мне еще сеттеры для id пришлось добавлять в модели
Почему кстати? Зачем калькулятору id? Вообще-то вся идея доктрины как раз в том чтобы отвзяать модель от базы.
> должно быть приятно благодаря краулеру и прочим шикарным библиотекам, и не нужно никаких селениумов.
Так яваскрипт не научишься тестировать. Хитрый какой, конечно голый HTML намного проще тестировать чем реальный браузер гонять со всеми багами и задежрками.
> Ну и если функциональные тесты требуют заполнять бд фейковыми данными, зачем я здесь занимался ручной сборкой десятков объектов?
> Не лучше ли сразу залить фейковую бд, и на ней выполнять как интеграционные, так и функциональные тесты за один присест?
ну я подумал что без базы тестировать проде должно быть проще и быстрее.
> Мне мешает "и так далее", даже не потому что оно занимает больше 100 строк кода, а то что это кропотливейшая и медленная работа.
решение давно известно: надо делать отдельные функции, создать тест, добавить вопрос итд.
> Набрать сто строчек не проблема, проблема нигде не ошибиться.
Плохо что ты прямо в тест код вставляешь, я бы выносил отдельно создание теста так как это наверняка и в других тестах понадобится да и код у тебя сейчас нечитаемый. Стена кода.
> Но мне приходилось постоянно заглядывать в свои модели, сверяться какие у них свойства, проверять вызовы в тестируемом классе,
Вот это мне кажется неправильно. Надо избегать закладывать в тесты знания о внутреннем устройстве кода. Ты тестируешь АПИ как черный ящик.
> Это надо держать целый отдел тестировщиков, чтобы они могли все свое время уделять не написанию кода, а его тестированию.
Такое бывает. Некоторые делают тестовые сценарии в екселе, некоторые в текстовом виде, и в большом приложении такие сценарии могут быть по объему как книга. И потом отдел живых тестеров по ним проходится. А как ты хотел? Если не тестировать что качество продукта на выходе стремительно пойдет вниз, это к гадалке не ходи.
Впрочем есть и другие примеры. Например, соцсети часто тестируют так: разработчик смотрит новую фичу, менеджер смотрит новую фичу (на продакшене), выкатывают на часть пользователей, потом на всех. Если идут жалобы - откатывают назад. Экономия на тестерах, но для банковского или медичинского приложения такой подход недопустим. Хотя... все мы помним историю с провалом дорогущего американского портала госуслуг по здравоохранению.
> что кто-то даст столько времени на тесты.
Зависит от масштаба проекта. Праздник оплачивает заказчик или работодатель.
> Кроме того, мне еще сеттеры для id пришлось добавлять в модели
Почему кстати? Зачем калькулятору id? Вообще-то вся идея доктрины как раз в том чтобы отвзяать модель от базы.
> должно быть приятно благодаря краулеру и прочим шикарным библиотекам, и не нужно никаких селениумов.
Так яваскрипт не научишься тестировать. Хитрый какой, конечно голый HTML намного проще тестировать чем реальный браузер гонять со всеми багами и задежрками.
> Ну и если функциональные тесты требуют заполнять бд фейковыми данными, зачем я здесь занимался ручной сборкой десятков объектов?
> Не лучше ли сразу залить фейковую бд, и на ней выполнять как интеграционные, так и функциональные тесты за один присест?
ну я подумал что без базы тестировать проде должно быть проще и быстрее.
Про дамп - я имел в виду вместо того чтобы перед каждым тестом делать полную инициализацию базы, сгенерировать дамп и дамп перезаливать.
Вообще, я сам предпочитаю не совсем чистое тестирование, когда просто перед тестом вставляются в БД нужные мне в тесте сущности. Чтобы защититься от дубликатов , приходится генерировать уникальные email, идентификаторы и тд.
truncate еще по моему как-то не дружит с внешними ключами...
> Кстати doctrine fixtures во время заливки данных по-умолчанию делает delete from table, что неудобно, потому что я не могу знать id.
> echo shell_exec('app/console doctrine:fixtures:load
А куда в этом варианте идут ошибки выполнения скрипта? Здесь же это даже не проверяется, ну и ад. А ты говоришь баш скрипты - плохо, но там хотя бы результат выполнения команд проверяется если ставить set -e в начале.
> Тесты на то и автоматические, что роботу все равно, какая у них там производительность.
Иногда тебе хочется прогнать тесты самому.
> не нужно их переписывать при смене платформы.
ох, ты все думаешь что можно написать приложение с поддержкой нескольких баз данных...
https://github.com/nsdvw/TestHub/blob/master/tests/TestHubBundle/Helper/StringGeneratorTest.php
В этом тесте незачем вызывать инициализацию БД.
Еще мне не нравитя что тестовые данные лежат не вместе с тестами. А что если кто-то на продакшене их загрузит?
Ну и конечно вставлять данные в базу через доктрину это самый медленный способ который только можно придумать. Пока у тебя тестов мало, это конечно не проблема, но мне кажется дальше надо будет как-то это оптимизировать. Ну например как я предложил, через дамп.
Ну и еще один вариант, про который я тоже выше говорил, выполнять тест внутри транзакции которую потом откатить, и если ничего в базе не поменялось, то дамп можно заново не заливать.
Ну ок, подумаю как вынести заливку данных из setUp в более надежное место.
Кстати то он в статье делает прямой shell_exec, я у себя обернул в примочки симфони, что-то типа
$application->run(new StringInput($command));
Сейчас разберусь, если там есть проверка на ошибки, то все оставлю как есть.
>ты все думаешь что можно написать приложение с поддержкой нескольких баз данных...
А что мне думать? Из серверов баз данных знаю только mysql, ни разу не писал под другую, и естественно ни разу не делал миграцию с одной бд на другую.
Только получив такой опыт, я смогу как-то заранее угадывать, какой кусок кода будет портируемым, а какой нет.
Впрочем здесь я уверен: классы LoadEntityData явно портируемые, потому что все что там происходит это
$entity = new Entity;
$entity->setSmth($smth);
$manager->persist($entity);
$manager->flush();
Полностью поддержку нескольких баз сделать не получится (доверюсь твоему авторитету и опыту), но хотя бы нужно стремиться минифицировать геморрой при переезде.
>>53798
>например как я предложил, через дамп.
Ты же выше говорил, что
>>51058
> с дампом есть подвох, что при изменениях в БД дамп не будет работать, надо решать проблему связей
>вставлять данные в базу через доктрину это самый медленный способ который только можно придумать.
Самый медленный для робота, для меня самый быстрый. Когда понадобится, тогда поправлю. Пока меня все устраивает.
>А что если кто-то на продакшене их загрузит?
Зачем? Во всяком случае это проблема того кто загрузит.
И в дев-режиме я их как раз использую, когда открываю сайт в браузере, не сидеть же на голой базе?
>Еще мне не нравитя что тестовые данные лежат не вместе с тестами.
http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html#writing-simple-fixtures
>For a bundle located at src/AppBundle, the fixture classes should live inside src/AppBundle/DataFixtures/ORM
Шуд так шуд.
Вообще мне кажется ты стремишься к перфекционизму там где это не нужно.
Выгадать 3 секунды на тестах, которые делает робот - зачем? Чтобы показать что могу?
Сидеть и 2 часа думать над нестандартным алгоритмом генерации дампа, чтобы выгадать даже не знаю что.
На практике есть 2 фактора: затраченное время и поддерживаемость кода. Мне нужно писать как можно быстрее, и чтобы мой код разобрали самые тупые коллеги.
Я могу сейчас сесть, потратить пару часов времени и накатать нечто, что будет выполнять тест максимально быстро.
Но во-первых зачем? Во-вторых потом моим воображаемым коллегам сидеть и изучать то что я натворю?
Следовательно я должен придерживаться стандартов, даже если они не всегда оптимальные.
Ладно, короче проверь потом как будет время тесты и все остальное, у меня пока есть чем заняться.
Ну ок, подумаю как вынести заливку данных из setUp в более надежное место.
Кстати то он в статье делает прямой shell_exec, я у себя обернул в примочки симфони, что-то типа
$application->run(new StringInput($command));
Сейчас разберусь, если там есть проверка на ошибки, то все оставлю как есть.
>ты все думаешь что можно написать приложение с поддержкой нескольких баз данных...
А что мне думать? Из серверов баз данных знаю только mysql, ни разу не писал под другую, и естественно ни разу не делал миграцию с одной бд на другую.
Только получив такой опыт, я смогу как-то заранее угадывать, какой кусок кода будет портируемым, а какой нет.
Впрочем здесь я уверен: классы LoadEntityData явно портируемые, потому что все что там происходит это
$entity = new Entity;
$entity->setSmth($smth);
$manager->persist($entity);
$manager->flush();
Полностью поддержку нескольких баз сделать не получится (доверюсь твоему авторитету и опыту), но хотя бы нужно стремиться минифицировать геморрой при переезде.
>>53798
>например как я предложил, через дамп.
Ты же выше говорил, что
>>51058
> с дампом есть подвох, что при изменениях в БД дамп не будет работать, надо решать проблему связей
>вставлять данные в базу через доктрину это самый медленный способ который только можно придумать.
Самый медленный для робота, для меня самый быстрый. Когда понадобится, тогда поправлю. Пока меня все устраивает.
>А что если кто-то на продакшене их загрузит?
Зачем? Во всяком случае это проблема того кто загрузит.
И в дев-режиме я их как раз использую, когда открываю сайт в браузере, не сидеть же на голой базе?
>Еще мне не нравитя что тестовые данные лежат не вместе с тестами.
http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html#writing-simple-fixtures
>For a bundle located at src/AppBundle, the fixture classes should live inside src/AppBundle/DataFixtures/ORM
Шуд так шуд.
Вообще мне кажется ты стремишься к перфекционизму там где это не нужно.
Выгадать 3 секунды на тестах, которые делает робот - зачем? Чтобы показать что могу?
Сидеть и 2 часа думать над нестандартным алгоритмом генерации дампа, чтобы выгадать даже не знаю что.
На практике есть 2 фактора: затраченное время и поддерживаемость кода. Мне нужно писать как можно быстрее, и чтобы мой код разобрали самые тупые коллеги.
Я могу сейчас сесть, потратить пару часов времени и накатать нечто, что будет выполнять тест максимально быстро.
Но во-первых зачем? Во-вторых потом моим воображаемым коллегам сидеть и изучать то что я натворю?
Следовательно я должен придерживаться стандартов, даже если они не всегда оптимальные.
Ладно, короче проверь потом как будет время тесты и все остальное, у меня пока есть чем заняться.
https://github.com/fidnex/students/
Тебе я отвечу в новом треде >>753595 (OP) позже
https://github.com/timrene/php-link-list/blob/master/решение задач.md
>>46693
W1.(Начало) http://ideone.com/92myam
Ок
W2.(Переменные) http://ideone.com/6wmX2d
Все верно
W3.(Переменные) http://ideone.com/xs7wtu
Тут все верно.
W4.1(Условия и игра в кубики) http://ideone.com/2uOaPB
Все правильно
W4.2(Циклы и айфон в кредит) http://ideone.com/gWyK0D
Верно
W5.1(Циклы и айфон в кредит) http://ideone.com/BqCQWa
> $creditBalance != 0;
Безопаснее ставить условие > 0 чтобы если получится чуть меньше нуля, цикл не оказался вечным.
> $paymentTotal = $paymentTotal + $monthlyPayment - abs($creditBalance);
Выглядит как-то костыльно. Ты уходишь в минус, а потом пытаешься вернуть переплаченные деньги. А нельзя ли их просто не вычитать? Например
- прибавляем проценты и комиссию к остатку долга (!не вычитаем ничего пока!)
- если остаток маленький, выплачиваем сколько осталось и уходим
- иначе платим 5000
W5.2(Циклы и айфон в кредит) http://ideone.com/ZNqjGw
Считает верно, но не советую писать циклы с большой шапкой, так как трудновато его читать. Лучше бы увеличение капитала поместить в тело цикла.
W5.3(Массивы и рулетка) http://ideone.com/ryNGzj
Правильно
W5.4(Массивы и рулетка) http://ideone.com/c4Wtet
Верно
W5.5(Массивы и рулетка) http://ideone.com/Cy79xO
Ок, все правильно.
W5.6(Массивы и рулетка) http://ideone.com/cVtRbN
Правильно.
Строки, хакеры и шифровки (В тылу врага) http://ideone.com/Otzabk
Да, правильно.
Строки, хакеры и шифровки (l33tspeak) http://ideone.com/51m937
Ок, тут все верно.
Строки, хакеры и шифровки (На словах ты Лев Толстой) http://ideone.com/eE0lka
Все правильно. Хотя ты бы мог использовать цикл чтобы не копипастить первые 2 строки.
Строки, хакеры и шифровки (Палиндром) http://ideone.com/07Ea89
Работает верно, но после первого несовпадения можно было бы выходить из цикла.
Функции и новый айпад http://ideone.com/shMxHY
Тут то же замечание что и к айфону: сложновато как-то, в остальном верно.
Регулярные выражения http://ideone.com/b4XOqO
да, правильно.
Регулярные выражения http://ideone.com/qT0TVN
Праивльно, но когда пишешь тесты то стоит выводить что именно неправильно, а у тебя просто пишется что проверка неуспешна, а почему - непонятно.
Регулярные выражения http://ideone.com/c1LKpO
Ок, верно
Регулярные выражения http://ideone.com/vLQDWx
Все правильно
Регулярные выражения https://ideone.com/FRAp6H
Почти верно, но в имени домена не может быть подчеркивания или плюса.
Регулярные выражения https://ideone.com/7oED9U
> [.,;:!?][^ ]
Это сработает на 2 вопроса подряд. Лучше требовать чтобы за знаком шла буква
> зделал|зделаю|зделан
Повторяющуюся часть можно сгруппировать вместе.
А так, в общем, хорошо. Продолжай в том же духе.
https://github.com/fidnex/students/
Тебе я отвечу в новом треде >>753595 (OP) позже
https://github.com/timrene/php-link-list/blob/master/решение задач.md
>>46693
W1.(Начало) http://ideone.com/92myam
Ок
W2.(Переменные) http://ideone.com/6wmX2d
Все верно
W3.(Переменные) http://ideone.com/xs7wtu
Тут все верно.
W4.1(Условия и игра в кубики) http://ideone.com/2uOaPB
Все правильно
W4.2(Циклы и айфон в кредит) http://ideone.com/gWyK0D
Верно
W5.1(Циклы и айфон в кредит) http://ideone.com/BqCQWa
> $creditBalance != 0;
Безопаснее ставить условие > 0 чтобы если получится чуть меньше нуля, цикл не оказался вечным.
> $paymentTotal = $paymentTotal + $monthlyPayment - abs($creditBalance);
Выглядит как-то костыльно. Ты уходишь в минус, а потом пытаешься вернуть переплаченные деньги. А нельзя ли их просто не вычитать? Например
- прибавляем проценты и комиссию к остатку долга (!не вычитаем ничего пока!)
- если остаток маленький, выплачиваем сколько осталось и уходим
- иначе платим 5000
W5.2(Циклы и айфон в кредит) http://ideone.com/ZNqjGw
Считает верно, но не советую писать циклы с большой шапкой, так как трудновато его читать. Лучше бы увеличение капитала поместить в тело цикла.
W5.3(Массивы и рулетка) http://ideone.com/ryNGzj
Правильно
W5.4(Массивы и рулетка) http://ideone.com/c4Wtet
Верно
W5.5(Массивы и рулетка) http://ideone.com/Cy79xO
Ок, все правильно.
W5.6(Массивы и рулетка) http://ideone.com/cVtRbN
Правильно.
Строки, хакеры и шифровки (В тылу врага) http://ideone.com/Otzabk
Да, правильно.
Строки, хакеры и шифровки (l33tspeak) http://ideone.com/51m937
Ок, тут все верно.
Строки, хакеры и шифровки (На словах ты Лев Толстой) http://ideone.com/eE0lka
Все правильно. Хотя ты бы мог использовать цикл чтобы не копипастить первые 2 строки.
Строки, хакеры и шифровки (Палиндром) http://ideone.com/07Ea89
Работает верно, но после первого несовпадения можно было бы выходить из цикла.
Функции и новый айпад http://ideone.com/shMxHY
Тут то же замечание что и к айфону: сложновато как-то, в остальном верно.
Регулярные выражения http://ideone.com/b4XOqO
да, правильно.
Регулярные выражения http://ideone.com/qT0TVN
Праивльно, но когда пишешь тесты то стоит выводить что именно неправильно, а у тебя просто пишется что проверка неуспешна, а почему - непонятно.
Регулярные выражения http://ideone.com/c1LKpO
Ок, верно
Регулярные выражения http://ideone.com/vLQDWx
Все правильно
Регулярные выражения https://ideone.com/FRAp6H
Почти верно, но в имени домена не может быть подчеркивания или плюса.
Регулярные выражения https://ideone.com/7oED9U
> [.,;:!?][^ ]
Это сработает на 2 вопроса подряд. Лучше требовать чтобы за знаком шла буква
> зделал|зделаю|зделан
Повторяющуюся часть можно сгруппировать вместе.
А так, в общем, хорошо. Продолжай в том же духе.
https://github.com/greenTea242/Student_List/compare/master@{20 day}...master
Тебе я отвечу в новом треде >>753595 (OP) позже
А зачем ты вынес bootstrap из public? Ведь там идея что корень сервера в public и все, что за ним, недоступно через веб-сервер. Я имел в виду что бутстрап должен быть в исходном, неисправленном виде в своей папке внутри public.
>> А если пользователь при редактировании захочет очистить какое-то поле, эта проверка ведь его не пропустит?
> Можно удалять поля полностью и добавлять их в бд? Я про это не знал. Мои регулярки не пропускают пустые значения. Менять? Пользователь что, вообще может не заполнять ничего?
Я имел в виду, что если там есть необязательное поле (не помню есть ли оно в условиях), то тогда разумеется пользователь может очистить это поле чтобы сохранить пустое значение.
> Я не нарушаю логику отображения перенося ее из шаблона в методы из вспомогательных классов как ты советовал? В борьбе с копипастом создал их(методов) несколько.
Немножечко нарушаешь, так что лучше особо большие куски кода так не делать. Вообще, в нормальных шаблонизаторах вроде twig есть макросы, то есть "подшаблоны", и в них можно выносить куски кода и позже их вызывать. У нас их нет, так что надо либо куски выносить в отдельные файлы либо делать класс-помощник.
Но для борьбы с копипастом, я думаю, это приемлемо. Просто изолируй все такие функции в отдельном классе.
> [Конфигурация нашей базы данных]
В ini файлах так обозначают не комментарий, а название секции. Их лучше делать латинницей и короткими (или вообще не делать если файл маленький). Комментарий это строка, начинающаяся с точки с запятой.
> static function isInputChecked($genderOfAbiturient, $valueFromInput)
вообще тут можно было просто написать <?= $student->getGender() == Student::GENDER_MALE ? ' checked' : '' ?>. Но можно и функцию.
Остальное позже в новом треде напишу.
> Это нормально что я несколько раз отклонился от общего для кода стиля(камелкейс) для имени переменной и имени метода и использовал вариант с нижним подчеркиванием ("CSRF_token")? Мне показалось, что не очень красиво если будет "CSRFToken". Стоило ли тогда тоже самое делать с authToken для, не знаю как сказать, симметрии чтоли.
Нет, нехорошо. Лучше CSRFToken или csrfToken.
> Ну он у меня в бд попадал через модель. Сейчас после манипуляций с двумя токенами я его добавляю отдельно https://github.com/greenTea242/Student_List/blob/master/public/register.php#L31 . Ок?
Я думаю, не стоит ради токена делать лишний аргумент в setProperties. Его можно задать отдельной командой вроде
$student->setToken(...);
>>46722
> Нужно было перенести функционал метода addAbiturient в createErrorList или сделать как я сделал(методы теперь получают отдельно объект, без добавления его в свойства класса)?
Главная идея что удобнее когда у нас есть 1 метод, которому мы даем студента и получаем ошибки, а не несколько методов и надо угадать в каком порядке их правильно вызывать. То есть твой вариант годится.
https://github.com/greenTea242/Student_List/compare/master@{20 day}...master
Тебе я отвечу в новом треде >>753595 (OP) позже
А зачем ты вынес bootstrap из public? Ведь там идея что корень сервера в public и все, что за ним, недоступно через веб-сервер. Я имел в виду что бутстрап должен быть в исходном, неисправленном виде в своей папке внутри public.
>> А если пользователь при редактировании захочет очистить какое-то поле, эта проверка ведь его не пропустит?
> Можно удалять поля полностью и добавлять их в бд? Я про это не знал. Мои регулярки не пропускают пустые значения. Менять? Пользователь что, вообще может не заполнять ничего?
Я имел в виду, что если там есть необязательное поле (не помню есть ли оно в условиях), то тогда разумеется пользователь может очистить это поле чтобы сохранить пустое значение.
> Я не нарушаю логику отображения перенося ее из шаблона в методы из вспомогательных классов как ты советовал? В борьбе с копипастом создал их(методов) несколько.
Немножечко нарушаешь, так что лучше особо большие куски кода так не делать. Вообще, в нормальных шаблонизаторах вроде twig есть макросы, то есть "подшаблоны", и в них можно выносить куски кода и позже их вызывать. У нас их нет, так что надо либо куски выносить в отдельные файлы либо делать класс-помощник.
Но для борьбы с копипастом, я думаю, это приемлемо. Просто изолируй все такие функции в отдельном классе.
> [Конфигурация нашей базы данных]
В ini файлах так обозначают не комментарий, а название секции. Их лучше делать латинницей и короткими (или вообще не делать если файл маленький). Комментарий это строка, начинающаяся с точки с запятой.
> static function isInputChecked($genderOfAbiturient, $valueFromInput)
вообще тут можно было просто написать <?= $student->getGender() == Student::GENDER_MALE ? ' checked' : '' ?>. Но можно и функцию.
Остальное позже в новом треде напишу.
> Это нормально что я несколько раз отклонился от общего для кода стиля(камелкейс) для имени переменной и имени метода и использовал вариант с нижним подчеркиванием ("CSRF_token")? Мне показалось, что не очень красиво если будет "CSRFToken". Стоило ли тогда тоже самое делать с authToken для, не знаю как сказать, симметрии чтоли.
Нет, нехорошо. Лучше CSRFToken или csrfToken.
> Ну он у меня в бд попадал через модель. Сейчас после манипуляций с двумя токенами я его добавляю отдельно https://github.com/greenTea242/Student_List/blob/master/public/register.php#L31 . Ок?
Я думаю, не стоит ради токена делать лишний аргумент в setProperties. Его можно задать отдельной командой вроде
$student->setToken(...);
>>46722
> Нужно было перенести функционал метода addAbiturient в createErrorList или сделать как я сделал(методы теперь получают отдельно объект, без добавления его в свойства класса)?
Главная идея что удобнее когда у нас есть 1 метод, которому мы даем студента и получаем ошибки, а не несколько методов и надо угадать в каком порядке их правильно вызывать. То есть твой вариант годится.
> Функция start_form подставляет в name именно из имени класса.
> Тогда как я не зная имя формы буду брать данные из запроса?
> $formData = $request->request->get('имя_формы?');
Форма сама берет данные из запроса если ты ей передаешь Request.
> (впрочем сейчас дошло, что к данные из формы можно получить не из реквеста, а из ее объекта, пропустив через $form->handle($request), и оно уже само разгребет какие данные ему нужны).
Вот именно. И разные названия форм защищают нас от случая когда мы обрабатываем данные не тем классом.
По алгоритму. Я думаю, что нам удобнее конечно передавать id сеанса прохождения явно скрытым полем либо в URL либо хранить его в куках (тут надо учесть что пользователь может теоретически попытатться пройти 2 разных теста параллельно - значит нужные разные куки).
Соответственно идея такая:
- пользователь заходит на вводную страницу и ПОСТит запрос "начать тест" (изменения на сервере мы делаем через ПОСТ)
- создается сеанс (и регистрация если надо), и пользователь получает ид сеанса (либо в куку либо в URL, вариант с URL более соответствует REST имхо - не имея сеанса нельзя открыть вопрос и не требуется проверять соответствие сеанса тесту. Также при истечении времени мы можем показать ссылку на результат). Например /test/session/{sessionId}/question/{qId} или вариант с /test/ + кука.
- далее, пользователя редиректит на первый вопрос
- если у пользователя нет id сеанса, и он хочет открыть вопрос, показываем ошибку (может время закончилось? может пользователь уже прошел тест? может пользователь опечатался в УРЛ? может баг у нас?) и предлагаем начать тест заново либо посмотреть результат например. Возможны варианты.
- отвечая, пользователь ПОСТит ответ на вопрос и редиректится либо на следующий либо на результат или видит ошибки если время истекло.
- заканчивая тест, пользователь ПОСТит соотв. запрос и редиректится на страницу результата.
У тебя мне не очень нравится вся эта магия с автоматическим созданием сеансов, да еще и в ответ на GET запрос, это по моему плохо соответствует логике HTTP. И смотри, у тебя там есть магия с угадыванием ид сеанса, а в моем варианте все однозначно - сеанс указан в УРЛ. Меньше проверок, меньше дыр, меньше ошибок. И у меня все чин чином - GET для получения данных, POST - Redirect - GET для изменения.
Что скажешь? И почитай про REST тогда если не читал.
> Функция start_form подставляет в name именно из имени класса.
> Тогда как я не зная имя формы буду брать данные из запроса?
> $formData = $request->request->get('имя_формы?');
Форма сама берет данные из запроса если ты ей передаешь Request.
> (впрочем сейчас дошло, что к данные из формы можно получить не из реквеста, а из ее объекта, пропустив через $form->handle($request), и оно уже само разгребет какие данные ему нужны).
Вот именно. И разные названия форм защищают нас от случая когда мы обрабатываем данные не тем классом.
По алгоритму. Я думаю, что нам удобнее конечно передавать id сеанса прохождения явно скрытым полем либо в URL либо хранить его в куках (тут надо учесть что пользователь может теоретически попытатться пройти 2 разных теста параллельно - значит нужные разные куки).
Соответственно идея такая:
- пользователь заходит на вводную страницу и ПОСТит запрос "начать тест" (изменения на сервере мы делаем через ПОСТ)
- создается сеанс (и регистрация если надо), и пользователь получает ид сеанса (либо в куку либо в URL, вариант с URL более соответствует REST имхо - не имея сеанса нельзя открыть вопрос и не требуется проверять соответствие сеанса тесту. Также при истечении времени мы можем показать ссылку на результат). Например /test/session/{sessionId}/question/{qId} или вариант с /test/ + кука.
- далее, пользователя редиректит на первый вопрос
- если у пользователя нет id сеанса, и он хочет открыть вопрос, показываем ошибку (может время закончилось? может пользователь уже прошел тест? может пользователь опечатался в УРЛ? может баг у нас?) и предлагаем начать тест заново либо посмотреть результат например. Возможны варианты.
- отвечая, пользователь ПОСТит ответ на вопрос и редиректится либо на следующий либо на результат или видит ошибки если время истекло.
- заканчивая тест, пользователь ПОСТит соотв. запрос и редиректится на страницу результата.
У тебя мне не очень нравится вся эта магия с автоматическим созданием сеансов, да еще и в ответ на GET запрос, это по моему плохо соответствует логике HTTP. И смотри, у тебя там есть магия с угадыванием ид сеанса, а в моем варианте все однозначно - сеанс указан в УРЛ. Меньше проверок, меньше дыр, меньше ошибок. И у меня все чин чином - GET для получения данных, POST - Redirect - GET для изменения.
Что скажешь? И почитай про REST тогда если не читал.
Можно положить внутрь label инпут и еще один элемент для подсветки кнопки. Тогда for не нужен.
>>46939
> Junior'a разработчика
Junior разработчика
>>46941
В $_SERVER посмотри
>>47172
https://github.com/nsdvw/TestHub я наверно гляну позже в новом >>753595 (OP) треде
> Дня 3 только обдумывал как сделать, переписывал несколько раз.
Это нормально учитывая что ты начинающий
> Как вообще по времени в нормальной конторе?
зависит от компании но долго тянуть в любом случае плохо. Пока ты начинающий впрочем требования не такие строгие.
> Короче, у меня трудности в проектировании архитектуры приложения
Ну не беда, думаю, с опытом будет лучше.
> Но я все-таки думаю, что в хорошей конторе действительно будет хотя бы командная работа
Вообще бывает по-разному. Некоторая самостоятельность не помешает. Но начинающим конечно обычно дают задачи попроще.
Можно положить внутрь label инпут и еще один элемент для подсветки кнопки. Тогда for не нужен.
>>46939
> Junior'a разработчика
Junior разработчика
>>46941
В $_SERVER посмотри
>>47172
https://github.com/nsdvw/TestHub я наверно гляну позже в новом >>753595 (OP) треде
> Дня 3 только обдумывал как сделать, переписывал несколько раз.
Это нормально учитывая что ты начинающий
> Как вообще по времени в нормальной конторе?
зависит от компании но долго тянуть в любом случае плохо. Пока ты начинающий впрочем требования не такие строгие.
> Короче, у меня трудности в проектировании архитектуры приложения
Ну не беда, думаю, с опытом будет лучше.
> Но я все-таки думаю, что в хорошей конторе действительно будет хотя бы командная работа
Вообще бывает по-разному. Некоторая самостоятельность не помешает. Но начинающим конечно обычно дают задачи попроще.
Тебя https://github.com/foobar1643/filehosting я наверно проверю в следующем >>753595 (OP) треде
>>47709
Быть старшим не так просто. Ведь если ты старший и ты допустим что-то забыл проверить или сделать, то джуниоры будут сидеть без дела или тупить. И терять время. Надо вовремя проверять их код, заранее подготавливать им задачи с пояснениями и постоянно отвечать на их вопросы (даже когда думаешь над задачей). Особенно весело когда что-то от тебя нужно сразу нескольким людям, включая не-разработчиков. И свои задачи успевать делать. И думать можно ли доверить человеку задачу требующую понимания архитектуры например, не наломает ли он дров.
А что касается времени, если это в офисе то те же самые 8 часов. ну может иногда придется задержаться или прийти пораньше чтобы джуну задачу подготовить или замечания написать.
А у джуниора ответственности меньше, если он протупил то никто другой не страдает.
Но вообще, везде по-разному. Где-то просто неопытных и несамостоятельных вообще не берут, где-то с ними возятся, где-то отправляют в гугл.
>>47760
> я бы например не против 9-часового дня
приходишь домой, открываешь мануал симфони и пхп тред и учишься :3
> Я к тому, что опытный квалифицированный чел должен быть избавлен от рутинной работы, ему поручить сложные общие вопросы типа архитектуры, алгоритма работы какой-то нестандартной фичи, а также код-ревью.
Ну, так никто работать не будет. Обычно старший разработчик тоже кодит.
> Я думал то оп бомбанул от предложения час в день тратить на детальный разбор полетов,
Час общего времени в день это много и дорого. Потому я говорил про 15 минут в неделю. А насчет проверки кода - это можно сделать в частном порядке, не отвлекая других.
[abcd]* значит любые указанные символы в любом порядке. Ты что-то не так наверно сделал, у тебя же идет вариант (a*b*c*d*){4} который как минимум странный.Например непонятно откуда цифра 4 взялась.
>>47953
1) логично в students добавить так как хеш это свойство студента
2) да, в классе Student
>>47986
тебя https://github.com/applejacky/tmp я прверю в новом треде >>753595 (OP)
>>48131
https://ideone.com/DXHFhC
правильно
https://ideone.com/qwtIDQ
Ок, верно, можно еще было исплоьзовать флаг i чтобы не писать отдельно маленькие и большие буквы.
https://ideone.com/pMXJWi
Тоже верно.
https://ideone.com/XL60TP
> "/ |-|\(|\)/";
Тут удобнее использовать квадратные скобки наверно. А так, праивльно.
https://ideone.com/PJMCE3
Ок. верно
https://ideone.com/Z2HIdy
Почти верно, ты только забыл что в домене могут быть цифры: 003.ru
[abcd]* значит любые указанные символы в любом порядке. Ты что-то не так наверно сделал, у тебя же идет вариант (a*b*c*d*){4} который как минимум странный.Например непонятно откуда цифра 4 взялась.
>>47953
1) логично в students добавить так как хеш это свойство студента
2) да, в классе Student
>>47986
тебя https://github.com/applejacky/tmp я прверю в новом треде >>753595 (OP)
>>48131
https://ideone.com/DXHFhC
правильно
https://ideone.com/qwtIDQ
Ок, верно, можно еще было исплоьзовать флаг i чтобы не писать отдельно маленькие и большие буквы.
https://ideone.com/pMXJWi
Тоже верно.
https://ideone.com/XL60TP
> "/ |-|\(|\)/";
Тут удобнее использовать квадратные скобки наверно. А так, праивльно.
https://ideone.com/PJMCE3
Ок. верно
https://ideone.com/Z2HIdy
Почти верно, ты только забыл что в домене могут быть цифры: 003.ru
> Это поведение можно менять session_set_cookie_params
Это парааметры жизни куки, а не самой сессии на сервере
> А что, по-твоему, было бы лучшей идеей для логина? Я пробую варинаты.
куки
>>48149
> Вроде в итоге справился с тестовым
Вообще, нет, ему по моему сказали учебники идти читать (второй раз).
>>48163
Реально, но может 1с удобнее?
>>48261
Код некачественный, по моему есть проблемы с безопасностью местами. "Соцсеть" - набо процедурной лапши.
>>48292
тебя https://github.com/TheSidSpears/Students я наверно проверю позже в новом треде >>753595 (OP)
>>48907
> . Конечно хорошо, когда только закоммитишь, а травис тебе уже во всю трубит о баге.
Потому что часто такие ситуации, когда баг обнаруживается спустя много времени, код разросся, исправлять тяжело и неудобно.
Даже хуже: ты написал функционал, через полгода ты или ньюфаг его сломал, и ошибка может остаться незамеченной. Тестами ты защищаешь свою сделанную работу.
> 1. В симфони нет бандла для тестирования?
Не знаю, погугли или помануаль. Там есть например эмулятор html браузера и штука для выборки узлов ДОМ по css селекторам (DomCrawler):
http://symfony.com/doc/current/book/testing.html
http://symfony.com/doc/current/best_practices/tests.html
> Это поведение можно менять session_set_cookie_params
Это парааметры жизни куки, а не самой сессии на сервере
> А что, по-твоему, было бы лучшей идеей для логина? Я пробую варинаты.
куки
>>48149
> Вроде в итоге справился с тестовым
Вообще, нет, ему по моему сказали учебники идти читать (второй раз).
>>48163
Реально, но может 1с удобнее?
>>48261
Код некачественный, по моему есть проблемы с безопасностью местами. "Соцсеть" - набо процедурной лапши.
>>48292
тебя https://github.com/TheSidSpears/Students я наверно проверю позже в новом треде >>753595 (OP)
>>48907
> . Конечно хорошо, когда только закоммитишь, а травис тебе уже во всю трубит о баге.
Потому что часто такие ситуации, когда баг обнаруживается спустя много времени, код разросся, исправлять тяжело и неудобно.
Даже хуже: ты написал функционал, через полгода ты или ньюфаг его сломал, и ошибка может остаться незамеченной. Тестами ты защищаешь свою сделанную работу.
> 1. В симфони нет бандла для тестирования?
Не знаю, погугли или помануаль. Там есть например эмулятор html браузера и штука для выборки узлов ДОМ по css селекторам (DomCrawler):
http://symfony.com/doc/current/book/testing.html
http://symfony.com/doc/current/best_practices/tests.html
git branch , git config, got log -p, git diff , git reset
>>50126
тебя https://github.com/TheSidSpears/Students я наверно проверю позже в новом треде >>753595 (OP)
>>51035
> Не знаю как из класса теста обратиться к доктрине.
Получить через контейнер
> где предлагают возню с xml-ными конфигами и дампами.
Вообще если дамп в том же csv или yaml то это удобнее чем код писать, меньше лишнего.
>>51212
Отлаживай код. Натыкай var_dump и посмотри что в переменных, смотри, какие ветки выполняются, а какие нет, сдампь что пришло в var_dump($_POST); и тд.
Также, надо либо включить режим вывода ошибок (display_errors) если речь о тестовом сервере либо смотреть их в логах (погугли) если речь о продакшене.
>>53816
> Кстати то он в статье делает прямой shell_exec, я у себя обернул в примочки симфони, что-то типа
> $application->run(new StringInput($command));
разниа есть, в первом случае запускается shell и из него процесс php, во втором все внутри текущего процесса происходит
git branch , git config, got log -p, git diff , git reset
>>50126
тебя https://github.com/TheSidSpears/Students я наверно проверю позже в новом треде >>753595 (OP)
>>51035
> Не знаю как из класса теста обратиться к доктрине.
Получить через контейнер
> где предлагают возню с xml-ными конфигами и дампами.
Вообще если дамп в том же csv или yaml то это удобнее чем код писать, меньше лишнего.
>>51212
Отлаживай код. Натыкай var_dump и посмотри что в переменных, смотри, какие ветки выполняются, а какие нет, сдампь что пришло в var_dump($_POST); и тд.
Также, надо либо включить режим вывода ошибок (display_errors) если речь о тестовом сервере либо смотреть их в логах (погугли) если речь о продакшене.
>>53816
> Кстати то он в статье делает прямой shell_exec, я у себя обернул в примочки симфони, что-то типа
> $application->run(new StringInput($command));
разниа есть, в первом случае запускается shell и из него процесс php, во втором все внутри текущего процесса происходит
Переходите в новый тред >>753595 (OP)
Вы видите копию треда, сохраненную 15 января 2020 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.