В интернете масса статей по созданию блога на Yii2 и я добавлю еще одну. Моя статья выгодно отличается от других тем, что лично для меня она построена на моем же опыте работы с Yii2. Всем прочим - на собственное усмотрение.
Делал для себя заметки по Yii2 когда разбирался с созданием блога, установкой и так далее. Данная статья написана на основе собственного опыта и информации из сети. Пошагово расписано создание блога и некоторые мелочи, все по собственному опыту.
Установка
https://github.com/yiisoft/yii2/blob/master/docs/guide-ru/start-installation.md
Вы можете установить Yii двумя способами: используя Composer или скачав архив. Первый способ предпочтительнее так как позволяет установить новые расширения или обновить Yii одной командой.
composer global require "fxp/composer-asset-plugin:^1.2.0"
composer create-project --prefer-dist yiisoft/yii2-app-basic basic
Первая команда устанавливает composer asset plugin, который позволяет управлять зависимостями пакетов bower и npm через Composer. Эту команду достаточно выполнить один раз. Вторая команда устанавливает последнюю стабильную версию Yii в директорию basic. Если хотите, можете выбрать другое имя директории.
Установилось!
В процессе установки требует ключ гитхаба
Нужно зарегистрироваться на Github и перейти в раздел Personal settings => Personal access tokens https://github.com/settings/tokens/new
И сгенерить ключ, который будет выглядеть примерно так:
3f9a71509f0c617244f22a209212061712bd7a84
Неплохо описано у этих чуваков:
https://xn--d1acnqm.xn--j1amh/%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D0%B8/yii-framework-2-%D0%BA%D0%B0%D0%BA-%D1%8F-%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%B0%D0%B2%D0%BB%D0%B8%D0%B2%D0%B0%D0%BB-yii2
Настройка подключения к БД
После установки необходимо настроить создать БД и настроить подключение. Файл с настройками подключения к БД находится в директории ./config/db.php:
return [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=yii2_blog',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
'tablePrefix' => 'tbl_'
];
Проектирование БД
Для начала создадим три таблицы:
Категории блога:
CREATE TABLE `tbl_category` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Пользователи блога, они же авторы:
CREATE TABLE `tbl_user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`login` varchar(40) NOT NULL,
`password` varchar(100) NOT NULL,
`email` varchar(100) NOT NULL,
`nickname` varchar(255) NOT NULL,
`about` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Посты блога:
CREATE TABLE `tbl_post` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`anons` text,
`content` mediumtext,
`category_id` int(10) unsigned DEFAULT NULL,
`author_id` int(10) unsigned DEFAULT NULL,
`publish_status` enum('draft','publish') NOT NULL DEFAULT 'draft',
`publish_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
CONSTRAINT `FK_post_category` FOREIGN KEY (`category_id`) REFERENCES `tbl_category` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT `FK_post_author` FOREIGN KEY (`author_id`) REFERENCES `tbl_user` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
База данных готова, переходим к созданию моделей. В этом нам поможет Yii code generator: http://localhost/index.php?r=gii
Создание модели БД
Переходим в gii, выбираем model generator, ставим опцию "Use Table Prefix".
В поля “Table Name” и “Model Class” поочерёдно вводим имена таблиц и имена генерируемых классов и жмите кнопку генерировать:
tbl_category: Category
tbl_user: User
tbl_post: Post
Note: во время генерации модели User перезапишите имеющийся файл User.php
На выходе генератора получаем классы представляющие модели для таблиц в БД.
Начинаем вводить "tbl ..."
table name: tbl_user
model name: User
При создании моделей подставляются уродские имена типа TblPost, но пока я не знаю как это обойти. Если сделать прост Post, то не генерится CRUD
Открываем /models/Post.php и исправляем имена методов с TblPost на Post. После этого работает.
Можно просто Post но не юзать table prefix
Когда модели созданы
/var/www/yii/basic/models/Category.php
/var/www/yii/basic/models/Post.php
/var/www/yii/basic/models/User.php
Контроллеры
Создаем контроллер
https://кодер.укр/записи/yii-2-вывод-списка-записей-с-помощью-виджета-listview-подробный-пример
Заходим в создание контроллера
Пишем в первом поле:
app\controllers\%controller_name%Controller
Вместе с контроллером будет создан и view
Как создавать CRUD
https://yiiframework.com.ua/ru/doc/guide/2/start-gii/
Заходим в CRUD Generator.
Model Class для Post указываем как app\models\TblPost.
Search Model Class - app\models\TblPostSearch.
Controller Class - app\controllers\TblPostController.
View Path - @app/views/TblPost.
Заходим в Controller Generator.
Controller Class - app\controllers\TblPostController.
Action IDs - index.
View Path - @app/views/TblPost.
Для проверки работы зайти по этим ссылкам (могут отличаться от ваших в зависимости от настроек):
http://yii.my/basic/web/index.php?r=post
http://yii.my/basic/web/index.php?r=post/index
http://test.my/basic/web/index.php?r=consumer
Откроется индексная страница контроллера post.
Один момент - убедиться в правильности пути к шаблону, у меня сразу выдало ошибку, оказалось, папка с шаблонами начинается с большой буквы.
Но страница пустая, нужно вывести список постов.
Список постов
В этой части есть варианты как я делал по инструкции и в тестовом задании.
Модель [yii/basic/models/Post.php]
Для получения записей из БД используем ActiveRecords
use yii\data\ActiveDataProvider;
public function getPosts()
{
return new ActiveDataProvider([
'query' => Post::find()
]);
}
-= или как в тестовом =-
Consumer.php
В модели никакой выборки, только описание полей
<?php
namespace app\models;
use yii\db\ActiveRecord;
use Yii;
use yii\web\UploadedFile;
/**
* This is the model class for table "{{%consumer}}".
*
* @property integer $consumerId
* @property integer $groupId
* @property string $login
* @property string $password
* @property string $email
* @property string $expirationDateAndTime
* @property string $imageExtention
*/
class Consumer extends \yii\db\ActiveRecord
{
public $file;
/**
* @inheritdoc
*/
public static function tableName()
{
return '{{%consumer}}';
}
/**
* @inheritdoc
*/
public function rules()
{
return [
[['groupId', 'login', 'password', 'email'], 'required'],
[['groupId'], 'integer'],
[['expirationDateAndTime'], 'safe'],
[['login', 'password', 'email'], 'string', 'max' => 254],
];
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'consumerId' => 'Consumer ID',
'groupId' => 'Group ID',
'login' => 'Login',
'password' => 'Password',
'email' => 'Email',
'expirationDateAndTime' => 'Expiration Date And Time',
'imageExtention' => 'Image Extention',
];
}
}
Контроллер [yii/basic/controllers/PostController.php]
Создаем метод indexAction()
и делаем выборку
$post = new Post(); $list = $post->find()->all();
var_dump($list);
public function actionIndex()
{
$post = new Post();
return $this->render('index', [
'posts' => $post->getPosts(),
]);
}
-= или как в тестовом =-
ConsumerController.php
В контроллере только вывод в шаблон
<?php
namespace app\controllers;
use app\models\Consumer;
class ConsumerController extends \yii\web\Controller
{
public function actionIndex()
{
return $this->render('index');
}
--- добавлено для сохранения формы ---
public function actionAdd()
{
$model = new EntryForm();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
// успех, пишем в базу
$model->login = $_POST['EntryForm']['login'];
$model->password = md5($_POST['EntryForm']['password']);
$model->email = $_POST['EntryForm']['email'];
$model->groupId = $_POST['EntryForm']['groupId'];
$model->expirationDateAndTime = $_POST['EntryForm']['expirationDateAndTime'];
$model->file = UploadedFile::getInstance($model, 'file');
// $model->imageExtention = $model->file->extension;
$filename = $model->file->baseName. rand() . '.' . $model->file->extension;
$model->imageExtention = $filename;
// вытащить последний id пользователя для корректного названия файла
// save file
$model->file->saveAs('uploads/' . $filename);
// save model
$model->save();
return $this->render('success', ['model' => $model]);
} else {
// страница отображается первый раз или ошибка
return $this->render('addForm', ['model' => $model]);
}
}
Выводим список статей в шаблон
Шаблоны списка [yii/basic/views/post/index.php] и анонса поста [yii/basic/views/post/shortView.php]
Анонс
<?php
use yii\helpers\Html;
?>
<h1><?= $model->title ?></h1>
<div class="content">
<?= $model->anons ?>
</div>
<?= Html::a('Читать далее', ['post/view', 'id' => $model->id], ['class' => 'btn btn-success']) ?>
Эти анонсы выводим циклом в списке
Список
<?php
/* @var $this yii\web\View */
use yii\helpers\Html;
?>
<h1>Список статей</h1>
<p>
You may change the content of this page by modifying
the file <code><?= __FILE__; ?></code>.
</p>
<div class="col-sm-8 post-index">
<h1><?= Html::encode($this->title) ?></h1>
<?php
foreach ($posts->models as $post) {
echo $this->render('shortView', [
'model' => $post
]);
}
?>
</div>
-= или выводим через ListView как в тестовом =-
делаем выборку из базы и сразу же выводим списком
<?php
use app\models\Consumer;
use yii\widgets\ListView;
use yii\data\ActiveDataProvider;
$dataProvider = new ActiveDataProvider([
'query' => Consumer::find(),
]);
Как устроена сортировка по определенному полю:
$dataProvider = new ActiveDataProvider([
'query' => Consumer::find()->orderBy(['(login)' => SORT_DESC]),
]);
Как добавить постраничную навигацию и другой вариант сортировки:
http://docs.mirocow.com/doku.php?id=yii2:activedataprovider
$dataProvider = new ActiveDataProvider([
'query' => Consumer::find(),
'sort'=>array(
'defaultOrder'=>['login' => SORT_DESC],
),
'pagination' => [
'pageSize' => 3,
'validatePage' => false,
],
]);
P.S. первый вариант сортировки более рабочий
Вывод через виджет ListView:
echo ListView::widget([
'dataProvider' => $dataProvider,
'itemView' => '_list',
]);
В дополнение создаем шаблон _list.php для вывода элемента с таким содержимым:
<?php
use yii\helpers\Html;
use yii\helpers\HtmlPurifier;
?>
<div class="news-item">
<?= HtmlPurifier::process($model->email) ?>
</div>
Итого, у нас есть список анонсов статей.
Нужно добавить еще пагинацию.
Постраничная навигация
Нашел два способа:
1. в контроллере
http://www.webapplex.ru/postranichnaya-navigacziya-v-yii-2.x
http://www.yiiframework.com/doc-2.0/yii-data-pagination.html
2. в модели через ActiveRecord
http://stackoverflow.com/questions/23336269/how-to-create-pager-in-yii2
Юзаю 2-й, т.к. я делаю выборку через ActiveRecord
В модели (Post.php) в метод выборку постов добавляем:
public function getPosts()
{
return new ActiveDataProvider([
'query' => Post::find(),
'pagination' => array('pageSize' => 3)
]);
}
Сразу выборка ограничилась до 3 постов, теперь нужно вывести постраничную навигацию.
В views/post/index.php добавляем
echo \yii\widgets\LinkPager::widget([
'pagination'=>$posts->pagination
]);
Все, постраничная навигация работает.
Теперь статья детально
Статья детально
https://github.com/Georgynet/Blog-Yii2/blob/master/common/models/Post.php
https://sllite.ru/2014/10/yii2-%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D0%B1%D0%BB%D0%BE%D0%B3%D0%B0-%D1%87%D0%B0%D1%81%D1%82%D1%8C-5-%D0%B2%D1%8B%D0%B2%D0%BE%D0%B4-%D0%BF%D0%BE%D1%81%D1%82%D0%BE%D0%B2-%D0%B8/
yii/basic/models/Post.php
Создаем метод для одного поста с выборкой по id
public function getPost($id)
{
return new ActiveDataProvider([
'query' => Post::findOne($id)
]);
}
yii/basic/controllers/PostController.php
public function actionView($id)
{
$post = new Post();
return $this->render('detail', [
'article' => $post->getPost($id),
]);
}
К этому методу можно достучаться по такому url:
index.php?r=post/post&id=1
Рендерим в шаблон detail
yii/basic/views/post/detail.php
<?php
use yii\helpers\Html;
?>
<h1><?= Html::encode($article->query->title);?></h1>
<div><?= Html::encode($article->query->anons);?></div>
<div><?= Html::encode($article->query->content);?></div>
Хлебные крошки
http://www.webapplex.ru/vizdzhet-breadcrumbs-(xlebnyie-kroshki)-na-yii-2.x
Виджет хлебных крошек по умолчанию уже есть в файле yii/basic/views/layouts/main.php
<div class="container">
<?= Breadcrumbs::widget([
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
]) ?>
<?= $content ?>
</div>
Если же его нет, можно подключить в любое представление таким образом:
- подкл. класс use yii\widgets\Breadcrumbs;
- подкл вижет
<div class="container">
<?= Breadcrumbs::widget([
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
]) ?>
<?= $content ?>
</div>
- передаем массив с параметрами
$this->params['breadcrumbs'][] = [
'template' => "<li><b>{link}</b></li>\n", // шаблон для этой ссылки
'label' => 'Категория', // название ссылки
'url' => ['/category'] // сама ссылка
];
$this->params['breadcrumbs'][] = ['label' => 'Подкатегория', 'url' => ['/category/subcategory']];
ЧПУ
В файле настроек yii/basic/config/web.php раскомментировать или добавить
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
],
],
в .htaccess добавить
RewriteEngine on
# If a directory or a file exists, use the request directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Otherwise forward the request to index.php
RewriteRule . index.php
Ссылки на детальные статьи формируются таким образом:
<?= Html::a('Читать далее', ['post/view', 'id' => $model->id], ['class' => 'btn btn-success']) ?>
post - название контроллера статей
view - название метода actionView($id)
Список категорий
по аналогии со списком постов
yii/basic/models/Category.php
/**
* @return ActiveDataProvider
* get categories list
*/
public function getCategories()
{
return new ActiveDataProvider([
'query' => Category::find(),
'pagination' => array('pageSize' => 5)
]);
}/**
* @param $id
* @return ActiveDataProvider
* get category by id
*/
public function getCategory($id)
{
return new ActiveDataProvider([
'query' => Category::findOne($id)
]);
}
yii/basic/controllers/CategoryController.php
public function actionIndex()
{
$category = new Category();
return $this->render('index', [
'categories' => $category->getCategories(),
]);
}
yii/basic/views/category/index.php
Здесь я сделаю немного не так как с постами. Напомню, у поста есть шаблон shortView, где выводится анонс, этот шаблон используется в списке постов. В категориях я обойдусь без него, выведу циклом в одном шаблоне.
foreach ($categories->models as $category) {
echo '<li class="list-group-item">';
echo Html::a($category->title, ['category/view', 'id' => $category->id], ['class' => 'list-group-item']);
echo '</li>';
}
еще один нюанс: вывод постов по категориям
В
yii/basic/models/Post.php
добавляем переменную с id категории и условие для выборки.
0<div class="container">
<?= Breadcrumbs::widget([
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
]) ?>
<?= $content ?>
</div>
Громоздко, но работает, позже отрефакторим.
yii/basic/controllers/PostController.php
1<div class="container">
<?= Breadcrumbs::widget([
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
]) ?>
<?= $content ?>
</div>
Добавляем в индексный контроллер переменные с id категории и все работает.
URL с таким параметром выведет список постов по категориям
/post?cid=2
Продолжение следует ...