Соцсеть своими руками - PHP Урок 26. Прикрепление контента к постам


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

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

Программируем на 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. Лента новостей.


Теория

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

Практика

Чтобы такие данные хранить нужна отдельная таблица в базе данных. Вот у меня получилась такая:

Таблица БД

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

CREATE TABLE IF NOT EXISTS `mc_content` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(10) unsigned NOT NULL,
  `type` tinyint(3) unsigned NOT NULL,
  `about` varchar(255) DEFAULT NULL,
  `content_time` bigint(20) unsigned NOT NULL,
  `status` tinyint(3) unsigned NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`,`type`),
  KEY `content_time` (`content_time`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=33 ;

Поле user_id хранит идентификатор автора (кто загрузил этот контент), а сам id указывающий на запись в таблице добавляется в поле content_id поста. Таким образом получается связь между записями из разных таблиц базы данных.
К тому же можно прикреплять один и тот же контент к разным постам при этом не трогая саму информацию (связь один ко многим т.е одна картинка может быть прикреплена ко многим постам).
Вы просто добавляете айди контента в поле контент-айди вашего поста при его создании.
И вот каким образом.

Функции core.php

Кстати, кто внимательно читает мои уроки, возможно уже обратили внимание, что структура у функций примерно одинаковая.
Сначала вызываются функции подготавливающие полученные функцией параметры для добавления в запрос.
В частности quote $user_id = $pdo->quote($user_id); - по сути просто добавляет кавычки по бокам, а те что уже есть внутри (если есть) - экранизирует (подставляет обратный слэш перед ними \).
Это необходимо для защиты от SQL-инъекций, так как кавычки в запросе являются ограничителями, и хакер может добавив кавычку указать конец запроса, а дальше вставить свой код, что конечно нам не нужно.

Затем идет собственно запрос, а после функции отправки запроса серверу БД и получения ответа (результата запроса).
Этот результат собственно и возвращается функцией.

Так что больше всего нас интересует собственно SQL запросы. О них и будем говорить.

Вот наши функции для работы с контентом (точнее с его данными в базе данных):

// Работа с контентом
    // добавление контента
    function addContentData($pdo, $user_id, $content_time, $content_type){
        $user_id = $pdo->quote($user_id);
        $content_time = $pdo->quote($content_time);
        $content_type = $pdo->quote($content_type);

        $sql_insert = "INSERT INTO mc_content (user_id, type, content_time) VALUES ($user_id, $content_type, $content_time)";
        //print $sql_insert;
        if($pdo->exec($sql_insert)){
            return true;
        }else{
            return false;
        }
    }

    // отображение списка контента пользователя
    function getContentsUser($pdo, $user_id){
        $user_id = $pdo->quote($user_id);
        $sql = "SELECT id, user_id, content_time FROM mc_content WHERE user_id=$user_id AND status != 0 ORDER BY content_time DESC";
        $stmt = $pdo->query($sql);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    function getContentData($pdo, $content_id){
        $content_id = $pdo->quote($content_id);
        $sql= "SELECT user_id, type, about,  content_time FROM mc_content WHERE id=$content_id";
        $stmt = $pdo->query($sql);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }

    // добавленные контента в пост
    function addContentToPostData($pdo, $post_id, $content_id){
        $post_id = $pdo->quote($post_id);
        $content_id = $pdo->quote($content_id);
        $sql_update = "UPDATE mc_post SET content_id=$content_id WHERE id=$post_id";
        if($pdo->exec($sql_update)){
            return true;
        }else{
            return false;
        }
    }

Первая функция вносит запись о контенте в таблицу mc_content:
INSERT INTO mc_content (user_id, type, content_time) VALUES ($user_id, $content_type, $content_time)
Применяется при загрузки контента. Ну то есть сразу после.
Если контент успешно загружен ку-да то на сервер (например в папку content, то вносится запись эта.
В ней указываем ИД пользователя, который загрузил контент (автора контента), тип контента (Картинка, видео и т.п) и время загрузки (его мы используем для создания уникальной ссылки на контент).

Две следующие функции отображают галерею контента, и конкретный один соответственно.

А последняя функция в данном списке, служит как раз для прикрепления нашего контента к посту.
Предполагается, что пост уже отправлен. Пост может уже быть опубликован (status=1), а может ожидать публикации (status=0).
Но запись с постом уже имеется в БД в таблице mc_post. Поэтому нам остается всего навсего изменить запись с нужным постом, добавив в него id контента:

UPDATE mc_post SET content_id=$content_id WHERE id=$post_id

Сам этот id генерируется автоматически, так как мы объявили при создании таблицы этот столбец AUTO_INCREMENT.

Если он нам нужен сразу же после добавления записи в таблицу mc_content, то можно воспользоваться php функцией $pdo->lastInsertId().

Функции в coreapi.php

Через AJAX вызываем только одну функцию:

// Добавление контента в пост
    function addContentToPost($pdo){
        $user_id = clearInt($_POST['user_id']);
        $user_hash = clearStr($_POST['user_hash']);
        $post_id = clearInt($_POST['post_id']);
        $content_id = clearInt($_POST['content_id']);
        //var_dump($_POST);
        if(check($pdo, $user_id, $user_hash)){
            //print_r($_POST);
            if(addContentToPostData($pdo, $post_id, $content_id)){
                print '{"response":1}';
            }else{
                print '{"response":0, "error":"Error added post"}';
            }
        }else{
            print '{"response":0, "error":"No avtorized user"}';
        }
    }

Однако она нам пригодиться на случай, если мы захотим изменить контент привязанный к посту (как помните у нас запрос UPDATE).

Как устроена кнопка Прикрепить контент или загрузка контента без обновления страницы

Нажимая кнопку "Прикрепить" в ВК может показаться, что контент подгружается по AJAX (страница то не перезагружается).
На самом деле AJAX тут ни причем. Конечно можно передать, преобразовав в base58, но если картинка много весит это не сработает (так как на многих серверах ограничение на запросы POST до 65 тыс. символов (по сути 64 кб всего).

Но как же тогда картинка загружается?

Все дело в теге iframe - мы просто вставляем одну страницу в другую и перезагружается фрейм, а не родительская страница (та которую мы открыли). Поэтому мы просто не заметим это.

Сам фрейм является полноценной страницей и хранится в отдельном файле. У меня вот такой его код:

<?
    define(ROOT, $_SERVER['DOCUMENT_ROOT']);
    require(ROOT.'/sys/core.php');

    $pdo >exec("SET NAMES utf8");
    $cookie_id = $_COOKIE['id'];
    $cookie_hash = $_COOKIE['hash'];
    $this_id = check($pdo, $cookie_id, $cookie_hash);
    $contents = getContentsUser($pdo, $this_id);
?>
<!-- Bootstrap -->

    <!--[if lt IE 9]>

<![endif]-->
<section>

<b>Кликните по изображению, чтобы добавить в новость</b><br />
<?//var_dump($_COOKIE);?>
<?//var_dump($contents);?>
<?foreach($contents as $item){?>
    " data-content-time="<>" >
    Бimg class="img-responsive img-rounded" src="/content/<>/s_<>.jpg" alt="" />

<?}?>

<br /><b>Загрузить в галерею новое изображение</b><br />
<form   >
<p><input   /></p>
<input   />
</form>
</section>

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

А каким образом взаимодействует фрейм с родительской страницей - ведь сам пост то у родителя записан, а у фрейма только контент.
В этом нам помогает вот эта JS функция:

$(".content-item").on("click", function(){
    var content_id = $(this).data("content-id");
    var content_time = $(this).data("content-time");
    $(this).css("border", "2px solid green");
    //alert('content-item: ' + content_id + ' ' + parent);
    parent.frameEvent(content_id, content_time);
});

С помощью parent.frameEvent(content_id, content_time); мы передаем в родительскую страницу ИД и Время контента.
То есть мы вызвали во фрейме функцию которая определена в содержащей его странице parent.ИмяФункции(Параметры).
А отображается он миниатюрой снизу подготовленного в родительской странице поста соответственно так:

function frameEvent(cid, content_time){
    //alert('fok ' + cid);
    content_id = cid;
    $('#attach-content').html('Бimg src="/content/' + this_id + '/s_' + content_time + '.jpg" class="img-response img-rounded" alt="" />');
    $('#form-content').hide();
}

Та самая функция frameEvent, которую мы вызвали из фрейма и передали параметры


В следующем уроке рассмотрим добавление и удаление комментариев к постам.

Comments 3


Хорошие статьи! Буду и дальше читать!

05.07.2017 12:14
0

Спасибо. Там про соц сеть осталось не много, где-то 4-5 уроков еще. Затем про PHP ООП начну и node.js

05.07.2017 12:44
0