PHP Урок 25. Лента новостей.
Предыдущие уроки:
Программируем на 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. Отправка и сохранение пользовательских постов
Теория
Сегодня мы займемся тем, без чего не обходится ни одна соц. сеть, а именно новостями от друзей (ну или тех, на кого вы подписались). Кстати говоря, совсем не обязательно, что в ленте должны быть одни новости. Наверное, кто-то вспомнит, что изначально в социальной сети ВК (викей), а на тот момент называвшейся и по сей день известной в России как Вконтакте, была совсем другая лента. Во-первых в те времена при входе в ВК пользователь попадал не в новости, а к себе на страницу профиля. Там он первым делом смотрел, ни написал ли один из его друзей какую-нибудь гадость на стене, а затем сам на своей стене писал что-то из Шекспира или подобное (репостов тогда не было). А в самой ленте, отображались действия пользователей, обычно кто кого добавил в друзья, отметил на фотографии, добавился в группу. То есть было все как-то более консервативно, не выносящий рассудок. Поэтому при появлении изменений возникли дикие споры среди пользователей и протесты.Теперь уже трудно представить, как бы мы чувствовали себя без фида, репостов и лайков. Поэтому будем все это изучать.
Самое интересное, что мы сегодня изучим, это собственно запрос SQL к БД MySQL, который эту ленту и формирует.
Но сначала давайте пройдемся по действиям, которые нужно выполнить, чтобы получилась такая лента (помним о news и posts из предыдущих уроков):
- Авторизироваться в соцсети, чтобы она знала ваш ID. Он присваивается, допустим, переменной $user_id.
- Выбрать в таблице news все blog_id по выражению WHERE user_id = $user_id (Напомню, в таблице news user_id - это вы (пользователь кто подписан), а blog_id - пользователь, на кого подписан.)
- Каждый раз при выборе очередного blog_id, мы делаем запрос к таблице posts, чтобы извлечь пост данного пользователя.
- Вытащенные посты пользователей (не все а до LIMIT) сортируются по времени (причем во время выборки) и добавляются в массив. Так что все посты от разных пользователей у нас идут по времени.
- Приклеивается к постам остальная информация (имя и фамилия пользователя из таблицы профиля, а также метаданные контента).
Взглянем теперь на сам SQL запрос:
SELECT mc_post.id, mc_post.user_id, mc_post.post_time, mc_post.content_id, LEFT(mc_post.text, 180) as text, mc_profile.first_name, mc_profile.last_name, mc_content.content_time
FROM mc_post
INNER JOIN mc_profile ON mc_post.user_id = mc_profile.user_id
LEFT OUTER JOIN mc_content ON mc_content.id = mc_post.content_id
WHERE mc_post.user_id IN (SELECT blog_id FROM mc_news WHERE user_id=$user_id AND status != 0)
AND mc_post.status != 0 ORDER BY mc_post.post_time
DESC LIMIT 0, 50
В SELECT у нас указаны не только имена столбцов, откуда мы выбираем строки, но и таблицы.
Это все по тому, что на самом деле в FROM (откуда брать) указаны целых 3 таблицы - mc_post, mc_content и mc_profile.
Данные из них формируют как будто-бы длинную строку из одной таблицы. А для того, чтобы было понятно, что к чему относится мы используем JOIN.
Синтаксис склеивания в простой форме можно описать так: FROM таблица1 JOIN таблица2 ON таблица1.столбец1 = таблица2.столбец2. Здесь главное, чтобы столбец1 соотносился со столбцом2 второй. Например это может быть общий user_id или content_id - там где они совпадают, строки и склеются в одну целую при выдаче.
Ну это про сами посты, а вот собственно формирование ленты: WHERE mc_post.user_id IN (SELECT blog_id FROM mc_news WHERE user_id=$user_id AND status != 0)
.
После того, как мы склеили все нужные таблицы в FROM и запросили из них значения в SELECT, нам собственно нужно условие - какие строки выбирать-то. Это мы делаем в разделе WHERE.
Тут мы говорим, что поиск ведем по таблице mc_post и сравниваем значение столбца user_id.
Если бы мы просто поставили равно и значение id, например 100, то нам бы вернулись только посты, пользователя с идентификатором 100.
Нам же нужны посты от всех, на кого мы подписаны (записи в таблице news). Поэтому мы используем оператор ON.
За оператором ON следует другой запрос в скобках, вот он как раз выбирает все blog_id которым соответствует наш user_id - то есть пользователей на кого мы подписывались ранее.
Оператор ON - это что-то типа foreach в SQL. Он берет все значения вернувшиеся запросом из скобок и по очереди перебирая подставляет их как если бы мы их все записали через равно к mc_post.user_id.
Понятно, что в скобках после оператора ON запрос SELECT должен выбирать только один какой-то столбец, значения из которого будут сравниваться.
Далее все выбранные посты сортируются по времени, начиная с самого нового DESC: ORDER BY mc_post.post_time DESC
и соответственно ставим лимит от самого свежего поста 50 штук всего.
Проверка status нужна, чтобы мы не получали посты, авторов который в ЧС, заблокирован или удаленные посты.
Практика
Выяснив принцип работы теперь можем посмотреть и на пару функций, которые выводят ленту новостей:
Функции core.php
// Получение новостей
function getNewsList($pdo, $user_id, $from, $len){
$user_id = $pdo->quote($user_id);
$sql = "SELECT mc_post.id, mc_post.user_id, mc_post.post_time, mc_post.content_id, LEFT(mc_post.text, 180) as text, mc_profile.first_name, mc_profile.last_name, mc_content.content_time FROM mc_post INNER JOIN mc_profile ON mc_post.user_id = mc_profile.user_id LEFT OUTER JOIN mc_content ON mc_content.id = mc_post.content_id WHERE mc_post.user_id IN (SELECT blog_id FROM mc_news WHERE user_id=$user_id AND status != 0) AND mc_post.status != 0 ORDER BY mc_post.post_time DESC LIMIT $from, $len";
$stmt = $pdo->query($sql);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// По популярности
function getMainNews($pdo, $from = 0, $len = 100){
$sql = "SELECT mc_post.id, mc_post.user_id, mc_profile.first_name, mc_profile.last_name, mc_post.post_time, LEFT(mc_post.text, 180) as text, mc_content.content_time, mc_post.likes_count, mc_post.content_id FROM mc_post INNER JOIN mc_profile ON mc_post.user_id = mc_profile.user_id LEFT OUTER JOIN mc_content ON mc_content.id = mc_post.content_id WHERE mc_post.status != 0 ORDER BY likes_count DESC, post_time DESC LIMIT $from, $len";
$stmt = $pdo->query($sql);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
Во второй функции мы сортируем посты не только по времени, но и по популярности, а точнее по количеству лайков у поста.
Для этого мы написали два условия сортировки ORDER BY likes_count DESC, post_time DESC
. Они будут работать вместе одновременно для каждого сортируемого поста.
Эту вторую функцию я использую для вывода новостей на главной странице. Хотя ее можно использовать где захочется в нашем проекте.
Вызов в контроллере
А здесь все просто:
case 'news':
$news = getNewsList($pdo, $this_id, 0, 20);
$title = "Новости";
$tpl = 'news';
break;
Так как наша функция практически выводит все данные о новостях - мы ей передаем в качестве второго параметра именно нашь id ($this_id) так, как она будет по нашему id искать, пользователей, на кого мы подписаны, чтобы сформировать ленту новостей.
И последние два параметра это новость с которой начинать (0 - самая свежая), и сколько всего новостей выводить. Эти значения понадобится изменять, при реализации пейдженации или подгрузке новостей при прокрутке страницы вниз (по средствам JavaScript API).
@rusldv Поздравляю! Вы добились некоторого прогресса на Голосе и были награждены следующими новыми бейджами:
Награда за количество полученных голосов
Вы можете нажать на любой бейдж, чтобы увидеть свою страницу на Доске Почета.
Чтобы увидеть больше информации о Доске Почета, нажмите здесь
Если вы больше не хотите получать уведомления, ответьте на этот комментарий словом
стоп
Голосуя за это уведомление, вы помогаете всем пользователям Голоса. Узнайте, как здесь.
Ваш пост поддержали следующие Инвесторы Сообщества "Добрый кит":
t3ran13, niiu, archibald116, gryph0n, karusel1, kvg, vika-teplo, anomalywolf, del137, generationg
Поэтому я тоже проголосовал за него!
Узнать подробности о сообществе можно тут:
Разрешите представиться - Кит Добрый
Правила
Инструкция по внесению Инвестиционного взноса
Вы тоже можете стать Инвестором и поддержать проект!!!
Если Вы хотите отказаться от поддержки Доброго Кита, то ответьте на этот комментарий командой "!нехочу"