Вы не зашли.
А вот, собственно обновленный код, который заменяет старую функцию ipcheck():
global $ip, $mysql, $userROW, $rating_array; /* в $list_id массив со всеми News ID на странице */ global $list_id; $list_id =array(7,5,2,1,6);
if(is_array($list_id) && count($list_id)){ // проверяем что $list_id - это массив, в нем что-то есть =)
for ($a = 1; $a <= count($list_id); $a++) {
$out_db=$out_db."news_id='".$list_id[$a-1]."' or "; // создаем кусок запроса к БД: "news_id=xx or news_id=xx ...."
}
$rating_out_db=$mysql->select("select news_id from ".prefix."_rating WHERE (user_id='".intval($userROW['id'])."' or ip=".db_squote($ip).") and (".substr($out_db, 0, strlen($out_db)-3).")"); // ищем все новости из списка ID`шников, за которые голосовал этот IP
for ($a = 1; $a <= count($rating_out_db); $a++) {
$rating_array[$a]=$rating_out_db[$a-1]['news_id']; // создаем массив (глобальный) с ID`шниками новостей за которые голосовал этот IP
}
}
/* А эта функция, теперь тупо ищет ID запрашиваемой новости в глобальном массиве и возвращает true или false,
в зависимости от того, найдет ли в массиве ID запрашиваемой новости. Параметр $upd - как обычно, позволяет записать
голос в БД + удалить старые записи из БД(если прошло 24 часа после прошлого удаления)
*/
function ipcheck ($news_id, $upd) {
global $rating_array, $ip, $mysql, $userROW;
if(is_array($rating_array)){
$temp=intval(array_search($news_id, $rating_array));
}
if (is_array($rating_array) && isset($temp) && $temp) {
return true; // возвращаем скрипту истину - этот IP уже голосовал за новость
} else {
// не найдено - можно голосовать
if($upd) {
/* Сохраняем голос в базу данных : START */
$mysql->query("INSERT INTO ".prefix."_rating set user_id='".intval($userROW['id'])."', vote_date=".time().", ip=".db_squote($ip).", news_id=".db_squote($news_id));
/* Сохраняем голос в базу данных : END */
/* Удаляем старые записи : START */
if(intval(pluginGetVariable('rating', 'last_clear')) <= time()) { // проверяем промежуток времени очистки таблицы
$mysql->query('DELETE FROM `'.prefix.'_rating` WHERE `vote_date` < '.(time()-5184000).';'); // удаляем старые записи
pluginSetVariable('rating', 'last_clear', (time()+86400));
pluginsSaveConfig();
}
/* Удаляем старые записи : END */
}
return false; // возвращаем скрипту ложь - этот IP еще не голосовал за новость
}
}
и оно как-то работает
с условием того что $list_id - это глобальный массив со всеми идентификаторами новостей на текущей странице, и скрипт должен получить его в самом начале...
Доступна для скачивания версия 0.9.3 Release Candidate 1 [SVN 856]
Ооо, это хорошо
vitaly, а список ID'шников всех новостей на странице в RC 1, до загрузки плагинов никак не получить?
Не реализовал ты эту фичу?
А то у меня уже функция почти готова, ей нужен только глобальный массив со всеми ID'шниками новостей на текущей странице, она делает один запрос и записывает в глобальный массив ID всех новостей за которые юзер голосовал, а при последующем обращении к функции запрашиваемый ID новости, не ищется в БД делая лишний запрос и создавая нагрузку, а ищется в глобальном массиве. Ну как-то так
ikv777vlg пишет:И за 6 обращений к функции, вывод в браузер получается такой:
1;
1;
1;2;
1;2;
1;2;5;
1;2;5;по 2 обращения на каждую новость
Вот именно, поэтому у тебя и запросы с дубликатами. Зачем у тебя два раза отрабатывает логика на каждую новость?
Долгая история...
Давно еще делал разные блоки голосования для короткой и полной новости, нужно было вывести в шаблон еще одно значение... и функция rating_show($newsID, $rating, $votes) вызывалась 2 раза..
Уже пофиксил, обошелся одним вызовом функции на одну новость, немного в ущерб дизайну(которого нет ), но суть дела не меняет:
1;
1;2;
1;2;5;
или
SQL queries:
[ 0.0003 ] select * from `ng_category` order by posorder asc
[ 0.0002 ] select * from ng_users where authcookie = '599386dbeb6845f459b6e226045a07c3' limit 1
[ 0.0005 ] SELECT * FROM ng_news WHERE (`approve` = '1') AND (`mainpage` = '1') order by ratesort desc, postdate desc limit 0,3
[ 0.0003 ] select * from ng_rating where (user_id='1' or ip='127.0.0.1') and news_id='1'
[ 0.0002 ] select * from ng_rating where (user_id='1' or ip='127.0.0.1') and news_id='2'
[ 0.0002 ] select * from ng_rating where (user_id='1' or ip='127.0.0.1') and news_id='5'
[ 0.0002 ] SELECT count(*) as count FROM ng_news WHERE (`approve` = '1') AND (`mainpage` = '1')
Теперь по одному запросу на новость. Например - если 20 новостей на странице - плюс 20 запросов в БД
Wolverine пишет:Надо vitaly спрашивать о внедрении такой возможности в news_showone, которая отображает все новости. Данные нужные там есть, осталось из записать в какую-нить глобальную переменную, типа $SYSTEM_FLAGS
Ты хотел сказать news_showlist()?
Не прокатит, т.к. новости рендерятся по мере получения данных... хотя.. $mysql->select() ведь сразу всё в память выгружает.
В принципе можно сделать сканирование массива перед отрисовкой данных и всем плагинам сразу же давать список ID новостей, которые будут показаны в этом цикле.
Подумаю над идеей.
Точно. Ради интереса попробовал... Я в news.php в функции news_showlist() объявил глобальную переменную $list_id,
и после
foreach ($mysql->select($query['result']) as $row) {
$i++;
$nCount++;
вставил
$list_id=$list_id.$row['id'].";";
а в начало функции ipcheck() в rating.php вставил
global $list_id;
echo $list_id."<br />";
И за 6 обращений к функции, вывод в браузер получается такой:
1;
1;
1;2;
1;2;
1;2;5;
1;2;5;
по 2 обращения на каждую новость
Vitaly, ты уж придумай что-нибудь
Надо vitaly спрашивать о внедрении такой возможности в news_showone, которая отображает все новости. Данные нужные там есть, осталось из записать в какую-нить глобальную переменную, типа $SYSTEM_FLAGS
А это ключ к успеху
Функция news_showone находится в news.php, нашел я ее, а как она работает, интересно...
Но она большая конечно
Вопрос в том, как получить список ID этих новостей, отображаемых на данной странице, чтобы засунуть их в глобальную переменную для последующего решения давать голосовать или нет.
Вот это я и хотел сказать
ikv777vlg, Делай лучше через join как vitaly написал. Запрос тогда получится как:
select * from ng_rating where (user_id='1' or ip='127.0.0.1') and news_id='1' and news_id='2' and news_id='5'
Пример реализации есть в плагине lastnews...
А как понять and news_id='1' and news_id='2' and news_id='5', а если у меня 100 новостей, 500?
А за один раз как по всем новостям пройти, как запрос должен выглядеть? Чувствую такая реализация мне не по силам
Посмотрю что там в lastnews...
Уговорил
OK, но все равно попробую, так, ради интереса
ikv777vlg, Я своё сообщение дополнил.
Да я вижу Но непонятно что-то, массив, цикл... Конструкция незнакомая, знаний PHP не хватает ..
Покурю мануал, попробую, отпишусь...
ikv777vlg, На счет скобок всё правильно.
$userROW['id'] так же как и $ip лучше обернуть, intval($userROW['id']) и db_squote($ip) они являются глобальными и их можно подменить другими значениями с другого плагина и лучше обезопасится.
Понятно
is_array($userROW) выдает ID пользователя, а что?
не, is_array определяет, является ли переданный параметр массивом
Вот это да
А я забыл, и машинально принял ее за обычную функцию, которая объявлена где-то в глубине CMS Бывает же )))))))
А ты прикинь, я незалогинен - is_array($userROW) возвращает - 0, если залогинен под учетной записью администратора, у меня User ID = 1, и is_array($userROW) тоже возвращала 1, регистрация пользователей запрещена - никакого подвоха, так-бы всё и работало ))))))))
Не очень понятна:
(user_id='".intval(is_array($userROW))."'
тут походу intval выручает))
Скорее:user_id='".intval($userROW['id'])."'
и скобки не обязательны...
is_array($userROW) выдает ID пользователя, а что? Как было в плагине, так я и скопировал, я же не знаю до конца что там в массиве ))) Понял, значит будет $userROW['id'] а в intval() его обязательно оборачивать?
А насчет скобок, а разве без скобок получится? select * from ng_rating where (user_id='1' or ip='127.0.0.1') and news_id='1' - этот запрос должен найти в таблице все строки с полями user_id или ip с новостью news_id='1' а как-же без скобок? Или синтаксис неправильный?
Тогда делай один запрос пройдясь по всей таблице и запиши всё в одну переменную и по ней уже сравнивай все новости.
P.S. всего кода не вижу и подробнее написать не могу...
А что его смотреть, функция вызывается всего 3 раза:
1) При голосовании, когда проверяется - вдруг в браузере уже есть печенька, и за эту новость уже голосовали
2) При голосовании, после того как в браузер ставится печенька
3) И при выборе - что показать юзеру, звёзды или просто рейтинг, и это тот самый вариант вызываемый 6 раз:
if ((isset($_COOKIE['rating'.$newsID]) && $_COOKIE['rating'.$newsID]) || (extra_get_param('rating','regonly') && !is_array($userROW)) || ipcheck($newsID, 0)) { // ipcheck 3
// Show
$tpl -> template('rating', $tpath['rating']);
$tpl -> vars('rating', $tvars);
return $tpl -> show('rating');
} else {
// Edit
$tpl -> template('rating.form', $tpath['rating.form']);
$tpl -> vars('rating.form', $tvars);
return $tpl -> show('rating.form');
}
Ну по-хорошему надо проверять и при заходе, чтобы пользователю показывать как именно он проголосовал.. но тут правильнее уже делать JOIN к основному select'у по новостям, не уверен что штатно это получится сделать.
JOIN к основному select'у
- непонятно
Надо, но походу и правда не получится, у меня есть вариант - показывать звезды только при просмотре полной новости, это как вариант... Т.е только при просмотре полной новости будет добавляться 2 запроса. И почему кстати их два??... Надо разбираться...
Так проверять нужно при нажатии, а не как только зайдет на сайт
Логично Но, я проверяю для того, чтобы выдать или не выдать к новости "5 больших звезд" для голосования, а зачем пользователю опять показывать звёзды, если он уже проголосовал? Рейтинг-то отображается в процентах от 1 до 100...
Апдейт. Только так и больше никак, и IP, и ID пользователя надо учитывать, если он зарегистрирован.
function ipcheck ($news_id, $upd) {
global $ip, $mysql, $userROW;
/* Ищем в базе строку с текущими IP или User ID и News ID */
if($mysql->record("select * from ".prefix."_rating where (user_id='".intval(is_array($userROW))."' or ip='".$ip."') and news_id='".$news_id."'")) {
return true; /* Юзер уже голосовал за эту новость, возвращаем истину */
}
else {
/* Юзер еще не голосовал за эту новость */
if($upd){
if(intval(pluginGetVariable('rating', 'last_clear')) <= time()) { // проверяем промежуток времени очистки таблицы
$mysql->query('DELETE FROM `'.prefix.'_rating` WHERE `vote_date` < '.(time()-5184000).';'); // удаляем старые записи
pluginSetVariable('rating', 'last_clear', (time()+86400));
pluginsSaveConfig();
}
// далее голосуем
$mysql->query("INSERT INTO ".prefix."_rating set user_id='".intval(is_array($userROW))."', vote_date=".time().", ip='".$ip."', news_id='".$news_id."'");
}
return false;
}
}
Вот только опять косяк у меня - 10 SQL запросов (time: 0.04 сек.) при загрузке главной страницы, и превью 3-х новостей, каждая новость добавляет два запроса!
SQL queries:
[ 0.0003 ] select * from `ng_category` order by posorder asc
[ 0.0002 ] select * from ng_users where authcookie = 'e20d1756ef6c888e3bd2a6ea99968536' limit 1
[ 0.0006 ] SELECT * FROM ng_news WHERE (`approve` = '1') AND (`mainpage` = '1') order by ratesort desc, postdate desc limit 0,3
[ 0.0002 ] select * from ng_rating where (user_id='1' or ip='127.0.0.1') and news_id='1'
[ 0.0002 ] select * from ng_rating where (user_id='1' or ip='127.0.0.1') and news_id='1'
[ 0.0002 ] select * from ng_rating where (user_id='1' or ip='127.0.0.1') and news_id='2'
[ 0.0002 ] select * from ng_rating where (user_id='1' or ip='127.0.0.1') and news_id='2'
[ 0.0002 ] select * from ng_rating where (user_id='1' or ip='127.0.0.1') and news_id='5'
[ 0.0002 ] select * from ng_rating where (user_id='1' or ip='127.0.0.1') and news_id='5'
[ 0.0002 ] SELECT count(*) as count FROM ng_news WHERE (`approve` = '1') AND (`mainpage` = '1')
Вот такая колбаса!
Буду исправлять...
или давать возможность разным пользователям голосовать с одного IP
Получается, логичнее это делать только при включенной опции "Рейтинг только для зарегистрированных" в настройках плагина?
Стоп, а разве даже если включено - "Рейтинг только для зарегистрированных", в БД не пишется UserID того кто проголосовал за новости, а тоже только печенька в браузер ставится?
Тогда конечно надо сделать, мне это не нужно, но кому-нибудь обязательно пригодится!..
Опять-же, если зарегистрированному юзеру удалить куки, и войти на сайт, с одного ip, потом с другого(если у него динамический IP), и возможность проголосовать может то появляться, то пропадать, если IP найдется.
Отсюда вывод - поле UserID я оставлю, просто нужно искать и сравнивать результаты в БД как я и сказал раньше - и IP+NewsID, и UserID+NewsID вот и логика, хоть включай "Рейтинг только для зарегистрированных", хоть выключай, результат одинаковый - один IP несколько раз проголосовать за одну новость не сможет, и один UserID тоже
С другой стороны - если включено "Рейтинг только для зарегистрированных" то на IP можно вообще не обращать внимания, а делить голосующих по UserID... Надо выбрать какой-нибудь вариант.
Ой я пойду покурю...
В твоем случае UserID не нужен, один хрен разрешать голосовать или нет ты разрешаешь только по IP
Вот и я о том-же..
Да, но могут-же и несколько юзеров с одного IP на сайт заходить, желательно бы их всех различать, но это невозможно, если только дополнительные заголовки вместе с IP не считывать, или еще как-то
Значит я UserID убираю, хотя я подумаю еще над этим...
vitaly, спасибо, один я бы ничего не сделал, спасибо всем вам!
Wolverine, я всё слышал!
Понятное дело, надо как-то зарабатывать деньги, что-то делать.. Мне нужно сайт запустить, нужен был плагин, были трудности, я к вам обратился, вы мне помогли, с меня - как минимум, индексируемая ссылка с главной страницы моего сайта, или даже со статьи, разберемся. Если, конечно, я этот сайт когда-нибудь доделаю
В сапе я и сам буду ссылки продавать, хочу попробовать аккуратно всё это реализовать, и чтобы это был СДЛ, но на ngcms.ru ссылку поставлю в любом случае, дай Бог и мне ТИЦа высокого
Вообще у меня сейчас достаточно свободного времени, пока я просто не работаю, а работа у меня не легкая, и график даалеко не 8-ми часовой, так что незнаю что будет дальше, как минимум еще неделю я свободен, а потом у меня времени и сил будет в разы меньше..
Тут знаете сколько проблем, помимо тех что в виртуальной реальности? Много! И учиться надо, и работать... Я даже не считаю то что у меня сейчас на правой руке или сильный ушиб, или трещина в кости, рука болит, собственно по этому я и не работаю сейчас, зато мышку могу перемещать по коврику
Ой от души выговорился, мужики, извиняйте за оффтоп
Проекту помогу еще не раз, чем смогу, надеюсь не один сайт на NG CMS буду делать, будет дебаг, предложения и доработки всякие, замутим что-нибудь, это обязательно
А теперь по теме...
vitaly, как раз у меня к тебе вопрос. Вот ты писал недавно:
Imho, правильней хранить данные о каждом голосе.
newsID, userID, IP, voteDate
для незарегенного юзера userID будет нулевым.
тогда и проблема очистки будет решаться намного проще и конфликтов вида "двое нажали одновременно на <голосовать>, но только один из голосов появился в таблице" (если несколько человек сидят на одном сайте за NAT'ом) не будет
и поставил меня на путь истинный , я немного переделал скрипт, добавил пару полей в БД всё работает, но...
Изначально в моей функции есть косяк. Я не пойму, причем тут User ID ?
Теперь получается что незалогиненный 127.0.0.1+UserID=0 проголосовал один раз за новость, потом вошел на сайт, и опять у него появляется возможность проголосовать за ту же новость, т.к. это уже 127.0.0.1+UserID=1
Что-то я не пойму логику, UserID надо или убрать, или делать как-то по другому
Запутался я короче говоря, или я неправильно тебя понял...
Хотя... Может писать в базу надо IP+UserID+NewsID а читать (IP+NewsID or UserID+NewsID) ?
А смысл? Опять я что-то не догоняю...
И еще... В rating.php перед "class RatingNewsFilter extends NewsFilter {" есть непонятный комментарий:
//
// ”Ё«мва ®ў®б⥩ (¤«п Ї®Є § ३⨣ )
//
Из-за этого все комментарии в скриптах CMS на английском пишите?
Кстати, в при создании плагина, нельзя комментарии на кириллице писать? Или мне всё на английский переводить?
Имхо, надо-же определяться, если Win-1251 - значит Win-1251...
Юзеры чаще будут просто просматривать новость, чем голосовать, тем-более голосование вызывается аяксом, пока будет очищаться таблица, юзер будет видеть картинку "Loading..." в центре страницы, и в итоге получается такой принцип: "Проголосовал? Оставил свой IP в базе? По братски - удали старые записи из БД!"
И в итоге при загрузке страницы очистка никак не выполнится, и страница будет грузиться быстрее.
Так что, ИМХО, всё логично и оптимально
Напиши какие именно функции из extras.inc.php ты хочешь знать и на http://wiki.ngcms.ru/ я их распишу...
P.S. Только не надо говорить просто ВСЕ
Все и в extras.inc.php и в functions.inc.php, и везде где они есть
ROZARD, а если серьезно, спасибо конечно тебе, но я же не знаю где какая функция, и в какой момент она мне может пригодиться. Вдруг я решусь создать какой-нибудь полезный плагин и что-то смогу реализовать на чистом PHP, а окажется что это уже реализовано в функциях CMS. Так-что всё постепенно. А при изменении плагина, и так можно разобраться где находится вызываемая функция, и что она делает, хотя бывает трудно, даже с комментариями на английском. В этом случае буду спрашивать тут
Там вроде комментарии есть хорошие.
Например по теме:
// // Save configuration parameters of plugins (should be called after pluginSetVariable) // function pluginsSaveConfig($suppressNotify = false)
Согласен. Но попробуй встать на мое место, если я первый раз вижу строчку комментария "Save configuration parameters of plugins" я же не могу понять какие параметры сохраняются, что это за параметры такие, при знании PHP "со словарем"
В далеком будущем при детальном изучении работы CMS, что да как, эти вопросы может и отпадут, но пока мало что понятно. NG CMS вообще заинтересовала меня, и отсутствие красивого дефолтного шаблона меня не испугало, буду делать сайт на NG CMS, вроде всё что нужно в ней есть
А пока я попробую сделать еще что-нибудь хорошее в плагине rating, раз взялся, надо же попытаться его до ума довести!
ikv777vlg пишет:А вообще, не хватает документации по всем подобным функциям, которые можно вызывать в плагинах.
Или такая документация есть? Я просто не в курсе, уж извиняйтеЕсть http://wiki.ngcms.ru но она пока не полная...
Но ведь никто лучше создателей CMS не знает что там за функции в том-же, например extras.inc.php , я заглянул туда, а там темный лес, черт ногу сломит с моими знаниями PHP, хотя некоторую информацию можно почерпнуть из комментариев на английском, это очень хорошо, но неудобно конечно
А теперь по сабжу. Я сделал вот так, вроде работает:
if(intval(pluginGetVariable('rating', 'last_clear')) <= time()) { // проверяем промежуток времени очистки таблицы
$mysql->query('DELETE FROM `'.prefix.'_rating` WHERE `vote_date` < '.(time()-5184000).';'); // удаляем старые записи
pluginSetVariable('rating', 'last_clear', (time()+86400));
pluginsSaveConfig();
}
как всё просто
Вот только куда этот IF лучше вставить? Я считаю, что лучше обрабатывать его когда пользователь голосует($upd=1), а не после каждой проверки голосовал ли юзер ($upd=0). Тем-более при обычной загрузки новости, плагин всегда обращается к функции, делается выбор - просто показать рейтинг, или показать рейтинг+ссылки для голосования (если IP или печенька не найдена).
Пишите что еще не так, может где-то еще я был не прав
А как работать c pluginSetVariable() и pluginGetVariable()?
Вот так правильно?
pluginSetVariable('rating', 'last_clear', time()); // сначала создаем переменную и задаем ей значение
pluginsSaveConfig(); // где-то сохраняем :)
echo intval(pluginGetVariable('rating', 'last_clear')); // и в любой момент, из любого скрипта, считываем значение
Переменная last_clear будет храниться постоянно? А если она вдруг больше не нужна, как ее удалить, задать пустое значение, или еще как?
А вообще, не хватает документации по всем подобным функциям, которые можно вызывать в плагинах.
Или такая документация есть? Я просто не в курсе, уж извиняйте
Ах точно!... я тут чуть-чуть не выспался , добавлю еще одно условие, очистку раз в сутки, как ты говоришь.
Действительно, с каждым голосом запускать очистку нет смысла, можно и раз в сутки, я же не корабль в космос запускаю, 1 день роли не сыграет.
Спасибо!
Ну вот
Я не стал искать проблем на причинное место, и решил не выходить за пределы моей функции ipcheck(), и всё реализовать в ней, и вот что получилось:
function ipcheck ($news_id, $upd) {
global $ip, $mysql, $userROW;
/* Ищем в базе строку с текущими IP, User ID и News ID */
if($mysql->record("select * from ".prefix."_rating where user_id='".intval(is_array($userROW))."' and news_id='".$news_id."' and ip='".$ip."'")) {
return true; /* Юзер уже голосовал за эту новость, возвращаем истину */
}
else {
/* Юзер еще не голосовал за эту новость, если параметр $upd = 1 голосуем и возвращаем ложь(на всякий случай),
а если $upd = 0 не голосуем, просто возвращаем ложь и в БД ничего не изменяем */
if($upd){
$mysql->query('DELETE FROM `'.prefix.'_rating` WHERE `vote_date` < '.(time()-5184000).';'); // удаляем старые записи
// далее голосуем
$mysql->query("INSERT INTO ".prefix."_rating set user_id='".intval(is_array($userROW))."', vote_date=".time().", ip='".$ip."', news_id='".$news_id."'");
}
return false;
}
}
Она удаляет за собой старые записи в БД(более 2-х месяцев)!!!
Структуру таблицы ng_rating, которую использует эта функция, можно посмотреть в прикрепленном скриншоте.
Везде кроме IP я решил использовать INT(11), я думаю что этого хватит за глаза
Если я где-то ошибся - поправьте меня!
А вот мне интересно, (по совету Wolverine) при каждом успешном голосовании выполняется
$mysql->query('DELETE FROM `'.prefix.'_rating` WHERE `vote_date` < '.(time()-5184000).';');
Правильно ли это? Не создаст лишнюю нагрузку?
ikv777vlg, now() пишется в самом запросе...
Не знал...
Можно и time(), только придется писать ".time()." а now() нужно без кавычек, и использоваться он может только при запросе к БД mysql... Надеюсь что я правильно понял
А now() и time() выдают один и тот-же результат - timestamp? Не "дд-мм-гггг"? (я просто еще не дошел до этого участка кода, не могу проверить)