Прежде чем что-либо программировать - нужно чётко понимать какие данные вы собираетесь хранить, как их показывать пользователю, по каким критериям извлекать из БД и т.д. От этого будет зависеть структура таблиц в базе данных, а также модели, которые будут описывать эти данные.
Я буду работать над клиентом https://www.vp-golos.ml , задуманным для помощи тематическим и локальным сообществам Vox-Populi.
Подробнее о vp-golos.ml
Создание модели
Одна из основных задач проекта - показывать ленту материалов не просто по тэгу или автору, а в виде более сложной выборки. Так в одной выборке могут участвовать сразу несколько авторов и тэгов. По одним контент будет показываться, а по другим исключаться.
Например: показывать материалы трёх авторов по тэгу "мысли", ещё у одного автора показывать "человек" и "любовь", ещё у двоих авторов показывать посты с тэгом "путешествия" и у всех шести авторов исключить посты с тэгом "эстафетацвета".
Суть задачи, думаю, ясна.
Теперь нужно решить как хранить посты и тэги. В таких ситуациях руки сами тянутся использовать третью нормальную форму. Такой подход очень удобен, когда нужно извлечь материалы по тэг(тэгам). Для этого выборка идёт по таблице-связке постов с тэгами и к ней джоиним таблицу постов.
Сейчас я представлю это схематично и напишу запрос.
select p.post_id, p.title, pt.tag_id
from post_tags pt
left join posts p on p.post_id=pt.post_id
where pt.tag_id in (12,15,16,9)
group by p.post_id
Тут всё легко, но как быть, если нужно исключить из выборки посты по тэгу?
Если пост прикреплён к пяти тэгам, и нужно исключить пост пост по наличию одного из них - то в выборке будет этот же пост с остальными тэгами. Сейчас я покажу что имею ввиду.
Посты по тэгам номер 12,15,16,9 (без группировки по post_id)
select p.post_id, p.title, pt.tag_id
from post_tags pt
left join posts p on p.post_id=pt.post_id
where pt.tag_id in (12,15,16,9)
Обращаем внимание на Post 6 - он рубрицирован тэгами 15 и 16.
Теперь попробуем достать посты по тэгам номер 12,15,16,9 за исключением тэга 15
select p.post_id, p.title, pt.tag_id
from post_tags pt
left join posts p on p.post_id=pt.post_id
where pt.tag_id in (12,15,16,9) and pt.tag_id!=15
Как видите - пост "Post 6" остался. Так как его связка с тэгом 16 удовлетворяет условию tag_id!=15
В этом случае нужно дописывать условие по id тэга в join части запроса. Если тэгов будет много - джоин очень раздуется. Если честно - я незнаю насколько этот запрос будет тяжелым. Я его потом проверю, а пока я немного изменю структуру таблицы и запроса. Я просто скопирую все id тэгов в таблицу постов. Это не есть гуд, но если в плане производительности будет выйгрыш - значит так всё и останется.
Одно из решений(без JOIN`ов)
Можно в таблице постов сделать 5 колонок - для каждого тэга по колонке. Но в этом случае в запросах будет куча OR-ов, поэтому не будем рассматривать этот вариант.
Предлагаю остановиться на следующем: сложить все 1-5 тэгов в одну строку и поместить это в одну колонку в таблице постов.
Выглядеть это будет так.
post_id | title | created | tags |
---|---|---|---|
1 | Мой пост | 1502888759 | *tag1*tag2*tag22* |
Между каждым тэгом и с краёв строки я поместил знак звёздочки(можно любой символ, не входящий в список допустимых символов в именах тэгов - это всё кроме букв, цифр, тире и знака подчёркивания)
Сделал я это для того, чтобы фильтр по тэгу tag2 не зацепил тэг tag22
Теперь в запросе я буду писать:
where tags like '%*tag2*%' and tags not like '%*tag22*%'
Имейте ввиду, что такой подход допустим только из-за того, что пост имеет максимум 5 тэгов. Если у поста будет 20 тэгов - так делать конечно же не стоит.
Пишем код
Теперь, когда мы чётко понимаем что нужно делать - можно начинать кодить.
Все таблицы моделей должны быть описаны в миграции.
Если вкратце - то миграция - это описание таблиц в файлах и создание их в БД в соответствии с ними. Работать с миграцией надо в консоли.
В корне сайта есть файл yii, просто, без расширения.
Попробуйте в консоли перейти в папку проекта и запустить его:
cd /var/www/site-path
./yii
Если вы видите ошибку Permission denied - сделайте файл yii исполнительным.
Создадим файл миграции для таблицы постов, назовём её posts.
Поля таблицы:
- post_id - первичное поле
- title - заголовок
- created - дата создания
- image - главное изображение поста
- author - автор
- tags - строка тэгов
- parent_permlink - раздел
- permlink - ссылка
- votes_count - количество голосов
- comments_count - количество комментариев
- reblogs_count - количество репостов
Выполняем команду:
./yii migrate/create posts
На вопрос о создании файла отвечаем yes
Готово.
У меня в корне сайта появилась папка migrations и в ней файл для таблицы posts
Создание таблицы описывается в методе up. Я написал следующий код:
public function up()
{
$this->createTable('posts', [
'post_id' => $this->primaryKey(),
'title' => $this->string(255),
'created' => $this->integer(),
'image' => $this->string(255),
'author' => $this->string(255),
'tags' => $this->string(255),
'parent_permlink' => $this->string(255),
'permlink' => $this->string(255),
'votes_count' => $this->integer()->defaultValue(0),
'comments_count' => $this->integer()->defaultValue(0),
'reblogs_count' => $this->integer()->defaultValue(0),
]);
}
Теперь нужно запустить миграцию. Для этого сперва зададим хост, логин, пароль и имя БД в конфиге. Хранится он в файле config/db.php. Затем выполняем команду:
./yii migrate/up
У меня таблица успешно создалась.
От таблиц для тэгов и таблицы-связки постов с тэгами я совсем отказываться не буду, поэтому создадим миграции и для них.
./yii migrate/create tags
./yii migrate/create post_tags
Описание таблицы tags
$this->createTable('tags', [
'tag_id' => $this->primaryKey(),
'name' => $this->string(255),
]);
Описание таблицы post_tags
public function up()
{
$this->createTable('post_tags', [
'id' => $this->primaryKey(),
'post_id' => $this->integer(),
'tag_id' => $this->integer(),
'delta' => $this->integer(),
]);
}
$this->createIndex('post_tag', 'post_tags', ['post_id', 'tag_id'], true);
В таблицу post_tags я добавил уникальный индекс, чтобы материал не мог привязаться к одному тэгу дважды.
Повторно запускаем ./yii migrate/up
- обе новые таблицы создадутся.
Заполнение таблиц данными
Данные для этих таблиц, будем брать с SQL сервера от @arcange.
Я наверное не буду сейчас показывать как я их стягиваю, там ничего сложного - один select из SQL-сервера и один insert в свою базу, образно говоря. Да и сильно раздувать пост не хочется. Если кому не до конца ясен этот момент - пожалуйста дайте знать.
Извлечение данных
Так как в этом уроке сделан упор на модели - пока не будем разбираться с возможностями контроллеров, а просто создадим action в SiteController.
public function actionTest() {
echo 'Test page';
}
После этого у нас появится страница с адресом /index.php?r=site/test
Выведем на ней последние 10 постов
Для того, чтобы удобно работать с моделями - нужно описать их в классах.
Давайте создадим класс для модели post. Создать его можно через консоль, а можно и при помощи утилиты Gii. Она доступна в том числе и через браузер. Чтобы её запустить - откройте файл config/web.php и найдите там следующий код:
$config['modules']['gii'] = [
'class' => 'yii\gii\Module',
// uncomment the following to add your IP if you are not connecting from localhost.
'allowedIPs' => ['127.0.0.1', '195.100.101.102', '::1'],
];
В переменную allowedIPs я уже вписал свой IP адрес 195.100.101.102. Если вы работаете локально - просто расскоментите эту строчку.
Теперь открывайте страницу /index.php?r=gii, на ней первым пунктом как раз будет Model Generator. Запускайте его.
В поле Table Name пишите posts.
В поле Model Class пишите Post.
Namespace и Base Class оставляем как есть.
Model Generator.png
Жмите Preview, а затем Generate.
Если у вас ничего не работает - проверьте права на запись в папку runtime, они должны быть у пользователя www-data. Тоже относится и к папке models.
Если у вас всё получилось - то в папке models появится файл Post.php с начальным описанием модели.
Если у вас не работает gii - то вы можете создать ровно такой же файл вручную, просто это займёт больше времени.
Точно так же создайте модель Tag.
Теперь извлечём последние 5 постов одного автора:
//в шапке файла
use app\models\Post;
//метод actionTest в теле класса SiteController
public function actionTest() {
echo 'Test page<br />';
$q = Post::find()
->where(['author' => 'tristamoff'])
->orderBy(['created' => SORT_DESC])
->limit(5);
$posts = $q->all();
foreach ($posts as $post) {
echo $post->title . '<br>';
echo date('Y.m.d H:i', $post->created) . '<br>';
echo '<hr />';
}
}
Код достаточно простой. Класс Post - это обращение к нашей модели. Метод find - сделает выборку из таблицы модели. Методы where, orderBy и limit интуитивно понятны.
Запускаем.
У меня всё получилось. В дальнейшем поговорим о взаимосвязях между моделями(пост - тэг) и конечно о темизации
Заглавное фото с сайта kwork.ru
Первая часть - вступление
Для обычных пользователей было бы удобнее плагином на Wordpress, чем с фреймворками возиться.
Самым обычным можно в ЖЖ блог завести.
А в сложном проекте возможностей Wordpress не хватит, и расширять его также, как код написанный на фреймворке не получится.
вы очень умный молодец
Практически ничего в этом не понимаю, но за работу вам лайк)
Абсолютно не моя сфера, но проголосую и подпишусь, @vp-webdev. ))
@vp-webdev, Поздравляю!
Ваш пост был упомянут в моем хит-параде в следующей категории:
Сложновато для простых умов, если бы готовые шаблоны были, было бы отлично.
В моделях нет шаблонов.
А если в целом - для фреймворков вообще не бывает шаблонов, как таковых.
Если что попроще - готовая CMS, там любая тема уже готовый шаблон, со стилями и прочим.
Ваш пост поддержали следующие Инвесторы Сообщества "Добрый кит":
yefet, knopki, vas, mishka, archibald116, urii, phoenix, larissa, tristamoff, semasping, gromozeka, yuriks2000, oksana0407, vika-teplo, sva-lana, sareon, nerengot, bag, vlad1m1r, xsen, anr, vsebudethorosho
Поэтому я тоже проголосовал за него!
Узнать подробности о сообществе можно тут:
Разрешите представиться - Кит Добрый
Правила
Инструкция по внесению Инвестиционного взноса
Вы тоже можете стать Инвестором и поддержать проект!!!
Если Вы хотите отказаться от поддержки Доброго Кита, то ответьте на этот комментарий командой "!нехочу"
Нехочу
Чего Вы не хотите?