PHP Урок 23. Подписчики и подписки.
Предыдущие уроки:
Программируем на PHP - Введение
PHP - Запросы от браузера к серверу
PHP - Как работает сервер
PHP - Урок 4. PHP - интерпретатор
PHP - Урок 5. Переменные сервера и глобальные переменные
PHP - Урок 6. Конструкции print и echo. Кавычки одинарные и двойные и конкатенация строк
PHP - Урок 7. Переменные, константы и условия
PHP - Урок 8. Точка входа в приложение. Настройка mod_rewrite и файл .htaccess
PHP - Урок 9. Массивы и switch. Кодим основной каркас
PHP - Урок 10. COOKIE
PHP - Урок 11. Функции. Добавляем ядро системы core.php
PHP - Урок 12. Обзор модели MVC. Добавляем шаблоны страниц в наше приложение
PHP - Урок 13. Введение в базы данных и SQL. СУБД MySQL. Подключаемся к БД из нашего приложения
PHP - Урок 14. Регистрация пользователей на сайте
PHP Урок 15. Авторизация пользователей
PHP. Урок 16. Проверка авторизации. Функция check().
PHP Урок 17. Добавляем CSS фреймворк Bootstrap и jQuery
PHP Урок 18. Загрузка файлов на сервер
PHP Урок 19. Добавляем меню навигации
PHP Урок 20. Создаем AJAX (JavaScript) API
PHP Урок 21. Циклы
PHP Урок 22. Данные пользователя.
Теория
Без функции подписки на новости интересных Вам людей не обходится ни одна социальная сеть. Практически это основа любой подобной системы. Задача подписок, - определить список пользователей, из постов которых будет формироваться ваша лента новостей (раздел новости). Стоит немного рассказать как это дело хранится в базе данных. Все очень просто. Я для этого создаю таблицу news в которой первое поле содержит id пользователя, кто подписывается, второе поле - на id кого, и третье - это статус подписки. При отписке просто изменяется статус - сама запись из БД не удаляется. Более того, из нашей базы вообще ничего не удаляется, просто везде есть поле статус, которое устанавливается и проверяется при выводе новостей. Кстати специальной таблицы для хранения конкретных новостей также нет. Новости хранятся все вместе в таблице, о которой мы поговорим в следующем уроке.Практика
Для начала как обычно структура таблицы:CREATE TABLE IF NOT EXISTS `mc_news` (
`user_id` bigint(20) unsigned NOT NULL,
`blog_id` bigint(20) unsigned NOT NULL,
`status` tinyint(3) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`user_id`,`blog_id`),
KEY `status` (`status`),
KEY `user_id` (`user_id`),
KEY `blog_id` (`blog_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Здесь наш PRIMARY KEY распростерся аж на два столбца. Дело в том, что PRIMARY индекс должен быть уникальным как и UNIQE) в нашей таблице и первый и второй id могут повторяться, то есть в первом столбце будет столько ваших id - сколько у вас подписок (не зависимо от статуса подписки). Понятное дело во втором - тоже id-шники повторяются. На одного и того же автора могут быть подписаны разные пользователи.
Кстати в данный реализации вы можете подписаться сами на себя. В этом не будет ошибки. Хотя во многих системах кнопку подписаться на себя убирают от самого пользователя. Это легко реализуется.
Так вот в нашем случае как раз ключ состоит из двух id - является уникальным и однозначно идентифицирующим запись в таблице - поэтому они и PRIMARY KEY.
Теперь посмотрим в core.php какие у нас там функции для подписок/отписок:
// Добавление в список новостей
function addNewsListData($pdo, $user_id, $blog_id){
$user_id = $pdo->quote($user_id);
$blog_id = $pdo->quote($blog_id);
$sql = "SELECT COUNT(status) FROM mc_news WHERE user_id=$user_id AND blog_id=$blog_id";
$stmt = $pdo->query($sql);
$row = $stmt->fetch(PDO::FETCH_NUM);
//print $row[0];
if($row[0]){
$sql_update = "UPDATE mc_news SET status='1' WHERE user_id=$user_id AND blog_id=$blog_id";
//print $sql_insert;
if($pdo->exec($sql_update)){
return true;
}else{
return false;
}
}else{
$sql_insert = "INSERT INTO mc_news (user_id, blog_id, status) VALUES ($user_id, $blog_id, 1)";
//print $sql_insert;
if($pdo->exec($sql_insert)){
return true;
}else{
return false;
}
}
}
function delNewsListData($pdo, $user_id, $blog_id){
$user_id = $pdo->quote($user_id);
$blog_id = $pdo->quote($blog_id);
$sql_update = "UPDATE mc_news SET status='0' WHERE user_id=$user_id AND blog_id=$blog_id";
//print $sql_insert;
if($pdo->exec($sql_update)){
return true;
}else{
return false;
}
}
function isNewsList($pdo, $user_id, $blog_id){
$user_id = $pdo->quote($user_id);
$blog_id = $pdo->quote($blog_id);
$sql = "SELECT status FROM mc_news WHERE user_id=$user_id AND blog_id=$blog_id";
$stmt = $pdo->query($sql);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$result = $row['status'];
if($result >= 1) return true;
return false;
}
Здесь обратим внимание на первую функцию - добавление в список новостей. Мы пердварительно проверяем. Есть ли запись уже в таблице (так как пользователь мог подписаться, а затем отписаться). Если нету, то добавляем новую запись, а если есть - инвертируем поле status.
Далее функции - обертки в coreapi.php:
// Новости - добавление в новостной список
function addNewsList($pdo){
$user_id = clearInt($_POST['user_id']);
$user_hash = clearStr($_POST['user_hash']);
$blog_id = clearInt($_POST['blog_id']);
if(check($pdo, $user_id, $user_hash)){
//print_r($_POST);
if(addNewsListData($pdo, $user_id, $blog_id)){
print '{"response":1}';
}else{
print '{"response":0, "error":"Error added post"}';
}
}else{
print '{"response":0, "error":"No avtorized user"}';
}
}
// Убираем из списка новостей блог
function delNewsList($pdo){
$user_id = clearInt($_POST['user_id']);
$user_hash = clearStr($_POST['user_hash']);
$blog_id = clearInt($_POST['blog_id']);
if(check($pdo, $user_id, $user_hash)){
//print_r($_POST);
if(delNewsListData($pdo, $user_id, $blog_id)){
print '{"response":1}';
}else{
print '{"response":0, "error":"Error added post"}';
}
}else{
print '{"response":0, "error":"No avtorized user"}';
}
}
Понятно, что для функции проверки статуса подписки - API обертка не нужна. Мы ей пользуемся только из PHP на сервере, а не посредством AJAX.
А вот так выглядят сами шаблоны (внутряки), отображающие подписчиков и тех на кого подписан пользователь:
sublist.tpl.php:
<h2>Читатели</h2>
<hr />
<?//var_dump($users)?>
<?foreach($users as $user){?>
<div class="row">
<div class="col-md-offset-1 col-md-1">
<a href="/blog/<?=$user['user_id']?>">
<?if(file_exists('content/'.$user['user_id'].'/s_ava.jpg')){?>
<im* class="img-responsive img-circle" src="/content/<?=$user['user_id']?>/s_ava.jpg" alt="" />
<?}else{?>
<im* class="img-responsive img-circle" src="/content/default/s_ava.jpg" alt="" />
<?}?>
</a>
</div>
<div class="col-md-6">
<h3><a href="/blog/<?=$user['user_id']?>"><?=$user['first_name']?> <?=$user['last_name']?></a></h3>
<?=$user['about']?><br />
</div>
</div>
<hr />
<?}?>
<br />
И соответственно readlist.tpl.php:
<h2>Тут те, кого вы читаете</h2>
<hr />
<?//var_dump($blogs)?>
<?foreach($blogs as $blog){?>
<div class="row">
<div class="col-md-offset-1 col-md-1">
<a href="/blog/<?=$blog['user_id']?>">
<?if(file_exists('content/'.$blog['user_id'].'/s_ava.jpg')){?>
<im* class="img-responsive img-circle" src="/content/<?=$blog['user_id']?>/s_ava.jpg" alt="" />
<?}else{?>
<im* class="img-responsive img-circle" src="/content/default/s_ava.jpg" alt="" />
<?}?>
</a>
</div>
<div class="col-md-6">
<h3><a href="/blog/<?=$blog['user_id']?>"><?=$blog['first_name']?> <?=$blog['last_name']?></a></h3>
<?=$blog['about']?><br />
</div>
<div class="col-md-2">
<br />
<p><button class="del-from-readlist btn btn-primary" data-user-id="<?=$blog['user_id']?>">Отписаться</button></p>
</div>
</div>
<hr />
<?}?>
<br />
<script src="/assets/js/news.js"></script>
Ну а внутри news.js как уже мы знаем размещаются всякие скрипты для запросов к нашему API.
$("#add-news-list").on("click", function(){
//alert('this_id ' + this_id + ' this_hash ' + this_hash + ' blog_id ' + blog_id);
$.post(
'/api/addNewsList',
{
"user_id": this_id,
"user_hash": this_hash,
"blog_id": blog_id,
},
function(data){
// тут добавляем наш пост сверху выборки
//alert(data);
location.reload(true);
}
);
});
$("#del-news-list").on("click", function(){
//alert('this_id ' + this_id + ' this_hash ' + this_hash + ' blog_id ' + blog_id);
$.post(
'/api/delNewsList',
{
"user_id": this_id,
"user_hash": this_hash,
"blog_id": blog_id,
},
function(data){
//alert(data);
location.reload(true);
}
);
});
Ну тут думаю все понятно - одна функция подписывает на новости блога, другая - описывает.