Вы видите копию треда, сохраненную 13 мая 2016 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.
Это не чат! Пожалуйста не флудите, а старайтесь постить только вопросы, решения и ответы. Сколько лет вы не можете найти работу никому не интересно. Высказывайтесь одним большим постом а не цепочкой мелких
Это тред для начинающих. Не написал за свою жизнь ни одной программы? Ты наш человек.
Устанавливать пока что ничего не требуется, разве что редактор кода вроде Sublime Text 3, Notepad++, Netbeans PHP или PhpStorm (с ним будет удобнее).
Предыдущий тред был тут: >>702800 (OP)
Что самое главное для программиста? Умение аккуратно оформлять код (читай второй пост прежде чем писать код).
Почему PHP? Потому что фейсбук и википедия на нем написаны, и вакансий море, и учить легко.
Правила: ведем себя воспитанно, помогаем новичкам, постим ссылки на решения задачек, ОП их проверяет и дает советы и замечания. ОП заходит где-то раз в 2-3 дня, не жди его, решай задачки дальше. ОП отвечает на все вопросы по его задачкам и учебнику, а вот насчет каких-то других вещей - только если останется время. Но в треде немало анонимных экспертов разного уровня, так что вряд ли вопрос останется без ответа.
У нас есть уроки по основам PHP, они собраны и выложены по адресу http://archive-ipq-co.narod.ru/ Это учебник для изучающих с нуля, то есть если ты вообще ничего не знаешь, то надо начать с него. Он простой и понятный (по крайней мере в начале). Там есть задачи, их надо решать обязательно (чтобы стать программистом, надо писать код — иначе никак). Пости ссылки на решения в тред, мы их проверим, напишем замечания и дадим советы по улучшению.
Если не знаешь как решать, запости код, напиши в каком месте остановился и попроси подсказку.
Ты прошел весь учебник? Молодец, но это были лишь основы языка PHP, этого недостаточно. Вот что в идеале надо изучить еще: ООП, как работает веб-сервер, HTML/CSS, SQL, PDO, работа с таблицами в БД, работа с формами, MVC, git, composer, JS, фреймворки, автоматизированное тестирование.
Надо переходить к более серьезным задачкам, которые научат тебя всему этому.
- для начала прочти урок https://github.com/codedokode/pasta/blob/master/soft/web-server.md
- установи Апач + PHP (советы выше и ниже) и читай туториал http://php.net/manual/ru/tutorial.php
- Учи HTML/CSS и SQL, PDO, хотя бы основы
- Далее простая, но полезная задача сделать список студентов, в ней много полезных советов: https://github.com/codedokode/pasta/blob/master/student-list.md
- Более сложная задача сделать файлообменник на микрофреймворке Slim: https://gist.github.com/codedokode/9424217
- Еще более сложная и долгая задача на Yii/Yii2: https://gist.github.com/codedokode/8733007
- После нее можно изучать автоматизированное тестирование
- Если ты все решил, переходи к Symfony 2/Doctrine 2
- Почитать про паттерны http://designpatternsphp.readthedocs.org/ru/latest/README.html (если ты не изучил ни одного фреймворка, то это будет рановато), тут с примерами кода http://designpatternsphp.readthedocs.org/ru/latest/README.html . Имей в виду что без примеров использования их учить бесполезно - не поймешь, хочешь увидеть примеры использования паттернов - ковыряй исходники Симфони, например Symfony Forms. Не заучивай паттерны - смотри код и думай, зачем тут они использованы.
Чтобы делать эти задания, тебе надо установить Апач + PHP (можно заодно сразу и MySQL) на компьютер. Вот полезные инструкции:
https://github.com/codedokode/pasta/blob/master/soft/php-install.md
https://github.com/codedokode/pasta/blob/master/soft/apache-install.md
Может тебе понадобится пользоваться командной строкой, вот гайд https://github.com/codedokode/pasta/blob/master/soft/cli.md
Решения задач лучше показать мне, особенно на ООП,так как сам ты вряд ли увидишь все ошибки. Пости свой код на гитхаб и вкидывай ссылку в тред по мере решения. Я прокомментирую и укажу на ошибки.
Также, у нас есть задачи которые позволят тебе изучить или подтянуть до нормального уровня знания JS/HTML/CSS/SQL. Решай их параллельно с задачами выше.
- HTML/CSS: https://github.com/codedokode/pasta/blob/master/html/html.md
- JS: https://gist.github.com/codedokode/ce30e7a036f18f416ae0
- SPA (сложно): https://github.com/codedokode/pasta/blob/master/js/spa.md
- Проверялка решений на JS: http://dkab.github.io/jasmine-tests/
- MySQL: https://gist.github.com/codedokode/10539213
Что почитать
- Мануал по PHP — http://www.php.net/manual/ru/langref.php
- Сайт phptherightway (перевод на русский: http://getjump.me/ru-php-the-right-way/ )
- По PHP: Профессиональное программирование на PHP Джордж Шлосснейгл
- По PHP: Мэтт Зандстра — PHP: Объекты, шаблоны, методики программирования
- JS: learn.javascript.ru
- Про Git: https://git-scm.com/book/ru/v1
Нужен ли ООП, фреймворки, MVC, git, composer? — Да, однозначно. Посмотри любую вакансию.
Сайт опять упал!!!!! — Не паникуй, а открой http://rghost.ru/6bfCY9lfl и получи личную немного устаревшую оффлайновую копию сайта (можно читать хоть на андроиде без интернета)
Оформляй код аккуратно!!! — например пропусти через phpformatter.com . Также, если ты пользуешься IDE вроде PhpStorm, Netbeans, Eclipse, то в них эта опция встроена, подробнее: https://gist.github.com/codedokode/8759492
ОП, сделай за меня мою работу или домашнее задание? — Это конечно, хорошая идея, но нет.
Подскажи сайты для поиска работы, я не умею гуглить? — hh.ru, geekjob.ru, moikrug.ru (склеен с brainstorage.me), fl.ru, upwork.com (бывший одеск). Имей в виду, что кроме фриланса есть еще постоянная удаленная работа (remote job) когда тебе не надо тратить время на поиск заказов и переговоры с неадекватными заказчиками.
Если тебе лень выравнивать код руками, закачай его на http://beta.phpformatter.com/ и нажми «format». Робот исправит выравнивание и отступы в мгновение ока (да, прогресс не стоит на месте). Если ты используешь мощную IDE вроде PhpStorm, там тоже есть функция форматирования кода.
Горячие клавиши для форматирования кода в разных IDE: https://gist.github.com/codedokode/8759492
Вообще, в PHP долгое время не было единого стандарта оформления кода, все писали как попало и было много бардака, но сейчас дело лучше — есть стандарты PSR-1 и 2. Вот как надо оформлять код:
- переменные и функции пишутся с маленькой буквы, подчеркивание не используется, используется camelCase, пример: $x, $numberOfPeople, printResults()
- Название функции начинается с глагола, в стиле «сделайЧтоТо»
- не знаешь английский? Не беда, в 21 веке есть решение этой проблемы. Не пиши транслитом, открой лучше Гугл Транслейт или slovari.yandex.ru и найди название для переменной там
- в именах классов используется CamelCase, первая буква большая, «_» может использоваться
- мы предпочитаем подстановку переменных вместо конкатенации строк: "I am $age years old" — хорошо, 'I am ' . $age . ' years old' — плохо из-за обилия точек и кавычек
- мы используем для отступов 4 пробела (можно настроить редактор, чтобы при нажатии Tab он вставлял 4 пробела)
Вот ссылка на стандарты, где все это описано подробнее и даны примеры оформления:
PSR-1: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-1-basic-coding-standard.md
PSR-2: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-2-coding-style-guide.md
------------------
Итак, ты зашел в тред и решил помочь какому-то анону, дав ему совет или подсказку. Спасибо! Но прочти сначала эти напоминания, чтобы твоя помощь действительно была полезной.
Давай удочку, а не рыбу
Лучше не давать готовое решение проблемы, а рассказать как его искать. Может дать ключевые слова для гугла или ссылку. Но помогай, а не пытайся показать превосходство. Если даешь ссылки на нерусскоязычные статьи, упомяни об этом.
Будь доброжелателен
Не годится: «Ты мануал хоть раз в жизни открывал, обезьяна?»
Не годится: «В гугле забанили?»
Не годится: «Твой код плохой»
Хорошо: «Вот, как можно улучшить этот код: ...»
Хорошо: «Ты неправильно используешь функцию abc(). Вот ее описание: ссылка, и как видишь ей надо передать строку, а не массив»
Не придирайся к знанию английского языка.
Объясняй
Не очень хорошо: «сделай как в этом коде»
Хорошо: «если ты вставляешь текст от пользователя в SQL запрос, то получается SQl-инъекция, которая позволяет взломать твой сервер (ссылки). Чтобы этого избежать, надо вставлять данные с помощью плейсхолдеров (ссылки)»
Хорошо: «Помни, что код пишется для людей. Если писать такие большие функции, то в них становится трудно разобраться...»
Не проповедуй
Мы учим использованию самых распространненных подходов, стандартов, библиотеки фреймворков. Если ты не любишь ООП, пробелы в коде, jQuery, сам PHP, то рассказать об этом стоит в каком-нибудь другом треде.
Не придирайся к знанию английского языка, анон пишет как умеет.
Ах да. Если тебе кажется, что что-то в учебнике или задачах можно сделать лучше — пиши, обратная связь всегда очень полезна.
Если тебе лень выравнивать код руками, закачай его на http://beta.phpformatter.com/ и нажми «format». Робот исправит выравнивание и отступы в мгновение ока (да, прогресс не стоит на месте). Если ты используешь мощную IDE вроде PhpStorm, там тоже есть функция форматирования кода.
Горячие клавиши для форматирования кода в разных IDE: https://gist.github.com/codedokode/8759492
Вообще, в PHP долгое время не было единого стандарта оформления кода, все писали как попало и было много бардака, но сейчас дело лучше — есть стандарты PSR-1 и 2. Вот как надо оформлять код:
- переменные и функции пишутся с маленькой буквы, подчеркивание не используется, используется camelCase, пример: $x, $numberOfPeople, printResults()
- Название функции начинается с глагола, в стиле «сделайЧтоТо»
- не знаешь английский? Не беда, в 21 веке есть решение этой проблемы. Не пиши транслитом, открой лучше Гугл Транслейт или slovari.yandex.ru и найди название для переменной там
- в именах классов используется CamelCase, первая буква большая, «_» может использоваться
- мы предпочитаем подстановку переменных вместо конкатенации строк: "I am $age years old" — хорошо, 'I am ' . $age . ' years old' — плохо из-за обилия точек и кавычек
- мы используем для отступов 4 пробела (можно настроить редактор, чтобы при нажатии Tab он вставлял 4 пробела)
Вот ссылка на стандарты, где все это описано подробнее и даны примеры оформления:
PSR-1: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-1-basic-coding-standard.md
PSR-2: https://github.com/php-fig/fig-standards/blob/master/accepted/ru/PSR-2-coding-style-guide.md
------------------
Итак, ты зашел в тред и решил помочь какому-то анону, дав ему совет или подсказку. Спасибо! Но прочти сначала эти напоминания, чтобы твоя помощь действительно была полезной.
Давай удочку, а не рыбу
Лучше не давать готовое решение проблемы, а рассказать как его искать. Может дать ключевые слова для гугла или ссылку. Но помогай, а не пытайся показать превосходство. Если даешь ссылки на нерусскоязычные статьи, упомяни об этом.
Будь доброжелателен
Не годится: «Ты мануал хоть раз в жизни открывал, обезьяна?»
Не годится: «В гугле забанили?»
Не годится: «Твой код плохой»
Хорошо: «Вот, как можно улучшить этот код: ...»
Хорошо: «Ты неправильно используешь функцию abc(). Вот ее описание: ссылка, и как видишь ей надо передать строку, а не массив»
Не придирайся к знанию английского языка.
Объясняй
Не очень хорошо: «сделай как в этом коде»
Хорошо: «если ты вставляешь текст от пользователя в SQL запрос, то получается SQl-инъекция, которая позволяет взломать твой сервер (ссылки). Чтобы этого избежать, надо вставлять данные с помощью плейсхолдеров (ссылки)»
Хорошо: «Помни, что код пишется для людей. Если писать такие большие функции, то в них становится трудно разобраться...»
Не проповедуй
Мы учим использованию самых распространненных подходов, стандартов, библиотеки фреймворков. Если ты не любишь ООП, пробелы в коде, jQuery, сам PHP, то рассказать об этом стоит в каком-нибудь другом треде.
Не придирайся к знанию английского языка, анон пишет как умеет.
Ах да. Если тебе кажется, что что-то в учебнике или задачах можно сделать лучше — пиши, обратная связь всегда очень полезна.
начинать изучение фреймворков с него? если он сейчас не актуален то на что лучше садится Joomla/Симфония 2 ?
>Нет флага u, нет привязки к краям регулярки. В случае неправльной записи налдо выводить ошибку.
Бро, ну я уже еду от стремления к универсальности. Я уже неделю решаю эту задачу, долго думал над этим автоматизированным создание экземпляров класса - и тут такое...
Русские буквы там не нужны, там у нас сферический список сотрудников в вакууме, он беспощаден в своей идеальности. Там исключены ошибки, ведь не в этом суть задачи вообще...
У меня тут нет понимания ООП до сих пор, а я, простите, целую неделю занимаюсь сексом с тем, как автоматизированно создавать экземпляры класса...
У меня нет возможности больше двух часов в день заниматься, я трачу время не совсем на то, что более нужно сейчас.
>Это не будет правильно работать, второй цикл будет отсчитываться не с нуля.
Спасибо, там, действительно, можно поставить не
>$employeesOfPurchaseDepartment[$x]
, а просто
>$employeesOfPurchaseDepartment[]
- но и в первом случае всё работало как надо: $x всегда соответствует нужному очередному ключу в массиве для создания соответствующего значения, я проверял на многих массивах (сейчас в массиве все указанные тобой сотрудники имеются, всё идеально работает).
Но переусложнение не нужно, поэтому просто добавление в массив сделаю, спасибо за подсказку.
>$x
>Это выражение ничего не делает, его можно вообще пропустить.
Просто для наглядности оставил, хотя PHPStorm, действительно, выводит предупреждение, что эта переменная нигде не употребляется.
>Это хорошо, но это должно быть не внутри класса Сотрудник. Ведь фактически у тебя есть некая таблица и по ней ищется базовая ставка. Нелогично делать поиск внутри сотрудника, так как это не имеет особого отношения к его зоне ответственности. Это явно какая-то внешняя по отношению к нему штука (Бухгалтерия?). И значит поиск надо делать снаружи, а в сотрудника передавать уже конкретную цифру.
Блин, я чем дальше в ООП, тем больше долбо... ну, нет смысла продолжать, всё и так понятно.
Я был на сто процентов уверен, что это как раз зона ответственности сотрудника - количество литров кофе, причисление литров в соответствии с профессией должно быть методом в его классе.
Ничего не понимаю теперь.
>А зачем тебе полная таблица? Тебе достаточно знать сколько кофе потребляет конкретно этот сотрудник. Можно ведь просто сделать в нем поле с базовым значением потребления кофе для этой профессии. Тогда будет так:
>$coffee = $ivan->getCoffeeConsumption();
Ну я вообще теперь запутался...
У меня же так и было с самого начала (просто поле было в конструкте с кофе, зарплатой, страницами), а ты посоветовал добавлять это автоматизированно?
Или я снова всё путаю уже в час ночи...
У меня сейчас ближе к тому, что - как мне показалось - ты потом советовал и сейчас говоришь. Только getCoffeeConsumption() называется getCoffeeByProfession(), но там перебор по ассоциативному массиву "профессия" => "количество литров" идёт.
>передавать каждый раз полную таблицу когда мы просто хотим узнать сколько этот сотрудник выпил кофе, нелогично.
Каждый раз там функция ищет соответствие - цикл проходит, соответствие находится.
Я уже сомневаюсь в своей логичности, мне это казалось и продолжает казаться самым простым (разумеется, кроме прямого указания потребления кофе наряду с профессией прямо при создании экземпляра класса).
>Затем можно добавить методы найма/увольнения. И имя департамента надо ведь хранить где-то.
Найм - это добавить нужное количество новых сотрудников. Увольнение - это пройтись по массиву с имеющимися и удалить тех, у кого находятся указанные соответствия (проблемы с тем, что это всё не строки, сразу так не соображу).
Спасибо, что помогаешь тугоумам вроде меня.
>Это хорошо, но это должно быть не внутри класса Сотрудник. Ведь фактически у тебя есть некая таблица и по ней ищется базовая ставка. Нелогично делать поиск внутри сотрудника, так как это не имеет особого отношения к его зоне ответственности. Это явно какая-то внешняя по отношению к нему штука (Бухгалтерия?). И значит поиск надо делать снаружи, а в сотрудника передавать уже конкретную цифру.
Блин, я чем дальше в ООП, тем больше долбо... ну, нет смысла продолжать, всё и так понятно.
Я был на сто процентов уверен, что это как раз зона ответственности сотрудника - количество литров кофе, причисление литров в соответствии с профессией должно быть методом в его классе.
Ничего не понимаю теперь.
>А зачем тебе полная таблица? Тебе достаточно знать сколько кофе потребляет конкретно этот сотрудник. Можно ведь просто сделать в нем поле с базовым значением потребления кофе для этой профессии. Тогда будет так:
>$coffee = $ivan->getCoffeeConsumption();
Ну я вообще теперь запутался...
У меня же так и было с самого начала (просто поле было в конструкте с кофе, зарплатой, страницами), а ты посоветовал добавлять это автоматизированно?
Или я снова всё путаю уже в час ночи...
У меня сейчас ближе к тому, что - как мне показалось - ты потом советовал и сейчас говоришь. Только getCoffeeConsumption() называется getCoffeeByProfession(), но там перебор по ассоциативному массиву "профессия" => "количество литров" идёт.
>передавать каждый раз полную таблицу когда мы просто хотим узнать сколько этот сотрудник выпил кофе, нелогично.
Каждый раз там функция ищет соответствие - цикл проходит, соответствие находится.
Я уже сомневаюсь в своей логичности, мне это казалось и продолжает казаться самым простым (разумеется, кроме прямого указания потребления кофе наряду с профессией прямо при создании экземпляра класса).
>Затем можно добавить методы найма/увольнения. И имя департамента надо ведь хранить где-то.
Найм - это добавить нужное количество новых сотрудников. Увольнение - это пройтись по массиву с имеющимися и удалить тех, у кого находятся указанные соответствия (проблемы с тем, что это всё не строки, сразу так не соображу).
Спасибо, что помогаешь тугоумам вроде меня.
>$coffee = $coffeeByProfession[$this->profession];
Только ведь разницы особой нет (всё тот же ассоциативный массив остаётся) и не о том ты вроде бы говоришь.
Сделать это константой? Так ведь констант тогда куча потребуется, старые-добрые массивы в этом плане удобнее...
Короче, я так и не понял, что тут нужно.
Указывал в готовом виде в аргументах при создании экземпляра класса - не то.
Ищу в массиве соответствие кофе профессии при автоматизированном создании экземпляра класса - снова не то.
ОП задал глобальные переменные $creditSum и $payOut, следовательно, их нужно использовать при составлении переменных $homoCreditTotal, $softBankTotal и $strawberryBankTotal на вывод. В таком случае, чтобы посчитать, сколько всего паренёк потратит на свой девайс, я складываю $creditSum с $payOut умноженную на количество месяцев, в течении которых паренёк расплачивается по кредиту. А описанная функция считает количество месяцев, которые понадобятся для погашения кредита, так? То есть, в конце описания функции мы return'ем количество прошедших месяцев. Но в последний месяц школьник, скорее всего, отдаст не 5000, а меньшую сумму. Вот и получается, что, скажем $homoCreditTotal = $creditSum + ($payOut * ($monthsPassed (4, 500)) - 1) + та сумма, которую школьник отдаст в последний месяц.
Я правильно мыслю, анон?
Тогда, если бы из функции можно было бы return'уть два значения, то всё было бы понятно.
>в конце описания функции мы return'ем количество прошедших месяцев.
Нет, лучше вернуть $paymentTotal - чтобы сравнить именно по этой величине все банки.
>Но в последний месяц школьник, скорее всего, отдаст не 5000, а меньшую сумму.
Тэкс, тэкс, что тут у нас? Ещё один новичок, который идёт по учебника ОПа не по порядку, перескакивая задачи? Ну наконец-то!
Покажи решение задачи на Айфон. В задаче про Айпад специально второй банк имеет те же параметры, что и в задаче про Айфон, - чтобы проверить правильность работы скрипта.
>получается, что, скажем $homoCreditTotal = $creditSum + ($payOut * ($monthsPassed (4, 500)) - 1) + та сумма, которую школьник отдаст в последний месяц
Переусложнение, вообще не понятно, что здесь что. Пробуй сначала про Айфон решить, по всему видно, что не решал.
>>715331
Можно вернуть массив.
function testReturn()
{
$one = 1;
$two = 2;
$three = 3;
$array = [];
$array[] = $one;
$array[] = $two;
$array[] = $three;
return $array;
}
$array = testReturn();
$one = $array[0];
$two = $array[1];
$three = $array[2];
echo $one;
echo $two;
echo $three;
Либо, как ОП посоветовал, в функции:
$array = [
'one' => $one,
'two' => $two,
'three' => $three
];
А дальше:
$one = $array['one'];
$two = $array['two'];
$three = $array['three'];
>в конце описания функции мы return'ем количество прошедших месяцев.
Нет, лучше вернуть $paymentTotal - чтобы сравнить именно по этой величине все банки.
>Но в последний месяц школьник, скорее всего, отдаст не 5000, а меньшую сумму.
Тэкс, тэкс, что тут у нас? Ещё один новичок, который идёт по учебника ОПа не по порядку, перескакивая задачи? Ну наконец-то!
Покажи решение задачи на Айфон. В задаче про Айпад специально второй банк имеет те же параметры, что и в задаче про Айфон, - чтобы проверить правильность работы скрипта.
>получается, что, скажем $homoCreditTotal = $creditSum + ($payOut * ($monthsPassed (4, 500)) - 1) + та сумма, которую школьник отдаст в последний месяц
Переусложнение, вообще не понятно, что здесь что. Пробуй сначала про Айфон решить, по всему видно, что не решал.
>>715331
Можно вернуть массив.
function testReturn()
{
$one = 1;
$two = 2;
$three = 3;
$array = [];
$array[] = $one;
$array[] = $two;
$array[] = $three;
return $array;
}
$array = testReturn();
$one = $array[0];
$two = $array[1];
$three = $array[2];
echo $one;
echo $two;
echo $three;
Либо, как ОП посоветовал, в функции:
$array = [
'one' => $one,
'two' => $two,
'three' => $three
];
А дальше:
$one = $array['one'];
$two = $array['two'];
$three = $array['three'];
Спасибо за наводку!
Задачу про айфон я решил. Я такой молодец, что вооще ни через чего не перепрыгиваю.
Я к гайду ОПа возвращаюсь раз или два в неделю, поэтому даже про возможность вывести массив я забыл.
Покажи решение на Идеоне.
Инфа 93%, что там не всё в порядке.
Потому что подход абсолютно тот же, а по твоим вопросам не видно, что имеется понимание.
Задача на айпад это то же самое что на айфон, просто там надо алгоритм расчет засунуть в функцию и вызвать 3 раза с разнными аргументами.
>Я к гайду ОПа возвращаюсь раз или два в неделю, поэтому даже про возможность вывести массив я забыл.
Возможно, ОП и добавил это недавно, но раньше такого не было в учебнике. Или я пропустил этот момент.
http://ideone.com/mBEGWA - я хотя бы на верном пути вообще?
Чёрт знает что...
Согласен, ООП вообще непростая штука. На первом месте работы у нас был код с классами, но никакого ООП не было - просто код писался стеной внутри классов без особой логики. Была просто видимость что якобы мы используем современный подход. Я сам тогда не разбирался в ООП, и не понимал идеи этого подхода, а лишь знал синтаксис: как объявить класс, как вызвать метод.
Потому давай начнем с основ. ООП подход подразумевает, что мы создаем классы, каждый из которых занимается чем-то своим (принцип единой ответственности). Классы условно можно делать на "модели" - их объекты хранят информацию о каких-то сущностях, например Сотрудниках и "сервисы" - эти объекты обычно существуют в одном экземпляре и делают какие-то операции над моделями.
Ну например в этой задаче моделью может быть Сотрудник, а сервисом - класс, который берет сотрудников и повышает им зарплату по определенному принципу.
Модели обычно хранят не только данные о сущности, но и содержат методы для работы с этими данными. Например, метод, который по базовой ставке и рангу определяет зарплату сотрудника, логично поместить в класс Сотрудник, так как ему не нужны какие-то дополнительные данные - все, что нужно, есть в самом сотруднике.
Самое главное - насколько удобно пользоваться объектной моделью при решении задачи: легко ли делается увольнение, найм сотрудников, изменение их рангов и зарплат, расчет статистики. Обычно правильно составленная модель удобна в использовании. И наоборот, если пользоваться неудобно, то скорее всего что-то не так спроектировано.
Потому мы всегда начинаем решать ООП задачу с проектирования: какие у нас есть классы, какие у объектов есть свойства и методы, как они связаны друг с другом. Если ты читал урок по ООП, то знаешь такие вещи как аггрегация и композиция.
---------------
Вот возьмем твой код. Я хочу сделать департамент из 2 сотрудников и узнать, сколько они потребляют кофе и тратят денег на зарплату. Напиши-ка в треде код, который
1) создаст департамент и добавит в него этих двух работников (должности и ранги ставь любые)
2) код который получает на вход департамент (не обязательно тот же что создан в первом пункте, а любой) и выводит с помощью echo, без оформления, без таблиц, общую зарплату и потребление кофе по департаменту
3) код, который получает департамент и увольняет половину сотрудников в нем (любых)
Напиши, а потом посмотрим. Вот как по моему это может выглядеть:
1) 3 строчки для создания департамента и работников, 2 строчки для найма их в департамент
2) 2 строчки, по 1 для каждого показателя
3) получаем список сотрудников, берем половину через array_slice, увольняем циклом - строчек 5-6
Согласен, ООП вообще непростая штука. На первом месте работы у нас был код с классами, но никакого ООП не было - просто код писался стеной внутри классов без особой логики. Была просто видимость что якобы мы используем современный подход. Я сам тогда не разбирался в ООП, и не понимал идеи этого подхода, а лишь знал синтаксис: как объявить класс, как вызвать метод.
Потому давай начнем с основ. ООП подход подразумевает, что мы создаем классы, каждый из которых занимается чем-то своим (принцип единой ответственности). Классы условно можно делать на "модели" - их объекты хранят информацию о каких-то сущностях, например Сотрудниках и "сервисы" - эти объекты обычно существуют в одном экземпляре и делают какие-то операции над моделями.
Ну например в этой задаче моделью может быть Сотрудник, а сервисом - класс, который берет сотрудников и повышает им зарплату по определенному принципу.
Модели обычно хранят не только данные о сущности, но и содержат методы для работы с этими данными. Например, метод, который по базовой ставке и рангу определяет зарплату сотрудника, логично поместить в класс Сотрудник, так как ему не нужны какие-то дополнительные данные - все, что нужно, есть в самом сотруднике.
Самое главное - насколько удобно пользоваться объектной моделью при решении задачи: легко ли делается увольнение, найм сотрудников, изменение их рангов и зарплат, расчет статистики. Обычно правильно составленная модель удобна в использовании. И наоборот, если пользоваться неудобно, то скорее всего что-то не так спроектировано.
Потому мы всегда начинаем решать ООП задачу с проектирования: какие у нас есть классы, какие у объектов есть свойства и методы, как они связаны друг с другом. Если ты читал урок по ООП, то знаешь такие вещи как аггрегация и композиция.
---------------
Вот возьмем твой код. Я хочу сделать департамент из 2 сотрудников и узнать, сколько они потребляют кофе и тратят денег на зарплату. Напиши-ка в треде код, который
1) создаст департамент и добавит в него этих двух работников (должности и ранги ставь любые)
2) код который получает на вход департамент (не обязательно тот же что создан в первом пункте, а любой) и выводит с помощью echo, без оформления, без таблиц, общую зарплату и потребление кофе по департаменту
3) код, который получает департамент и увольняет половину сотрудников в нем (любых)
Напиши, а потом посмотрим. Вот как по моему это может выглядеть:
1) 3 строчки для создания департамента и работников, 2 строчки для найма их в департамент
2) 2 строчки, по 1 для каждого показателя
3) получаем список сотрудников, берем половину через array_slice, увольняем циклом - строчек 5-6
http://ideone.com/ikDXi6 - миллион в банке
http://ideone.com/ScQ62W - айфон в кредит
http://ideone.com/2F2Go6 - рост
http://ideone.com/1YPcvW - рулетка
И я понять не могу как сделать http://ideone.com/6RqNUq генератор имен. Слоги нужно через array_rand выбирать? Как их склеивать в одну переменную? Я еще оче глупый.
Представляю сколько глистов у этих кошатниц.
Ну, вот есть например у меня класс, методы которого, описывают, извлечение, удаление и добавление в бд юзеров. А другой класс делает то же самое, но со статьями, например. Сейчас в каждом методе каждого класса прописывается что объект коннета к бд — это global.
Как правильно сделать?
Подскажите стек технологий, освоив который. можно будет начинать?
Пока что точно определился с буцтрапом.
inb4 html/css/js/php/mysql
Сервис. Или контейнер для внедрения зависимости (dependency injection).
Из реализаций: pimple например, используется в частности в третьем слиме.
Гугли короче по этим ключевым словам. "Контейнер dependency injection", "service layer, service locator".
И зачем ограничиваться pdo? Вдруг понадобится сменить драйвер? Есть такая удобная библиотека для работы с базой dbal (компонент доктрины), это фактически обертка над pdo, mysqli и т.д.
>>715746
>html/css/js/php/mysql
Да.
>>715526
Держи котиков :З
Так без знания языка ты не сможешь выучить библиотеки/фреймворки, даже заучивая наизусть последовательность букв из апи, это нереально.
Php кстати учится быстрее и легче, чем фреймворки.
И на фрилансе придется править чужой код, написанный некачественно и часто на голом php.
Ну ок.
Из крупных фреймворков:
Symfony, Laravel (его любят в штатах), Yii (больше популярен в снг).
CMS: Wordpress, Drupal, Joomla (хотя бы 2 из 3).
Само собой разговорный английский, иначе как ты будешь общаться с заказчиками?
<?php
$recepient = "abu@2ch.hk";
$sitename = "Название сайта";
$name = $_POST['name'];
$phone = $_POST['phone'];
$email = $_POST['email'];
$adress = $_POST['adress'];
$pagetitle = "Новая заявка с сайта \"$sitename\"";
$message = "Имя: $name \nТелефон: $phone \nПочта: $email \nАдресс: $adress" ;
mail($recepient, $pagetitle, $message, "Content-type: text/plain; charset=\"utf-8\"\n From: $recepient");
Всё же вроде правильно, в чём проблема?
Как вариант (я делал), можно заполнять массив слогами, потом склеиваешь через implode
Изучай Zend, самый основательный фреймворк.
(?i)<a([^>]+)>(.+?)</a>
(?i)<a([^>]+)>(.+?)</a>
Юзай DomDocument, искать ссылки регулярками - полное извращение. Они не для этого.
http://php.net/manual/de/book.dom.php
http://php.net/manual/de/domdocument.loadhtmlfile.php
Спасибо, конечно, но. Я бы с радостью, да только пишу на джавке, и по заданию надо именно через регулярки. А сюда зашел потому, что у здешних анонов должен быть побольше опыт использования регулярок.
Зачем там туториал, он же интуитивно понятный. В чем там разбираться-то? Зашел в настройки, поставил под себя клавиши, поставил шрифт, включил дебагер, импортировал проект. Я за 5 мин настроил. Если есть вопросы, задавай.
Тогда тебе сюда - https://regex101.com/
В окно снизу забиваешь твою хтмл, в верхнее регулярку, сбоку подсказки по операторам.
спасибо
Например, можно настроить вывод не в браузер, а куда-нибудь в другое место? (в visual studio выводит в отдельное окно, например)
Да и вообще, где посмотреть настройки вывода? (ужасный шрифт, когда выводит массив, все в куче)
Workbench - forward/reverse engineering
https://habrahabr.ru/post/110426/
>без громоздких пакетов
Чем измеряется громоздкость?
Аноны, есть один сайт " http://www.example.com/login-now" с формой входа, в "actions" которой указано "/login-now". Как сделать так чтоб пользователь вводил логин и пароль на сайте www.example2.com, а php скрипт его перенаправлял на сайт www.example.com и передавал данные в action "/login-now"? Пробовал использовать библиотеку:
https://github.com/Garik-/http-request
\Garik\HttpRequest::post( $url )->form( array('password'=>"asdasd", "login"=>"asdaasd") )->headers();
header('Location: http://www.example.com/');
Но я в php нуб, может кто пожалуйста написать код для работы с двумя сайтами примерами?
В настройки заходил? Fonts and colors, все меняется.
Про вывод не в браузер хз, тут это не так работает - в браузере вешается плагин для дебаггинга, phpstorm его подхватывает. Если сдебажить что надо, отдельное окно вываливает со всеми переменными и классами.
Не прокатит, тебе же cookie ставить надо от example.com после логина, а это может сделать только example.com. example2.com этого не сможет, политика безопасности в браузерах такая. Возможна такая схема - ты шлешь логин с паролем на example.com, он генерирует одноразовый секретный url, на него отправляешь пользователя, там пользователю ставят cookie, и он залогинен.
Есть что-нибудь такое?
Вот массив такого типа:
$array = [
$one,
$two,
$three
];
Значение $one начинается с единицы, далее $two на тройке, а $three на пятёрке.
Как обнулить эту последовательность? Сделать ключи по порядку с нуля.
Что-то встречалось такое, но не могу вспомнить и найти.
ksort не подходит, массив нужен не ассоциативный.
*ключ, а не значение
Ключ $one начинается с единицы, далее $two на тройке, а $three на пятёрке.
>Есть что-нибудь такое?
Есть.
>не могу вспомнить и найти.
Смотри на php.net список функций для работы с массивами.
И код, который возвращает некорректные данные, нужно переписать, чтобы сразу возвращался массив в нужном формате.
http://php.net/manual/ru/array.sorting.php
Тебе сюда. Скорее всего обычный sort() тебе пойдет.
>>716065
>Смотри на php.net список функций для работы с массивами.
Вот спасибо, так спасибо! Лучше удочка, чем рыба!
Я там смотрел, конечно же, но пока не нашёл подходящего. Я могу жутко тупить и не понимать очевидного, бывает. Может, причина у меня в другом. С какого перепугу я вообще решил, что в неассоциативном массиве может быть какая-то иная последовательность значения, а не с нуля?
>И код, который возвращает некорректные данные, нужно переписать, чтобы сразу возвращался массив в нужном формате.
Да, буду думать.
>>716071
>>716058
array_values. sort менее эффективен так как сортирует массив и требует больше времени.
> Может, причина у меня в другом. С какого перепугу я вообще решил, что в неассоциативном массиве может быть какая-то иная последовательность значения
Элементы могут иметь любые ключи, лишь бы они были уникальные и идти в любом порядке. даже странно такие элементарные вещи объяснять. "Неассоциативных" массивов в пхп нет.
Да, наверну, действительно, спасибо.
>>716087
Спасибо!
>>716091
Да, я помню, что если мы назначаем ключ, допустим, 7 у одного значения, то дальше они с семи и идут.
Ну вот при var_dump ключи эти обнаруживаются, а так-то и непонятно, какие там есть.
Array_values поможет просто всё к единому привести.
Домен принципиален? Если нет, то можно так сделать:
1) В htaccess перенаправляешь все запросы на index.php
2) Запрашивается example2.com/login
3) Выполняется index.php и анализируется адрес. Через cURL отсылаем куки, GET и POST, и получаем html страницы example.com/login
4) Этот html (или бинарник картинки и любую другую инфу) шлем в браузер
Если ты лох, то можно в паблик это дело пустить и пиздить аккаунты/кредитки
Пример, зеркало рутрекера:
http://rutracker.unblock.ga/forum/viewforum.php?f=1425
Да и прочую хуйню. Коды ответа, таймауты на скрипт и curl, лимит на размер файлов.
Не панацея короч, но можно выебнуться и часть данных грузить не в скрипте, а доверить это дело браузеру (дописать в урлы оригинальный домен)
Еще было один раз "mysql:localhost;dbname=test;charset=utf-8". Тоже неприятная штука, не сразу поймешь в чем дело.
Я правильно понял MVC?
>хотя бы через полгода - год
>хотя бы
>полгода
>год
Можно посоветовать только пойти на хуй. Отрасль полна задротов, которые с младших классов начали дрочить пеку, начали пердолить линь и языки в средних классах, а в старших пилили уже адекватные проекты на 10000+ строк кода. К 25 годам эти задроты становятся такими зверями что тебе в жизни их не догнать.
А я читал, что вид напрямую работает с моделью.
Тебе уже 22. Поздно. Все проебано. У пердоликов в 22 уже по 8-15 лет опыта. Я тебе серьезно говорю. Малолетних пердоликов расплодилось как грязи, как макак в свое время расплодилось (макаки как раз и начинали в 20-25 лет). Сейчас кризис и бизнес спешно заменяет макак на пердоликов и людей с опытом. Сужу по своему опыту. Время макак прошло.
Он врёт, не слушай его. Учи программирование и будет не жизнь, а малина у тебя.
На то, что будет легко и быстро, я не рассчитываю, ибо ничего так просто не дается. Но про
>22 уже
>8 - 15 лет опыта
Это, что с пеленок уже учится.
Гении может быть, да. Но сколько таких.
>Это, что с пеленок уже учится.
Аутсайдеры в школе же. Родители купили компьютер и ребенок завис в нем. Сперва играют, потом ломают игры, потом учатся писать игры параллельно запиливая свои первые сайты. Потом дрочат информатику, лезут в интернет. Потом пилят приложухи для телефонов. Участвуют в олимпиадах и занимаются спортивным программированием. Дохуя таких.
Я скажу нихуя. Чую толстоту. Буду пробовать вообщем. Все равно делать нечего.
>Перетолстил
Да ты охуел! Специально посмотрел по сторонам и не увидел никого кроме пердоликов. Кстати увольняться скоро буду, шеф нанял еще одного пердолика и сократил мне зарплату в 3 раза.
Толще тебя только анон, притворявшейся тянкой-мидлом, которая не могла найти работу даже на джуниора.
>Буду пробовать вообщем.
Добро пожаловать в психбольницу тогда. Встаньте в очередь наполеонов.
class queue extends Model
{
protected function checkNodeExist(){
//проверка на существовании ноды в таблицах "очередь" и "ноды"
}
protected function enqueue($nodesArray = []){
//добавить в очередь
}
protected function dequeue(){
//удалить из очереди (первая запись в таблице)
}
protected function processingQueue(){
//проверить нажата ли пауза, пуста ли "очередь", достигнуто ли максимальное количество записей на выборку.
//достать первую запись из таблицы "очередь"
//если очередь не пуста, то
// {
// checkNodeExist()
// если ноды нет, то getLinks::getLinksArray;
// enqueue()
// добавить ноду в таблицу "ноды"
// добавить ребра в таблицу "edges"
// dequeue()
// обновить вид "очередь" (удалить обработанную ноду и добавить из неё новые ноды)
// обновить вид "ноды"
// }
}
public static function getQueue(){
//"очередь" через websockets отправляется в вид.
}
}
class queue extends Model
{
protected function checkNodeExist(){
//проверка на существовании ноды в таблицах "очередь" и "ноды"
}
protected function enqueue($nodesArray = []){
//добавить в очередь
}
protected function dequeue(){
//удалить из очереди (первая запись в таблице)
}
protected function processingQueue(){
//проверить нажата ли пауза, пуста ли "очередь", достигнуто ли максимальное количество записей на выборку.
//достать первую запись из таблицы "очередь"
//если очередь не пуста, то
// {
// checkNodeExist()
// если ноды нет, то getLinks::getLinksArray;
// enqueue()
// добавить ноду в таблицу "ноды"
// добавить ребра в таблицу "edges"
// dequeue()
// обновить вид "очередь" (удалить обработанную ноду и добавить из неё новые ноды)
// обновить вид "ноды"
// }
}
public static function getQueue(){
//"очередь" через websockets отправляется в вид.
}
}
ОК, уже пишу, жди.
>>716398
>>716397
Это не чат. Не флудите
>>716403
- имена класса пишутся с большой буквы
- не вижу весь код но наследование скорее всего тут не применимо
- непонятно вообще при чем тут вебсокеты и зачем
- непонятно почему getQueue статический
В общем тут в каждой строчке ошибки
>>716207
Зачем тут пхп? Тут наверно нгинкс или сквид уместнее.
>- имена класса пишутся с большой буквы
забыл
>- не вижу весь код но наследование скорее всего тут не применимо
зачем наследование?
>- непонятно вообще при чем тут вебсокеты и зачем
обновленная модель как то должна грузиться в браузер же
>- непонятно почему getQueue статический
Этот метод не продумал еще
>В общем тут в каждой строчке ошибки
И при этом пока все работает
>>714627
>Далее, тут https://github.com/someApprentice/Students/blob/master/app/Test/validAllStudents.php#L9 много однотипных строк. Это плохо тем, что когда мы например добавляем с студента новое поле, мы должны найти это место и не забыть добавить сюда еще одну строку. Это нелогично. надо чтобы была функция которой на вход можно дать студента и которая его проверит.
Намекаешь на то что в этой функции должен быть цикл? Как тогда вызывать нужную функцию валидации для каждого ключа? Делать универсальную функцию которое будет принимать не только значение но и имя поля?
class Entity
{
public $name;
public $password
...
}
class Validations
{
public function validFields(Entity $entity)
{
$errors = array();
foreach ($entity as $key => $value) {
switch ($key) {
case "name":
//valid name and return error
...
return $errors;
}
Или же сделать тоже самое только обернуть это в функцию? У меня уже есть такая, только она подходит для проверки формы, а не студента
https://github.com/someApprentice/Students/blob/master/app/Model/Validators/Validations.php#L151
Я не хотел переделывать или добавлять функцию для тестов
>>714636
>> Стоит ли переделать класс в универсальный хелпер для всех классов, а не только для регистрации? Например, функция перенаправления подойдет для всех классов.
>Перенаправления там вообещ не дложно быть. Редирект - это выдача HTTP заголовков, а взаимодействием с GET/POST данными и формированием ответа занимается контроллер. Хелпер не должен этим заниматься.
У меня контроллер называется RegisterAction, я думал он должен заниматься каким-то одним действием.
Если реализовывать редирект в контроллере а не в хелпере, то получится что хелпер совсем не нужен, потому что даже функцию получения данных из $_POST я перенес в класс формы
https://github.com/someApprentice/Students/blob/master/app/Model/Entity/RegisterStudentForm.php#L13-L85
>>714627
>Далее, тут https://github.com/someApprentice/Students/blob/master/app/Test/validAllStudents.php#L9 много однотипных строк. Это плохо тем, что когда мы например добавляем с студента новое поле, мы должны найти это место и не забыть добавить сюда еще одну строку. Это нелогично. надо чтобы была функция которой на вход можно дать студента и которая его проверит.
Намекаешь на то что в этой функции должен быть цикл? Как тогда вызывать нужную функцию валидации для каждого ключа? Делать универсальную функцию которое будет принимать не только значение но и имя поля?
class Entity
{
public $name;
public $password
...
}
class Validations
{
public function validFields(Entity $entity)
{
$errors = array();
foreach ($entity as $key => $value) {
switch ($key) {
case "name":
//valid name and return error
...
return $errors;
}
Или же сделать тоже самое только обернуть это в функцию? У меня уже есть такая, только она подходит для проверки формы, а не студента
https://github.com/someApprentice/Students/blob/master/app/Model/Validators/Validations.php#L151
Я не хотел переделывать или добавлять функцию для тестов
>>714636
>> Стоит ли переделать класс в универсальный хелпер для всех классов, а не только для регистрации? Например, функция перенаправления подойдет для всех классов.
>Перенаправления там вообещ не дложно быть. Редирект - это выдача HTTP заголовков, а взаимодействием с GET/POST данными и формированием ответа занимается контроллер. Хелпер не должен этим заниматься.
У меня контроллер называется RegisterAction, я думал он должен заниматься каким-то одним действием.
Если реализовывать редирект в контроллере а не в хелпере, то получится что хелпер совсем не нужен, потому что даже функцию получения данных из $_POST я перенес в класс формы
https://github.com/someApprentice/Students/blob/master/app/Model/Entity/RegisterStudentForm.php#L13-L85
Судя по всему никак, раз в доках так и пишут эти сопли.
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-objects.html#by-primary-key
В растерянности от объема информации, не знаю с какого места это все начинать читать и как это учить.
Вроде все исправил.
http://ideone.com/JpQMaO
Сейчас делаю задачу про клавишу shift. Не могу понять почему буквы не делаются заглавными.
http://ideone.com/gtsOLA
Там можно объявить алиас и писать find('MegaApp:User', 1)
http://stackoverflow.com/questions/12835366/namespace-alias-for-doctrine/25285375#25285375
> В растерянности от объема информации, не знаю с какого места это все начинать читать и как это учить.
Я бы рекомендовал найти время и прочесть сначала докуменацию целиком (это легкая часть), а потом расковырять код и понять как она работает. Узнаешь много нового, познакомишься с паттернами проектирования и разными приемами, научишься расширять доктрину как угодно.
Я бы советовал эти проекты для начинающих, которые хотят познакомиться с тем как пишутся реальные фреймворки и библиотеки:
- Symfony Http Foundation
- Pimple
- Doctrine
- Symfony Forms
В идеале неплохо бы в каждом покоыряться в коде. Потому что только в теории ООП, архитектуру и паттерны не изучишь.
Нужен orm.
Pdo сгодится когда кода меньше тысячи строк.
Как ты будешь джойн пяти таблиц делать? Писать sql прямо в коде? Ок, а в следующих проектах, опять писать класс с методами уровня выбратьШотоОтсюдоваИОттудова?
Доктрина великолепна, только уж сильно много букв учить, хочу постепенно, а в документации каша.
>>716829
Я думаю сначала надо найти обычые мануалы и наделать хелловорлдов на все случаи жизни, потом только лезть перечитывать исходные коды.
Пойду гуглить гайды.
Почему у тебя вот здесь не используется результат того, что было получено в цикле:
>$result = preg_replace('/\\sзвездочка([,!?:;.]|(\\.\\.\\.|\\?\\?))\\sзвездочка/u','$1 ', $text);
?
Получается, что было создано в цикле, - просто ушло в никуда.
Советую получающееся в mb_strtoupper(mb_substr($sentence, 0, 1)) . mb_substr($sentence, 1); конкатенировать, а потом поместить вместо $text в preg_replace.
Implode там не нужна вообще.
Из функции лучше всегда делать return - так ТРУЪ.
Вместо \\.\\.\\. лучше использовать квадратные скобки и количество повторений символа.
Вообще регулярка запутанная. Вот ты можешь посимвольно её объяснить?
У доктрины годный мануал, с кучей примеров, просто надо прочесть его целиком. Если ты слаб в англйиском, что плохо, есть русский перевод.
Стоит. АПИ медленнее, бывают ошибки и там есть ограничения на частоту запросов. Ненормально если у тебя каждый запрос к сайту вызывает запрос куда-то на фейсбук. Твой сайт будет торомзить 99% времени.
Добавить обновление данных в расписание cron
Что ты подразумеваешь под "чистым ООП"? ООП может использоваться, а может не использоваться. Серьёзные сайты конечно будет лучше разрабатывать с помощью ООП и паттерна MVC, чтобы потом без особых трудностей ты, или любой другой нормальный программист смог что-нибудь добавить\изменить, не меняя при этом код всего приложения. Или ты под "чистым ООП" подразумеваешь не использовать фреймворки? Тут на самом деле от задач зависит, но почти всегда будет лучше взять фреймворк, чем писать свой велосипед с нуля.
Я имею в виду не использовать фреймворк
>все поля надо проверять, при ошибке ввода подсветить ошибочное поле
Толкните в нужном направлении. У меня форма имеет 8 полей.
В условии написано что ошибки удобно будет записывать в массив $errors, например. Будет удобно валидацию вынести в отдельный метод или даже класс.
Насчет подсветки ошибочных полей, если используешь bootstrap, в нем есть класс has-error, вот пример http://getbootstrap.com/css/#forms-control-validation
Я ошибки понимаю как записывать. Я не понимаю, как с отдельным полем в форме работать.
Каждой ошибке соответствует элемент в массиве. В разметку каждого поля добавь проверку, есть ли в этом поле ошибка, и если она есть - добавляй к полю класс has-error.
Лол, на фирме проект под гиг кода, высоконагруженный сайт, тысячи таблиц. Расскажи мне про тысячу строк, везде только pdo. Естественно не напрямую, все от моделей отделено через классы ридеров-мапперов-адаптеров. А доктрины ваши одни тормоза только добавляют, они как раз для мелких сайтов.
<?
$investorAge = 16;
for ($deposit = 10000; $deposit < 1000000; $deposit *= 1.1) {
echo nl2br("Вкладчику {$investorAge} лет. Сумма на счету: {$deposit}\n");
$investorAge++;
}
Фирме больше 10 лет, через нее уже легион программистов прошел, написаны горы кода, там в коде археологические раскопки проводить можно. Иногда в такие места кода случайно залезешь, который с начала 2000х никто не трогал, и все уже и автора и его назначение давно забыли.
Переписываются актуальные части, которые прибыль приносят, там все на новейших технологиях, с нормальными паттернами и архитектурой. Неактуальные остаются, как были. Их не трогают, чтобы не ломалась совместимость. Вдруг старые клиенты захотят воспользоваться фичами, какими ХХ лет назад пользовались, а там все уже поломалось. Плюс сервис центрам время от времени надо поднимать старые записи, логи, транзакции всякие, историю закупок, там тоже старый код везде. Никто же не будет переписывать код под какой-нибудь продукт, который давно уже снят с производства, а обращения к нему тем не менее идут.
А нанять опытных программистов и нормально написать старые фичи? Все организовать как удобно?
> Никто же не будет переписывать код под какой-нибудь продукт, который давно уже снят с производства
Код же вроде один под любой продукт? Это всего лишь вывод.
>>717132
Переписывать практически всегда глупая затея. Зачем, если он отлажен, протестирован и работает? А ты предлагаешь по сути выбросить деньги на ветер, расходы есть, а в чем выгода не видно.
Плюс, это потребует много ресурсов. Если в код вложено несколько человеко-лет, ты его за месяц вряд ли перепишешь. Переписать мало, надо отладить, протестировать, а тестов наверняка нет,как и документации.
Однако, рефакторинг уместен если приходится дорабатывать старый код и неудачная архитектура осложняет этот процесс, приводит к возникновению ошибок.
Ты думаешь на высоконагруженном сайте опытных программистов не хватает? Несколько команд работает.
>Код же вроде один под любой продукт?
Это ты в терминах мелкого бизнеса все мыслишь. В крупном бизнесе под каждый крупный продукт свой проект пилится, со своей командой, руководством и планом, пишется гора кода с горой фич под него, которая затем в общую систему встраивается. Дальше требования меняются, эту гору кода просто перестают поддерживать и переключаются на другие актуальные задачи, иногда только фиксят, если критические баги. И таких заброшенных проектов в коде несколько тысяч, с годами соответственно все больше.
Ну и попробуй поставить себя на место бизнеса:
- а давайте устроим рефакторинг, возьмем N специалистов и потратим M тугриков
- а что нам это даст? повысится прибыль? будет больше клиентов?
- ну ... код будет лучше и я смогу добавить строчку в резюме в раздел достижений
Да, все так и есть, причем в больших компаниях часто бывает своя инфраструктура:
- свои репозитории
- свои фреймворки и билиотеки которые никто не использует и пишет очередной велосипед
- свои css фреймворки
- свои форки опен сурс проектов
итд.
Да, у нас репозиториев больше 200 уже. Сейчас еще задачу поставили расчистить немного основной код, решили делать это через дополнительные репозитории, скоро их будет уже под 1000. Фреймворки все свои естественно, под нагруженный сайт чужие не пойдут. Давали задачу паре команд тестировать под нагрузкой другие фреймворки, оказалось лучше все самодельное иметь. Опен сурс проекты раньше форкали, сейчас выкинули и оставили только в фронтенде пару незаменимых библиотек, остальное свое написали.
А если смотреть на будущее. Возможно в дальнейшем придется нанимать меньше специалистов, меньше трудозатрат. Не все сразу.
Поэтому актуальные части кода постепенно и переписывают, где основной поток прибыли идет. Код, где прибыль маленькая, никто переписывать не будет. Вся система модульно построена, почти никакие части от других не зависят и коммуницируют между собой через Event сервер. Вполне нормальна ситуация, когда код 2016го года коммуницирует через эвенты с кодом из 2000го.
А что берётся от фреймворков, какие-то отдельные модули?
Частично реализовал аутентификацию...
Как всегда, несколько вопросов:
Я захотел сделать чтобы для залогинивания можно было использовать и почту, и фамилию, и имя, я подошел к этому таким способом:
https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L40
Это можно как-нибудь сократить или это нужно вынести в отдельную функцию чтобы сделать контроллер 'тонким'? Если выносить в отдельную функцию, то кто этим должен заниматься? Контроллер или хелпер?
Что будет если сделать редирект перед остановкой цикла?
https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L52-L54
Кто должен заниматься сравниванием хэша из ДБ и хэша полученного про залогинивании? Класс валидации или контроллер? И если контролер то откуда передавать ошибку об несоответствии? Я пока склоняюсь к тому чтобы передавать ошибку прямо из контроллера https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L59
bump вопросу
Например, вы делаете фронтенд, я делаю бэкенд или наоборот.
Не там людей ищешь, тебе в /wrk/, там есть люди которые могут убеждать заказчиков.
Он старый и плохой. Открой иходники.
>>715211
> У меня тут нет понимания ООП до сих пор, а я, простите, целую неделю занимаюсь сексом с тем, как автоматизированно создавать экземпляры класса...
Цикл надо делать в таких случаях. Для повторяюихся однотипных действий нужен цикл.
>>Это выражение ничего не делает, его можно вообще пропустить.
> Просто для наглядности оставил, хотя PHPStorm, действительно, выводит предупреждение, что эта переменная нигде не употребляется.
Не надо оставлять ничего не делающие выражения. тем более по логике там должно быть $x = 0
>>Это хорошо, но это должно быть не внутри класса Сотрудник. Ведь фактически у тебя есть некая таблица и по ней ищется базовая ставка. Нелогично делать поиск внутри сотрудника, так как это не имеет особого отношения к его зоне ответственности. Это явно какая-то внешняя по отношению к нему штука (Бухгалтерия?). И значит поиск надо делать снаружи, а в сотрудника передавать уже конкретную цифру.
> Блин, я чем дальше в ООП, тем больше долбо... ну, нет смысла продолжать, всё и так понятно.
> Я был на сто процентов уверен, что это как раз зона ответственности сотрудника - количество литров кофе, причисление литров в соответствии с профессией должно быть методом в его классе.
Да, знать сколько он кофе выпил - зона ответственности Сотрудника. А вот искать его по переданной снаружи таблице - не очень. Раз оно ищется по таблице, надо либо делать это снаружи в другом классе либо при создании высчитыать и сохранять цифру либо сделать объект с таблицей внутри и хранить ссылку на него в сотруднике. Чтобы для получения кофе не надо было ничего передавать. Я вижу такие варианты:
- сделать с кофе как с зарплатой, что позволяет в том чисел индивидуально задавать для каждого потребление кофе
- хранить в объекте Сотрудника другой объект (назовем его например Калькулятор), который отвечает за хранение таблицы потребления кофе и умеет по ней считать сколько какая профессия потребляет. Выгода тут в том что если например организация решает поменять потребление кофе инженерами, это достаточно сделать только в одном месте - в Калькуляторе. Также, можно сделать несколько объектов-Калькуляторов (например, особый калькулятор для одного из департаментов). Но индивидуально задать потребление нельзя.
- сделать расчет потребления во внешнем по отношению к Сотруднику объекте. Назовем его например Калькулятор (ничего лучше в голову не пришло). Убрать у сотрудника и департамента метод расчета потребления кофе. Выгода такая же как и в предыдущем случае, но по моему это менее удобно. Ведь теперь тебе надо везде таскать этот Калькулятор и нельзя иметь несколько их экземпляров.
Какие преимущества ты видишь в своем подходе в сравнении с этими?
Если для того чтобы получить потребление кофе мы передаем таблицу, то мы вынуждены будем ее таскать по всей программе, передавать во все функции.
Вот этот метод очень странный:
public function getDepartmentCoffee($employees, $coffeeByProfession)
Почему я должен передавать список сотрудников? Что, Департамент сам не знает кто в нем работает?
Мне кажется тут типичный пример аггрегации - Департамент содержит в себе список Сотрудников, которые в нем работают, и за счет этого умеет высчитывать по ним статистику. Он же изменяет этот список при увольнении/найме работников. Разве это не его зона ответственности? А когда ты хранишь список снаружи, то получается в любую функцию надо отдельно передавать департамент, отдельно список сотрудников в нем.
Также, мне не нравится что надо передавать $coffeeByProfession для расчет потребления кофе. Это неудобно, так как мы должны по всей программе таскать эту таблицу.
>>Это хорошо, но это должно быть не внутри класса Сотрудник. Ведь фактически у тебя есть некая таблица и по ней ищется базовая ставка. Нелогично делать поиск внутри сотрудника, так как это не имеет особого отношения к его зоне ответственности. Это явно какая-то внешняя по отношению к нему штука (Бухгалтерия?). И значит поиск надо делать снаружи, а в сотрудника передавать уже конкретную цифру.
> Блин, я чем дальше в ООП, тем больше долбо... ну, нет смысла продолжать, всё и так понятно.
> Я был на сто процентов уверен, что это как раз зона ответственности сотрудника - количество литров кофе, причисление литров в соответствии с профессией должно быть методом в его классе.
Да, знать сколько он кофе выпил - зона ответственности Сотрудника. А вот искать его по переданной снаружи таблице - не очень. Раз оно ищется по таблице, надо либо делать это снаружи в другом классе либо при создании высчитыать и сохранять цифру либо сделать объект с таблицей внутри и хранить ссылку на него в сотруднике. Чтобы для получения кофе не надо было ничего передавать. Я вижу такие варианты:
- сделать с кофе как с зарплатой, что позволяет в том чисел индивидуально задавать для каждого потребление кофе
- хранить в объекте Сотрудника другой объект (назовем его например Калькулятор), который отвечает за хранение таблицы потребления кофе и умеет по ней считать сколько какая профессия потребляет. Выгода тут в том что если например организация решает поменять потребление кофе инженерами, это достаточно сделать только в одном месте - в Калькуляторе. Также, можно сделать несколько объектов-Калькуляторов (например, особый калькулятор для одного из департаментов). Но индивидуально задать потребление нельзя.
- сделать расчет потребления во внешнем по отношению к Сотруднику объекте. Назовем его например Калькулятор (ничего лучше в голову не пришло). Убрать у сотрудника и департамента метод расчета потребления кофе. Выгода такая же как и в предыдущем случае, но по моему это менее удобно. Ведь теперь тебе надо везде таскать этот Калькулятор и нельзя иметь несколько их экземпляров.
Какие преимущества ты видишь в своем подходе в сравнении с этими?
Если для того чтобы получить потребление кофе мы передаем таблицу, то мы вынуждены будем ее таскать по всей программе, передавать во все функции.
Вот этот метод очень странный:
public function getDepartmentCoffee($employees, $coffeeByProfession)
Почему я должен передавать список сотрудников? Что, Департамент сам не знает кто в нем работает?
Мне кажется тут типичный пример аггрегации - Департамент содержит в себе список Сотрудников, которые в нем работают, и за счет этого умеет высчитывать по ним статистику. Он же изменяет этот список при увольнении/найме работников. Разве это не его зона ответственности? А когда ты хранишь список снаружи, то получается в любую функцию надо отдельно передавать департамент, отдельно список сотрудников в нем.
Также, мне не нравится что надо передавать $coffeeByProfession для расчет потребления кофе. Это неудобно, так как мы должны по всей программе таскать эту таблицу.
Учебник обновляется
>>715497
http://ideone.com/ikDXi6 - миллион в банке
> if ($money >= 1000000) {
Этот код выполняется после набора миллиона. Значит, стоит вынести его и поставить после цикла, if будет не нужен.
Так, ответ правильный.
http://ideone.com/ScQ62W - айфон в кредит
Ответ верный, но код можно бы и упростить. Например, сделать чтобы строки не повторялись 2 раза
> $paymentTotal += $monthlyPayment;
> echo "Шел {$month} месяц.
if можно заменить на расчет выплаты через min или max.
http://ideone.com/2F2Go6 - рост
Все верно.
http://ideone.com/1YPcvW - рулетка
> mt_rand(0, 5);
Лучше бы максимальное значение считалось само, из размера массива.
А так, верно.
>>715289
> nav > ul > li { list-style-type: none; }
list-style-type можно задавать на ul, он наследуется.
> .container { width: 350px; }
Ширина статьи не должна быть фиксированной, а должна тянуться от меню до правого края.
Пункты меню надо расставить чуть подальше друг от друга по вертикали. Заголовок должен быть сделан не жирным шрифтом.
>>715746
Бутстрап только для верстки админок и страниц без дизайна. На обычных сайтах он больше мешается чем помогает.
Осваивать надо много, читай шапку. Если хочешь поскорее то моно попробовать стать верстальщиком, то есть начать с html/css/js/dom.
>>715778
Что там изучать в CMS? Если ты базовые технологии хорошо знаешь, то быстро разберешься.
Учебник обновляется
>>715497
http://ideone.com/ikDXi6 - миллион в банке
> if ($money >= 1000000) {
Этот код выполняется после набора миллиона. Значит, стоит вынести его и поставить после цикла, if будет не нужен.
Так, ответ правильный.
http://ideone.com/ScQ62W - айфон в кредит
Ответ верный, но код можно бы и упростить. Например, сделать чтобы строки не повторялись 2 раза
> $paymentTotal += $monthlyPayment;
> echo "Шел {$month} месяц.
if можно заменить на расчет выплаты через min или max.
http://ideone.com/2F2Go6 - рост
Все верно.
http://ideone.com/1YPcvW - рулетка
> mt_rand(0, 5);
Лучше бы максимальное значение считалось само, из размера массива.
А так, верно.
>>715289
> nav > ul > li { list-style-type: none; }
list-style-type можно задавать на ul, он наследуется.
> .container { width: 350px; }
Ширина статьи не должна быть фиксированной, а должна тянуться от меню до правого края.
Пункты меню надо расставить чуть подальше друг от друга по вертикали. Заголовок должен быть сделан не жирным шрифтом.
>>715746
Бутстрап только для верстки админок и страниц без дизайна. На обычных сайтах он больше мешается чем помогает.
Осваивать надо много, читай шапку. Если хочешь поскорее то моно попробовать стать верстальщиком, то есть начать с html/css/js/dom.
>>715778
Что там изучать в CMS? Если ты базовые технологии хорошо знаешь, то быстро разберешься.
Видимо не все. Изучай исходник пришедшего письма, лог php, логи сервера, почтового демона.
Алсо почему пробел перед From?
>>715805
У ОПа кстати есть пример поиска ссылок через ДОМ: https://github.com/codedokode/pasta-link-checker/blob/master/LinkChecker.php#L72
>>715844
Нужно настроить чтобы он запускал скрипт в консоли. В пхпсторме есть встроенная консоль. Также, возможно, стоит сначала научиться запускать пхп скрипт ручками в настоящей командной строке. Подробности где-то в ОП посте - урок по командной строке и по установке пхп.
>>715877
Не использовать точку либо добавить флаг чтобы она искала и перенос строки.
>>716017
Поставить в форме соовтетствующий action. Если ты нуб, то иди читать теорию по формам в HTML.Алсо может тебе поможет гугление по словам single sign on.
>>716047
Все работает, просто надо запускать скрипт не через веб-сервер, а в командной строке.
>>716285
У меня про MVC написано тут: https://github.com/codedokode/pasta/blob/master/student-list.md#mvc
Модель в МВЦ это не конкретный класс, а часть приложения.
>>716355
Все зависит от тебя
Видимо не все. Изучай исходник пришедшего письма, лог php, логи сервера, почтового демона.
Алсо почему пробел перед From?
>>715805
У ОПа кстати есть пример поиска ссылок через ДОМ: https://github.com/codedokode/pasta-link-checker/blob/master/LinkChecker.php#L72
>>715844
Нужно настроить чтобы он запускал скрипт в консоли. В пхпсторме есть встроенная консоль. Также, возможно, стоит сначала научиться запускать пхп скрипт ручками в настоящей командной строке. Подробности где-то в ОП посте - урок по командной строке и по установке пхп.
>>715877
Не использовать точку либо добавить флаг чтобы она искала и перенос строки.
>>716017
Поставить в форме соовтетствующий action. Если ты нуб, то иди читать теорию по формам в HTML.Алсо может тебе поможет гугление по словам single sign on.
>>716047
Все работает, просто надо запускать скрипт не через веб-сервер, а в командной строке.
>>716285
У меня про MVC написано тут: https://github.com/codedokode/pasta/blob/master/student-list.md#mvc
Модель в МВЦ это не конкретный класс, а часть приложения.
>>716355
Все зависит от тебя
> Намекаешь на то что в этой функции должен быть цикл? Как тогда вызывать нужную функцию валидации для каждого ключа? Делать универсальную функцию которое будет принимать не только значение но и имя поля?
Вот смотри, у меня есть модель Студента и надо ее проверить. У тебя надо писать стену кода, для каждого свойства отдельно. А хотелось бы что-то такое:
$errors = $validator->validateStudent($student);
У меня надо написать 1 строчку, у тебя - восемь, для одного и того же действия. Разве мой подход не лучше?
> foreach ($entity as $key => $value) {
Тут так не получится сделать. Поля могут быть приватными и в этом случае цикл их не переберет, также некотоые поля могут быть не предназначены для валидации. В будущем кто-то может добавить в класс еще поля. Должен быть явный список полей и цикл обходящий этот список. Не надо полагаться на те поля, которые там есть сейчас.
> Или же сделать тоже самое только обернуть это в функцию? У меня уже есть такая, только она подходит для проверки формы, а не студента
> https://github.com/someApprentice/Students/blob/master/app/Model/Validators/Validations.php#L151
Я вижу тут копипасту. Ее надо заменить циклом. Например, сделать массив в котором хранятся имена полей и функции-валидаторы для них.
> Если реализовывать редирект в контроллере а не в хелпере, то получится что хелпер совсем не нужен, потому что даже функцию получения данных из $_POST я перенес в класс формы
И пусть.
Как теперь заполнять форму данными не из POST? Ты выбрал неправильный подход. Ты мог бы сделать чтобы форма умела заполнять себя данными из любого массива, но вместо этого ты сделал что она может работать только с POST. Какая в этом выгода?
Вообще, я мельком посмотрел и вижу что у тебя в коде не очень четко распределены зоны отвественности. В ООП обычно каждый класс имеет свою зону ответсвенности и занимается своим делом.
У тебя есть места, где в классе находится код, который должен быть не в нем. Вот что выглядит сомнительно:
- форма обращается к POST, но работать с POST это задача контроллера, а не формы
- RegistrationHelper.php обращается к GET данным в функции redirect. Почему ты сделал функцию которая может работать только с данными в GET['go'] в то время как мог бы передавать адрес для редиректа через аргументы и получиь универсальную функцию, не завязанную на массив GET?
- в модели студента есть метод хеширования пароля, но не лучше ли если бы он был в хелпере отвечающем за авторизацию? Если посмотреть, этот метод вообще к $this не обращается и непонятно что он там делает в классе студента.
- методы генерации соли и токена - не лучше ли разместить в классе авторизации?
Вот еще логическая ошибка:
https://github.com/someApprentice/Students/blob/master/app/Model/Helper/RegistrationHelper.php#L8
> $post = null;
> $post[$key] = is_scalar($value) ? $value : '';
У тебя в переменной null, а ты пытаешься с ней работать как с массивом. Разве можно в null доабвлять элементы? Конечно PHP тут тихонечко преобразует null в пустой массив (и это плохо, это недоработка в пхп, лучше бы он выдавал ошибку), но возникает вопрос: а почему с самого начала не положить туда пустой массив? Почему null?
> https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L28
тут стена копипасты. Сделай так, чтобы у формы была функция заполнения данных из любого переданного массива (не только из POST) одним действием:
$data = [
'name' => 'Ivan',
'xzzzzzzz' => 123456
];
$form->fillFromArray($data);
А как у тебя это реализовать? Вообще никак, так как форма не умеет брать данные ниоткуда, кроме POST. Непонятно какая выгода от такой привязки, и вообще работу с POST лучше бы оставить в контроллере.
При написании таких методов стоит помнить что нельзя просто копирвоать все поля. Могут быть какие-то поля (например статус модератора) которые не должны заполняться из пришедших данных. Только те поля, что разрешено править.
Еще мне не очень нравится это:
> if (!count(array_filter($registerStudentForm->getErrors()))) {
Это длинно и неудобно. Я вызвал функцию вадидации и теперь должен еще писать какой-то громоздкий код, чтобы узнать есть там ошибки или нет. Предлагаю сделать список ошибок объектом ErrorList такого вида:
$errors->hasErrors() - возвращает есть ли ошибки или нет
$errors->getError('name') - возвращает текст ошибки для поля
$errors->hasError('name') - возвращает есть ли ошибка в данном поле
> https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L42
Здесь ты копипастишь значения из формы в студента. Как минимум, здесь не должно быть копипасты, а должен быть цикл. Также, форма могла бы предоставлять готовые методы для копирования данных в/из студента.
Но если задуматься, какой в этом смысл, хранить 2 копии данных? Не проще ли внутри формы хранить сам объект студента с данными? Тогда и валидация упростится. И вообще все упростится.
Вот представь что мы хотим взять модель студента и заполнить форму его данными. Сколько для этого кода надо? А хотелось бы чтобы это делалось в 2-3 строчки:
$form->setStudent($sudent);
Или наоборот, заполнить студента данными из формы. Хотелось бы чтобы это делаломсь просто и в одну строчку, а не в восемь.
Насчет логина по имени - странная идея. Разве имена уникальны? Нужно тогда как минимум проверять что имя уникально и если нет, не разрешать логиниться через него. Также, надо иметь не 3 поля, а одно, в которое можно ввести что угодно. Это же неудобно, когда в форме лишние поля.
Наконец обрати внимание еще на это место:
> https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L42
Чтобы залогиниться у тебя надо написать сложный код. Почему бы не сделать проще, напрмиер так:
if ($authService->isValidPassword($student, $password)) {
$authService->login($student);
....
}
Простые действия (проверить пароль студента) должны делаться простым кодом.
Для авторизации не стоит исплоьзовать сессию. Она же устаревает и удаляется через 20-30 минут неактивности. Лучше просто использовать куку с id и хешем пароля.
> Намекаешь на то что в этой функции должен быть цикл? Как тогда вызывать нужную функцию валидации для каждого ключа? Делать универсальную функцию которое будет принимать не только значение но и имя поля?
Вот смотри, у меня есть модель Студента и надо ее проверить. У тебя надо писать стену кода, для каждого свойства отдельно. А хотелось бы что-то такое:
$errors = $validator->validateStudent($student);
У меня надо написать 1 строчку, у тебя - восемь, для одного и того же действия. Разве мой подход не лучше?
> foreach ($entity as $key => $value) {
Тут так не получится сделать. Поля могут быть приватными и в этом случае цикл их не переберет, также некотоые поля могут быть не предназначены для валидации. В будущем кто-то может добавить в класс еще поля. Должен быть явный список полей и цикл обходящий этот список. Не надо полагаться на те поля, которые там есть сейчас.
> Или же сделать тоже самое только обернуть это в функцию? У меня уже есть такая, только она подходит для проверки формы, а не студента
> https://github.com/someApprentice/Students/blob/master/app/Model/Validators/Validations.php#L151
Я вижу тут копипасту. Ее надо заменить циклом. Например, сделать массив в котором хранятся имена полей и функции-валидаторы для них.
> Если реализовывать редирект в контроллере а не в хелпере, то получится что хелпер совсем не нужен, потому что даже функцию получения данных из $_POST я перенес в класс формы
И пусть.
Как теперь заполнять форму данными не из POST? Ты выбрал неправильный подход. Ты мог бы сделать чтобы форма умела заполнять себя данными из любого массива, но вместо этого ты сделал что она может работать только с POST. Какая в этом выгода?
Вообще, я мельком посмотрел и вижу что у тебя в коде не очень четко распределены зоны отвественности. В ООП обычно каждый класс имеет свою зону ответсвенности и занимается своим делом.
У тебя есть места, где в классе находится код, который должен быть не в нем. Вот что выглядит сомнительно:
- форма обращается к POST, но работать с POST это задача контроллера, а не формы
- RegistrationHelper.php обращается к GET данным в функции redirect. Почему ты сделал функцию которая может работать только с данными в GET['go'] в то время как мог бы передавать адрес для редиректа через аргументы и получиь универсальную функцию, не завязанную на массив GET?
- в модели студента есть метод хеширования пароля, но не лучше ли если бы он был в хелпере отвечающем за авторизацию? Если посмотреть, этот метод вообще к $this не обращается и непонятно что он там делает в классе студента.
- методы генерации соли и токена - не лучше ли разместить в классе авторизации?
Вот еще логическая ошибка:
https://github.com/someApprentice/Students/blob/master/app/Model/Helper/RegistrationHelper.php#L8
> $post = null;
> $post[$key] = is_scalar($value) ? $value : '';
У тебя в переменной null, а ты пытаешься с ней работать как с массивом. Разве можно в null доабвлять элементы? Конечно PHP тут тихонечко преобразует null в пустой массив (и это плохо, это недоработка в пхп, лучше бы он выдавал ошибку), но возникает вопрос: а почему с самого начала не положить туда пустой массив? Почему null?
> https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L28
тут стена копипасты. Сделай так, чтобы у формы была функция заполнения данных из любого переданного массива (не только из POST) одним действием:
$data = [
'name' => 'Ivan',
'xzzzzzzz' => 123456
];
$form->fillFromArray($data);
А как у тебя это реализовать? Вообще никак, так как форма не умеет брать данные ниоткуда, кроме POST. Непонятно какая выгода от такой привязки, и вообще работу с POST лучше бы оставить в контроллере.
При написании таких методов стоит помнить что нельзя просто копирвоать все поля. Могут быть какие-то поля (например статус модератора) которые не должны заполняться из пришедших данных. Только те поля, что разрешено править.
Еще мне не очень нравится это:
> if (!count(array_filter($registerStudentForm->getErrors()))) {
Это длинно и неудобно. Я вызвал функцию вадидации и теперь должен еще писать какой-то громоздкий код, чтобы узнать есть там ошибки или нет. Предлагаю сделать список ошибок объектом ErrorList такого вида:
$errors->hasErrors() - возвращает есть ли ошибки или нет
$errors->getError('name') - возвращает текст ошибки для поля
$errors->hasError('name') - возвращает есть ли ошибка в данном поле
> https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L42
Здесь ты копипастишь значения из формы в студента. Как минимум, здесь не должно быть копипасты, а должен быть цикл. Также, форма могла бы предоставлять готовые методы для копирования данных в/из студента.
Но если задуматься, какой в этом смысл, хранить 2 копии данных? Не проще ли внутри формы хранить сам объект студента с данными? Тогда и валидация упростится. И вообще все упростится.
Вот представь что мы хотим взять модель студента и заполнить форму его данными. Сколько для этого кода надо? А хотелось бы чтобы это делалось в 2-3 строчки:
$form->setStudent($sudent);
Или наоборот, заполнить студента данными из формы. Хотелось бы чтобы это делаломсь просто и в одну строчку, а не в восемь.
Насчет логина по имени - странная идея. Разве имена уникальны? Нужно тогда как минимум проверять что имя уникально и если нет, не разрешать логиниться через него. Также, надо иметь не 3 поля, а одно, в которое можно ввести что угодно. Это же неудобно, когда в форме лишние поля.
Наконец обрати внимание еще на это место:
> https://github.com/someApprentice/Students/blob/master/app/Controller/LoginAction.php#L42
Чтобы залогиниться у тебя надо написать сложный код. Почему бы не сделать проще, напрмиер так:
if ($authService->isValidPassword($student, $password)) {
$authService->login($student);
....
}
Простые действия (проверить пароль студента) должны делаться простым кодом.
Для авторизации не стоит исплоьзовать сессию. Она же устаревает и удаляется через 20-30 минут неактивности. Лучше просто использовать куку с id и хешем пароля.
Вряд ли
>>717022
Добавление класса к хтмл элементу в шаблоне делается с помощью if.
>>717031
Ну так у вас получается тоже есть свой велсипедный недо ORM.
>>717107
Можно поставить эхо после цикла.
>>717128
да http://www.mediafire.com/download/rcs9c460k6ywj41/pr-threads-69-74.zip
>>717177
Фреймворки вроде симфони действительно "тяжелые" так как по умолчанию содержат много разных модулей, которые загружаются и инициализируются при каждом запросе (увы, не мгновенно) даже если не нужны. Но использовать компоненты от той же Симфони может быть весьма полезно. Например, для форм, http foundation, symfony process, итд.
>>717241
> Это можно как-нибудь сократить или это нужно вынести в отдельную функцию чтобы сделать контроллер 'тонким'? Если выносить в отдельную функцию, то кто этим должен заниматься? Контроллер или хелпер?
Можно, но я думаю что не имеет смысла так как этот код нигде никогда больше не понадобится. Сам код цикла содержит много ошибок.
> Что будет если сделать редирект перед остановкой цикла?
Не знаю, а что-то дложно быть? Редирект это просто выдача заголовка. после него код выполнять особого смысла нет.
> Кто должен заниматься сравниванием хэша из ДБ и хэша полученного про залогинивании? Класс валидации или контроллер? И если контролер то откуда передавать ошибку об несоответствии
А почему контроллер должен считать какие-то хеши. Вот смотри, что у нас на входе:
- пароль в исходном виде
- студент или какая-то информация о нем
Что мы хотим получить?
- правильный ли пароль?
Значит, надо сделать такой метод:
...->ispasswordValid($student, $password)
Почему контроллер должен считать какие-то хеши? Почему он вообще должен знать что у нас исплоьзуются хеши? Это ответственность хелпера (сервиса) авторизации, знать как она устроена. Контроллер просто просит сервис проверить пароль на правильность. Или залогнинить студента. Или разлогинить. Не вдаваясь в детали, как он устроено.
Вряд ли
>>717022
Добавление класса к хтмл элементу в шаблоне делается с помощью if.
>>717031
Ну так у вас получается тоже есть свой велсипедный недо ORM.
>>717107
Можно поставить эхо после цикла.
>>717128
да http://www.mediafire.com/download/rcs9c460k6ywj41/pr-threads-69-74.zip
>>717177
Фреймворки вроде симфони действительно "тяжелые" так как по умолчанию содержат много разных модулей, которые загружаются и инициализируются при каждом запросе (увы, не мгновенно) даже если не нужны. Но использовать компоненты от той же Симфони может быть весьма полезно. Например, для форм, http foundation, symfony process, итд.
>>717241
> Это можно как-нибудь сократить или это нужно вынести в отдельную функцию чтобы сделать контроллер 'тонким'? Если выносить в отдельную функцию, то кто этим должен заниматься? Контроллер или хелпер?
Можно, но я думаю что не имеет смысла так как этот код нигде никогда больше не понадобится. Сам код цикла содержит много ошибок.
> Что будет если сделать редирект перед остановкой цикла?
Не знаю, а что-то дложно быть? Редирект это просто выдача заголовка. после него код выполнять особого смысла нет.
> Кто должен заниматься сравниванием хэша из ДБ и хэша полученного про залогинивании? Класс валидации или контроллер? И если контролер то откуда передавать ошибку об несоответствии
А почему контроллер должен считать какие-то хеши. Вот смотри, что у нас на входе:
- пароль в исходном виде
- студент или какая-то информация о нем
Что мы хотим получить?
- правильный ли пароль?
Значит, надо сделать такой метод:
...->ispasswordValid($student, $password)
Почему контроллер должен считать какие-то хеши? Почему он вообще должен знать что у нас исплоьзуются хеши? Это ответственность хелпера (сервиса) авторизации, знать как она устроена. Контроллер просто просит сервис проверить пароль на правильность. Или залогнинить студента. Или разлогинить. Не вдаваясь в детали, как он устроено.
Жаль что фотографа не побили. Надо принимать законы вводящие строгую ответственность за фотографирование лиц людей без разрешения. Соцсетям запретить публиковать лица без разрешения.
Кстати, на японских сайтах я часто вижу фото где запикселизованы лица. И даже на аватарках бывает часть лица замазана. Какие ответственные.
(это не оффтопик? думаю, не очень, так как имеет непосредственное отошение к информационным технрлогиям и разработке сайтов)
>это не оффтопик
во-первых, да
>Жаль что фотографа не побили
ты ебан?
>
Надо принимать законы вводящие строгую ответственность за фотографирование лиц людей без разрешения
ты ебан
Законы о защите частной жизни уже есть, надо только дополнить. Нельзя собирать и тем более распространять публично информацию например что Вася Иванов ехал в такой-то день туда-то с тем-то. Нельзя делать фото человека, если он основной объект съемки, без его согласия. Потому что есть право на приватность.
Просто наша страна со своими экспериментами по поиску "особого пути" отстала в плане защиты прав граждан от цивилизованных стран лет на 100.
> Хуила добровольно создал аккаунт в соцсети и добровольно выложил свои фотки
> право на приватность
Иди толсти в другом месте.
Шутка про пакет на голове.
Вы там в москве совсем зажрались, сразу видно тебя никогда не материли в транспорте, не били
и не грабили. Посмотреть на него без нотариально заверенного согласия нельзя.
Приезжай к нам в нижние залупки, открой для себя новый дивный мир.
"особый путь" и заключается в целенаправленной поддержке жлобского менталитета и прочих православных ценностей.
Иначе кто ж за них голосовать будет?
Я тут давеча включил национальный канал впервые за десять лет, слегка опешил. Половина рекламы политически ангажированная, газпром прямо
мантры читает ватникам о "силе, стабильности" и т.д. Мне даже послышался отголосок раскатистого "ррряяя! абыррр!" на заднем фоне.
Короче, ты прав но иди нахуй. Нечего лезть к свиньям со своим уставом, изолируйся или переезжай.
https://github.com/codedokode/pasta/blob/master/security/xsrf.md Урок опа не дал ответ на этот вопрос.
В куках пользователя. Ты генерируешь токен, кладешь его в куки и пишешь в форму. При получении данных формы сравниваешь токен в форме с токеном в куках. Злоумышленник не видит кук пользователя и не может в своей форме подставить правильный токен.
Отдельно убедись что при открытии нескольких форм в нескольких вкладках и отправке в проивзольном порядке ничего не ломается.
Вот я вбил MATCH('hello|world'), оно мне пишет
0 => string 'a world of'
1 => string 'all over the world'
и т.д. по алфавиту
Само слово 'world' где-то на 105 месте.
Нужно чтобы точное совпадение было на 1 месте, плюс десяток-другой близких фраз, остальное обрезать.
http://sphinxsearch.com/docs/current/sorting-modes.html
Как этим пользоваться?
>SPH_SORT_RELEVANCE mode, that sorts by relevance in descending order (best matches first);
Где эту штуковину (опцию конфига? константу?) писать?
Это наверное для api, но я подключаюсь по протоколу mysql через pdo.
Погуглил, везде пользуют api, и мне придется, нигде не могу найти как настроить pdo-шный клиент.
В сфинксе есть разные алгоритмы вычисления веса документа. Придется прочесть документацию, чтобы понять чем они различаются и как вычисляются.
http://sphinxsearch.com/docs/current.html#weighting
Там мы видим упоминание функции WEIGHT() (раньше это было псевдополе weight) и разных опций в OPTION. Опциями ты задаешь правила вычисления веса, а потом по нему сортируешь как тебе удобно. Возможностей много.
Насчет SphinxQL и конкретных опций, открывай мануал по SELECT и читай:
http://sphinxsearch.com/docs/current.html#sphinxql-select
Как я понимаю, SphinxQL дает все те же возможности что и АПИ.
Алсо обрати внимание на вспомогательные функции:http://sphinxsearch.com/docs/current.html#expr-func-min-top-weight
> >SPH_SORT_RELEVANCE mode, that sorts by relevance in descending order (best matches first);
> Где эту штуковину (опцию конфига? константу?) писать?
В OPTION
HTML,CSS,PHP,Jquery + парочка фреймворков (Wordpress и Joomla нормально будет ? ) джуном.
Открой вакансии и почитай, чего глупые вопросы задавать в треде, в котором работает только ОП?
Сайт визитка/интернет-магазин/ что-то ещё?
Программисты не делают "портфолио", это для дизайнеров/верстальщиков, тебе в /web.
Я делаю.
Кому нужен твой гитхаб с хелловорлдами уровня задачи про список студентов?
Разве что в качестве фильтра, чтобы не тратить время на совсем умственно отсталых.
От собеседования с пристрастием наличие пары блогов на гитхабе не избавит.
И это не "портфолио" (откуда вообще откопали это слово?), скорее примеры работ.
Хотя опять-таки они никому особо не нужны. Большие проекты в публичный доступ не выкладывают, а мелочь типа файлообменников разве что как показатель активности и трудолюбия.
Портфолио (дизайн сайта + верстка) требуют веб-студии, но они не имеют отношения к программированию.
Теперь проблема в том, что не выводится переменная в хрефе.
<li><a href="index.php?action=logout&token=<?=$token?>">Выход</a></li>
В url так и выводится ...<?=$token?>. Как это лечить?
Аноны, вышла visual studio code в том числе с поддержкой веб-языков: https://m.habrahabr.ru/company/microsoft/blog/281667/
Кто-нибудь пробовал? В плане технологий там в екзешник запакован браузер (webview) и HTML/CSS/JS код.
Там есть несколько похожих платформ: node-webkit, electron (основа atom), платформы для того чтобы делать мобильные приложения на основе веб-технологий. Что меня беспокоит, а не тяжелые ли они? не тормозят ли? Кто-нибудь тестировал?
Ты случайно не сборочкой пользуешься со старым пхп? Если так. то выкинь ее. Если нет, напиши, какой УРЛ в браузере.
Почему & как & написан? Это спецсимвол вообще-то. Ты HTML изучал? Его надо через хтмл-мнемонику (html entity) записывать.
Да нет вроде, новое стоит. http://localhost:63342/studentlist/public/index.php?action=logout&token==$token?>
У тебя вообще пхп работает? Если создать файл с стрчокой
<?php phpinfo();
и открыть в браузере через localhost...., есть таблица?
А если написать <?= phpinfo() ?> вместо этого?
На чистом апаче тоже самое.
Почитал про ранжирование, ничего не понятно.
Даже в подробной статье разработчиков на хабре.
https://habrahabr.ru/company/sphinx/blog/62287/
>BM25 ... зависит от частот ключевых слов в текущем документе с одной стороны и общем наборе документов (коллекции) с другой
Что такое "общий набор документов (коллекции)"? Все записи индекса, или только текущая выборка?
Неважно, в моем случае частота меня не интересует. Мне нужна степень совпадения посимвольно.
>proximity (вес фразы) ... совсем не учитывает частоты, зато учитывает взаимное расположение ключевых слов запросе и документе.
Вроде то что нужно.
Это были поисковые факторы (bm25 и фактор веса). Теперь режимы ранжирования. Могут комбинировать факторы, например
>SPH_RANK_PROXIMITY_BM25 комбинирует вес фразы с BM25
SPH_RANK_PROXIMITY и SPH_RANK_BM25 вычисляют вес только в зависимости от одного фактора. Тут главное в терминах не запутаться: "proximity" это фактор веса, а конечный вес, или ранг, вычисляется по заданному алгоритму (режиму).
>Второй (SPH_RANK_BM25) честно считает только BM25 (частоту нахождения подстроки в тексте-документе), а веса фразы в каждом совпавшем поле вместо долгих расчетов быстро принимает равными единице.
Стоп, так значит этот вариант скорее вернет вначале выдачи полное совпадение? Единица это же максимальный ранг?
Как тогда будет вычисляться ранг фразы по SPH_RANK_PROXIMITY?
Вот у меня слово 'world' и два документа 'hello world' и 'world'. Какой из них будет выше рангом?
Походу результат будет одинаковый. Вот они приводят пример
1) query = one two three, field = one and two three
field_phrase_weight = 2 (совпала подфраза "two three", длиной 2 ключевых слова)
2) query = one two three, field = one and two and three
field_phrase_weight = 1 (отдельные слова совпадают, но подфразы нет)
Значит 'world' даст один и тот же результат (единицу) и при полном совпадении, и частичном. Или нет? Совпала подфраза "world", длиной одно ключевое слово. Даст единицу? В случае 'hello world' совпадет одно отдельное слово, тоже единица. Значит меня это не устраивает.
Это мы говорим про SPH_RANK_PROXIMITY.
SPH_RANK_BM25 вроде бы колеблется от 0 до 1, и при полном совпадении дает единицу.
Про комбинированный SPH_RANK_PROXIMITY_BM25 ничего не известно. Используется по умолчанию.
Есть еще всякие SPH_RANK_WORDCOUNT, SPH_RANK_MATCHANY, но эти более специфические.
Ладно, с такой документацией придется императивно (методом тыка) выяснять, какой из режимов необходим.
И все вот это ради того чтобы прописать одну несчастную опцию, здорово, да?
Почитал про ранжирование, ничего не понятно.
Даже в подробной статье разработчиков на хабре.
https://habrahabr.ru/company/sphinx/blog/62287/
>BM25 ... зависит от частот ключевых слов в текущем документе с одной стороны и общем наборе документов (коллекции) с другой
Что такое "общий набор документов (коллекции)"? Все записи индекса, или только текущая выборка?
Неважно, в моем случае частота меня не интересует. Мне нужна степень совпадения посимвольно.
>proximity (вес фразы) ... совсем не учитывает частоты, зато учитывает взаимное расположение ключевых слов запросе и документе.
Вроде то что нужно.
Это были поисковые факторы (bm25 и фактор веса). Теперь режимы ранжирования. Могут комбинировать факторы, например
>SPH_RANK_PROXIMITY_BM25 комбинирует вес фразы с BM25
SPH_RANK_PROXIMITY и SPH_RANK_BM25 вычисляют вес только в зависимости от одного фактора. Тут главное в терминах не запутаться: "proximity" это фактор веса, а конечный вес, или ранг, вычисляется по заданному алгоритму (режиму).
>Второй (SPH_RANK_BM25) честно считает только BM25 (частоту нахождения подстроки в тексте-документе), а веса фразы в каждом совпавшем поле вместо долгих расчетов быстро принимает равными единице.
Стоп, так значит этот вариант скорее вернет вначале выдачи полное совпадение? Единица это же максимальный ранг?
Как тогда будет вычисляться ранг фразы по SPH_RANK_PROXIMITY?
Вот у меня слово 'world' и два документа 'hello world' и 'world'. Какой из них будет выше рангом?
Походу результат будет одинаковый. Вот они приводят пример
1) query = one two three, field = one and two three
field_phrase_weight = 2 (совпала подфраза "two three", длиной 2 ключевых слова)
2) query = one two three, field = one and two and three
field_phrase_weight = 1 (отдельные слова совпадают, но подфразы нет)
Значит 'world' даст один и тот же результат (единицу) и при полном совпадении, и частичном. Или нет? Совпала подфраза "world", длиной одно ключевое слово. Даст единицу? В случае 'hello world' совпадет одно отдельное слово, тоже единица. Значит меня это не устраивает.
Это мы говорим про SPH_RANK_PROXIMITY.
SPH_RANK_BM25 вроде бы колеблется от 0 до 1, и при полном совпадении дает единицу.
Про комбинированный SPH_RANK_PROXIMITY_BM25 ничего не известно. Используется по умолчанию.
Есть еще всякие SPH_RANK_WORDCOUNT, SPH_RANK_MATCHANY, но эти более специфические.
Ладно, с такой документацией придется императивно (методом тыка) выяснять, какой из режимов необходим.
И все вот это ради того чтобы прописать одну несчастную опцию, здорово, да?
5.6.13
а не *
>SPH_RANK_SPH04, added in version 1.10-beta, is generally based on the default SPH_RANK_PROXIMITY_BM25 ranker, but additionally boosts the matches when they occur in the very beginning or the very end of a text field. Thus, if a field equals the exact query, SPH04 should rank it higher than a field that contains the exact query but is not equal to it. (For instance, when the query is "Hyde Park", a document entitled "Hyde Park" should be ranked higher than a one entitled "Hyde Park, London" or "The Hyde Park Cafe".)
Вроде оно.
Потратил 3 часа времени, чтобы написать "OPTION ranker=sph04"
Занавес. Аплодисменты.
Про BM25 можно прочесть например в википедии:
сначала https://ru.wikipedia.org/wiki/TF-IDF
потом https://ru.wikipedia.org/wiki/Okapi_BM25
На первый взгляд там жуткая математика, но если выписать основную формулу и разобрать ее на части, то видно что это просто число которое вычисляется из:
- того, как часто искомые слова встречаются в одном найденном документе
- какая доля всех документов содержит эти искомые слова
- отношения длины данного документа к средней длине документов
Если ты попробуешь смотреть не с точки зрения математики, а с точки зрения логики, то все будет очень логично: наиболее высокий рейтинг имеет документ, содержащий высокую долю искомых слов, причем редкие слова вносят больший вклад в расчет величины.
> Мне нужна степень совпадения посимвольно.
сфинкс ищет по словам, а не по символам. А BM25 учитывает частоту появления слова и длину документа. Документ состоящий только из искомого слова, будет иметь высокий BM, а если слово редкое то - очень высокий. Удели время гуглению и изчению особенностей этого индекса.
Заметь что ты можешь там написать свою формулу ранжирования, чтобы например вес считался из обоих факторов, BM25 и PROXIMITY. А также, ты можешь задавать веса для разных частей документа, например отдельно для слов в заголовке, отдельно для тела.
Как я понимаю, в ранних поисковых системах что-то вроде BM25 и использовалось для сортировки по "релевантности". Как я понимаю, его преимущество в том что нужные для его расчета данные (частота слова в документе, длина документа, число документов содержащих слово) легко запихиваются в индекс и его можно вычислять не запрашивая полный текст документа, только по данным из индекса.
О! Чтобы лучше понять, как это работает, могу дать такую задачу. Попробуй написать как должен выглядеть поисковый индекс, чтобы по нему можно было считать BM25 и PROXIMITY, не запрашивая из базы полный текст документа (сам понимаешь, запрос тысяч документов из огромной БД быстро работать не будет). В поисковых системах обычно используется индекс такого вида:
слово1: ... информация по этому слову ...
слово2: ....
Там ключом является слово. Это позволяет шардить индекс на много серверов, выделяя каждому серверу определенный диапазон слов (типа сервер 1 = от А до В, сервер 2 = от Г до З и так далее.)
Вот, подумай, как должен выглядеть индекс, что в нем должно храниться, чтобы по нему можно было получить ид документов, где есть все искомые слова, и их рейтинги BM25 и PROXIMITY.
>>proximity (вес фразы) ... совсем не учитывает частоты, зато учитывает взаимное расположение ключевых слов запросе и документе.
> Вроде то что нужно.
Это другое. Это фактор который показывает как близко слова в документе, то есть если ты ищешь hello world то
- для фразы a well known hello world program - коэффициент большой
- а для фразы hello is a word you can say to the world - маленький так как слова далеко друг от друга
Потому он и называется "фактор близости".
> Вот у меня слово 'world' и два документа 'hello world' и 'world'. Какой из них будет выше рангом?
proximity тут не применим так как запрос состоит из одного слова.
> Ладно, с такой документацией придется императивно (методом тыка) выяснять, какой из режимов необходим.
> И все вот это ради того чтобы прописать одну несчастную опцию, здорово, да?
Ты ведь не совсем начинающий, судя по вопросам. Ты еще на заметил? Если ты хочешь работать программистом, то изучением малопонятных документаций тебе придется заниматься ближайшие несколько лет.
Не отчаивайся. Если ты справишься, ты во-первых, чуть-чуть повысишь себе самооценку, во-вторых будешь лучше понимать как работает поиск и как его настраивать.
Про BM25 можно прочесть например в википедии:
сначала https://ru.wikipedia.org/wiki/TF-IDF
потом https://ru.wikipedia.org/wiki/Okapi_BM25
На первый взгляд там жуткая математика, но если выписать основную формулу и разобрать ее на части, то видно что это просто число которое вычисляется из:
- того, как часто искомые слова встречаются в одном найденном документе
- какая доля всех документов содержит эти искомые слова
- отношения длины данного документа к средней длине документов
Если ты попробуешь смотреть не с точки зрения математики, а с точки зрения логики, то все будет очень логично: наиболее высокий рейтинг имеет документ, содержащий высокую долю искомых слов, причем редкие слова вносят больший вклад в расчет величины.
> Мне нужна степень совпадения посимвольно.
сфинкс ищет по словам, а не по символам. А BM25 учитывает частоту появления слова и длину документа. Документ состоящий только из искомого слова, будет иметь высокий BM, а если слово редкое то - очень высокий. Удели время гуглению и изчению особенностей этого индекса.
Заметь что ты можешь там написать свою формулу ранжирования, чтобы например вес считался из обоих факторов, BM25 и PROXIMITY. А также, ты можешь задавать веса для разных частей документа, например отдельно для слов в заголовке, отдельно для тела.
Как я понимаю, в ранних поисковых системах что-то вроде BM25 и использовалось для сортировки по "релевантности". Как я понимаю, его преимущество в том что нужные для его расчета данные (частота слова в документе, длина документа, число документов содержащих слово) легко запихиваются в индекс и его можно вычислять не запрашивая полный текст документа, только по данным из индекса.
О! Чтобы лучше понять, как это работает, могу дать такую задачу. Попробуй написать как должен выглядеть поисковый индекс, чтобы по нему можно было считать BM25 и PROXIMITY, не запрашивая из базы полный текст документа (сам понимаешь, запрос тысяч документов из огромной БД быстро работать не будет). В поисковых системах обычно используется индекс такого вида:
слово1: ... информация по этому слову ...
слово2: ....
Там ключом является слово. Это позволяет шардить индекс на много серверов, выделяя каждому серверу определенный диапазон слов (типа сервер 1 = от А до В, сервер 2 = от Г до З и так далее.)
Вот, подумай, как должен выглядеть индекс, что в нем должно храниться, чтобы по нему можно было получить ид документов, где есть все искомые слова, и их рейтинги BM25 и PROXIMITY.
>>proximity (вес фразы) ... совсем не учитывает частоты, зато учитывает взаимное расположение ключевых слов запросе и документе.
> Вроде то что нужно.
Это другое. Это фактор который показывает как близко слова в документе, то есть если ты ищешь hello world то
- для фразы a well known hello world program - коэффициент большой
- а для фразы hello is a word you can say to the world - маленький так как слова далеко друг от друга
Потому он и называется "фактор близости".
> Вот у меня слово 'world' и два документа 'hello world' и 'world'. Какой из них будет выше рангом?
proximity тут не применим так как запрос состоит из одного слова.
> Ладно, с такой документацией придется императивно (методом тыка) выяснять, какой из режимов необходим.
> И все вот это ради того чтобы прописать одну несчастную опцию, здорово, да?
Ты ведь не совсем начинающий, судя по вопросам. Ты еще на заметил? Если ты хочешь работать программистом, то изучением малопонятных документаций тебе придется заниматься ближайшие несколько лет.
Не отчаивайся. Если ты справишься, ты во-первых, чуть-чуть повысишь себе самооценку, во-вторых будешь лучше понимать как работает поиск и как его настраивать.
Можешь потратить еще полчаса на придумывание как организовать индекс в воображаемой поисковой системе. Не знаю где это тебе пригодится, вдруг на собеседовании спросят или вдруг ты поисковик написать захочешь.
Я например как-то хотел сделать поиск для статических сайтов (то есть без скриптов на сервере, работающий в том числе вообще без сервера, из папки на диске), и там это знание пригодилось бы.
Я ж тебе писал уже индексатор тредов сосача. >>712548
>>717869
Ну я может и ною, но пытаюсь хоть что-то делать.
Вторую (или уже третью? сбился со счета) неделю пишу тот несчастный сервис для тестов по английскому языку.
То доктрина засосет на несколько недель, теперь еще со сфинксом дрючиться. Но это уже прогресс, файлообменник полгода писал.
Кстати придумал как сделать, чтобы оно находило словосочетания, а не отдельные слова. Только вот думаю насколько это будет в плане производительности затратно.
1. Индексируем словарь (колонку с английской версией).
2. Сфинкс возвращает все совпадения (id), в том числе словоформы и словосочетания.
3. По этим id берем записи из mysql. Кстати, а как заставить mysql вернуть записи именно в том порядке, в котором перечислены id в условии IN?
4. Проверяем в цикле для каждого словосочетания, присутствует ли оно как подстрока в тексте (strpos).
5. Таким образом отфильтрованные словосочетания добавляем в таблицу text_dictionary (text_id, article_id).
Чтобы пользователь мог игнорировать некоторые слова, связь пользователя со словарем user_dictionary.
> Кстати, а как заставить mysql вернуть записи именно в том порядке, в котором перечислены id в условии IN?
Либо пересортировать в пхп, либо http://stackoverflow.com/questions/958627/mysql-order-by-values-within-in/958642#958642 . Имей в виду, эта сортировка не использует индексы и работает только для небольшого числа записей.
ну и не очень понимаю, зачем там тебе сфинкс. Сфинкс нужен именно для поиска документов по содержащимся словам. Если тебе нужен словарь (поиск статьи по слову), сфинкс тут бесполезен, тут просто нужен поиск в БД по слову.
Индексатор ты писал, но ведь он не умеет считать ранги. Ну как хочешь, захочешь как нибудь голову поломать - подумай.
Так в словаре не только отдельные слова, но и словосочетания.
Допустим, есть текст
‘Now they are neither fish nor fowl and fewer students are taking them’, he said.
Мне нужно, чтобы была найдена и фраза "neither fish nor fowl", и каждое из одинарных слов по отдельности, но не "be a fish out of water".
Поэтому поиск по каждому слову, например "fish" вернет все варианты, нужно только отфильтровать те что не встречаются в исходном тексте.
Не знаю, как сделать подобное средствами mysql. Кажется, там тоже есть полнотекстовый поиск (в myisam и новейших версиях innodb). Но я не в курсе возможностей и производительности этого поиска, поэтому взял сфинкс, когда-то уже немного работал с ним.
Ну он-то просто дебаггер не настроил, в какой-нибудь неприспособленной IDE работал, но вообще это характерно: сваливать на язык то, что напортачили криворукие макаки прежде тебя.
Застопарился на подключении файлов. Просто include_once'ить это понятно. Как правильно прикрутить autoload с namespace?
В каждом нужном файле прописывать в ручную namespace? Или карту какую сделать?
На словах, как с autoload+namespace организовать роутер?
Нет, всё куда хуже. Это целый каскад криворуких макак - начиная от тех, кто придумал что нужно настраивать, и без этого не поедет, проходя через тех, кто придумал shared-хостинг, но не знал, что нужно настраивать, и заканчивая теми, кто писал конкретную поделку (я) кривыми руками (по-другому "нельзя" by design) на корявой CMS, написанной другими криворукими макаками. Всё. Отсюда можно только выйти, "культуру" изменить невозможно.
https://github.com/codedokode/pasta/blob/master/php/autoload.md
>>717972
Разные культуры есть среди php-разработчиков. Очень много макак, это правда.
Есть и профессиональные программисты, и вторые не так часто пересекаются с первыми.
Если не нравятся условия работы, есть смысл сменить контору на более продвинутую, где нет такого говнокода.
Может тебя просто не берут в хорошую контору? Тогда неча на язык пенять.
>Может тебя просто не берут в хорошую контору? Тогда неча на язык пенять.
Я 3 года как укатился в руби, но бывают иногда такие флэшбэки, видимо, чтобы напомнить мне, что не зря, чтобы ценил настоящее.
На вход скрипта дан введенный пользователем номер телефона в виде 8-911-404-44-11 или +7(812)6786767 (в начале 8 или +7, потом идут 10 цифр и, возможно, какие-то символы). То есть, как и в прошлой задаче, человек вводит номер как хочет. Надо проверить номер на правильность и привести любой номер к единому формату 89114044411 (то есть, заменить +7 на 8 и выкинуть весь мусор вроде пробелов, скобок и минусов, кроме цифр)
Но тут нихуя не понятно. Перечитал кусок про preg_replace раз 20, и все равно нихуя не понятно. Может, стоит объяснять на легком примере?
Ну и как мне это заменять? Я, конечно, понимаю, что "найди и разберись сам", но раз здесь ОП берет на себя функцию объяснения и ответственность за учебник, а я его прочитал, то и вопросы направлю ему.
Если с текстом или эмеилом все понятно - есть текст, сравниваем, есть $0 в емели - сравниваем, то как блядь телефон сравнить, с чем? В нем куча символов, помимо цифр, почему это не написано сразу в учебнике?
А когда сравнишь, на что его заменять-то? Это не кошка и собака, чтоб заменить его на зверя. Пиздец я горю
Разбей задачу на шаги.
Сначала добейся, чтобы находило
+7
+ 7
8
При этом чтобы меняло всё строго на 8.
Попробуй сначала этого добиться.
Затем дальше иди.
ты не про то, с регулярками я уже справился, я про замену.
Но ларчик просто открывался: $reg_exp ="/[^0-9]/";
Для этого перечитай место в учебнике про $1, $2, $3 - про реплейсмент.
>>718049
Вообще ни хера тебя не понял тогда, ну да ладно.
Бамп вопросу. Если не можете ответить на него скажите что используется для автоматической генерации страниц? Для изьятия контента - парсер, а для генерации страниц из награбленного контента что?
генератор страниц
поясни за следующий пункт - там куча условий, жи, ши, запятые эти. Можно их обернуть в массив и скормить регулярке его?
Или тупо в строчку написать все это?
В массив ассоциативный.
'Ошибка' => 'Описание ошибки'
Цикл foreach, который проходится ключами массива (то есть регулярками же) по тексту, а в случае совпадения с текстом - выдаёт значение напротив этого ключа.
Ёбаный четырежды в жопу ОП-членосос совсем охуел. Приходит раз в 1-2 суток чтобы нагородить ёбанную стену текста. Нормальный человек все вопросы к этому времени уже решит. Ёбанные олени-последователи ОПа-пидараса тоже городят ёбанные стены текста. Сил нет уже скрывать это говно.
Ни в одном треде такой хуйни нет.
иди нахуй
Тут такая штука. К сфинксу нельзя делать мультизапрос (или можно?), когда тебе надо найти не одно слово, а много. А вот к mysql можно, через in.
Потому можно конечно и в mysql попробовать сделать таблицы
words <--> dictionary
где words хранит только слова, а dictionary - слова и словосочетания. Связь там будет многие-ко-многим. Не знаю, есть ли выгода, но можно было бы проверить и сравнить.
Просто сфинкс он больше для поиска по документам задумывался, не знаю, подходит ли он тут.
> Но я не в курсе возможностей и производительности этого поиска,
Там нагрузка только при изменении записей, а у тебя словарь редко ведь меняться будет, я бы проверил и сравнил. полнотекстовый поиск не позволяет искать много слов за раз, а решение с таблице выше (вручную созданный индекс по словам) - позволяет.
>>717972
на руби и ноде говорят, то же самое.
>>717992
как писать код без тайп хинтов?
>>717993
В Друпале все сделано через так называемые ноды, и страница - это наследник этой ноды. И есть АПИ которое позволяет их создавать и вставлять в базу. Вот с ними и надо разобраться. Учти, что это небыстро и не одну страницу длкументации придется прочесть. Я когда-то давно что-то такое делал.
>>717994
Там есть АПИ для вставки нод в базу, надо его исопльзовать. Оно позволяет вставить страницу программно.
>>718036
Надо проверить телефон на правильность. если неправильный - так и пиши. Если правильный - убираешь минусы, скобки, пробелы, а +7 в начале заменяешь на 8 и выводишь. То есть надо привести номер к единому формату вроде 81234567890, если он правильный.
> Ну и как мне это заменять?
echo preg_replace('регулярка ищущая минусы, скобки, пробелы в любом числе', '', $number);
>>718066
В этом треде не обсуждаются программы для замусоривания интернета. Также, это давно уже не работает и твой сайт высоко не поднимется, а может даже под бан попадет.
>>718073
Можно массив сделать.
>>718079
Зависть?
Тут такая штука. К сфинксу нельзя делать мультизапрос (или можно?), когда тебе надо найти не одно слово, а много. А вот к mysql можно, через in.
Потому можно конечно и в mysql попробовать сделать таблицы
words <--> dictionary
где words хранит только слова, а dictionary - слова и словосочетания. Связь там будет многие-ко-многим. Не знаю, есть ли выгода, но можно было бы проверить и сравнить.
Просто сфинкс он больше для поиска по документам задумывался, не знаю, подходит ли он тут.
> Но я не в курсе возможностей и производительности этого поиска,
Там нагрузка только при изменении записей, а у тебя словарь редко ведь меняться будет, я бы проверил и сравнил. полнотекстовый поиск не позволяет искать много слов за раз, а решение с таблице выше (вручную созданный индекс по словам) - позволяет.
>>717972
на руби и ноде говорят, то же самое.
>>717992
как писать код без тайп хинтов?
>>717993
В Друпале все сделано через так называемые ноды, и страница - это наследник этой ноды. И есть АПИ которое позволяет их создавать и вставлять в базу. Вот с ними и надо разобраться. Учти, что это небыстро и не одну страницу длкументации придется прочесть. Я когда-то давно что-то такое делал.
>>717994
Там есть АПИ для вставки нод в базу, надо его исопльзовать. Оно позволяет вставить страницу программно.
>>718036
Надо проверить телефон на правильность. если неправильный - так и пиши. Если правильный - убираешь минусы, скобки, пробелы, а +7 в начале заменяешь на 8 и выводишь. То есть надо привести номер к единому формату вроде 81234567890, если он правильный.
> Ну и как мне это заменять?
echo preg_replace('регулярка ищущая минусы, скобки, пробелы в любом числе', '', $number);
>>718066
В этом треде не обсуждаются программы для замусоривания интернета. Также, это давно уже не работает и твой сайт высоко не поднимется, а может даже под бан попадет.
>>718073
Можно массив сделать.
>>718079
Зависть?
Любому программисту приходится каждый день писать в десять раз больше кода, чем вмещается в один пост на ссаче.
А читать и подавно, открой первый раз в жизни любую документацию и ужаснись.
Ты так глупо палишься и выдаешь свой возвраст, что это даже не смешно.
>Зависть?
Не пиши ёбанные стены текста, ёббаный олень. Тебя читают тут.
>>718089
Битой бы тебе башку расколотить, трольчиха. Сил нет уже читать все это говно. Из всего треда мне 5-10 ключевых слов надо. Ебитесь тут со своими задачками сколько угодно, тогда не городите ёбанные стены текста.
>В этом треде не обсуждаются программы для замусоривания интернета.
Здравствуйте. Олень не знает о датамайнинге.
Прохожу собеседование
Есть класс, который умножает 2 числа.
Как сделать так, чтоб он умножал модули чисел, при этом не изменяя код класса, интерфейса и строки с выводом результата.
Т.е. вызов echo $multiplier->multiply(2, -5);
должен вывести 10.
[code]
// Не менять
interface MultiplierInterface
{
// Не менять
public function multiply($x, $y);
}
// Не менять
final class Multiplier implements MultiplierInterface
{
// Не менять
public function multiply($x, $y)
{
return $x * $y;
}
}
$multiplier = new Multiplier();
// Не менять
echo $multiplier->multiply(2, -5);
[/code]
Как изменить метод multiply ? Класс же final там ничего нельзя сделать, наверное?
Прохожу собеседование
Есть класс, который умножает 2 числа.
Как сделать так, чтоб он умножал модули чисел, при этом не изменяя код класса, интерфейса и строки с выводом результата.
Т.е. вызов echo $multiplier->multiply(2, -5);
должен вывести 10.
[code]
// Не менять
interface MultiplierInterface
{
// Не менять
public function multiply($x, $y);
}
// Не менять
final class Multiplier implements MultiplierInterface
{
// Не менять
public function multiply($x, $y)
{
return $x * $y;
}
}
$multiplier = new Multiplier();
// Не менять
echo $multiplier->multiply(2, -5);
[/code]
Как изменить метод multiply ? Класс же final там ничего нельзя сделать, наверное?
Ээ, поехавший злобный инфантил, называвший всех школьниками, не палится.
Поганой метлой таких из треда.
Иди дальше решай свои задачки, даун. Синтезировать нужно, а не повторять за оленем-праведником. ОП плодит дебилов, которым изначально не место в айти.
и как заставить его брать значение ассоциативного массива, если индексы [0] там не работают?
А ты foreach разве ещё не прошёл? Я не помню, где он там, но вроде раньше намного.
Если бы проходил, то такого вопроса бы не было - у тебя явно нет знакомства с ним.
Ну вот запусти такое:
foreach ($array as $key=> $value) {
echo "{$key} --- {$value}\n";
}
Но массив должен быть такого типа, как я выше показывал ('Ошибка' => 'Описание ошибки').
Сделай пока не сложные регулярки, а просто такого типа:
'м' => 'Это буква \"М\"',
'н' => 'Это буква \"Н\"',
'о' => 'Это буква \"О\"'
- и так далее.
Посмотри, как работает foreach.
Это не регулярки, если что.
Это просто чтобы понять работу цикла foreach.
Регулярки сам лучше напиши, чтобы возникло понимание.
да я просто не очень запомнил это, после того как написал загуглил и нашел ответ.
Какие-то аспекты запоминаются, какие-то нет
ОП ЗАЧЕМ ТЫ НА ШАПКУ КОТОВ СТАВИШЬ?
>К сфинксу нельзя делать мультизапрос (или можно?)
WHERE MATCH('first|second|third...')
меня прекрасно устраивает.
Тем временем наткнулся на интересную вещь: браузер съедает некоторые теги.
В частности, тег <tr>, который встречается вне таблиц, почему-то пропадает.
У меня этот тег встречается в xml исходнике словаря, пикрелейтед.
Он там выделяет транскрипцию. Я бы с его помощью как-то выделил транскрипцию стилями, а он куда-то
девается.
Что интересно нестандартный тег <k> почему-то на месте. Зачем так сделали?
Да, в спецификации сказано
https://www.w3.org/TR/html-markup/tr.html
что tr может находиться только внутри родительских table, thead, tbody.
Пробовал добавить table, не помогает. Видимо там еще и td нужен, но с этим уже не знаю что поделать.
Придется править xml, позаменять все <tr> на что-то более приличное и залить обратно.
Исходник 150 мб, десктопный редактор такое еле открывает, а тут еще поиск с заменой.
Надо попробовать что-то консольное, типа sed.
Ребятушки, далеко зашёл с вашим тредом и вот уже заливаю свой первый php-сайт на сервер. Но у меня возникла очень печальная проблема, нигде нету гайдов, как это правильно делается ... Возможно вы мне поможете. На своём компьютере всё делал с помощью WAMP, файлы заливал в папку www и на localhost'е всё прекрасно работало.
Взял пока почти бесплатный хостинг, Хостингер, там есть поддержка PHP, MySQL и phpmyadmin, а большего мне и не надо. Но вот беда, заливаю содержимое папки www в нужное место, а мне пишет: mysqli::mysqli(): (HY000/2003): Can't connect to MySQL server on '11.1.1.11' (111 "Connection refused")
Где 11.1.1.11, это на сколько я понял, айпишник хостингера. Если в коде меняю на домен сайта, то та же ошибка, только айпи меняется на домен. Вот собственно код, от которого исходит ошибка:
$dbhost = '11.1.1.11';
$dbname = 'ххх';
$dbuser = 'root';
$dbpass = '';
$appname = "yyy";
$connection = new mysqli($dbhost, $dbuser, $dbpass, $dbname);
if ($connection->connect_error) die($connection->connect_error);
Как вообще правильно развёртывать приложения с wamp на сервер ? Спасибо.
хотфикс пишет = выдаёт ошибку, когда перехожу на сайт.
Почитай письмо, которое тебе прислали после регистрации. Может там есть полезная информация.
Попробовал вставить и ай-пи сайта и ай-пи сервера и все ай-пи, которые есть в этом письме. Ничего не помогло ...
Если Вы запрашивали доменное имя во время регистрации, пожалуйста помните о том, что Ваш домен не будет доступен моментально. Этот процесс называется делегированием DNS зоны и он может занять до 48 часов. Ваш сайт и электронная почта не будут функционировать до тех пор, пока DNS зона не делегирует.
Блеать ... Просто "...засунь мой хуй себе в рот, я знаю, ты любишь отсасывать !" (с) Хостингер
Нет что-бы написать на столько важное сообщение, прямо у себя на ёбаном сайте, они прислали это в одном из своих писем.
Хотя конечно моя ошибка. Кто ж мне виноват, что я не прочитал. Спасибо анон, за твою мудрость в 4 часа утра. Так бы и ебался дальше ...
Если Вы запрашивали доменное имя во время регистрации, пожалуйста помните о том, что Ваш домен не будет доступен моментально. Этот процесс называется делегированием DNS зоны и он может занять до 48 часов. Ваш сайт и электронная почта не будут функционировать до тех пор, пока DNS зона не делегирует.
Блеать ... Просто "...засунь мой хуй себе в рот, я знаю, ты любишь отсасывать !" (с) Хостингер
Нет что-бы написать на столько важное сообщение, прямо у себя на ёбаном сайте, они прислали это в одном из своих писем.
Хотя конечно моя ошибка. Кто ж мне виноват, что я не прочитал. Спасибо анон, за твою мудрость в 4 часа утра. Так бы и ебался дальше ...
Если Вы запрашивали доменное имя во время регистрации, пожалуйста помните о том, что Ваш домен не будет доступен моментально. Этот процесс называется делегированием DNS зоны и он может занять до 48 часов. Ваш сайт и электронная почта не будут функционировать до тех пор, пока DNS зона не делегирует.
Блеать ... Просто "...засунь мой хуй себе в рот, я знаю, ты любишь отсасывать !" (с) Хостингер
Нет что-бы написать на столько важное сообщение, прямо у себя на ёбаном сайте, они прислали это в одном из своих писем.
Хотя конечно моя ошибка. Кто ж мне виноват, что я не прочитал. Спасибо анон, за твою мудрость в 4 часа утра. Так бы и ебался дальше ...
Если Вы запрашивали доменное имя во время регистрации, пожалуйста помните о том, что Ваш домен не будет доступен моментально. Этот процесс называется делегированием DNS зоны и он может занять до 48 часов. Ваш сайт и электронная почта не будут функционировать до тех пор, пока DNS зона не делегирует.
Блеать ... Просто "...засунь мой хуй себе в рот, я знаю, ты любишь отсасывать !" (с) Хостингер
Нет что-бы написать на столько важное сообщение, прямо у себя на ёбаном сайте, они прислали это в одном из своих писем.
Хотя конечно моя ошибка. Кто ж мне виноват, что я не прочитал. Спасибо анон, за твою мудрость в 4 часа утра. Так бы и ебался дальше ...
А нет, отбой. Бамп вопросу. Удалил сломаные строчки, сайт работает. Если вы не знаете как исправить ошибку, то хоть научите как правильно PHP приложение на сервер залить ...
А то в интернете информации ноль ...
Если их разрешается не использовать, зачем они нужны?Олдфагов-эстетов удовлетворять?
https://www.w3.org/TR/html5/semantics#the-html-element
Нагуглилось по этому поводу только кукарекание петухов про стандарты.
Все равно, мне кажется немного странным добавлять сфинкс для поиска по словам, а не по текстам.
> Придется править xml, позаменять все <tr> на что-то более приличное и залить обратно.
> Исходник 150 мб, десктопный редактор такое еле открывает, а тут еще поиск с заменой.
> Надо попробовать что-то консольное, типа sed.
Во-первых для работы с XML есть DOM. загружаешь дерево и обходишь узлы, генерируя новое HTML-дерево. Какой sed? Во-вторых, конвертировать можно и в процессе вывода, а можно и заранее, добавив колонку с html-версией статьи. В-третьих ты не должен был изначально выводить XML в HTML код. tr в словаре и в HTML значат совсем разные вещи. В четвертых, еще есть XSLT для конвертирования XML в XML/HTML по заданным правилам.
>>718145
> $dbuser = 'root';
Ты задумывался, что значит эта строчка? Никто тебе не даст на бесплатном хостинге доступ из под рута, да еще и без пароля.
Алсо ты используешь mysqli, ты не забываешь после каждого действия с базой ставить if и проверять результат, не вернул ли он false?
>>718152
Это не при чем.
>>718167
Тебе - (пока) нельзя. Пропускать можно только тем кто хорошо разбирается в спецификации HTML и знает правила подстановки тегов.
> Если их разрешается не использовать, зачем они нужны?
Их можно не использовать, так как браузер в каких-то случаях умеет их подставлять. Но надо понимать в каких, чтобы он не подставил их как-то неправильно. Там многие теги необяхательны, например иногда можно пропускать закрывающий p или li.
> , т.к. доктайп хтмл5 выглядит как открывающий хтмл тег
не выглядит, неправда. Доктайп нужен чтобы сказать браузеру что сайт поддерживает стандарты, а не является сделанным до 2000 года сайтом ориентированным на древнюю версию HTML.
Все равно, мне кажется немного странным добавлять сфинкс для поиска по словам, а не по текстам.
> Придется править xml, позаменять все <tr> на что-то более приличное и залить обратно.
> Исходник 150 мб, десктопный редактор такое еле открывает, а тут еще поиск с заменой.
> Надо попробовать что-то консольное, типа sed.
Во-первых для работы с XML есть DOM. загружаешь дерево и обходишь узлы, генерируя новое HTML-дерево. Какой sed? Во-вторых, конвертировать можно и в процессе вывода, а можно и заранее, добавив колонку с html-версией статьи. В-третьих ты не должен был изначально выводить XML в HTML код. tr в словаре и в HTML значат совсем разные вещи. В четвертых, еще есть XSLT для конвертирования XML в XML/HTML по заданным правилам.
>>718145
> $dbuser = 'root';
Ты задумывался, что значит эта строчка? Никто тебе не даст на бесплатном хостинге доступ из под рута, да еще и без пароля.
Алсо ты используешь mysqli, ты не забываешь после каждого действия с базой ставить if и проверять результат, не вернул ли он false?
>>718152
Это не при чем.
>>718167
Тебе - (пока) нельзя. Пропускать можно только тем кто хорошо разбирается в спецификации HTML и знает правила подстановки тегов.
> Если их разрешается не использовать, зачем они нужны?
Их можно не использовать, так как браузер в каких-то случаях умеет их подставлять. Но надо понимать в каких, чтобы он не подставил их как-то неправильно. Там многие теги необяхательны, например иногда можно пропускать закрывающий p или li.
> , т.к. доктайп хтмл5 выглядит как открывающий хтмл тег
не выглядит, неправда. Доктайп нужен чтобы сказать браузеру что сайт поддерживает стандарты, а не является сделанным до 2000 года сайтом ориентированным на древнюю версию HTML.
>как писать код без тайп хинтов?
Использовать Virtus в strict mode. В остальных случаях придется проверять конечно. Но куда больше ломает отсутствие интерфейсов, чем проверка типов.
Нигде, только покупать. Ну или качать сомнительного вида кряк на пиратской бухте на свой страх и риск.
http://ideone.com/Wke5bM
теперь он все заменяет, но не в одном тексте а в каждом только одно значение, как быть?
Есть страничка с формой, которая отображает то, что ей передали POST'ом, если передали: http://pastebin.com/1Zyxp7jt
Заполняю форму, всё работает. Вбиваю в консоли curl -X POST -d '{"name":"FOO","password":"BAR"} http://localhost/post_test/index.php
В результате только форма, переданных данных нет. Где я ошибся?
Впервые я почувствовал как легко можно заработать когда умеешь то чего не умеют другие хотя хули то уметь и даун справится
и сразу же стало очково, а вдруг придется добавить какую, логику, например она хочет в будущем сделать сайт на трех языках или еще что сделать, а у меня все сделано посредством MVC, но фронт контроллеры и контроллеры писал я сам и вот стремаюсь, может пока не поздно стоит перенести сайт на cms какую или фреймворк? Вдруг например она захочет добавить ленту новостей, а для этого ей нужна будет админка, это придется все самому настраивать и писать.
Что посоветуете?
>для одной женщины
сочная милфа?
Если планируешь с этим клиентом еще работать, конечно нужно переписать под cms или фреймворк.
Если нет, то забей, пусть другие макаки страдают. Так все норм пацаны делают.
Господа, один простой вопрос
Легко ли сейчас найти работу оффлайн связанную с программированием на PHP? Один нюанс - без опыта работы вообще. Хедхантер и суперджоб выдают ничтожно малое количество вакансий.
Неужели все так плохо и надо учить джаву?
Погугли про метод POST в протоколе HTTP. Ты обязан указать тип и длину тела запроса (длину я думаю подставляет сам курл).
PHP распознает и разбирает в массив POST запросы только 2 типов: application/x-www-form-encoded (или как-то так) и multipart/form-data (погугли что это за способы кодирования данных). Внезапно, это те же 2 способа которыми кодирует данные браузер.
Если тело запроса использует другой способ, ты можешь его разобрать самостоятельно, прочитав из php://input
Погуглив, подумай: почему ты решил что данные из формы кодируются джейсоном? Яваскрипт-дети думают что все стандарты строятся вокруг их яваскрипта.
>>718331
Смотря как ты знаешь. Без английского конечно шансов меньше.
>>718348
Стоит исопльзовать популярную CMS, например Вордпресс или Друпал (бррррр), так как
1) можно править через админку
2) много специалистов которые могут поддерживать сайт
>>718364
С этим в тред непозвонивших.
>>718377
не, но в принципе, ей нужен сайт визитка, то есть по идее, ну сделаю я сайт на трех языках, ну добавлю там бложик и простенькую админку для добавления статей, но больше то ничего не нужно будет по идее.
Просто у меня чувство, что я велосипед выдумываю, когда есть готовые решения и от этого не по себе.
Но с другой стороны я, например, в вордпрессе не шарю, сейчас только глянул видос как шаблон натягивать, в принципе можно и туда перенести, но у меня, почему-то, вордпресс ассоциируется с какой-то огромной непонятной помойкой, а в моем коде ничего лишнего только то, что нужно. Но, в принципе, еще подумаю.
>сочная милфа?
еще как, я к ней домой зашел, она была после тренировки в лосинах, никого нет, кровать не заправлена, даже представил как я ее на этой кровати
Делаю тоже самое за 30-60 тысяч рублей, затем еще продвигаю за 5-20 ежемесячно.
ну я даже ей о сумме не говорил, она сама определила сумму, вообще думал, если спроит, то просить меньше, я, к сожалению, не владею мастерством пиздежа и навешивания лапши на уши и мне это кажется откровенным наебыванием.
Наглость - второе счастье.
возьми тогда ModX - скормишь ему хтмл, потом додинамируешь статички свои.
Почему же. Клиент покупает у меня N количество часов, работаю в основном, с мелким-средним бизнесом, поэтому им нужны продажи с сайта - я делаю продажи, все довольны.
Разобрался, спасибо.
Пока гуглил наконец-то нашёл нормальный ман по cURL и работе с HTTP, а то что ни статья - то одни скупые описания параметров cURL'а.
Вот: https://curl.haxx.se/docs/httpscripting.html
Тэкс, вот мой айфон: http://ideone.com/QwD35a
Но вот с айпадом всё равно не задаётся: http://ideone.com/1widZx
И вообще. Если я сосчитаю совокупные траты анона внутри функции, то мне останется только привязать результат выполнения функции к соответствующей переменной: $homoCreditTotal = $countTotalCost (1.04, 500);
$softBankTotal = $countTotalCost (1.03, 1000);
$strawberryBankTotal = $countTotalCost (1.02, 7777).
Вопрос: зачем создавать отдельные глобальные переменные $creditSum = 39999 и $payout = 5000, если вне функции нам они ни к чему?
>
>- форма обращается к POST, но работать с POST это задача контроллера, а не формы
>- RegistrationHelper.php обращается к GET данным в функции redirect. Почему ты сделал функцию которая может работать только с данными в GET['go'] в то время как мог бы передавать адрес для редиректа через аргументы и получиь универсальную функцию, не завязанную на массив GET?
Ты говорил что контроллер должен быть тонким, я это воспринял то что должно быть всего действия и меньше лишнего кода. Так значит что в контроллере может быть несколько методов?
Контроллеры делаются тонкими не просто так. Код в контроллерах не повторно используемый, его нельзя использовать для другой цели, кроме обработки запроса. Ну например контроллер регистрации кроме работы с формой регистрации никак по-другому нельзя использовать.
Потому объем контроллеров стараются минимизировать. Ну и код читается лучше, когда он короткий. Потому надо не писать код сплошной стеной, а выделять в нем отдельные действия и выносить их в методы. Если метод универсальный, и его можно использовать где-то еще, то его выносят куда-нибудь в хелпер.
То есть код должен выглядеть так:
- разобрать данные POST и обновить указанные поля в модели студента
- провалидировать модель студента
- если все ок, сохранить студента в базу
- средиректить куда-нибудь
А не так:
- взять данные из POST['name'] и записать в поле name у студента
- взять данные из POST['surname'] и записать в студента
..... еще 20 строк ....
- взять данные из POST['year'] и записать в студента
- проверить поле name у студента
- проверить поле surname у студента
- проверить поле points у студента
..... еще 20 строк ....
- проверить поле year у студента
В таком длинном коде автор не смог разбить его на отдельные действия, а записал все стеной. Читабельность кода значительно ухудшается, а если мы захотим провалидировать студента где-то еще нам надо будет копипастить эти 20 строк.
Также, читай пункт про побочные эффекты: https://github.com/codedokode/pasta/blob/master/good-code.md#Избегай-побочных-эффектов
Твоя функция редиректа явно обладает побочным эффектом, так как ее поведение зависит от параметра go, который ей не передается явно. Какая по твоему выгода от того, что в нее жестко заложено что надо брать адрес из go? Разве не лучше передавать ей его явно через аругменты?
Также, она не повторно используема, так как завязана на параметр go. Ты не можешь использовать ее в другом месте кода. Она намертво привязана к контроллеру регистрации. Почему тогда она не часть этого контроллера?
Также, с POST и GET обычно работает только контроллер.
Получается, есть такие варианты:
- либо перенести ее в контроллер, так как она все равно не повторно исопльзуемая
- либо сделать универсальную функцию редиректа которая получает адрес для перехода через аргументы а не берет его сама откуда-то
Вообще, хелперы (или сервсиы) должны содержать код без обращения ко всяким GET и POST, не привязанный к конкретному контроллеру, а универсальный. Ну например метод валидации модели студента должен работать одинаково независимо от того, откуда взят этот студент - из базы или из данных в POST. И не должен быть привязан к какому-то конкретному контроллеру.
У тебя это явно нарушается, функция редиректа не универсаьная,а заточена только на работу вместе с контроллером регистрации. Раз так, это часть контроллера, а не универсальный хелпер.
Если что-то еще непонятно, спрашивай. Все эти правила они не просто так придуманы, все ради читабельности и удобства написания кода.
Контроллеры делаются тонкими не просто так. Код в контроллерах не повторно используемый, его нельзя использовать для другой цели, кроме обработки запроса. Ну например контроллер регистрации кроме работы с формой регистрации никак по-другому нельзя использовать.
Потому объем контроллеров стараются минимизировать. Ну и код читается лучше, когда он короткий. Потому надо не писать код сплошной стеной, а выделять в нем отдельные действия и выносить их в методы. Если метод универсальный, и его можно использовать где-то еще, то его выносят куда-нибудь в хелпер.
То есть код должен выглядеть так:
- разобрать данные POST и обновить указанные поля в модели студента
- провалидировать модель студента
- если все ок, сохранить студента в базу
- средиректить куда-нибудь
А не так:
- взять данные из POST['name'] и записать в поле name у студента
- взять данные из POST['surname'] и записать в студента
..... еще 20 строк ....
- взять данные из POST['year'] и записать в студента
- проверить поле name у студента
- проверить поле surname у студента
- проверить поле points у студента
..... еще 20 строк ....
- проверить поле year у студента
В таком длинном коде автор не смог разбить его на отдельные действия, а записал все стеной. Читабельность кода значительно ухудшается, а если мы захотим провалидировать студента где-то еще нам надо будет копипастить эти 20 строк.
Также, читай пункт про побочные эффекты: https://github.com/codedokode/pasta/blob/master/good-code.md#Избегай-побочных-эффектов
Твоя функция редиректа явно обладает побочным эффектом, так как ее поведение зависит от параметра go, который ей не передается явно. Какая по твоему выгода от того, что в нее жестко заложено что надо брать адрес из go? Разве не лучше передавать ей его явно через аругменты?
Также, она не повторно используема, так как завязана на параметр go. Ты не можешь использовать ее в другом месте кода. Она намертво привязана к контроллеру регистрации. Почему тогда она не часть этого контроллера?
Также, с POST и GET обычно работает только контроллер.
Получается, есть такие варианты:
- либо перенести ее в контроллер, так как она все равно не повторно исопльзуемая
- либо сделать универсальную функцию редиректа которая получает адрес для перехода через аргументы а не берет его сама откуда-то
Вообще, хелперы (или сервсиы) должны содержать код без обращения ко всяким GET и POST, не привязанный к конкретному контроллеру, а универсальный. Ну например метод валидации модели студента должен работать одинаково независимо от того, откуда взят этот студент - из базы или из данных в POST. И не должен быть привязан к какому-то конкретному контроллеру.
У тебя это явно нарушается, функция редиректа не универсаьная,а заточена только на работу вместе с контроллером регистрации. Раз так, это часть контроллера, а не универсальный хелпер.
Если что-то еще непонятно, спрашивай. Все эти правила они не просто так придуманы, все ради читабельности и удобства написания кода.
Судя по скриншоту, ты не понимаешь что такое цикл и что такое массив, так что тебе сюда:
http://archive-ipq-co.narod.ru/l1/loops.html
http://archive-ipq-co.narod.ru/l1/arrays.html
Так как мне собрать один массив из двух циклов перебора? Ключом должен быть номер поста, а значением сообщение.
http://ideone.com/oJEi0M
То ебанько, что тебе ответило, не имело и малейшего понятия, о чем ты спрашивал.
Захватывай не конкретные поля, а контейнеры. Проходи по ним и смотри нужных детей. Если, конечно, библиотека поддерживает такое.
Написал небольшой отрывок для тестирования куков. На локальном хосте всё работает и выводит "Hello, world", скинул сайт на бесплатный хостинг — выводит только "Hello, ".
Возможно, что хостинг подрезает мои куки, как это проверить?
>При превышении данного ограничения, сайты пользователя переводятся в кэшируемый режим (при запросе веб-серверу не передаются cookie, установленые в браузере, и как следствие не работает механизм сессий PHP)
- мерзкие табы вернулись к нам из глубины веков
- нет классов и конструкторов
- нет значений по умолчанию для полей структур
- значит нельзя гарантировать что у структуры всегда будут валидные значения полей
>>718662
Это можно проверить, открыв dev tools (ctrl + shift + I) в Хроме и посмотрев заголовки на вкладке network
Смотрел уже, хром вообще не отсылает никакие куки при загрузке страницы.
Я думал, что если хостинг отключает их, то хром то всё равно должен их посылать, просто сервер их принимать/обрабатывать не будет.
>>718318
Блин, у тебя, похоже, нет понимания этого всего.
Замени $result на $text, чтобы при каждом проходе брался не оригинальный $text, а уже прошедший через изменения. А echo поставь за пределами цикла, чтобы один раз выдало изменённый текст.
Но это вторая часть задачи, а где первая - ошибка и пояснения к ней?
Табы и всё форматирование — вкусовщина, и как раз Go шлёт на хуй всю вкусовщину, но именно \t и позволяет разработчику в своём редакторе настроить ширину отступа, чтобы не плеваться.
> нет классов и конструкторов
Зачем ты ищешь ООП в языке без ООП?
> нет значений по умолчанию для полей структур
> значит нельзя гарантировать что у структуры всегда будут валидные значения полей
И не может быть, потому что это структуры. Не пиши код, полагающийся на валидность всех полей структуры. Тут чистый C-way.
Но не суть, даже если он бесплатный.
Я хочу понять: мой это косяк, или всё-таки из-за сайта проблемы.
Знаю пока только способ, когда все связанные объекты целиком кладутся в определенное поле owning side объекта, что-то типа
$words = $em->getRepository('Word')->findById($ids);
$text->setWords($words); // связанные объекты целиком кладутся в ArrayCollection в свойстве words
$em->persist($text);
$em->flush();
Это естественно не устраивает, потому что объектов может быть тысячи. Мне бы только ID указать в связях, чтобы сохранились
в таблицу связей.
Ткните в место в документации.
https://refactoring.guru/ru
Код работает с БД. Выполняет элементарную функцию конекта.
На локалхосте всё нработает, на Агаве всё работает. Тот же самый код, на Godaddy выдаёт ошибку
"Call to a member function real_query() on a non-object in..."
Вывожу коннект через var_dump и офигеваю от того что мне выдаёт NULL.
Кто сталкивался? У них особая система конекта к БД?
Чёт я как-то засомневался аж.
- Главная / каталог товаров
- корзина / оформление заказа / оплата
- текстовая страница / акции / доставка и тд.
Сейчас есть html+css. Хочу оживить статику, думаю выбрать cms и натянуть на него верстку. Опыт создания шаблонов только под WP, статья + форма заказа (CF7).
Цель:
1. показывать заказчикам, как готовый проект. Т.е. купить домен, разместить сайт на хостинге.
2. добавить шаблон на templatemonster.com / themeforest.net
Поэтому CMS должна быть бесплатной, и чтобы я смог ее поднять на своем хостинге самостоятельно.
Выбираю между:
joomla / prestashop / opencart / magento
Что посоветуете? Что будет проще для новичка? Перспективнее в качестве опыта для фрилансера?
Нахуя если есть спеицализированные cms под магазины?
use Foo\Bar это тоже что и include_once 'Foo/Bar.php' ?
Нет.
Юзом ты, можно сказать, расшариваешь заюзанный неймспейс в свой. Делается это чисто для удобства, чтобы писать "new Class", а не "new \Foo\Bar\Baz\Class".
И юз ничего не инклудит. Это может делать функция-автолоадер.
> (a < x < b)
Ну давай разберем по частям, тобою написанное ))
a < x вернет boolean (true или false), после чего оный сравнивается с переменной b. Тоисть b сравнивается не с x, а с 0 или 1)))
если бы шаблон сайта был уже собран под wp, я бы еще подумал, а так мне кажется это извращение.
Делать сайт на вп, а затем ставить плагин для инет магазина. Потом ебля с версткой для вп, и для вукома. И две админки. Куча лишних страниц. Пожалуй проще сразу отрезать себе ногу. Но, я почитаю про вуком, спасибо за совет.
А что по поводу создания шаблона для joomla / prestashop / opencart / magento? Что из этого наиболее оптимальный вариант для новичка?
То-есть если я перед объявлением (class Bar (){.. }) напишу namespace Foo;
то извне я смогу обратиться к нему через use Foo\Bar ?
Да.
use Foo\Bar;
...
$var = new Bar();
...
Без юза пришлось бы писать:
...
$var = new \Foo\Bar();
...
Спасибо.
Потому что это равносильно
(($a < $x) < $b)
Первое сравнение вернет false/true (истина/ложь) и получится выражение
false < $b или true < $b
И это явно не то что ты хотел. В программировании нельзя писать любые выражения которые есть в математике. Также, люди делают ошибку когда пишут такое:
if ($x == 1 || 2)
Это интерпретируется как
if ((false или true) || 2)
И таким образов срабатывает при любом знаечнии $x
Какие именно? Линтер - это программа, проверяющая синтаксис, а также опционально ищущая подозрительные места в коде (неиспользуемые переменные, опечатки и тд).
В php встроен линтер который только проверяет правильность синтакиса. Он вызывается через php -l file.php
Для JS есть jshint который не только проверяет синтаксис, но и обращает внимание на сомнительные и скорее всего ошибочные (хоть и соответствующие синтаксису) места.
В ИДЕ тоже бывают встроены линтеры.
Можно написать скрипты для репозитория, чтобы например линетром автоматически проверять все файлы после каждого коммита.
>имеет в виду, что если тебе сейчас 13, то у тебя есть ещё какое-то будущее в пыхапе
Ржака.жпг
Все решения, которые находил - на php
Я нихуя не понимаю, с чего начать, что куда лепить и как все это запускать
Как разобраться,что такое язык программирования с самого блядского нуля? Как начать писать хоть какой-то код? Везде одно говно, мне очень грустно,помогите
Так, у меня получилось начать
Как подобраться к интересующей меня теме?>>719369
К чему она относится?
Спасибо :3
http://ideone.com/1widZx
>кажется немного странным добавлять сфинкс для поиска по словам, а не по текстам.
Я других способов не знаю.
Есть слово "fish", нужно найти запись "fish", "be a fish out of water", "fishing", "neither fish nor flesh". Как сделать средствами mysql не знаю.
>услышал слово xml и возбудился:
>Во-первых для работы с XML есть DOM... во-вторых, в десятых
Я не знаю xml и тем более xlst (что ето вообще такое? аналог css?). И xml там не валидный, после конвертации от него остается всего лишь пара ошметков вида
<k>слово</k><tr>транскрипция</tr>стена текста без тегов
Вот в таком виде это вставляется в базу.
Поэтому мне проще заменить теги в текстовом редакторе и залить дамп обратно.
Ты слишком много требуешь, я же не программист с десятилетним опытом, я учился около года по твоему учебнику, поэтому еще не знаю многих технологий, а те что знаю пока поверхностно.
Новая версия проекта
https://github.com/nsdvw/Memtext
Новый алгоритм перевода и формирования словарей к текстам. Теперь 2 словаря: один с подробными статьями, второй для прохождения тестов, поэтому краткий.
Перевел работу с базой на доктрину. Правда пока коряво, нужно разобраться как там экономно сохранять связанные объекты. Бамп вопросу >>718887
Если будет время, можешь глянуть по общим моментам. Проект считаю близким к завершению, пока замораживаю работу над ним, пошел основательно учить доктрину.
>$paymentTotal = $paymentTotal + $debt + ($debt * $percent) + $serviceFee;
Чё-то как-то больно много.
Ты на Айфон задачу решал вообще?
В Айпаде второй банк как раз для проверки нужен, по нему можно определить, правильно ли всё считает.
Давай-ка показывай своё решение Айфона, лалка.
Можно использовать Entity References. Это по сути прокси-модель - та же самая штука, что заменяет реальные связанные сущности когда ты находишь одну запись по id. Я советую разобраться в концепции прокси-объектов, как они делаются. Прокси нужны, так как объекты связаны друг с другом и если грузить все связанные объекты, то мы в итоге можем загрузить в память половину базы. Однако реализровать их не так просто. Смотри, какие условия должны соблюдаться:
- прокси-модели должны подставляться вместо настоящих в существующий код и ничего не ломать.
- Они должны быть совместимы с тайп-хинтами на оригинальный класс и содержать все те же методы (в идеале и публичные поля, но это малореально реализовать в пхп. Потому модели в доктрине не должны содержать публичных полей).
- При попытке получить какие-то данные, кроме id, или вызвать метод, должна происходить ленивая загрузка данных.
То есть все эти трансформации должны происходить прозрачно для кода. ORM должен создавать для кода видимость что граф объектов находится в памяти и абстрагироваться от наличия БД.
Попробуй подумать как это реализуется. Ну или в коде посмотри.
А возвращаясь к твоему вопросу, можно вместо настоящих записей получить entity reference которые и являются прокси-моделями, и добавить их в коллекцию. доктрине нужны только id так что при сохранении ленивой загрузки не будет (если только ты сам не попытаешься из них что-то получать).
Ну и есть еще второй вариант. Доктрина оптимизирована на упрощение написания кода и абстрагирование от БД, а не на операции над огромным числом объектов. Вполне возможно что в твоей ситуации проще в репозиторий добавить метод который бы обновлял данные в БД напрямую, через SQL запрос. Не забудь что после этого данные в памяти могут устареть и хорошим тоном было бы как-то найти в Entity Map сущность для которой обновлены связи, и как-то ей об этом сказать. Например заменить коллекцию старых связей на новую, ленивую (ленивую чтобы не грузить только что вставленные данные). И большими буквами написать про это в комментарии к методу. Ну подумай в общем, как избежать расхождения в данных .А то если ты сохранишь свой объект потом, вполне возможно что старые данные перезапишут те что в базе. Может конечно и нет. Погугли, там есть методы вроде refresh которые говорят обновить модель данными из БД. Подойдут ли они тут?
Можно использовать Entity References. Это по сути прокси-модель - та же самая штука, что заменяет реальные связанные сущности когда ты находишь одну запись по id. Я советую разобраться в концепции прокси-объектов, как они делаются. Прокси нужны, так как объекты связаны друг с другом и если грузить все связанные объекты, то мы в итоге можем загрузить в память половину базы. Однако реализровать их не так просто. Смотри, какие условия должны соблюдаться:
- прокси-модели должны подставляться вместо настоящих в существующий код и ничего не ломать.
- Они должны быть совместимы с тайп-хинтами на оригинальный класс и содержать все те же методы (в идеале и публичные поля, но это малореально реализовать в пхп. Потому модели в доктрине не должны содержать публичных полей).
- При попытке получить какие-то данные, кроме id, или вызвать метод, должна происходить ленивая загрузка данных.
То есть все эти трансформации должны происходить прозрачно для кода. ORM должен создавать для кода видимость что граф объектов находится в памяти и абстрагироваться от наличия БД.
Попробуй подумать как это реализуется. Ну или в коде посмотри.
А возвращаясь к твоему вопросу, можно вместо настоящих записей получить entity reference которые и являются прокси-моделями, и добавить их в коллекцию. доктрине нужны только id так что при сохранении ленивой загрузки не будет (если только ты сам не попытаешься из них что-то получать).
Ну и есть еще второй вариант. Доктрина оптимизирована на упрощение написания кода и абстрагирование от БД, а не на операции над огромным числом объектов. Вполне возможно что в твоей ситуации проще в репозиторий добавить метод который бы обновлял данные в БД напрямую, через SQL запрос. Не забудь что после этого данные в памяти могут устареть и хорошим тоном было бы как-то найти в Entity Map сущность для которой обновлены связи, и как-то ей об этом сказать. Например заменить коллекцию старых связей на новую, ленивую (ленивую чтобы не грузить только что вставленные данные). И большими буквами написать про это в комментарии к методу. Ну подумай в общем, как избежать расхождения в данных .А то если ты сохранишь свой объект потом, вполне возможно что старые данные перезапишут те что в базе. Может конечно и нет. Погугли, там есть методы вроде refresh которые говорят обновить модель данными из БД. Подойдут ли они тут?
> Поэтому мне проще заменить теги в текстовом редакторе и залить дамп обратно.
Ты думаешь что ты сэкономил время но на самом деле ты обрекаешь себя на рутинную ручную работу при обновлении или любых правках данных. Лучше бы сделать простой скрипт для этого.
>>кажется немного странным добавлять сфинкс для поиска по словам, а не по текстам.
> Я других способов не знаю.
Ну как минимум в голову приходит мысль сделать таблицу words, в ней слова по одному, и связь многие-ко-многим на таблицу со словарем.
Ну не знаю, я просто привык к поисковикам относиться как к черным ящикам с нечеткой логикой, ты им даешь запрос, они тебе дают что-то отдаленно ему соответствующее. Может конечно тут и есть смысл использовать сфинкс как заточенную на поиск штуку чтобы разгрузить БД, но у тебя ведь и нагрузки пока никакой нет.
Сейчас уже наверно смысла менять нет, но хотя бы понять как это сделать теоретически, надо. ну как так можно, элементарная же вещь, точно так же делается как например связь статей с тегами, через многие-ко-многим.
> xlst (что ето вообще такое? аналог css?).
Язык трансформации XML файла в XML или HTML
Там всё верно.
Почему бы тебе не взять и просто не вставить это решение в функцию?
У тебя в Айпаде полный сумбур, который и рядом не стоит с этим решением, а механизм там одинаковый.
Насчет доктрины - идея такая. Она в первую очередь для ускорения написания кода. Чтобы не надо было писать по 100 раз скл запрос для поиска сущности по id. для тяжелых операций наверно лучше использовать SQL запросы, причем доктрина тем и хороша, что не запрещает это делать. Только помни что в памяти могут быть объекты с старым состоянием и надо как-то о них позаботиться - тут есть риск что при чтении из них ты получишь старые данные. При записи скорее всего риска нет так как доктрина держит в памяти копию данных на момент выборки из базы, сравнивает модель с ней и сохраняет только изменившиеся поля. Но лучше вообще избегать таких ситуаций.
Но когда тебе надо получить одну или несколько сущностей, доктрина очень даже хорошо работает. Плюс, она сама умеет искать изменения в объектах.
Также, не забывай что ты можешь писать свои репозитории и добавлять любые удобные методы туда. Правда, ты не контролируешь процесс их создания и не можешь внедрять туда сервисы (или это уже не так?), но может так и правильнее.
Также, знай что у доктрины есть события. Можно отлавливать например persist для сущностей и делать принудительную валидацию и не позволять сохранять плохие данные. Может тебе где это пригодится.
Вот в помощь мануал по оптимизациям:
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/improving-performance.html
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/batch-processing.html (это хороший вариант для кли скриптов где удобство важнее скорости)
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/change-tracking-policies.html
Также, никогда не используй PARTIAL. Это ломает саму идею ООП, так как у тебя в приложении будут "неполноценные" модели у которых нет части полей и это ведет к труднообнаружимым багам.
> CREATE TABLE `shortdict`
> CREATE TABLE `fulldict`
почему сделано 2 таблицы, а не одна? Тут по моему есть риск что нам придется все связи дублировать по 2 раза. не лучше ли сделать одну таблицу? Есть какие-то планы в будущем тут что-то менять, поля добавлять какие-то?
https://github.com/nsdvw/Memtext/blob/master/web/index.php
Тут становится много текста. более того, так как определения сервисов прописаны в индексе, мы не можем например писать консольные скрипты. Пора подумать, а не вынести ли всю инициализацию (начиная с автозагрузки и заканчивая заполнением контейнера) в отдельный bootstrap скрипт?
Насчет этого: https://github.com/nsdvw/Memtext/blob/master/config/yaml/Memtext.Model.ShortDict.dcm.yml - на мой вкус, отдельный конфиг это полная ерунда. Ты правишь класс и надо искать этот конфиг и проверять все ли там поля соответствуют друг другу. Мне кажется гораздо удобнее использовать аннотации (заодно узнаешь что это такое, это взяли из Явы, правда там они аж на уровне языка поддерживаются).
Может YAML это вариант для тех случаев когда ты не можешь править классы? Я вообще не очень понимаю, где он мог бы пригодиться?
https://github.com/nsdvw/Memtext/blob/master/app/Form/LoginForm.php
По идее наверно проверка логина должна быть в сервисе залогинивания, а не в форме, нет?
У Симфони кстати есть модуль валидации (в том числе через аннотации на модели) и модуль форм, но не знаю, не сложноваты ли они. Вот валидацию я бы советовал тебе посмореть, может что пригодится http://symfony.com/doc/current/book/validation.html
Не советую в форме делать так:
public function __construct(Request $request)
лучше сделать отдельный метод parse/processRequest.
В качестве хранилища данных в форме не уместнее ли использовать саму модель? Хотя это сложный вопрос, например Симфони Формс позволяют привязать модель к форме, но там каждый виджет хранит свои данные в себе, а в нужный момент они копируются из/в модель.
Но валидацию ты зря завязываешь на формы. Не понимаю, какая выгода? Разве не лучше иметь отдельный сервис, получающий модель и дающий список ошибок? мне кажется это ты просто с архитектурой накосячил и смешал 2 функции в одном классе.
В сфинксе, ты ищешь по словам только id статей словаря? Тогда нет смысла его использовтаь, быстрее будет наверно сразу искать слова в mysql. сфинкс еще бы имел смысл как кеш, если бы сразу возвращал статьи или если бы ты использовал функции вроде стемминга.
https://github.com/nsdvw/Memtext/blob/master/app/Service/LoginManager.php#L35
> public function validateLoginForm(LoginForm $form)
Это явно ошибка в архитектуре. Почему сервис должен что-то знать о форме (которая тесно связана с view и controller)? А если будет еще одна форма, ты еще метод допишешь?
Должна быть либо валидация вынесена из формы и форма вызвает сервис валидации, либо валидация сделана в форме и форма сама лезет в репозиторий.
Вместо setcookie лучше ставить их через Слим, используя Response/Request (а лучше не их, а только коллекции кук из них). Не знаю как лучше внедрять его - сервиси по идее не должен зависеть от реквеста, так что может надо сделать его не синглтоном в контейнере, а сделать так, чтобы он создавался в начале обработки запроса. нe представь что мы например захотим поменять модель работы приложения и сделать чтобы приложение было консольным, само слушало порт, принимало HTTP запросы, посылало ответы. Тогда setcookie не будет работать, и твой сервис тоже. И сервис не может тогда зависеть от реквеста так как при каждом запросе у нас новый объект реквеста.
В общем, тебе надо бы получше разобраться в том кто за что отвечает, какой жизненный цикл у объекта: существует ли он всегда в одном экземплчре и доступен всегда или например актуален только на время обработки HTTP запроса и привязан к этому запросу.
Насчет доктрины - идея такая. Она в первую очередь для ускорения написания кода. Чтобы не надо было писать по 100 раз скл запрос для поиска сущности по id. для тяжелых операций наверно лучше использовать SQL запросы, причем доктрина тем и хороша, что не запрещает это делать. Только помни что в памяти могут быть объекты с старым состоянием и надо как-то о них позаботиться - тут есть риск что при чтении из них ты получишь старые данные. При записи скорее всего риска нет так как доктрина держит в памяти копию данных на момент выборки из базы, сравнивает модель с ней и сохраняет только изменившиеся поля. Но лучше вообще избегать таких ситуаций.
Но когда тебе надо получить одну или несколько сущностей, доктрина очень даже хорошо работает. Плюс, она сама умеет искать изменения в объектах.
Также, не забывай что ты можешь писать свои репозитории и добавлять любые удобные методы туда. Правда, ты не контролируешь процесс их создания и не можешь внедрять туда сервисы (или это уже не так?), но может так и правильнее.
Также, знай что у доктрины есть события. Можно отлавливать например persist для сущностей и делать принудительную валидацию и не позволять сохранять плохие данные. Может тебе где это пригодится.
Вот в помощь мануал по оптимизациям:
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/improving-performance.html
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/batch-processing.html (это хороший вариант для кли скриптов где удобство важнее скорости)
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/change-tracking-policies.html
Также, никогда не используй PARTIAL. Это ломает саму идею ООП, так как у тебя в приложении будут "неполноценные" модели у которых нет части полей и это ведет к труднообнаружимым багам.
> CREATE TABLE `shortdict`
> CREATE TABLE `fulldict`
почему сделано 2 таблицы, а не одна? Тут по моему есть риск что нам придется все связи дублировать по 2 раза. не лучше ли сделать одну таблицу? Есть какие-то планы в будущем тут что-то менять, поля добавлять какие-то?
https://github.com/nsdvw/Memtext/blob/master/web/index.php
Тут становится много текста. более того, так как определения сервисов прописаны в индексе, мы не можем например писать консольные скрипты. Пора подумать, а не вынести ли всю инициализацию (начиная с автозагрузки и заканчивая заполнением контейнера) в отдельный bootstrap скрипт?
Насчет этого: https://github.com/nsdvw/Memtext/blob/master/config/yaml/Memtext.Model.ShortDict.dcm.yml - на мой вкус, отдельный конфиг это полная ерунда. Ты правишь класс и надо искать этот конфиг и проверять все ли там поля соответствуют друг другу. Мне кажется гораздо удобнее использовать аннотации (заодно узнаешь что это такое, это взяли из Явы, правда там они аж на уровне языка поддерживаются).
Может YAML это вариант для тех случаев когда ты не можешь править классы? Я вообще не очень понимаю, где он мог бы пригодиться?
https://github.com/nsdvw/Memtext/blob/master/app/Form/LoginForm.php
По идее наверно проверка логина должна быть в сервисе залогинивания, а не в форме, нет?
У Симфони кстати есть модуль валидации (в том числе через аннотации на модели) и модуль форм, но не знаю, не сложноваты ли они. Вот валидацию я бы советовал тебе посмореть, может что пригодится http://symfony.com/doc/current/book/validation.html
Не советую в форме делать так:
public function __construct(Request $request)
лучше сделать отдельный метод parse/processRequest.
В качестве хранилища данных в форме не уместнее ли использовать саму модель? Хотя это сложный вопрос, например Симфони Формс позволяют привязать модель к форме, но там каждый виджет хранит свои данные в себе, а в нужный момент они копируются из/в модель.
Но валидацию ты зря завязываешь на формы. Не понимаю, какая выгода? Разве не лучше иметь отдельный сервис, получающий модель и дающий список ошибок? мне кажется это ты просто с архитектурой накосячил и смешал 2 функции в одном классе.
В сфинксе, ты ищешь по словам только id статей словаря? Тогда нет смысла его использовтаь, быстрее будет наверно сразу искать слова в mysql. сфинкс еще бы имел смысл как кеш, если бы сразу возвращал статьи или если бы ты использовал функции вроде стемминга.
https://github.com/nsdvw/Memtext/blob/master/app/Service/LoginManager.php#L35
> public function validateLoginForm(LoginForm $form)
Это явно ошибка в архитектуре. Почему сервис должен что-то знать о форме (которая тесно связана с view и controller)? А если будет еще одна форма, ты еще метод допишешь?
Должна быть либо валидация вынесена из формы и форма вызвает сервис валидации, либо валидация сделана в форме и форма сама лезет в репозиторий.
Вместо setcookie лучше ставить их через Слим, используя Response/Request (а лучше не их, а только коллекции кук из них). Не знаю как лучше внедрять его - сервиси по идее не должен зависеть от реквеста, так что может надо сделать его не синглтоном в контейнере, а сделать так, чтобы он создавался в начале обработки запроса. нe представь что мы например захотим поменять модель работы приложения и сделать чтобы приложение было консольным, само слушало порт, принимало HTTP запросы, посылало ответы. Тогда setcookie не будет работать, и твой сервис тоже. И сервис не может тогда зависеть от реквеста так как при каждом запросе у нас новый объект реквеста.
В общем, тебе надо бы получше разобраться в том кто за что отвечает, какой жизненный цикл у объекта: существует ли он всегда в одном экземплчре и доступен всегда или например актуален только на время обработки HTTP запроса и привязан к этому запросу.
В случае с айфоном я не описываю никаких функций - просто цикл. В цикле можно использовать глобальные переменные, а в расчёте итоговой стоимости айпада все переменные нужно задавать внутри функции. Поэтому я не понимаю, как можно просто взять и всунуть код из задачи с айфоном в функцию.
Я наверняка туплю, но уверен, что ты мне поможешь прозреть, анон.
> $this->repo->findOneBy( ['email'=>
можно писать findOneByEmail(...)
> https://github.com/nsdvw/Memtext/blob/master/app/Service/LoginManager.php#L15
> public function __construct(EntityRepository $repo, $token) {
Ты не видишь тут ничего странного? В тайп хинте ЛогинМенеджер говорит что его устроит любой репозиторий. Тут наверно было бы красивее если был бы конкретный класс репозитория и в зависимости явно указан он.
Также, заведи привычку перед каждым классом (сервисы, модели) писать доккомеент с кратким описанием зачем он нужен и за что отвечает, хотя бы одно предложение. Для контроллеров конечно это не надо , там обычно и так очевидно.
> $entity = '\Memtext\Model\ShortDict';
> $repo = $this->entityManager->getRepository($entity)
надо бы сделать алиас вместо неймспейса
> https://github.com/nsdvw/Memtext/blob/master/app/Helper/HashGenerator.php#L27
> return sha1($salt . $password);
Это мне кое-что напомнило. Когда-то в одной платежной системе была уязвимость . Там параметры запроса на оплату склеивались с закрытым ключом и хешировались, чтобы получить подпись:
$signature = hash($password . $orderId . $sum . $comment . $accountNumber)
Подпись передавалась в форме в скрытом поле с сайта магазина на сервер платежной системы и подтверждала что данные в форме (цена, номер товара, комментарий покупателя, ид магазина в платежной системе) одобрены магазином и покупатель не подменил ничего. То есть пользователь видит подпись но не знает пароль для ее генерации. Можешь ли ты найти тут уязвимость?
https://github.com/nsdvw/Memtext/tree/master/templates
делай подпапки по именам контроллеров (разделов сайта) например. Старайся чтобы вещи относящиеся к одной странице (контроллер, шаблон, скрипты, стили, префиксы ксс классов) формировались по единой системе и не надо было задумываться как тот или иной файл называется.
https://github.com/nsdvw/Memtext/blob/master/templates/csrf_inputs.twig
Нельзя имя сделать фиксированным? Что то я не вижу в чем смысл передавать 2 параметра.
https://github.com/nsdvw/Memtext/blob/master/templates/register_page.twig
Если будет время, прикрути клиентскую HTML5 валидацию. Подумай, как решить проблему с тем что правила валиадции хранятся в 2 разных местах.
> $this->repo->findOneBy( ['email'=>
можно писать findOneByEmail(...)
> https://github.com/nsdvw/Memtext/blob/master/app/Service/LoginManager.php#L15
> public function __construct(EntityRepository $repo, $token) {
Ты не видишь тут ничего странного? В тайп хинте ЛогинМенеджер говорит что его устроит любой репозиторий. Тут наверно было бы красивее если был бы конкретный класс репозитория и в зависимости явно указан он.
Также, заведи привычку перед каждым классом (сервисы, модели) писать доккомеент с кратким описанием зачем он нужен и за что отвечает, хотя бы одно предложение. Для контроллеров конечно это не надо , там обычно и так очевидно.
> $entity = '\Memtext\Model\ShortDict';
> $repo = $this->entityManager->getRepository($entity)
надо бы сделать алиас вместо неймспейса
> https://github.com/nsdvw/Memtext/blob/master/app/Helper/HashGenerator.php#L27
> return sha1($salt . $password);
Это мне кое-что напомнило. Когда-то в одной платежной системе была уязвимость . Там параметры запроса на оплату склеивались с закрытым ключом и хешировались, чтобы получить подпись:
$signature = hash($password . $orderId . $sum . $comment . $accountNumber)
Подпись передавалась в форме в скрытом поле с сайта магазина на сервер платежной системы и подтверждала что данные в форме (цена, номер товара, комментарий покупателя, ид магазина в платежной системе) одобрены магазином и покупатель не подменил ничего. То есть пользователь видит подпись но не знает пароль для ее генерации. Можешь ли ты найти тут уязвимость?
https://github.com/nsdvw/Memtext/tree/master/templates
делай подпапки по именам контроллеров (разделов сайта) например. Старайся чтобы вещи относящиеся к одной странице (контроллер, шаблон, скрипты, стили, префиксы ксс классов) формировались по единой системе и не надо было задумываться как тот или иной файл называется.
https://github.com/nsdvw/Memtext/blob/master/templates/csrf_inputs.twig
Нельзя имя сделать фиксированным? Что то я не вижу в чем смысл передавать 2 параметра.
https://github.com/nsdvw/Memtext/blob/master/templates/register_page.twig
Если будет время, прикрути клиентскую HTML5 валидацию. Подумай, как решить проблему с тем что правила валиадции хранятся в 2 разных местах.
Спасибо за наводку, буду разбираться.
>>719481
Для этого нужно выучить xml+xlst (+ потом еще что-нибудь как всегда всплывет по цепочке), оцениваю в 3-4 недели.
Я не экономлю время, а выстраиваю приоритеты. Не могу (сейчас) бросать все и садиться учить xml ради того, чтобы заменить один тег.
Гораздо приоритетнее сейчас разобраться с доктриной, некоторыми компонентами симфони (а может и со всеми? я так понял, что симфони это модульная штука, ее можно собирать из пакетов как линукс).
>>719490
>как минимум в голову приходит мысль сделать таблицу words, в ней слова по одному, и связь многие-ко-многим на таблицу со словарем.
Мне не пришло в голову. Когда встал вопрос о поиске по слову, сразу вспомнился сфинкс, поэтому быстро и решительно впихнул его. Тем более в сфинксе есть морфология (стемминг), в mysql насколько я знаю только в полнотекстовых индексах (и те работают или в негодном myisam, или новейших innodb; у меня до сих пор 5.5 стоит)
Что дает твой вариант с новой таблицей:
+ не использовать сторонние движки типа сфинкса
(других преимуществ не вижу)
- таблицу со словами придется заполнять, писать скрипт который будет брать каждую запись из большого словаря и бить на слова, сохранять связи; ну это не сложно, но все же лишнее телодвижение
- дополнительные джойны
- (скорее всего) невозможность морфологии; я имею ввиду что мне нужно находить "say" по запросу "said" или "saying"
Что дает сфинкс:
+ прост)))
- нужен сфинкс
>Сейчас уже наверно смысла менять нет
Могу переписать, не проблема. С исполнительностью все в порядке, не хватает опыта, чтобы принимать правильные решения в проектировании. И это естественно, чтобы принять правильное решение, нужно уже быть знакомым с несколькими вариантами, знать их преимущества и недостатки, тогда принять решение.
Я выбираю первое (и единственное) что приходит в голову. Может со временем пройдет, может нет.
Но переписывать не буду, не вижу преимуществ (кроме нежелания иметь дело со сфинксом) у того способа с еще одной связью, который ты предлагаешь.
Кстати, как правильно работать с ветками в гите? Вот у меня есть допустим рабочий проект. Я им доволен, но решаю попробовать кардинально поменять логику (например сменить технологию). Старую версию гробить не хочется. Пока создаю новую ветку для фиксации состояния, а потом переключаюсь обратно на мастер-бранч и делаю что хочу.
Проблема в том, как одновременно внести изменения в то место кода, которое общее у обеих веток? Допустим, я работаю в главной ветке, тут обнаружил баг, который есть и в старой ветке. В главной я его пофикшу, а во второй ветке? Переключаться на нее и дублировать изменения не вариант, наверняка есть хорошее решение. Может быть для кого-то "очевидное".
>>719534
>почему сделано 2 таблицы, а не одна?
Один словарь содержит подробные статьи в качестве перевода. Эти статьи подойдут для того, чтобы разместить их под загруженным пользователем текстом в качестве словаря.
Но этот большой словарь меня не устроит в том месте, где пользователь проходит тесты. Нужно чтобы пользователю показывалось слово (или несколько синонимов через запятую) на русском, а он вбивал ответ на английском.
Не могу использовать большой словарь в этих целях, там много букв и спойлеры ответа.
Да, по-дурацки получается, но ничего не могу поделать. Не могу придумать качественную архитектуру, но очень хочу писать код.
YAML мне очень понравился, чрезвычайно удобно и красиво, в отличие от аннотаций (комментарии php это комментарии, кому пришло в голову засовывать в них логику или конфиги?).
Плюс это новая вещь, захотелось поиграться. И в симфони я глянул, там тоже используется, например для описания роутов, так что в будущем пригодится.
>модуль форм, модуль валидации
В очереди после доктрины. Когда выучу, выкину свои велосипедные формы. Но думаю важно было в учебных целях попытаться написать что-то свое. Хоть какой-то опыт.
>если бы ты использовал функции вроде стемминга
Использую, это важно.
>>719545
>$signature = hash($password . $orderId . $sum . $comment . $accountNumber)
>Можешь ли ты найти тут уязвимость?
Прям заинтриговал. Профессионалы годами не догадывались об этой уязвимости, а я должен знать?
Ну что, тут строка от пользователя ($comment) подставляется прямо в формулу. Мне лично это напомнило sql-инъекцию, когда хакер внедряет код в запрос.
Возможно здесь что-то подобное? Не знаю. Я не разбираюсь в технологиях шифрования.
Есть ли у пользователя возможность "подсунуть" в формулу пустые строки вместо $orderId, $sum, $comment, $accountNumber?
Если есть, он получит хеш этого "закрытого ключа". Из хеша наверное легко получить ключ перебором или из радужных таблиц.
Если я угадал, то в моем случае уязвимости нет, соль не общая для всех пользователей, а уникальная для каждого. Ну и пустой пароль не знаю как мне смогут подсунуть, там же валидация и на клиенте, и на сервере.
Во всяком случае пора учить библиотеку для работы с формами.
>Если будет время, прикрути клиентскую HTML5 валидацию.
На это много времени не нужно, пугают ситуации когда нужно выучить целый язык программирования для того чтобы исправить два символа в строке (xml).
Подставить регулярное выражение в атрибут инпута не сложно.
>как решить проблему с тем что правила валидации хранятся в 2 разных местах.
Вынести куда-то в конфиг. Подставлять в html
<input type="email" pattern="{{ pattern|raw }}"> // для имейла впрочем браузер самостоятельно проверяет наличие @, но нам бы еще точку
В js
<script>var rules = {{ rules|json_encode|raw }}</script>
>Нельзя имя сделать фиксированным? Что то я не вижу в чем смысл передавать 2 параметра.
Этот файл уже нигде не используется, забыл удалить этот мусор, остался от странного пакета, который в слиме предлагают использовать для защиты от csrf
https://github.com/slimphp/Slim-Csrf
Там какая-то петрушка с сессиями, куча параметров для инпутов, решил дропнуть этот пакет и написать свой класс (пока не разберусь с хорошей библиотекой).
Спасибо за наводку, буду разбираться.
>>719481
Для этого нужно выучить xml+xlst (+ потом еще что-нибудь как всегда всплывет по цепочке), оцениваю в 3-4 недели.
Я не экономлю время, а выстраиваю приоритеты. Не могу (сейчас) бросать все и садиться учить xml ради того, чтобы заменить один тег.
Гораздо приоритетнее сейчас разобраться с доктриной, некоторыми компонентами симфони (а может и со всеми? я так понял, что симфони это модульная штука, ее можно собирать из пакетов как линукс).
>>719490
>как минимум в голову приходит мысль сделать таблицу words, в ней слова по одному, и связь многие-ко-многим на таблицу со словарем.
Мне не пришло в голову. Когда встал вопрос о поиске по слову, сразу вспомнился сфинкс, поэтому быстро и решительно впихнул его. Тем более в сфинксе есть морфология (стемминг), в mysql насколько я знаю только в полнотекстовых индексах (и те работают или в негодном myisam, или новейших innodb; у меня до сих пор 5.5 стоит)
Что дает твой вариант с новой таблицей:
+ не использовать сторонние движки типа сфинкса
(других преимуществ не вижу)
- таблицу со словами придется заполнять, писать скрипт который будет брать каждую запись из большого словаря и бить на слова, сохранять связи; ну это не сложно, но все же лишнее телодвижение
- дополнительные джойны
- (скорее всего) невозможность морфологии; я имею ввиду что мне нужно находить "say" по запросу "said" или "saying"
Что дает сфинкс:
+ прост)))
- нужен сфинкс
>Сейчас уже наверно смысла менять нет
Могу переписать, не проблема. С исполнительностью все в порядке, не хватает опыта, чтобы принимать правильные решения в проектировании. И это естественно, чтобы принять правильное решение, нужно уже быть знакомым с несколькими вариантами, знать их преимущества и недостатки, тогда принять решение.
Я выбираю первое (и единственное) что приходит в голову. Может со временем пройдет, может нет.
Но переписывать не буду, не вижу преимуществ (кроме нежелания иметь дело со сфинксом) у того способа с еще одной связью, который ты предлагаешь.
Кстати, как правильно работать с ветками в гите? Вот у меня есть допустим рабочий проект. Я им доволен, но решаю попробовать кардинально поменять логику (например сменить технологию). Старую версию гробить не хочется. Пока создаю новую ветку для фиксации состояния, а потом переключаюсь обратно на мастер-бранч и делаю что хочу.
Проблема в том, как одновременно внести изменения в то место кода, которое общее у обеих веток? Допустим, я работаю в главной ветке, тут обнаружил баг, который есть и в старой ветке. В главной я его пофикшу, а во второй ветке? Переключаться на нее и дублировать изменения не вариант, наверняка есть хорошее решение. Может быть для кого-то "очевидное".
>>719534
>почему сделано 2 таблицы, а не одна?
Один словарь содержит подробные статьи в качестве перевода. Эти статьи подойдут для того, чтобы разместить их под загруженным пользователем текстом в качестве словаря.
Но этот большой словарь меня не устроит в том месте, где пользователь проходит тесты. Нужно чтобы пользователю показывалось слово (или несколько синонимов через запятую) на русском, а он вбивал ответ на английском.
Не могу использовать большой словарь в этих целях, там много букв и спойлеры ответа.
Да, по-дурацки получается, но ничего не могу поделать. Не могу придумать качественную архитектуру, но очень хочу писать код.
YAML мне очень понравился, чрезвычайно удобно и красиво, в отличие от аннотаций (комментарии php это комментарии, кому пришло в голову засовывать в них логику или конфиги?).
Плюс это новая вещь, захотелось поиграться. И в симфони я глянул, там тоже используется, например для описания роутов, так что в будущем пригодится.
>модуль форм, модуль валидации
В очереди после доктрины. Когда выучу, выкину свои велосипедные формы. Но думаю важно было в учебных целях попытаться написать что-то свое. Хоть какой-то опыт.
>если бы ты использовал функции вроде стемминга
Использую, это важно.
>>719545
>$signature = hash($password . $orderId . $sum . $comment . $accountNumber)
>Можешь ли ты найти тут уязвимость?
Прям заинтриговал. Профессионалы годами не догадывались об этой уязвимости, а я должен знать?
Ну что, тут строка от пользователя ($comment) подставляется прямо в формулу. Мне лично это напомнило sql-инъекцию, когда хакер внедряет код в запрос.
Возможно здесь что-то подобное? Не знаю. Я не разбираюсь в технологиях шифрования.
Есть ли у пользователя возможность "подсунуть" в формулу пустые строки вместо $orderId, $sum, $comment, $accountNumber?
Если есть, он получит хеш этого "закрытого ключа". Из хеша наверное легко получить ключ перебором или из радужных таблиц.
Если я угадал, то в моем случае уязвимости нет, соль не общая для всех пользователей, а уникальная для каждого. Ну и пустой пароль не знаю как мне смогут подсунуть, там же валидация и на клиенте, и на сервере.
Во всяком случае пора учить библиотеку для работы с формами.
>Если будет время, прикрути клиентскую HTML5 валидацию.
На это много времени не нужно, пугают ситуации когда нужно выучить целый язык программирования для того чтобы исправить два символа в строке (xml).
Подставить регулярное выражение в атрибут инпута не сложно.
>как решить проблему с тем что правила валидации хранятся в 2 разных местах.
Вынести куда-то в конфиг. Подставлять в html
<input type="email" pattern="{{ pattern|raw }}"> // для имейла впрочем браузер самостоятельно проверяет наличие @, но нам бы еще точку
В js
<script>var rules = {{ rules|json_encode|raw }}</script>
>Нельзя имя сделать фиксированным? Что то я не вижу в чем смысл передавать 2 параметра.
Этот файл уже нигде не используется, забыл удалить этот мусор, остался от странного пакета, который в слиме предлагают использовать для защиты от csrf
https://github.com/slimphp/Slim-Csrf
Там какая-то петрушка с сессиями, куча параметров для инпутов, решил дропнуть этот пакет и написать свой класс (пока не разберусь с хорошей библиотекой).
Не пойму как в строке выудить последнюю букву
https://ideone.com/xceF2e
добавление знака "минус" не помогло ибо первый раз считает с $i = 0 и выводит первый символ
точно, спасибо
Есть две таблицы:
results (название таблицы)
id
num
text
answers (название таблицы)
id
result_id
num
answer
text
description
Вот. И нужно вывести данные из таблицы results, но с условием что в прилегающей к ней таблице answers есть записи равные num = 1 and answer = 'man' плюс num = 27 and answer = 'old'
Если пишу запрос просто (num = 1 and answer = 'man') and (num = 27 and answer = 'old') то получаю пустой результат.
Не могли бы вы помочь?
>Или контейнер для внедрения зависимости (dependency injection).
Я запутался.
В dependency injection нужные классы впрыскиваются в класс.
Дет пример видел, там в комментариях перед классом пишут @inject и перечисляют названия классов. Потом, при создании объекта, какая-та хуета читает ту строку комментариев перед классом и впрыскивает в указанные в @inject классы. Это типа круто, это DI.
Сервис локатор это тот самый контейнер с набором классов, типа pimple, который передается в конструктор класса с зависимостями. Если он тоже DI, то что такое локатор в таком случае?
При url "site.com/1" и "site.com/1/" после form action="/action" получим "site.com/action" и "site.com/1/action" соответственно, но нужен второй вариант в обоих случаях.
Хорошо, тогда советую хотя бы сделать микробенчмарк для сфинкса, чтобы получить данные такого вида:
число слов в запросе | время поиска
10 | ?
100 | ?
1000 | ?
просто проверить что все будет работать как надо. Так как сфинкс может быть не оптимизирован под большие поисковые запросы.
> Для этого нужно выучить xml+xlst (+ потом еще что-нибудь как всегда всплывет по цепочке), оцениваю в 3-4 недели.
Да не факт, с ДОМом ты наверняка знаком по явакрипту, в PHP правда у DOM есть некоторые неприятные особенности для которых нужно искать обходные пути, но я думаю, часов за 10 можно разобраться и написать скрипт. Но даже если делать замену через preg_replace - ты бы мог сделать ее скриптом - это лучше чем врунчную в редакторе, так как позволяет потом обновлять словарь, исправлять баги итд. Стоит стараться автоматизировать все, что автоматизируется.
> Допустим, я работаю в главной ветке, тут обнаружил баг, который есть и в старой ветке. В главной я его пофикшу, а во второй ветке? Переключаться на нее и дублировать изменения не вариант, наверняка есть хорошее решение. Может быть для кого-то "очевидное".
ну конечно есть, это же гит. Вообще конечно таких ситуаций лучше избегать - лучше например периодически домердживать обновления из мастера в твою ветку если они не мешают тебе. Это хорошо работает если ты потом планируешь вмерджить ветку в мастер.
Но если есть необходимость именно вести раздельные ветки - смотри в сторону:
Если один коммит - git cherry-pick (отобрать черешню? ну и название): http://stackoverflow.com/questions/17070293/git-apply-changes-from-one-commit-onto-another-branch
Если много - git rebase (в примере переносят коммиты на более раннюю точку так что их можно вмерджить в обе ветки) http://stackoverflow.com/questions/2474353/how-to-copy-commits-from-one-branch-to-another/2474371#2474371
Также, есть вариант "засквошить" несколько коммитов в один, если они относятся к одной фиче и сделать cherry-pick. В некоторых проектах просят сквошить коммиты в пулл-реквесте в один ради более аккуратной истории в мастере.
Я сам этим не пользовался, так как считаю что это уже перебор. Это уже не программирование, а олимпиада по гиту. Но тебе, как начинающему, стоит в этом разобраться, вдруг пригодится.
Также, если речь о какой-то экспериментальной фиче, их обычно реализуют через feature switch, то есть опцию в конфиге или константу + ифы по коду. В том же Хроме например вообще экспериментальные фичи можно включать через about:flags
Хорошо, тогда советую хотя бы сделать микробенчмарк для сфинкса, чтобы получить данные такого вида:
число слов в запросе | время поиска
10 | ?
100 | ?
1000 | ?
просто проверить что все будет работать как надо. Так как сфинкс может быть не оптимизирован под большие поисковые запросы.
> Для этого нужно выучить xml+xlst (+ потом еще что-нибудь как всегда всплывет по цепочке), оцениваю в 3-4 недели.
Да не факт, с ДОМом ты наверняка знаком по явакрипту, в PHP правда у DOM есть некоторые неприятные особенности для которых нужно искать обходные пути, но я думаю, часов за 10 можно разобраться и написать скрипт. Но даже если делать замену через preg_replace - ты бы мог сделать ее скриптом - это лучше чем врунчную в редакторе, так как позволяет потом обновлять словарь, исправлять баги итд. Стоит стараться автоматизировать все, что автоматизируется.
> Допустим, я работаю в главной ветке, тут обнаружил баг, который есть и в старой ветке. В главной я его пофикшу, а во второй ветке? Переключаться на нее и дублировать изменения не вариант, наверняка есть хорошее решение. Может быть для кого-то "очевидное".
ну конечно есть, это же гит. Вообще конечно таких ситуаций лучше избегать - лучше например периодически домердживать обновления из мастера в твою ветку если они не мешают тебе. Это хорошо работает если ты потом планируешь вмерджить ветку в мастер.
Но если есть необходимость именно вести раздельные ветки - смотри в сторону:
Если один коммит - git cherry-pick (отобрать черешню? ну и название): http://stackoverflow.com/questions/17070293/git-apply-changes-from-one-commit-onto-another-branch
Если много - git rebase (в примере переносят коммиты на более раннюю точку так что их можно вмерджить в обе ветки) http://stackoverflow.com/questions/2474353/how-to-copy-commits-from-one-branch-to-another/2474371#2474371
Также, есть вариант "засквошить" несколько коммитов в один, если они относятся к одной фиче и сделать cherry-pick. В некоторых проектах просят сквошить коммиты в пулл-реквесте в один ради более аккуратной истории в мастере.
Я сам этим не пользовался, так как считаю что это уже перебор. Это уже не программирование, а олимпиада по гиту. Но тебе, как начинающему, стоит в этом разобраться, вдруг пригодится.
Также, если речь о какой-то экспериментальной фиче, их обычно реализуют через feature switch, то есть опцию в конфиге или константу + ифы по коду. В том же Хроме например вообще экспериментальные фичи можно включать через about:flags
> Да, по-дурацки получается, но ничего не могу поделать. Не могу придумать качественную архитектуру, но очень хочу писать код.
Так что тут думать - можно сделать одну таблицу с колонкой-дискиминатором в виде енума, указывающей к какому словарю относится слово. Тут правда тоже свои особенности - нельзя будет делать внешний ключ на конкретный словарь, только на таблицу в целом. Ну это наверно и не надо.
А вообще, ты знаешь 3 паттерна реализации наследования таблиц в SQL? Если нет, нагугли и изучи Class Table Inheritance, Concrete TI и Single TI.
> YAML мне очень понравился, чрезвычайно удобно и красиво, в отличие от аннотаций
Ну ладно
> И в симфони я глянул, там тоже используется, например для описания роутов, так что в будущем пригодится.
Роуты это другое. Роуты удобно описывать в YAML так как тогда у тебя есть один файл со всеми роутами, а когда они раскиданы в виде аннотаций - с этим невозможно нормально работать. Там есть команда которая их сканирует и выводит общую табоицу, но это неудобно.
> я так понял, что симфони это модульная штука, ее можно собирать из пакетов как линукс).
Симфони это компоненты Симфони + фреймворк Симфони, построенный на них. Ну например есть доктрина сама по себе, а есть DoctrineBundle которая является чем-то вроде адаптера доктрины к фреймворку Симфони. Это вообще сильная сторона Симфони, что ты можешь брать только нужные компоненты и строить на них свой фреймворк, так как сама Симфони довольно тяжелая и мне там не все нравится. Например, то что касается авторизации и ролей на мой взгляд переусложнено.
Симфони расширяется за счет бандлов, бандл - это вроде библитеки, но он имеет возможность также встраиваться в фреймворк при подключении, например добавлять свои роуты, обработчики событий, добавлять сервисы в контейнер, добавлять виджеты для форм, и тд. Ну например бандл для автокомплита добавляет роут, который отвечает на аякс-запросы от виджета.
Бандлы это конечно уже часть фреймворка симфони, компоненты никак с бандлами не связаны.
Вообще то, что они смогли вынести код в изолированные компоненты, это говорит о хорошей архитектуре. И другие люди теперь могут на этих компонентах что-то свое строить. Хотя находятся конечно и те, кто считает что там слишком много абстракций, фабрик. Но это все делается для максимальной гибкости, чтобы ты мог менять поведение любой части.
Не все фреймворки так умеют. Например, Юи - это монолит, ты либо его используешь, либо нет, и например просто так взять от него модели active record вряд ли получится. Ну, у них другие цели.
Зенд также компонентный, раньше, продвинутые программисты именно их использовали, но с второй версии Симфони стала популярнее зенда.
Советую поизучать их, в том числе код посмотреть, может что интересного подчерпнешь.
> Есть ли у пользователя возможность "подсунуть" в формулу пустые строки вместо $orderId, $sum, $comment, $accountNumber?
Есть. Там такой сценарий:
- пользователь на сайте магазина оформляет заказ
- магазин подсчитывает все и создает форму оплаты, в скрытые поля которой записывает все эти параметры заказа + подпись. В качестве action указывается обработчик на сайте платежной системы.
- пользователь в браузере отправляет форму на сайт платежной системы, и кончено он может менять содержимое полей. Но подпись как раз и предназначена для защиты от этого.
> Если есть, он получит хеш этого "закрытого ключа". Из хеша наверное легко получить ключ перебором или из радужных таблиц.
Ключ может быть длинный, так же некоторые параметры менять не получится, например id заказа, номер счета и сумма заказа может генерироваться на сервере. А комментарий допустим вводится пользователем где-то раньше.
> Если я угадал
нет
> то в моем случае уязвимости нет, соль не общая для всех пользователей,
В твоем случае нет, просто я вспомнил про подвох при использовании хеша как цифровой подписи.
> <input type="email" pattern="{{ pattern|raw }}
Для емайл наверно все таки не надо писать паттерн.
> pattern="{{ pattern|raw }}"
raw кстати это неправильно. Значения атрибутов экранируются.
>>719724
Можно сделать 2 джойна на answers. Или сджойнить, сгруппировать и проверить через HAVING.
>>719739
> Дет пример видел, там в комментариях перед классом пишут @inject и перечисляют названия классов.
> Потом, при создании объекта, какая-та хуета читает ту строку комментариев перед классом и впрыскивает в указанные в @inject классы. Это типа круто, это DI.
Нет. Это autowiring для DI или что -то вроде этого. Это просто вспомогательный синтаксис.
> Сервис локатор это тот самый контейнер с набором классов, типа pimple, который передается в конструктор класса с зависимостями. Если он тоже DI, то что такое локатор в таком случае?
Сервис локатор это когда ты передаешь контейнер в конструктор и это плохо (разве что в контроллерах так делают). Читай мой урок https://gist.github.com/codedokode/e1d31a31b37d5f635057
Ты по моему все усложняешь. DI это когда зависимости передаются в класс снаружи. Вот и все. Для DI не требуются контейнеры или аннотации. У тебя каша в голове.
> Да, по-дурацки получается, но ничего не могу поделать. Не могу придумать качественную архитектуру, но очень хочу писать код.
Так что тут думать - можно сделать одну таблицу с колонкой-дискиминатором в виде енума, указывающей к какому словарю относится слово. Тут правда тоже свои особенности - нельзя будет делать внешний ключ на конкретный словарь, только на таблицу в целом. Ну это наверно и не надо.
А вообще, ты знаешь 3 паттерна реализации наследования таблиц в SQL? Если нет, нагугли и изучи Class Table Inheritance, Concrete TI и Single TI.
> YAML мне очень понравился, чрезвычайно удобно и красиво, в отличие от аннотаций
Ну ладно
> И в симфони я глянул, там тоже используется, например для описания роутов, так что в будущем пригодится.
Роуты это другое. Роуты удобно описывать в YAML так как тогда у тебя есть один файл со всеми роутами, а когда они раскиданы в виде аннотаций - с этим невозможно нормально работать. Там есть команда которая их сканирует и выводит общую табоицу, но это неудобно.
> я так понял, что симфони это модульная штука, ее можно собирать из пакетов как линукс).
Симфони это компоненты Симфони + фреймворк Симфони, построенный на них. Ну например есть доктрина сама по себе, а есть DoctrineBundle которая является чем-то вроде адаптера доктрины к фреймворку Симфони. Это вообще сильная сторона Симфони, что ты можешь брать только нужные компоненты и строить на них свой фреймворк, так как сама Симфони довольно тяжелая и мне там не все нравится. Например, то что касается авторизации и ролей на мой взгляд переусложнено.
Симфони расширяется за счет бандлов, бандл - это вроде библитеки, но он имеет возможность также встраиваться в фреймворк при подключении, например добавлять свои роуты, обработчики событий, добавлять сервисы в контейнер, добавлять виджеты для форм, и тд. Ну например бандл для автокомплита добавляет роут, который отвечает на аякс-запросы от виджета.
Бандлы это конечно уже часть фреймворка симфони, компоненты никак с бандлами не связаны.
Вообще то, что они смогли вынести код в изолированные компоненты, это говорит о хорошей архитектуре. И другие люди теперь могут на этих компонентах что-то свое строить. Хотя находятся конечно и те, кто считает что там слишком много абстракций, фабрик. Но это все делается для максимальной гибкости, чтобы ты мог менять поведение любой части.
Не все фреймворки так умеют. Например, Юи - это монолит, ты либо его используешь, либо нет, и например просто так взять от него модели active record вряд ли получится. Ну, у них другие цели.
Зенд также компонентный, раньше, продвинутые программисты именно их использовали, но с второй версии Симфони стала популярнее зенда.
Советую поизучать их, в том числе код посмотреть, может что интересного подчерпнешь.
> Есть ли у пользователя возможность "подсунуть" в формулу пустые строки вместо $orderId, $sum, $comment, $accountNumber?
Есть. Там такой сценарий:
- пользователь на сайте магазина оформляет заказ
- магазин подсчитывает все и создает форму оплаты, в скрытые поля которой записывает все эти параметры заказа + подпись. В качестве action указывается обработчик на сайте платежной системы.
- пользователь в браузере отправляет форму на сайт платежной системы, и кончено он может менять содержимое полей. Но подпись как раз и предназначена для защиты от этого.
> Если есть, он получит хеш этого "закрытого ключа". Из хеша наверное легко получить ключ перебором или из радужных таблиц.
Ключ может быть длинный, так же некоторые параметры менять не получится, например id заказа, номер счета и сумма заказа может генерироваться на сервере. А комментарий допустим вводится пользователем где-то раньше.
> Если я угадал
нет
> то в моем случае уязвимости нет, соль не общая для всех пользователей,
В твоем случае нет, просто я вспомнил про подвох при использовании хеша как цифровой подписи.
> <input type="email" pattern="{{ pattern|raw }}
Для емайл наверно все таки не надо писать паттерн.
> pattern="{{ pattern|raw }}"
raw кстати это неправильно. Значения атрибутов экранируются.
>>719724
Можно сделать 2 джойна на answers. Или сджойнить, сгруппировать и проверить через HAVING.
>>719739
> Дет пример видел, там в комментариях перед классом пишут @inject и перечисляют названия классов.
> Потом, при создании объекта, какая-та хуета читает ту строку комментариев перед классом и впрыскивает в указанные в @inject классы. Это типа круто, это DI.
Нет. Это autowiring для DI или что -то вроде этого. Это просто вспомогательный синтаксис.
> Сервис локатор это тот самый контейнер с набором классов, типа pimple, который передается в конструктор класса с зависимостями. Если он тоже DI, то что такое локатор в таком случае?
Сервис локатор это когда ты передаешь контейнер в конструктор и это плохо (разве что в контроллерах так делают). Читай мой урок https://gist.github.com/codedokode/e1d31a31b37d5f635057
Ты по моему все усложняешь. DI это когда зависимости передаются в класс снаружи. Вот и все. Для DI не требуются контейнеры или аннотации. У тебя каша в голове.
Использовать абсолютный путь, также сделать чтобы у одной страницы был один УРЛ, а не два, отличающиеся слешем.
Спасибо, а я в репозиториях у него искал.
>DI это когда зависимости передаются в класс снаружи. Вот и все.
Получается локатор это разновидность DI? Поэтому pimple обзывают di, а не sl?
ДИ контейнер просто используется для того чтобы не размазывать код создания объектов по всей программе и гарантировать существование одного экземпляра.
Сервис локатором обычно называют плохой подход когда контейнер передают в конструктор.
Передай их в функцию в виде аргументов - поставь в скобках, они станут доступны внутри функции, ими станет возможным оперировать.
>>719543
Ты и он - одно лицо?
Вот тогда назначай при создании функции в скобках у неё аргументы, которые потребуются для работы цикла внутри функции. Затем сделай return с окончательной стоимостью кредита за пределами цикла в функции. Вот и всё.
Попробуй все данные ОПом примеры в уроке запустить на Идеоне или в своей IDE, где ты там запускаешь скрипты.
В задаче про студентов, например, в поле "Имя" нельзя ввести латинские буквы, знаки, цифры с лат. буквами, знаки с лат. буквами и так далее. Как организовать проверку этого шлака?
Чем аякс запросы отличаются от обычных? Их обрабатывают точно так же. Если аякс-зарос дублирует не-аякс действие то имеет смысл для них использовать один и тот же код с ифами внутри.
>>720076
Валидация модели студента на сервере перед записью в БД. Читал комментарии к задаче и урок про формы? https://github.com/codedokode/pasta/blob/master/forms.md
Для удобства пользователя стоит также добавить HTML5 валидацию на клиенте.
>можно сделать одну таблицу с колонкой-дискриминатором в виде енума, указывающей к какому словарю относится слово
Черт. Вот до этого точно должен был сам додуматься. Слишком тороплюсь.
Ок, так все-таки, если я решусь преобразовать xml в html, какими тегами мне выделять транскрипцию например?
Определение слова пожалуй можно выделить dfn (или dt?). А транскрипцию?
Думал залезу на сайт какого-нибудь переводчика, подсмотрю мудрую семантику.
Ага, h2>span.bold для определения и для транскрипции вообще картинка (надеюсь они ее хоть генерируют из php).
Почему люди на картинке открыли рты? Может они голодные?
В базе xml вида
<ar>
<k>April</k>
<tr>ˈeɪprəl</tr>
сущ. апрель April weather ≈ то дождь, то солнце; перен. то смех
, то слезы April fish
</ar>
В каком виде такой фрагмент вывести в браузер?
Картинка - защита от любителей воровать базы данных. Я бы еще УРЛ сделал похожий на имя файла чтобы можно было сгенерированную картинку сохранить на диск и отдавать без запуска кода приложения.
> В каком виде такой фрагмент вывести в браузер?
Заменить на дивы и спаны с классами. Соответственно потом их можно как хочешь стилизовать через ксс.
Также, есть тег dl/dt/dd но это не для одного слова, а для списка определений. Наверно тут не очень подойдет.
Ой, ошибся, картинка там для того чтобы не было проблем с отображением символов обозначающих произношение.
Ну ладно, я надеялся что в html5 есть семантичные теги на этот случай. Придется клепать скучные дивы с классами.
>>720132
А в чем может быть проблема с отображением символов, обозначающих произношение?
Обычные юникодные символы. Может просто сайт старый, тогда не было поддержки юникода.
Для их отображения надо чтобы в установленных у пользователя шрифтах пользователя были эти символы. С чего бы они должны там быть? Не забывай что есть много разных платформ, старые винды (включая обрезанные "умельцами" версии с рутрекера), андроиды и экзотика вроде WinMobile. Может они хотят на максимальном числе устройств отображать сайт. Или просто это сделали 10 лет назад когда с этим было все плохо, и так и оставили.
>Валидация модели студента на сервере перед записью в БД.
>Читал комментарии к задаче и урок про формы?
Да, читал. Там нет ответа на мой вопрос. Какими функциями можно организовать валидацию перед записью в БД? Например, нельзя пропустить "Имя" Валера13:?2\.
Это единственный вариант?
Спасибо
Да. Учи вторую.
Нет, учи первую.
Не знаю. Учи обе.
Братишки, вы только не ругайтесь.
Как сделать, чтобы когда человек вводит в браузере website.com/something/somewhere/, открывался нужный мне файл, к примеру somewhere.php ? А то даже не знаю как загуглить ...
Сейчас я просто использую website.com/somewhere.php, что выглядит как-то уёбищно.
Самое простое - сделать точку входы, чтобы все запросы шли через index.php к примеру. Там разбираешь запрос $_SERVER['REQUEST_URI'] и в зависимости от того что пришло делаешь что надо. А вообще гугли Front Controller
А есть какие-то профиты от использования маршрутизации через htaccess? Я как понял, что это абсолютно бесполезная фича, так как лучше писать маршрутизацию сразу в php чтобы не было потом проблем с переходом например на nginx.
Это и есть маршрутизация в пхп. Ты мод реврайтом ставишь чтобы все запросы шли на index.php и там их дальше анализируешь.
Но бывает же когда в htaccess куча правил, а в index нет никакого разбора строки адреса.
Делаю уроки по основам, застопорился на регулярных выражениях.
Почему вот это '/^ ?(8|[+]7)([ -()]*[0-9]){10}$/u' не описывает номер? В начале возможно пробел, потом или 8 или +7, потом произвольное число пробелов дефисов и скобок, потом цифра, повторить десять раз.
Ну то есть почему вот такой 84951234567 проходит, а вот такой 8 (911) - 123 - 45 - 67 нет?
Что означает это:
>([ -()]*[0-9])
Пробел, дефис, скобка открывается, скобка закрывается в любом порядке неограниченное количество раз либо отсутствует. Далее одна из цифр.
А затем повторить всё это 10 раз.
Вот поэтому и срабатывает на чистом номере - по цифре 10 раз повторить.
Подсказка: пробел, дефис, скобка открывается, скобка закрывается, затем одна из цифр - всё это в любом порядке неограниченное количество раз. Повторить 10 раз.
> [ -(]
Осторожнее с дефисом в квадратных скобках, он имеет значение перечисления. Например
[d-h] любая буква от d до h
[4-8] цифра от 4 до 8 включая границы
Соответственно
[ -(] дефис будет интерпретирован как указатель на промежуток, поэтому дефис нужно ставить либо в самое начало после открывающей квадратной скобки, либо перед закрывающей
[+] какой в этом смысл? Квадратные скобки означают один любой из перечисленных символов, когда символ один скобки не нужны
Если нужно заэкранировать спецсимвол, есть обратный слеш \+
Кроме пробелов есть еще табуляции, переводы строк и т.д., так что лучше писать \s.
Так ведь в квадратных скобках все спецсимволы не работают, поэтому и плюсик так записал. То есть дефис будет всё равно работать внутри скобок?
Да, поменял местами пробел и дефис и всё заработало. Как-то это коряво, это же просто список символов по идее, порядок не должен влиять.
Возможно есть какие то пожелания.
https://ideone.com/MqEuKu
У тебя в самом конце оплаты будет заплочено лишнее. Например осталось платить 2к, а ты платишь 5к.
забыл добавить break в проверку если размер кредита меньше выплачиваемой суммы
https://ideone.com/MqEuKu
if ($percent == 1.02) {
$credit += 7777;
}
Нет логики - нет и не должно быть такой привязки к двум процентам, чтобы в этом случае прибавлять 7777. Это ты надумал.
А так считает всё верно.
Да, откуда взялось 30 месяцев? А что если кредит придётся выплачивать дольше? Лучше условием выхода из цикла ставить размер кредита ниже или равному нулю.
https://symfony.com/doc/current/components/form/introduction.html#rendering-the-form
https://symfony.com/doc/current/book/forms.html#rendering-the-form
А можно как-нибудь обойтись без Twig?
https://symfony.com/doc/current/book/forms.html#rendering-the-form
><?php echo $view['form']->start($form) ?>
> <?php echo $view['form']->errors($form) ?>
>
> <?php echo $view['form']->row($form['task']) ?>
> <?php echo $view['form']->row($form['dueDate']) ?>
><?php echo $view['form']->end($form) ?>
Откуда берется $view?
Если код неправильный - она перестаёт работать. Исправляй, редактируй, запускай снова.
Не, раньше при нажатии на кнопку писалось что вот компилирует, запускает, ошибку выдаёт и прочее. А сейчас кнопка субмит просто неактивна, я на неё жму и ничего не происходит. Может лимит дневной выбрал?
И подготовленные переменные.
Вот, например:
$statement = ['foo'=>'bar', 'pageId'=>$pageId];
$STH = $this->DBH->prepare('SELECT * FROM pages WHERE page_id=:pageId');
$STH->execute($statement);
$STH->setFetchMode(PDO::FETCH_ASSOC);
$maybeExsist= $STH->fetchAll();
Вот эта $statement['foo'] не дает выполняться моему коду нормально. Я не знаю, что происходит, но ничего не работает! Стоит мне ее убрать, и все работает!
Кажется, словно ему плевать на именнованные холдеры, он пытается запихнуть в подготовленное выражение даже то, для чего нет холдера. Или как так? Почему не работает, если в массиве подготовленных переменных, есть что-либо лишнее?
Сам могу поделиться ContentDownloaderом Ultimate версии
Мне кажется ты не понимаешь, что такое портфолио.
Всё должно работать как угодно долго и без ограничений.
Сначала submit, а потом всё время run.
пилю сейчас прослойку на основном сайте, чтобы была возможность обрабатывать определённые запросы с внешних сайтов и встал такой вопрос:
я использую easyXDM который в невидимом айфрейме открывает заданный адрес. По идее в этом же айфрейме будут выполняться ajax запросы к серверу, но для них нужен yii_csrf_token. Так вот, могу ли я вручную прописывать токен в куки, если сервер не найдёт его? сейчас я для его получения использую
CHtml::beginForm();
CHtml::endForm();
Но мне интересно, можно ли как-то без костылей?
$result = $pdo->query("SELECT * FROM bd");
Далее я узнаю сколько там строк:
$rows = $result->rowCount();
Далее я беру цикл и вывожу все в табличку:
<?php for ($i = 0; $i < $rows; ++ $i) :?>
<?= $row = $result->FETCH(PDO::FETCH_NUM);?>
<tr>
<td><?=$row[0]?></td>
<td><?=$row[1]?></td>
<td><?=$row[2]?></td>
</tr>
<?php endfor;?>
В ответ на это оно все выводит правильно, но ругается: Notice: Array to string conversion in ...
Если выводить просто средствами html, то никто не ругается:
for ($i = 0; $i < $rows; ++ $i) {
$row = $result->FETCH(PDO::FETCH_NUM);
echo <<<_END
<pre>
$row[0]
$row[1]
$row[2]
</pre>
_END;
}
Почему так?
Собственног ругается на строку
$row = $result->FETCH(PDO::FETCH_NUM);
не могу понять что не так.
>А можно как-нибудь обойтись без Twig?
Перефразирую вопрос: Можно ли как-нибудь зарендерить шаблон без Twig?
<?= $row = $result->FETCH(PDO::FETCH_NUM);?>
поменяй на
<?php $row = $result->FETCH(PDO::FETCH_NUM);?>
Спасибо, как всегда в мелочи лоханулся.
Офигел с говнокода. Зачем ты такое советуешь?
Из-за того, что большинство сайтов, которые нужно поддерживать PHP-программисту, выглядят внутри именно так?
Начал изучать Php и дошел до регулярок. Бля, какие же они охуенные. :3
Это была рубрика "Вещи, которые вы никогда не услышите от начинающего PHP разработчика"
$regexp4 = '/[.] [а-яёa-z]/u';
if (preg_match($regexp4,$testText)){
$count = preg_match_all($regexp4,$testText);
for($i=0;$i<$count;$i++){
$matches = array();
preg_match($regexp4,$testText,$matches);
$testText = preg_replace($regexp4,mb_strtoupper($matches[0]),$testText);
}
}
Идти в С++ тред.
PHP может сделать, но это будет как самосвал основаный на костылях. Тут надо искать другие средства и языки программирования.
>function countTotalPayment ($creditDebt, $percent, $serviceFee, $monthlyPayment)
>countTotalPayment ($creditSum, 1.02, 7777, $payout);
Нет, тут ты немного напутал, от этого считает для третьего банка неверно (там 53к должно получиться).
Там надо один раз прибавить к сумме кредита с самого начала 7777, а дальше больше не прибавлять.
Можно сделать то же самое на веб-страницах: кнопку, поля для ввода значений, вот это всё.
Все проекты под NDA, портфолио нет. Чтобы доказать свою состоятельность, готов поработать за еду пару недель.
deuzsit7EEeANUSgmaiqQ?lPUNCTUM%5Lcom
https://geektimes.ru/post/274586/
Я тут закинул лицо в http://traveladmin.ru/admin/avia_search/search_flight_details.php-ok 272K
Что делает строка вроде $("#Name"+PassangersID).val(LatName); Это вообще PHP или javascript? Мой phpstorm не смог толком переварить файл.
Это жквери вроде, "#Name"+PassengersID я так понимаю дает строку типа "#NameVASILIY" именно так, без пробела, потом на странице ищется элемент с таким айди, и ему выставляется значение в переменной LatName.
Если ты хочешь делать страницы в браузере, придется поставить веб-сервер: https://github.com/codedokode/pasta/blob/master/soft/web-server.md
>>721302
Да, регулярки - очень мощная штука в умелых руках. Мастер одной строчкой может сделать то, что новичок будет делать большим куском кода.
>>721432
Почему на сайты с вакансиями не идешь? Я знаю как минимум одну вакансию, правда не уверен, хватит ли там "полставочки".
>>721434
Это яваскрипт. Там можно в именах переменных использовать знак доллара и он ничего не значит, просто обычный символ.
>>721440
Ага, беда с этим. Версткой часто занимаются люди, которые не осилили программирование (или те что хотят побыстрее найти любую работу), уровень соответствующий.
Есть 2 ссылки на разные языковые версии сайта /en/ и /ru/
Как сделать чтобы нажатии на одну из них для нее менялся стиль? Причем надо чтобы этот стиль сохранялся пока юзер ходит по сайту. Уже всю голову сломал, пока придумал только вариант распаршивать урл и в зависимости от того какой там язык менять класс на ссылке. Но это как-то слишком черезкостыльно как мне кажется.
Кстати в нашем треде мы учим анонов делать отдельную публичную папку, а все остальное хранить вне ее.
Времени нет, отвечу кратко. Надо открыть исходники компонента и смотреть их: https://github.com/symfony/form
Как видишь, там нет никакого твига. Там все очень абстрактно и вроде есть только такой абстрактный интерфейс: https://github.com/symfony/form/blob/master/FormRendererEngineInterface.php
В Симфони рендеринг форм через твиг засунут в twig-bundle. Этот бандл добавляет поддержку твига не только в формы, а еще и в других компонентах. Там можно увидеть например шаблоны элементов форм.
А за шаблонизацию, как я понимаю, отвечает компонент Templating: https://github.com/symfony/templating
Обрати внимание, что формы и templating - независимые компоненты, а twig bundle - это часть фреймворка Симфони, завязанная на него.
Вот осталось только выяснить как можно привязать формы к templating. Где связующее звено и шаблоны форм. Я порылся по исходникам и мне кажется, оно в FrameworkBundle:
https://github.com/symfony/framework-bundle/tree/master/Resources/views/Form
https://github.com/symfony/framework-bundle/blob/master/Templating/Helper/FormHelper.php
Похоже что компонент форм абстрагирован и не содержит никаких средств для вывода форм. Ты должен изучить имеющиеся там интерфейсы и реализовать классы для них, либо взять где-то готовые.
-------
> Откуда берется $view?
Выше же в коде есть $form->createView():
https://github.com/symfony/form/blob/master/Form.php#L988
Сделай поиск по коду и изучи места где встречается createView, buildView.
Там логика примерно такая:
- вызывается buildView у типа виджета (FormType)
- у всех его предков, от которых он унаследован через getParent
- у расширений типа виджеа (FormTypeExtension) которые на него повешены.
Я советую тебе разобраться всерьез с формами, изучив их код. Начать можно с https://github.com/symfony/form/blob/master/FormFactoryBuilder.php и далее смотреть кто кого создает или кого наследует. Советую разобраться из каких кусочков собирается форма: Form Type, Form, FormView, Data Transformer, Data Mapper, Form extension, Form Type Extension и что там есть еще. Если хочешь, могу дать задачку сделать какой-то виджет вроде универсального автокомплита, чтобы было над чем поломать голову.
В идеале надо понимать как связаны все эти составляющие и какие из них нужны для решения той или иной задачи.
Времени нет, отвечу кратко. Надо открыть исходники компонента и смотреть их: https://github.com/symfony/form
Как видишь, там нет никакого твига. Там все очень абстрактно и вроде есть только такой абстрактный интерфейс: https://github.com/symfony/form/blob/master/FormRendererEngineInterface.php
В Симфони рендеринг форм через твиг засунут в twig-bundle. Этот бандл добавляет поддержку твига не только в формы, а еще и в других компонентах. Там можно увидеть например шаблоны элементов форм.
А за шаблонизацию, как я понимаю, отвечает компонент Templating: https://github.com/symfony/templating
Обрати внимание, что формы и templating - независимые компоненты, а twig bundle - это часть фреймворка Симфони, завязанная на него.
Вот осталось только выяснить как можно привязать формы к templating. Где связующее звено и шаблоны форм. Я порылся по исходникам и мне кажется, оно в FrameworkBundle:
https://github.com/symfony/framework-bundle/tree/master/Resources/views/Form
https://github.com/symfony/framework-bundle/blob/master/Templating/Helper/FormHelper.php
Похоже что компонент форм абстрагирован и не содержит никаких средств для вывода форм. Ты должен изучить имеющиеся там интерфейсы и реализовать классы для них, либо взять где-то готовые.
-------
> Откуда берется $view?
Выше же в коде есть $form->createView():
https://github.com/symfony/form/blob/master/Form.php#L988
Сделай поиск по коду и изучи места где встречается createView, buildView.
Там логика примерно такая:
- вызывается buildView у типа виджета (FormType)
- у всех его предков, от которых он унаследован через getParent
- у расширений типа виджеа (FormTypeExtension) которые на него повешены.
Я советую тебе разобраться всерьез с формами, изучив их код. Начать можно с https://github.com/symfony/form/blob/master/FormFactoryBuilder.php и далее смотреть кто кого создает или кого наследует. Советую разобраться из каких кусочков собирается форма: Form Type, Form, FormView, Data Transformer, Data Mapper, Form extension, Form Type Extension и что там есть еще. Если хочешь, могу дать задачку сделать какой-то виджет вроде универсального автокомплита, чтобы было над чем поломать голову.
В идеале надо понимать как связаны все эти составляющие и какие из них нужны для решения той или иной задачи.
Во-первых, включи отображение ошибки и смотри что пишется. Во-вторых, не передавай лишние данные. В-третьих, в массиве плейсхолдеры тоже должны быть с двоеточием.
$config->addEntityNamespace('User', 'App\Model\User');
...
$repo = $em->getRepository('User');
Это не работает, говорит класс User не существует. С полным неймспейсом все хорошо.
Это плохая идея, хотя бы потому что там просто на нгинкс не переклчиться будет, да и плохо что УРЛ вырваны из кода. Хотя тех кто такое пишет вопросы качества кода обычно мало интересуют.
>>720177
> я шифрую куки через функцию password_hash
Это не шифрование, а хеширование. шифрование это когда обратно можно расшифровать.
И вопрос, а что мешает злоумышленнику взять числа от 1 до 100, захешировать их и передавать хеши по очереди, пока не совпадет с каким-нибудь существующим?
Простое хеширование не додавляет никакой защищенности так как оно не увеличивает число вариантов которые должен перебрать злоумышленник. В случае с числами ему достаточно перебрать число от 1 до 100, в случае с хешами - аналогичные хеши.
Защита была бы если бы ты хешировал id вместе с секретным ключом, который злоумышленник не знает и потому не может получить хеш:
хеш = хеш-функция(id . секретный ключ)
Но в этой задаче можно поступить проще.
> Мне нужно доставать данные из бд и я делаю так, я достаю массив айдишников из бд и каждый пропускаю через password_verify, если айдишник совпал с солью (зашифрованным куки)
Это ненормально. Ты выбираешь 100000 записей только чтобы найти одну? Это неэффективно. Так делать неправильно. Если такой требуется то правильнее передавать отдеьно id в открытом виде, а отдельно хеш. По id мы ищем запись в базе. Но тут это не надо
В этой задаче можно поступить проще. для идентификации и проверки подлинности студента надо использоваь не короткие легко подбираемые id, а длинные и сложноподбираемые. Например, код (токен) из 32 случайных символов. При регистрации мы генерируем случайный код и сохраняем его в базу и куки пользователю. При попытке редактирвоания данных мы по коду находим соотв. запись в базе. Так как код абсолютно случайный и возможно огромное количество кодов, то злоумышленник может хоть годами их перебирать.
Давай оценим число вариантов. Допустим мы используем в коде 32 случайных цифры от 0 до 9. Это дает 10 в 32 степени вариантов. 10 в 32 степени это число состоящее из единицы и 32 нулей после нее. Это много. А если мы будем использовать не только цифры, а большие и маленькие латинские буквы то у нас будет 62 в 32 степени вариантов. Гугл говорит что это равно такому числу: https://www.google.ru/search?q=62^32&newwindow=1&gbv=1&sei=vMMWV5HwIKmE6QSB16ToBQ
на нынешних технлологиях такой код и за миллион лет не подобрать.
Это плохая идея, хотя бы потому что там просто на нгинкс не переклчиться будет, да и плохо что УРЛ вырваны из кода. Хотя тех кто такое пишет вопросы качества кода обычно мало интересуют.
>>720177
> я шифрую куки через функцию password_hash
Это не шифрование, а хеширование. шифрование это когда обратно можно расшифровать.
И вопрос, а что мешает злоумышленнику взять числа от 1 до 100, захешировать их и передавать хеши по очереди, пока не совпадет с каким-нибудь существующим?
Простое хеширование не додавляет никакой защищенности так как оно не увеличивает число вариантов которые должен перебрать злоумышленник. В случае с числами ему достаточно перебрать число от 1 до 100, в случае с хешами - аналогичные хеши.
Защита была бы если бы ты хешировал id вместе с секретным ключом, который злоумышленник не знает и потому не может получить хеш:
хеш = хеш-функция(id . секретный ключ)
Но в этой задаче можно поступить проще.
> Мне нужно доставать данные из бд и я делаю так, я достаю массив айдишников из бд и каждый пропускаю через password_verify, если айдишник совпал с солью (зашифрованным куки)
Это ненормально. Ты выбираешь 100000 записей только чтобы найти одну? Это неэффективно. Так делать неправильно. Если такой требуется то правильнее передавать отдеьно id в открытом виде, а отдельно хеш. По id мы ищем запись в базе. Но тут это не надо
В этой задаче можно поступить проще. для идентификации и проверки подлинности студента надо использоваь не короткие легко подбираемые id, а длинные и сложноподбираемые. Например, код (токен) из 32 случайных символов. При регистрации мы генерируем случайный код и сохраняем его в базу и куки пользователю. При попытке редактирвоания данных мы по коду находим соотв. запись в базе. Так как код абсолютно случайный и возможно огромное количество кодов, то злоумышленник может хоть годами их перебирать.
Давай оценим число вариантов. Допустим мы используем в коде 32 случайных цифры от 0 до 9. Это дает 10 в 32 степени вариантов. 10 в 32 степени это число состоящее из единицы и 32 нулей после нее. Это много. А если мы будем использовать не только цифры, а большие и маленькие латинские буквы то у нас будет 62 в 32 степени вариантов. Гугл говорит что это равно такому числу: https://www.google.ru/search?q=62^32&newwindow=1&gbv=1&sei=vMMWV5HwIKmE6QSB16ToBQ
на нынешних технлологиях такой код и за миллион лет не подобрать.
Да не, я уже разобрался. Думал что preg_peplace заменяет только первое появление маски, а она заменяет все.
Ну как минимум можно использовать объект для обозначения одной записи которую распарсил парсер.
А вообще, все зависит от того, какая у тебя логика:
- надо ли обрабатывать данные поточно или можно сначала все распарсить, потом все сохранить
- надо ли проверять наличие в базе таких же записей, и не обновились ли они на сайте
Ну например если у нас нет цели делать поточный парсер то логика получается очень простая:
список URl = получить список URL
для каждого УРЛ {
объект = расппарсить страницу
}
сохранить в базу массив объектов.
Если ты плохо знаешь ООП, прочти и реши раздел по ООП в учебнике из ОП поста.
А, ты вообще неправильно сделал. Там объявляется не синоним для класса, а синоним для неймспейса.
http://cackle.me/
Они как-то общую базу данных где-то у себя на сервере хранят для комментов и отзывов? Или же грузят бд тебе на сервер?
Дык нет, это фактически не ошибки, просто написание программы разными способами. Но разные варианты очевидно работают по разному, хотя и могут выдавать одинаковый результат. Сейчас столкнулся с этим выполняя задания из учебника, у меня получилось не так как у автора, вот и думаю почему так.
Толсто
Не понимаю, а в чем проблема? Первичный ключ может включать несклоько полей, и джойны можно по нескоьким полям делать.
Ничего не понятно из описания паттерна на википедии и из примеров.
>Заместитель (англ. Proxy) — структурный шаблон проектирования, который предоставляет объект, который контролирует доступ к другому объекту, перехватывая все вызовы (выполняет функцию контейнера).
Ок, то что это контейнер для упрощения работы с тяжелым объектом, это понятно. Непонятна реализация. Примеры приводят какие-то дурацкие.
https://ru.wikipedia.org/wiki/Proxy_(шаблон_проектирования)#PHP5
Есть класс Math, который умножает, делит, складывает и вычитает числа.
У класса MathProxy те же методы. Только зачем-то подписали, что сложение это
>/// Быстрая операция - не требует реального субъекта
а умножение
>/// Медленная операция - требует создания реального субъекта
Не очень наглядно, если честно не вижу особой разницы в скорости между умножением и сложением.
Ну хорошо, наверное привели неудачный пример. Возможно в исходном классе Math хранится еще куча всего на все случаи жизни (какие-нибудь таблицы Брадиса и т.д.), тогда как некоторые методы типа сложения/вычитания не требуют создания гигантского объекта со всем хардкорным мясом которое в нем хранится.
А в классе Proxy дублируются легкие методы, таким образом создание класса Proxy позволит сэкономить ресурсы.
Класс Proxy обязан наследоваться (или реализовать тот же интерфейс) от класса, который он замещает?
http://designpatternsphp.readthedocs.org/ru/latest/Structural/Proxy/README.html
Тут совсем непонятный пример.
Есть класс Record, у него свойство $data.
Наследуют от него RecordProxy, наследуют свойство $data (то есть выгоды вообще никакой), только добавляются какие-то странные свойства $isDirty и $isInitialized, которые выставляются в true, если $data не равна null.
Зачем это? Если пишут пример, писали бы уже полный, я должен догадываться, что они там упустили.
Возможно, у класса Record есть еще какая-то легкая логика, не нуждающаяся в $data, и эту логику наследует прокси. Но в приведенном примере свойство $data класса Recrod необязательно заполнять, с таким же успехом можно было создать и экземпляр Record с пустой data. В чем тогда смысл прокси? Или опять неудачный пример?
Пока буду считать, что все-таки Record жить не может без data, а прокся может, плюс содержит ту же логику что и Record.
Ничего не понятно из описания паттерна на википедии и из примеров.
>Заместитель (англ. Proxy) — структурный шаблон проектирования, который предоставляет объект, который контролирует доступ к другому объекту, перехватывая все вызовы (выполняет функцию контейнера).
Ок, то что это контейнер для упрощения работы с тяжелым объектом, это понятно. Непонятна реализация. Примеры приводят какие-то дурацкие.
https://ru.wikipedia.org/wiki/Proxy_(шаблон_проектирования)#PHP5
Есть класс Math, который умножает, делит, складывает и вычитает числа.
У класса MathProxy те же методы. Только зачем-то подписали, что сложение это
>/// Быстрая операция - не требует реального субъекта
а умножение
>/// Медленная операция - требует создания реального субъекта
Не очень наглядно, если честно не вижу особой разницы в скорости между умножением и сложением.
Ну хорошо, наверное привели неудачный пример. Возможно в исходном классе Math хранится еще куча всего на все случаи жизни (какие-нибудь таблицы Брадиса и т.д.), тогда как некоторые методы типа сложения/вычитания не требуют создания гигантского объекта со всем хардкорным мясом которое в нем хранится.
А в классе Proxy дублируются легкие методы, таким образом создание класса Proxy позволит сэкономить ресурсы.
Класс Proxy обязан наследоваться (или реализовать тот же интерфейс) от класса, который он замещает?
http://designpatternsphp.readthedocs.org/ru/latest/Structural/Proxy/README.html
Тут совсем непонятный пример.
Есть класс Record, у него свойство $data.
Наследуют от него RecordProxy, наследуют свойство $data (то есть выгоды вообще никакой), только добавляются какие-то странные свойства $isDirty и $isInitialized, которые выставляются в true, если $data не равна null.
Зачем это? Если пишут пример, писали бы уже полный, я должен догадываться, что они там упустили.
Возможно, у класса Record есть еще какая-то легкая логика, не нуждающаяся в $data, и эту логику наследует прокси. Но в приведенном примере свойство $data класса Recrod необязательно заполнять, с таким же успехом можно было создать и экземпляр Record с пустой data. В чем тогда смысл прокси? Или опять неудачный пример?
Пока буду считать, что все-таки Record жить не может без data, а прокся может, плюс содержит ту же логику что и Record.
Заебали с этими вопросами. В этом треде ты ответа не узнаешь.
Вот я меняю схему базы данных. Для начала это изменение файла дампа. Переписываю классы. Фиксю экшены. Фиксю шаблоны: в объекте теперь другие свойства и соотв.другие геттеры, ну и сам виджет изменился, нужна правка css.
Как мне теперь это все коммитить? Одним ли коммитом вида "change db schema"? Тогда будет странно, что эта надпись светится над именем css-файла например.
Зато изменения были внесены атомарно так сказать, приложение остается в рабочем состоянии в каждом коммите.
Или сделать десяток коммитов уровня "change db shema" для файла дампа, "change models" для классов моделей, "черт знает как назвать" для изменений в контроллере, "fix template for a new widget" в html/css?
Это дает более аккуратные мелкие изменения для конкретной группы файлов, но приложение находится в нерабочем состоянии между первым и последним из коммитов.
Для нерабочих состояний существуют ветки. Ты форкаешь в новую Feature ветку, делаешь там сотню коммитов, получаешь рабочее состояние и мерджишь обратно в основную. В логе будут видны точки форка и мерджа, на них можно будет откатывать. Коммиты лучше описывать более подробно, чтобы без заглядывания в код было понятно, что сделано. Тут мелкие коммиты как раз лучше подходят, их можно более детально описать.
Итак появилась идея написать сайт но проблема в том что я совсем не умею в код хотя если что могу чёйнить поменять там если тыкнуть пальцем, быстро обучаюсь. Мой взгляд пал на сие чудо, управление лёгкое но вот функционал подойдёт мне? Посмотрел шаблоны а там говнецо уровня мелко конторка, звоните договоримся и бложик карочи.
Сможет ли wordpress в авто режиме отправлять деньги, допустим через битки или киви? Может он в регистрацию и ЛК? Отправляет ли письма на мыло? Там есть всякие расширения, они типа дают всякие свистопиздюльки как это модно в браузерах?
Или всё это параша для блога?
Примеры плохие. Давай рассмотрим на примере некоего абстрактного ORM вроде Доктрины. Допустим у нас есть блог, в нем модель Пост, и у него есть Автор:
class User { ... }
class Post {
...
/ @var User|null */
protected $author;
....
}
Я думаю, как выглядят соответствующие таблицы в БД, писать не надо.
Теперь представь что пользователь загружает Пост по id:
$post = $orm->findPostById($id);
// допустим, мы далее что-то делаем с постом:
$rating = calculatePostRating($post);
// или так
$post->addViewCount(1);
Внимание, вопрос, как должен поступить ORM с автором поста?
- загрузить модель Поста и связанного с ним Пользователя, и засунуть его в $post->author
- загрузить только модель Поста, пользователь ведь просил только его. В поле $author тогда будет null.
Отвечая, помни, что хороший ОРМ пытается скрыть наличие базы и сделать вид что весь граф объектов находится в памяти. Что объект может получаться из БД в одном месте, а исользоваться совсем в другом. Что не должно быть 2 разных объекта соответствующей одной строке в таблице (то есть если пользователь второй раз просит тот же пост, он должен получить тот же самый объект).
Ответь на вопрос, и давай рассмотрим пример поинтереснее. Допустим, мы решили допилить приложение и добавить больше моделей и связей. Теперь у нас есть:
- Пост, содержит ссылку на Автора, одну Категорию, Теги, Комментарии
- Автор, модель содержит коллекцию Постов и коллекцию Комментариев
- Комментарий, содержит ссылку на Автора, родительский Комментарий, коллекцию дочерних Комментариев
Возвращаясь назад, зададим еще раз тот же вопрос: что должен загрузить ORM?
- только Пост
- Пост + Автора + Категорию + Теги + Комментарии и все
- Пост + связанные сущности на 2 уровня в глубину
- Пост + связанные сущности на 3 уровня в глубину
- свой вариант ответа
Ответь на вопросы, как бы ты сделал, а потом попробуем подумать может ли нам помочь тут паттерн Прокси.
Примеры плохие. Давай рассмотрим на примере некоего абстрактного ORM вроде Доктрины. Допустим у нас есть блог, в нем модель Пост, и у него есть Автор:
class User { ... }
class Post {
...
/ @var User|null */
protected $author;
....
}
Я думаю, как выглядят соответствующие таблицы в БД, писать не надо.
Теперь представь что пользователь загружает Пост по id:
$post = $orm->findPostById($id);
// допустим, мы далее что-то делаем с постом:
$rating = calculatePostRating($post);
// или так
$post->addViewCount(1);
Внимание, вопрос, как должен поступить ORM с автором поста?
- загрузить модель Поста и связанного с ним Пользователя, и засунуть его в $post->author
- загрузить только модель Поста, пользователь ведь просил только его. В поле $author тогда будет null.
Отвечая, помни, что хороший ОРМ пытается скрыть наличие базы и сделать вид что весь граф объектов находится в памяти. Что объект может получаться из БД в одном месте, а исользоваться совсем в другом. Что не должно быть 2 разных объекта соответствующей одной строке в таблице (то есть если пользователь второй раз просит тот же пост, он должен получить тот же самый объект).
Ответь на вопрос, и давай рассмотрим пример поинтереснее. Допустим, мы решили допилить приложение и добавить больше моделей и связей. Теперь у нас есть:
- Пост, содержит ссылку на Автора, одну Категорию, Теги, Комментарии
- Автор, модель содержит коллекцию Постов и коллекцию Комментариев
- Комментарий, содержит ссылку на Автора, родительский Комментарий, коллекцию дочерних Комментариев
Возвращаясь назад, зададим еще раз тот же вопрос: что должен загрузить ORM?
- только Пост
- Пост + Автора + Категорию + Теги + Комментарии и все
- Пост + связанные сущности на 2 уровня в глубину
- Пост + связанные сущности на 3 уровня в глубину
- свой вариант ответа
Ответь на вопросы, как бы ты сделал, а потом попробуем подумать может ли нам помочь тут паттерн Прокси.
5.6. Напиши генератор случайного имени для кошки, собаки, брата или сестры.
Для этого мы сделаем массив со слогами и несколько раз выберем из него случайный элемент. Эти элементы мы собираем по кусочкам в переменную $name и в конце выводим.
Вот код:
<?php
error_reporting(-1);
/ Слоги, из которых составляется имя /
$letters = array(
'ко', 'и', 'дзу', 'ми',
'са', 'ку', 'ра', 'да',
'чи', 'а', 'ки', 'ми',
'на', 'го', 'ха', 'ру'
);
/ В эту переменную запишем получившееся имя /
$name = "";
/ Гененрируем 4 слога /
for ($i = 1; $i <= 4; $i++) {
/ Выкидываем случайное число (count - число элементов в массиве) /
$random = array_rand($letters);
$randomText = $letters[$random];
echo "Выпало число {$random}, слог {$randomText}\n";
};
echo "\n";
echo "Советую имя: {$name} - не прогадаешь!\n";
?>
Я не могу собрать элементы выданные циклом и занести в $name. Я понимаю что нужно именно в цикле это сделать, но как не знаю.
5.6. Напиши генератор случайного имени для кошки, собаки, брата или сестры.
Для этого мы сделаем массив со слогами и несколько раз выберем из него случайный элемент. Эти элементы мы собираем по кусочкам в переменную $name и в конце выводим.
Вот код:
<?php
error_reporting(-1);
/ Слоги, из которых составляется имя /
$letters = array(
'ко', 'и', 'дзу', 'ми',
'са', 'ку', 'ра', 'да',
'чи', 'а', 'ки', 'ми',
'на', 'го', 'ха', 'ру'
);
/ В эту переменную запишем получившееся имя /
$name = "";
/ Гененрируем 4 слога /
for ($i = 1; $i <= 4; $i++) {
/ Выкидываем случайное число (count - число элементов в массиве) /
$random = array_rand($letters);
$randomText = $letters[$random];
echo "Выпало число {$random}, слог {$randomText}\n";
};
echo "\n";
echo "Советую имя: {$name} - не прогадаешь!\n";
?>
Я не могу собрать элементы выданные циклом и занести в $name. Я понимаю что нужно именно в цикле это сделать, но как не знаю.
Рядом есть перезвоним-тред, специально для таких вопросов. Также есть раздел /wrk.
>>721819
> Как мне теперь это все коммитить? Одним ли коммитом вида "change db schema"? Тогда будет странно, что эта надпись светится над именем css-файла например.
Связанные изменения конечно лучше коммитить вместе.
> Одним ли коммитом вида "change db schema"?
А зачем ты меняешь схему? От нечего делать? Наверно есть какая-то причина почему ты это делаешь. Например, это нужно для реализации какой-то фичи, закрытия бага, оптимизации производительности или структуры Бд. То есть лучше писать не какой файл ты поменял, а какую задачу решил. Хотя коммиты в общем - они для программистов и там вполне нормально писать низкоуровневые вещи.
Вот посмотри например коммиты в Хромиум:
https://chromium.googlesource.com/chromium/chromium/
https://chromium.googlesource.com/chromium/chromium/+/5b0874e7d87040fce31a6f862f1636d010025368
Вот пример коммита в Андроид: https://chromium.googlesource.com/aosp/platform/system/core/+/48d4c0c42afc1cb77c4573ac22adb61d2f92ccd3
Вот видно, люди стараются. В прстых проектах конечно писать длинные описания не надо, хватит 1 строки, но в сложных проектах уровня Хромиума эти описания помогают понять, зачем сделаны те или иные изменения.
С другой стороны, в VS code коммиты довольно краткие: https://github.com/Microsoft/vscode/commits/master
В доктрине тоже краткие: https://github.com/doctrine/doctrine2/commits/master
Писать какой файл ты поменял в большинстве слчаев не надо - это видно в списке изменений. Надо писать для какой задачи ты это сделал.
> Или сделать десяток коммитов уровня "change db shema" для файла дампа, "change models" для классов моделей, "черт знает как назвать" для изменений в контроллере, "fix template for a new widget" в html/css?
не вижу особого смысла
>>721831
Если все коммиты в рамках одной задачи, что в них напишешь? Если это большая задача, ну допустим можно разбить ее на этапы и коммитить раз в день, а если это баг или небольшая фича, то по моему удобнее одним коммитом добавить.
>>721883
Вряд ли
Рядом есть перезвоним-тред, специально для таких вопросов. Также есть раздел /wrk.
>>721819
> Как мне теперь это все коммитить? Одним ли коммитом вида "change db schema"? Тогда будет странно, что эта надпись светится над именем css-файла например.
Связанные изменения конечно лучше коммитить вместе.
> Одним ли коммитом вида "change db schema"?
А зачем ты меняешь схему? От нечего делать? Наверно есть какая-то причина почему ты это делаешь. Например, это нужно для реализации какой-то фичи, закрытия бага, оптимизации производительности или структуры Бд. То есть лучше писать не какой файл ты поменял, а какую задачу решил. Хотя коммиты в общем - они для программистов и там вполне нормально писать низкоуровневые вещи.
Вот посмотри например коммиты в Хромиум:
https://chromium.googlesource.com/chromium/chromium/
https://chromium.googlesource.com/chromium/chromium/+/5b0874e7d87040fce31a6f862f1636d010025368
Вот пример коммита в Андроид: https://chromium.googlesource.com/aosp/platform/system/core/+/48d4c0c42afc1cb77c4573ac22adb61d2f92ccd3
Вот видно, люди стараются. В прстых проектах конечно писать длинные описания не надо, хватит 1 строки, но в сложных проектах уровня Хромиума эти описания помогают понять, зачем сделаны те или иные изменения.
С другой стороны, в VS code коммиты довольно краткие: https://github.com/Microsoft/vscode/commits/master
В доктрине тоже краткие: https://github.com/doctrine/doctrine2/commits/master
Писать какой файл ты поменял в большинстве слчаев не надо - это видно в списке изменений. Надо писать для какой задачи ты это сделал.
> Или сделать десяток коммитов уровня "change db shema" для файла дампа, "change models" для классов моделей, "черт знает как назвать" для изменений в контроллере, "fix template for a new widget" в html/css?
не вижу особого смысла
>>721831
Если все коммиты в рамках одной задачи, что в них напишешь? Если это большая задача, ну допустим можно разбить ее на этапы и коммитить раз в день, а если это баг или небольшая фича, то по моему удобнее одним коммитом добавить.
>>721883
Вряд ли
Помогите пожалуйста.
Можно склеивать строки через точку:
$x = $x . 'ssdada';
$x .= 'sdasdad';
Можно сложить в новый массив и склеить его implode - помнишь про такую функцию? Если нет, перечитай урок.
Я пробовал $name = implode(", ", $randomText) вставить в цикл. Но результат нулевой.
Я не могу понять как правильно записать в цикле данную конструкцию.
Вот пример кода:
for ($i = 1; $i <= 4; $i++) {
/ Выкидываем случайное число (count - число элементов в массиве) /
$random = array_rand($letters);
$randomText = $letters[$random];
$name = implode(",", $randomText);
echo "Выпало число {$random}, слог {$randomText}\n";
};
Т.е. я оставляю свои 100, 101, 102 айдишники, но добавляю токен в бд? У меня есть токен для защиты от XSRF, его использовать или новый нужен? И еще вопрос, как мне получить 32 случайных числа. У mt_rand маленький диапозон. uniqid("", true) даст 23 символа (включая буквы), эта функция подойдет?
>Внимание, вопрос
Я тебе друзь что ли?
Вот так всегда, задашь простой вопрос в надежде, что тебе напишут пару строчек, и все станет ясно. Ан нет, пошел диалог длиннопостами, от которых у шизомода случается истерика.
Но мне важно осилить доктрину, поэтому ничего не поделать, придется сыграть в сократа и критона.
>как должен поступить ORM с автором поста?
Должен загрузить, иначе какой это ORM? Если вдруг понадобится (однозначно понадобится) обратиться к свойствам автора, например вывести в виджете логин на сайте
<section class="post-container">
<h2 class="post-title">{{ post.title }}</h2>
<article>{{ post.content }}</article>
<time class="post-added">{{ post.added }}</time>
<div class="post-author-login">{{ post.author.getLogin() }}</div> // решил похвастаться семантичной версткой, но тут не знаю какой тег подобрать
</section>
>не должно быть 2 разных объекта соответствующей одной строке в таблице (то есть если пользователь второй раз просит тот же пост, он должен получить тот же самый объект).
Надо куда-то складывать в памяти. В доктрине эта шняга называется кажется UnitOfWork, если я ничего не путаю. Во всяком случае запросы оно точно складывает в какую-то очередь, и выполняет все сразу при срабатывании $em->flush();
> добавить больше моделей и связей
> грузить ли всю паутину объектов
Нет, не нужно. Нужно создать объекты-коробочки с идентификаторами, которые будут заменять реальные объекты. Реальные объекты должны выдергиваться из базы ленивой загрузкой только при прямом обращении к ним. Например получение логина автора сработает аж в самом виджете, будет подгружен автор и у него выберется свойство.
"Это" и называется прокси. Но я не понимаю как "это" реализуется, поэтому и спрашиваю.
Кажется, получить проксю можно через getReference или getPartialReference.
http://www.doctrine-project.org/api/orm/2.5/source-class-Doctrine.ORM.EntityManager.html#353-380
Мне это нужно чтобы решить проблему сохранения связанных записей без реального создания связанных записей.
Не уверен, что это то что нужно, сейчас буду проверять методом тыка, но хотелось бы разобраться, как это работает, а не писать буквы наугад.
>>721916
>А зачем ты меняешь схему? От нечего делать?
У меня в приложении было две таблицы со словарями, теперь сделал один словарь для упрощения. Понятия не имею, как назвать такой коммит. Пусть будет "use one dictionary instead of two". Тоже галиматья какая-то.
>создать новую ветку, коммитить туда "для себя" мелкие изменения с бессмысленными именами или вообще с номерами, затем слить с мастер-бранчем и дать вменяемое имя всей фиче
А мне этот способ понравится. С точки зрения главного бранча только один большой коммит, описывающий фичу. Промежуточные для себя, чтобы не потерять изменения. Потому что один такой большой коммит можно неделю пилить, боязливо не сохраняться столько времени.
И отдельная веточка никому не мешает.
>Внимание, вопрос
Я тебе друзь что ли?
Вот так всегда, задашь простой вопрос в надежде, что тебе напишут пару строчек, и все станет ясно. Ан нет, пошел диалог длиннопостами, от которых у шизомода случается истерика.
Но мне важно осилить доктрину, поэтому ничего не поделать, придется сыграть в сократа и критона.
>как должен поступить ORM с автором поста?
Должен загрузить, иначе какой это ORM? Если вдруг понадобится (однозначно понадобится) обратиться к свойствам автора, например вывести в виджете логин на сайте
<section class="post-container">
<h2 class="post-title">{{ post.title }}</h2>
<article>{{ post.content }}</article>
<time class="post-added">{{ post.added }}</time>
<div class="post-author-login">{{ post.author.getLogin() }}</div> // решил похвастаться семантичной версткой, но тут не знаю какой тег подобрать
</section>
>не должно быть 2 разных объекта соответствующей одной строке в таблице (то есть если пользователь второй раз просит тот же пост, он должен получить тот же самый объект).
Надо куда-то складывать в памяти. В доктрине эта шняга называется кажется UnitOfWork, если я ничего не путаю. Во всяком случае запросы оно точно складывает в какую-то очередь, и выполняет все сразу при срабатывании $em->flush();
> добавить больше моделей и связей
> грузить ли всю паутину объектов
Нет, не нужно. Нужно создать объекты-коробочки с идентификаторами, которые будут заменять реальные объекты. Реальные объекты должны выдергиваться из базы ленивой загрузкой только при прямом обращении к ним. Например получение логина автора сработает аж в самом виджете, будет подгружен автор и у него выберется свойство.
"Это" и называется прокси. Но я не понимаю как "это" реализуется, поэтому и спрашиваю.
Кажется, получить проксю можно через getReference или getPartialReference.
http://www.doctrine-project.org/api/orm/2.5/source-class-Doctrine.ORM.EntityManager.html#353-380
Мне это нужно чтобы решить проблему сохранения связанных записей без реального создания связанных записей.
Не уверен, что это то что нужно, сейчас буду проверять методом тыка, но хотелось бы разобраться, как это работает, а не писать буквы наугад.
>>721916
>А зачем ты меняешь схему? От нечего делать?
У меня в приложении было две таблицы со словарями, теперь сделал один словарь для упрощения. Понятия не имею, как назвать такой коммит. Пусть будет "use one dictionary instead of two". Тоже галиматья какая-то.
>создать новую ветку, коммитить туда "для себя" мелкие изменения с бессмысленными именами или вообще с номерами, затем слить с мастер-бранчем и дать вменяемое имя всей фиче
А мне этот способ понравится. С точки зрения главного бранча только один большой коммит, описывающий фичу. Промежуточные для себя, чтобы не потерять изменения. Потому что один такой большой коммит можно неделю пилить, боязливо не сохраняться столько времени.
И отдельная веточка никому не мешает.
Погоди, где твой код на Идеоне?
Выложи туда и покажи.
Тебе же выше братишка советовал конкатенацию сделать?
Результат того, что даёт тебе implode, надо конкатенировать в $name.
$name .= $result.
Каждый раз при прохождении цикла это будет прибавлять к $name по слогу.
А вообще, действительно, перечитай урок снова - не совсем возникло у тебя понимание этих довольно несложных моментов.
С чего начать, есть мануалы для простейших сайтов?
Я кстати, конкатенацию тоже пытался сделать с implode в теле цикла. Но результата нет.
Кстати, я еще вспомнил, чем хорош паттерн data mapper. Он настолько абстрагирован от БД что можно вообще обходиться без нее - например создать сущности руками или сделать какие-то свои репозитории (например для тестов). Актив рекорд такое не позволит так как там код работы с БД заложен в модели.
По коммиту: оптимизация/улучшение схемы хранения словарей в БД
По поводу доктрины: да, нужны прокси так как без них мы загрузим в память половину базы данных. Тогда второй вопрос, а как это реализовать на практике. вот, какие получаются требования:
- прокси-модуль должна быть совместима с оригинальной на уровне типов. То есть если есть функция:
function updatePost(Post $post)
то прокси-модель должна прозрачно проходить проверку на тайп-хинт, а php строг в этом плане.
- прокси-модель должна иметь совместимый интерфейс с исходной. То есть все те же методы, чтобы ее можно было подсунуть вместо исходной. Неплохо бы также иметь точно такие же публичные поля, но для этого в пхп не хватает возможностей языка. В Яве эту проблему тоже как-то сложно решают, чуть ли не на уровне переписывания скомпилированного байткода.
- прокси-модель должна быть дешевой при создании, не требуя обращения к БД, так как их будет много. Соответственно, данных БД изначально в ней нет.
- при этом при попытке обращения к этим данным, которых в ней нет, она должна магическим образом загрузить их. То есть:
$userProxy = .????
$post->setAuthor($userProxy);
echo $post->getAuthor()->getName(); // -- в этом месте должно загрузиться и вывестись имя втора
- при этом методы исходной модели могут быть сколько угодно сложными, не просто getName, а например такой метод:
class User
{
public funtion getRating()
{
return count($this->comments) 100 + $this->getAge() 5;
}
....
}
$userProxy = .????
echo $userProxy->getRating();
Вот смотри, имея требования выше, как можно реализовать прокси-модели? Попробуй написать класс UserProxy, соответствующий всем выше указанным требованиям. В качестве подсказки можешь взять ранее найденный код в описании паттерна.
> Я тебе друзь что ли?
Ну ты все-таки не начинающий, да и если ты сам додумаешься, пользы больше будет.
> решил похвастаться семантичной версткой, но тут не знаю какой тег подобрать
я тоже
> В доктрине эта шняга называется кажется UnitOfWork,
Да и это работает примерно так при загрузке данных из БД:
- запрашиваем из БД данные, получаем массив
- для каждой строчки берем id и ищем в IdentityMap:
- если такой объект есть, берем его
- если нет, создаем и доабвляем в I.Map
- возвращаем результаты
> Во всяком случае запросы оно точно складывает в какую-то очередь, и выполняет все сразу при срабатывании $em->flush();
не, не так. Запросы не складываются никуда. При flush обходятся все сущности в IMAp и проверяются на изменения. По изменениям генерируются запросы.
Плюсы:
- не надо указывать что поменялось, программа сама найдет (за что мы и любим ОРМ)
- не забудешь что надо сохранить изменения в данной модели
- не надо ничего делать - ОРМ все сделает сама
Минусы:
- скорость очень зависит от числа сущностей и полей. Причем если у тебя 10 000 сущностей и изменилась только одна, перебирать придется все.
Если тебе это не нравится, есть способы оптмиизации:
- помечать сущности как read only, их проверять не надо
- перейти на схему когда сущность сама сообщает о измененях в ней. Производительность подскочит, но если ты сделашеь ошибку и забудешь сообщить об измненении - оно не сохранится. Мучителен будет поиск бага. Потому такую штуку лучше делать как-то может через какой-то базовый класс и магисеские методы, чтобы точно не забыть.
> С точки зрения главного бранча только один большой коммит, описывающий фичу. Промежуточные для себя, чтобы не потерять изменения. Потому что один такой большой коммит можно неделю пилить, боязливо не сохраняться столько времени.
> И отдельная веточка никому не мешает.
ну дело вкуса, я больше люблю большие коммиты.
Кстати, я еще вспомнил, чем хорош паттерн data mapper. Он настолько абстрагирован от БД что можно вообще обходиться без нее - например создать сущности руками или сделать какие-то свои репозитории (например для тестов). Актив рекорд такое не позволит так как там код работы с БД заложен в модели.
По коммиту: оптимизация/улучшение схемы хранения словарей в БД
По поводу доктрины: да, нужны прокси так как без них мы загрузим в память половину базы данных. Тогда второй вопрос, а как это реализовать на практике. вот, какие получаются требования:
- прокси-модуль должна быть совместима с оригинальной на уровне типов. То есть если есть функция:
function updatePost(Post $post)
то прокси-модель должна прозрачно проходить проверку на тайп-хинт, а php строг в этом плане.
- прокси-модель должна иметь совместимый интерфейс с исходной. То есть все те же методы, чтобы ее можно было подсунуть вместо исходной. Неплохо бы также иметь точно такие же публичные поля, но для этого в пхп не хватает возможностей языка. В Яве эту проблему тоже как-то сложно решают, чуть ли не на уровне переписывания скомпилированного байткода.
- прокси-модель должна быть дешевой при создании, не требуя обращения к БД, так как их будет много. Соответственно, данных БД изначально в ней нет.
- при этом при попытке обращения к этим данным, которых в ней нет, она должна магическим образом загрузить их. То есть:
$userProxy = .????
$post->setAuthor($userProxy);
echo $post->getAuthor()->getName(); // -- в этом месте должно загрузиться и вывестись имя втора
- при этом методы исходной модели могут быть сколько угодно сложными, не просто getName, а например такой метод:
class User
{
public funtion getRating()
{
return count($this->comments) 100 + $this->getAge() 5;
}
....
}
$userProxy = .????
echo $userProxy->getRating();
Вот смотри, имея требования выше, как можно реализовать прокси-модели? Попробуй написать класс UserProxy, соответствующий всем выше указанным требованиям. В качестве подсказки можешь взять ранее найденный код в описании паттерна.
> Я тебе друзь что ли?
Ну ты все-таки не начинающий, да и если ты сам додумаешься, пользы больше будет.
> решил похвастаться семантичной версткой, но тут не знаю какой тег подобрать
я тоже
> В доктрине эта шняга называется кажется UnitOfWork,
Да и это работает примерно так при загрузке данных из БД:
- запрашиваем из БД данные, получаем массив
- для каждой строчки берем id и ищем в IdentityMap:
- если такой объект есть, берем его
- если нет, создаем и доабвляем в I.Map
- возвращаем результаты
> Во всяком случае запросы оно точно складывает в какую-то очередь, и выполняет все сразу при срабатывании $em->flush();
не, не так. Запросы не складываются никуда. При flush обходятся все сущности в IMAp и проверяются на изменения. По изменениям генерируются запросы.
Плюсы:
- не надо указывать что поменялось, программа сама найдет (за что мы и любим ОРМ)
- не забудешь что надо сохранить изменения в данной модели
- не надо ничего делать - ОРМ все сделает сама
Минусы:
- скорость очень зависит от числа сущностей и полей. Причем если у тебя 10 000 сущностей и изменилась только одна, перебирать придется все.
Если тебе это не нравится, есть способы оптмиизации:
- помечать сущности как read only, их проверять не надо
- перейти на схему когда сущность сама сообщает о измененях в ней. Производительность подскочит, но если ты сделашеь ошибку и забудешь сообщить об измненении - оно не сохранится. Мучителен будет поиск бага. Потому такую штуку лучше делать как-то может через какой-то базовый класс и магисеские методы, чтобы точно не забыть.
> С точки зрения главного бранча только один большой коммит, описывающий фичу. Промежуточные для себя, чтобы не потерять изменения. Потому что один такой большой коммит можно неделю пилить, боязливо не сохраняться столько времени.
> И отдельная веточка никому не мешает.
ну дело вкуса, я больше люблю большие коммиты.
randomText это не массив. Его нельзя передавать в implode.
>>721952
Да, добавляешь.
А токен для защиты от CSRF ведь в БД не хранится? Тогда его нельзя использовать. Но можно сделать наоборот - исплоьзовать токен авторизации еще и как CSRF.
> И еще вопрос, как мне получить 32 случайных числа
Вызвать mt_rand 32 раза.
Нет, в данный момент не хранится. Отлично, спасибо оп!
Давай подойдем с другой стороны. Можешь ли ты создать массив, содержащий квадраты чисел от 1 до N?
$n =10;
... магия ...
var_dump($result); // выведет 1, 4, 9, 16 ...100
Извините за мою тупость. Но разве нет способа вносить данные в переменную после каждого прохода цикла? Что то вроде $name = $randomText++
Переменная может хранить только одно значение. Новое значение заменяет старое.
Надо либо как-то объединять новое значение со старым при перезаписи либо использовать массив который - сюрприз - позволяет хранить много значений и добавлять их. Для этого реши выше задачу про числа.
Ну вот точно также можно складывать в массив отдельные слоги. А в конце склеить его в одну строку.
Хотя нет, не так. ты зачем 2 вложенных цикла сделал, да еще и с одной и той же переменной, так что внешний цикл работает всего 1 раз и ровно ничего не делает?
>>721998
>>721985
>>721970
Огромное спасибо за вашу помощь и потраченное время на объяснение элементарного.
Вот код: http://ideone.com/qShrFK
Ещё раз благодарю.
Сделать 1 цикл. Ты вообще, понимаешь, что делать цикл? Цикл делает одни и те же действия N раз подряд. А если ты вкладываешь циклы (с разными переменными) то всего действие выполнится MxN раз.
А если ты вкладываешь циклы с одной и той же пермеенной то это ошибка и непонимание того как работают циклы. переделай программу правильно и сделай еще вторую задачку:
- выведи таблицу умножения, первое число меняется от 1 до 4, второе от 1 до 5:
1x1 = 1
1x2 = 2
1x3 = 3
1x4 = 4
1x5 = 5
2x1 = 1
2x2 = 4
...
4x5 = 20
- сделал? А теперь передай так, чтобы второй множитель был строго больше первого. То есть 1x1 или 2x1 не должны выводиться.
1) сделать с хостинга редирект 301 для каждой страницы на нем на аналогичную на основном сайте
2) в robots txt указать что он зеркало
Вообще читай мауналы https://yandex.ru/support/webmaster/yandex-indexing/site-mirrors.xml
Хочу придти к успеху, выучить php и кодить за миску рамена, совмещая с институтом.
Я правильно понимаю, что нужно linux + mysql + php + javascript + html/css, причем желательно еще фреймворков?
Где почитать, как использовать вместе всю эту связку при построении сайта, а не отдельно синтаксис языков?
И где здесь низкий порог входа?
>желательно еще фреймворков
Не желательно, а обязательно.
Почитать учебник опа по ссылкам из оп-поста.
>где здесь низкий порог входа?
Низкий порог только для неквалифицированной работы за копейки.
Так как конкуренция очень большая, требования к претендентам на хорошие вакансии еще выше, чем для других языков.
Год займет неспешным темпом. Я учил. Что надо? Удивительные истории о миллионных зарплатах?
Нет, теперь еще год учить фреймворки.
Но я верю в загробную жизнь.
После твоего поста я ещё раз проинспектировал свой код и нашёл оплошность в условии внутри цикла.
Было:
if ($serviceFee == 7777 && $months > 3) {
$creditDebt = $creditDebt - $serviceFee;
}
Стало:
if ($serviceFee == 7777 && $months >= 3) {
$creditDebt = $creditDebt - $serviceFee;
}
Но даже при таком исправлении не получилось 53к.
http://ideone.com/t3VyEi
Ты один раз прибавил 7777, а я это сумму прибавляю каждый месяц, но условие вычитает её, начиная с третьего месяца кредита.
В чём может быть загвоздка?
За 3 месяца научился писать лабы в вузике, или приняли на позицию миддл-лысп разработчика через 3 месяца обучения с нуля?
>если $months >= 3
>В чём может быть загвоздка?
В этом. В том, что условие это срабатывает тогда, когда $months становится равна 3, то есть на третий месяц.
Попробуй ввести дополнительную переменную для этой платы за открытие счёта и просто единожды прибавить её к сумме кредита с самого начала. Для других банков эта же плата пусть будет равна нулю (потому что вдруг понадобится для ещё каких-нибудь банков её включать?).
У этого банка изначально прибавляется 7777, а у других изначально прибавляется эта же переменная, которая равна нулю (мы в скобках же её указываем, там у них будут нули, а у третьего банка - 7777).
А дальше считай всё так, как и должно быть, там всё правильно у тебя дальше, что видно по двум другим банкам.
Почитай ОП пост. Там есть задания и ссылки по HTML/CSS, так что и теория и практика.
Начсет задач - порядок такой:
- учебник
- ставим вебсервер
- студенты
- файлообменник
Смотри, какой у тебя уровень и за что тебе браться. Я бы советовал как минимум в учебнике регулярки и ООП посмотреть.
7777 надо просто в начале, перед расчетом один раз прибавить к долгу. То есть
начальная сумма долга = сумма кредита + доп. плата
>Вот смотри, имея требования выше, как можно реализовать прокси-модели?
Не знаю.
>Попробуй написать класс UserProxy, соответствующий всем выше указанным требованиям.
Как?
Наличие методов с одинаковыми именами может дать интерфейс общий для Enity и Proxy. Или унаследовать Proxy от Entity и переопределить методы.
Но как реализовать ленивую загрузку не знаю.
Возможно в класс прокси положить ссылку на репозиторий, чтобы когда идет обращение к свойству, которое в прокси пустое, из репозитория извлекалась реальная Entity и у нее соответствующий геттер.
Короче от тебя помощи как с козла молока.
Придется тупо заучить, что получение прокси происходит через $em->getReference('EntityName', $id);
Или getPartialReference, когда нужен только идентификатор.
В моем случае для сохранения связанных данных:
$ids = [1, 2, 3];
$refs = [];
foreach ($ids as $id) {
$refs[] = $em->getPartialReference('EntityName', $id);
}
$owner = new Owner;
$owner->setOneToMany($refs);
$em->persist($owner);
$em->flush();
Проверил - работает. Может это и костыль, мне почем знать? С такими мануалами ничего не понятно.
Сволочи.
>Вот смотри, имея требования выше, как можно реализовать прокси-модели?
Не знаю.
>Попробуй написать класс UserProxy, соответствующий всем выше указанным требованиям.
Как?
Наличие методов с одинаковыми именами может дать интерфейс общий для Enity и Proxy. Или унаследовать Proxy от Entity и переопределить методы.
Но как реализовать ленивую загрузку не знаю.
Возможно в класс прокси положить ссылку на репозиторий, чтобы когда идет обращение к свойству, которое в прокси пустое, из репозитория извлекалась реальная Entity и у нее соответствующий геттер.
Короче от тебя помощи как с козла молока.
Придется тупо заучить, что получение прокси происходит через $em->getReference('EntityName', $id);
Или getPartialReference, когда нужен только идентификатор.
В моем случае для сохранения связанных данных:
$ids = [1, 2, 3];
$refs = [];
foreach ($ids as $id) {
$refs[] = $em->getPartialReference('EntityName', $id);
}
$owner = new Owner;
$owner->setOneToMany($refs);
$em->persist($owner);
$em->flush();
Проверил - работает. Может это и костыль, мне почем знать? С такими мануалами ничего не понятно.
Сволочи.
> Наличие методов с одинаковыми именами может дать интерфейс общий для Enity и Proxy.
В коде в тайп-хинтах везде стоит Entity, а не EntityInterface следовательно подход с интерфейсом неработоспособен. Вариант с наследованием опирается на принцип Лисков и выглядит более многообещающе.
> Возможно в класс прокси положить ссылку на репозиторий, чтобы когда идет обращение к свойству, которое в прокси пустое, из репозитория извлекалась реальная Entity и у нее соответствующий геттер.
Ссылка на репозиторий - нормальная идея. А вот насчет свойства - не очень удачная. Магические методы срабатвают только когда свойства нет в объекте, а не когда оно пустое. Так что перехватывать обращения к свойстуу не получится.
Подскажу: можно отловить вызов метода, переопределив его.
> С такими мануалами ничего не понятно.
У доктрины достатчоно хороший и адекватный мануал. ты как будто первый раз видишь документацию. Часто бывает намного хуже. кстати ты бы мог посмотреть в коде getReference как оно работает.
Кстати в JS делают поддержку прокси на уровне языка (ну точнее стандартной библиотеки):
https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Proxy
https://learn.javascript.ru/proxy
>Подскажу: можно отловить вызов метода, переопределив его.
http://ideone.com/6TwREv
> 28 minutes ago
Я догадался. Но что дальше?
>ты как будто первый раз видишь документацию
Не первый, но не перестаю проклинать, когда написано плохо. Нет описания, как реализованы прокси в доктрине. Они наверное рассчитывают, что это будут читать люди, получившие профильное образование в каком-нибудь mit.
Поэтому я утверждаю, что документация плохая.
Щас психану.
Кстати если бы не требование совместимости по типам, то сделать прокси было бы легче - достаточно сделать класс без публичных методов и свойств, с магическими методами. Любые обращения снаружи перехватывались бы ими.
Что такое магические методы?
__get, __call?
Но что это дает?
__call сработает только если метода не существует. Но мы наследуем прокси от сущности, значит в нем уже есть одноименные методы, и __call не сработает.
Что такое репозиторий? Это data mapper, который при обращении к нему лезет в базу? Или он только хранит объекты? Или и то и другое (возвращает объект из контейнера если есть, если нет лезет за ним в базу)?
Документация для пользователя (а не разработчика) и не обязана описывать реализацию. Она описывает поведение. реализация может меняться со временем. хотя конечно в такой библиотеке как доктрина, абстрагироваться от реализации не получится, можно получить либо баги либо тормоза.
> public function __construct($id)
>{
> $this->repository = new UserRepository;
Репозиторий стоит инжектить так как у него могут быть зависимости. Ну и вообще, DI же нужна.
Подход с загрузкой немного неверный. Вот посмотри:
> public function getRating()
>{
> if ($this->raging === null) {
> return $this->repository->findById($this->id)->getRating();
> }
> parent::getComments();
>}
во-первых, в такой реализации нельзя иметь rating равный null. Для признака наличия данных лучше сделать отдельное поле.
Во-вторых, ты забыл про identity map. $this->repository->findById($this->id) вернет $this то есть тот же объект. Соответственно твой код впадет в вечный цикл (на самом деле не вечный, после примерно 100 вызовов стек закончится и программа упадет).
наконец вызов getComments вообще ни к селу ни к городу. и ты забыл return.
Логика должна быть немного другая. Логика такая, что мы не можем вызвать оригинальный метод если в объекте не заполнено хоть одно поле (так как мы не знаем какие поля нужны будут методу). Логика должна быть такая:
Если (данные не загружены) {
// ленивая загрузка
загрузить все данные из базы в поля объекта;
пометить что данные загружены;
}
вызвать оригинальный метод;
(кстати, доктрина загружает данные в обход ограничений доступа к полю через reflection. это в принципе мелочь, можно было бы заставить ее использовать сеттеры, но тогда ты бы был обязан их реализовать, а в нынешней реализации ты не обязан делать ни сеттеры, ни геттеры. Например если ты хочешь запретить менять какое-то поле извне)
Единственное исключение можно сделать для метода getId - ему обращение к БД не нужно.
Ты писал прокси руками. Доктрина делает шаг дальше и сама генерирует за тебя прокси (я же говорил, какая это хорошая библиотека):
https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Proxy/ProxyGenerator.php
https://github.com/doctrine/doctrine2/tree/master/lib/Doctrine/ORM/Proxy
ты можешь найти прокси во временной папке, путь к ней указывается в конфиграции доктрины. (найди и посмотри)
Там есть 2 режима:
- в debug -режиме при каждом запросе прокси генерируются если их нет или файл был обновлен.
- на продакшене прокси генерируются при деплое приложения. И соответственно во время работы проверок не устарел ли файл, н еделается
Разберись как режимы переключаются.
-----
ну и напоследок, подумай, какие ограничения на нас накладвает эта схема. Вот что я вижу:
- конструктор модели должен быть без аргументов (это нужно чтобы доктрина могла их создавать)
- нельзя исплоьзовать публичные свойства так как обращение к ним не будет увидено и прокси будет вести себя не как исходная модель:
$userProxy = new UserProxy...
$userProxy->field = 1; // это поле будет перезаписано при ленивой загрузке
подумай, поддерживает ли эта схема:
- наследование моделей
- сериализацию моделей
- магические методы в моделях
ну или можешь попробовать найти овтет в докуентации.
В любом случае, когда ты знаешь как устроены прокси, ты будешь лучше понимать возможности что они дают и ограничения что они накладывают.
Документация для пользователя (а не разработчика) и не обязана описывать реализацию. Она описывает поведение. реализация может меняться со временем. хотя конечно в такой библиотеке как доктрина, абстрагироваться от реализации не получится, можно получить либо баги либо тормоза.
> public function __construct($id)
>{
> $this->repository = new UserRepository;
Репозиторий стоит инжектить так как у него могут быть зависимости. Ну и вообще, DI же нужна.
Подход с загрузкой немного неверный. Вот посмотри:
> public function getRating()
>{
> if ($this->raging === null) {
> return $this->repository->findById($this->id)->getRating();
> }
> parent::getComments();
>}
во-первых, в такой реализации нельзя иметь rating равный null. Для признака наличия данных лучше сделать отдельное поле.
Во-вторых, ты забыл про identity map. $this->repository->findById($this->id) вернет $this то есть тот же объект. Соответственно твой код впадет в вечный цикл (на самом деле не вечный, после примерно 100 вызовов стек закончится и программа упадет).
наконец вызов getComments вообще ни к селу ни к городу. и ты забыл return.
Логика должна быть немного другая. Логика такая, что мы не можем вызвать оригинальный метод если в объекте не заполнено хоть одно поле (так как мы не знаем какие поля нужны будут методу). Логика должна быть такая:
Если (данные не загружены) {
// ленивая загрузка
загрузить все данные из базы в поля объекта;
пометить что данные загружены;
}
вызвать оригинальный метод;
(кстати, доктрина загружает данные в обход ограничений доступа к полю через reflection. это в принципе мелочь, можно было бы заставить ее использовать сеттеры, но тогда ты бы был обязан их реализовать, а в нынешней реализации ты не обязан делать ни сеттеры, ни геттеры. Например если ты хочешь запретить менять какое-то поле извне)
Единственное исключение можно сделать для метода getId - ему обращение к БД не нужно.
Ты писал прокси руками. Доктрина делает шаг дальше и сама генерирует за тебя прокси (я же говорил, какая это хорошая библиотека):
https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Proxy/ProxyGenerator.php
https://github.com/doctrine/doctrine2/tree/master/lib/Doctrine/ORM/Proxy
ты можешь найти прокси во временной папке, путь к ней указывается в конфиграции доктрины. (найди и посмотри)
Там есть 2 режима:
- в debug -режиме при каждом запросе прокси генерируются если их нет или файл был обновлен.
- на продакшене прокси генерируются при деплое приложения. И соответственно во время работы проверок не устарел ли файл, н еделается
Разберись как режимы переключаются.
-----
ну и напоследок, подумай, какие ограничения на нас накладвает эта схема. Вот что я вижу:
- конструктор модели должен быть без аргументов (это нужно чтобы доктрина могла их создавать)
- нельзя исплоьзовать публичные свойства так как обращение к ним не будет увидено и прокси будет вести себя не как исходная модель:
$userProxy = new UserProxy...
$userProxy->field = 1; // это поле будет перезаписано при ленивой загрузке
подумай, поддерживает ли эта схема:
- наследование моделей
- сериализацию моделей
- магические методы в моделях
ну или можешь попробовать найти овтет в докуентации.
В любом случае, когда ты знаешь как устроены прокси, ты будешь лучше понимать возможности что они дают и ограничения что они накладывают.
> Что такое репозиторий?
Абстрактное хранилище которое умеет доставать откуда-то сущности и сохранять их куда-то. В том числе в базу.
> Это data mapper, который при обращении к нему лезет в базу?
думаю, да. Это дата маппер по сути.
> Или и то и другое (возвращает объект из контейнера если есть, если нет лезет за ним в базу)?
Контейрер это identity map. На сайте с паттернами наверно есть отдельный раздел про паттерны ORM.
> __call сработает только если метода не существует. Но мы наследуем прокси от сущности, значит в нем уже есть одноименные методы, и __call не сработает.
Важно в прокси его не забыть перелпределить, а то может прлизойти вызов метода __call у исходной модели без данных:
$p = new UserProxy
$p->inexsitingMethod(); // упс
Также в доктрине кроме прокси для моделей есть еще прокси-коллекции. ну с ними легче так как классы коллекций это часть доктрины и с ними мы можем делать что угодно, в отличие от пользовательских моделей.
Я также замечу, что прокси-модели можно реализовать и без генерации классов-наследников. Но это требует каких-то ограничений, ну например ты можешь обязать разработчиков наследовать свои модели от BaseModel и перед любым действием вызывать $this->lazyLoadIfNeeded(); который будет проверять и делать ленивую загрузку если надо.
но такой подход очень неудобен, надо писать лишний однотипный код, надо следовать каким-то лишним правилам, легко забыть и получить баг. Потому ценность доктрины и прокси в том, что она старается делать это максимально прозрачно, чтобы прокси работали с обычными объектами и загрузка работала незаметно для разработчика.
>>Важно в прокси его не забыть перелпределить, а то может прлизойти вызов метода __call у исходной модели без данных
Что?
Еще раз:
>В контексте объекта при вызове недоступных методов вызывается метод __call().
Я правильно понимаю, что __call сработает только при обращении к несуществующему методу? Но ведь метод существует, так как мы пришли к выводу что прокси наследуется от сущности.
>В контексте объекта
Что такое контекст объекта? Унаследованные от родителя свойства и методы в него не входят?
>>722313
>Во-вторых, ты забыл про identity map
Я о нем слышу второй раз в жизни. Что это такое вообще не имею представления.
>Документация для пользователя (а не разработчика)
Это вообще пушка. Мы все еще говорим про доктрину или уже про вордпресс например?
Пойду посплю лучше, уже крыша едет.
Может завтра просветлеет.
Нет, ты видишь в чем проблема? Не описываются основы, я типа должен знать кучу паттернов, знать про всякие identity map, еще кучу всякого говна.
Я-то разберусь в конце концов, я поехавший.
Но большинство разумеется не будет задрачивать всю эту чушь, лезть в исходные коды и т.д., поэтому люди и ищут готовые рецепты или скринкасты уровня "покажи мне куда тыкать".
Начал читать документацию симфони, прямо глоток свежего воздуха.
Например в главе про HttpFoundation соблаговолили человеческим языком объяснить про HTTP протокол! Слыхано ли?
http://symfony.com/doc/current/book/http_fundamentals.html
Вот это я понимаю, вот это уважаю.
Правда это только начало, может там дальше тоже пойдет хардкор.
>>Важно в прокси его не забыть перелпределить, а то может прлизойти вызов метода __call у исходной модели без данных
Что?
Еще раз:
>В контексте объекта при вызове недоступных методов вызывается метод __call().
Я правильно понимаю, что __call сработает только при обращении к несуществующему методу? Но ведь метод существует, так как мы пришли к выводу что прокси наследуется от сущности.
>В контексте объекта
Что такое контекст объекта? Унаследованные от родителя свойства и методы в него не входят?
>>722313
>Во-вторых, ты забыл про identity map
Я о нем слышу второй раз в жизни. Что это такое вообще не имею представления.
>Документация для пользователя (а не разработчика)
Это вообще пушка. Мы все еще говорим про доктрину или уже про вордпресс например?
Пойду посплю лучше, уже крыша едет.
Может завтра просветлеет.
Нет, ты видишь в чем проблема? Не описываются основы, я типа должен знать кучу паттернов, знать про всякие identity map, еще кучу всякого говна.
Я-то разберусь в конце концов, я поехавший.
Но большинство разумеется не будет задрачивать всю эту чушь, лезть в исходные коды и т.д., поэтому люди и ищут готовые рецепты или скринкасты уровня "покажи мне куда тыкать".
Начал читать документацию симфони, прямо глоток свежего воздуха.
Например в главе про HttpFoundation соблаговолили человеческим языком объяснить про HTTP протокол! Слыхано ли?
http://symfony.com/doc/current/book/http_fundamentals.html
Вот это я понимаю, вот это уважаю.
Правда это только начало, может там дальше тоже пойдет хардкор.
Аноны, помогите же. Я знаю, вы скачивали. Хоть ссылкой на Яндекс диск поделитесь.
Понапропускал дохуя запятых, ну да похуй. Я ведь только проснулся.
Ещё были идеи таки допилить плагин до чего-то более универсального и общеприменимого, и попытаться монетизировать его, но я - безвольное омежное хуйло, неспособное продать себя.
В "Мы вам перезвоним" тред и ему подобные.
Да, так всё получается: http://ideone.com/9BHsJD
Но я с самого начала ориентировался на шаблон ОПа. Ведь он в гайде не задавал никаких отдельных переменных. Поэтому я подумал, что проблему с единичным взносом клубничного банка нужно решать внутри заретушированной части функции.
Вопрос: Можно ли одним запросом в MySql реализовать переход к следующей строке(с наибольшем id например) и если такого не найдено, то брать наименьший т.е. возвращаться в начало?
Флуд: Кто нибудь пробовал уже симфони3 ? Что там вообще на поле фреймворков сейчас происходит? На что стоит обратить внимание? Кто фаворит?
Что-то типа "SELECT * FROM table_name WHERE entry_id > ".$last_entry_id." ORDER_BY entry_id LIMIT 1 OR entry_id = MIN(entry_id)". Можно ещё через юнионы сделать, но это думаю перебор.
Если я проверяю каждое поле формы через регулярку, то получается, что смысла в обработке полей от спецсимволов/пробелов и остальной шляпы как на пике - нет?
Ну, если регулярки не допускают пробелов, спецсимволов, тегов и прочего говна, то отдельно обрабатывать не надо, верно?
Не надо. Но ты особо всё равно не парься на этот счёт. Когда перейдёшь на всякие фреймворки и прочее, там всё равно будут готовые сладкие классы-обёртки для работы с базами данных, поэтому самостоятельную проверку ввода проводить не нужно будет, по крайней по части безопасности.
Что значит "внутри"? ORDER_BY - это независимое выражение, такое же как и SELECT, к которому ты и задаёшь опциональное условие WHERE. Сначала выполняется SELECT и возможный WHERE, затем уже его выхлоп проходит через ORDER_BY и передаётся дальше LIMIT'y который берёт только первую из переданных ему записей.
Да, там лимит должен быть в самом конце ведь.
Операционные системы пишут только на html.
Давай напишем
набегите, посоветуйте, плес, ребят.
Так был один ебанутый на хабре с циклом статей "Операционная система на PHP". Но на самом деле после фраз, типа: "Современные ОС написаны на неуправляемых языках, где возможны утечки памяти и переполнения. Это путь к уязвимостям. Если же писать ОС на управляемом языке, то проблемы с повреждением памяти уйдут в прошлое, как страшный сон. Будет не только достигнут невиданный ранее уровень безопасности, но и появлятся расширенные средства для управления всем процессом работы приложения", - я понял, что он просто тролль. ЧСХ, разрывы байтоёбов в коментах были эпичны.
На шарпе же дофига опенсорсных проектов осей было в своё время. Этот петушок был действительно шизик, если не заметил, что его мечты уже давно стали реальностью.
>($creditDebt * $percent) + $serviceFee
Вот это три раза повторяется, надо бы избавиться от повторов.
>$month = 2
Не вполне правильно вообще начинать не с нуля, а тем более тут неправильно будет посчитано количество месяцев, если потребуется их тоже вывести.
Но считает сам кредит всё правильно, это уже хорошо.
Нашел лишь в песочнице
А фиг знает. Не думаю, что они куда-то делись. Просто ведь их по-большому счёту писали в академических целях, нежели в практических.
Мне вот интересно, почему нет универсальной оси, на которой можно запустить без пердолинга любую программу для любой оси (Windows, Linux, iOS, Android, MacOS - самых популярных)?
Ведь есть эмуляторы или Вайн, которые это позволяют для разных осей.
Это как приложение, которое объединило бы все возможные мессенджеры, избавило бы от необходимости просматривать всё (такое пытаются делать, но там тупо браузер с открытыми вкладками, годится только для соцсетей с работой через веб).
Даже ведь не пытались, насколько мне известно.
Заебешся и будет слишком костыльно. Я бы мечтал об общем годном стандарте, но даже не все страны используют физическую систему СИ.
Вполне возможно, к этому придут в итоге.
Как и к единому суржику на основе английского языка.
Если верить фантастам.
терзаю лекцию по пхп
не могу понять зачем объявляют свойства(переменные) в классе, которые потом один хрен передаются через метод(функцию)
пример - public $variable; зачем препод написал эту строку?
<?php
class A
{
public $variable; // зачем нужна сия строка? если ее закомментить, всеравно работает.
public function __construct($variable)
{ $this->variable =$variable; }
public function ff()
{
return "Выводим: $this->variable";
}
}
$A1 = new A("Привет");
echo $A1->ff()."<BR>";
Хуёвая лекция видимо, либо тебя плавно подводят к понятию публичного и приватного состояния объекта.
>плавно подводят к понятию публичного и приватного состояния объекта.
про он сказал, что пока пишем "паблик" и не думаем, типа так надо. потом объяснит
мне не понятно сакральное объявление переменной в данном случае, что оно дает?
Работает потому, что у тебя конструктор получает данные в переменную с таким же именем $variable, никогда так не делай ибо по невнимательности можно долго тупить.
>>722703
Так и есть. А вообще какое-то странное задание. Ты часом не в какое нить ФГУП или ещё что-то такое государственное устраиваться собрался? Я когда в бодишоп пришел в своё время, так у меня вообще по коду нихуя не спрашивали когда сказал, что есть пара работающих проектов, бородач просто код в понедельник бегло полистал и всё.
Нет, зарегистрировался на hh, какая-то московская фирма сама прислала предложение на удаленку. Знаю, звучит подозрительно, но тестовые вроде простые, что я теряю?
Ничего, покуда тебе на начнут слать их текущие задачи под видом тестовых работ. Хотя в этом тоже есть своя практическая польза для тебя.
А шапку вообще хоть кто-нибудь делал за всю историю тредов, включая задание на тестирование?
Многие делали, там же примитивщина, кроме последних двух (файлообменник и тестирование, а также задача на spa, но то по js).
Человек десять делало файлообменник на моей памяти (за последний год).
Задача на тестирование очень большая и требует хорошего знания сложного фреймворка (Yii или Symfony), умения писать тесты и еще хз чего.
Оп говорит, что пару лет назад кто-то (почти) сделал, но не закончил. Еще несколько (я в том чилсе) начинали и бросали ввиду недостатка знаний.
Сейчас сижу учу доктрину и симфони, авось через полгода смогу начать ее делать.
Обязательно отпишитесь, как на вас повлияла предоставленная нами информация.
Подпись: бюро статистики.
Меня взяли поле файлообменика, тогда только-только начинал изучать Yii
Но у меня был опыт кодинга и до пхп
public/index.php
vendor/twbs/bootstrap/dist/
Вот. Докрут папка public, как мне подключать файлы бутстрапа? Явно есть изящное решение.
Ну это же тоже самое, только без корня.
Нет изящного. Либо ставить всякую js блевоту типа grunt/gulp, либо писать пост-установочный баш-скрипт.
https://getcomposer.org/doc/articles/scripts.md#scripts
>>723097
Сервер может отдавать файлы только из DocumentRoot. Он не может подняться на уровень выше, это же не php.
Подскажите в чём проблема. Делаю задачу банкомата из учебника, но почему-то если для решения нужно больше шести купюр, программа вылетает с сообщением
>Превышено ограничение на времяtime: 5 memory: 328896 signal:24
Границы переменной цикла проверяются, должны быть меньше 1000000, но до миллиона они не доходят даже. Там правда размер массива растёт как степень 6. Но я не понял как эту задачу иначе можно решать.
композер это менеджер пакетов (то есть программа для установки библиотек) ориентированный на пхп. Он не ставит своей целью помочь тебе установить jquery. Однако! Так как люди быстро привыкают к хорошему, то они начинают добавлять в него и не-пхп библиотеки.
Кроме композера, есть ориентированный на статику пакетный менеджер - bower. Но он тоже лишь скачивает библиотеки с зависимостями в свою папку.
Предполагается что у тебя есть еще отлаженный процесс управления статическими файлами. Например, gulp (если ты любишь ноду) или самописные скрипты. Ведь часто требуется:
- оптимизировать картинки
- генерировать css спрайты
- склеивать js и css файлы
И предполагается что ты как-то сам должен обеспечить копирование файлов в public.
Вот пример одного варианта решения: http://andrey.4devs.io/ru/dependency-manager/Install-bower-and-npm-libraries-using-composer.html
В симфони есть например assetic (некоторые критикуют его за сложность и неуклюжесть)
Я в таких случях раньше писал свои скрипты, но не знаю, может не стоит с меня брать пример в строительстве велосипедов. Но стандартные решения часто тоже не ахти какие, например в том же gulp код абсолютно нечитаемый получается на мой взгляд. Вот для примера gulp скрипт для сборки майкрософтовского редактора vs code: https://github.com/Microsoft/vscode/blob/master/gulpfile.js
Мне кажется. тебе пока проще всего сделать bash скрипт для копирования файлов. Ну то есть чтобы использовать gulp или requirejs надо в них как следует разобраться, явно уровня статьи выше недостаточно.
Можешь посмотреть как опытный анон решил проблему:
https://github.com/nsdvw/Memtext/blob/master/composer.json
https://github.com/nsdvw/Memtext/blob/master/scripts/post-update-cmd.sh
(тут видно что в общем-то все пока на ручном управлении и все пути прописаны вручную)
То есть для проектов со сложной статикой мы делаем систему сборки, а для простых проще наверно баш скрипт сделать для копирования файлов.
>>723102
vendor вне публичной папки и отдавать
композер это менеджер пакетов (то есть программа для установки библиотек) ориентированный на пхп. Он не ставит своей целью помочь тебе установить jquery. Однако! Так как люди быстро привыкают к хорошему, то они начинают добавлять в него и не-пхп библиотеки.
Кроме композера, есть ориентированный на статику пакетный менеджер - bower. Но он тоже лишь скачивает библиотеки с зависимостями в свою папку.
Предполагается что у тебя есть еще отлаженный процесс управления статическими файлами. Например, gulp (если ты любишь ноду) или самописные скрипты. Ведь часто требуется:
- оптимизировать картинки
- генерировать css спрайты
- склеивать js и css файлы
И предполагается что ты как-то сам должен обеспечить копирование файлов в public.
Вот пример одного варианта решения: http://andrey.4devs.io/ru/dependency-manager/Install-bower-and-npm-libraries-using-composer.html
В симфони есть например assetic (некоторые критикуют его за сложность и неуклюжесть)
Я в таких случях раньше писал свои скрипты, но не знаю, может не стоит с меня брать пример в строительстве велосипедов. Но стандартные решения часто тоже не ахти какие, например в том же gulp код абсолютно нечитаемый получается на мой взгляд. Вот для примера gulp скрипт для сборки майкрософтовского редактора vs code: https://github.com/Microsoft/vscode/blob/master/gulpfile.js
Мне кажется. тебе пока проще всего сделать bash скрипт для копирования файлов. Ну то есть чтобы использовать gulp или requirejs надо в них как следует разобраться, явно уровня статьи выше недостаточно.
Можешь посмотреть как опытный анон решил проблему:
https://github.com/nsdvw/Memtext/blob/master/composer.json
https://github.com/nsdvw/Memtext/blob/master/scripts/post-update-cmd.sh
(тут видно что в общем-то все пока на ручном управлении и все пути прописаны вручную)
То есть для проектов со сложной статикой мы делаем систему сборки, а для простых проще наверно баш скрипт сделать для копирования файлов.
>>723102
vendor вне публичной папки и отдавать
Но ты имей в виду, даже если сейчас установка js библиотек через пакетный менеджер работает не очень хорошо, все равно это тот способ, к которому мы должны стремиться, ну и надеяться что в будущем это допилят. То есть в идеале у нас должен быть файл с указанием версий и названий библиотек, а не куча скопированных вручную файлов.
Вот еще замечания по баш скрипту:
https://github.com/nsdvw/Memtext/blob/master/scripts/post-update-cmd.sh#L6
> for font in `ls vendor/twbs/bootstrap/dist/fonts/`
> do
> cp "vendor/twbs/bootstrap/dist/fonts/$font" "web/fonts/$font"
Тут не нужен ls, можно писать
for font in vendor/twbs/bootstrap/dist/fonts/*
И даже цикл тут не нужен так как можно написать:
cp vendor/twbs/bootstrap/dist/fonts/* target
Также, не стоит разбирать внешние библиотеки на файлы. Если там есть папка dist (файлы для рапространения) то надо просто ее целиком скопировать в публичную папку веб-сервера.
https://github.com/nsdvw/Memtext/blob/9b4fc4973196900252ea7c2d1d20af17a0afc3b7/app/Service/TextService.php#L50
Это не ошибка, но Query Builder предназначен для построения запроса по частям, например в цикле или с использованием if.
Если у тебя запрос всегда одинаков то выгоднее написать его на DQL/SQL. А не городить кучу скобочек, стрелочек и кавычек.
https://github.com/nsdvw/Memtext/blob/60cae08ef3f559dc48f2707186fd0b8446ef1e86/web/js/test.js#L25
> userAnswer.on("keypress", pressingEnter);
Ты наверно хотел написать submit? Отправить форму можно несколькими способами:
- нажать кнопку
- нажать ентер
- нажать клавишу отправки на экранной клавиатуре на мобильном устройстве
Твой подход не позволяет реализовть поддержку третьего варианта. надо использлвать событие submit. Чтобы не делать таких ошибок, прочитай-ка список событий в браузере
https://learn.javascript.ru/introduction-browser-events
http://frontender.info/an-introduction-to-dom-events/
https://developer.mozilla.org/ru/docs/Web/Events
или если ты предпочитаешь официальные спецификации
https://www.w3.org/TR/uievents/#event-types-list
https://www.w3.org/TR/DOM-Level-3-Events/#event-types-list
https://github.com/nsdvw/Memtext/blob/9b4fc4973196900252ea7c2d1d20af17a0afc3b7/app/Service/TextService.php#L50
Это не ошибка, но Query Builder предназначен для построения запроса по частям, например в цикле или с использованием if.
Если у тебя запрос всегда одинаков то выгоднее написать его на DQL/SQL. А не городить кучу скобочек, стрелочек и кавычек.
https://github.com/nsdvw/Memtext/blob/60cae08ef3f559dc48f2707186fd0b8446ef1e86/web/js/test.js#L25
> userAnswer.on("keypress", pressingEnter);
Ты наверно хотел написать submit? Отправить форму можно несколькими способами:
- нажать кнопку
- нажать ентер
- нажать клавишу отправки на экранной клавиатуре на мобильном устройстве
Твой подход не позволяет реализовть поддержку третьего варианта. надо использлвать событие submit. Чтобы не делать таких ошибок, прочитай-ка список событий в браузере
https://learn.javascript.ru/introduction-browser-events
http://frontender.info/an-introduction-to-dom-events/
https://developer.mozilla.org/ru/docs/Web/Events
или если ты предпочитаешь официальные спецификации
https://www.w3.org/TR/uievents/#event-types-list
https://www.w3.org/TR/DOM-Level-3-Events/#event-types-list
>Можешь посмотреть как опытный анон решил проблему
Вот я примерно так и решил. Создал еще одну папку vendor в док руте, пробросил в нее симлинки до нужных мне файлов и добавил ее в гитигнор.
Вместо $(pwd) можно писать
./vendor/...
или
vendor/...
Относительные пути (не начинабщиеся со слеша) разрешаются относительно текущей директории.
Ну и у симлинков на мой взгляд есть недостатки, например некоторые программы при рекурсивном удалении файлов могут удалить содержимое симлинкнутой папки, если в них это не предусмотрено. Ну то есть у меня скорее интуитивное недоверие к такому подходу, а убедительных аргументов нет.
Так и делал вначале, ссылки почему-то битые получались. Этот $(pwd) нагуглился как решение.
> как опытный анон решил проблему
Не очень и опытный. Учился около 1.5 лет с перерывами, сначала полгода по всяким васянским бложикам и видеокурсам, потом когда наткнулся на тред дело пошло значительно быстрее под твоим чутким руководством.
Плюс подтянул английский, будто второй раз родился.
>>723196
Написано после пятиминутного знакомства с синтаксисом bash, поэтому криво, знаю.
>там есть папка dist (файлы для рапространения) то надо просто ее целиком скопировать в публичную папку веб-сервера.
Это разве что у бутстрапа.
У jquery другая папка, и там куча всякого барахла. Нет, места конечно не жалко, но странно копировать 10 файлов, когда мне нужен один.
Да и у бутстрапа мне нужно только 3 или 4 файла, зачем мне например boostrap.css.map или bootstrap-theme.css например?
Про ckeditor вообще молчу, там в комплекте идут примеры, хелп, еще куча всякого мусора.
Вот мне как-то совсем не нужно, чтобы пользователь мог перейти на моем сайте по url /ckeditor/samples/index.html и увидеть приветственную страничку об успешной установке.
Мало того, там еще большую часть плагинов скорее всего нужно выкинуть, просто не было времени разбираться, что нужно удалить что оставить.
Поэтому вот так кастомно копировал некоторые файлы.
>>723203
>Query Builder предназначен для построения запроса по частям
Хотелось попробовать все формы построения запроса, я же только учу доктрину, еще неуверенно в ней чувствую, где там какую форму использовать.
>Ты наверно хотел написать submit? Отправить форму можно несколькими способами
Хотел, но передумал. В том-то и дело, что это не форма. Это тупо кнопка для взаимодействия с приложением.
Пользователь вводит в инпут (инпут тоже форме не принадлежит), и клацает кнопку. Js реагирует на нажатие (?), проверяет введенный пользователем ответ и перестраивает страницу.
Нету формы. Или там даже не кнопка, а ссылка со стилями кнопки, не помню.
В общем событие сабмит на ней не сработает, оно ведь принадлежит форме, не так ли?
Хотел как раз спросить, но забыл и так оставил.
>куча ссылок
Завтра почитаю.
Помоги мне лучше разобраться с доктриной.
Я имею ввиду внутреннее устройство. Эти все identity map, репозитории, unit of work и прочие непонятные слова.
Сегодня перечитал кучу всякой ерудны, все равно ничего непонятно.
Нашел даже книгу, как мне показалось автор как раз намеревался раскрыть как там все устроено.
От восторга начал даже переводить
https://gist.github.com/nsdvw/5d4f07bd782cf860bc95a7ed6cd2f759
а оказалось облом, автор написал велосипед, реализующий 1/10 часть возможностей доктрины, потом говорит: "видите какое говно получается? давайте ценить доктрину!". И давай пересказывать оф.документацию, что я и так знаю. Да, наверное надо было сначала глянуть оглавление.
Что я от тебя хочу? Проведи за ручку по коду.
Типа вот тут вызывается
$em->find('Namespace:EntityName', 123);
Что происходит? Entity manager лезет в репозиторий, который лежит в свойстве таком-то.
У репозитория в свойстве identity_map (?) хранятся сущности, извлеченные из базы, или вновь созданные. Чтобы отличать статус сущности, где-то там указывается свойство $isDirty, или что-то подобное.
Ну и так далее. Где там эти долбанные прокси-объекты создаются? Из того что я смутно понял из документации и других источников, классы прокси генерируются самой доктриной? Как такое вообще возможно? Пишет новый файл с классом что ли? Чудеса акробатики какие-то.
Запутано все слишком, я сам не разберусь. Тут в принципе делов на десять минут. Что там вызывается, создается, куда оно там сохраняется. Потому что у меня в голве уже каша от всего этого.
> как опытный анон решил проблему
Не очень и опытный. Учился около 1.5 лет с перерывами, сначала полгода по всяким васянским бложикам и видеокурсам, потом когда наткнулся на тред дело пошло значительно быстрее под твоим чутким руководством.
Плюс подтянул английский, будто второй раз родился.
>>723196
Написано после пятиминутного знакомства с синтаксисом bash, поэтому криво, знаю.
>там есть папка dist (файлы для рапространения) то надо просто ее целиком скопировать в публичную папку веб-сервера.
Это разве что у бутстрапа.
У jquery другая папка, и там куча всякого барахла. Нет, места конечно не жалко, но странно копировать 10 файлов, когда мне нужен один.
Да и у бутстрапа мне нужно только 3 или 4 файла, зачем мне например boostrap.css.map или bootstrap-theme.css например?
Про ckeditor вообще молчу, там в комплекте идут примеры, хелп, еще куча всякого мусора.
Вот мне как-то совсем не нужно, чтобы пользователь мог перейти на моем сайте по url /ckeditor/samples/index.html и увидеть приветственную страничку об успешной установке.
Мало того, там еще большую часть плагинов скорее всего нужно выкинуть, просто не было времени разбираться, что нужно удалить что оставить.
Поэтому вот так кастомно копировал некоторые файлы.
>>723203
>Query Builder предназначен для построения запроса по частям
Хотелось попробовать все формы построения запроса, я же только учу доктрину, еще неуверенно в ней чувствую, где там какую форму использовать.
>Ты наверно хотел написать submit? Отправить форму можно несколькими способами
Хотел, но передумал. В том-то и дело, что это не форма. Это тупо кнопка для взаимодействия с приложением.
Пользователь вводит в инпут (инпут тоже форме не принадлежит), и клацает кнопку. Js реагирует на нажатие (?), проверяет введенный пользователем ответ и перестраивает страницу.
Нету формы. Или там даже не кнопка, а ссылка со стилями кнопки, не помню.
В общем событие сабмит на ней не сработает, оно ведь принадлежит форме, не так ли?
Хотел как раз спросить, но забыл и так оставил.
>куча ссылок
Завтра почитаю.
Помоги мне лучше разобраться с доктриной.
Я имею ввиду внутреннее устройство. Эти все identity map, репозитории, unit of work и прочие непонятные слова.
Сегодня перечитал кучу всякой ерудны, все равно ничего непонятно.
Нашел даже книгу, как мне показалось автор как раз намеревался раскрыть как там все устроено.
От восторга начал даже переводить
https://gist.github.com/nsdvw/5d4f07bd782cf860bc95a7ed6cd2f759
а оказалось облом, автор написал велосипед, реализующий 1/10 часть возможностей доктрины, потом говорит: "видите какое говно получается? давайте ценить доктрину!". И давай пересказывать оф.документацию, что я и так знаю. Да, наверное надо было сначала глянуть оглавление.
Что я от тебя хочу? Проведи за ручку по коду.
Типа вот тут вызывается
$em->find('Namespace:EntityName', 123);
Что происходит? Entity manager лезет в репозиторий, который лежит в свойстве таком-то.
У репозитория в свойстве identity_map (?) хранятся сущности, извлеченные из базы, или вновь созданные. Чтобы отличать статус сущности, где-то там указывается свойство $isDirty, или что-то подобное.
Ну и так далее. Где там эти долбанные прокси-объекты создаются? Из того что я смутно понял из документации и других источников, классы прокси генерируются самой доктриной? Как такое вообще возможно? Пишет новый файл с классом что ли? Чудеса акробатики какие-то.
Запутано все слишком, я сам не разберусь. Тут в принципе делов на десять минут. Что там вызывается, создается, куда оно там сохраняется. Потому что у меня в голве уже каша от всего этого.
>Вообще, я мельком посмотрел и вижу что у тебя в коде не очень четко распределены зоны отвественности. В ООП обычно каждый класс имеет свою зону ответсвенности и занимается своим делом.
Да, я не могу понять кто чем должен заниматься и кто что должен хранить. Например, сейчас нужно сделать для валидации массив которые хранит название полей и функции валидации к ним, но откуда я должен получить этот массив? Прописывать прямо по среди функции валидации какой-то сущности или же получать его из аргумента? Если из аргумента, то где тогда он должен быть реализован? Хранится в самой проверяемой сущности?
Как мне может придти это всё сразу в голову? Посмотри как много вопросов ко мне приходит чтобы прийти к хотя бы к какому-то осмысленному решению, и это при условии что ты мне намекнул в чём ошибка. Я не могу этому просто догадаться, это совсем не очевидно. Мне нужны простые примеры где я видел бы всё перед своими глазами как всё устроенно. Нужна карта перед глазами, иначе это будет попыткой добраться, скажем, из Москвы до Парижа в слепую.
>>717443
>> https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L28
>тут стена копипасты. Сделай так, чтобы у формы была функция заполнения данных из любого переданного массива (не только из POST) одним действием:
>
>$data = [
>'name' => 'Ivan',
>'xzzzzzzz' => 123456
>];
>
>$form->fillFromArray($data);
>
>А как у тебя это реализовать? Вообще никак, так как форма не умеет брать данные ниоткуда, кроме POST. Непонятно какая выгода от такой привязки, и вообще работу с POST лучше бы оставить в контроллере.
>
>При написании таких методов стоит помнить что нельзя просто копирвоать все поля. Могут быть какие-то поля (например статус модератора) которые не должны заполняться из пришедших данных. Только те поля, что разрешено править.
Я не вижу такого решения с помощью цикла, только прописывать всё в ручную
$data = [
'allowed' => 'somevalue';
'notallowed' => 'anothervalue';
]
...
function fillFromArray(array $data)
{
$this->allowed = $data['allowed'];
}
А почему именно из массива? Вот например у меня раньше брались данные из $_POST, и я подумать не мог что они будут браться откуда-то еще. Если мы собрались расширять гибкость кода, то давайте расширять до конца. Например, мы можем получать данные и из объекта.
Кстати, меня почему-то продолжает тянуть писать переменные не в camelcase - короткие слова легко разобрать и так. Полагаю, что camelcase был придуман чтобы различать слова, но какой смысл в его использовании, если и так всё различается с первого взгляда?
>>717443
>> if (!count(array_filter($registerStudentForm->getErrors()))) {
>Это длинно и неудобно. Я вызвал функцию вадидации и теперь должен еще писать какой-то громоздкий код, чтобы узнать есть там ошибки или нет. Предлагаю сделать список ошибок объектом ErrorList такого вида:
Может лучше на каждую сущность иметь свой класс Error? Например PostErrors... И все бы они наследовались от одного класса Errors который содержит методы для работы, и еще в них можно хранить выше упомянутый массив с именами полей и их функциями валидации.
>>717443
>Также, форма могла бы предоставлять готовые методы для копирования данных в/из студента.
А почему форма, а не контроллер? Я бы сказал это его задача передавать данные из одного в другое.
>>717433
>Но если задуматься, какой в этом смысл, хранить 2 копии данных? Не проще ли внутри формы хранить сам объект студента с данными? Тогда и валидация упростится. И вообще все упростится.
Нет, ничего не упроститься, всё наслоится друг на друга и не будет понятно что на самом деле есть класс PostForm. Тоже самое я заметил когда изучал symfony forms:
Чтобы понять что делает метод add() мне пришлось открывать 12 файлов, и при этом, когда ты изучаешь какую-то функцию, ты забываешь что её вызывало >3 файлов назад. И даже после перечитывания, я всё равно застрял на этом моменте:
https://github.com/symfony/form/blob/master/FormFactory.php#L85
https://github.com/symfony/form/blob/master/Extension/Core/Type/FormType.php
https://github.com/symfony/form/blob/master/Extension/Core/Type/BaseType.php
https://github.com/symfony/form/blob/master/AbstractType.php
Где реализуется метод createBuilder()? Я вижу что он есть в FormFactory, но там обращаются к этому методу от класса FormType.
Затем я подумал что symfony form это кофеварка которая предоставляет наружу методы, а как она устроенна меня волновать не должно, и я попробовал её реализовать у себя основываясь только на примерах из документации, но у меня ничего не получилось >>720677
>>720677
Пока не знаю что и сказать, для меня это сложно пока. Могу я сначала изучить код и задавать вопросы по нему сюда, и потом вернутся к тому посту, и сделать что нужно?
>>721507
>> Откуда берется $view?
>Выше же в коде есть $form->createView():
Так я писал его, и приписывал еще $view = $form->createView(), во всех случаях я получал ошибку о неизвестной переменной\ключе\методе.
>Вообще, я мельком посмотрел и вижу что у тебя в коде не очень четко распределены зоны отвественности. В ООП обычно каждый класс имеет свою зону ответсвенности и занимается своим делом.
Да, я не могу понять кто чем должен заниматься и кто что должен хранить. Например, сейчас нужно сделать для валидации массив которые хранит название полей и функции валидации к ним, но откуда я должен получить этот массив? Прописывать прямо по среди функции валидации какой-то сущности или же получать его из аргумента? Если из аргумента, то где тогда он должен быть реализован? Хранится в самой проверяемой сущности?
Как мне может придти это всё сразу в голову? Посмотри как много вопросов ко мне приходит чтобы прийти к хотя бы к какому-то осмысленному решению, и это при условии что ты мне намекнул в чём ошибка. Я не могу этому просто догадаться, это совсем не очевидно. Мне нужны простые примеры где я видел бы всё перед своими глазами как всё устроенно. Нужна карта перед глазами, иначе это будет попыткой добраться, скажем, из Москвы до Парижа в слепую.
>>717443
>> https://github.com/someApprentice/Students/blob/master/app/Controller/RegisterAction.php#L28
>тут стена копипасты. Сделай так, чтобы у формы была функция заполнения данных из любого переданного массива (не только из POST) одним действием:
>
>$data = [
>'name' => 'Ivan',
>'xzzzzzzz' => 123456
>];
>
>$form->fillFromArray($data);
>
>А как у тебя это реализовать? Вообще никак, так как форма не умеет брать данные ниоткуда, кроме POST. Непонятно какая выгода от такой привязки, и вообще работу с POST лучше бы оставить в контроллере.
>
>При написании таких методов стоит помнить что нельзя просто копирвоать все поля. Могут быть какие-то поля (например статус модератора) которые не должны заполняться из пришедших данных. Только те поля, что разрешено править.
Я не вижу такого решения с помощью цикла, только прописывать всё в ручную
$data = [
'allowed' => 'somevalue';
'notallowed' => 'anothervalue';
]
...
function fillFromArray(array $data)
{
$this->allowed = $data['allowed'];
}
А почему именно из массива? Вот например у меня раньше брались данные из $_POST, и я подумать не мог что они будут браться откуда-то еще. Если мы собрались расширять гибкость кода, то давайте расширять до конца. Например, мы можем получать данные и из объекта.
Кстати, меня почему-то продолжает тянуть писать переменные не в camelcase - короткие слова легко разобрать и так. Полагаю, что camelcase был придуман чтобы различать слова, но какой смысл в его использовании, если и так всё различается с первого взгляда?
>>717443
>> if (!count(array_filter($registerStudentForm->getErrors()))) {
>Это длинно и неудобно. Я вызвал функцию вадидации и теперь должен еще писать какой-то громоздкий код, чтобы узнать есть там ошибки или нет. Предлагаю сделать список ошибок объектом ErrorList такого вида:
Может лучше на каждую сущность иметь свой класс Error? Например PostErrors... И все бы они наследовались от одного класса Errors который содержит методы для работы, и еще в них можно хранить выше упомянутый массив с именами полей и их функциями валидации.
>>717443
>Также, форма могла бы предоставлять готовые методы для копирования данных в/из студента.
А почему форма, а не контроллер? Я бы сказал это его задача передавать данные из одного в другое.
>>717433
>Но если задуматься, какой в этом смысл, хранить 2 копии данных? Не проще ли внутри формы хранить сам объект студента с данными? Тогда и валидация упростится. И вообще все упростится.
Нет, ничего не упроститься, всё наслоится друг на друга и не будет понятно что на самом деле есть класс PostForm. Тоже самое я заметил когда изучал symfony forms:
Чтобы понять что делает метод add() мне пришлось открывать 12 файлов, и при этом, когда ты изучаешь какую-то функцию, ты забываешь что её вызывало >3 файлов назад. И даже после перечитывания, я всё равно застрял на этом моменте:
https://github.com/symfony/form/blob/master/FormFactory.php#L85
https://github.com/symfony/form/blob/master/Extension/Core/Type/FormType.php
https://github.com/symfony/form/blob/master/Extension/Core/Type/BaseType.php
https://github.com/symfony/form/blob/master/AbstractType.php
Где реализуется метод createBuilder()? Я вижу что он есть в FormFactory, но там обращаются к этому методу от класса FormType.
Затем я подумал что symfony form это кофеварка которая предоставляет наружу методы, а как она устроенна меня волновать не должно, и я попробовал её реализовать у себя основываясь только на примерах из документации, но у меня ничего не получилось >>720677
>>720677
Пока не знаю что и сказать, для меня это сложно пока. Могу я сначала изучить код и задавать вопросы по нему сюда, и потом вернутся к тому посту, и сделать что нужно?
>>721507
>> Откуда берется $view?
>Выше же в коде есть $form->createView():
Так я писал его, и приписывал еще $view = $form->createView(), во всех случаях я получал ошибку о неизвестной переменной\ключе\методе.
> ([,!:;]|[.?]{1,3})
здесь было наверно лучше просто написать: 1 или больше любых знаков.
А так, все верно.
>>718099
Можно применить делегирование, то есть сделать новый класс и вызвать методы старого объекта.
>>718134
Традиция
>>718318
> $result = preg_replace($key, $val, $text);
Ты на каждом щаге цикла берешь $text, применяешь у нему очередное правило и кладешь в $result. В итоге там останется только результат последней операции.
>>718401
Прежде чем верстать адаптивно, надо научиться верстать резиново. Когда блоки подстраиваются под ширину страницы и высоту содержимого.
После этого осваивай правило @media и читай про meta viewport, затем нагугли таблицу размеров экранов.
Адаптивность обычно заключается в том, что мы определяем 2 или 3 варианта дизайна в зависимости от ширины экрана (хотя адаптивность в теории может опироваться не только на ширину, а на тип устройства, плотность пикселей, и тд). Ну например, вариант дизайна для совсем узких экранов (телефон), средней ширины (планшет), большой ширины (десктоп). С помощью правил @media мы вносим изменения привязанные к ширине. ну и тегом meta говорим что наш сайт адаптивен.
Конкретные примеры гугли по словам вроде "N лучших адаптивных дизайнов"
Если у тебя с этим сложности, скорее всего у тебя пробелы в знании CSS. В Оп посте есть задачи по этой теме, в том числе в конце задача на верстку адаптивного макета.
> ([,!:;]|[.?]{1,3})
здесь было наверно лучше просто написать: 1 или больше любых знаков.
А так, все верно.
>>718099
Можно применить делегирование, то есть сделать новый класс и вызвать методы старого объекта.
>>718134
Традиция
>>718318
> $result = preg_replace($key, $val, $text);
Ты на каждом щаге цикла берешь $text, применяешь у нему очередное правило и кладешь в $result. В итоге там останется только результат последней операции.
>>718401
Прежде чем верстать адаптивно, надо научиться верстать резиново. Когда блоки подстраиваются под ширину страницы и высоту содержимого.
После этого осваивай правило @media и читай про meta viewport, затем нагугли таблицу размеров экранов.
Адаптивность обычно заключается в том, что мы определяем 2 или 3 варианта дизайна в зависимости от ширины экрана (хотя адаптивность в теории может опироваться не только на ширину, а на тип устройства, плотность пикселей, и тд). Ну например, вариант дизайна для совсем узких экранов (телефон), средней ширины (планшет), большой ширины (десктоп). С помощью правил @media мы вносим изменения привязанные к ширине. ну и тегом meta говорим что наш сайт адаптивен.
Конкретные примеры гугли по словам вроде "N лучших адаптивных дизайнов"
Если у тебя с этим сложности, скорее всего у тебя пробелы в знании CSS. В Оп посте есть задачи по этой теме, в том числе в конце задача на верстку адаптивного макета.
Задача на айфон неправильно считает сумму кредита в 1000: http://ideone.com/ZYZ5ls
> Вопрос: зачем создавать отдельные глобальные переменные $creditSum = 39999 и $payout = 5000, если вне функции нам они ни к чему?
Любые исходные данные должны быть в одном экземпляре а не скопипащены 10 раз по коду. Чтобы их было удобно менять и чтобы было понятно по имени переменной, что это за число. Иначе будет антипаттерн "магические числа".
В задаче на айпад ты нарушаешь эти принципы и копипастишь цифру 5000 несколько раз. Более того, ты прописал параметры кредита в функции и нельзя вызвать функцию с другими параметрами, например узнать что будет если платить 7000 в месяц.
Код в задаче на айпад переусложнен, его можно сделать короче. Например выражение
($debt + ($debt * $percent) + $serviceFee
повторяется аж 3 раза - в этом нет необходимости. В уроке с задачей на афйон по моему описан правильный алгоритм решения в подсказках.
Чтобы понять, почему она считает неправильно, надо поставить внутри цикла echo который будет выводить номер месяца и значения всех переменных. И глядя на них попробовать понять в какой и почему берутся неправильные знаечния. Если будет не очевидно - запость ссылку на код, я или доброаноны глянут.
>>718473
"тонкий контроллер" это значит минимум специфичного для контроллера кода и максимум универсальных функций. От того что ты вынес код в другой класс, при этом оставив его неуниверсальным твой контроллер не стал тонким, он стал размазанным по нескольким классам.
>>718566
лучше поменять алгоритм, сделать один цикл.
Посмотри пока эти, или ты их уже скачал?
http://www.mediafire.com/download/5o7mmd25bvn2o9d/архив-тредов-с-марта-2015.zip
http://www.mediafire.com/download/kgzl1f9366gc6ed/threads-archive-11..20.zip
http://www.mediafire.com/download/gza5360wdzqd743/threads-archive-pr-1..17.zip
>>718624
> $words = preg_split('/(?<=\\s)/ui', $sentence, 0, PREG_SPLIT_NO_EMPTY);//Разделяем на слова
Тут незачем сохранять рядом со словами пробелы. Значит, не надо использовать утверждения.
> $abc = preg_replace('/[.!?]/', ' ', $implode);//Убираем лишние знаки
Если ты убираешь знаки, то ты мог просто в первом preg_split не использовать утверждения, и он бы их вырезал: $sentences = preg_split('/(?<=[.!?])/u'....
Либо не используй утвреждения, либо используй но сохраняй знак в конце предложения. Думаю, первый вариант проще.
> $result = str_replace(',', '', $string);//Удаляем запятые
Это логично делать до обработки, чтобы они потом не мешались.
> $deleteSpace = preg_replace('/[\\s]{1,2}(?=\\.)/', '', $dot);//Убираем лишний пробел в конце предложения
Тут проще было сделать trim который отрезает пробелы с краев.
>>718662
>>718762
Хром их не отсылает потому что в нем нет кук. Это логично. Смотреть надо что приходит с сервера в заголовке Set-Cookie. Например там может быть указан не тот домен, путь или время жизни.
И если ты плохо знаешь протокол HTTP и как ставятся куки в этом протоколе, тебе стоит погуглить. Для начала можно почитать статью в википедии.
>>718768
> Не пиши код, полагающийся на валидность всех полей структуры.
Вот с этим у Го и проблемы. Еще из проблем: какая-то мутная схема с модулями, когда одна переменная видна во всех файлах той же папки, дурацкая схема с определением видимости через регистр букв, сильно уродует код (так как структуры пишутся то с маленькой то с большой буквы), архаичные табы, путаница с указателями, нет абстрактных типов (нельзя указать что функция возвращает слайс без указания слайс чего именно)
Нет правил для разнесения кода по файлам (вроде 1 файл = 1 класс). Из-за этого все идет вперемешку, читабельность плохая, непонятно как по имени функции или структуры понять где она определена. Реально, глупо и непродуманно.
Команда запуска тестов запускает их только в одной папке. Нет даже библиотеки для ассертов, все пиши руками.
Нет пока единого менеджера пакетов. Идея с GOPATH дурацкая изначально.
Нельзя нормально делать ограничения, private полей нет (они видны во всех файлах в папке).
Из плюсов: статическая типизация, как удобно
В общем, пока он годится для маленьких утилит и демонов - писать веб-приложение я бы не решился. И с ОРМ тут пока все плохо, и с формами, и ообще почти со всем.
Посмотри пока эти, или ты их уже скачал?
http://www.mediafire.com/download/5o7mmd25bvn2o9d/архив-тредов-с-марта-2015.zip
http://www.mediafire.com/download/kgzl1f9366gc6ed/threads-archive-11..20.zip
http://www.mediafire.com/download/gza5360wdzqd743/threads-archive-pr-1..17.zip
>>718624
> $words = preg_split('/(?<=\\s)/ui', $sentence, 0, PREG_SPLIT_NO_EMPTY);//Разделяем на слова
Тут незачем сохранять рядом со словами пробелы. Значит, не надо использовать утверждения.
> $abc = preg_replace('/[.!?]/', ' ', $implode);//Убираем лишние знаки
Если ты убираешь знаки, то ты мог просто в первом preg_split не использовать утверждения, и он бы их вырезал: $sentences = preg_split('/(?<=[.!?])/u'....
Либо не используй утвреждения, либо используй но сохраняй знак в конце предложения. Думаю, первый вариант проще.
> $result = str_replace(',', '', $string);//Удаляем запятые
Это логично делать до обработки, чтобы они потом не мешались.
> $deleteSpace = preg_replace('/[\\s]{1,2}(?=\\.)/', '', $dot);//Убираем лишний пробел в конце предложения
Тут проще было сделать trim который отрезает пробелы с краев.
>>718662
>>718762
Хром их не отсылает потому что в нем нет кук. Это логично. Смотреть надо что приходит с сервера в заголовке Set-Cookie. Например там может быть указан не тот домен, путь или время жизни.
И если ты плохо знаешь протокол HTTP и как ставятся куки в этом протоколе, тебе стоит погуглить. Для начала можно почитать статью в википедии.
>>718768
> Не пиши код, полагающийся на валидность всех полей структуры.
Вот с этим у Го и проблемы. Еще из проблем: какая-то мутная схема с модулями, когда одна переменная видна во всех файлах той же папки, дурацкая схема с определением видимости через регистр букв, сильно уродует код (так как структуры пишутся то с маленькой то с большой буквы), архаичные табы, путаница с указателями, нет абстрактных типов (нельзя указать что функция возвращает слайс без указания слайс чего именно)
Нет правил для разнесения кода по файлам (вроде 1 файл = 1 класс). Из-за этого все идет вперемешку, читабельность плохая, непонятно как по имени функции или структуры понять где она определена. Реально, глупо и непродуманно.
Команда запуска тестов запускает их только в одной папке. Нет даже библиотеки для ассертов, все пиши руками.
Нет пока единого менеджера пакетов. Идея с GOPATH дурацкая изначально.
Нельзя нормально делать ограничения, private полей нет (они видны во всех файлах в папке).
Из плюсов: статическая типизация, как удобно
В общем, пока он годится для маленьких утилит и демонов - писать веб-приложение я бы не решился. И с ОРМ тут пока все плохо, и с формами, и ообще почти со всем.
Мануал пояснит. Погугли добавив в запрос слово "php.net"
>>719423
Тебе выше советы написал, если кратко: поставь эхо и выводи на каждом шаге цикла значения всех переменных, далее смотри на этот текст и анализируй что пошло не так.
>>723229
Почитай мануал по ln, возможно там надо ссылку как-то отнситеьно целевой папки указывать, или перед созданием ссылки надо перейти в эту папку, я не помню.
Информацию о ссылках лучше просматривать в консоли через stat или readlink
> У jquery другая папка, и там куча всякого барахла.
У jquery надо ровно 2 файла: минимизированная и неминимизированная версия (для отладки на дев-сервере). Потому там и нет папки dist.
> Да и у бутстрапа мне нужно только 3 или 4 файла, зачем мне например boostrap.css.map или bootstrap-theme.css например?
Ну во-первых быстрее скорпировать все, во-втрых, а вдруг понядобятся, думаю тут проще скопировать чем отлаживать баг почему что-то не работает. Места они не много едят на диске. Ну можно конечно и не копировать, думаю это не очень принципиально тут.
Также могу посоветовать указывать в названиях папок номера версий - так будет чуть наждежнее так как код пишется и тестируется под конкретной версией библиотеки, а не под любую. И соответственно в шаблоне стоит ссылка на конкретную версию.
> Про ckeditor вообще молчу, там в комплекте идут примеры, хелп, еще куча всякого мусора.
не оптимизирована для автоматической установки наверно.
> Вот мне как-то совсем не нужно, чтобы пользователь мог перейти на моем сайте по url /ckeditor/samples/index.html и увидеть приветственную страничку об успешной установке.
Вообще, это еще не страшно. Страшно когда в библиотеке лежит php скрипт для демо загрузки файлов и он принимает любые файлы без проверок. Но да, неаккуратненько.
> Пользователь вводит в инпут (инпут тоже форме не принадлежит)
так может надо поместить его в форму вместе с кнопкой? С каких это пор мы начали делать инпуты без формы? наверно есть случаи когда это уместно но у тебя тут явно форма.
> Или там даже не кнопка, а ссылка со стилями кнопки, не помню.
Ссылка должна куда-то вести. Иначе это кнопка.
> Помоги мне лучше разобраться с доктриной.
> Я имею ввиду внутреннее устройство. Эти все identity map, репозитории, unit of work и прочие непонятные слова.
Для начала почитай краткое описание каждого паттерна. Не факт, что все поймешь, но может что-то в голове будет.
> Что происходит? Entity manager лезет в репозиторий, который лежит в свойстве таком-то.
Начнем с того, зачем нужны репозитории вообще. https://www.google.ru/search?q=паттерн+репозиторий&newwindow=1&gbv=1&sei=B4oZV42FDYLXsAHotrSYCQ (обрати внимание на рисунок у Фаулера).
Репозиторий это штука для абстрагирования поиска в хранилище данных. То есть чтобы ты мог например искать сущности не только в базе, а в монгодб, в редис, файлы или массиве в памяти прозрачно для остальной части кода. Под ним лежит персистер - класс который отвечает за сохранение и загрузку сущностей из самого хранилища.
Доктрина вообще очень сильно разбита на части. Ну например там в отдельный компонент вынесен hydrator - это штука, которая получает на вход массив строк из БД и выдает на выходе массив моделей. Чтобы понять зачем нужен тот или иной компонент, полезно еще посмотреть на его интерфейс:
https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Persistence/ObjectRepository.php
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/EntityRepository.php
https://github.com/doctrine/collections/blob/master/lib/Doctrine/Common/Collections/Selectable.php
Из интерфейса мы как раз видим что задача репозитория - искать сущности по разным критериям. В реализации мы видим что вызовы вроде find перенаправляются в EntityManager и далее в EntityPersister:
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/Persisters/Entity/EntityPersister.php
тут видно из интерфейса что это очень sql-специфичная штука.
Доктрина позволяет исплоьзовать для каждого типа сущности свой репозиторий. Как и другие компоненты, которые могут быть в нескольких экземплярах. Помни об этом, изучая код.
> У репозитория в свойстве identity_map (?) хранятся сущности, извлеченные из базы, или вновь созданные.
Это паттерн identityMap (погугли). Мы обязаны возвращать старую сущность при повтороном запросе, потому должны держать массив всех сущностей под управлением ORM (полученных из БД либо добавленных через persist). Также, мы должны уметь искать изменения в них, потому отдельно мы храним копию всех полей в сущности.
Логика такая при поиске по id:
- если сущность есть в identity map, возвращаем ее (этот метод не привязан к конкретному способу хранения данных)
- если нет, достаем из хранилища (этот метод сильно зависит от используемого способа хранения)
Вот проследи путь выполнения функции find:
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/EntityManager.php#L378
тут мы видим поиск в identity map (которая хранится внутри UnitOfWork):
> if (($entity = $unitOfWork->tryGetById($sortedId, $class->rootEntityName)) !== false) {
затем, если сущности там нет, то мы берем persister (низкоуровневый компонент работающий с конкретным хранилищем) и вытаскиваем сущность из хранилища:
> $entity = $persister->load($sortedId);
Что такое $persister? давай смотреть комментарии к методу getEntityPersister (я не очень понимаю, почему он в UnitOfWork, мне кажется это просто исторически так сложилось, логики тут нет конечно):
> @return \Doctrine\ORM\Persisters\Entity\EntityPersister
> public function getEntityPersister($entityName)
Видно что это экземпляр реализ. интерйфейс EntityPersister, пойдем посмотрим кто реализует этот интефрйес и что у него в методе load.
Так же мы видим что для каждого типа сущности может быть свой персистер (логично, раз репозитории могут быть разные то и персистеры должны быть разные):
> $persister = new BasicEntityPersister($this->em, $class);
> $persister = new SingleTablePersister($this->em, $class);
> $persister = new JoinedSubclassPersister($this->em, $class);
Смотрим BasicEntityPersister#load:
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php#L708
> $sql = $this->getSelectSQL($criteria, $assoc, $lockMode, $limit, null, $orderBy);
> $stmt = $this->conn->executeQuery($sql, $params, $types);
Это пояснений не требует?
> $hydrator = $this->em->newHydrator($this->currentPersisterContext->selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
> $entities = $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, $hints);
Вот, здесь на сцену выходит новый объект-гидратор. Посомтрим его базовый класс:
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php
Гидратор - это объект, который получает на вход сырой плоский массив полей из БД, а на выходе дает данные в требуемом полльзователем формате: сущности, вложенные массивы и тд. Гидратор делает это используя еще один класс - ResultSetMapping. Он описывает соовтетствие полей результатта запроса и полей объектов. Помни что один запрос может содержать данные для нескольких сджойненных сущностей, могут потребоваться преобразования итд.
Посмотрим код:
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php#L305
Функия сложное, но само создание сущности делается не тут а в UnitOfWork (ой, как много умеет этот класс):
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/UnitOfWork.php#L2491
И уже тут мы видим место где проверяютяс связи сущности и в них записывается прокси, в который передается внешний ключ:
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/UnitOfWork.php#L2706
> $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
Вот метод getProxy:
https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php#L104
Смотри дальше что происходит в коде. Учти что прокси-класс генерируется на основе оригинального класса модели и сохраняется куда-то на диск.
Можешь попробовать нарисовать схему кто кого создает, кто где хранится и кто кого вызывает. Также, обращай внимание на интерфейсы, тайп-хинты и типы возвращаемых значений - они могут многое сказать.
> У jquery другая папка, и там куча всякого барахла.
У jquery надо ровно 2 файла: минимизированная и неминимизированная версия (для отладки на дев-сервере). Потому там и нет папки dist.
> Да и у бутстрапа мне нужно только 3 или 4 файла, зачем мне например boostrap.css.map или bootstrap-theme.css например?
Ну во-первых быстрее скорпировать все, во-втрых, а вдруг понядобятся, думаю тут проще скопировать чем отлаживать баг почему что-то не работает. Места они не много едят на диске. Ну можно конечно и не копировать, думаю это не очень принципиально тут.
Также могу посоветовать указывать в названиях папок номера версий - так будет чуть наждежнее так как код пишется и тестируется под конкретной версией библиотеки, а не под любую. И соответственно в шаблоне стоит ссылка на конкретную версию.
> Про ckeditor вообще молчу, там в комплекте идут примеры, хелп, еще куча всякого мусора.
не оптимизирована для автоматической установки наверно.
> Вот мне как-то совсем не нужно, чтобы пользователь мог перейти на моем сайте по url /ckeditor/samples/index.html и увидеть приветственную страничку об успешной установке.
Вообще, это еще не страшно. Страшно когда в библиотеке лежит php скрипт для демо загрузки файлов и он принимает любые файлы без проверок. Но да, неаккуратненько.
> Пользователь вводит в инпут (инпут тоже форме не принадлежит)
так может надо поместить его в форму вместе с кнопкой? С каких это пор мы начали делать инпуты без формы? наверно есть случаи когда это уместно но у тебя тут явно форма.
> Или там даже не кнопка, а ссылка со стилями кнопки, не помню.
Ссылка должна куда-то вести. Иначе это кнопка.
> Помоги мне лучше разобраться с доктриной.
> Я имею ввиду внутреннее устройство. Эти все identity map, репозитории, unit of work и прочие непонятные слова.
Для начала почитай краткое описание каждого паттерна. Не факт, что все поймешь, но может что-то в голове будет.
> Что происходит? Entity manager лезет в репозиторий, который лежит в свойстве таком-то.
Начнем с того, зачем нужны репозитории вообще. https://www.google.ru/search?q=паттерн+репозиторий&newwindow=1&gbv=1&sei=B4oZV42FDYLXsAHotrSYCQ (обрати внимание на рисунок у Фаулера).
Репозиторий это штука для абстрагирования поиска в хранилище данных. То есть чтобы ты мог например искать сущности не только в базе, а в монгодб, в редис, файлы или массиве в памяти прозрачно для остальной части кода. Под ним лежит персистер - класс который отвечает за сохранение и загрузку сущностей из самого хранилища.
Доктрина вообще очень сильно разбита на части. Ну например там в отдельный компонент вынесен hydrator - это штука, которая получает на вход массив строк из БД и выдает на выходе массив моделей. Чтобы понять зачем нужен тот или иной компонент, полезно еще посмотреть на его интерфейс:
https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Persistence/ObjectRepository.php
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/EntityRepository.php
https://github.com/doctrine/collections/blob/master/lib/Doctrine/Common/Collections/Selectable.php
Из интерфейса мы как раз видим что задача репозитория - искать сущности по разным критериям. В реализации мы видим что вызовы вроде find перенаправляются в EntityManager и далее в EntityPersister:
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/Persisters/Entity/EntityPersister.php
тут видно из интерфейса что это очень sql-специфичная штука.
Доктрина позволяет исплоьзовать для каждого типа сущности свой репозиторий. Как и другие компоненты, которые могут быть в нескольких экземплярах. Помни об этом, изучая код.
> У репозитория в свойстве identity_map (?) хранятся сущности, извлеченные из базы, или вновь созданные.
Это паттерн identityMap (погугли). Мы обязаны возвращать старую сущность при повтороном запросе, потому должны держать массив всех сущностей под управлением ORM (полученных из БД либо добавленных через persist). Также, мы должны уметь искать изменения в них, потому отдельно мы храним копию всех полей в сущности.
Логика такая при поиске по id:
- если сущность есть в identity map, возвращаем ее (этот метод не привязан к конкретному способу хранения данных)
- если нет, достаем из хранилища (этот метод сильно зависит от используемого способа хранения)
Вот проследи путь выполнения функции find:
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/EntityManager.php#L378
тут мы видим поиск в identity map (которая хранится внутри UnitOfWork):
> if (($entity = $unitOfWork->tryGetById($sortedId, $class->rootEntityName)) !== false) {
затем, если сущности там нет, то мы берем persister (низкоуровневый компонент работающий с конкретным хранилищем) и вытаскиваем сущность из хранилища:
> $entity = $persister->load($sortedId);
Что такое $persister? давай смотреть комментарии к методу getEntityPersister (я не очень понимаю, почему он в UnitOfWork, мне кажется это просто исторически так сложилось, логики тут нет конечно):
> @return \Doctrine\ORM\Persisters\Entity\EntityPersister
> public function getEntityPersister($entityName)
Видно что это экземпляр реализ. интерйфейс EntityPersister, пойдем посмотрим кто реализует этот интефрйес и что у него в методе load.
Так же мы видим что для каждого типа сущности может быть свой персистер (логично, раз репозитории могут быть разные то и персистеры должны быть разные):
> $persister = new BasicEntityPersister($this->em, $class);
> $persister = new SingleTablePersister($this->em, $class);
> $persister = new JoinedSubclassPersister($this->em, $class);
Смотрим BasicEntityPersister#load:
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php#L708
> $sql = $this->getSelectSQL($criteria, $assoc, $lockMode, $limit, null, $orderBy);
> $stmt = $this->conn->executeQuery($sql, $params, $types);
Это пояснений не требует?
> $hydrator = $this->em->newHydrator($this->currentPersisterContext->selectJoinSql ? Query::HYDRATE_OBJECT : Query::HYDRATE_SIMPLEOBJECT);
> $entities = $hydrator->hydrateAll($stmt, $this->currentPersisterContext->rsm, $hints);
Вот, здесь на сцену выходит новый объект-гидратор. Посомтрим его базовый класс:
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php
Гидратор - это объект, который получает на вход сырой плоский массив полей из БД, а на выходе дает данные в требуемом полльзователем формате: сущности, вложенные массивы и тд. Гидратор делает это используя еще один класс - ResultSetMapping. Он описывает соовтетствие полей результатта запроса и полей объектов. Помни что один запрос может содержать данные для нескольких сджойненных сущностей, могут потребоваться преобразования итд.
Посмотрим код:
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php#L305
Функия сложное, но само создание сущности делается не тут а в UnitOfWork (ой, как много умеет этот класс):
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/UnitOfWork.php#L2491
И уже тут мы видим место где проверяютяс связи сущности и в них записывается прокси, в который передается внешний ключ:
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/UnitOfWork.php#L2706
> $newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
Вот метод getProxy:
https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php#L104
Смотри дальше что происходит в коде. Учти что прокси-класс генерируется на основе оригинального класса модели и сохраняется куда-то на диск.
Можешь попробовать нарисовать схему кто кого создает, кто где хранится и кто кого вызывает. Также, обращай внимание на интерфейсы, тайп-хинты и типы возвращаемых значений - они могут многое сказать.
> Например, сейчас нужно сделать для валидации массив которые хранит название полей и функции валидации к ним, но откуда я должен получить этот массив?
Тут 2 варианта:
- если твой класс - универсальный валидатор для любой формы, очевидно настройки он получает снаружи
- если твой класс валидирует только одну сущность или форму, настройки можно заложить прямо в него, например в отдельный метод. А можно и в сущность. Но наверно тут лучше их в валидатор заложить для простоты.
> Я не могу этому просто догадаться, это совсем не очевидно.
Вот задавай конкретные вопросы как выше, я постараюсь ответить.
> Я не вижу такого решения с помощью цикла, только прописывать всё в ручную
Почему цикл не годится? ты можешь обраьиться к полю по имени в переменной, такой конструкцией:
$fieldName = 'year';
$this->{$fieldName} = 1990;
И соответственно можно использовать цикл по массиву разрешенных имен полей:
$fields = ['name' ,surname'];
foreach ($fields as $field) {
....
$this->{$field} = ....;
}
> Может лучше на каждую сущность иметь свой класс Error?
Теоретически можно так как разные сущности имеют разный набор ошибок. Но мне кажется это будет ненужным усложнением, городить наследование, и лучше сделать просто класс который является чем-то вроде улучшенного массива с ошибками. То есть в него можно добавить ошибку, получить ощибку и проверить есть ли они там вообще.
> класса Errors который содержит методы для работы,
Нельзя. Класс Errors описывает список ошибок. С какой стати в списке найденных ошибок могут быть правила валидации? Они тут по моему не к селу ник городу. Смотри какая схема у нас:
Сущность: хранит информацию например о студенте
Форма: хранит введенные данные в форму
ВалидаторСщности: берет сущность, возвращает СписокОшибок
ВалидаторФорм: берет Форму, проверяет, возвращет СписокОшибок
(можно также передавать в валидатор уже готовый Список, чтобы он его дополнил)
Ну как тут правила могут быть в списке ошибок, если он не существует до валидации?
>>Также, форма могла бы предоставлять готовые методы для копирования данных в/из студента.
> А почему форма, а не контроллер? Я бы сказал это его задача передавать данные из одного в другое.
А за что у тебя отвечает класс Формы? За прием, хранение данных пришедших из формы. но можно в принципе и в контроллере брать данные из ПОСТ и забивать в форму. Но тогда просто польза от Формы становится чуть меньше.
> Нет, ничего не упроститься, всё наслоится друг на друга и не будет понятно что на самом деле есть класс PostForm. Тоже самое я заметил когда изучал symfony forms:
Я не думаю. Вот смотри, у тебя есть валидация формы, а есть вадидаци модели. У них ведь очень много одинакового кода будет. ты читал в моем учебнике в главе ООП про аггрегацию и композицию, пример со спортивными командами? Почитай.
> мне пришлось открывать 12 файлов, и при этом, когда ты изучаешь какую-то функцию, ты забываешь что её вызывало >3 файлов назад. И даже после перечитывания, я всё равно застрял на этом моменте:
Сифмони формс очень универсальный и потому сложный компонент. От тебя не требуется такой же универсальный написать, ты можешь сделать неуниверсаьный который работает только с одной формой в одном приложении. Потому у тебя не будет12 файлов.
ну и чтобы не путаться надо рисовать схемы, кто кого вызвает, какой объект что содержит. Держать в голове это трудно. Попробуй порисовать схемы связей между объектами.
> Где реализуется метод createBuilder()? Я вижу что он есть в FormFactory, но там обращаются к этому методу от класса FormType.
Сделай поиск по коду по этому слову и проанализируй результаты. Обращай внимание на тайп-хинты и типы возвращаемых функцией значений, типы полей, интерфейсы которые реализуют классы. Там немного запутано, да, но если рисовать схемы то может будет яснее.
> Затем я подумал что symfony form это кофеварка которая предоставляет наружу методы, а как она устроенна меня волновать не должно, и я попробовал её реализовать у себя основываясь только на примерах из документации, но у меня ничего не получилось
Приведи пример кода, который не рабоатет, желательно минималистичный, без кучи контроллеров, моделей и прочего.
> Пока не знаю что и сказать, для меня это сложно пока. Могу я сначала изучить код и задавать вопросы по нему сюда, и потом вернутся к тому посту, и сделать что нужно?
Ты можешь изучать как тебе удобно. Можно позже. Вопрос мне задать можно всегда.
>>> Откуда берется $view?
>>Выше же в коде есть $form->createView():
> Так я писал его, и приписывал еще $view = $form->createView(), во всех случаях я получал ошибку о неизвестной переменной\ключе\методе.
Приведи небольшой пример кода дающий ошибку.
> Например, сейчас нужно сделать для валидации массив которые хранит название полей и функции валидации к ним, но откуда я должен получить этот массив?
Тут 2 варианта:
- если твой класс - универсальный валидатор для любой формы, очевидно настройки он получает снаружи
- если твой класс валидирует только одну сущность или форму, настройки можно заложить прямо в него, например в отдельный метод. А можно и в сущность. Но наверно тут лучше их в валидатор заложить для простоты.
> Я не могу этому просто догадаться, это совсем не очевидно.
Вот задавай конкретные вопросы как выше, я постараюсь ответить.
> Я не вижу такого решения с помощью цикла, только прописывать всё в ручную
Почему цикл не годится? ты можешь обраьиться к полю по имени в переменной, такой конструкцией:
$fieldName = 'year';
$this->{$fieldName} = 1990;
И соответственно можно использовать цикл по массиву разрешенных имен полей:
$fields = ['name' ,surname'];
foreach ($fields as $field) {
....
$this->{$field} = ....;
}
> Может лучше на каждую сущность иметь свой класс Error?
Теоретически можно так как разные сущности имеют разный набор ошибок. Но мне кажется это будет ненужным усложнением, городить наследование, и лучше сделать просто класс который является чем-то вроде улучшенного массива с ошибками. То есть в него можно добавить ошибку, получить ощибку и проверить есть ли они там вообще.
> класса Errors который содержит методы для работы,
Нельзя. Класс Errors описывает список ошибок. С какой стати в списке найденных ошибок могут быть правила валидации? Они тут по моему не к селу ник городу. Смотри какая схема у нас:
Сущность: хранит информацию например о студенте
Форма: хранит введенные данные в форму
ВалидаторСщности: берет сущность, возвращает СписокОшибок
ВалидаторФорм: берет Форму, проверяет, возвращет СписокОшибок
(можно также передавать в валидатор уже готовый Список, чтобы он его дополнил)
Ну как тут правила могут быть в списке ошибок, если он не существует до валидации?
>>Также, форма могла бы предоставлять готовые методы для копирования данных в/из студента.
> А почему форма, а не контроллер? Я бы сказал это его задача передавать данные из одного в другое.
А за что у тебя отвечает класс Формы? За прием, хранение данных пришедших из формы. но можно в принципе и в контроллере брать данные из ПОСТ и забивать в форму. Но тогда просто польза от Формы становится чуть меньше.
> Нет, ничего не упроститься, всё наслоится друг на друга и не будет понятно что на самом деле есть класс PostForm. Тоже самое я заметил когда изучал symfony forms:
Я не думаю. Вот смотри, у тебя есть валидация формы, а есть вадидаци модели. У них ведь очень много одинакового кода будет. ты читал в моем учебнике в главе ООП про аггрегацию и композицию, пример со спортивными командами? Почитай.
> мне пришлось открывать 12 файлов, и при этом, когда ты изучаешь какую-то функцию, ты забываешь что её вызывало >3 файлов назад. И даже после перечитывания, я всё равно застрял на этом моменте:
Сифмони формс очень универсальный и потому сложный компонент. От тебя не требуется такой же универсальный написать, ты можешь сделать неуниверсаьный который работает только с одной формой в одном приложении. Потому у тебя не будет12 файлов.
ну и чтобы не путаться надо рисовать схемы, кто кого вызвает, какой объект что содержит. Держать в голове это трудно. Попробуй порисовать схемы связей между объектами.
> Где реализуется метод createBuilder()? Я вижу что он есть в FormFactory, но там обращаются к этому методу от класса FormType.
Сделай поиск по коду по этому слову и проанализируй результаты. Обращай внимание на тайп-хинты и типы возвращаемых функцией значений, типы полей, интерфейсы которые реализуют классы. Там немного запутано, да, но если рисовать схемы то может будет яснее.
> Затем я подумал что symfony form это кофеварка которая предоставляет наружу методы, а как она устроенна меня волновать не должно, и я попробовал её реализовать у себя основываясь только на примерах из документации, но у меня ничего не получилось
Приведи пример кода, который не рабоатет, желательно минималистичный, без кучи контроллеров, моделей и прочего.
> Пока не знаю что и сказать, для меня это сложно пока. Могу я сначала изучить код и задавать вопросы по нему сюда, и потом вернутся к тому посту, и сделать что нужно?
Ты можешь изучать как тебе удобно. Можно позже. Вопрос мне задать можно всегда.
>>> Откуда берется $view?
>>Выше же в коде есть $form->createView():
> Так я писал его, и приписывал еще $view = $form->createView(), во всех случаях я получал ошибку о неизвестной переменной\ключе\методе.
Приведи небольшой пример кода дающий ошибку.
Вот насчет "не могу понять кто за что отвечает" - старайся писать или рисовать такие схемы как я привел выше:
> ВалидаторСщности: берет сущность, возвращает СписокОшибок
> ВалидаторФорм: берет Форму, проверяет, возвращет СписокОшибок
Согласись, тут сразу видно, кто за что отвечает и выглядит оно довольно просто (а хороший код как правило простой).
По переводу прокомментриую чуть чуть:
> Чтобы инкапсулировать "проецирование данных"
проецирование - по моему не очень понятный перевод. Лучше в таких случаях дописывать оригинальное слово в скобках:
Чтобы инкапсулировать "проецирование данных" (data mapping)
Я еще видел перевод "отображение" (потому я и не люблю русские переводы некоторых терминов что ничего не понять). Ну и есть слово "соответствие".
> // мои замечания: mapper тоже нужно было передать через DI
может это намеренно? Может это композиция ( https://ru.wikipedia.org/wiki/Агрегирование_(программирование) ) объектов и маппер не имеет смысла вне репозитория, не имеет смысл менять его настройки итд? хотя не знаю.
echo 'ho ';
sleep(1);
$foo = Input::get('state');
if (!$foo) break;
}
Как извне остановить выполнение скрипта, поменяв значение переменной state?
>возможно там надо ссылку как-то относительно целевой папки указывать
Ага, вот так получилось:
>ln -sf ../../../vendor/twbs/bootstrap/dist/ www/media/vendor/bootstrap
Тебе зачем такое вообще? Это уже работа с потоками и маркерами отмены, если по-хорошему.
Любитель дотнета, не надо тут писать ерунду. В PHP нет CancellationTokens и с такими комментариями тебе лучше пройти в тред своего языка.
А как тогда? Мне для очереди очень нужна остановка. Управление очередью идет с морды. Сейчас у меня аякс вызывает итерацию очереди по событию аякскомплит. Это же не путь джедая. лучше бы чтобы сервер сам крутил скрипт без помощи фронтэнда. Как вариант хочу аяксом менять содержимое жейсон файла и проверять его содержимое при каждой итерации цикла. Но опять же это тоже не путь джедая. Есть еще сокеты, но я там еще не разбирался.
Вот этого анона двачую. Постоянно стопорюсь на новых для меня штуках, ебусь весь день в гуглах и потом с этой кашей в голове не могу ничего написать. Подозреваю что я не с того начал или что-то упустил. Может надо было с какой книжки по патернам задачку с студентами делать, ахуеть окончательно и выпилиться с профессии пока не поздно.
Немного проясняется, хотя все равно муторно.
Репозиторий это класс, у которого есть ссылка на em.
Через em осуществляет crud сущностей. Непонятно, почему его репозиторием называют.
Я думал, что именно в него, в объекты этого класса, куда-то в свойства и сохраняются
подгруженные ранее сущности.
Оказывается нет, хранилищем (репозиторием) является не класс репозиторий, а
identity map, который хранится в unit of work.
Identity map это оказывается даже не класс, а массив. Хранится в свойстве класса
Unit of work, зона ответственности которого вообще пока не понятна. Судя по всему
туда сваливается все подряд, что не знали куда положить.
Что касается Persister, то это судя по всему то что мне привычнее называть Data Mapper,
то есть класс который занимается запросами к базе данных.
Коллекция перзистеров хранится в свойстве объекта Unit of work
Persister еще пропускает полученные из бд данные через Гидратор (что за дикое имя?),
который выполняет функцию форматирования данных. Допустим когда пользователь хочет
получить массив, или объект.
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/UnitOfWork.php#L2491
Кажется, кто-то говорил, что если в методе больше 10 строк, то это говнокод и его
нужно разбивать на отдельные методы?
Думаю с меня почти хватит. Давай еще глянем методы getReference и getPartialReference
и успокоимся на этом.
Исходный вопрос, напомню: >>718887
>>719477
Что такое прокси более менее уяснил. Как они делаются не совсем, но думаю пока это не
так принципиально важно.
Разберемся в отличиях между em->getReference и em->getPartialReference
Первый судя по апи дает либо ссылку на существующую сущность (о господи), либо создает
прокси.
Второй (ниже, #401-419) прокси не создает, он создает именно экземпляр сущности,
заполняя из свойств только id.
Встает вопрос: так что выгоднее использовать с точки зрения производительности?
Прокси или partial objects?
Вот мне к owning side однонаправленной (unidirectional) связи многие ко многим нужно
приаттачить допустим 1000 связанных сущностей.
Например у поста тысяча комментариев. У меня есть массив с номерами (id) этих комментариев.
Делать ли мне getPartialReference или getReference по этим номерам?
>>723672
Терпение и труд. Думаешь оп всю эту ахинею наизусть знает?
Когда в треде появляется вопрос, он идет гуглит оф.доки или stackoverflow, потом пытается своими
пересказать и объяснить. Другое дело что благодаря опыту знает в каком направлении копать и
это занимает в 20 раз меньше времени чем у меня например.
Немного проясняется, хотя все равно муторно.
Репозиторий это класс, у которого есть ссылка на em.
Через em осуществляет crud сущностей. Непонятно, почему его репозиторием называют.
Я думал, что именно в него, в объекты этого класса, куда-то в свойства и сохраняются
подгруженные ранее сущности.
Оказывается нет, хранилищем (репозиторием) является не класс репозиторий, а
identity map, который хранится в unit of work.
Identity map это оказывается даже не класс, а массив. Хранится в свойстве класса
Unit of work, зона ответственности которого вообще пока не понятна. Судя по всему
туда сваливается все подряд, что не знали куда положить.
Что касается Persister, то это судя по всему то что мне привычнее называть Data Mapper,
то есть класс который занимается запросами к базе данных.
Коллекция перзистеров хранится в свойстве объекта Unit of work
Persister еще пропускает полученные из бд данные через Гидратор (что за дикое имя?),
который выполняет функцию форматирования данных. Допустим когда пользователь хочет
получить массив, или объект.
https://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/UnitOfWork.php#L2491
Кажется, кто-то говорил, что если в методе больше 10 строк, то это говнокод и его
нужно разбивать на отдельные методы?
Думаю с меня почти хватит. Давай еще глянем методы getReference и getPartialReference
и успокоимся на этом.
Исходный вопрос, напомню: >>718887
>>719477
Что такое прокси более менее уяснил. Как они делаются не совсем, но думаю пока это не
так принципиально важно.
Разберемся в отличиях между em->getReference и em->getPartialReference
Первый судя по апи дает либо ссылку на существующую сущность (о господи), либо создает
прокси.
Второй (ниже, #401-419) прокси не создает, он создает именно экземпляр сущности,
заполняя из свойств только id.
Встает вопрос: так что выгоднее использовать с точки зрения производительности?
Прокси или partial objects?
Вот мне к owning side однонаправленной (unidirectional) связи многие ко многим нужно
приаттачить допустим 1000 связанных сущностей.
Например у поста тысяча комментариев. У меня есть массив с номерами (id) этих комментариев.
Делать ли мне getPartialReference или getReference по этим номерам?
>>723672
Терпение и труд. Думаешь оп всю эту ахинею наизусть знает?
Когда в треде появляется вопрос, он идет гуглит оф.доки или stackoverflow, потом пытается своими
пересказать и объяснить. Другое дело что благодаря опыту знает в каком направлении копать и
это занимает в 20 раз меньше времени чем у меня например.
> Оказывается нет, хранилищем (репозиторием) является не класс репозиторий, а
identity map, который хранится в unit of work.
Нет. Я же тебе советовал начинать с описания паттернов, почитай про айдентити мап: http://design-pattern.ru/patterns/identity-map.html
> Обеспечивает однократную загрузку объекта, сохраняя данные об объекте в карте соответсвия. При обращении к объектам, ищет их в карте соответсвия.
Главная цель айдентити мап - обеспечить отсутствие дубликатов в памяти. Она ничего не загружает. просто перед тем как обращаться к реальному хранилищу (вроде БД) делается поиск в айдентити мап. Айдентити мап не привязана к конкретному типу хранилища, она универсальна.
За загрузку из БД (и запись в нее, генерацию SQL запросов) отвечает персистер, и они могут быть разные для разных классов сущностей. Можно часть сущностей хранить в SQL а часть в монго например (хотя не знаю, будут ли связи работать корректно)
Про репозиторий, перечитай теорию ( http://design-pattern.ru/patterns/repository.html ) и посмотри интерфейс. Репозиторий это что-то вроде фасада для поиска объектов по разным критериям.
Там в доктрине это конечно все немного запутано, и вызовы идут между разными объектами, ну видимо пока ни у кого не нашлось время распутать.
> Unit of work, зона ответственности которого вообще пока не понятна.
Ну вот. Опять же начал бы с теории: http://design-pattern.ru/patterns/unit-of-work.html
> Можно записывать в БД каждое изменение объекта, но это приведёт к большому количеству мелких запросов к БД, что закончится замедлением работы приложения. Более того, это требует держать открытую транзакцию всё время работы приложения, что непрактично, если приложение обрабатывает несколько запросов одновременно. Ситуация ещё хуже, если необходимо следить за чтением из и БД, чтобы избежать неконсистентного чтения.
Юнит оф ворк нужен для накопления изменений, чтобы потом сбросить эти изменения в хранилище по команде flush(). Посмотри на диаграмму класса, посмотри на названия публичных методов в классе.
> Кажется, кто-то говорил, что если в методе больше 10 строк, то это говнокод и его
> нужно разбивать на отдельные методы?
Ну попробуй разбей
Насчет partialreference: это очень опасная штука. Он выглядит как полноценный объект но при этом поля пустые. partial объекты вообще не должны использоваться. Потому что у тебя в приложении будут "полноценные" и "неполноценные" объекты, неразличимые друг от друга и очень легко использовать "неполноценный" объект думая что он нормальный. Я удивлен, что ты такое вообще всерьез рассматриваешь.
Либо используй прокси, либо делай обновление через SQL в обход доктрины. Я же писал большой пост где-то выше.
> так что выгоднее использовать с точки зрения производительности?
SQL запросы
>>723672
Спрашивал бы конкретные вопросы. Оп всегда охотно отвечает если это не вопрос из серии "погугли за меня" или "реши за меня".
> Оказывается нет, хранилищем (репозиторием) является не класс репозиторий, а
identity map, который хранится в unit of work.
Нет. Я же тебе советовал начинать с описания паттернов, почитай про айдентити мап: http://design-pattern.ru/patterns/identity-map.html
> Обеспечивает однократную загрузку объекта, сохраняя данные об объекте в карте соответсвия. При обращении к объектам, ищет их в карте соответсвия.
Главная цель айдентити мап - обеспечить отсутствие дубликатов в памяти. Она ничего не загружает. просто перед тем как обращаться к реальному хранилищу (вроде БД) делается поиск в айдентити мап. Айдентити мап не привязана к конкретному типу хранилища, она универсальна.
За загрузку из БД (и запись в нее, генерацию SQL запросов) отвечает персистер, и они могут быть разные для разных классов сущностей. Можно часть сущностей хранить в SQL а часть в монго например (хотя не знаю, будут ли связи работать корректно)
Про репозиторий, перечитай теорию ( http://design-pattern.ru/patterns/repository.html ) и посмотри интерфейс. Репозиторий это что-то вроде фасада для поиска объектов по разным критериям.
Там в доктрине это конечно все немного запутано, и вызовы идут между разными объектами, ну видимо пока ни у кого не нашлось время распутать.
> Unit of work, зона ответственности которого вообще пока не понятна.
Ну вот. Опять же начал бы с теории: http://design-pattern.ru/patterns/unit-of-work.html
> Можно записывать в БД каждое изменение объекта, но это приведёт к большому количеству мелких запросов к БД, что закончится замедлением работы приложения. Более того, это требует держать открытую транзакцию всё время работы приложения, что непрактично, если приложение обрабатывает несколько запросов одновременно. Ситуация ещё хуже, если необходимо следить за чтением из и БД, чтобы избежать неконсистентного чтения.
Юнит оф ворк нужен для накопления изменений, чтобы потом сбросить эти изменения в хранилище по команде flush(). Посмотри на диаграмму класса, посмотри на названия публичных методов в классе.
> Кажется, кто-то говорил, что если в методе больше 10 строк, то это говнокод и его
> нужно разбивать на отдельные методы?
Ну попробуй разбей
Насчет partialreference: это очень опасная штука. Он выглядит как полноценный объект но при этом поля пустые. partial объекты вообще не должны использоваться. Потому что у тебя в приложении будут "полноценные" и "неполноценные" объекты, неразличимые друг от друга и очень легко использовать "неполноценный" объект думая что он нормальный. Я удивлен, что ты такое вообще всерьез рассматриваешь.
Либо используй прокси, либо делай обновление через SQL в обход доктрины. Я же писал большой пост где-то выше.
> так что выгоднее использовать с точки зрения производительности?
SQL запросы
>>723672
Спрашивал бы конкретные вопросы. Оп всегда охотно отвечает если это не вопрос из серии "погугли за меня" или "реши за меня".
Уже не один раз было, когда проверяю код с интернета и на PHP87+Apache2.4+MySQL5.7 не работает, а на говноденвере прекрасно работает.
Схуяли ошибки то?
>Оказывается нет, хранилищем (репозиторием) является не класс репозиторий, а identity map
>Нет. Я же тебе советовал начинать с описания паттернов
Ты не понял, я говорю с точки зрения нормального человека, а не программиста. Слышу слово "хранилище" -> представляю себе структуру данных в которой что-то хранится.
Хранится ли что-то в классе Repository? Нет. Хранятся сущности в identity map.
Да, поэтому такая путаница.
И хватит тыкать ссылками на теорию. Если бы там было что-то вразумительное, я бы не срал в этом треде вопросами.
У тебя есть дети? Вот ребенок тебя спросит: "папа (или мама, я же не знаю кто ты там), а что такое лошадь?". Ты ему такой хрясь ссылку на википедию
>Ло́шади (лат. Equus) — единственный современный род семейства лошадиных (Equidae) отряда непарнокопытных.
>Самок лошадей называют кобылами, самцов — жеребцами[1]. Конями называют либо жеребцов, либо кастрированных жеребцов — меринов
Ребенок такой: "непарнока... кастрирора... позвольте вам заметить, маман, что я в легком недоумении от такой манеры преподавания".
>Я удивлен, что ты такое вообще всерьез рассматриваешь.
А почему я не должен был такое всерьез рассматривать?
Что у меня есть из исходных данных? Есть статейка в документации, где говорится: кароч partial objects это такие objects, whose state is not fully initialized (что? о чем мне это говорит? да ни о чем не говорит) and that is disconnected from the rest of its data. (ну дисконнектед так дисконнектед, проблема то в чем?)
>Use of partial objects is tricky. Fields that are not retrieved from the database will not be updated by the UnitOfWork even if they get changed in your objects.
А мне и не нужно, чтобы эти поля заполнять или обновлять через UnitOfWork. Мне блин ссылку нужно сделать, чтобы в таблице связей появились записи. После этого мой код прекращает работу.
>In short, partial objects are problematic because they are usually objects with broken invariants.
Да пусть хоть 50 раз повторят, что они problematic и broken. Они меня повторами хотят убедить, или что? Покажите случай, когда partial objects могут что-то сломать, я себе не могу это представить, может ты основываясь на опыте и более высоком icq можешь смоделировать в головном мозгу такой случай, я не могу пока не увижу своими глазами.
>As such, code that uses these partial objects tends to be very fragile
Пруфы.
>code needs to “know” which fields or methods can be safely accessed or add checks around every field access or method invocation.
Мне не нужно оттуда ничего получать и не нужно ничего устанавливать. Мне нужна просто ссылка.
Если бы была такая возможность, я бы использовал прямо массив.
$post->setComments([123, 124, 125, 126]);
$em->persist($post);
$em->flush();
// дальше идет рендер страницы и все, никаких доступов к свойствам, что якобы небезопасно
>You usually simply assume the state you need in the method is available, after all you properly constructed this object before you pushed it into the database, right? These blind assumptions can quickly lead to null reference errors when working with such partial objects.
Это не смог перевести. Но наверное опять вангуют скрытую угрозу без пруфов естественно.
Короче я не пойму в чем проблема, пока не увижу код, в котором происходит не то что должно по вине partial objects.
Ну ладно, я наверное надоел со всем этим. Пока сливаюсь. Потом может другую тему помусолим. У меня тут например назревает акробатика со сфинксом. Потом отпишусь в новом выпуске мурзилки. В новом треде то есть.
>Оказывается нет, хранилищем (репозиторием) является не класс репозиторий, а identity map
>Нет. Я же тебе советовал начинать с описания паттернов
Ты не понял, я говорю с точки зрения нормального человека, а не программиста. Слышу слово "хранилище" -> представляю себе структуру данных в которой что-то хранится.
Хранится ли что-то в классе Repository? Нет. Хранятся сущности в identity map.
Да, поэтому такая путаница.
И хватит тыкать ссылками на теорию. Если бы там было что-то вразумительное, я бы не срал в этом треде вопросами.
У тебя есть дети? Вот ребенок тебя спросит: "папа (или мама, я же не знаю кто ты там), а что такое лошадь?". Ты ему такой хрясь ссылку на википедию
>Ло́шади (лат. Equus) — единственный современный род семейства лошадиных (Equidae) отряда непарнокопытных.
>Самок лошадей называют кобылами, самцов — жеребцами[1]. Конями называют либо жеребцов, либо кастрированных жеребцов — меринов
Ребенок такой: "непарнока... кастрирора... позвольте вам заметить, маман, что я в легком недоумении от такой манеры преподавания".
>Я удивлен, что ты такое вообще всерьез рассматриваешь.
А почему я не должен был такое всерьез рассматривать?
Что у меня есть из исходных данных? Есть статейка в документации, где говорится: кароч partial objects это такие objects, whose state is not fully initialized (что? о чем мне это говорит? да ни о чем не говорит) and that is disconnected from the rest of its data. (ну дисконнектед так дисконнектед, проблема то в чем?)
>Use of partial objects is tricky. Fields that are not retrieved from the database will not be updated by the UnitOfWork even if they get changed in your objects.
А мне и не нужно, чтобы эти поля заполнять или обновлять через UnitOfWork. Мне блин ссылку нужно сделать, чтобы в таблице связей появились записи. После этого мой код прекращает работу.
>In short, partial objects are problematic because they are usually objects with broken invariants.
Да пусть хоть 50 раз повторят, что они problematic и broken. Они меня повторами хотят убедить, или что? Покажите случай, когда partial objects могут что-то сломать, я себе не могу это представить, может ты основываясь на опыте и более высоком icq можешь смоделировать в головном мозгу такой случай, я не могу пока не увижу своими глазами.
>As such, code that uses these partial objects tends to be very fragile
Пруфы.
>code needs to “know” which fields or methods can be safely accessed or add checks around every field access or method invocation.
Мне не нужно оттуда ничего получать и не нужно ничего устанавливать. Мне нужна просто ссылка.
Если бы была такая возможность, я бы использовал прямо массив.
$post->setComments([123, 124, 125, 126]);
$em->persist($post);
$em->flush();
// дальше идет рендер страницы и все, никаких доступов к свойствам, что якобы небезопасно
>You usually simply assume the state you need in the method is available, after all you properly constructed this object before you pushed it into the database, right? These blind assumptions can quickly lead to null reference errors when working with such partial objects.
Это не смог перевести. Но наверное опять вангуют скрытую угрозу без пруфов естественно.
Короче я не пойму в чем проблема, пока не увижу код, в котором происходит не то что должно по вине partial objects.
Ну ладно, я наверное надоел со всем этим. Пока сливаюсь. Потом может другую тему помусолим. У меня тут например назревает акробатика со сфинксом. Потом отпишусь в новом выпуске мурзилки. В новом треде то есть.
Какого сочного двачую налил тебе свертушки и по хардкору!
Эти ссылки на теорию - хочется грязно выругаться, когда их вижу.
Хотя часто помогает.
В задаче про студентов зарегистрированный пользователь может отредактировать свое мыло которое уникально?
Коданы, выручайте.
Что из этих двух стоит почитать?
Хранилищем является база данных, если уж на то пошло. Identity map да, сохраняет все управляемые сущности, но ради того чтобы избежать дубликатов и временно, а не навсегда.
Партиал объекты это неудачный (на мой взгляд) костыль для оптимизации. Ну например если у тебя в таблице есть несколько маленьких полей и большое поле text, может ты не хочешь его загружать (так как по объему один текст как 10 обычных записей). Или у тебя 30 полей , а нужны сейчас только 3. Это штука для оптимизации. Но проблема в том что это идет вразрез с логикой, так как у тебя в приложении начинают появляться "неполноценные" частично заполненные объекты. Причем так как они попадают в айдентити мап, то при запросе вроде
$em->find(..)
$em->createQuery()->getResult();
ты получишь назад этот неполноценный объект. Партиал объекты это 100% путь к труднообнаружимым багам. Не используй их вообще.
В других языках, например в Яве в Hibernate (по мотивам которого написана доктрина) эта проблема решена наличием "ленивых" полей, которые загружаются только при первом обращении. Они используют хитрые трюки с редактирвоанием байткода методов класса (то есть фактически ищут в коде класса обращения к ленивому полю и добавляют туда проверку и код ленивой загрузки). Потому там эта проблема решается хоть и более сложным, но более правильным способом. Теоретически наверно это и в php можно сделать, но очень сложно и пока никто не сделал.
И у меня была еще одна идея, как можно сделать оптимизацию загрузки полей, идея была в том чтобы мапить таблицу не на один класс, а на два: с основной и с дополнительной информацией. При этом для кода эти классы выглядят как 2 сущности со связью 1-1 и сущность с дополнительными данными подгружается только если она нужна. Ну, это реализовать намного проще на мой взгляд. Если есть желающие сделать такой патч - пожалуйста, делайте.
> У тебя есть дети?
к счастью(?) пока нет. Но пример ты привел не очень удачный. Ведь не ты один сталкиваешься с доктриной, и другие как-то в ней в итоге разбираются потихоньку (ну или запоминают основные конструкции). ORM это сложная штука, и чтобы в ней хорошо разбраться, надо приложить усилия. Но зато зная внутреннее устройство ты лучше понимаешь сильные и слабые стороны, что делать не стоит итд.
> whose state is not fully initialized (что? о чем мне это говорит? да ни о чем не говорит)
Это значит что не все поля заполнены.
> Мне блин ссылку нужно сделать, чтобы в таблице связей появились записи. После этого мой код прекращает работу.
Тогда либо прокси либо напрямую через SQL. Массовые обновления так удобнее всего делать, хотя при этом не срабатывают обработчики событий, так как объекты не загружаются в память.
> Покажите случай, когда partial objects могут что-то сломать,
Я написал выше. Я сталкивался с реальными багами в реальном приложении из-за них. В одном месте программы у объекта часть полей пустая и надо искать по всем коду какой именно партиал в этом виноват. Я категорически против них. Этой опции там не должно было быть с самого начала.
> Если бы была такая возможность, я бы использовал прямо массив.
Либо прокси (getReference) либо SQL.
> дальше идет рендер страницы и все, никаких доступов к свойствам,
Это пока. Пока ты только закладываешь мину.
> Это не смог перевести. Но наверное опять вангуют скрытую угрозу без пруфов естественно.
написано что программист думает что перед ним нормальный объект с заполненными свойствами, а на самом деле нет - получается баг. Ну и эти объекты попадают в айдентити мап и последующие обращения к доктрине могут вернуть партиал объект даже если в запросе явно запрашивается полноценный объект. Потому что этот подход противоречит нормальной логике.
> В новом треде то есть
ну можешь и в этом писать, я же не против . И может кто из анонов тоже что полезное узнает из наших постов.
Хранилищем является база данных, если уж на то пошло. Identity map да, сохраняет все управляемые сущности, но ради того чтобы избежать дубликатов и временно, а не навсегда.
Партиал объекты это неудачный (на мой взгляд) костыль для оптимизации. Ну например если у тебя в таблице есть несколько маленьких полей и большое поле text, может ты не хочешь его загружать (так как по объему один текст как 10 обычных записей). Или у тебя 30 полей , а нужны сейчас только 3. Это штука для оптимизации. Но проблема в том что это идет вразрез с логикой, так как у тебя в приложении начинают появляться "неполноценные" частично заполненные объекты. Причем так как они попадают в айдентити мап, то при запросе вроде
$em->find(..)
$em->createQuery()->getResult();
ты получишь назад этот неполноценный объект. Партиал объекты это 100% путь к труднообнаружимым багам. Не используй их вообще.
В других языках, например в Яве в Hibernate (по мотивам которого написана доктрина) эта проблема решена наличием "ленивых" полей, которые загружаются только при первом обращении. Они используют хитрые трюки с редактирвоанием байткода методов класса (то есть фактически ищут в коде класса обращения к ленивому полю и добавляют туда проверку и код ленивой загрузки). Потому там эта проблема решается хоть и более сложным, но более правильным способом. Теоретически наверно это и в php можно сделать, но очень сложно и пока никто не сделал.
И у меня была еще одна идея, как можно сделать оптимизацию загрузки полей, идея была в том чтобы мапить таблицу не на один класс, а на два: с основной и с дополнительной информацией. При этом для кода эти классы выглядят как 2 сущности со связью 1-1 и сущность с дополнительными данными подгружается только если она нужна. Ну, это реализовать намного проще на мой взгляд. Если есть желающие сделать такой патч - пожалуйста, делайте.
> У тебя есть дети?
к счастью(?) пока нет. Но пример ты привел не очень удачный. Ведь не ты один сталкиваешься с доктриной, и другие как-то в ней в итоге разбираются потихоньку (ну или запоминают основные конструкции). ORM это сложная штука, и чтобы в ней хорошо разбраться, надо приложить усилия. Но зато зная внутреннее устройство ты лучше понимаешь сильные и слабые стороны, что делать не стоит итд.
> whose state is not fully initialized (что? о чем мне это говорит? да ни о чем не говорит)
Это значит что не все поля заполнены.
> Мне блин ссылку нужно сделать, чтобы в таблице связей появились записи. После этого мой код прекращает работу.
Тогда либо прокси либо напрямую через SQL. Массовые обновления так удобнее всего делать, хотя при этом не срабатывают обработчики событий, так как объекты не загружаются в память.
> Покажите случай, когда partial objects могут что-то сломать,
Я написал выше. Я сталкивался с реальными багами в реальном приложении из-за них. В одном месте программы у объекта часть полей пустая и надо искать по всем коду какой именно партиал в этом виноват. Я категорически против них. Этой опции там не должно было быть с самого начала.
> Если бы была такая возможность, я бы использовал прямо массив.
Либо прокси (getReference) либо SQL.
> дальше идет рендер страницы и все, никаких доступов к свойствам,
Это пока. Пока ты только закладываешь мину.
> Это не смог перевести. Но наверное опять вангуют скрытую угрозу без пруфов естественно.
написано что программист думает что перед ним нормальный объект с заполненными свойствами, а на самом деле нет - получается баг. Ну и эти объекты попадают в айдентити мап и последующие обращения к доктрине могут вернуть партиал объект даже если в запросе явно запрашивается полноценный объект. Потому что этот подход противоречит нормальной логике.
> В новом треде то есть
ну можешь и в этом писать, я же не против . И может кто из анонов тоже что полезное узнает из наших постов.
да
>>723575
Либо сделать запись в базе и проверять ее либо изучать сетевое программирование и делать реальный асинхронный демон.
>>723943
да книги быдлокодерские если честно. Можешь почитать но приготовься потом переучиваться. Ты студентов делал? Замечания к ним читал? Ты вот этот урок делал?
https://github.com/codedokode/pasta/blob/master/soft/web-server.md
и это читал? http://php.net/manual/ru/tutorial.php
>Ты студентов делал?
Попробовал, но пиздец просто, как сложно, особенно искать всю инфу разрозненную. Проще, когда вся инфа есть в книге одной, это удобнее.
Какую из двух этих книжек лучше почитать? Не обе же, они вроде как вводные. Одна за 2015 год, другая за 2010
Смог выдавить из себя только /(\+7|8)/, а дальше не пойму как сделать проверку на наличие символов(-, , (, )) между цифрами.
перечитал, но в голову всё равно не лезет как можно сделать проверку
(цифра, за ней любое число минусов и скобок) x 10 раз
Ты знаешь как записать это?
- минус, скобки или пробел
- любое число минусов, скобок, пробелов
Норм. Круглые скобки внутри квадратных не имеют специального значения и их можно не экранировать.
Вечер в хату уважаемые, вообщем начал решать задачки, чтобы размять мозги и возник вопрос. http://codepad.org/mrBJKHqS
Вообщем все работает, но у меня закралась мысль что можно это сделать код короче и проще.
> Либо сделать запись в базе и проверять ее либо изучать сетевое программирование и делать реальный асинхронный демон.
Стоп! Можно поподробнее специально для самых умных? Вот что я понял: PHP в вебе работает как CGI-приложение из коробки - это раз. Веб-сервер передает http запросы в php.exe - это два. Имя скрипта php, запрошенное браузером, передается параметром в php.exe - это три. Дальше больше. php.exe, как любое нормальное приложение, работает в многопоточном режиме и обрабатывает множество соединений одновременно. Все это основано на обработке событий ОС. Теперь вернемся к скриптам. Скрипт работает в своем потоке - это ясно. Но я не могу понять вот что: cookie появились в 1994 году, JS в 1995. Cуперглобальные переменные появились только в версии 4.1(через 5 лет после изобретения php), когда разработали Zend-ядро, но за последующие 15 лет разрабы не додумались добавить способ прерывания запущенного скрипта и обновления значений пары глобальных переменных!?
CGI никто не использует на пратике, он был придуман для запуска простых скриптов в ответ на запрос, сейчас используется либо mod_php как часть апача либо демон php-fpm общающийся с сервером через FastCGI. Но это в вопросу не имеет никакого отношения.
> Дальше больше. php.exe, как любое нормальное приложение, работает в многопоточном режиме и обрабатывает множество соединений одновременно.
Нет. Не много потоков, а много процессов. Многопоточность по моему исплоьзовалась только на винде, а на линуксе и в mod_php и в php-fpm однопоточные процессы. Это опять же не имеет отношения к твоей задаче.
> когда разработали Zend-ядро, но за последующие 15 лет разрабы не додумались добавить способ прерывания запущенного скрипта и обновления значений пары глобальных переменных!?
Подумай чуть-чуть головой. Ты говоришь о том что веб-скрипт должен прерывать работу консольного скрипта. Каким образом веб-скрипт и консольный скрипт могут выполняться в раках одного процесса? Ты по моему не разбираешься в процессах и потоках, и насочинял небылиц.
Это будут в любом случае разные процессы. Значит тут нужны минимум средства IPC, а если бумать на перспективу то это могут быть процессы запущенные на разных машинах и значит нужны средства коммуникации между отдельными серверами. А какие это средства? Сокеты.
Потому я тебе и предложил 2 варианта:
- использовать ячейку в mysql для сигнализации
- сделать свой демон с сокетами, имитирующий многопоточность за счет асинхронности
- либо использовать какие-то средтства IPC если есть гарантия что 2 сркипта всегда будут работать на олдой машине
Самый простой вариант - с ячейкой в mysql, и для других вариантов у тебя пока явно не хватает знаний.
А почему не работает? Посмотри например на первый несовпавший номер. Обрати внимание что твоя регулярка частично совпала и с неправильными номерами так как ты не прикрепляешь ее к краям.
Не сердись. У меня за плечами только 3 книги (пикрилейтед). В вебе и нете я только полгода и пока как слепой котенок делаю все на ощупь.
В ОП посте есть книги если хочется что-то еще почитать по PHP. Что касается твоей задачи, делай проверку на останов через периодичесикй опрос ячейки в mysql, redis или чем-то таком. На худой конец файл, но база данных лучше.
>- использовать ячейку в mysql для сигнализации
А чем плох файл? Скорость сильно упадет?
Придется запускать еще один скрипт для работы с базой или файлом - а это время на исполнения и задержка, так что вариант отпадает и буду пердолиться глубже.
>- сделать свой демон с сокетами, имитирующий многопоточность за счет асинхронности
>- либо использовать какие-то средтства IPC
Разница в скорости между IPC и сокетами большая, как думаешь?
Предыдущие 2 дня поисков постоянно натыкался на WebSockets, ReactPHP и HHVM. Теперь еще phpDaemon и https://laravel.com/docs/5.1/queues нашел. Спасибо.
У меня так сделано. Дли битрикса 5 пыха, для остального 7.
Как то через цги, возможно ебское решение. По какому то мутному мануалу делал, сам особо не понимаю как оно работает.
>В ОП посте есть книги если хочется что-то еще почитать по PHP.
Я Котерова 200 страниц прочитал, мне хватило для зажигания. Еще лениво листал 3 книги в заглавном треде этой доски, кажется время настало опять их полистать. Пока приспособился к официальной документации - написана шикарно. ОП пост не читал, многабукв и левые задачи какие-то.
> А чем плох файл?
тем что нельзя получить доступ с другой машины
> Разница в скорости между IPC и сокетами большая, как думаешь?
Ответ можно получить только измерив ее.
>>724137
Зря игнорируешь задачи из Оп поста. Не раз люди приходили, который училис по другим курсам, с высокой самооценкой и после столкновения с задачами меняли ее.
>>724151
погугли
Не реклама, просто на HN увидел.
>>724066
Можно использовать array_rand. Также, заметь что первые 2 строки генерируются по одному принципу и значит вместо копипасты можно написать цикл.
Также, у тебя ошибка неправильно вычисляется верхняя граница случайного числа. В массиве нет эклкмента с индексом равным count
Также, не надо делать 9 переменных random1 .. random9 - тут явно напрашивается массив из 9 элементов.
А у его автора больше лайков чем у меня
>Зря игнорируешь задачи из Оп поста. Не раз люди приходили, который училис по другим курсам, с высокой самооценкой и после столкновения с задачами меняли ее.
ОП советует использовать 4 пробела вместо табов, дальше читать не захотелось.
PSR тоже самое рекомендует, табы из прошлого, ломают отображение кода в разных программах, регулировать отступ можно и без табов. давно уже обсудили.
>регулировать отступ можно и без табов
Спасибо, пройдено. Раньше пробел дрочил в редакторах. Теперь если автоподстановки пробелов через таб нету, то редактор в топку.
function canGet($time, $byWhat) {
return array('time' => $time, 'by' => $byWhat);
}
Интересует этот момент, анончик. Вот как бы ты сделал вариант, если писать time и byWhat нужно? То есть, я просто хочу узнать поточнее, как работать с многомерными массивами.
Покажи на примере этого:
'chk' => array(
'pet' => canGet(10, BUS),
'spo' => canGet(3, SUBWAY)
),
Вот он у тебя так записан, а если бы писать тайм и bywhat, то вышло бы вот так?
'chk' => array(
'pet' => array('time' => 10, 'byWhat' => subway),
'spo' => array('time' =>3, 'byWhat' => bus)
),
?
>вышло бы вот так?
Да. Только ты перепутал немного в программировании не бывает немного.
Будет так:
'chk' => array(
'pet' => array('time' => 3, 'by' => subway),
'spo' => array('time' =>10, 'by' => bus)
);
Ага, понял, спасибо тебе.
Пишут что за 13 месяцев выплачивается долг. Что у меня не правильно?
Чё-т кекнул в голос.
$paymentTotal попробуй вывести - сколько всего уплачено. Должно быть 61270 с копейками.
У тебя к 4138 не прибавляются последний раз 1к обслуживания и 3% - тогда было бы больше 5к и цикл снова бы пошёл на итерацию. Осталось бы 262р - и снова была бы итерация (с прибавлением 1к обслуживания и 3% от 262).
И вышло бы 13 месяцев.
У нас на фирме тоже так. Всех табберов нахуй послали, сделали автоформатирование табов на 4 пробела.
На данный момент имею
[code]
<?php
if($_POST){
$name = $_POST['fullname'];
$phone = $_POST['phone'];
$email = $_POST['email'];
$message = $_POST['message'];
//send email
mail("mypPL-mailANUSoutB6ClookPUNCTUMcoj9(m",
"Message from ".$name." (".$email.").",
$message);
}
?>
[/code]
Но ожидаемо ничерта не работает. В какую сторону рыть?
Сам код уже на хостинге, почтовый сервер у них поднят вроде как
нашел ответ на хабре
^((8|\+7)[\- ]?)?(\(?\d{3}\)?[\- ]?)?[\d\- ]{7,10}$
имхо, для новичка слишком ебовая задача.
goutte
Чего же ОП не упомянул об этом в уроках? Походу лучше сначала весь синтаксис выучить, прежде чем читать уроки.
Вообще-то официальную доку по языку прежде всего читать нужно и не по одному разу, ты php собрался учить или что? Тебе сюда:
http://php.net/manual/ru/
>другие как-то в ней в итоге разбираются потихоньку
Потихоньку и я разберусь. Хотя жаль затраченного времени. Ведь пейсателям документации было достаточно нашкрябать одну несчастную страничку с толковыми объяснениями, это сэкономило бы мне и многим другим уйму времени. А самое главное предотвратило бы появление как минимум 50% быдлокодеров, которых все так ругают, и никто не прилагает минимальных усилий чтобы дать людям нормальную учебную литературу.
Вот если бы ты мне не объяснил в чем проблема partial objects, я бы продолжал их использовать.
> (ну или запоминают основные конструкции)
Э, ну это же не наш путь, это какой-то макакинг. Я же хочу понимать что пишу а не зубрить рецепты.
>можешь и в этом треде писать, я же не против
Так перекат ведь скоро. Или опять будешь тянуть до 1000 постов? У меня набитый тред еле обновляется, сильно тормозит. Какую выгоду ты видишь в уходе в бамплимит?
Разве что меньше чуханов с вопросами уровня "чего у меня не работает денвер" и "решил вкатица, скоко платят?".
Ну ладно. Короче, я тут замучился с изобретением велосипеда по поиску точного перевода слов. Вроде получилось, но довольно костыльно.
Напоминаю предысторию. Пользователь загружает кусок текста на английском, например фрагмент из книги.
Приложение должно разобрать текст и найти перевод для всех слов, которые в нем встречаются, с доп.условиями:
а) должны находиться словоформы, т.е. если в тексте встретилось слово `creating`, должен найти перевод для `create`
б) должны находиться устойчивые словосочетания, например `pay attention`(`обратить внимание`)
Для индексации использовал сфинкс со стеммингом. Мы уже обсуждали, почему так. Возможно позже я разберусь с полнотекстовым поиском в mysql 5.6 и перепишу под него. Не важно пока.
Алгоритм такой:
1. Бьем текст на слова preg_split по \W
2. В цикле запрашиваем каждое слово в сфинксе where match
3. Сфинкс возвращает набор результатов
4. Так как набор большой, и вернутся слова не встречающиеся в тексте, нужно сделать strpos для каждого, проверить встречается ли оно в оригинальном тексте.
Ну то есть что я имею ввиду: по слову `fish` вернется `fish`, `fishing`, `neither fish nor fowl`, `be a fish out of water`. Нужно отсеять только словосочетания, которые в тексте встречаются.
strpos работает быстро, я проверял: цикл в 1млн итераций на тексте в 1000 слов занял 5 секунд на дохлом процессоре моего нетбука.
5. Учитывая, что слоп-слова типа предлогов встречаются в словаре тысячи раз, выбрасываю их из индекса. У сфинкса есть возможность указать файл со списком стоп-слов.
http://sphinxsearch.com/docs/current/conf-stopwords.html
Сфинкс сам может сгенерировать список этих стоп-слов по существующему индексу
indexer index_name --buildstops stopwords.txt 1000
С опцией --buildfreqs даже будет указана частота с которой слово встречается в словаре.
В словаре на 500 000 слов некоторые слова повторяются уж очень часто.
of 16207
in 4575
Даже эти мусорные слова в принципе нужно тоже перевести, а не игнорить полностью. Что интересно, многие важные слова тоже встречаются несколько сот раз.
water 2244
pressure 1561
surface 1229
И так далее. Допустим, я проигнорирую (то есть уберу из индекса) все слова, которые в словаре встречаются более 100 раз.
Это значит что сфинкс будет мне возвращать максимум 100 результатов поиска за раз (и искать в исходном тексте придется не более 100 раз).
Но слово water и многие другие сфинкс вообще не будет находить.
6. Поэтому для ненайденных слов (если сфинкс вернул 0 записей), отправляем запрос к mysql на точное совпадение select id from dictionary where keyword = 'water'.
Таким образом хотя бы будет найден дословный перевод.
Какие минусы такого алгоритма:
- бешеное кол-во итераций циклов (сначала для каждого из например 500 слов отправляем запрос в сфинкс), во вложенном цикле strpos для каждого из <100 результатов поиска; надежды здесь только на скорость сфинкса и функции strpos
- при всех затратах неполноценно выполняет возложенную на него миссию: найдены будут не все фразы, например есть фраза `take off`, как можно догадаться оба слова являются стоп-словами, `take` встречается в 569 записях, `off` 1626. set up, get out и т.д. Чтобы находило такие фразы, придется повысить до 500 порог стоп-слова, это даст соответственно увеличение итераций strpos до 500.
Даже при пороге 100 для стоп-слов думает 8 секунд. А если 100 одновременных загрузок? Фейл.
Возможно стоит отказаться от идеи "быстрого" построения словаря для текста и перенести все в очередь на сервер. А шустрый пользователь если залезет раньше времени на страницу где должен быть словарь, выдавать ему "перевод еще не готов, процесс обработки может занимать до 5 минут".
Теперь придется пердолиться с построением очереди, ага. Временную таблицу?
Еще варианты?
>другие как-то в ней в итоге разбираются потихоньку
Потихоньку и я разберусь. Хотя жаль затраченного времени. Ведь пейсателям документации было достаточно нашкрябать одну несчастную страничку с толковыми объяснениями, это сэкономило бы мне и многим другим уйму времени. А самое главное предотвратило бы появление как минимум 50% быдлокодеров, которых все так ругают, и никто не прилагает минимальных усилий чтобы дать людям нормальную учебную литературу.
Вот если бы ты мне не объяснил в чем проблема partial objects, я бы продолжал их использовать.
> (ну или запоминают основные конструкции)
Э, ну это же не наш путь, это какой-то макакинг. Я же хочу понимать что пишу а не зубрить рецепты.
>можешь и в этом треде писать, я же не против
Так перекат ведь скоро. Или опять будешь тянуть до 1000 постов? У меня набитый тред еле обновляется, сильно тормозит. Какую выгоду ты видишь в уходе в бамплимит?
Разве что меньше чуханов с вопросами уровня "чего у меня не работает денвер" и "решил вкатица, скоко платят?".
Ну ладно. Короче, я тут замучился с изобретением велосипеда по поиску точного перевода слов. Вроде получилось, но довольно костыльно.
Напоминаю предысторию. Пользователь загружает кусок текста на английском, например фрагмент из книги.
Приложение должно разобрать текст и найти перевод для всех слов, которые в нем встречаются, с доп.условиями:
а) должны находиться словоформы, т.е. если в тексте встретилось слово `creating`, должен найти перевод для `create`
б) должны находиться устойчивые словосочетания, например `pay attention`(`обратить внимание`)
Для индексации использовал сфинкс со стеммингом. Мы уже обсуждали, почему так. Возможно позже я разберусь с полнотекстовым поиском в mysql 5.6 и перепишу под него. Не важно пока.
Алгоритм такой:
1. Бьем текст на слова preg_split по \W
2. В цикле запрашиваем каждое слово в сфинксе where match
3. Сфинкс возвращает набор результатов
4. Так как набор большой, и вернутся слова не встречающиеся в тексте, нужно сделать strpos для каждого, проверить встречается ли оно в оригинальном тексте.
Ну то есть что я имею ввиду: по слову `fish` вернется `fish`, `fishing`, `neither fish nor fowl`, `be a fish out of water`. Нужно отсеять только словосочетания, которые в тексте встречаются.
strpos работает быстро, я проверял: цикл в 1млн итераций на тексте в 1000 слов занял 5 секунд на дохлом процессоре моего нетбука.
5. Учитывая, что слоп-слова типа предлогов встречаются в словаре тысячи раз, выбрасываю их из индекса. У сфинкса есть возможность указать файл со списком стоп-слов.
http://sphinxsearch.com/docs/current/conf-stopwords.html
Сфинкс сам может сгенерировать список этих стоп-слов по существующему индексу
indexer index_name --buildstops stopwords.txt 1000
С опцией --buildfreqs даже будет указана частота с которой слово встречается в словаре.
В словаре на 500 000 слов некоторые слова повторяются уж очень часто.
of 16207
in 4575
Даже эти мусорные слова в принципе нужно тоже перевести, а не игнорить полностью. Что интересно, многие важные слова тоже встречаются несколько сот раз.
water 2244
pressure 1561
surface 1229
И так далее. Допустим, я проигнорирую (то есть уберу из индекса) все слова, которые в словаре встречаются более 100 раз.
Это значит что сфинкс будет мне возвращать максимум 100 результатов поиска за раз (и искать в исходном тексте придется не более 100 раз).
Но слово water и многие другие сфинкс вообще не будет находить.
6. Поэтому для ненайденных слов (если сфинкс вернул 0 записей), отправляем запрос к mysql на точное совпадение select id from dictionary where keyword = 'water'.
Таким образом хотя бы будет найден дословный перевод.
Какие минусы такого алгоритма:
- бешеное кол-во итераций циклов (сначала для каждого из например 500 слов отправляем запрос в сфинкс), во вложенном цикле strpos для каждого из <100 результатов поиска; надежды здесь только на скорость сфинкса и функции strpos
- при всех затратах неполноценно выполняет возложенную на него миссию: найдены будут не все фразы, например есть фраза `take off`, как можно догадаться оба слова являются стоп-словами, `take` встречается в 569 записях, `off` 1626. set up, get out и т.д. Чтобы находило такие фразы, придется повысить до 500 порог стоп-слова, это даст соответственно увеличение итераций strpos до 500.
Даже при пороге 100 для стоп-слов думает 8 секунд. А если 100 одновременных загрузок? Фейл.
Возможно стоит отказаться от идеи "быстрого" построения словаря для текста и перенести все в очередь на сервер. А шустрый пользователь если залезет раньше времени на страницу где должен быть словарь, выдавать ему "перевод еще не готов, процесс обработки может занимать до 5 минут".
Теперь придется пердолиться с построением очереди, ага. Временную таблицу?
Еще варианты?
>У меня набитый тред еле обновляется, сильно тормозит.
Лол, винду почисти или комп обнови. У меня 30 табов в браузере открыто, тред вообще не тормозит, даже когда под 1000.
>В ООП конечно нужно все оборачивать.
Алгоритм у парсера такой же, как указан здесь >>721517, но я так и не понял, как это по классам раскидать. ООП пригодится, если захочу добавить возможность сохранения информации как в файлы, так и в БД, как это можно организовать? ООП код понимаю, с проектированием вроде тоже не всё так плохо.
Это зависит от железа. Тут просто код течет, и если ты например неделю держишь тред откртым, обнловляя его, он начинает тормозить все сильнее. И еще начинает тормозить если быстро печатать текст, особенно когда его много. Потому я теперь набираю ответы в саблайме (который тоже не выдерживает моей скорости набора и тоже начинает раздуваться в памяти и тормозить).
Плюс иногда тамт что-то сходит с ума и он начинает есть под 100% ядра. Это все решается перезагрузкой страницы.
Вангую, у тебя какой-то скрипт для борд поставлен, у них обычно такое поведение. У меня ничего не тормозит, хоть по 2 недели тред открыт.
Это просто сокращенная форма $a = $a . 'какой-то текст';
Бамп
Никак. И что вообще за идиотизм? Если тебе не нравятся анонимные пользователи, сделай чтобы при регистрации они обязаны были приложить видео где они показывают в камеру паспорт (фото недостаточно, легко подделать).
Или например сделай регистрацию через вконтакте, а чтобы отсеять анонимов, требуй наличие не менее 200 друзей.
>>724757
Мне просто интересно как можно такое реализовать. Заметил, что если в режиме инкогнито из хрома попытаться зайти в /e, то будет 404.
>>724757
Чего бомбишь?
Это скорее чувак ищет защиту от майнинга, а не от анонимных дрочеров.
Сами коммуниздят контент у конкурентов, и хотят сворованное уберечь от других раков. Как это по-пидорашкински.
https://glot.io/snippets/ee19fz7qdv
Переделал, не понял только как сделать массив из 9 элементов, точнее уже из 5-ти.
https://ru.hexlet.io/professions/php
https://m.habrahabr.ru/company/hexlet/blog/282117/
Начинающий, который плохо разбирается в нашей профессии, подумает, что этого курса достаточно чтобы стать веб-разработчиком на php и найти работу. Вроде как выгодное предложение.
Но посмотрим внимательнее. Во-первых, видно что это курс только по PHP с небольшим включением postgresql (SqL на самом базовом уровне). Например HTML/CSS он не научит. Во-вторых, можно увидеть что авторы слегка поехавшие и под видом обучения пытаются "впарить" начинающему то, что им нравится:
> 6. PHP: Функциональное программирование
> 7. PHP: Алгоритмы
> 16. Linux: Пользователи
> 17. Основы Ansible
Для тех, кто не знает, ansible это система деплоя для проектов, у которых много серверов. Хотя это бесплатный продукт, но он распространяется с целью рекламы платной Ansible Tower, которая позволяет управлять деплоем через графический интерфейс (расчет видимо на то что айтишники начнут использовать ансибл в командной строке, а менеджер или тимлид которому не по чину в ней ковыряться, купит тауэр).
Зато вот что не освещается:
- большая часть SQL, внешние ключи и ограничения
- принципы работы с формами
- MVC
- фреймворки
- ORM
- DI
- HTML/CSS
- JS/DOM
С одной стороны я могу понять авторов курса, которые хотят обучить людей каким-то фундаментальным знаниям, про алогритмы например. Но зачем например ансибл? Они поехавшие? человек ни одного фрейморка не знает, а будет писать скрипты деплоя на 10 серверов?
По сути объем знаний у них (если убрать уроки про линукс, алгоритмы и тестирование) по php как у меня в учебнике + HTTP, PDO и работа с этим в PHP. ООП вроде как есть, но ему уделено мало внимания. Вряд ли у них есть задача вроде наших кошек-мышек на которой сломался не один изучающий.
В конечном итоге, мне не нравится то, что они дают ложные обещания. Смотрите, вверху страницы написано:
> В области веб-программирования PHP — самый популярный язык. На нем написаны миллионы сайтов, по нему больше всего предложений о работе.
....
> [Вступить в профессию]
Но трюк в том что вы не вступите в профессию с такими знаниями.
https://ru.hexlet.io/professions/php
https://m.habrahabr.ru/company/hexlet/blog/282117/
Начинающий, который плохо разбирается в нашей профессии, подумает, что этого курса достаточно чтобы стать веб-разработчиком на php и найти работу. Вроде как выгодное предложение.
Но посмотрим внимательнее. Во-первых, видно что это курс только по PHP с небольшим включением postgresql (SqL на самом базовом уровне). Например HTML/CSS он не научит. Во-вторых, можно увидеть что авторы слегка поехавшие и под видом обучения пытаются "впарить" начинающему то, что им нравится:
> 6. PHP: Функциональное программирование
> 7. PHP: Алгоритмы
> 16. Linux: Пользователи
> 17. Основы Ansible
Для тех, кто не знает, ansible это система деплоя для проектов, у которых много серверов. Хотя это бесплатный продукт, но он распространяется с целью рекламы платной Ansible Tower, которая позволяет управлять деплоем через графический интерфейс (расчет видимо на то что айтишники начнут использовать ансибл в командной строке, а менеджер или тимлид которому не по чину в ней ковыряться, купит тауэр).
Зато вот что не освещается:
- большая часть SQL, внешние ключи и ограничения
- принципы работы с формами
- MVC
- фреймворки
- ORM
- DI
- HTML/CSS
- JS/DOM
С одной стороны я могу понять авторов курса, которые хотят обучить людей каким-то фундаментальным знаниям, про алогритмы например. Но зачем например ансибл? Они поехавшие? человек ни одного фрейморка не знает, а будет писать скрипты деплоя на 10 серверов?
По сути объем знаний у них (если убрать уроки про линукс, алгоритмы и тестирование) по php как у меня в учебнике + HTTP, PDO и работа с этим в PHP. ООП вроде как есть, но ему уделено мало внимания. Вряд ли у них есть задача вроде наших кошек-мышек на которой сломался не один изучающий.
В конечном итоге, мне не нравится то, что они дают ложные обещания. Смотрите, вверху страницы написано:
> В области веб-программирования PHP — самый популярный язык. На нем написаны миллионы сайтов, по нему больше всего предложений о работе.
....
> [Вступить в профессию]
Но трюк в том что вы не вступите в профессию с такими знаниями.
> Со-основатель и технический директор образовательного проекта Hexlet. Со-организатор конференции Nastachku.ru. В прошлом руководил филиалом undev.ru в Ульяновске, а до этого работал в Qik (Skype). Создает инженерную культуру в компаниях и несет в массы XP, DDD и DevOps. Евангелист ментального программирования.
Это же вообще не программист. А менеджер который "создает инженерную культуру" и организует конференции.
Зато после твоих задач на айфон в кредит можно сразу сеньором устраиваться.
Во всяком случае это явно лучше всяких быдло-курсов типа "школы программирования", "специалиста" или лофтскул.
Да, их курсы лучше этих клонуских курсов где на первой странице пишут зарплаты вроде 100 000 р
Вот еще они на ютуб выкладывают свои собеседования на джуниора: https://www.youtube.com/watch?v=H8OZ3B2_X3U
>имхо, для новичка слишком ебовая задача.
Мне знакомые программисты с высшим образованием сказали, когда посмотрели на учебник ОПа, что там начиная с 4-5 страницы идут уже хорошие такие вузовские задачки.
Где-то выше братишка получил в виде тестового задания задачу на палиндром.
С другой стороны, знакомый школьник сказал, что задачу на числа прописью решают на олимпиадах (тоже, в принципе, достаточно высокий уровень).
А по поводу твоей регулярки - она перегружена, как мне кажется, но в целом нормально всё. Там так-то не обязательно получать код города, там нет этого в условии, это было в предыдущей задаче.
У меня вообще треды все предыдущие открыты, пока их не смывает. Несколько почт, учебник ОПа, LI, разные партнёрки.
Проблема с железом у тебя - это верно.
Ну так-то спасибо, можно будет посмотреть.
Когда после твоего учебника читаешь и пробуешь что-то другое, то сначала морщишься, а потом вроде начинаешь отделять зёрна от плевелов. Часто иной подход помогает просто лучше усвоить пройденное.
> когда посмотрели на учебник ОПа, что там начиная с 4-5 страницы идут уже хорошие такие вузовские задачки.
Так это не с учебником и не с ОПом проблемы, а с нашими вузами. Аноны, не верьте таким заявлениями и тем кто говорит что у меня сложные задачи. В США в вузах на полном серьезе начинают с Явы (с ООП так как без него на ней писать нельзя), с SICP, с парсинга текста в соовтетсвии с граммитикой, и тд. Мои задачи в сравнении с этим намного легче. Мои задачи в учебнике - уровня ПТУ и проще могут быть только задачи в школе на информатике.
> С другой стороны, знакомый школьник сказал, что задачу на числа прописью решают на олимпиадах (тоже, в принципе, достаточно высокий уровень).
только если на школьных.
>>724543
Твоя регулярка неправильная. Ну-ка вбей-ка свою регулярку сюда https://regex101.com/r/qF7vT8/3
Разумеется, может быть полезно читать про те же вещи, но изложенные другим человеком. Я только за, только в итоге мои задачки решать не забывайте, а то вдруг в другом учебнике будут школьные задачки и вы из-за этого переоените свои способности.
Он решал её на областной олимпиаде по информатике, но да - школьный уровень.
>Твоя регулярка неправильная
Ох щи, действительно. Я и не проверил, просто посмотрел на неё. Зазнался совсем.
Почему она такая сложная? Вот эта короче в два раза и все находит ^ ?(8|\+ ?7)([ \-()]*[0-9]){10}$
"кошки-мышки", только с n-мерной доской
сильно извращенный поиск кратчайшего пути
"Числа прописью" легкая задача, только муторная. Не понимаю зачем она на олимпиаде. Тут больше требование к знаниям русского языка как по мне.
"Палиндром" простая задача, что бы в школе циклы объяснить.
А в универе на паграмиста за первые 2 года у меня был только 1 семестр собственно программирования, преподавали какую-то древнюю версию Delphi. Надеюсь, что за 10 лет там все поменялось. Хотя не жалею что тогда дропнул и сейчас с нуля начинаю.
Пытался там давно чему-то научиться, в итоге ушел сюда и на w3schools, все более полезное и доступное оказалось.
Плюс там все платное, что во времена кучи бесплатных туториалов и курсов как-то вообще не привлекает.
Правильный вопрос - насколько твой код вырастет в будущем, и будут ли его еще где-то потом использовать. Ты этого почти всегда не знаешь, поэтому лучше юзать ООП.
Они там стажировку гарантируют, думаю за это и платишь.
Привет, оп. У меня опять местами получилось пикрелейтед3. Дурацкий GUI гитхаб, я не понимаю почему изменения в одних файлах отображаются, а в других, где они точно были, нет. Ну точно если доживу до файлообменника буду консолью пользоваться, сейчас ̶б̶ы̶л̶о̶ ̶л̶е̶н̶ь̶ ̶и̶з̶у̶ч̶а̶т̶ь̶ ̶к̶о̶н̶с̶о̶л̶ь̶ подумал что раз уж работал через GUI, то нельзя будет коммитить через консоль. Дурацкое оправдание, но я правда так думал и мне правда очень стыдно что ты лишнее время потратишь на список изменений. Ладно, по коду:
> тут ты делаешь несклоько замен. Что если у нас в искомых словах есть слово mark? ты заменишь название тега. Это явно тянет на XSS уязвимсоть. Что если ты после замены нарушишь праила вложенности тегов?
Так я еще тогда разобрался с этим, ты break в цикле наверное не заметил. Пруф результата пикрелейтед2.
> В setCookie ничего не передается и это как минимум странно. Таких побочных эффектов быть не должно. Метод должен назваться loginStudent и принимать на вход что-нибудь, и работать всегда. Более того, у тебя же уже есть метод login. его и надо использовать.
Не понятно. Я явно должен передавать в метод аргументы? В классе нельзя их вызвать через собственные методы? Или же тебе не понравился метод getLastInsertID? Если да, то чем?
> относительные имена файлов работают плохо и ищутся не относиельно текущего файла, а где попало. Используй абсолютные имена например через __DIR__. Использовал $_SERVER[“DOCUMENT_ROOT”], это ок? Только в обработчике исключений это делать или везде рекваеры в абсолютные превращать? Если честно, я с __DIR__ не разобрался. Можешь привести на примере моего кода замену на __DIR__?
> Тут скопипащена шапка. Избавься от повторов.
Шапка которая в <head></head>? А я думал ты про заголовок вверху страницы.
Кроме защиты разлогинивая от CSRF я попробовал защитить и форму регистрации. Судить насколько у меня это получилось тебе. Вообщем с токенами я больше всего провозился. Они теперь и в базе, и в защите. Я по этому совету сделал >>721985 Остальные твои замечания тоже учел. Например возможный NULL в маппере у меня не встретить, только “”, регулярки теперь двух типов и т.д.
Привет, оп. У меня опять местами получилось пикрелейтед3. Дурацкий GUI гитхаб, я не понимаю почему изменения в одних файлах отображаются, а в других, где они точно были, нет. Ну точно если доживу до файлообменника буду консолью пользоваться, сейчас ̶б̶ы̶л̶о̶ ̶л̶е̶н̶ь̶ ̶и̶з̶у̶ч̶а̶т̶ь̶ ̶к̶о̶н̶с̶о̶л̶ь̶ подумал что раз уж работал через GUI, то нельзя будет коммитить через консоль. Дурацкое оправдание, но я правда так думал и мне правда очень стыдно что ты лишнее время потратишь на список изменений. Ладно, по коду:
> тут ты делаешь несклоько замен. Что если у нас в искомых словах есть слово mark? ты заменишь название тега. Это явно тянет на XSS уязвимсоть. Что если ты после замены нарушишь праила вложенности тегов?
Так я еще тогда разобрался с этим, ты break в цикле наверное не заметил. Пруф результата пикрелейтед2.
> В setCookie ничего не передается и это как минимум странно. Таких побочных эффектов быть не должно. Метод должен назваться loginStudent и принимать на вход что-нибудь, и работать всегда. Более того, у тебя же уже есть метод login. его и надо использовать.
Не понятно. Я явно должен передавать в метод аргументы? В классе нельзя их вызвать через собственные методы? Или же тебе не понравился метод getLastInsertID? Если да, то чем?
> относительные имена файлов работают плохо и ищутся не относиельно текущего файла, а где попало. Используй абсолютные имена например через __DIR__. Использовал $_SERVER[“DOCUMENT_ROOT”], это ок? Только в обработчике исключений это делать или везде рекваеры в абсолютные превращать? Если честно, я с __DIR__ не разобрался. Можешь привести на примере моего кода замену на __DIR__?
> Тут скопипащена шапка. Избавься от повторов.
Шапка которая в <head></head>? А я думал ты про заголовок вверху страницы.
Кроме защиты разлогинивая от CSRF я попробовал защитить и форму регистрации. Судить насколько у меня это получилось тебе. Вообщем с токенами я больше всего провозился. Они теперь и в базе, и в защите. Я по этому совету сделал >>721985 Остальные твои замечания тоже учел. Например возможный NULL в маппере у меня не встретить, только “”, регулярки теперь двух типов и т.д.
И получается что-то типа этого
http://web.archive.org/web/20120607054846/http://www.phppatterns.com/docs/design/hello_world_in_patterns
А теперь подумай какие приложения пишутся в реальности - хелло ворлды или все же приложения с какой-то логикой? У тебя аргументы типичного человека который не осилил ООП и фреймворки и вместо того чтобы в этом признаться, начинает доводить все до абсурда. Реальные приложения пишутся не с нуля, а на фреймворке и все фабрики там уже написаны за тебя. Тебе надо только контроллеры, модели, сервисы и вьюшки добавить да конфиги подкрутить.
>Использовал $_SERVER[“DOCUMENT_ROOT”], это ок?
Когда запустишь скрипт с консоли или с крона этой переменной не будет и все пойдет по пизде.
Я так разок обосрался с $_SERVER['HTTP_HOST'] и выгрузил в яндекс маркет товары с ссылками типа http://undefined/shirts/poebota. Клиент устроил истерику, меня вызвали на ковер, отхуесосили как следует и лишили премии. Теперь стараюсь избегать использования $_SERVER.
Удваиваю этого анона. Более того, в некоторых конфигурациях тот же Апач передает не ту папку в этом свойстве. И я тоже сталкивался с багами из-за него. Не используйте DOCUMENT_ROOT никогда.
Лол, в твоей истории все пиздец. И что написанный скрипт не тестируется интеграционными тестами, и что нет проводящего тестирование отдела перед релизом, и что за ошибки менеджмента в наладке нормального процесса отладки продукта наказывают программиста. Ну и шарага.
Никто обычно не покрывает тестами простые приложения вроде магазинов или корпоративных сайтов.
Вот у меня пользователь сохраняет записи в таблице. Эти записи нужно обработать. Обработка получается очень долгой, поэтому решил добавлять ее в очередь (отдельную таблицу), чтобы по расписанию запускался скрипт и обрабатывал N записей из очереди и затем удалял их из очереди (ну или выставлял статус "обработано").
Вопрос в том, как сделать так чтобы несколько скриптов одновременно не начали обрабатывать одну и ту же запись. Дело в том что промежуток между запусками крон-скрипта нужно сделать небольшим, допустим 30 секунд. Скрипт выполняется долго, теоретически может быть дольше этого времени. Так что есть риск что запустится уже следующий. Как быть, чтобы они не начали обрабатывать одни и те же записи?
Вспомнилась блокировка "select for update". А как она работает? Я имею ввиду, что будет делать mysql, когда получит "select x from queue", а в ней заблочены некоторые строки? Молча вернет незаблоченные, или будет висеть и ждать пока освободятся? А если заблоченные записи будут вообще удалены? Как отреагирует то соединение, которое ждет снятия блокировки?
Или может сразу при выборе некоторых записей ставить им другой статус? Но что будет если скрипт помрет? Если не использовать транзакции, то статус так и останется "занято". Если использовать, то другие скрипты ведь их просто не увидят.
Короче я тогда еще временную метку прилеплю, когда задача была добавлена/обновлена.
Второй крон-скрипт будет ходить и удалять все записи со статусом "обработано", а записи "обрабатывается" проверять по времени, если они были созданы хз давно, значит скрипт который их начал обрабатывать и выставил промежуточный статус скоропостижно скончался, и надо бы вернуть обычный статус.
Если кто надумает более интересное решение, пишите, звоните.
Ты ошибся.
> $infinity = 1000000;
есть и настоящая бесконечность: http://php.net/manual/ru/math.constants.php
> function initialTime
название функции должно начинаться с глагола: сделатьЧтоТо()
> $pathArray[$name] = array('totalTime'=>$paths[$pointName][$name]['time'],'listOfRouts'=>array(1=>array($name,$paths[$pointName][$name]['time'],$paths[$pointName][$name]['by'])));
Это нечитаемо. Такие строки надо упрощать, перенося их, вынося часть выражений в переменные.
И вообще не надо строить массивы такой вложенности. В алгоритме Дейкстры достаточно иметь 2 массива:
[ название точки => минимальное расстояние в минутах до нее ]
[ название точки => откуда мы в нее приходим ]
Я думаю, тут 2 отдельных массива лучше чем один сложный. Третий элемент (на чем мы пришли) не нужен так как его можно получить из таблицы связей.
И соответственно хранить там список возможных путей тоже не надо:
> $pathArrayInPoint[$name]['listOfRouts']
Надо хранить лишь путь, по которому мы пришли в точку за минимальное время
> array($name,0,'Ты уже здесь.')
Вот как понять что значит 0 тут? Абсолютно непонятно так как элементы массива не подписаны понятными именами.
Более того, тут не нужен массив. В алгоритме Дейкстры мы записываем для каждой точки в какую точку надо идти из нее (или из какой мы в нее пришли)
> switch ($routStat[2]){
Что значит 2? Невозможно угадать. Нужно использовать буквенные ключи.
> case "sub":
Откуда взялась эта строка? Я не вижу где она записывается в массив. Там должна использоваться константа.
> $listOfPoints = array($startPoint);
> $listOfPoints2 = array();
Тоже неуданый момент, 2 однотипных переменнх, непонятно чем отличающихся.
В общем, давай, упрощай код, пока его читать очень тяжело и алгоритм переусложнен.
> $infinity = 1000000;
есть и настоящая бесконечность: http://php.net/manual/ru/math.constants.php
> function initialTime
название функции должно начинаться с глагола: сделатьЧтоТо()
> $pathArray[$name] = array('totalTime'=>$paths[$pointName][$name]['time'],'listOfRouts'=>array(1=>array($name,$paths[$pointName][$name]['time'],$paths[$pointName][$name]['by'])));
Это нечитаемо. Такие строки надо упрощать, перенося их, вынося часть выражений в переменные.
И вообще не надо строить массивы такой вложенности. В алгоритме Дейкстры достаточно иметь 2 массива:
[ название точки => минимальное расстояние в минутах до нее ]
[ название точки => откуда мы в нее приходим ]
Я думаю, тут 2 отдельных массива лучше чем один сложный. Третий элемент (на чем мы пришли) не нужен так как его можно получить из таблицы связей.
И соответственно хранить там список возможных путей тоже не надо:
> $pathArrayInPoint[$name]['listOfRouts']
Надо хранить лишь путь, по которому мы пришли в точку за минимальное время
> array($name,0,'Ты уже здесь.')
Вот как понять что значит 0 тут? Абсолютно непонятно так как элементы массива не подписаны понятными именами.
Более того, тут не нужен массив. В алгоритме Дейкстры мы записываем для каждой точки в какую точку надо идти из нее (или из какой мы в нее пришли)
> switch ($routStat[2]){
Что значит 2? Невозможно угадать. Нужно использовать буквенные ключи.
> case "sub":
Откуда взялась эта строка? Я не вижу где она записывается в массив. Там должна использоваться константа.
> $listOfPoints = array($startPoint);
> $listOfPoints2 = array();
Тоже неуданый момент, 2 однотипных переменнх, непонятно чем отличающихся.
В общем, давай, упрощай код, пока его читать очень тяжело и алгоритм переусложнен.
> Ведь пейсателям документации было достаточно нашкрябать одну несчастную страничку с толковыми объяснениями
если они каждому будут по страничке писать, им некогда будет писать код наверно. хотя краткая UML-диаграмма для архитектуры бы не помешала, но ты в приницпе и сам можешь ее построить потратив пару часов.
> Так перекат ведь скоро. Или опять будешь тянуть до 1000 постов?
надо бы ответить анончикам сначала
> Возможно позже я разберусь с полнотекстовым поиском в mysql 5.6 и перепишу под него
не стоит, сфинкс умеет делать полнотекстовый поиск лучше.
> 1. Бьем текст на слова preg_split по \W
Числа и подчеркивания - тоже слова? И наоборот, don't или слово с дефисом разобьет на части.
> 2. В цикле запрашиваем каждое слово в сфинксе where match
да ну, неэффективно. Почему не сразу все?
> 4. Так как набор большой, и вернутся слова не встречающиеся в тексте,
как так? Не должно быть такого. Алсо strpos никак не учитывает стемминг и регистр
> Даже эти мусорные слова в принципе нужно тоже перевести, а не игнорить полностью.
А еще надо подумать что делать с словами без перевода, например the, an, don't, am, is
> 6. Поэтому для ненайденных слов (если сфинкс вернул 0 записей), отправляем запрос к mysql на точное совпадение select id from dictionary where keyword = 'water'.
Не понимаю смысла. Если сфинкс не нашел слово то его и в бД не будет. Не надо исключать нормальные слова. Список стоп слов проще найти в сети и отредактирвоать руками в данном случае.
> например есть фраза `take off`, как можно догадаться оба слова являются стоп-словами, `take` встречается в 569 записях, `off` 1626.
Вот это конечно пугает.
> Даже при пороге 100 для стоп-слов думает 8 секунд.
Надо это делать только один раз, при загрузке текста, и кешировать результаты. Кешировать можно либо в БД либо в какой-нибудт редис (Бд по моему удобнее тем что можно завернуть все это в транзакцию и вообще надежнее хранит). Можно даже в какой-нибудь сериализованной форме.
> Теперь придется пердолиться с построением очереди, ага
gearman глянь.
> Ведь пейсателям документации было достаточно нашкрябать одну несчастную страничку с толковыми объяснениями
если они каждому будут по страничке писать, им некогда будет писать код наверно. хотя краткая UML-диаграмма для архитектуры бы не помешала, но ты в приницпе и сам можешь ее построить потратив пару часов.
> Так перекат ведь скоро. Или опять будешь тянуть до 1000 постов?
надо бы ответить анончикам сначала
> Возможно позже я разберусь с полнотекстовым поиском в mysql 5.6 и перепишу под него
не стоит, сфинкс умеет делать полнотекстовый поиск лучше.
> 1. Бьем текст на слова preg_split по \W
Числа и подчеркивания - тоже слова? И наоборот, don't или слово с дефисом разобьет на части.
> 2. В цикле запрашиваем каждое слово в сфинксе where match
да ну, неэффективно. Почему не сразу все?
> 4. Так как набор большой, и вернутся слова не встречающиеся в тексте,
как так? Не должно быть такого. Алсо strpos никак не учитывает стемминг и регистр
> Даже эти мусорные слова в принципе нужно тоже перевести, а не игнорить полностью.
А еще надо подумать что делать с словами без перевода, например the, an, don't, am, is
> 6. Поэтому для ненайденных слов (если сфинкс вернул 0 записей), отправляем запрос к mysql на точное совпадение select id from dictionary where keyword = 'water'.
Не понимаю смысла. Если сфинкс не нашел слово то его и в бД не будет. Не надо исключать нормальные слова. Список стоп слов проще найти в сети и отредактирвоать руками в данном случае.
> например есть фраза `take off`, как можно догадаться оба слова являются стоп-словами, `take` встречается в 569 записях, `off` 1626.
Вот это конечно пугает.
> Даже при пороге 100 для стоп-слов думает 8 секунд.
Надо это делать только один раз, при загрузке текста, и кешировать результаты. Кешировать можно либо в БД либо в какой-нибудт редис (Бд по моему удобнее тем что можно завернуть все это в транзакцию и вообще надежнее хранит). Можно даже в какой-нибудь сериализованной форме.
> Теперь придется пердолиться с построением очереди, ага
gearman глянь.
> Не понятно. Я явно должен передавать в метод аргументы? В классе нельзя их вызвать через собственные методы? Или же тебе не понравился метод getLastInsertID? Если да, то чем?
Тем что с базой у нас работает только маппер. Это не задача класса авторизации определять какой id был последним вставлен в базу. И то что сейчас мне тоже не нравится. Этот код
> $gateway->getLastInsertID()
работает исправно только если перед этим была выплнена вставка. от нее надо избавиться - можно чтобы метод вставки получал и возвращал этот id, а лучше чтобы он его прописывал в абитуриента.
Вот почитай про побочные эффекты: https://github.com/codedokode/pasta/blob/master/good-code.md#Избегай-побочных-эффектов
Вообще, я мельком глянул и нашел еще сомнительные места:
> https://github.com/greenTea242/Student-list/blob/master/src/Authorization.php#L32
Весь этот класс какой-то мутный. Вот например функция logIn. Судя по названию, она получает на вход данные о студенте и залогинивает его. Но если посмотреть в код, там вообще все совсем другое. Она создает токены. ищет студентов в Бд и делает еще непонятно что. Зачем ты ищешь студента в БД если ты на вход уже получил id и токен?
Хуже, в одном случае она возаращет из базы полноценного заполненного студента, а в другом какой-то огрызок где заполнено только одно поле. Что это за недочеловек без имени, фамилии и номера группы?
Можешь объяснить кратко словами:
- назначение функции
- что она получает на вход
- как рабоатет
Я не понимаю. И вообще, давай посмотрим на то какие публичные методы есть у класса авторизации:
> public function setAbiturientIDToCookie($abiturientID)
> public function setTokenToCookie($token)
Тут уже возникает вопрос почему эти функции отдельные и какой смысл ставить одну куку, не поставив другую?
> public function checkTokenForCSRF($token, $cookieToken)
Непонятно какое отношение имеет защита форм от CSRF к авторизации? по моему абсолютно никакого. Это 2 разных задачи и ими занимаются 2 разных класса.
> public function logIn($abiturientID, $token)
Метод странный, написал выше.
> public function createToken()
Непонятно зачем этот метод выставлн наружу. Я думал задача класса авторизации авторизовывать, разлогинивать студентов и проверять авторизацию.
> public function logOut()
Единственный адекватно выглядящий метод.
Когда ты решаешь задачу по ООП, тебе надо решить за что отвечает тот или иной класс, и исходя из этого, какие у него должны быть публичные поля и методы. Ну если класс отвечает за авторизацию, какие у нег омогут быть методы?
- загогиниться как студент (то есть выставить нужные куки)
- разлогиниться
- получить информацию о залогиненном пользователе (то есть прочитть куки и поискать в базе)
Ну или как должно быть по твоему?
давай учиться распределять зоны ответственности по классам. Чтобы каждый отвечал только за свою часть и была четко определенная зона ответственности. Я советую в будущем перед тем как писать класс, написать перед ним за что он будет отвечать - может это поможет тебе привести мысли в порядок.
Насчет этого:
https://github.com/greenTea242/Student-list/blob/master/src/ViewHelper.php#L32
Ты поставил break, это предотвратит замену тегов. Но это имеет тот недостаток что подсвечивается только первое слово. Надо заменять все слова параллелно. preg_replace по моему позволяет принять на вход массив выражений и массив замен, а также есть мега-функция preg_replace_callback. Оба этих способа дают возможность заменять за раз несколько разных слов в исходном тексте.
https://github.com/greenTea242/Student-list/blob/master/src/ini.php#L11
советую вообще не использовать DOCUMENT_ROOT
> Дурацкий GUI гитхаб, я не понимаю почему изменения в одних файлах отображаются, а в других, где они точно были, нет
ты их мог не закоммитить. Коммит коммитит только изменения, добавленные в него явно. Почитай git-book. Ты вообще зря делаешь что используешь ГУи не работав с командной строкой. Ты на любой проблеме будешь спотыкаться, делать ошибки, нгекоторые вообще репозиторий сломать умудряются. Чтобы выучить основы работы, достаточно один-два дня выделить на гит.
> Не понятно. Я явно должен передавать в метод аргументы? В классе нельзя их вызвать через собственные методы? Или же тебе не понравился метод getLastInsertID? Если да, то чем?
Это не задача класса авторизации работать с базой + он работает только если перед этим была вызвана вставка в БД, то есть чуть реже чем никогда.
Кого залогинивает метод залогинивания? Студента. Ну так и передавай его модель в аргументы.
разберись пока с этим, потом остальное посмотрим.
> Не понятно. Я явно должен передавать в метод аргументы? В классе нельзя их вызвать через собственные методы? Или же тебе не понравился метод getLastInsertID? Если да, то чем?
Тем что с базой у нас работает только маппер. Это не задача класса авторизации определять какой id был последним вставлен в базу. И то что сейчас мне тоже не нравится. Этот код
> $gateway->getLastInsertID()
работает исправно только если перед этим была выплнена вставка. от нее надо избавиться - можно чтобы метод вставки получал и возвращал этот id, а лучше чтобы он его прописывал в абитуриента.
Вот почитай про побочные эффекты: https://github.com/codedokode/pasta/blob/master/good-code.md#Избегай-побочных-эффектов
Вообще, я мельком глянул и нашел еще сомнительные места:
> https://github.com/greenTea242/Student-list/blob/master/src/Authorization.php#L32
Весь этот класс какой-то мутный. Вот например функция logIn. Судя по названию, она получает на вход данные о студенте и залогинивает его. Но если посмотреть в код, там вообще все совсем другое. Она создает токены. ищет студентов в Бд и делает еще непонятно что. Зачем ты ищешь студента в БД если ты на вход уже получил id и токен?
Хуже, в одном случае она возаращет из базы полноценного заполненного студента, а в другом какой-то огрызок где заполнено только одно поле. Что это за недочеловек без имени, фамилии и номера группы?
Можешь объяснить кратко словами:
- назначение функции
- что она получает на вход
- как рабоатет
Я не понимаю. И вообще, давай посмотрим на то какие публичные методы есть у класса авторизации:
> public function setAbiturientIDToCookie($abiturientID)
> public function setTokenToCookie($token)
Тут уже возникает вопрос почему эти функции отдельные и какой смысл ставить одну куку, не поставив другую?
> public function checkTokenForCSRF($token, $cookieToken)
Непонятно какое отношение имеет защита форм от CSRF к авторизации? по моему абсолютно никакого. Это 2 разных задачи и ими занимаются 2 разных класса.
> public function logIn($abiturientID, $token)
Метод странный, написал выше.
> public function createToken()
Непонятно зачем этот метод выставлн наружу. Я думал задача класса авторизации авторизовывать, разлогинивать студентов и проверять авторизацию.
> public function logOut()
Единственный адекватно выглядящий метод.
Когда ты решаешь задачу по ООП, тебе надо решить за что отвечает тот или иной класс, и исходя из этого, какие у него должны быть публичные поля и методы. Ну если класс отвечает за авторизацию, какие у нег омогут быть методы?
- загогиниться как студент (то есть выставить нужные куки)
- разлогиниться
- получить информацию о залогиненном пользователе (то есть прочитть куки и поискать в базе)
Ну или как должно быть по твоему?
давай учиться распределять зоны ответственности по классам. Чтобы каждый отвечал только за свою часть и была четко определенная зона ответственности. Я советую в будущем перед тем как писать класс, написать перед ним за что он будет отвечать - может это поможет тебе привести мысли в порядок.
Насчет этого:
https://github.com/greenTea242/Student-list/blob/master/src/ViewHelper.php#L32
Ты поставил break, это предотвратит замену тегов. Но это имеет тот недостаток что подсвечивается только первое слово. Надо заменять все слова параллелно. preg_replace по моему позволяет принять на вход массив выражений и массив замен, а также есть мега-функция preg_replace_callback. Оба этих способа дают возможность заменять за раз несколько разных слов в исходном тексте.
https://github.com/greenTea242/Student-list/blob/master/src/ini.php#L11
советую вообще не использовать DOCUMENT_ROOT
> Дурацкий GUI гитхаб, я не понимаю почему изменения в одних файлах отображаются, а в других, где они точно были, нет
ты их мог не закоммитить. Коммит коммитит только изменения, добавленные в него явно. Почитай git-book. Ты вообще зря делаешь что используешь ГУи не работав с командной строкой. Ты на любой проблеме будешь спотыкаться, делать ошибки, нгекоторые вообще репозиторий сломать умудряются. Чтобы выучить основы работы, достаточно один-два дня выделить на гит.
> Не понятно. Я явно должен передавать в метод аргументы? В классе нельзя их вызвать через собственные методы? Или же тебе не понравился метод getLastInsertID? Если да, то чем?
Это не задача класса авторизации работать с базой + он работает только если перед этим была вызвана вставка в БД, то есть чуть реже чем никогда.
Кого залогинивает метод залогинивания? Студента. Ну так и передавай его модель в аргументы.
разберись пока с этим, потом остальное посмотрим.
gearman посмотри. Крон имеет недостаток что запускается раз в минуту. Тебе же наверно нужен демон который постоянно будет работать, а не по крону.
> Вопрос в том, как сделать так чтобы несколько скриптов одновременно не начали обрабатывать одну и ту же запись.
самый простой способ иметь только один экземпояр демона. Но он не сможет загрузить больше 1 ядра проуессора. для большого числа демонов надо сделать блокировку - демон берет задачу, помечая ее своим идентификатором, напрмиер PID и временем взятия блокировки.
> Вспомнилась блокировка "select for update". А как она работает?
А погугли. Это кстати не такой и плохой враиант, но как я понимаю там недостаток что такие зблокированные записи нельзя исключать из поиска, вместо этого все блокируются при попытке чтения и эксклюзивной блокировке.
> или будет висеть и ждать пока освободятся?
ждать если блокировка эксклюивная (на запись), ничего если она на чтение так как читатель не блокирует читателя.
> А если заблоченные записи будут вообще удалены? Как отреагирует то соединение, которое ждет снятия блокировки?
нельзя удалить заблокированную запись.
> Но что будет если скрипт помрет?
надо ставить время начала работы и высчитывть таймаут. Также имея PID можно проверить жив ли скрипт через kill если мы на одном сервере. Также есть другие варианты, например
Прежде чем разбираться с блокировками, надо понять зачем они придуманы.
Кусочек из моей большой пасты про мультиверсионность, устройство InnoDB и блокировки
-------------
MySQL все равно блокирует строки, которые изменяются или удаляются. Это делается для того чтобы 2 транзакции не могли сделать противоречащие изменения (например записать разные значения в одну и ту же строку) и нарушить ACID. MySQL делает блокировки как минимум в таких случаях:
- есть 2 писателя (UPDATE/DELETE) для 1 и той же строки
- есть писатель + читатель (SELECT) для 1 и той же строки
В этих случаях доступ к строке получает только первый захвативший блокировку, а второй будет ждать пока она освободится. Блокировки снимаются при коммите транзакции. Если их не делать то нарушается изоляция транзакций.
https://dev.mysql.com/doc/refman/5.0/en/innodb-transaction-model.html
http://webew.ru/articles/1383.webew
В особо тяжелых слуаях можно словить взаимную блокировку - deadlock:
https://ru.wikipedia.org/wiki/Взаимная_блокировка
Например транзакция A хочет апдейтить строку 1, за ней строку 2. Транзакия B хочет апдейтить сначала 2, потом 1. Каждая их них захватит блокировку по одной строке и никогда не получит блокировку по второй. MySQL обнаруживает дедлоки и решает их принудительным откатом одной из транзакций.
---------------
SELECT FOR UPDATE это примерно то же самое, только с явной блокировкой выбранных строк на запись. Есть еще SELECT ... LOCK IN SHARED MODE.
gearman посмотри. Крон имеет недостаток что запускается раз в минуту. Тебе же наверно нужен демон который постоянно будет работать, а не по крону.
> Вопрос в том, как сделать так чтобы несколько скриптов одновременно не начали обрабатывать одну и ту же запись.
самый простой способ иметь только один экземпояр демона. Но он не сможет загрузить больше 1 ядра проуессора. для большого числа демонов надо сделать блокировку - демон берет задачу, помечая ее своим идентификатором, напрмиер PID и временем взятия блокировки.
> Вспомнилась блокировка "select for update". А как она работает?
А погугли. Это кстати не такой и плохой враиант, но как я понимаю там недостаток что такие зблокированные записи нельзя исключать из поиска, вместо этого все блокируются при попытке чтения и эксклюзивной блокировке.
> или будет висеть и ждать пока освободятся?
ждать если блокировка эксклюивная (на запись), ничего если она на чтение так как читатель не блокирует читателя.
> А если заблоченные записи будут вообще удалены? Как отреагирует то соединение, которое ждет снятия блокировки?
нельзя удалить заблокированную запись.
> Но что будет если скрипт помрет?
надо ставить время начала работы и высчитывть таймаут. Также имея PID можно проверить жив ли скрипт через kill если мы на одном сервере. Также есть другие варианты, например
Прежде чем разбираться с блокировками, надо понять зачем они придуманы.
Кусочек из моей большой пасты про мультиверсионность, устройство InnoDB и блокировки
-------------
MySQL все равно блокирует строки, которые изменяются или удаляются. Это делается для того чтобы 2 транзакции не могли сделать противоречащие изменения (например записать разные значения в одну и ту же строку) и нарушить ACID. MySQL делает блокировки как минимум в таких случаях:
- есть 2 писателя (UPDATE/DELETE) для 1 и той же строки
- есть писатель + читатель (SELECT) для 1 и той же строки
В этих случаях доступ к строке получает только первый захвативший блокировку, а второй будет ждать пока она освободится. Блокировки снимаются при коммите транзакции. Если их не делать то нарушается изоляция транзакций.
https://dev.mysql.com/doc/refman/5.0/en/innodb-transaction-model.html
http://webew.ru/articles/1383.webew
В особо тяжелых слуаях можно словить взаимную блокировку - deadlock:
https://ru.wikipedia.org/wiki/Взаимная_блокировка
Например транзакция A хочет апдейтить строку 1, за ней строку 2. Транзакия B хочет апдейтить сначала 2, потом 1. Каждая их них захватит блокировку по одной строке и никогда не получит блокировку по второй. MySQL обнаруживает дедлоки и решает их принудительным откатом одной из транзакций.
---------------
SELECT FOR UPDATE это примерно то же самое, только с явной блокировкой выбранных строк на запись. Есть еще SELECT ... LOCK IN SHARED MODE.
> 2. В цикле запрашиваем каждое слово в сфинксе where match
> да ну, неэффективно. Почему не сразу все?
Потому что только слово "of" вернет 16 000 записей, сфинкс такое не осилит.
Я так сначала и сделал, пришлось увеличивать max_matches до 10000, потом понял что и этого мало, и вообще делаю что-то неправильно.
Еще раз говорю: в словаре 500 тысяч слов и словосочетаний. Поэтому при запросе некоторого слова находит все записи, в которых оно встречается.
Допустим, у меня есть текст "He was neither fish nor flesh that time."
Поиск по слову "fish" вернет "fishing", "neither fish nor flesh", "be a fish out of water" и еще если не ошибаюсь 265 вариантов. Я ставлю перед собой задачу показать пользователю перевод крылатой фразы "neither fish nor flesh", значит придется перебирать через strpos все 265 вариантов.
Но если этих вариантов 5-15 тысяч, то это уже ни в какие ворота не лезет. Такие слова нужно удалять из индекса сфинкса.
Поиск по слову "of" вообще ничего не должен находить, в таком случае я полезу в mysql и достану оттуда точное совпадение where keyword = 'of'.
Такой алгоритм.
Остается только вопрос, какой порог считать стоп-словом? Допустим, я выбрал 100 повторений.
Тогда скорость относительно неплохая, я там слегка оптимизировал, получилось уже 5 секунд обрабатывает большой текст из ~1000 слов.
Но это мне кажется все равно много, чтобы делать это именно при загрузке текста. К тому же понижение порога стоп-слов приведет к потере качества перевода, потому
что не будет находить словосочетания "get out", "take off" и тому подобные.
Кстати надо будет погуглить, что-то такое слышал про проблему "to be or not to be" при индексации (пользователь ищет фразу, полностью состоящую из стоп-слов), не могу вспомнить.
Так что наверное будет действительно повысить порог ~700 и выполнять обработку текста отдельно от загрузки.
Я кстати не знал что кроном можно не чаще раз в минуту запускать.
Gearman выглядит интересно, ок.
> 6. Поэтому для ненайденных слов (если сфинкс вернул 0 записей), отправляем запрос к mysql на точное совпадение select id from dictionary where keyword = 'water'.
> Не понимаю смысла. Если сфинкс не нашел слово то его и в бД не будет. Не надо исключать нормальные слова. Список стоп слов проще найти в сети и отредактирвоать руками в данном случае.
Дык я залил такой словарь, что в нем даже поиск по "нормальным" словам возвращает по 500-1000 записей.
water 2244
pressure 1561
surface 1229
>Если сфинкс не нашел слово то его и в бД не будет.
Чего это? Я же говорю из индекса сфинкса можно удалить некоторые слова. Ну вернее при индексации игнорировать слова из указанного файла.
Этот файл можно получить при помощи утилит того же сфинкса. Из базы данных оно никуда не денется. Я его только из индекса сфинкса удаляю, чтоб не засоряло поиск.
Ну ладно, раз лучших вариантов нет, на этом и остановимся.
Классная вещь ООП кстати, я тут обнаружил, что мне вообще не нужно переделывать ни строчки кода, для того чтобы написать консольный скрипт.
Нужно только инициализировать доктрину (уже есть, файл bootstrap.php, подключаем его), создаем TextService с его зависимостями (TextParser, SphinxService, EntityManager) и вызываем метод save.
https://github.com/nsdvw/Memtext/blob/master/app/Service/TextService.php#L25-L33
Круто, да?
> 2. В цикле запрашиваем каждое слово в сфинксе where match
> да ну, неэффективно. Почему не сразу все?
Потому что только слово "of" вернет 16 000 записей, сфинкс такое не осилит.
Я так сначала и сделал, пришлось увеличивать max_matches до 10000, потом понял что и этого мало, и вообще делаю что-то неправильно.
Еще раз говорю: в словаре 500 тысяч слов и словосочетаний. Поэтому при запросе некоторого слова находит все записи, в которых оно встречается.
Допустим, у меня есть текст "He was neither fish nor flesh that time."
Поиск по слову "fish" вернет "fishing", "neither fish nor flesh", "be a fish out of water" и еще если не ошибаюсь 265 вариантов. Я ставлю перед собой задачу показать пользователю перевод крылатой фразы "neither fish nor flesh", значит придется перебирать через strpos все 265 вариантов.
Но если этих вариантов 5-15 тысяч, то это уже ни в какие ворота не лезет. Такие слова нужно удалять из индекса сфинкса.
Поиск по слову "of" вообще ничего не должен находить, в таком случае я полезу в mysql и достану оттуда точное совпадение where keyword = 'of'.
Такой алгоритм.
Остается только вопрос, какой порог считать стоп-словом? Допустим, я выбрал 100 повторений.
Тогда скорость относительно неплохая, я там слегка оптимизировал, получилось уже 5 секунд обрабатывает большой текст из ~1000 слов.
Но это мне кажется все равно много, чтобы делать это именно при загрузке текста. К тому же понижение порога стоп-слов приведет к потере качества перевода, потому
что не будет находить словосочетания "get out", "take off" и тому подобные.
Кстати надо будет погуглить, что-то такое слышал про проблему "to be or not to be" при индексации (пользователь ищет фразу, полностью состоящую из стоп-слов), не могу вспомнить.
Так что наверное будет действительно повысить порог ~700 и выполнять обработку текста отдельно от загрузки.
Я кстати не знал что кроном можно не чаще раз в минуту запускать.
Gearman выглядит интересно, ок.
> 6. Поэтому для ненайденных слов (если сфинкс вернул 0 записей), отправляем запрос к mysql на точное совпадение select id from dictionary where keyword = 'water'.
> Не понимаю смысла. Если сфинкс не нашел слово то его и в бД не будет. Не надо исключать нормальные слова. Список стоп слов проще найти в сети и отредактирвоать руками в данном случае.
Дык я залил такой словарь, что в нем даже поиск по "нормальным" словам возвращает по 500-1000 записей.
water 2244
pressure 1561
surface 1229
>Если сфинкс не нашел слово то его и в бД не будет.
Чего это? Я же говорю из индекса сфинкса можно удалить некоторые слова. Ну вернее при индексации игнорировать слова из указанного файла.
Этот файл можно получить при помощи утилит того же сфинкса. Из базы данных оно никуда не денется. Я его только из индекса сфинкса удаляю, чтоб не засоряло поиск.
Ну ладно, раз лучших вариантов нет, на этом и остановимся.
Классная вещь ООП кстати, я тут обнаружил, что мне вообще не нужно переделывать ни строчки кода, для того чтобы написать консольный скрипт.
Нужно только инициализировать доктрину (уже есть, файл bootstrap.php, подключаем его), создаем TextService с его зависимостями (TextParser, SphinxService, EntityManager) и вызываем метод save.
https://github.com/nsdvw/Memtext/blob/master/app/Service/TextService.php#L25-L33
Круто, да?
Но если вот такое в папку кладешь, то файлы (стили, бутстрап) не загружаются на сайт.
<IfModule authz_core_module>
Require all denied
</IfModule>
<IfModule !authz_core_module>
Deny from all
</IfModule>
Юнит тесты != интеграционные. Юнит тесты покрывают все классы, занимают много времени при написании, интеграционные тестируют работу конкретных фич в боевых условиях, взаимодействие всех модулей. Для критических вещей интеграционный тест по-любому пишут, это быстро, тестов всего пара штук, и сразу придает надежности всей системе.
Конечно не загружаются, ты же доступ со всех ip блокируешь. Юзай RedirectMatch 404 с масками нужного файла и папок.
Ну и нахуй я этим занимался тогда, если это правда?
Впизду, лучше бы в игры играл и пивас пил, чем так тупо прожигать время а теперь и стул.
Идите нахуй короче.
Не знаю, над чем смеяться - то ли над тем, что пхп-ебарь сравнил пхп с крестами, или над тем, что он так уверовал в вечность бэкенда.
Не пизди, меня взяли верстаком после джуниора на пхп, даже зарплату больше дали. У верстаков оказалось заказов больше.
Да я, собственно, не имел цели лгать, просто сидишь ты такой, верстаешь очередной макет, ну и вдруг читаешь, что верстальщики вымерли как профессия.
Гуглишь - везде одно и то же. Ненужны, устарели, "лучше костыли от фротенд-разраба", "вон цмски какие пошли" и всё в таком духе.
И складывается такое ощущение, что я неплохо так проебал время за дрочевом никому, как оказывается, ненужного занятия.
Ну и руки чет опускаются так медленно но верно.
Ну такое там было. Когда я выкатывал на рабочий, оно работало и пару месяцев позже тоже.
То битрикс был. У него есть своя система запуска скриптов без крона, когда они срабатывают от запросов пользователей и когда переменная $_SERVER в тех скриптах есть.
Вот. Однажды ту систему перевели на крон и мое поделие отвалилось. Несколько дней товары выгружались с битыми ссылками, потеряли много денег. Такое вот гавно.
Верстальщики всё равно нужны. Даже взяв левую цмску + готовую тему под неё приходится что-то подгонять-менять, а если ещё цмска допиливалась то и тем более.
Переделал задачу про Йоду.
http://ideone.com/oJEi0M
Сумма прописью. Все работает, но выдает ошибки, которые я не знаю как убрать.
http://ideone.com/Gq8ePe
Проблема скорее всего в функции zeroinNumbers
- посмотри в одном случае она просто ничего не возвращает, при этом ты используешь результат её выполнения как индекс массива
https://ideone.com/mx0f9r
https://ideone.com/yWzQjh
https://ideone.com/kI5NTV
Что-то не могу найти.
http://php.net/manual/ru/language.oop5.constants.php
В мануале symfony form
http://symfony.com/doc/current/components/form/introduction.html#creating-a-simple-form
приводится такой вызов
TextType::class
и
DateType::class
Это что такое? Я знаю только волшебную __CLASS__.
Полазил по исходникам, может они там что-то нахимичили
тут нет
https://github.com/symfony/form/blob/master/Extension/Core/Type/TextType.php
и тут нет
https://github.com/symfony/form/blob/master/AbstractType.php
и тут ничего подозрительного
https://github.com/symfony/form/blob/master/FormTypeInterface.php
Откуда берется xxx::class?
-------------------------------
update:
Еле нашел.
http://php.net/manual/ru/migration55.new-features.php#migration55.new-features.class-name
Тут читай
http://php.net/manual/en/language.oop5.basic.php
>Since PHP 5.5, the class keyword is also used for class name resolution. You can get a string containing the fully qualified name of the ClassName class by using ClassName::class. This is particularly useful with namespaced classes.
>Nastachku.ru
Прочитал как Наташку.ру, обдвачевался.
А так забавно, конечно: евангелист ментального программирования - не программист сам будет.
Я Котерова 200 страниц прочитал, мне хватило для зажигания.
Так там с 198 страницы только начинается Hello world
Я вот делаю редирект и у меня стили вообще вырубаются.
Вот есть у меня карта myMap.
И есть метка, с координатами coords;
я делаю:
myMap.setCenter(coords,16);
Карта центрируется на метке.
А потом делаю:
myMap.balloon.open(myMap.getCenter() );
И открывается пустой баллун! Но ведь у этой метки есть содержимое балуна! Если я клику мышкой по метке — покажется баллун с содержимым!
Что я делаю не так?
Но пыха действительно неплохой язык с хорошими возможностями.
да
Спасибо.
$result = mb_strtoupper(mb_substr($text, 0, 1)) . mb_substr($text, 1);
return $result;
}
Аноны, return заменяет $text на $result?
>return заменяет $text на $result?
Чё-т в голос.
Куда ты торопишься, пройди уроки перед этим, лалка.
Аргумент, который мы передаём в функцию, используется внутри неё. А результат мы можем вывести какой угодно, return просто выводит то, что нам нужно в данном случае.
Нет. return ничего не заменяет, он просто возвращает значение туда, откуда была вызвана функция.
В твоем случае чтобы получить значение которое возвращает return нужно будет написать
$var = makeFirstletterUppercase($text);
Это понятно, что нужно вызвать функцию, но когда вызову, то что возвращат return заменит $text?
Нет, $text не изменится.
Пишу в студентах страничку формы. Отрывок:
$errors = $formHelper->getError($post); // Собираю массив с ошибками
if(!$errors) {
$student = $tdg->fillField(new Student, $post); // Заполняю модель
$tdg->add($student);
// Записываю
}
Оно верно? Может какой нибудь хелпер должен заполнять модель? Как это правильно делается?
И я тут еще один стул придумал - создать метод в контроллере и им заполнять. Может так лучше будет?
> $student = $tdg->fillField(new Student, $post); // Заполняю модель
Странный код. TDG отвечает за работу с базой данных, с какой стати там метод заполнения объекта? Это не его зона отвественности. Каждый класс должен заниматься своим делом.
Также, ты сделал белый список полей которые можно обновлять? Если не делать его то будет уязвимость, что можно любые поля из POST перезаписать.
>>726888
Можно и так и так
Засунул все formhelper
>Также, ты сделал белый список полей которые можно обновлять?
Ага, и это тоже туда засунул. До этого в контроллере держал.
>Можно и так и так
Действительно. Странно что массив, хоть и пустой, равен фолс.
Потому что там массив - это объект. А объект всегда приравнивается к true. А в PHP массив это особый тип данных, живущий по своим правилам.
http://ideone.com/tQTjy9
>Undefined property: Manager::$rank in /home/7kZUHc/prog.php on line 65
private не наследуется, для этого есть protected
>Undefined variable: baseSalary in /home/7kZUHc/prog.php on line 39
тут $this забываешь
Да, это я поправил. Поменял свойство всех полей на паблик, вдруг из-за нехватки доступа было это. Но всё равно в поля менеджеру ничего не пишет. Говорит что поля нулл, хотя оно забито в самом классе.
http://ideone.com/wLsCfJ
А, всё, нашел где проебался.
http://ideone.com/m5Ho3A
Но вот вопрос, теперь мне надо проверить
>соответствует ли твой код принципам ООП? Гибок ли он и легко ли поддается изменениям?
И уже я вижу проблему, потому что я записывал всех работников и руководителей в один массив внутри класса Департамент. То есть условия изменились и мне придётся переписывать довольно много кода. И вот вопрос, как лучше всего складывать данные в класс, так чтобы они могли удовлетворять любым новым условиям?
по моему тут ООП не требуется так как по сути нужна одна функция. Ты перемудрил.
> public function __construct(array $data)
да ну, неудачный подход по моему так как не проверяется есть такое поле или нет, и передается массив непонятного формата. как мне понять какие в нем должны быть поля? Это какое-то массиво-ориентированное программирование. Нельзя без чтения кода понять что туда надо передавать, что обязательно а что нет.
Тогда уж лучше было бы просто сделать либо поля публичными, либо описать аргументы явно:
construct($servicePayment, $percent, ...)
> public function getValue($value)
То же самое. Это не ООП, а массиво-ориентированное программирование так как непонятно какие значения можно передавать. Надо сделать нормальные методы-геттеры.
Тем более неправильно возвращать при ошибке строку 'No such element here' как будто бы ничего не случилось. Ты что, после каждого вызова getValue будешь писать иф и проверять не вернули ли нам это строку? При ошибке надо выбрасвать исключение.
Также, думаю, что метод render тут лишний - класс должен возвращать значения по отдельности, а не строку. Как из твоего класса например получить сколько месяцев прошло? Ты смешиваешь 2 функции в одном классе - расчет кредита и вывод текста, а тут явно второе не требуется.
Думаю, должно быть либо как-то так:
$calc = new Calculator($credit, $servicePayment, ...);
$months = $calc->getTotalMonths();
$paid = $calc->getTotalPaid();
либо как-то так:
$calc = new Calculator($servicePayment, ...);
$creditParams = $calc->calculate($credit);
либо так:
$calc = new Calculator();
$creditParams = $calc->calcluate($credit, $servicePayment, ...);
Второй и третий подход позволяет одним объектом посчитать несколько кредитов.
> if ($this->credit >= $this->monthlyPayment) {
> $this->credit -= $this->monthlyPayment;
Тут можно исплоьзловать min вместо if
> return $this->totalMonths < 1000
Явное лучше неявного. Надо сделать поле с фактом ошибки или бросать исключение, а не полагаться на нигде не прописанную договореннсоть что если прошло больше 1000 месяцев то это ошибка. И кстати число 1000 скопипастено 2 раза что нарушает принцип DRY. Это число должно быть в поле или переменной или константе с понятным названием.
по моему тут ООП не требуется так как по сути нужна одна функция. Ты перемудрил.
> public function __construct(array $data)
да ну, неудачный подход по моему так как не проверяется есть такое поле или нет, и передается массив непонятного формата. как мне понять какие в нем должны быть поля? Это какое-то массиво-ориентированное программирование. Нельзя без чтения кода понять что туда надо передавать, что обязательно а что нет.
Тогда уж лучше было бы просто сделать либо поля публичными, либо описать аргументы явно:
construct($servicePayment, $percent, ...)
> public function getValue($value)
То же самое. Это не ООП, а массиво-ориентированное программирование так как непонятно какие значения можно передавать. Надо сделать нормальные методы-геттеры.
Тем более неправильно возвращать при ошибке строку 'No such element here' как будто бы ничего не случилось. Ты что, после каждого вызова getValue будешь писать иф и проверять не вернули ли нам это строку? При ошибке надо выбрасвать исключение.
Также, думаю, что метод render тут лишний - класс должен возвращать значения по отдельности, а не строку. Как из твоего класса например получить сколько месяцев прошло? Ты смешиваешь 2 функции в одном классе - расчет кредита и вывод текста, а тут явно второе не требуется.
Думаю, должно быть либо как-то так:
$calc = new Calculator($credit, $servicePayment, ...);
$months = $calc->getTotalMonths();
$paid = $calc->getTotalPaid();
либо как-то так:
$calc = new Calculator($servicePayment, ...);
$creditParams = $calc->calculate($credit);
либо так:
$calc = new Calculator();
$creditParams = $calc->calcluate($credit, $servicePayment, ...);
Второй и третий подход позволяет одним объектом посчитать несколько кредитов.
> if ($this->credit >= $this->monthlyPayment) {
> $this->credit -= $this->monthlyPayment;
Тут можно исплоьзловать min вместо if
> return $this->totalMonths < 1000
Явное лучше неявного. Надо сделать поле с фактом ошибки или бросать исключение, а не полагаться на нигде не прописанную договореннсоть что если прошло больше 1000 месяцев то это ошибка. И кстати число 1000 скопипастено 2 раза что нарушает принцип DRY. Это число должно быть в поле или переменной или константе с понятным названием.
Хорошо, только имена функций пишутся с маленькой буквы и с глагола: сделатьЧтоТо, например посчитатьКредит()
>>720692
не знаю
>>720720
не знаю
>>720821
> сейчас я для его получения использую
> CHtml::beginForm();
> CHtml::endForm();
Это явно неправильно так как должна быть какая-то функция для получения значения токена.
> Так вот, могу ли я вручную прописывать токен в куки, если сервер не найдёт его?
Так тогда защиты не будет. защита основана на том что сервер сверяет токен в ПОСТ данных и в куках. Если ты передаешь токен не через куки, ты должен где-то хранить на сервере его копию чтобы подтвердить что он верный.
Вообще, как-то у тебя все сложно выглядит.
>>721014
Выглядит верно
Хорошо, только имена функций пишутся с маленькой буквы и с глагола: сделатьЧтоТо, например посчитатьКредит()
>>720692
не знаю
>>720720
не знаю
>>720821
> сейчас я для его получения использую
> CHtml::beginForm();
> CHtml::endForm();
Это явно неправильно так как должна быть какая-то функция для получения значения токена.
> Так вот, могу ли я вручную прописывать токен в куки, если сервер не найдёт его?
Так тогда защиты не будет. защита основана на том что сервер сверяет токен в ПОСТ данных и в куках. Если ты передаешь токен не через куки, ты должен где-то хранить на сервере его копию чтобы подтвердить что он верный.
Вообще, как-то у тебя все сложно выглядит.
>>721014
Выглядит верно
Тут не нужны регулярки. Надо разбить строку на массив предложений, у каждого сделать первую букву заглавной и склеить обратно. Ну или если ты хочешь на регулярках, надо использовать preg_replace_callback.
> То есть массив не изменяется после первого прохода цикла.
Поставь var_dump и смотри значения всех переменных на каждом шаге. Код какой-то запутанный и ничего не понять так без отладки.
> $regexp2 = '/ [.] /u';
> $regexp3 = '/ [,] /u';
Эти регулярки можно объединить в одну, работающую и с точкой, и с запятой, и даже с другими символами.
Также, у тебя почему-то скопипастено mb_strtoupper 2 раза. Надо в одном месте программы менять регистр букв, какой смысл делать это 2 раза в разных местах?
также у тебя там ошибка:
> PHP Notice: Undefined offset: 0 in /home/S6DHub/prog.php on line 27
Ставь var_dump и отлаживай.
>>721289
В /wrk или тред перезвонивших с такими вопросами
>>721456
С помощью ифа добавлять css класс к ссылке.
> Причем надо чтобы этот стиль сохранялся пока юзер ходит по сайту.
надо чтобы все УРЛ на странице выводились с учетом языка. Некоторые еще куки исопльзуют, но как бы багов не было.
Тут не нужны регулярки. Надо разбить строку на массив предложений, у каждого сделать первую букву заглавной и склеить обратно. Ну или если ты хочешь на регулярках, надо использовать preg_replace_callback.
> То есть массив не изменяется после первого прохода цикла.
Поставь var_dump и смотри значения всех переменных на каждом шаге. Код какой-то запутанный и ничего не понять так без отладки.
> $regexp2 = '/ [.] /u';
> $regexp3 = '/ [,] /u';
Эти регулярки можно объединить в одну, работающую и с точкой, и с запятой, и даже с другими символами.
Также, у тебя почему-то скопипастено mb_strtoupper 2 раза. Надо в одном месте программы менять регистр букв, какой смысл делать это 2 раза в разных местах?
также у тебя там ошибка:
> PHP Notice: Undefined offset: 0 in /home/S6DHub/prog.php on line 27
Ставь var_dump и отлаживай.
>>721289
В /wrk или тред перезвонивших с такими вопросами
>>721456
С помощью ифа добавлять css класс к ссылке.
> Причем надо чтобы этот стиль сохранялся пока юзер ходит по сайту.
надо чтобы все УРЛ на странице выводились с учетом языка. Некоторые еще куки исопльзуют, но как бы багов не было.
>Требование к тестовому заданию
>Дамп базы данных, результирующее количество записей в выводимой таблице - не менее 1 000 000.
Или я чего-то не понимаю, или от меня ждут таблицу с миллионом строк? Это что шутка?
Ты не можешь написать скрипт для заполнения базы что ли?
Другое дело зачем им это? Может там дальше нужно провести нагрузочные тесты.
Хотя в таком случае достаточно написать скрипт, сам дамп не нужен.
https://www.youtube.com/watch?v=Aw28-krO7ZM&list=PL7A20112CF84B2229
Единственный годный гайд по MVC, который я встречал.
бампану просьбу
/([,;!?:][^ ])|(жы|шы)|([^,] (\bно\b|\bа\b))|(координально|сдесь|здела(л|ю|н))/gu
Так ладно, допустим я все же решил заполнить эту несчастную таблицу данными. Мне так же надо реализовать поиск по каждому стобцу этой таблицы. Значит ли это, что придется добавлять индекс для каждого столбца, чтобы такой поиск на занимал целую вечность. Алсо, доживу ли я вообще до конца выполнения скрипта, который должен вставить миллион записей в таблицу, где все столбцы являются индексами? Я просто никогда не работал с таким огромным объемом данных и с индексами знаком понаслышке.
На моем ноуте можно делать вставку по 1000-2000 строк в секунду так что думаю да.
>>727643
А ты можешь описать сущности "Вопрос", "Варианты ответа", "Введенные ответы"? Тут надо начинать с проектирования базы данных. Какие таблицы, какие поля, какие связи между ними.
Так как вопросы бывают разных типов то перед нами явно наследование таблиц и надо выбрать один из 3 паттернов описаннх тут http://design-pattern.ru/ (Single TI, Class TI, Concrete TI).
Ты бы сначала связался с работодателем и у него спросил, прежде чем делать.
Может оно того вообще не стоит. Может там ни одного компетентного человека, и скажут еще "а типерь выложите на хостинг, что бы мы магли пасматреть))))".
Индексы можно отключать на время массовой вставки.
>Тут надо начинать с проектирования базы данных.
А я что по твоему делаю?
tests
------
id
title
author_id
time_limit
description
questions
-----------
id
content
position
points
answer_type (enum={single, multiple, string, integer})
variants
---------
id
value
is_right (enum={single, multiple, string, number})
Не вижу тут никакого наследования.
Примеры с designpatterns непонятные.
http://design-pattern.ru/patterns/class-table-inheritance.html
Я не играю в крикет и боулинг, поэтому не понимаю о чем там речь.
И не понимаю, как применить это в данном случае с ответами, вопросами или вариантами.
>"Введенные ответы"
Есть еще невведенные? Или это чтобы не путать с вариантами ответов?
>Тут надо начинать с проектирования базы данных.
А я что по твоему делаю?
tests
------
id
title
author_id
time_limit
description
questions
-----------
id
content
position
points
answer_type (enum={single, multiple, string, integer})
variants
---------
id
value
is_right (enum={single, multiple, string, number})
Не вижу тут никакого наследования.
Примеры с designpatterns непонятные.
http://design-pattern.ru/patterns/class-table-inheritance.html
Я не играю в крикет и боулинг, поэтому не понимаю о чем там речь.
И не понимаю, как применить это в данном случае с ответами, вопросами или вариантами.
>"Введенные ответы"
Есть еще невведенные? Или это чтобы не путать с вариантами ответов?
Допустим, Footballer футболист? игрок в настоящий футбол, или в пиндосскую пародию на регби? наследует класс Player.
Как это происходит в Php понятно.
А в реляционной мазе?
По какому полю связаны таблицы Player и Footballer?
Может в Footballer есть еще player_id? Тогда почему этого нет на диаграмме?
Или у них совпадает первичный ключ? Как тогда обеспечить это совпадение? Инсертить одной транзакцией?
Как мне запрос к такой странной схеме писать?
http://ideone.com/96SE5L
Бро, у меня тоже была такая ошибка новичка - мысль, что надо сразу решить огромную хреновину, одним махом.
Но постепенно пришёл к тому, что каждую ерунду стал тестировать, что есть в учебнике ОПа или мануале, чуть изменять, смотреть, как всё работает.
Ну вот это, например, что за задница вообще:
>function yodaText ($text) {
>makeTextlower ($text);
>firstLetterUpper ($text);
>reverseText ($text);
}
Всё, там больше ничего нет...
Где, простите, return или хотя бы echo? Где переменная, с которой ты связываешь работу функции, ты же должен её как-то определить?
Перечитай урок ОПа про функции ещё раз, попробуй небольшие запустить снова. Тут у тебя куча глобальных ошибок, сразу их увидишь и поймёшь.
<?php
error_reporting(-1);
$text = 'Windows';
$words = str_split($text);
foreach ($words as $letter) {
echo "̶$letter";
}
Она выдает сроку с дополнительными симфолами, строку вставляешь вконтакик и получается зачеркнутый текст. Но она не работает с русскими буквами. Куда копать?
Попробуй preg_split, но там нужна регулярка.
<?php
$str = 'string';
$chars = preg_split('//', $str, -1, PREG_SPLIT_NO_EMPTY);
print_r($chars);
?>
Следующие уроки.
Чуть глаза не сломал, пока читал этот пост. Тебе нужна эта функция http://php.net/manual/ru/function.get-class.php я правильно понял?
Да, спасибо.
Так?
<?php
mb_internal_encoding('utf-8');
error_reporting(-1);
$text = 'лололо';
//$words = str_split($text);
$words = preg_split('//', $text, -1, PREG_SPLIT_NO_EMPTY);
//var_dump ($words);
foreach ($words as $letter) {
echo "̶$letter";}
Неа, не работает.
<?php
mb_internal_encoding('utf-8');
error_reporting(-1);
$text = 'лuоuлuоuлuо';
//$words = str_split($text);
$words = preg_split('/u/', $text, -1, PREG_SPLIT_NO_EMPTY);
//var_dump ($words);
foreach ($words as $letter) {
echo "̶$letter";}
Вот так? Так вроде работает.
Это получается мне перед разбивкой на массив нужно будет между буквами пихать разделители?
Ты траллишь так?
Потому что в этом случае есть return и работа функций определена в какие-то переменные.
Я второй день на ПХП.
> Не вижу тут никакого наследования.
А оно есть. Ты почему-то думаешь что все вопросы однотипные, но это не так.
Вот у нас есть разные типы вопросов, ну например один вопрос с вариантами ответов, другой с вводом числа с погрешностью.
Вопрос с вариантами ответов:
id
text
(+ связь 1-М на таблицу с вариантами)
Вопрос с вводом числа:
id
text
answer DECIMAL
deviation DECIMAL (допустимая погрешность)
Видишь, наборы полей разные. Хотя эти 2 вида вопросов имеют много общего, но есть и различия. Тут явно наследование таблиц, и есть 3 паттерна наследования, где мы используем либо одну, либо много таблиц. Ты должен сравнить преимущества и недостатки каждого подхода, с точки зрения удобства вставки и выборки данных например.
Ну и некоторые подходы например позволят на уровне внешних ключей разрешить привязывать варианты ответа только к определенным типам вопросов, некоторые не позволят.
Важно все это понимать.
> Есть еще невведенные? Или это чтобы не путать с вариантами ответов?
ну те ответы которые ввел или выбрал студент при прохождении теста.
>>727700
> По какому полю связаны таблицы Player и Footballer?
по первичному ключу который не показан на схеме (точнее подразумевается так как там есть стрелочки). Там отношение 1:1 между таблицами, ты ведь знаешь как оно реализуется?
Если ты не очень хорошо знаешь БД, возможно тебе стоит глянуть теорию и задачки: https://gist.github.com/codedokode/10539213
Также я могу дать дополнительные задачки по проектированию БД, например один анон у нас тут проектировал БД сервиса продажи музыки, довольно большую.
> Или у них совпадает первичный ключ? Как тогда обеспечить это совпадение? Инсертить одной транзакцией?
Инсертишь одну запись, получаешь ключ и инсертишь вторую. У второй не должно быть автоинкремента. И конечно одной транзакцией.
> Как мне запрос к такой странной схеме писать?
один ответ на все вопросы - джойн.
> Не вижу тут никакого наследования.
А оно есть. Ты почему-то думаешь что все вопросы однотипные, но это не так.
Вот у нас есть разные типы вопросов, ну например один вопрос с вариантами ответов, другой с вводом числа с погрешностью.
Вопрос с вариантами ответов:
id
text
(+ связь 1-М на таблицу с вариантами)
Вопрос с вводом числа:
id
text
answer DECIMAL
deviation DECIMAL (допустимая погрешность)
Видишь, наборы полей разные. Хотя эти 2 вида вопросов имеют много общего, но есть и различия. Тут явно наследование таблиц, и есть 3 паттерна наследования, где мы используем либо одну, либо много таблиц. Ты должен сравнить преимущества и недостатки каждого подхода, с точки зрения удобства вставки и выборки данных например.
Ну и некоторые подходы например позволят на уровне внешних ключей разрешить привязывать варианты ответа только к определенным типам вопросов, некоторые не позволят.
Важно все это понимать.
> Есть еще невведенные? Или это чтобы не путать с вариантами ответов?
ну те ответы которые ввел или выбрал студент при прохождении теста.
>>727700
> По какому полю связаны таблицы Player и Footballer?
по первичному ключу который не показан на схеме (точнее подразумевается так как там есть стрелочки). Там отношение 1:1 между таблицами, ты ведь знаешь как оно реализуется?
Если ты не очень хорошо знаешь БД, возможно тебе стоит глянуть теорию и задачки: https://gist.github.com/codedokode/10539213
Также я могу дать дополнительные задачки по проектированию БД, например один анон у нас тут проектировал БД сервиса продажи музыки, довольно большую.
> Или у них совпадает первичный ключ? Как тогда обеспечить это совпадение? Инсертить одной транзакцией?
Инсертишь одну запись, получаешь ключ и инсертишь вторую. У второй не должно быть автоинкремента. И конечно одной транзакцией.
> Как мне запрос к такой странной схеме писать?
один ответ на все вопросы - джойн.
Есть instanceof. Но вообще как правило в ООП если ты явно начинаешь проверять классы это может быть признаком не очень хорошего кода. принято использовать полиморфизм, то есть способность переопределить метод в наследнике класса. Хотя опять же, не всегда.
>>727779
Нужно больше подробностей
есть простая страница с текстом,под ним есть форма с кнопкой отправить,как на пыхе сделать так,чтоб после перезагрузки на странице появился мой комментарий,дрочусь уже 2 час и нихуя.можно базовый алгоритм с кодом!
Это не такая и простая задача. например, где ты будешь хранить введенные комментарии? В базе данных? Умеешь с ними работать? Также, надо уметь работать с HTML формами и с данными в POST.
<?php
if (данные формы комментария пришли методом пост) {
создать модель комментария из данных формы;
сохранить в базу данных;
сделать редирект на ту же самую страницу;
}
$article = получить объект статьи;
$comments = получить все комментарии к статье;
?>
<html>
...
<h1><?php echo htmlspecialchars($article->title); ?></h1>
<article><?php echo htmlspecialchars($article->content); ?></article>
<section class="comments">
<?php foreach($comments as $comment): ?>
<div class="comment"><?php echo htmlspecialchars($comment->content); ?></div>
<?php endforeach; ?>
</section>
на базовом уровне
>$sentence2, $sentence3 и т.п.
Переменные лучше называть по тому, что мы в них имеем, что получаем. Иначе в большой программе запутаешься.
Хотя потом ты будешь многое просто сокращать:
>$sentence = firstLetterUpper (reverseSentence ($value));
Также обычно не разъединяют название функции и скобки, которые за ней следуют:
>$a = makeTextlower ($text);
>$a = makeTextlower($text);
Return не обязательно делать в кавычках, вообще не имеет смысла:
>return ($result);
>return $result;
Но это всё мелочи. Так-то у тебя там всё вроде бы в норме.
Хотя ОП что-нибудь увидит, чего не видно невооружённым глазом.
В первом случае разбивал строку на массив, после с помощью for редактировал ее, затем склеивал строку через implode - все работало. Затем я подумал почему бы не использовать forech для перебора, сказано сделано, но что-то не выходит.
1-й, работает http://ideone.com/JEAitM
2-й, кривой http://ideone.com/aunZoy
Поясните в чем проблема.
Page Not Found
The page you are looking for could not be found. Check the address bar to ensure your URL is spelled correctly. If all else fails, you can visit our home page at the link below.
Visit the Home Page
Слим из коробки не будет работать. Для этого нужно веб-сервер настраивать, почитай вот http://www.slimframework.com/docs/start/web-servers.html
создал директорию public, выполнил команду
php -S localhost:8080 -t ./public/
.htaccess был - ничего не изменилось
Я сначала так и начал делать, но потом столкнулся с проблемой выборки данных из таких таблиц при обработке ответов.
Поэтому перешел на схему
questions
-----------
id
content
position
points
answer_type (enum={single, multiple, string, integer})
variants
---------
id
value
is_right (enum={right, wrong})
Тут у нас одна таблица с вариантами ответов. Туда же сохраняем все остальные типы ответов. Понятно что получится один тип данных, строка.
Но уже в самом приложении можно по типу вопроса (string, decimal, multiple, single) преобразовывать данные.
Допустим, пользователь отвечает на вопрос и жмет далее. Из формы придут данные, выбираем из бд вопрос и связанные с ним варианты ответов.
В зависимости от типа ответа, привязанного к варианту, провести валидацию. Хотя тут конечно получается костыльный велосипед.
Хорошо, перейдем к паттернам наследования. Я о них слышал до этого, но кроме бессмысленного определения не знал как и где их применять.
Может тут получится присобачить.
Что получается с STI (я так понял скидывать все общие/уникальные свойства в одну таблицу, если их не очень много конечно)
question
---------
id
content
position
points
type (enum={single, multiple, string, integer})
answer_decimal
answer_string
deviation
, где поля answer_decimal, answer_string и deviation могут принимать NULL и зависят от поля-дескриминатора type.
Ключ хранится в inverse side отношения, то есть на стороне многих (variants).
STI более простой для понимания, из минусов большое (в данном случае относительно небольшое) количество пустых полей (null).
Concrete TI фактически выглядит так: создаем каждому классу по таблице, общие поля одинаковые, дополнительно уникальные поля, характерные для этого класса.
Кроме отсутствия пустых полей не вижу преимуществ в создании кучи таблиц и соответственно кучи копипасты в моделях.
Class TI вообще не понимаю, как это выглядит на практике.
Поэтому выбираю STI. Если бы там было >10 полей, допускающих NULL, можно было бы подумать о вариантах.
Возвращаемся к исходному вопросу. Как хранить ответы пользователей и статистику прохождений тестов? Возможно статистику не нужно хранить а выполнять каждый раз убойный запрос на выборку по ответам.
Вот пользователь зашел на наш сайт, так. Увидел тест, нажал пройти.
Как должно реагировать приложение? Если пользователь не зарегистрирован, сохранить пустышку в таблице пользователей с токеном доступа.
Выставить куку с токеном.
Перейти на страницу с первым вопросом теста (впрочем у нас все на аяксе, да, имеется ввиду показать форму с первым вопросом).
Тут сразу вопрос. Возможно стоит сделать отдельную таблицу "прохождений", и на нее ссылаться ключами "введенных ответов" пользователей (предлагаю определиться с терминологией и называть "ответом", а варианты ответов, хранящиеся в базе, называть "вариантами").
passages (?)
---------
id
user_id
start_time
answers
---------
id
value
passage_id
Наличие таблицы passages (прохождения?) даст возможность зафиксировать начало прохождения теста. Или там время привязано к вопросам? Проверил макет, привязано к тесту.
Плюс возможно она облегчит нам проблему сохранения статистики прохождения. А может и нет, не представляю пока.
Но эта таблица создает другие проблемы: если id вопроса и теста я могу получить из урла и формы, то как быть с id прохождения? Лепить его в url? Или хранить в форме?
Если отказаться от таблицы прохождений, то нужно копипастить id пользователя и временную метку по всем ответам.
answers
---------
id
value
question_id
Ну хорошо, дальше. А что в value ответа? В зависимости от того, какой тип вопроса (string, decimal, внешний ключ на вариант), разные данные, строка/число/идентификатор_варианта.
Тут тоже наследование?
answers
---------
id
question_id
decimal_value (DECIMAL)
string_value (VARCHAR)
variant_id (INTEGER)
Непонятно правда, нужно ли лепить сюда дескриминатор? По-моему не нужно, у нас ведь уже есть в question по ключу. Хотя возможно и понадобится в каком-то запросе, не знаю.
Господи, у меня уже моск напух, пойду отдохну. Потом может перечитаю все что написал и попробую на чем-то остановиться.
>по первичному ключу который не показан на схеме (точнее подразумевается так как там есть стрелочки)
Не разбираюсь в этих ваших стрелочках. Мне нужно описание вопроса на расейском литературном языке. Когда я вижу квадратики и треугольники, мне это ни о чем не говорит.
>Там отношение 1:1 между таблицами, ты ведь знаешь как оно реализуется?
Да, по ключу на обратной стороне отношения. Допустим между профилем пользователя и собственно пользователем ключ будет держать профиль.
>Если ты не очень хорошо знаешь БД, возможно тебе стоит глянуть теорию и задачки
Знаю не очень хорошо, но не настолько. "Задачками" уже сыт по горло, по-моему тут только я их решаю.
По sql у тебя к тому же там только кинотеатр и лайки.
Задачу про музыкальные лейблы тоже решал когда-то, кажется там были такие сущности как лейбл, музыкант, группа, трек, альбом. Что альбом может быть недоступен в ряде стран типа северной кореи, что артист может за карьеру сменить несколько групп, или одновременно состоять в нескольких, или иметь сольную карьеру. Короче тоже муторное что-то. Не помню, наверное так до конца не дорешал.
Я сначала так и начал делать, но потом столкнулся с проблемой выборки данных из таких таблиц при обработке ответов.
Поэтому перешел на схему
questions
-----------
id
content
position
points
answer_type (enum={single, multiple, string, integer})
variants
---------
id
value
is_right (enum={right, wrong})
Тут у нас одна таблица с вариантами ответов. Туда же сохраняем все остальные типы ответов. Понятно что получится один тип данных, строка.
Но уже в самом приложении можно по типу вопроса (string, decimal, multiple, single) преобразовывать данные.
Допустим, пользователь отвечает на вопрос и жмет далее. Из формы придут данные, выбираем из бд вопрос и связанные с ним варианты ответов.
В зависимости от типа ответа, привязанного к варианту, провести валидацию. Хотя тут конечно получается костыльный велосипед.
Хорошо, перейдем к паттернам наследования. Я о них слышал до этого, но кроме бессмысленного определения не знал как и где их применять.
Может тут получится присобачить.
Что получается с STI (я так понял скидывать все общие/уникальные свойства в одну таблицу, если их не очень много конечно)
question
---------
id
content
position
points
type (enum={single, multiple, string, integer})
answer_decimal
answer_string
deviation
, где поля answer_decimal, answer_string и deviation могут принимать NULL и зависят от поля-дескриминатора type.
Ключ хранится в inverse side отношения, то есть на стороне многих (variants).
STI более простой для понимания, из минусов большое (в данном случае относительно небольшое) количество пустых полей (null).
Concrete TI фактически выглядит так: создаем каждому классу по таблице, общие поля одинаковые, дополнительно уникальные поля, характерные для этого класса.
Кроме отсутствия пустых полей не вижу преимуществ в создании кучи таблиц и соответственно кучи копипасты в моделях.
Class TI вообще не понимаю, как это выглядит на практике.
Поэтому выбираю STI. Если бы там было >10 полей, допускающих NULL, можно было бы подумать о вариантах.
Возвращаемся к исходному вопросу. Как хранить ответы пользователей и статистику прохождений тестов? Возможно статистику не нужно хранить а выполнять каждый раз убойный запрос на выборку по ответам.
Вот пользователь зашел на наш сайт, так. Увидел тест, нажал пройти.
Как должно реагировать приложение? Если пользователь не зарегистрирован, сохранить пустышку в таблице пользователей с токеном доступа.
Выставить куку с токеном.
Перейти на страницу с первым вопросом теста (впрочем у нас все на аяксе, да, имеется ввиду показать форму с первым вопросом).
Тут сразу вопрос. Возможно стоит сделать отдельную таблицу "прохождений", и на нее ссылаться ключами "введенных ответов" пользователей (предлагаю определиться с терминологией и называть "ответом", а варианты ответов, хранящиеся в базе, называть "вариантами").
passages (?)
---------
id
user_id
start_time
answers
---------
id
value
passage_id
Наличие таблицы passages (прохождения?) даст возможность зафиксировать начало прохождения теста. Или там время привязано к вопросам? Проверил макет, привязано к тесту.
Плюс возможно она облегчит нам проблему сохранения статистики прохождения. А может и нет, не представляю пока.
Но эта таблица создает другие проблемы: если id вопроса и теста я могу получить из урла и формы, то как быть с id прохождения? Лепить его в url? Или хранить в форме?
Если отказаться от таблицы прохождений, то нужно копипастить id пользователя и временную метку по всем ответам.
answers
---------
id
value
question_id
Ну хорошо, дальше. А что в value ответа? В зависимости от того, какой тип вопроса (string, decimal, внешний ключ на вариант), разные данные, строка/число/идентификатор_варианта.
Тут тоже наследование?
answers
---------
id
question_id
decimal_value (DECIMAL)
string_value (VARCHAR)
variant_id (INTEGER)
Непонятно правда, нужно ли лепить сюда дескриминатор? По-моему не нужно, у нас ведь уже есть в question по ключу. Хотя возможно и понадобится в каком-то запросе, не знаю.
Господи, у меня уже моск напух, пойду отдохну. Потом может перечитаю все что написал и попробую на чем-то остановиться.
>по первичному ключу который не показан на схеме (точнее подразумевается так как там есть стрелочки)
Не разбираюсь в этих ваших стрелочках. Мне нужно описание вопроса на расейском литературном языке. Когда я вижу квадратики и треугольники, мне это ни о чем не говорит.
>Там отношение 1:1 между таблицами, ты ведь знаешь как оно реализуется?
Да, по ключу на обратной стороне отношения. Допустим между профилем пользователя и собственно пользователем ключ будет держать профиль.
>Если ты не очень хорошо знаешь БД, возможно тебе стоит глянуть теорию и задачки
Знаю не очень хорошо, но не настолько. "Задачками" уже сыт по горло, по-моему тут только я их решаю.
По sql у тебя к тому же там только кинотеатр и лайки.
Задачу про музыкальные лейблы тоже решал когда-то, кажется там были такие сущности как лейбл, музыкант, группа, трек, альбом. Что альбом может быть недоступен в ряде стран типа северной кореи, что артист может за карьеру сменить несколько групп, или одновременно состоять в нескольких, или иметь сольную карьеру. Короче тоже муторное что-то. Не помню, наверное так до конца не дорешал.
>Напоминаем, что письма с приглашениями были разосланы, проверьте почтовые ящики, указанные вами при подаче заявки (не забудьте о папке "Спам")
Как навсегда отключить эту еботу?
Есть где-нибудь подробная пошаговая инструкция? после установки компосером? Странный фреймворк какой то, Yii2 работает из коробки после скачивания, а эти 3 версии выпустили и до сих пор не додумались запилить чтобы работал "хелло ворлд" из коробки
Тебе уже выше дали ссылку на гайд по настройке веб-сервера. Все там есть, просто ты читать не умеешь.
Всё, вроде стартонуло, они так описали что я подумал, что папка public это не сама директория где лежит фреймворк, а папка в ней с index и htaccess
public-accessible directory это как раз публичная папка с твоим index.php. Делать папку vendor публичной не нужно, там файлы фреймворка, которые ты будешь подключать в index.php. Вот на этом >>727907 скриншоте можно заметить что у тебя нет публичной папки, и это очень неправильно, потому что ты даешь возможность обращаться к файлам в папке vendor. Конечно, во всех современных фреймворках это вроде как ничего не сломает, но все равно лучше так не делать.
Можно настроить, а можно и не настроить. И так невнимательный пользователь который установит твое приложение себе на сервер, сам того не зная сделает публичными все файлы фреймворка и еще много чего.
Делаю свой стиль для этого сайта, но никак не могу понять какой параметр делает этот напрыл серого цвета снизу.
Встроенный в php веб-сервер не читает файлы htaccess. Они только для Апача. Если ты хочешь запустить приложение на Слиме под встроенным сервером, то изучи мануал:
http://php.net/manual/ru/features.commandline.webserver.php
Тебе нужно чтобы любые HTTP запросы (которые не соответствуют статическим файлам) обрабатывались в index.php. Значит тебе надо написать так называемый "скрипт маршрутизации", который например будет смотреть на URL, на что он заканчивается, и либо возвращать false либо подключать index.php. Ну например для css или png файлов надо возвращать false, а для остальных путей - вызывать index.php
Также, может чем-то поможет урок https://github.com/codedokode/pasta/blob/master/soft/web-server.md
>>727925
> Тут у нас одна таблица с вариантами ответов. Туда же сохраняем все остальные типы ответов. Понятно что получится один тип данных, строка.
> Но уже в самом приложении можно по типу вопроса (string, decimal, multiple, single) преобразовывать данные.
Да ну, кривые костыли какие-то. Что мешает использовать 1 из 3 паттернов наследования?
> Я о них слышал до этого, но кроме бессмысленного определения не знал как и где их применять.
Надо решать задачки на SQL и просить у ОПа задачу на проектирование какой-нибудь БД посложнее. Интернет-магазин или сайт объявлений например.
> из минусов большое (в данном случае относительно небольшое) количество пустых полей (null).
Да, но это мне кажется мелочи. Разница между паттернами не только в удобстве вставки или выборки, но и в том, какие ограничения мы можем делать. Ну к примеру когда у нас используется Concrete TI мы можем сделать чтобы определенная таблица могла ссылалаться только на определенный тип вопроса. Ну и наоборот, в случае с Single TI меньше ограничений на уровне БД: например можно по ошибке заполнить поля относящиеся к нескольким классам (видам вопросов). Правда, эту проблему можно решить добавлением условий CHECK который есть например в PostgreSQL ( http://www.postgresql.org/docs/8.1/static/ddl-constraints.html )
По моим ощущениям, в плане возможностей использования внешних ключей и ограничений:
Class TI > Concrete TI > Single TI
С другой стороны, Single TI самая простая в реализации, не требует кучи таблиц, позволяет менять тип вопроса. Ну и не каждый ORM может поддерживать все эти схемы.
> Class TI вообще не понимаю, как это выглядит на практике.
Ну разница в том что class ti это когда 1 класс = 1 таблица, включая абстрактные классы. Поля не дублируются, то есть например points и content будут только в базовом абстракном классе и соотв. ему таблице.
Contrete TI это когда мы дублируем поля, делая на каждый конкретный (неабстрактный) класс 1 таблицу со всеми полями. Минус как я понимаю в том, что например таблицы в такой схеме могут быть вообще не связаны и даже например иметь неуникальные между разными таблицами id (это можно победить вынеся id в отдельную таблицу и связав ее через 1:1 с таблицами конкретных классов).
Посмотри на диаграммы классов и таблиц и сравни:
http://martinfowler.com/eaaCatalog/concreteTableInheritance.html
http://martinfowler.com/eaaCatalog/classTableInheritance.html (тут почему-то в таблицах не написаны первичные и внешние ключи но они там есть)
Встроенный в php веб-сервер не читает файлы htaccess. Они только для Апача. Если ты хочешь запустить приложение на Слиме под встроенным сервером, то изучи мануал:
http://php.net/manual/ru/features.commandline.webserver.php
Тебе нужно чтобы любые HTTP запросы (которые не соответствуют статическим файлам) обрабатывались в index.php. Значит тебе надо написать так называемый "скрипт маршрутизации", который например будет смотреть на URL, на что он заканчивается, и либо возвращать false либо подключать index.php. Ну например для css или png файлов надо возвращать false, а для остальных путей - вызывать index.php
Также, может чем-то поможет урок https://github.com/codedokode/pasta/blob/master/soft/web-server.md
>>727925
> Тут у нас одна таблица с вариантами ответов. Туда же сохраняем все остальные типы ответов. Понятно что получится один тип данных, строка.
> Но уже в самом приложении можно по типу вопроса (string, decimal, multiple, single) преобразовывать данные.
Да ну, кривые костыли какие-то. Что мешает использовать 1 из 3 паттернов наследования?
> Я о них слышал до этого, но кроме бессмысленного определения не знал как и где их применять.
Надо решать задачки на SQL и просить у ОПа задачу на проектирование какой-нибудь БД посложнее. Интернет-магазин или сайт объявлений например.
> из минусов большое (в данном случае относительно небольшое) количество пустых полей (null).
Да, но это мне кажется мелочи. Разница между паттернами не только в удобстве вставки или выборки, но и в том, какие ограничения мы можем делать. Ну к примеру когда у нас используется Concrete TI мы можем сделать чтобы определенная таблица могла ссылалаться только на определенный тип вопроса. Ну и наоборот, в случае с Single TI меньше ограничений на уровне БД: например можно по ошибке заполнить поля относящиеся к нескольким классам (видам вопросов). Правда, эту проблему можно решить добавлением условий CHECK который есть например в PostgreSQL ( http://www.postgresql.org/docs/8.1/static/ddl-constraints.html )
По моим ощущениям, в плане возможностей использования внешних ключей и ограничений:
Class TI > Concrete TI > Single TI
С другой стороны, Single TI самая простая в реализации, не требует кучи таблиц, позволяет менять тип вопроса. Ну и не каждый ORM может поддерживать все эти схемы.
> Class TI вообще не понимаю, как это выглядит на практике.
Ну разница в том что class ti это когда 1 класс = 1 таблица, включая абстрактные классы. Поля не дублируются, то есть например points и content будут только в базовом абстракном классе и соотв. ему таблице.
Contrete TI это когда мы дублируем поля, делая на каждый конкретный (неабстрактный) класс 1 таблицу со всеми полями. Минус как я понимаю в том, что например таблицы в такой схеме могут быть вообще не связаны и даже например иметь неуникальные между разными таблицами id (это можно победить вынеся id в отдельную таблицу и связав ее через 1:1 с таблицами конкретных классов).
Посмотри на диаграммы классов и таблиц и сравни:
http://martinfowler.com/eaaCatalog/concreteTableInheritance.html
http://martinfowler.com/eaaCatalog/classTableInheritance.html (тут почему-то в таблицах не написаны первичные и внешние ключи но они там есть)
> Возможно статистику не нужно хранить а выполнять каждый раз убойный запрос на выборку по ответам.
В любом случае мы сначала делаем нормализованную БД, оцениваем тяжесть запроса (число аггрегируемых записей), а потом думаем нужна ли денормализация и если да то какая - таблицы статистики, кеш?
> Тут сразу вопрос. Возможно стоит сделать отдельную таблицу "прохождений",
Конечно таблица нужна. Ведь "попытка сдачи" - это отдельная сущность и надо их фиксировать, чтобы знать сколько раз студенты пытался сдать тест, когда. И тебе ведь надо к чему-то привязать сущность "ответ студента на вопрос".
Назвать можно например sessions, attempts или как-то так.
> то как быть с id прохождения? Лепить его в url? Или хранить в форме?
можно передавать скрытым полем. Ответ на вопрос - это ведь POST. Не забудь как-то это связать, чтобы ты на сервере мог проверить: имеет ли право данный пользователь отвечать на вопросы данного теста с данным id попытки.
> А что в value ответа? В зависимости от того, какой тип вопроса (string, decimal, внешний ключ на вариант), разные данные, строка/число/идентификатор_варианта.
> Тут тоже наследование?
Да. Причем я помню когда-то мы с другим аноном наши ответ на вопрос как это улчше всего реализовать, но я забыл его. Придется тебе с нуля снова искать ответ на этот вопрос. Интересно, вопросы и ответы студента образуют одинаковые иерархии наследования - нельзя ли как-то их обобщить?
> Непонятно правда, нужно ли лепить сюда дескриминатор? По-моему не нужно, у нас ведь уже есть в question по ключу. Хотя возможно и понадобится в каком-то запросе, не знаю.
Начинать надо с нормализованной схемы. Надеюсь принципы нормализации ты помнишь наизусть. Если бы ты помнил, у тебя бы даже вопроса такого "надо ли хранить одно и то же значение в двух местах" не возникло.
> Не разбираюсь в этих ваших стрелочках. Мне нужно описание вопроса на расейском литературном языке. Когда я вижу квадратики и треугольники, мне это ни о чем не говорит.
my bad. Слева UML-диаграмма классов и стрелка в ней значит наследование. Таблицы изображены справа и почему-то без ключей.
Насчет 1:1 не забывай что оно должно дополняться уникальным ключом.
> Задачу про музыкальные лейблы тоже решал когда-то
ай-яй, такую сложную базу спроектировал и все забыл.
>>727934
нужно разбираться хотя бы в основах того как работает и настраивается веб сервер.
>>727953
Вообще то public это и есть отдельная папка с index.php
> Возможно статистику не нужно хранить а выполнять каждый раз убойный запрос на выборку по ответам.
В любом случае мы сначала делаем нормализованную БД, оцениваем тяжесть запроса (число аггрегируемых записей), а потом думаем нужна ли денормализация и если да то какая - таблицы статистики, кеш?
> Тут сразу вопрос. Возможно стоит сделать отдельную таблицу "прохождений",
Конечно таблица нужна. Ведь "попытка сдачи" - это отдельная сущность и надо их фиксировать, чтобы знать сколько раз студенты пытался сдать тест, когда. И тебе ведь надо к чему-то привязать сущность "ответ студента на вопрос".
Назвать можно например sessions, attempts или как-то так.
> то как быть с id прохождения? Лепить его в url? Или хранить в форме?
можно передавать скрытым полем. Ответ на вопрос - это ведь POST. Не забудь как-то это связать, чтобы ты на сервере мог проверить: имеет ли право данный пользователь отвечать на вопросы данного теста с данным id попытки.
> А что в value ответа? В зависимости от того, какой тип вопроса (string, decimal, внешний ключ на вариант), разные данные, строка/число/идентификатор_варианта.
> Тут тоже наследование?
Да. Причем я помню когда-то мы с другим аноном наши ответ на вопрос как это улчше всего реализовать, но я забыл его. Придется тебе с нуля снова искать ответ на этот вопрос. Интересно, вопросы и ответы студента образуют одинаковые иерархии наследования - нельзя ли как-то их обобщить?
> Непонятно правда, нужно ли лепить сюда дескриминатор? По-моему не нужно, у нас ведь уже есть в question по ключу. Хотя возможно и понадобится в каком-то запросе, не знаю.
Начинать надо с нормализованной схемы. Надеюсь принципы нормализации ты помнишь наизусть. Если бы ты помнил, у тебя бы даже вопроса такого "надо ли хранить одно и то же значение в двух местах" не возникло.
> Не разбираюсь в этих ваших стрелочках. Мне нужно описание вопроса на расейском литературном языке. Когда я вижу квадратики и треугольники, мне это ни о чем не говорит.
my bad. Слева UML-диаграмма классов и стрелка в ней значит наследование. Таблицы изображены справа и почему-то без ключей.
Насчет 1:1 не забывай что оно должно дополняться уникальным ключом.
> Задачу про музыкальные лейблы тоже решал когда-то
ай-яй, такую сложную базу спроектировал и все забыл.
>>727934
нужно разбираться хотя бы в основах того как работает и настраивается веб сервер.
>>727953
Вообще то public это и есть отдельная папка с index.php
Правильно ли я сформировал классы и методы? Что-нибудь надо переделать?
> protected $numberOfWorkers = 0;
Это поле дублирует listOfWorkers. Ты вынужден теперь при любых изменениях его поддерживать. Проще его удалить.
> if( ( strtolower(get_class($worker)) == $group )
Лучше без strtolower. зачем это делать если имя класса само по себе уникальная константа?
> $workersGroup[$i] = $worker;
зачем тут $i ?
> if( $worker->getСhief() )
> {
> $chief = $worker;
Если босс найден зачем продолжать цикл?
> public function upWorkersGroupLevel( $group, $fromRank, $toRank, $growth, $percent )//поднять уровень проценту выбранной группы рабочих
комментарий надо написать над функцией а не справа так как слишком длинно получается. ограничение около 80 символов на строку
Этот метод довольно неуниверсальный и потому мне кажется что он должен быть в другом месте. Если ты под каждую задачу будешь добавлять по методу, у тебя класс будет огромный. Каждый класс занимается своим делом. В Департаменте должны быть только общие методы для управления им, а вот это вот должно быть где-то в антикризисном комитете.
То есть метод "найти работников по условию" еще допустим, а "поднять X% человек зарплату" - нет так как это явно часть антикризисных мер.
> public function getWorkersInfos()
Какая-то мутная функция которая ничего не возвращает хотя и называется get. Думаю, ее тут не должно быть
> foreach( $this->listOfWorkers as $number=>$worker )
number не используется, зачем ее писать?
> public function getDepartmentInfos() > //получить информацию о департаменте
Вот это бессмысленный метод. зачем тебе массив когда у тебя есть объект со всеми этими данными? Этот метод не добавляет никакой ценности и его надо убрать из депарамента. Нет смысла преобразовывать хороший объект в плохой массив.
> $this->department = $department->getName();
Тоже ошибка. Какая польза от названия когда ты можешь хранить ссылку на сам объект которая дает гораздо больше возможностей? Плюс если поменять название у департамента - тут оно поменяется? Ты дублируешь одну и ту же информацию в нескольких местах.
Ну и я не уверен что надо делать департамент обзательным для создания сотрудника.
Не лучше ли вообще не хранить в сотруднике ссылку на департамент? У тебя тут явно попытка сделать композицию, хотя мне кажется что аггрегация больше подходит так как сотрудник вполне может существовать и вне Департамента какое-то время (например, в случае увольнения - ты обнуляешь ссылку на департамент? а надо).
То есть если не хранить ссылку на департамент то сотрудника становится проще нанимать, перемещать и увольнять, и в задаче она не нужна. Значит лучше ее и не делать.
> if ( $this->rank+$growth<=3 )
> {
> $this->rank += $growth;
А если нельзя поднять ранг, ты молча игнорируешь ошибку и притворяешься что все ок? Надо выбрасывать исключение а не умалчивать об ошибках - принцип fail fast
> switch( $this->rank )
> {
> case 1:
Нужен default для защиты от ошибок программиста
> public function getInfos()
Класс не должен сам выводить что-то. У тебя получился очень неуниверсальный метод. Для отладки лучше сделать чтобы он просто возвращал строку (а дальше ты делаешь с ней что хочешь), и назвать метод например toString(). Более того,в php уже есть встроенный метод __toString() который можно для этого использовать.
> class Manager extends Worker
> {
>protected $coffeeConsumptionRate = 20;
Вот тут интересное место. Для корректной работы новый класс-наследник работника должен переопределить несколько полей. Но откуда программист об этом узнает? И что если он забудет это сделать? Есть ли у нас в ООП способ обязать программиста задать зарплату, потребление кофе и подобные вещи? Подсказка: абстрактные методы конечно
> return str_repeat(" ",$columns-mb_strlen($string)).$string;
Нет проверки на отрицательные значения при вычитании
> function printInfos( $arrayOfSomething,$columns )
$arrayOfSomething это ничего не значит. Нужно нормальное название, например cells (ячейки таблицы)
И что за название? infos ничего не значит и получается твоя функция просто называется print. Напечатать - что?
> case "ме1":
> case "ме2":
> case "ме3":
Там копипаста на копипасте. Надо избавиться - например разбирать слово регуляркой и сделать массив соответсвия сокращение - имя класса. Или даже можешь сделать класс-фабрику, в котором метод получает название и уровень и производит Сотрудника нужного типа.
> new Manager( $departmentList["закупок"], 2, true );
Это странный код конечно так как объет как-то самопроизвольно себя в департамент добавляет. Обычно так не делают - либо явно добавляют объект в департамент, либо делают чтобы департамент внутри себя создавал и добавлял объект.
Не вижу класса отвечающего за антикризисные меры.
> foreach( $departmentList as $i=>$department )
> {
>$newDepartmentList[$i] = clone $department;
Это не клонирует отдельных сотрудников а только копирует ссылки на них. И ссылки на департамент в них тоже остаются старые, кстати.
Также не вижу класса представляющего всю Компанию в целом. Почему ты везде передаешь какие-то мутные массивы департаментов когда можно иметь один объект представляющий компанию?
> protected $numberOfWorkers = 0;
Это поле дублирует listOfWorkers. Ты вынужден теперь при любых изменениях его поддерживать. Проще его удалить.
> if( ( strtolower(get_class($worker)) == $group )
Лучше без strtolower. зачем это делать если имя класса само по себе уникальная константа?
> $workersGroup[$i] = $worker;
зачем тут $i ?
> if( $worker->getСhief() )
> {
> $chief = $worker;
Если босс найден зачем продолжать цикл?
> public function upWorkersGroupLevel( $group, $fromRank, $toRank, $growth, $percent )//поднять уровень проценту выбранной группы рабочих
комментарий надо написать над функцией а не справа так как слишком длинно получается. ограничение около 80 символов на строку
Этот метод довольно неуниверсальный и потому мне кажется что он должен быть в другом месте. Если ты под каждую задачу будешь добавлять по методу, у тебя класс будет огромный. Каждый класс занимается своим делом. В Департаменте должны быть только общие методы для управления им, а вот это вот должно быть где-то в антикризисном комитете.
То есть метод "найти работников по условию" еще допустим, а "поднять X% человек зарплату" - нет так как это явно часть антикризисных мер.
> public function getWorkersInfos()
Какая-то мутная функция которая ничего не возвращает хотя и называется get. Думаю, ее тут не должно быть
> foreach( $this->listOfWorkers as $number=>$worker )
number не используется, зачем ее писать?
> public function getDepartmentInfos() > //получить информацию о департаменте
Вот это бессмысленный метод. зачем тебе массив когда у тебя есть объект со всеми этими данными? Этот метод не добавляет никакой ценности и его надо убрать из депарамента. Нет смысла преобразовывать хороший объект в плохой массив.
> $this->department = $department->getName();
Тоже ошибка. Какая польза от названия когда ты можешь хранить ссылку на сам объект которая дает гораздо больше возможностей? Плюс если поменять название у департамента - тут оно поменяется? Ты дублируешь одну и ту же информацию в нескольких местах.
Ну и я не уверен что надо делать департамент обзательным для создания сотрудника.
Не лучше ли вообще не хранить в сотруднике ссылку на департамент? У тебя тут явно попытка сделать композицию, хотя мне кажется что аггрегация больше подходит так как сотрудник вполне может существовать и вне Департамента какое-то время (например, в случае увольнения - ты обнуляешь ссылку на департамент? а надо).
То есть если не хранить ссылку на департамент то сотрудника становится проще нанимать, перемещать и увольнять, и в задаче она не нужна. Значит лучше ее и не делать.
> if ( $this->rank+$growth<=3 )
> {
> $this->rank += $growth;
А если нельзя поднять ранг, ты молча игнорируешь ошибку и притворяешься что все ок? Надо выбрасывать исключение а не умалчивать об ошибках - принцип fail fast
> switch( $this->rank )
> {
> case 1:
Нужен default для защиты от ошибок программиста
> public function getInfos()
Класс не должен сам выводить что-то. У тебя получился очень неуниверсальный метод. Для отладки лучше сделать чтобы он просто возвращал строку (а дальше ты делаешь с ней что хочешь), и назвать метод например toString(). Более того,в php уже есть встроенный метод __toString() который можно для этого использовать.
> class Manager extends Worker
> {
>protected $coffeeConsumptionRate = 20;
Вот тут интересное место. Для корректной работы новый класс-наследник работника должен переопределить несколько полей. Но откуда программист об этом узнает? И что если он забудет это сделать? Есть ли у нас в ООП способ обязать программиста задать зарплату, потребление кофе и подобные вещи? Подсказка: абстрактные методы конечно
> return str_repeat(" ",$columns-mb_strlen($string)).$string;
Нет проверки на отрицательные значения при вычитании
> function printInfos( $arrayOfSomething,$columns )
$arrayOfSomething это ничего не значит. Нужно нормальное название, например cells (ячейки таблицы)
И что за название? infos ничего не значит и получается твоя функция просто называется print. Напечатать - что?
> case "ме1":
> case "ме2":
> case "ме3":
Там копипаста на копипасте. Надо избавиться - например разбирать слово регуляркой и сделать массив соответсвия сокращение - имя класса. Или даже можешь сделать класс-фабрику, в котором метод получает название и уровень и производит Сотрудника нужного типа.
> new Manager( $departmentList["закупок"], 2, true );
Это странный код конечно так как объет как-то самопроизвольно себя в департамент добавляет. Обычно так не делают - либо явно добавляют объект в департамент, либо делают чтобы департамент внутри себя создавал и добавлял объект.
Не вижу класса отвечающего за антикризисные меры.
> foreach( $departmentList as $i=>$department )
> {
>$newDepartmentList[$i] = clone $department;
Это не клонирует отдельных сотрудников а только копирует ссылки на них. И ссылки на департамент в них тоже остаются старые, кстати.
Также не вижу класса представляющего всю Компанию в целом. Почему ты везде передаешь какие-то мутные массивы департаментов когда можно иметь один объект представляющий компанию?
>Надо решать задачки на SQL и просить у ОПа задачу на проектирование какой-нибудь БД посложнее. Интернет-магазин или сайт объявлений например.
Твою маму за ногу. Делал я твои задачки (по несколько раз). И сайт объявлений делал именно я. (Правда так и не дописал до конца, надо будет взяться и закончить, плюс портировать наконец на вторую версию yii).
https://github.com/nsdvw/classifieds
И почему ты говоришь о себе в третьем лице?
Мне мало попоболи, теперь хочу страдать над симфони. Вроде разобрался на уровне hello world с формами, валидацией, foundation, а также доктриной (доктрину впрочем изрядно
поковырял и даже интегрировал в Memtext).
Модуль авторизации и контроля доступа даже ты кажется говорил что там слишком громоздкий и сложный, может посоветуешь прикрутить что-то вместо него?
>Разница между паттернами не только в удобстве вставки или выборки, но и в том...
Ничего не понял. Короче давай я начну с STI, тогда будет наглядно видно, какие она дает преимущества и в чем ограничивает. Потом если нужно не проблема переписать хоть 2-3 раза под другие схемы.
>оцениваем тяжесть запроса (число аггрегируемых записей)
Каких?
Ладно, короче писать пока так, потом что-то там оценим, переделаю.
В общем, пока такая логика:
при переходе на страницу теста "/test/test_NNN/quest_NNN" проверяем зарегистрирован ли пользователь. Если нет, то создать пустого пользователя с токеном доступа (поле salt можно использовать с этой целью, когда пользователь зарегистрируется, сгенерировать заново соль и хеш) и повесить куки.
Дальше создать запись в таблице попыток attempts.
>Не забудь как-то это связать, чтобы ты на сервере мог проверить: имеет ли право данный пользователь отвечать на вопросы данного теста с данным id попытки.
user_id из attempts сравнить с id в куках (предварительно проверить валидность токена)
При сабмите ответа на вопрос пишем в таблицу answers attempt_id плюс в зависимости от question->answerType значение в ту или другую колонку.
>например можно по ошибке заполнить поля относящиеся к нескольким классам
Вот ты про что, понял, ок. Но давай все равно так сделаю, потом перепишу. Мне тяжело охватить сразу все аспекты и оценить все преимущества, недостатки, и что там еще есть.
Вроде все пока. Я имею ввиду можно начать делать общий костяк и экшен отвечающий за test/NNN/question.
Кстати там пользователь походу может (ведь может?) вернуться назад и ответить по-другому? Значит нужно предусмотреть не только вставку, но и апдейт.
Впрочем доктрине с ее $em->persist($answer) по-барабану.
Возможно с каждым ответом на вопрос есть смысл обновлять attempts, типа обновлять поля со статистикой, которые я пока не предусмотрел. А может и нет.
Потом короче, я не могу охватить все эти моменты, легче делать много мелких правок.
Список тестов на главной и /tests тоже уже можно делать.
Кстати эти две страницы дублируют друг друга, тебе не кажется? На главной из доп.информации только "о сайте" и может быть другая сортировка записей в виджете (по популярности что ли).
На /tests есть пагинация, возможно другая сортировка, а также поиск. Сфинкс меня кстати уже надоел, может посмотреть на elastic? С другой стороны наверное лучше хоть одну вещь качественно вызубрить от начала до конца, а то я все мечусь от одного к другому.
Короче не вижу смысла в 2 отдельных страницах. (Кстати знакомый фронтендщик когда ему показывал файлообменник, упрекал в многостраничности, на его взгляд там spa в самый раз; но то ведь учебная задача). Нет, твою задачку на spa не делал и пока не планирую.
Что такое теги? Теги пользователь может создавать несмотря на активное подсовывание ему автодополнением уже существующих?
Я так понял может создавать. Ну ладно, пока не будем вскрывать тему создания чего либо. Страница создания нового теста на мой взгляд самая сложная. Например дикая трехэтажная форма, где мы создаем сразу тест, вопросы к нему, варианты к вопросу. (А еще может быть теги, и пользователя регистрируем в фоновом режиме).
Пока сделаю главную страницу + /tests (они одинаковые), страницу текущего вопроса, паблиш наверное.
test/preface и test/result возможно, хотя пока непонятно какими такими запросами выводить статистику и даже как ее хранить. Ладно, позже.
В общем пока отбрасываю авторизацию и создание тестов, остальное более менее есть представление как делать.
>Надо решать задачки на SQL и просить у ОПа задачу на проектирование какой-нибудь БД посложнее. Интернет-магазин или сайт объявлений например.
Твою маму за ногу. Делал я твои задачки (по несколько раз). И сайт объявлений делал именно я. (Правда так и не дописал до конца, надо будет взяться и закончить, плюс портировать наконец на вторую версию yii).
https://github.com/nsdvw/classifieds
И почему ты говоришь о себе в третьем лице?
Мне мало попоболи, теперь хочу страдать над симфони. Вроде разобрался на уровне hello world с формами, валидацией, foundation, а также доктриной (доктрину впрочем изрядно
поковырял и даже интегрировал в Memtext).
Модуль авторизации и контроля доступа даже ты кажется говорил что там слишком громоздкий и сложный, может посоветуешь прикрутить что-то вместо него?
>Разница между паттернами не только в удобстве вставки или выборки, но и в том...
Ничего не понял. Короче давай я начну с STI, тогда будет наглядно видно, какие она дает преимущества и в чем ограничивает. Потом если нужно не проблема переписать хоть 2-3 раза под другие схемы.
>оцениваем тяжесть запроса (число аггрегируемых записей)
Каких?
Ладно, короче писать пока так, потом что-то там оценим, переделаю.
В общем, пока такая логика:
при переходе на страницу теста "/test/test_NNN/quest_NNN" проверяем зарегистрирован ли пользователь. Если нет, то создать пустого пользователя с токеном доступа (поле salt можно использовать с этой целью, когда пользователь зарегистрируется, сгенерировать заново соль и хеш) и повесить куки.
Дальше создать запись в таблице попыток attempts.
>Не забудь как-то это связать, чтобы ты на сервере мог проверить: имеет ли право данный пользователь отвечать на вопросы данного теста с данным id попытки.
user_id из attempts сравнить с id в куках (предварительно проверить валидность токена)
При сабмите ответа на вопрос пишем в таблицу answers attempt_id плюс в зависимости от question->answerType значение в ту или другую колонку.
>например можно по ошибке заполнить поля относящиеся к нескольким классам
Вот ты про что, понял, ок. Но давай все равно так сделаю, потом перепишу. Мне тяжело охватить сразу все аспекты и оценить все преимущества, недостатки, и что там еще есть.
Вроде все пока. Я имею ввиду можно начать делать общий костяк и экшен отвечающий за test/NNN/question.
Кстати там пользователь походу может (ведь может?) вернуться назад и ответить по-другому? Значит нужно предусмотреть не только вставку, но и апдейт.
Впрочем доктрине с ее $em->persist($answer) по-барабану.
Возможно с каждым ответом на вопрос есть смысл обновлять attempts, типа обновлять поля со статистикой, которые я пока не предусмотрел. А может и нет.
Потом короче, я не могу охватить все эти моменты, легче делать много мелких правок.
Список тестов на главной и /tests тоже уже можно делать.
Кстати эти две страницы дублируют друг друга, тебе не кажется? На главной из доп.информации только "о сайте" и может быть другая сортировка записей в виджете (по популярности что ли).
На /tests есть пагинация, возможно другая сортировка, а также поиск. Сфинкс меня кстати уже надоел, может посмотреть на elastic? С другой стороны наверное лучше хоть одну вещь качественно вызубрить от начала до конца, а то я все мечусь от одного к другому.
Короче не вижу смысла в 2 отдельных страницах. (Кстати знакомый фронтендщик когда ему показывал файлообменник, упрекал в многостраничности, на его взгляд там spa в самый раз; но то ведь учебная задача). Нет, твою задачку на spa не делал и пока не планирую.
Что такое теги? Теги пользователь может создавать несмотря на активное подсовывание ему автодополнением уже существующих?
Я так понял может создавать. Ну ладно, пока не будем вскрывать тему создания чего либо. Страница создания нового теста на мой взгляд самая сложная. Например дикая трехэтажная форма, где мы создаем сразу тест, вопросы к нему, варианты к вопросу. (А еще может быть теги, и пользователя регистрируем в фоновом режиме).
Пока сделаю главную страницу + /tests (они одинаковые), страницу текущего вопроса, паблиш наверное.
test/preface и test/result возможно, хотя пока непонятно какими такими запросами выводить статистику и даже как ее хранить. Ладно, позже.
В общем пока отбрасываю авторизацию и создание тестов, остальное более менее есть представление как делать.
Оказывается, теперь оно ищет их в папке
/etc/php5/cli/php.ini
Где можно почитать о правильной настройке php на дебианах?
Потому что я не знаю например куда мне сейчас вносить изменения: раньше писал в /etc/apache2/php5/php.ini, все было в порядке.
Теперь вот инсталлятор симфони настаивает на другом пути. Мне что, копипастить эти директивы, или может создать третий (или какой уже по счету?) файл, чтобы вынести отдельно?
>зачем тут $i ?
Хранить ключ объекта, под которым он в основном массиве?
>а вот это вот должно быть где-то в антикризисном комитете.
То есть нужен отдельный класс для оперирования рабочими? Но ему нужны будут данные департаментов. Их делать паблик или добавить в департамент ещё кучу методов по типу get.../set...?
>Какая-то мутная функция которая ничего не возвращает хотя и называется get.
Она выводит на экран рабочих.
>Вот это бессмысленный метод. зачем тебе массив когда у тебя есть объект со всеми этими данными?
Так ведь данных нет до вызова этой функции. В ней вызываются все функции расчёта и результат можно отправлять на печать. Или все вычисляемые данные надо хранить в отдельных полях?
>Ну и я не уверен что надо делать департамент обзательным для создания сотрудника.
>Как работник может быть вне департамента? Если он вне, значит уже не работник, ведь так? Поэтому работники хранятся только в департаменте, в случае увольнения они полностью исчезают.
>Подсказка: абстрактные методы конечно
А как обязать его выполнить этот метод?
>Это не клонирует отдельных сотрудников а только копирует ссылки на них. И ссылки на департамент в них тоже остаются старые, кстати.
Как тогда сделать чтобы департаменты копировались полностью? В процессе надо менять, удалять работников и каждый раз нужны первоначальные данные. Заново пересоздавать их?
>Также не вижу класса представляющего всю Компанию в целом.
Зачем, он же будет один и внутри содержать тот же массив департаментов?
>Если ты под каждую задачу будешь добавлять по методу, у тебя класс будет огромный.
Вот ещё, задачи все разные ведь. Как можно одним и тем же методом и повышать уровни и увольнять и назначать начальников? То есть если появилась новая задача, которую нельзя сделать имеющимися методами так и так придётся добавлять новый. Или нет?
просмотреть использованные файлы можно в php.ini
надо помнить что для cli и для веба могут быть отдельные папки
свои настройки по моему удобнее хранить в отдельном файле, например /etc/php5/cli/ivan.ini
про дебиан https://wiki.debian.org/PHP/
> Теперь вот инсталлятор симфони настаивает на другом пути.
Э? Какое отношение имеет инсталлятор Симфони к хранению конфигов php?
(также интереснее было бы не использовать симфони из инсталлятора, а подключить нужные компоненты через композер и сделать свой кернел).
Уже разобрался, то консольная утилита, которая проверяет системные требования, упорно ищет директивы именно в /etc/php5/cli
Если запускать из браузера /web/config.php все нормально находит.
А какую из двух папок таки использовать для кеша и лога? app или var?
Дал права на обе.
> Хранить ключ объекта, под которым он в основном массиве
А зачем тебе этот ключ? Тебе надо просто вернуть массив отобранных работников, а зачем знать под каким ключом они хранились в исходном списке?
> Но ему нужны будут данные департаментов. Их делать паблик или добавить в департамент ещё кучу методов по типу get.../set...?
Ты переусложняешь. Что делает антикризисный комитет? Применяет антикризисные меры. К чему? К Компании. Соответственно напрашивается такой метод:
public function applyPlan(Company $company)
почему ты хотел сделать их полями? Поля нужны для долго временного хранения каких-то данных. но антикризисному комитету незачем хранить компанию - он ее обработал и больше она ему не нужна.
>Вот это бессмысленный метод. зачем тебе массив когда у тебя есть объект со всеми этими данными?
> Так ведь данных нет до вызова этой функции. В ней вызываются все функции расчёта и результат можно отправлять на печать.
Это не задача класса Сотрудник выводить таблицу статисткии. Это частная задача которая к нему вообще никакого отношения не имеет. Каждый класс должен заниматься только своим делом, задача сотрудника - знать сколько он чего произвел, а таблицы рисовать не его дело.
> А как обязать его выполнить этот метод?
PHP не позволяет унаследовать класс не реализовав все абстрактные методы
>>Это не клонирует отдельных сотрудников а только копирует ссылки на них. И ссылки на департамент в них тоже остаются старые, кстати.
> Как тогда сделать чтобы департаменты копировались полностью?
Сделать магический метод __clone который вызывается при клонировании и будет клонировать все вложенные объекты. В моем учебнике вроде это упомянуто.
> >Также не вижу класса представляющего всю Компанию в целом.
> Зачем, он же будет один и внутри содержать тот же массив департаментов?
Его можно передавать в функцию, клонировать. А не таскать везде этот массив департаментов и копировать его руками.
>>728150
Ты делаешь в Департаменте только универсальные методы, которые можно использовать везде. Найм, поиск по критериям или увольнение - это как раз универсальные методы. Ну и без них обойтись вряд ли получится. А вот "повысить зарплату X% работников" - это уже метод который явно должен быть не в департменте и без которого тут можно обойтись.
Это принцип единой ответственности - каждый должен заниматься своим делом. Ну если сравнить с каким-то реальным предприятием - там ведь обычно тоже есть отделы, и каждый отвечает за что-то свое. И инженер не пытается делать работу бухгалтера. А HR - не принимает решения о сокращении (а только их реализует).
У тебя это явно нарушено - ты поместил код реализации антикризисных мер и часть кода вывода таблицы в класс Департамент. То есть он у тебя выполняет 3 задачи вместо одной - хранение и управление данными о департаменте.
Вообще, если идти дальше, можно и подсчет расходов по департаменту выносить, но тут, мне кажется, будет уже перебор, так мы насоздаем кучу классов.
Если не соблюдать принцип единой ответственности то можно получить god object https://ru.wikipedia.org/wiki/Божественный_объект
Я попробовал поискать статьи по теме, но они мутные какие-то, не знаю, помогут ли:
http://x-twig.ru/blog/single-responsibility-principle/
https://ru.wikipedia.org/wiki/Принцип_единственной_обязанности
http://blog.byndyu.ru/2009/10/blog-post.html
https://habrahabr.ru/post/208442/
> Хранить ключ объекта, под которым он в основном массиве
А зачем тебе этот ключ? Тебе надо просто вернуть массив отобранных работников, а зачем знать под каким ключом они хранились в исходном списке?
> Но ему нужны будут данные департаментов. Их делать паблик или добавить в департамент ещё кучу методов по типу get.../set...?
Ты переусложняешь. Что делает антикризисный комитет? Применяет антикризисные меры. К чему? К Компании. Соответственно напрашивается такой метод:
public function applyPlan(Company $company)
почему ты хотел сделать их полями? Поля нужны для долго временного хранения каких-то данных. но антикризисному комитету незачем хранить компанию - он ее обработал и больше она ему не нужна.
>Вот это бессмысленный метод. зачем тебе массив когда у тебя есть объект со всеми этими данными?
> Так ведь данных нет до вызова этой функции. В ней вызываются все функции расчёта и результат можно отправлять на печать.
Это не задача класса Сотрудник выводить таблицу статисткии. Это частная задача которая к нему вообще никакого отношения не имеет. Каждый класс должен заниматься только своим делом, задача сотрудника - знать сколько он чего произвел, а таблицы рисовать не его дело.
> А как обязать его выполнить этот метод?
PHP не позволяет унаследовать класс не реализовав все абстрактные методы
>>Это не клонирует отдельных сотрудников а только копирует ссылки на них. И ссылки на департамент в них тоже остаются старые, кстати.
> Как тогда сделать чтобы департаменты копировались полностью?
Сделать магический метод __clone который вызывается при клонировании и будет клонировать все вложенные объекты. В моем учебнике вроде это упомянуто.
> >Также не вижу класса представляющего всю Компанию в целом.
> Зачем, он же будет один и внутри содержать тот же массив департаментов?
Его можно передавать в функцию, клонировать. А не таскать везде этот массив департаментов и копировать его руками.
>>728150
Ты делаешь в Департаменте только универсальные методы, которые можно использовать везде. Найм, поиск по критериям или увольнение - это как раз универсальные методы. Ну и без них обойтись вряд ли получится. А вот "повысить зарплату X% работников" - это уже метод который явно должен быть не в департменте и без которого тут можно обойтись.
Это принцип единой ответственности - каждый должен заниматься своим делом. Ну если сравнить с каким-то реальным предприятием - там ведь обычно тоже есть отделы, и каждый отвечает за что-то свое. И инженер не пытается делать работу бухгалтера. А HR - не принимает решения о сокращении (а только их реализует).
У тебя это явно нарушено - ты поместил код реализации антикризисных мер и часть кода вывода таблицы в класс Департамент. То есть он у тебя выполняет 3 задачи вместо одной - хранение и управление данными о департаменте.
Вообще, если идти дальше, можно и подсчет расходов по департаменту выносить, но тут, мне кажется, будет уже перебор, так мы насоздаем кучу классов.
Если не соблюдать принцип единой ответственности то можно получить god object https://ru.wikipedia.org/wiki/Божественный_объект
Я попробовал поискать статьи по теме, но они мутные какие-то, не знаю, помогут ли:
http://x-twig.ru/blog/single-responsibility-principle/
https://ru.wikipedia.org/wiki/Принцип_единственной_обязанности
http://blog.byndyu.ru/2009/10/blog-post.html
https://habrahabr.ru/post/208442/
Потому что в дебиане для консоли и для веб-сервера могут быть разные конфиги.
> А какую из двух папок таки использовать для кеша и лога? app или var?
var значит variable, "изменчивый" так что выбор по моему очевиден.
Для инсталлятора не очевиден, говорит
>Change the permissions of either "app/cache/" or "var/cache/" directory so that the web server can write into it.
Я не знаю, зачем ему кеш класть в app, поэтому и спросил, мало ли.
>PHP не позволяет унаследовать класс не реализовав все абстрактные методы
Блин, если эти параметры задавать через метод, то какой вообще смысл делать четыре класса работников, которые отличаются только названием? В каждом будет реализован этот абстрактный метод из предка, причём реализован одинаково, просто копипаст. Можно же сразу реализовать его в основном классе, а под профессию завести переменную.
нельзя добавлять новые профессии не трогая старый код. Нельзя переопределять поведение представителей разных профессий.
Так они и не отличаются, лол. Надо было им хоть какое-то отличие оставить.
Я сейчас делаю компонент на аяксе. Сейчас разбор параметров post-запроса происходит в контроллере, который дергает различные статические функции модели. Это правильно? Или надо передавать массив параметров в модель чтобы она меняла свое состояние сама?
И еще. Делать запрос в модель для получения массива состояния вида, возвращать обновленный вид - это задача контроллера или модели?
http://ideone.com/Uz35VA // Числа прописью
Внёс несколько исправлений по прошлым замечаниям,
сделал проверку нулевой суммы.
>> while ($i < $number) {
> А не лучше ли было сделать цикл по bit от 0 до 3? Я не очень понимаю логики в условии while, почему ты счетчик
> сравниваешь с числом. Мне кажется без i было бы проще.
Если сделать цикл по bit от 0 до 3, то мы ограничим размер переименовываемых чисел.
Логика в том, что мы суммируем все части числа, которые переименовали, и сравниваем эту сумму с изначальным
числом. Это позволяет переименовывать числа любых разрядов (размеров), просто добавив названия разряда в массив $words,
например для миллиардов, триллионов и т.д.
Можно сделать через bit, единожды определяя разрядность числа (ты советовал через логарифм по основанию 10), но тут есть
свои подводные камни (log10(1000000)=6, а log10(1000001)=7). Операция суммирования чисел мне кажется более простой.
Переделал класс countCommonTotal в класс Компания
> class countCommonTotal {
> Тут ведь есть еще другие варианты. Либо
> 1) сделать класс Компания, содержащий массив работников и методы расчета суммы зарплаты, часов и тд.
> Профит что можно добавлять работников ,увольнять, узнавать сколько в этом случае будет часов и вообще
> класс представляет информацию о компании
Мне нравится этот вариант, т.к. класс Компания менее абстрактный с точки зрения человеческого восприятия чем
класс CountCommonTotal. И возможность добавить новый функционал, не захламляя код отдельными функциями,
а добавив новый метод в класс Компания.
> 2) сделать класс СтрокаТаблицы, хранящий одну строку таблицы, но без расчета суммы и вообще без методов.
> Профит в том что этим классом можно хранить не только сумму, но и любую строку в таблице.
Я правильно понимаю, если мы ее добавим, можно будет работать со строками?
Например менять местами в таблице, при сортировке? Это возможность добавить функционал в будущем?
А почему класс СтрокаТаблицы без методов? В него можно положить методы padLeft, padRight?
Т.к. сейчас эти функции относятся к строке, но лежат в середине кода, и не понятно к чему относятся.
> 3) не делать этот класс, а сумму копить в переменной
Если мы добавляем класс Компания, разве не логичнее хранить значения и методы расчета в нем, а не в отдельно
лежащей переменной в коде?
Если мы отказываемся от класса CountCommonTotal и не делаем класс Компания, то придется сделать функцию для расчета
значений, тогда может быть, хранить значения внутри функции в глобальной переменной?
Если выбирать между классом CountCommonTotal и переменной + функция, мне кажется лучшим решением класс CountCommonTotal,
т.к. и методы и значения хранятся в одном месте в коде.
Переделал класс countCommonTotal в класс Компания
> class countCommonTotal {
> Тут ведь есть еще другие варианты. Либо
> 1) сделать класс Компания, содержащий массив работников и методы расчета суммы зарплаты, часов и тд.
> Профит что можно добавлять работников ,увольнять, узнавать сколько в этом случае будет часов и вообще
> класс представляет информацию о компании
Мне нравится этот вариант, т.к. класс Компания менее абстрактный с точки зрения человеческого восприятия чем
класс CountCommonTotal. И возможность добавить новый функционал, не захламляя код отдельными функциями,
а добавив новый метод в класс Компания.
> 2) сделать класс СтрокаТаблицы, хранящий одну строку таблицы, но без расчета суммы и вообще без методов.
> Профит в том что этим классом можно хранить не только сумму, но и любую строку в таблице.
Я правильно понимаю, если мы ее добавим, можно будет работать со строками?
Например менять местами в таблице, при сортировке? Это возможность добавить функционал в будущем?
А почему класс СтрокаТаблицы без методов? В него можно положить методы padLeft, padRight?
Т.к. сейчас эти функции относятся к строке, но лежат в середине кода, и не понятно к чему относятся.
> 3) не делать этот класс, а сумму копить в переменной
Если мы добавляем класс Компания, разве не логичнее хранить значения и методы расчета в нем, а не в отдельно
лежащей переменной в коде?
Если мы отказываемся от класса CountCommonTotal и не делаем класс Компания, то придется сделать функцию для расчета
значений, тогда может быть, хранить значения внутри функции в глобальной переменной?
Если выбирать между классом CountCommonTotal и переменной + функция, мне кажется лучшим решением класс CountCommonTotal,
т.к. и методы и значения хранятся в одном месте в коде.
а оно как-то неправильно устанавливается.
Добавляю в DefaultController хелло ворлд
/
@Route("/test", name="test")
/
public function testAction(Request $request)
{
return new Response('Hello world!');
}
Пишет "No route found for "GET /test""
Погуглил по тексту ошибки, говорят поставить в web/app.php
$kernel = new AppKernel('prod', true);
Так заработало. Но все равно это неправильно.
И тулбара внизу почему-то нет.
Еще ставится 3.0.4-dev версия.
Как избавиться от dev?
Пробовал и через инсталлятор
symfony new myapp
и через композер
composer create-project symfony/framework-standard-edition myapp
Где найти нормальный мануал, чтобы хотя бы хелловорлд работал из коробки?
>>728165
>(также интереснее было бы не использовать симфони из инсталлятора, а подключить нужные компоненты через композер и сделать свой кернел).
Ссылку в студию.
а оно как-то неправильно устанавливается.
Добавляю в DefaultController хелло ворлд
/
@Route("/test", name="test")
/
public function testAction(Request $request)
{
return new Response('Hello world!');
}
Пишет "No route found for "GET /test""
Погуглил по тексту ошибки, говорят поставить в web/app.php
$kernel = new AppKernel('prod', true);
Так заработало. Но все равно это неправильно.
И тулбара внизу почему-то нет.
Еще ставится 3.0.4-dev версия.
Как избавиться от dev?
Пробовал и через инсталлятор
symfony new myapp
и через композер
composer create-project symfony/framework-standard-edition myapp
Где найти нормальный мануал, чтобы хотя бы хелловорлд работал из коробки?
>>728165
>(также интереснее было бы не использовать симфони из инсталлятора, а подключить нужные компоненты через композер и сделать свой кернел).
Ссылку в студию.
Спасибо анон, действительно годно.
Исправил имена переменных, чтобы код был читаем. Заменил хвостовую рекурсивную функция на цикл.
>> Перебираем количество банкнот текущего номинала
>> от минимально требуемого (или того что есть) до 1
>> for ($minQuant=min($bill['quantity'], intval(floor($currentAmount/$bill['value']))); $minQuant >= 1; $minQuant--) {
>> Эта проверка на $value[1] учитывает ли что мы могли уже выдать часть купюр и запас уменьшился?
Не учитывает. Она проверяет сколько вообще банкнот в запасе и сколько нужно банкнот данного номинала для размена
данной суммы, и выбирает наименьшее из этих двух значений. Для каждой суммы от 0 до 6600 мы делаем проверку, можно ли ее разменять банкнотой $текущего-номинала,
если после выдачи у нас остается остаток, мы проверяем, можно ли этот остаток выдать банкнотами меньшего номинала, если и после выдачи всех монет предыдущего номинала остается остаток, мы проверяем пред-пред номинал, и если в конце мы не придем к 0, то для $текущего-номинала, данная сумма будет считаться неразменной.
То есть, возможна ли теоретически выдача текущей суммы текущей банкнотой (учитывая их количество и все банкноты меньшего номинала).
Нечто вроде проверки количества банкнот, которых мы уже выдали, происходит в findAns. За счет того, что для каждой суммы, начиная с самой большой, выбрав выдачу определенной банкнотой, мы уменьшаем текущую сумму на номинал этой монеты.
Следовательно для меньшей суммы, потребуется меньше монет. Так, как в условии (for ($minQuant=min($bill['quantity'], intval(floor($currentAmount/$bill['value']))); $minQuant >= 1; $minQuant--)) мы изначально брали минимальное количество монет.
А если бы, данную сумму нельзя было разменять данной монетой (учитывая и все банкноты меньшего номинала и их кол-во), то в таблице было бы значение INF, то есть сумма была бы неразменной для данной банкноты.
>> Также, тебе вопрос. Вот ты генерируешь массив results, размера (сумма x число купюр).
>> Довольно большой и довольно долго генерируется. Нельзя ли обойтись без него, вычисляя его динамически -
>> через рекурсивную функцию getMinBills(billIndex, sum)?
Возможно, я не совсем понимаю, что ты хочешь сказать. Поправь если так.
Сейчас, вначале генерируется не весь массив, в только часть (в блоке Частичное заполнение массива). Заполняем нулями все значения для размена нулевой суммы для всех банкнот,
и заполняем бесконечностью все значения для банкноты достоинством 0 для всех сумм.
В дальнейшем, если мы приходим к 0 - значит размен возможен, если к INF размен не возможен. Остальная часть массива
генерируется походу, но для каждого номинала и каждой суммы от 0+1 до 6600.
Для каждого текущего вычисления мы используем предыдущие вычисления.
Если вообще отказаться от записи промежуточных значений в массиве, то количество вычислений увеличится, т.к. для каждого номинала ф-я будет считать заново предыдущие номиналы.
Наверное, можно проверять перебирая сумму с шагом не +1, а +100.
>> Можешь прикинуть, есть ли выгода от отказа от массива? Сколько операций (вызовов) примерно придется выполнить в случае использования функции?
Если вообще отказаться от записи промежуточных значений в массиве, и каждый раз читать все значения заново, сумма * (количество номиналов^2)
>> Так как функция может вызываться несколько раз для одних и тех же значений, придется использовать мемоизацию - то есть, добавить тот же массив, только >> заполняемый по мере надобности.
Но ведь сейчас он так и заполняется, по мере расчета, или нет?
>>Альтернативная идея: можно ли в массиве не заполнять все клетки, а только те, где хранятся измененные числа? А те где нули или бесконечность вообще не хранить чтобы не тратить память?
Если проверять не значение элемента массива, а существует ли вообще элемент, т.е. != NULL ?
Исправил имена переменных, чтобы код был читаем. Заменил хвостовую рекурсивную функция на цикл.
>> Перебираем количество банкнот текущего номинала
>> от минимально требуемого (или того что есть) до 1
>> for ($minQuant=min($bill['quantity'], intval(floor($currentAmount/$bill['value']))); $minQuant >= 1; $minQuant--) {
>> Эта проверка на $value[1] учитывает ли что мы могли уже выдать часть купюр и запас уменьшился?
Не учитывает. Она проверяет сколько вообще банкнот в запасе и сколько нужно банкнот данного номинала для размена
данной суммы, и выбирает наименьшее из этих двух значений. Для каждой суммы от 0 до 6600 мы делаем проверку, можно ли ее разменять банкнотой $текущего-номинала,
если после выдачи у нас остается остаток, мы проверяем, можно ли этот остаток выдать банкнотами меньшего номинала, если и после выдачи всех монет предыдущего номинала остается остаток, мы проверяем пред-пред номинал, и если в конце мы не придем к 0, то для $текущего-номинала, данная сумма будет считаться неразменной.
То есть, возможна ли теоретически выдача текущей суммы текущей банкнотой (учитывая их количество и все банкноты меньшего номинала).
Нечто вроде проверки количества банкнот, которых мы уже выдали, происходит в findAns. За счет того, что для каждой суммы, начиная с самой большой, выбрав выдачу определенной банкнотой, мы уменьшаем текущую сумму на номинал этой монеты.
Следовательно для меньшей суммы, потребуется меньше монет. Так, как в условии (for ($minQuant=min($bill['quantity'], intval(floor($currentAmount/$bill['value']))); $minQuant >= 1; $minQuant--)) мы изначально брали минимальное количество монет.
А если бы, данную сумму нельзя было разменять данной монетой (учитывая и все банкноты меньшего номинала и их кол-во), то в таблице было бы значение INF, то есть сумма была бы неразменной для данной банкноты.
>> Также, тебе вопрос. Вот ты генерируешь массив results, размера (сумма x число купюр).
>> Довольно большой и довольно долго генерируется. Нельзя ли обойтись без него, вычисляя его динамически -
>> через рекурсивную функцию getMinBills(billIndex, sum)?
Возможно, я не совсем понимаю, что ты хочешь сказать. Поправь если так.
Сейчас, вначале генерируется не весь массив, в только часть (в блоке Частичное заполнение массива). Заполняем нулями все значения для размена нулевой суммы для всех банкнот,
и заполняем бесконечностью все значения для банкноты достоинством 0 для всех сумм.
В дальнейшем, если мы приходим к 0 - значит размен возможен, если к INF размен не возможен. Остальная часть массива
генерируется походу, но для каждого номинала и каждой суммы от 0+1 до 6600.
Для каждого текущего вычисления мы используем предыдущие вычисления.
Если вообще отказаться от записи промежуточных значений в массиве, то количество вычислений увеличится, т.к. для каждого номинала ф-я будет считать заново предыдущие номиналы.
Наверное, можно проверять перебирая сумму с шагом не +1, а +100.
>> Можешь прикинуть, есть ли выгода от отказа от массива? Сколько операций (вызовов) примерно придется выполнить в случае использования функции?
Если вообще отказаться от записи промежуточных значений в массиве, и каждый раз читать все значения заново, сумма * (количество номиналов^2)
>> Так как функция может вызываться несколько раз для одних и тех же значений, придется использовать мемоизацию - то есть, добавить тот же массив, только >> заполняемый по мере надобности.
Но ведь сейчас он так и заполняется, по мере расчета, или нет?
>>Альтернативная идея: можно ли в массиве не заполнять все клетки, а только те, где хранятся измененные числа? А те где нули или бесконечность вообще не хранить чтобы не тратить память?
Если проверять не значение элемента массива, а существует ли вообще элемент, т.е. != NULL ?
Разобрался:
Запускать нужно через app_dev.php (контроллер для режима разработчика), то есть
example.com/app_dev.php
Тулбар появился. Почему в мои предыдущие попытки не появлялся? Наверное страница бралась из кеша.
По поводу dev, то это как я понял имеется ввиду именно режим симфони, а не версия пакета как подумалось вначале.
Но почему тогда мне писало 3.0.4-dev, когда я находился в prod-режиме? Странно.
Ну ладно, проехали. Хотя гайд в документации нерабочий, лучше бы его не было. Хоть бы для начала объяснили разницу между
режимами и настройку конфигов.
>подключить нужные компоненты через композер и сделать свой кернел
Как это сделать? Хочу подключить только нужные компоненты, потому что через create-project действительно поустанавливалась куча барахла типа swiftmailer, monolog и т.п. Мне в принципе не мешают, но все равно хочется знать альтернативы.
> @Route("/test", name="test")
имей в виду что это неудобный (на мой взгляд) спосо и удобнее хранить роуты в yml файле, так как они все перед глазами будут.
> Погуглил по тексту ошибки, говорят поставить в web/app.php
скорее всего он не увидел твою аннотацию и надо что-то поменять что за это отвечает
> И тулбара внизу почему-то нет.
Он в конфиге по моему включается. Мне кстати не нравится.
> Как избавиться от dev?
Не скачивать dev версии
> Ссылку в студию.
Поищи тут для начала: http://symfony.com/doc/current/components/http_kernel/index.html
По идее тебе просто надо сделать свой index.php и свой кернел, вроде этого хватит. Вообще, я не помню наизучть что там еще надо. Разберись пока с инсталляцией из коробки и может поймешь.
>>728604
> По поводу dev, то это как я понял имеется ввиду именно режим симфони, а не версия пакета как подумалось вначале.
Это версия. dev значит скорее всего что она не очень стабильная либо это 3.0.4 с какими-то доп. доработками.
> Запускать нужно через app_dev.php (контроллер для режима разработчика)
Ну он по моему там только dev-режим включает и все. Ты можешь по идее и как-то по другому режимы переключать.
> Тулбар появился. Почему в мои предыдущие попытки не появлялся? Наверное страница бралась из кеша.
для дева и прода используются разные конфиги. Кстати, что меня раздражает то что они именно разные и по моему в них опции нельзя переопределять. хотя я не уверен, может я что-то путаю. Проверь сам, нет ли там копипасты.
> Как это сделать? Хочу подключить только нужные компоненты,
Как я понимаю, инсталлятор как раз ставит какие-то входные скрипты типа index.php или app.php + все эти модули. Тебе надо либо найти в нем опции для их отключения либо писать свой кернел, подключив только нужные модули. Увы, не помню точно как именно, но уверен что ты разберешься. Изучи код инсталляции пока.
> @Route("/test", name="test")
имей в виду что это неудобный (на мой взгляд) спосо и удобнее хранить роуты в yml файле, так как они все перед глазами будут.
> Погуглил по тексту ошибки, говорят поставить в web/app.php
скорее всего он не увидел твою аннотацию и надо что-то поменять что за это отвечает
> И тулбара внизу почему-то нет.
Он в конфиге по моему включается. Мне кстати не нравится.
> Как избавиться от dev?
Не скачивать dev версии
> Ссылку в студию.
Поищи тут для начала: http://symfony.com/doc/current/components/http_kernel/index.html
По идее тебе просто надо сделать свой index.php и свой кернел, вроде этого хватит. Вообще, я не помню наизучть что там еще надо. Разберись пока с инсталляцией из коробки и может поймешь.
>>728604
> По поводу dev, то это как я понял имеется ввиду именно режим симфони, а не версия пакета как подумалось вначале.
Это версия. dev значит скорее всего что она не очень стабильная либо это 3.0.4 с какими-то доп. доработками.
> Запускать нужно через app_dev.php (контроллер для режима разработчика)
Ну он по моему там только dev-режим включает и все. Ты можешь по идее и как-то по другому режимы переключать.
> Тулбар появился. Почему в мои предыдущие попытки не появлялся? Наверное страница бралась из кеша.
для дева и прода используются разные конфиги. Кстати, что меня раздражает то что они именно разные и по моему в них опции нельзя переопределять. хотя я не уверен, может я что-то путаю. Проверь сам, нет ли там копипасты.
> Как это сделать? Хочу подключить только нужные компоненты,
Как я понимаю, инсталлятор как раз ставит какие-то входные скрипты типа index.php или app.php + все эти модули. Тебе надо либо найти в нем опции для их отключения либо писать свой кернел, подключив только нужные модули. Увы, не помню точно как именно, но уверен что ты разберешься. Изучи код инсталляции пока.
Для доктрины в аннотациях, для роутов в ямле, мне кажется уже что-то одно надо использовать?
Хотя лично мне по-барабану, знать нужно все системы записи (в том числе ненавистный xml), а там
уже как тим решит, так все и будут писать.
Тулбар в prod-режиме не показывается (если я правильно понял методом тыка), только в dev.
Потому я его и не видел. Ну мне как начинающему приятно, такие красивые разноцветные плашечки ))), тебе может он и надоел.
> Как избавиться от dev?
>Не скачивать dev версии
Как не скачивать dev версии? До этого сколько ни устанавливал пакеты композером, всегда ставились стабильные, поэтому даже не задумывался.
Чо теперь делать? Что куда прописать чтобы устанавливалась stable версия?
На getcomposer залез, говорят что по-умолчанию должны ставиться только stable пакеты.
> This defines the default behavior for filtering packages by stability. This defaults to stable
https://getcomposer.org/doc/04-schema.md#minimum-stability
Ну действительно же так, всегда ставились пакеты без дев-суффикса, теперь только с симфони такой вопрос.
Может вот эту штуку куда-то прописать
https://getcomposer.org/doc/04-schema.md#prefer-stable
А куда?
>Увы, не помню точно как именно, но уверен что ты разберешься.
Вот ты хитрый дракон.
Помоги хоть разобраться с dev-пакетами композера. Потому что я не знаю куда гуглить. Нагуглил вот эти несколько директив, не факт что они
здесь нужны, и непонятно куда их писать. В командной строке composer install --stability stable как-то так, или в какой-то глобальный файл
конфига композера?
Попробовал
composer create-project --stability stable symfony/framework-standard-edition myapp "3.0.*"
Все равно ставит 3.0.4-dev
На данный момент меня все устраивает, но все равно хотелось бы разобраться. Что делать?
Пересмотрел лог установки, нет тут никаких dev-пакетов.
- Installing doctrine/lexer (v1.0.1)
Loading from cache
- Installing doctrine/annotations (v1.2.7)
Loading from cache
- Installing twig/twig (v1.24.0)
Loading from cache
- Installing symfony/polyfill-util (v1.1.1)
Loading from cache
- Installing paragonie/random_compat (v1.4.1)
Loading from cache
- Installing symfony/polyfill-php70 (v1.1.1)
Loading from cache
- Installing symfony/polyfill-php56 (v1.1.1)
Loading from cache
- Installing symfony/polyfill-mbstring (v1.1.1)
Loading from cache
- Installing symfony/symfony (v3.0.4)
Loading from cache
- Installing symfony/polyfill-intl-icu (v1.1.1)
Loading from cache
- Installing psr/log (1.0.0)
Loading from cache
- Installing doctrine/inflector (v1.1.0)
Loading from cache
- Installing doctrine/collections (v1.3.0)
Loading from cache
- Installing doctrine/cache (v1.6.0)
Loading from cache
- Installing doctrine/common (v2.6.1)
Loading from cache
- Installing jdorn/sql-formatter (v1.2.17)
Loading from cache
- Installing doctrine/doctrine-cache-bundle (1.3.0)
Loading from cache
- Installing doctrine/dbal (v2.5.4)
Loading from cache
- Installing doctrine/doctrine-bundle (1.6.2)
Loading from cache
- Installing doctrine/instantiator (1.0.5)
Loading from cache
- Installing doctrine/orm (v2.5.4)
Loading from cache
- Installing incenteev/composer-parameter-handler (v2.1.2)
Loading from cache
- Installing sensiolabs/security-checker (v3.0.2)
Loading from cache
- Installing sensio/distribution-bundle (v5.0.5)
Loading from cache
- Installing sensio/framework-extra-bundle (v3.0.16)
Loading from cache
- Installing monolog/monolog (1.18.1)
Loading from cache
- Installing symfony/monolog-bundle (v2.10.0)
Loading from cache
- Installing swiftmailer/swiftmailer (v5.4.1)
Loading from cache
- Installing symfony/swiftmailer-bundle (v2.3.11)
Loading from cache
- Installing sensio/generator-bundle (v3.0.6)
Loading from cache
- Installing symfony/phpunit-bridge (v3.0.4)
Loading from cache
Для доктрины в аннотациях, для роутов в ямле, мне кажется уже что-то одно надо использовать?
Хотя лично мне по-барабану, знать нужно все системы записи (в том числе ненавистный xml), а там
уже как тим решит, так все и будут писать.
Тулбар в prod-режиме не показывается (если я правильно понял методом тыка), только в dev.
Потому я его и не видел. Ну мне как начинающему приятно, такие красивые разноцветные плашечки ))), тебе может он и надоел.
> Как избавиться от dev?
>Не скачивать dev версии
Как не скачивать dev версии? До этого сколько ни устанавливал пакеты композером, всегда ставились стабильные, поэтому даже не задумывался.
Чо теперь делать? Что куда прописать чтобы устанавливалась stable версия?
На getcomposer залез, говорят что по-умолчанию должны ставиться только stable пакеты.
> This defines the default behavior for filtering packages by stability. This defaults to stable
https://getcomposer.org/doc/04-schema.md#minimum-stability
Ну действительно же так, всегда ставились пакеты без дев-суффикса, теперь только с симфони такой вопрос.
Может вот эту штуку куда-то прописать
https://getcomposer.org/doc/04-schema.md#prefer-stable
А куда?
>Увы, не помню точно как именно, но уверен что ты разберешься.
Вот ты хитрый дракон.
Помоги хоть разобраться с dev-пакетами композера. Потому что я не знаю куда гуглить. Нагуглил вот эти несколько директив, не факт что они
здесь нужны, и непонятно куда их писать. В командной строке composer install --stability stable как-то так, или в какой-то глобальный файл
конфига композера?
Попробовал
composer create-project --stability stable symfony/framework-standard-edition myapp "3.0.*"
Все равно ставит 3.0.4-dev
На данный момент меня все устраивает, но все равно хотелось бы разобраться. Что делать?
Пересмотрел лог установки, нет тут никаких dev-пакетов.
- Installing doctrine/lexer (v1.0.1)
Loading from cache
- Installing doctrine/annotations (v1.2.7)
Loading from cache
- Installing twig/twig (v1.24.0)
Loading from cache
- Installing symfony/polyfill-util (v1.1.1)
Loading from cache
- Installing paragonie/random_compat (v1.4.1)
Loading from cache
- Installing symfony/polyfill-php70 (v1.1.1)
Loading from cache
- Installing symfony/polyfill-php56 (v1.1.1)
Loading from cache
- Installing symfony/polyfill-mbstring (v1.1.1)
Loading from cache
- Installing symfony/symfony (v3.0.4)
Loading from cache
- Installing symfony/polyfill-intl-icu (v1.1.1)
Loading from cache
- Installing psr/log (1.0.0)
Loading from cache
- Installing doctrine/inflector (v1.1.0)
Loading from cache
- Installing doctrine/collections (v1.3.0)
Loading from cache
- Installing doctrine/cache (v1.6.0)
Loading from cache
- Installing doctrine/common (v2.6.1)
Loading from cache
- Installing jdorn/sql-formatter (v1.2.17)
Loading from cache
- Installing doctrine/doctrine-cache-bundle (1.3.0)
Loading from cache
- Installing doctrine/dbal (v2.5.4)
Loading from cache
- Installing doctrine/doctrine-bundle (1.6.2)
Loading from cache
- Installing doctrine/instantiator (1.0.5)
Loading from cache
- Installing doctrine/orm (v2.5.4)
Loading from cache
- Installing incenteev/composer-parameter-handler (v2.1.2)
Loading from cache
- Installing sensiolabs/security-checker (v3.0.2)
Loading from cache
- Installing sensio/distribution-bundle (v5.0.5)
Loading from cache
- Installing sensio/framework-extra-bundle (v3.0.16)
Loading from cache
- Installing monolog/monolog (1.18.1)
Loading from cache
- Installing symfony/monolog-bundle (v2.10.0)
Loading from cache
- Installing swiftmailer/swiftmailer (v5.4.1)
Loading from cache
- Installing symfony/swiftmailer-bundle (v2.3.11)
Loading from cache
- Installing sensio/generator-bundle (v3.0.6)
Loading from cache
- Installing symfony/phpunit-bridge (v3.0.4)
Loading from cache
$query->debugDumpParams() от такое выводит:
>SQL: [94] SELECT name, surname, sgroup, points FROM students ORDER BY :sortColumn DESC LIMIT 50 OFFSET 0 Params: 1 Key: Name: [11] :sortColumn paramno=-1 name=[11] ":sortColumn" is_param=1 param_type=2
Спасибо
Создал спец страницу, где сделал вывод пользователей(даже постранично), у которых отмечено галкой, что они этого хотят. Всё выводится, всё ок. Но!
Мне нужно сделать фильтр по сортировке этих пользователей в зависимости от их дополнительных полей. Допустим пусть будут поля - город, возраст, пол. Ну для примера. Т.е. например, выбираем Москва, и у нас на странице только пользователи из Москвы и т.д.
И как мне такой фильтр сделать на странице? Нужно будет писать запросы к базе данных? Аноны, подскажите, я просто в тупике. Не понимаю, дальше то что делать. Гуглил, но что-то ничего не нашёл по теме.
https://github.com/fidnex/students
По ООП ли на этот раз?
Перед этим разобрал решение братишки и в целом вроде бы разобрался в том, как что должно идти, как наследовать, какие методы делать защищёнными, а какие публичными.
Не совсем понятно в этой ситуации, для чего и как делать класс Компания? Какой должен быть у неё экземпляр, например, куча департаментов просто?
Опять же не понимаю, как антикризисные меры к этому всему присобачить.
Объясните вот этот конкретный момент, пожалуйста.
>>729027
>>729032
>>729033
Бамплимит же есть...
Вот этот вариант всё равно оставляет ссылки.
http://ideone.com/0ywS0T
Что тут надо переделать?
>> Или же сделать тоже самое только обернуть это в функцию? У меня уже есть такая, только она подходит для проверки формы, а не студента
>> https://github.com/someApprentice/Students/blob/master/app/Model/Validators/Validations.php#L151
>Я вижу тут копипасту. Ее надо заменить циклом. Например, сделать массив в котором хранятся имена полей и функции-валидаторы для них.
В свойствах не получается хранить функции https://ideone.com/hLkjWc
Еще не понятно как передавать аргументы в функции хранящиеся в массиве.
>>Профессиональное программирование на PHP Джордж Шлосснейгл
Ищу литературу читать по дороге на работу. Конечная цель - написать с нуля портал, включающий в себя новостной блок, форум (похожий на вакабу) и магазин. Освоил начальные знания html и css, хочу взяться за php, затем sql и js.
Буду очень благодарен, если подскажите на что делать упор и какая литература будет актуальна для меня.
Спасибо
preg_replace('/,[^ ]/',', ',$text);
то символ "не пробел" заменится на пробел и я получу уже другую ошибку, в следующем слове.
и маленький бонусный вопрос: с какой версии пхп конструкция вида
$buf = reset($array)['key'];
стала рабочей?
если $array представляет из себя массив массивов, например
$array = array(array('key'=>123,'key2'=>321),array('key'=>111,'key2'=>222));
то reset($array) вернёт первый элемент массива $array, а reset($array)['key] вернет элемент с ключом 'key' из первого элемента массива $array. на старых версиях пхп это не работет
Ясно, спасибо.
думаю что у них своя бд
>>721977
>>722020
Все верно, только массив $a я бы назвал как $syllables (слоги)
>>722045
К какой это задаче? Если к этой "выведи таблицу умножения, первое число меняется от 1 до 4, второе от 1 до 5" то неверно, если к этой "А теперь передай так, чтобы второй множитель был строго больше первого." то тоже не то.
>>722276
> Проверил - работает. Может это и костыль, мне почем знать?
Не используй getPartialReference а используй обычный getReference
>>722329
> Я правильно понимаю, что __call сработает только при обращении к несуществующему методу? Но ведь метод существует, так как мы пришли к выводу что прокси наследуется от сущности.
Нет, я хотел сказать что если в оригинальной модели есть __call то в прокси тоже надо его переопределить иначе возможен вызов метода __call из оригинальной модели до загрузки данных в нее.
>>В контексте объекта
> Что такое контекст объекта?
В данном случае контекст это объект на котором вызван метод. То есть в коде $obj->callSomeMethod() это $obj.
> Но большинство разумеется не будет задрачивать всю эту чушь, лезть в исходные коды и т.д., поэтому люди и ищут готовые рецепты или скринкасты уровня "покажи мне куда тыкать".
Они бросят доктрину и перейдут на что-нибудь полегче.
думаю что у них своя бд
>>721977
>>722020
Все верно, только массив $a я бы назвал как $syllables (слоги)
>>722045
К какой это задаче? Если к этой "выведи таблицу умножения, первое число меняется от 1 до 4, второе от 1 до 5" то неверно, если к этой "А теперь передай так, чтобы второй множитель был строго больше первого." то тоже не то.
>>722276
> Проверил - работает. Может это и костыль, мне почем знать?
Не используй getPartialReference а используй обычный getReference
>>722329
> Я правильно понимаю, что __call сработает только при обращении к несуществующему методу? Но ведь метод существует, так как мы пришли к выводу что прокси наследуется от сущности.
Нет, я хотел сказать что если в оригинальной модели есть __call то в прокси тоже надо его переопределить иначе возможен вызов метода __call из оригинальной модели до загрузки данных в нее.
>>В контексте объекта
> Что такое контекст объекта?
В данном случае контекст это объект на котором вызван метод. То есть в коде $obj->callSomeMethod() это $obj.
> Но большинство разумеется не будет задрачивать всю эту чушь, лезть в исходные коды и т.д., поэтому люди и ищут готовые рецепты или скринкасты уровня "покажи мне куда тыкать".
Они бросят доктрину и перейдут на что-нибудь полегче.
> Найдя более минималистичный бесплатный плагин я начал его колхозить, начав с функциональности, которую реквестила большая часть комментаторов плагина
Ты проверил под какой лицензией был исходный плагин? Нельзя просто так брать и переделывать код из интернета не глядя на лицензии.
> Достаточно-ли такого портфолио, чтобы вкатиться
Я не знаю какие требования у ваших контор. Почему бы тебе не посмотреть вакансии?
> Там иногда даже учить берутся, правда без какой-либо оплаты в течение месяца.
Сомневаюсь что есть смысл так как сам ты можешь больше времени уделять учебе чем работая.
Если то что ты описал это твой уровень знаний то считай что ты ничего дальше основ не освоил. Конечно есть шанс найти какую-то неквалифицированную работу по натягиванию верстки на CMS но почему бы не разобраться получше в веб-разработке? Оп пост содержит задачи по самым разным темам.
>>722420
Ты пробовал писать на Го? Я пробовал и не представляю как на этом улучшенном Си писать что-то большое и сложное, так как там даже классов нет. Как он заменит пхп если там ноль фреймворков для веб-разработки?
>>722490
> for ($months = 2,
почему начинается с 2?
> for ($months = 2, $creditDebt = $creditDebt + $separateChunk, $paymentTotal = 0; $months <= 20; $months ++) {
Слишком длинно, надо либо вынести часть кода отсюда либо разбить на 3 строки
> ($creditDebt * $percent) + $serviceFee
Повторяется 3 раза, подумай как обойтись без повторов
Если подставить сумму кредита в 1000 то считается неправильно: http://ideone.com/BHbFCb - в софтобанке должно быть 2030.
> Найдя более минималистичный бесплатный плагин я начал его колхозить, начав с функциональности, которую реквестила большая часть комментаторов плагина
Ты проверил под какой лицензией был исходный плагин? Нельзя просто так брать и переделывать код из интернета не глядя на лицензии.
> Достаточно-ли такого портфолио, чтобы вкатиться
Я не знаю какие требования у ваших контор. Почему бы тебе не посмотреть вакансии?
> Там иногда даже учить берутся, правда без какой-либо оплаты в течение месяца.
Сомневаюсь что есть смысл так как сам ты можешь больше времени уделять учебе чем работая.
Если то что ты описал это твой уровень знаний то считай что ты ничего дальше основ не освоил. Конечно есть шанс найти какую-то неквалифицированную работу по натягиванию верстки на CMS но почему бы не разобраться получше в веб-разработке? Оп пост содержит задачи по самым разным темам.
>>722420
Ты пробовал писать на Го? Я пробовал и не представляю как на этом улучшенном Си писать что-то большое и сложное, так как там даже классов нет. Как он заменит пхп если там ноль фреймворков для веб-разработки?
>>722490
> for ($months = 2,
почему начинается с 2?
> for ($months = 2, $creditDebt = $creditDebt + $separateChunk, $paymentTotal = 0; $months <= 20; $months ++) {
Слишком длинно, надо либо вынести часть кода отсюда либо разбить на 3 строки
> ($creditDebt * $percent) + $serviceFee
Повторяется 3 раза, подумай как обойтись без повторов
Если подставить сумму кредита в 1000 то считается неправильно: http://ideone.com/BHbFCb - в софтобанке должно быть 2030.
> if ($months == 2) {
> $creditDebt = $creditDebt + 7777;
Не имеет смысла так писать. Если ты что-то хочешь сделать на первом шаге то проще это поставить перед циклом. Цикл для повторяющихся действий, а не для одноразовых.
>>722547
Твой код странный. Почему у тебя htmlspecialchars не в шаблоне используется? Зачем это вообще?
Советую решить эту задачу: https://github.com/codedokode/pasta/blob/master/soft/web-server.md#Экранирование
>>722550
У тебя каша в голове. А что если это поле комментария куда можно вводить любые символы? использовать htmlspecialchars надо только в шаблоне в момент вывода.
>>722609
тред удален
>>722642
А ты пробовал подумать почему? Например что нужно чтобы запустилась программа написанная под определенную ОС и платформу (например windows на x86)? ЧТо предоставляет эта ОС и чего нет в других? Подсказка: форматы исполняемых файлов, системные вызовы, архитектура процессора, среда исполнения (runtime), динамические библиотеки, системные файлы, демоны.
Да и есть ли смысл запускать модильное приложение на десктопе или наоборот? Им очень неудобно пользоваться.
Нужно определять в классе все поля чтобы было видно что они есть и чтоыб к ним можно было обращаться даже без инициализации.
>>722697
По идее в классе должен быть определен список полей. Это дает возможность посмотреть на определение класса и понять какие в нем поля.
Это ошибка php что он позволяет легко динамичечки добавлять новые поля. Из-за этого при опечатке в имени поля не будет выведено никакой ошибки.
>>722991
Мало кто доделывает - люди находят работу и у них нет времени на наш тредик. ну или может они бросают изучение и становятся NEET, кто знает. Мы никому не навязываем темпы изучения, можно хоть год одну здачу решать если вас это устраивает.
>>723189
> while ($n<1000000){
Откуда взалось число 1 млн? непонятно.
Код очень плохо читать, не говорящие ничего названия переменных, массивы сложной структуры, код с большой глубиной вложенности.
Циклы абсолютно непонятные. Ну например внешний цикл:
> $n = 1;
> while ($n<1000000){
> ....
> $n = count($resultListOfBill);
> }
Как это понимать? Я понимаю так что тут потенциально бесконечный цикл. Так как $n всегда наверно будет меньше миллиона и он никогда не завершится. Также, тут куч однобуквенных пременнх.
Не, код надо отрефакторить - в таком виде это максимум черновик, а не готовое решение. Надо структурировать код, может можно что-то убрать, что-то вынести в функции, где-то переименовать переменные.
Я вообще не понимаю как он работает. Наверняка там куча лишних действий которые можно убрать. Ты сам-то можешь объяснить?
>>723949
Там плохой код. В конце - самый ад. Человек со знаниями уровня хелло ворлд пытается написать соцсеть.
>>723956
Не знаю, попробуй обе и выбери ту что больше нравится
>>724103
Это закрытая информация
Нужно определять в классе все поля чтобы было видно что они есть и чтоыб к ним можно было обращаться даже без инициализации.
>>722697
По идее в классе должен быть определен список полей. Это дает возможность посмотреть на определение класса и понять какие в нем поля.
Это ошибка php что он позволяет легко динамичечки добавлять новые поля. Из-за этого при опечатке в имени поля не будет выведено никакой ошибки.
>>722991
Мало кто доделывает - люди находят работу и у них нет времени на наш тредик. ну или может они бросают изучение и становятся NEET, кто знает. Мы никому не навязываем темпы изучения, можно хоть год одну здачу решать если вас это устраивает.
>>723189
> while ($n<1000000){
Откуда взалось число 1 млн? непонятно.
Код очень плохо читать, не говорящие ничего названия переменных, массивы сложной структуры, код с большой глубиной вложенности.
Циклы абсолютно непонятные. Ну например внешний цикл:
> $n = 1;
> while ($n<1000000){
> ....
> $n = count($resultListOfBill);
> }
Как это понимать? Я понимаю так что тут потенциально бесконечный цикл. Так как $n всегда наверно будет меньше миллиона и он никогда не завершится. Также, тут куч однобуквенных пременнх.
Не, код надо отрефакторить - в таком виде это максимум черновик, а не готовое решение. Надо структурировать код, может можно что-то убрать, что-то вынести в функции, где-то переименовать переменные.
Я вообще не понимаю как он работает. Наверняка там куча лишних действий которые можно убрать. Ты сам-то можешь объяснить?
>>723949
Там плохой код. В конце - самый ад. Человек со знаниями уровня хелло ворлд пытается написать соцсеть.
>>723956
Не знаю, попробуй обе и выбери ту что больше нравится
>>724103
Это закрытая информация
> Потому что только слово "of" вернет 16 000 записей, сфинкс такое не осилит.
Печально как-то конечно. Вообще, of это стоп слово которое надо обрабатывать в частном порядке. У обычных слов вариантов должно быть меньше.
Вообще если тебе интересна максимальная производительность может выгоднее держать словарь или хотя бы индекс поиска по нему в памяти - ну например представь демон который держит словрь в памяти, ждет пока в Бд появится новый текст и сразу же его переводит. Но с другой стороны, сфинкс по сути и есть такой демон, держащий индекс в памяти, хотя он и медленнее из-за необходимости слать запросы и ответы. Я не удивлюсь если там значительное время уходит именно на обмен данными со сфинксом и mysql.
Так что я бы пока делал без прицела на максимальную производиткльность. Ну пусть он там 10 секунд будет переводить, ну и ладно. Лишь бы интерфейс на 10 секунд не зависал.
> Поиск по слову "fish" вернет "fishing", "neither fish nor flesh", "be a fish out of water" и еще если не ошибаюсь 265 вариантов. Я ставлю перед собой задачу показать пользователю перевод крылатой фразы "neither fish nor flesh", значит придется перебирать через strpos все 265 вариантов.
Не уверен в strpos. Надо регистр не забывать, также, слово может быть написано в другом варианте:
словарь - to be aware
текст - he was aware of ...
словарь - to get used to
текст - he got used to ...
словарь - to do somebody's best
текст - I'll do my best
Это конечно можно делать во вторую очередь, такие случаи. Например можно в первую очередь искать рядом неизменные слова, а потом искать возможные формы изменяемых слов.
> Тогда скорость относительно неплохая, я там слегка оптимизировал, получилось уже 5 секунд обрабатывает большой текст из ~1000 слов.
Можно текст разбивать на предложения и переводить по предложениям еще. Ну и 5 секунд это немного, если это делается 1 раз при загрузке текста.
Также, можно посмотреть на готовые решения - вдруг что есть (не факт конечно):
https://en.wikipedia.org/wiki/Comparison_of_machine_translation_applications
http://linuxgazette.net/152/oregan.html
https://www.google.ru/search?q=open+source+translation&btnG=Поиск&newwindow=1&gbv=1
> Дык я залил такой словарь, что в нем даже поиск по "нормальным" словам возвращает по 500-1000 записей.
Это поиск именно по словам и выражениям, а не по примерам фраз в статьях? Ты правильно индексы настроил в сфинксе?
> Классная вещь ООП кстати, я тут обнаружил, что мне вообще не нужно переделывать ни строчки кода
Так и задумывалось.
> Потому что только слово "of" вернет 16 000 записей, сфинкс такое не осилит.
Печально как-то конечно. Вообще, of это стоп слово которое надо обрабатывать в частном порядке. У обычных слов вариантов должно быть меньше.
Вообще если тебе интересна максимальная производительность может выгоднее держать словарь или хотя бы индекс поиска по нему в памяти - ну например представь демон который держит словрь в памяти, ждет пока в Бд появится новый текст и сразу же его переводит. Но с другой стороны, сфинкс по сути и есть такой демон, держащий индекс в памяти, хотя он и медленнее из-за необходимости слать запросы и ответы. Я не удивлюсь если там значительное время уходит именно на обмен данными со сфинксом и mysql.
Так что я бы пока делал без прицела на максимальную производиткльность. Ну пусть он там 10 секунд будет переводить, ну и ладно. Лишь бы интерфейс на 10 секунд не зависал.
> Поиск по слову "fish" вернет "fishing", "neither fish nor flesh", "be a fish out of water" и еще если не ошибаюсь 265 вариантов. Я ставлю перед собой задачу показать пользователю перевод крылатой фразы "neither fish nor flesh", значит придется перебирать через strpos все 265 вариантов.
Не уверен в strpos. Надо регистр не забывать, также, слово может быть написано в другом варианте:
словарь - to be aware
текст - he was aware of ...
словарь - to get used to
текст - he got used to ...
словарь - to do somebody's best
текст - I'll do my best
Это конечно можно делать во вторую очередь, такие случаи. Например можно в первую очередь искать рядом неизменные слова, а потом искать возможные формы изменяемых слов.
> Тогда скорость относительно неплохая, я там слегка оптимизировал, получилось уже 5 секунд обрабатывает большой текст из ~1000 слов.
Можно текст разбивать на предложения и переводить по предложениям еще. Ну и 5 секунд это немного, если это делается 1 раз при загрузке текста.
Также, можно посмотреть на готовые решения - вдруг что есть (не факт конечно):
https://en.wikipedia.org/wiki/Comparison_of_machine_translation_applications
http://linuxgazette.net/152/oregan.html
https://www.google.ru/search?q=open+source+translation&btnG=Поиск&newwindow=1&gbv=1
> Дык я залил такой словарь, что в нем даже поиск по "нормальным" словам возвращает по 500-1000 записей.
Это поиск именно по словам и выражениям, а не по примерам фраз в статьях? Ты правильно индексы настроил в сфинксе?
> Классная вещь ООП кстати, я тут обнаружил, что мне вообще не нужно переделывать ни строчки кода
Так и задумывалось.
изучай доки по апачу и по дрективам которые ты использовал. По моему проще выделить отдельно публичную папку и отдавать только файлы из нее.
>>725415
Не все так радужно. Интеграционный тест не обязательно получается простым и не факт что быстро пишется. Плюс ему нужно определенное состояние приложения (например записи в БД).
>>725630
> задачу про Йоду.
> $deleteComma = str_replace(',', '', $text);//Удаляем запятые
> $deleteSpace = trim($implode)
> $dot = $deleteSpace . ". ";
В таких случаях не надо заводить новую переменную.
Сам алгоритм верный.
>>725687
Задача на проверку номера - выглядит верно, плохо что ты не проверяешь ее работу по массиву номеров из учебника.
Задача про автозамену:
> $regExp = '/дурак|дурaк|д у р а к/ui';
А что если там написано например 'ду рак'? Или часть букв латинская?
> if (preg_match($regExp, $text)) {
Можно без ифа, только preg_replace обойтись
Задача про емайлы
> '/([a-zA-Z0-9+.-]+)@([a-z.-]+)/';
В имени домена тоже могут быть цифры. В имени пользователя - подчеркивание.
> PHP Notice: Undefined offset: 2 in /home/bHI4Lm/prog.php on line 7
И эту ошибку надо исправить. Скорее всего виновато условие <= в цикле.
>>725786
Это удобная константа так как позволяет использовать имя класса как индетификатор чего-ниьудь.
>>726055
Попробуй сделать поиск или на сайте ларавела поискать примеры проектов.
>>726350
Отладчиком (Ctrl + Shift + i) посмотри.
изучай доки по апачу и по дрективам которые ты использовал. По моему проще выделить отдельно публичную папку и отдавать только файлы из нее.
>>725415
Не все так радужно. Интеграционный тест не обязательно получается простым и не факт что быстро пишется. Плюс ему нужно определенное состояние приложения (например записи в БД).
>>725630
> задачу про Йоду.
> $deleteComma = str_replace(',', '', $text);//Удаляем запятые
> $deleteSpace = trim($implode)
> $dot = $deleteSpace . ". ";
В таких случаях не надо заводить новую переменную.
Сам алгоритм верный.
>>725687
Задача на проверку номера - выглядит верно, плохо что ты не проверяешь ее работу по массиву номеров из учебника.
Задача про автозамену:
> $regExp = '/дурак|дурaк|д у р а к/ui';
А что если там написано например 'ду рак'? Или часть букв латинская?
> if (preg_match($regExp, $text)) {
Можно без ифа, только preg_replace обойтись
Задача про емайлы
> '/([a-zA-Z0-9+.-]+)@([a-z.-]+)/';
В имени домена тоже могут быть цифры. В имени пользователя - подчеркивание.
> PHP Notice: Undefined offset: 2 in /home/bHI4Lm/prog.php on line 7
И эту ошибку надо исправить. Скорее всего виновато условие <= в цикле.
>>725786
Это удобная константа так как позволяет использовать имя класса как индетификатор чего-ниьудь.
>>726055
Попробуй сделать поиск или на сайте ларавела поискать примеры проектов.
>>726350
Отладчиком (Ctrl + Shift + i) посмотри.
> if($number == 0){
> $array[] = $numbertoWords[$number];
Тут проще сразу ретурн поставить, зачем дальше что-то делать?
> $array[] = zeroinNumbers($hundreds, $numbertoWords); //Сотни
При отстутвии сотен добавляется пустой элемент, плохо, лучше делать проверку ифом.
> elseif((($lastDigit == 4) && ($last2Digits !==14)) || (($lastDigit == 3) && ($last2Digits !== 13))|| (($lastDigit == 2) && ($last2Digits !== 12))){
Нельзя как-то проще? Например проверять на 11-19 ифом выше?
> $result = preg_split('/,/',$format);
Не надо с числами работать как со строками. Испьзуй математику.
> $inclineRubles = inclineWord($result[0], "рубль ", "рубля ", "рублей ");
> $array[] = spellSmallNumber($result[1]) . $inclineRubles;
Посторяется 4 раза, избавься от копипасты.
>>726520
Погугли про reflection еще
>>727212
А я не разбирал задачу? Что-то код знакомый
> protected $listOfWorkers = array();
> protected $numberOfWorkers = 0;
Тут дубливроание данных - число рабочих можно определить из массива и незачем его хранить отдельно.
> И вот вопрос, как лучше всего складывать данные в класс, так чтобы они могли удовлетворять любым новым условиям?
Не дублировать данные. Хранить минимально возможный набор данных и не хранить то, что можно вычислить на месте. Сделать чтобы каждый объект занимался только своим делом.
Ну и если ты сделал логичную объектную модель то как правило и код с ней пишется легко. И наоброт.
> public function summonWorkers( $quantity, $type, $rank, $leadership )
Это не задача департамента создавать работников.
> case 1:
для обозначения типа надо использовать константы (или лучше имена классов), а не непонятные цифры.
> public function getWorkersInfos()
Странная функция которая ничего не возвращает.
>>727720
str_split не работает с русскими буквами, как и многие другие функции - урок https://gist.github.com/codedokode/ff99e357e9860ea169b8
> if($number == 0){
> $array[] = $numbertoWords[$number];
Тут проще сразу ретурн поставить, зачем дальше что-то делать?
> $array[] = zeroinNumbers($hundreds, $numbertoWords); //Сотни
При отстутвии сотен добавляется пустой элемент, плохо, лучше делать проверку ифом.
> elseif((($lastDigit == 4) && ($last2Digits !==14)) || (($lastDigit == 3) && ($last2Digits !== 13))|| (($lastDigit == 2) && ($last2Digits !== 12))){
Нельзя как-то проще? Например проверять на 11-19 ифом выше?
> $result = preg_split('/,/',$format);
Не надо с числами работать как со строками. Испьзуй математику.
> $inclineRubles = inclineWord($result[0], "рубль ", "рубля ", "рублей ");
> $array[] = spellSmallNumber($result[1]) . $inclineRubles;
Посторяется 4 раза, избавься от копипасты.
>>726520
Погугли про reflection еще
>>727212
А я не разбирал задачу? Что-то код знакомый
> protected $listOfWorkers = array();
> protected $numberOfWorkers = 0;
Тут дубливроание данных - число рабочих можно определить из массива и незачем его хранить отдельно.
> И вот вопрос, как лучше всего складывать данные в класс, так чтобы они могли удовлетворять любым новым условиям?
Не дублировать данные. Хранить минимально возможный набор данных и не хранить то, что можно вычислить на месте. Сделать чтобы каждый объект занимался только своим делом.
Ну и если ты сделал логичную объектную модель то как правило и код с ней пишется легко. И наоброт.
> public function summonWorkers( $quantity, $type, $rank, $leadership )
Это не задача департамента создавать работников.
> case 1:
для обозначения типа надо использовать константы (или лучше имена классов), а не непонятные цифры.
> public function getWorkersInfos()
Странная функция которая ничего не возвращает.
>>727720
str_split не работает с русскими буквами, как и многие другие функции - урок https://gist.github.com/codedokode/ff99e357e9860ea169b8
> $sentence2 = reverseSentence ($value);
> $sentence3 = firstLetterUpper ($sentence2);
> $a = makeTextlower ($text);
> $b = reverseText ($a);
Тут не надо создавать новые переменные, а надо использовать исходную.
> $lowerText = mb_strtolower($text);
> return ($lowerText);
не очень понятно зачем делать функцю единственной задачей которой является вызов mb_strtolower
> return ($result);
Скобки не нужны
> PHP Notice: Undefined variable: result in /home/Z7XqlX/prog.php on line 33
Надо исправить
>>727889
> 1-й, работает http://ideone.com/JEAitM
> $regexp2="/[\s]+,|,[\s]+/u";
А это сработает если пробелы стоят и до и после запятой? По моему нет. А если запятая без пробелов рядом?
> for ($i = 0; $i < $count; $i++) {
Тут лучше foreach
Так, алгоритм верный.
> 2-й, кривой http://ideone.com/aunZoy
> return implode(" ", $text);
в имплоде надо давать массив строк, а не строку $text.
> $sentence2 = reverseSentence ($value);
> $sentence3 = firstLetterUpper ($sentence2);
> $a = makeTextlower ($text);
> $b = reverseText ($a);
Тут не надо создавать новые переменные, а надо использовать исходную.
> $lowerText = mb_strtolower($text);
> return ($lowerText);
не очень понятно зачем делать функцю единственной задачей которой является вызов mb_strtolower
> return ($result);
Скобки не нужны
> PHP Notice: Undefined variable: result in /home/Z7XqlX/prog.php on line 33
Надо исправить
>>727889
> 1-й, работает http://ideone.com/JEAitM
> $regexp2="/[\s]+,|,[\s]+/u";
А это сработает если пробелы стоят и до и после запятой? По моему нет. А если запятая без пробелов рядом?
> for ($i = 0; $i < $count; $i++) {
Тут лучше foreach
Так, алгоритм верный.
> 2-й, кривой http://ideone.com/aunZoy
> return implode(" ", $text);
в имплоде надо давать массив строк, а не строку $text.
> Модуль авторизации и контроля доступа даже ты кажется говорил что там слишком громоздкий и сложный, может посоветуешь прикрутить что-то вместо него?
Увы, нет. Для простых случаев наверно проще написать свой велосипед из нескольких функций.
>>оцениваем тяжесть запроса (число аггрегируемых записей)
> Каких?
Сколько записей придется обойти mysql чтобы посчитать статистику. Это и есть косвенный индикатор "тяжести" запроса.
> В общем, пока такая логика:
> при переходе на страницу теста "/test/test_NNN/quest_NNN" проверяем зарегистрирован ли пользователь.
ок
> Если нет, то создать пустого пользователя с токеном доступа (поле salt можно использовать с этой целью, когда пользователь зарегистрируется, сгенерировать заново соль и хеш) и повесить куки.
странно создавать пользователя посередине теста. Логичнее в начале.
> Кстати там пользователь походу может (ведь может?) вернуться назад и ответить по-другому? Значит нужно предусмотреть не только вставку, но и апдейт.
хорошая идея
> Возможно с каждым ответом на вопрос есть смысл обновлять attempts, типа обновлять поля со статистикой, которые я пока не предусмотрел
делай сначала нормализованную схему. Статистика это денормализация
> Кстати эти две страницы дублируют друг друга, тебе не кажется? На главной из доп.информации только "о сайте" и может быть другая сортировка записей в виджете (по популярности что ли)
На главной только несколько тестов для примера, а в /tests все
> Кстати знакомый фронтендщик когда ему показывал файлообменник, упрекал в многостраничности, на его взгляд там spa в самый раз;
На мой взгля он неправ. Я условно делю ресурсы на "сайты" и "приложения". Сайт это например блог, состоит из отдельных страниц, индексируется гуглом, применять спа смысла нет. Прилоежние это например редактор схем или google docs, часто закрыто извне, страницы очень интерактивныЮ пользователь проводит много времени - в таком случае можно применять спа.
Возможно он просто плохо знает серверный язык и пытается не связываться с ним и по максимум все делать на яваскрипте, или просто думает что спа это модно и надо пихать его везде. Я не вижу смысла делать так файлообменник так как типичный сценарий - перейти по ссылке и скачать файл, где тут приложение?
Спа это всегда усложнение. Ты вместо монолитного приложения делаешь два - клиентское и серверное, определяешь интерфейс между ними, пишешь кучу клиентского кода. Это больше работы, и в итоге наш сайт не сможет индексировать гугл. Зачем так делать?
> Что такое теги? Теги пользователь может создавать несмотря на активное подсовывание ему автодополнением уже существующих?
там не написано в задаче? Тогда пусть может создавать. С разумными ограничениями на число, длину, содержимое.
> Например дикая трехэтажная форма, где мы создаем сразу тест, вопросы к нему, варианты к вопросу.
Придется заморочиться с яваскриптом. Тест еще и редактировать можно.
> Модуль авторизации и контроля доступа даже ты кажется говорил что там слишком громоздкий и сложный, может посоветуешь прикрутить что-то вместо него?
Увы, нет. Для простых случаев наверно проще написать свой велосипед из нескольких функций.
>>оцениваем тяжесть запроса (число аггрегируемых записей)
> Каких?
Сколько записей придется обойти mysql чтобы посчитать статистику. Это и есть косвенный индикатор "тяжести" запроса.
> В общем, пока такая логика:
> при переходе на страницу теста "/test/test_NNN/quest_NNN" проверяем зарегистрирован ли пользователь.
ок
> Если нет, то создать пустого пользователя с токеном доступа (поле salt можно использовать с этой целью, когда пользователь зарегистрируется, сгенерировать заново соль и хеш) и повесить куки.
странно создавать пользователя посередине теста. Логичнее в начале.
> Кстати там пользователь походу может (ведь может?) вернуться назад и ответить по-другому? Значит нужно предусмотреть не только вставку, но и апдейт.
хорошая идея
> Возможно с каждым ответом на вопрос есть смысл обновлять attempts, типа обновлять поля со статистикой, которые я пока не предусмотрел
делай сначала нормализованную схему. Статистика это денормализация
> Кстати эти две страницы дублируют друг друга, тебе не кажется? На главной из доп.информации только "о сайте" и может быть другая сортировка записей в виджете (по популярности что ли)
На главной только несколько тестов для примера, а в /tests все
> Кстати знакомый фронтендщик когда ему показывал файлообменник, упрекал в многостраничности, на его взгляд там spa в самый раз;
На мой взгля он неправ. Я условно делю ресурсы на "сайты" и "приложения". Сайт это например блог, состоит из отдельных страниц, индексируется гуглом, применять спа смысла нет. Прилоежние это например редактор схем или google docs, часто закрыто извне, страницы очень интерактивныЮ пользователь проводит много времени - в таком случае можно применять спа.
Возможно он просто плохо знает серверный язык и пытается не связываться с ним и по максимум все делать на яваскрипте, или просто думает что спа это модно и надо пихать его везде. Я не вижу смысла делать так файлообменник так как типичный сценарий - перейти по ссылке и скачать файл, где тут приложение?
Спа это всегда усложнение. Ты вместо монолитного приложения делаешь два - клиентское и серверное, определяешь интерфейс между ними, пишешь кучу клиентского кода. Это больше работы, и в итоге наш сайт не сможет индексировать гугл. Зачем так делать?
> Что такое теги? Теги пользователь может создавать несмотря на активное подсовывание ему автодополнением уже существующих?
там не написано в задаче? Тогда пусть может создавать. С разумными ограничениями на число, длину, содержимое.
> Например дикая трехэтажная форма, где мы создаем сразу тест, вопросы к нему, варианты к вопросу.
Придется заморочиться с яваскриптом. Тест еще и редактировать можно.
у тебя все странно выглядит, я бы мог написать замечания к каждой строчке но некогда. Ну например почему у тебя всюду статические методы или что за странный класс table (и погчему он с маленькой буквы?). Почему имя класса GetLinks начинается с глагола?
Тебе надо разобраться с ООП, MVC, прежде чем браться писать такие приложения. В ОП посте есть задача про студентов с комментариями, почитай. Про ООП есть в моем учебнике в ОП посте.
> . Это правильно? Или надо передавать массив параметров в модель чтобы она меняла свое состояние сама?
А за что по твоему отвечает модель? Чем она отличается от контроллера?
> Делать запрос в модель для получения массива состояния вида, возвращать обновленный вид - это задача контроллера или модели?
А почему массив а не объект? Модель не должна знать ничего про view и потому возвращать его не может.
>>728820
проблемы из-за того что ты делаешь замену более одного раза. Второй раз ты правишь не исходный текст а HTML код. Должно быть одно преобразование вида
исходный текст -> размеченный HTML код
>>729102
__clone вызывается на копии объекта после выполнения операции клонирования. Его задача заменить ссылки на какие-то вложенные объекты на их клоны.
> foreach( $this->departmentList as $department )
> {
> $department = clone $department;
куда по твоему сохраняется клон департамента? Поле departmentList не меняется. Так как department это копия ссылки на объект и меняя ее ты не меняешь ссылку на старый объект в массиве
>>729208
В качестве значения по умолчанию нельзя использовать выражение. И по моему ты плохо понял что написал. Когда ты пишешь
$this->x = func();
ты не сохраняешь функцию в поле. ты сохраняешь то,что она вернула - число например.
Я имел в виду сделать например так:
$validate = [
'validateName' => 'getName',
....
];
foreach (...) {
...
$error = call_user_func([$this, $validator], $value);
...
}
> как передавать аргументы в функции хранящиеся в массиве.
Аргументы передаются в момент вызова, в скобках.
>>729240
старовата, но актуальные темы есть.
>>729279
Визуал студия не нужна, нужен небольшой пакет библитек от майкрософт. Досбокс не нужен, нужна командная строка. Инструкции в ОП посте, проще вряд ли получится.
у тебя все странно выглядит, я бы мог написать замечания к каждой строчке но некогда. Ну например почему у тебя всюду статические методы или что за странный класс table (и погчему он с маленькой буквы?). Почему имя класса GetLinks начинается с глагола?
Тебе надо разобраться с ООП, MVC, прежде чем браться писать такие приложения. В ОП посте есть задача про студентов с комментариями, почитай. Про ООП есть в моем учебнике в ОП посте.
> . Это правильно? Или надо передавать массив параметров в модель чтобы она меняла свое состояние сама?
А за что по твоему отвечает модель? Чем она отличается от контроллера?
> Делать запрос в модель для получения массива состояния вида, возвращать обновленный вид - это задача контроллера или модели?
А почему массив а не объект? Модель не должна знать ничего про view и потому возвращать его не может.
>>728820
проблемы из-за того что ты делаешь замену более одного раза. Второй раз ты правишь не исходный текст а HTML код. Должно быть одно преобразование вида
исходный текст -> размеченный HTML код
>>729102
__clone вызывается на копии объекта после выполнения операции клонирования. Его задача заменить ссылки на какие-то вложенные объекты на их клоны.
> foreach( $this->departmentList as $department )
> {
> $department = clone $department;
куда по твоему сохраняется клон департамента? Поле departmentList не меняется. Так как department это копия ссылки на объект и меняя ее ты не меняешь ссылку на старый объект в массиве
>>729208
В качестве значения по умолчанию нельзя использовать выражение. И по моему ты плохо понял что написал. Когда ты пишешь
$this->x = func();
ты не сохраняешь функцию в поле. ты сохраняешь то,что она вернула - число например.
Я имел в виду сделать например так:
$validate = [
'validateName' => 'getName',
....
];
foreach (...) {
...
$error = call_user_func([$this, $validator], $value);
...
}
> как передавать аргументы в функции хранящиеся в массиве.
Аргументы передаются в момент вызова, в скобках.
>>729240
старовата, но актуальные темы есть.
>>729279
Визуал студия не нужна, нужен небольшой пакет библитек от майкрософт. Досбокс не нужен, нужна командная строка. Инструкции в ОП посте, проще вряд ли получится.
>куда по твоему сохраняется клон департамента? Поле departmentList не меняется. Так как department это копия ссылки на объект
Вот так выглядит функция в учебнике.
function __clone()
{
$this->object1 = clone $this->object1;
}
То есть нам надо все поля-объекты вот так переприсвоить. Но массив это не объект, поэтому приходится переприсваивать в цикле. С одиночными объектами у меня всё работает, клон становится именно клоном. А вот с массивами не получается.
Я проверил почти все посты кроме вчерашних. Тем кому я не ответил тут я отвечу позже, в это треде. Если я вас пропустил - напомните о себе в новом треде.
>>729423
Да, в цикле, я написал выше: ты делаешь клон департамента но не заменяешь ссылку на него в $this->deamrtmentsList. надо сделать новый массив с клонами и заменить им старый.
>$validate = [
>'validateName' => 'getName',
>....
>];
А разве ключом не должно быть название поля? Как тогда обозначить ключ ошибки?
>>729418
>
>В качестве значения по умолчанию нельзя использовать выражение. И по моему ты плохо понял что написал. Когда ты пишешь
>
>$this->x = func();
>
>ты не сохраняешь функцию в поле. ты сохраняешь то,что она вернула - число например.
Нет нет, я понял что здесь подвох. Я просто не знал про функцию call_user_func.
а я уже учил массивы ифы кейсы и подобное в джаве >_<
на написание кода времени больше трачу чем на решение >_<
Я в правильном направлении?
http://ideone.com/asjGPa
echo "$sasamba";
echo "{$sasamba}";
> if (4 < $number % 100 && $number % 100 <= 19) {
> $word = $word5;
тут проще сразу писать return $word5;
> } elseif ($lastNum >= 5 || $lastNum == 0) {
Тут можно просто else
> если число между 11 и 19, то дальше переименовывать не нужно
> нижние две строчки копипастятся, это плохо?
нет, не плохо. Это лучше чем лишний увровень вложенности внутри else
> if (10 <= $lastTwoNum && $lastTwoNum <= 99) {
$lastTwoNum <= 99 выполняется всегда (это остаток от деления на 100) и не требует проверки. Как и $lastNum <= 9
> $pieceNumber = floor(intval($number/(pow(1000, $bit)))) % 1000;
Вообще откусывать разряды с конца можно проще:
$tail = $number % 1000;
$number = floor($number / 1000);
Условием продолжения цикла можно было поставить тогда $number > 0 и выкинуть пару переменных.
Так, в остальном, хорошо.
> log10(1000001)=7
Вообще, нет: http://ideone.com/JV47NM
> Операция суммирования чисел мне кажется более простой.
Ну хорошо, тут не обязательно именно логарифм использовать
>>728419
> Я правильно понимаю, если мы ее добавим, можно будет работать со строками?
> Например менять местами в таблице, при сортировке? Это возможность добавить функционал в будущем?
Вообще, в будущем это вряд ли понадобится. Но если тебе хочется сделать строку таблицы объектом, то это можно сделать.
> А почему класс СтрокаТаблицы без методов? В него можно положить методы padLeft, padRight?
Ну вроде по логике они к нему не относятся. Это методы работы со строками, а не с таблицей.
> Т.к. сейчас эти функции относятся к строке, но лежат в середине кода, и не понятно к чему относятся.
Обычно для них делают класс Util или StringUtil из статических методов. паттерн utility class: https://en.wikipedia.org/wiki/Utility_pattern
> Если мы добавляем класс Компания, разве не логичнее хранить значения и методы расчета в нем, а не в отдельно
> лежащей переменной в коде?
Можно и так
> class Company {
> public $name = "";
Не уверен что это поле нужно, так как название компании в задаче никак не используется.
> public $totalHours = 0; // Всего часов для всех работников
Вот это типичная ошибка. Ты сохраняешь в свойстве общее число отработанных часов. А если я теперь добавлю сотруднику еще часы:
$employee->hours[] = 10;
Эта сумма обновится? Не обновится.
У этого свойства проблема: оно показывает актуальную информацию только в промежутке между вызовом функции getTotalHours() и изменением данных сотрудника или доабвления нового сотрудника. То есть большую часть времени оно хранит неактуальные данные. Легко сделать ошибку, выведя эти неактуальные данные вместо актуальных.
Более того, это свойство избыточно, так как его всегда можно посчитать из массива сотрудников. Непонятно зачем его тогда хранить вообще. Удалив это свойство, мы заодно и решим проблему поддержания его в актуальном состоянии.
Почитай еще про побочные эффекты: https://github.com/codedokode/pasta/blob/master/good-code.md#Избегай-побочных-эффектов
> str_repeat(" ", $length-mb_strlen($string));
Тут может получиться отрицательное число при вычитании и будет ошибка.
В остальном, хорошо.
> if (4 < $number % 100 && $number % 100 <= 19) {
> $word = $word5;
тут проще сразу писать return $word5;
> } elseif ($lastNum >= 5 || $lastNum == 0) {
Тут можно просто else
> если число между 11 и 19, то дальше переименовывать не нужно
> нижние две строчки копипастятся, это плохо?
нет, не плохо. Это лучше чем лишний увровень вложенности внутри else
> if (10 <= $lastTwoNum && $lastTwoNum <= 99) {
$lastTwoNum <= 99 выполняется всегда (это остаток от деления на 100) и не требует проверки. Как и $lastNum <= 9
> $pieceNumber = floor(intval($number/(pow(1000, $bit)))) % 1000;
Вообще откусывать разряды с конца можно проще:
$tail = $number % 1000;
$number = floor($number / 1000);
Условием продолжения цикла можно было поставить тогда $number > 0 и выкинуть пару переменных.
Так, в остальном, хорошо.
> log10(1000001)=7
Вообще, нет: http://ideone.com/JV47NM
> Операция суммирования чисел мне кажется более простой.
Ну хорошо, тут не обязательно именно логарифм использовать
>>728419
> Я правильно понимаю, если мы ее добавим, можно будет работать со строками?
> Например менять местами в таблице, при сортировке? Это возможность добавить функционал в будущем?
Вообще, в будущем это вряд ли понадобится. Но если тебе хочется сделать строку таблицы объектом, то это можно сделать.
> А почему класс СтрокаТаблицы без методов? В него можно положить методы padLeft, padRight?
Ну вроде по логике они к нему не относятся. Это методы работы со строками, а не с таблицей.
> Т.к. сейчас эти функции относятся к строке, но лежат в середине кода, и не понятно к чему относятся.
Обычно для них делают класс Util или StringUtil из статических методов. паттерн utility class: https://en.wikipedia.org/wiki/Utility_pattern
> Если мы добавляем класс Компания, разве не логичнее хранить значения и методы расчета в нем, а не в отдельно
> лежащей переменной в коде?
Можно и так
> class Company {
> public $name = "";
Не уверен что это поле нужно, так как название компании в задаче никак не используется.
> public $totalHours = 0; // Всего часов для всех работников
Вот это типичная ошибка. Ты сохраняешь в свойстве общее число отработанных часов. А если я теперь добавлю сотруднику еще часы:
$employee->hours[] = 10;
Эта сумма обновится? Не обновится.
У этого свойства проблема: оно показывает актуальную информацию только в промежутке между вызовом функции getTotalHours() и изменением данных сотрудника или доабвления нового сотрудника. То есть большую часть времени оно хранит неактуальные данные. Легко сделать ошибку, выведя эти неактуальные данные вместо актуальных.
Более того, это свойство избыточно, так как его всегда можно посчитать из массива сотрудников. Непонятно зачем его тогда хранить вообще. Удалив это свойство, мы заодно и решим проблему поддержания его в актуальном состоянии.
Почитай еще про побочные эффекты: https://github.com/codedokode/pasta/blob/master/good-code.md#Избегай-побочных-эффектов
> str_repeat(" ", $length-mb_strlen($string));
Тут может получиться отрицательное число при вычитании и будет ошибка.
В остальном, хорошо.
> $op = $char;
> $number = 0;
> if($op == '='){
> echo $result;
Это копипаста. Повторяется аж 3 раза. Избавься от нее.
В остальном неплохо.
>>728599
Я посмотрел код, он выглядит вроде верно. Насчет учета количества купюр - там вроде мы в столбике в массиве мы учитываем каждый вид купюр только 1 раз и проверяем их количество, так что ошибки быть не должно.
Вот с комментариями конечно все равно печально - тут ключевой массив это $result и не объяснено:
- в выражении $results[$numBill][$currentAmount] что значит $numBills? Как я понимаю, это индекс максимальной использованной купюры
- не сказано что является значением элемента, как я понял - миним. число купюр которыми можно выдать сумму при условии использования купюр не старше $numBills
Это и надо было писать в комментариях.
Что касается генерации массива, там явно есть лишние элементы, например если у нас номинал всех купюр кратен 100, то нас интересуют только значения $currentAmount которые тоже кратны 100. Ленивая генерация массива решила бы проблему заполнения лишних элементов.
У тебя генерируется полный массив размером amount x numBills. Идея ленивой генерации в том что массив создается пустой и генерируются только те ячейки которые запрашиваются алгоритмом явно. В этом случае ячейки для сумм вроде 99 никогда не будут запрашиваться и массив получится меньше. Также, код вроде
> for ($currentAmount = 0; $currentAmount <= $amount; $currentAmount++) {
> $results[0][$currentAmount] = INF;
будет не нужен, так как мы можем заменить его на проверку:
function getArrayElement($amount, $numBills) {
if ($numBills == 0) {
return INF;
}
...
}
Правда тут есть подвох: придется использовать рекурсию, и есть риск получить большое число вложенных вызовов и превысить разрешенный лимит на глубину рекурсии.
> Сейчас, вначале генерируется не весь массив, в только часть
нет, генерируется весь массив размером numBIlls x amount
> Если вообще отказаться от записи промежуточных значений в массиве, то количество вычислений увеличится, т.к. для каждого номинала ф-я будет считать заново предыдущие номиналы.
А мы можем сохранять однажды вычисленное значение в массив и при повторном запросе возвращать его.
> Если проверять не значение элемента массива, а существует ли вообще элемент, т.е. != NULL ?
Да, только проверку делать чрез isset а не равенсто null так как нельзя обращаться к несуществующим элементам массива
> Но ведь сейчас он так и заполняется, по мере расчета, или нет?
Нет, сейчас он заполняется целиком до начала определения идеальной комбинации.
В общем, решение меня устраивает.
> $op = $char;
> $number = 0;
> if($op == '='){
> echo $result;
Это копипаста. Повторяется аж 3 раза. Избавься от нее.
В остальном неплохо.
>>728599
Я посмотрел код, он выглядит вроде верно. Насчет учета количества купюр - там вроде мы в столбике в массиве мы учитываем каждый вид купюр только 1 раз и проверяем их количество, так что ошибки быть не должно.
Вот с комментариями конечно все равно печально - тут ключевой массив это $result и не объяснено:
- в выражении $results[$numBill][$currentAmount] что значит $numBills? Как я понимаю, это индекс максимальной использованной купюры
- не сказано что является значением элемента, как я понял - миним. число купюр которыми можно выдать сумму при условии использования купюр не старше $numBills
Это и надо было писать в комментариях.
Что касается генерации массива, там явно есть лишние элементы, например если у нас номинал всех купюр кратен 100, то нас интересуют только значения $currentAmount которые тоже кратны 100. Ленивая генерация массива решила бы проблему заполнения лишних элементов.
У тебя генерируется полный массив размером amount x numBills. Идея ленивой генерации в том что массив создается пустой и генерируются только те ячейки которые запрашиваются алгоритмом явно. В этом случае ячейки для сумм вроде 99 никогда не будут запрашиваться и массив получится меньше. Также, код вроде
> for ($currentAmount = 0; $currentAmount <= $amount; $currentAmount++) {
> $results[0][$currentAmount] = INF;
будет не нужен, так как мы можем заменить его на проверку:
function getArrayElement($amount, $numBills) {
if ($numBills == 0) {
return INF;
}
...
}
Правда тут есть подвох: придется использовать рекурсию, и есть риск получить большое число вложенных вызовов и превысить разрешенный лимит на глубину рекурсии.
> Сейчас, вначале генерируется не весь массив, в только часть
нет, генерируется весь массив размером numBIlls x amount
> Если вообще отказаться от записи промежуточных значений в массиве, то количество вычислений увеличится, т.к. для каждого номинала ф-я будет считать заново предыдущие номиналы.
А мы можем сохранять однажды вычисленное значение в массив и при повторном запросе возвращать его.
> Если проверять не значение элемента массива, а существует ли вообще элемент, т.е. != NULL ?
Да, только проверку делать чрез isset а не равенсто null так как нельзя обращаться к несуществующим элементам массива
> Но ведь сейчас он так и заполняется, по мере расчета, или нет?
Нет, сейчас он заполняется целиком до начала определения идеальной комбинации.
В общем, решение меня устраивает.
> Для доктрины в аннотациях, для роутов в ямле, мне кажется уже что-то одно надо использовать?
ну мне например удобно именно так. Ты можешь делать как тебе удобнее.
> Тулбар в prod-режиме не показывается (если я правильно понял методом тыка), только в dev.
По моему это все же в конфигах задается и видимо в дев конфиге эта опция просто включена.
> Чо теперь делать? Что куда прописать чтобы устанавливалась stable версия?
Тут пишут http://symfony.com/doc/current/book/installation.html#creating-a-symfony-application-with-composer что composer create-project должен ставить стабильную версию. Ты как ставил?
Видимо dev тут значит не то, что это нестабильная версия, а что-то другое. Тут нет ответа?
http://symfony.com/doc/current/contributing/community/releases.html
http://symfony.com/roadmap
далее овтет тут, спамфильтр: http://pastebin.ru/cecYoQPo
>>728926
> И как мне такой фильтр сделать на странице? Нужно будет писать запросы к базе данных?
Не знаю. Если в вордпрессе есть готовый код сортировки и ты его используешь то может и нет.
Это не все, только что с первого взгляда видно
https://github.com/fidnex/students/blob/master/config.ini
В конфиг стоит выносить только то, что можно менять. Тип БД (mysql) вряд ли можно поменять на другой, так как код рассчитан на MySQL.
https://github.com/fidnex/students/blob/master/app/Controller/ControllerMain.php#L22
> $pagination = new PaginationHelper(
> $this->container['config']->getParam('items_on_page'),
> $this->container['studentTDG']->getTotalStudent($tableHelper->search
Это стоит выносить в переменные, а не городить такие сложные выражения
https://github.com/fidnex/students/blob/master/app/Model/Helper/TableHelper.php
тут есть обращения к GET.
https://github.com/fidnex/students/blob/master/app/Controller/ControllerForm.php
тут не надо копипастить выражение $this->container['formHelper'] а стоит либо сделать поле $this->formHelper либо переменную.
> $errors = $this->container['formHelper']->getValidError($student);
> if(!isset($error['email']) && !$isAuth && $this->container['studentTDG']->checkExistEmail($student->email)) {
> $errors['email'] = 'Введенный почтовый адрес уже зарегистрирован.';
А почему эта проверка не в валидаторе, а отдельно?
https://github.com/fidnex/students/blob/master/app/Model/Gateway/StudentTDG.php#L62
какой смысл в update делать lastInsertId?
> .'surname=:surname, '
> .'gender=:gender, '
> .'sgroup=:sgroup, '
В строках и запросах можно использовать перевод строки
> public function checkExistEmail($mail) {
При редактировании надо еще проверять что найденный емайл это не наш собственный email.
> SELECT COUNT() FROM students WHERE email = ? LIMIT 1
LIMIT применяется после COUNT и ничего не решает. Если ты хочешь оптимизировать то надо делать SELECT id ... LIMIT 1
https://github.com/fidnex/students/blob/master/app/Model/Gateway/StudentTDG.php#L104
> $query->bindValue(':search', '%'.$search.'%');
Тут будет ошибка если в запросе нет этого плейсхолдера
> $sql .= " ORDER BY $sortColumn $sortType LIMIT $limit OFFSET $offset";
Нет проверки подствляемых значений по белому списку - значит нельзя сказать что тут нет инъекции. Точнее она есть. LIMIT и OFFSEt надо подставлять через плейсхолдеры с указанием INT в bindValue.
https://github.com/fidnex/students/blob/master/app/Model/Helper/AuthHelper.php#L14
Тут надо использовать иф а не городить сложное выражение
> public function setToken($token) {
Мне кажется тут уместнее бы была функция вроде "залогинить студента". ну или хотя бы переименовать эту, а то она раскрывает наружу детали как работает автоизация, а лучше бы эти детали скрыть внутри класса.
> const TOKEN_NAME = 'auth';
Я бы сделал приватным статическим полем - чтобы снаружи не было доступно
> setcookie(AuthHelper::TOKEN_NAME, $token, $expire, '', '', false, true);
path надо стаивть в / иначе кука будет доступна не на всех старницах. Почитай за что отвечает path в куках.
> public function getNewToken() {
> return md5(md5(time()rand()).md5(chr(rand(0,90))));
какой смысл в нагромождении функций если они не увеличивают число вариантов? Время очень легко предугадать, по крайней мере примерно. Надежность низкая. У тебя токен который условно говоря принимает миллион значений, а с помощью md5 ты просто раздуваешь его размер не увеличивая число вариантов. md5 это не функция для генерации токенов. Она тут бесполезна.
Давай посмотрим реальную энтропию тут:
> rand()
дает 32 бита (число от 0 до 4 млрд)
> rand(0,90)
дает 7 бит
> time()
Допустим время регистрации мы можем угадать с точностью до месяца, так что случайным остается число секунд в месяце, это около 2 млн или 21 бит.
В сумме 60 бит энтропии. Для их записи хватит 10 символов (если каждый принимает 64 значения = 6 бит), а не 32.
И почитай еще https://github.com/codedokode/pasta/blob/master/good-code.md#Не-злоупотребляй-хешированием
https://github.com/fidnex/students/blob/master/app/Model/Helper/FormHelper.php#L69
> $student->$key = strval(trim($data[$key]));
Есть гарантия что в массиве есть это поле?
Имя и фамимлию кроме длины надо проверять на допустимые символы (буквы, дефис, апостроф, пробел)
Проверка уникальности email должна быть в валидаторе вместе с другими проверками.
> $this->formFields['gender']['pattern'] = '^('.Student::GENDER_MALE.'|'.Student::GENDER_FEMALE.')$';
Выглядит неуклюже. Не лучше ли тут через in_array делать проверку?
https://github.com/fidnex/students/blob/master/templates/footer.html
размещение стилей в футере приведет к тому что может отобразиться старница без стилей на какое-то время. размещение скриптов в футере чревато тем, что при использовании <button onсlick="fn()"> до загрузки скрипта мы получаем неработающую кнопку.
https://github.com/fidnex/students/tree/master/app/View
непонятно почему шаблоны раскиданы по 2 разным папкам . думаю, нет в этом смысла.
https://github.com/fidnex/students/blob/master/app/View/form.html#L8
> value='<?=$student->name;?>'
нет защиты от XSS
> pattern='<?=$formFields['surname']['pattern']?>'
Плохо что ты тут передаешь массивы сложной непонятной структуры. Лучше получать регулярку через метод. Также, ты используешь одну регулярку для HTML и для PHP, а ты в курсе что там немного разные диалекты регулярок?
> <div class="col-md-12 text-center">Все получилось.</div>
Для уведомлений в бутстрапе есть класс alert. Изучи возможности бутстрапа.
> <th><a href='<?=$tableHelper->getSortLink('name');?>'>Имя</a></th>
надо бы еще выделять колонку и направление сортировки например стрелочкой
> <?php foreach($students as $student):?>
> <td><?=$student->name;?></td>
Лучше бы выводить через htmlspecialchars
> <a href="<?=$tableHelper->getPaginationLink($i);
Символ & в ссылках надо выводить как & amp, например за счет htmlspecialchars
>>729042
> Не совсем понятно в этой ситуации, для чего и как делать класс Компания?
А как ты представишь сущность Компания в коде? Вот у тебя есть функция, которая что-то делает с Компанией (например применяет антикризисные меры). Что ты в нее передашь? Или функция создания компании - что она вернет?
> Опять же не понимаю, как антикризисные меры к этому всему присобачить.
Можно сделать объект Антикризисный Комитет. В нем 3 метода соответствующие 3 планам. Каждый метод получает на вход Компанию и делает с ней какие-то действия.
> class Manager extends Employee {
> protected $rank = 1;
> protected $basicSalary = 500;
Вот ты переопределяешь тут базовую зарплату. А вот представь пришел новый человек и хочет сделать новую профессию. Откуда он узнает что надо переопределить это свойство? И как гарантировать что он его переопределит? Тут могут помочь абстрактные методы. И кстати базовый класс тоже стоит сделать абстрактным
> function __construct($rank, $boss = false) {
> $this->rank = $rank;
Можно не копипастить конструктор. а объявить в базовом классе 1 раз.
> public function addEmployee($profession, $amount, $rank = 1, $boss = false)
А почему департамент создает работников, а не нанимает готовых? Тебе не кажется что создание работников не совсем задача департамента? да и не понятно как например перевести работника из департамента в департамент тогда.
> str_repeat(" ", $widthOfTableCell - mb_strlen($string));
При вычитании может получиться отрицательное число
> $departmentName = $departments[$number]->getDepartmentName();
Есть же переменная department
Это не все, только что с первого взгляда видно
https://github.com/fidnex/students/blob/master/config.ini
В конфиг стоит выносить только то, что можно менять. Тип БД (mysql) вряд ли можно поменять на другой, так как код рассчитан на MySQL.
https://github.com/fidnex/students/blob/master/app/Controller/ControllerMain.php#L22
> $pagination = new PaginationHelper(
> $this->container['config']->getParam('items_on_page'),
> $this->container['studentTDG']->getTotalStudent($tableHelper->search
Это стоит выносить в переменные, а не городить такие сложные выражения
https://github.com/fidnex/students/blob/master/app/Model/Helper/TableHelper.php
тут есть обращения к GET.
https://github.com/fidnex/students/blob/master/app/Controller/ControllerForm.php
тут не надо копипастить выражение $this->container['formHelper'] а стоит либо сделать поле $this->formHelper либо переменную.
> $errors = $this->container['formHelper']->getValidError($student);
> if(!isset($error['email']) && !$isAuth && $this->container['studentTDG']->checkExistEmail($student->email)) {
> $errors['email'] = 'Введенный почтовый адрес уже зарегистрирован.';
А почему эта проверка не в валидаторе, а отдельно?
https://github.com/fidnex/students/blob/master/app/Model/Gateway/StudentTDG.php#L62
какой смысл в update делать lastInsertId?
> .'surname=:surname, '
> .'gender=:gender, '
> .'sgroup=:sgroup, '
В строках и запросах можно использовать перевод строки
> public function checkExistEmail($mail) {
При редактировании надо еще проверять что найденный емайл это не наш собственный email.
> SELECT COUNT() FROM students WHERE email = ? LIMIT 1
LIMIT применяется после COUNT и ничего не решает. Если ты хочешь оптимизировать то надо делать SELECT id ... LIMIT 1
https://github.com/fidnex/students/blob/master/app/Model/Gateway/StudentTDG.php#L104
> $query->bindValue(':search', '%'.$search.'%');
Тут будет ошибка если в запросе нет этого плейсхолдера
> $sql .= " ORDER BY $sortColumn $sortType LIMIT $limit OFFSET $offset";
Нет проверки подствляемых значений по белому списку - значит нельзя сказать что тут нет инъекции. Точнее она есть. LIMIT и OFFSEt надо подставлять через плейсхолдеры с указанием INT в bindValue.
https://github.com/fidnex/students/blob/master/app/Model/Helper/AuthHelper.php#L14
Тут надо использовать иф а не городить сложное выражение
> public function setToken($token) {
Мне кажется тут уместнее бы была функция вроде "залогинить студента". ну или хотя бы переименовать эту, а то она раскрывает наружу детали как работает автоизация, а лучше бы эти детали скрыть внутри класса.
> const TOKEN_NAME = 'auth';
Я бы сделал приватным статическим полем - чтобы снаружи не было доступно
> setcookie(AuthHelper::TOKEN_NAME, $token, $expire, '', '', false, true);
path надо стаивть в / иначе кука будет доступна не на всех старницах. Почитай за что отвечает path в куках.
> public function getNewToken() {
> return md5(md5(time()rand()).md5(chr(rand(0,90))));
какой смысл в нагромождении функций если они не увеличивают число вариантов? Время очень легко предугадать, по крайней мере примерно. Надежность низкая. У тебя токен который условно говоря принимает миллион значений, а с помощью md5 ты просто раздуваешь его размер не увеличивая число вариантов. md5 это не функция для генерации токенов. Она тут бесполезна.
Давай посмотрим реальную энтропию тут:
> rand()
дает 32 бита (число от 0 до 4 млрд)
> rand(0,90)
дает 7 бит
> time()
Допустим время регистрации мы можем угадать с точностью до месяца, так что случайным остается число секунд в месяце, это около 2 млн или 21 бит.
В сумме 60 бит энтропии. Для их записи хватит 10 символов (если каждый принимает 64 значения = 6 бит), а не 32.
И почитай еще https://github.com/codedokode/pasta/blob/master/good-code.md#Не-злоупотребляй-хешированием
https://github.com/fidnex/students/blob/master/app/Model/Helper/FormHelper.php#L69
> $student->$key = strval(trim($data[$key]));
Есть гарантия что в массиве есть это поле?
Имя и фамимлию кроме длины надо проверять на допустимые символы (буквы, дефис, апостроф, пробел)
Проверка уникальности email должна быть в валидаторе вместе с другими проверками.
> $this->formFields['gender']['pattern'] = '^('.Student::GENDER_MALE.'|'.Student::GENDER_FEMALE.')$';
Выглядит неуклюже. Не лучше ли тут через in_array делать проверку?
https://github.com/fidnex/students/blob/master/templates/footer.html
размещение стилей в футере приведет к тому что может отобразиться старница без стилей на какое-то время. размещение скриптов в футере чревато тем, что при использовании <button onсlick="fn()"> до загрузки скрипта мы получаем неработающую кнопку.
https://github.com/fidnex/students/tree/master/app/View
непонятно почему шаблоны раскиданы по 2 разным папкам . думаю, нет в этом смысла.
https://github.com/fidnex/students/blob/master/app/View/form.html#L8
> value='<?=$student->name;?>'
нет защиты от XSS
> pattern='<?=$formFields['surname']['pattern']?>'
Плохо что ты тут передаешь массивы сложной непонятной структуры. Лучше получать регулярку через метод. Также, ты используешь одну регулярку для HTML и для PHP, а ты в курсе что там немного разные диалекты регулярок?
> <div class="col-md-12 text-center">Все получилось.</div>
Для уведомлений в бутстрапе есть класс alert. Изучи возможности бутстрапа.
> <th><a href='<?=$tableHelper->getSortLink('name');?>'>Имя</a></th>
надо бы еще выделять колонку и направление сортировки например стрелочкой
> <?php foreach($students as $student):?>
> <td><?=$student->name;?></td>
Лучше бы выводить через htmlspecialchars
> <a href="<?=$tableHelper->getPaginationLink($i);
Символ & в ссылках надо выводить как & amp, например за счет htmlspecialchars
>>729042
> Не совсем понятно в этой ситуации, для чего и как делать класс Компания?
А как ты представишь сущность Компания в коде? Вот у тебя есть функция, которая что-то делает с Компанией (например применяет антикризисные меры). Что ты в нее передашь? Или функция создания компании - что она вернет?
> Опять же не понимаю, как антикризисные меры к этому всему присобачить.
Можно сделать объект Антикризисный Комитет. В нем 3 метода соответствующие 3 планам. Каждый метод получает на вход Компанию и делает с ней какие-то действия.
> class Manager extends Employee {
> protected $rank = 1;
> protected $basicSalary = 500;
Вот ты переопределяешь тут базовую зарплату. А вот представь пришел новый человек и хочет сделать новую профессию. Откуда он узнает что надо переопределить это свойство? И как гарантировать что он его переопределит? Тут могут помочь абстрактные методы. И кстати базовый класс тоже стоит сделать абстрактным
> function __construct($rank, $boss = false) {
> $this->rank = $rank;
Можно не копипастить конструктор. а объявить в базовом классе 1 раз.
> public function addEmployee($profession, $amount, $rank = 1, $boss = false)
А почему департамент создает работников, а не нанимает готовых? Тебе не кажется что создание работников не совсем задача департамента? да и не понятно как например перевести работника из департамента в департамент тогда.
> str_repeat(" ", $widthOfTableCell - mb_strlen($string));
При вычитании может получиться отрицательное число
> $departmentName = $departments[$number]->getDepartmentName();
Есть же переменная department
Этот тред закрыт.
>>729303
Попробуй глянуть документацию
$anonDice1 = mt_rand(1,6);
$anonDice2 = mt_rand(1,6);
$compDice1 = mt_rand(1,6);
$compDice2 = mt_rand(1,6);
echo "У Анона выпало {$anonDice1} и {$anonDice2}\nУ Компьютера {$compDice1} и {$compDice2}\n";
$anonSum = ($anonDice1 + $anonDice2);
$compSum = ($compDice1 + $compDice2);
if (($anonDice1 == $anonDice2) && ($compDice1 == $compDice2)) {
echo "2 дабла - тебя ждет удача\n";
exit ();
}
Я не так давно вкатился, освоил поверхностно html и css (сейчас прохожу курсы на htmlacademy.ru для закрепления), но в какую сторону двигаться дальше?
Цель: начать фрилансить.
Я для себя наметил несколько направлений, но уже, если честно, сломал всю голову, поэтому спрашиваю у вас, не ругайтесь только, в какой последовательности лучше изучать?
То, что собираюсь выучить: JavaScript, PHP, MySQL, WP и Joomla. Хочу поскорее брать заказики на weblancer.net и остальное доучивать уже на практике, так я гораздо лучше усваиваю. А уже через полгода-год подтяну ангельский и перекачусь на upwork. Помогите лишь определиться с последовательностью? Я у кого ни спрошу, кто советует с КМСок начать, кто с ЖС, кто с пыха.
тебе нужно создать новое условие
если сумма анона больше суммы компьютера, то анон вин, если наоборот - комп вин. Или ничья
Вы видите копию треда, сохраненную 13 мая 2016 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.