Уважаемые пользователи Голос!
Сайт доступен в режиме «чтение» до сентября 2020 года. Операции с токенами Golos, Cyber можно проводить, используя альтернативные клиенты или через эксплорер Cyberway. Подробности здесь: https://golos.io/@goloscore/operacii-s-tokenami-golos-cyber-1594822432061
С уважением, команда “Голос”
GOLOS
RU
EN
UA
rusldv
7 лет назад

Своя соцсеть. PHP Урок 28. Лайки.

PHP Урок 28. Лайки.



Предыдущие уроки:


Программируем на 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. Данные пользователя.

PHP Урок 23. Подписчики и подписки

PHP Урок 24. Отправка и сохранение пользовательских постов

PHP Урок 25. Лента новостей.

PHP Урок 26. Прикрепление контента к постам

PHP Урок 27. Комментарии к постам


Теория

Вообще лайки в соцсетях были не всегда. Были времена, когда и ВК мог похвастаться лишь наличием возможности комментировать посты и фотографии. Об этом реч шла в предыдущем уроке. Что же такое лайк по своей сущности. Прежде всего это такой немногословный комментарий, который содержит что-то вроде смайлика (ну в FB так уже давно сделано). Сама таблица лайков в БД отдельная, но напоминает таблицу комментариев. То есть когда пользователь ставит лайк, он также как и в случае с комментарием, передает свой id серверу, и там сохраняется в таблицу лайков, таблица весьма похожа на таблицу подписок-подписчиков (news). По ней легко определить, кто кому поставил лайк. Можно легко написать такую функцию в core.php под названием лайки-подписки. Она бы в отдельную ленту выводила посты тех авторов, которым вы поставили лайк или лайки. На самом деле на лайках можно реализовать много интересных функций. Тут главное фантазия. Но мы поговорим только об основном. Как собственно их можно реализовать в нашей учебной соцсети.

Практика

Как обычно начнем с таблицы. Она как и news (таблица-связей) содержит 3 поля - целые неотрицательные числа. Статус как обычно управляет отображением пользователям. Ну для лайков это либо поставлен, либо нет. Хотя если бы мы решили добавить лайки-смайлики, то вполне бы могли использовать статус, как номер смайлика для отображения. (поле статус относительно постов может иметь помимо удален-неудален, значения - спам, нарушение правил сайта, 18+ и т.п).

Таблица БД

--
-- Структура таблицы `mc_likes`
--


CREATE TABLE IF NOT EXISTS `mc_likes` (
  
`post_id` bigint(20) unsigned NOT NULL,
  
`user_id` int(10) unsigned NOT NULL,
  
`status` tinyint(3) unsigned NOT NULL DEFAULT '1',
 
 PRIMARY KEY (`post_id`,`user_id`)
) 
ENGINE=InnoDB DEFAULT CHARSET=utf8;

Я тут поставил индекс PK только для поиска конкретного лайка (по двум полям).
Однако правильнее было бы добавить еще 2 индекса на поля post_id и user_id, как по id поста мы выбираем всех пользователей лайкнувших его, а по user_id можем посмотреть, какие посты лайкнул пользователь (по крайней мере предоставив возможность посмотреть их самому этому пользователю).
Нет конечно и без индексов это будет работать, но при большом количестве записей будет сильно тормозить. Индексы же ускорят процесс в сотни раз.

Функции core.php

// Лайки
    function getPostLikesCount($pdo, $post_id){
        $post_id = $pdo->quote($post_id);
        $sql = "SELECT COUNT(user_id) FROM mc_likes WHERE post_id=$post_id AND status!=0";
        $stmt = $pdo->query($sql);
        $row = $stmt->fetch(PDO::FETCH_NUM);
        return $row[0];
    }
    
    function getPostLikesData($pdo, $post_id){
        $post_id = $pdo->quote($post_id);
        $sql = "SELECT user_id, first_name, last_name FROM mc_profile WHERE user_id IN (SELECT user_id FROM mc_likes WHERE post_id=$post_id AND status!=0)";
        $stmt = $pdo->query($sql);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    
    function addPostLikeData($pdo, $user_id, $post_id){
        $post_id = $pdo->quote($post_id);
        $user_id = $pdo->quote($user_id);
        
        $sql = "SELECT COUNT(status) FROM mc_likes WHERE post_id=$post_id AND user_id=$user_id";
        $stmt = $pdo->query($sql);
        $row = $stmt->fetch(PDO::FETCH_NUM);
        //print $row[0];
        if($row[0]){
            $sql_update = "
            BEGIN;
            UPDATE mc_likes SET status=1 WHERE post_id=$post_id AND user_id=$user_id;
            UPDATE mc_post SET likes_count = likes_count+1 WHERE id=$post_id;
            COMMIT;
            ";
            //print $sql_insert;
            if(!$pdo->exec($sql_update)){
                return true;
            }else{
                return false;
            }
        }else{
            $sql_insert = "
            BEGIN;
            INSERT INTO mc_likes (post_id, user_id) VALUES ($post_id, $user_id);
            UPDATE mc_post SET likes_count = likes_count+1 WHERE id=$post_id;
            COMMIT;
            ";
            //print $sql_insert;
            if(!$pdo->exec($sql_insert)){
                return true;
            }else{
                return false;
            }
        }
    }
    
    function delPostLikeData($pdo, $user_id, $post_id){
        $post_id = $pdo->quote($post_id);
        $user_id = $pdo->quote($user_id);
        
        $sql_update = "
        BEGIN;
        UPDATE mc_likes SET status=0 WHERE post_id=$post_id AND user_id=$user_id;
        UPDATE mc_post SET likes_count = likes_count-1 WHERE id=$post_id;
        COMMIT;
        ";
        //print $sql_insert;
        if(!$pdo->exec($sql_update)){
            return true;
        }else{
            return false;
        }
    }
    
    function isPostLike($pdo, $post_id, $user_id){
        $post_id = $pdo->quote($post_id);
        $user_id = $pdo->quote($user_id);
        
        $sql = "SELECT COUNT(status) FROM mc_likes WHERE post_id = $post_id AND user_id = $user_id AND status != 0";
        
        $stmt = $pdo->query($sql);
        $row = $stmt->fetch(PDO::FETCH_NUM);
        return $row[0];
    }
    

Вот казалось бы всего-то какие то лайки, а кода для них чуть ли не больше, чем для комментариев. Даже такую конструкцию применить пришлось

BEGIN;
        UPDATE mc_likes SET status=0 WHERE post_id=$post_id AND user_id=$user_id;
        UPDATE mc_post SET likes_count = likes_count-1 WHERE id=$post_id;
        COMMIT;

Это мы так удаляем лайк (как видно из названия функции.
Как я писал в уроке про посты у нас для ускорения подсчета количества лайков в записях самого поста их количество также хранится (вместо использования функции COUNT() SQL - это нужно из-за особенностей движка InnoDB).
Мы конечно могли бы сделать два запроса: один добавлял бы запись в таблицу mc_likes, а второй изменял значение бы поле в mc_posts.
Однако что если один из этих запросов окажется не удачным. Возникает несоответствие количеству лайков в поле поста и записей в таблице лайков.
C помощью BEGIN ... COMMIT; мы превращаем обычные SQL - команды в транзакцию.
Это означает, что либо они выполнятся все вместе, либо ни одного - произойдет откат.
Таким образом мы гарантируем, что команды не выполнятся по разнясь и данные не будут расходиться.

Вызов в контроллере


Как и в случае комментариев у лайков нет отдельной страницы. Они выводятся сначала на страницы поста:

case 'post':
,,,
$post_likes_count = getPostLikesCount($pdo, $post_id);
$post_likes = getPostLikesData($pdo, $post_id);
,,,

А затем с помощью AJAX API.

$("#add_like").on("click", function(){
    //alert("like " + post_id + " " + this_id);
    
    $.post(
        '/api/addPostLike',
        {
            "user_id": this_id,
            "user_hash": this_hash,
            "post_id": post_id,
        },
        function(data){
            // тут добавляем наш пост сверху выборки
            //alert(data);
            location.reload(true);
        }
    );
});

$("#del_like").on("click", function(){
    //alert("like " + post_id + " " + this_id);
    
    $.post(
        '/api/delPostLike',
        {
            "user_id": this_id,
            "user_hash": this_hash,
            "post_id": post_id,
        },
        function(data){
            // тут добавляем наш пост сверху выборки
            //alert(data);
            location.reload(true);
        }
    );
});

и соответственно в coreapi.php:

function addPostLike($pdo){
        $user_id = clearInt($_POST['user_id']);
        $user_hash = clearStr($_POST['user_hash']);
        $post_id = clearInt($_POST['post_id']);
        
        //print_r($_POST);
        if(check($pdo, $user_id, $user_hash)){
            //print_r($_POST);
            if(addPostLikeData($pdo, $user_id, $post_id)){
                print '{"response":1}';
            }else{
                print '{"response":0, "error":"Error added post"}';
            }
        }else{
            print '{"response":0, "error":"No avtorized user"}';
        }
    }
    
    function delPostLike($pdo){
        $user_id = clearInt($_POST['user_id']);
        $user_hash = clearStr($_POST['user_hash']);
        $post_id = clearInt($_POST['post_id']);
        
        //print_r($_POST);
        if(check($pdo, $user_id, $user_hash)){
            //print_r($_POST);
            if(delPostLikeData($pdo, $user_id, $post_id)){
                print '{"response":1}';
            }else{
                print '{"response":0, "error":"Error added post"}';
            }
        }else{
            print '{"response":0, "error":"No avtorized user"}';
        }
    }

Нам достаточно добавить функции, которые бы обращались через API к определенным в core.php (в частности getPostLikesData($pdo, $post_id);).


В следующем уроке расскажу про реализацию групп (сообществ). В нашем проекте они называются медиацентры.
245
301.783 GOLOS
На Golos с November 2016
Комментарии (1)
Сортировать по:
Сначала старые