Вы видите копию треда, сохраненную 16 июля 2017 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.
Пожалуйста, пишите один большой пост вместо нескольких маленьких и не флудите не по теме.
Это тред для начинающих. Не написал за свою жизнь ни одной программы и имеешь тройку по математике? Ты наш человек.
Устанавливать пока что ничего не требуется, разве что редактор кода вроде Sublime Text 3, Notepad++, Visual Studio Code, Netbeans PHP или PhpStorm (с ним будет удобнее).
Предыдущий тред был тут: >>988868 (OP)
Еще предыдущие треды ищутся в гугле по словам "клуб php" или в архиваче. Еще есть такой архив тредов: phpclub.rf.gd
Мейлач лежит? Есть запасной тред: доброчан-орг/s/res/23225.xhtml#i46467
Что самое главное для программиста? Умение аккуратно оформлять код (читай второй пост, прежде чем писать код).
Правила: ведем себя воспитанно, помогаем новичкам, постим ссылки на решения задачек, ОП их проверяет и дает советы и замечания. ОП заходит редко, где-то раз в 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/Symfony: https://gist.github.com/codedokode/8733007
- После нее можно изучать автоматизированное тестирование https://gist.github.com/codedokode/a455bde7d0748c0a351a
- Если ты все решил, переходи к 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://github.com/codedokode/pasta/blob/master/db/databases.md
Что почитать
- Мануал по PHP — http://www.php.net/manual/ru/langref.php
- Сайт phptherightway (перевод на русский: http://getjump.me/ru-php-the-right-way/ )
- По PHP: Профессиональное программирование на PHP Джордж Шлосснейгл
- По PHP: Мэтт Зандстра — PHP: Объекты, шаблоны, методики программирования
- JS: learn.javascript.ru
- Про Git: https://git-scm.com/book/ru/v1
Оформляй код аккуратно!!! — например пропусти через phpformatter.com . Также, если ты пользуешься IDE вроде PhpStorm, Netbeans, Eclipse, то в них эта опция встроена, подробнее: https://gist.github.com/codedokode/8759492
У ОПа нет аккаунтов и групп вконтакте, в фейсбуке, в твиттере, все "пхп-треды" там поддельные.
Платиновые вопросы
- Почему PHP? Потому что фейсбук и википедия на нем написаны, и вакансий море, и учить легко.
- Сайт опять упал!!!!! — Не паникуй, а открой http://rghost.ru/6bfCY9lfl и получи личную немного устаревшую оффлайновую копию сайта (можно читать хоть на андроиде без интернета)
- Что надо знать чтобы найти работу - разработчику: PHP, SQL, HTML/CSS, JS, ООП, Git, композер, MVC, фреймворк. Верстальщику - HTML/CSS, JS, jQuery
- Можно подробнее про поиск работы, собеседования - нет, ОП писать не будет, но может кто из анонов захочет рассказать. Поищите тред перезвонивших, а также раздел /wrk/.
- Сколько времени надо изучать все это? - все зависит от тебя, но не меньше 6-8 месяцев
- Посоветуйте редактор кода - Sublime Text 3, Notepad++, PhpStorm
- Нужен ли ООП, фреймворки, MVC, git, composer? — Да, однозначно. Посмотри любую вакансию.
- Что самое главное для программиста? Умение аккуратно оформлять код.
- ОП, сделай за меня мою работу или домашнее задание? — Это конечно, хорошая идея, но нет.
- Подскажи сайты для поиска работы, я не умею гуглить? — 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/samdark/fig-standards-ru/blob/master/accepted/ru/PSR-1-basic-coding-standard.md
PSR-2: https://github.com/samdark/fig-standards-ru/blob/master/accepted/ru/PSR-2-coding-style-guide.md
------------------
Итак, ты зашел в тред и решил помочь какому-то анону, дав ему совет или подсказку. Спасибо! Но прочти сначала эти напоминания, чтобы твоя помощь действительно была полезной.
Давай удочку, а не рыбу
Лучше не давать готовое решение проблемы, а рассказать как его искать. Может дать ключевые слова для гугла или ссылку. Но помогай, а не пытайся показать превосходство. Если даешь ссылки на нерусскоязычные статьи, упомяни об этом.
Будь доброжелателен
Не годится: «Ты мануал хоть раз в жизни открывал, обезьяна?»
Не годится: «В гугле забанили?»
Не годится: «Твой код плохой»
Хорошо: «Вот, как можно улучшить этот код: ...»
Хорошо: «Ты неправильно используешь функцию 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/samdark/fig-standards-ru/blob/master/accepted/ru/PSR-1-basic-coding-standard.md
PSR-2: https://github.com/samdark/fig-standards-ru/blob/master/accepted/ru/PSR-2-coding-style-guide.md
------------------
Итак, ты зашел в тред и решил помочь какому-то анону, дав ему совет или подсказку. Спасибо! Но прочти сначала эти напоминания, чтобы твоя помощь действительно была полезной.
Давай удочку, а не рыбу
Лучше не давать готовое решение проблемы, а рассказать как его искать. Может дать ключевые слова для гугла или ссылку. Но помогай, а не пытайся показать превосходство. Если даешь ссылки на нерусскоязычные статьи, упомяни об этом.
Будь доброжелателен
Не годится: «Ты мануал хоть раз в жизни открывал, обезьяна?»
Не годится: «В гугле забанили?»
Не годится: «Твой код плохой»
Хорошо: «Вот, как можно улучшить этот код: ...»
Хорошо: «Ты неправильно используешь функцию abc(). Вот ее описание: ссылка, и как видишь ей надо передать строку, а не массив»
Не придирайся к знанию английского или русского языка.
Объясняй
Не очень хорошо: «сделай как в этом коде»
Хорошо: «если ты вставляешь текст от пользователя в SQL запрос, то получается SQl-инъекция, которая позволяет взломать твой сервер (ссылки). Чтобы этого избежать, надо вставлять данные с помощью плейсхолдеров (ссылки)»
Хорошо: «Помни, что код пишется для людей. Если писать такие большие функции, то в них становится трудно разобраться...»
Не проповедуй
Мы учим использованию самых распространненных подходов, стандартов, библиотеки фреймворков. Если ты не любишь ООП, пробелы в коде, jQuery, сам PHP, то рассказать об этом стоит в каком-нибудь другом треде.
Не придирайся к знанию английского языка, анон пишет как умеет.
Ах да. Если тебе кажется, что что-то в учебнике или задачах можно сделать лучше — пиши, обратная связь всегда очень полезна.
>>997929 https://github.com/Merkalov/Students
Если кого-то еще забыл, напомните о себе тут.
господа, не обращайте внимание на несоответствие цитаты и автора, потом исправлю, там неправильно БД перекатил просто
Собственно, мне стало интересно и я загуглил "что означает одинарное двоеточие после объявления функции в php"
Указывает тип возвращаемого значения.
http://php.net/manual/ru/functions.returning-values.php
Полагаю, потому что постоянная смена фреймворков создает рабочие места и повышает зарплаты, а стагнирование на одном - нет. То есть не стоит искать какую-то абстрактную истину и объективное превосходство.
Не видел нигде документацию лучше чем для Laravel. Войти в фреймворк и писать REST API на нем можно через 4 дня уже, хуй знает в чем твоя проблема.
Хайп и только, никаких киллер-фич нет. Там очень много статики (новичок может писать в привычном процедурном стиле) и eloquent-магии, которая постоянно стреляет по ногам как только ты собираешься делать что-то сложнее обычного CRUD'а. На пике высказывания одного из контрибьюторов в ядро PHP.
> "в yii2 уебищные виджеты
Это вообще какой-то странный аргумент, сейчас же такое повсюду на JS пишется и его фреймворках.
Виджеты действительно говно почти все, так ведь их и не заставляют использовать.
Чисто по мне ларавел реально ужасен, исключительно громоздкий в тех местах, где этого не нужно, исключительно сахарный в тех местах, где важно понимание происходящего и так далее. Это еще просто мой вкус, хер бы с ним, опытные разрабы на любой фреймворк быстро переходят независимо от прозрачности. По зрелым соображениям он исключительно тормозной. Бенчи можно погуглить. Свои тоже проводил год назад, реально нет ничего лагучее этого дерьма. Брать ларавел - это стрелять себе в ноги, а потом докупать коляски, чтобы быть в состоянии передвигаться. Юи2 шустрее гораздо. Но для реальной скорости я и юи2 то не взял бы.
Спасибо, а что если тип возвращаемого значения указан как ?string , что значит знак вопроса?
>Э? А почему оно должно меняться? Для обычных индексов надо заново запустить индексацию. Это индексер ведь выкачивает данные из базы и создает/обновляет индексы. Для RT-индексов надо дропнуть их и пересоздать заново наверно. Или может демон перезапустить.
Да, я имел ввиду что после индексации и перезапуска демона rt-индексы не обновляются.
sudo searchd --stop
sudo indexer --config sphinx.conf --all
sudo searchd --config sphinx.conf
#rt-индексы не обновились, нужно удалять их в ручную и перезагружать систему
searchd - на самом деле не запускает\выключает сам Sphinx? В документации написано что это всего лишь его инструмент
Анон, не ленись и гугли. Знак вопрос значит "nullable", то есть тип, который может принимать значение null. Тогда ?string значит "строка или null". Вот список:
https://secure.php.net/manual/en/migration70.new-features.php
https://secure.php.net/manual/en/migration71.new-features.php
кек, он его осваивать будет дольше чем пыху.
игра-головоломка на 2 игрока
>Выглядит красиво, в т.ч. описание на гитхабе. Как верстал визуальную часть? Где хостил? За месяц, на мой взгляд, не плохо. Только названия коммитом типа "make it better" это неинформативно, название должно давать кратко понять, что произошло, и желательно чтобы коммиыт на каждую переделку/фичу были разными. Алсо, папка vendor на гитхабе не нужна, достаточно composer.json и ненавязчивое напоминание о зависимостях в md.
Взял дефолтный шаблон бутстрапа http://getbootstrap.com/examples/cover/ арендовал самый дешевый выделенный сервер за 130 руб https://ruvds.com/ru-rub/ получил голую убунту и доступ только через консоль, до сих пор удивляюсь как получилось так быстро настроить что-то рабочее, даже почту могу отправлять :)
>Откуда ты взял идею контроллеров и прочего?
Прочитал 3-4 статьи об MVC и сделал 3-4 поделки на этой архитектуре, плюс очень помогли советы в треде. Мне не очень нравится результат, в контроллерах слишком избыточный код, трудно сходу разобраться даже не смотря на комментарии.
Жаль что никто толком не отписался. То ли там баговать нечему, то ли просто застремались по ссылке пройти. На всякий случай еще раз оставлю http://bobylquote.ru/quote/
Что значит "Generates cryptographically secure pseudo-random integers"? Чем эта функция лучше mt_rand, при условии, что я генерирую числа в пределах 1 - 999999 для временного токена? Как понимаю, функция random_int отталкивается от каких-то более случайных значений, чем mt_rand, но работает в связи с этим дольше?
Почитай https://ru.wikipedia.org/wiki/Криптографически_стойкий_генератор_псевдослучайных_чисел
Главное отличие это предсказуемость. В криптографии случайные числа должны быть непредскауземы, даже если известен ряд предыдущих сгенерированных чисел.
rand() использует линейный ГПСЧ, который абсолютно предсказуем и имеет относительно небольшой период https://ru.wikipedia.org/wiki/Линейный_конгруэнтный_метод
mt_rand() использует чуть более хороший вихрь Мерсена с большим периодом https://ru.wikipedia.org/wiki/Вихрь_Мерсенна
Криптографический генератор использует случайные числа, полченные например из времени прихода пакетов, нажатия клавиш, и тд. Есть также аппаратные генераторы случайных чисел, например на основе теплового шума или других процессов. В некоторые процессоры интел встроен аппаратный ГСЧ.
Вот еще статьи про генераторы
https://habrahabr.ru/post/151187/
https://habrahabr.ru/post/137864/
Где нашел заказчика?
А зайчем там комментарии? И так вроде понятно все. Я не вбрасыватель этого кода, если что.
В html элементарная форма
if (isset($_POST["submit"])) {
$name = $_POST["firstname"];
echo "Hello, $name";
}
Дело в том, что код в котором не экономят на комментариях быстрее и проще понимать.
Ну и как бы ты поркомментировал гет корня, например? Ящетаю, что комментировать надо неочевидные вещи, а тут таковых нет.
Да, я думал, что нужно писать в value. Спасибо и сори за нубский вопрос.
Подозреваю, у тебя еще не было ситуации, чтобы ты открывал свой старый код, с которым не работал полгода-год. Тогда относительность понятия "очевидный" сыграет злую шутку.
Погуглил за тебя: https://www.tutorialspoint.com/yii/yii_widgets.htm
Суть в том, что такое гораздо проще писать на JavaScript из-за того, что он выполняется в браузере и позволяет манипулировать DOM'ом интерактивно. На PHP-виджетах такую штуку не сделать: http://tour.ubuntu.com/en/ (это эмуляция Ubuntu)
Тот факт, что в 2k17-м кто-то из-за PHP-виджетов меняет фреймворк, очень красноречиво говорит о компетенции таких разработчиков. Видимо, люди застряли в 2010-м и с тех пор никак не развиваются.
>>1001022
Проще понимать тот в код, в котором нормально разделены ответственности, комментарии же вообще не от хорошей жизни ставят, а в каких-то неочевидных моментах. Например, ты пишешь клиент для API со странным способом формирования параметров URL: все строки нужно передавать обязательно в двойных кавычках, а все числа - обязательно без. Этот момент и можно описать в комментариях, чтобы читающий потом не задавался вопросом, зачем же в коде лишний цикл с ифами для параметров запроса.
>>1001088
У меня такое было, когда процедурные портянки писал. Если разобраться с SOLID/GRASP и модульным (не браузерным) тестированием, то код на выходе будет гораздо проще в сопровождении.
Недавно начал изучать php. Поставил задачу сделать небольшой сайтнебольшую информационную систему. Условно говоря, есть БД (предложение и спрос земельных участков), на данный момент реализована: авторизация, регистрация, изменение данных пользователя, форма заполнения пользователем предложения, вывод всех предложений пользователя.
Решил делать все сразу и походу разбираясь что и как, в следствии чего столкнулся с проблемой. https://ideone.com/XOXNOi
Структура вывода участков
пхп код : цикл выводящий html ровно столько, сколько товаров
И ПО ИДЕЕ я должен был с помощью кнопки отправлять значение переменной в сессию нужного товара. Но поняв, что я обосрался нужная переменная всегда будет принимать свое последнее значение (из за цикла) я приуныл и уже вторые сутки не сплю, пытаясь разрешить данную проблему. Понимаю, что подход может быть в корне не верный, но неужели нет выхода из сложившейся ситуации?
>Тот факт, что в 2k17-м кто-то из-за PHP-виджетов меняет фреймворк, очень красноречиво говорит о компетенции таких разработчиков. Видимо, люди застряли в 2010-м и с тех пор никак не развиваются.
Там всё-таки, скорее всего, просто вытягивают деньги у заказчиков и сами повышают собственную планку. Laravel более популярен в мире, чем Yii2, в среднем и зарплаты разработчиков выше.
Ничего личного, просто бизнес.
Анон выше сказал об этом.
А так всё верно, где-то видел статью на Хабре про то, что надо слать тех, которые говорят, "переписывайте на N, потому что это лучше/быстрее/молодёжнее" - и т.п., подставить любое. Чаще всего это не оправдано.
Мы же здесь о конкретном случае говорим, что в этом контроллере комментировать?
Возможно вообще стоило не здесь спрашивать.
Почему бы не выводить переменную в соответствии с нужным условием?
Объясни, что за переменная, для чего нужна именно? Всегда можно привязать такой вывод к ID или к счётчику - зависит от необходимости.
Вот допустим у нас 10 предложений
Все они вывелись на экран с помощью цикла
вывод html кода внутри которого представляет собой
-----файл-predlozheni9.php--------------
-------------------------------
for($i=0;$i<10;$i++){
echo "<div>Площадь:".$area." </div>";
$_SESSION['id']=$i; из за цикла значение переменной будет 9
echo '<a href="tovar.php">'
}
-------------------------------
---------tovar.php----------
-------------------------------
$id=$_SESSION['id']; и вот тут мне нужно значение товара, на которое я нажимал до этого, а я получу 9, вместо нужного
--------------------------------
мне показалось, что так удобнее на первом этапе, а чем больше пишу, начинаю понимать что лучше все структурировать хоть как то ))
для начала да.но когда проэкт станет больше,придется разбивать на файлы.каждый файл отвечает за какой то логический блок программы
Немного не понимаю тебя опять.
Цикл же просто вывел десять (с 0 по 9) товаров, на что ты нажимал прежде?
А если тупо отнять от девяти 1 и такой $id вернуть, чтобы попробовать?
Такие тупые вопросы, которые я задаю, часто помогают натолкнуться на правильное решение, вообще когда объясняешь кому-то проблему - уже наполовину находишь решение, так что давай.
реализовал задуманное, использовал $_GET запрос и подключил файл
изначально не в том направлении думал ну или не догодался
AJAX запросы каждую минуту с автоподгрузкой. Я так делал ещё в 2012, мне понравилось.
Зачем работать с нищебродами??? Попробуй писать Symfony2, говорят это самый высокооплачиваемый фреймворк.
/Merkalov/
я писал в тех. поддержку, ответили на все вопросы
Обычно есть какой-то порог и по памяти, и по CPU, если ты его превышаешь, предлагают перейти на тариф подороже. Иногда еще в админке бывает график, который это показывает.
Вот мои тупые вопросы как-то помогли сориентироваться? Мне чисто с психологического аспекта это интересно.
Потому что по себе такое замечаю: стоит начать объяснять, как решение приходит само.
А какого типа приложение? Просто шаблон с курдами? Схерали это должно стоить больше, если вордпресс вообще все это из коробки делает?
Ну вот пили на вордпрессе то, для чего это говно лучше всего подходит, и рассуждения о цене свои среди подобных тебе домохозяек оставь.
честно, вчера был в таком состоянии, что не смог понять. что ты написал
я по другому сделал)
Мимонищук
Отлично, спасибо анон, буду пользоваться
Так схерали это должно стоить больше, если это просто и по силам воннаби погромисту? Там gii все делает за тебя, композер и тп, а ты хочешь за голую хуиту 10к. Умерь аппетиты. Тут или что-то годное за большие деньги, или в пределах 10к несколько крудов. Охуевший совсем.
Я не хочу, я за это браться и не стал бы. Я тебе отвечаю просто. Если заказчик хочет хуйню, которая делается в два движения жопой на вп, но сделанную на фреймворке, притом за цену, как с вп - значит он куда-то не туда обратился. Или там не только круды.
$regexp = '/\b([а-яё]+)([a-z]+)([а-яё]*)/ui';
В итоге при прогонке текста латиница выделяется скобками, но одна проблема - выделяет только первую латинскую букву в русском слове, последующие не выделяются. Была мысль запихать в цикл, чтобы прогоняло и ставило скобки, пока латиница в слове не закончится, но не выходит придумать, как это сделать.
Это хайп детка. Хуета когда станвоится на слуху, всем становится поебать что там на самом деле. Тупо вот про кодигнайтер, на котором писал последние 2 года нихуя не слышал вообще, ну кроме новостей с офф сайта, что там новая версия грядет и прочее ко-ко. Зато про ЛАравел куда больше инфо-шума чем про все блядь остальное. Ща уверен что если вакансии открыть, то будет 10 вакансий с требованиями его знать. Ну пойду хэло-ворд накачу на нем да можно резюмехи слать, почему нет.
Я передаю в шаблон массив вида...
$errors = [
"field1" => "error desc",
"field2" => "error desc"
];
... и проверяю наличие ключей массива в шаблоне. Если определенный ключ есть, вывожу error description.
1. Есть ли смысл НЕ закрывать одиночные теги? Типа <br />
2. Какой вообще смысл использовать method="get" вместо post?
3. Кто-нибудь вообще использует <code> <kbd> <samp> <var> <pre> ?
4.Посмотрел отличия XHTML от HTML, так и не понял: когда на практике используют XHTML?
5. Какой смысл в <output> FOR ??? Вроде без него и так нормально все показывает https://planner-rhinoceros-63366.bitballoon.com/
>2. Какой вообще смысл использовать method="get" вместо post?
Ссылки для поисковых систем, закладки для пользователя.
Обычно используют get для чтения данных, другие методы (post, put и т.д.) - для изменения.
>4.Посмотрел отличия XHTML от HTML, так и не понял: когда на практике используют XHTML?
Никто не использует, он провалился. Последний стандарт HTML не совместим с ним.
ну ты такой делаешь сайт
На нем например есть юзеры.
ты как хочешь сделать так, что бы легко и быстро по юзерам было бегать со странички на страничку?
Ты же не будешь форму хуярить, в которую будешь вбивать id юзера, и отправлять постом на сервер?
Ты просто у себя раскидаешь ссылки вида site.gg/user?id=123 и люди просто будут по ним кликать - изи
1) в XHTML обязательно, в HTML не надо
2) это разные методы для разных целей, GET для получения информации, POST для внесения изменений
3) да, те, кто хочет вставить на страницу код, например в моем учебнике в ОП посте они используются
4) давно уже все разобрано тут
http://softwaremaniacs.org/blog/2005/12/19/xhtml-you-say/
http://softwaremaniacs.org/blog/2006/01/17/xhtml-once-more/
5) ответ можно поискать в спецификации HTML на англ:
https://html.spec.whatwg.org/multipage/forms.html#the-output-element
> The for content attribute allows an explicit relationship to be made between the result of a calculation and the elements that represent the values that went into the calculation or that otherwise influenced the calculation.
Если ты хочешь как-то указать, на основании каких данных сгенерирован результат, то ты можешь это сделать с помощью for. То есть ты не обязан это делать, но если вдруг захочется - есть готовый атрибут. Может быть у тебя есть какой-то скрипт, который его будет использовать.
В HTML много необязательного. Например ты не обязан заключать время в тег time, но если захочется - то надо использовать именно этот элемент.
Можно разбить текст на отдельные слова, каждое слово проверять, и уже в найденном слове делать замену без \b что позволит заменять любое число букв.
>>1001778
doctrine2 использует Data Mapper, Yii2 - ActiveRecord.
Для начала ознакомься с этими паттернами если не знаешь их https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Главное отличие в том что с DM модели не привязаны к БД и не содержат кода для работы с БД. В теории, они не привязаны к доктрине тоже и в теории это позволяет заменить доктрину на другой DM, но я бы на это не рассчитывал. В то же время с Юи твои модели будут содержать код работы с БД и будут намертво привязаны к Юи.
А вообще, я с Юи и с ActiveRecord так много не работал, чтобы с ходу назвать недостатки.
>>1001338
Только вместо чисел лучше константы использовать.
>>1001858
Можно, там даже уже готовый протокол придуман (WAMP, Websocket application message protocol по моему).
>>1001207
Если много пользователей, серверу может стать тяжело.
>>1001178
Да, компоненты СИмфони не привязаны к фреймворку Симфони и их можно использовать где угодно.
>>1001154
Ты мыслишь не в парадигме REST. Идея REST как раз в том чтобы сессии были не нужны, а id нужного объекта содержался в URL.
Вот как с твоими сессиями скинуть кому-то ссылку на какую-то страницу?
Можно разбить текст на отдельные слова, каждое слово проверять, и уже в найденном слове делать замену без \b что позволит заменять любое число букв.
>>1001778
doctrine2 использует Data Mapper, Yii2 - ActiveRecord.
Для начала ознакомься с этими паттернами если не знаешь их https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Главное отличие в том что с DM модели не привязаны к БД и не содержат кода для работы с БД. В теории, они не привязаны к доктрине тоже и в теории это позволяет заменить доктрину на другой DM, но я бы на это не рассчитывал. В то же время с Юи твои модели будут содержать код работы с БД и будут намертво привязаны к Юи.
А вообще, я с Юи и с ActiveRecord так много не работал, чтобы с ходу назвать недостатки.
>>1001338
Только вместо чисел лучше константы использовать.
>>1001858
Можно, там даже уже готовый протокол придуман (WAMP, Websocket application message protocol по моему).
>>1001207
Если много пользователей, серверу может стать тяжело.
>>1001178
Да, компоненты СИмфони не привязаны к фреймворку Симфони и их можно использовать где угодно.
>>1001154
Ты мыслишь не в парадигме REST. Идея REST как раз в том чтобы сессии были не нужны, а id нужного объекта содержался в URL.
Вот как с твоими сессиями скинуть кому-то ссылку на какую-то страницу?
Я не очень конечно понял вопрос, вот эту часть:
> и чтобы мне вывести список какого то модуля мне надо делать зависимость к соответствующей модели
Постараюсь написать, что понял.
Если у тебя страницы однотипные (таблица чего-нибудь, форма редактирования чего-нибудь) то можно попробовать как-то это абстрагировать, сделать класс в который ты передаешь например объект модели, а он уже сам из него генерирует таблицу с нужными колонками или форму. Но на практике может оказаться что например в таблице должны быть не совсем такие же поля, как в БД, или надо вывести еще данные из другой таблицы, а в форме как-нибудь переставить поля, и придется усложнять код.
А так, попробуй, не обязательно все в один класс сваливать, можно сделать один класс для отображения однотипных таблиц, другой для получения данных и тд.
Можешь еще посмотреть например генератор админок для Симфони https://symfony.com/doc/current/bundles/SonataAdminBundle/index.html если не боишься.
После searchd --stop надо проверять, например через ps, что он остановился. Тут все зависит, от того, что ты используешь - общесистемный демон (который скорее всего управляется через systemd) или демон, который ты руками запускаешь от своего пользователя. Судя по sudo, скорее всего ты пытаешься в обход systemd лезть к общесистемному демону.
Также попробуй указывать флаги которые делают более подробный вывод, чтобы увидеть, что происходит.
> #rt-индексы не обновились, нужно удалять их в ручную и перезагружать систему
Это да, так как индексер обновляет только обычные. Я думаю, надо очистить данные из RT индексов, поменять конфиг, перезапустить демон. Проверить это можно через mysql клиент.
> searchd - на самом деле не запускает\выключает сам Sphinx
Если ты используешь версию из дистрибутива то лучше использовать стандартный способ управления демоном (скорее всего systemctl start/stop) а не обращаться напрямую. Так как там могут быть какие-то дополнительные скрипты или настройки добавлены от авторов дистрибутива.
Да, можно передавать id теста через query string, можно просто вписывать в URL вроде /tests/123/addQuestion/
Спасибо
>>1002013
>>1001278
Есть урок про обработку форм https://github.com/codedokode/pasta/blob/master/forms.md
Описанный там подход не годится? Т.е. одна функция-контроллер, которая обрабатывает и GET, и POST?
Соответственно мы либо
1) делаем чтобы addComment умела еще и выводить форму (и при ошибке мы ее видим)
2) либо делаем обработку формы в index
>Ты мыслишь не в парадигме REST. Идея REST как раз в том чтобы сессии были не нужны, а id нужного объекта содержался в URL.
>Вот как с твоими сессиями скинуть кому-то ссылку на какую-то страницу?
Концепция такая, что доступ на содержимое сайта дается только авторизованным пользователям.
Как вообще адаптируют страницу под разные разрешения экранов?
Каждое прописывают в хтмльном @media?
Или есть что-то с жс\пхп\...?
Сама задача тут http://archive-ipq-co.narod.ru/l1/pasta.html
Достал строку, пытаюсь её перевести в нижний регистр с кавычками - бесполезно.
Как одному авторизованному пользователю скинуть ссылку на товар другому авторизованному пользователю? Нужно использовать id в URL, а не в сессии.
Вообще, сессии для этого неудобны так как например сессия общая для всех вкладок браузера. И если открыть несколько вкладок с сайтом, они будут перезаписывать данные друг друга.
>на каком примерно уровне нужно знать php
на уровне знания
echo
foreach
if
function
>ы попасть куда-нибудь джуниором хотя бы на 500$ в месяц?
Ну то есть за три дня решил оповские задачки, далее берешь открываешь какой-нибудь htmlacademy и w3s, делаешь простейшую html страничку я не знаю там, форму регистрации какую-нибудь, на бутстрапе с сохранением юзера в базу.
Сюда потом вкидываешь, тебе пояснят где тут уязвимости и почему нельзя делать
$query = "INSERT INTO users (name, password) VALUES ($user_name, $user_password);";
Далее берешь открываешь сайты вакансий своего города. Смотришь какую там говноцмс в основном используют. Если это битрикс, то наверное лучше идти в соседний тред и там спрашивать.
Если же есть вакансии по всяким wordpress или joomla, то гуглишь как на локальной машине на какой-нибудь openserver фастиком развернуть свой helowold на это цмс с натягиванием простой темы. Это еще пару дней займет. И через неделю-две ты уже в принципе конкурентный специалист на рынке труда своей мухосрани (без шуток)
Прикол в том, что можно месяцами постигать сложные вещи, а в итоге прийти и всё равно не найти ничего лучше, чем натягивать дизайны на вордпресс поначалу.
Уж лучше изучать сеть, ооп, безопасность, базы данных, продвинутую верстку, js и прочее, уже работая с чем-то и имея миску супа, чем сидя дома дрочить регулярки и задачи на котиков-студентов месяцами прокрастинируя (как я делал в своё время), и спустя год всё равно устроиться туда же, куда мог бы устроиться тупо блядь сразу после прохождения первых уроков.
>>1002112
Я не оп, но отвечу.
Ну в целом то норм, код с текущей задачей справляется, но с точки зрения расширяемости код как раз не работает вроде как. У тебя классы жесткие. Что вот мне с твоим кодом делать, если нужно изменить зп для работяг какой-то профессии?
у тебя в родителе должны быть не только геттеры, но и сеттеры(прочитай как раз что это). А когда создаешь наследника, то реализуешь не жесткое задавание всех параметров, а через сеттеры например. И в любой момент любому сотруднику заодно с их помощью можно будет зп поменять. Понимаешь?
То есть в классе Employee нужно добавить поля зарплаты, кофе и отчетов и сделать типа такой функции:
public function SetParam()
{
if ($this ->Profession == "Me")
{
$this -> solCoef = 400;
$this -> coffeeCoef = 15;
$this -> reportsCoef =150;
}
etc для других профессий
return $this;
}
А эти поля в классе профессии их вообще не определять.
И сразу еще вопрос по доступам к полям.
Пошёл ка я надрачивать джумлы с вордпрессами, и обмазываться несвежими говнами
Мимо такой же
Не совсем понял что ты себе представил в голове, но откопал эту же задачу у себя на компе, точнее последнюю её версию, которую делал зимой и вот как у меня там было сделано. не помню выкладывал ли даже я её на проверку сюда, и если выкладывал, то получила ли она одобрение. Ща и разберемся.
пик 1 - все поля приватны, и доступ к ним только через геттеры и сеттеры, если не ошибаюсь, то это реализация инкапсуляции. То есть у тебя нельзя в коде в объект залезть и наворотить хуйни.
В общем вот тебе книгу залил хорошую, где в первых главах прям хорошо объясняется как и зачем так делать, у меня в своё время прям просветление от первых глав этой книги настало, до того как там про паттерны началась вода литься.
http://rgho.st/private/6FxD8kMsQ/988d18544217b8ca03c5b100e5a8c5a2
>Employee нужно добавить поля зарплаты, кофе и отчетов
Ну это как бы да. У тебя ведь каждый работяга имеет и зарплату и кофе и отчеты, так что логично именно в классе Employee их описать, а не копипастить потом в каждом наследнике.
>public function SetParam()
Далее по твоей вот этой функции - я не совсем понял задумки, у меня вот такое вот васянство в итоге получилось: пик 2.
Суть в итоге всё та же. На выходе получаешь сотрудника, которому ты не можешь изменить никак его поля через:
$vasya->salary = 123;
Но можешь через:
$vasya->setSalary(123);
Из-за инкапсуляции.
Помоему в антикризисных мерах это необходимо будет, а когда ты пишешь
public $coffe = 20;
То это точно провал.
>Для типа ltree определены обычные операторы сравнения =, <>, <, >, <=, >=. Сравнение сортирует пути в порядке движения по дереву, а потомки узла сортируются по тексту метки.
SELECT * FROM comments WHERE tree >= '51';
id | file | author | date | content | tree | depth
----+------+--------+---------------------+--------------+-------+-------
13 | 51 | Jack | 2017-06-07 03:11:26 | Hello World | 51 | 0
14 | 51 | Alice | 2017-06-07 03:11:38 | Hello Bob! | 51 | 0
15 | 51 | Bob | 2017-06-07 03:11:51 | Hello Alice! | 51.14 | 1
16 | 51 | Union | 2017-06-07 03:12:26 | Hello Jack; | 51.13 | 1
(4 rows)
Почему ничего не отсортировалось?
Спасибо братишка!
http://students-list.ru.host1590257.serv66.hostland.pro/
https://github.com/Merkalov/Students
Подожди еще немного, я постараюсь проверить. У нас всегда, к сожалению, большие задания с задержкой проверяются.
>models
>views
>controllers
Это у вас в пэхэпэ такая бест практис - размазывать mvc по 100500 папкам? Нахуя объясните мне?
SELECT * FROM comments WHERE tree >= '51' ORDER BY tree;
id | file | author | date | content | tree | depth
----+------+--------+---------------------+--------------+-------+-------
13 | 51 | Jack | 2017-06-07 03:11:26 | Hello World | 51 | 0
14 | 51 | Alice | 2017-06-07 03:11:38 | Hello Bob! | 51 | 0
16 | 51 | Union | 2017-06-07 03:12:26 | Hello Jack; | 51.13 | 1
15 | 51 | Bob | 2017-06-07 03:11:51 | Hello Alice! | 51.14 | 1
(4 rows)
Ты нашёл у кого спросить, я месяц как вкатился.
А если классов будет, например, 100, ты будешь по все папке искать где контроллеры а где сервисы?
А зачем мне искать конкретно контроллеры или конкретно сервисы? Я буду искать файл с конкретным названием, а не все файлы, относящиеся к одному типу.
Что ты подразумеваешь под "разбираться"? От корки до корки читать его, лол? У него будет таск и он пойдет от entry point-а искать, где сидят те места, в которых нужно попилить, чтобы выполнить таск. Как ему в этом поможет разбиение по папкам?
>Главное таск решить!
Так и есть. Никому, кроме тебя самого, не интересно, как, где и что ты писал, главное - выполнение бизнес-требований.
Это не в PHP или MVC, а в любом языке хорошая идея раскладывать файлы по папкам. Раздражают например старые проекты на Си где 100 файлов свалены в одну папку и невозможно ориентироваться.
С папками гораздо удобнее: тут контроллеры, тут вспомогательные классы, тут работа с БД, тут шаблоны.
>>1002635
Ты читал урок https://github.com/codedokode/pasta/blob/master/db/trees.md ?
ltree это реализация Materialized Path. Выбирать ветку дерева надо не по условию >= 51, а по условию 51.* или как-то так. В уроке вроде есть какие-то пояснения про этот паттерн.
>>1002641
В PHP путь к файлу обычно соответствует имени класса. Если у тебя класс Symfony\HttpFoundation\Request то путь к нему будет вроде vendor/Symfony/HttpFoundation/Request.php
Урок про PSR-4, если интересно https://github.com/codedokode/pasta/blob/master/php/autoload.md
Вообще, классы и неймспейсы это удобно. Когда я вижу или пишу на Го или Си, всегда непонятно, куда класть ту или иную функцию, приходится какие-то способы придумывать вместо того чтобы использовать единый стандарт. Го поэтому мне не нравится, почему они такие элементарные вещи не продумали, видимо он рассчитан на тех кто любит писать все в одном файле.
>>1002659
Написание плохого кода увеличивает затраты времени на его поддержку и развитие в дальнейшем.
>Ты читал урок https://github.com/codedokode/pasta/blob/master/db/trees.md ?
Да, хоть и признаюсь, что сначала не особо внимательно прочитал про этот паттерн, т.к. у меня глаз сразу пал на технологию где это уже реализовано.
>Выбирать ветку дерева надо не по условию >= 51, а по условию 51.* или как-то так.
Я и так тоже пробовал
SELECT * FROM comments WHERE tree ~ '51.*';
id | file | author | date | content | tree | depth
----+------+--------+---------------------+--------------+-------+-------
13 | 51 | Jack | 2017-06-07 03:11:26 | Hello World | 51 | 0
14 | 51 | Alice | 2017-06-07 03:11:38 | Hello Bob! | 51 | 0
15 | 51 | Bob | 2017-06-07 03:11:51 | Hello Alice! | 51.14 | 1
16 | 51 | Union | 2017-06-07 03:12:26 | Hello Jack; | 51.13 | 1
и даже после перепрочтения урока по бд добавлял к запросу ORDER BY tree. Результат получается такой же как и если просто отсортировать колонку tree как строку, как в примере выше >>1002635 .
есть книга про ООП,но она сука на английском
Что имеется ввиду под словом 'правильно'? В моём случае, с комментариями, не получится выставить значения по порядку т.к. они добавляются произвольно, если вы это имеете ввиду. Я разве неправильно составил дерево? Я начинаю сомневаться что можно отсортировать комментарии и по дате (или хотя бы по их расположению в бд), и по порядку движения по дереву одним запросом.
Если VPS, то нет. Читай у каждого дилера что он там предоставляет.
Bootstrap
При вводе 12 переадресовывало на 12.html при вводе asfd1 переадресовывало на asfd1.html
Еще и шортлинк сделал, совсем время свое не ценишь
Спасибо, работает.
Вообщем смущает то что год 2006, сильно ли это критично?
Я б не сказал. Нормальные книги как раз годами ценятся, там достаточно универсальные знания, а переиздают каждый год всякие апдейты к руководствам по новомодным технологиям.
Все окей. У новичков в программировании она часто туго идет. Иногда по пару дней делают.
Возможно имелось ввиду добавлять к дереву ещё и id комментария. Гугл предлагает использовать функцию lastval(), но поскольку я использую ORM придётся сначала добавлять запись в бд, а затем обновлять её.
Очень плохое решение.
Работал с asp.net пару лет назад, не знаю как сейчас, но скорость разработки довольно маленькая, в основном из-за того, что приходилось довольно долго исправлять непонятные незадокументированные баги, на некоторые msdn давал ответы, на некоторые давал неправильные, на некоторые вообще не давал.
Сопровождаемость - хз. Сейчас там сидит админ и админит это, периодически перезагружает сервак из-за каких то проблем с кешем. А так проект работает до сих пор.
Честно говоря PHP проще в этом отношении, всё задокументированно и описано в 1000 разных источниках. Относительно сопровождаемости запилил и всё работает. Вон 1с запилили свой битрикс на PHP5.3 и как-то всё это до сих пор не развалилось.
У asp есть довольно весомый профит в виде корпоративного саппорта из коробки всяких вкусностей. Например запилить сайт связанный с MSSQL можно буквально за день, а прикрутить авторизацию из домена - ещё сутки.
Ты разобрался вообще в том, как работает паттерн Materialized Path? У меня такое ощущение, что пока не разобрался в теории. А просто где-то увидел пример и пытаешься сделать по аналогии, не понимая принцип.
У меня в уроке дан пример. Там путь состоит из значений, которые должны при сортировке давать нужный порядок вывода комментариев.
Для этого каждому комментарию верхнего уровня проставляется какое-то значение, которое возрастает. Например: порядковый номер, id, время вставки. Затем, когда вставляется дочерний комментарий, он наследует номер родителя и добавляет к нему собственный номер.
Получается что путь комментария содержит в себе номера всех предков.
Ну к примеру путь 001.003.002 обозначает, что наш комментарий имеет 2 предков и сам список комментариев такой:
001
001.001 <- это идут дети комментария 001
001.002
001.003
001.003.001 <- это дети комментария 001.003
001.003.002 <- наш комментарий
.....
В качестве номера можно использовать разные значения: порядковый номер, начиная с 1, id, время, лишь бы они при сортировке давали нужный порядок вывода. Но порядковый номер занимает меньше места и потому выгоднее. Хотя, для его вычисления нужно делать лишний запрос.
Время хорошо тем, что его можно получить без дополнительного запроса к БД, но оно занимает намного больше места.
Если хочется оптимизировать по максимуму, то есть еще более эффективные способы хранения пути. В случае с числом, оно кодируется цифрами, каждая из которых занимает 1 байт. Байт может хранить одно из 256 значений, но мы используем только 10. Соответственно для более эффективного хранения было бы лучше задействовать какую-то систему кодирования, использующую больше символов - base64, base127 и так далее. Лишь бы она не использовала точку. Ну и надо учесть что для хранения строки используется скорее всего кодировка utf-8 и нужно чтобы используемые символы бы эффективно в ней кодировались.
Если не использовать ltree, то можно сделать путь бинарным и использовать все 256 возможных значений байта. То есть сделать например что 1, 1.5 или 2 байта кодируют один сегмент пути. А точки тогда становятся не нужны. Это дает максимальную плотность хранения данных.
Есть еще альтернативная система, когда вместо пути с точками используются дробные числа от 0 до 1, и при вставке нового комментария ему присваивается число, находящееся посередине между предыдущим и следующим комментарием. Но там нужно учесть что тип DOUBLE имеет ограничение по точности и после какой-то глубины начнут получаться одинаковые числа.
> Гугл предлагает использовать функцию lastval(), но поскольку я использую ORM придётся сначала добавлять запись в бд, а затем обновлять её.
Номер можно вычислять заранее, ну и даже если требуется обновлять комментарий, это не проблема с использованием транзакций.
Ты разобрался вообще в том, как работает паттерн Materialized Path? У меня такое ощущение, что пока не разобрался в теории. А просто где-то увидел пример и пытаешься сделать по аналогии, не понимая принцип.
У меня в уроке дан пример. Там путь состоит из значений, которые должны при сортировке давать нужный порядок вывода комментариев.
Для этого каждому комментарию верхнего уровня проставляется какое-то значение, которое возрастает. Например: порядковый номер, id, время вставки. Затем, когда вставляется дочерний комментарий, он наследует номер родителя и добавляет к нему собственный номер.
Получается что путь комментария содержит в себе номера всех предков.
Ну к примеру путь 001.003.002 обозначает, что наш комментарий имеет 2 предков и сам список комментариев такой:
001
001.001 <- это идут дети комментария 001
001.002
001.003
001.003.001 <- это дети комментария 001.003
001.003.002 <- наш комментарий
.....
В качестве номера можно использовать разные значения: порядковый номер, начиная с 1, id, время, лишь бы они при сортировке давали нужный порядок вывода. Но порядковый номер занимает меньше места и потому выгоднее. Хотя, для его вычисления нужно делать лишний запрос.
Время хорошо тем, что его можно получить без дополнительного запроса к БД, но оно занимает намного больше места.
Если хочется оптимизировать по максимуму, то есть еще более эффективные способы хранения пути. В случае с числом, оно кодируется цифрами, каждая из которых занимает 1 байт. Байт может хранить одно из 256 значений, но мы используем только 10. Соответственно для более эффективного хранения было бы лучше задействовать какую-то систему кодирования, использующую больше символов - base64, base127 и так далее. Лишь бы она не использовала точку. Ну и надо учесть что для хранения строки используется скорее всего кодировка utf-8 и нужно чтобы используемые символы бы эффективно в ней кодировались.
Если не использовать ltree, то можно сделать путь бинарным и использовать все 256 возможных значений байта. То есть сделать например что 1, 1.5 или 2 байта кодируют один сегмент пути. А точки тогда становятся не нужны. Это дает максимальную плотность хранения данных.
Есть еще альтернативная система, когда вместо пути с точками используются дробные числа от 0 до 1, и при вставке нового комментария ему присваивается число, находящееся посередине между предыдущим и следующим комментарием. Но там нужно учесть что тип DOUBLE имеет ограничение по точности и после какой-то глубины начнут получаться одинаковые числа.
> Гугл предлагает использовать функцию lastval(), но поскольку я использую ORM придётся сначала добавлять запись в бд, а затем обновлять её.
Номер можно вычислять заранее, ну и даже если требуется обновлять комментарий, это не проблема с использованием транзакций.
Нельзя сравнивать компилируемый язык со статический сторогой типизацией и скриптоговно.
Ну вот, скатил всё в срачь. Досвиданья мальчик юниор.
>Ты разобрался вообще в том, как работает паттерн Materialized Path? У меня такое ощущение, что пока не разобрался в теории. А просто где-то увидел пример и пытаешься сделать по аналогии, не понимая принцип.
Я прочитал всё что было написано в уроке по древовидным структурам и в ссылках по PostgreSQL. Скорей всего я действительно просто не понял принцип. Я не уверен что я даже сейчас до конца его понимаю, но сейчас у меня всё работает и не возникает никаких вопросов. Я хотел бы иметь больше интереса к тому как это работает, но, сейчас, всё что меня беспокоит, это задача по js, в которой у меня полно ошибок и которую я не могу начать не закончив хостинг. Сейчас я поставлю шаблонизатор, сделаю несколько мелких фиксов, и всё будет готово. Сейчас только справлюсь с прострацией.
> Гугл предлагает использовать функцию lastval(), но поскольку я использую ORM придётся сначала добавлять запись в бд, а затем обновлять её.
>Номер можно вычислять заранее, ну и даже если требуется обновлять комментарий, это не проблема с использованием транзакций.
>Номер можно вычислять заранее
А можно подсказку как это сделать?
>даже если требуется обновлять комментарий
С этим даже будет проще, потому что уже будет известен его id.
>транзакции
О каких транзакциях идёт речь?
Номер нового комментария вычисляется подсчетом числа комментариев-детей у нужного родителя и увеличением его на 1.
> О каких транзакциях идёт речь?
http://www.mstu.edu.ru/study/materials/zelenkov/ch_4_9.html
https://ru.wikipedia.org/wiki/Транзакция_(информатика)
>Номер нового комментария вычисляется подсчетом числа комментариев-детей у нужного родителя и увеличением его на 1.
А тут имелся ввиду порядковый номер, а не id. Мне более по нраву использовать id. Это очень критично делать лишний запрос чтобы его получить?
Я же выше написал, что есть разные варианты:
- порядковый номер
- id
- время вставки
Чтобы получить id, в postgres по моему есть SEQUENCE (генераторы id). В MysQL с автогенерацией id его нельзя получить до вставки, но может быть можно сделать аналог sequence на основе таблицы с единственной ячейкой (только придется с блокировками повозиться).
id имеет тот недостаток, что требует больше места для хранения. Сколько места нужно зарезервировать для 32- и 64-битных id, предлагаю рассчитать самостоятельно.
И в чем кстати проблема в лишнем запросе? В программировании (да и не только) часто приходится выбирать из вариантов, когда ты в чем-то проигрываешь, но в чем-то выигрываешь. Надо взвешивать все особенности того или иного подхода, а не стремиться к оптимизации числа запросов любой ценой.
Да, простите, я видел что вы писали про id, я просто думал что можно как-то получить id с помощью ORM доктрины заранее а не писать нативные запросы.
>>1003020
Не очевидное решение, которое усложняет код (правда другие решения ещё больше его бы усложнили, но зато не было бы лишнего запроса). Как вы сказали, что нужно выбирать из вариантов, я сомневался какой вариант лучше, либо сделать ещё менее понимаемым код и уменьшить нагрузку на бд, либо делать лишний запрос. По мне так, в глобальном смысле, лучше один раз написать сложный код, чем каждый раз происходил бы лишний запрос к бд.
Я уже написал, что в postgres можно отказаться от автоматической генерации id при вставке записи (Доктрина это поддерживает), получать id для новой записи из SEQUENCE и вписывать вручную куда требуется:
$ id = ... получение id
$comment->setId($id);
$comment->setParent($parent, $id); // проставляет путь
Вообще, в некоторых системах id генерируют не в базе, а на стороне приложения. Например, с целью сделать базу распределенной (чтобы она хранилась не на одном сервере, а по частям на разных) или когда может отсутствовать связь с БД (генерация id в мобильном клиенте в оффлайне).
Для генерации id в таких случаях можно использовать например UUID ( https://ru.wikipedia.org/wiki/UUID ). Для комментариев это конечно будет переусложнением, но я хочу просто показать, что есть разные подходы.
Если совсем не хочется лезть в базу, я уже писал выше, можно использовать время добавления комментария. Но оно делает путь более длинным. По моему третий раз уже пишу это.
> По мне так, в глобальном смысле, лучше один раз написать сложный код, чем каждый раз происходил бы лишний запрос к бд.
Я бы не назвал это сильным усложнением. Мне нравится вариант с номерами так как он позволяет делать более компактный путь, понятно что требуется запрос для вычисления номера, но это по моему не проблема.
> а не писать нативные запросы.
Вот это тоже не очень правильный подход. По моему опыту, без нативных запросов обойтись вряд ли получится, так как Доктрина не все умеет делать эффективно. Многие вещи выгоднее делать через нативные запросы, так как с Доктриной они сведутся в выборке большого числа записей и последующему их отбрасыванию, что будет гораздо медленнее SQL запроса.
Простой пример - подсчет числа комментариев к посту, выгоднее сделать SQL запросом (а еще выгоднее может быть просто хранить его в посте) чем выбирать все комментарии из БД.
Есть один сайт, сделан на Битриксе. На этом сайте есть форма опроса. Как я понимаю, после нажатия на кнопку Submit данные из формы передаются в суперглобальный массив POST. А оттуда их забирает некоторый скрип, который их обрабатывает и записывает в базу данных.
Я делаю мобильное приложение и мне нужно реализовать в нём эту голосовалку. Т.е. мне нужно как-то наебать скрипт и отправить ему заполненный массив POST минуя формочку на сайте. Правильно я понимаю?
И воторой вопрос: как мне найти этот сраный скрипт в файловой системе сайта? Почему здесь action="/" стоит слэш, вместо имени скрипта?
Ты слышал что-нибудь про протокол HTTP и HTTP-клиенты? Без этого делать приложение будет затруднительно потому что в данной ситуации надо использовать библиотеку-HTTP-клиент.
Я понимаю, что мне нужен будет протокол HTTP для того, чтобы отправить POST на сервер. Ты мне лучше расскажи, как мне откопать в структуре сайта скрипт, обрабатывающий этот запрос?
В атрибуте action формы не требуется указывать "имя скрипта". Там указывается URL, на который отправляются данные формы методом POST. Соответственно, этот URL разбирается программой на сервере, если это Битрикс, то тебе придется разбираться, как именно он анализирует URL запроса и где там находится обработчик этого запроса. Я не специалист по Битриксу.
Скорее всего этот запрос обрабатывает какой-то модуль голосования, встроенный в Битрикс. Так что я бы погуглил документацию битрикса по этому вопросу.
Блин, фигово. Я думал может есть какие-то способы выдрать путь к скрипту из настроек формы. Спасибо. Пошёл читать документацию.
Ещё у тебя "erore_reporting", лолка.
Вот те рабочий код еще на всякий случай.
И старайся в следующий раз еще и код ошибки демонстрировать
<?php
error_reporting(-1);
echo "Бросаем кубик...\n";
$min = 1;
$max = 6;
$rand = mt_rand($min, $max);
echo "Выпало: $rand";
В 17 строке /n должен быть внутри ковычек "блабла\n";
Огонь. Код теперь работает?
хотя теперь каждый раз пишет что победил ии хотя результат может говорить о обратном
В 22 линии не else, а elseif
В 14 $copmSum замени на $compSum
В 16 не $anonDise2, а $anonDice2
Потому что exit срабатывает только 1 раз когда встречается впервые. Он тупо прекращает дальнейшее выполнение скрипта. http://php.net/manual/ru/function.exit.php
https://pastebin.com/tv92ZVv8
Жесть, человече))) Подумай немного и посмотри на условие предпоследнее и последнее. elseif ($anonSum > $compSum) {
echo"Человек победил\n";
} elseif ($anonSum < $compSum) {
echo "ИИ лучше всех\n";
}
Опять начинаешь, реактивный? Поправил тебя http://ideone.com/BgG9LI
У тебя вычислился $otvet один раз и не изменялся.
Я как у тебя и не смог бы=( тут чего то не хватает? http://archive-ipq-co.narod.ru/l1/loops.html
Я прикалывался, бро. Через год для тебя все будет изи-бризи.
Да тебе надо в цыкле вычислять otvet на каждой итерации.
Он у тебя оди раз вычислился а потом только использовался.
>делаем чтобы addComment умела еще и выводить форму (и при ошибке мы ее видим)
Что значит выводить форму? Как это? Мне не чтобы все формы страницы обрабатывал один метод контролера, он очень толстый получится.
for ($i=0; $i <= $halfLenght; $i++) {
if (mb_substr($text, $i, $i + 1) == mb_substr($text, gmp_neg($i)/инверсия переменной/ ) {
echo "Полиндром!";
}
else{
echo "Не Полиндром";
}
В 9 строке закрывающую скобку забыл ")" в if
Я со скуки тебе написал готовое решение
Использовать один метод контроллера для POST и GET запросов это следование принципу DRY.
А толстый контроллер это контроллер, в котором содержится бизнес-логика. Например в контроллере происходит хеширование пароля или отправка HTTP-запроса к стороннему API через curl или file_get_contents какой-нибудь. Оба этих сценария по хорошему нужно обернуть в объект, а из контроллера только управлять ими по принципу чёрного ящика. Хотя, мне кажется что люди, не пишущие юнит-тесты, этого никогда не поймут.
Можно, но они написаны на языке Си. Его впрочем можно при желании тоже изучить.
Вот исходный код интерпретатора PHP: https://github.com/php/php-src
Си, как ты можешь заметить, внешне напоминает PHP, но есть и отличия:
- перед переменными не ставят знак доллара
- при объявлении (создании) переменной нужно всегда указать ее тип
- у функций также указывается тип возвращаемого значения и типы аргументов
Например код в PHP: $a = 1 соответствует такой строчке на Си: int a = 1;
Вот функция на Си, которая вызывается при вызове функции count в PHP: https://github.com/php/php-src/blob/master/ext/standard/array.c#L776
Тут видно, что идет проверка типа аргумента функции count и в зависимости от этого вызываются другие функции. Вот кусочек кода который отвечает за массивы:
> zend_long cnt; // объявляется переменная типа zend_long
> ...
> case IS_ARRAY:
> cnt = zend_array_count(Z_ARRVAL_P(array));
> ...
> RETURN_LONG(cnt);
То есть вызывается функция zend_array_count. Вот ее код:
https://github.com/php/php-src/blob/master/Zend/zend_hash.c#L295
К сожалению, чтобы его понять, придется изучить не только Си, но и способы хранения php-данных (вроде массивов). Они описаны например тут https://romka.gitbooks.io/php-internals-book-ru/
Я могу сказать, что в случае с count(), там в структуре, представляющий php массив, хранится число элементов и count просто возвращает это значение.
Можно, но они написаны на языке Си. Его впрочем можно при желании тоже изучить.
Вот исходный код интерпретатора PHP: https://github.com/php/php-src
Си, как ты можешь заметить, внешне напоминает PHP, но есть и отличия:
- перед переменными не ставят знак доллара
- при объявлении (создании) переменной нужно всегда указать ее тип
- у функций также указывается тип возвращаемого значения и типы аргументов
Например код в PHP: $a = 1 соответствует такой строчке на Си: int a = 1;
Вот функция на Си, которая вызывается при вызове функции count в PHP: https://github.com/php/php-src/blob/master/ext/standard/array.c#L776
Тут видно, что идет проверка типа аргумента функции count и в зависимости от этого вызываются другие функции. Вот кусочек кода который отвечает за массивы:
> zend_long cnt; // объявляется переменная типа zend_long
> ...
> case IS_ARRAY:
> cnt = zend_array_count(Z_ARRVAL_P(array));
> ...
> RETURN_LONG(cnt);
То есть вызывается функция zend_array_count. Вот ее код:
https://github.com/php/php-src/blob/master/Zend/zend_hash.c#L295
К сожалению, чтобы его понять, придется изучить не только Си, но и способы хранения php-данных (вроде массивов). Они описаны например тут https://romka.gitbooks.io/php-internals-book-ru/
Я могу сказать, что в случае с count(), там в структуре, представляющий php массив, хранится число элементов и count просто возвращает это значение.
Но что тогда должны делать остальные экшны? Они все public методы и вызываются роутером через URL формата http://localapp/controller/action. Т.е. как они тогда вообще могут взаимодействовать с пользователем, если у них отнять POST и GET методы?
В кодигнайтере через хэлперы и сделано.
Пишешь:
$this->load->helper('cookie');
А потом через методы хэлпера работаешь с куками (в контроллере например)
Я хз карчое что ты имеешь в виду.
Имею я ввиду следующее:
https://github.com/someApprentice/Students/blob/master/app/Model/Helper/Helper.php#L8
Дайте, пожалуйста совет, с чего вообще начать? Ведь это мой первый проект.
Записать время логина, записать время логаута. Если нет логаута, записать время автоматического логаута.
Написать хранимку с одной строчкой: дайтдифф между двумя датами.
У нас тимлид сделал генерацию ключей заказов на стороне сервера, т.к. сервера стоят у клиентов + центральный сервер куда все потом сходится. В итоге получили конфликты значений ключей и долго слушали матюки от клиентов, почему некоторые заказы не проходят в систему. Потом дизайн поменяли, добавив id клиента,где все генерируется.
Потому стоит изучать опыт других разработчиков. Я упоминал выше UUID и его алгоритм формирования, но есть еще другие системы, например в Twitter Snowflake ключ содержит 64 бита: https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake.html
Ну и есть презентация по теме, если кому интересно: https://www.slideshare.net/davegardnerisme/unique-id-generation-in-distributed-systems
Как сделать, чтобы таблица
<table>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
при изменении масштаба экрана и на телефона была адаптивной? Что она хочет от меня, я сейчас зарыдаю уже в голос...
Там бутстрап, не понимаю, как совладать с этой сучкой.
Она при изменении масштаба или на мобилах улезает вбок, нельзя даже прокрутить до её края.
Мне нужно, чтобы на мобилах она была просто по размерам контейнера, в которой находится, там можно будет раздвинуть, увеличить и так посмотреть.
Бесит уже, не могу просто.
http://htmlbook.ru/css/media
https://www.w3schools.com/cssref/css3_pr_mediaquery.asp
@media screen and (min-width: 480px) {
//Переопределяешь стили для мобильного разрешения
}
Бро, спасибо, хотя всё это я так-то пробовал.
Но после твоей ссылки понял, что надо стили и для <tbody>, <tr> и <td> тоже переопределить для другого разрешения, а заодно ещё и <p>, <ul> и <li>, которые там есть внутри.
Я пердолилися только с самой <table>, вот те раз.
Делай конкатенацию внутри цикла: получившийся слог, затем к этому получившемуся новый слог, потом другой новый.
$name .= $syllable;
Верстка это ад, сука, ничего не получается всё равно.
Бутстрап столько всего переопределяет, что обосраться можно.
Оп, привет.
>Но задачу на SPA хорошо бы сделать. Мало кто сейчас такие штуки умеет делать.
Я решил сделать SPA из оп-поста, немного изменив условия. Программа для сохранения и напоминания дат праздников у друзей и родственников. Функционал программы похож на тот, что в задаче "Расписание для преподавателей". Основное окно - вкладка "Расписание", где вместо студентов друзья и родственники, а вместо оценок на календаре их праздничные даты. Кликнув на день в календаре можно добавить день рождение родственника или отредактировать уже созданное. Программа присылает оповещения на почту на кануне и в день праздника.
Решил для фронт-энд части использовать React (потому, что он и Angular на Upwork делят проекты 50/50, но React новее);
Серверная часть - Silex (потому что почти Symfony).
И то, и другое, мне не знакомо. С чего начать?
План такой:
0. чтения мануалов и создание хелоувордов React/Silex.
1. определиться с функционалом, и сделать варфрейм интерфейса;
2. проектирование бд;
3. создание части функционала серверной части при помощи Silex (регистрация?);
4. верстка необходимого части интерфейса для проверки серверной части;
5. интеграция React;
6. повторение п. 4 и 5 до полной готовности приложения.
Прочел туториал Реакта про игру Tic-tac-toe, сейчас возьмусь за доки и туты по Silex.
Какую версию ES использовать, ES3 или уже можно перейти на ES2015?
Оп, привет.
>Но задачу на SPA хорошо бы сделать. Мало кто сейчас такие штуки умеет делать.
Я решил сделать SPA из оп-поста, немного изменив условия. Программа для сохранения и напоминания дат праздников у друзей и родственников. Функционал программы похож на тот, что в задаче "Расписание для преподавателей". Основное окно - вкладка "Расписание", где вместо студентов друзья и родственники, а вместо оценок на календаре их праздничные даты. Кликнув на день в календаре можно добавить день рождение родственника или отредактировать уже созданное. Программа присылает оповещения на почту на кануне и в день праздника.
Решил для фронт-энд части использовать React (потому, что он и Angular на Upwork делят проекты 50/50, но React новее);
Серверная часть - Silex (потому что почти Symfony).
И то, и другое, мне не знакомо. С чего начать?
План такой:
0. чтения мануалов и создание хелоувордов React/Silex.
1. определиться с функционалом, и сделать варфрейм интерфейса;
2. проектирование бд;
3. создание части функционала серверной части при помощи Silex (регистрация?);
4. верстка необходимого части интерфейса для проверки серверной части;
5. интеграция React;
6. повторение п. 4 и 5 до полной готовности приложения.
Прочел туториал Реакта про игру Tic-tac-toe, сейчас возьмусь за доки и туты по Silex.
Какую версию ES использовать, ES3 или уже можно перейти на ES2015?
http://archive-ipq-co.narod.ru/l1/conditions.html
Спасибо
Это было вступление
Есть магазин на битриксе. У товара есть перечеркнутая цена. Как убрать эту полоску? С меня как всегда
мимоньюфаг
Отредактировать шаблон компонента/стили.
Крякни ее, дебил. Ключи в инете гуглятся сразу же.
<?php
define("kurs", 65.52);
$US_dolars = 650;
$roubles = $US_dollars * kurs;
echo "У вас $roubles рублей";
?>
Выдает ноль, яннп!
Есть бесплатная версия http://confluence.jetbrains.com/display/PhpStorm/PhpStorm+Early+Access+Program
Можно получить student license отправив им фото своего студня, как сделал я. Продление раз в год.
бобы работают шустрее, вернее phpstorm тормозит настолько, что пользоваться им не приятно.
что непонятно в тексте по ссылке?
Найди в инстаграме любое фото студенческого билета (большинство студенток их очень любит фоткать после поступления, чтобы похвастаться) и зарегай левую почту , чтобы получить лицензию студенчесскую
Да идите вы. Это сейчас у меня два проекта в шторме открыто, бывает и по 4. Нередко параллельно с другими иде. Как вы умудряетесь в 2017 лагадром у себя устраивать? У меня ведро 2010 года, лол.
Бичовый АМД на 4 ядра, 8 оперативки, без ссд. На скрине все, что сейчас запущено.
fx-4300
Ну это пиздец. У меня нетбук мощнее. Мать с процем ищи подходящую бюджетную, сейчас как бы нет проблем с железом под комфортную разработку.
Что думаете, господа? После таких новостей хочется всё бросить. В айти вообще возможно вкатиться не дроча код по 100 часов в неделю на протяжении 4-5 лет?
Как работать с русскими строками?
Есть $a = "собака";
Я хочу сделать $a[5] = 'и'; и нихера. Через фигурные скобки тоже никак. Как-то попроще, чем mb_substr можно?
н-н-н-о... дуров... лайкну...
Пиздец, даже mb_substr_replace нет, итераторов, похоже, тоже нет? Это хотя бы во фреймворках решается как-то?
1й пик роут
2й пик контроллер
Сейчас выходит какая-то параша.
Чет на первый взгляд скорее хрень. На выходе будет хороший кодер, но никак не программист. Наговнокодят костылей в погоне за скоростью (а в команде из нескольких балбесов без опыта проект превратится в полный пиздец), а потом на реальных проектах свои велосипеды за собой потянут. От такого старательного джуна команда конечно будет рада, но проектировать тоже кто-то должен.
>> Как остановить setInterval, который был вызван в предыдущем событии?
>Есть clearInterval, но проще использовать setTimeout.
>clearInterval
А как его вызвать на интервале который был вызван до него?
//После клика на другой блок, интервал на первом кликнутом блоке не остановился
https://jsfiddle.net/tux1yebv/
>setTimeout
А зачем тут setTimeout, который вызывается всего один раз с делеем? Он сбрасывает setInterval?
>> Как лучше сохранять не отправленные сообщения?
>Можно сохранять их в localStorage. Но тогда есть риск, если пользователь на каком-нибудь общем компьютере набирал сообщение, то потом его может найти и прочесть другой человек. Можно сохранять сообщение на сервер. Но тогда может человек сотрет сообщение, а на сервере оно сохранится. Тоже некрасиво.
Мне нравиться вариант с сохранением на сервер, но где именно? Можно сделать отдельную таблицу unsendedmessages в БД.
>Но тогда может человек сотрет сообщение, а на сервере оно сохранится.
Тогда нужно сделать чтобы сообщение отправлялось каждый раз как пользователь его меняет. Не будет ли это создавать большую нагрузку на БД, если, например, делать это на добавление\удаление нового символа или хотя бы нового слова\перевода строки?
С другой стороны, я видел, как в популярных мессенджерах, на разных устройствах\приложениях сохраняются свои не отправленные сообщения.
Даже не знаю что лучше, сохранять сообщения локально и подвергать пользователя тому, что его неотправленные сообщения украдут, или хранить их на сервере и создавать нагрузку на БД. Наверно, пользователь должен сам отвечать за доступ к своему устройству, и его об этом нужно предупредить.
>Еще одна вещь - мне кажется, хорошим тоном было бы до отправки сообщения в чате писать информацию о том, куда сохраняется история этого чата. Чтобы пользователь знал, где будут храниться его сообщения. Никто почти так не делает, я только один раз видел это в каком-то линуксовом мессенджере, он спрашивал, надо ли сохранять историю. И это многое говорит об отношении разработчиков к приватности пользователя.
А какую именно информацию нужно написать? Расположение сервера где находиться БД? Или просто уведомить о том что сообщения сохраняются либо в БД, либо локально (в течении сессии)?
А что если один пользователь захочет хранить историю а другой нет? Тогда в любом случае сообщения будут храниться в одном экземпляре (если денормализовать сообщения на inbox и outbox).
И, наверно, при отключенной истории сообщений, сообщения текущей сессии нужно хранить в какой-нибудь переменной messages, которая будет обнуляться при закрытии\обнавлении окна.
http://phpclub.rf.gd/pr/res/919074.html#934872
>Если ты хочешь хранить сообщения на сервере (для истории и синхронизации), то надо хранить их зашифрованными. А ключ хранить где-то в другом месте. Но может быть лучше вооще их не хранить? Или хотя бы спрашивать пользователя, хочет ли он вести запись истории или нет. Программа, которая уважает приватность пользователя, не должна ничего записывать без его согласия.
>ключ хранить где-то в другом месте
А можете посоветовать где лучше всего хранить ключ?
>Вообще, это как раз тот случай, когда на БД это реализовать неудобно. По идее да, клиент должен посылать уведомление на сервер, тот сохранять куда-то (в таблицу уведомлений?) и отдавать другому клиенту. Уведомление желательно сделать актуальным только на небольшой период времени, чтобы при отсоедиении клиента у его собеседника не висело это уведомление вечно.
Можно сделать на стороне получателя setTimout через одну или полсекунды сбрасывающий это уведомление. Только я не знаю как тут будет с сихронизацией: у отправителя на каждый нажатый символ отправляется уведомление на сервер, а получатель при обновлении, каждую секунду сбрасывает это уведомление.
notify = function () {
//отсылаем уведомление
$.ajax(...);
}
getNotify = function () {
//получаем уведомление
$.getJSON(...);
setTimeout(function() {
//сбрасываем уведомление
$.ajax(...);
},
1000
);
}
$(textarea).on('keypress', function(e) {
notify();
});
setInterval(function() {getNotify();}, 1000);
Наверно, тут никакого рассинхрона не будет, я не знаю. Я слишком запутался в вопросе как работают счётчики.
Опять же, я не знаю какая будет нагрузка при отправлении уведомления на каждую нажатую кнопку (ссылаюсь на тот же вопрос о сохранении неотправленного сообщения при нажатии на клавишу).
>В случае с использованием comet, вебсокетов или аналогичных технологий уведомление можно никуда не сохранять, а просто переслать от одного клиента другому (через демон-ретранслятор на сервере или может быть даже напрямую, если есть возможность установить peer-to-peer соединение между клиентами).
Подождите, я только изучу эти технологии прежде чем что-то спросить. Конечно написание демона-ретранслятора или установление p2p соединения (хотя в браузеры уже встроен WebRTC и должно быть не сложно) представляется мне чем-то сложным.
>Также, перед проектированием АПИ, советую почитать про REST и HATEOS (и далее, стандарт про шаблоны URL https://tools.ietf.org/html/rfc6570 ), но тут конечно есть разные мнения, ну к примеру в иделогии REST одним запросом нельзя получить несколько несвязанных массивов данных, а HATEOS раздувает размер ответа.
Это мне тоже нужно сначала изучить прежде чем что-то спросить.
>Насчет MVC, там у тебя все запутано. Ну вот например тут модель лезет в DOM:
>https://github.com/someApprentice/chat/blob/master/public/js/chat.js#L51
>Модель ведь по задумке должна хранить данные и реализовывать логику работы приложения, а не считать величину отступа где-то на странице.
Получается считать величину отступа нужно в отоброжении?
>> $('.contacts-not-found').remove();
>Вот вместо постоянного копирования этих кусков кода, обычно удобнее найти элемент и сохранить ссылку на него в переменную и далее работать с этой ссылкой. Это сделает и код аккуратнее, и заодно и ускорит работу, так как $(..) имеет сложность O(N) от числа элементов DOM на странице, там оптимизирован только поиск по id.
А где обычно создаются такие переменные? В самом вверху, перед "классами" MVC?
>Вот еще пример плохого использования jQuery: $('textarea[name="message"]').val($('textarea[name="message"]').val() + "\n");
Как тогда по другому добавить перенос на новую строку по нажатию Ctrl + Enter?
>> Contreller.prototype.getConversation = function(withUser) {
>> $.getJSON('api/getmessages.php?with=' + withUser, function(results) {
>> window.history.pushState({}, '', '/conversation.php?with=' + withUser);
> Во-первых, с историей лучше работать через JS-библиотеку, это позволит поддерживать браузеры без pushState(). Во-вторых, если со страницы диалогов вызвать эту функцию, в историю не запишется переход на ту же самую страницу? В-третьих, ты делаешь переход только после получения ответа, это плохо, так как при проблемах со связью и обновлении страницы переход не произойдет. Ну и нет обработки ошибок при выполнении запроса.
>Вообще, для работы с историей, есть такая штука, как клиентский роутер: https://www.google.ru/search?q=js+router&newwindow=1&gbv=1&sei=TWEyWc6YBYLVsAHegKrACg
Клиентский роутер это и есть JS-библиотека о которой вы упомянули в начале? Если нет, то что есть JS-библиотека в таком случае?
>Ну и нет обработки ошибок при выполнении запроса.
А что за обработка ошибок должна быть? Имеется ввиду та которая присходит при неудачном получении JSON?
>> Как остановить setInterval, который был вызван в предыдущем событии?
>Есть clearInterval, но проще использовать setTimeout.
>clearInterval
А как его вызвать на интервале который был вызван до него?
//После клика на другой блок, интервал на первом кликнутом блоке не остановился
https://jsfiddle.net/tux1yebv/
>setTimeout
А зачем тут setTimeout, который вызывается всего один раз с делеем? Он сбрасывает setInterval?
>> Как лучше сохранять не отправленные сообщения?
>Можно сохранять их в localStorage. Но тогда есть риск, если пользователь на каком-нибудь общем компьютере набирал сообщение, то потом его может найти и прочесть другой человек. Можно сохранять сообщение на сервер. Но тогда может человек сотрет сообщение, а на сервере оно сохранится. Тоже некрасиво.
Мне нравиться вариант с сохранением на сервер, но где именно? Можно сделать отдельную таблицу unsendedmessages в БД.
>Но тогда может человек сотрет сообщение, а на сервере оно сохранится.
Тогда нужно сделать чтобы сообщение отправлялось каждый раз как пользователь его меняет. Не будет ли это создавать большую нагрузку на БД, если, например, делать это на добавление\удаление нового символа или хотя бы нового слова\перевода строки?
С другой стороны, я видел, как в популярных мессенджерах, на разных устройствах\приложениях сохраняются свои не отправленные сообщения.
Даже не знаю что лучше, сохранять сообщения локально и подвергать пользователя тому, что его неотправленные сообщения украдут, или хранить их на сервере и создавать нагрузку на БД. Наверно, пользователь должен сам отвечать за доступ к своему устройству, и его об этом нужно предупредить.
>Еще одна вещь - мне кажется, хорошим тоном было бы до отправки сообщения в чате писать информацию о том, куда сохраняется история этого чата. Чтобы пользователь знал, где будут храниться его сообщения. Никто почти так не делает, я только один раз видел это в каком-то линуксовом мессенджере, он спрашивал, надо ли сохранять историю. И это многое говорит об отношении разработчиков к приватности пользователя.
А какую именно информацию нужно написать? Расположение сервера где находиться БД? Или просто уведомить о том что сообщения сохраняются либо в БД, либо локально (в течении сессии)?
А что если один пользователь захочет хранить историю а другой нет? Тогда в любом случае сообщения будут храниться в одном экземпляре (если денормализовать сообщения на inbox и outbox).
И, наверно, при отключенной истории сообщений, сообщения текущей сессии нужно хранить в какой-нибудь переменной messages, которая будет обнуляться при закрытии\обнавлении окна.
http://phpclub.rf.gd/pr/res/919074.html#934872
>Если ты хочешь хранить сообщения на сервере (для истории и синхронизации), то надо хранить их зашифрованными. А ключ хранить где-то в другом месте. Но может быть лучше вооще их не хранить? Или хотя бы спрашивать пользователя, хочет ли он вести запись истории или нет. Программа, которая уважает приватность пользователя, не должна ничего записывать без его согласия.
>ключ хранить где-то в другом месте
А можете посоветовать где лучше всего хранить ключ?
>Вообще, это как раз тот случай, когда на БД это реализовать неудобно. По идее да, клиент должен посылать уведомление на сервер, тот сохранять куда-то (в таблицу уведомлений?) и отдавать другому клиенту. Уведомление желательно сделать актуальным только на небольшой период времени, чтобы при отсоедиении клиента у его собеседника не висело это уведомление вечно.
Можно сделать на стороне получателя setTimout через одну или полсекунды сбрасывающий это уведомление. Только я не знаю как тут будет с сихронизацией: у отправителя на каждый нажатый символ отправляется уведомление на сервер, а получатель при обновлении, каждую секунду сбрасывает это уведомление.
notify = function () {
//отсылаем уведомление
$.ajax(...);
}
getNotify = function () {
//получаем уведомление
$.getJSON(...);
setTimeout(function() {
//сбрасываем уведомление
$.ajax(...);
},
1000
);
}
$(textarea).on('keypress', function(e) {
notify();
});
setInterval(function() {getNotify();}, 1000);
Наверно, тут никакого рассинхрона не будет, я не знаю. Я слишком запутался в вопросе как работают счётчики.
Опять же, я не знаю какая будет нагрузка при отправлении уведомления на каждую нажатую кнопку (ссылаюсь на тот же вопрос о сохранении неотправленного сообщения при нажатии на клавишу).
>В случае с использованием comet, вебсокетов или аналогичных технологий уведомление можно никуда не сохранять, а просто переслать от одного клиента другому (через демон-ретранслятор на сервере или может быть даже напрямую, если есть возможность установить peer-to-peer соединение между клиентами).
Подождите, я только изучу эти технологии прежде чем что-то спросить. Конечно написание демона-ретранслятора или установление p2p соединения (хотя в браузеры уже встроен WebRTC и должно быть не сложно) представляется мне чем-то сложным.
>Также, перед проектированием АПИ, советую почитать про REST и HATEOS (и далее, стандарт про шаблоны URL https://tools.ietf.org/html/rfc6570 ), но тут конечно есть разные мнения, ну к примеру в иделогии REST одним запросом нельзя получить несколько несвязанных массивов данных, а HATEOS раздувает размер ответа.
Это мне тоже нужно сначала изучить прежде чем что-то спросить.
>Насчет MVC, там у тебя все запутано. Ну вот например тут модель лезет в DOM:
>https://github.com/someApprentice/chat/blob/master/public/js/chat.js#L51
>Модель ведь по задумке должна хранить данные и реализовывать логику работы приложения, а не считать величину отступа где-то на странице.
Получается считать величину отступа нужно в отоброжении?
>> $('.contacts-not-found').remove();
>Вот вместо постоянного копирования этих кусков кода, обычно удобнее найти элемент и сохранить ссылку на него в переменную и далее работать с этой ссылкой. Это сделает и код аккуратнее, и заодно и ускорит работу, так как $(..) имеет сложность O(N) от числа элементов DOM на странице, там оптимизирован только поиск по id.
А где обычно создаются такие переменные? В самом вверху, перед "классами" MVC?
>Вот еще пример плохого использования jQuery: $('textarea[name="message"]').val($('textarea[name="message"]').val() + "\n");
Как тогда по другому добавить перенос на новую строку по нажатию Ctrl + Enter?
>> Contreller.prototype.getConversation = function(withUser) {
>> $.getJSON('api/getmessages.php?with=' + withUser, function(results) {
>> window.history.pushState({}, '', '/conversation.php?with=' + withUser);
> Во-первых, с историей лучше работать через JS-библиотеку, это позволит поддерживать браузеры без pushState(). Во-вторых, если со страницы диалогов вызвать эту функцию, в историю не запишется переход на ту же самую страницу? В-третьих, ты делаешь переход только после получения ответа, это плохо, так как при проблемах со связью и обновлении страницы переход не произойдет. Ну и нет обработки ошибок при выполнении запроса.
>Вообще, для работы с историей, есть такая штука, как клиентский роутер: https://www.google.ru/search?q=js+router&newwindow=1&gbv=1&sei=TWEyWc6YBYLVsAHegKrACg
Клиентский роутер это и есть JS-библиотека о которой вы упомянули в начале? Если нет, то что есть JS-библиотека в таком случае?
>Ну и нет обработки ошибок при выполнении запроса.
А что за обработка ошибок должна быть? Имеется ввиду та которая присходит при неудачном получении JSON?
Это легко или легче пять соток заплатить какому-нибудь студентику, чтобы он это сделал?
>>1000397
>Вообще, SQL базы не очень хорошо подходят для таких задач, насколько я знаю, все нагруженные чаты в итоге пишут какие-то свои хранилища, заточенные именно под хранение сообщений. Но это конечно долго и сложно.
А что, например, нужно затачивать для этого?
Спасибо.
Легко.
Мне поставили задачу создать кучу страничек на основе файлов. С получением списка файлов и их преобразованием в темплейты и URLы я не вижу.
А вот как организвать всю эту обработку в целом — я не знаю. Вот например, нужно несколько тысячь записей автоматически в БД сделать и их обновлять — как такое делается вообще-а? Что нужно почитать? Что-то про cron? Какие фреймворки стоит использовать? Все что я делал в PHP происходило в браузере, и я вообще не представляю, что делать с этим.
Ты знаешь что такое parser? Если нет, то загугли как его сделать на php. Если хорошо знаешь php, то легко сможешь написать собственный.
XAMPP?
https://habrahabr.ru/post/111508/
а какой оптимальный?
Прикрутил капчу - пакет из композера. Причем через какие-то костыли, потому что то ли автор задумывал его использование не так как я, то ли я не понял автора.
Самая мякотка, что сильно не хватает знания линукса. ЛАМП, ВАМП, ВАГРАНТ, это хуйня, а боевой ВДС - совсем другое. Делаю одно - отваливается другое. А самое паршивое, когда не отваливается, а работает через раз, как мой фтп. Я уже не человек, а мистер Робот нахуй.
Сори за оффтоп, всё равно мертвый сезон.
Бля. Тогда обидно
Зато функционал расширил, считай, имиджборду написал, осталось загрузку файлов добавить.
Алсо, охуеваю как на дваче всё продумано. Вроде, смотришь - хуйня начала нулевых, а сам подобное начинаешь делать и тихо восторгаешься.
http://ideone.com/32QpwQ Задание: дан текст из нескольких предложений, предложение состоит из русских слов, разделенных пробелами. Написать программу, переставляющую слова в предложениях в обратном порядке. Знаки препинания в конце предложений (точка, восклицательный знак) можно не сохранять. не смог сделать на русском, что бы большие буквы были в начале предложения и маленькие в конце.
http://ideone.com/XnwxXg Дана сумма, находящаяся в банке на счету, в рублях. Вывести ее в текстовом виде вроде "шестнадцать миллионов десять тысяч три (16010003) рубля". может как то это можно оптимизировать или вообще все неправильно...
http://ideone.com/9fc5tF Задача: написать программу, имитирующую работу обычного кнопочного калькулятора (если у тебя нет калькулятора, можешь попробовать запустить его на телефоне/айфоне/компьютере). Калькулятору на вход дается строка, содержащая целые числа и знаки «+», «-», «» и «=». По знаку «=» калькулятор выводит ответ. Порядок действий не соблюдается (как и в обычном калькуляторе), то есть 2+22= выведет 8.
http://ideone.com/8hUwnm вывести выигрышные места в считалке
http://ideone.com/MSiND0 вывести текст вертикально
Спасибо.
*фикс по второй
Нахуя спойлеры, поехавший
>>1004956
Да не, мне не парсер нужен. Мне нужно просто понять, как php используется для автоматизированного бекенда.
Я просто фронтендер и php использовал для того, что называется "бизнес-логика". Мне просто нужен какой-то пример приложения на php, которое работает в фоновом режиме и обрабатывает данные базы хм, наверное, парсер как раз и подойдет для примера.
Если речь идет про cli, то все как обычно. Пишешь значит код, запускаешь как консольное приложение. Хош через крон, нехош так нахуй крон, похуй ваще, епта.
Можно заебенить вполне себе живучий воркер и запустить его каким-нибудь способом в виде демона например.
Ты сформулируй яснее сначала, а то хуйню какую-то несешь из которой не ясно, что именно тебе надо. Файлы, базы, апдейты, херь какая-то. Тебе как надо, типа запустил один раз, оно один раз отработало и все? Или же запустил и забыл, а оно там само работает до конца аптайма?
Встроенный фильтр date [1] твига умеет только форматировать через метод DateInterval::format, который очевидно ничего не знает о человекочитаемых единицах времени. Есть расширение [2] которое как раз переводит единицы времени и правильно определяет число (множественное/единственное) для разных языков, но оно почему-то работает только с DateTime, а мне нужен именно DateInterval + оно считает time ago, что не подходит.
Вот тут [3] такая же ситуация.
[1] https://twig.sensiolabs.org/doc/2.x/filters/date.html
[2] https://github.com/twigphp/Twig-extensions/blob/master/lib/Twig/Extensions/Extension/Date.php
[3] https://github.com/mikemadisonweb/DateIntervalTwigExtension/blob/master/src/Madison/Twig/DateIntervalExtension.php
У тебя вопрос в итоге в чем? В том, что ты не можешь сделать так что бы у тебя вместо:
1 часов, 2 часов, 3 часов, 4 часов, 5 часов, 6 часов...
выводилось по умненькому:
1 час, 2 часа, 3 часа, 4 часа, 5 часов, 6 часов
?
Или ты просто не понимаешь как интервал из массива вывести в виде строки?
> выводилось по умненькому:
> 1 час, 2 часа, 3 часа, 4 часа, 5 часов, 6 часов
Да, только для разных языков и без велосипедов, должны же быть готовые решения. Тут [1][2] есть даже примеры склонения слов для английского и русского и мне бы подошло это расширение, если бы оно работало с DateInterval.
[1] https://github.com/mikemadisonweb/DateIntervalTwigExtension/blob/master/src/Madison/Twig/DateIntervalExtension.php#L146
[2] https://github.com/mikemadisonweb/DateIntervalTwigExtension/blob/master/src/Madison/Twig/DateIntervalExtension.php#L32
Используй Intl.
http://ideone.com/81UVOP
http://ideone.com/SwUO54
Какой в данном случае способ лучше? Запутался после слов опа пикрелейтед.
>http://rgho.st/private/6FxD8kMsQ/988d18544217b8ca03c5b100e5a8c5a2
>Загружен неделю назад
>Скачано 0 раз.
Блядь да вы охуели что ли? 0 помощи теперь долбоебам в этом треде от меня.
У них счётчик навернулся. Или считает только скачки зарегестрированных юзеров. Только что скачал, а ничего не поменялось.
>>1000424
> Куки на 10 лет + привязка к IP-адресу.
Вообще, тут есть недостатки. У многих провайдеров динамический IP. В случае с беспроводным интернетом или с DSL-модемом - при пересоединении получаешь новый адрес. В теории можно определять по IP-адресу подсеть (через базу спарсенную с whois) и привязываться к подсети, но за 10 лет и это может поменяться, и надо поддерживать базу.
Также можно привязываться к городу, определенному по GeoIP. Опять же базу придется поддерживать в актуальном состоянии.
Так что стоит все это учитывать при привязке к IP. Так как по факту получится, что авторизация будет слетать по непонятным для пользователя причинам.
> ЗАКРЫВАЙТЕ ФОРМЫ
Эти ошибки можно обнаружить, проверив HTML код валидатором вроде этого https://validator.w3.org/ также есть какие-то расширения для браузеров, позволяющие быстро отправить страницу на валидацию.
По коду:
> https://github.com/Merkalov/Students/blob/master/README.md
Тут стоит еще кратко описать как развернуть проект (какие программы нужны, каких версий, какие команды выполнить, какой конфиг подправить).
Папки вроде models я бы советовал называть в том же регистре что и неймспейс, чтобы было меньше путаницы.
Публичная папка у тебя есть, но она сделана как-то не так. Идея публичной папки в том, что в конфиге веб-сервера она прописывается как корневая папка и соответственно за ее пределами файлы недоступны снаружи. И значит index.php должен лежать в ней. Так будет безопаснее ну и как-то логичнее разделить файлы на публичные и непубличные.
Ну например сейчас у тебя можно скачать SQL дамп из-за этого.
В дампе не надо указывать имя БД, так как это не позволяет его залить в другую БД. Не нужно в дамп прописывать CREATE DATABASE.
> CREATE TABLE `info` (
Плохое название таблицы. Любая таблица хранит информацию, так что слово info ничего не значит. Также, я не очень понял зачем тут 2 таблицы для хранения информации о пользователях. Не помешал бы комментарий. Название можно было бы сделать user_profile или user_details.
Связи между таблицами надо оформить с помощью внешних ключей.
https://github.com/Merkalov/Students/blob/master/config/127_0_0_1.sql#L33
> `userID` varchar(255) NOT NULL,
Это ссылка на таблицу users? Тогда тип явно должен быть не varchar. Также, если связь 1-1, то незачем делать 2 разных id, можно сделать userId первичным ключом.
Также, у тебя там как-то непонятно сделана привязка к IP, а можно с нескольких разных IP заходить?
> `name` text NOT NULL,
TEXT позволяет хранить до 65536 байт, что явно много для имени. Нужно использовать тут varchar.
> `gender` varchar(11)
Тут лучше подойдет ENUM (или на худой конец TINYINT). В коде пол надо сделать константами.
> `numberGroup` varchar(11)
11 символов многовато для номера группы
> varchar(255)
Кстати у varchar ограничение - не 255, а по моему 65535, но фактически там есть еще ограничение на размер строки таблицы в 65535 байт, один символ в utf8 занимает несколько байт, так что реально получится меньше.
> https://github.com/Merkalov/Students/blob/master/config/127_0_0_1.sql#L161
> ALTER TABLE `users`
> ADD PRIMARY KEY (`id`),
> ADD UNIQUE KEY `id_3` (`id`),
Эти индексы дублируют друг друга.
Конфиг БД возможно стоило все же вынести в отдельный файл, чтобы не надо было править что-то в коде. При этом вынести в конфиг стоит только то, что можно менять (тип БД или кодировку нет смысла выносить).
> https://github.com/Merkalov/Students/blob/master/config/Database.php
Вот ты сделал класс-фабрику для получения объекта Medoo. А для других классов ты тоже сделаешь свои фабрики для каждого? Если нет, то почему? Ну и посоветую почитать урок про DI https://github.com/codedokode/pasta/blob/master/arch/di.md
> https://github.com/Merkalov/Students/blob/master/index.php#L6
> ini_set('display_errors',1);
Это лучше прописывать в php.ini, иначе как твой код выкладывать на продакшен?
> require_once(ROOT . '\vendor\autoload.php');
Бекслеши не работают в линуксе. Лучше использовать прямые слеши.
> define('ROOT', dirname(__FILE__));
Зачем константы? Почему не просто переменная? У переменной ограничена зона, где она доступна в то время как константа доступна глобально, что поощряет использовать ее где попало.
> //Вызов FrontController
> use Controllers\FrontController;
use ставят в самом начале файла, а не в конце. Почитай PSR-1 и 2.
> $router = new FrontController();
Одинаковые вещи нужно называть одинаково.
> $this->routes = include($routesPath);
Лучше require, почему - смотри в мануале
> $url = mb_strtolower($_SERVER['REQUEST_URI']); //в нижний регистр
> trim($url, '/');
Вот это плохая идея. У тебя из-за этого получится так, что одной странице соответствует множество URL. Это плохо и с точки зрения логики (УРЛ это ведь идентификатор страницы и он должен быть единственный) и с точки зрения поисковой оптимизации. То же касается слешей, не надо делать 2 УРЛ отличающиеся только наличием слеша. Лучше выбрать один вариант, а при неправильном расположении слешей или регистре букв можно редиректить на правильный URL.
И то же касается дополнительных параметров, из-за них не должны появляться дублирующиеся страницы.
> } elseif ($total == $counter) {
Как-то переусложнено. Лучше писать так:
foreach ($routes as $route) {
if (...) {
return $this->handleRoute(...);
}
}
// вывод 404 ошибки
> $controllerName = ucfirst(array_shift($segments)) . 'Controller';
Лучше просто писать имя контроллера как есть в конфиге. Тогда оно например найдется при поиске по имени класса. То же касается и имени метода (хотя это мое мнение, во многих фреймворках делают как у тебя).
Вообще, имя класса можно писать как SomeClass::CLASS - тогда оно будет проверяться на отстутствие опечаток, на то что такой класс существует, будет работать автодополнение. Как минус, будет срабатывать автозагрузка.
> self::_redirect('404', 302);
Вот я у каждого первого встречаю эту ошибку. Где такому учат? Редирект это ответ с кодом 302, он имеет смысл "искомая страница есть, но она в другом месте", а надо выдавать страницу с кодом 404. Почитай про коды статуса ответа в протоколе HTTP.
И не стоит начинать название функции с подчеркивания.
> ob_start(); //Уворачиваемя от вывода любой инфы, ведь мы всюду суём заголовки
Может лучше сначала вывести заголовки, а только потом тело страницы?
> https://github.com/Merkalov/Students/blob/master/controllers/InfoController.php#L26
> public function actionMyInfo()
> ob_start();
Зачем это тут второй раз? Есть же в фронт контроллере? И конечно не надо пытаться костылем заткнуть неудачную архитектуру.
> https://github.com/Merkalov/Students/blob/master/models/Cookie.php
Тут почему-то все методы статические. Однозначно стоит почитать про DI.
Класс Cookie назван неправильно. Он что, хранит информацию о какой-то куке? Нет.
То же самое касается метода checkCookie. "Проверить куку". Непонятно из названия, что делает этот метод, что он возвращает, а комментариев нет.
> $hash_cookie = md5($_POST['email']);
Ок, то есть зная email мы можем автоматически сгенерировать куку для авторизации от его имени?
>>1000424
> Куки на 10 лет + привязка к IP-адресу.
Вообще, тут есть недостатки. У многих провайдеров динамический IP. В случае с беспроводным интернетом или с DSL-модемом - при пересоединении получаешь новый адрес. В теории можно определять по IP-адресу подсеть (через базу спарсенную с whois) и привязываться к подсети, но за 10 лет и это может поменяться, и надо поддерживать базу.
Также можно привязываться к городу, определенному по GeoIP. Опять же базу придется поддерживать в актуальном состоянии.
Так что стоит все это учитывать при привязке к IP. Так как по факту получится, что авторизация будет слетать по непонятным для пользователя причинам.
> ЗАКРЫВАЙТЕ ФОРМЫ
Эти ошибки можно обнаружить, проверив HTML код валидатором вроде этого https://validator.w3.org/ также есть какие-то расширения для браузеров, позволяющие быстро отправить страницу на валидацию.
По коду:
> https://github.com/Merkalov/Students/blob/master/README.md
Тут стоит еще кратко описать как развернуть проект (какие программы нужны, каких версий, какие команды выполнить, какой конфиг подправить).
Папки вроде models я бы советовал называть в том же регистре что и неймспейс, чтобы было меньше путаницы.
Публичная папка у тебя есть, но она сделана как-то не так. Идея публичной папки в том, что в конфиге веб-сервера она прописывается как корневая папка и соответственно за ее пределами файлы недоступны снаружи. И значит index.php должен лежать в ней. Так будет безопаснее ну и как-то логичнее разделить файлы на публичные и непубличные.
Ну например сейчас у тебя можно скачать SQL дамп из-за этого.
В дампе не надо указывать имя БД, так как это не позволяет его залить в другую БД. Не нужно в дамп прописывать CREATE DATABASE.
> CREATE TABLE `info` (
Плохое название таблицы. Любая таблица хранит информацию, так что слово info ничего не значит. Также, я не очень понял зачем тут 2 таблицы для хранения информации о пользователях. Не помешал бы комментарий. Название можно было бы сделать user_profile или user_details.
Связи между таблицами надо оформить с помощью внешних ключей.
https://github.com/Merkalov/Students/blob/master/config/127_0_0_1.sql#L33
> `userID` varchar(255) NOT NULL,
Это ссылка на таблицу users? Тогда тип явно должен быть не varchar. Также, если связь 1-1, то незачем делать 2 разных id, можно сделать userId первичным ключом.
Также, у тебя там как-то непонятно сделана привязка к IP, а можно с нескольких разных IP заходить?
> `name` text NOT NULL,
TEXT позволяет хранить до 65536 байт, что явно много для имени. Нужно использовать тут varchar.
> `gender` varchar(11)
Тут лучше подойдет ENUM (или на худой конец TINYINT). В коде пол надо сделать константами.
> `numberGroup` varchar(11)
11 символов многовато для номера группы
> varchar(255)
Кстати у varchar ограничение - не 255, а по моему 65535, но фактически там есть еще ограничение на размер строки таблицы в 65535 байт, один символ в utf8 занимает несколько байт, так что реально получится меньше.
> https://github.com/Merkalov/Students/blob/master/config/127_0_0_1.sql#L161
> ALTER TABLE `users`
> ADD PRIMARY KEY (`id`),
> ADD UNIQUE KEY `id_3` (`id`),
Эти индексы дублируют друг друга.
Конфиг БД возможно стоило все же вынести в отдельный файл, чтобы не надо было править что-то в коде. При этом вынести в конфиг стоит только то, что можно менять (тип БД или кодировку нет смысла выносить).
> https://github.com/Merkalov/Students/blob/master/config/Database.php
Вот ты сделал класс-фабрику для получения объекта Medoo. А для других классов ты тоже сделаешь свои фабрики для каждого? Если нет, то почему? Ну и посоветую почитать урок про DI https://github.com/codedokode/pasta/blob/master/arch/di.md
> https://github.com/Merkalov/Students/blob/master/index.php#L6
> ini_set('display_errors',1);
Это лучше прописывать в php.ini, иначе как твой код выкладывать на продакшен?
> require_once(ROOT . '\vendor\autoload.php');
Бекслеши не работают в линуксе. Лучше использовать прямые слеши.
> define('ROOT', dirname(__FILE__));
Зачем константы? Почему не просто переменная? У переменной ограничена зона, где она доступна в то время как константа доступна глобально, что поощряет использовать ее где попало.
> //Вызов FrontController
> use Controllers\FrontController;
use ставят в самом начале файла, а не в конце. Почитай PSR-1 и 2.
> $router = new FrontController();
Одинаковые вещи нужно называть одинаково.
> $this->routes = include($routesPath);
Лучше require, почему - смотри в мануале
> $url = mb_strtolower($_SERVER['REQUEST_URI']); //в нижний регистр
> trim($url, '/');
Вот это плохая идея. У тебя из-за этого получится так, что одной странице соответствует множество URL. Это плохо и с точки зрения логики (УРЛ это ведь идентификатор страницы и он должен быть единственный) и с точки зрения поисковой оптимизации. То же касается слешей, не надо делать 2 УРЛ отличающиеся только наличием слеша. Лучше выбрать один вариант, а при неправильном расположении слешей или регистре букв можно редиректить на правильный URL.
И то же касается дополнительных параметров, из-за них не должны появляться дублирующиеся страницы.
> } elseif ($total == $counter) {
Как-то переусложнено. Лучше писать так:
foreach ($routes as $route) {
if (...) {
return $this->handleRoute(...);
}
}
// вывод 404 ошибки
> $controllerName = ucfirst(array_shift($segments)) . 'Controller';
Лучше просто писать имя контроллера как есть в конфиге. Тогда оно например найдется при поиске по имени класса. То же касается и имени метода (хотя это мое мнение, во многих фреймворках делают как у тебя).
Вообще, имя класса можно писать как SomeClass::CLASS - тогда оно будет проверяться на отстутствие опечаток, на то что такой класс существует, будет работать автодополнение. Как минус, будет срабатывать автозагрузка.
> self::_redirect('404', 302);
Вот я у каждого первого встречаю эту ошибку. Где такому учат? Редирект это ответ с кодом 302, он имеет смысл "искомая страница есть, но она в другом месте", а надо выдавать страницу с кодом 404. Почитай про коды статуса ответа в протоколе HTTP.
И не стоит начинать название функции с подчеркивания.
> ob_start(); //Уворачиваемя от вывода любой инфы, ведь мы всюду суём заголовки
Может лучше сначала вывести заголовки, а только потом тело страницы?
> https://github.com/Merkalov/Students/blob/master/controllers/InfoController.php#L26
> public function actionMyInfo()
> ob_start();
Зачем это тут второй раз? Есть же в фронт контроллере? И конечно не надо пытаться костылем заткнуть неудачную архитектуру.
> https://github.com/Merkalov/Students/blob/master/models/Cookie.php
Тут почему-то все методы статические. Однозначно стоит почитать про DI.
Класс Cookie назван неправильно. Он что, хранит информацию о какой-то куке? Нет.
То же самое касается метода checkCookie. "Проверить куку". Непонятно из названия, что делает этот метод, что он возвращает, а комментариев нет.
> $hash_cookie = md5($_POST['email']);
Ок, то есть зная email мы можем автоматически сгенерировать куку для авторизации от его имени?
>>1000424
https://github.com/Merkalov/Students/blob/master/models/Student.php
Какой паттерн тут реализован? Прочитай урок, если не знаешь: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Класс назван неправильно. Он что, хранит информацию о студенте? Нет. Значит название должно быть другое - например, StudentTableGateway или StudentDbHelper, или что-то еще такое.
Принцип MVC у тебя не соблюдается. Идея MVC в отделении кода обработки HTTP-запроса от внутренней логики приложения, а у тебя в одном классе смешана работа с БД, разбор параметров POST-запроса, работа с авторизационными куками и определение текущего пользователя. Вот у тебя есть метод addInfo(), а как его вызвать с произвольными данными, не полученными из формы? Чтобы допустим добавить студента программно, не через форму? Разумеется, с пустыми куками?
И перечитай внимательно урок https://github.com/codedokode/pasta/blob/master/arch/mvc.md
Особенно это:
> Например, в программе на PHP Модель не должна обращаться к внешним переменным вроде $_GET/$_POST/$_SESSION/$_COOKIE и не должна ничего выводить через echo. Все необходимые данные она получает через аргументы функций, и возвращает результат через return.
> Весь функционал приложения содержится в модели. ... К примеру, если мы делаем сайт объявлений, с такими функциями, как "добавить объявление", "удалить объявление", "найти объявления по критериям", то для каждого действия где-то в модели должна быть функция, которую можно вызвать.
У тебя пока нет MVC, просто ты назвал папки controllers, models и views.
> public function searchID($searchQuery)
> "OR" => [
> "name" => $searchQuery,
> "surname[REGEXP]" => $searchQuery,
Это позволяет искать по части имени? Мне кажется, тут прямой SQL запрос был бы понятнее и читабельнее, чем сложная конструкция из массивов.
Ну и результат поиска помещается в переменную $userID - там только один id хранится, судя по названию?
> $full_name = $database->select('info',
> return array_shift($full_name);
Что-то я не очень понимаю, там разве нет метода для выборки единственной записи?
> public function getInfoSomeStudents($startLimit, $sort, $typeSort)
Тут нет проверки переменных sort и typeSort, это безопасно? Тут не сделают SQL инъекцию?
https://github.com/Merkalov/Students/blob/master/models/Helper.php
Этот класс назван неудачно, название ничего не говорит о содержании, также, у тебя валидатор зачем-то намертво привязан к массиву POST и не способен проверять данные из других источников.
В валидаторе по моему есть повторяющийся код - это очень плохо, копипасты быть не должно и повторов тоже.
https://github.com/Merkalov/Students/blob/master/models/Url.php
Класс назван неудачно.
https://github.com/Merkalov/Students/blob/master/models/Page.php
Неудачное название, плохо что HTML перемешан с PHP, на пагинатор зачем-то возложена функция подсчета записей в БД, и вообще пагинатор получился неуниверсальный и его нельзя использовать с другими табличными и нетабличными данными. Опять же, ты пока не можешь отделить работу с БД от подсчета страниц и формирование URL ссылки от формирования HTML тега <a>.
У тебя маленькое приложение и вся работа с БД должна быть собрана в 1-2 классах, а не размазана по приложению.
Наверняка, тебе что-то непонятно, задавай вопросы тогда.
Также, просмотри внимательно комментарии к студентам.
Также, если вдруг не делал задачу про Вектор, то советую сделать, чтобы лучше разобраться с ООП.
>>1000424
https://github.com/Merkalov/Students/blob/master/models/Student.php
Какой паттерн тут реализован? Прочитай урок, если не знаешь: https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
Класс назван неправильно. Он что, хранит информацию о студенте? Нет. Значит название должно быть другое - например, StudentTableGateway или StudentDbHelper, или что-то еще такое.
Принцип MVC у тебя не соблюдается. Идея MVC в отделении кода обработки HTTP-запроса от внутренней логики приложения, а у тебя в одном классе смешана работа с БД, разбор параметров POST-запроса, работа с авторизационными куками и определение текущего пользователя. Вот у тебя есть метод addInfo(), а как его вызвать с произвольными данными, не полученными из формы? Чтобы допустим добавить студента программно, не через форму? Разумеется, с пустыми куками?
И перечитай внимательно урок https://github.com/codedokode/pasta/blob/master/arch/mvc.md
Особенно это:
> Например, в программе на PHP Модель не должна обращаться к внешним переменным вроде $_GET/$_POST/$_SESSION/$_COOKIE и не должна ничего выводить через echo. Все необходимые данные она получает через аргументы функций, и возвращает результат через return.
> Весь функционал приложения содержится в модели. ... К примеру, если мы делаем сайт объявлений, с такими функциями, как "добавить объявление", "удалить объявление", "найти объявления по критериям", то для каждого действия где-то в модели должна быть функция, которую можно вызвать.
У тебя пока нет MVC, просто ты назвал папки controllers, models и views.
> public function searchID($searchQuery)
> "OR" => [
> "name" => $searchQuery,
> "surname[REGEXP]" => $searchQuery,
Это позволяет искать по части имени? Мне кажется, тут прямой SQL запрос был бы понятнее и читабельнее, чем сложная конструкция из массивов.
Ну и результат поиска помещается в переменную $userID - там только один id хранится, судя по названию?
> $full_name = $database->select('info',
> return array_shift($full_name);
Что-то я не очень понимаю, там разве нет метода для выборки единственной записи?
> public function getInfoSomeStudents($startLimit, $sort, $typeSort)
Тут нет проверки переменных sort и typeSort, это безопасно? Тут не сделают SQL инъекцию?
https://github.com/Merkalov/Students/blob/master/models/Helper.php
Этот класс назван неудачно, название ничего не говорит о содержании, также, у тебя валидатор зачем-то намертво привязан к массиву POST и не способен проверять данные из других источников.
В валидаторе по моему есть повторяющийся код - это очень плохо, копипасты быть не должно и повторов тоже.
https://github.com/Merkalov/Students/blob/master/models/Url.php
Класс назван неудачно.
https://github.com/Merkalov/Students/blob/master/models/Page.php
Неудачное название, плохо что HTML перемешан с PHP, на пагинатор зачем-то возложена функция подсчета записей в БД, и вообще пагинатор получился неуниверсальный и его нельзя использовать с другими табличными и нетабличными данными. Опять же, ты пока не можешь отделить работу с БД от подсчета страниц и формирование URL ссылки от формирования HTML тега <a>.
У тебя маленькое приложение и вся работа с БД должна быть собрана в 1-2 классах, а не размазана по приложению.
Наверняка, тебе что-то непонятно, задавай вопросы тогда.
Также, просмотри внимательно комментарии к студентам.
Также, если вдруг не делал задачу про Вектор, то советую сделать, чтобы лучше разобраться с ООП.
Спасибо, анон. Я затупил, там на изичах все.
Надо смотреть на логику.
Если ты хочешь написать функцию, которая может найти, а может не найти пользователя (то есть это ожидаемая ситуация, что он может не найтись) - тогда надо возвращать null. Но тогда ты должен проверять результат функции на этот null всегда.
При этом функция все равно может выбросить исключение - ну например если id не числовой или если не удается соединиться с БД (PDO выбрасывает исключение PDOException в этом случае). То есть мы различаем штатные и нештатные ситуации таким образом.
Если ты уверен что передаваемый id правильный и не найтись пользователь не может - то надо использовать исключение и результат можно не проверять.
В случае когда id приходит от пользователя, конечно, он может оказаться неправильным. В этом случае код с if будет смотреться аккуратнее, чем отлов исключения (хотя некоторые используют исключения в этом случае). Если ты ловишь исключение, надо сделать свой класс исключений для этого случая и ловить только его.
> if (!isset($file)) {
Проверять на null надо через is_null или === null
>>1005418
Переменные, созданные внутри функции (локальные), не видны снаружи, и наоборот. Значения (не только переменные, но и числа, строки) надо передавать в функцию через аргументы в скобках, а возвращать из функции наружу через return.
У тебя есть return, но значение никуда не сохраняется, надо писать $x = func(...); чтобы его сохранить.
Ты же пишешь $a = mt_rand(1, 6), так же надо делать и тут.
>>1005336
>>1005325
Увы, не знаю, готового решения. Можно запилить что-то на основе http://php.net/manual/ru/class.messageformatter.php но если тебе нужен только один язык (русский), то может проще написать кодом.
В JS есть такие библиотеки: https://www.google.ru/search?q=js+time+library&btnG=Поиск&newwindow=1&gbv=1
Посмотри, может есть аналоги на PHP или можно сделать аналог самому и опубликовать на гитхабе.
То, что встроено в DateTime - это по сути интерфейс к древней сишной функции http://man7.org/linux/man-pages/man3/strftime.3.html и конечно никаких склонений она делать не умеет.
>>1005335
У него вопрос, какие есть готовые библиотеки или расширения, чтобы не изобретать то, что уже изобретено.
Надо смотреть на логику.
Если ты хочешь написать функцию, которая может найти, а может не найти пользователя (то есть это ожидаемая ситуация, что он может не найтись) - тогда надо возвращать null. Но тогда ты должен проверять результат функции на этот null всегда.
При этом функция все равно может выбросить исключение - ну например если id не числовой или если не удается соединиться с БД (PDO выбрасывает исключение PDOException в этом случае). То есть мы различаем штатные и нештатные ситуации таким образом.
Если ты уверен что передаваемый id правильный и не найтись пользователь не может - то надо использовать исключение и результат можно не проверять.
В случае когда id приходит от пользователя, конечно, он может оказаться неправильным. В этом случае код с if будет смотреться аккуратнее, чем отлов исключения (хотя некоторые используют исключения в этом случае). Если ты ловишь исключение, надо сделать свой класс исключений для этого случая и ловить только его.
> if (!isset($file)) {
Проверять на null надо через is_null или === null
>>1005418
Переменные, созданные внутри функции (локальные), не видны снаружи, и наоборот. Значения (не только переменные, но и числа, строки) надо передавать в функцию через аргументы в скобках, а возвращать из функции наружу через return.
У тебя есть return, но значение никуда не сохраняется, надо писать $x = func(...); чтобы его сохранить.
Ты же пишешь $a = mt_rand(1, 6), так же надо делать и тут.
>>1005336
>>1005325
Увы, не знаю, готового решения. Можно запилить что-то на основе http://php.net/manual/ru/class.messageformatter.php но если тебе нужен только один язык (русский), то может проще написать кодом.
В JS есть такие библиотеки: https://www.google.ru/search?q=js+time+library&btnG=Поиск&newwindow=1&gbv=1
Посмотри, может есть аналоги на PHP или можно сделать аналог самому и опубликовать на гитхабе.
То, что встроено в DateTime - это по сути интерфейс к древней сишной функции http://man7.org/linux/man-pages/man3/strftime.3.html и конечно никаких склонений она делать не умеет.
>>1005335
У него вопрос, какие есть готовые библиотеки или расширения, чтобы не изобретать то, что уже изобретено.
По крону запускать если нужно периодически, если одноразово, то руками в конcоли, можно через nohup, если долгая задача. Если запускать при загрузке сервера, то надо оформить скрипт как сервис в systemd.
>>1005185
Пусть будут спойлеры, что плохого-то.
>>1005145
Спам автоматизирован, боты просто ходят по интернету, ищут все формы, анализируют названия полей, подставляют в них подходящие данные и отправляют. Кроме капчи, можно проверять поддержку JS, а также делать поля-ловушки, которые не видны человеку, но которые бот может охотно заполнить. Особенно если оно называется name или email.
Что касается настройки сервера, советую читать мануалы а не статьи "как скопировать конфиг не поняв ни строчки". Я могу подсказать, если что-то непонятно.
FTP советую не использовать, он небезопасен и передает все в открытую, если есть SSH доступ, то можно использовать SCP и программы, работающие поверх SSH.
>>1005126
Кеширование это не простая вещь, в неумелых руках оно больше навредить может. Так как создает новые проблемы, вроде дублирования данных, устаревания данных и тд. Во многих случаях простая простановка индексов и оптимизация запросов может ускорить код так, что кеш будет не нужен.
> Вкратце — при каждом добавлении новой записи в кеш составные части ключа записываются в монго, давая тем самым возможность полностью воссоздать ключ кеша, зная только его часть.
Уже пошли усложнения какие-то. Он монго решил подключить только для хранения частей имен ключа кеша?
То, что описано в статье, в общей форме называется "тегирование ключей кеша" и это попытка решить проблему обновления кеша. Когда значение ключа кеша соответствует какой-то одной сущности (например, пользователю) то все просто - при обновлении записи пользователя в БД мы должны обновить/сбросить соответствующий ключ кеша с тем же id.
Это называется "зависимость", то есть ключ user:123 зависит от записи в таблице пользователей с id=123 и он устаревает когда эта запись меняется в БД.
Но когда запись в кеше формируется на основе нескольких записей из БД, ситуация становится сложнее. Ну например, мы решили хранить в кеше зачем-то 5 последних постов пользователя. Теперь ключ кеша зависит от всех этих постов - если пользователь удалит или переименует пост, мы должны обновить ключ кеша. Для этого мы проставляем ключу теги с id постов, например [post123, post243, post565, post777, post 888]. Теперь, когда пользователь обновляет пост с id 888, мы находим все зависящие от этого поста ключи кеша по тегу post888 и удаляем или обновляем их.
Разумеется, тегирование кеша не бесплатно - мы должны поддерживать какие-то структуры, хранящие список тегов, мы должны искать ключи по тегу, этих ключей может быть много. Очевидно что объем кеша увеличится. При неудачном проектировании у ключа может быть слишком много тегов или может быть слишком много тегов, соответствующих ключу, что вызовет проблемы с производительностью.
Есть и другие подходы (это я сам придумал) - можно хранить в ключе кеша даты модификации постов, от которых он зависит, а при чтении ключа сравнивать эти даты с реальным временем обновления постов, если они новее, чем сохранены в ключе, то он устарел и его нельзя использовать. По сути то же тегирование, только мы заменяем удаление устеревших ключей на проверку времени при чтении.
Лучший способ решения проблем с заивисимостями - избегать их и дублирования данных. Например, в случае с постами, можно хранить в кеше только id последних постов, а сами посты брать из БД - и зависимости исчезают. А еще лучше не использовать кеш, а оптимизировать запросы к БД. Так как кеш это дублирование, создание избыточных данных.
Проблема статьи в том, что автор приводит куски кода для решения какой-то его задачи, но не может сформулировать общий принцип работы кеша, рассказать про преимущества и недостатки тегирования кеша. Не пытается провести анализ, оценить, как это влияет на время доступ к кешу, его объем. Плохая статья. Он просто где-то услышал что кеширование как-то связано с высокой производительностью и тыкается наугад.
Хотя конечно кеширование в некоторых случаях дает дешевое решение проблемы, а недостатками можно пренебречь. Например, статический кеш страниц блога на вордпресс разгоняет его до небывалых скоростей.
> Это что реально оптимальный вариант кеширования динамики? Класть ключи в бд и делать класс-наследник от мемкашед?
Извини, но ты по моему понял еще меньше, чем автор статьи. При чем тут вообще наследование, тут речь о тегировании ключей кеша, а какой класс от чего наследуется, это не важно.
Если ты интересуешься кешированием, начни хотя бы отсюда
- http://lib.custis.ru/Кэширование_в_веб-приложениях_-_что,_где,_когда
- https://www.google.ru/search?q=кеширование+в+веб-приложениях&newwindow=1&gbv=1&sei=o4tAWcHbLsri6ATJtL34Bg
По крону запускать если нужно периодически, если одноразово, то руками в конcоли, можно через nohup, если долгая задача. Если запускать при загрузке сервера, то надо оформить скрипт как сервис в systemd.
>>1005185
Пусть будут спойлеры, что плохого-то.
>>1005145
Спам автоматизирован, боты просто ходят по интернету, ищут все формы, анализируют названия полей, подставляют в них подходящие данные и отправляют. Кроме капчи, можно проверять поддержку JS, а также делать поля-ловушки, которые не видны человеку, но которые бот может охотно заполнить. Особенно если оно называется name или email.
Что касается настройки сервера, советую читать мануалы а не статьи "как скопировать конфиг не поняв ни строчки". Я могу подсказать, если что-то непонятно.
FTP советую не использовать, он небезопасен и передает все в открытую, если есть SSH доступ, то можно использовать SCP и программы, работающие поверх SSH.
>>1005126
Кеширование это не простая вещь, в неумелых руках оно больше навредить может. Так как создает новые проблемы, вроде дублирования данных, устаревания данных и тд. Во многих случаях простая простановка индексов и оптимизация запросов может ускорить код так, что кеш будет не нужен.
> Вкратце — при каждом добавлении новой записи в кеш составные части ключа записываются в монго, давая тем самым возможность полностью воссоздать ключ кеша, зная только его часть.
Уже пошли усложнения какие-то. Он монго решил подключить только для хранения частей имен ключа кеша?
То, что описано в статье, в общей форме называется "тегирование ключей кеша" и это попытка решить проблему обновления кеша. Когда значение ключа кеша соответствует какой-то одной сущности (например, пользователю) то все просто - при обновлении записи пользователя в БД мы должны обновить/сбросить соответствующий ключ кеша с тем же id.
Это называется "зависимость", то есть ключ user:123 зависит от записи в таблице пользователей с id=123 и он устаревает когда эта запись меняется в БД.
Но когда запись в кеше формируется на основе нескольких записей из БД, ситуация становится сложнее. Ну например, мы решили хранить в кеше зачем-то 5 последних постов пользователя. Теперь ключ кеша зависит от всех этих постов - если пользователь удалит или переименует пост, мы должны обновить ключ кеша. Для этого мы проставляем ключу теги с id постов, например [post123, post243, post565, post777, post 888]. Теперь, когда пользователь обновляет пост с id 888, мы находим все зависящие от этого поста ключи кеша по тегу post888 и удаляем или обновляем их.
Разумеется, тегирование кеша не бесплатно - мы должны поддерживать какие-то структуры, хранящие список тегов, мы должны искать ключи по тегу, этих ключей может быть много. Очевидно что объем кеша увеличится. При неудачном проектировании у ключа может быть слишком много тегов или может быть слишком много тегов, соответствующих ключу, что вызовет проблемы с производительностью.
Есть и другие подходы (это я сам придумал) - можно хранить в ключе кеша даты модификации постов, от которых он зависит, а при чтении ключа сравнивать эти даты с реальным временем обновления постов, если они новее, чем сохранены в ключе, то он устарел и его нельзя использовать. По сути то же тегирование, только мы заменяем удаление устеревших ключей на проверку времени при чтении.
Лучший способ решения проблем с заивисимостями - избегать их и дублирования данных. Например, в случае с постами, можно хранить в кеше только id последних постов, а сами посты брать из БД - и зависимости исчезают. А еще лучше не использовать кеш, а оптимизировать запросы к БД. Так как кеш это дублирование, создание избыточных данных.
Проблема статьи в том, что автор приводит куски кода для решения какой-то его задачи, но не может сформулировать общий принцип работы кеша, рассказать про преимущества и недостатки тегирования кеша. Не пытается провести анализ, оценить, как это влияет на время доступ к кешу, его объем. Плохая статья. Он просто где-то услышал что кеширование как-то связано с высокой производительностью и тыкается наугад.
Хотя конечно кеширование в некоторых случаях дает дешевое решение проблемы, а недостатками можно пренебречь. Например, статический кеш страниц блога на вордпресс разгоняет его до небывалых скоростей.
> Это что реально оптимальный вариант кеширования динамики? Класть ключи в бд и делать класс-наследник от мемкашед?
Извини, но ты по моему понял еще меньше, чем автор статьи. При чем тут вообще наследование, тут речь о тегировании ключей кеша, а какой класс от чего наследуется, это не важно.
Если ты интересуешься кешированием, начни хотя бы отсюда
- http://lib.custis.ru/Кэширование_в_веб-приложениях_-_что,_где,_когда
- https://www.google.ru/search?q=кеширование+в+веб-приложениях&newwindow=1&gbv=1&sei=o4tAWcHbLsri6ATJtL34Bg
Ручками PHP, а если надо, то и Апач. В Оп посте есть частичная полезная информация.
>>1004895
> А что, например, нужно затачивать для этого?
Что-то я сейчас уже плохо соображаю, давай ты поломаешь голову. Подумай, какие особенности отличают набор сообщений чата от каких-то других данных, которые хранят в таблицах. Ну например: какие операции (запросы) с ними делаются, какой характерный размер данных, количество записей, скорость добавления записей, виды операций (добавление, изменение ,удаление).
Ну например, одна из проблем баз данных - число транзакций (изменений данных) в секунду. Она упирается в необходимость сбросить данные на диск и дождаться ответа о том, что данные физически записаны на поверхность диска (или в ячейки флеш-памяти, если это SSD), а не находятся где-то в кеше ОС или кеше жесткого диска. Только после получения ответа от ОС, что данные сброшены, СУБД сообщает клиенту (PHP коду) о завершении транзакции. Операция сброса на диск по моему назвыается sync() в Линуксе.
Причин, почему есть такое ограничение, много:
- обновление или вставка данных тянет за собой еще и обновление индексов в таблице
- обновление данных и индексов подразумевает много случайного доступа к кускам файлов, причем часто требуется сначала прочитать с диска сектор с данными, изменить кусочек и только потом записать обратно. Обновление только одного индекса при вставке одной записи может потребовать несколько таких операций
- база данных должна гарантирвать целостность таблиц и сохранность данных при любых условиях, вплоть до пропадания питания во время работы, по крайней мере, пока оборудование исправно.
Проще говоря, если ты выполняешь COMMIT и база данных ответила что он завершен (и PHP код выполняется дальше) то это значит что данные сохранены на диске надежно, что даже при пропадании питания они никуда не денутся. Если же питание прервется до завершения коммита, то в теории невыполненная транзакция просто должна будет откатиться, когда сервер включат и запустят сервис базы данных, и никакие данные не должны повредиться.
Случайный доступ относительно медленный. В тестах дисков всегда меряют 2 скорости - скорость последовательного чтения или записи большого файла и скорость случайного чтения 4Кб кусочков (соответствуют размеру сектора). И если первая скорость меряется десятками и сотнями мегабайт в секунду, то вторая намного меньше, особенно на магнитных (не SSD) дисках. В магнитных жествикх дисках головка может перемещаться только по радиусу (между дорожками). Магнитный диск крутится, и надо дождаться, пока нужный сектор окажется под головкой, потом перемещаться на другую дорожку, снова ждать. При скорости 15 000 обор/мин, мы имеем 250 оборотов в секунду и это ограничивает число операций случайного доступа, даже если предположить, что головка перемещается мгновенно.
Чтобы меньше ждать, СУБД кешируют сектора с данными в памяти - в MySQL это наызвается innoDB Pool cache. В этом случае нам не надо тратить время на чтение сектора, он уже есть в памяти. Но с записью ждать все равно приходится.
На SSD этой проблемы нет, но там есть свои особенности, замедляющие запись. Там для записи данных необходимо полностью очистить большой блок данных (сохранив данные из него) и записать в него изменнные данные. Контроллер SSD пытается поддерживать резерв пустых блоков и очищать их в фоне, но тут есть свои ограничения.
Информацию об этом можно поискать где-то тут на англ
- https://dev.mysql.com/doc/refman/5.7/en/optimization.html
- https://dev.mysql.com/doc/refman/5.7/en/innodb-storage-engine.html
Конечно, там можно подкрутить настройки и например отказаться от ожидания подтверждения того, что данные записаны на диск. Это увеличит скорость ценой снижения надежности.
Но например, система, которая бы вместо случайного доступа использовала последовательную запись в файл, могла бы достичь гораздо большей производительности. Проблема с индексами может решаться асинхронным обновлением индексов (при этом они чуть будут отставать от данных, но это иногда допустимо). Еще можно повысить производительность, если отказаться от выполнения sync при каждой вставке, ценой потери данных за последние N секунд при аварии.
Также, разработчики вконтакте выложили код некоторых своих хранилищ, и там есть интересная информация по теме:
- https://github.com/vk-com/kphp-kdb/blob/master/docs/ru/DBMS_Storage_Comparison.wiki
- https://github.com/vk-com/kphp-kdb/blob/master/docs/ru/KittenDB_Texts.wiki
Вконтакте использует специализированные хранилища на Си, которые оптимизированы на работу с разными типами данных (сообщения, лайки, фото, списки друзей, поиск). И конечно для максимальной скорости чтения они стараются все данные хранить в памяти.
Ручками PHP, а если надо, то и Апач. В Оп посте есть частичная полезная информация.
>>1004895
> А что, например, нужно затачивать для этого?
Что-то я сейчас уже плохо соображаю, давай ты поломаешь голову. Подумай, какие особенности отличают набор сообщений чата от каких-то других данных, которые хранят в таблицах. Ну например: какие операции (запросы) с ними делаются, какой характерный размер данных, количество записей, скорость добавления записей, виды операций (добавление, изменение ,удаление).
Ну например, одна из проблем баз данных - число транзакций (изменений данных) в секунду. Она упирается в необходимость сбросить данные на диск и дождаться ответа о том, что данные физически записаны на поверхность диска (или в ячейки флеш-памяти, если это SSD), а не находятся где-то в кеше ОС или кеше жесткого диска. Только после получения ответа от ОС, что данные сброшены, СУБД сообщает клиенту (PHP коду) о завершении транзакции. Операция сброса на диск по моему назвыается sync() в Линуксе.
Причин, почему есть такое ограничение, много:
- обновление или вставка данных тянет за собой еще и обновление индексов в таблице
- обновление данных и индексов подразумевает много случайного доступа к кускам файлов, причем часто требуется сначала прочитать с диска сектор с данными, изменить кусочек и только потом записать обратно. Обновление только одного индекса при вставке одной записи может потребовать несколько таких операций
- база данных должна гарантирвать целостность таблиц и сохранность данных при любых условиях, вплоть до пропадания питания во время работы, по крайней мере, пока оборудование исправно.
Проще говоря, если ты выполняешь COMMIT и база данных ответила что он завершен (и PHP код выполняется дальше) то это значит что данные сохранены на диске надежно, что даже при пропадании питания они никуда не денутся. Если же питание прервется до завершения коммита, то в теории невыполненная транзакция просто должна будет откатиться, когда сервер включат и запустят сервис базы данных, и никакие данные не должны повредиться.
Случайный доступ относительно медленный. В тестах дисков всегда меряют 2 скорости - скорость последовательного чтения или записи большого файла и скорость случайного чтения 4Кб кусочков (соответствуют размеру сектора). И если первая скорость меряется десятками и сотнями мегабайт в секунду, то вторая намного меньше, особенно на магнитных (не SSD) дисках. В магнитных жествикх дисках головка может перемещаться только по радиусу (между дорожками). Магнитный диск крутится, и надо дождаться, пока нужный сектор окажется под головкой, потом перемещаться на другую дорожку, снова ждать. При скорости 15 000 обор/мин, мы имеем 250 оборотов в секунду и это ограничивает число операций случайного доступа, даже если предположить, что головка перемещается мгновенно.
Чтобы меньше ждать, СУБД кешируют сектора с данными в памяти - в MySQL это наызвается innoDB Pool cache. В этом случае нам не надо тратить время на чтение сектора, он уже есть в памяти. Но с записью ждать все равно приходится.
На SSD этой проблемы нет, но там есть свои особенности, замедляющие запись. Там для записи данных необходимо полностью очистить большой блок данных (сохранив данные из него) и записать в него изменнные данные. Контроллер SSD пытается поддерживать резерв пустых блоков и очищать их в фоне, но тут есть свои ограничения.
Информацию об этом можно поискать где-то тут на англ
- https://dev.mysql.com/doc/refman/5.7/en/optimization.html
- https://dev.mysql.com/doc/refman/5.7/en/innodb-storage-engine.html
Конечно, там можно подкрутить настройки и например отказаться от ожидания подтверждения того, что данные записаны на диск. Это увеличит скорость ценой снижения надежности.
Но например, система, которая бы вместо случайного доступа использовала последовательную запись в файл, могла бы достичь гораздо большей производительности. Проблема с индексами может решаться асинхронным обновлением индексов (при этом они чуть будут отставать от данных, но это иногда допустимо). Еще можно повысить производительность, если отказаться от выполнения sync при каждой вставке, ценой потери данных за последние N секунд при аварии.
Также, разработчики вконтакте выложили код некоторых своих хранилищ, и там есть интересная информация по теме:
- https://github.com/vk-com/kphp-kdb/blob/master/docs/ru/DBMS_Storage_Comparison.wiki
- https://github.com/vk-com/kphp-kdb/blob/master/docs/ru/KittenDB_Texts.wiki
Вконтакте использует специализированные хранилища на Си, которые оптимизированы на работу с разными типами данных (сообщения, лайки, фото, списки друзей, поиск). И конечно для максимальной скорости чтения они стараются все данные хранить в памяти.
Пока выполнял студентов, столкнулся с тем, что мои классы, связанные с отображением, требуют много разнообразной и слабо связанной между собой информации. Например, на скриншоте под постом отображению страницы требуются:
список студентов $students,
текст для показа режима работы сайта пользователю,
массив (возможно, пустой) текстовых сообщений пользователю,
набор ссылок для перемещения по результатам данного поискового запроса.
Для передачи параметров есть следующие способы:
1) Передавать параметры в виде набора аргументов (так передаются параметры внутреннему методу контроллера на первом пике).
Преимущества: сразу видно, какие данные и какого типа нужны методу извне; легко получить контекстную помощь IDE, меньше грамматических ошибок.
Недостатки: любое изменение параметров метода заставит менять сигнатуры всех его вызовов (добавлять/убирать параметр); невозможно использовать метод при наследовании или объявить в абстрактном классе.
2) Передавать параметры в виде array или arrayObject. В т.ч. рекомендуемый ОПом DependencyContainer.
Преимущества: можно использовать одну сигнатуру для всех вызовов метода даже в случае, если в методе что-то сильно меняется.
Недостатки: полностью отсутствует контекстная помощь IDE; приходится подглядывать а) в справку метода, где нужно описать подробно все необходимые записи в array и их тип, б) в source класса, который я хочу передать в качестве параметра. Если я хочу передать в array объект StudentMapper, то IDE мне не поможет, придётся лезть в StudentMapper и смотреть, какие аргументы требуют его методы; вероятность ошибки возрастает стократно. Если это не дно, то где оно?
3) Передавать параметры в виде объекта класса, в котором отдельными методами захардкодены типы получаемых данных и объектов. То есть, нелюбимый ОПом паттерн Registry.
Преимущества: можно использовать одну сигнатуру; можно использовать контекстную справку IDE внутри метода, которому передан Registry.
Недостатки: если делать Registry один на несколько клиентских классов, то в него придётся пихать данные для всех этих классов, тем самым в Registry будет маловразумительная каша, и класс, который передаёт параметры клиенту через Registry не будет точно знать, какие данные клиент оттуда возьмёт. Следовательно, IDE не сможет подсказать, от каких зависимостей (dependencies) зависит клиент.
Если же писать Registry отдельно для каждого клиентского класса или метода, то нужно написать много дополнительных классов. Помощь IDE станет лучше, но не будет идеальной.
Итак, какой метод вы предпочтёте в такой ситуации и почему? Если можно изменить ситуацию, то как бы вы это сделали?
Пока выполнял студентов, столкнулся с тем, что мои классы, связанные с отображением, требуют много разнообразной и слабо связанной между собой информации. Например, на скриншоте под постом отображению страницы требуются:
список студентов $students,
текст для показа режима работы сайта пользователю,
массив (возможно, пустой) текстовых сообщений пользователю,
набор ссылок для перемещения по результатам данного поискового запроса.
Для передачи параметров есть следующие способы:
1) Передавать параметры в виде набора аргументов (так передаются параметры внутреннему методу контроллера на первом пике).
Преимущества: сразу видно, какие данные и какого типа нужны методу извне; легко получить контекстную помощь IDE, меньше грамматических ошибок.
Недостатки: любое изменение параметров метода заставит менять сигнатуры всех его вызовов (добавлять/убирать параметр); невозможно использовать метод при наследовании или объявить в абстрактном классе.
2) Передавать параметры в виде array или arrayObject. В т.ч. рекомендуемый ОПом DependencyContainer.
Преимущества: можно использовать одну сигнатуру для всех вызовов метода даже в случае, если в методе что-то сильно меняется.
Недостатки: полностью отсутствует контекстная помощь IDE; приходится подглядывать а) в справку метода, где нужно описать подробно все необходимые записи в array и их тип, б) в source класса, который я хочу передать в качестве параметра. Если я хочу передать в array объект StudentMapper, то IDE мне не поможет, придётся лезть в StudentMapper и смотреть, какие аргументы требуют его методы; вероятность ошибки возрастает стократно. Если это не дно, то где оно?
3) Передавать параметры в виде объекта класса, в котором отдельными методами захардкодены типы получаемых данных и объектов. То есть, нелюбимый ОПом паттерн Registry.
Преимущества: можно использовать одну сигнатуру; можно использовать контекстную справку IDE внутри метода, которому передан Registry.
Недостатки: если делать Registry один на несколько клиентских классов, то в него придётся пихать данные для всех этих классов, тем самым в Registry будет маловразумительная каша, и класс, который передаёт параметры клиенту через Registry не будет точно знать, какие данные клиент оттуда возьмёт. Следовательно, IDE не сможет подсказать, от каких зависимостей (dependencies) зависит клиент.
Если же писать Registry отдельно для каждого клиентского класса или метода, то нужно написать много дополнительных классов. Помощь IDE станет лучше, но не будет идеальной.
Итак, какой метод вы предпочтёте в такой ситуации и почему? Если можно изменить ситуацию, то как бы вы это сделали?
>Насчет MVC, там у тебя все запутано.
>Модель ведь по задумке должна хранить данные и реализовывать логику работы приложения, а не считать величину отступа где-то на странице. У тебя тут не MVC, а просто часть кода случайно помещена в классы с названиями Controller и Model.
>Вообще, по поводу JS, я конечно могу дать такой урок про MVC, но учти, что он довольно сложный https://github.com/codedokode/pasta/blob/master/js/minesweeper-mvc.md Может быть ты что-то из него захочешь взять. Только помни, что не стоит переусложнять код без необходимости.
https://github.com/codedokode/pasta/blob/master/js/minesweeper-mvc.md#Контроллер
>Контроллер отвечает за обработку команд пользователя.
То есть внутри него должны вызываться события? У меня не получилось так сделать https://jsfiddle.net/0ocuy4a9/
Ещё я заметил у себя такое переусложнение:
https://github.com/someApprentice/chat/blob/master/public/js/chat.js#L9
https://github.com/someApprentice/chat/blob/master/public/js/chat.js#L175-L181
, хотя можно было просто написать $.get(...).
Это тоже всё должно быть в контроллере, но я не понимаю как вынести события в контроллер. Нужно вызывать события где-то посреди документа и уже внутри них вызывать функции контроллера?
>В document.ready у тебя код идет стеной. Но лучше было бы, возможно, разбить экран на компоненты (список контактов, диалог и тд), и каждый реализовать в своем классе.
Если события должны будут быть вне классов, то если разбить код на компоненты, события относящиеся к этим компонентам должны быть сразу под ними?
Не опытный. Тоже пользовался разными подходами, третий пункт сразу отмел, поскольку ёбнешься с таким количеством кода, который ещё и будет нарастать со временем. Пишу пока просто в Нотепаде, но даже так и первый и второй способы одинаково ущербны. НО! первый мне кажется более правильным с точки зрения программирования вообще. Я например, часто забываю положить какие-то данные в массив, или запятую поставить, или ставлю лишнюю запятую. Не говоря уже о том, что получаются раздражающие ситуации вида пикрклкйтед.
>Спам автоматизирован, боты просто ходят по интернету, ищут все формы, анализируют названия полей, подставляют в них подходящие данные и отправляют. Кроме капчи, можно проверять поддержку JS, а также делать поля-ловушки, которые не видны человеку, но которые бот может охотно заполнить. Особенно если оно называется name или email.
>
>FTP советую не использовать, он небезопасен и передает все в открытую, если есть SSH доступ, то можно использовать SCP и программы, работающие поверх SSH.
На счёт ботов, наткнулся на сайты, где есть список всех зарегистрированных доменов по датам, там был и мой. Хуй знает откуда они их берут. Прикрутил капчу и больше не спамят.
SCP погуглю
Моё увожение.
>У меня не получилось так сделать https://jsfiddle.net/0ocuy4a9/
Забыл создать объект https://jsfiddle.net/0ocuy4a9/1/
Ответьте лишь, события должны вызываться в "методах" или в "конструкторе" объекта?
он использовал незнакомые мне (действия?)
Мб $reply нужно считать внутри for, а $i объявить в аргументах? Или ты что-то другое пытаешься сделать\понять?
Да внутри...спасибо
Команда $reply = $i x $i выполняется только один раз, перед началом цикла. Но тебе ведь наверно надо ответ вычислять на каждом шаге заново, значит это действие лучше перенести в тело цикла.
А вместо этой команды логично поставить $i = 1, она должна только в начале и выполняться.
Всегда обсираюсь, когда приходят такие ребята, а через пару месяцев они уже ебашат студентов, а ты сидишь как сыч и деградируешь, плак, плак
INSERT INTO comments (..., tree) VALUES (..., '123.' + nextval('comments_sequence_id'));
Лучше использовать 3v4l.org
Но да, второй вариант действительно проще использовать.
Даже не представляю зачем тот анон так раздул этот вопрос.
Почему лучше? Какие аргументы?
Как ты исправишь отсутствие контекстной помощи ide при добыче объектов из array? inb4 не использовать помощь
Аргументы, аргументы.
http://ideone.com/uWAvxM
держите, накатал стену блядь (еще раз, но в два раза ужатее), которую никто в итоге не осилит или не поймет
Тоже мне стена, у меня хелоуворлды многословнее!!!!
Ну а так зря ты распинался, во-первых таки да, поддерживается по дефолту в фреймворках, а значит сразу заметят, как только работать начнут. Ну а во вторых он про вьюху кажется спрашивал, там есть еще некоторые плюсы в сторону этого подхода именно.
Это прекрасно смотрится, когда речь идет о передаче простых данных: строк, чисел. Но представь, что нужно передать объект. А потом, внутри метода, которому ты передал объект, что-то получить от него. Ты передал объект, но редактор внутри кода не может знать, какого он типа. Не может тебя поправить, подсказать. Тебе придется вручную по буковкам, как в стандартном нотпаде, писать вызов каждого метода, который тебе от объекта нужен, и каждый аргумент проверять по справке этого объекта. Тут что-то не так. Что ты на это скажешь?
Нет, там чел спрашивал про передачи аргументов в методы классов (да даже в обычные функции когда пишешь учебные задачи процедурно). Причем по нему создается впечатление, что теории в виде всяких паттернов он нахватался, а реального кода чет не успел понаписать горы.
>>1005807
>Но представь, что нужно передать объект.
ну и передаешь объект в чем проблема.
Если у тебя несколько объектов одного класса то передаешь массив объектов. С ними инфа 99% будет удобнее работать через foreach
> но редактор внутри кода не может знать
Почему я уже в третьем посте за сегодня читаю какой-то бред про редактор? Ты сам себе программист, или придаток к редактору? Не можешь отследить что у тебя в коде откуда растет?
>Что ты на это скажешь?
Скажу, что:
> Тут что-то не так.
$text[2] - это не второй символ, а третий (отсчет идет с нуля) байт. Латинские буквы помещаются в один байт, а русские занимают два, и русскую букву ты так точно не достанешь.
- https://github.com/codedokode/pasta/blob/master/php/strings-utf8.md
- https://github.com/codedokode/pasta/blob/master/cs/strings.md
Потому в моем учебнике такому и не учат.
Черт! Как же тогда доставать символы с конца?
Можешь намекнуть, а не писать решение что бы я сам допер?
function palindrom_test ($word) {
if (reverse($word)==$word){
return true;
}
return false;
}
Что не так?
Так вот как? Надо перевернуть слово, а не сравнивать по буквам? Тогда как? Записать слово в массив, затем перевернуть массив и сравнить?
Во-первых, если тебе нужен N-й символ с конца, а длина строки равна L, то из этих данных можно определить позицию символа от начала строки. Возьми листочек в клеточку или таблицу в екселе и посчитай.
Во-вторых, если в mb_substr передать отрицательное число, то это воспринимается как отступ от кона строки: http://php.net/manual/ru/function.mb-substr.php
>>1006052
В php нет готовой функции reverse наверно.
>Можешь намекнуть, а не писать решение что бы я сам допер?
> А я бля сидел неделю
Ой да кому ты пиздишь, мамкино ленивое хуйло))))
https://3v4l.org/T5HLN
Тут наверно удобнее просто выбрать нужные данные в PHP и там с ними работать, чем пытаться все сделать одним сложным запросом.
Про склеивание строк и строковые функции можно прочесть тут https://postgrespro.ru/docs/postgrespro/9.6/functions-string
>>1005807
Во-первых, не надо полагаться на автодополнение, надо уметь писать все самому. И не стоит код специально затачивать под IDE. Во-вторых, можно указать на тип переменной 2 способами:
- с помощью тайп-хинтов (их понимают все IDE)
- с помощью phpDoc комментариев (стоит проверить мануал твоей IDE, чтобы узнать поддерживаемые возможности).
>>1005788
Твой код с массивами имеет серьезный недостаток: никак не документируется, какие поля должны быть в массиве, в каком формате должны быть значения (например, время - строкой или в виде числа unix timestamp?). Никак не проверяется наличие каких-то лишних полей. Со временем по мере развития проекта обнаружится, что функция вызывается с разными наборами полей, и приходится в ней писать условия, проверяющие, есть ли там какое-то поле, прежде чем с ним что-то сделать. Ну и вообще сложно понять, какой именно массив надо передать в функцию.
Потому вместо громоздких массивов иногда лучше потратить время и сделать класс, и передавать в функцию объект. На объект можно ставить тайп-хинты, известно, какие в нем поля, и к каждому полю можно приписать комментарий с типом, а еще можно добавлять в объект методы.
Вот урок по теме https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
>>1005757
Я пишу ответ в редакторе (sublime) и он не теряется даже если пропадет питание (хотя у меня ноутбук и есть время на сохранение).
Идея такая: вот у нас есть страница, или кусок страницы, и мы хотим как-то добавить ему интерактивности с использованием MVC. Модель, понятно, с этим куском страницы никак не связана, с ним работают только контроллеры и вью. И тут уже появляются разные варианты, кто (из пары представление-контроллер) будет ведущим, кто занимается инициализацией, создаем ли мы DOM программно или навешиваемся на уже существующий, если создаем, то кто это делает, и тд.
Я сейчас приведу совсем простые примеры, которые вообще не используют модель, только вью и контроллер.
По идее задача контроллера именно обрабатывать события, а задача представления - отображать данные. Инициализацию страницы с установкой обработчиков событий можно возложить на контроллер:
class Controller
{
init()
{
// инициализируем представление, задав обработчик для кнопки
$('#some-button').click(this.handleSomeButtonClick.bind(this));
}
handleSomeButtonClick(e)
{
...
$('#some-field').text('Hello');
}
}
Но тут есть проблемка, что у нас контроллер занимается работой с DOM (поиском кнопки), а можно убрать все работу с DOM в представление (так будут лучше разделены ответственности). Например, так:
init()
{
// Вью можно и не создавать, а передавать снаружи, но не очень
// понятно, в чем выгода
this.view = new SomePageView(...);
this.view.findSomeButton().click(this.handleClick.bind(this));
}
handleClick()
{
this.view.findSomeField().text('Hello');
}
В этом варианте только представление имеет ссылки на DOM элементы страницы, получается понятное разделение ответственности: вью управляет элементами DOM, а контроллер вызывается при каких-то действиях пользователя. В этом варианте вью не зависит от контроллера и не знает о нем, потому его можно использовать и без контроллера. Или можно сделать несколько разных вью, например, отображающие данные в каком-то другом виде.
Можно усилить инкапсуляцию так, чтобы вью не выдавал ссылки на элементы DOM наружу:
init()
{
this.view.setButtonClickHandler(this.handleClick.bind(this));
}
handleClick()
{
this.view.setSomeFieldText('Hello');
}
Это позволит нам например сделать консольный вью, выводящий информацию в консоль, а не на страницу, и подсунуть его контроллеру.
Далее, можно сделать главным не контроллер, а представление, так что инициализацией занимается оно. Теперь у нас контроллер знает про вью, а вью про контроллер, они зависят друг от друга:
class SomeView
{
// Контроллер можно передавать снаружи, а не создавать
init(controller)
{
controller.setView(this);
this.findSomeButton().click(controller.handleClick.bind(controller));
}
setSomeFieldText(...) { ... }
}
Наконец, есть еще варианты, когда контроллер и представление объединяют в один класс (React например предлагает такую возможность).
Есть варианты, когда инициализация делается снаружи. То есть мы создаем вью, создаем контроллер, а затем как-то связываем их. Но я не вижу, в чем тут выгода, выносить часть кода из класса наружу. Ведь эти вью и контроллер зависимы друг от друга и как-то по-другому их связать все равно нельзя.
Если добавить модель, то конечно ситуация становится сложнее, можно попытаться прочитать этот большой урок https://github.com/codedokode/pasta/blob/master/js/minesweeper-mvc.md
Хочу предупредить также, что разделение ответсвенности может привести к увеличению объема кода.
Предлагаю тебе самому подумать, чем отличаются эти варианты. В любом случае, нужно определиться, кто будет за что отвечать, чем заниматься.
В твоем простом случае с 3 дивами я бы просто сделал один ВьюКонтроллер.
>>1005631
Ой, код неудачный. Во-первых, нет никаких проверок и валидаций. Во-вторых, название класса работы с БД, таблицы и полей абсолютно нечитаемое. В-третьих, использование больших массивов приводит к более запутанному коду, может быть тут стоило бы использовать класс для представления сущности, хотя это увеличит объем кода.
>>1005629
>>Контроллер отвечает за обработку команд пользователя.
> То есть внутри него должны вызываться события?
Это значит, что когда пользователь жмет на кнопку, то функция или метод, которая обрабатывает это событие, является контроллером. То есть контроллер вызывается при каких-то командах пользователя и выполняет эти команды.
> Ещё я заметил у себя такое переусложнение
У тебя пока там все очень сумбурно, и по моему это пока не MVC. Ты должен сначала определиться, кто за что отвечает, а у тебя там просто код без всякой логики раскидан.
Ну например у тебя перемешан поиск элеиментов с логикой обработки событий и туда же вставлен код отправки аякса-запросов, по всему коду раскиданы конструкции вроде $('.message:first-child'). Это, по моему мнению, плохой стиль. Гораздо удобнее было бы вынести поиск элементов куда-то отдельно, в метод или сохранить ссылки на них в переменные. И вынести взаимодействие с АПИ тоже отдельно. У тебя нет никакого разделения, просто код идет стеной, как у начинающих.
Ну вот простой пример, показывающий, что у тебя код никак не разделен на части. Допустим, я хочу программно отправить сообщение в чат - как это сделать? Код отправки сообщения не выделен отдельно, а перемешан с кодом обработки событий. Или я хочу программно добавить сообщение на экран - это невозможно. Или получить список выделенных контактов. Нет такой функции, которую я бы мог вызвать.
Ну и подсчет высоты сообщений - это явно что-то неправильное. Это нужно для выравнивания сообщений вертикально? Неужели это нельзя было сделать средствами CSS? Задание размеров и расположения элементов через JS работает плохо, может влиять на производительность и работать с задержками.
> хотя можно было просто написать $.get(...).
Не забудь только прочитать мой урок про работу с аяксом.
> Если события должны будут быть вне классов, то если разбить код на компоненты, события относящиеся к этим компонентам должны быть сразу под ними?
Идея компонента в том, что он не зависит от каких-то других элементов. Условно говоря, если есть компонент "список контактов", то мы можем просто на пустой странице его отобразить, и как-то им управлять. И он не требует наличия рядом с ним компонента "список сообщений".
То есть мы разбиваем интерфейс на компоненты, и делаем их максимально независимыми друг от друга. Это упрощает код, так как мы можем рассматривать компонент отдельно от других. И мы можем его тестировать отдельно от других. А когда разделения нет, нам приходится анализировать большой объем кода и можно запутаться.
Это компоненты еще иногда называют виджеты. В архитектуре MVC у каждого виджета будут свои контроллер, вью и ViewModel (ViewModel - это модель, управляющая не данными и бизнес-логикой приложения, а данными отдельно взятого компонента).
Также, разбиение на компоненты позволяет легко перестраивать интерфейс, например, перемещать или скрывать часть компонентов.
Компоненты используются в программировании графических интерфейсов в Windows, на андроид. Ну например, в Андроиде есть готовые компоненты вроде "поле ввода", "выпадающий список", "поле выбора даты", и тд.
Есть еще так называемые "веб-компоненты", это немного другое, это возможность создавать новые HTML теги, при вставке которых в страницу вставляется соответствующий компонент. Но компоненты можно реализовать и без них.
Уточняй, если что-то непонятно.
Идея такая: вот у нас есть страница, или кусок страницы, и мы хотим как-то добавить ему интерактивности с использованием MVC. Модель, понятно, с этим куском страницы никак не связана, с ним работают только контроллеры и вью. И тут уже появляются разные варианты, кто (из пары представление-контроллер) будет ведущим, кто занимается инициализацией, создаем ли мы DOM программно или навешиваемся на уже существующий, если создаем, то кто это делает, и тд.
Я сейчас приведу совсем простые примеры, которые вообще не используют модель, только вью и контроллер.
По идее задача контроллера именно обрабатывать события, а задача представления - отображать данные. Инициализацию страницы с установкой обработчиков событий можно возложить на контроллер:
class Controller
{
init()
{
// инициализируем представление, задав обработчик для кнопки
$('#some-button').click(this.handleSomeButtonClick.bind(this));
}
handleSomeButtonClick(e)
{
...
$('#some-field').text('Hello');
}
}
Но тут есть проблемка, что у нас контроллер занимается работой с DOM (поиском кнопки), а можно убрать все работу с DOM в представление (так будут лучше разделены ответственности). Например, так:
init()
{
// Вью можно и не создавать, а передавать снаружи, но не очень
// понятно, в чем выгода
this.view = new SomePageView(...);
this.view.findSomeButton().click(this.handleClick.bind(this));
}
handleClick()
{
this.view.findSomeField().text('Hello');
}
В этом варианте только представление имеет ссылки на DOM элементы страницы, получается понятное разделение ответственности: вью управляет элементами DOM, а контроллер вызывается при каких-то действиях пользователя. В этом варианте вью не зависит от контроллера и не знает о нем, потому его можно использовать и без контроллера. Или можно сделать несколько разных вью, например, отображающие данные в каком-то другом виде.
Можно усилить инкапсуляцию так, чтобы вью не выдавал ссылки на элементы DOM наружу:
init()
{
this.view.setButtonClickHandler(this.handleClick.bind(this));
}
handleClick()
{
this.view.setSomeFieldText('Hello');
}
Это позволит нам например сделать консольный вью, выводящий информацию в консоль, а не на страницу, и подсунуть его контроллеру.
Далее, можно сделать главным не контроллер, а представление, так что инициализацией занимается оно. Теперь у нас контроллер знает про вью, а вью про контроллер, они зависят друг от друга:
class SomeView
{
// Контроллер можно передавать снаружи, а не создавать
init(controller)
{
controller.setView(this);
this.findSomeButton().click(controller.handleClick.bind(controller));
}
setSomeFieldText(...) { ... }
}
Наконец, есть еще варианты, когда контроллер и представление объединяют в один класс (React например предлагает такую возможность).
Есть варианты, когда инициализация делается снаружи. То есть мы создаем вью, создаем контроллер, а затем как-то связываем их. Но я не вижу, в чем тут выгода, выносить часть кода из класса наружу. Ведь эти вью и контроллер зависимы друг от друга и как-то по-другому их связать все равно нельзя.
Если добавить модель, то конечно ситуация становится сложнее, можно попытаться прочитать этот большой урок https://github.com/codedokode/pasta/blob/master/js/minesweeper-mvc.md
Хочу предупредить также, что разделение ответсвенности может привести к увеличению объема кода.
Предлагаю тебе самому подумать, чем отличаются эти варианты. В любом случае, нужно определиться, кто будет за что отвечать, чем заниматься.
В твоем простом случае с 3 дивами я бы просто сделал один ВьюКонтроллер.
>>1005631
Ой, код неудачный. Во-первых, нет никаких проверок и валидаций. Во-вторых, название класса работы с БД, таблицы и полей абсолютно нечитаемое. В-третьих, использование больших массивов приводит к более запутанному коду, может быть тут стоило бы использовать класс для представления сущности, хотя это увеличит объем кода.
>>1005629
>>Контроллер отвечает за обработку команд пользователя.
> То есть внутри него должны вызываться события?
Это значит, что когда пользователь жмет на кнопку, то функция или метод, которая обрабатывает это событие, является контроллером. То есть контроллер вызывается при каких-то командах пользователя и выполняет эти команды.
> Ещё я заметил у себя такое переусложнение
У тебя пока там все очень сумбурно, и по моему это пока не MVC. Ты должен сначала определиться, кто за что отвечает, а у тебя там просто код без всякой логики раскидан.
Ну например у тебя перемешан поиск элеиментов с логикой обработки событий и туда же вставлен код отправки аякса-запросов, по всему коду раскиданы конструкции вроде $('.message:first-child'). Это, по моему мнению, плохой стиль. Гораздо удобнее было бы вынести поиск элементов куда-то отдельно, в метод или сохранить ссылки на них в переменные. И вынести взаимодействие с АПИ тоже отдельно. У тебя нет никакого разделения, просто код идет стеной, как у начинающих.
Ну вот простой пример, показывающий, что у тебя код никак не разделен на части. Допустим, я хочу программно отправить сообщение в чат - как это сделать? Код отправки сообщения не выделен отдельно, а перемешан с кодом обработки событий. Или я хочу программно добавить сообщение на экран - это невозможно. Или получить список выделенных контактов. Нет такой функции, которую я бы мог вызвать.
Ну и подсчет высоты сообщений - это явно что-то неправильное. Это нужно для выравнивания сообщений вертикально? Неужели это нельзя было сделать средствами CSS? Задание размеров и расположения элементов через JS работает плохо, может влиять на производительность и работать с задержками.
> хотя можно было просто написать $.get(...).
Не забудь только прочитать мой урок про работу с аяксом.
> Если события должны будут быть вне классов, то если разбить код на компоненты, события относящиеся к этим компонентам должны быть сразу под ними?
Идея компонента в том, что он не зависит от каких-то других элементов. Условно говоря, если есть компонент "список контактов", то мы можем просто на пустой странице его отобразить, и как-то им управлять. И он не требует наличия рядом с ним компонента "список сообщений".
То есть мы разбиваем интерфейс на компоненты, и делаем их максимально независимыми друг от друга. Это упрощает код, так как мы можем рассматривать компонент отдельно от других. И мы можем его тестировать отдельно от других. А когда разделения нет, нам приходится анализировать большой объем кода и можно запутаться.
Это компоненты еще иногда называют виджеты. В архитектуре MVC у каждого виджета будут свои контроллер, вью и ViewModel (ViewModel - это модель, управляющая не данными и бизнес-логикой приложения, а данными отдельно взятого компонента).
Также, разбиение на компоненты позволяет легко перестраивать интерфейс, например, перемещать или скрывать часть компонентов.
Компоненты используются в программировании графических интерфейсов в Windows, на андроид. Ну например, в Андроиде есть готовые компоненты вроде "поле ввода", "выпадающий список", "поле выбора даты", и тд.
Есть еще так называемые "веб-компоненты", это немного другое, это возможность создавать новые HTML теги, при вставке которых в страницу вставляется соответствующий компонент. Но компоненты можно реализовать и без них.
Уточняй, если что-то непонятно.
Обычно при выводе шаблона используют второй подход (метод вида render($template, array $args)). Так например работает шаблонизатор Twig. Так приходится писать меньше кода. Но вообще, другие подходы тоже имеют право на жизнь.
Ну например, мы бы могли завернуть шаблон в функцию и описать все параметры и их типы:
<?php
function showIndexPage($a, $b, $c, Something $something) {
?>
... код шаблона...
Это выглядит не очень красиво, но зато тут описаны все параметры шаблона и их типы. Разумеется, можно придумать какой-то более аккуратный синтаксис и генерировать их него обычные функции.
И также, возможен и вариант с передачей одного объекта в шаблон. Такой объект можно называть ViewModel, например StudentListViewModel. То есть это моделька, отвечающая только за хранение или получение данных для вывода этого шаблона. Тут преимущество тоже в том, что явно задан список полей, а также можно добавлять методы.
Можно в этот класс добавить метод render, который будет вызывать шаблон так, что объект будет доступен через $this. Но тогда это будет уже не ViewModel, а View. Что-то похожее было в ZF1, там был объект Zend_View.
Что касается кода с третьей картинки, то в нем все явно смешано в кучу. Я вижу, что ты передаешь Registry в конструктор View, это явно плохая идея, так как в шаблон лучше передавать какие-то конкретные данные, а не все объекты-сервисы. Зачем в шаблоне объект PDO? Он там точно использоваться не должен. И data mapper в шаблоне не нужен. Не забывай, что задача View - отображать переданные ему данные, а не самостоятельно их получать.
> 1) Передавать параметры в виде набора аргументов
Вполне нормальный способ.
> Недостатки: любое изменение параметров метода заставит менять сигнатуры всех его вызовов (добавлять/убирать параметр);
Это плюс, если ты добавил в шаблонен новый параметр, то гарантированно придется найти все вызовы шаблона и добавить передачу этого параметра, иначе вылетит ошибка. В варианте с массивом ошибку труднее найти, так как список параметров не описан нигде.
> невозможно использовать метод при наследовании или объявить в абстрактном классе.
Не понял, почему. Думаю, что можно.
> Передавать параметры в виде array или arrayObject.
Можно и так. Плюс в том, что меньше кода писать, минус в том что труднее разобраться. Но в Твиге используется именно такой подход. Обычно каждый шаблон вызывается ровно из одного контроллера, и название шаблона соответствует названию контроллера, так что найти место вызова нетрудно.
> В т.ч. рекомендуемый ОПом DependencyContainer.
Это совсем не то. DI Container используется для получения сервисов (например дата мапперов, валидаторов). В шаблон его передавать незачем, и эти сервисы там не нужны. Ты что-то путаешь.
> 3) Передавать параметры в виде объекта класса, в котором отдельными методами захардкодены типы получаемых данных и объектов. То есть, нелюбимый ОПом паттерн Registry.
Нет, если это класс, который написан специально для данного шаблона, то правильнее это назвать ViewModel.
- http://design-pattern.ru/patterns/registry.html
- http://designpatternsphp.readthedocs.io/ru/latest/Structural/Registry/README.html
Обычно Registry называют глобально доступный реестр (не написанный специально под конкретный шаблон). Хотя иногда Registry может быть расположен внутри какого-то класса и исплоьзоваться только им. Например, в Зенде были всякие реестры плагинов, хелперов, обработчиков и еще чего-то.
Такие понятия как DI container, registry - они относятся не к передаче данных в шаблон, а к поиску сервисов, зависимостей для класса. Конечно, View не должен обращаться ни к DI Container, ни к Registry.
Задавай уточняющие вопросы, если что-то еще непонятно.
Обычно при выводе шаблона используют второй подход (метод вида render($template, array $args)). Так например работает шаблонизатор Twig. Так приходится писать меньше кода. Но вообще, другие подходы тоже имеют право на жизнь.
Ну например, мы бы могли завернуть шаблон в функцию и описать все параметры и их типы:
<?php
function showIndexPage($a, $b, $c, Something $something) {
?>
... код шаблона...
Это выглядит не очень красиво, но зато тут описаны все параметры шаблона и их типы. Разумеется, можно придумать какой-то более аккуратный синтаксис и генерировать их него обычные функции.
И также, возможен и вариант с передачей одного объекта в шаблон. Такой объект можно называть ViewModel, например StudentListViewModel. То есть это моделька, отвечающая только за хранение или получение данных для вывода этого шаблона. Тут преимущество тоже в том, что явно задан список полей, а также можно добавлять методы.
Можно в этот класс добавить метод render, который будет вызывать шаблон так, что объект будет доступен через $this. Но тогда это будет уже не ViewModel, а View. Что-то похожее было в ZF1, там был объект Zend_View.
Что касается кода с третьей картинки, то в нем все явно смешано в кучу. Я вижу, что ты передаешь Registry в конструктор View, это явно плохая идея, так как в шаблон лучше передавать какие-то конкретные данные, а не все объекты-сервисы. Зачем в шаблоне объект PDO? Он там точно использоваться не должен. И data mapper в шаблоне не нужен. Не забывай, что задача View - отображать переданные ему данные, а не самостоятельно их получать.
> 1) Передавать параметры в виде набора аргументов
Вполне нормальный способ.
> Недостатки: любое изменение параметров метода заставит менять сигнатуры всех его вызовов (добавлять/убирать параметр);
Это плюс, если ты добавил в шаблонен новый параметр, то гарантированно придется найти все вызовы шаблона и добавить передачу этого параметра, иначе вылетит ошибка. В варианте с массивом ошибку труднее найти, так как список параметров не описан нигде.
> невозможно использовать метод при наследовании или объявить в абстрактном классе.
Не понял, почему. Думаю, что можно.
> Передавать параметры в виде array или arrayObject.
Можно и так. Плюс в том, что меньше кода писать, минус в том что труднее разобраться. Но в Твиге используется именно такой подход. Обычно каждый шаблон вызывается ровно из одного контроллера, и название шаблона соответствует названию контроллера, так что найти место вызова нетрудно.
> В т.ч. рекомендуемый ОПом DependencyContainer.
Это совсем не то. DI Container используется для получения сервисов (например дата мапперов, валидаторов). В шаблон его передавать незачем, и эти сервисы там не нужны. Ты что-то путаешь.
> 3) Передавать параметры в виде объекта класса, в котором отдельными методами захардкодены типы получаемых данных и объектов. То есть, нелюбимый ОПом паттерн Registry.
Нет, если это класс, который написан специально для данного шаблона, то правильнее это назвать ViewModel.
- http://design-pattern.ru/patterns/registry.html
- http://designpatternsphp.readthedocs.io/ru/latest/Structural/Registry/README.html
Обычно Registry называют глобально доступный реестр (не написанный специально под конкретный шаблон). Хотя иногда Registry может быть расположен внутри какого-то класса и исплоьзоваться только им. Например, в Зенде были всякие реестры плагинов, хелперов, обработчиков и еще чего-то.
Такие понятия как DI container, registry - они относятся не к передаче данных в шаблон, а к поиску сервисов, зависимостей для класса. Конечно, View не должен обращаться ни к DI Container, ни к Registry.
Задавай уточняющие вопросы, если что-то еще непонятно.
Валидация выше по коду. При именовании SQL ориентировался на статью http://citforum.ru/database/articles/naming_rule/
, если вникнуть, всё просто запоминается. А как бы ты предложил на моем примере назвать всё, просто вот из интереса?
А вот как раз третье это да. Нужно больше сущностей. И в моем случае это даже снизит объем кода!
>При именовании SQL ориентировался на статью
>Citforum
>Королевство Дельфи
Не делай так больше.
Возьми стандарт у Алана Бьюли и Тома Кайта, они спокойно писали «... WHERE street_id = street.id AND ...» и это никому не мешало.
Названия переменных плохие, и код наверно стоило бы разбить на функции.
>>1005169
> не смог сделать на русском, что бы большие буквы были в начале предложения и маленькие в конце.
Читай урок https://github.com/codedokode/pasta/blob/master/php/strings-utf8.md
В моем учебнике тоже описаны функции (mb_...), которые надо использовать.
> http://ideone.com/32QpwQ йода
> (\\.)|(\\!)|(\\?)
Вместо этого лучше использовать квадратные скобки. Ну и круглые скобки тут не требуются, без них будет то же самое.
> foreach ($arrayOffers as $key=>$value){
Плохие названия переменных. $key не нужна, а $value надо было назвать $sentence (предложение). array писать в названии переменной не надо, достаточно множественного числа.
> [\s]+
Квадратные скобки не требуются.
Ошибки надо исправить:
> PHP Notice: Undefined variable: editedText in /home/z0nZFe/prog.php on line 14
> PHP Notice: Undefined variable: editedTextFull in /home/z0nZFe/prog.php on line 18
> $editedText = strtolower($editedText);
Этой строчкой ты многократно обрабатываешь уже обработанный текст, это неэффективно.
Названия переменных довольно неудачные.
> http://ideone.com/XnwxXg сумма прописью
> $millions = array(
> 0 => 'миллионов',
> 11 => 'миллионов',
> 1 => 'миллион',
Ну тут одно и то же повторяется. Этот массив не нужен, так как в русском слово может быть в одной из 3 форм в зависимости от 2 последних цифр числа и надо просто написать несколько ифов, выбирающих 1 из 3 вариантов.
Код у тебя написан длинной стеной. Такой код трудно читать и трудно использовать. Надо разбить его на отдельные функции.
В коде явно скопированы куски с небольшими изменениями. Их надо вынести в функцию, чтобы дублирования не было.
В нынешнем состоянии проверять код тяжело и я не вижу смысла это делать. Разбей его на более простые функции. Если не понимаешь как, попроси подсказки.
Вдобавок внизу там ошибки:
> PHP Notice: Undefined variable: text in /home/XTUN1t/prog.php on line 118
> PHP Notice: Undefined variable: numberOn in /home/XTUN1t/prog.php on line 139
> http://ideone.com/9fc5tF калькулятор
Не хватает вывода окончательного ответа.
> http://ideone.com/8hUwnm считалка
В общем-то алгоритм верный, но можно было обойтись без перестройки массива. Можно было обходить массив в цикле, увеличивая счетчик на каждом шаге и удаляя текущий элеимент, когда счетчик доходит до $skip.
> http://ideone.com/MSiND0 вертикальный текст
$array, $value - неудачные названия переменных.
Определить максимальную длину строки можно с помощью array_map и max.
> $longHigh = ($longHigh>$long)? $longHigh : $long;
Тут стоило использовать max()/min()
> PHP Notice: Undefined variable: longHigh in /home/09pNsX/prog.php on line 13
Ошибка
> ($array as $key => &$value){
Зачем тут & ?
Ну и код отформатирован не по стандарту, смотри второй пост треда.
Названия переменных плохие, и код наверно стоило бы разбить на функции.
>>1005169
> не смог сделать на русском, что бы большие буквы были в начале предложения и маленькие в конце.
Читай урок https://github.com/codedokode/pasta/blob/master/php/strings-utf8.md
В моем учебнике тоже описаны функции (mb_...), которые надо использовать.
> http://ideone.com/32QpwQ йода
> (\\.)|(\\!)|(\\?)
Вместо этого лучше использовать квадратные скобки. Ну и круглые скобки тут не требуются, без них будет то же самое.
> foreach ($arrayOffers as $key=>$value){
Плохие названия переменных. $key не нужна, а $value надо было назвать $sentence (предложение). array писать в названии переменной не надо, достаточно множественного числа.
> [\s]+
Квадратные скобки не требуются.
Ошибки надо исправить:
> PHP Notice: Undefined variable: editedText in /home/z0nZFe/prog.php on line 14
> PHP Notice: Undefined variable: editedTextFull in /home/z0nZFe/prog.php on line 18
> $editedText = strtolower($editedText);
Этой строчкой ты многократно обрабатываешь уже обработанный текст, это неэффективно.
Названия переменных довольно неудачные.
> http://ideone.com/XnwxXg сумма прописью
> $millions = array(
> 0 => 'миллионов',
> 11 => 'миллионов',
> 1 => 'миллион',
Ну тут одно и то же повторяется. Этот массив не нужен, так как в русском слово может быть в одной из 3 форм в зависимости от 2 последних цифр числа и надо просто написать несколько ифов, выбирающих 1 из 3 вариантов.
Код у тебя написан длинной стеной. Такой код трудно читать и трудно использовать. Надо разбить его на отдельные функции.
В коде явно скопированы куски с небольшими изменениями. Их надо вынести в функцию, чтобы дублирования не было.
В нынешнем состоянии проверять код тяжело и я не вижу смысла это делать. Разбей его на более простые функции. Если не понимаешь как, попроси подсказки.
Вдобавок внизу там ошибки:
> PHP Notice: Undefined variable: text in /home/XTUN1t/prog.php on line 118
> PHP Notice: Undefined variable: numberOn in /home/XTUN1t/prog.php on line 139
> http://ideone.com/9fc5tF калькулятор
Не хватает вывода окончательного ответа.
> http://ideone.com/8hUwnm считалка
В общем-то алгоритм верный, но можно было обойтись без перестройки массива. Можно было обходить массив в цикле, увеличивая счетчик на каждом шаге и удаляя текущий элеимент, когда счетчик доходит до $skip.
> http://ideone.com/MSiND0 вертикальный текст
$array, $value - неудачные названия переменных.
Определить максимальную длину строки можно с помощью array_map и max.
> $longHigh = ($longHigh>$long)? $longHigh : $long;
Тут стоило использовать max()/min()
> PHP Notice: Undefined variable: longHigh in /home/09pNsX/prog.php on line 13
Ошибка
> ($array as $key => &$value){
Зачем тут & ?
Ну и код отформатирован не по стандарту, смотри второй пост треда.
Модель не должна работать с сессией. Да и капчу проверять наверно тоже. А то при добавлении в командной строке где ты возьмешь сессию или капчу?
>>1004889
> А как его вызвать на интервале который был вызван до него?
В clearInterval надо передать идентификатор таймера, который вернула функция setInterval. Это уже твоя проблема, куда его сохранить.
> //После клика на другой блок, интервал на первом кликнутом блоке не остановился
Потому что у тебя код неправильный. Подумай, когда создается и когда уничтожается переменная interval.
> А зачем тут setTimeout, который вызывается всего один раз с делеем? Он сбрасывает setInterval?
Его преимущество в том, что если произойдет ошибка то новый таймер не создастся и скрипт остановится. А в случае setInterval, если в коде ошибка, то этот код все равно будет продолжать вызываться.
> Мне нравиться вариант с сохранением на сервер, но где именно? Можно сделать отдельную таблицу unsendedmessages в БД.
Можно. Можно назвать ее drafts.
Хотя конечно с точки зрения приватности не очень хорошо сохранять неотправленные сообщения на сервере.
> Тогда нужно сделать чтобы сообщение отправлялось каждый раз как пользователь его меняет. Не будет ли это создавать большую нагрузку на БД, если, например, делать это на добавление\удаление нового символа или хотя бы нового слова\перевода строки?
Конечно, будет создавать и нагрузку и трафик. Но нам наверно и не требуется каждую секунду его сохранять.
> Даже не знаю что лучше, сохранять сообщения локально и подвергать пользователя тому, что его неотправленные сообщения украдут,
С другой стороны, если пользователь не разлогинился, то злоумышленник и так получит доступ к истории сообщений. Надо наверно просто очищать черновики при разлогинивании или при успешной отправке сообщения.
> Или просто уведомить о том что сообщения сохраняются либо в БД, либо локально (в течении сессии)?
Я думаю, что можно вывести краткое напоминание и/или тут же ссылку на настройки или кнопку. Например так: "История переписки сохраняется в вашем аккаунте на сервере. [Подробнее]"
> А что если один пользователь захочет хранить историю а другой нет? Тогда в любом случае сообщения будут храниться в одном экземпляре (если денормализовать сообщения на inbox и outbox).
Тут возможны варианты. Можно сделать, чтобы у каждого была своя история со своими настройками, можно - чтобы не было.
Вообще, если подумать, сейчас почти все мессенджеры сохраняют полную историю всех переписок. Что, если человек что-то писал N лет назад, а сейчас ему неудобно за эти слова? Когда мы общаемся лично, то никакой истории не сохраняется, так ли она вообще нужна? Хотя наверно есть люди, которым наоборот, хочется иметь полную историю сообщений.
> А можете посоветовать где лучше всего хранить ключ?
Ключ можно хранить локально, можно в аккаунте на сервере (но тогда его можно украсть), можно генерировать из пароля, можно создавать временный, который удаляется через определенное время. Вопрос, чего мы хотим добиться в итоге.
> Только я не знаю как тут будет с сихронизацией: у отправителя на каждый нажатый символ отправляется уведомление на сервер, а получатель при обновлении, каждую секунду сбрасывает это уведомление.
Это плохой вариант, при плохой связи задержка отправки запроса может быть десяток секунд. Отправки запроса на каждую клавишу просто перегрузит очередь запросов.
> Получается считать величину отступа нужно в отоброжении?
да, а еще лучше попробовать решить проблему средствами CSS.
> А где обычно создаются такие переменные? В самом вверху, перед "классами" MVC?
Логично сделать их свойствами объекта View.
> Как тогда по другому добавить перенос на новую строку по нажатию Ctrl + Enter?
Я имел в виду не копипастить $('textarea[name="message"]') два раза.
> Клиентский роутер это и есть JS-библиотека о которой вы упомянули в начале? Если нет, то что есть JS-библиотека в таком случае?
Я имел в виду библиотеку для управления историей. Вроде history.js https://github.com/browserstate/history.js/
>>Ну и нет обработки ошибок при выполнении запроса.
> А что за обработка ошибок должна быть? Имеется ввиду та которая присходит при неудачном получении JSON?
Ну например, если отсоединиться от Интернета, как будет работать твой код?
Вообще, наивно было бы думать, что ajax-запросв всегда сработает успешно. Вот что может произойти:
- может нарушиться связь с интернетом, или произойти проблема где-то на полпути. Это проявляется в том, что срабатывает обработчик ошибки с кодом 0. Причем в неудачных случаях, если пакет для установления TCP соединения потерялся где-то в сети, он сработает только после таймаута в 30-60 секунд.
- сервер может быть отключен/недоступен
- на сервере может произойти ошибка, это проявляется в HTTP статусе ответа, не равном 200, или в неверном Content-Type ответа
- при попытке раскодировать JSON может произойти ошибка, если он некорректный
Вопрос, как твое приложение будет вести себя в этих случаях? Или в случае поездки в метро, когда интернет появляется и пропадает периодически.
Модель не должна работать с сессией. Да и капчу проверять наверно тоже. А то при добавлении в командной строке где ты возьмешь сессию или капчу?
>>1004889
> А как его вызвать на интервале который был вызван до него?
В clearInterval надо передать идентификатор таймера, который вернула функция setInterval. Это уже твоя проблема, куда его сохранить.
> //После клика на другой блок, интервал на первом кликнутом блоке не остановился
Потому что у тебя код неправильный. Подумай, когда создается и когда уничтожается переменная interval.
> А зачем тут setTimeout, который вызывается всего один раз с делеем? Он сбрасывает setInterval?
Его преимущество в том, что если произойдет ошибка то новый таймер не создастся и скрипт остановится. А в случае setInterval, если в коде ошибка, то этот код все равно будет продолжать вызываться.
> Мне нравиться вариант с сохранением на сервер, но где именно? Можно сделать отдельную таблицу unsendedmessages в БД.
Можно. Можно назвать ее drafts.
Хотя конечно с точки зрения приватности не очень хорошо сохранять неотправленные сообщения на сервере.
> Тогда нужно сделать чтобы сообщение отправлялось каждый раз как пользователь его меняет. Не будет ли это создавать большую нагрузку на БД, если, например, делать это на добавление\удаление нового символа или хотя бы нового слова\перевода строки?
Конечно, будет создавать и нагрузку и трафик. Но нам наверно и не требуется каждую секунду его сохранять.
> Даже не знаю что лучше, сохранять сообщения локально и подвергать пользователя тому, что его неотправленные сообщения украдут,
С другой стороны, если пользователь не разлогинился, то злоумышленник и так получит доступ к истории сообщений. Надо наверно просто очищать черновики при разлогинивании или при успешной отправке сообщения.
> Или просто уведомить о том что сообщения сохраняются либо в БД, либо локально (в течении сессии)?
Я думаю, что можно вывести краткое напоминание и/или тут же ссылку на настройки или кнопку. Например так: "История переписки сохраняется в вашем аккаунте на сервере. [Подробнее]"
> А что если один пользователь захочет хранить историю а другой нет? Тогда в любом случае сообщения будут храниться в одном экземпляре (если денормализовать сообщения на inbox и outbox).
Тут возможны варианты. Можно сделать, чтобы у каждого была своя история со своими настройками, можно - чтобы не было.
Вообще, если подумать, сейчас почти все мессенджеры сохраняют полную историю всех переписок. Что, если человек что-то писал N лет назад, а сейчас ему неудобно за эти слова? Когда мы общаемся лично, то никакой истории не сохраняется, так ли она вообще нужна? Хотя наверно есть люди, которым наоборот, хочется иметь полную историю сообщений.
> А можете посоветовать где лучше всего хранить ключ?
Ключ можно хранить локально, можно в аккаунте на сервере (но тогда его можно украсть), можно генерировать из пароля, можно создавать временный, который удаляется через определенное время. Вопрос, чего мы хотим добиться в итоге.
> Только я не знаю как тут будет с сихронизацией: у отправителя на каждый нажатый символ отправляется уведомление на сервер, а получатель при обновлении, каждую секунду сбрасывает это уведомление.
Это плохой вариант, при плохой связи задержка отправки запроса может быть десяток секунд. Отправки запроса на каждую клавишу просто перегрузит очередь запросов.
> Получается считать величину отступа нужно в отоброжении?
да, а еще лучше попробовать решить проблему средствами CSS.
> А где обычно создаются такие переменные? В самом вверху, перед "классами" MVC?
Логично сделать их свойствами объекта View.
> Как тогда по другому добавить перенос на новую строку по нажатию Ctrl + Enter?
Я имел в виду не копипастить $('textarea[name="message"]') два раза.
> Клиентский роутер это и есть JS-библиотека о которой вы упомянули в начале? Если нет, то что есть JS-библиотека в таком случае?
Я имел в виду библиотеку для управления историей. Вроде history.js https://github.com/browserstate/history.js/
>>Ну и нет обработки ошибок при выполнении запроса.
> А что за обработка ошибок должна быть? Имеется ввиду та которая присходит при неудачном получении JSON?
Ну например, если отсоединиться от Интернета, как будет работать твой код?
Вообще, наивно было бы думать, что ajax-запросв всегда сработает успешно. Вот что может произойти:
- может нарушиться связь с интернетом, или произойти проблема где-то на полпути. Это проявляется в том, что срабатывает обработчик ошибки с кодом 0. Причем в неудачных случаях, если пакет для установления TCP соединения потерялся где-то в сети, он сработает только после таймаута в 30-60 секунд.
- сервер может быть отключен/недоступен
- на сервере может произойти ошибка, это проявляется в HTTP статусе ответа, не равном 200, или в неверном Content-Type ответа
- при попытке раскодировать JSON может произойти ошибка, если он некорректный
Вопрос, как твое приложение будет вести себя в этих случаях? Или в случае поездки в метро, когда интернет появляется и пропадает периодически.
> Как в Slim в контроллере сделать проверку введенного в значения, и если там пусто, то перенаправить на домашнюю страничку, иначе отправить по нужной страничке и передать в нее имя?
Использовать редирект. Имя можно передать через параметр в URL.
>>1004656
Либо разбить текст на массив букв, либо mb-функции. Работать с отдельным буквами неэффективно, твою задачу нельзя решить по-другому, например, регуляркой вроде preg_replace_callback?
>>1004557
А у меня в ноут нельзя поставить больше 4Гб в принципе. Я подозреваю, успешные менеджеры из Интел так реализуют запланированное устаревание. И даже сейчас на многих материнках стоит ограничение в 16 Гб, это явно искуственно делается.
>>1004463
А что вместо него?
>>1004448
Константы принято писать большими буквами.
Также у тебя должно выводиться сообщение об ошибке из-за неправильного имени переменной, если только ты их не отключил.
>>1004440
Это все равно будет нечестно и не по лицензии.
>>1004355
Не даем готовых решений, но можем проверить или подсказать, если что-то непонятно.
> Как в Slim в контроллере сделать проверку введенного в значения, и если там пусто, то перенаправить на домашнюю страничку, иначе отправить по нужной страничке и передать в нее имя?
Использовать редирект. Имя можно передать через параметр в URL.
>>1004656
Либо разбить текст на массив букв, либо mb-функции. Работать с отдельным буквами неэффективно, твою задачу нельзя решить по-другому, например, регуляркой вроде preg_replace_callback?
>>1004557
А у меня в ноут нельзя поставить больше 4Гб в принципе. Я подозреваю, успешные менеджеры из Интел так реализуют запланированное устаревание. И даже сейчас на многих материнках стоит ограничение в 16 Гб, это явно искуственно делается.
>>1004463
А что вместо него?
>>1004448
Константы принято писать большими буквами.
Также у тебя должно выводиться сообщение об ошибке из-за неправильного имени переменной, если только ты их не отключил.
>>1004440
Это все равно будет нечестно и не по лицензии.
>>1004355
Не даем готовых решений, но можем проверить или подсказать, если что-то непонятно.
> Программа для сохранения и напоминания дат праздников у друзей и родственников.
Ну это конечно очень просто получается, это любой может сделать. Так как там довольно простые данные и простая синхронизация - можно тупо все данные загружать при входе в приложение и выгружать при изменениях.
Ведь в моей задаче объем данных потенциально большой, и там несколько видов сущностей, которые связаны между собой. И возможно параллельное редактирование несколькими пользователями, оффлайн-режим.
Ну попробуй, ладно, почему бы и нет. Посмотри тогда Google calendar, там что-то похожее по смыслу.
> План такой:
Вполне хороший план.
> . верстка необходимого части интерфейса для проверки серверной части;
Если есть АПИ, то его можно тестировать курлом или автоматизированными тестами (!это тоже интересная тема!)
> Какую версию ES использовать, ES3 или уже можно перейти на ES2015?
ES5 можно использовать (Реакт наверно ниже ES5 не поддерживается, проверь этот момент). Но конечно если хочется перфекционизма то можно было бы писать на ES3, чтобы обеспечить максимальный охват. Ведь ES5 дает не так много новых фич, и они прекрасно реализуются вспомогательной библиотекой совместимости. Увы, когда речь идет о бизнесе, то там часто нет времени, и недостаточно квалификации разработчиков (особенно если это вкатывальщики), и пишут просто как попало, не особо заморачиваясь даже с тестированием.
>>1004095
надо изучать CSS, а не тыкаться наугад.
>>1003992
Надо либо сделать переменную, и приписывать слоги к ней, либо складывать в массив.
>>1003628
Для начала надо определиться, что ты считаешь "временем сессии". И тогда станет понятнее, что надо измерять.
> Программа для сохранения и напоминания дат праздников у друзей и родственников.
Ну это конечно очень просто получается, это любой может сделать. Так как там довольно простые данные и простая синхронизация - можно тупо все данные загружать при входе в приложение и выгружать при изменениях.
Ведь в моей задаче объем данных потенциально большой, и там несколько видов сущностей, которые связаны между собой. И возможно параллельное редактирование несколькими пользователями, оффлайн-режим.
Ну попробуй, ладно, почему бы и нет. Посмотри тогда Google calendar, там что-то похожее по смыслу.
> План такой:
Вполне хороший план.
> . верстка необходимого части интерфейса для проверки серверной части;
Если есть АПИ, то его можно тестировать курлом или автоматизированными тестами (!это тоже интересная тема!)
> Какую версию ES использовать, ES3 или уже можно перейти на ES2015?
ES5 можно использовать (Реакт наверно ниже ES5 не поддерживается, проверь этот момент). Но конечно если хочется перфекционизма то можно было бы писать на ES3, чтобы обеспечить максимальный охват. Ведь ES5 дает не так много новых фич, и они прекрасно реализуются вспомогательной библиотекой совместимости. Увы, когда речь идет о бизнесе, то там часто нет времени, и недостаточно квалификации разработчиков (особенно если это вкатывальщики), и пишут просто как попало, не особо заморачиваясь даже с тестированием.
>>1004095
надо изучать CSS, а не тыкаться наугад.
>>1003992
Надо либо сделать переменную, и приписывать слоги к ней, либо складывать в массив.
>>1003628
Для начала надо определиться, что ты считаешь "временем сессии". И тогда станет понятнее, что надо измерять.
>>1003464
Можно работать с куками, если класс специально предназначен для работы с этими куками. В фреймворках обычно есть объект, представляющий куки, и его передают явно в функцию.
В твоем случае можно вынести работу с CSRF токенами в отдельный класс. А можно и не выносить, но добавить слово xsrf в название функций.
Также, вместо str_shuffle лучше генерировать полностью случайный токен с возможностью повтора символов.
>>1003272
А ты точно читал урок https://github.com/codedokode/pasta/blob/master/forms.md ?
> Мне не чтобы все формы страницы обрабатывал один метод контролера, он очень толстый получится
Я это и не предлагаю, я предлагаю обрабатывать одним методом как GET, так и POST запросы к одной отдельной форме. Для другой формы - сделать другой метод.
>>1003354
> Оба этих сценария по хорошему нужно обернуть в объект,
Только важно, чтобы этот объект не был замаскированным контроллером.
Все равно код плохой так как у тебя нет отделения кода вставки данных в БД от кода обработки параметров запроса, все смешано вместе.
Ну то есть, можно ли у тебя вставить запись, полученную не из формы? Можно ли вызват валидацию для записи не из формы? Нельзя.
По поводу названий - нужно разделять слова и не использовать непонятные сокращения:
invoice_date или invoiceDate
asset_id или assetId
По поводу статьи - она дает дурные советы.
> Имя должно быть как можно короче. Оптимально - 2-4 буквы, максимум до 10.
Почему? Видимо во время написания статьи не было автодополнения и автор просто устал печатать запросы руками. Это не повод делать непонятные имена.
> И если названия таблицы и пяти соединенных справочников будут несокращенными словами в 16-20 символов, то это порадует лишь поклонников языка Шекспира, но успешно затуманит смысл простейших SQL-запросов
Не согласен.
> "группа пользователей" - "ugroup", "группа домов" - "hgroup".
user_group, house_group
> если у вас уже есть таблица 'документ' (doc),
То ее лучше называть documents.
> CSTRTYPE
street_types
> JCALCOP
calc_operations_log
Это конечно мое личное мнение, основанное на опыте работы с проектами с сотнями таблиц.
> Три поля с одинаковыми названиями в результате запроса. Какое безобразие!
Не сталкивался с проблемами из-за этого.
Стремление сокращать названия нездоровое. Автор экономит себе 5 секунд на наборе кода, а потом кто-то другой теряет минуты, пытаясь понять этот код.
Все равно код плохой так как у тебя нет отделения кода вставки данных в БД от кода обработки параметров запроса, все смешано вместе.
Ну то есть, можно ли у тебя вставить запись, полученную не из формы? Можно ли вызват валидацию для записи не из формы? Нельзя.
По поводу названий - нужно разделять слова и не использовать непонятные сокращения:
invoice_date или invoiceDate
asset_id или assetId
По поводу статьи - она дает дурные советы.
> Имя должно быть как можно короче. Оптимально - 2-4 буквы, максимум до 10.
Почему? Видимо во время написания статьи не было автодополнения и автор просто устал печатать запросы руками. Это не повод делать непонятные имена.
> И если названия таблицы и пяти соединенных справочников будут несокращенными словами в 16-20 символов, то это порадует лишь поклонников языка Шекспира, но успешно затуманит смысл простейших SQL-запросов
Не согласен.
> "группа пользователей" - "ugroup", "группа домов" - "hgroup".
user_group, house_group
> если у вас уже есть таблица 'документ' (doc),
То ее лучше называть documents.
> CSTRTYPE
street_types
> JCALCOP
calc_operations_log
Это конечно мое личное мнение, основанное на опыте работы с проектами с сотнями таблиц.
> Три поля с одинаковыми названиями в результате запроса. Какое безобразие!
Не сталкивался с проблемами из-за этого.
Стремление сокращать названия нездоровое. Автор экономит себе 5 секунд на наборе кода, а потом кто-то другой теряет минуты, пытаясь понять этот код.
http://sandbox.onlinephpfunctions.com/code/d06fe5fc8dc3cf074e241851614041b100631a23
>> хотя можно было просто написать $.get(...).
>Не забудь только прочитать мой урок про работу с аяксом.
У меня есть пара небольших вопросов, но они возникли в задаче с файлхостингом, поэтому мне будет удобней спросить на её примере, когда я её вброшу.
>Ну и подсчет высоты сообщений - это явно что-то неправильное. Это нужно для выравнивания сообщений вертикально? Неужели это нельзя было сделать средствами CSS? Задание размеров и расположения элементов через JS работает плохо, может влиять на производительность и работать с задержками.
>Это нужно для выравнивания сообщений вертикально?
Это нужно сделать отступ сверху, если сообщений слишком мало, и они начинались как бы снизу вверх, а не сверху вниз.
Я посмотрел как такое сделано в телеграмме и мне показалось что это сделано с помощью js (хотя бы потому что отступ меняется динамически и стиль прописывается в атрибутах элемента).
<div class="im_history_messages" ng-class="{im_history_messages_group: historyPeer.id < 0}" style="margin-top: 43px;">...</div>
>Это компоненты еще иногда называют виджеты. В архитектуре MVC у каждого виджета будут свои контроллер, вью и ViewModel (ViewModel - это модель, управляющая не данными и бизнес-логикой приложения, а данными отдельно взятого компонента).
ViewModel это просто обычная логика компонента, которая могла бы называться просто Model? Я просто запутался в терминологии.
>>1006196
>> А зачем тут setTimeout, который вызывается всего один раз с делеем? Он сбрасывает setInterval?
>Его преимущество в том, что если произойдет ошибка то новый таймер не создастся и скрипт остановится. А в случае setInterval, если в коде ошибка, то этот код все равно будет продолжать вызываться.
То есть его нужно будет вызывать рекурсивно, если нужно будет вызывать код, например, каждую секунду?
>> А что если один пользователь захочет хранить историю а другой нет? Тогда в любом случае сообщения будут храниться в одном экземпляре (если денормализовать сообщения на inbox и outbox).
>Тут возможны варианты. Можно сделать, чтобы у каждого была своя история со своими настройками, можно - чтобы не было.
>
>Вообще, если подумать, сейчас почти все мессенджеры сохраняют полную историю всех переписок. Что, если человек что-то писал N лет назад, а сейчас ему неудобно за эти слова? Когда мы общаемся лично, то никакой истории не сохраняется, так ли она вообще нужна? Хотя наверно есть люди, которым наоборот, хочется иметь полную историю сообщений.
Иногда хранить историю нужно, чтобы удобно было найти какую-то информацию, например номер телефона скинутый собеседником.
Я ещё подумаю как тут лучше сделать.
>> А можете посоветовать где лучше всего хранить ключ?
>Ключ можно хранить локально, можно в аккаунте на сервере (но тогда его можно украсть), можно генерировать из пароля, можно создавать временный, который удаляется через определенное время. Вопрос, чего мы хотим добиться в итоге.
Конечно, чтобы никто другой не смог прочитать сообщения кроме собеседников, и чтобы эти собеседники могли прочитать сообщения друг друга даже через N лет.
Оптимальным вариантом мне кажется генерировать из пароля, но если пароль смениться, то нужно будет и перезашифровать эти сообщения для всех собеседников. Хотя, если мы решили хранить их в нескольких экземплярах, то можно сделать это только для одного пользователя.
>> хотя можно было просто написать $.get(...).
>Не забудь только прочитать мой урок про работу с аяксом.
У меня есть пара небольших вопросов, но они возникли в задаче с файлхостингом, поэтому мне будет удобней спросить на её примере, когда я её вброшу.
>Ну и подсчет высоты сообщений - это явно что-то неправильное. Это нужно для выравнивания сообщений вертикально? Неужели это нельзя было сделать средствами CSS? Задание размеров и расположения элементов через JS работает плохо, может влиять на производительность и работать с задержками.
>Это нужно для выравнивания сообщений вертикально?
Это нужно сделать отступ сверху, если сообщений слишком мало, и они начинались как бы снизу вверх, а не сверху вниз.
Я посмотрел как такое сделано в телеграмме и мне показалось что это сделано с помощью js (хотя бы потому что отступ меняется динамически и стиль прописывается в атрибутах элемента).
<div class="im_history_messages" ng-class="{im_history_messages_group: historyPeer.id < 0}" style="margin-top: 43px;">...</div>
>Это компоненты еще иногда называют виджеты. В архитектуре MVC у каждого виджета будут свои контроллер, вью и ViewModel (ViewModel - это модель, управляющая не данными и бизнес-логикой приложения, а данными отдельно взятого компонента).
ViewModel это просто обычная логика компонента, которая могла бы называться просто Model? Я просто запутался в терминологии.
>>1006196
>> А зачем тут setTimeout, который вызывается всего один раз с делеем? Он сбрасывает setInterval?
>Его преимущество в том, что если произойдет ошибка то новый таймер не создастся и скрипт остановится. А в случае setInterval, если в коде ошибка, то этот код все равно будет продолжать вызываться.
То есть его нужно будет вызывать рекурсивно, если нужно будет вызывать код, например, каждую секунду?
>> А что если один пользователь захочет хранить историю а другой нет? Тогда в любом случае сообщения будут храниться в одном экземпляре (если денормализовать сообщения на inbox и outbox).
>Тут возможны варианты. Можно сделать, чтобы у каждого была своя история со своими настройками, можно - чтобы не было.
>
>Вообще, если подумать, сейчас почти все мессенджеры сохраняют полную историю всех переписок. Что, если человек что-то писал N лет назад, а сейчас ему неудобно за эти слова? Когда мы общаемся лично, то никакой истории не сохраняется, так ли она вообще нужна? Хотя наверно есть люди, которым наоборот, хочется иметь полную историю сообщений.
Иногда хранить историю нужно, чтобы удобно было найти какую-то информацию, например номер телефона скинутый собеседником.
Я ещё подумаю как тут лучше сделать.
>> А можете посоветовать где лучше всего хранить ключ?
>Ключ можно хранить локально, можно в аккаунте на сервере (но тогда его можно украсть), можно генерировать из пароля, можно создавать временный, который удаляется через определенное время. Вопрос, чего мы хотим добиться в итоге.
Конечно, чтобы никто другой не смог прочитать сообщения кроме собеседников, и чтобы эти собеседники могли прочитать сообщения друг друга даже через N лет.
Оптимальным вариантом мне кажется генерировать из пароля, но если пароль смениться, то нужно будет и перезашифровать эти сообщения для всех собеседников. Хотя, если мы решили хранить их в нескольких экземплярах, то можно сделать это только для одного пользователя.
Потому что она работает: Первый раз ты итерируешь массив и на первом элементе уменьшаешь $count - 1 = 2, затем ты передаешь этот же массив в функцию и заново его итерируешь, но уже со значением $count = 2, и уменьшаешь его ещё на -1, $count - 1 = 1, и заново вызываешь функцию, но уже со значением $count = 1, и снова уменьшаешь его на -1, $count - 1 = 0, затем снова вызываешь функцию со значением $count = 0, и пропускаешь все элементы массива. Далее начинается следующая итерация массива где мы первый раз вызвали функцию рекурсивно (т.е. второй раз за всю программу в целом) где наше значение $count уже равно 2, и повторяешь те же действия, и так далее...
Model это модель, содержащая данные и логику всего приложения. Например, список контактов, список сообщений.
ViewModel это модель, содержащая данные и логику только одного виджета. Ну например, если в виджете списка контактов есть поиск то ViewModel может содержать ключевое слово для поиска и метод для получения списка контактов, соответствующих этому слову.
При этом данные в ViewModel могут не совпадать с данными в Model. Ну например если мы делаем виджет формы редактирования данных о студенте, то его ViewModel может содержать измененные пользователем данные, которые пока не перенесены в модель приложения.
То есть мы можем рассматривать виджет как мини-приложение, у которого есть своя собственная модель данных (ViewModel). Хотя возможен и случай, когда виджет работает с переданной ему снаружи моделью. Тогда ViewModel нету.
Можно, но смысла нет так как вряд ли пользователю нужно менять эти цифры и проще просто прописать их в валидаторе. А так получается ненужное усложнение кода.
Все правильно делаешь, только не перебарщивай. Всякую срань выносить в конфиг не нужно, но то что потенциально может изменится - выноси в конфиг.
Спецназ веб-программирования.
zend
это не пхп
>а также делать поля-ловушки, которые не видны человеку
Запилил. А то спам всё равно идёт. Посмотрим.
>>1006196
>Модель не должна работать с сессией. Да и капчу проверять наверно тоже. А то при добавлении в командной строке где ты возьмешь сессию или капчу?
Не понимаю что ты имеешь ввиду, говоря о командной строке, не рассчитывал скрипт на неё.
По поводу работы с сессией в модели согласен, чуть переделал. Но создавать или передавать весь объект Request в модель - не очень. Пока так оставлю, спать хочется.
Ты неправильно сделал капчу, если у тебя больше 1 капчи на странице или несколько вкладок с капчами то они не будут работать, так как в сессии выделена одна переменная под все.
Тут надо взвешивать плюсы и минусы. Плюс в том, что у пользователя больше возможностей для настройки, минус в том что конфиг усложняется и утяжеляется и труднее разобраться, какие места в нем надо менять обязательно, а какие можно не трогать.
Бля, я об этом даже не догадывался, попозже исправлю, уже придумал как
>Тут наверно удобнее просто выбрать нужные данные в PHP и там с ними работать, чем пытаться все сделать одним сложным запросом.
>
>Про склеивание строк и строковые функции можно прочесть тут https://postgrespro.ru/docs/postgrespro/9.6/functions-string
Даже не знаю почему я сразу не заметил функцию concat().
Почему выдается ошибка: could not determine data type of parameter? Такого не может быть чтобы не определился тип параметра.
$q = prepare("INSERT INTO comments (..., tree) VALUES (..., text2ltree(concat(:file, '.', currval('comments_id_seq'))))");
...
$q->bindValue(':file', ...);
Я тестировал, и это определённо из-за того что как-то неправильно передаётся параметр :file. То есть если подставить за место этого параметра какое-нибудь значение или просто вставить переменную, то ошибки не будет.
Что касается генерации id программно, то можно вызвать persist($comment) и получить id, но в мануале доктрины сказано, что на такой метод не следует полагаться:
>http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-objects.html
Generated entity identifiers / primary keys are guaranteed to be available after the next successful flush operation that involves the entity in question. You can not rely on a generated identifier to be available directly after invoking persist. The inverse is also true. You can not rely on a generated identifier being not available after a failed flush operation.
Иначе, я даже не догадываюсь как получить id программно.
Первый: я запилил сайт одностраничник. html+css, обработка действий пользователя на js, логика работы с данными на php, база mysql. Всё хорошо, но теперь требуется сделать из сайта многостраничник. Чтобы статьи вставлять. И у меня два стула: либо делать (и осваивать, как это вообще делается), либо вытащить из сайта всю полезную начинку и тупо прикрутить её к Вордпрессу. Мне бы хотелось выбрать первый вариант, но не чрезмерно ли он затратен по времени разработки?
Второй: если таки перепиливать и добавлять статьи, то как хранить их тексты? Понятно, что в базе. А дальше? Одна статья в одной ячейке? Или одна статья - одна строка таблицы, а в ней каждый абзац в отдельной ячейке? А как хранить прикреплённые картинки?
>Первый: я запилил сайт одностраничник. html+css, обработка действий пользователя на js, логика работы с данными на php, база mysql. Всё хорошо, но теперь требуется сделать из сайта многостраничник. Чтобы статьи вставлять. И у меня два стула: либо делать (и осваивать, как это вообще делается), либо вытащить из сайта всю полезную начинку и тупо прикрутить её к Вордпрессу. Мне бы хотелось выбрать первый вариант, но не чрезмерно ли он затратен по времени разработки?
Создание разных страниц это типовая задача. Время рассчитывай сам.
>Второй: если таки перепиливать и добавлять статьи, то как хранить их тексты? Понятно, что в базе. А дальше? Одна статья в одной ячейке? Или
одна статья - одна строка таблицы, а в ней каждый абзац в отдельной ячейке? А как хранить прикреплённые картинки?
>одна статья - одна строка таблицы
>Одна статья в одной ячейке
Вот это.
CREATE TABLE article (..., content);
>А как хранить прикреплённые картинки?
Сохраняешь их на диск, и в базе хранишь путь к ним.
https://secure.php.net/manual/ru/features.file-upload.post-method.php
>Сохраняешь их на диск, и в базе хранишь путь к ним.
Это-то понятно. Я имел в виду, как в базе это всё хранить?
Одна строка - одна статья, и в ней нулевая ячейка - id, первая - текст статьи, вторая - путь к прикреплённой сверху статьи картинке. Так?
А если нужно вставлять много картинок в произвольные места текста? Не лучше ли вставлять в ячейку абзацы и/или указания на пить к файлам картинок? А когда нужно их вынуть, просто перебирать в foreach ячейки, пока не кончатся.
>А если нужно вставлять много картинок в произвольные места текста?
>в произвольные места текста
Тогда нужно использовать разметку в контентной части (html-тэг <img>).
>Не лучше ли вставлять в ячейку абзацы и/или указания на пить к файлам картинок?
Не лучше. Ты что знаешь сколько у тебя абзацев будет?
>Ты что знаешь сколько у тебя абзацев будет?
Дык foreach же. Будет перебирать, пока не кончатся.
Так, давай опишу подробно, о чём я думаю.
Нужно отрисовать статью. Запрашиваем в базе строку с нужным id, затем в цикле foreach делаем что-то вроде:
$article .= "<p>"."вставить текст из ячейки"."</p>"
А потом этот $article вставляем в страницу. А если вместо текста в ячейке ссылка, то оформляем её соответственно. В чём недостаток такого подхода?
В том что ты не знаешь сколько у тебя будет абзацев и соответственно сколько ячеек будет в таблице.
В ней например такой набор столбцов:
id - ид
header - тут заголовок твоей статьи будет
text - тут хранишь отформатированный в html текст статьи, без всякого разбиения на обзацы, че это вообще за высер? А если надо переписать статью или вырезать обзацы или склеить 2 а 1?
author - имя автора или id юзера если у тебя в системе есть юзеры
image - ссылка на картинку например, если у тебя статья с картинкой или превьюхой.
url - тут хранишь чпу например, что бы при создании статьи его указывать самому или система за тебя это делала.
ну это если ты хочешь на сайте что бы урлы к статьям были не вида site.qw/article/123
а например site.qw/srticle/shok_eto_po_nashemu
ну для начала подойдет имхо.
Научи меня циклам в php
До этого писал только на Pascal, решил выбрать своим первым языком PhP, начал с сайта из ОП-поста
И вот там была простейшая задача написать таблицу умножения чисел самих на себя, с чем я уже не смог справиться
Укажи на мои ошибки, что я делаю не так и как это фиксить?
Второй аргумент функции for определяет при каком условии цикл будет продолжать выполнение. У тебя стоит $x = 9, что присваивает значение, а не возвращает true. Не уверен почему твой цикл не уходит в бесконечность, потому что $x = 9, возвращает 9, что в свою очередь является истинным значением.
https://secure.php.net/manual/ru/control-structures.for.php
>В начале каждой итерации оценивается выражение expr2. Если оно принимает значение TRUE, то цикл продолжается, и вложенные операторы будут выполнены. Если оно принимает значение FALSE, выполнение цикла заканчивается.
Ну вайл это понятно, но я сейчас фор прохожу :)
Из-за этого я 2 месяца назад забросил пхп, из-за этого сейчас ничего не получается
Почему в моей программе не входит в цикл?
Алсо, если есть какие-то правился при написании цикла, то был бы рад услышать
Цикл будет дрочиться покуда условие $x==9 = true. У тебя оно изначально false. Поменяй на $<=9
1)Сделать сигнатуру сеттера setAccess с параметрами access и accessHash вместо одного параметра access.
2)Явно не заставлять заполнять свойство, но проверять в валидаторе, что если есть значение "pass", то значит должно быть заполнено свойство access.
3)Что-то другое.
И еще вопрос, который косвенно относится к первому и тоже меня интересует. Модель вообще не должна контролировать в сетерах получаемые значения, это должен выполнять валидатор? Если да, то не должна до такой степени, что если в свойстве будет объект определенного класса, то в сеттере модели не ставить тайпхинт ClassName, а все потом через inctanceof в валидаторе? Или тайпхинт можно оставить
if ($file->access == ''pass") {
$file->accessHash = ...;
}
>Модель вообще не должна контролировать в сетерах получаемые значения, это должен выполнять валидатор?
Может и контролировать, сеттеры для этого и нужны чтобы преобразовать значение в нужный формат, если нужно.
Но валидатор лучше делать отдельно.
Не должны.
>Иначе, я даже не догадываюсь как получить id программно.
Точно, я забыл, что вы советовали получать его из uuid и ещё кучу способов.
>>1006812
НАДО ЗАСЕСТЬ ЗА УЧЁБУ
@
ФИКСИШЬ БАГИ
@
ВТОРУЮ НЕДЕЛЮ
https://github.com/grigoryMovchan/AphorismCMS/blob/master/app/Models/CaptchaModel.php
Кстати, фишка с невидимым полем 'email', которое скрывает джс, сработало. Спам больше не появлялся.
Надо не полагаться на автокомплит, тем более начинающему.
>>1007245
Получить новый id из Postgres можно запросом SELECT nextval('x') (можно использовать SELECT без таблиц). Также можно отказаться от получения id из БД и генерировать их, по алгоритму UUID, Twitter Snowflake или любому другому. Я не вижу правда в этом особой выгоды в данной ситуации.
>>1007232
Нет, зачем? Лишний столбец будет занимать лишнее место, и требовать усилий по обновлению при изменении исходных столбцов.
>>1007195
Удобнее всего сделать в модели 2 метода: setFreeAccess() и setPasswordAccess($password). Если свойства модели приватные, то задать "неправильную" комбинацию свойств в обход этих методов не получится, даже случайно или по ошибке.
Заметь, что мы тут просто используем возможности ООП (инкапсуляция), а база данных тут вообще не при чем. Инкапсуляция как раз и помогает защититься от перевода объекта в недопустимое состояние.
> Модель вообще не должна контролировать в сетерах получаемые значения, это должен выполнять валидатор?
Тут есть варианты:
- никак не контролировать - тогда в свойство можно записать значение любого типа, например, строку вместо массива, что позже вызовет ошибку, когда эту строку прочтет код, ожидающий что там хранится массив
- контролировать полностью, выкидывая исключение при попытке записи неверного значения. Тут есть 2 проблемы: во-первых, пользователь может ввести неверные значения в форму и нам надо уметь как-то их представлять (хранить) внутри программы. Во-вторых, для этого нам по сути надо встроить валидацию в модель (что уже не очень хорошо), но некоторые типы валидации в модели могут быть невозможны. Ну например, проверка, что введенный email уникален, требует доступа к БД, а его внутри модели может не быть.
Потому на практике используют компромисс между 2 вариантами: например, мы не позволяем записать массив в свойство-строку, но позволяем туда ввести не соответствующую правилам валидации строку. Позже валидатор обнаружит ошибку и не допустит сохранения в БД неверных данных. Но создать модель с неверными данными вполне можно.
Ну и как дополнение, можно еще для подстраховки добавить правила в БД - с помощью конструкций вроде CHECK ( https://postgrespro.ru/docs/postgrespro/9.5/ddl-constraints ), которые будут отлавливать ошибочные данные, которые каким-то образом (из-за невнимательности разработчика) обошли валидацию. Ну к примеру мы можем на уровне БД указать что цена должна быть целым числом больше нуля.
Не забудь использовать "соленый", а не обычный хеш пароля. И сделать константы для значений типов доступа (или использовать true/false).
Надо не полагаться на автокомплит, тем более начинающему.
>>1007245
Получить новый id из Postgres можно запросом SELECT nextval('x') (можно использовать SELECT без таблиц). Также можно отказаться от получения id из БД и генерировать их, по алгоритму UUID, Twitter Snowflake или любому другому. Я не вижу правда в этом особой выгоды в данной ситуации.
>>1007232
Нет, зачем? Лишний столбец будет занимать лишнее место, и требовать усилий по обновлению при изменении исходных столбцов.
>>1007195
Удобнее всего сделать в модели 2 метода: setFreeAccess() и setPasswordAccess($password). Если свойства модели приватные, то задать "неправильную" комбинацию свойств в обход этих методов не получится, даже случайно или по ошибке.
Заметь, что мы тут просто используем возможности ООП (инкапсуляция), а база данных тут вообще не при чем. Инкапсуляция как раз и помогает защититься от перевода объекта в недопустимое состояние.
> Модель вообще не должна контролировать в сетерах получаемые значения, это должен выполнять валидатор?
Тут есть варианты:
- никак не контролировать - тогда в свойство можно записать значение любого типа, например, строку вместо массива, что позже вызовет ошибку, когда эту строку прочтет код, ожидающий что там хранится массив
- контролировать полностью, выкидывая исключение при попытке записи неверного значения. Тут есть 2 проблемы: во-первых, пользователь может ввести неверные значения в форму и нам надо уметь как-то их представлять (хранить) внутри программы. Во-вторых, для этого нам по сути надо встроить валидацию в модель (что уже не очень хорошо), но некоторые типы валидации в модели могут быть невозможны. Ну например, проверка, что введенный email уникален, требует доступа к БД, а его внутри модели может не быть.
Потому на практике используют компромисс между 2 вариантами: например, мы не позволяем записать массив в свойство-строку, но позволяем туда ввести не соответствующую правилам валидации строку. Позже валидатор обнаружит ошибку и не допустит сохранения в БД неверных данных. Но создать модель с неверными данными вполне можно.
Ну и как дополнение, можно еще для подстраховки добавить правила в БД - с помощью конструкций вроде CHECK ( https://postgrespro.ru/docs/postgrespro/9.5/ddl-constraints ), которые будут отлавливать ошибочные данные, которые каким-то образом (из-за невнимательности разработчика) обошли валидацию. Ну к примеру мы можем на уровне БД указать что цена должна быть целым числом больше нуля.
Не забудь использовать "соленый", а не обычный хеш пароля. И сделать константы для значений типов доступа (или использовать true/false).
Неудачный код, так как никто не запрещает где-то в другом месте выставить недопустимую комбинацию свойств. Лучше использовать инкапсуляцию или хотя бы методы-сеттеры, задающие правильную комбинацию свойств.
>>1007151
Надо указывать условие продолжения, а не останова цикла. Ну то есть $x <= 9
>>1007095
= это присваивание
== это проверка на равенство
>>1006965
Вообще, взять вордпресс или другую CMS может быть быстрее.
> Понятно, что в базе. А дальше? Одна статья в одной ячейке? Или одна статья - одна строка таблицы, а в ней каждый абзац в отдельной ячейке? А как хранить прикреплённые картинки?
Хранить код статьи в формате HTML. Возможно, что ты будешь делать какие-то преобразования этого HTML кода, в таком случае делается 2 ячейки - исходный код, введенный пользователем и преобразованный код для вывода на сайте.
Картинки хранить на диске, а информацию о них стоит хранить в таблице картинок, которую можно связать с таблицей статей. Это позволит запретить удалять картинки, используемые в статьях.
>>1006982
> Создание разных страниц это типовая задача.
Там еще надо прикручивать редактор, фильтр HTML, плагины к редактору, загрузчик картинок, улучшатель HTML и тд.
>>1007005
Сделать таблицу картинок и связь многие-ко-многим со статьями.
Неудачный код, так как никто не запрещает где-то в другом месте выставить недопустимую комбинацию свойств. Лучше использовать инкапсуляцию или хотя бы методы-сеттеры, задающие правильную комбинацию свойств.
>>1007151
Надо указывать условие продолжения, а не останова цикла. Ну то есть $x <= 9
>>1007095
= это присваивание
== это проверка на равенство
>>1006965
Вообще, взять вордпресс или другую CMS может быть быстрее.
> Понятно, что в базе. А дальше? Одна статья в одной ячейке? Или одна статья - одна строка таблицы, а в ней каждый абзац в отдельной ячейке? А как хранить прикреплённые картинки?
Хранить код статьи в формате HTML. Возможно, что ты будешь делать какие-то преобразования этого HTML кода, в таком случае делается 2 ячейки - исходный код, введенный пользователем и преобразованный код для вывода на сайте.
Картинки хранить на диске, а информацию о них стоит хранить в таблице картинок, которую можно связать с таблицей статей. Это позволит запретить удалять картинки, используемые в статьях.
>>1006982
> Создание разных страниц это типовая задача.
Там еще надо прикручивать редактор, фильтр HTML, плагины к редактору, загрузчик картинок, улучшатель HTML и тд.
>>1007005
Сделать таблицу картинок и связь многие-ко-многим со статьями.
> Я тестировал, и это определённо из-за того что как-то неправильно передаётся параметр :file. То есть если подставить за место этого параметра какое-нибудь значение или просто вставить переменную, то ошибки не будет.
А что именно передается в bindValue? Скорее всего, что-то не то.
> Иначе, я даже не догадываюсь как получить id программно.
SELECT nextval(...);
>>1006764
> По поводу работы с сессией в модели
Можно просто разбить модуль капчи на M, V и С. Пусть контроллер (или помощник для вызова из контроллера) работает с сессией или куками, модель отвечает за логику, а вид отображает эту капчу. Если грамотно подойти к делу, может быть даже получится универсальный модуль, который можно прикрутить к любому сайту. А может конечно и нет.
>>1006380
> Это нужно сделать отступ сверху, если сообщений слишком мало, и они начинались как бы снизу вверх, а не сверху вниз.
Это называется "вертикальное выравнивание". Я бы поискал варианты сделать это средствами CSS. Вот что приходит в голову:
- flex, новая технология, которая не везде есть. С ее помощью можно выделить часть экрана под область сообщений, и задать выравнивание блока сообщений по нижнему краю. Ну и не забыть про overflow, чтобы при большом числе сообщений блок не растягивался, а появлялась бы прокрутка. Минус - работает в новых браузерах
- display: table - работает начиная с ИЕ8 и древних фаерфоксов. Это как flex, только слабее по возможностям и со своими недостатками. Табличное отображение позволяет прижать содержимое ячейки (зоны сообщений) вниз. Тут есть проблемка - чтобы сделать прокрутку, блоку сообщений надо задать высоту, и задать ее в виде 100% (=высоте окна) наверно не получится, значит придется этот момент сделать через JS (определение доступной высоты блока и изменение ее при измнеении размера окна)
- прижать блок сообщений вниз с помощью абс. поз. Работает тоже везде, и можно задать высоту как 100% от зоны для вывода сообщений (при условии что не используется display: table, так как абс. поз. не работает с элементами таблиц).
Я бы советовал попробовать сделать отдельный пример (примеры) в jsfiddle или аналогичном сервисе, и потестировать его на маленьком и большом числе сообщений (когда должна появляться прокрутка).
Я могу подсказать по CSS, но имея частично работающий код, сделать это проще.
> Я посмотрел как такое сделано в телеграмме
Да, там почему-то выбрали такой способ. Но даже при таком подходе нет необходимости считать высоту каждого сообщения по отдельности - достаточно посчитать scrollHeight или offsetHeight блока, содержащего их.
> То есть его нужно будет вызывать рекурсивно, если нужно будет вызывать код, например, каждую секунду?
Не совсем рекурсивно, но да, примерно так:
function doStep() {
...
setTimeout(doStep, t);
}
setTimeout(doStep, t);
> но если пароль смениться, то нужно будет и перезашифровать эти сообщения для всех собеседников
Тут наверно будет проблема, что сообщений много, они могут храниться на сервере, и непонятно что делать, если программа упала на полпути в процессе перешифрования.
Советую почитать, как сделаны секретные чаты в Телеграм. Или например как сделано хранение шифрованных сообщений в iMessage. Вот тут что-то есть по теме: https://techcrunch.com/2014/02/27/apple-explains-exactly-how-secure-imessage-really-is/ (если нет, то можешь сам погуглить).
Также, можно почитать про шифрование диска в Андроид:
- https://nelenkov.blogspot.ru/2012/08/changing-androids-disk-encryption.html
- https://source.android.com/security/encryption/
- https://source.android.com/security/encryption/full-disk
Как я понимаю, там используется такая схема: генерируется ключ, им шифруется диск. А ключ шифруется паролем пользователя и сохраняется. При смене пароля перешифруется только ключ.
Кстати, так же можно и уничтожать сообщения: вместо их уничтожения достаточно уничтожить ключ, которым они зашифрованы, что гораздо быстрее.
> Я тестировал, и это определённо из-за того что как-то неправильно передаётся параметр :file. То есть если подставить за место этого параметра какое-нибудь значение или просто вставить переменную, то ошибки не будет.
А что именно передается в bindValue? Скорее всего, что-то не то.
> Иначе, я даже не догадываюсь как получить id программно.
SELECT nextval(...);
>>1006764
> По поводу работы с сессией в модели
Можно просто разбить модуль капчи на M, V и С. Пусть контроллер (или помощник для вызова из контроллера) работает с сессией или куками, модель отвечает за логику, а вид отображает эту капчу. Если грамотно подойти к делу, может быть даже получится универсальный модуль, который можно прикрутить к любому сайту. А может конечно и нет.
>>1006380
> Это нужно сделать отступ сверху, если сообщений слишком мало, и они начинались как бы снизу вверх, а не сверху вниз.
Это называется "вертикальное выравнивание". Я бы поискал варианты сделать это средствами CSS. Вот что приходит в голову:
- flex, новая технология, которая не везде есть. С ее помощью можно выделить часть экрана под область сообщений, и задать выравнивание блока сообщений по нижнему краю. Ну и не забыть про overflow, чтобы при большом числе сообщений блок не растягивался, а появлялась бы прокрутка. Минус - работает в новых браузерах
- display: table - работает начиная с ИЕ8 и древних фаерфоксов. Это как flex, только слабее по возможностям и со своими недостатками. Табличное отображение позволяет прижать содержимое ячейки (зоны сообщений) вниз. Тут есть проблемка - чтобы сделать прокрутку, блоку сообщений надо задать высоту, и задать ее в виде 100% (=высоте окна) наверно не получится, значит придется этот момент сделать через JS (определение доступной высоты блока и изменение ее при измнеении размера окна)
- прижать блок сообщений вниз с помощью абс. поз. Работает тоже везде, и можно задать высоту как 100% от зоны для вывода сообщений (при условии что не используется display: table, так как абс. поз. не работает с элементами таблиц).
Я бы советовал попробовать сделать отдельный пример (примеры) в jsfiddle или аналогичном сервисе, и потестировать его на маленьком и большом числе сообщений (когда должна появляться прокрутка).
Я могу подсказать по CSS, но имея частично работающий код, сделать это проще.
> Я посмотрел как такое сделано в телеграмме
Да, там почему-то выбрали такой способ. Но даже при таком подходе нет необходимости считать высоту каждого сообщения по отдельности - достаточно посчитать scrollHeight или offsetHeight блока, содержащего их.
> То есть его нужно будет вызывать рекурсивно, если нужно будет вызывать код, например, каждую секунду?
Не совсем рекурсивно, но да, примерно так:
function doStep() {
...
setTimeout(doStep, t);
}
setTimeout(doStep, t);
> но если пароль смениться, то нужно будет и перезашифровать эти сообщения для всех собеседников
Тут наверно будет проблема, что сообщений много, они могут храниться на сервере, и непонятно что делать, если программа упала на полпути в процессе перешифрования.
Советую почитать, как сделаны секретные чаты в Телеграм. Или например как сделано хранение шифрованных сообщений в iMessage. Вот тут что-то есть по теме: https://techcrunch.com/2014/02/27/apple-explains-exactly-how-secure-imessage-really-is/ (если нет, то можешь сам погуглить).
Также, можно почитать про шифрование диска в Андроид:
- https://nelenkov.blogspot.ru/2012/08/changing-androids-disk-encryption.html
- https://source.android.com/security/encryption/
- https://source.android.com/security/encryption/full-disk
Как я понимаю, там используется такая схема: генерируется ключ, им шифруется диск. А ключ шифруется паролем пользователя и сохраняется. При смене пароля перешифруется только ключ.
Кстати, так же можно и уничтожать сообщения: вместо их уничтожения достаточно уничтожить ключ, которым они зашифрованы, что гораздо быстрее.
>Неудачный код, так как никто не запрещает где-то в другом месте выставить недопустимую комбинацию свойств. Лучше использовать инкапсуляцию или хотя бы методы-сеттеры, задающие правильную комбинацию свойств.
Да, вы правы, конечно, если нужно жестче прописать значения и всегда задавать нужную комбинацию, то лучше задать это в сеттере. Я просто не подумал, что лишняя гибкость кода не всегда хорошо и привёл упрощенный пример.
Надеюсь тот анон увидит это замечание.
>> Я тестировал, и это определённо из-за того что как-то неправильно передаётся параметр :file. То есть если подставить за место этого параметра какое-нибудь значение или просто вставить переменную, то ошибки не будет.
>А что именно передается в bindValue? Скорее всего, что-то не то.
Я пытался даже передать строку сверх-принудительно $q->bindValue(':file', (string) "123", PDO::PARAM_STR);
К тому же, это не имеет значения что передавать - concat() должен преобразовать всё в строку в любом случае.
Вот подробности ошибки:
An exception occurred while executing 'INSERT INTO comments VALUES (nextval('comments_id_seq'), :file, :author, CURRENT_TIMESTAMP, :content, text2ltree(concat_ws('.', :filetree, currval('comments_id_seq'))), :depth)' with params [24, "A", "H", "123", 0]: SQLSTATE[42P18]: Indeterminate datatype: 7 ERROR: could not determine data type of parameter $4
Подозрительно, что на деле :filetree получается "123", а не "123.currval(..)"
Если избавиться от функции concat(), то всё будет ок, только нельзя будет вставить currval().
>> Иначе, я даже не догадываюсь как получить id программно.
>SELECT nextval(...);
Да, я так и делаю, только не получается почему-то.
Мне нужно сделать 2 языка: русский и английский по дефолту.
Есть какие-то удобные средства для этого?
Такой вопрос уже задавался здесь https://arhivach.org/thread/261841/#996038
Ctrl + F мультиязычность
Как минимум будет с чего начать.
Смотрел кто-нибудь? Чет начал вроде интересно, но по факту автор вроде как просто хуярит код и на выходе получается что вжух и всё работает. Ни зачем, ни почему. Может я конечно еще мало посмотрел и толком нихуя не понял. Ну в общем жду от вас какого-нибудь фидбека.
А как работает middleware? В документации довольно схематично это описано. Это просто, то что вызывает до и после контроллера?
Например
$app->get(...)->add(foo())->add(bar());
Сначала вызывается bar(), затем foo(), затем контроллер, затем снова foo(), затем снова bar(), я правильно понимаю?
А как делаются несколько соединений в Доктрине? Нужно создавать несколько EntityManager'ов?
А как работает функция readfile()? Почему если посреди кода вызвать в этой функции .php файл, он не выполниться? В документации же написано, что он выводит файл. (Хотя, это другое определение, в отличение от include, где написано включает и выполняет файл.)
В самом интерфейсе мозиллы, в форме выбора открытия или сохранения файла, тип определяется как HTML документ, хотя в загрузках отображается нормально. (В хроме проверить не получилось т.к. он сразу предлагает выбрать путь сохранения.)
Все заголовки выдаются здесь https://github.com/someApprentice/filehosting/blob/master/src/Controller/DownloadController.php#L97-L98
Что я сделал не так?
https://gist.github.com/codedokode/9424217
>прикрутить загрузку файлов простым перетаскиванием на страницу с помощью jQuery плагина (конечно по хоршему надо бы заставить написать тебя загрузчик с нуля, чтобы разобраться в яваскрипте и DOM, но это наверно сложовато)
У меня был загрузчик и на чистом js https://github.com/someApprentice/filehosting/blob/bfeedf404e8ab28de6c6bae87a1e1a679b890b6f/public/js/dragndrop.js
Но у меня есть несколько вопросов:
Функцию отмены запроса нужно выполнять после его отправки, т.е. её нужно вызвать в событии onprogress, если нужно. Я правильно понимаю?
Вы выше привили пример какие ошибки могут произойти >>1006196
Я правильно понимаю, что эти ошибки нужно обрабатывать так:
>- может нарушиться связь с интернетом, или произойти проблема где-то на полпути. Это проявляется в том, что срабатывает обработчик ошибки с кодом 0. Причем в неудачных случаях, если пакет для установления TCP соединения потерялся где-то в сети, он сработает только после таймаута в 30-60 секунд.
xhr.status == 0
xhr.ontimeout
>- сервер может быть отключен/недоступен
xhr.status == 503
>- на сервере может произойти ошибка, это проявляется в HTTP статусе ответа, не равном 200, или в неверном Content-Type ответа
xhr.status != 200 or xhr.resultType == null
>- при попытке раскодировать JSON может произойти ошибка, если он некорректный
А что тут нужно обработать?
А как реализуются каталоги? В бд сохраняются колонки с именем, урл и деревом, и затем это урл обрабатывается контроллером?
А зачем пишутся Doc-блоки, если PHPDoc всё равно не используется?
И какие тэги были бы всегда желательны?
Мультиязычность
https://arhivach.org/thread/261841/#996038
>Стоит наверно сделать разные URL для разных версий страниц, иначе нельзя дать ссылку на страницу на определенном языке и они не будут нормально индексироваться. Википедия напримр использует на каждый язык свой поддомен. Можно использовать и папку в URL.
А что насчет выдавания страницы с нужным языком по геолокации? Такие страницы тоже индексируются как надо потому что, как я полагаю, роботы гугла находятся в разных странах и выдают результат тоже в соответствии с геолокацией.
А как работает middleware? В документации довольно схематично это описано. Это просто, то что вызывает до и после контроллера?
Например
$app->get(...)->add(foo())->add(bar());
Сначала вызывается bar(), затем foo(), затем контроллер, затем снова foo(), затем снова bar(), я правильно понимаю?
А как делаются несколько соединений в Доктрине? Нужно создавать несколько EntityManager'ов?
А как работает функция readfile()? Почему если посреди кода вызвать в этой функции .php файл, он не выполниться? В документации же написано, что он выводит файл. (Хотя, это другое определение, в отличение от include, где написано включает и выполняет файл.)
В самом интерфейсе мозиллы, в форме выбора открытия или сохранения файла, тип определяется как HTML документ, хотя в загрузках отображается нормально. (В хроме проверить не получилось т.к. он сразу предлагает выбрать путь сохранения.)
Все заголовки выдаются здесь https://github.com/someApprentice/filehosting/blob/master/src/Controller/DownloadController.php#L97-L98
Что я сделал не так?
https://gist.github.com/codedokode/9424217
>прикрутить загрузку файлов простым перетаскиванием на страницу с помощью jQuery плагина (конечно по хоршему надо бы заставить написать тебя загрузчик с нуля, чтобы разобраться в яваскрипте и DOM, но это наверно сложовато)
У меня был загрузчик и на чистом js https://github.com/someApprentice/filehosting/blob/bfeedf404e8ab28de6c6bae87a1e1a679b890b6f/public/js/dragndrop.js
Но у меня есть несколько вопросов:
Функцию отмены запроса нужно выполнять после его отправки, т.е. её нужно вызвать в событии onprogress, если нужно. Я правильно понимаю?
Вы выше привили пример какие ошибки могут произойти >>1006196
Я правильно понимаю, что эти ошибки нужно обрабатывать так:
>- может нарушиться связь с интернетом, или произойти проблема где-то на полпути. Это проявляется в том, что срабатывает обработчик ошибки с кодом 0. Причем в неудачных случаях, если пакет для установления TCP соединения потерялся где-то в сети, он сработает только после таймаута в 30-60 секунд.
xhr.status == 0
xhr.ontimeout
>- сервер может быть отключен/недоступен
xhr.status == 503
>- на сервере может произойти ошибка, это проявляется в HTTP статусе ответа, не равном 200, или в неверном Content-Type ответа
xhr.status != 200 or xhr.resultType == null
>- при попытке раскодировать JSON может произойти ошибка, если он некорректный
А что тут нужно обработать?
А как реализуются каталоги? В бд сохраняются колонки с именем, урл и деревом, и затем это урл обрабатывается контроллером?
А зачем пишутся Doc-блоки, если PHPDoc всё равно не используется?
И какие тэги были бы всегда желательны?
Мультиязычность
https://arhivach.org/thread/261841/#996038
>Стоит наверно сделать разные URL для разных версий страниц, иначе нельзя дать ссылку на страницу на определенном языке и они не будут нормально индексироваться. Википедия напримр использует на каждый язык свой поддомен. Можно использовать и папку в URL.
А что насчет выдавания страницы с нужным языком по геолокации? Такие страницы тоже индексируются как надо потому что, как я полагаю, роботы гугла находятся в разных странах и выдают результат тоже в соответствии с геолокацией.
>Вопрос: если поставлю в виртуалхосте document root в папку .../Students/public, то уже не смогу как раньше ввести виртуалхост/Students, чтобы попасть в папку проекта. Значит, если я захочу иметь на одном сайте несколько пректов в разных папках, мне нужно будет парсить URL через фронт-контроллер в /public?
>парсить URL через фронт-контроллер в /public?
Это называется роутер. Но обычно он делается для одного сайта.
Попробовал gettext: не работает, что может быть не так? setlocale всегда возвращает false
putenv('LC_ALL=en_EN');
echo setlocale(LC_ALL, 'en_EN') == false ? "false" : "true";
bindtextdomain("test", "../Locale");
textdomain("test");
echo gettext("This is a text!");
Пути:
root/application/index.php
root/Locale/ru_RU/LC_MESSAGES/test.mo
msgid "This is a text!"
msgstr "Переведенная строка!"
Куда удобно, туда и клади, главное - не в публичную папку. Религиозное следование догмам не сделает твой код лучше, попробуешь написать гем для руби - поймёшь, что никакой папки src/ у них нет и там вообще многое по-другому. Главное руководствоваться здравым смыслом. Для улучшения кода есть объективно более полезные подходы - например юнит-тестирование.
>>1007720
На вёрстке гораздо быстрее можно начать зарабатывать + не нужно разбираться с экосистемой (CMS, фреймворки). Ещё вёрстка всегда пригодится в будущем, даже если на С# пишешь, а от PHP польза сомнительная.
Читай документацию.
> Returns the new current locale, or FALSE if the locale functionality is not implemented on your platform, the specified locale does not exist or the category name is invalid.
https://github.com/someApprentice/chat/blob/810302ee45dba11ea1bc22a0f157f3427011bb64/public/js/chat.js#L74
Немножко работы с отображением вылезло в контроллер, но здесь нужно так поступить, чтобы получить нужный атрибут ссылки. Такое очень критично в js?
в 2017 делать всё на прототипах , когда есть ES2016 и всякие фичи для поддержки браузеров , алсо js в php треде
Можешь попробовать, у нас кстати есть проект https://github.com/someApprentice/phpClub , куда это можно было бы включить.
Если что, мой имейл указан в профиле.
например сахар для создания классов
https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Classes
Дело в том, что метод проверки свойства сущности из СущностьValidator мне понадобился в СущностьHelper (проверка аргумента). Не хочется для public методов возвращать mixed результат.
Учти, что вкатывальщики твои тесты у себя на локалке не поднимут и по-хорошему нужен сайт, куда можно вставить решение и проверить на каких тестах что упало, вроде этого: https://dkab.github.io/jasmine-tests/
>Также, работу с АПИ наверно было бы лучше вынести в отдельный класс. Чтобы иметь возможность писать что-нибудь вроде backend.getMessages(...). Это позволит, может быть, позже туда вкрутить поддержку кеширования данных и оффлайн-режима.
Что-то мне не очень понятно как возвращать ответ из ajax, с помощью такой функции. Или вы имели ввиду чтобы getMessages() выводил шаблон с полученными сообщениями, а не возвращал json?
если быть точнее что то по типу CodeCombat
юззер заходит , пишет или копипастит решение , оно прогоняется юнит тестом и выдаёт верно или нет , при том что сам код в функционалом стиле
en_EN - такой локали нет. Есть только en_US, en_GB и может еще какие-то для других англоязычных стран.
Ты можешь сам проверить, какие локали есть в системе, в линуксе, командой
locale -a
http://ru.manpages.org/locale
Она выведет много строк, потому, возможно, стоит использовать grep.
В винде про локали можно поискать где-то тут https://msdn.microsoft.com/ru-ru/library/windows/desktop/dd318716(v=vs.85).aspx
Я для переключения языков использовал не локаль, а textdomain - можно сделать разные домены для разных переводов. Можно использовать функцию http://php.net/manual/ru/function.dgettext.php где домен указывается явно.
Я думал насчет юнит тестов, но вопрос, будут ли выводимые сообщения на русском языке и будет ли понятно пользователю, в чем проблема? Я там писал какой-то свой велосипед для этого, посмотрю, может его можно выложить.
текст ошибок можно же менять , алсо если найдёшь анон кинь тут ссыль плиз , я просто не разу дело с песочницами не имел , интересно как работает , помню игра такая была , название не вспомню , давалось задание и 2 игрока кодили на скорость , на разных языках , там всё очевидно тестами покрывалось , которые присылали как и задание в пул реквесты
Трейты используются там где есть проблемы с множественным наследованием , если у вас этих проблем нет то выносите в хеллпер
>>1007928
Вот мои недописанные проекты на эту тему:
https://github.com/codedokode/task-checker - библиотека, позволяющая писать сценарии для проверки задач. Пока на уровне демо-версии.
https://github.com/codedokode/guarddog - утилита, позволяющая ограничить программу с помощью seccomp-bpf (фильтра системных вызовов linux). Вроде работает.
https://github.com/codedokode/guardbox - демон, который должен был по задумке принимать HTTP-запросы на выполнение программ и запускать их в песочнице, но который не работает и на который я пока не вижу особого смысла тратить время
Что касается организации песочницы, есть разные способы, самый простой - запускать программу в докере, но он конечно не для этого придуман, так что не знаю, как там с надежностью. А так, можно использовать имеющиеся в линукс механизмы:
- seccomp-bpf
- неймспейсы (linux kernel namespaces)
- ulimit
- bind mounts
- tmpfs
оно, спасибо
Нет, это не MVC. У нас есть урок по нему https://github.com/codedokode/pasta/blob/master/arch/mvc.md
Погугли про относительные и абсолютные пути
http://prntscr.com/flngg7 - как визуально выглядит после действия алгоритма.
http://ideone.com/dIQpTL
Сделал аналог поиск пути Дейкстры, но получается что высчитываю только минимальный по времени путь с начальной до последний точки.
Я бы мог сделать что бы постился путь, но для этого нужен список пройденных точек, а он содержит даже бесполезные.
То есть нужно как то отделить правильные точки, от неправильных и это я не могу придумать.
http://sandbox.onlinephpfunctions.com/code/bf4b127914459970326b3cc99ffaa2f5aefdcbd4
Если наша директория выглядит так:
App
App/public
App/src
То когда мы пишем в консоле App# npm install somePackage , то в корневой папке приложения App создается папка node_modules, но папка где нужно запросить пакет находиться в директории ниже, в App/public (или, возможно, даже ещё ниже App/public/js). Это не имеет значения и require() в любом случае найдет нужный пакет? Или нужно ещё где-то настроить расположение нужной директории?
Здесь нет информации, которая бы помогла ответить на вопрос? https://nodejs.org/api/modules.html#modules_loading_from_node_modules_folders
Как я помню, по моему там папка node_modules ищется начиная с текущей директории вверх до кррня файловой системы.
1. Какая версия mysql оптимальна для Lavarel? Поставил 5.7, но боюсь проблем с STRICT модом.
2. Как избавиться на Ubuntu от вечного использование sudo, на работе я могу через vi, xdg-open и любые другие комманды писать без судо. Дома - дико не удобно.
3. Какие есть годные инет уроки, можно и англ туториал, нашел кучку русских бормочащих под нос - не ахти.
туториалы по Lavarel
Есть 2 таблицы Mysql(пик 1)
Есть код https://pastebin.com/ejSnv5HW
Есть то что выходит при работе кода(пик2)
Как сделать чтобы в столбце клиент выводило имя клиента соответсвующее его client_id
Сделал. Нормально получилось. В качестве редактора nicEdit прикрутил. Есть к нему претензии, но всё работает. Теперь в админке могу создавать, править и удалять статьи. Теперь надо будет перепилить всю вёрстку сайта, избавившись от рудиментов и костылей. Мне уже страшно. Бэк хоть логичен, что напрограммировал, то и получил. А фронт - неведомая ёбаная хуйня. Покрасил стену в красный - провалился потолок. Прикрутил потолок - перекосилась дверь и окна. Поправил их - дом вообще развалился нахрен.
низкий порог входа , соответственно квалификация мамкиных фрилансеров соответствующая , хотя при учёбе на 1 курсе , понял что на яве гавнокодить можно огого как
Ни один жаббашарп не позволяет пейсать SQL-запросы между тегами <title> и </title>.
Низкий уровень зарплаты, работа в подвалах (это где в вентиляцiи крыска здохла), перерывы между контрактами, голод, выпавшие зубы, страх и нищета.
Мало-средние васяны-кооператоры из 80-х, вечные вакансии, десятилетиями висящие в Интернете.
Умение разбираться в чужом коде...
Охуенно. Надо будет попробовать.
Олсо, видел и сопровождал сайты, написанные на аспнете (шарп). Это был полный пиздец, написанный душевнобольными.
как там в 2000 году норм ? В 2к17 есть ORM , Doctrine и тд. которые как бы намекают что писать свой SQL вообще не нужно , для 70% комерц дерьма
мы про PHP тут говорим а не про какой-то C++ лол
не добавляет запись, помогите пожалуйста!
нашел сам
Id---product_id---prop_id
1----2-------------2
2----2-------------1
3----6-------------2
4----7-------------3
5----7-------------2
6----3-------------2
Как получить id всех товаров у которых есть свойство 1 И 2?
Что значит "свойство"? В каком-то столбце имеется 1 и 2?
SELECT id FROM table_name WHERE product_id = 1 OR product_id = 2 AND prop_id = 1 OR prop_id = 2
prop_id от слова проперти , что и есть свойство
Он выберет товары которые содержат свойство или 1 или 2 а мне нудно чтоб товар содержат и оба единовременно
Есть соблазн закопаться в какую-нибудь цмску и пилить пирожки.
Отвечу сам себе, скорее всего верный ответ
SELECT prod_id FROM qwe GROUP BY prod_id WHERE COUNT(id) = 2 AND (prop_id = 1
Отступы добавить и скобки.
https://pastebin.com/ZSVnJ4A6 - а этот редактирует
Сам нашел, пздц, запятую забыл поставить.
Ты задавай вопрос, а ответит кто-то или нет это уже зависит от вопроса. Думаю на просьбу "сделайте мне курсач/сайт бесплатно без смс" добровольцев не найдется
Да, я решил отказаться от идеи, и помощь теперь нужно только в нахождении ошибки.
https://pastebin.com/FeyveKfg - не добавляет запись в базу, хз почему, найти никак не могу.
Структура базы
каких именно? если после тех которые после инсерт, то не из-за этого.
Вот это работает - https://pastebin.com/gUkezpNh
datetime это тип данных в sql, в то же время у тебя это название колонки, отрабатывающий запрос ниже без неё, чтобы указать, что это название колонки юзай `` вокруг.
mysql_query(" INSERT INTO payments(`service_name`,`service_cost`,`summ`,`datetime`)
VALUES ('$service_name','$service_cost','$summ','$datetime') ");
Пик-релейтед в ОП-посте.
Или в гугле любая ссылка по отображению ошибок.
ini_set('error_reporting', E_ALL);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
написало что это не работает:
<?php $result = mysql_query(" SELECT * FROM services "); ?>
хотя оба выпадающих списка работают
$service_id = strip_tags(trim($_POST['service_id']));
$detail_id = strip_tags(trim($_POST['detail_id']));
$summ = strip_tags(trim($_POST['summ']));
$datetime = strip_tags(trim($_POST['datetime']));
mysql_query(" INSERT INTO
payments(service_name,service_cost,summ,datetime)
VALUES ('$service_name','$service_cost','$summ','$datetime') ");
Создал одни переменные, в инсерте совсем другие(первые две)
Из-за кавычек кстати тоже не работало
>Да, нужны
ну ок (((
досмотрел эту залупу. След тема PDO - интересная тема, схавалась на лету, а следом за ней пошло опять говнище под названием Reflection API, что по этому поводу скажешь? Очень нужное?
>>1009440
Что там в Reflection и SPL изучать? Итерабельный объект, это объект, по которому можно пройтись foreach'ем. Пример - класс ошибок валидации в symfony/validation: https://github.com/symfony/validator/blob/master/ConstraintViolationList.php#L113
На рефлексии работает разного рода магия, тот же ActiveRecord в Laravel. Без понимания этой магии сложно написать что-то сложнее TODO-приложухи. Ну и я бы советовал не уроки от Васянов смотреть, а пилить что-то своё. В какой-нибудь консольной игре на PHP однозначно можно применить итераторы с SPL.
>>1009669
Не нужен джойн там.
>>1009584
select product_id
from tovar
GROUP BY product_id
having sum(if(prop_id = 1, 1, 0)) and sum(if(prop_id = 2, 1, 0))
>>1009595
Через sum(if(condition, if_true, if_false))
>Не нужен джойн там.
Каким образом ты можешь без JOIN'а связать одним запросом разные записи связанные только по product_id ?
с Join'ом это выглядит так
SELECT pr1.product_id FROM `table` as pr1
INNER JOIN `table` as pr2 USING(product_id)
WHERE pr2.prop_id = 1 AND pr1.prop_id = 2
Покажи как ты считаешь это может выглядеть без JOIN'а и учти что таблица нормализована.
нужно чтобы вывелись все поля из таблицы sd_dilers, которые использовались в другой таблице - таблице заказов sd_orders
пишу так
select sd.* from sd_dilers sd
LEFT JOIN sd_orders so ON so.shop_id = sd.id
WHERE so.date >= '2017-06-19 13:33:18
'
не выхходит
>>1009877
>select product_id
>from tovar
>GROUP BY product_id
>having sum(if(prop_id = 1, 1, 0)) and >sum(if(prop_id = 2, 1, 0))
Неоптимизированный запрос, даже в моём варианте с джоинами на 3+ джоинах отработает быстрее.
Пик-1 сравнение (первый - твой, второй - мой). Визуал эксплейны на обоих запросах пик 2-3
Алсо тест проводил на таблице с 8.7кк записей, пик - пересечение на 4ёх параметрах
Нет
Есть, Hacklang
Не обязательно. Но тогда ты должен написать в нём методы так, чтобы они не меняли состояния программы, а "считали" что-то, т.е. получали на вход какое-то значение, а на выходе возвращали новое.
Например: $mb = transformBytesToMegaBytes($file->getSize());
Плохой пример:
transformBytesToMegaBytes($file)
{
...
$file->setSize($mb);
}
96 строка, не берется $id, помогите плиз, если вместо $id подставить число соответсвующее $id то все нормально сохраняется.
>>1000716
Вообще, идея микрофреймворка в том, что сайты на нем простые и контроллеры легко умещаются в функции.
Ну раз ты решил сделать их отдельными классами, пусть будут, почему бы и нет.
Также, я вижу, ты там прикрепляешь защиту от csrf как middleware. Но тут есть подвох, где гарантия, что ты не забудешь прикрепить это middleware? Лучше было бы включить защиту по умолчанию для всех POST и аналогичных (PUT, DELETE) запросов, а потом сделать опцию для отключения ее там, где это не требуется.
Также, ты в начале кода открываешь сессию. На мой взгляд это плохой паттерн, хотя бы потому, что пока сессия открыта, другой процесс не может ее открыть, и значит, запросы от одного пользователя не могут обрабатываться параллельно. Вряд ли тебе сессия нужна постоянно - может только в отдельных местах кода - и было бы лучше только там ее и использовать. А может она и не так и нужна.
Сессию также плохо использовать для авторизации, так как она удаляется через 20-30 минут неиспользования.
Также, надо помнить, что сессия общая для всех вкладок в браузере.
В случае ботов, которые не сохраняют куки, сессии создаются для них на каждый запрос.
Также, в случае с CSRF обрати внимание на этот момент: https://github.com/slimphp/Slim-Csrf#token-persistence
По умолчанию они генерируют новый (одноразовый) токен на каждый реквест, вопрос, будут ли корректно работать аякс-запросы, будут ли работать страницы, открытые в соседних вкладках? Как по мне, так это оверинжиниринг, вполне хватает одного долговечного токена, который защищает от CSRF. Есть конечно мнения, что так надежнее, но я подробно их не изучал, предлагаю тебе (раз ты выбрал этот подход) найти причины, чем одноразовые токены лучше и от каких атак они защищают дополнительно.
https://github.com/someApprentice/filehosting/blob/master/config/config.ini
Я думаю, что в конфиг не нужно выносить настройки, которые пользователь поменять все равно не может (тип БД и кодировка). Их можно просто прописать где-то в коде создания соединения с БД.
https://github.com/someApprentice/filehosting/blob/master/config/cli-config.php
Это наверно не совсем конфиг
https://github.com/someApprentice/filehosting/blob/master/config/sphinx.conf
Для конфигов есть интересная фича, которая позволяет не копипастить какие-то куски по всему конфигу (например, путь к корневой папке, где хранятся файлы индексов): http://sphinxsearch.com/blog/2013/11/05/sphinx-configuration-features-and-tricks/
Также, есть еще такой формат хранения конфигов, как dotEnv. Суть в том, что конфиг представляется в виде шелл-скрипта, задающего переменные окружения:
APP_DB_HOST=database
APP_DB_PASS=123456
APP_SPHINX_ROOT=/var/lib/sphinxsearch
Идея в том, что пользователь приложения загружает этот скрипт командой source config.env, затем запускает скрипт командной строки и приложение берет конфигурацию из переменных окружения. В случае запуска под php-fpm или Апачем, разумеется, эти переменные тоже надо как-то передать, например, их можно прописать в конфиге веб-сервера или заставить приложение читать их из .env файла.
Ну то есть может быть правило вроде такого, что сначала читается .env файл, если он есть, и значения из него переопределяются переменными окружения или как-то так.
Популярным этот подход стал с распространением докера, облачными сервисами, где часто файловая система доступна только на чтение и конфиг удобнее передавать через переменные окружения. А на дев-сервере - прописать в виде файла.
Плюсы этого подхода описаны тут: https://12factor.net/config Дополнительынй плюс в том, что переменные окружения доступны программе на любом языке программирования. Как вариант, есть php-библиотека dotEnv, парсящая такие конфиги напрямую.
Минусы там не описаны, и в восторженных статьях их тоже почему-то не пишут, но я-то понимаю, что тут не все просто:
- при использовании нескольких приложений надо перезагружать их переменные, или использовать отдельные консоли
- значения конфига могут быть только строками, не массивами, не булевыми значениями
- в случае опечатки никаких предупреждений не будет выведено
- у bash сложные правила экранирования спецсимволов
- не очень понятно, какие возможности и какой оболочки мы можем там использовать. sh? bash?
Я вспомнил про этот вариант, чтобы придумать, как можно вынести конфигурацию сфинкса в общий конфиг. Хотя конечно, можно читать ее и из обычного ini-файла.
Библиотеки
- https://github.com/vlucas/phpdotenv
- https://github.com/josegonzalez/php-dotenv
Есть конечно и другие подходы. Симфони использует один файл конфига и несколько файлов с параметрами вроде parameters.yml, parameters.dev.yml. Там еще приходится изощряться из-за того, что когда несколько разработчиков, им может понадобиться переопределить какие-то параметры конфига у себя.
https://github.com/someApprentice/filehosting/blob/master/src/Types/Ltree.php
Вот тут, раз уж ты определил тип для ltree, можно было бы в PHP сделать более удобное представление. Ну например, представлять путь в виде массива значений. Или в виде объекта TreePath с разными удобными методами манипулирования путем (добавление значений, получение глубины, и тд).
https://github.com/someApprentice/filehosting/blob/master/src/init.php#L49
Параметры соединения со сфинксом почему-то в конфиг не вынесены в отличие от БД.
Также, в ридми нет описания, как развернуть проект. Да и ридми самого тоже нет.
> $basePath = rtrim(str_ireplace('index.php', '', $c['request']->getUri()->getBasePath()), '/');
Вот эта строчка как-то странно выглядит - она точно правильная? Там не dirname() должно быть или что-то такое?
https://github.com/someApprentice/filehosting/blob/master/src/Model.php
Вот тут класс по моему неправильно назван. Это модель чего? По моему тут просто свалены в кучу разные вспомогательные функции.
https://github.com/someApprentice/filehosting/blob/master/src/Entity/File.php#L13
> protected $originalname;
Надо разделять слова, originalName.
В сущности надо прописывать значения по умолчанию для полей, если они есть в БД. Кстати, а где в проекте дамп структуры БД? Или миграции?
> / @Column(type="json_array") /
Возможно, было бы лучше представить информацию о файле в виде объекта MediaInfo с разными полезными методами, а не в виде массива непонятной структуры.В него бы хорошо поместились функции вроде isAudio().
> public function setDate()
> $this->date = new \Datetime("now");
Функцию тогда стоило назвать вроде setDateNow().
https://github.com/someApprentice/filehosting/blob/master/src/Entity/File.php#L129
> return json_decode($this->info);
Нет проверки на ошибку распаковки json - в твоем коде она просто игнорируется. Никаких предупреждений не будет.
По доктрине - разобрался ли ты с генерацией прокси-классов и кешем метаданных? В dev режиме удобнее, чтобы они генерировались на лету, но в продакшен режиме их выгоднее генерировать в момент деплоя либо при первом обращении, иначе производительность снизится.
> $csrfNameKey = $this->csrf->getTokenNameKey();
> $csrfValueKey = $this->csrf->getTokenValueKey();
Эту копипасту, которая повторяется в каждом экшене, надо как-то убрать.
https://github.com/someApprentice/filehosting/blob/master/src/Controller/IndexController.php#L56
Тут стена кода, и никакого намека на MVC - все делается прямо в контроллере. Это так называемый "толстый" контроллер. Разделение на MVC должно позволять сделать любую операцию программно, но у тебя это невозможно, так как нужный код находится в контроллере.
Чтобы лучше понять разделение на M, V и C, сделай консольную команду для загрузки файлов (со всеми нужными валидациями), например, такого вида:
$ php cli/upload.php /tmp/1.jpg /tmp/2.jpg
https://filebox.example/file/123
https://filebox.example/file/124
А также, команду для поиска файлов из консоли.
>>1000716
Вообще, идея микрофреймворка в том, что сайты на нем простые и контроллеры легко умещаются в функции.
Ну раз ты решил сделать их отдельными классами, пусть будут, почему бы и нет.
Также, я вижу, ты там прикрепляешь защиту от csrf как middleware. Но тут есть подвох, где гарантия, что ты не забудешь прикрепить это middleware? Лучше было бы включить защиту по умолчанию для всех POST и аналогичных (PUT, DELETE) запросов, а потом сделать опцию для отключения ее там, где это не требуется.
Также, ты в начале кода открываешь сессию. На мой взгляд это плохой паттерн, хотя бы потому, что пока сессия открыта, другой процесс не может ее открыть, и значит, запросы от одного пользователя не могут обрабатываться параллельно. Вряд ли тебе сессия нужна постоянно - может только в отдельных местах кода - и было бы лучше только там ее и использовать. А может она и не так и нужна.
Сессию также плохо использовать для авторизации, так как она удаляется через 20-30 минут неиспользования.
Также, надо помнить, что сессия общая для всех вкладок в браузере.
В случае ботов, которые не сохраняют куки, сессии создаются для них на каждый запрос.
Также, в случае с CSRF обрати внимание на этот момент: https://github.com/slimphp/Slim-Csrf#token-persistence
По умолчанию они генерируют новый (одноразовый) токен на каждый реквест, вопрос, будут ли корректно работать аякс-запросы, будут ли работать страницы, открытые в соседних вкладках? Как по мне, так это оверинжиниринг, вполне хватает одного долговечного токена, который защищает от CSRF. Есть конечно мнения, что так надежнее, но я подробно их не изучал, предлагаю тебе (раз ты выбрал этот подход) найти причины, чем одноразовые токены лучше и от каких атак они защищают дополнительно.
https://github.com/someApprentice/filehosting/blob/master/config/config.ini
Я думаю, что в конфиг не нужно выносить настройки, которые пользователь поменять все равно не может (тип БД и кодировка). Их можно просто прописать где-то в коде создания соединения с БД.
https://github.com/someApprentice/filehosting/blob/master/config/cli-config.php
Это наверно не совсем конфиг
https://github.com/someApprentice/filehosting/blob/master/config/sphinx.conf
Для конфигов есть интересная фича, которая позволяет не копипастить какие-то куски по всему конфигу (например, путь к корневой папке, где хранятся файлы индексов): http://sphinxsearch.com/blog/2013/11/05/sphinx-configuration-features-and-tricks/
Также, есть еще такой формат хранения конфигов, как dotEnv. Суть в том, что конфиг представляется в виде шелл-скрипта, задающего переменные окружения:
APP_DB_HOST=database
APP_DB_PASS=123456
APP_SPHINX_ROOT=/var/lib/sphinxsearch
Идея в том, что пользователь приложения загружает этот скрипт командой source config.env, затем запускает скрипт командной строки и приложение берет конфигурацию из переменных окружения. В случае запуска под php-fpm или Апачем, разумеется, эти переменные тоже надо как-то передать, например, их можно прописать в конфиге веб-сервера или заставить приложение читать их из .env файла.
Ну то есть может быть правило вроде такого, что сначала читается .env файл, если он есть, и значения из него переопределяются переменными окружения или как-то так.
Популярным этот подход стал с распространением докера, облачными сервисами, где часто файловая система доступна только на чтение и конфиг удобнее передавать через переменные окружения. А на дев-сервере - прописать в виде файла.
Плюсы этого подхода описаны тут: https://12factor.net/config Дополнительынй плюс в том, что переменные окружения доступны программе на любом языке программирования. Как вариант, есть php-библиотека dotEnv, парсящая такие конфиги напрямую.
Минусы там не описаны, и в восторженных статьях их тоже почему-то не пишут, но я-то понимаю, что тут не все просто:
- при использовании нескольких приложений надо перезагружать их переменные, или использовать отдельные консоли
- значения конфига могут быть только строками, не массивами, не булевыми значениями
- в случае опечатки никаких предупреждений не будет выведено
- у bash сложные правила экранирования спецсимволов
- не очень понятно, какие возможности и какой оболочки мы можем там использовать. sh? bash?
Я вспомнил про этот вариант, чтобы придумать, как можно вынести конфигурацию сфинкса в общий конфиг. Хотя конечно, можно читать ее и из обычного ini-файла.
Библиотеки
- https://github.com/vlucas/phpdotenv
- https://github.com/josegonzalez/php-dotenv
Есть конечно и другие подходы. Симфони использует один файл конфига и несколько файлов с параметрами вроде parameters.yml, parameters.dev.yml. Там еще приходится изощряться из-за того, что когда несколько разработчиков, им может понадобиться переопределить какие-то параметры конфига у себя.
https://github.com/someApprentice/filehosting/blob/master/src/Types/Ltree.php
Вот тут, раз уж ты определил тип для ltree, можно было бы в PHP сделать более удобное представление. Ну например, представлять путь в виде массива значений. Или в виде объекта TreePath с разными удобными методами манипулирования путем (добавление значений, получение глубины, и тд).
https://github.com/someApprentice/filehosting/blob/master/src/init.php#L49
Параметры соединения со сфинксом почему-то в конфиг не вынесены в отличие от БД.
Также, в ридми нет описания, как развернуть проект. Да и ридми самого тоже нет.
> $basePath = rtrim(str_ireplace('index.php', '', $c['request']->getUri()->getBasePath()), '/');
Вот эта строчка как-то странно выглядит - она точно правильная? Там не dirname() должно быть или что-то такое?
https://github.com/someApprentice/filehosting/blob/master/src/Model.php
Вот тут класс по моему неправильно назван. Это модель чего? По моему тут просто свалены в кучу разные вспомогательные функции.
https://github.com/someApprentice/filehosting/blob/master/src/Entity/File.php#L13
> protected $originalname;
Надо разделять слова, originalName.
В сущности надо прописывать значения по умолчанию для полей, если они есть в БД. Кстати, а где в проекте дамп структуры БД? Или миграции?
> / @Column(type="json_array") /
Возможно, было бы лучше представить информацию о файле в виде объекта MediaInfo с разными полезными методами, а не в виде массива непонятной структуры.В него бы хорошо поместились функции вроде isAudio().
> public function setDate()
> $this->date = new \Datetime("now");
Функцию тогда стоило назвать вроде setDateNow().
https://github.com/someApprentice/filehosting/blob/master/src/Entity/File.php#L129
> return json_decode($this->info);
Нет проверки на ошибку распаковки json - в твоем коде она просто игнорируется. Никаких предупреждений не будет.
По доктрине - разобрался ли ты с генерацией прокси-классов и кешем метаданных? В dev режиме удобнее, чтобы они генерировались на лету, но в продакшен режиме их выгоднее генерировать в момент деплоя либо при первом обращении, иначе производительность снизится.
> $csrfNameKey = $this->csrf->getTokenNameKey();
> $csrfValueKey = $this->csrf->getTokenValueKey();
Эту копипасту, которая повторяется в каждом экшене, надо как-то убрать.
https://github.com/someApprentice/filehosting/blob/master/src/Controller/IndexController.php#L56
Тут стена кода, и никакого намека на MVC - все делается прямо в контроллере. Это так называемый "толстый" контроллер. Разделение на MVC должно позволять сделать любую операцию программно, но у тебя это невозможно, так как нужный код находится в контроллере.
Чтобы лучше понять разделение на M, V и C, сделай консольную команду для загрузки файлов (со всеми нужными валидациями), например, такого вида:
$ php cli/upload.php /tmp/1.jpg /tmp/2.jpg
https://filebox.example/file/123
https://filebox.example/file/124
А также, команду для поиска файлов из консоли.
https://github.com/someApprentice/filehosting/blob/master/src/Controller/SearchController.php#L78
> echo json_encode($unique_results);
Надо наверно использовать объект Response.
Ну и стандартный вопрос, как насчет автоматизированных тестов для проекта? Урок есть в ОП посте.
https://github.com/someApprentice/filehosting/blob/master/src/Controller/DownloadController.php#L97
> header("Content-disposition: attachment; filename=\"{$file->getOriginalName()}\"");
Заголовки надо ставить через Response (поможет при тестировании), а также почитай комментарии к задаче по поводу того, как задать имя файла при скачивании. В заголовках можно использовать только символы ASCII (то есть латиницу).
https://github.com/someApprentice/filehosting/blob/master/templates/base.html
Этот файл правильнее назвать base.html.twig
> <img class="media pull-left audio-artwork" src="/{{ file.getThumbnail() }}">
Получение путей и URL должно делаться в какой-то одной функции, в одном месте кода. Так как это отдельное действие, которое не надо размазывать по шаблонам.
> {% if file.getInfo().tags.id3v2.artist is defined %}
Ну вот тут бы и пригодился объект с методом getArtistName() или getProperty('id3v3.artist').
Насчет имен CSS классов, почитай про БЭМ (именно про подход к названиям CSS-классов, хотя если интересно, можно и остальное почитать).
> <audio class="media" src="/{{ file.getPath() }}/{{ file.getNewName() }}" controls></audio>
Тут может быть стоило бы добавлять проверки, поддерживается ли формат файла в браузере и ОС? Есть статья по теме https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats
Таблица очень сложная (+ 26 примечаний), и на мой взгляд, оптимальный способ - если есть JS, то проверять через canPlay(), если нет - можно просто выводить плеер в надежде что заработает. Такое вот получилось HTML5 audio/video.
Файлы бутстрапа не стоит перемешивать с другими, а стоит вынести в отдельную папку - так проще понять, где чье, так проще обновлять их.
> $('form[action="/search"]
Лучше дать форме идентификатор. Или передавать в функцию объект формы:
<script>
var form = ... ;
setupAutocomplete(form);
Вот я вижу, у тебя код завернут в $.ready(), так часто делают, и используют в статьях, но по моему, это плохой способ:
- непонятно, где используется этот код инициализации. Со временем на сайте накапливаются огромные блоки $.ready, и никто не решается их трогать, и они выполняются при каждой загрузке страницы, хотя там может быть половина кода уже не нужна
Мне больше нравится подход, когда код инициализации вызвыается явно.
И есть еще такой момент: страница загружается не мгновенно. Браузер парсит HTML постепенно, и есть промежуток, когда элемент отображается на странице, но скрипт для него, который идет ниже, еще не загружен и не выполнен. Учтено ли это?
И еще, насчет драг-н-дропа: его надо протестировать в имеющихся браузерах в разных условиях:
- перетаскивание 1 файла
- перетаскивание нескольких файлов
- перетаскивание папки
- перетаскивание картинки из браузера
- перетаскивание картинки из офисного текстового редактора вроде ворда или опенофиса
По хорошему, надо бы проверять, есть ли в браузере поддержка перетаскивания, и показывать зону для перетаскивания только когда скрипт загружен и поддержка есть.
> $(this).css('border-color', 'black');
Это непраивльно, стили должны быть в CSS, тут надо просто добавлять класс.
> $('form[action="/"]')
Тут лучше использовать идентификатор, класс (js-upload-form) или передавать объект формы, а то стоит чуть поменять URL и JS код сломается. Ненадежно.
> $.each($('form[action="/"] input[type="hidden"]'), function(i, csrf) {
> formdata.append($(csrf).attr('name'), $(csrf).attr('value'));
FormData можно создавать на основе формы: https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects#Retrieving_a_FormData_object_from_an_HTML_form
https://github.com/someApprentice/filehosting/blob/master/public/js/dragndrop.js#L14
Тут по моему не выполняются советы из урока про аякс: https://github.com/codedokode/pasta/blob/master/js/ajax.md
После загрузки файла надо редиректить на его страницу.
Не надо приписывать форму к каждому комментарию. Гораздо выгоднее переносить одну форму между комментариями.
https://github.com/someApprentice/filehosting/blob/master/src/Controller/SearchController.php#L78
> echo json_encode($unique_results);
Надо наверно использовать объект Response.
Ну и стандартный вопрос, как насчет автоматизированных тестов для проекта? Урок есть в ОП посте.
https://github.com/someApprentice/filehosting/blob/master/src/Controller/DownloadController.php#L97
> header("Content-disposition: attachment; filename=\"{$file->getOriginalName()}\"");
Заголовки надо ставить через Response (поможет при тестировании), а также почитай комментарии к задаче по поводу того, как задать имя файла при скачивании. В заголовках можно использовать только символы ASCII (то есть латиницу).
https://github.com/someApprentice/filehosting/blob/master/templates/base.html
Этот файл правильнее назвать base.html.twig
> <img class="media pull-left audio-artwork" src="/{{ file.getThumbnail() }}">
Получение путей и URL должно делаться в какой-то одной функции, в одном месте кода. Так как это отдельное действие, которое не надо размазывать по шаблонам.
> {% if file.getInfo().tags.id3v2.artist is defined %}
Ну вот тут бы и пригодился объект с методом getArtistName() или getProperty('id3v3.artist').
Насчет имен CSS классов, почитай про БЭМ (именно про подход к названиям CSS-классов, хотя если интересно, можно и остальное почитать).
> <audio class="media" src="/{{ file.getPath() }}/{{ file.getNewName() }}" controls></audio>
Тут может быть стоило бы добавлять проверки, поддерживается ли формат файла в браузере и ОС? Есть статья по теме https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats
Таблица очень сложная (+ 26 примечаний), и на мой взгляд, оптимальный способ - если есть JS, то проверять через canPlay(), если нет - можно просто выводить плеер в надежде что заработает. Такое вот получилось HTML5 audio/video.
Файлы бутстрапа не стоит перемешивать с другими, а стоит вынести в отдельную папку - так проще понять, где чье, так проще обновлять их.
> $('form[action="/search"]
Лучше дать форме идентификатор. Или передавать в функцию объект формы:
<script>
var form = ... ;
setupAutocomplete(form);
Вот я вижу, у тебя код завернут в $.ready(), так часто делают, и используют в статьях, но по моему, это плохой способ:
- непонятно, где используется этот код инициализации. Со временем на сайте накапливаются огромные блоки $.ready, и никто не решается их трогать, и они выполняются при каждой загрузке страницы, хотя там может быть половина кода уже не нужна
Мне больше нравится подход, когда код инициализации вызвыается явно.
И есть еще такой момент: страница загружается не мгновенно. Браузер парсит HTML постепенно, и есть промежуток, когда элемент отображается на странице, но скрипт для него, который идет ниже, еще не загружен и не выполнен. Учтено ли это?
И еще, насчет драг-н-дропа: его надо протестировать в имеющихся браузерах в разных условиях:
- перетаскивание 1 файла
- перетаскивание нескольких файлов
- перетаскивание папки
- перетаскивание картинки из браузера
- перетаскивание картинки из офисного текстового редактора вроде ворда или опенофиса
По хорошему, надо бы проверять, есть ли в браузере поддержка перетаскивания, и показывать зону для перетаскивания только когда скрипт загружен и поддержка есть.
> $(this).css('border-color', 'black');
Это непраивльно, стили должны быть в CSS, тут надо просто добавлять класс.
> $('form[action="/"]')
Тут лучше использовать идентификатор, класс (js-upload-form) или передавать объект формы, а то стоит чуть поменять URL и JS код сломается. Ненадежно.
> $.each($('form[action="/"] input[type="hidden"]'), function(i, csrf) {
> formdata.append($(csrf).attr('name'), $(csrf).attr('value'));
FormData можно создавать на основе формы: https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects#Retrieving_a_FormData_object_from_an_HTML_form
https://github.com/someApprentice/filehosting/blob/master/public/js/dragndrop.js#L14
Тут по моему не выполняются советы из урока про аякс: https://github.com/codedokode/pasta/blob/master/js/ajax.md
После загрузки файла надо редиректить на его страницу.
Не надо приписывать форму к каждому комментарию. Гораздо выгоднее переносить одну форму между комментариями.
Можно, но по моему смысла нет. В случае страницы ошибки, мы хотим сделать код как можно проще, чтобы не получить ошибку внутри обработчика ошибок. Что касается 404, то такой проблемы там нет, просто можно и без контроллера ее вывести. В микрофреймворках обычно есть какой-то стандартный генератор таких страниц ошибок, и надо просто его заменить. Видимо на второй картинке это и делается.
>>1010086
С умелыми руками - возможны любые уязвимости. Особенно если копировать код из плохих устаревших уроков и учебников.
>>1010047
Можно задать в контроллере, можно в шаблоне. Если сложная логика, то ее точно в шаблон тащить не стоит.
>>1010046
Это не то же самое, что TypeScript.
>>1010034
Ты тут постишь свой код, но тебе надо немного остановиться и изучить важные базовые вещи, прежде чем писать такие скрипты:
- отделение логики от HTML-шаблона
- MVC
- Защита от SQL инъекций (у тебя сейчас 100% инъекция)
- использование PDO вместо давно устревших mysql-функций
- защита от XSS-уязвимостей (тоже имеются)
Про уязвимости почитать можно эти статьи: https://github.com/codedokode/pasta/tree/master/security
Ну а вообще, я бы советовал решить сначала задачу про студентов из ОП поста - там много-много полезных советов по тому, как писать приложения с формами и базой данных.
И включи отображение ошибок, если не включил еще.
Пока качество кода низкое, такой код в продакшен запускать нельзя.
Можно, но по моему смысла нет. В случае страницы ошибки, мы хотим сделать код как можно проще, чтобы не получить ошибку внутри обработчика ошибок. Что касается 404, то такой проблемы там нет, просто можно и без контроллера ее вывести. В микрофреймворках обычно есть какой-то стандартный генератор таких страниц ошибок, и надо просто его заменить. Видимо на второй картинке это и делается.
>>1010086
С умелыми руками - возможны любые уязвимости. Особенно если копировать код из плохих устаревших уроков и учебников.
>>1010047
Можно задать в контроллере, можно в шаблоне. Если сложная логика, то ее точно в шаблон тащить не стоит.
>>1010046
Это не то же самое, что TypeScript.
>>1010034
Ты тут постишь свой код, но тебе надо немного остановиться и изучить важные базовые вещи, прежде чем писать такие скрипты:
- отделение логики от HTML-шаблона
- MVC
- Защита от SQL инъекций (у тебя сейчас 100% инъекция)
- использование PDO вместо давно устревших mysql-функций
- защита от XSS-уязвимостей (тоже имеются)
Про уязвимости почитать можно эти статьи: https://github.com/codedokode/pasta/tree/master/security
Ну а вообще, я бы советовал решить сначала задачу про студентов из ОП поста - там много-много полезных советов по тому, как писать приложения с формами и базой данных.
И включи отображение ошибок, если не включил еще.
Пока качество кода низкое, такой код в продакшен запускать нельзя.
Для вызова статических методов объект не нужен, так что не надо.
>>1009985
Почти каждый день.
>>1009919
Справедливости ради, это еще зависит от того, много ли товаров с таким значением атрибутов или нет. Если их там условно 5% по каждому атрибуту, то джойны намного быстрее, если эти атрибуты есть у 95% товаров, то джойны окажутся медленнее одного прохода с группировкой.
>>1009912
Какая ошибка пишется?
>>1009836
Нужно включить отображение ошибок, или читать логи.
>>1009803
Нельзя данные напрямую в запрос подставлять, уязвимость.
Боюсь что для пересечения по 5-6 атрибутам в многие-ко-многим денормализация никак не поможет. Если таблица большая, то придется что-то нестандартное выдумывать. Если не очень большая, то лучше просто сделать, чтобы она помещалась целиком в RAM (если брать цифры с потолка, то обход миллиона строк займет порядка несколько секунд).
>>1009237
Вообще, Доктрина не все хорошо умеет делать, и SQL использовать придется. Она не для того, чтобы не надо было изучать SQL, приудмана (а для маппинга записей на объекты).
>>1009175
Большинство их, я думаю, сами не могут внятно объяснить, почему.
>>1009213
Ява хороший язык, но код на нем писаться будет медленнее и дороже, чем на PHP.
>>1009103
Не знаю, а как Гугл и Яндекс решают проблему? Они же парсят другие сайты и их никто не банит, что тебе мешает так же поступить? Например, сделать так, чтобы от этого было взаимно выгодно и тебе и владельцам тех сайтов - и я уверен, ситуация изменится.
Вообще, конечно, местами до маразма доходит - в США за скрейпинг вполне можно попасть под суд ( https://en.wikipedia.org/wiki/Web_scraping#Legal_issues ). Мне кажется, это неправильно, это например ограничивает возможности для исследований, анализа данных. Перегружать сервер безнаказанно конечно должно быть нельзя, воровать плоды чужого труда нельзя, но для своих каких-то целей, почему бы и не скрейпить. А то у них под этим поводом банят софт для сравнения цен. Это же бред - цены на сайте это публично доступная информация.
>>1009092
Задачки на CSS из ОП поста порешать не хочешь?
А зачем ты так часто используешь sudo? Может ты делаешь что-то не так? Вообще, если это твой компьютер, к которому никто больше доступа не имеет, можно в конфиге судо убрать запрос пароля для определенных команд или вообще для любых команд.
>>1008889
Я думаю, node_modules лучше создавать в корне проекта, а не раскидывать эти паки везде.
>>1008623
Имена методов принято писать с маленькой буквы в PSR, вокруг -> пробелы не ставятся.
> AddDepartment($department)
Тут нужен тайп-хинт
> foreach ($this -> departments as $key => $value)
key тут не нужен, а value надо назвать department.
> GetMedianSolary
Придерусь к мелочам, но это не медианная, а средняя (average) зарплата. Медианная - это такая, выше которой зарплата ровно половины работников.
> public function StimulationAnalyst()
Методы, отвечающие за антикризисные меры, правильнее вынести в отдельный класс (АнтикризисныйКомитет). Так как иначе ты в класс Компания сваливаешь все подряд, и его зона ответственности становится расплывчатой. Этот метод явно нужен только для антикризисных мер.
> $department -> StimulationAnalyst();
Имена методов должны начинаться с глагола, чтоДелать, stimulateAnalysts()
> public function GetFiredEnginiers()
Метод называется "получить уволенных инженеров", и судя по названию, никого увольнять не должен. Неудачное название.
> public function AddEmployee($count,$range,$Profession, $IsHead)
Лучше передавать объект инженера, это дает больше гибкости.
> public function RiseRange($percent, $profession)
Это слишком специфичный метод, сделанный для конкретной задачи. Я бы советовал сделать в департаменте общие методы вроде "найти сотрудника", "уволить сотрудника", а тут явно код который должен быть где-то в другом месте.
Само повышение ранга сделано не очень правильно. Не надо создавать нового работника, логичнее поднять ранг существующему. Иначе можно получить много проблем, например, то, что где-то в программе остаются ссылки на старый объект или то, что какие-то свойства не перенесутся в нового работника.
Вместо removeHeadDepartment правильнее наверно сделать метод "заменитьГлавуНаНового".
> $Temployees
Плохое название.
> return $this -> coffeeCoef(1+(int)$this -> isHead);
Тут лучше использовать if или тернарный оператор для читабельности. Формулы получились малопонятными.
> public function SetIsDifferentHead
Неудачный метод так как непонятно, какой будет результат вызова. Лучше явно передавать флаг.
> boolval(1-$b);
!$b
> public function SetCoefs($coffeeCoef, $solaryCoef, $reportsCoef)
Неудачный метод, так как нельзя поменять одно свойство.
Ты сделал наследование, но где гарантия, что наследник задаст коэффициенты? Лучше исопльзовать абстрактный метод, чтобы обязать наследника указать начальные значения коэффициентов.
Про инкапсуляцию.
-------------------
Инкапсуляция. У этого слова есть разные определения, в том числе такие что ничего не понять, потому объясню простыми словами.
Суть инкапсуляции в том, что класс скрывает (инкапслирует) в себе логику работы с данными и сами данные, а наружу выставляет методы. Пользователю этих методов не важно, как класс устроен внутри, как он хранит данные, ему достаточно вызвать нужный метод чтобы получить результат.
Это упрощает понимание кода: тебе не надо читать и разбирать код класса, достаточно прочитать название метода (и может быть комментарий к нему). Также, это упрощает изменение кода: если какое-то свойство имеет уровень private то доступ к нему возможен только из того же класса и тебе не надо бегать по всему коду и смотреть что там с этим свойством делается, тебе достаточно просмотреть один файл с этим классом.
Как плюс, мы можем поставить какие-то проверки в методах, и запретить установку неправильных значений свойств. Таким образом, снаружи записать неправльное значение в объект будет нельзя и автор класса может гарантировать его корректную работу в любой ситуации.
Инкапсуляция это хорошо. Так как весь код, который занимается одной задачей, оказывается заключен внутри одного класса. Противоположный случай это когда код (или знание о его внутреннем устройстве) вылезает из класса и размазывается по всей программе.
Если проводить аналогии, то можно представить кофе-машину. Ты нажимаешь кнопку (=вызываешь публичный метод) и получаешь кофе (=результат вызова этого метода), при этом ты не видишь что происходит внутри нее и тебе не надо в этом разбираться.
Лучше наверно привести пример, чем обсуждать абстрактные вещи. Допустим, мы решили представить ломаную линию из нескольких отрезков (например, маршрут на карте) в виде объекта. Для начала, мы спроектируем, что можно будет делать с нашим объектом:
- создать, указав начальную точку (мы решили, что ломаная линия, не содержащая ни одной точки, невозможна, потому требуем передать начальные координаты в конструктор. Я сейчас не могу сказать как выгоднее - разрешить линии без точек вообще, требовать наличие минимум одной или требовать минимум 2 точки. Это надо сравнивать)
- добавить еще одну точку к линии
- найти длину линии (т.е сумму длин всех ее отрезков)
Теперь, имея этот список, мы можем написать класс PolyLine:
class PolyLine
{
// Массив со списком точек
private $points = [];
public function __construct($x, $y)
{
$this->points[] = [$x, $y];
}
public function addPoint($x, $y)
{
$this->points[] = [$x, $y];
}
/ Посчитать общую длину линии /
public function calculateLength()
{
$length = 0;
for ($i = 1; $i < count($this->points)) {
$length += $this->calculateSegmentLength(
$this->points[$i - 1][0],
$this->points[$i - 1][1],
$this->points[$i][0],
$this->points[$i][1]
);
}
return $length;
}
private function calculateSegmentlength($x1, $y1, $x2, $y2) { ... }
}
Вот в этом классе мы делаем публичными только те методы, которые запроектировали выше. А все остальное мы закрываем. Например, мы закрываем от доступа снаружи массив points. Это имеет такие преимущества:
- мы гарантируем что никто нам не положит в массив что-то не то, например, какую-то строку или что-то еще
- людям, которые изучают наш код, не надо смотреть на то, как класс реализован внутри, им достаточно увидеть заголовки и комментарии публичных методов и использовать их
- мы можем в любой момент поменять что-то во внутренней реализации класса, и не трогать остальной код, который его использует.
Ну например завтра мы решим что хранить координаты точки удобнее не в числовом массиве [$x, $y], а в таком ['x' => $x, 'y' => $y]. Или в виде объектов класса Point. И так как этот массив у нас приватный, мы можем смело менять его и нам не придется править код снаружи класса PolyLine так как изменилась только внутренняя реализация, а внешний интерфейс остался таким же.
Если проводить аналогии с автоматом по продаже кофе - мы можем заменить внутреннюю начинку в нем, а пользователи этого даже не заметят, если он будет так же работать. Хороший ООП код как раз и должен состоять из таких вот классов.
До ООП зачастую никакой инкапсуляции и не было, код состоял из функций и глобальных переменных, и можно было обращаться к любой. По мере роста объема программы разбираться в этом было сложнее. И разделение кода на классы нужно для того, чтобы справиться с этой сложностью, чтобы огромная программа могла бы оставаться относительно понятной, чтобы мы могли рассмтаривать класс отдельно от остального кода.
-------------------
А зачем ты так часто используешь sudo? Может ты делаешь что-то не так? Вообще, если это твой компьютер, к которому никто больше доступа не имеет, можно в конфиге судо убрать запрос пароля для определенных команд или вообще для любых команд.
>>1008889
Я думаю, node_modules лучше создавать в корне проекта, а не раскидывать эти паки везде.
>>1008623
Имена методов принято писать с маленькой буквы в PSR, вокруг -> пробелы не ставятся.
> AddDepartment($department)
Тут нужен тайп-хинт
> foreach ($this -> departments as $key => $value)
key тут не нужен, а value надо назвать department.
> GetMedianSolary
Придерусь к мелочам, но это не медианная, а средняя (average) зарплата. Медианная - это такая, выше которой зарплата ровно половины работников.
> public function StimulationAnalyst()
Методы, отвечающие за антикризисные меры, правильнее вынести в отдельный класс (АнтикризисныйКомитет). Так как иначе ты в класс Компания сваливаешь все подряд, и его зона ответственности становится расплывчатой. Этот метод явно нужен только для антикризисных мер.
> $department -> StimulationAnalyst();
Имена методов должны начинаться с глагола, чтоДелать, stimulateAnalysts()
> public function GetFiredEnginiers()
Метод называется "получить уволенных инженеров", и судя по названию, никого увольнять не должен. Неудачное название.
> public function AddEmployee($count,$range,$Profession, $IsHead)
Лучше передавать объект инженера, это дает больше гибкости.
> public function RiseRange($percent, $profession)
Это слишком специфичный метод, сделанный для конкретной задачи. Я бы советовал сделать в департаменте общие методы вроде "найти сотрудника", "уволить сотрудника", а тут явно код который должен быть где-то в другом месте.
Само повышение ранга сделано не очень правильно. Не надо создавать нового работника, логичнее поднять ранг существующему. Иначе можно получить много проблем, например, то, что где-то в программе остаются ссылки на старый объект или то, что какие-то свойства не перенесутся в нового работника.
Вместо removeHeadDepartment правильнее наверно сделать метод "заменитьГлавуНаНового".
> $Temployees
Плохое название.
> return $this -> coffeeCoef(1+(int)$this -> isHead);
Тут лучше использовать if или тернарный оператор для читабельности. Формулы получились малопонятными.
> public function SetIsDifferentHead
Неудачный метод так как непонятно, какой будет результат вызова. Лучше явно передавать флаг.
> boolval(1-$b);
!$b
> public function SetCoefs($coffeeCoef, $solaryCoef, $reportsCoef)
Неудачный метод, так как нельзя поменять одно свойство.
Ты сделал наследование, но где гарантия, что наследник задаст коэффициенты? Лучше исопльзовать абстрактный метод, чтобы обязать наследника указать начальные значения коэффициентов.
Про инкапсуляцию.
-------------------
Инкапсуляция. У этого слова есть разные определения, в том числе такие что ничего не понять, потому объясню простыми словами.
Суть инкапсуляции в том, что класс скрывает (инкапслирует) в себе логику работы с данными и сами данные, а наружу выставляет методы. Пользователю этих методов не важно, как класс устроен внутри, как он хранит данные, ему достаточно вызвать нужный метод чтобы получить результат.
Это упрощает понимание кода: тебе не надо читать и разбирать код класса, достаточно прочитать название метода (и может быть комментарий к нему). Также, это упрощает изменение кода: если какое-то свойство имеет уровень private то доступ к нему возможен только из того же класса и тебе не надо бегать по всему коду и смотреть что там с этим свойством делается, тебе достаточно просмотреть один файл с этим классом.
Как плюс, мы можем поставить какие-то проверки в методах, и запретить установку неправильных значений свойств. Таким образом, снаружи записать неправльное значение в объект будет нельзя и автор класса может гарантировать его корректную работу в любой ситуации.
Инкапсуляция это хорошо. Так как весь код, который занимается одной задачей, оказывается заключен внутри одного класса. Противоположный случай это когда код (или знание о его внутреннем устройстве) вылезает из класса и размазывается по всей программе.
Если проводить аналогии, то можно представить кофе-машину. Ты нажимаешь кнопку (=вызываешь публичный метод) и получаешь кофе (=результат вызова этого метода), при этом ты не видишь что происходит внутри нее и тебе не надо в этом разбираться.
Лучше наверно привести пример, чем обсуждать абстрактные вещи. Допустим, мы решили представить ломаную линию из нескольких отрезков (например, маршрут на карте) в виде объекта. Для начала, мы спроектируем, что можно будет делать с нашим объектом:
- создать, указав начальную точку (мы решили, что ломаная линия, не содержащая ни одной точки, невозможна, потому требуем передать начальные координаты в конструктор. Я сейчас не могу сказать как выгоднее - разрешить линии без точек вообще, требовать наличие минимум одной или требовать минимум 2 точки. Это надо сравнивать)
- добавить еще одну точку к линии
- найти длину линии (т.е сумму длин всех ее отрезков)
Теперь, имея этот список, мы можем написать класс PolyLine:
class PolyLine
{
// Массив со списком точек
private $points = [];
public function __construct($x, $y)
{
$this->points[] = [$x, $y];
}
public function addPoint($x, $y)
{
$this->points[] = [$x, $y];
}
/ Посчитать общую длину линии /
public function calculateLength()
{
$length = 0;
for ($i = 1; $i < count($this->points)) {
$length += $this->calculateSegmentLength(
$this->points[$i - 1][0],
$this->points[$i - 1][1],
$this->points[$i][0],
$this->points[$i][1]
);
}
return $length;
}
private function calculateSegmentlength($x1, $y1, $x2, $y2) { ... }
}
Вот в этом классе мы делаем публичными только те методы, которые запроектировали выше. А все остальное мы закрываем. Например, мы закрываем от доступа снаружи массив points. Это имеет такие преимущества:
- мы гарантируем что никто нам не положит в массив что-то не то, например, какую-то строку или что-то еще
- людям, которые изучают наш код, не надо смотреть на то, как класс реализован внутри, им достаточно увидеть заголовки и комментарии публичных методов и использовать их
- мы можем в любой момент поменять что-то во внутренней реализации класса, и не трогать остальной код, который его использует.
Ну например завтра мы решим что хранить координаты точки удобнее не в числовом массиве [$x, $y], а в таком ['x' => $x, 'y' => $y]. Или в виде объектов класса Point. И так как этот массив у нас приватный, мы можем смело менять его и нам не придется править код снаружи класса PolyLine так как изменилась только внутренняя реализация, а внешний интерфейс остался таким же.
Если проводить аналогии с автоматом по продаже кофе - мы можем заменить внутреннюю начинку в нем, а пользователи этого даже не заметят, если он будет так же работать. Хороший ООП код как раз и должен состоять из таких вот классов.
До ООП зачастую никакой инкапсуляции и не было, код состоял из функций и глобальных переменных, и можно было обращаться к любой. По мере роста объема программы разбираться в этом было сложнее. И разделение кода на классы нужно для того, чтобы справиться с этой сложностью, чтобы огромная программа могла бы оставаться относительно понятной, чтобы мы могли рассмтаривать класс отдельно от остального кода.
-------------------
Один из вариантов - это вместе с вычислением очков для вершин прописывать для каждой, из какой вершины в нее можно пройти (и менять это значение при нахождении более короткого пути на ту вершину, из которой можно пройти быстрее). Потом можно будет пройти от конца к началу по этим указателям.
Еще можно погуглить по запросам вроде "алгоритм дейкстры получить путь"
> while ($paths[$end_point][visited] == false) { // циклим пока точка до которой мы идем не станет true
Вообще, это неправильно, так как может быть там есть еще более короткий путь, чем мы нашли. Надо выполнять цикл, пока есть непосещенные вершины. Если я не путаю.
> [neighbors
Имя поля надо брать в кавычки. PHP должен писать предупреждение, у тебя наверно отключен вывод ошибок. на ideone ошибок много.
Ну и у тебя алгоритм пока нерекурсивный, то есть делает только 1 шаг, как я понял.
>>1008410
Урок в помощь https://github.com/codedokode/pasta/blob/master/network/urls.md
>>1008307
Это отделение логики от HTML-шаблона. Первый файл является "толстым контроллером", второй файл - "представлением".
>>1008163
В таких случаях, когда надо добавить методы в класс, и не подходит ни наследование, ни делегирование (вынесение методов в отдельный объект). Можно поискать примеры использования трейтов на гитхабе, но предупрежу, что они сложные:
- https://github.com/slimphp/Slim/search?utf8=✓&q=trait&type=
- https://github.com/symfony/symfony/search?utf8=✓&q=trait&type=
Так, по моему, они редко когда могут быть полезны. Ну типичный пример - добавление логгера в класс (методов setLogger, getLogger) ради небольшой экономии места.
В твоем случае трейт однознасно не нужен и генерацию рандомной строки можно вынести в статический метод в классе Util или в классе-генераторе случайных строк. Или в функцию (некоторые предпочитают вместо статических методов делать функции в неймспейсе).
Один из вариантов - это вместе с вычислением очков для вершин прописывать для каждой, из какой вершины в нее можно пройти (и менять это значение при нахождении более короткого пути на ту вершину, из которой можно пройти быстрее). Потом можно будет пройти от конца к началу по этим указателям.
Еще можно погуглить по запросам вроде "алгоритм дейкстры получить путь"
> while ($paths[$end_point][visited] == false) { // циклим пока точка до которой мы идем не станет true
Вообще, это неправильно, так как может быть там есть еще более короткий путь, чем мы нашли. Надо выполнять цикл, пока есть непосещенные вершины. Если я не путаю.
> [neighbors
Имя поля надо брать в кавычки. PHP должен писать предупреждение, у тебя наверно отключен вывод ошибок. на ideone ошибок много.
Ну и у тебя алгоритм пока нерекурсивный, то есть делает только 1 шаг, как я понял.
>>1008410
Урок в помощь https://github.com/codedokode/pasta/blob/master/network/urls.md
>>1008307
Это отделение логики от HTML-шаблона. Первый файл является "толстым контроллером", второй файл - "представлением".
>>1008163
В таких случаях, когда надо добавить методы в класс, и не подходит ни наследование, ни делегирование (вынесение методов в отдельный объект). Можно поискать примеры использования трейтов на гитхабе, но предупрежу, что они сложные:
- https://github.com/slimphp/Slim/search?utf8=✓&q=trait&type=
- https://github.com/symfony/symfony/search?utf8=✓&q=trait&type=
Так, по моему, они редко когда могут быть полезны. Ну типичный пример - добавление логгера в класс (методов setLogger, getLogger) ради небольшой экономии места.
В твоем случае трейт однознасно не нужен и генерацию рандомной строки можно вынести в статический метод в классе Util или в классе-генераторе случайных строк. Или в функцию (некоторые предпочитают вместо статических методов делать функции в неймспейсе).
Тут есть варианты:
- возвращать можно сырые аякс-данные - тогда получатель должен их преобразовать в нужные объекты
- возвращать можно сразу объекты - это наверно удобнее и приавльнее, так как мы скрываем от получателя, откуда получены эти объекты. Зачем ему знать, как они кодируются?
Тебя наверно смущает, что аякс-запросы асинхронные и ответ придет позже. Это известная проблема, и есть разные решения, все с недостатками:
- передавать фукнции-коллбеки getMessages(userId, onSuccess, onError)
- возвращать объект-промис (или future, есть несколько версий промисов):
-- https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Promise
-- https://promisesaplus.com/
-- https://ru.wikipedia.org/wiki/Futures_and_promises
Главный косяк промисов - то, что по умолчанию ошибки просто игнорируются (некоторые среды выполнения яваскрипта выводят сообщение в консоль об этом). Я не понимаю, как такое можно было спроектировать. Также, они любят перехватывать исключения и допустим ошибка доступа к переменной (ошибка в коде) "съедается" и приравнивается к ошибке выполнения асинхронной операции, хотя это не ошибка в асинхронной операции, а вообще неправильная программа. И самое интересное, что в статьях про промисы никто это не упоминает и только нахвативает их, впрочем ничего нового. Но плюс промисов в том, что их можно возвращать, передавать, сохранять в переменные, и относительно удобно пользоваться. Надеюсь, что ты изучишть, как в промисах обрабатываются ошибки и будешь делать это правильно. Интуиция подсказывает что большинство разработчиков делает это неправильно, промисы к этому располагают.
- передавать в функцию объект, похожий на промис, который будет заполнен при получении ответа с сервера. Плюс в том, что тут можно реализовать выброс исключения при необработанной ошибке, минус в том, что его надо передавать в функцию, а не возвращать. Такое мало где используют, но все же это не я придумал.
- использовать await/async в новой версии JS. Это по сути синтаксический сахар для промисов, довольно удобный, не знаю, какие там недостатки, попробуй сам определить.
> Или вы имели ввиду чтобы getMessages() выводил шаблон с полученными сообщениями, а не возвращал json?
Нет, конечно, как я могу посоветовать, чтобы код работы с АПИ занимался генерацией шаблонов? Только если это с сервера HTML код придет. Но для толстого клиента приходить с сервера должны данные, а не HTML.
>>1007965
А зачем тебе коды ошибок? Возвращай сразу сообщения либо объекты ошибок. Коды ошибок использовали раньше для экономии памяти, сейчас такой проблемы нет, а так от них больше вреда, чем пользы. Ну например, нельзя добавить подробности ошибки, "файл не существует", а какой - непонятно.
> возвращать вместо TRUE и "some message"
А вот это плохой выбор, так как непустая строка приравнивается к true при преобразовании в boolean.
Тут есть варианты:
- возвращать можно сырые аякс-данные - тогда получатель должен их преобразовать в нужные объекты
- возвращать можно сразу объекты - это наверно удобнее и приавльнее, так как мы скрываем от получателя, откуда получены эти объекты. Зачем ему знать, как они кодируются?
Тебя наверно смущает, что аякс-запросы асинхронные и ответ придет позже. Это известная проблема, и есть разные решения, все с недостатками:
- передавать фукнции-коллбеки getMessages(userId, onSuccess, onError)
- возвращать объект-промис (или future, есть несколько версий промисов):
-- https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Promise
-- https://promisesaplus.com/
-- https://ru.wikipedia.org/wiki/Futures_and_promises
Главный косяк промисов - то, что по умолчанию ошибки просто игнорируются (некоторые среды выполнения яваскрипта выводят сообщение в консоль об этом). Я не понимаю, как такое можно было спроектировать. Также, они любят перехватывать исключения и допустим ошибка доступа к переменной (ошибка в коде) "съедается" и приравнивается к ошибке выполнения асинхронной операции, хотя это не ошибка в асинхронной операции, а вообще неправильная программа. И самое интересное, что в статьях про промисы никто это не упоминает и только нахвативает их, впрочем ничего нового. Но плюс промисов в том, что их можно возвращать, передавать, сохранять в переменные, и относительно удобно пользоваться. Надеюсь, что ты изучишть, как в промисах обрабатываются ошибки и будешь делать это правильно. Интуиция подсказывает что большинство разработчиков делает это неправильно, промисы к этому располагают.
- передавать в функцию объект, похожий на промис, который будет заполнен при получении ответа с сервера. Плюс в том, что тут можно реализовать выброс исключения при необработанной ошибке, минус в том, что его надо передавать в функцию, а не возвращать. Такое мало где используют, но все же это не я придумал.
- использовать await/async в новой версии JS. Это по сути синтаксический сахар для промисов, довольно удобный, не знаю, какие там недостатки, попробуй сам определить.
> Или вы имели ввиду чтобы getMessages() выводил шаблон с полученными сообщениями, а не возвращал json?
Нет, конечно, как я могу посоветовать, чтобы код работы с АПИ занимался генерацией шаблонов? Только если это с сервера HTML код придет. Но для толстого клиента приходить с сервера должны данные, а не HTML.
>>1007965
А зачем тебе коды ошибок? Возвращай сразу сообщения либо объекты ошибок. Коды ошибок использовали раньше для экономии памяти, сейчас такой проблемы нет, а так от них больше вреда, чем пользы. Ну например, нельзя добавить подробности ошибки, "файл не существует", а какой - непонятно.
> возвращать вместо TRUE и "some message"
А вот это плохой выбор, так как непустая строка приравнивается к true при преобразовании в boolean.
Больше поддержки браузеров. То знаешь, какие браузеры отваливаются при переходе на ES5+? ES6+? Что насчет Android 4 например?
Есть конечно варианты транспилировать, но почему-то доступен транспайлер только в ES5, а в ES3 - нет. Почему так, интересно? Какая выгода отсекать часть браузеров? Там есть какой-то es5-shim, но не знаю, поможет ли он ES3 браузераим воспринимать код? Будет ли он быстро работать? Надо проверять. Если проблем нет, то почему бы и не использовать.
Изучать новые фичи конечно нужно, но нужно также понимать, где они поддерживаются, а где нет. Для меня например поддержка разных браузеров важнее каких-то сомнительных функций, которые при необходимости легко заменяются. Но у других людей может быть другое мнение. Например, для коммерческой компании поддерживать слишком старые браузеры может быть невыгодно, особенно если их мало. Но я не коммерческая компания и меня это не волнует.
Но опять же, у других людей может быть другое мнение, например, что до-ES5 браузеры сейчас и так почти ничего не отображают дальше гугла и википедии, и может не стоит с ними заморачиваться. А кто-то и ниже ES6 ни с чем работать не хочет (кстати, мой Хромиум вроде ES6 не поддерживает). Так что я не против поддержки ES5+ или настройки транспиляции из более нового стандарта в ES5.
http://kangax.github.io/compat-table/es5/
https://caniuse.com/#search=es5
Хотя вот например, Гугл, когда делал свои GWT (возможность писать клиент-серверные приложения на Яве, которая компилировалась в яваскрипт, еще до эпохи SPA), сделал поддержку максимально широкого круга браузеров.
Ну и будем честны, для многих разработчиков такие вещи за гранью их понимания.
Можно было бы обрабатывать клик по контакту в view и передавать в контроллер уже данные пользователя. А так да, придется обращаться к DOM элеиментам из контроллера. Не знаю, ты видишь какие-то недостатки? Если нет, можно оставить.
Вот только работа с аякс в контроллере это точно не хорошо.
И где обработка ошибок? Я же писал урок про работу с аяксом и специально там это упоминал...
Шаблоны надо бы добавить еще, а то тяжело поддерживать такой HTML код, ну например при изменении верстки придется ее разбирать на теги и по кусочкам переносить в View.
> this.view.contactList.click(this.view, this.view.turnCheckedClass);
Вот это наверно должно быть во view, тут контроллер ведь никак не задействован.
Пересоздавать полный список сообщений неэффективно. И у тебя там скролл все время сбрасывается, тоже очень неудобно.
И еще, у тебя тут пока фактически модель не выделена. Если ты вынесешь взаимодействие с сервером в отдельный класс, уже появится, что-то напоминающее модель, а когда она сможет хранить данные и работать в оффлайне, тогда она станет полноценной.
> function Contacts(modelView, view) {
Наверно лучше назвать ContactsController?
> this.searchbox = $('input[name="q"]');
Лучше написать $('input[name="q"]', this.searchForm); так как на странице может быть несколько форм и другие инпуты. Вообще, если ты пишешь виджет, то можно передавать в его view корневой элемент, чтобы он искал элементы только внутри него. Тогда мы можем делать на странице несколько неконфликтующих одинаковых виджетов (ну а вдруг пригодится).
То есть при написании селекторов надо избегать потенциальных проблем в будущем.
> var template =
> '<ul class="contacts">' +
Это неудобно, шаблоны лучше засунуть в HTML в script или template (плюс template - в том, что оно парсится браузером как HTML и проверяется, минус - то что нельзя туда пихать что угодно):
- https://learn.javascript.ru/templates
- https://learn.javascript.ru/template-tag
Также, можно пойти дальше и поместить шаблоны в отдельные html- или .ejs-файлы. На дев-сервере их можно грузить аяксом при запуске приложения, на продакшене - вклеивать в код при сборке (для этого придется изучить сборщики вроде webpack). Для начала можно просто их засунуть в тег скрипт с нестандартным типом.
Также, есть еще веб-компоненты, которые чем-то напоминают виджеты по сути (тем что представляют собой изолированный компонент): https://learn.javascript.ru/webcomponents
Можно было бы обрабатывать клик по контакту в view и передавать в контроллер уже данные пользователя. А так да, придется обращаться к DOM элеиментам из контроллера. Не знаю, ты видишь какие-то недостатки? Если нет, можно оставить.
Вот только работа с аякс в контроллере это точно не хорошо.
И где обработка ошибок? Я же писал урок про работу с аяксом и специально там это упоминал...
Шаблоны надо бы добавить еще, а то тяжело поддерживать такой HTML код, ну например при изменении верстки придется ее разбирать на теги и по кусочкам переносить в View.
> this.view.contactList.click(this.view, this.view.turnCheckedClass);
Вот это наверно должно быть во view, тут контроллер ведь никак не задействован.
Пересоздавать полный список сообщений неэффективно. И у тебя там скролл все время сбрасывается, тоже очень неудобно.
И еще, у тебя тут пока фактически модель не выделена. Если ты вынесешь взаимодействие с сервером в отдельный класс, уже появится, что-то напоминающее модель, а когда она сможет хранить данные и работать в оффлайне, тогда она станет полноценной.
> function Contacts(modelView, view) {
Наверно лучше назвать ContactsController?
> this.searchbox = $('input[name="q"]');
Лучше написать $('input[name="q"]', this.searchForm); так как на странице может быть несколько форм и другие инпуты. Вообще, если ты пишешь виджет, то можно передавать в его view корневой элемент, чтобы он искал элементы только внутри него. Тогда мы можем делать на странице несколько неконфликтующих одинаковых виджетов (ну а вдруг пригодится).
То есть при написании селекторов надо избегать потенциальных проблем в будущем.
> var template =
> '<ul class="contacts">' +
Это неудобно, шаблоны лучше засунуть в HTML в script или template (плюс template - в том, что оно парсится браузером как HTML и проверяется, минус - то что нельзя туда пихать что угодно):
- https://learn.javascript.ru/templates
- https://learn.javascript.ru/template-tag
Также, можно пойти дальше и поместить шаблоны в отдельные html- или .ejs-файлы. На дев-сервере их можно грузить аяксом при запуске приложения, на продакшене - вклеивать в код при сборке (для этого придется изучить сборщики вроде webpack). Для начала можно просто их засунуть в тег скрипт с нестандартным типом.
Также, есть еще веб-компоненты, которые чем-то напоминают виджеты по сути (тем что представляют собой изолированный компонент): https://learn.javascript.ru/webcomponents
> А как работает middleware?
Да, это примерно такая вещь, которая оборачивается вокруг контроллера (или другого middleware):
function middleware(Request req, Response res, controller) {
// можно что-то сделать до вызова контроллера
controller(req, res);
// можно что-то сделать после или вместо вызова контроллера
...
}
Можно использовать для:
- логгирования
- шифрования и/или хеширования кук
- реализации сессий
- проверки CSRF токенов
- добавления/проверки каких-то HTTP заголовков
- проверки доступа (требовать пароль для доступа внутрь папки /admin/)
- какой-то постобработки ответа, например, сжатия
- кеширования ответов
Ну вот например список middleware для JS-фреймворка Express, чем-то похожего на Slim: https://expressjs.com/en/resources/middleware.html
Обычно это что-то, что работает на уровне HTTP, то есть с запросом или ответом, с заголовками, итд.
> Сначала вызывается bar(), затем foo(), затем контроллер, затем снова foo(), затем снова bar(), я правильно понимаю?
Вроде бы да.
> А как делаются несколько соединений в Доктрине? Нужно создавать несколько EntityManager'ов?
Как минимум нужно несколько объектов Connection. А дальше надо посмотреть на то, как конфигурируется Доктрина ( http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/advanced-configuration.html ). Судя по тому, что ConnectionOptions передаются в конструктор EM, придется создавать несколько EM.
Я бы еще глянул код EM, может там заложено что-нибудь для работы с несколькими соединениями.
> А как работает функция readfile()? Почему если посреди кода вызвать в этой функции .php файл, он не выполниться? В документации же написано, что он выводит файл.
Она читает файл с диска и передает содержимое в тело HTTP ответа (то есть примерно равносильна echo file_get_contents(..)). Только она не читает файл целиком в память, а читает и выводит кусочками.
Код она не выполняет, так как не предназначена для этого в отличие от include.
> Хотя, это другое определение, в отличение от include, где написано включает и выполняет файл
"включает" - это неправильный термин, ничего никуда не включается. include просто выполняет php код из указанного файла. Эта путаница видимо из-за того, что в языке Си директива #include действительно включает один файл в другой, но у нас не Си и ничего никуда не включается.
> В самом интерфейсе мозиллы, в форме выбора открытия или сохранения файла, тип определяется как HTML документ, хотя в загрузках отображается нормально.
Наверно, в ответе сервера content-type равен text/html. Почитай про HTTP и заголовки ответа, если не читал - именно заголовок Content-Type определяет, какого типа файл передан в теле ответа и браузер в зависимости от этого ведет себя по разному. Ну например, если при отдаче картинки в теле ответа поставить тип text/html то вместо картинки увидим просто кучу бессмысленных символов.
Проверить, какие заголовки отдает сервер, можно на вкладке Network в инструментах разработчика.
> Функцию отмены запроса нужно выполнять после его отправки, т.е. её нужно вызвать в событии onprogress, если нужно. Я правильно понимаю?
Думаю, что да, отменять нужно в промежутке между началом отправки запроса и получением полного ответа.
> Я правильно понимаю, что эти ошибки нужно обрабатывать так
Нужно сделать такие проверки:
- код ответа равен 200
- тип ответа (Content-Type) равен требуемому
- в теле ответа данные в правильном формате
- в раскодированном ответе нет сообщения об ошибке от сервера
> сервер может быть отключен/недоступен
> xhr.status == 503
Тут скорее всего тоже будет 0, так как не удалось установить TCP соединение с сервером. Ответить 503 он может только если соединени удалось установить, но например в PHP-коде произошла ошибка или например в нгинксе (веб-сервере) что-то сломалось.
> А что тут нужно обработать?
Это надо обрабатывать точно также как 503 ошибку - с сервером что-то не в порядке, если он прислал неправильный ответ. Если используется jQuery то вызовется обработчик error, если нативный яваскрипт - вылетит исключение при декодировании JSON.
При любой ошибке надо уведомить пользователя и предложить отправить запрос повторно (ну или может как-то автоматически это делать с увеличением времени паузы).
> А как реализуются каталоги? В бд сохраняются колонки с именем, урл и деревом, и затем это урл обрабатывается контроллером?
Да, плюс есть урок по древовидным данным https://github.com/codedokode/pasta/blob/master/db/trees.md (или это не то, что надо? мы про рубрикаторы говорим или какие-то другие каталоги?)
> А зачем пишутся Doc-блоки, если PHPDoc всё равно не используется?
Для тех, кто читает код, чтобы было понятнее. Это как комментарии, только машинночитаемые. Плюс они помогают IDE в автодополнении, а также выводе подсказок.
> И какие тэги были бы всегда желательны?
Перед классом писать, зачем он нужен. Перед публичными методами, зачем они нужны. Перед полями и константами.
Там, где все понятно из названия, писать не надо (ну например поля вроде id или filename). Не надо дублировать информацию из тайп-хинтов. Писать надо не очевидные вещи, например, зачем нужно это поле или какую-то особенность поведения функции, или в каком формате хранится путь.
Ты можешь посмотреть код Слима или Симфони, или Доктрины, там есть и комментарии, и phpDoc.
Лучше всего конечно когда все понятно без комментариев, но на практике так не бывает.
> А что насчет выдавания страницы с нужным языком по геолокации?
Плохо, будет путаница, нельзя дать ссылку на нужную языковую версию. Ты даешь ссылку ии неизвестно что по ней откроется.
> роботы гугла находятся в разных странах и выдают результат тоже в соответствии с геолокацией.
Не факт, у меня нет подтверждения этому, и индексировать они это будут плохо, и пользователю вряд ли понравится прочесть в выдаче английское описание и попасть на русскую страницу.
Гугл учитывает геолокацию пользователя, но наличие нескольких страниц по одному УРЛ ему точно не понравится.
Смотри на википедию, она ведь так не делает? Если ты хочешь использовать геолокацию (или настройки языка в заголовках запроса) то можно выводить плашку с предложением перейти на другую языковую версию. Некоторые делают редирект, но по моему это крайне дурной тон, человека перебрасывать, не спрашивая его мнения.
Тут стоит помнить еще, что и геолокация, и настройки языка не точны. Представь что ты приехал в Индию и тебе все сайты начали показывать страницы на местном языке. Неудобно ведь.
То же самое с языком браузера - у русскоязычного пользователя может стоять англоязычная версия браузера. Или это может быть русский пользователь в Германии за компьютером с ОС с немецкой локалью.
> А как работает middleware?
Да, это примерно такая вещь, которая оборачивается вокруг контроллера (или другого middleware):
function middleware(Request req, Response res, controller) {
// можно что-то сделать до вызова контроллера
controller(req, res);
// можно что-то сделать после или вместо вызова контроллера
...
}
Можно использовать для:
- логгирования
- шифрования и/или хеширования кук
- реализации сессий
- проверки CSRF токенов
- добавления/проверки каких-то HTTP заголовков
- проверки доступа (требовать пароль для доступа внутрь папки /admin/)
- какой-то постобработки ответа, например, сжатия
- кеширования ответов
Ну вот например список middleware для JS-фреймворка Express, чем-то похожего на Slim: https://expressjs.com/en/resources/middleware.html
Обычно это что-то, что работает на уровне HTTP, то есть с запросом или ответом, с заголовками, итд.
> Сначала вызывается bar(), затем foo(), затем контроллер, затем снова foo(), затем снова bar(), я правильно понимаю?
Вроде бы да.
> А как делаются несколько соединений в Доктрине? Нужно создавать несколько EntityManager'ов?
Как минимум нужно несколько объектов Connection. А дальше надо посмотреть на то, как конфигурируется Доктрина ( http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/advanced-configuration.html ). Судя по тому, что ConnectionOptions передаются в конструктор EM, придется создавать несколько EM.
Я бы еще глянул код EM, может там заложено что-нибудь для работы с несколькими соединениями.
> А как работает функция readfile()? Почему если посреди кода вызвать в этой функции .php файл, он не выполниться? В документации же написано, что он выводит файл.
Она читает файл с диска и передает содержимое в тело HTTP ответа (то есть примерно равносильна echo file_get_contents(..)). Только она не читает файл целиком в память, а читает и выводит кусочками.
Код она не выполняет, так как не предназначена для этого в отличие от include.
> Хотя, это другое определение, в отличение от include, где написано включает и выполняет файл
"включает" - это неправильный термин, ничего никуда не включается. include просто выполняет php код из указанного файла. Эта путаница видимо из-за того, что в языке Си директива #include действительно включает один файл в другой, но у нас не Си и ничего никуда не включается.
> В самом интерфейсе мозиллы, в форме выбора открытия или сохранения файла, тип определяется как HTML документ, хотя в загрузках отображается нормально.
Наверно, в ответе сервера content-type равен text/html. Почитай про HTTP и заголовки ответа, если не читал - именно заголовок Content-Type определяет, какого типа файл передан в теле ответа и браузер в зависимости от этого ведет себя по разному. Ну например, если при отдаче картинки в теле ответа поставить тип text/html то вместо картинки увидим просто кучу бессмысленных символов.
Проверить, какие заголовки отдает сервер, можно на вкладке Network в инструментах разработчика.
> Функцию отмены запроса нужно выполнять после его отправки, т.е. её нужно вызвать в событии onprogress, если нужно. Я правильно понимаю?
Думаю, что да, отменять нужно в промежутке между началом отправки запроса и получением полного ответа.
> Я правильно понимаю, что эти ошибки нужно обрабатывать так
Нужно сделать такие проверки:
- код ответа равен 200
- тип ответа (Content-Type) равен требуемому
- в теле ответа данные в правильном формате
- в раскодированном ответе нет сообщения об ошибке от сервера
> сервер может быть отключен/недоступен
> xhr.status == 503
Тут скорее всего тоже будет 0, так как не удалось установить TCP соединение с сервером. Ответить 503 он может только если соединени удалось установить, но например в PHP-коде произошла ошибка или например в нгинксе (веб-сервере) что-то сломалось.
> А что тут нужно обработать?
Это надо обрабатывать точно также как 503 ошибку - с сервером что-то не в порядке, если он прислал неправильный ответ. Если используется jQuery то вызовется обработчик error, если нативный яваскрипт - вылетит исключение при декодировании JSON.
При любой ошибке надо уведомить пользователя и предложить отправить запрос повторно (ну или может как-то автоматически это делать с увеличением времени паузы).
> А как реализуются каталоги? В бд сохраняются колонки с именем, урл и деревом, и затем это урл обрабатывается контроллером?
Да, плюс есть урок по древовидным данным https://github.com/codedokode/pasta/blob/master/db/trees.md (или это не то, что надо? мы про рубрикаторы говорим или какие-то другие каталоги?)
> А зачем пишутся Doc-блоки, если PHPDoc всё равно не используется?
Для тех, кто читает код, чтобы было понятнее. Это как комментарии, только машинночитаемые. Плюс они помогают IDE в автодополнении, а также выводе подсказок.
> И какие тэги были бы всегда желательны?
Перед классом писать, зачем он нужен. Перед публичными методами, зачем они нужны. Перед полями и константами.
Там, где все понятно из названия, писать не надо (ну например поля вроде id или filename). Не надо дублировать информацию из тайп-хинтов. Писать надо не очевидные вещи, например, зачем нужно это поле или какую-то особенность поведения функции, или в каком формате хранится путь.
Ты можешь посмотреть код Слима или Симфони, или Доктрины, там есть и комментарии, и phpDoc.
Лучше всего конечно когда все понятно без комментариев, но на практике так не бывает.
> А что насчет выдавания страницы с нужным языком по геолокации?
Плохо, будет путаница, нельзя дать ссылку на нужную языковую версию. Ты даешь ссылку ии неизвестно что по ней откроется.
> роботы гугла находятся в разных странах и выдают результат тоже в соответствии с геолокацией.
Не факт, у меня нет подтверждения этому, и индексировать они это будут плохо, и пользователю вряд ли понравится прочесть в выдаче английское описание и попасть на русскую страницу.
Гугл учитывает геолокацию пользователя, но наличие нескольких страниц по одному УРЛ ему точно не понравится.
Смотри на википедию, она ведь так не делает? Если ты хочешь использовать геолокацию (или настройки языка в заголовках запроса) то можно выводить плашку с предложением перейти на другую языковую версию. Некоторые делают редирект, но по моему это крайне дурной тон, человека перебрасывать, не спрашивая его мнения.
Тут стоит помнить еще, что и геолокация, и настройки языка не точны. Представь что ты приехал в Индию и тебе все сайты начали показывать страницы на местном языке. Неудобно ведь.
То же самое с языком браузера - у русскоязычного пользователя может стоять англоязычная версия браузера. Или это может быть русский пользователь в Германии за компьютером с ОС с немецкой локалью.
>>1000694
Что, кроме дефолтного шаблона мне надосервер есть? На какие дефолтные грабли я могу наступить?
Будет ли снизу мелким шрифтом написано "сделано по шаблону жопотрах на сайте гомогеев.нет"?
мимо пхп изучал по мемосам про духевнобольных говнокодеров
Можешь в каком нибудь сервисе собрать из готовых блоков (например Tilda), потом закидываешь себе на сервер и все.
>Tilda
>потом закидываешь себе на сервер и все.
Функция экспорта кода доступна только на бизнес-плане. Как наебать систему?
И да, не бейте, первый раз сюда пишу.
Ладно, зря я это написал. Какая нахуй разница, пойду костылировать дальше.
>вас не бесит camelCase? Хочу использовать under_score
Меня не бесит, но раздражает. Впрочем, второй вариант тоже раздражает. Первый "выёбывается", второй "колхозит". В итоге выбрал для себя колхозный вариант. Пишу в Notepad++, так что имею возможность выбора.
Я другой анон и согласен, что веб-приложение в теории должно охватывать наиболее широкий круг браузеров, в том числе и устаревших. Но отсекать часть браузеров смысл есть, чтобы стимулировать пользователя перейти на новую версию браузера. В этом разработчик заинтересован, так как это упрощает ему жизнь. Но на практике оказывается, что писать кроссбраузерно очень непросто. Например я (не верстальщик) не знаю как без CSS трансформаций выровнять вертикально и горизонтально абсолютно спозиционированный элемент. У меня работает только такой подход:
.dialog {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
// ...
}
Ширина и высота не фиксированная, а определяется содержимым.
С другой стороны пользователь может не желать обновлять браузер по объективным причинам. Например новый FF требует PulseAudio, а меня вполне устраивает простенькая ALSA. Ещё многие аддоны не работают с новой версией FF из-за того, что он сейчас стал multiprocess.
бамп.
Никто даже мельком не ознакомился с первой парой уроков? Ну всего-то час потратить, ну что же вы.
Посмотрел первое видео, оно очень уныло. То, что читается за полминуты, в видео растянуто на пять. И голос неприятный, явно не лекторский. Ему не хватает плавности.
Но я вообще видеогайды не воспринимаю. Тяжёлое детство без интернета и компьютеров приучило к текстовой информации.
И форма подачи тоже так себе. Делаем это, потом делаем это, пишем сюда это, ставим туда это - ну и что в итоге? Если уж обучать созданию цмс, то нужно отталкиваться от потребностей. Мол, стоит такая-то задача, нужно реализовать для её решения то-то и то-то, для этого это делаем так, а это эдак. И уже с пониманием задач и методов приступать к созданию папок и файлов. А так получается подвешенность в воздухе. Создали три папки. Почему? Потому что.
Видимо, не смотрят тут видеуроки. Учиться нужно создавая, а не бездумно потребляя; исследуя, а не копируя готовое. Не то шаг влево-вправо от того, что объяснили в видеоуроке - и всё, человек садится в лужу.
>>1010362
Со своим уставом в чужой монастырь лезть не нужно. Когда дело касается таких субъективных вещей как camel case/snake case, то единственно верным решением будет поступать так, как принято большинством в коммьюнити, чтобы избежать бардака.
snake_case выглядел бы нормально, если бы переменные не нужно было предварять знаком доллара, как в Ruby и Python. PHP же во многом ориентируется на Java и C# и правильное делает.
>>1010377
Ну не используй. Алсо в PHPStorm по умолчанию включено много ненужных функций (потому что IDE), но эти функции отключаются.
> А так получается подвешенность в воздухе. Создали три папки. Почему? Потому что.
Так я и хочу разобраться, тупо копировать что он там вывалил в сжатом виде как-то не особо весело. Хочется понять что и зачем.
>>1010386
>Учиться нужно создавая, а не бездумно потребляя; исследуя, а не копируя готовое.
Спиздани мне еще, что можно не копируя никого, достичь самому такого же уровня на раз два. Прелесть изучения чужой работы в том, что ты можешь постичь плоды того к чему шли годами за короткое время и двигаться дальше, а не пердолить велосипеды, которые уже заложены везде и без тебя.
двачую эту макаку
Ну так открой исходники популярных проектов и постигай плоды долгих лет, к чему тебе недалёкая мычащая русская нянька с сомнительной компетенцией, когда на гитхабе лежит уйма готовых проектов, из которых реально можно многому научится:
- бандлы Sylius: https://github.com/Sylius
- бандлы Sonata: https://github.com/sonata-project
- сайт-аггрегатор: https://github.com/a-ast/symfony-trends
Там везде есть тесты, по которым можно понять, как работает код.
>Со своим уставом в чужой монастырь лезть не нужно. Когда дело касается таких субъективных вещей как camel case/snake case, то единственно верным решением будет поступать так, как принято большинством в коммьюнити, чтобы избежать бардака.
Если лажу по чужому коду - стараюсь использовать тот тип написания который там уже задействован. Если своё пишу, мне кэмлкэйс ближе как-то
Двачну, попытался как-то поставить. Ахуел.
Дохуя перепробовал чего. Notepad++ с плагинами - единственное что устраивает и тебе советую
>с плагинами
Подробнее. Я как поставил его, так и ничего не менял. Что там есть реально полезного?
Консоль пиздатая. Минификатор и разминификатор JS кода. Проводник работающий с FTP, просто проводник по файловой системе компухтера пиздато сделан, просмтрщик SQLite баз данных, дерево PHP классов, методов, свойств, тестирование регулярок, сниппеты, тысячи их. Удобная настройка всего, горячих клавиш например. Портативность, бесплатность, скорость. Можно прикрутить xdebug но я не пробовал. Плюс дохуя чего пиздатого из коробки
>>1010494
Тоже пробовал - хипстерское дно
Ну понятно, что он не IDE и не может полностью реализовать IDEшный функционал. Лично мне у npp нехватает 3х вещей: умное автодополнение, настраиваемое форматирование кода (отступы переносы и пр по клику) и определение языка в одном файле. Без всех этих вещей можно жить и их отсутствие никак не вызывает у меня желание перейти на IDE, т.к. я даже близко не смог настроить кучу из них, как я настроил npp
Боже, это гениально.
минусы - страница всего одна, хуево работает с поисковиками. отдельные страницы могли бы индексироваться и попадать в ранжирование гугла или яндекса.
собери сам как попало из бутстрапа
И сразу статья про то, почему Uber перешел с Postgres на MySQL: https://habrahabr.ru/company/southbridge/blog/322624/
>возвращать объект-промис
Я уже пытался с ним разобраться, но я не понял их предназначения - они всё равно не дают возможности получить результат запроса, это тоже самое что и success\.done(...) только аякс обернут во что-то.
Может его смысл как раз в том чтобы только была возможность передать "обещание" запроса куда-нибудь ещё, но на этом всё. Если я хочу сохранить куда-то результат, то я не знаю что с этим поделать.
Model.getMessages() {
var promise = $.ajax(...);
return promise;
}
Controller.runMessages() {
var promise = this.model.getMessages();
promise.then(
//success
function(data) {
this.model.messages = data; //undefined
this.view.render(data);
//Только так можно получить "данные"
this.view.messages = $('.message');
},
//error
function() {
...
});
}
>Надеюсь, что ты изучишть, как в промисах обрабатываются ошибки и будешь делать это правильно.
Надеюсь, в примере выше, я правильно показал как это нужно делать?
>возвращать можно сырые аякс-данные
А что это за данные?
>возвращать можно сразу объекты
Что вы имели ввиду? Ничего же нельзя вернуть из результата запроса.
>>1010325
>Вот только работа с аякс в контроллере это точно не хорошо.
Почему? Это же задача контроллера работать с внешними данными.
>И где обработка ошибок? Я же писал урок про работу с аяксом и специально там это упоминал...
Я ещё работаю над задачей. Лучше не задавать несколько маленьких вопросов по отдельности, а сразу несколько в одном посте по готовности?
>И еще, у тебя тут пока фактически модель не выделена. Если ты вынесешь взаимодействие с сервером в отдельный класс, уже появится, что-то напоминающее модель, а когда она сможет хранить данные и работать в оффлайне, тогда она станет полноценной.
В этом и есть проблема, что нельзя получить данные из гет запросов. (Это тот же вопрос про "обещания")
>> var template =
>> '<ul class="contacts">' +
>Это неудобно, шаблоны лучше засунуть в HTML в script или template (плюс template - в том, что оно парсится браузером как HTML и проверяется, минус - то что нельзя туда пихать что угодно):
Я надеялся, что вы никогда не увидите это безобразие. Я планировал исправить это, но, скорее всего, я бы тогда не узнал про тэг template.
>Также, можно пойти дальше и поместить шаблоны в отдельные html- или .ejs-файлы. На дев-сервере их можно грузить аяксом при запуске приложения, на продакшене - вклеивать в код при сборке (для этого придется изучить сборщики вроде webpack). Для начала можно просто их засунуть в тег скрипт с нестандартным типом.
У меня были шаблоны .ejs на старом ejs http://www.embeddedjs.com/ https://github.com/someApprentice/chat/tree/541ca1a3298b038af4e3e307db9a7096593285b1/public/js/templates , но почему-то у него не работало экранирование html-тэгов.
Затем я перешел на Handlebars, затем снова на ejs, только уже на новую версию ejs.co.
Кстати, это нормально делать перевод на новую строку в цикле https://github.com/someApprentice/chat/blob/master/public/js/chat.js#L262-L270 ?
>возвращать объект-промис
Я уже пытался с ним разобраться, но я не понял их предназначения - они всё равно не дают возможности получить результат запроса, это тоже самое что и success\.done(...) только аякс обернут во что-то.
Может его смысл как раз в том чтобы только была возможность передать "обещание" запроса куда-нибудь ещё, но на этом всё. Если я хочу сохранить куда-то результат, то я не знаю что с этим поделать.
Model.getMessages() {
var promise = $.ajax(...);
return promise;
}
Controller.runMessages() {
var promise = this.model.getMessages();
promise.then(
//success
function(data) {
this.model.messages = data; //undefined
this.view.render(data);
//Только так можно получить "данные"
this.view.messages = $('.message');
},
//error
function() {
...
});
}
>Надеюсь, что ты изучишть, как в промисах обрабатываются ошибки и будешь делать это правильно.
Надеюсь, в примере выше, я правильно показал как это нужно делать?
>возвращать можно сырые аякс-данные
А что это за данные?
>возвращать можно сразу объекты
Что вы имели ввиду? Ничего же нельзя вернуть из результата запроса.
>>1010325
>Вот только работа с аякс в контроллере это точно не хорошо.
Почему? Это же задача контроллера работать с внешними данными.
>И где обработка ошибок? Я же писал урок про работу с аяксом и специально там это упоминал...
Я ещё работаю над задачей. Лучше не задавать несколько маленьких вопросов по отдельности, а сразу несколько в одном посте по готовности?
>И еще, у тебя тут пока фактически модель не выделена. Если ты вынесешь взаимодействие с сервером в отдельный класс, уже появится, что-то напоминающее модель, а когда она сможет хранить данные и работать в оффлайне, тогда она станет полноценной.
В этом и есть проблема, что нельзя получить данные из гет запросов. (Это тот же вопрос про "обещания")
>> var template =
>> '<ul class="contacts">' +
>Это неудобно, шаблоны лучше засунуть в HTML в script или template (плюс template - в том, что оно парсится браузером как HTML и проверяется, минус - то что нельзя туда пихать что угодно):
Я надеялся, что вы никогда не увидите это безобразие. Я планировал исправить это, но, скорее всего, я бы тогда не узнал про тэг template.
>Также, можно пойти дальше и поместить шаблоны в отдельные html- или .ejs-файлы. На дев-сервере их можно грузить аяксом при запуске приложения, на продакшене - вклеивать в код при сборке (для этого придется изучить сборщики вроде webpack). Для начала можно просто их засунуть в тег скрипт с нестандартным типом.
У меня были шаблоны .ejs на старом ejs http://www.embeddedjs.com/ https://github.com/someApprentice/chat/tree/541ca1a3298b038af4e3e307db9a7096593285b1/public/js/templates , но почему-то у него не работало экранирование html-тэгов.
Затем я перешел на Handlebars, затем снова на ejs, только уже на новую версию ejs.co.
Кстати, это нормально делать перевод на новую строку в цикле https://github.com/someApprentice/chat/blob/master/public/js/chat.js#L262-L270 ?
если через ajax тога
.succes(function(dara){
his.model.messages = data; //undefined
this.view.render(data);
//Только так можно получить "данные"
this.view.messages = $('.message');
})
.error(function(err){
console.log(err);
});
Но не факт что данные успеют вернуться
если через промис
var peomise = new Promise(function(res,rej){
$.ajax(...).succes(function(res){
return this.res;
})
.error(function(err){
return this.rej=err;
})
});
Controller.runMessages() {
var promise = this.model.getMessages();
promise.then(functin(data){
this.model.messages = data;
this.view.render(data);
})
.error(function(err){
console.log(err);
});
снова js в php треде
если через ajax тога
.succes(function(dara){
his.model.messages = data; //undefined
this.view.render(data);
//Только так можно получить "данные"
this.view.messages = $('.message');
})
.error(function(err){
console.log(err);
});
Но не факт что данные успеют вернуться
если через промис
var peomise = new Promise(function(res,rej){
$.ajax(...).succes(function(res){
return this.res;
})
.error(function(err){
return this.rej=err;
})
});
Controller.runMessages() {
var promise = this.model.getMessages();
promise.then(functin(data){
this.model.messages = data;
this.view.render(data);
})
.error(function(err){
console.log(err);
});
снова js в php треде
>>1010742
https://api.jquery.com/jquery.ajax/
>jqXHR.then(function( data, textStatus, jqXHR ) {}, function( jqXHR, textStatus, errorThrown ) {});
>Incorporates the functionality of the .done() and .fail() methods, allowing (as of jQuery 1.8) the underlying Promise to be manipulated. Refer to deferred.then() for implementation details.
Промис - это будущий результат (или ошибка), которые там появятся позже. В отличие от коллбеков, они позволяют писать более логичный код (функция возвращает значение через return, а не через коллбеки). Промисы можно передавать, сохранять. Сам факт того, что у нас один промис, а не 2 или более коллбеков, уже хорош.
Если ты хочешь что-то сделать с результатом, то должен использовать коллбек. Так как результат может появиться только в будущем.
Промисы позволяют писать последовательные действия последовательно, а не в виде лестницы вложенных функций:
var result = doOperation1().
then(doOperation2).
then(doOperation3);
При этом в result мы получим либо промис для результата операции 3, либо ошибку. Попробуй напиши то же самое на коллбеках - скорее всего код будет хуже читаться.
Пример такой ситуации - HTTP-клиент. Тебе надо сделать DNS-запрос, потом установить соединение, потом послать запрос, потом получить ответ. При этом одно действие нельзя сделать до завершения другого.
Также, над промисами можно делать операции (!). Это та часть, которая на коллбеках реализуется только с помощью кучи лапши. Ну например, можно запустить 3 параллельных операции и дождаться их окончания:
var a = doSomething1();
var b = doSomething1();
var c = doSomething1();
Promise.all(a, b, c).then(function (results) {
// здесь мы имеем результат всех 3 операций
}, function (...) { ... });
Попробуй напиши то же на коллбеках, с обработкой ошибок - код будет намного сложнее. Промисы позволяют строить произвольные комбинации из параллельных и последовательных действий.
Соответственно ты можешь писать в контроллере примерно так:
backend.getMessages().then({
обновить представление;
}, function () {
проинформировать об ошибке;
});
Разумеется, тебе придется что-то менять в коде ради промисов, но тут ничего не поделать, асинхронный код в любом случае совсем простым не сделать.
Минусы промисов:
- они смешивают вместе понятия "ошибка выполнения асинхронной операции" (например, не удалось выполнить HTTP-запрос) и исключение в процессе выполнения какой-то операции (например обращение к несуществующей переменной или функции)
- легко забыть обработать ошибку и в этом случае о ней никак не узнать (некоторые среды яваскрипт дают об этом сообщение в консоль)
Я писал одну штуку на промисах на PHP и конечно обработка ошибок в них очень плохо сделана, и я так и не решил, переделывать код или попытаться сделать более хорошую версию промисов (там куча своих сложностей, я как голову не ломал, хорошего решения придумать не могу).
Еще по моему, на промисах плохо пишутся асинхронные аналоги циклов (while/for) и конструкций вроде if/switch. Но они и на коллбеках одинаково плохо получаются. Асинхронный код не такая простая вещь. В этом плане, кстати, синхронная модель PHP (в отличиие от Ноды например) очень облегчает жизнь - страшно представить, насколько усложнится код типичного php-приложения, работающего с БД, с переходом на асинхронность.
> var promise = $.ajax(...);
Хочу предупредить, что промисы в jQuery сломанные, они не соответствуют идее Promise/A (они могут менять свое состояние многократно): http://www.vasanthk.com/jquery-promises-and-deferred-objects/
> //Только так можно получить "данные"
> this.view.messages = $('.message');
У тебя парой строчек выше есть переменная data вообще-то, где они и хранятся. Или ты имел в виду, получить данные вне коллбека? Конечно, это сделать нельзя. Если тебе надо сохранить данные на будущее, то есть 2 варианта:
- сохранить промис, из него всегда можно получить данные через then
- построить нормальное хранилище (базу данных) на стороне клиента, и синхронизировать данные в нем с данными на сервере асинхронно. А в контроллере брать те данные, что сейчас в хранилище. Я уже давно хотел написать урок про такие ситуации, и типичные подводные камни, паттерны, но все руки не доходят. Надо написать, а то эту информацию наверно так просто и не найти, тем более в виде урока.
>>возвращать можно сырые аякс-данные
> А что это за данные?
Это данные в том виде, в котором их возвращает серверное АПИ. Если там JSON, то будет большой массив или словарь сложной структуры наверно.
>>возвращать можно сразу объекты
> Что вы имели ввиду? Ничего же нельзя вернуть из результата запроса.
Естественно, если мы хотим преобразовать данные асинхронно, нам надо использоватьт промисы и функцию-конвертор:
var responsePromise = backend.sendRequest();
var dataPromise = responsePromise.then(transfromData);
Вот кстати, обрати внимание, как с промисами легко асинхронно трансформировать данные, которые пока еще не получены. Причем transformData может быть синхронной функцией (сразу вернет результат) или асинхронной (вернет промис результата). Ну-ка, напиши-ка то же самое на коллбеках.
Под "объектами" я имел в виду объекты-сущности или модели предметной области. Ну например, User, Conversation, Message, Attachment - такие объекты. Если ты будешь делать более-менее сложное приложение, то тебе надо как-то в нем представлять эти сущности, и объекты тут хорошо подходят.
>>Вот только работа с аякс в контроллере это точно не хорошо.
> Почему? Это же задача контроллера работать с внешними данными.
Неверно. Это в серверной модели MVC задача контроллера проанализировать запрос пользователя. Но отправлять HTTP-запросы - точно не его задача. С чего бы? У тебя клиентская версия MVC, тут вообще нет "HTTP запроса пользователя", тут действия пользователя вызывают появление событий, которые обрабатывает контроллер.
Что-то мне кажется, ты что-то путаешь в понимании MVC. В MVC у тебя по есть модель - хранить данные и логику приложения (пользователей, чаты, действия над ними, и ествественно, взаимодействие с сервером), есть представление, которое отображает данные из модели и есть контроллеры, которые обрабатывают команды пользователя, нажатия на кнопки например. Но в общем, если у тебя есть только модель, то этого достаточно, чтобы делать любые действия программно (например, вызывая какие-то функции). Контроллеры и вью нужны только для реализации пользовательского интерфейса к модели.
Эта модель была придумана при разработке приложений с GUI, автор хотел отделить внутреннюю логику работы программы от пользовательского интерфейса. У тебя ничего не отделено - в одной функции идет обработка событий от кнопки и отправка запросов на бекенд.
Вот теперь посмотри на свой код, и подумай - можно ли у тебя убрать Контроллеры и Вью, оставить только Модель и получить список сообщений программно? Отправить сообщение? Добавить контакт? Если нет - у тебя не MVC.
Посмотри статьи про MVC, например, и не путай серверную и клиентскую версии MVC:
- https://github.com/codedokode/pasta/blob/master/js/minesweeper-mvc.md (тут про игру, но надеюсь, ты сможешь мысленно перенести изложенные там идеи на мессенджер)
- https://ru.wikipedia.org/wiki/Model-View-Controller (тут местами не очень понятно)
- https://github.com/codedokode/pasta/blob/master/arch/mvc.md
> Лучше не задавать несколько маленьких вопросов по отдельности, а сразу несколько в одном посте по готовности?
Как тебе удобнее, так и делай.
>>И еще, у тебя тут пока фактически модель не выделена.
> В этом и есть проблема, что нельзя получить данные из гет запросов. (Это тот же вопрос про "обещания")
Это оправдания. Отделить модель от интерфейса возможно. Возвращать данные можно разными способами, через коллбеки, промисы, либо реализовав локальную базу данных с фоновой синхронизацией с сервером.
> Кстати, это нормально делать перевод на новую строку в цикле https://github.com/someApprentice/chat/blob/master/public/js/chat.js#L262-L270 ?
Это какие-то кривые костыли. Получается, логика отображения разбросана между шаблоном и JS кодом. Мы ведь в PHP не пытаемся так делать после генерации HTML из шаблона? И тут не надо. Наверняка там есть какие-нибудь хелперы для таких преобразований. В чем проблема сделать какой-нибудь хелпер для преобразования текстов сообщений?
Промис - это будущий результат (или ошибка), которые там появятся позже. В отличие от коллбеков, они позволяют писать более логичный код (функция возвращает значение через return, а не через коллбеки). Промисы можно передавать, сохранять. Сам факт того, что у нас один промис, а не 2 или более коллбеков, уже хорош.
Если ты хочешь что-то сделать с результатом, то должен использовать коллбек. Так как результат может появиться только в будущем.
Промисы позволяют писать последовательные действия последовательно, а не в виде лестницы вложенных функций:
var result = doOperation1().
then(doOperation2).
then(doOperation3);
При этом в result мы получим либо промис для результата операции 3, либо ошибку. Попробуй напиши то же самое на коллбеках - скорее всего код будет хуже читаться.
Пример такой ситуации - HTTP-клиент. Тебе надо сделать DNS-запрос, потом установить соединение, потом послать запрос, потом получить ответ. При этом одно действие нельзя сделать до завершения другого.
Также, над промисами можно делать операции (!). Это та часть, которая на коллбеках реализуется только с помощью кучи лапши. Ну например, можно запустить 3 параллельных операции и дождаться их окончания:
var a = doSomething1();
var b = doSomething1();
var c = doSomething1();
Promise.all(a, b, c).then(function (results) {
// здесь мы имеем результат всех 3 операций
}, function (...) { ... });
Попробуй напиши то же на коллбеках, с обработкой ошибок - код будет намного сложнее. Промисы позволяют строить произвольные комбинации из параллельных и последовательных действий.
Соответственно ты можешь писать в контроллере примерно так:
backend.getMessages().then({
обновить представление;
}, function () {
проинформировать об ошибке;
});
Разумеется, тебе придется что-то менять в коде ради промисов, но тут ничего не поделать, асинхронный код в любом случае совсем простым не сделать.
Минусы промисов:
- они смешивают вместе понятия "ошибка выполнения асинхронной операции" (например, не удалось выполнить HTTP-запрос) и исключение в процессе выполнения какой-то операции (например обращение к несуществующей переменной или функции)
- легко забыть обработать ошибку и в этом случае о ней никак не узнать (некоторые среды яваскрипт дают об этом сообщение в консоль)
Я писал одну штуку на промисах на PHP и конечно обработка ошибок в них очень плохо сделана, и я так и не решил, переделывать код или попытаться сделать более хорошую версию промисов (там куча своих сложностей, я как голову не ломал, хорошего решения придумать не могу).
Еще по моему, на промисах плохо пишутся асинхронные аналоги циклов (while/for) и конструкций вроде if/switch. Но они и на коллбеках одинаково плохо получаются. Асинхронный код не такая простая вещь. В этом плане, кстати, синхронная модель PHP (в отличиие от Ноды например) очень облегчает жизнь - страшно представить, насколько усложнится код типичного php-приложения, работающего с БД, с переходом на асинхронность.
> var promise = $.ajax(...);
Хочу предупредить, что промисы в jQuery сломанные, они не соответствуют идее Promise/A (они могут менять свое состояние многократно): http://www.vasanthk.com/jquery-promises-and-deferred-objects/
> //Только так можно получить "данные"
> this.view.messages = $('.message');
У тебя парой строчек выше есть переменная data вообще-то, где они и хранятся. Или ты имел в виду, получить данные вне коллбека? Конечно, это сделать нельзя. Если тебе надо сохранить данные на будущее, то есть 2 варианта:
- сохранить промис, из него всегда можно получить данные через then
- построить нормальное хранилище (базу данных) на стороне клиента, и синхронизировать данные в нем с данными на сервере асинхронно. А в контроллере брать те данные, что сейчас в хранилище. Я уже давно хотел написать урок про такие ситуации, и типичные подводные камни, паттерны, но все руки не доходят. Надо написать, а то эту информацию наверно так просто и не найти, тем более в виде урока.
>>возвращать можно сырые аякс-данные
> А что это за данные?
Это данные в том виде, в котором их возвращает серверное АПИ. Если там JSON, то будет большой массив или словарь сложной структуры наверно.
>>возвращать можно сразу объекты
> Что вы имели ввиду? Ничего же нельзя вернуть из результата запроса.
Естественно, если мы хотим преобразовать данные асинхронно, нам надо использоватьт промисы и функцию-конвертор:
var responsePromise = backend.sendRequest();
var dataPromise = responsePromise.then(transfromData);
Вот кстати, обрати внимание, как с промисами легко асинхронно трансформировать данные, которые пока еще не получены. Причем transformData может быть синхронной функцией (сразу вернет результат) или асинхронной (вернет промис результата). Ну-ка, напиши-ка то же самое на коллбеках.
Под "объектами" я имел в виду объекты-сущности или модели предметной области. Ну например, User, Conversation, Message, Attachment - такие объекты. Если ты будешь делать более-менее сложное приложение, то тебе надо как-то в нем представлять эти сущности, и объекты тут хорошо подходят.
>>Вот только работа с аякс в контроллере это точно не хорошо.
> Почему? Это же задача контроллера работать с внешними данными.
Неверно. Это в серверной модели MVC задача контроллера проанализировать запрос пользователя. Но отправлять HTTP-запросы - точно не его задача. С чего бы? У тебя клиентская версия MVC, тут вообще нет "HTTP запроса пользователя", тут действия пользователя вызывают появление событий, которые обрабатывает контроллер.
Что-то мне кажется, ты что-то путаешь в понимании MVC. В MVC у тебя по есть модель - хранить данные и логику приложения (пользователей, чаты, действия над ними, и ествественно, взаимодействие с сервером), есть представление, которое отображает данные из модели и есть контроллеры, которые обрабатывают команды пользователя, нажатия на кнопки например. Но в общем, если у тебя есть только модель, то этого достаточно, чтобы делать любые действия программно (например, вызывая какие-то функции). Контроллеры и вью нужны только для реализации пользовательского интерфейса к модели.
Эта модель была придумана при разработке приложений с GUI, автор хотел отделить внутреннюю логику работы программы от пользовательского интерфейса. У тебя ничего не отделено - в одной функции идет обработка событий от кнопки и отправка запросов на бекенд.
Вот теперь посмотри на свой код, и подумай - можно ли у тебя убрать Контроллеры и Вью, оставить только Модель и получить список сообщений программно? Отправить сообщение? Добавить контакт? Если нет - у тебя не MVC.
Посмотри статьи про MVC, например, и не путай серверную и клиентскую версии MVC:
- https://github.com/codedokode/pasta/blob/master/js/minesweeper-mvc.md (тут про игру, но надеюсь, ты сможешь мысленно перенести изложенные там идеи на мессенджер)
- https://ru.wikipedia.org/wiki/Model-View-Controller (тут местами не очень понятно)
- https://github.com/codedokode/pasta/blob/master/arch/mvc.md
> Лучше не задавать несколько маленьких вопросов по отдельности, а сразу несколько в одном посте по готовности?
Как тебе удобнее, так и делай.
>>И еще, у тебя тут пока фактически модель не выделена.
> В этом и есть проблема, что нельзя получить данные из гет запросов. (Это тот же вопрос про "обещания")
Это оправдания. Отделить модель от интерфейса возможно. Возвращать данные можно разными способами, через коллбеки, промисы, либо реализовав локальную базу данных с фоновой синхронизацией с сервером.
> Кстати, это нормально делать перевод на новую строку в цикле https://github.com/someApprentice/chat/blob/master/public/js/chat.js#L262-L270 ?
Это какие-то кривые костыли. Получается, логика отображения разбросана между шаблоном и JS кодом. Мы ведь в PHP не пытаемся так делать после генерации HTML из шаблона? И тут не надо. Наверняка там есть какие-нибудь хелперы для таких преобразований. В чем проблема сделать какой-нибудь хелпер для преобразования текстов сообщений?
Только не надо городить лестницу из коллбеков, а надо написать или найти преобразователь jQuery Deferred в промис. Причем я думаю, можно написать так, что будут доступны и функции из jQuery Deferred (вроде abort или что там есть для отмены запроса).
>Также, над промисами можно делать операции (!). Это та часть, которая на коллбеках реализуется только с помощью кучи лапши. Ну например, можно запустить 3 параллельных операции и дождаться их окончания:
Почему это так важно? Я не вижу в этом ничего особенного потому что это просто сокращение от Promise.then(doSomething1())x3.then(function(result)...);
>Вот кстати, обрати внимание, как с промисами легко асинхронно трансформировать данные, которые пока еще не получены. Причем transformData может быть синхронной функцией (сразу вернет результат) или асинхронной (вернет промис результата). Ну-ка, напиши-ка то же самое на коллбеках.
А как transfromData может быть синхронной или асинхронной функцией, это же не запрос браузера (ведь
же XMLHttpRequset это API браузера?), а просто передающаяся функция?
>Promise.then(doSomething1())x3.then(function(result)...);
Это последовательное выполнение операций, а не параллельное
> А как transfromData может быть синхронной или асинхронной функцией,
Она может внутри использовать асинхронные операции: XHR запросы, работу с асинхронными API, таймауты, события и тогда она будет асинхронной. Мысль была в том, что промисы позволяют преобразовывать данные с помощью синхронных и асинхронных функций.
>>1010744
>Она может внутри использовать асинхронные операции
>Ну-ка, напиши-ка то же самое на коллбеках.
Не понимаю в чём тогда подвох задачи, если синхронность или асинхронность зависит от самой функции.
function foo(callback) {
$.ajax(
...,
function(data) {
callback(data);
});
}
foo(transformData);
Возможно, ещё нужно добавить опцию async в аргументы, которая меняет опцию аякса с аналогичным названием, в зависимости от требований коллбек функции.
алсо, бэкэнд буду на ларавел городить, на этот раз постараюсь без велосипедов, надёргаю пакетов и норм
$string = "Артём Головкин";
$string = stringToArray($string);
for ($i = 0; $i < count($string); $i++) {
$string[$i] = $translitArray[$string[$i]];
}
foreach ($string as $value) {
echo $value;
}
Здесь translitArray - это просто массив вида "щ" => "sch", расположенный выше, я его естественно копипастить для удобства не стал.
Код отрабатывает как положено, все четко! Но я вот сейчас попытался упаковать это дело в функцию и потерпел фиаско - оно не работает, оно просто выводит ничего. На ретерн подавал $string. Помогите, пожалуйста, если нетрудно.
Твоя идея через прописывание для каждой точки, из какой мы в неё пришли сработала. Так что я сделал.
http://ideone.com/FjiolC
Но кинь свой код, что бы я понял на сколько мой неправильный.
https://pastebin.com/tZriBJxV вот лог
Оберни desc в символы ``, это зарезервированное слово для указания вида сортировки (desc/asc)
Во-первых, в твоем примере никуда не сохраняется результат вызова transformData.
Во-вторых, не обрабатываются ошибки - в функции $.ajax и в функции transformData
Ну вот тебе простой пример асинхронных функций (на коллбеках). Я использую тут таймаут для примера:
// Асинхронно увеличивает аргумент на 1
function asyncSuccess(input, onSuccess, onError) {
setTimeout(function() {
onSucess(input + 1);
}, 1);
}
// Асинхронно возвращает сообщение об ошибке
function asyncFail(input, onSuccess, onError) {
setTimeout(function () {
onError(new Error("Failed"));
}, 1);
}
var doOp1 = asyncSuccess,
doOp2 = asyncFail,
doOp3 = asyncSuccess;
var p1 = toPromise(doOp1),
p2 = toPromise(doOp2),
p3 = toPromise(doOp3);
Вот теперь с этими функциями попробуй написать последовательное и параллельное выполнение 3 операций с обработкой ошибок и сохранением результата. Ну вот как выглядел бы код на промисах:
p1(1).then(p2).then(p3).then(function (result) {
console.log(result); // выведет 4
}, function (error) {
console.log(error);
});
Promise.all([p1(10), p2(20), p3(30)]).
then(function (results) {
console.log(results); // [11, 21, 31]
}, function (error) {
console.log(error);
});
Вот попробуй написать аналогичный код на коллбеках например. И это ведь простые примеры, я бы мог еще усложнить, используя смесь параллельных и последовательных операций.
> Возможно, ещё нужно добавить опцию async в аргументы, которая меняет опцию аякса с аналогичным названием, в зависимости от требований коллбек функции.
Так делать не надо, так как использование синхронных аякс-вызовов блокирует интерфейс браузера (и выполнение JS скриптов тоже). Не надо их никогда использовать.
Во-первых, в твоем примере никуда не сохраняется результат вызова transformData.
Во-вторых, не обрабатываются ошибки - в функции $.ajax и в функции transformData
Ну вот тебе простой пример асинхронных функций (на коллбеках). Я использую тут таймаут для примера:
// Асинхронно увеличивает аргумент на 1
function asyncSuccess(input, onSuccess, onError) {
setTimeout(function() {
onSucess(input + 1);
}, 1);
}
// Асинхронно возвращает сообщение об ошибке
function asyncFail(input, onSuccess, onError) {
setTimeout(function () {
onError(new Error("Failed"));
}, 1);
}
var doOp1 = asyncSuccess,
doOp2 = asyncFail,
doOp3 = asyncSuccess;
var p1 = toPromise(doOp1),
p2 = toPromise(doOp2),
p3 = toPromise(doOp3);
Вот теперь с этими функциями попробуй написать последовательное и параллельное выполнение 3 операций с обработкой ошибок и сохранением результата. Ну вот как выглядел бы код на промисах:
p1(1).then(p2).then(p3).then(function (result) {
console.log(result); // выведет 4
}, function (error) {
console.log(error);
});
Promise.all([p1(10), p2(20), p3(30)]).
then(function (results) {
console.log(results); // [11, 21, 31]
}, function (error) {
console.log(error);
});
Вот попробуй написать аналогичный код на коллбеках например. И это ведь простые примеры, я бы мог еще усложнить, используя смесь параллельных и последовательных операций.
> Возможно, ещё нужно добавить опцию async в аргументы, которая меняет опцию аякса с аналогичным названием, в зависимости от требований коллбек функции.
Так делать не надо, так как использование синхронных аякс-вызовов блокирует интерфейс браузера (и выполнение JS скриптов тоже). Не надо их никогда использовать.
Спасибо большое! Помогло.
>Во-первых, в твоем примере никуда не сохраняется результат вызова transformData.
Так и в обещаниях никуда не сохраняется результат.
>>1010744
>> //Только так можно получить "данные"
>> this.view.messages = $('.message');
>У тебя парой строчек выше есть переменная data вообще-то, где они и хранятся. Или ты имел в виду, получить данные вне коллбека? Конечно, это сделать нельзя.
Если я не хочу писать хранилище, то я могу только сохранить обещание. Но и в этом случае я не вижу смысла, потому что всё равно нельзя никуда сохранить результат, как я писал в примере выше:
Controller.runMessages() {
var promise = Model.getMessages();
promise.then(function(data) {
Model.messages = data;
});
}
console.log(Model.messages); //undefined
Или вы имели ввиду что функция transformData будет выводить шаблон и парсить из него данные, в нашем случае, в Model.messages? Это совсем не эффективно, но я не вижу другого выхода.
Я до сих пор не понимаю как получить данные, чтобы потом с ними можно было работать локально.
> я не хочу писать хранилище
> как получить данные, чтобы потом с ними можно было работать локально.
Это ведь противоречит друг другу. Если ты хранишь где-то данные - то это и есть хранилище.
Либо надо писать хранилище, либо можно просто использовать промис:
messagesPromise = backend.getMessages();
messagesPromise.then(function (data) {
renderMessageList(data);
});
Тут еще конечно такой момент, что все эти вызовы асинхронные, ответ придет неизвестно когда, и важно не пытаться отрисовывать сообщения для Пети, если пользователь уже переключился на Васю. Значит, ты теперь должен еще по получению данных проверить, какой у нас сейчас открыт диалог (и открыт ли он вообще).
Твой код в посте в общем неправильный - ты пытаешься его писать последовательно (как на PHP), но это работает только с синхронными функциями. Суть асинхронных функций в том, что ты запускаешь какую-то операцию и не ждешь, пока она завершится, а что-то делаешь дальше.
То есть если пользователь щелкает на имя - то ты обновляешь интерфейс, очищаешь диалог, запускаешь загрузку сообщений с сервера. А когда они придут, если они нам все еще нужны, то мы отрисовываем их на экране.
По моему ты выше где-то писал "я не могу вынести код в модель, так как использую коллбеки", а я тебе пытаюсь объяснить что наличие коллбков или промисов никак не мешает разносить код по разным классам. Промисы для этого не требуются, можно на коллбеках писать, только не так удобно получается.
Вот тебе пример с коллбеками:
backend.getMessagesFor('Ivan', onSuccess, onError);
Суть проблемы в том, что желательно вынести код взаимодействия с бекендом в отдельный класс или слой, а не писать все в контроллере.
> Или вы имели ввиду что функция transformData будет выводить шаблон и парсить из него данные, в нашем случае, в Model.messages? Это совсем не эффективно, но я не вижу другого выхода.
Нет, я этого не имел в виду. Я написал, что есть разные способы, которыми можно передавать данные: в виде массивов, полученных с сервера, или в виде объектов-сущностей. И функция transform - это какая-то условная функция, которая преобразует данные из одного формата в другой.
> я не хочу писать хранилище
> как получить данные, чтобы потом с ними можно было работать локально.
Это ведь противоречит друг другу. Если ты хранишь где-то данные - то это и есть хранилище.
Либо надо писать хранилище, либо можно просто использовать промис:
messagesPromise = backend.getMessages();
messagesPromise.then(function (data) {
renderMessageList(data);
});
Тут еще конечно такой момент, что все эти вызовы асинхронные, ответ придет неизвестно когда, и важно не пытаться отрисовывать сообщения для Пети, если пользователь уже переключился на Васю. Значит, ты теперь должен еще по получению данных проверить, какой у нас сейчас открыт диалог (и открыт ли он вообще).
Твой код в посте в общем неправильный - ты пытаешься его писать последовательно (как на PHP), но это работает только с синхронными функциями. Суть асинхронных функций в том, что ты запускаешь какую-то операцию и не ждешь, пока она завершится, а что-то делаешь дальше.
То есть если пользователь щелкает на имя - то ты обновляешь интерфейс, очищаешь диалог, запускаешь загрузку сообщений с сервера. А когда они придут, если они нам все еще нужны, то мы отрисовываем их на экране.
По моему ты выше где-то писал "я не могу вынести код в модель, так как использую коллбеки", а я тебе пытаюсь объяснить что наличие коллбков или промисов никак не мешает разносить код по разным классам. Промисы для этого не требуются, можно на коллбеках писать, только не так удобно получается.
Вот тебе пример с коллбеками:
backend.getMessagesFor('Ivan', onSuccess, onError);
Суть проблемы в том, что желательно вынести код взаимодействия с бекендом в отдельный класс или слой, а не писать все в контроллере.
> Или вы имели ввиду что функция transformData будет выводить шаблон и парсить из него данные, в нашем случае, в Model.messages? Это совсем не эффективно, но я не вижу другого выхода.
Нет, я этого не имел в виду. Я написал, что есть разные способы, которыми можно передавать данные: в виде массивов, полученных с сервера, или в виде объектов-сущностей. И функция transform - это какая-то условная функция, которая преобразует данные из одного формата в другой.
>Это ведь противоречит друг другу. Если ты хранишь где-то данные - то это и есть хранилище.
Прощу прощения, я думал написать хранилище, это значит написать целую БД. Тогда нужно написать и хранилище.
>Твой код в посте в общем неправильный - ты пытаешься его писать последовательно (как на PHP), но это работает только с синхронными функциями. Суть асинхронных функций в том, что ты запускаешь какую-то операцию и не ждешь, пока она завершится, а что-то делаешь дальше.
Но мы же создаем функцию которая выполниться когда запрос выполниться успешно.
По поводу локального хранилища, я только что подумал, что оно понадобиться в другой ситуации, когда пользователь захочет не хранить историю на сервере. Для этого нужен будет совсем другой алгоритм, который минует БД. Тут нужно будет ещё подумать как это сделать. Наверно, нужно будет использовать сокеты, я пока не изучил эту технологию.
Хотя, можно написать его и просто так, чтобы приложение могло работать в офлайне. Только не понятно как это сделать, потому что сорри никак не сохранить данные вне коллбак функции.
Чтобы приложение работало в оффлайне, нужно хранилище. Хотя бы для того, чтобы хранить написанные, но пока не доставленные на сервер, сообщения.
Без хранилища можно обойтись, но тогда на каждое действие придется делать запросы к серверу.
> Но мы же создаем функцию которая выполниться когда запрос выполниться успешно.
Тогда другое дело.
> Тогда нужно написать и хранилище.
Только я бы советовал попробовать все же сначала без хранилища реализовать MVC. А потом уже его добавлять.
> Наверно, нужно будет использовать сокеты, я пока не изучил эту технологию.
Нет, почему, сокеты тут не при чем. Тут наверно надо изучать localStorage и IndexedDB.
>>1011302
> то я могу только сохранить обещание. Но и в этом случае я не вижу смысла, потому что всё равно нельзя никуда сохранить результат,
Зачем сохранять результат, если он хранится внутри Promise и его всегда можно оттуда получить?
>> Тогда нужно написать и хранилище.
>Только я бы советовал попробовать все же сначала без хранилища реализовать MVC. А потом уже его добавлять.
Да, об этом: Если я сделаю отдельный класс Backend, то модель компонентов контактов и сообщений будет пустой, как сделано это сейчас. Лучше сделать свой Backend для каждого из компонентов?
>> то я могу только сохранить обещание. Но и в этом случае я не вижу смысла, потому что всё равно нельзя никуда сохранить результат,
>Зачем сохранять результат, если он хранится внутри Promise и его всегда можно оттуда получить?
Что вы имеете ввиду? Я как не пытался не смог получить результат аякса, ни из самого аякса, ни из промиса. Именно из-за этого у нас получилось такая большая дискуссия, и в итоге, я всё равно не понимаю как это сделать.
Сохранять результат нужно чтобы было хранилище.
Backend - это часть модели приложения, а не часть виджета. Так что нет, нелогично делать для каждого виджета свой класс для работы с API. Виджет, если это требуется, вообще-то не обязан отображать данные именно с сервера, он может отображать что угодно.
> Что вы имеете ввиду? Я как не пытался не смог получить результат аякса, ни из самого аякса, ни из промиса.
Я думаю, тут нужен пример кода, а то мы так и не поймем друг друга.
И еще. В асинхронном коде нельзя написать так:
var data = getDataAsync(); // синхронный код
Можно только так:
var promise = getDataAsync();
promise.then(...);
или так:
getDataAsync(onSuccess, onError);
Соответственно, код надо писать с учетом этой особенности.
В хранилище данные сохранить тоже просто:
var promise = getDataFromServer();
promise.then(function (r) {
storage.putData(r);
});
Но я получаю undefined почему-то постоянно. Пример всё тот же:
> Controller.runMessages() {
> var promise = Model.getMessages();
>
> promise.then(function(data) {
> Model.messages = data;
> });
> }
>
> console.log(Model.messages); //undefined
Причем не важно, оборачиваю я сообщения в сеттер или нет.
Что не так?
Точнее не undefined, а значение которое я выставил конструкторе Model.
Так вызывается. Но что если нужно будет что-то проделать с сообщениями, например поиск по ним? Неужели, это тоже нужно будет делать внутри коллбека?
Можно из коллбека вызвать отдельную функцию поиска, не вижу тут никакой проблемы.
Да даже если есть локальное хранилище, но оно сделано например на IndexedDB, то выборки из него тоже будут асинхронными. То есть надо учиться писать асинхронный код в любом случае, если хочешь сделать хорошее приложение.
>То есть надо учиться писать асинхронный код в любом случае, если хочешь сделать хорошее приложение.
Я попробую.
> Для начала, никогда не храни открытые пароли. Храни соленые хеши от них
Имеется ввиду не хранить в базе данных, в самом классе моделей можно? А то тогда что, пароль из поля формы таскать по функциям валидатором отдельно? validate ($model, $pass)?
В уроке ОПа метод getAgeDays всего лишь формирует разницу в днях из уже существующих данных, бизнес логики в методе нет. В твоём примере password_hash - это бизнес логика, которая может поменятся. Ещё проблемы:
- функции password_hash не нужна соль, функция сама её генерирует и добавляет в хеш: https://secure.php.net/manual/en/function.password-hash.php
> It is now preferred to simply use the salt that is generated by default.
- уместней функцию password_hash и вообще всё, что касается регистрации вынести в отдельный класс. Тогда этот класс у тебя может стать независимым и использоваться в разных проектах.
- я не понял, что такое del в имени метода, к чему эти непонятные сокращения?
> А то я вот думаю, что модель сущность - это просто хранилище данных
Всякие Фаулеры с этим не согласны, такое хранилище данных они называют анемичной моделью (глупым хранилищем данных, ничем от массива не отличающимся). Но на практике я вижу у всех анемичную модель и сам её использую.
> или же некоторые (внешние)
Фикс: или же внутренние, полученные без пользовательских данных. Даты и айдишник, например.
Такой же вопрос, только теперь про константы. У меня есть метод FileHelper::renameFile($file), который смотрит константы максимальной длины имени и формата файла из класса FileValidator.. Нужно ли отразить класс FileValidator в зависимостях класса FileHelper?
https://jsfiddle.net/44whaL2v/
Что я делаю не так?
У тебя переменная $payout не передаётся в функцию.
В отличие от js, функции видят переменные только в своей области видимости.
Если интересно подробнее об этом, то в справочнике есть об этом информация: https://secure.php.net/manual/ru/language.variables.scope.php
Валидировать нужно данные которые не надёжны, то есть, зачастую которые приходят от пользователя.
Переведи в переводчике как переводиться слово validation (~проверка\аттестация) - ты же не будешь проверять свои собственные данные, которым ты доверяешь.
>В твоём примере password_hash - это бизнес логика, которая может поменятся.
А разве сеттеры не предназначены для того чтобы преобразовывать данные в нужный формат?
>>1011815
Вообще-то да, в том случае это бизнес логика. Но что если это сделать в сеттере?
setHash($password)
{
$this->hash = password_hash(...);
}
П.С, Знаю что оффтоп, но вы же тут сайтоделы или нет?.
За 60 секунд не получится, лучше заплати кому-нибудь на бирже фриланса - выйдет быстрее и проще. Уважай труд других, люди годами улучшают и оттачивают свои навыки, не всё так просто как тебе внушили в рекламе.
Господа, только начал осваивать php, до этого никогда не кодил и образование с профессией вообще с компами не связано.
Учусь по учебнику из шапки. Выполнил задачу с кубиками, но не уверен, что код был написан правильно из-за чего могут быть проблемы в будущем с другими задачками.
Задача выполнена. Кубики бросаются и результат говорится верно, но есть ли какие-то замечания?
Заранее спасибо, ребят.
1 пик - мой код, 2 пик - код который нужно было дописать.
>выйдет быстрее и проще
Знаю, но я думал сам поковыряться.
>люди годами улучшают и оттачивают свои навыки
Вот и я хочу улучшать и оттачивать навыки. Мне и нужен гайд для начинающих вангую что там должен быть пример "как вставить 3 картинки на сайт", а дальше уже буду скроллить тред и т.д. Просто у вас в уроках по основам переменные/циклы/массивы в которые я давно умею.
Ты забыл что есть оператор elseif (условие), который означает - иначе, если.
Об этом написано в самом начале урока.
Подробнее здесь:
https://secure.php.net/manual/ru/control-structures.if.php
https://secure.php.net/manual/ru/control-structures.else.php
https://secure.php.net/manual/ru/control-structures.elseif.php
Спасибо огромное!
+frontend
--models
--views
--controllers
+backend
--models
--views
--controllers
libs
index.php
Route.php
Config.php
У меня вообще не работает ни в одном коде, просто умножение не работает вообще, результат всегда 0.
Вот у меня работает http://ideone.com/q12wyG.
У тебя нет каких-то сторонних плагинов или расширений в браузере, которые могут вмешиваться в работу редактора кода?
https://github.com/enotocode/birthday_reminder
Привет!
Пока еще не запилил сервер для проверки проекта онлайн.
>>И возможно параллельное редактирование несколькими пользователями, оффлайн-режим.
Сделаю возможность доступа и одновременного редактирования с нескольких устройств, например с телефона и компьютера, это несколько усложнит задачу
>>оффлайн-режим
почему бы и нет
>>Если есть АПИ, то его можно тестировать курлом или автоматизированными тестами (!это тоже интересная тема!)
Пожалуй как закончу с регистрацией/аутенфикацией и приведением в порядок кода и структуры файлов.
Познакомился с Silex/React/React-router, сделал форму регистрации, которая отправляет данные на API серверной части, где происходит валидация, и отправка сообщений об ошибках обратно в форму.
Буквально каждый шаг сопровождается чтением мануалов, пока процесс напоминает тест компонентов. Структура файлов проекта тоже запутанная. Как рассортировать это добро?
https://github.com/enotocode/birthday_reminder/blob/master/src/Classes/User.php#L18
По валидации формы, может быть нужно сделать валидацию на стороне клиента, например, пользователь еще при вводе имени видит сообщения об ошибках, например если он ввел цифру. Как лучше это делать, полностью на стороне клиента, или отправляя запрос на сервер после вода каждого символа? Если делать на стороне клиента, тогда валидация будет дублироваться, т.к. перед тем, как отправить запрос в базу, все равно нужно будет проверять инфу пришедшую извне.
https://github.com/enotocode/birthday_reminder/blob/master/src/Js/SignupForm.js
Вопрос по формам в React, где хранить состояния полей, в родительском элементе, или каждое поле само хранит свое состояние? Как организовать код формы, чтобы не дублировать обработчики событий или код базовых элементов TextInput и FormLabel в форме регистрации и форме аутенфикации?
>Если делать на стороне клиента, тогда валидация будет дублироваться, т.к. перед тем, как отправить запрос в базу, все равно нужно будет проверять инфу пришедшую извне.
Я мимо и нихуя не понял твоего поста в целом, но как истиный хэлоувордщик доебусь до мелочи и отвечу по ней.
У тебя эти валидации служат разным задачам. Валидация на стороне клиента призвана помочь клиенту легко и быстро вбивать правильные данные, без того что бы ввел кучу хуйни а в ответ после перезагрузки страницы "сосите писос, у вас тут хуета)))"
А валидация на стороне сервера - что бы защитить систему от попыток взлома и прочих: Vasya`;show database;
Так что нихуя они не дублируют друг друга, просто понять надо какая где реакция на какую проверку.
Еще напишу, что все эти "ну мб валидации на стороне клиента будет достаточно, ведь кто там будет пытаться хакать нас через отключение фронтэнд проверок в своем браузере" это только до первого взлома, после чего ты уже всегда ждешь этой хуйни и пишешь проверку предпочтительно на серере, а на фронте лишь для удобства пользователей, да.
>как на локальной машине на какой-нибудь openserver фастиком развернуть свой helowold на это цмс с натягиванием простой темы.
а нету часом гайда уровня /pr?
function getMediaInfo()
{
return $this->mediaInfo->getContainer()
}
...или все таки надо возвращать MediaInfo объект и клиентским код пусть вытаскивает этот массив сам?
...потому что я не пишу getMediaInfoWidth(), getMediaInfoHeight(). А просто getContainer() звучит не понятно.
Нашел ответ: оказалось, что во время обновления, поднятие мышки совершалось уже на не существующем элементе.
https://jsfiddle.net/44whaL2v/1/
>>1012278
Так то оно так, тогда нужно сделать вывод сообщений об ошибках и от сервера, и от клиентской части.
Как человек, подробатывавший лабами и репетиторством сообщаю, что это довольно частая фигня и не означает что "все пропало", "дальше ловить нечего" и т.д.
28 лет, сука, и он уже бог программирования, да что там, он уже в 16 лет был признанным богом
Я чуть младше него и у меня ни-ху-я, хотя я постоянно какие-то потуги совершаю, но всё мимо. Я по сравнению с ним ребёнок в песочнице.
Пиздец демотивирует
>Можно из коллбека вызвать отдельную функцию поиска, не вижу тут никакой проблемы.
А разве это не нарушение распределения ответственности? Получается что функция обновления сообщений отвечает и за поиск сообщений, и может быть ещё за другие функции, которые нужно будет проделать с сообщениями.
И ещё.
Исходя из этого, поскольку, при обновлении контактов, есть необходимость обработать клик по ним и провести работу с другим виджетом Сообщений, пришлось перенести эту функцию в главный контроллер, чтобы не нарушать зависимости.
https://github.com/someApprentice/chat/blob/9c54e467af60ea2921cd3c462a2e617f034218b5/public/js/chat.js#L51-L69
https://github.com/someApprentice/chat/blob/9c54e467af60ea2921cd3c462a2e617f034218b5/public/js/chat.js#L66
После этого, мне показалось логичным вынести все остальные обработчики тоже в него, раз теперь он обрабатывает хотя бы один из них. Не размазывать же их по коду. Я правильно поступаю?
https://github.com/someApprentice/chat/blob/9c54e467af60ea2921cd3c462a2e617f034218b5/public/js/chat.js#L5-L14
Распишите мне пожалуйста эту задачу, как работает функция поиска? Что делает оператор "as"? Я тупой
Усе, почему только в англосегменте такие вопросы обсуждают?
Там нету функции поиска. Ты должен сделать её сам.
foreach это итератор оператор, который обходит каждый элемент итерируемого значения массива.
Оператор as определяет чему будет равен текущий элемент итерации.
Подробнее тут https://secure.php.net/manual/ru/control-structures.foreach.php
>сделать вывод сообщений об ошибках и от сервера
Нет, у тебя законопослушный пользователь никогда не попадет на ответ от сервера. Потому что он не будет отключать фронтэнд проверки и слать тебе напрямую запросы. Поэтому с законопослушными пользователями по человечески обращаешься, а с кулцкахерами мамкиными хоть как со скотом можно имхо. просто белый экран оставляй им, нехуй вообще инфу давать долбоебам
Давно отметил для себя как первую заповедь. Сразу снимается куча головной боли.
Кубики https://ideone.com/X3rtNK
Вклад https://ideone.com/Byzfl1
Айфон в кредит https://ideone.com/YxZyJ8
Задачка на массивы №2 https://ideone.com/kPRsg4
Задачка на массивы №3 https://ideone.com/NWidN6
Генератор имён https://ideone.com/W78oyQ
>2к17
>аноны ебашат процедурщину в похапе
Кажется, даже если в пхп добьют все ООПшные штуки, введут ебанный тип вектор и даже сделают нормальную типизацию, ситуацию не изменить. Господь, жги.
>Только начал разбираться с MVC. Необходимо сделать сайт с админкой, поясни анон, такая архитектура норм?
Только это не архитектура. Это набор папок и подпапок. Покажи код, тогда уже можно будет о чем-то сказать. А так ну похоже на MVC. Мой тебе совет -- не пиши велосипед, если так интересно, как оно устроено внутри, то бери и разбирай по кирпичикам с помощью xdebug или т.п. хрени устройство какого-нибудь yii, laravel, symfony, так поймешь, как там потроха фреймов работают по итогу.
>Мой тебе совет -- не пиши велосипед, если так интересно, как оно устроено внутри, то бери и разбирай по кирпичикам с помощью xdebug или т.п. хрени устройство какого-нибудь yii, laravel, symfony,
Что-то это совсем хардкор. Я так-то MVC велосипедил и то в 600 строках кода разбирался неделю, ибо сложно было понять как вообще это всё гоняется, а уж как в БД всё это пихать и к шаблонам приконектить как я хочу - так вообще до сих пор иногда подумать приходится.
А уж такие монстры как yii, laravel, symfony - это вообще жесть. Там чёрт ногу сломит искать что к чему. Особенно с нуля.
Мимопроходил.
Это все-таки уморительно, когда человек думает, что допустимо учить будущего программиста писать код так, чтобы он не соответствовал ни одному стандарту языка. А зачем? Мы же не на продакшене!111
Все-таки это не я путаю продакшен код с учебным, это ты просто считаешь, что учебный код должен быть лютым пахучим калом, который обязательно из тебя вылезет в продакшене.
>Что-то это совсем хардкор. Я так-то MVC велосипедил и то в 600 строках кода разбирался неделю, ибо сложно было понять как вообще это всё гоняется, а уж как в БД всё это пихать и к шаблонам приконектить как я хочу - так вообще до сих пор иногда подумать приходится.
>А уж такие монстры как yii, laravel, symfony - это вообще жесть. Там чёрт ногу сломит искать что к чему. Особенно с нуля.
>Мимопроходил.
Ну так-то да. Я сам в потроха лезу от дела к делу, а чтобы с нуля написать свой mvc-фреймворк -- это пока что для меня что-то жуткое. Ну прям совсем. А что до того как пихать БД, то в случае Yii -- например, AR. Можно ее отдельно поизучать, если интересно.
Спасибо, ваше мнение очень важно для нас, хорошо что вы делитесь с нами своим бесценным опытом, который без сомнения будет нам полезен. Вы, судя по всему очень много работали программистом, раз не смогли отличить код для решения примеров с продакшн кодом. А посему делитесь своим опытом чаще. САРКАЗМ.
Да вроде у опа есть на эту тему небольшие уроки. Должно помочь более менее ориентироваться.
Ну сложно ему в 11 строках кода разобраться, бывает. Не трогай его, может он заслуженный учитель России, и знает как нужно лучше учить программистов чем все остальные в мире.
set_error_handler(function(...) {
header('HTTP/1.1 500 Internal Server Error');
});
"Заголовок": "Амортизатор капота багажника" ;
"Товар": "ST34_4" ;
Нужно это дело представить на странице сайта как на пикчи в виде списка. Но информацию я получаю через php и как ее передать в css?
То есть необходимо, как я думаю, сделать генерацию таблицы css в зависимости от размера принятых данных. Как сделать подобное? Можете подсказать где про это можно почитать или посмотреть.
Нужно понимать разные принципы, на основании которых построены фреймворки. Если ты делал задачу про студентов из ОП поста, то там в комментариях даются ссылки на уроки про MVC, про DI, про паттерны работы с БД и так далее. Но конечно, даже с ними придется поломать голову, чтобы понять как что устроено.
Если ты вдруг захочешь разобраться в каком-нибудь фреймворке, или написать свой, можешь писать тут то, что тебе непонятно - я или кто-нибудь еще постараемся помочь разобраться (ну или отправим читать теорию).
Мне большинство вещей в фреймворках (Юи, Симфони) понятно. Хотя чтобы разобраться в Symfony Forms, мне пришлось нарисовать большую диаграмму взаимодействия классов, параллельно просматривая исходный код. Без диаграммы было трудно, так как там названия классов часто довольно абстрактные и плохо укладываются в голове, фабрика на фабрике.
>>1013250
Что плохого в процедурном коде?
Это задачи для самых начинающих, тех, кто может быть всего несколько дней назад написал свою первую программу. Для программы из 10 строк процедурный подход выглядит абсолютно оправданным. Ты не предложил альтернативного подхода или альтернативного решения, только намекнул на ООП, но я считаю, что для такой программы использование ООП неоправданно и не дает преимуществ.
И разумеется, при изучении операторов вроде if, тратить перед этим время на изучение сложных парадигм программирования также неоправданно.
Этот пост я написал специально для других анонов, чтобы они помнили, что в разных случаях используются разные подходы. Не надо тянуть сложный фреймворк в задачу, которая решается скриптом в 10 строчек.
---
Кстати, в Википедии есть статьи про разные парадигмы: https://ru.wikipedia.org/wiki/Процедурное_программирование - советую глянуть тем, кто интересуется.
>>1013256
Каким стандартам не соответствуют мои уроки? Или решения, предоставленные аноном? Пока нет конкретных сссылок, буду считать что ты просто скучающий тролль.
Нужно понимать разные принципы, на основании которых построены фреймворки. Если ты делал задачу про студентов из ОП поста, то там в комментариях даются ссылки на уроки про MVC, про DI, про паттерны работы с БД и так далее. Но конечно, даже с ними придется поломать голову, чтобы понять как что устроено.
Если ты вдруг захочешь разобраться в каком-нибудь фреймворке, или написать свой, можешь писать тут то, что тебе непонятно - я или кто-нибудь еще постараемся помочь разобраться (ну или отправим читать теорию).
Мне большинство вещей в фреймворках (Юи, Симфони) понятно. Хотя чтобы разобраться в Symfony Forms, мне пришлось нарисовать большую диаграмму взаимодействия классов, параллельно просматривая исходный код. Без диаграммы было трудно, так как там названия классов часто довольно абстрактные и плохо укладываются в голове, фабрика на фабрике.
>>1013250
Что плохого в процедурном коде?
Это задачи для самых начинающих, тех, кто может быть всего несколько дней назад написал свою первую программу. Для программы из 10 строк процедурный подход выглядит абсолютно оправданным. Ты не предложил альтернативного подхода или альтернативного решения, только намекнул на ООП, но я считаю, что для такой программы использование ООП неоправданно и не дает преимуществ.
И разумеется, при изучении операторов вроде if, тратить перед этим время на изучение сложных парадигм программирования также неоправданно.
Этот пост я написал специально для других анонов, чтобы они помнили, что в разных случаях используются разные подходы. Не надо тянуть сложный фреймворк в задачу, которая решается скриптом в 10 строчек.
---
Кстати, в Википедии есть статьи про разные парадигмы: https://ru.wikipedia.org/wiki/Процедурное_программирование - советую глянуть тем, кто интересуется.
>>1013256
Каким стандартам не соответствуют мои уроки? Или решения, предоставленные аноном? Пока нет конкретных сссылок, буду считать что ты просто скучающий тролль.
> Кубики https://ideone.com/X3rtNK
Все верно
> Вклад https://ideone.com/Byzfl1
Тоже верно
> Айфон в кредит https://ideone.com/YxZyJ8
$month = 1 можно было поместить в шапку цикла for (внутрь скобок).
Блок else не требуется, так как внутри if стоит break, и код можно было написать просто после if.
Решено верно.
> Задачка на массивы №2 https://ideone.com/kPRsg4
Тут все правильно.
> Задачка на массивы №3 https://ideone.com/NWidN6
Тоже верно
> Генератор имён https://ideone.com/W78oyQ
> $randomText[$i] =
В таких случаях проще полагаться на автоматическую простановку ключей и писать $a[] = ....
Решение верное.
>>1013068
Белый экран как раз плохая идея. Ведь кроме хакеров, запросы напрямую могут отправлять коллеги-разработчики и отстутствие информации заставит их потратить больше времени. Потому в случае ошибки так и нужно писать, что произошла ошибка. А подробности ошибки пусть идут в лог.
Также, на дев-сервере (когда включен display_errors) удобнее показывать текст ошибки сразу.
И конечно, если ошибка возникла при генерации обычной страницы, нужно добавлять соответствующий HTTP код вроде 500 или 503, чтобы роботы не путались и не пытались проиндесировать сообщение об ошибке.
Ну и еще есть пользователи старых браузеров без HTML5 валидации и пользователи, отключившие яваскрипт или у которых он не загрузился из-за плохой связи. У них тоже проверки на стороне браузера не будут проведены.
Также, есть вещи, которые не проверить только на клиенте: например, уникальность email или проверка сообщения на спам. Потому лучше заложить возможность серверу вернуть ошибку, а клиенту ее отобразить.
> Кубики https://ideone.com/X3rtNK
Все верно
> Вклад https://ideone.com/Byzfl1
Тоже верно
> Айфон в кредит https://ideone.com/YxZyJ8
$month = 1 можно было поместить в шапку цикла for (внутрь скобок).
Блок else не требуется, так как внутри if стоит break, и код можно было написать просто после if.
Решено верно.
> Задачка на массивы №2 https://ideone.com/kPRsg4
Тут все правильно.
> Задачка на массивы №3 https://ideone.com/NWidN6
Тоже верно
> Генератор имён https://ideone.com/W78oyQ
> $randomText[$i] =
В таких случаях проще полагаться на автоматическую простановку ключей и писать $a[] = ....
Решение верное.
>>1013068
Белый экран как раз плохая идея. Ведь кроме хакеров, запросы напрямую могут отправлять коллеги-разработчики и отстутствие информации заставит их потратить больше времени. Потому в случае ошибки так и нужно писать, что произошла ошибка. А подробности ошибки пусть идут в лог.
Также, на дев-сервере (когда включен display_errors) удобнее показывать текст ошибки сразу.
И конечно, если ошибка возникла при генерации обычной страницы, нужно добавлять соответствующий HTTP код вроде 500 или 503, чтобы роботы не путались и не пытались проиндесировать сообщение об ошибке.
Ну и еще есть пользователи старых браузеров без HTML5 валидации и пользователи, отключившие яваскрипт или у которых он не загрузился из-за плохой связи. У них тоже проверки на стороне браузера не будут проведены.
Также, есть вещи, которые не проверить только на клиенте: например, уникальность email или проверка сообщения на спам. Потому лучше заложить возможность серверу вернуть ошибку, а клиенту ее отобразить.
А ты читал урок про цикл foreach в учебнике? Он в главе про массивы упомянут. Этот цикл берет по одному элементы массива, копирует ключ и значение текущего элемента в указанные переменные, выполняет тело цикла, затем переходит к следующему элементу, и так далее, пока массив не закончится.
> Что делает оператор "as"?
Это часть конструкции foreach (отдельно от foreach as не употребляется), слева от оператора ты пишешь массив, который надо перебрать, справа - указываешь переменные, в которые надо копировать ключ и значение текущего элемента массива.
foreach ([массив, который надо перебрать] as [переменная, куда копируется ключ] => [переменная, куда копировать значение]) {
тело цикла, которое будет выполнено столько раз, сколько элементов в массиве;
}
Соответственно, для решения этой задачи тебе надо завести переменную-счетчик. Затем внутри цикла ты сравниваешь рост текущего ученика с ростом анона и, если он выше, то увеличиваешь счетчик на один. После окончания цикла ты получишь ответ.
Давай тебе дадим еще дополнительную задачу, для закрепления знаний (после того, как ты решишь задачу про рост и прочитаешь урок про массивы). Только теперь посложнее. Допустим, тебе даны 2 массива. Один содержит температуру по дням в начале 2016 года, другой - температуру в те же дни в 2017 году:
$temp2016 = [-10, -14, -15, -5, -3, 0, 1, -2];
$temp2017 = [-7, -5, -3, -7, -4, 0, -2, -5];
Посчитай с помощью программы:
- сколько дней в 2017 году оказались теплее, чем те же дни в 2016 (ответ - 3 дня)
- сколько холоднее (4 дня)
- сколько дней была такая же температура что и ровно год назад (1 день, когда было 0 градусов)
И еще задача. Дано 2 списка файлов в 2 папках:
$files1 = ['test.txt', 'image.png', 'image2.png'];
$files2 = ['image.jpeg', 'image2.png', 'test.txt', 'test.doc'];
Определи с помощью программы:
- какие файлы есть в первой папке, но отстутствуют во второй
- какие файлы есть только во второй папке
- какие файлы есть в обеих папках
>>1012816
> А разве это не нарушение распределения ответственности? Получается что функция обновления сообщений отвечает и за поиск сообщений, и может быть ещё за другие функции, которые нужно будет проделать с сообщениями.
А я не говорю, что они должны быть в одной функции. Ну вот например:
onUpdateMessagesClick = function () {
var messagesPromise = getMessages();
messagesPromise.then(function (messages) {
displayMessages(messages);
});
};
onSearchKeyPressed = function (searchWord) {
var messagesPromise = getMessages();
messagesPromise.then(function (messages) {
var filtered = filterMessages(messages, searchWord);
displayMessages(filtered);
});
};
Разумеется, при поиске мы можем использовать другую функцию вместо getMesasges - например, getSavedMessages(), которая не шлет запрос на сервер, если данные уже получены.
Ну и конечно код выше - просто пример, он довольно неэффективен, например в том плане, что каждый раз область сообщений перерисовывается целиком, даже если ничего не изменилось. Но вопросы оптимизации лучше рассмотреть отдельно от работы с промисами.
> Исходя из этого, поскольку, при обновлении контактов, есть необходимость обработать клик по ним и провести работу с другим виджетом Сообщений, пришлось перенести эту функцию в главный контроллер, чтобы не нарушать зависимости.
Взаимодействие между виджетами обычно реализуется с помощью событий или коллбеков. То есть код снаружи ставит обработчик на событие выбора контакта и виджет его вызывает:
var contactListWidget = new ContactListWidget(..);
contactListWidget.onContactSelect(function (contact) {
...
});
Заметь, что мы ставим обработчик на событие виджета, не на DOM-событие клика, чтобы не нарушать инкапсуляцию.
Это называется "паттерн Observer" и использование событий позволяет разделить компоненты так, что виджет контактов ничего не знает про другие виджеты (но главный контроллер, конечно, про него знает). Часто есть возможность и снаружи как-то влиять на виджет, вызывая его методы, например:
contactListWidget.selectContact(contact);
Главное делать это без фанатизма, а то можно дойти до того, что каждую строчку текста мы сделаем отдельным виджетом и получится слишком переусложненная система.
> После этого, мне показалось логичным вынести все остальные обработчики тоже в него, раз теперь он обрабатывает хотя бы один из них.
У тебя в коде, по моему идут нарушения инкапсуляции. Ну например:
> clearInterval(that.contacts.contactsInterval);
Зачем ты хранишь свои данные в поле другого объекта? Это выглядит как лишнее усложнение, непонятно, зачем это сделано. Почему не this.contactsInterval?
> that.contacts.backend
Опять же, почему не this.backend?
> that.contacts.view.contactList.mousedown(
Тут вообще никакой инкапсуляции. Почему главный контроллер должен знать подробности внутреннего устройства виджета, вплоть до того, на какой DOM элеимент повесить событие? Идея виджетов в том, чтобы разбить код на изолированные компоненты, которые можно рассматривать по отдельности. Но твой код не следует этому принципу - если поменять верстку в виджете контактов, может сломаться код в главном контроллере.
Ну то есть в твоем коде нет требуемой изоляции, объекты знают то, что им знать не следует, и это приводит к большой спутанности кода, когда изменение в одном месте может что-то сломать где-то в другом месте.
Если твой виджет контактов изолирован, то мы можем просто создать отдельную страницу с одним этим виджетом и он будет работать. Или с несколькими виджетами, и они будут работать либо независимо, либо синхронно (в зависимости от того, как реализованы - используют ли они общую модель или каждый свою).
Попробуй сделать для своего виджета такую страницу. Я думаю, удобно будет вынести код виджета в отдельный файл, чтобы можно было подключить только его. Без главного контроллера.
Также, здесь не очень понятно, почему поиск по контактам сделан в Controller, хотя при этом он постоянно лезет внутрь виджета списка контактов. Раз так, может быть этот код надо перенести в контроллер виджета контактов?
Также, надо решить, с какими данными будет работать виджет контактов? Либо он работает напрямую с бекендом, либо же он получает сверху модель списка контактов и получает данные из нее.
Также, по поводу этого:
> var contacts = new Contacts(backend, contactsModelView, contactsView);
Если виджет контактов может работать только с единственным View, то можно отказаться от его внедрения снаружи, и создавать его в самом виджете.
Еще, contactsModelView никак не используется, зачем она нужна?
А ты читал урок про цикл foreach в учебнике? Он в главе про массивы упомянут. Этот цикл берет по одному элементы массива, копирует ключ и значение текущего элемента в указанные переменные, выполняет тело цикла, затем переходит к следующему элементу, и так далее, пока массив не закончится.
> Что делает оператор "as"?
Это часть конструкции foreach (отдельно от foreach as не употребляется), слева от оператора ты пишешь массив, который надо перебрать, справа - указываешь переменные, в которые надо копировать ключ и значение текущего элемента массива.
foreach ([массив, который надо перебрать] as [переменная, куда копируется ключ] => [переменная, куда копировать значение]) {
тело цикла, которое будет выполнено столько раз, сколько элементов в массиве;
}
Соответственно, для решения этой задачи тебе надо завести переменную-счетчик. Затем внутри цикла ты сравниваешь рост текущего ученика с ростом анона и, если он выше, то увеличиваешь счетчик на один. После окончания цикла ты получишь ответ.
Давай тебе дадим еще дополнительную задачу, для закрепления знаний (после того, как ты решишь задачу про рост и прочитаешь урок про массивы). Только теперь посложнее. Допустим, тебе даны 2 массива. Один содержит температуру по дням в начале 2016 года, другой - температуру в те же дни в 2017 году:
$temp2016 = [-10, -14, -15, -5, -3, 0, 1, -2];
$temp2017 = [-7, -5, -3, -7, -4, 0, -2, -5];
Посчитай с помощью программы:
- сколько дней в 2017 году оказались теплее, чем те же дни в 2016 (ответ - 3 дня)
- сколько холоднее (4 дня)
- сколько дней была такая же температура что и ровно год назад (1 день, когда было 0 градусов)
И еще задача. Дано 2 списка файлов в 2 папках:
$files1 = ['test.txt', 'image.png', 'image2.png'];
$files2 = ['image.jpeg', 'image2.png', 'test.txt', 'test.doc'];
Определи с помощью программы:
- какие файлы есть в первой папке, но отстутствуют во второй
- какие файлы есть только во второй папке
- какие файлы есть в обеих папках
>>1012816
> А разве это не нарушение распределения ответственности? Получается что функция обновления сообщений отвечает и за поиск сообщений, и может быть ещё за другие функции, которые нужно будет проделать с сообщениями.
А я не говорю, что они должны быть в одной функции. Ну вот например:
onUpdateMessagesClick = function () {
var messagesPromise = getMessages();
messagesPromise.then(function (messages) {
displayMessages(messages);
});
};
onSearchKeyPressed = function (searchWord) {
var messagesPromise = getMessages();
messagesPromise.then(function (messages) {
var filtered = filterMessages(messages, searchWord);
displayMessages(filtered);
});
};
Разумеется, при поиске мы можем использовать другую функцию вместо getMesasges - например, getSavedMessages(), которая не шлет запрос на сервер, если данные уже получены.
Ну и конечно код выше - просто пример, он довольно неэффективен, например в том плане, что каждый раз область сообщений перерисовывается целиком, даже если ничего не изменилось. Но вопросы оптимизации лучше рассмотреть отдельно от работы с промисами.
> Исходя из этого, поскольку, при обновлении контактов, есть необходимость обработать клик по ним и провести работу с другим виджетом Сообщений, пришлось перенести эту функцию в главный контроллер, чтобы не нарушать зависимости.
Взаимодействие между виджетами обычно реализуется с помощью событий или коллбеков. То есть код снаружи ставит обработчик на событие выбора контакта и виджет его вызывает:
var contactListWidget = new ContactListWidget(..);
contactListWidget.onContactSelect(function (contact) {
...
});
Заметь, что мы ставим обработчик на событие виджета, не на DOM-событие клика, чтобы не нарушать инкапсуляцию.
Это называется "паттерн Observer" и использование событий позволяет разделить компоненты так, что виджет контактов ничего не знает про другие виджеты (но главный контроллер, конечно, про него знает). Часто есть возможность и снаружи как-то влиять на виджет, вызывая его методы, например:
contactListWidget.selectContact(contact);
Главное делать это без фанатизма, а то можно дойти до того, что каждую строчку текста мы сделаем отдельным виджетом и получится слишком переусложненная система.
> После этого, мне показалось логичным вынести все остальные обработчики тоже в него, раз теперь он обрабатывает хотя бы один из них.
У тебя в коде, по моему идут нарушения инкапсуляции. Ну например:
> clearInterval(that.contacts.contactsInterval);
Зачем ты хранишь свои данные в поле другого объекта? Это выглядит как лишнее усложнение, непонятно, зачем это сделано. Почему не this.contactsInterval?
> that.contacts.backend
Опять же, почему не this.backend?
> that.contacts.view.contactList.mousedown(
Тут вообще никакой инкапсуляции. Почему главный контроллер должен знать подробности внутреннего устройства виджета, вплоть до того, на какой DOM элеимент повесить событие? Идея виджетов в том, чтобы разбить код на изолированные компоненты, которые можно рассматривать по отдельности. Но твой код не следует этому принципу - если поменять верстку в виджете контактов, может сломаться код в главном контроллере.
Ну то есть в твоем коде нет требуемой изоляции, объекты знают то, что им знать не следует, и это приводит к большой спутанности кода, когда изменение в одном месте может что-то сломать где-то в другом месте.
Если твой виджет контактов изолирован, то мы можем просто создать отдельную страницу с одним этим виджетом и он будет работать. Или с несколькими виджетами, и они будут работать либо независимо, либо синхронно (в зависимости от того, как реализованы - используют ли они общую модель или каждый свою).
Попробуй сделать для своего виджета такую страницу. Я думаю, удобно будет вынести код виджета в отдельный файл, чтобы можно было подключить только его. Без главного контроллера.
Также, здесь не очень понятно, почему поиск по контактам сделан в Controller, хотя при этом он постоянно лезет внутрь виджета списка контактов. Раз так, может быть этот код надо перенести в контроллер виджета контактов?
Также, надо решить, с какими данными будет работать виджет контактов? Либо он работает напрямую с бекендом, либо же он получает сверху модель списка контактов и получает данные из нее.
Также, по поводу этого:
> var contacts = new Contacts(backend, contactsModelView, contactsView);
Если виджет контактов может работать только с единственным View, то можно отказаться от его внедрения снаружи, и создавать его в самом виджете.
Еще, contactsModelView никак не используется, зачем она нужна?
Лучше возвращать объект MediaInfo, а то получается куча методов, которые сами ничего не делают, а только делегируют свою работу другому объекту. Делегация подошла бы, если надо как-то ограничить доступ к объекту MediaInfo или как-то обрабатывать возвращаемые им значения, а так - смысла ее использовать нет.
> function getMediaInfo()
> {
> return $this->mediaInfo->getContainer()
По моему должно быть
function getMediaInfo()
{
return $this->mediaInfo;
}
> или все таки надо возвращать MediaInfo объект и клиентским код пусть вытаскивает этот массив сам?
Пусть он не лезет в массив, а вызвает методы класса MediaInfo вроде getWidth().
>>1012266
Пиши в гугле запрос "run php code online" и тестируй, что тебе подходит.
>>1012267
Запости уже ссылку с кодом, где не работает умножение. Так угадать, почему не работает, нельзя.
>>1012249
Зачем ты пытаешься засунуть объект в константу? Во-первых, туда можно класть только строки. Во-вторых, объект не должен быть доступен во всей программе, а только там, где нужна работа с БД. Почитай урок https://github.com/codedokode/pasta/blob/master/arch/di.md
>>1012164
Так же, как и раньше - через getLastInsertId() или аналог.
> Вместо первичного ключа auto increment integer, но меня также интересует вариант для составных ключей.
AUTO_INCREMENT ставится только на одно поле.
Лучше возвращать объект MediaInfo, а то получается куча методов, которые сами ничего не делают, а только делегируют свою работу другому объекту. Делегация подошла бы, если надо как-то ограничить доступ к объекту MediaInfo или как-то обрабатывать возвращаемые им значения, а так - смысла ее использовать нет.
> function getMediaInfo()
> {
> return $this->mediaInfo->getContainer()
По моему должно быть
function getMediaInfo()
{
return $this->mediaInfo;
}
> или все таки надо возвращать MediaInfo объект и клиентским код пусть вытаскивает этот массив сам?
Пусть он не лезет в массив, а вызвает методы класса MediaInfo вроде getWidth().
>>1012266
Пиши в гугле запрос "run php code online" и тестируй, что тебе подходит.
>>1012267
Запости уже ссылку с кодом, где не работает умножение. Так угадать, почему не работает, нельзя.
>>1012249
Зачем ты пытаешься засунуть объект в константу? Во-первых, туда можно класть только строки. Во-вторых, объект не должен быть доступен во всей программе, а только там, где нужна работа с БД. Почитай урок https://github.com/codedokode/pasta/blob/master/arch/di.md
>>1012164
Так же, как и раньше - через getLastInsertId() или аналог.
> Вместо первичного ключа auto increment integer, но меня также интересует вариант для составных ключей.
AUTO_INCREMENT ставится только на одно поле.
Во-первых, папка model выглядит странно, так как модель это часто самая большая часть приложения и придется создавать там подпапки, зачем, лучше просто не делать эту папку. Во-вторых, админке может понадобиться доступ к моделям от фронтенда.
libs - непонятно, для чего, для внешних библиотек?
Ну и наконец, публичную папку стоит сделать отдельной, а у тебя по сути весь код лежит в публичной папке, что плохо.
>>1011842
Мы здесь изучаем веб-технологии, а не как скопировать HTML код. Попробуй открыть код сайта в инструментах разработчика и скопировать оттуда, если он конечно как-нибудь не защищен от копирования.
> как вставить 3 картинки на сайт
HTML изучай.
>>1011866
Скобки там вокруг переменной можно было не ставить, то есть писать
if ($cub1 == $cub2) {
Также, нет проверки на то, что у человека и робота выпали даблы (по 2 одинаковых значения), в результате чего тоже получается ничья. Проверка на это есть в коде в учебнике.
Вместо 3 отдельных иф лучше написать одну группу if () {} elseif () {} else {}
>>1011769
А запрос от браузера на получение файла, он обрабатывается только веб-сервером или веб-сервером + PHP? Во втором случае можно добавить произвольные заголовки, в первом нельзя.
>>1011476
Ответ на вопрос, "кто должен хешировать пароль" - это по сути вопрос о разделении ответственности, какой класс за что отвечает.
Если хеширование делается не только с данными из модели ($this->password), очевидно, его надо вынести наружу. Если оно явно не входит в зону ответственности модели - нужно делать его снаружи. Иначе его можно оставить в модели.
Разделение зон ответственности нужно, чтобы у каждого класса было свое назначение, чтобы он решал свою часть задачи. И чтобы мы могли при правке кода рассматривать только один класс, не трогая другие.
Я бы все-таки вынес его отдельно. Ибо всегда может понадобиться например взять откуда-то пароль и получить его хеш, не имея объекта-модели.
Есть кстати еще третий вариант: сделать хеширование публичным статическим методом в модели. Так его можно будет вызывать с любыми данными, но оно останется в модели (и нам не надо отдельный класс для хеширования паролей). Так что в итоге это сводится к вопросу "чья это зона ответственности". Если у тебя есть сервис авторизации, или что-то, работающее с паролями, наверно лучше поместить туда.
> А то я вот думаю, что модель сущность - это просто хранилище данных, и такие действия не ее ответственность
Так-то да, но какие-то простые манипуляции с этими данными там делать допустимо. Типичный пример из учебников - если в модели есть поля "имя", "фамилия", "отчество", допустимо сделать метод, который будет возвращать полное имя в виде "Иванов И.И.". При этом генерировать это имя он может как сам, так и вызывая внешнюю функцию.
Наверно, мой ответ оказался немного расплывчатым, но каких-то четких критериев я не знаю. На практике - делают и так, и так.
Главное чтобы тебе не пришлось копипастить и писать password_hash в нескольких местах кода.
Во-первых, папка model выглядит странно, так как модель это часто самая большая часть приложения и придется создавать там подпапки, зачем, лучше просто не делать эту папку. Во-вторых, админке может понадобиться доступ к моделям от фронтенда.
libs - непонятно, для чего, для внешних библиотек?
Ну и наконец, публичную папку стоит сделать отдельной, а у тебя по сути весь код лежит в публичной папке, что плохо.
>>1011842
Мы здесь изучаем веб-технологии, а не как скопировать HTML код. Попробуй открыть код сайта в инструментах разработчика и скопировать оттуда, если он конечно как-нибудь не защищен от копирования.
> как вставить 3 картинки на сайт
HTML изучай.
>>1011866
Скобки там вокруг переменной можно было не ставить, то есть писать
if ($cub1 == $cub2) {
Также, нет проверки на то, что у человека и робота выпали даблы (по 2 одинаковых значения), в результате чего тоже получается ничья. Проверка на это есть в коде в учебнике.
Вместо 3 отдельных иф лучше написать одну группу if () {} elseif () {} else {}
>>1011769
А запрос от браузера на получение файла, он обрабатывается только веб-сервером или веб-сервером + PHP? Во втором случае можно добавить произвольные заголовки, в первом нельзя.
>>1011476
Ответ на вопрос, "кто должен хешировать пароль" - это по сути вопрос о разделении ответственности, какой класс за что отвечает.
Если хеширование делается не только с данными из модели ($this->password), очевидно, его надо вынести наружу. Если оно явно не входит в зону ответственности модели - нужно делать его снаружи. Иначе его можно оставить в модели.
Разделение зон ответственности нужно, чтобы у каждого класса было свое назначение, чтобы он решал свою часть задачи. И чтобы мы могли при правке кода рассматривать только один класс, не трогая другие.
Я бы все-таки вынес его отдельно. Ибо всегда может понадобиться например взять откуда-то пароль и получить его хеш, не имея объекта-модели.
Есть кстати еще третий вариант: сделать хеширование публичным статическим методом в модели. Так его можно будет вызывать с любыми данными, но оно останется в модели (и нам не надо отдельный класс для хеширования паролей). Так что в итоге это сводится к вопросу "чья это зона ответственности". Если у тебя есть сервис авторизации, или что-то, работающее с паролями, наверно лучше поместить туда.
> А то я вот думаю, что модель сущность - это просто хранилище данных, и такие действия не ее ответственность
Так-то да, но какие-то простые манипуляции с этими данными там делать допустимо. Типичный пример из учебников - если в модели есть поля "имя", "фамилия", "отчество", допустимо сделать метод, который будет возвращать полное имя в виде "Иванов И.И.". При этом генерировать это имя он может как сам, так и вызывая внешнюю функцию.
Наверно, мой ответ оказался немного расплывчатым, но каких-то четких критериев я не знаю. На практике - делают и так, и так.
Главное чтобы тебе не пришлось копипастить и писать password_hash в нескольких местах кода.
> Но что если это сделать в сеттере?
Это хорошая идея, но функцию хеширования стоит сделать отдельной, если нам понадобится вызвыать ее где-то в другом месте, чтобы не копипастить параметры алгоритма хеширования, чтобы была единственная функция, которая знает, как хешируется пароль.
>>1011812
Иногда стоит проверять и доверенные данные, так как из-за ошибок программиста они могут оказаться неверными. Такая проверка поможет обнаружить ошибку раньше. Обычно используют assert() или аналог.
Ну то есть, если ты знаешь, что в функцию можно передать только положительное число, то можешь написать в начале
assert($x > 0);
>>1011750
А почему функция renameFile смотрит эти константы? Они используются при валидации данных пользователя? Она проверяет новое имя на соответствие формату? Тогда стоит сделать в валидаторе функцию проверки имени и вызвать ее.
Если же они нужны только при формировании имени файла, но не для валидации, тогда им нечего делать в валидаторе.
А так, при обращении к константам класс в зависимостях указывать не нужно.
>>1011740
Есть разные точки зрения, но мне кажется, что удобнее использовать исключения в случае нестандартных ситуаций - а неправильные пользовательские данные это стандартная и ожидаемая ситуация.
Недостаток исключений как минимум в том, что с их помощью нельзя вернуть несколько ошибок.
То есть тут вопрос не "как правильно", а "как удобнее".
> Какие данные нужно валидировать в модели? Все (внешние, которые приходят из пользовательских форм и внутренние полученные внутри приложения, вроде дат, айдишников) или же некоторые (внешние)?
А откуда мы знаем, какие данные там внешние? Валидатор, если работает с моделью, ничего не знает о форме ввода данных. Знать о ней может только валидатор для формы, а не для модели.
Например, мы можем, например, создать модель из данных в БД, и пропустить ее через валидатор, чтобы проверить, что данные в БД корректные.
В теории, стоило бы валидаторовать все. Но тогда возникает другая проблема - что, если например мы сохраняем IP адрес пользователя, и он не соответствует формату? Мы же не можем написать пользователю "у вас IP неправильный". Это ведь не пользователь виноват, а где-то ошибка в нашем коде, из-за которой IP сохранился неверно.
Потому придется еще и разделить ошибки на "пользовательские", о которых мы ему сообщаем, и "технические", о которых не можем сообщить. Для "технических" ошибок мы можем выкинуть исключение (и пользователь увидит например страницу HTTP ошибки 503), либо можем не проверять их (то есть не проверять, что IP адрес правильный), или проверять в облегченном режиме (проверять что IP адрес есть, но не проверять его формат).
> Но что если это сделать в сеттере?
Это хорошая идея, но функцию хеширования стоит сделать отдельной, если нам понадобится вызвыать ее где-то в другом месте, чтобы не копипастить параметры алгоритма хеширования, чтобы была единственная функция, которая знает, как хешируется пароль.
>>1011812
Иногда стоит проверять и доверенные данные, так как из-за ошибок программиста они могут оказаться неверными. Такая проверка поможет обнаружить ошибку раньше. Обычно используют assert() или аналог.
Ну то есть, если ты знаешь, что в функцию можно передать только положительное число, то можешь написать в начале
assert($x > 0);
>>1011750
А почему функция renameFile смотрит эти константы? Они используются при валидации данных пользователя? Она проверяет новое имя на соответствие формату? Тогда стоит сделать в валидаторе функцию проверки имени и вызвать ее.
Если же они нужны только при формировании имени файла, но не для валидации, тогда им нечего делать в валидаторе.
А так, при обращении к константам класс в зависимостях указывать не нужно.
>>1011740
Есть разные точки зрения, но мне кажется, что удобнее использовать исключения в случае нестандартных ситуаций - а неправильные пользовательские данные это стандартная и ожидаемая ситуация.
Недостаток исключений как минимум в том, что с их помощью нельзя вернуть несколько ошибок.
То есть тут вопрос не "как правильно", а "как удобнее".
> Какие данные нужно валидировать в модели? Все (внешние, которые приходят из пользовательских форм и внутренние полученные внутри приложения, вроде дат, айдишников) или же некоторые (внешние)?
А откуда мы знаем, какие данные там внешние? Валидатор, если работает с моделью, ничего не знает о форме ввода данных. Знать о ней может только валидатор для формы, а не для модели.
Например, мы можем, например, создать модель из данных в БД, и пропустить ее через валидатор, чтобы проверить, что данные в БД корректные.
В теории, стоило бы валидаторовать все. Но тогда возникает другая проблема - что, если например мы сохраняем IP адрес пользователя, и он не соответствует формату? Мы же не можем написать пользователю "у вас IP неправильный". Это ведь не пользователь виноват, а где-то ошибка в нашем коде, из-за которой IP сохранился неверно.
Потому придется еще и разделить ошибки на "пользовательские", о которых мы ему сообщаем, и "технические", о которых не можем сообщить. Для "технических" ошибок мы можем выкинуть исключение (и пользователь увидит например страницу HTTP ошибки 503), либо можем не проверять их (то есть не проверять, что IP адрес правильный), или проверять в облегченном режиме (проверять что IP адрес есть, но не проверять его формат).
> глупым хранилищем данных, ничем от массива не отличающимся
Отличается наличием четко определенных полей и дополнительными методами. Но ты прав, что есть споры "за" и "против" "глупых" моделей.
На практике использование "умных" моделей приводит к тому, что в них кладут все подряд. Например в Юи (и в Руби он Рейлс) используется Active Record, там модель умеет делать многое - и работать с БД, и валидировать сама себя, а особо продвинутые туда еще и какой-нибудь код рассылки писем могут запихнуть. И получается антипаттерн God Object, и гигантские классы моделей.
Возьмем в качестве примера Magento - движок для интернет-магазина, который писало много разработчиков, который пережил не один рефакторинг. Как вам модель товара?
- https://github.com/magento/magento2/blob/develop/app/code/Magento/Catalog/Model/Product.php
Обратите внимание на конструктор, а также на то, что местами там есть статические вызовы Registry (ObjectManager::getInstance()).
>>1011469
Если модель представляет запись в БД, то очевидно, что пароль в ней храниться не может, а должен храниться хеш. Так что да, надо либо хранить пароль отдельно (и передавать в валидатор отдельно), либо иметь объект, представляющий данные формы.
>>1010968
Вроде бы у меня нет кода решения для своей же задачи. Но я могу посмотреть твой, и сказать, что не так.
У тебя там есть ошибки, если посмотреть внизу:
> PHP Notice: Use of undefined constant pet - assumed 'pet' in /home/6JVyqO/prog.php on line 225
> PHP Notice: Use of undefined constant nov - assumed 'nov' in /home/6JVyqO/prog.php on line 226
Их надо исправить.
Поля 'visited', 'from', 'shortest_path' наверно стоит хранить в отдельных массивах. Они ведь не являются частью исходной информации о точках, а нужны временно, пока выполняется алгоритм.
Название функции asd неудачное, так как оно не имеет смысла.
Плохо, что у тебя весь код сделан одной функцией - удобнее было бы разделить поиск пути и вывод ответа. И четко определить, что делает каждая функция, что получает на вход, что дает на выходе.
> while ($paths[$end_point]['visited'] == false) { // циклим пока точка до которой мы идем не станет true
Это ведь неправильно, по моему цикл надо выполнять, пока есть непосещенные вершины, а то может получиться, что мы нашли один путь до конца, но не проверили другие варианты, и не нашли более короткий путь.
Посмотри например алгоритм Дейкстры в Википедии: https://ru.wikipedia.org/wiki/Алгоритм_Дейкстры#.D0.90.D0.BB.D0.B3.D0.BE.D1.80.D0.B8.D1.82.D0.BC
Я вижу у тебя несколько отклонений от алгоритма, ну например, там предлагается в начале цикла выбирать для рассмотрения вершину с минимальным расстоянием от старта. У тебя же это перенесено в конце цикла. Ты уверен, что это изменение не повлияет на работу алгоритма?
Также, в цикле поиска непосещенной вершины с мин. расстоянием нет проверки - а что, если таких вершин больше нет (например, все посещены или есть непосещенные вершины, не связанные со стартом)? Для надежности стоило бы проверять этот вариант.
$start_point названа неудачно, правильнее ее назвать $current_point (текущая точка). Учись давать такие имена, чтобы код легче читался.
> krsort($faster_paths); // сортируем что бы по порядку шёл
для этого есть array_reverse.
> echo $transportName[$paths[$paths[$path]['from']]['neighbors'][$path]['transport']]
Это тяжело читать, тут надо вынести часть выражения в отдельные переменные с понятным названием.
> глупым хранилищем данных, ничем от массива не отличающимся
Отличается наличием четко определенных полей и дополнительными методами. Но ты прав, что есть споры "за" и "против" "глупых" моделей.
На практике использование "умных" моделей приводит к тому, что в них кладут все подряд. Например в Юи (и в Руби он Рейлс) используется Active Record, там модель умеет делать многое - и работать с БД, и валидировать сама себя, а особо продвинутые туда еще и какой-нибудь код рассылки писем могут запихнуть. И получается антипаттерн God Object, и гигантские классы моделей.
Возьмем в качестве примера Magento - движок для интернет-магазина, который писало много разработчиков, который пережил не один рефакторинг. Как вам модель товара?
- https://github.com/magento/magento2/blob/develop/app/code/Magento/Catalog/Model/Product.php
Обратите внимание на конструктор, а также на то, что местами там есть статические вызовы Registry (ObjectManager::getInstance()).
>>1011469
Если модель представляет запись в БД, то очевидно, что пароль в ней храниться не может, а должен храниться хеш. Так что да, надо либо хранить пароль отдельно (и передавать в валидатор отдельно), либо иметь объект, представляющий данные формы.
>>1010968
Вроде бы у меня нет кода решения для своей же задачи. Но я могу посмотреть твой, и сказать, что не так.
У тебя там есть ошибки, если посмотреть внизу:
> PHP Notice: Use of undefined constant pet - assumed 'pet' in /home/6JVyqO/prog.php on line 225
> PHP Notice: Use of undefined constant nov - assumed 'nov' in /home/6JVyqO/prog.php on line 226
Их надо исправить.
Поля 'visited', 'from', 'shortest_path' наверно стоит хранить в отдельных массивах. Они ведь не являются частью исходной информации о точках, а нужны временно, пока выполняется алгоритм.
Название функции asd неудачное, так как оно не имеет смысла.
Плохо, что у тебя весь код сделан одной функцией - удобнее было бы разделить поиск пути и вывод ответа. И четко определить, что делает каждая функция, что получает на вход, что дает на выходе.
> while ($paths[$end_point]['visited'] == false) { // циклим пока точка до которой мы идем не станет true
Это ведь неправильно, по моему цикл надо выполнять, пока есть непосещенные вершины, а то может получиться, что мы нашли один путь до конца, но не проверили другие варианты, и не нашли более короткий путь.
Посмотри например алгоритм Дейкстры в Википедии: https://ru.wikipedia.org/wiki/Алгоритм_Дейкстры#.D0.90.D0.BB.D0.B3.D0.BE.D1.80.D0.B8.D1.82.D0.BC
Я вижу у тебя несколько отклонений от алгоритма, ну например, там предлагается в начале цикла выбирать для рассмотрения вершину с минимальным расстоянием от старта. У тебя же это перенесено в конце цикла. Ты уверен, что это изменение не повлияет на работу алгоритма?
Также, в цикле поиска непосещенной вершины с мин. расстоянием нет проверки - а что, если таких вершин больше нет (например, все посещены или есть непосещенные вершины, не связанные со стартом)? Для надежности стоило бы проверять этот вариант.
$start_point названа неудачно, правильнее ее назвать $current_point (текущая точка). Учись давать такие имена, чтобы код легче читался.
> krsort($faster_paths); // сортируем что бы по порядку шёл
для этого есть array_reverse.
> echo $transportName[$paths[$paths[$path]['from']]['neighbors'][$path]['transport']]
Это тяжело читать, тут надо вынести часть выражения в отдельные переменные с понятным названием.
бамп
А покажи код, упакованный в функцию, а то не понятно, что там не так.
>>1010841
Селекторы неудачные. Зачем в ul.soc-network-list указывать ul?
Также, height: 100% работает только в отдельных случаях:
- если есть предок, где высота указана явно (например в px), и у всех родитилей до этого предка высота указана в %. То есть если ее можно вычислить, глядя на высоту родителей.
- если у всех родителей вплоть до HTML высота указана в %
- если элемент использует абс./фикс. поз.
Идея использовать base64 спорная. Ну например, как редактировать такую картинку? Надо тогда как-то тогда генерировать этот base64 код автоматически. Ну и возможно, что выгоднее сделать картинку отдельным файлом, чтобы облегчить CSS.
public лучше вынести из src, и оставить там только PHP код. А JS/CSS перенести внутрь public.
Так как ты используешь webpack, то исходники JS тоже в принципе можно не класть в публичную папку, а например, сделать папку frontend или js и класть туда (правда тогда их нельзя будет загрузить без сборки).
Classes надо переименовать. autoloader унести из Services.
Ну то есть как минимум надо отделить бекенд от фронтенда:
backend/Controller/...
backend/views/...
js/...
public/...
Еще, не знаю, совместимо ли это с webpack, но в dev возможно удобнее загружать файлы напрямую, без сборки. Сборка ведь нужна для оптимизации загрузки скриптов в HTTP/1.1 и для конвертации кода для поддержки старых браузеров, но в dev окружении этих проблем нет, а сборка выглядит как лишний промежуточный шаг.
> Replace the path in RewriteBase directive with path that you use to access to the project in your browser with some changes:
Вроде в Апач 2.4 RewriteBase стало можно не указывать. Ну и почему бы public не сделать корневой папкой сервера?
> return $app->json('New user created', 201)
В JSON корнем должен быть объект или массив. Не строка.
> class TextInput
Название наверно неудачное, можно подумать что тут просто элемент input.
> this.handleInputChange = this.handleInputChange.bind(this);
Вот эта конструкция тоже мне не очень нравится, что мы переписываем ссылку на метод. Разве это нормально?
Ну и конечно компонент React (то есть view/controller в терминах MVC) не должен слать запросы на сервер.
> По валидации формы, может быть нужно сделать валидацию на стороне клиента, например, пользователь еще при вводе имени видит сообщения об ошибках, например если он ввел цифру.
Хорошая идея.
> Как лучше это делать, полностью на стороне клиента, или отправляя запрос на сервер после вода каждого символа?
Вообще, тут можно использовать HTML5 валидацию для начала. Наверно неэффективно слать аякс-запросы, чтобы проверить длину строки.
Ты ведь используешь Валидацию Симфони, значит ничто не мешает тебе автоматизированно получать из модели параметры валидации и также автоматизированно передавать их в фронтенд.
> Если делать на стороне клиента, тогда валидация будет дублироваться, т.к. перед тем, как отправить запрос в базу, все равно нужно будет проверять инфу пришедшую извне.
Валидация на клиенте - это подсказки для удобства пользователя, валидация на сервере - настоящая валидация.
> Вопрос по формам в React, где хранить состояния полей, в родительском элементе, или каждое поле само хранит свое состояние?
Вообще, я не знаю правильного ответа. Надо наверно сравнить недостатки и преимущества каждого подхода.
Можно еще изучить ответы в Гугле: https://www.google.ru/search?q=react+storing+data+in+components&btnG=Поиск&newwindow=1&gbv=1
> Как организовать код формы, чтобы не дублировать обработчики событий или код базовых элементов TextInput и FormLabel в форме регистрации и форме аутенфикации?
А для двух форм нельзя использовать один и тот же TextInput? Ведь это по идее изолированный компонент, и он наверно к любой форме подойдет. То же касается и FormLabel.
Еще по моему неправильно класть работу с аяксом в компонент, да еще и напрямую работать с xhr. Разве не логичнее, что компонент только отвечает за вывод формы, а валидацию делегирует в какой-нибудь валидатор? Что-то мне не нравится твой подход. Ты весь код в контроллере пишешь.
Да и вообще, по задумке компоненты должны быть изолированы. Мне кажется, компонент должен либо:
1) использовать какую-то модель, которую в него передают, для валидации (для этого нужно DI, и я не представляю, как его тут сделать)
2) генерировать событие изменения, в ответ на которое внешний контроллер будет запускать валидацию и помещать в компонент ее результат
Разработчики одним из преимуществ реакта как раз указывают изоляцию: https://facebook.github.io/react/docs/components-and-props.html
> Components let you split the UI into independent, reusable pieces, and think about each piece in isolation.
Очевидно, что компонент, в который гвоздями прибита отправка аякс-запросов, точно не изолированный.
Также ты не учел, что аякс-запросы выполняются асинхронно, у тебя они могут отправляться параллельно и результат более раннего запроса может прийти позже. Тебе нужно отменять лишние запросы или как-то еще организовать очередь, а также скрывать ошибки при изменении данных.
Реакт по идее отвечает только за реализацию интерфейса. Следовательно, бизнес-логика в компонентах содержаться не должна.
public лучше вынести из src, и оставить там только PHP код. А JS/CSS перенести внутрь public.
Так как ты используешь webpack, то исходники JS тоже в принципе можно не класть в публичную папку, а например, сделать папку frontend или js и класть туда (правда тогда их нельзя будет загрузить без сборки).
Classes надо переименовать. autoloader унести из Services.
Ну то есть как минимум надо отделить бекенд от фронтенда:
backend/Controller/...
backend/views/...
js/...
public/...
Еще, не знаю, совместимо ли это с webpack, но в dev возможно удобнее загружать файлы напрямую, без сборки. Сборка ведь нужна для оптимизации загрузки скриптов в HTTP/1.1 и для конвертации кода для поддержки старых браузеров, но в dev окружении этих проблем нет, а сборка выглядит как лишний промежуточный шаг.
> Replace the path in RewriteBase directive with path that you use to access to the project in your browser with some changes:
Вроде в Апач 2.4 RewriteBase стало можно не указывать. Ну и почему бы public не сделать корневой папкой сервера?
> return $app->json('New user created', 201)
В JSON корнем должен быть объект или массив. Не строка.
> class TextInput
Название наверно неудачное, можно подумать что тут просто элемент input.
> this.handleInputChange = this.handleInputChange.bind(this);
Вот эта конструкция тоже мне не очень нравится, что мы переписываем ссылку на метод. Разве это нормально?
Ну и конечно компонент React (то есть view/controller в терминах MVC) не должен слать запросы на сервер.
> По валидации формы, может быть нужно сделать валидацию на стороне клиента, например, пользователь еще при вводе имени видит сообщения об ошибках, например если он ввел цифру.
Хорошая идея.
> Как лучше это делать, полностью на стороне клиента, или отправляя запрос на сервер после вода каждого символа?
Вообще, тут можно использовать HTML5 валидацию для начала. Наверно неэффективно слать аякс-запросы, чтобы проверить длину строки.
Ты ведь используешь Валидацию Симфони, значит ничто не мешает тебе автоматизированно получать из модели параметры валидации и также автоматизированно передавать их в фронтенд.
> Если делать на стороне клиента, тогда валидация будет дублироваться, т.к. перед тем, как отправить запрос в базу, все равно нужно будет проверять инфу пришедшую извне.
Валидация на клиенте - это подсказки для удобства пользователя, валидация на сервере - настоящая валидация.
> Вопрос по формам в React, где хранить состояния полей, в родительском элементе, или каждое поле само хранит свое состояние?
Вообще, я не знаю правильного ответа. Надо наверно сравнить недостатки и преимущества каждого подхода.
Можно еще изучить ответы в Гугле: https://www.google.ru/search?q=react+storing+data+in+components&btnG=Поиск&newwindow=1&gbv=1
> Как организовать код формы, чтобы не дублировать обработчики событий или код базовых элементов TextInput и FormLabel в форме регистрации и форме аутенфикации?
А для двух форм нельзя использовать один и тот же TextInput? Ведь это по идее изолированный компонент, и он наверно к любой форме подойдет. То же касается и FormLabel.
Еще по моему неправильно класть работу с аяксом в компонент, да еще и напрямую работать с xhr. Разве не логичнее, что компонент только отвечает за вывод формы, а валидацию делегирует в какой-нибудь валидатор? Что-то мне не нравится твой подход. Ты весь код в контроллере пишешь.
Да и вообще, по задумке компоненты должны быть изолированы. Мне кажется, компонент должен либо:
1) использовать какую-то модель, которую в него передают, для валидации (для этого нужно DI, и я не представляю, как его тут сделать)
2) генерировать событие изменения, в ответ на которое внешний контроллер будет запускать валидацию и помещать в компонент ее результат
Разработчики одним из преимуществ реакта как раз указывают изоляцию: https://facebook.github.io/react/docs/components-and-props.html
> Components let you split the UI into independent, reusable pieces, and think about each piece in isolation.
Очевидно, что компонент, в который гвоздями прибита отправка аякс-запросов, точно не изолированный.
Также ты не учел, что аякс-запросы выполняются асинхронно, у тебя они могут отправляться параллельно и результат более раннего запроса может прийти позже. Тебе нужно отменять лишние запросы или как-то еще организовать очередь, а также скрывать ошибки при изменении данных.
Реакт по идее отвечает только за реализацию интерфейса. Следовательно, бизнес-логика в компонентах содержаться не должна.
Изучи в мануале как работает set_error_handler. Большинство ошибок в PHP не фатальные, то есть вызывает обработчик ошибок, после чего выполнение программы продолжается, если ты явно ее не остановишь. Ты это не учел.
При фатальных ошибках обработчик вызван, по моему, не будет.
В PHP7 часть фатальных ошибок сделали исключениями.
Также, ошибки надо логгировать - а ты поставил свой обработчик и отключил тем самым запись ошибок в лог.
Еще есть вариант преобразовывать ошибки в исключения - а уже в обработчике исключпений показывать страницу 5xx. http://php.net/manual/ru/class.errorexception.php
>>1013278
В CSS задается только оформление страницы, зачем туда "передавать" названия товаров? CSS-файл пишется руками один раз независимо от того, какие там товары есть.
То что ты описываешь - это уже совсем другая схема. Я то думал мы тут учим человека в обычном MVC не только на фронтэнде (во вьюхе) html5|js валидацию делать, а еще и на сервере обязательно фильтровать всё.
Точнее даже так:
1. Всегда все валидировать на сервере, не обсуждается.
2. Опционально всё валидировать на фронтенде для удобства юзеров/красоты.
>Как проводить валидацию для уникальных полей, вроде имя пользователя или его почту? Делать отдельный запрос для каждого подобного поля?
Наверное самый мейнстрим сейчас - когда юзер ввел имя/емейл и кликнул на другое поле - у тебя тогда летит запрос на сервер с проверкой занятости уже такого никнейма и емейла.
Можно в принципе и по нажатию на кнопку это делать, возвращая ответ об успехе или о том какое поле не нравится.
Но это уже сложные для меня темы, тут все таки пхп тред, мы тут не фронтэнд хуярим наверное в основном.
>В PHP7 часть фатальных ошибок сделали исключениями.
Где можно узнать какой обработчик какие ошибки обрабатывает?
>Также, ошибки надо логгировать - а ты поставил свой обработчик и отключил тем самым запись ошибок в лог.
Есть ещё какие-нибудь нюансы, которые делает стандартный обработчик, которые я не учел?
Обработчиков 2:
http://php.net/manual/ru/function.set-error-handler.php - обработчик ошибок (кроме совсем уж фатальных)
К "ошибкам" относятся так же notice, warning, strict standards и прочие виды замечаний.
http://php.net/manual/ru/function.set-exception-handler.php - обработчик непойманных исключений
Обработчик должен:
- залоггировать ошибку в лог (error_log())
- выдать подходящий HTTP код
- вывести страницу ошибки для пользователя
Написал небольшой сервис и нуждаюсь в мнении со стороны. Архитектура, оформление, что неправильно, что можно улучшить?
https://github.com/tsubaku/flights
Суть сервиса в следующем: есть рейсы автотранспорта, которые сопровождает охрана. И эти рейсы нужно контролировать. Когда выезд, когда приезд, состояние пломбы на кузове, полный экипаж едет, или кто-то по дороге потерялся. Пока что это контролируется телефонными звонками и вручную.
Схема работы сервиса такая:
Менеджер заходит на сайт, логинится в сервис и получает таблицу рейсов. Он может добавлять строки рейсов и удалять ненужные, записывать в них информацию (для кого везём, когда везём и т.д.) и назначать охранника-исполнителя.
Клиентов берём из отдельного списка, который можно редактировать во вкладке "Клиенты", а охранников тоже из списка, его редактируем в "Охранники".
Охранник логинится на сайте через смартфон и ему выдаётся календарь (четвёртый скрин) с его рейсами. Нажимает на активную дату, получает форму рейса с минимумом данных (когда, откуда, куда, кто заказчик) и полями для ввода номера автомашины, фактического времени выезда и приезда, и кнопкой для прикладывания фото (тут же делается фотография экипажа и пломб). Всё это улетает на сервер и менеджер видит в более-менее реальном времени, что происходит с экипажем (то есть, выехали они, или до сих пор на складе маринуются). Фото при загрузке автоматически маркируется временем и номером рейса, для которого сделано. Менеджер может просматривать фото в галерее для каждого рейса.
Кроме того, в общей таблице (в правой части) ещё дюжина столбцов с финансовой частью: Простой часы, Простой, Ставка за охранника, Простой сумма, Ставка без НДС, Ставка с НДС, Счёт, ЗП и т.д. Часть из них забивает менеджер, часть считается на основе вбитого автоматически.
Ухх щас набегут, готовься. Как по мне, главное чтобы работало, да без багов. И если ты это напилил за неделю например с нуля, то ты молодец, что бы тут ни говорили.
Писал четыре месяца. Правда, там бывали перерывы в неделю-две, так что реально потрачено месяца два с половиной, я думаю. Приходилось во всём разбираться (хотя бы до уровня "о, наконец-то заработало"). Код во многом накопипащен отовсюду.
Сейчас сервис работает, но с тестированием на добровольцах начальник охраны тянет, там у них запарка и нужно разгрести завал. Как только более-менее освободятся, будем проверять работу сервиса в реальных условиях. А я пока буду убирать костыли и переписывать велосипеды.
Не обижайся, но я бы полностью все переделал. Система использует процедурный подход, плохо тестируема и её будет трудно поддерживать. Почитай задачки после учебника из ОП-поста и посмотри какой-нибудь популярный фреймворк типа Symfony.
Не обижаюсь, но и полностью переделывать не буду. Это нецелесообразно, ящитаю. Вот переделать всё, но постепенно - это я готов.
>Symfony
Так же можно сказать, что от чистого js нужно отказаться в сторону Ангуляра, а от css в сторону Бутстрапа. Я пока ко всему этому присматриваюсь, но ещё не осилил.
Я подобной хуйней на работе занимался, только с другими функциями.
Тоже сначала в твоем стиле накидал, всё работало, но потом переписывал всё полностью на ООП и MVC, когда понадобилось новых скриптов накидать. Было проще переписать, чем думать как в ту мешанину что-то пихать.
Теперь что-то добавить вообще изи, хуяк пару строчек в контроллер, хуяк новую модель и в продакшен!
>Так же можно сказать, что от чистого js нужно отказаться в сторону Ангуляра, а от css в сторону Бутстрапа.
Не совсем корректное сравнение. Юзать MVC можно на ванильном пхп - это универсальный паттерн.
Вот что получилось https://github.com/grigoryMovchan/STKApps В ридми есть ссылка на то что было для сравнения.
Честно говоря, у меня плохо с абстракцией вообще и с ООП в частности. И ещё я так и не понял, что такое MVC на практике, применительно к вебу. Читал разные статьи, но везде пишут разное. Даже в терминах не могут сойтись. Один считает Моделью одно, другой другое. И в примерах сплошной Идеальный Код В Стране Эльфов, а не реальные задачи. Но главное, я не знаю, как вообще прикрутить ООП к моему проекту. В смысле концепции.
Я много говно наклепал и переписывал примеры почти из всех статей, чтоб потом запустить их у себя, прежде чем более-менее разобраться
>от чистого js нужно отказаться в сторону Ангуляра
невозможно, внутри компонентов приходится писать на чистом js.
>от css в сторону Бутстрапа
невозможно лол, из его стандартных компонентов только парашу можно сложить. Вообще не понимаю дроч на Бутстрап, может кто объяснит?
>Что плохого в процедурном коде?
Я не вижу смысла использовать его в пхп в принципе. Даже для скрипта в десять строчек, когда тоже можно написать в ООП-шном стиле. Как минимум, это полезно, человек будет привыкать к правильному стилю программирования, а не писать кучу процедурной лапши. А если хочется попрогать в процедурном стиле, то есть прекрасные скриптовые языки типа питона или перла, там можно в процедурщине разгуляться. Всему свое место.
>Каким стандартам не соответствуют мои уроки?
Тут нужно было спросить, а каким соотвествует. Сам пишешь в ОП-посте Зандстру, но при этом обучаешь, прости, программированию на поцкале.
>>1013596
>популярны Laravel, Symfony и Yii 2, причём на первый фапают больше всех, а по последнему больше всего упоминаний в вакансиях на hh. Вот и выбирай кота в мешке. Yii, наверное выберу. Хрен его знает зачем, может быть потому, что название у него дурацкое.
Laravel -- как Symfony, но только проще. Собственно, он движок симфони и использует. Симфони все-таки достаточно сложный и навороченный фрейм, но зато там используются все новомодные паттерны. Yii -- как по мне золотая середина. Мощный и простой фрейм, с которым можно творить самые настоящие чудеса. Так что выбор хороший.
>Честно говоря, у меня плохо с абстракцией вообще и с ООП в частности. И ещё я так и не понял, что такое MVC на практике, применительно к вебу. Читал разные статьи, но везде пишут разное. Даже в терминах не могут сойтись. Один считает Моделью одно, другой другое. И в примерах сплошной Идеальный Код В Стране Эльфов, а не реальные задачи. Но главное, я не знаю, как вообще прикрутить ООП к моему проекту. В смысле концепции.
Крайне советую данный сайт. Очень крутой прогер и разжевывает все. Могу, если мне будет не такой похуй и западло, скинуть всем аноном его охуеннейшие лекции по ООП.
elisdn.ru
А вообще читай Зандстру. Зандстру читай, сука. Ну и по-тихонечку проникайся. Я тоже раньше прелесть ООП не понимал, а сейчас понимаю, насколько это крутой подход и ссу на рожу всем этим "кокооко массивы бистрее!11"
Посмотрел код. Это, конечно, жуть. Тебе правильно говорят, анон, что переделывать надо буквально все, потому что постепенно не выйдет, каждый переделаннный костыль породит еще десять таких, вот прям вангую сходу. А насчет симфони даже не смотри. Ты там просто утонешь. Бери намного более простой Yii и пытайся на нем все это переделать ну практически с нуля (хотя, конечно, тебе нужно ООП-матчасть подучить), потому что скорость работы всего этого, безопасность и просто стабильность -- оставляют желать явно лучшего. И ты проклянешь тот день, когда эта куча говна сядет тебе на спину в виде поддержки в продакшене.
Я просто тебе могу сходу сказать, что ты наебался в login.php, у тебя там в базу идет прямой запрос без экранирования прочего говна. Да и в register.php тоже самое. И так во всем коде. И это лишь то, что я вижу глядь пять минут в несколько строк кода.
Да в целом фреймы сейчас все быстрые. Тут зависит от удобства. Можно и на yii такое реализовать. А можно и на симфони заморочиться. Я бы лично спокойно делал на Yii. Не вижу тут никаких проблем.
Я не ОП, но люблю повыебываться.
10-20к не так много так что любой фрейм , только часть для API делал бы на каком то микро фрейме отдельно
мимо не ОП
>запрос без экранирования
Согласен, безопасности мало внимания уделил. Сделаю.
>>1013683
Почитаю. Буду потихоньку вкатываться в Yii и ООП, но только параллельно допиливанию этого сервиса. Как только научусь делать во фреймворке и объектно ориентированно, перепишу его. Пока что поверю вам на слово, что ООП лучше процедурного подхода.
в yii есть свой крудо генератор который ускоряет разработку backend , у yii есть свой AR , симфа более модульная и интерпраз фрамеворк , с симфы легко перекатиться в тот же ZF
Ну вкатиться быстрее однозначно именно в Yii
- портейбл
- хорошо настраеваемый
- в частности интересует возможно настройки тулбара и кнопочек на нем
Atom с миллионом плагинов.
И меня всё еще мучает вопрос. В каком объекте лучше всего хранить координаты животных, в самом животном тупо хранить его положение в мире? Или таки в мире хранить животных на разных "клетках"?
>var contactListWidget = new ContactListWidget(..);
>contactListWidget.onContactSelect(function (contact) {
>...
>});
Что делает функция onContactSelect()? Она задает сам обработчик?
onContactSelect(callable) {
this.contacts.click(function(e) {
...
callable(this);
});
}
То есть, callable и будет передаваемой зависимостью?
Теперь, когда появилось возможность более гибко обращаться с зависимостями, появляется возможность управлять сообщениями не в главном контроллере, а в самом контроллере Сообщений. То есть, получиться так:
contactListWidget.onContactSelect(conversation.runMessages(contact));
Это верно?
Где лучше ставить такие обработчики, в коде снаружи или инициализировать в главном контроллере?
>Главное делать это без фанатизма, а то можно дойти до того, что каждую строчку текста мы сделаем отдельным виджетом и получится слишком переусложненная система.
Почему вы для списка контактов сделали отдельный виджет, если есть один общий виджет Контактов, который может содержать этот список?
>У тебя в коде, по моему идут нарушения инкапсуляции. Ну например:
>
>> clearInterval(that.contacts.contactsInterval);
>Зачем ты хранишь свои данные в поле другого объекта? Это выглядит как лишнее усложнение, непонятно, зачем это сделано. Почему не this.contactsInterval?
Почему другого? Это же интервал обновления контактов, значит и храниться он должен в соответствующем объекте.
На самом деле, я вижу логичность перенести это свойство в главный контроллер, потому что его задает сам метод главного контроллера. Раньше этим занимался контроллер Контактов. Так просто получилось.
>var contactListWidget = new ContactListWidget(..);
>contactListWidget.onContactSelect(function (contact) {
>...
>});
Что делает функция onContactSelect()? Она задает сам обработчик?
onContactSelect(callable) {
this.contacts.click(function(e) {
...
callable(this);
});
}
То есть, callable и будет передаваемой зависимостью?
Теперь, когда появилось возможность более гибко обращаться с зависимостями, появляется возможность управлять сообщениями не в главном контроллере, а в самом контроллере Сообщений. То есть, получиться так:
contactListWidget.onContactSelect(conversation.runMessages(contact));
Это верно?
Где лучше ставить такие обработчики, в коде снаружи или инициализировать в главном контроллере?
>Главное делать это без фанатизма, а то можно дойти до того, что каждую строчку текста мы сделаем отдельным виджетом и получится слишком переусложненная система.
Почему вы для списка контактов сделали отдельный виджет, если есть один общий виджет Контактов, который может содержать этот список?
>У тебя в коде, по моему идут нарушения инкапсуляции. Ну например:
>
>> clearInterval(that.contacts.contactsInterval);
>Зачем ты хранишь свои данные в поле другого объекта? Это выглядит как лишнее усложнение, непонятно, зачем это сделано. Почему не this.contactsInterval?
Почему другого? Это же интервал обновления контактов, значит и храниться он должен в соответствующем объекте.
На самом деле, я вижу логичность перенести это свойство в главный контроллер, потому что его задает сам метод главного контроллера. Раньше этим занимался контроллер Контактов. Так просто получилось.
И вот сейчас, мне нужно написать хайп проект. Нормально если я предоставлю хайп-пирамиду как пример моей работы? Там я планирую реализовать кучу всякой хуйни, например многоуровневую реферальную систему, админку и пр. Т.е. это был бы пиздатый пример того, как я могу, вместо какого-то банального всрато-блога. Но не зашкварно ли это? Или лучше написать всрато-блог?
Расскажи поподробнее о многоуровневой реф системе? Я работал в одном проекте, но там заложено изначально было всё так пиздецово, что местные бы охуели от того как всё было написано. Тоже 3 уровня рефереальности, никаким ооп и не пахло, бабло с плавающей запятой с 99.999999999999999 рублями на акккаунтах и пр))
Всмысле? Не понимаю твоего вопроса. Какое отношение ООП и 99.99999... имеют к реферальной системе? Регается участник под кого-то - становится его рефералом, рефералы этого учатника становятся рефералами 2 уровня для его реферера и т.д. Ну и соответственно процент на каждом уровне отличается +зависит от колличества рефералов. Или что ты спрашиваешь?
Да я понимаю блин, не нужно мне реф систему описывать ирл, как бы ты это реализовал в коде/базе?
>акое отношение ООП и 99.99999
Просто думал это передаст общий вид пиздеца
Ну обычно так и писал. Участник реагается и в базе записывается кто его реферер, а потом на уровне кода искал бы других юзеров у которых этот записан как реферер и т.д. в зависимости от кол-ва уровней реферальной системы.
Но я чет не думаю, что так неправильно и планирую, как-то это организовать дабы запиливалась в БД вся инфа обо всех рефералах, но как конкретно это сделать еще не задумывался
Ну можно тогда у юзера хранить сразу всю цепочку его "предков", а не только того единственного кто его привел.
ид_васи_который_привел: 222
ид_того_кто_привел_васю: 123
ид_того_кто_привел_приводителя_васи: 34
Видел такое, и не смотря на всю копипастность подобной ебалы, такое может реально оказаться полезным. Например когда по ошибке удалили юзера, в системе, в которой у одного юзера инфа только об 1 предке, при проебе юзера - ты уже никогда не узнаешь кто был его предком. А с такой вот херью, всегда можно поискать по деревьям чужих юзеров своего васю, и понять на каком месте в какой цепочке он встроен.
Сейчас подумал, наверное буду делать так: создам отдельную таблицу для хранения инфы о рефералах, в которой будут три столбца: id участников, id их рефереров или хз как правильно называются не те, кто их пригласил, а и те, чьими они рефералами являются на более низких уровнях, реферальный уровень. И записывать все данные туда при регистрации участника. Столбец рефбек еще можно добавить. Так можно запилить возможность управлять количеством реферальных уровней из админки.
Технически любой фреймворк это просто набор классов, который облегчает разработку проекта. Если твой код работает на сервере, и при этом ты не хочешь ничего разворачивать у себя, ставить ничего не обязательно. На хостинге ничего включать не нужно, только поставить фрейворк через Composer.
>Технически любой фреймворк это просто набор классов,
Ага, вот теперь понятно.
>Composer
Вот как раз Композер и проверяет версию установленного пхп. И без пхп не хочет ставиться категорически.
>На хостинге ничего включать не нужно, только поставить фрейворк через Composer.
Стоп. То есть, всё же нужно поставить фреймворк на хостинг? И если не поставить, то работать не будет. А если у меня нет доступа к настройкам сервера хостера и тарифным планом не предусмотрена поддержка Yii из-коробки? Значит, воспользоваться фреймворком будет нельзя?
Потому что композер написан на PHP, понятное дело он не будет без этого работать.
>>1014028
Если доступа к серверу по ssh нет, тогда стоит подумать о смене хостера. Также есть вариант вручную загрузить папку vendor с фреймворком на сервер. Она не должна лежать в публичной папке сервера, обычно она находится на уровень выше. Поддержки фореймворков обычно не предоставляют, потому что это файлы с которыми работает разработчик. Все что тебе нужно это доступ по ssh.
SSH есть. Но всё это выглядит слишком сложным, монструозным и непонятным. Пожалуй, я не буду вкатываться в фреймворки. Это слишком сложно для меня.
блядь как же всё таки сложно. Сидишь и пытаешься в голове спроектировать эту херню и не можешь понять как должно быть сделано всё в этом ооп.
Думаешь а как в играх это блин интересно сделано. Вообще голова взрывается :(
Каждую клетку же нецелесообразно делать объектом? Как сделать так, что бы можно было мышкой смотреть на мир? Не все объекты же в мире опрашивать? Надо что бы она только вокруг себя смотрела. Стало быть нужен какой-то упрощенный слепок мира, на который животные могут поглядывать, что бы оценивать что вокруг себя?
Ты две первые задачи из оп-поста делал? Подозреваю что нет, они специально для новичков сделаны.
Можно вообще не хранить координаты. Просто двумерный массив и в ячейке или ноль, или ссылка на объект (или 1 если ты хочешь добавить на поле стены например).
Можно передовать их как объект или массив методам просчета хода (понадобится если у нас n-мерное поле например).
Вообще задача несложная, но муторная. Т.е. решение довольно объемное.
Как сделать так, чтобы он умножал на одно и то же число. Помоги, анон. Я понимаю, что для тебя это крайне легко и ты меня считаешь имбецилом, но я только вливаюсь в пхп и раньше никогда не кодил, не понимаю, как тут всё устроено.
Та я то это понимаю, но как это сделать я не ебу.
Напиши, как оно должно выглядеть, а то я сейчас умом тронусь.
Заранее спасибо, добрый анон
http://ideone.com/w8752S
Я вот исходя из этого пытаюсь исходить. Как-то его так поменять, чтобы он x увеличивал на 1 и при этом ещё и на этот же x умножал. Я понимаю, что надо вместо "x++" поставить "x" но на что умножить то, блядь? Если ставлю "x = $x" то он мне выбивает очевидную ошибку, а как иначе сделать я прочитав все условия не могу додуматься.
>Поля 'visited', 'from', 'shortest_path' наверно стоит хранить в отдельных массивах. Они ведь не являются частью исходной информации о точках, а нужны временно, пока выполняется алгоритм.
А смысл хранить их отдельно. Так же работает =|
>Плохо, что у тебя весь код сделан одной функцией - удобнее было бы разделить поиск пути и вывод ответа. И четко определить, что делает каждая функция, что получает на вход, что дает на выходе.
Разделил на 2 функции. Получение короткого пути и принт пути.
>Это ведь неправильно, по моему цикл надо выполнять, пока есть непосещенные вершины, а то может получиться, что мы нашли один путь до конца, но не проверили другие варианты, и не нашли более короткий путь.
Дейкстра если дошёл до точки. То есть поставил её посещение True, то он дошел до неё самым коротким путем. Он ищет от самых коротких путей до самых длинных.
>
Также, в цикле поиска непосещенной вершины с мин. расстоянием нет проверки - а что, если таких вершин больше нет (например, все посещены или есть непосещенные вершины, не связанные со стартом)? Для надежности стоило бы проверять этот вариант.
Тут я решил проверять только входные точки. Так как если они правильные, то путь найдет.
>для этого есть array_reverse.
Не смог найти эту функцию. А вообще есть разница?
>Это тяжело читать, тут надо вынести часть выражения в отдельные переменные с понятным названием.
А это я не понял.
Еще я подумал головой и понял что поле from == shortest_path. То есть по правильному надо через него путь искать. А по примеру from до меня дошло как.
http://ideone.com/fDcUub
Можно теперь я пойду ООП изучать?
>Поля 'visited', 'from', 'shortest_path' наверно стоит хранить в отдельных массивах. Они ведь не являются частью исходной информации о точках, а нужны временно, пока выполняется алгоритм.
А смысл хранить их отдельно. Так же работает =|
>Плохо, что у тебя весь код сделан одной функцией - удобнее было бы разделить поиск пути и вывод ответа. И четко определить, что делает каждая функция, что получает на вход, что дает на выходе.
Разделил на 2 функции. Получение короткого пути и принт пути.
>Это ведь неправильно, по моему цикл надо выполнять, пока есть непосещенные вершины, а то может получиться, что мы нашли один путь до конца, но не проверили другие варианты, и не нашли более короткий путь.
Дейкстра если дошёл до точки. То есть поставил её посещение True, то он дошел до неё самым коротким путем. Он ищет от самых коротких путей до самых длинных.
>
Также, в цикле поиска непосещенной вершины с мин. расстоянием нет проверки - а что, если таких вершин больше нет (например, все посещены или есть непосещенные вершины, не связанные со стартом)? Для надежности стоило бы проверять этот вариант.
Тут я решил проверять только входные точки. Так как если они правильные, то путь найдет.
>для этого есть array_reverse.
Не смог найти эту функцию. А вообще есть разница?
>Это тяжело читать, тут надо вынести часть выражения в отдельные переменные с понятным названием.
А это я не понял.
Еще я подумал головой и понял что поле from == shortest_path. То есть по правильному надо через него путь искать. А по примеру from до меня дошло как.
http://ideone.com/fDcUub
Можно теперь я пойду ООП изучать?
бамп
Просто съеби уже, говень ленивый, не засоряй тред. В интернете куча энтри-левел примеров, ты за день мог столько всего выучить, чем тут клянчить.
Попробуй для начала просто вывести
1x1 =
2x2 =
То есть без подсчета результата. Затем добавь команду, которая для данного $x посчитает произведение переменной самой на себя и сохранит в отдельную переменную. И затем добавь вывод значения этой переменной.
Потому что IndexForm это не то же самое что и Entity. Или пиши IndexForm или не используй тайпхинтинг.
По принципу Лисков наследник должен быть совместим с базовым классом. То есть в код можно передать объект наследника вместо базового класса.
Сужение допустимого типа аргумента нарушает совместимость, так как наследник не принимает те аргументы, которые бы принял базовый класс.
Вообще, на мой взгляд ты наследование реализовал неудачно. Ну например, почему метод валидации должен всегда принимать ровно один аргумент-объект? Может для каких-то сущностей там будут передаваться еще какие-то параметры. Ты сделал лишнее ограничение, в котором не было никакой необходимости.
Мне кажется, методы валидации объекта не должны наследоваться.
>вместо какого-то банального всрато-блога
Я как раз сел пилить всрато-блог :( Только делаю это на ларавеле, на ванильном пхп я легко могу это седлать, есть свои заготовки
Проблема.
После перевода некоторого ресурса на https отваливается наглухо $_SESSION после перехода через header.
Без https работало ок.
session_start() в начале страницы есть Ебучий стэковерфлоу этим советом переполнен и везде заплюсовано.
На публикации в haproxy редирект с 301 статусом норм стоит (браузер корректно отображает отработку этого редиректа.
phpinfo() пикрил (менять параметры по привязке к секьюр и т.д. пробовал).
Указание перехода через хидер в явном виде с указанием протокола и полного пути (да и в относительных путях без указания протокола) проблемы не решает.
При вытягивании на нужной странице из $_SESSION данных, так пусто. На странице, где происходит формирование данных, а также в http виде из $_SESSION всё отлично вытягивается.
Передаю через POST.
В браузере кукисы возникают нормально. И значения там верные. Но вот на целевой странице из $_SESSION я ничего не имею.
Какие могут быть варианты? Подозреваю, что где-то на серваке глупое упустил.
>Передаю через POST.
Уточнение. Непосредственно не куки и сессии, но данные, которые потом применяются для формирования. Связано слабо,но всё же.
Да. Хоть и костыльно выглядело, пробовал. Не взлетело. Хотя вот сейчас ещё мысль на базе этого появилась, проверю.
В общем попробовал догадку. Выяснил, что до кучи через $_GET я тоже данные не получаю. Хрень какая-то.
>netbeans
>notepad++
>laravel
>foxit reader
>chrome
>windows 10
>keepass
>то, как настроено отображение окон на панеле в 10ке
ты кто такой вообще бля? Хуле у тебя все тоже самое что и у меня? Толко вот вместо OneNote, я юзаю CheryTree
Решил так и сделать, допустим у меня есть поле - двумерный массив, в котором либо нули - либо сами объекты животных лежат. И вот теперь я даже не знаю как мне будучи мышкой писать методы осматривания вокруг. Я чет не понимаю как мышке понять для начала на какой она клетке стоит.
1. Объект класса X лежит в массиве.
2. какой метод написать классу Х, что бы он находил своё положение в массиве.
В общем к чему я пока что пришел:
Нужно хранить как у самого животного координаты его положения в мире, так и в мире хранить координаты животного с помощью двумерного массива.
Копипаста скажете вы? Ну да, но это решает 2 важные проблемы, каждая из которых возникает если не зделать одно из этих действий.
1. Если хранить координаты животного только в саом мире - то как животному узнать быстро и безболезненно где оно находится? Ну можно офк делать поиск по миру, но если у нас тут огромный мир нарисуется? Привет тормоз?
2. Если же хранить только у самого животного координаты - то как узнать кто вокруг тебя? Придется делать поиск по всем объектам в мире, что бы найти тех кто в соседних клетках. Тоже херовый вариант, потому что чем больше объеков - тем хуже.
А так у тебя мир знает где в нем объект, и объект знает где он в мире - и всё. Решена гигантская проблема с производительностью в перспективе. Ну а теперь обоссывайте.
https://www.digitalocean.com/community/tutorials/apache-ubuntu-14-04-lts-ru
Если директория хоста в /var/www все четенько работает, а вот если попробоват создать хост в директории пользователя, дает 403.
Спасло дописывание "Allow from all Require all granted".
https://stackoverflow.com/a/13923435
Но почему, что за магия? Чем папка /home/username/www отличается от /var/www? (с правами все в порядке)
Я даже документацию по такому случаю открыл, там ничего о такой подставе не говорится.
http://httpd.apache.org/docs/2.0/ru/vhosts/name-based.html
В Апаче нужно явно описывать, какие папки будут доступны через веб. Это делается с помощью директивы Directory: http://httpd.apache.org/docs/current/mod/core.html#directory
Скорее всего папка /var/www открыта где-то в стандартном конфиге, а /home/user - нет.
Соответственно нужно дописать
<Directory "/user/xxx/">
Require all granted
</Directory>
Про require: http://httpd.apache.org/docs/2.4/mod/mod_authz_core.html#require
> Allow from all
Это директива из Апача 2.2 и ее не стоит смешивать с require.
>>1013940
>>1014124
Это хороший вопрос, потому давайте сравним варианты:
Если хранить координаты только в объекте:
- легко узнать координаты имея объект
- перемещение делается изменением координат
- чтобы найти соседние объекты, нужно делать обход массива объектов (сложность O(N))
Если хранить объекты в двухмерном массива клеток:
- чтобы получить координаты по объекту, надо перебирать все клетки, сложность WxH, где W/H - ширина/высота поля
- чтобы позволить нескольким объектам находиться в одной клетке, каждую клетку надо также сделать массивом
- чтобы переместить объект, его надо удалить из одной клетки и поместить в другую
- чтобы найти соседние объекты в радиусе R, надо перебрать (2R)^2 клеток
- для хранения поля нужно WxH ячеек массива (если поле огромное, то много памяти)
То есть за более быстрый поиск соседей нам придется заплатить усложнением кода, и увеличением потребляемой памяти. И надо смотреть, что для нас важнее.
Там есть еще вариант - хранить координаты в объекте и дополнительно хранить объекты в двухмерном массиве, но в этом случае мы должны гарантировать перемещение объекта в другую ячейку при изменении координат.
Мне все же кажется, что координаты придется хранить в объекте, так как иначе их быстро не определить, имея этот объект.
А если захочется оптимизировать поиск соседей, то тогда можно добавлять какие-то дополнительные структуры. Это не обязательно должно быть именно поле из клеточек - есть более оптимальные варианты. Ну например, в играх иногда разбивают поле на сектора и хранят список объектов в каждом секторе. Преимущество в том, что секторов меньше, чем клеток, и требуется меньше памяти, при этом такая структура позволяет искать соседей только в нескольких секторах, а не на всем поле. Также, есть различные двухмерные индексы (geospatial indexes) для быстрого поиска объектов в определенном радиусе. Например:
- R-Tree https://ru.wikipedia.org/wiki/R-дерево_(структура_данных)
- Quadtree https://ru.wikipedia.org/wiki/Дерево_квадрантов
- https://en.wikipedia.org/wiki/K-d_tree
- https://en.wikipedia.org/wiki/Spatial_database#Spatial_index
Начинающему будет непросто разобраться в этих структурах, потому, если захочешь их изучить, начни с одномерных индексов - начни с метода поиска делением пополам в отсортированном массиве. Этот метод позволяет быстро искать значение, не перебирая весь массив. Увы, отсортированный массив тяжело изменять, он подходит только для неизменямых данных. Чтобы искать в изменямых данных, нужно двоичное дерево (B-tree). Ну а от двоичного дерева тебе будет не так и сложно перейти к двоичному разбиению пространства (binary space partition), на основе которого и работают двухмерные индексы.
Если тебе не хочется изучать их (жаль), то скажу, что в этой задаче животных немного, и ускорять поиск соседей не требуется. Но если тебе захочется, можешь все же изучить эти индексы и прикрутить их к задаче в виде отдельного класса, чтобы можно было добавить его для ускорения поиска на полях с большим числом объектов.
В Апаче нужно явно описывать, какие папки будут доступны через веб. Это делается с помощью директивы Directory: http://httpd.apache.org/docs/current/mod/core.html#directory
Скорее всего папка /var/www открыта где-то в стандартном конфиге, а /home/user - нет.
Соответственно нужно дописать
<Directory "/user/xxx/">
Require all granted
</Directory>
Про require: http://httpd.apache.org/docs/2.4/mod/mod_authz_core.html#require
> Allow from all
Это директива из Апача 2.2 и ее не стоит смешивать с require.
>>1013940
>>1014124
Это хороший вопрос, потому давайте сравним варианты:
Если хранить координаты только в объекте:
- легко узнать координаты имея объект
- перемещение делается изменением координат
- чтобы найти соседние объекты, нужно делать обход массива объектов (сложность O(N))
Если хранить объекты в двухмерном массива клеток:
- чтобы получить координаты по объекту, надо перебирать все клетки, сложность WxH, где W/H - ширина/высота поля
- чтобы позволить нескольким объектам находиться в одной клетке, каждую клетку надо также сделать массивом
- чтобы переместить объект, его надо удалить из одной клетки и поместить в другую
- чтобы найти соседние объекты в радиусе R, надо перебрать (2R)^2 клеток
- для хранения поля нужно WxH ячеек массива (если поле огромное, то много памяти)
То есть за более быстрый поиск соседей нам придется заплатить усложнением кода, и увеличением потребляемой памяти. И надо смотреть, что для нас важнее.
Там есть еще вариант - хранить координаты в объекте и дополнительно хранить объекты в двухмерном массиве, но в этом случае мы должны гарантировать перемещение объекта в другую ячейку при изменении координат.
Мне все же кажется, что координаты придется хранить в объекте, так как иначе их быстро не определить, имея этот объект.
А если захочется оптимизировать поиск соседей, то тогда можно добавлять какие-то дополнительные структуры. Это не обязательно должно быть именно поле из клеточек - есть более оптимальные варианты. Ну например, в играх иногда разбивают поле на сектора и хранят список объектов в каждом секторе. Преимущество в том, что секторов меньше, чем клеток, и требуется меньше памяти, при этом такая структура позволяет искать соседей только в нескольких секторах, а не на всем поле. Также, есть различные двухмерные индексы (geospatial indexes) для быстрого поиска объектов в определенном радиусе. Например:
- R-Tree https://ru.wikipedia.org/wiki/R-дерево_(структура_данных)
- Quadtree https://ru.wikipedia.org/wiki/Дерево_квадрантов
- https://en.wikipedia.org/wiki/K-d_tree
- https://en.wikipedia.org/wiki/Spatial_database#Spatial_index
Начинающему будет непросто разобраться в этих структурах, потому, если захочешь их изучить, начни с одномерных индексов - начни с метода поиска делением пополам в отсортированном массиве. Этот метод позволяет быстро искать значение, не перебирая весь массив. Увы, отсортированный массив тяжело изменять, он подходит только для неизменямых данных. Чтобы искать в изменямых данных, нужно двоичное дерево (B-tree). Ну а от двоичного дерева тебе будет не так и сложно перейти к двоичному разбиению пространства (binary space partition), на основе которого и работают двухмерные индексы.
Если тебе не хочется изучать их (жаль), то скажу, что в этой задаче животных немного, и ускорять поиск соседей не требуется. Но если тебе захочется, можешь все же изучить эти индексы и прикрутить их к задаче в виде отдельного класса, чтобы можно было добавить его для ускорения поиска на полях с большим числом объектов.
Ты пытаешься возложить на мышку задачу (поиск соседей), которая не относится к ее области ответственности. За знание о том, кто где находится, отвечает "поле", оно и должно искать соседей.
Нужно сделать "поле" объектом, у этого объекта метод поиска соседей, и передать в мышку этот объект. Например, через конструктор либо через метод "поместить на поле(поле)"/"снять с поля". Методы позволяют ставить и снимать животных на разные поля (если это нужно), а в случае с конструктором привязка происходит один раз и навсегда.
>>1014347
Нужно изучить, что происходит при редиректе. А не гадать, пробуя разные решения вслепую.
Ты перепробовал кучу решений, а посмотреть на заголовки HTTP-запроса/ответа почему-то не хочешь.
Нужно открыть в браузере отладчик (Ctrl + SHift + I) на вкладке Network и там посмотреть, каким заголовком выставляются куки, с какими параметрами. Какие куки браузер отправляет на сервер. И может быть, будет видна причина.
Либо отправлять запросы программой вроде curl, которая умеет показвыать заголовки.
А так, да, у кук например есть флаг secure, если ты не знаком с ним, то можно поискать его например тут http://citforum.ru/internet/html/cookie.shtml
Этот флаг говорит, что кука должна пересылаться только по защищенному протоколу.
Обычно переход на https не вызывает никаких проблем, так как весь сайт работает на https. Если у тебя половина сайта на https, а половина на http - то да, проблемы могут быть, но тогда непонятно, какой смысл вообще был внедрять https.
Алсо, параметр cookie_secure у тебя включен (и это хорошо).
>>1014232
Не надо так
Ты пытаешься возложить на мышку задачу (поиск соседей), которая не относится к ее области ответственности. За знание о том, кто где находится, отвечает "поле", оно и должно искать соседей.
Нужно сделать "поле" объектом, у этого объекта метод поиска соседей, и передать в мышку этот объект. Например, через конструктор либо через метод "поместить на поле(поле)"/"снять с поля". Методы позволяют ставить и снимать животных на разные поля (если это нужно), а в случае с конструктором привязка происходит один раз и навсегда.
>>1014347
Нужно изучить, что происходит при редиректе. А не гадать, пробуя разные решения вслепую.
Ты перепробовал кучу решений, а посмотреть на заголовки HTTP-запроса/ответа почему-то не хочешь.
Нужно открыть в браузере отладчик (Ctrl + SHift + I) на вкладке Network и там посмотреть, каким заголовком выставляются куки, с какими параметрами. Какие куки браузер отправляет на сервер. И может быть, будет видна причина.
Либо отправлять запросы программой вроде curl, которая умеет показвыать заголовки.
А так, да, у кук например есть флаг secure, если ты не знаком с ним, то можно поискать его например тут http://citforum.ru/internet/html/cookie.shtml
Этот флаг говорит, что кука должна пересылаться только по защищенному протоколу.
Обычно переход на https не вызывает никаких проблем, так как весь сайт работает на https. Если у тебя половина сайта на https, а половина на http - то да, проблемы могут быть, но тогда непонятно, какой смысл вообще был внедрять https.
Алсо, параметр cookie_secure у тебя включен (и это хорошо).
>>1014232
Не надо так
Я правильно понял, что dist будет пытаться стащить с packagist,
а source с гитхаба? (Доку читал, но плохо понял https://getcomposer.org/doc/03-cli.md#install)
Алсо, пару дней назад было собеседование, чувак меня жестко поимел
(не знаю, чем я ему не понравился).
По sql например завалил на пивотах, потом стал спрашивать как удалить
картинки с ватермарками(/штрихкодом/сиськами и т.д.),
как разобрать гигабайтный json (не используя библиотеки).
Может брать по одной строчке fgets, анализировать все эти фигурные/квадратные
скобочки и из этих палок слепливать объект?
Треш короче какой-то.
Хотя мне контора не оч и понравилась ("все равно виноград зеленый"), проект - каталог
товаров, соответственно бесконечный парсинг всякого говна на функциях и скриптах,
какой-то дикий самописный фреймворк, а я хочу норм ООП и прочие модные шняги.
В случае с некоторыми репозиториями вроде Гитхаба, есть 2 способа скачать файлы проекта: как zip-архив с файлами или же можно склонировать репозиторий проекта с помощью git. prefer-dist значит предпочитать zip-архивы (если есть выбор), prefer-source - клонировать репозиторий если возможно.
А в общем, dist - это сокращение от "дистрибутив", то есть файлы, предназначенные для распространения. Обычно они весят меньше, чем полный репозиторий с историей. Например, для компилируемых языков dist обычно включает в себя результат компиляции (объектный файл), а не сами исходники. PHP не компилируется, так что тут разница менее заметна.
Вот например релизы для Symfony Framework Bundle: https://github.com/symfony/framework-bundle/releases - это просто архивы с содержимым репозитория.
Правда, в случае с репозиторием, он теоретически будет тратить меньше трафика на скачивание обновлений.
Официально это описано тут: https://getcomposer.org/doc/05-repositories.md
> Dist: The dist is a packaged version of the package data. Usually a released version, usually a stable release.
> Source: The source is used for development. This will usually originate from a source code repository, such as git. You can fetch this when you want to modify the downloaded package.
> Алсо, пару дней назад было собеседование, чувак меня жестко поимел
Можно было в ответ поинтересоваться, используется ли все описанное у них в работе.
> как удалить картинки с ватермарками
Если ватермарка не фиксированного размера и не расположена в фиксированном месте, то да, интересный вопрос. Подозреваю, что надо гуглить алгоритмы распознавания изображений.
> как разобрать гигабайтный json
Вообще, JSON плохо подходит для таких задач, где данные надо обрабатывать потоково. Лучше для такого объема данных использовать csv, xml или другой подходящий формат. А так, подозреваю, понадобится писать свой парсер JSON, который будет читать данные поблочно и разбирать.
Для csv все очевидно, есть готовая функция: http://php.net/manual/ru/function.fgetcsv.php
Для XML есть класс для поточного чтения: http://php.net/manual/ru/class.xmlreader.php
Гигантские XML используются например в выгрузках Openstreetmap, а также в какой-то российской официальной базе адресов (КЛАДР может быть? ФИАС? забыл, по моему по-другому называется).
> Может брать по одной строчке fgets,
Строчка может быть тоже гигабайтной, так что придется читать именно блоками в N байт.
> анализировать все эти фигурные/квадратные скобочки и из этих палок слепливать объект?
Да, это называется лексический и синтаксический анализ.
> а я хочу норм ООП
Это не везде есть, конечно
В случае с некоторыми репозиториями вроде Гитхаба, есть 2 способа скачать файлы проекта: как zip-архив с файлами или же можно склонировать репозиторий проекта с помощью git. prefer-dist значит предпочитать zip-архивы (если есть выбор), prefer-source - клонировать репозиторий если возможно.
А в общем, dist - это сокращение от "дистрибутив", то есть файлы, предназначенные для распространения. Обычно они весят меньше, чем полный репозиторий с историей. Например, для компилируемых языков dist обычно включает в себя результат компиляции (объектный файл), а не сами исходники. PHP не компилируется, так что тут разница менее заметна.
Вот например релизы для Symfony Framework Bundle: https://github.com/symfony/framework-bundle/releases - это просто архивы с содержимым репозитория.
Правда, в случае с репозиторием, он теоретически будет тратить меньше трафика на скачивание обновлений.
Официально это описано тут: https://getcomposer.org/doc/05-repositories.md
> Dist: The dist is a packaged version of the package data. Usually a released version, usually a stable release.
> Source: The source is used for development. This will usually originate from a source code repository, such as git. You can fetch this when you want to modify the downloaded package.
> Алсо, пару дней назад было собеседование, чувак меня жестко поимел
Можно было в ответ поинтересоваться, используется ли все описанное у них в работе.
> как удалить картинки с ватермарками
Если ватермарка не фиксированного размера и не расположена в фиксированном месте, то да, интересный вопрос. Подозреваю, что надо гуглить алгоритмы распознавания изображений.
> как разобрать гигабайтный json
Вообще, JSON плохо подходит для таких задач, где данные надо обрабатывать потоково. Лучше для такого объема данных использовать csv, xml или другой подходящий формат. А так, подозреваю, понадобится писать свой парсер JSON, который будет читать данные поблочно и разбирать.
Для csv все очевидно, есть готовая функция: http://php.net/manual/ru/function.fgetcsv.php
Для XML есть класс для поточного чтения: http://php.net/manual/ru/class.xmlreader.php
Гигантские XML используются например в выгрузках Openstreetmap, а также в какой-то российской официальной базе адресов (КЛАДР может быть? ФИАС? забыл, по моему по-другому называется).
> Может брать по одной строчке fgets,
Строчка может быть тоже гигабайтной, так что придется читать именно блоками в N байт.
> анализировать все эти фигурные/квадратные скобочки и из этих палок слепливать объект?
Да, это называется лексический и синтаксический анализ.
> а я хочу норм ООП
Это не везде есть, конечно
Слово function используется для объявления (создания) функции. Для вызова его писать не надо, пишется только имя функции и круглые скобки.
спасибо!
По поводу функций - разделение пока не идеальное. У тебя функция поиска пути всегда заканчивается его выводом. Но разделение подразумевает, что мы можем найти кратчайший путь, не выводя его. Например, мы хотим дальше как-то его программно обработать.
То есть хотелось бы чтобы было именно 2 отдельных функции (поиск пути и вывод пути), было определено, что они получают на вход и что дают на выходе. А у тебя функция всегда печатает ответ на выходе, и программно с ним сделать ничего нельзя. Отдельной функции только поиска пути нет.
Умение разбивать код на функции тебе обязательно понадобится позже, когда объемы кода будут больше.
> foreach ($paths as $name => $value) { // Проверяем есть ли такие точки.
> if ($start_point == $name) {
Используй array_key_exists в таких случаях. Вообще, тебе стоит посмотреть на список функций для работы с массивами, либо в моем уроке, либо тут: http://php.net/manual/ru/ref.array.php
> $paths[$current_point]['shortest_path'] = 0; // делаем длинну пути стартовой точки = 0
Тут комментарий лучше писать сверху, так как строка получается слишком длинная, и PSR рекомендует не делать строк длинее 80-120 символов ( http://www.php-fig.org/psr/psr-2/ ).
> $j = INF;
Лучше было назвать $min_path, $min_length или как-то так
> while ($paths[$curent_point]['shortest_path'] != 0) {
Логичнее наверно было while ($current_point != $start_point), мы ведь ищем именно стартовую точку
> if ($path == 'pet') {
> continue;
Почему тут вписано исключение? А что, если мы поменяем стартовую или конечную точку, исключение нам тоже надо менять руками?
> А смысл хранить их отдельно. Так же работает =|
"работает" - этого мало, нужно еще чтобы программа выглядела логично и понятно. А с точки зрения логики массив соединений между точками и массив посещенных вершин (или массив расстояний) - это разные наборы данных. Ну к примеру, если мы хотим найти 2 пути, то массив соединений в обоих случаях будет одинаков, а массив расстояний - разный.
> Дейкстра если дошёл до точки. То есть поставил её посещение True, то он дошел до неё самым коротким путем.
Тогда стоило об этом написать в комментарии. Человек, который читает код, видит, что алгоритм изменен, а почему - специально или это ошибка - непонятно.
> Не смог найти эту функцию. А вообще есть разница?
Думаю, что сортировка будет работать медленее, так как требует больше сравнений и перестановок.
>>Это тяжело читать, тут надо вынести часть выражения в отдельные переменные с понятным названием.
> А это я не понял
Трудно читать такие выражения как
> $transportName[$paths[$made_path[$key-1]]['neighbors'][$path]['transport']]
Так как тут много скобок и переменных и на первый взгляд даже непонятно какой глубины тут массив и что к чему относится. Нужно выносить части выражения в отдельные переменные с понятными именами, например:
$previous_point = $paths[$made_path[$key-1]];
$used_transport = $previous_point['neighbors'][$path]['transport'];
$used_transport_name = $transports[$user_transport];
Также, ты смешиваешь имена переменных в разных стилях: $transportName и $made_path. В PSR нет рекомендаций насчет имен переменных и функций, но я бы советовал именовать их так же как методы, кемелкейсом.
Также, лучше бы было написать цикл без необходимости писать $key - 1, и вообще без $key - было бы аккуратнее.
> Можно теперь я пойду ООП изучать?
Изучать ООП конечно можно, но хорошо бы и с этой задачей разобраться.
По поводу функций - разделение пока не идеальное. У тебя функция поиска пути всегда заканчивается его выводом. Но разделение подразумевает, что мы можем найти кратчайший путь, не выводя его. Например, мы хотим дальше как-то его программно обработать.
То есть хотелось бы чтобы было именно 2 отдельных функции (поиск пути и вывод пути), было определено, что они получают на вход и что дают на выходе. А у тебя функция всегда печатает ответ на выходе, и программно с ним сделать ничего нельзя. Отдельной функции только поиска пути нет.
Умение разбивать код на функции тебе обязательно понадобится позже, когда объемы кода будут больше.
> foreach ($paths as $name => $value) { // Проверяем есть ли такие точки.
> if ($start_point == $name) {
Используй array_key_exists в таких случаях. Вообще, тебе стоит посмотреть на список функций для работы с массивами, либо в моем уроке, либо тут: http://php.net/manual/ru/ref.array.php
> $paths[$current_point]['shortest_path'] = 0; // делаем длинну пути стартовой точки = 0
Тут комментарий лучше писать сверху, так как строка получается слишком длинная, и PSR рекомендует не делать строк длинее 80-120 символов ( http://www.php-fig.org/psr/psr-2/ ).
> $j = INF;
Лучше было назвать $min_path, $min_length или как-то так
> while ($paths[$curent_point]['shortest_path'] != 0) {
Логичнее наверно было while ($current_point != $start_point), мы ведь ищем именно стартовую точку
> if ($path == 'pet') {
> continue;
Почему тут вписано исключение? А что, если мы поменяем стартовую или конечную точку, исключение нам тоже надо менять руками?
> А смысл хранить их отдельно. Так же работает =|
"работает" - этого мало, нужно еще чтобы программа выглядела логично и понятно. А с точки зрения логики массив соединений между точками и массив посещенных вершин (или массив расстояний) - это разные наборы данных. Ну к примеру, если мы хотим найти 2 пути, то массив соединений в обоих случаях будет одинаков, а массив расстояний - разный.
> Дейкстра если дошёл до точки. То есть поставил её посещение True, то он дошел до неё самым коротким путем.
Тогда стоило об этом написать в комментарии. Человек, который читает код, видит, что алгоритм изменен, а почему - специально или это ошибка - непонятно.
> Не смог найти эту функцию. А вообще есть разница?
Думаю, что сортировка будет работать медленее, так как требует больше сравнений и перестановок.
>>Это тяжело читать, тут надо вынести часть выражения в отдельные переменные с понятным названием.
> А это я не понял
Трудно читать такие выражения как
> $transportName[$paths[$made_path[$key-1]]['neighbors'][$path]['transport']]
Так как тут много скобок и переменных и на первый взгляд даже непонятно какой глубины тут массив и что к чему относится. Нужно выносить части выражения в отдельные переменные с понятными именами, например:
$previous_point = $paths[$made_path[$key-1]];
$used_transport = $previous_point['neighbors'][$path]['transport'];
$used_transport_name = $transports[$user_transport];
Также, ты смешиваешь имена переменных в разных стилях: $transportName и $made_path. В PSR нет рекомендаций насчет имен переменных и функций, но я бы советовал именовать их так же как методы, кемелкейсом.
Также, лучше бы было написать цикл без необходимости писать $key - 1, и вообще без $key - было бы аккуратнее.
> Можно теперь я пойду ООП изучать?
Изучать ООП конечно можно, но хорошо бы и с этой задачей разобраться.
> Что делает функция onContactSelect()? Она задает сам обработчик?
Да, мне было лень писать setOnContactSelectHandler. Вообще, иногда задание обработчиков выносят в отдельный объект, например:
widget.events.on('contactSelect', function () { ... });
или
widget.contactSelectEvent.addHandler(function () { ... });
Второй вариант лучше тем, что видно, какие в объекте есть события, но требует больше объектов создавать.
> То есть, callable и будет передаваемой зависимостью?
Гм, почему "зависимостью"? Наверно, правильнее сказать, что это обработчик (слушатель) события.
> Теперь, когда появилось возможность более гибко обращаться с зависимостями, появляется возможность управлять сообщениями не в главном контроллере, а в самом контроллере Сообщений. То есть, получиться так:
Да, так и должно быть, чтобы виджеты не знали друг о друге и мы могли разрабатывать их независимо. Более того, может быть мы не хотим, чтобы conversation знал про контакты и будем вместо них передавать в него список сообщений, которые надо отобразить.
> Где лучше ставить такие обработчики, в коде снаружи или инициализировать в главном контроллере?
С точки зрения виджета главный контроллер и есть "код снаружи". Так что не знаю, ставь где удобнее. Если главный контроллер у тебя создает виджеты, наверно ему и нужно их связывать.
> Почему вы для списка контактов сделали отдельный виджет, если есть один общий виджет Контактов,
Я этот виджет и имел в виду, просто назвал его "список контактов"
> Почему другого? Это же интервал обновления контактов, значит и храниться он должен в соответствующем объекте.
Ну тогда этот объект и должен с ним работать. Это плохая идея, пользоваться тем, что в JS нет приватных свойств, и лезть внутрь других объектов, так как это делает объекты более спутанными и усиливает их зависимость друг от друга, и сложнее становится их редактировать, так как можно сломать что-то в другом объекте.
Я советую тебе сделать например, чтобы приватные поля и методы начинались с подчеркивания (за неимением нормальных приватных полей) и не нарушать инкапсуляцию.
> Что делает функция onContactSelect()? Она задает сам обработчик?
Да, мне было лень писать setOnContactSelectHandler. Вообще, иногда задание обработчиков выносят в отдельный объект, например:
widget.events.on('contactSelect', function () { ... });
или
widget.contactSelectEvent.addHandler(function () { ... });
Второй вариант лучше тем, что видно, какие в объекте есть события, но требует больше объектов создавать.
> То есть, callable и будет передаваемой зависимостью?
Гм, почему "зависимостью"? Наверно, правильнее сказать, что это обработчик (слушатель) события.
> Теперь, когда появилось возможность более гибко обращаться с зависимостями, появляется возможность управлять сообщениями не в главном контроллере, а в самом контроллере Сообщений. То есть, получиться так:
Да, так и должно быть, чтобы виджеты не знали друг о друге и мы могли разрабатывать их независимо. Более того, может быть мы не хотим, чтобы conversation знал про контакты и будем вместо них передавать в него список сообщений, которые надо отобразить.
> Где лучше ставить такие обработчики, в коде снаружи или инициализировать в главном контроллере?
С точки зрения виджета главный контроллер и есть "код снаружи". Так что не знаю, ставь где удобнее. Если главный контроллер у тебя создает виджеты, наверно ему и нужно их связывать.
> Почему вы для списка контактов сделали отдельный виджет, если есть один общий виджет Контактов,
Я этот виджет и имел в виду, просто назвал его "список контактов"
> Почему другого? Это же интервал обновления контактов, значит и храниться он должен в соответствующем объекте.
Ну тогда этот объект и должен с ним работать. Это плохая идея, пользоваться тем, что в JS нет приватных свойств, и лезть внутрь других объектов, так как это делает объекты более спутанными и усиливает их зависимость друг от друга, и сложнее становится их редактировать, так как можно сломать что-то в другом объекте.
Я советую тебе сделать например, чтобы приватные поля и методы начинались с подчеркивания (за неимением нормальных приватных полей) и не нарушать инкапсуляцию.
правила валидации прописываешь в моделе , а функцию проверки вызываешь в контроллере
как это правильно сделать? Самому написать? Например: в моделе создать свойство - массив с правилами, а в контроллере к нему обратиться или как-то иначе?
Вообще, честно, код плохой и похож на код из какого-нибудь учебника неграмотного автора 10-летней давности.
Вот, что стоило бы исправить:
Выделить публичную папку (т.е. папку для файлов, которые можно запросить снаружи) отдельно. Сейчас у тебя по сути все лежит в публичной папке веб-сервера, что небезопасно. Например, можно скачать шаблоны и увидеть их исходный код, а также скачать конфиг.
В шаблонах у тебя вроде бы есть повторяющиеся куски - шапка страницы.
Советую убрать опцию переименования таблиц через конфиг, это требует много усилий, а выгода неочевидна.
Советую убрать реальные даты, названия компаний, номера и фамилии из скриншота, заменить на вымышленные.
Тут код отформатирован ужасно: https://github.com/tsubaku/flights/blob/master/submit.php - читай второй пост в треде, там написано как исправить это полуавтоматически.
> https://github.com/tsubaku/flights/blob/master/register_client.php
Если ты отдаешь JSON, то тип ответа должен быть application/json, не надо изобретать свои стандарты. И логично заголовки ставить не в начале скрипта, а перед отдачей самого JSON
> $pass = md5(md5(trim($pass)));
Безопаснее использовать соленый хеш, как описано в моем уроке https://github.com/codedokode/pasta/blob/master/security/password-hashing.md
При загрузке картинок нет проверки типа файла, человек загрузит .png или .txt, а ты его переименуешь в .jpg например.
В коде видно много копипасты - например выставление заголовков в начале файла.
> https://github.com/tsubaku/flights/blob/master/show_list_clients.php#L10
> while(list ($key, $val) = each ($_POST)){
Это давно устарело, нужно использовать foreach тут
Тут https://github.com/tsubaku/flights/blob/master/show_list_clients.php в одном файле смешана логика и вывод данных в HTML, почитай про шаблоны http://web.archive.org/web/20161119062218/www.phpinfo.su/articles/practice/shablony_v_php.html
В твоем варианте, например, представь, как неудобно делать какие-то исправления в верстке таблиц, когда она перемешана с PHP-кодом и засунута в кавычки.
Также, прочитай про популярные уязвимости и способы защиты тут https://github.com/codedokode/pasta/tree/master/security
https://github.com/tsubaku/flights/blob/master/index.php#L10
> if ($user_level == 9) {
Тут нужно ииспользовать константу с понятным названием вместо цифры
> $level = 'manager';
И тот тоже желательно константу.
Название функции должно начинаться с глагола, то есть не protection, а redirectIfNoPrivileges(...)
> <a href='#' class='a_button_delete'
Ссылка должна куда-то вести. Для создания кнопки есть тег button.
> echo "<br />";
В HTML слеш в конце тега не ставится, в отличие от XML/XHTML.
> $object_operation_name = $value[0];
Это явно что-то непраивльное, что ты получаешь параметр из POST по номеру, а не по имени.
> while ($i <= count($ru_rows_array)-1){
Тут нужно использовать foreach. Ты сишник наверно? Не надо тянуть плохие практики из Си в язык, где есть foreach.
Вообще, если ты хочешь научиться писать приложения в соответствии с всеми правилами и хорошими практиками, я предлагаю почитать задачу про студентов из ОП поста, к ней идет много комментариев.
> Пока что поверю вам на слово, что ООП лучше процедурного подхода.
ООП лучше, когда кода становится много и надо его организовывать. У тебя все явно к этому идет. Но вместо организации мы пока видим файлы, которые явно создаются методом копирования и правки.
А что касается фреймворка, то он позволяет не изобретать велосипед и не писать то, что уже где-то написано.
> https://github.com/tsubaku/flights/blob/master/functions.php#L292
> header("Location: status_codes.php?result=403");
Если ты хочешь отдать код ответа HTTP, то надо его отдавать сразу. Потому что редирект - это код вроде 302. То есть ты вместо отдачи кода 403 отдаешь код 302 в ответ на запрос. И вместо "доступ запрещен" говоришь "страница переехала на другой адрес".
> if (is_null($_COOKIE['id'])
Это неправильно и вызовет ошибку, если в массиве нет ключа 'id'. У тебя наверно включено игнорирование предупреждений, или же ты не смотришь в логи ошибок, раз этого не видишь. Нужно использовать array_key_exists или isset здесь.
https://github.com/tsubaku/flights/blob/master/functions.php#L187
> $info = getimagesize($path);
Тут не проверяется вариант, когда функция вернет вместо массива false.
https://github.com/tsubaku/flights/blob/master/functions.php#L5
Тут слишком сложная функция с слишком большой глубиной отступов
Вообще, честно, код плохой и похож на код из какого-нибудь учебника неграмотного автора 10-летней давности.
Вот, что стоило бы исправить:
Выделить публичную папку (т.е. папку для файлов, которые можно запросить снаружи) отдельно. Сейчас у тебя по сути все лежит в публичной папке веб-сервера, что небезопасно. Например, можно скачать шаблоны и увидеть их исходный код, а также скачать конфиг.
В шаблонах у тебя вроде бы есть повторяющиеся куски - шапка страницы.
Советую убрать опцию переименования таблиц через конфиг, это требует много усилий, а выгода неочевидна.
Советую убрать реальные даты, названия компаний, номера и фамилии из скриншота, заменить на вымышленные.
Тут код отформатирован ужасно: https://github.com/tsubaku/flights/blob/master/submit.php - читай второй пост в треде, там написано как исправить это полуавтоматически.
> https://github.com/tsubaku/flights/blob/master/register_client.php
Если ты отдаешь JSON, то тип ответа должен быть application/json, не надо изобретать свои стандарты. И логично заголовки ставить не в начале скрипта, а перед отдачей самого JSON
> $pass = md5(md5(trim($pass)));
Безопаснее использовать соленый хеш, как описано в моем уроке https://github.com/codedokode/pasta/blob/master/security/password-hashing.md
При загрузке картинок нет проверки типа файла, человек загрузит .png или .txt, а ты его переименуешь в .jpg например.
В коде видно много копипасты - например выставление заголовков в начале файла.
> https://github.com/tsubaku/flights/blob/master/show_list_clients.php#L10
> while(list ($key, $val) = each ($_POST)){
Это давно устарело, нужно использовать foreach тут
Тут https://github.com/tsubaku/flights/blob/master/show_list_clients.php в одном файле смешана логика и вывод данных в HTML, почитай про шаблоны http://web.archive.org/web/20161119062218/www.phpinfo.su/articles/practice/shablony_v_php.html
В твоем варианте, например, представь, как неудобно делать какие-то исправления в верстке таблиц, когда она перемешана с PHP-кодом и засунута в кавычки.
Также, прочитай про популярные уязвимости и способы защиты тут https://github.com/codedokode/pasta/tree/master/security
https://github.com/tsubaku/flights/blob/master/index.php#L10
> if ($user_level == 9) {
Тут нужно ииспользовать константу с понятным названием вместо цифры
> $level = 'manager';
И тот тоже желательно константу.
Название функции должно начинаться с глагола, то есть не protection, а redirectIfNoPrivileges(...)
> <a href='#' class='a_button_delete'
Ссылка должна куда-то вести. Для создания кнопки есть тег button.
> echo "<br />";
В HTML слеш в конце тега не ставится, в отличие от XML/XHTML.
> $object_operation_name = $value[0];
Это явно что-то непраивльное, что ты получаешь параметр из POST по номеру, а не по имени.
> while ($i <= count($ru_rows_array)-1){
Тут нужно использовать foreach. Ты сишник наверно? Не надо тянуть плохие практики из Си в язык, где есть foreach.
Вообще, если ты хочешь научиться писать приложения в соответствии с всеми правилами и хорошими практиками, я предлагаю почитать задачу про студентов из ОП поста, к ней идет много комментариев.
> Пока что поверю вам на слово, что ООП лучше процедурного подхода.
ООП лучше, когда кода становится много и надо его организовывать. У тебя все явно к этому идет. Но вместо организации мы пока видим файлы, которые явно создаются методом копирования и правки.
А что касается фреймворка, то он позволяет не изобретать велосипед и не писать то, что уже где-то написано.
> https://github.com/tsubaku/flights/blob/master/functions.php#L292
> header("Location: status_codes.php?result=403");
Если ты хочешь отдать код ответа HTTP, то надо его отдавать сразу. Потому что редирект - это код вроде 302. То есть ты вместо отдачи кода 403 отдаешь код 302 в ответ на запрос. И вместо "доступ запрещен" говоришь "страница переехала на другой адрес".
> if (is_null($_COOKIE['id'])
Это неправильно и вызовет ошибку, если в массиве нет ключа 'id'. У тебя наверно включено игнорирование предупреждений, или же ты не смотришь в логи ошибок, раз этого не видишь. Нужно использовать array_key_exists или isset здесь.
https://github.com/tsubaku/flights/blob/master/functions.php#L187
> $info = getimagesize($path);
Тут не проверяется вариант, когда функция вернет вместо массива false.
https://github.com/tsubaku/flights/blob/master/functions.php#L5
Тут слишком сложная функция с слишком большой глубиной отступов
Если с PDO неудобно работать напрямую, можно сделать обертку, которая например получает массив аргументов и вызывает для каждого элемента bindValue (почему bindParam? где ты такое прочел? он используется для возможности возврата значения обратно в переменную).
>>1014744
Может он сам ничего не валидирует, а только вызывает метод модели? Посмотри в исходнике например.
>>1014769
Там в мануале не написано?
Кнопка "читать дальше" - яркий и заметный элемент страницы, привлекающий внимание. Но кнопка с числом комментариев рядом только зря отвлекает это внимание. Советую убрать черную кнопку вокруг числа комментариев.
> Тут нужно было спросить, а каким соотвествует
Это же ты завел речь про стандарты, ты и должен пояснять, что чему не соответствует. А пока это пустая болтовня.
>>1013687
По этим данным нельзя исключить какой-то фреймворк, любой подойдет. Я бы выбрал Юи или Симфони.
> сервис пинга мобильного приложения на ноде.
Нода-то зачем?
>>1013660
Ты не видишь, а я вижу. Незачем усложнять код без необходимости.
Ну и ты полностью проигнорировал, что это второй или третий урок из учебника для людей, которые может быть первый раз в жизни пишут код.
>>1013617
Бутстрап полезен, если хочется сделать страницу с оформлением как у бутстрапа, с цветными кнопочками, аккуратными таблицами и формами, так как в стандартных стилях браузера этого нет.
Если дизайн другой, то конечно, тащить его не нужно, будет только мешать.
Это значит, что надо почитать какие-нибудь уроки для начинающих наверно. В Оп посте может быть что-то найдется полезное.
Надо поставить yii-advanced, обновил composer - всё сделал как в мануале на гите (они его походу никогда не обновляют)
и всё равно нихера не ставится (пикрелейтед)
>Выделить публичную папку
Хм, а разве .httpacess не защищает? Хотя, я уже не помню, что в него писал. Действительно, надо будет проверить.
>>1014773
>Советую убрать опцию переименования таблиц через конфиг
Не понял, что за опция? Ты имеешь в виду, что к ним надо обращаться просто по имени и имена прибить гвоздями? В принципе, в коде сейчас именно так и в конфиге имена таблиц, пожалуй, лишние.
>>1014773
>Советую убрать реальные даты, названия компаний, номера и фамилии из скриншота, заменить на вымышленные.
Они и есть вымышленные. Или ты имеешь в виду, что они должны быть нарочито выдуманные, чтобы никаких сомнений в этом не было? Хорошо, переименую в такие.
>>1014773
>Тут код отформатирован ужасно
Почитаю, переформатирую. Там частично копипаста, но перепиленная. Кстати, надо будет убрать из комментов автора старой копипасты, потому что уже перепилено.
>>1014773
>При загрузке картинок нет проверки типа файла, человек загрузит .png или .txt, а ты его переименуешь в .jpg например.
Да, есть такое. Я пока оставил это так, в будущем собираюсь проверять тип файла, блокировать левые, а картинки пережимать. И корректно обрабатывать присылаемые видео. Пока что всё на честном слове.
>>1014773
>в одном файле смешана логика и вывод данных в HTML
Окей, перепишу.
>>1014773
>В HTML слеш в конце тега не ставится, в отличие от XML/XHTML.
Да, но там же доктайп именно XHTML.
>файлы, которые явно создаются методом копирования и правки.
Истинно так. Я не умею писать сам с нуля, сначала нужно заставить работать уже кем-то написанное, разобраться, как оно работает. Вот потом, после пары-тройки таких копипастов, уже получается писать самостоятельно.
>тут слишком сложная функция с слишком большой глубиной отступов
Да, там та ещё простыня, которую ваял, лишь бы работало, а потом рука так и не поднялась переписать набело.
Большое тебе спасибо за подробный анализ, с понедельника начну переписывать, исправляя косяки.
>Выделить публичную папку
Хм, а разве .httpacess не защищает? Хотя, я уже не помню, что в него писал. Действительно, надо будет проверить.
>>1014773
>Советую убрать опцию переименования таблиц через конфиг
Не понял, что за опция? Ты имеешь в виду, что к ним надо обращаться просто по имени и имена прибить гвоздями? В принципе, в коде сейчас именно так и в конфиге имена таблиц, пожалуй, лишние.
>>1014773
>Советую убрать реальные даты, названия компаний, номера и фамилии из скриншота, заменить на вымышленные.
Они и есть вымышленные. Или ты имеешь в виду, что они должны быть нарочито выдуманные, чтобы никаких сомнений в этом не было? Хорошо, переименую в такие.
>>1014773
>Тут код отформатирован ужасно
Почитаю, переформатирую. Там частично копипаста, но перепиленная. Кстати, надо будет убрать из комментов автора старой копипасты, потому что уже перепилено.
>>1014773
>При загрузке картинок нет проверки типа файла, человек загрузит .png или .txt, а ты его переименуешь в .jpg например.
Да, есть такое. Я пока оставил это так, в будущем собираюсь проверять тип файла, блокировать левые, а картинки пережимать. И корректно обрабатывать присылаемые видео. Пока что всё на честном слове.
>>1014773
>в одном файле смешана логика и вывод данных в HTML
Окей, перепишу.
>>1014773
>В HTML слеш в конце тега не ставится, в отличие от XML/XHTML.
Да, но там же доктайп именно XHTML.
>файлы, которые явно создаются методом копирования и правки.
Истинно так. Я не умею писать сам с нуля, сначала нужно заставить работать уже кем-то написанное, разобраться, как оно работает. Вот потом, после пары-тройки таких копипастов, уже получается писать самостоятельно.
>тут слишком сложная функция с слишком большой глубиной отступов
Да, там та ещё простыня, которую ваял, лишь бы работало, а потом рука так и не поднялась переписать набело.
Большое тебе спасибо за подробный анализ, с понедельника начну переписывать, исправляя косяки.
>Нужно изучить, что происходит при редиректе.
Ну как бы с сетевой частью-то я знаком хорошо. Область-то интересов так-то лет 10 уже.
А теперь самая вишенка на торте.
Оно каким-то хуем заработало. Простояло субботу, сейчас попробовал - работает.
В последний раз себя так по-блядски вёл только ms lync server 2013.
Завтра полезу смотреть, что же случилось.
Если что выплывет - напишу, мало ли, вдруг ещё кто-то натолкнётся.
> "поле" объектом, у этого объекта метод поиска соседей, и передать в мышку этот объект. Например, через конструктор либо через метод "поместить на поле(поле)"/"снять с поля". Методы позволяют ставить и снимать животных на разные поля (если это нужно), а в случае с конструктором привязка проис
У меня какие-то серьезные проблемы с пониманимем ооп.
Ну вот как мне написать у мышки метод, который будет её двигать? Очевидно что двигаться то это её область ответственности? Как мышка должна взаимодействовать с миром? Мне что, в такой простой казалось бы метод движения на 1 клетку ей целый мир передавать аргументом???
Ощущаю себя дико тупым, блядь как же сука горит невыносимо.
Мышка должна знать законы физики твоего мира, но при этом ей совсем не нужно знать, как они работают.
Она знает что есть такое "движение вперед", и просто делает его, она не знает про химические, электрические реакции в мышцах и нервах.
Изи.
Мышка (М) никуда не двигается.
1. М спрашивает у поля где она.
2. М спрашивает у поля что вокруг.
3. М принимает решение куда хочет пойти и говорит об этом полю.
4. Поле меняет координаты М.
>По поводу функций - разделение пока не идеальное.
Но вот я разделил. И во вторую функцию теперь тоже идут start_pont и end_point. И мне надо опять туда проверку засовывать? А вообще смысл проверок. И так же понятно что если не те данные ввести не сработает.
>Лучше было назвать $min_path, $min_length или как-то так
А она разве не относится к временной переменной. http://prntscr.com/fqrjru
>Также, ты смешиваешь имена переменных в разных стилях: $transportName и $made_path. В PSR нет рекомендаций насчет имен переменных и функций, но я бы советовал именовать их так же как методы, кемелкейсом.
Забыл поменять. Читал книгу и эти доводы меня убедили http://prntscr.com/fqro8l . Но как я понимаю большинство все равно кемелкейс использует и лучше его использовать?
>"работает" - этого мало, нужно еще чтобы программа выглядела логично и понятно. А с точки зрения логики массив соединений между точками и массив посещенных вершин (или массив расстояний) - это разные наборы данных. Ну к примеру, если мы хотим найти 2 пути, то массив соединений в обоих случаях будет одинаков, а массив расстояний - разный.
Ну тогда получается и на получение массива с вершинами по которым мы идем надо свою функцию писать, и на каждый шаг писать свою функцию, а это долго.
http://ideone.com/D5PJG0
В общем, это снова я. Причина отваливающихся кук кроется в SNI на IIS8. Оно заставляет спотыкаться куки. Ну, как минимум, на той версии (7.0.6), что у меня.
Из подозрений:
1) Баг IIS.
2) Баг конкретной ревизии php (или какой-либо её разрядности).
3) Баг из-за того, что что-то портит haproxy (тоже по sni определяет).
П3, скорее всего - нет, т.к. бредово на фоне того, что ha просто публикует. Плюс к тому, на опубликованных на nginx https ресурсах такой проблемы нет. Так что или п.1 или п2.
Почему бы тебе не запостить сюда заголовки запросов/ответов, на которых было бы видно проблему (заголовки Cookie/Set-Cookie)? Мы бы тоже посмотрели и может быть что-то подсказали.
>camelCase
Это_прям_так_обязательно?
И если прям так, то как быть с переменными типа read_from_bd? readFromDb? Меня вот это Db сильно смущает.
>мы используем для отступов 4 пробела
Почему не таб? Он же самим богом дан людям для формирования отступов.
Помогите понять почему не работает, может какой-то совет или хитрость есть которой я не знаю/не понимаю.
Также прилагаю кусок с хтмл форм регистрации.
>Почему не таб? Он же самим богом дан людям для формирования отступов.
Как настроишь свой редактор. В Атоме, например можно сделать, чтобы на таб было 4 пробела.
Я сам если вам интересно, подозреваю что на хостинге сайт не включен cURL. Можно ли это как-то проверить?
Я так понял, что в файле должны быть именно не табуляции, а символы пробела. Или нет?
да, все верно.
ОП задолбался объяснять уже. ТАК ПРИНЯТО У Х
ОРОШИХ ЛЮДЕЙ, смирись^w приучайся к хорошим манерам. Табы — это как мальчику сикать сидя.
Проблема с табами в том, что они создают путаницу. Визуально таб ничем не отличается от 4 пробелов и потому при использовании табов ты будешь иметь в реальности смесь табов и пробелов.
И непонятно зачем придумывать себе все эти правила, когда ставить таб, когда пробел, если можно всегда ставить пробел и не задумываться.
Также, табы криво отображаются в разных средах. Например, на гитхабе таб отображается как 8 пробелов и все разъезжается.
То есть плюсов у табов никаких, только минусы.
Фанаты табов любят говорить что они позволяют им настраивать отступы, но что им мешает настраивать отступы в файлах с пробелами, кроме своей лени, я не понимаю?
Что касается some_name vs someName - это чисто религиозный вопрос и явных преимуществ нет ни у одного способа.
Вопрос про from_db vs fromDb vs fromDB - чисто религиозный. Явных преимуществ нет ни у одного варианта. Нужно просто выбрать один вариант и придерживаться.
Разработчики фреймворков в рамках PSR сравнили используемые стили, увидели что большинство использует кемелкейс для классов и методов, и прописали это в PSR.
По поводу переменных, функций, полей такого единогласного решения не было и они ничего не прописали (жаль).
Я для себя просто решил писать так, как пишут в Симфони (там все кемелкейсом). Так как мне нравятся компоненты Симфони, ну и это вроде как у нас ведущий фреймворк.
Но если вы хотите писать через подчеркивания, пожалуйста, я не против, если PSR ничего не говорит по этому поводу. Если вам из религиозных соображений не нравится PSR, то понимания у меня вы не встретите.
>смирись
Хорошо, хорошо, смирюсь.
>>1015505
>при использовании табов ты будешь иметь в реальности смесь табов и пробелов.
Это с чего бы? Все отступы делать табами.
>табы криво отображаются в разных средах
О, вот это уже аргумент.
Вот мышка хочет сходить. Как она смотрит на объект "поле"?
Ей передается поле аргументом в этот метод? Я просто рили недогоняю. Или мышь как бы знает о существовании поля по умолчанию? Как глобального объекта? Опять же как сходить? Получается что мышь может изменять такой объект как поле?
Просто не могу въехать и всё. Видимо я уже достаточно старый и мозг мой не гибок.
Вот допустим взять предыдущую задачу. Там была своеобразная иерархия управления классов.
Всё действия над сотрудником производились из департамента, а действия над департаментом - проводились внутри организации. Всё легко и просто (вроде).
Опять же по аналогии не будешь делать тут, так что бы у тебя поле управляло животными, которые на нем? Нужны какие-то обратные зависимости? Типа сами животные управляют и изменяют поле, на котором они обитают? А животными в свою очередь управляет какой-то объект "время" ну или "игровой цикл" я не знаю.
зачем, если там об этом не написано?
http://ideone.com/tbwdW9
function убери в 15 строке, балбес. Ты уже определил функцию, теперь ты ее используешь.
спасибо
помогите пожалуйста
что сейчас не так?
выдает один и тот же банк
http://ideone.com/hjsYnC
if ($servicePayment = 500)
= это оператор присвоения
== это сравнения
=== это тебе пока рано
И убери из функции эти эту движуху с именами. Передавай имя еще одним параметром в функцию.
Ведь сплошной профит от неё.
-Резиновая.
-Простая.
-Можно применить стиль сразу ко всей таблице и не ебаться с 30 дивами.
-Ебическая кроссплатформенность. Поддерживается и одинаковая даже на первых нетскейпах и альфа версиях експлорера и отображается везде одинаково.
-Нет ебли с отображением.
-Чёткое крепление элементов. Сразу понятно где у тебя будет контент.
-Независимость элементов (ты в 3 ячейке справа и тебе насрать что в яцейке слева происходит.) В отличии от дива который можно распидорасить изменив соседний див.
-Чёткий синтаксис. (Сразу видно в какой ячейке контент.)
Я вдруг задал вопрос всем своим знакомым веб девелоперам даже с опытом 8 лет. И никто не смог дать чёткого ответа. В основном "Так модно же", "Ну так принято везде".
Потому что тег table обозначает таблицу с данными и не должен использоваться для задания внешнего вида сайта.
Однако, допустимо использовать display: table в CSS, если хочется заставить элементы раскладываться как части таблицы.
Практическое соображение: как ты адаптивную версию сделаешь для узких экранов для табличной верстки? Там все гвоздями прибито и поменять через CSS ничего нельзя.
> Простая.
Это же неправда. Для таблицы с колонками width=100%, width=0, width=20 - какая в реальности будет ширина колонок, можешь посчитать?
Правила вычисления размеров ячеек таблиц сложные, местами не стандартизованные и зависят от браузера. Как их можно назвать простыми?
> кроссплатформенность
При том что алгоритм расчета размеров ячеек даже не стандартизован
> Независимость элементов (ты в 3 ячейке справа и тебе насрать что в яцейке слева происходит.) В отличии от дива который можно распидорасить изменив соседний див.
Ровно наоборот. Ячейки таблицы влияют друг на друга, увеличение одной может задать уменьшение другой, для ячеек width это фактически min-width, в то время как для дива width это и есть width, ни пикселя больше.
По моему ты просто CSS не изучал, не знаешь его, потому избегаешь использования CSS и по этой причине табличная верстка тебе кажется более простой. Я могу посоветовать наш курс HTML/CSS в ОП посте. Уверен, он поменяет твои взгляды.
> И никто не смог дать чёткого ответа.
Это говорит о том, насколько они хорошо знают CSS. Может они бекендщики, разработчики драйверов или хаскеллисты?
> Сразу видно в какой ячейке контент.
По моему <div class="content"> или <article> гораздо красноречивее.
><div class="content">
Проблема в том, что таких дивов может быть 30 со вложенностью, И я долго разбираюсь со всем.
А ты видел ирл сайт на табличной верстке?
Каждая таблица это минимум уже 3 левела вложенности.
Скажем у тебя страница это табилца, разбитая там на хедер, футер, и посерединке контент и меню слева.
В каждой такой ячейке лежит еще таблица, в каждой по надобности может еще быть по 2-3 уровня таблиц.
У тебя там изи по 20-30 левелов вложенности может в итоге высраться, и это пиздец.
Нужно давать хорошие названия и не городить лишние блоки.
>>1016130
Я помню, когда скругленные блоки с рамкой делали таблицей с 9 ячейками. Справедливости ради, в ИЕ6 сделать такое без таблиц было сложно, помню как сам долго отлаживал резиновые скругленные поля ввода.
Я не срач хочу устроить, хотя возможно вам так покажется. Я просто хочу для себя поставить точку в этом неразрешимом пока для меня споре.
>Для таблицы с колонками width=100%, width=0, width=20 - какая в реальности будет ширина колонок, можешь посчитать?
Ты задал сразу 3 размера ширины. Естественно сложно подсчитать. А если по отдельности - ширина 100%, и неопределённые ширины ибо px не проставлены.
>Правила вычисления размеров ячеек таблиц сложные, местами не стандартизованные и зависят от браузера.
А есть примеры? В каких браузерах оно по разному, впервые об этом читаю.
>алгоритм расчета размеров ячеек даже не стандартизован
Сам указывай размер. Я всегда так делаю, для Firefox, Opera, IE, Safary как-то работает.
>По моему ты просто CSS не изучал, не знаешь его, потому избегаешь использования CSS
Вчера закончил курс HTML academy.
И там всё гораздо сложнее с принципами каскадирования и приоритетов. Надо постоянно держать в уме, высчитывать приоритеты, все эти 1010 или 0101 или 111, что приоритетнее и какой стиль в итоге победит. Когда впервые об этом прочитал просто голова взорвалась.
>избегаешь использования CSS
Просто сейчас я вижу в исходниках уж ОЧЕНЬ много вложенных дивов и в итоге то на то и выходит, что несколько вложенных таблиц, что по 5 - 6 слоёв дивов.
>Я помню, когда скругленные блоки с рамкой делали таблицей с 9 ячейками.
Я и сейчас так делаю. У меня до сих пор клиент на IE 6 есть.
Просто попробуй. Сделай одну страничку посложнее. А потом открой ее в разных браузерах. Ты охуеешь. Если бы ты сперва попробовал, ты бы не задавал такой нет, не тупой, вопрос. Такие вопросы это тоже часть опыта, увы, некоторым приходится ебаться с ними лично, но для тебя есть наш уютный тред, где тебя предостерегут от излишней жопаболи.
Спасибо анон.
>>>>>что если нужно будет что-то проделать с сообщениями, например поиск по ним? Неужели, это тоже нужно будет делать внутри коллбека?
>>>>Можно из коллбека вызвать отдельную функцию поиска, не вижу тут никакой проблемы.
>> А разве это не нарушение распределения ответственности? Получается что функция обновления сообщений отвечает и за поиск сообщений, и может быть ещё за другие функции, которые нужно будет проделать с сообщениями.
>А я не говорю, что они должны быть в одной функции.
Это вынужденная необходимость копипастить код?
>Разумеется, при поиске мы можем использовать другую функцию вместо getMesasges - например, getSavedMessages(), которая не шлет запрос на сервер, если данные уже получены.
Вы имели ввиду если используются локальное хранилище? Что-то из IndexerDB или localStorage?
При частичной выборке сообщений, при обновлении, если появляются новые сообщения, то пропадают старые вначале, потому что выбирается конкретное число данных. Как это починить?
Приходит на ум, только, не обновлять сообщения $(messages).html(data), а добавлять в конец новые $(messages).append(data).
У меня уже были замечания о том чтобы не подгружать каждый раз, но я никак не могу придумать, как вызывать запрос только один раз при появлении новых сообщений.
Я не верстала, но долго верстал в тейблах лол, друг постоянно обоссывал меня в конце концов пришлось насильно переучиваться в дивную верстку от чего у меня болела жопа, мне казалось это более сложным хайповым/хипстерским говном
Ну у меня также было. А сейчас стала задача сверстать под IE6 и я вспомнил всё это. И мне кажется такой-то годнотой таблица. Запилил таблицу, распихал хеадер в верхние ячейки, распихал футер в нижние. Запилил в бок менюшку, таблица сама за меня под высоту контента растянулась, сама отмасштабировалась, автоматом боковое меню поставила на место. Это при том что я даже CSS не прописывал!
Я много лет назад вроде верстал что-то на таблицах, но о переходе на CSS ни секунды не жалел, так как там все проще и логичнее. Но я в свое время разбирался в CSS основательно, вплоть до чтения спецификаций, может потому он мне понятным кажется.
>>1016199
Я ведь уже выше писал что можно использовать display: table. То есть любую верстку на дивах можно заставить себя вести как табличную. На дивах можно сделать то же самое. Плохо ты изучал CSS, если на это не обратил внимания. .
Потому я все же считаю, что ты не разобрался в CSS, отчего от тебе сложным и кажется. Что там сложного-то? Всего 6 или 7 способов позиционирования с относительно четко заданными правилами определения размеров.
Или может ты видел примеры какой-то плохой верстки. Но скорее всего все же проблема в том, что ты не разобрался.
> Ты задал сразу 3 размера ширины. Естественно сложно подсчитать. А если по отдельности - ширина 100%, и неопределённые ширины ибо px не проставлены.
А я имел в виду HTML-атрибут width, там px не указывают. Во времена табличной верстки ведь использовали именно HTML-атрибуты, тем более когда CSS толком не поддерживался.
Твой ответ про расчет ширины в любом случае неправильный. Для таблиц width задает только минимальную ширину, а это значит, что реальная ширина будет зависеть от содержимого ячеек и размера этого содержимого.
Пример, который это показывает: https://codepen.io/anon/pen/RgJZNJ
> И там всё гораздо сложнее с принципами каскадирования и приоритетов. Надо постоянно держать в уме, высчитывать приоритеты, все эти 1010 или 0101 или 111, что приоритетнее и какой стиль в итоге победит
Во-первых, считать не так сложно (id > класс > тэг > звездочка), во-вторых, а зачем к одному элементу применять конфликтующие стили? Лучше использовать подходы вроде БЭМ, где все относительно однозначно. В третьих, инструменты разработчика в браузере показывают все примененные к элементу стили.
> Просто сейчас я вижу в исходниках уж ОЧЕНЬ много вложенных дивов
Ну так надо спрашивать у автора верстки, зачем он там столько дивов поставил. CSS тут не при чем. С таблицами ты в принципе меньше 3 элементов (table, tr, td) использовать не можешь.
Я много лет назад вроде верстал что-то на таблицах, но о переходе на CSS ни секунды не жалел, так как там все проще и логичнее. Но я в свое время разбирался в CSS основательно, вплоть до чтения спецификаций, может потому он мне понятным кажется.
>>1016199
Я ведь уже выше писал что можно использовать display: table. То есть любую верстку на дивах можно заставить себя вести как табличную. На дивах можно сделать то же самое. Плохо ты изучал CSS, если на это не обратил внимания. .
Потому я все же считаю, что ты не разобрался в CSS, отчего от тебе сложным и кажется. Что там сложного-то? Всего 6 или 7 способов позиционирования с относительно четко заданными правилами определения размеров.
Или может ты видел примеры какой-то плохой верстки. Но скорее всего все же проблема в том, что ты не разобрался.
> Ты задал сразу 3 размера ширины. Естественно сложно подсчитать. А если по отдельности - ширина 100%, и неопределённые ширины ибо px не проставлены.
А я имел в виду HTML-атрибут width, там px не указывают. Во времена табличной верстки ведь использовали именно HTML-атрибуты, тем более когда CSS толком не поддерживался.
Твой ответ про расчет ширины в любом случае неправильный. Для таблиц width задает только минимальную ширину, а это значит, что реальная ширина будет зависеть от содержимого ячеек и размера этого содержимого.
Пример, который это показывает: https://codepen.io/anon/pen/RgJZNJ
> И там всё гораздо сложнее с принципами каскадирования и приоритетов. Надо постоянно держать в уме, высчитывать приоритеты, все эти 1010 или 0101 или 111, что приоритетнее и какой стиль в итоге победит
Во-первых, считать не так сложно (id > класс > тэг > звездочка), во-вторых, а зачем к одному элементу применять конфликтующие стили? Лучше использовать подходы вроде БЭМ, где все относительно однозначно. В третьих, инструменты разработчика в браузере показывают все примененные к элементу стили.
> Просто сейчас я вижу в исходниках уж ОЧЕНЬ много вложенных дивов
Ну так надо спрашивать у автора верстки, зачем он там столько дивов поставил. CSS тут не при чем. С таблицами ты в принципе меньше 3 элементов (table, tr, td) использовать не можешь.
> Это вынужденная необходимость копипастить код?
А какой код копипастится? Строчка
messagesPromise.then ?
Это не копипаста. Если ты хочешь использовать значение в промисе, тебе всегда надо писать then.
> Вы имели ввиду если используются локальное хранилище?
Да, или если данные просто хранятся в памяти в переменной. Это не принципиально.
> При частичной выборке сообщений, при обновлении, если появляются новые сообщения, то пропадают старые вначале, потому что выбирается конкретное число данных. Как это починить?
А что значит "пропадают"? Если мы хотим отобразить последние 10 сообщений, то естественно, те, что шли раньше, не отображаются.
А так - можно менять логику работы API. Вместо того,чтобы запрашивать ровно 10 сообщений, можно запрашивать например сообщения от текущего до определенного.
Вообще, когда ты работаешь с "лентами" (список новостей, список писем), не стоит привязываться к номерам сообщений, так как при появлении новых сообщений эти номера меняются. Лучше привязываться к каким-то другим идентификаторам, например, id внутри треда или время.
То есть если ты запрашиваешь сервер "дай мне сообщения 1-10" то при приходе новых сообщений содержимое ответа поменяется. Но если ты говоришь "дай мне сообщения с id 1234 до 12678" или "дай мне сообщения с 12:00 до 18:00" то такой проблемы нет.
> Приходит на ум, только, не обновлять сообщения
Твой нынешний алгоритм, полностью обновлять область сообщений, наверно все равно не очень эффективный (особенно если сообщений много), так что потом можно будет подумать о его улучшении.
Вообще, хорошо бы потом протестировать производительность твоего кода, когда у нас много данных - тысячи контактов, тысячи сообщений в истории. Ну то есть можно будет встроить таймеры, которые будут мерять время выполнения той или иной операции.
>а зачем к одному элементу применять конфликтующие стили? Лучше использовать подходы вроде БЭМ
Ну это понятно тебе и мне. Но на вебаче говорили что бывают случаи в практике когда не всё так однозначно. Честно говоря я тут полностью с тобой согласен.
>Ну так надо спрашивать у автора верстки, зачем он там столько дивов поставил.
Спросил, говорит он не причём, это всё шаблонизатор так генерит.
Спасибо за столь развёрнутый ответ анон. Прямо приятно общаться.
>> Это вынужденная необходимость копипастить код?
>А какой код копипастится? Строчка messagesPromise.then ?
Нет, то что идёт внутри неё. Ведь мы же можем захотеть что-то сделать после получения данных, и этот код может быть довольно большим.
Возможно, это ничего страшного если в новой операции будут выполняться те же функции + ещё какие-то.
>> Вы имели ввиду если используются локальное хранилище?
>Да, или если данные просто хранятся в памяти в переменной. Это не принципиально.
Из коллбак функции нельзя же никуда сохранить данные, только пользоваться ими внутри.
Синхронный код
var data = getData();
doSomething(data);
Асинхронный
var promise = getData();
promise.then(function (data) {
doSomething(data);
}, function ...);
По моему разница не такая и большая.
> нельзя же никуда сохранить данные,
Во-первых, можно, во-вторых, даже если не сохранять, не вижу, в чем проблема дописать then.
>По моему разница не такая и большая.
Но она есть, я не могу сохранить данные в переменную и брать её снаружи коллбак функции. Если речь заходит об этом:
>>Разумеется, при поиске мы можем использовать другую функцию вместо getMesasges - например, getSavedMessages(), которая не шлет запрос на сервер, если данные уже получены.
>>Вы имели ввиду если используются локальное хранилище?
>Да, или если данные просто хранятся в памяти в переменной. Это не принципиально.
>> нельзя же никуда сохранить данные,
>Во-первых, можно, во-вторых, даже если не сохранять, не вижу, в чем проблема дописать then.
А, в смысле promise.then(...).then(...) ? Второй then получает получает обещание из первого, но что за обещание передает первый? Уже выполненное с полученными данными или то которое мы вызываем в самом начале, и мы просто вызываем его второй раз?
Вот сопсно видос с привязкой ко времени, где об этом рассказывается: https://youtu.be/XDRhO-v5et8?list=PL2J-r_iYvITcBi5CNjGbQ9J9_LTKWU-wi&t=3442
Я не особо шарю и решил спросить об этом в пхп чатике телеграма. В ответ услышал, что лектор дно т.к. пользуется виндой и у него офис не активирован и подготовленные выражения надо юзать. Сказал, что винда - это нихуя не доказательство, что лектор не прав от чего словил агр админа, мол тащи пруфы обратного или улетишь в бан. Лол, говорю, как я те пруф принесу если я сам не шарю, слово за слово, у админа загорелась жопа и я улетел в бан ибо посягнул на его религию. Посему спрашиваю здесь: в чем не прав лектор?
{ какой-то код по итогам которого $month будет рандомным целым числом }
$month = $month %10;
if ($month = 1) { $monthName = 'месяц' }
if ($month != 1 && $month <= 4) { $monthName = 'месяца' }
if ($month != 4 && $month <= 9) { $monthName = 'месяцев' }
как это можно без х3 if решить?
>не могу
Можешь!
Во-первых Промис придумали чтобы выполнение кода не застревало на участках, где результат может вернуться, может не вернуться, может исключение, но главное, мы не знаем сколько времени это займет. Вернется когда сможет, а мы пока работаем код дальше.
Вот тут https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#style-y080 все наглядно показано. При вызове функции activate() она записывает в уже объявленную переменную значение, и еще и непосредственно возврашает его.
А ты читал урок https://github.com/codedokode/pasta/blob/master/security/sql-injection.md ?
Там объясняется механизм SQL инъекции. Само по себе использование PDO ни от чего не защищает. Как предлагается защищаться от инъекций без подготовленных запросов?
Так-то да, одна из целей подготовленных запросов была в том, чтобы избежать необходимости заново строить план выполнения запроса для множества однотипных запросов, но для подстановки данных в запрос все равно нужна какая-то система, почему бы не использовать готовую?
$monthNames = ['месяц', 'месяца', 'месяца', 'месяца', 'месяцев','месяцев','месяцев','месяцев','месяцев','месяцев','месяцев'];
$name = $monthNames[$month];
Лол.
Недостаток описанного там подхода в том, что в переменной часть времени находится пустой массив, и только позже поступают данные. То есть создается видимость, что у нас данные доступны всегда, но на самом деле они там появятся позже, а может вообще не появятся.
Лучше везде работать с данными в асинхронном стиле, а не пытаться создать видимость синхронного кода.
> Промис придумали чтобы выполнение кода не застревало на участках,
Вообще не совсем так, чтобы выполнение кода не застревало, придумали асинхронный код, а промисы - это лишь способ упорядочить его (отойдя от коллбеков) и представление для ожидаемого в будущем результата.
лучше травка
Нет не читал. Есть что читать постоянно, к сожалению если все читать - жизни не хватит.
По сабжу:
Он говорит, что необходимо просто экранировать через quote(). Вся строка обрамится кавычками, а внутри кавычек все нарушающие запрос символы экранируются.
Как-то так:
$name = $pdo->quote($name);
$sql = "INSERT INTO users(name) VALUES($name)";
ща пробежался, там об этом вроде только одна строка в конце:
>это замусоривает код, и
Чем замусоривает?
>появляется вероятность, что кто-то подставит в запрос неэкранированное значение
Тут непонятно о чем речь?
"замусоривает" в том смысле, что приходится писать эти quote много раз, хотя можно было бы не писать.
Ну и глядя на SQL запрос, труднее понять, есть там инъекция или нет, так как надо проверять каждую вставленную переменную, откуда она пришла. А в случае с плейсхолдерами - не надо.
> Тут непонятно о чем речь?
Люди иногда пишут код по аналогии. Например, кто-то увидев, что данные вставляют через переменные, сделает так же, только без использования quote.
Ну и непонятно, что в принципе плохого в плейсхолдерах, они автору по религиозным соображениям не нравятся?
Я сейчас опишу алгоритм, что нужно сделать, а вы мне скажите где про это можно почитать.
1) Получаю в браузере страницу erohin.ru/think
2) На странице есть информация скрытая в конструкциях
<td class=example >
<span>"Как дела?"</span>
</td>
3) Как можно изменить аргумент span, при этом не скачивая страницу и делая это незаметно для пользователя. Например, вместо "Как дела?" написать "Что такое?".
В общем, что можете посоветовать? Пожалуйста, буду рад любому совету.
p.s
Естественно необходимо, чтобы была возможность сразу изменить информацию во всех td, которые имеют класс example. При том, что можно было бы учитывать значение той информации, что в span. То есть если в span написано "Пиздец", то его менять не нужно.
Выходит, что подготовленные выражение нужно использовать только для повышения читабельности кода? Что является вещью субъективной и вообще делом привычки.
Автору не нравится плейсхолдеры, потому-что их использование - тоже самое, что "микроскопом забивать гвозди" цитирую его - использовать не по назначению.
А выдержки из документации, где написано, что плейсхолдеры не предназначены для такого - у автора имеются?
Насчет субъективности - согласен, что дело субъективное. Одно дело когда ты теоретические вопросы рассматриваешь или делаешь учебные задания из 3 запросов, другое дело когда каждый день пишешь запросы многократно.
Я тут аргументов не вижу, если кому-то из упертости хочется писать лишний код - его право, если он работает один, пусть пишет.
>плейсхолдеры не предназначены для такого
Ты же сам этого начал тут >>1016961
но в документации вроде написано, что их нужно использовать, но я не пойму почему.
>Я тут аргументов не вижу, если кому-то из упертости хочется писать лишний код
аргумент например в том, что подготовленное выражение будет выполнятся дольше
> аргумент например в том, что подготовленное выражение будет выполнятся дольше
Нужно проверять тестом. Насколько дольше?
> Ты же сам этого начал
Я написал "одна из целей", так как точно не знаю.
А упорство автора выглядит именно как религиозное. Я написал уже выше, у тебя руки отвалятся каждый день эти quote многократно писать.
>Нужно проверять тестом. Насколько дольше?
да я хз, не критично наверное. Но то, что метод используется как костыль и производит выполнение кода дольше - убедительный аргумент.
>Я написал уже выше, у тебя руки отвалятся каждый день эти quote многократно писать
тогда так и нужно пояснять, что "писать меньше". Я то не против, меня просто волновал вопрос прав лектор или нет.
И забава в том, что когда я спрашивал в этом самом телеграмском чатике какую кодировку для БД выбрать мне советовал этотже админ - utf8mb4_general_ci, я спросил почему не utf8mb4_unicode_ci, которую везде советуют и которая менее глючная - он ответил, что с general быстрее работать будет. Выходит, что в этом случае скорость важна, а в формировании запросов - нет.
Разница в скорости работы между этими 2 кодировками не более 3 наносекунд, так что не думаю, что есть разница, какую использовать. Или для тебя это серьезная разница?
Для меня - нет, написал же, что мне советовали, те, кто счел это важным а скорость выполнения sql запроса - не важным
Двачую
Я так и не смог сделать задание с рандомом, без введения минимальных и максимальных значений отдельными пунктами, пришлось через rand делать.
Я уже понял, что это можно через js сделать. И раз я это понял без твоей помощи, то объясни как сделать так, чтобы мой скрипт выполнялся сразу после построения DOM-дерева, но при этом на экране браузера еще не было никакого изображения.
Например, есть картинка, которая отображается при загрузке сайта. Нужно ее убрать, например с помощью скрипта, но он ее убирает уже после того как она отобразилась на экране.
Тогда мой пак будет твоим.
Так есть она или нет?
Смотри, ты хочешь, чтобы сначала загрузился весь сайт со всем содержимым и только потом прошла смена картинки. Но при этом картинка с самого начала не должна отображаться у одного и отображаться у другого. Если я правильно понял, что ты хочешь, то это невозможно.
Ты можешь поставить в css свойство картинки так, чтобы она не отображалась. А после загрузки страницы скриптом изменить его. Или оставить пустой элемент, а уже потом вставить в него произвольный код с произвольными свойствами.
Ну, смотри.
Есть например сайт: https://sklad.autotrade.su/
В верху слева, есть отображение их логотипа. Элемент имеет свойство id = "logotip". Я пишу в скрипте:
var sel = document.getElementById("logotip");
sel.remove(1);
И элемент удаляется и картинка исчезает. Но это происходит уже поле того как картинка загрузится, отобразится для пользователя. Нужно же сделать, так, чтобы она исчезал еще до того момента как быть увиденным пользователем.
В общем, как я понимаю нужно где-то задать условие, чтобы мой скрипт имел больший приоритет для выполнения чем скрипты страницы, но при этом, чтобы уже было загружено dom-дерево, чтобы можно было удалить элемент.
Проще написать в css
#logotip{
display: none; / в oбычнoм сoстoянии логотип не дoлжнo быть видно/
}
А в js-скрипте
$(document).ready(function() { // срабатывает после полной загрузки страницы
$('#logotip').css('display', 'block'); // Показываем
});
Но я всё равно не понимаю, если тебе вообще не нужно показывать логотип, то может лучше его совсем убрать? Или тебе, всё же, нужно, чтобы он показывался при некоторых условиях? Если так, то в js можно эти условия прописать.
Как ты собрался добавить свой JS скрипт на чужой сайт? Ты понимаешь, как в общих чертах работает веб-сервер и веб-браузер?
По твоему вопросу - чтобы поменять логотип или текст, проще всего поменять его в коде на сервере, который его выводит. Если есть такая возможность.
Есть хостинг (на винде), на хостинге каталог с сайтом, на который дадены полные права системе, админам, владельцу каталога и ещё паре юзеров. Внутри есть подкаталоги, в том числе uploads. Права на подкаталоги наследуются от родительского, я проверял. И есть скрипт загрузки файлов в данный каталог путём move_uploaded_file().
Файл он вполне успешно закачивает, но права на файл устанавливаются, как полный доступ системе, админам, владельцу. И всё, все остальные учётки из доступа убраны. Почему так происходит? Мне нужно просто залить файл так, чтобы он наследовал права каталога.
if( move_uploaded_file( $file['tmp_name'], $uploaddir . basename($file['name']) ) ){
$files[] = realpath( $uploaddir . $file['name'] );
}
> Но она есть, я не могу сохранить данные в переменную и брать её снаружи коллбак функции.
Ты хочешь работать с кодом, как будто он синхронный. Только с синхронном коде можно так писать:
var data = getData();
doSmth(data);
Асинхронный код тем и отличается, что так написать нельзя. И это надо помнить.
Во-первых, ты можешь сделать сохранение данных в хранилище, если тебе так хочется, и например, сделать, чтобы данные искались в хранилище, если их там нет, то отправлялся бы запрос. Но тогда могут появиться какие-то другие проблемы, например, необходимость обновлять данные в этом хранилище.
Вот простой пример с сохранением данных в переменную:
var savedData = null;
var dataIsLoaded = false;
getDataASync.then(function (data) {
savedData = data;
dataIsLoaded = true;
});
Естественно, данные появятся в переменной не сразу, а только когда промис разрезолвится, потому перед их использованием надо проверять, что данные загружены. Вообще, в итоге может оказаться что проще их и не сохранять, а сохранять промис:
var promise = getDataAsync();
someButton.click(function () {
promise.then(function (data) {
displayData(data);
});
});
Во-вторых, я не понимаю, почему тебе так принципиально сохранить эти данные в переменную. В асинхронном коде логичнее может быть сохранять не данные, а промис с ними. А может, конечно, и нет, зависит от ситуации.
Если ты про поиск в списке контактов - то надо решить, будет это делаться локально или через запросы к серверу. И в зависимости от этого решать, как хранить данные. Вот пример с промисами:
var promise = getDataAsync();
searchForm.on('input', function () {
promise.then(function (data) {
var searchText = searchInput.val();
var result = findContacts(data, searchText);
displayContacts(result);
});
});
Мой код использует ранее загруженные с сервера данные и делает по ним поиск без повторной отправки запроса.
> А, в смысле promise.then(...).then(...) ? Второй then получает получает обещание из первого, но что за обещание передает первый? Уже выполненное с полученными данными или то которое мы вызываем в самом начале, и мы просто вызываем его второй раз?
Это ведь по сути вопрос по документации, изучал ли ты ее тщательно? Справедливости ради, я погуглил, мало где это нормально описано, потому дам такую ссылку: https://promisesaplus.com/
> then must return a promise [3.3].
> promise2 = promise1.then(onFulfilled, onRejected);
> If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x)
> If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
> ....
В then() ты указываешь 1 или 2 обработчика. then() возвращает новый промис, который рано или поздно переходит в одно из состояний:
- если (когда) обработчики выкинут исключение, реджектится с этим исключением
- если обработчики вернут какое-то значение, резолвится в него
- если обработчик вернет промис и тот резолвится или реджектится, повторяет его поведение
Соответственно then().then() задает последовательную цепочку действий, где второе действие будет выполнено только после завершения первого. Это, например, используется для асинхронных преобразований данных.
Если ты хочешь запросить данные с сервера один раз, а потом несколько раз их использовать, то очевидно, что это тебе не нужно. Тебе нужно что-то такое:
var p = getDataAsync();
p.then(...);
p.then(...);
p.then(...);
button.click(function () {
p.then(...);
});
> Но она есть, я не могу сохранить данные в переменную и брать её снаружи коллбак функции.
Ты хочешь работать с кодом, как будто он синхронный. Только с синхронном коде можно так писать:
var data = getData();
doSmth(data);
Асинхронный код тем и отличается, что так написать нельзя. И это надо помнить.
Во-первых, ты можешь сделать сохранение данных в хранилище, если тебе так хочется, и например, сделать, чтобы данные искались в хранилище, если их там нет, то отправлялся бы запрос. Но тогда могут появиться какие-то другие проблемы, например, необходимость обновлять данные в этом хранилище.
Вот простой пример с сохранением данных в переменную:
var savedData = null;
var dataIsLoaded = false;
getDataASync.then(function (data) {
savedData = data;
dataIsLoaded = true;
});
Естественно, данные появятся в переменной не сразу, а только когда промис разрезолвится, потому перед их использованием надо проверять, что данные загружены. Вообще, в итоге может оказаться что проще их и не сохранять, а сохранять промис:
var promise = getDataAsync();
someButton.click(function () {
promise.then(function (data) {
displayData(data);
});
});
Во-вторых, я не понимаю, почему тебе так принципиально сохранить эти данные в переменную. В асинхронном коде логичнее может быть сохранять не данные, а промис с ними. А может, конечно, и нет, зависит от ситуации.
Если ты про поиск в списке контактов - то надо решить, будет это делаться локально или через запросы к серверу. И в зависимости от этого решать, как хранить данные. Вот пример с промисами:
var promise = getDataAsync();
searchForm.on('input', function () {
promise.then(function (data) {
var searchText = searchInput.val();
var result = findContacts(data, searchText);
displayContacts(result);
});
});
Мой код использует ранее загруженные с сервера данные и делает по ним поиск без повторной отправки запроса.
> А, в смысле promise.then(...).then(...) ? Второй then получает получает обещание из первого, но что за обещание передает первый? Уже выполненное с полученными данными или то которое мы вызываем в самом начале, и мы просто вызываем его второй раз?
Это ведь по сути вопрос по документации, изучал ли ты ее тщательно? Справедливости ради, я погуглил, мало где это нормально описано, потому дам такую ссылку: https://promisesaplus.com/
> then must return a promise [3.3].
> promise2 = promise1.then(onFulfilled, onRejected);
> If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x)
> If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
> ....
В then() ты указываешь 1 или 2 обработчика. then() возвращает новый промис, который рано или поздно переходит в одно из состояний:
- если (когда) обработчики выкинут исключение, реджектится с этим исключением
- если обработчики вернут какое-то значение, резолвится в него
- если обработчик вернет промис и тот резолвится или реджектится, повторяет его поведение
Соответственно then().then() задает последовательную цепочку действий, где второе действие будет выполнено только после завершения первого. Это, например, используется для асинхронных преобразований данных.
Если ты хочешь запросить данные с сервера один раз, а потом несколько раз их использовать, то очевидно, что это тебе не нужно. Тебе нужно что-то такое:
var p = getDataAsync();
p.then(...);
p.then(...);
p.then(...);
button.click(function () {
p.then(...);
});
>>Ты понимаешь, как в общих чертах работает веб-сервер и веб-браузер?
К сожалению, только в общих чертах и понимаю.
>>Как ты собрался добавить свой JS скрипт на чужой сайт?
Никак, но можно сделать следующее https://habrahabr.ru/post/129343/
На винде скорее всего права на файл берутся из данных пользователя, от которого запущен сервер. А может от настроек родительских папок.
Если это Апач + PHP , то он по умолчанию работает от имени Network Service по моему.
А вообще, тут ответа нет?
https://technet.microsoft.com/en-us/library/dd277411.aspx
https://technet.microsoft.com/en-us/library/cc783530(v=ws.10).aspx
Поищи по слову inherit
PHP тут вряд ли поможет так как он не настраивает windows-права на файл.
Юзерскрипт устанавливается пользователем в свой браузер. То есть он будет работать только в твоем браузере (и у тех кто установил юзерскрипт). Тебе это нужно?
Да, всё так.
Блин, давай я объясню. Знаешь плагин для хрома addBlock Plus? Он убирает рекламу со страницы и при этом он убирает ее так, что ты при просмотре страницы ее не видишь.
У меня же получается как на пике. То есть, сначала изображение показывается, а потом исчезает.
И что? Ты объявил переменную, присвоил ей пустой массив, вызвал функцию, которая еще не получила значения, и сразу законсолил ещё пустую переменную. Чтобы в this.Messages появились значения, нужно их дождаться. Попробуй, например, не сразу вывести console.log(), а через Timeout() в секунд 5, и получишь свои значения в консоли.
Это подход ангуляра (не хочу утверждать, что он супер правильный и т.п.), там есть для этого свои служебные сервисы ($timeout).
$input_text = strip_tags($input_text);
$input_text = htmlspecialchars($input_text);
$input_text = mysql_escape_string($input_text);
return $input_text;
}
Вот простая функция. Но я все еще не понимаю, куда эта функция возвращает значение переменной? Куда блджат?
Ты что, каждый раз скроллишь до треда с нулевой вместо того, чтобы просто добавить тред в закладки?
>>1017516
Если объект инициализируется в другом объекте, то что-то не так с ООП в коде. Объект должен инициализироваться в конструкторе, это же неочевидно, когда нужно как-то конфигурировать объект уже после его создания. То, что в Yii наплевали на это - плохо, с них пример брать не стоит.
>>1017600
> куда эта функция возвращает значение переменной?
Туда, где нужен её результат. Пример:
$clearedInput = clearinput2('строка, которую нужно очистить');
Теперь в переменной $clearedInput очищенная строка. Стоить заметить, что это пример неправильной функции, не нужно экранировать/фильтровать всё подряд лишь бы было, это может привести к ошибкам - например у тебя на форуме в комментах можно постить код, тогда функция htmlspecialchars преобразует символы < > ' " в HTML-сущности, что превратит листинг пользователя в нечитаемое месиво, я видел такое на некоторых древних сайтах.
>>1017394
Я бы делал не валидатор формы, а валидатор значений. Например валидаторНомера, валидаторПароля, такой подход используется в symfony/validation
В твоём подходе недостаток в том, что если функция checkPass понадобится в ещё каких-то валидаторах форм, то тебе придётся её туда копипастить. Либо выкручиваться наследованием. Советую погуглить "Composition over inheritance"
Какие же программисты - уебаны. Именно хуесосы, просто кукарекают религиозные мантры патамушо так надо, а на деле, вопреки своим кукарекам, делают по другому
а что надо смотреть?
Но ведь там есть два похода.
1. Толстая модель - тонкий контроллер
2. Тонкая модель - толстый контроллер
Ну и давай начнем ловить тебя на пиздеже прямо с первой строчки кароч:
>Пересмотрел дохуя видеоуроков по фреймворкам
какие именно ты посмотрел, пости ссылки, будем анализировать вместе, че там и как.
Но ведь там есть два стула
Хз че это такое. Мне рассказывают про MVC и сразу делают по другому.
>Ну и давай начнем ловить тебя на пиздеже прямо с первой строчки кароч:
ой соси, быдло, я ебал тебе ссылочки искать, темболее что ты их смотреть не будешь, штук 5 разных авторов по ларавелю и 2-3 по Yii2 (но это уже давно было)
В целом мне отетили в одном чатике, борцуны тоже долго копротивлялись, пока один не признался как дела обстоят на самом деле:
Изначально в классическом варианте MVC вся логика, как и работа с БД должна быть в модели, контроллер выступает только связующим звеном. В ВЕБе контроллер стал содержать в себе всю логику, в модели методы для работы с БД (как, например, в codeigniter), в таких фреймворках, как yii/laravel и методы для работы с БД перенесли в контроллер, классы модели бывают вообще пустыми. Но не всегда. Поэтому в ларе в основном, вся логика у тебя в контроллере, валидация и т.п., также у тебя запросы к БД, например, с помощью query builder у тебя тоже в контроллере, но внутри фреймворка, в его ядре, все эти запросы проходят через модели, даже если они пустые. В двух словах как-то так
С MVC такая история. Оно вообще придумывалось когда-то для десктопных приложений с графическим интерфейсом, чтобы отделить бизнес-логику приложения от кода, отвечающего за вывод данных на экран. Потому если например взять какие-то исходные статьи про MVC то там вообще мало что общего будет с веб-фреймворками.
То, что сейчас на сервере называется MVC - оно немного другое, например, там используется пассивная, а не активная (умеющая сообщать об изменениях состояния) модель. Но идея та же, отделение кода приложения от кода взаимодействия с внешним миром (в случае веб-приложения - это код обработки HTTP запросов).
Вот у меня в уроке все расписано https://github.com/codedokode/pasta/blob/master/arch/mvc.md
Не все хорошо понимают MVC, там есть много заблуждений, например некоторые думают, что модель - это класс для работы с базой данных, или например что каждому контроллеру соответствует ровно одна модель, итд.
То, что где-то какой-то автор или какой-то пользователь чата не так понимает MVC - это проблема этого автора.
>>1017740
> В ВЕБе контроллер стал содержать в себе всю логику
В MVC ничего такого не говорится, это ты уже от себя придумываешь.
>>1017702
Нету 2 подходов, "толстый контроллер" это либо непонимание MVC , либо какая-то другая архитектура. MVC подразумевает, что бизнес-логика находится в модели и точка.
>>1017670
Это всего лишь видение авторов урока.
http://robintail.cz/
>Взаимодействие между виджетами обычно реализуется с помощью событий или коллбеков. То есть код снаружи ставит обработчик на событие выбора контакта и виджет его вызывает:
>То есть код снаружи ставит обработчик
А что если элемент появляется не сразу? Логично будет задать тогда при его выводе элемента (то есть обработчик будет частью отображения) или в контроллере?
View.handler() {
this.element.on(...);
}
View.showElement() {
//render element
this.handler();
}
или
Widget.handler() {
view.element.on(...);
}
Widget.run() {
view.showElement();
this.handler();
}
View.showElement() {
//render element
}
>Взаимодействие между виджетами обычно реализуется с помощью событий или коллбеков. То есть код снаружи ставит обработчик на событие выбора контакта и виджет его вызывает:
>То есть код снаружи ставит обработчик
А что если элемент появляется не сразу? Логично будет задать тогда при его выводе элемента (то есть обработчик будет частью отображения) или в контроллере?
View.handler() {
this.element.on(...);
}
View.showElement() {
//render element
this.handler();
}
или
Widget.handler() {
view.element.on(...);
}
Widget.run() {
view.showElement();
this.handler();
}
View.showElement() {
//render element
}
Или ещё 3-ий вариант - передать в метод вывода элемента сам обработчик:
Widget.handler() {
view.element.on(...);
}
View.showElement(handler) {
//render element
handler();
}
>Это всего лишь видение авторов урока
и авторов документации по фреймворку ;) Т.к. валидация полученых данных из формы в контроллере или отдельном классе, а не в моделе - это не MVC. А вдоках именно так и пишут. И сама логика фреймворков построена какраз так, что это необходимо делать в контроллере, а не в моделе. Впрочем как и всю логику, где угодно, но не в моделе.
>В MVC ничего такого не говорится
в MVC не говорится, а в веб фреймворках пиздящих, что они MVC - говорится
>некоторые думают, что модель - это класс для работы с базой данных
ну а как еще думать, если в веб-фреймворках моделью называется класс для работы с БД?
>То, что где-то какой-то автор или какой-то пользователь чата не так понимает MVC - это проблема этого автора.
MVC - Он понимает правильно, также правильно он понимает, что например, laravel или yii - это не MVC
Да и похуй. Многие кодеры с детства (с пеленок) учились говнокодить. Просто у них было больше времени на пробы и ошибки.
Пока мы катались на велосипедах, кидали шифер в костер, стреляли из напалечника рябиной, задроты писали тетрисы и пинпонги на спектруме.
Вот регулярка по проверке номера телефона. Рейт.
Доктор, насколько всё ужасно?
Если я начну делать замечания, то я решу задачу за тебя.
Ок, я попробую.
>/(^[+][\s()-]7|[^+]?8)([\s()-][\d]){10}[\s()-]$/
Метасимвол начала строки логичнее ставить в начале регулярного выражения, а не в подмаске (даже если он стоит в самом начале).
>/(^[+][\s()-]7|[^+]?8)([\s()-][\d]){10}[\s()-]$/
Не подойдёт для номеров которые не имеют + в начале.
>/(^[+][\s()-]7|[^+]?8)([\s()-][\d]){10}[\s()-]$/
Зачем здесь пустая подмаска?
>/(^[+][\s()-]7|[^+]?8)([\s()-][\d]){10}[\s()-]$/
Не подойдет для номеров которые не имеют пробела после +.
Подойдет для номеров которые имеют +- в начале.
>/(^[+][\s()-]7|[^+]?8)([\s()-][\d]){10}[\s()-]$/
Символ альтернативного выбора, вроде, должен быть между двумя подмасками.
>/(^[+][\s()-]7|[^+]?8)([\s()-][\d]){10}[\s()-]$/
Что делает этот метасимвол? Отсутствие плюса ноль или одного раза?
>/(^[+][\s()-]7|[^+]?8)([\s()-][\d]){10}[\s()-]$/
Подойдет только, если перед каждой цифрой из 10 цифр стоит пробел или - .
>/(^[+][\s()-]7|[^+]?8)([\s()-][\d]){10}[\s()-]$/
Зачем в конце строки искать пробелы или знаки - . Должны ли они быть вообще?
Проверять свои регулярные выражения можно тут https://regex101.com/
Если я начну делать замечания, то я решу задачу за тебя.
Ок, я попробую.
>/(^[+][\s()-]7|[^+]?8)([\s()-][\d]){10}[\s()-]$/
Метасимвол начала строки логичнее ставить в начале регулярного выражения, а не в подмаске (даже если он стоит в самом начале).
>/(^[+][\s()-]7|[^+]?8)([\s()-][\d]){10}[\s()-]$/
Не подойдёт для номеров которые не имеют + в начале.
>/(^[+][\s()-]7|[^+]?8)([\s()-][\d]){10}[\s()-]$/
Зачем здесь пустая подмаска?
>/(^[+][\s()-]7|[^+]?8)([\s()-][\d]){10}[\s()-]$/
Не подойдет для номеров которые не имеют пробела после +.
Подойдет для номеров которые имеют +- в начале.
>/(^[+][\s()-]7|[^+]?8)([\s()-][\d]){10}[\s()-]$/
Символ альтернативного выбора, вроде, должен быть между двумя подмасками.
>/(^[+][\s()-]7|[^+]?8)([\s()-][\d]){10}[\s()-]$/
Что делает этот метасимвол? Отсутствие плюса ноль или одного раза?
>/(^[+][\s()-]7|[^+]?8)([\s()-][\d]){10}[\s()-]$/
Подойдет только, если перед каждой цифрой из 10 цифр стоит пробел или - .
>/(^[+][\s()-]7|[^+]?8)([\s()-][\d]){10}[\s()-]$/
Зачем в конце строки искать пробелы или знаки - . Должны ли они быть вообще?
Проверять свои регулярные выражения можно тут https://regex101.com/
>Метасимвол начала строки логичнее ставить в начале регулярного выражения, а не в подмаске (даже если он она (т.е. подмаска) стоит в самом начале).
бля, не ту кинул вот нормальная
/^([+][\s()-]7|[^+]?8)([\s()-][\d]){10}$/
, на том сайте проверил , всё работает. Вопрос в том какое решение должно быть самым идеальным , например у тебя , ты скинь, чтобы можно было сравнить
ясно здесь звездочки не отображаются походу
вот рабочая ссылка https://pastebin.com/YZdm7QPf
test
Когда вижу регулярные выражения, сразу появляется желание сжечь их нахуй. Это норма, или я странный?
ты просто тупой. Пиши сайты на юкозе
>public function handle($request, Closure $next, ...$guards)
что значат 3 точки?
Шифровка - https://ideone.com/UJvHBU
На словах ты Лев Толстой - https://ideone.com/Xp3CMg
Айпад в кредит - https://ideone.com/xECgZm
Ты либо новичок, либо не очень адекватен. Для программиста совершенно нормально гуглить/читать на английском даже толком не осознавая этого. Тем более что технический английский очень простой, это не худ лит читать.
мимо-другой-анон
Так это только подтверждает их превосходство и недосягаемость.
для программиста, нормально писать код. Не очень адекватный, это когда есть документация на русском, а ты кидаешь на английском, чтобы подчеркнуть свою илитарность
Нет тут никакой элитарности, все программисты, с кем я общаюсь ИРЛ, гуглят вопросы по программированию на английском даже не задумываясь, это полезная привычка и обзаводиться ей нужно чем раньше - тем лучше. Документация на русском часто устаревает, либо неполная, либо с опечатками. Тебе помогли - скажи спасибо, никто не обязан нянчиться с тобой и подбирать наиболее удобную для тебя статью.
> а ты кидаешь на английском,
Я не кидал тебе ничего, читай мой предыдущий пост (он на русском) внимательней.
>>1017930
Есть просто более трудоспособные и волевые люди, умеющие ценить время, но ты конечно же для личного успокоения можешь списать всё на то, что они задроты без личной жизни и интересов.
>массив с названиями цветов. Количество разных цве-
тов должно быть больше четырех. Затем с помощью
PHP отобразить на странице четыре div одинаково-
го размера. Сделать так, чтобы при загрузке страни-
цы все четыре div заливались случайными цветами,
выбранными из массива. При этом, все четыре div
должны заливаться разными цветами. Ни один цвет
не должен повторяться.
Есть такой код:
<?php
$arr = array ('red','green','blue','yellow','black');
?>
<?php
$rand_keys = array_rand($arr, 4);
$a = $arr[$rand_keys[0]];
$b = $arr[$rand_keys[1]];
$c = $arr[$rand_keys[2]];
$d = $arr[$rand_keys[3]];
echo " <div style=\"width: 280px; height: 150px; background-color:$a; \">
<div style=\"width: 280px; height: 150px; background-color:$b; \">
<div style=\"width: 280px; height: 150px; background-color:$c; \">
<div style=\"width: 280px; height: 150px; background-color:$d; \">";
?>
Есть проблема: отображается 1 див вместо 4-х, и не меняются цвета.
Что не так?
>массив с названиями цветов. Количество разных цве-
тов должно быть больше четырех. Затем с помощью
PHP отобразить на странице четыре div одинаково-
го размера. Сделать так, чтобы при загрузке страни-
цы все четыре div заливались случайными цветами,
выбранными из массива. При этом, все четыре div
должны заливаться разными цветами. Ни один цвет
не должен повторяться.
Есть такой код:
<?php
$arr = array ('red','green','blue','yellow','black');
?>
<?php
$rand_keys = array_rand($arr, 4);
$a = $arr[$rand_keys[0]];
$b = $arr[$rand_keys[1]];
$c = $arr[$rand_keys[2]];
$d = $arr[$rand_keys[3]];
echo " <div style=\"width: 280px; height: 150px; background-color:$a; \">
<div style=\"width: 280px; height: 150px; background-color:$b; \">
<div style=\"width: 280px; height: 150px; background-color:$c; \">
<div style=\"width: 280px; height: 150px; background-color:$d; \">";
?>
Есть проблема: отображается 1 див вместо 4-х, и не меняются цвета.
Что не так?
http://htmlbook.ru/html/div
>Закрывающий тег
>Обязателен.
>не меняются цвета
Посмотри что у тебя в переменных находиться.
>Закрывающий тег
Ох, точно, спасибо.
>Посмотри что у тебя в переменных находиться.
Вроде все нормально теперь.
>но ты конечно же для личного успокоения можешь списать всё на то, что они задроты без личной жизни и интересов.
Успокаивайся - не успокаивайся, а более трудолюбивым и волевым не станешь.
Обычно биография таких трудоголиков выглядит так, как будто они просто плыли по течению и у них особого выбора не было. Папка притащил пеку юному шизоиду, друзей нет, физуха так себе, мультики заебали, хуле еще делать? И понеслось.
Они, конечно, молодцы и достойны увожения, но не надо тут заливать про их особенное трудолюбие и ницшеанскую волю.
<?
$firstofmonth = mktime(0, 0, 0, $m, 1, 2017);
$dayofweek = date("w",$firstofmonth);
?>
Проблема в том, что если месяц выпадает с воскресенья, то в $dayofweek записывается 0.
Чем фиксить, чтоб выдавало 7?
Я, конечно, написал костыль
if ($dayofweek == 0)
{
$dayofweek= 7;
}
но должны же быть другие способы?
Мой перевод на человеческий язык (просьба пофиксить, если я что-то не так понял):
ORM — api для работы с базой данных.
Фреймворк, предоставляющий функционал для абстрагирования от бд (позволяющий вместо sql-кода использовать вызовы готовых методов).
ORM - это отображение (создание соответствия между) строками таблицы и объектами в приложении. То есть это штука (часть кода), которая позволяет загружать записи из БД в виде объектов. И сохранять изменения в этих объектах обратно в БД.
Хороший ORM старается создать видимость, что никакой базы данных нет, а просто есть куча связанных объектов, с которыми мы работаем так, как будто бы они всегда были в памяти.
Соотвтетсвенно, код, работающий с ORM, в теории может использоваться и без базы данных (например в тестах), работая просто с объектами. На практике, впрочем, такая сильная степень абстракции трудно реализуема. "Забыть" про наличие БД не получится.
Есть 2 основных варианта реализации ORM: Active Record и Data Mapper. Описание с примерами есть в моем уроке https://github.com/codedokode/pasta/blob/master/db/patterns-oop.md
А API к базе данных - это библиотеки вроде PDO или mysqli.
>>1018642
Можно посмотреть мануал функции date, может там есть какая-то буква, которая возвращает день в нужном виде, если нет то использовать if.
В некоторых странах неделя начинается с воскресенья, может в этом дело.
>>1018531
Привычка может и полезная, но если есть документация на русском и она не плохая, можно и на нее ссылку дать.
Ничего плохого ведь в русскоязычной документации нет.
>>1018165
> Шифровка - https://ideone.com/UJvHBU
Все правильно
> На словах ты Лев Толстой - https://ideone.com/Xp3CMg
> $wordNumber = mt_rand(0, 6);
Здесь проблема в том, что надо вручную считать эти цифры, и менять их, если исходный массив изменился. Лучше сделать, чтобы цифры считались сами, либо использовать array_rand().
> Айпад в кредит - https://ideone.com/xECgZm
Все верно.
>>1018094
Нужно просто разобраться в них, в том, что ты не понимаешь, и тогда ты удивишься, какой это мощный и удобный инструмент для работы с текстом. В моем текстовом редакторе кстати есть поиск с использованием регулярных выражений, и он иногда оказывается полезен.
>>1018642
Можно посмотреть мануал функции date, может там есть какая-то буква, которая возвращает день в нужном виде, если нет то использовать if.
В некоторых странах неделя начинается с воскресенья, может в этом дело.
>>1018531
Привычка может и полезная, но если есть документация на русском и она не плохая, можно и на нее ссылку дать.
Ничего плохого ведь в русскоязычной документации нет.
>>1018165
> Шифровка - https://ideone.com/UJvHBU
Все правильно
> На словах ты Лев Толстой - https://ideone.com/Xp3CMg
> $wordNumber = mt_rand(0, 6);
Здесь проблема в том, что надо вручную считать эти цифры, и менять их, если исходный массив изменился. Лучше сделать, чтобы цифры считались сами, либо использовать array_rand().
> Айпад в кредит - https://ideone.com/xECgZm
Все верно.
>>1018094
Нужно просто разобраться в них, в том, что ты не понимаешь, и тогда ты удивишься, какой это мощный и удобный инструмент для работы с текстом. В моем текстовом редакторе кстати есть поиск с использованием регулярных выражений, и он иногда оказывается полезен.
Проблема вот тут:
> [^+]?8
[^+] заматчит любую цифру или букву, или символ, кроме знака "плюс".
Вообще, в регулярках сложно писать условия вроде "не должно быть таких-то символов". Обычно пишут "должны быть такие-то символы". Не надо писать "перед восьмеркой должен быть не плюс", надо просто писать "начало строки, за ним восьмерка".
В остальном регулярка хорошая.
>>1018009
(^a|b) - здесь символ ^ относится только к варианту с a, но не относится к варианту с b, надо писать либо (^a|^b), либо ^(a|b)
>>1017801
> Т.к. валидация полученых данных из формы в контроллере или отдельном классе, а не в моделе - это не MVC. А вдоках именно так и пишут.
Авторы документации могут писать что угодно, оригинальный MVC для GUI приложений был придуман вообще в 80-е годы, задолго до появления PHP фреймворков.
Также, не путаешь ли ты ничего? Может быть из контроллера просто вызывается метод валидации, который находится в модели?
> или отдельном классе,
Ну так этот отдельный класс может быть частью модели
> ну а как еще думать, если в веб-фреймворках моделью называется класс для работы с БД?
А где, по-твоему, тогда будет модель в приложениях, которые не используют базу данных? Бред же.
Я уже много раз по моему написал, что идея MVC - это идея отделения бизнес-логики приложения от кода взаимодействия с внешним миром (ввод и вывод данных).
>>1017784
> А что если элемент появляется не сразу? Логично будет задать тогда при его выводе элемента (то есть обработчик будет частью отображения) или в контроллере?
Во-первых, "установка обработчика" не обязательно значит "установка обработчика на DOM элемент". В виджете может быть своя система событий, и ставится обработчик на внутреннее событие виджета, а не на DOM элемент.
Во-вторых, можно запретить ставить обработчики до вывода виджета.
В-третьих, можно в виджете создавать неприкрепленное к документу дерево DOM, и тогда на него можно будет вешать событие, даже до того, как мы вставим его в документ.
То есть вариантов решения много, нужно выбирать тот, который лучше подойдет к ситуации.
> Или ещё 3-ий вариант - передать в метод вывода элемента сам обработчик:
Это нелогично, получается что не задав обработчик, мы не можем отобразить виджет.
Проблема вот тут:
> [^+]?8
[^+] заматчит любую цифру или букву, или символ, кроме знака "плюс".
Вообще, в регулярках сложно писать условия вроде "не должно быть таких-то символов". Обычно пишут "должны быть такие-то символы". Не надо писать "перед восьмеркой должен быть не плюс", надо просто писать "начало строки, за ним восьмерка".
В остальном регулярка хорошая.
>>1018009
(^a|b) - здесь символ ^ относится только к варианту с a, но не относится к варианту с b, надо писать либо (^a|^b), либо ^(a|b)
>>1017801
> Т.к. валидация полученых данных из формы в контроллере или отдельном классе, а не в моделе - это не MVC. А вдоках именно так и пишут.
Авторы документации могут писать что угодно, оригинальный MVC для GUI приложений был придуман вообще в 80-е годы, задолго до появления PHP фреймворков.
Также, не путаешь ли ты ничего? Может быть из контроллера просто вызывается метод валидации, который находится в модели?
> или отдельном классе,
Ну так этот отдельный класс может быть частью модели
> ну а как еще думать, если в веб-фреймворках моделью называется класс для работы с БД?
А где, по-твоему, тогда будет модель в приложениях, которые не используют базу данных? Бред же.
Я уже много раз по моему написал, что идея MVC - это идея отделения бизнес-логики приложения от кода взаимодействия с внешним миром (ввод и вывод данных).
>>1017784
> А что если элемент появляется не сразу? Логично будет задать тогда при его выводе элемента (то есть обработчик будет частью отображения) или в контроллере?
Во-первых, "установка обработчика" не обязательно значит "установка обработчика на DOM элемент". В виджете может быть своя система событий, и ставится обработчик на внутреннее событие виджета, а не на DOM элемент.
Во-вторых, можно запретить ставить обработчики до вывода виджета.
В-третьих, можно в виджете создавать неприкрепленное к документу дерево DOM, и тогда на него можно будет вешать событие, даже до того, как мы вставим его в документ.
То есть вариантов решения много, нужно выбирать тот, который лучше подойдет к ситуации.
> Или ещё 3-ий вариант - передать в метод вывода элемента сам обработчик:
Это нелогично, получается что не задав обработчик, мы не можем отобразить виджет.
> классы модели бывают вообще пустыми.
Справедливости ради, они ведь унаследованы от базового класса с кучей кода, так что совсем пустыми их назвать нельзя. Но вообще, у меня ощущение, что ты понимаешь идею MVC (неужели благодаря моему уроку?), просто у тебя какие-то замечания к документации фреймворков. Тогда тебе лучше посмотреть ее внимательно, и если там действительно что-то нелогичное, задать вопрос в issues на гитхабе например. Правда, высока вероятность что тебя отфутболят или предложат сделать pull request.
>>1017655
Я хочу добавить, что идеи "очистки" переменных в общем плохие. Если у тебя переменная может быть только числом, то, конечно, можно ее "очистить" с помощью того же intval. Но например от SQL-инъекций или XSS так защищаться нельзя, так как переменные получаются в одном месте кода, а исопльзуются в другом, и трудно проверить, откуда они пришли и правильно ли там все экранировано, экранировать данные нужно в том месте, где они используются.
Да и при работе с "очищенными" данными могут быть проблемы, вот пример:
$value = htmlspecialchars($_GET['value']); // например, "<a>&" -> "& lt;a& gt;"
$value = mb_substr($value, 0, 6); // & lt;a& - в итоге выведется совсем не то, что нужно
Или еще пример:
$value = $mysqli->real_escape_string($_GET['value']); // "aaa'" -> "aaa\'"
$value = mb_substr($value, 0, 4); // "aaa\'" -> "aaa\" - при подстановке в запрос это вызовет ошибку, так как бекслеш заэкранирует идущую дальше кавычку
Так что "очищайте" только в случаях вроде целых чисел или данных, где гарантированно может быть ограниченный набор символов и можно смело вырезать все лишнее. То есть такие варианты допустимы:
$value = intval($value); // пропустит целое число
$value = preg_replace("/[^a-z]/u", '', $value); // пропускает только латиницу
>>1017600
Это плохая и неправильная функция и ее лучше вообще не использовать. Ну и еще тебе стоит почитать теорию про функции и про работу оператора return.
> классы модели бывают вообще пустыми.
Справедливости ради, они ведь унаследованы от базового класса с кучей кода, так что совсем пустыми их назвать нельзя. Но вообще, у меня ощущение, что ты понимаешь идею MVC (неужели благодаря моему уроку?), просто у тебя какие-то замечания к документации фреймворков. Тогда тебе лучше посмотреть ее внимательно, и если там действительно что-то нелогичное, задать вопрос в issues на гитхабе например. Правда, высока вероятность что тебя отфутболят или предложат сделать pull request.
>>1017655
Я хочу добавить, что идеи "очистки" переменных в общем плохие. Если у тебя переменная может быть только числом, то, конечно, можно ее "очистить" с помощью того же intval. Но например от SQL-инъекций или XSS так защищаться нельзя, так как переменные получаются в одном месте кода, а исопльзуются в другом, и трудно проверить, откуда они пришли и правильно ли там все экранировано, экранировать данные нужно в том месте, где они используются.
Да и при работе с "очищенными" данными могут быть проблемы, вот пример:
$value = htmlspecialchars($_GET['value']); // например, "<a>&" -> "& lt;a& gt;"
$value = mb_substr($value, 0, 6); // & lt;a& - в итоге выведется совсем не то, что нужно
Или еще пример:
$value = $mysqli->real_escape_string($_GET['value']); // "aaa'" -> "aaa\'"
$value = mb_substr($value, 0, 4); // "aaa\'" -> "aaa\" - при подстановке в запрос это вызовет ошибку, так как бекслеш заэкранирует идущую дальше кавычку
Так что "очищайте" только в случаях вроде целых чисел или данных, где гарантированно может быть ограниченный набор символов и можно смело вырезать все лишнее. То есть такие варианты допустимы:
$value = intval($value); // пропустит целое число
$value = preg_replace("/[^a-z]/u", '', $value); // пропускает только латиницу
>>1017600
Это плохая и неправильная функция и ее лучше вообще не использовать. Ну и еще тебе стоит почитать теорию про функции и про работу оператора return.
Нет, в этом нет смысла, так как функция всегда вернет тот же объект, что в нее передан. Нет смысла делать возврат значения, если оно и так известно.
>>1017466
Надо старые посты проверить. Кому я не ответил, напомните о себе.
>>1017394
Можно писать, почему бы и нет. Но мне кажется, ты неудачно выбрал способ возврата значения - true/строка, так как они оба при приведении к boolean дают true. Лучше выбрать вариант null/ст рока например или возвращать из функции 2 значения: $isValid и $errorText.
Ну и в пароле нужно разрешать больше допустмых символов.
>>1017380
Можно попробовать порыться в коде плагина или документации по написанию расширений. Скорее всего там либо используется изменение DOM на раннем этапе, до отображения, либо какой-то не-DOM метод для фильтрации данных до того, как они попадут на страницу.
>>1017077
Не знаю, есть ли такие уроки, но могу попробовать ответить.
> что в какой версии изменялось
Документация, what's new in PHP7.0
> почему используется точка при конкатенации строк,
Подозреваю, из какого-то другого языка взяли (из Перл может быть? не помню, как там конкатенация делается). Или может, чтобы путаницы с '+' не было.
> почему сделали костыль множественного наследования в виде трейтов, вместо реального множественного наследования
Множественное наследование само по себе очень спорная фича и нигде особо не применяется. Одна из проблем - если ты добавишь какой-то метод в один из базовых классов, который совпадает по названию с методом в другом базовом классе, внезапно сломается класс-наследник из-за конфликта. То есть проблем очень много, а ценность непонятна. Наследование вообще не так и часто используется (и с одиночным наследованием при желании можно такой код нагородить, что долго придется ломать голову), а трейты, наверно, еще реже.
На практике надо писать как можно более простой код, а не городить паттерн на паттерне, потому что разбираться и поддерживать его будет тяжело. У того, кому надо добавить фичу в твой код, нет времени на то, чтобы изучать твою гениальную архитектуру, даже если она документирована (а скорее всего нет).
Нет, в этом нет смысла, так как функция всегда вернет тот же объект, что в нее передан. Нет смысла делать возврат значения, если оно и так известно.
>>1017466
Надо старые посты проверить. Кому я не ответил, напомните о себе.
>>1017394
Можно писать, почему бы и нет. Но мне кажется, ты неудачно выбрал способ возврата значения - true/строка, так как они оба при приведении к boolean дают true. Лучше выбрать вариант null/ст рока например или возвращать из функции 2 значения: $isValid и $errorText.
Ну и в пароле нужно разрешать больше допустмых символов.
>>1017380
Можно попробовать порыться в коде плагина или документации по написанию расширений. Скорее всего там либо используется изменение DOM на раннем этапе, до отображения, либо какой-то не-DOM метод для фильтрации данных до того, как они попадут на страницу.
>>1017077
Не знаю, есть ли такие уроки, но могу попробовать ответить.
> что в какой версии изменялось
Документация, what's new in PHP7.0
> почему используется точка при конкатенации строк,
Подозреваю, из какого-то другого языка взяли (из Перл может быть? не помню, как там конкатенация делается). Или может, чтобы путаницы с '+' не было.
> почему сделали костыль множественного наследования в виде трейтов, вместо реального множественного наследования
Множественное наследование само по себе очень спорная фича и нигде особо не применяется. Одна из проблем - если ты добавишь какой-то метод в один из базовых классов, который совпадает по названию с методом в другом базовом классе, внезапно сломается класс-наследник из-за конфликта. То есть проблем очень много, а ценность непонятна. Наследование вообще не так и часто используется (и с одиночным наследованием при желании можно такой код нагородить, что долго придется ломать голову), а трейты, наверно, еще реже.
На практике надо писать как можно более простой код, а не городить паттерн на паттерне, потому что разбираться и поддерживать его будет тяжело. У того, кому надо добавить фичу в твой код, нет времени на то, чтобы изучать твою гениальную архитектуру, даже если она документирована (а скорее всего нет).
Зачем бы мы ставили его в ОП пост? конечно.
>>1017024
Вообще, неудобно же без плейсхолдеров. Ну сравни сам:
$count = $dbal->fetchOne('SELECT COUNT(x) FROM y WHERE z = ? AND w = ?', [$z, $w]);
$rows = $dbal->fetchAll('SELECT a, b, c FROM t WHERE x = :x AND y = :y', ['x' => $x, 'y' => $y]);
примеры взяты для этой библиотеки http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/data-retrieval-and-manipulation.html
И сравни этот код с идеей везде писать quote руками. Как-то у меня называть это "костылем" язык не повернется, по моему, хорошее решение. При этом как оно устроено внутри - использует нативные плейсхолдеры или само делает quote и отправляет в БД уже собранный запрос - для меня не особо принципиально. Нативные плейсхолдеры неэффективны только при вставке большого числа параметров (сотни, тысячи, например IN(?, ?, ?...) с тысячью значений).
>>1016970
На что угодно, впрочем это можно сделать и без изучения PHP.
>>1016199
Недавно столкнулся еще с одной особенностью таблиц (про которую, к своему стыду, не знал или забыл): к ячейкам таблиц (и элементам с display: table-cell) не применяется max-width/min-width, по крайней мере если он выражен в процентах:
- https://stackoverflow.com/questions/8465385/how-can-i-set-the-max-width-of-a-table-cell-using-percentages
- https://www.w3.org/TR/CSS21/visudet.html#propdef-max-width
> In CSS 2.1, the effect of 'min-width' and 'max-width' on tables, inline tables, table cells, table columns, and column groups is undefined.
То есть тебе придется внутрь ячейки вложить еще див и уже на нем задавать max-width, а если не задать max-width, то слишком широкие элементы (картинки или длинные слова) растянут ячейку. Вот тебе и простая верстка
Также, насколько я помню, к ячейкам таблицы нельзя применять overflow, они не могут служить базой для абсолютного позиционирования. То есть у них есть куча ограничений и не определенного в стандарте поведения.
А то, что в твоем коде дивов много - я думаю, что в табличной верстке там было бы примерно такое же количество или больше тегов таблиц.
Но главная причина, конечно, семантика, теги должны обозначать то, что в них написано, и table должен использоваться для разметки реальных таблиц.
Зачем бы мы ставили его в ОП пост? конечно.
>>1017024
Вообще, неудобно же без плейсхолдеров. Ну сравни сам:
$count = $dbal->fetchOne('SELECT COUNT(x) FROM y WHERE z = ? AND w = ?', [$z, $w]);
$rows = $dbal->fetchAll('SELECT a, b, c FROM t WHERE x = :x AND y = :y', ['x' => $x, 'y' => $y]);
примеры взяты для этой библиотеки http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/data-retrieval-and-manipulation.html
И сравни этот код с идеей везде писать quote руками. Как-то у меня называть это "костылем" язык не повернется, по моему, хорошее решение. При этом как оно устроено внутри - использует нативные плейсхолдеры или само делает quote и отправляет в БД уже собранный запрос - для меня не особо принципиально. Нативные плейсхолдеры неэффективны только при вставке большого числа параметров (сотни, тысячи, например IN(?, ?, ?...) с тысячью значений).
>>1016970
На что угодно, впрочем это можно сделать и без изучения PHP.
>>1016199
Недавно столкнулся еще с одной особенностью таблиц (про которую, к своему стыду, не знал или забыл): к ячейкам таблиц (и элементам с display: table-cell) не применяется max-width/min-width, по крайней мере если он выражен в процентах:
- https://stackoverflow.com/questions/8465385/how-can-i-set-the-max-width-of-a-table-cell-using-percentages
- https://www.w3.org/TR/CSS21/visudet.html#propdef-max-width
> In CSS 2.1, the effect of 'min-width' and 'max-width' on tables, inline tables, table cells, table columns, and column groups is undefined.
То есть тебе придется внутрь ячейки вложить еще див и уже на нем задавать max-width, а если не задать max-width, то слишком широкие элементы (картинки или длинные слова) растянут ячейку. Вот тебе и простая верстка
Также, насколько я помню, к ячейкам таблицы нельзя применять overflow, они не могут служить базой для абсолютного позиционирования. То есть у них есть куча ограничений и не определенного в стандарте поведения.
А то, что в твоем коде дивов много - я думаю, что в табличной верстке там было бы примерно такое же количество или больше тегов таблиц.
Но главная причина, конечно, семантика, теги должны обозначать то, что в них написано, и table должен использоваться для разметки реальных таблиц.
Тут есть варианты:
- если мышь привязывается к полю с момента создания и никогда от него не отвязывается, и поле обящательно для функционирования мыши, логично поле передать в конструктор:
$mouse = new Mouse($field);
- если мышь можно помещать и снимать с полей, логично сделать методы placeOnField($field) и removeFromField()
- наконец, если объект не нужен для функционирования мыши в общем, а нужен только в одном методе, может его стоит передавать аргументом в этот метод
Тут я думаю подойдет вариант 1 или 2.
> Или мышь как бы знает о существовании поля по умолчанию? Как глобального объекта?
Это очень плохая идея, такие "неявные" знания. Код получается запутанным. Лучше все передавать явно.
> Опять же как сходить? Получается что мышь может изменять такой объект как поле?
Это зависит от того, как хранится информация о расположении. Если она хранится в свойствах мыши, то ей просто достаточно изменить свои свойтсва. Если в поле - попросить его переместить себя. Если и там и там, то тоже есть варианты - мышь может уведомлять поле об изменении координат, или же поле может хранить их копию и перепроверять, не изменились ли они.
Выбирать нужно тот вариант, который удобнее для решения задачи и обеспечивает простой, понятный код. Иногда еще бывают другие критерии - например, производительность.
Самый простой вариант - хранить координаты только в животном, тогда и обновлять их нетрудно.
>>1015515
> Это с чего бы? Все отступы делать табами.
Табы и пробелы визуально неразличимы, а значит если кто-то впишет туда пробелы, то сразу он сам или кто-то еще это не заметит и значит в твоем коде в итоге будет их смесь.
>>1015274
>>Лучше было назвать $min_path, $min_length или как-то так
> А она разве не относится к временной переменной.
Да, но по моему было бы понятнее с более подробным названием, не надо было бы изучать цикл, чтобы понять, что в нее сохраняется.
> Но как я понимаю большинство все равно кемелкейс использует и лучше его использовать?
Не знаю насчет большинства, я опросы не проводил, но Симфони и Доктрина используют кемелкейс. Как и код на java в Андроид например. А Питон и Руби - подчеркивания.
Насчет имен в базе данных - да, их почти всегда пишут с подчеркиваниями, но я видел и варианты с кемелкейсом.
> Ну тогда получается и на получение массива с вершинами по которым мы идем надо свою функцию писать, и на каждый шаг писать свою функцию, а это долго.
Да не придется, я думаю. Какие еще функции? Надо всего лишь вместо 1 переменной сделать несколько отдельных.
> if (array_key_exists($start_point, $paths)) {
> if (array_key_exists($end_point, $paths)) {
Тут надо использовать отрицание:
if (!array_key_exists) {
...
return false;
}
if (!array_key_exists) {
...
return false;
}
Так структура (вложенность) кода получается проще. Ну и пустой блок в if это вообще странно.
При ошибке во входных данных в таких случаях лучше всего выбрасывать исключение, о которых ты потом наверно позже как-нибудь прочитаешь.
Функции у тебя все равно не очень логичные, ну например, get_shortest_path() возвращает сложный массив с кучей лишних данных. Зачем это нужно? Логичнее возвращать только то, что требуется, то есть искомый путь.
И опять же, нелогично, что ты в исходных данных о маршрутах добавляешь поля вроде shortest_path, которые к этой информации не имеют никакого отношения.
Когда кто-то захочет разобраться в твоем коде, его это запутает. Почему на вход функции надо подавать массив с каким-то дополнительными полями? Почему функция возвращает кучу лишних данных? Это плохо спроектированная функция. Должно быть проще:
искомый путь = get_shortest_path(список маршрутов, старт, финиш);
Искомый путь логично представить, например, в виде массива названий станций.
Ну представь, если бы функция сложения двух чисел возвращала вместо ответа какой-то сложный массив, в котором надо еще найти этот ответ.
Я вижу, что у тебя пока не очень хорошо получается разбивать код на функции, и думаю, что тебе надо еще немного поработать над этим.
Тут есть варианты:
- если мышь привязывается к полю с момента создания и никогда от него не отвязывается, и поле обящательно для функционирования мыши, логично поле передать в конструктор:
$mouse = new Mouse($field);
- если мышь можно помещать и снимать с полей, логично сделать методы placeOnField($field) и removeFromField()
- наконец, если объект не нужен для функционирования мыши в общем, а нужен только в одном методе, может его стоит передавать аргументом в этот метод
Тут я думаю подойдет вариант 1 или 2.
> Или мышь как бы знает о существовании поля по умолчанию? Как глобального объекта?
Это очень плохая идея, такие "неявные" знания. Код получается запутанным. Лучше все передавать явно.
> Опять же как сходить? Получается что мышь может изменять такой объект как поле?
Это зависит от того, как хранится информация о расположении. Если она хранится в свойствах мыши, то ей просто достаточно изменить свои свойтсва. Если в поле - попросить его переместить себя. Если и там и там, то тоже есть варианты - мышь может уведомлять поле об изменении координат, или же поле может хранить их копию и перепроверять, не изменились ли они.
Выбирать нужно тот вариант, который удобнее для решения задачи и обеспечивает простой, понятный код. Иногда еще бывают другие критерии - например, производительность.
Самый простой вариант - хранить координаты только в животном, тогда и обновлять их нетрудно.
>>1015515
> Это с чего бы? Все отступы делать табами.
Табы и пробелы визуально неразличимы, а значит если кто-то впишет туда пробелы, то сразу он сам или кто-то еще это не заметит и значит в твоем коде в итоге будет их смесь.
>>1015274
>>Лучше было назвать $min_path, $min_length или как-то так
> А она разве не относится к временной переменной.
Да, но по моему было бы понятнее с более подробным названием, не надо было бы изучать цикл, чтобы понять, что в нее сохраняется.
> Но как я понимаю большинство все равно кемелкейс использует и лучше его использовать?
Не знаю насчет большинства, я опросы не проводил, но Симфони и Доктрина используют кемелкейс. Как и код на java в Андроид например. А Питон и Руби - подчеркивания.
Насчет имен в базе данных - да, их почти всегда пишут с подчеркиваниями, но я видел и варианты с кемелкейсом.
> Ну тогда получается и на получение массива с вершинами по которым мы идем надо свою функцию писать, и на каждый шаг писать свою функцию, а это долго.
Да не придется, я думаю. Какие еще функции? Надо всего лишь вместо 1 переменной сделать несколько отдельных.
> if (array_key_exists($start_point, $paths)) {
> if (array_key_exists($end_point, $paths)) {
Тут надо использовать отрицание:
if (!array_key_exists) {
...
return false;
}
if (!array_key_exists) {
...
return false;
}
Так структура (вложенность) кода получается проще. Ну и пустой блок в if это вообще странно.
При ошибке во входных данных в таких случаях лучше всего выбрасывать исключение, о которых ты потом наверно позже как-нибудь прочитаешь.
Функции у тебя все равно не очень логичные, ну например, get_shortest_path() возвращает сложный массив с кучей лишних данных. Зачем это нужно? Логичнее возвращать только то, что требуется, то есть искомый путь.
И опять же, нелогично, что ты в исходных данных о маршрутах добавляешь поля вроде shortest_path, которые к этой информации не имеют никакого отношения.
Когда кто-то захочет разобраться в твоем коде, его это запутает. Почему на вход функции надо подавать массив с каким-то дополнительными полями? Почему функция возвращает кучу лишних данных? Это плохо спроектированная функция. Должно быть проще:
искомый путь = get_shortest_path(список маршрутов, старт, финиш);
Искомый путь логично представить, например, в виде массива названий станций.
Ну представь, если бы функция сложения двух чисел возвращала вместо ответа какой-то сложный массив, в котором надо еще найти этот ответ.
Я вижу, что у тебя пока не очень хорошо получается разбивать код на функции, и думаю, что тебе надо еще немного поработать над этим.
> как мне написать у мышки метод, который будет её двигать?
Это зависит от того, где хранится информация о положении мыши. Если информация хранится в свойствах самой мыши, достаточно их изменить.
>>1014880
Наверно, нет. А вообще, ты оф. сайт смотреть не пробовал? Там по моему много информации, хотя и нелегко найти нужную: https://aws.amazon.com/ru/whitepapers/?nc1=f_cc
"облако" - это просто сервера, хранилища или вычислительные мощности, которые принадлежат другой компании и сдаются в пользование. Хостинг для сайта - это тоже в какой-то мере "облако", просто это слово тогда еще не использовалось. Еще такие вещи называют "...-as-a-service".
Amazon EC2 - это "облако" виртуальных машин. Ты можешь создавать виртуальные машины вручную в админке или программно через API, а дальше ставишь туда нужный дистрибутив линукса, нужные программы и тд. Если твой сервис может работать на многих машинах, и у тебя автоматизировано развертывание новых машин, то ты например можешь автоматически создавать их при росте нагрузки и убирать при снижении, чтобы экономить.
Разумеется, как ты уже догадался, нужно знание Линукса или способный сисадмин, которому можно поручить администрирование.
AWS Lambda - какая-то мутная штука
Amazon S3 - это хранилище. Ты закачиваешь туда файлы вручную, или через API, и они там хранятся. Можно их потом скачать, а можно получить ссылку на скачивание и допустим вывести ее на сайте. Хранить и раздавать файлы можно конечно и в EC2, но выйдет дороже.
Ну например, ты можешь хранить там бекапы данных или загружаемые пользователями картинки. Если, конечно, тебя не беспокоит, что их сможет просматривать NSA.
Amazon Glacier - это хранилище, ориентированное на хранение бекапов без раздачи наружу.
Amazon Route 53 - это DNS-сервер (DNS-хостинг).
В общем, почитай тут https://d0.awsstatic.com/whitepapers/aws-overview.pdf только там надо промотать первые 10 страниц с маркетинговым буллшитом.
Преимущество аренды сервисов вместо покупки и настройки своего сервера - это более низкая цена (сервера дорогие), мгновенное развертывание (а сервер надо привезти в дата-центр и подключить), отсутствие необходимости настраивать и администрировать сервер (но надо настраивать "облако"). Если тебе срочно понадобился второй сервер - надо его искать, покупать, а вычислительные мощности в "облаке" можно арендовать мгновенно. И так же мгновенно отключить, когда нагрузка упадет, чтобы не платить далее за аренду. Например, ночью обычно нагрузка на сервера низкая.
Но конечно это все не так просто - надо оптимизировать свой код под работу в таком режиме, под поддержку работы на нескольких серверах, надо писать скрипты для автоматизации развертывания.
Также, минус в том, что вложив силы в адаптацию кода под сервисы Amazon, тебе будет трудно перейти на другую платформу, так как там будут свои API и нужны будут новые скрипты.
По моим ощущениям, по российским меркам Amazon не такой и дешевый, и может быть арендовать VPS (виртуальный сервер) у другого провайдера будет дешевле. Там не будет модного названия и API, но по цене может выйти меньше. Сравнить цены ты можешь сам, они есть в интернете.
"Кластер" это группа однотипных серверов, выполняющих общую задачу.
> как мне написать у мышки метод, который будет её двигать?
Это зависит от того, где хранится информация о положении мыши. Если информация хранится в свойствах самой мыши, достаточно их изменить.
>>1014880
Наверно, нет. А вообще, ты оф. сайт смотреть не пробовал? Там по моему много информации, хотя и нелегко найти нужную: https://aws.amazon.com/ru/whitepapers/?nc1=f_cc
"облако" - это просто сервера, хранилища или вычислительные мощности, которые принадлежат другой компании и сдаются в пользование. Хостинг для сайта - это тоже в какой-то мере "облако", просто это слово тогда еще не использовалось. Еще такие вещи называют "...-as-a-service".
Amazon EC2 - это "облако" виртуальных машин. Ты можешь создавать виртуальные машины вручную в админке или программно через API, а дальше ставишь туда нужный дистрибутив линукса, нужные программы и тд. Если твой сервис может работать на многих машинах, и у тебя автоматизировано развертывание новых машин, то ты например можешь автоматически создавать их при росте нагрузки и убирать при снижении, чтобы экономить.
Разумеется, как ты уже догадался, нужно знание Линукса или способный сисадмин, которому можно поручить администрирование.
AWS Lambda - какая-то мутная штука
Amazon S3 - это хранилище. Ты закачиваешь туда файлы вручную, или через API, и они там хранятся. Можно их потом скачать, а можно получить ссылку на скачивание и допустим вывести ее на сайте. Хранить и раздавать файлы можно конечно и в EC2, но выйдет дороже.
Ну например, ты можешь хранить там бекапы данных или загружаемые пользователями картинки. Если, конечно, тебя не беспокоит, что их сможет просматривать NSA.
Amazon Glacier - это хранилище, ориентированное на хранение бекапов без раздачи наружу.
Amazon Route 53 - это DNS-сервер (DNS-хостинг).
В общем, почитай тут https://d0.awsstatic.com/whitepapers/aws-overview.pdf только там надо промотать первые 10 страниц с маркетинговым буллшитом.
Преимущество аренды сервисов вместо покупки и настройки своего сервера - это более низкая цена (сервера дорогие), мгновенное развертывание (а сервер надо привезти в дата-центр и подключить), отсутствие необходимости настраивать и администрировать сервер (но надо настраивать "облако"). Если тебе срочно понадобился второй сервер - надо его искать, покупать, а вычислительные мощности в "облаке" можно арендовать мгновенно. И так же мгновенно отключить, когда нагрузка упадет, чтобы не платить далее за аренду. Например, ночью обычно нагрузка на сервера низкая.
Но конечно это все не так просто - надо оптимизировать свой код под работу в таком режиме, под поддержку работы на нескольких серверах, надо писать скрипты для автоматизации развертывания.
Также, минус в том, что вложив силы в адаптацию кода под сервисы Amazon, тебе будет трудно перейти на другую платформу, так как там будут свои API и нужны будут новые скрипты.
По моим ощущениям, по российским меркам Amazon не такой и дешевый, и может быть арендовать VPS (виртуальный сервер) у другого провайдера будет дешевле. Там не будет модного названия и API, но по цене может выйти меньше. Сравнить цены ты можешь сам, они есть в интернете.
"Кластер" это группа однотипных серверов, выполняющих общую задачу.
> Хм, а разве .httpacess не защищает?
Ты предлагаешь все выложить в публичный доступ, а потом закрывать отдельные файлы, а я предлагаю закрыть все и выкладывать только нужные файлы. Вдобавок htaccess работает только с Апачем и для nginx тебе придется писать правила заново.
И еще есть вариант, когда кто-то добавит файл, но не обновит правила и этот файл станет публично доступен.
> Они и есть вымышленные.
Названия компаний вроде были реалистичные, может им не понравится, что ты их торговую марку используешь для продвижения своего продукта.
Вместо "охранник1" я бы использовал "Иванов И.И" или "Константинопольский Константин" (для проверки поддержки длинных имен), хотя это уже не принципиально.
Папки php (->src) и templates я бы перенес в корень.
Дампа БД нету.
И еще, юридический момент, если бы ты писал код в рабочее время по заданию руководства, то авторские права на него по умолчанию принадлежали бы компании и необходимо было бы получать разрешение на публикацию кода. Это просто, вдруг кто-то из анонов про такие вещи не в курсе.
Важно помнить о таких вещах, а то бывают ситуации, когда человек работает в компании, а потом решает основать свою компанию и делать свой продукт, и тут внезапно выясняется что что-то было написано во время работы на старом месте и появляется риск лишиться продукта. Например, такая ситуация была у автора nginx, который работал в Рамблере (Рамблер сказал, что у него нет претензий на nginx), у Дурова, который параллельно был руководителем Вконтакте и разрабатывал Телеграм (собирались судиться), у ребят, которые сделали какую-то программу для искажения фотографий и работали в мейл-ру (тоже какая-то сложная ситуация, которая резрешилась добровольно-принудительной продажей части акций в мейл-ру). Недавно еще топ-менеджер ушел из Waymo (принадлежит гуглу, разрабатывает самодвижущиеся автомобили) в Uber, случайно прихватив с собой документацию на автопилот, и спалился. Тоже большой юридический конфуз вышел. В США за такие вещи могут быть очень серьезные наказания.
>>1014656
Судя по тексту ошибки, в строке 16 ты перепутал регистр букв в названии переменной.
> Хм, а разве .httpacess не защищает?
Ты предлагаешь все выложить в публичный доступ, а потом закрывать отдельные файлы, а я предлагаю закрыть все и выкладывать только нужные файлы. Вдобавок htaccess работает только с Апачем и для nginx тебе придется писать правила заново.
И еще есть вариант, когда кто-то добавит файл, но не обновит правила и этот файл станет публично доступен.
> Они и есть вымышленные.
Названия компаний вроде были реалистичные, может им не понравится, что ты их торговую марку используешь для продвижения своего продукта.
Вместо "охранник1" я бы использовал "Иванов И.И" или "Константинопольский Константин" (для проверки поддержки длинных имен), хотя это уже не принципиально.
Папки php (->src) и templates я бы перенес в корень.
Дампа БД нету.
И еще, юридический момент, если бы ты писал код в рабочее время по заданию руководства, то авторские права на него по умолчанию принадлежали бы компании и необходимо было бы получать разрешение на публикацию кода. Это просто, вдруг кто-то из анонов про такие вещи не в курсе.
Важно помнить о таких вещах, а то бывают ситуации, когда человек работает в компании, а потом решает основать свою компанию и делать свой продукт, и тут внезапно выясняется что что-то было написано во время работы на старом месте и появляется риск лишиться продукта. Например, такая ситуация была у автора nginx, который работал в Рамблере (Рамблер сказал, что у него нет претензий на nginx), у Дурова, который параллельно был руководителем Вконтакте и разрабатывал Телеграм (собирались судиться), у ребят, которые сделали какую-то программу для искажения фотографий и работали в мейл-ру (тоже какая-то сложная ситуация, которая резрешилась добровольно-принудительной продажей части акций в мейл-ру). Недавно еще топ-менеджер ушел из Waymo (принадлежит гуглу, разрабатывает самодвижущиеся автомобили) в Uber, случайно прихватив с собой документацию на автопилот, и спалился. Тоже большой юридический конфуз вышел. В США за такие вещи могут быть очень серьезные наказания.
>>1014656
Судя по тексту ошибки, в строке 16 ты перепутал регистр букв в названии переменной.
>Во-первых, "установка обработчика" не обязательно значит "установка обработчика на DOM элемент". В виджете может быть своя система событий, и ставится обработчик на внутреннее событие виджета, а не на DOM элемент.
Я не знаком с такими событиями. Как можно узнать по подробней об этом? Или увидеть простой пример?
>Во-вторых, можно запретить ставить обработчики до вывода виджета.
Что вы имеете ввиду? Как это сделать?
>В-третьих, можно в виджете создавать неприкрепленное к документу дерево DOM, и тогда на него можно будет вешать событие, даже до того, как мы вставим его в документ.
Тоже в первые слышу о таком. Можно ещё одну подсказку как это делается?
Теоретическая часть описана в паттерне observer: https://www.google.ru/search?q=шаблон+наблюдатель&btnG=Поиск&newwindow=1&gbv=1
Реализаций - пожалуйста, полный гитхаб: https://www.google.ru/search?q=github+event+emitter&btnG=Поиск&newwindow=1&gbv=1
Также, есть еще подход, когда создается не один эмиттер для всех событий, а свой объект на каждое событие: https://millermedeiros.github.io/js-signals/
Так получается больше объектов, но лучше документированный код и труднее ошибиться.
>>Во-вторых, можно запретить ставить обработчики до вывода виджета.
> Что вы имеете ввиду? Как это сделать?
...
function setSomeEventHandler(callback) {
if (!this.isRendered()) {
throw new Error("Widget is not rendered yet");
}
...
}
Про отсоединенные DOM элементы - это элементы, которые созданы через createElement, но пока не вставлены в DOM с помощью appendChild. Их называют detached DOM elements. Они, очевидно, не отображаются на экране, но вешать на них обработчики вполне возможно.
1. Как реализовывается наследование? через оператор extends ? Пишу например A extends B, и все свойства класса А переходят в класс Б? Так просто?
2. Взаимодействие внутри класса между свойствами реализовывается через функцию? Или можно все проще сделать?
>A extends B, и все свойства класса А переходят в класс Б? Так просто?
наоборот. Класс А наследует свойства и методы класса Б.
Что значит взаимодействие между свойствами? Например одно свойтво + другое? Да, только щас тебя обоссут илитарии, что ты метод функцией назвал. И за notepad++ увожение
>>>Во-вторых, можно запретить ставить обработчики до вывода виджета.
>> Что вы имеете ввиду? Как это сделать?
>...
>function setSomeEventHandler(callback) {
>if (!this.isRendered()) {
>throw new Error("Widget is not rendered yet");
>}
>...
>}
Но всё равно же придётся вызвать эту функцию только после создания элемента. Я думал вы имели ввиду, что есть какой-то способ, который задаст обработчик только после создания элемента.
>Про отсоединенные DOM элементы - это элементы, которые созданы через createElement, но пока не вставлены в DOM с помощью appendChild. Их называют detached DOM elements. Они, очевидно, не отображаются на экране, но вешать на них обработчики вполне возможно.
Что-то у меня не получается повесить обработчик
https://jsfiddle.net/ngww14z1/
https://jsfiddle.net/13uxLhfg/
Во втором варианте очевидно что выводимого элемента просто нету в массиве handled.
Допустим есть у нас массив из семи элементов, нам нужно заполнить другой массив равный 10 элементам взяв семь элементов из первого массива и оставшиеся 3 из начала того же первого массива.
1. [1,2,3,4,5,6,7] => 2. [1,2,3,4,5,6,7,1,2,3].
Как это лучше сделать?
смотри стандартные функции для работы с массивами
>могу попробовать ответить
спасибо конечно, но мне не ответы на эти вопросы нужны. А в целом вся исторически-теоретическая база PHP. поскольку имею свободное время, хотет таким образом хорошо подготовится к моему будущему собеседованию на работку
чето нафейлил, где-то чето бесконечно лупится
ошибок нет
как такое дебажат обычно?
> Что-то у меня не получается повесить обработчик
Тебе стоило бы получше изучить DOM, а пока ты пишешь код, не понимая толком, как он работает. В частности, почитать например про дерево DOM: https://learn.javascript.ru/document
Эта функция: $('.handled') ищет элементы в дерево внутри объекта document.documentElement. Каким образом она может найти отсоединенный элемент, который не находится в дереве?
Точно также, если у тебя например есть iframe, в котором свое дерево DOM, эта команда не найдет элемент там, потому что она ищет его в дереве DOM текущей страницы.
> Во втором варианте очевидно что выводимого элемента просто нету в массиве handled.
Потому что ты ищешь внутри дерева DOM в document, а там элемента нет.
> Но всё равно же придётся вызвать эту функцию только после создания элемента
Ну да, это может выглядеть примерно так:
var widget = new Widget(...);
widget.render();
widget.setSomeHandler(...);
А почему у тебя вообще возникла такая проблема, что надо вешать обработчик события на еще не добавленные элементы? Что-то у меня ощущение, что ты просто что-то не то делаешь.
>>1018913
> через оператор extends ?
Да. Наследование позволяет создать класс не с нуля, а взяв за основу существующий класс.
Наследование используется для однотипных сущностей, например можно унаследовать класс Банк от класса Организация. Несвязанные сущности наследовать нельзя.
> Взаимодействие внутри класса между свойствами реализовывается через функцию? Не понял, о чем речь и что имеется в виду под "взаимодействием". Свойства просто хранят в себе какие-то значения.
>>1019090
Официальный мануал можно попробовать почитать, а остальное погуглить - если на английском гуглить, наверняка найдется ответ.
>>1019095
Тогда можно просто натыкать по всей программе die("1");, убирать их по одному и смотреть, когда программа зависнет - тогда станет видно, в каком именно месте.
> Что-то у меня не получается повесить обработчик
Тебе стоило бы получше изучить DOM, а пока ты пишешь код, не понимая толком, как он работает. В частности, почитать например про дерево DOM: https://learn.javascript.ru/document
Эта функция: $('.handled') ищет элементы в дерево внутри объекта document.documentElement. Каким образом она может найти отсоединенный элемент, который не находится в дереве?
Точно также, если у тебя например есть iframe, в котором свое дерево DOM, эта команда не найдет элемент там, потому что она ищет его в дереве DOM текущей страницы.
> Во втором варианте очевидно что выводимого элемента просто нету в массиве handled.
Потому что ты ищешь внутри дерева DOM в document, а там элемента нет.
> Но всё равно же придётся вызвать эту функцию только после создания элемента
Ну да, это может выглядеть примерно так:
var widget = new Widget(...);
widget.render();
widget.setSomeHandler(...);
А почему у тебя вообще возникла такая проблема, что надо вешать обработчик события на еще не добавленные элементы? Что-то у меня ощущение, что ты просто что-то не то делаешь.
>>1018913
> через оператор extends ?
Да. Наследование позволяет создать класс не с нуля, а взяв за основу существующий класс.
Наследование используется для однотипных сущностей, например можно унаследовать класс Банк от класса Организация. Несвязанные сущности наследовать нельзя.
> Взаимодействие внутри класса между свойствами реализовывается через функцию? Не понял, о чем речь и что имеется в виду под "взаимодействием". Свойства просто хранят в себе какие-то значения.
>>1019090
Официальный мануал можно попробовать почитать, а остальное погуглить - если на английском гуглить, наверняка найдется ответ.
>>1019095
Тогда можно просто натыкать по всей программе die("1");, убирать их по одному и смотреть, когда программа зависнет - тогда станет видно, в каком именно месте.
>А почему у тебя вообще возникла такая проблема, что надо вешать обработчик события на еще не добавленные элементы? Что-то у меня ощущение, что ты просто что-то не то делаешь.
При частичной выборке сообщений, нужно обработать нажатие кнопки для получения более старых сообщений, которая отрисовывается вместе с сообщениями, при нажатии по контакту.
getMessages(with, offset).then(function() {
...
showMoreMessagesButton(with, offset + 1);
...
}
);
Во-первых нужно поставить на эту кнопку preventDefault(), чтобы ссылка не открывала новую страницу, и во-вторых, нужно по нажатию на эту кнопку получить более старые сообщения и динамически обновлять получение новых.
Спасибо, надеюсь сегодня закончу задачу
@
жавакод
Да, @media + резиновая верстка, где размеры не заданы жестко в пикселях (а например, заданы в процентах или определяются автоматически).
>>1002680
Тут неправильно стоят значения ltree. У тебя там значения 51, 51, 51.13, 51.14. Во-первых, можно убрать оттуда id файла. Во-вторых, нужно туда вставить номер либо id комментария, чтобы получилось:
0001
0001.0001 (1-й ответ на 0001)
0002
0002.0001 (ответ на 0002)
Либо так, если использовать id:
000000013
000000013.0000000015
000000014
000000014.0000000016
Кроме id и номеров можно еще использовать время или какую-то другую увеличивающуюся величину.
И тогда сортировка по tree будет давать нужный порядок.
Также, стоит наверно добавить еще колонку parentId, она поможет например восстановить tree при ошибке, а также поможет связать комментарии внешними ключами.
>>1002722
background-size, поддерживается примерно с 2011-2012 года с префиксами. Помни, что увеличение растровых картинок больше исходного размера ведет к их размыванию.
>>1002954
id можно получить из SEQUENCE, и вставить его как в поле id, так и в путь. Но порядковые номера использовать выгоднее чем id, так как они займут меньше места и пути будут короче.
>>1003275
Вместо gmp_neg($i) можно просто писать $x = -$i;
Проблема в твоем коде что ты сравниваешь только 2 символа в слове и по ним делаешь вывод, палиндром это или нет, а надо сравнить все.
Да, @media + резиновая верстка, где размеры не заданы жестко в пикселях (а например, заданы в процентах или определяются автоматически).
>>1002680
Тут неправильно стоят значения ltree. У тебя там значения 51, 51, 51.13, 51.14. Во-первых, можно убрать оттуда id файла. Во-вторых, нужно туда вставить номер либо id комментария, чтобы получилось:
0001
0001.0001 (1-й ответ на 0001)
0002
0002.0001 (ответ на 0002)
Либо так, если использовать id:
000000013
000000013.0000000015
000000014
000000014.0000000016
Кроме id и номеров можно еще использовать время или какую-то другую увеличивающуюся величину.
И тогда сортировка по tree будет давать нужный порядок.
Также, стоит наверно добавить еще колонку parentId, она поможет например восстановить tree при ошибке, а также поможет связать комментарии внешними ключами.
>>1002722
background-size, поддерживается примерно с 2011-2012 года с префиксами. Помни, что увеличение растровых картинок больше исходного размера ведет к их размыванию.
>>1002954
id можно получить из SEQUENCE, и вставить его как в поле id, так и в путь. Но порядковые номера использовать выгоднее чем id, так как они займут меньше места и пути будут короче.
>>1003275
Вместо gmp_neg($i) можно просто писать $x = -$i;
Проблема в твоем коде что ты сравниваешь только 2 символа в слове и по ним делаешь вывод, палиндром это или нет, а надо сравнить все.
Есть 2 варианта:
- сделать для каждой формы свой URL, который и выводит ее (при GET), и обрабатывает запрос (при POST). И форма с главной шлет запрос на этот URL, при ошибке выводится только сама форма с ошибкой.
- сделать обработчики в контроллере главной страницы, тогда при ошибке выводится главная с заполненной форме. Чтобы контроллер не был "толстым", можно обработку форм вынести в отдельные функции. Лучше наверно первый вариант, он аккуратнее выглядит.
>>1007165
Юи
>>1007436
Не знаю, надо разбираться в коде
>>1007572
Во-первых, ты можешь использовать встроенный в PHP вебсервер и запустить его в папке public, так что можно будет писать http://localhost:9001/ чтобы открыть сайт.
Во-вторых, если ты используешь Апач, ты можешь сделать свой домен вроде example.dev, прописать его в hosts и в конфиге Апача прописать для этого сайта корневую папку.
То есть не мучаться с папками, а сделать отдельный домен для сайта.
>>1010379
По поводу центрирования: у твоего метода есть пара недостатков:
- абс. поз. не позволяет содержимому влиять на размер контейнера
- если в браузере (до 2011-2012 годов) не поддерживается transform, то пользователь увидит сильно смещенное содержимое
Обе этих проблемы решаются использованием второго враппера с translate(50%, 50%) вместо абсолютного позиционирования:
.wrapper1 {
префиксы...
transform: translate(50%, 50%);
}
.wrapper2 {
префиксы...
transform: translate(-50%, -50%);
}
Тогда в случае отсутствия поддержки transform контент просто окажется прижат вверх, что не так страшно.
Есть 2 варианта:
- сделать для каждой формы свой URL, который и выводит ее (при GET), и обрабатывает запрос (при POST). И форма с главной шлет запрос на этот URL, при ошибке выводится только сама форма с ошибкой.
- сделать обработчики в контроллере главной страницы, тогда при ошибке выводится главная с заполненной форме. Чтобы контроллер не был "толстым", можно обработку форм вынести в отдельные функции. Лучше наверно первый вариант, он аккуратнее выглядит.
>>1007165
Юи
>>1007436
Не знаю, надо разбираться в коде
>>1007572
Во-первых, ты можешь использовать встроенный в PHP вебсервер и запустить его в папке public, так что можно будет писать http://localhost:9001/ чтобы открыть сайт.
Во-вторых, если ты используешь Апач, ты можешь сделать свой домен вроде example.dev, прописать его в hosts и в конфиге Апача прописать для этого сайта корневую папку.
То есть не мучаться с папками, а сделать отдельный домен для сайта.
>>1010379
По поводу центрирования: у твоего метода есть пара недостатков:
- абс. поз. не позволяет содержимому влиять на размер контейнера
- если в браузере (до 2011-2012 годов) не поддерживается transform, то пользователь увидит сильно смещенное содержимое
Обе этих проблемы решаются использованием второго враппера с translate(50%, 50%) вместо абсолютного позиционирования:
.wrapper1 {
префиксы...
transform: translate(50%, 50%);
}
.wrapper2 {
префиксы...
transform: translate(-50%, -50%);
}
Тогда в случае отсутствия поддержки transform контент просто окажется прижат вверх, что не так страшно.
Можно вместо динамического создания/удаления кнопки просто ее скрывать. Также, есть еще другие варианты:
- сделать в виджете собственные события, вроде Widget.EVENT_LOAD_MESSAGES - тогда подписка на них не требует наличия кнопки в дереве DOM
- поменять схему получения данных: вместо "глупого" виджета, который генерирует события, и которому мы снаружи передаем данные для отображения, сделать "умный" виджет, который получает модель списка сообщений снаружи и сам берет из нее данные для отображения. При этом эта модель может быть реализована по-разному - хранить сообщения внутри себя или же запрашивать их через АПИ.
Здесь стоит сначала спроектировать, кто за что отвечает, а потом уже писать код.
>>1019223
Как-то странно будет мне говорить, чем может заниматься департамент. Если ты можешь обойтись без него, то может он тебе и не нужен. Пиши тогда код пока без него.
Также, больно смотреть на названия вроде Sotrudnik, используй Google Translate хотя бы.
Также, у тебя есть недостаток в коде: у тебя при создании нового класса сотрудника надо переопределить в нем поля, но это нигде не документировано и никак не проверяется. Лучше использовать абстрактные методы для получения свойств конкретной профессии вроде зарплаты, так что не определить их не получится.
>>1018968
Циклом
>>1019299
Трудно понять без примера кода (вот это вот "в итоге после присвоения она тупо меняет все элементы в самом массиве"). Скинь его в новый тред.
>Можно вместо динамического создания/удаления кнопки просто ее скрывать.
Получается нужно ещё до инициализации js создать её где-то в шаблоне? Так делают в SPA приложениях?
>- сделать в виджете собственные события, вроде Widget.EVENT_LOAD_MESSAGES - тогда подписка на них не требует наличия кнопки в дереве DOM
Я плохо понимаю что значат события в js. Я читал про паттерн наблюдатель, но, на сколько я понял, мы просто помещаем в него коллбеки с какими-то идентификаторами, а затем вызываем их, передав какие-то данные. Я плохо понимаю как это можно применить на практике, разве что если нужно объединить выполнение нескольких функций.
В вашем примере "событие" это просто функция? Что она должна сделать?
>- поменять схему получения данных: вместо "глупого" виджета, который генерирует события, и которому мы снаружи передаем данные для отображения, сделать "умный" виджет, который получает модель списка сообщений снаружи и сам берет из нее данные для отображения. При этом эта модель может быть реализована по-разному - хранить сообщения внутри себя или же запрашивать их через АПИ.
>сделать "умный" виджет, который получает модель списка сообщений снаружи и сам берет из нее данные для отображения
Приведите пожалуйста пример. Я почему-то совсем не понимаю о чём идет речь, когда разговор заходит о js.
Если честно, я ещё не понимаю почему нельзя сделать методы задания событий DOM элементов частью отображения т.е. ModelView, ведь это же часть пользовательского интерфейса. Я не вижу их сходства с собственными событиями виджета, которые на самом деле просто функции.
сайт pisos.ru
как сделать, чтобы файл
/folderWithCandies/dick.html
открывался по адресу pisos.ru/dick ?
через редирект 301 или что-то типа того
блять я не знаю как такое вообще может быть
но я что-то натыкал в htacces, что он тупа перестал открывать одно название файла
даже после восстановление его полность и потом даже бекапа всего
мб что-то с хостингом связано, хз
Как закрыть папку, чтобы извне никто не мог смотреть/исполнять файлыы, а сам сервер мог к ним оборащаться?
Экспериментировал с такими конструкциями и либо не блокируется ничего, либо блокируется для всех:
<FilesMatch ".(htaccess|php|jpg)$">
Order Allow,Deny
Deny from all
</FilesMatch>
Order deny,allow
Deny from all
<Files ~ ".(xml|css|jpe?g|php)$">
Allow from all
</Files>
Вы видите копию треда, сохраненную 16 июля 2017 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.