Professional Documents
Culture Documents
ПЗ КР 17в Snizhko Kasyanenko
ПЗ КР 17в Snizhko Kasyanenko
Курсова робота
з дисципліни «Проектування веб-орієнтованих інформаційних систем»
на тему «Розроблення web-орієнтованої інформаційної системи у вигляді блогу на
базі архітектурного шаблону MVC з використанням фреймворку YII2»
Група ІТ.м-32
Суми 2023
ЗМІСТ
ВСТУП..............................................................................................................................3
1 ПОСТАНОВКА ЗАДАЧІ........................................................................................4
2 ПРАКТИЧНА РЕАЛІЗАЦІЯ ЗАДАЧІ...................................................................6
2.1 Архітектура додатку................................................................................................6
2.2 Реалізація бази даних..............................................................................................8
2.3 Програмні модулі інформаційної системи..........................................................12
2.4 Відображення результатів роботи у браузері.....................................................16
2.5 Відображення ходу роботи над розробкою додатку у GitHub..........................23
3 ТЕСТУВАННЯ...........................................................................................................25
ВИСНОВКИ...................................................................................................................42
СПИСОК ВИКОРИСТАНОЇ ЛІТЕРАТУРИ...............................................................43
ДОДАТОК А..................................................................................................................44
ДОДАТОК Б.................................................................................................................115
3
ВСТУП
4
1 ПОСТАНОВКА ЗАДАЧІ
5
Рисунок 1.2 – Функції автора
7
2 ПРАКТИЧНА РЕАЛІЗАЦІЯ ЗАДАЧІ
2.1.1 Архітектура ІС
8
Структуру фреймворку показано на рисунку 2.2. Основний корінь додатку
міститься в базовому (кореневому) каталозі. Внутрішні папки включають config
для конфігураційних файлів, controllers для контролерів, models для моделей,
views для представлень та web для веб-ресурсів [2]. Основні компоненти Yii2 – це
модель, що відповідає за управління даними та бізнес-логікою, представлення, яке
визначає структуру та вигляд веб-сторінок, та контролер, який обробляє HTTP-
запити та взаємодіє з моделлю та представленням.
10
Після запуску міграцій було створено базу даних, фізична модель якої
представлена на рисунку 2.4.
11
Рисунок 2.6 – Вміст таблиці «article»
12
Рисунок 2.9 – Структура таблиці «comment»
13
2.3 Програмні модулі інформаційної системи
14
говорять вказують вимоги до типу даних і максимального розміру для зазначених
полів.
Також модель користувача містить методи для отримання доступу до
атрибутів, пошуку запису в базі даних та інших перевірок. Наприклад, наступний
метод використовується при авторизації для перевірки правильності введеного
паролю:
public function validatePassword($password)
{
return Yii::$app
->getSecurity()
->validatePassword($password, $this->password) ? true :
false;
}
Клас UserSearch виконує пошук користувачів. Наслідується від описаної
вище моделі User і працює з таблицею user в базі даних.
Робота з формати також реалізована за допомогою моделей. Наприклад, за
авторизацію відповідає LoginForm. Цей клас містить в атрибутах посилання на
модель user і працює з нею.
В інформаційній системи було створено додаткові модулі для
адміністратора і користувача за допомогою графічного інтерфейсу «yii code
generator». Модуль представляє собою підсистему, яка сама по собі містить
елементи MVC, такі як моделі, представлення, контролери тощо [4].
Модулі адміністрування для звичайного користувача та адміністратора
мають схожий функціонал. Перший – більш обмежений за функціоналом. З
допомогою цього модулю користувач має можливість створювати, редагувати та
видаляти власні статті, а також змінити аби видалити свій обліковий запис.
Перелік компонентів модулю показано на рисунку 2.14.
15
Рисунок 2.14 – Компоненти модулю користувача
Модуль для адміністратора, окрім описаних можливостей, дозволяє
створювати нових користувачів, видаляти будь-які коментарі, а також
створювати, видаляти й редагувати статті й категорії. Перелік компонентів даного
модулю показано на рисунку 2.15.
16
Рисунок 2.15 – Компоненти модулю адміністратора
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
Представлення index.php відображає отримані від контролера дані
користувачеві у вигляді таблиці. Фрагмент програмного коду наведено нижче.
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'id',
'text',
'user_id',
'comment_id',
'article_id',
//'date',
//'delete',
[
'class' => ActionColumn::class,
'template' => '{view} {delete}',
'urlCreator' => function ($action, Comment $model,
$key, $index, $column) {
return Url::toRoute([$action, 'id' => $model-
>id]);
}
],
],
]); ?>
Програмний код у повному обсязі міститься в додатку А.
18
2.4 Відображення результатів роботи у браузері
19
Рисунок 2.17 – Головна сторінка (частина 2)
На рисунках 2.18 – 2.19 представлена сторінка перегляду статті з
коментарями до неї. Також зазначено автора, категорію, теги, і дату публікації.
Користувач має можливість поділитися постом у соціальних мережах, залишити
власний коментар та відповісти на коментарі інших.
20
Автор, окрім вже описаного функціоналу, має можливість зареєструватися
та / або авторизуватися. Для цього можна скористатися командою в головному
меню. Форма реєстрації показана на рисунку 2.20.
21
Після проходження даного етапу користувач отримує доступ до панелі
управління. Перейти до неї можна за допомогою головного меню. Головна
сторінка даної панелі зображена на рисунку 2.22.
22
Рисунок 2.24 – Статті автора
23
Рисунок 2.26 – Функціонал панелі адміністратора
24
Це саме стосується статей і категорій. На рисунках 2.29 – 2.31
представлено функціонал створення і видалення категорії.
25
2.5 Відображення ходу роботи над розробкою додатку у GitHub
Для роботи над проектом було використано системи контролю версій Git.
Відкритий репозиторій створено на GitHub, використано профіль kasianenkod
(рис. 2.32). Графік здійснення комітів продемонстровано на рисунку 2.33.
26
На рисунку 2.34 зображена аналітика репозиторію, що демонструє роботу
над проектом.
27
3 ТЕСТУВАННЯ
28
Продовження таблиці 3.1
№ Тестування Очікуваний Отриманий результат Резуль-
кей результат тат
-су кейсу
4 Спроба Повідом- Пройде-
відправити не лення про ний
заповнену помилки біля
фурму відповідних
авторизації полів
29
Таблиця 3.2 – Тестування пошуку статей за тегами
№ Тестування Очікуваний Отриманий результат Резуль-
кей результат тат
-су кейсу
1 Спроба Повідом- Пройде-
відправити не лення про те, ний
заповнену що текст для
форму пошуку не
введено
2 Відправка Престав- Пройде-
форми з лення статей, ний
існуючим як містять
тегом введений тег
30
Продовження таблиці 3.3
№ Тестування Очікуваний Отриманий результат Резуль-
кей результат тат
-су кейсу
3 Здійснено Успішно Пройде-
вихід. Додано доданий ний
коментар як коментар
гість
31
Продовження таблиці 3.3
№ Тестування Очікуваний Отриманий результат Резуль-
кей результат тат
-су кейсу
6 Створено Успішно Пройде-
новий видалений ний
коментар і коментар
видалено його
32
Продовження таблиці 3.4
№ Тестування Очікуваний Отриманий результат Резуль-
кей результат тат
-су кейсу
4 Виконано вхід Помилка про Пройде-
під тестовим некорект- ний
обліковим ність даних
записом.
Відкрито
форму
оновлення
користувача.
Вказано
некоректний
логін
5 Спроба Повідомлен- Пройде-
надіслати не ня про ний
заповнену помилки біля
форму відповідних
полів
33
Продовження таблиці 3.4
№ Тестування Очікуваний Отриманий результат Резуль-
кей результат тат
-су кейсу
8 Зміна Повідом- Пройде-
зображення. лення про ний
Вибрано файл помилку
не
дозволеного
розширення
9 Відправка Додано Пройде-
форми зміни зображення ний
зображення.
Обрано
дозволений
формат файлу
34
Продовження таблиці 3.4
№ Тестування Очікуваний Отриманий результат Резуль-
кей результат тат
-су кейсу
12 Виконано вхід Повідом- Пройде-
під обліковим лення про ний
записом помилки
автора.
Створення
статі. Спроба
відправити
пусту форму
35
Продовження таблиці 3.4
№ Тестування Очікуваний Отриманий результат Резуль-
кей результат тат
-су кейсу
15 Відправка Додано Пройде-
форми зображення ний
додавання
зображення
36
Продовження таблиці 3.5
№ Тестування Очікуваний Отриманий результат Резуль-
кей результат тат
-су кейсу
3 Спроба Помилка 404 Пройде-
доступу до ний
модулю як
гість
4 Спроба Повідом- Пройде-
надіслати лення про ний
пусту форму помилки біля
для створення відповідних
користувача. полів форми
37
Продовження таблиці 3.5
№ Тестування Очікуваний Отриманий результат Резуль-
кей результат тат
-су кейсу
7 Спроба Повідом- Пройде-
надіслати не лення про ний
заповнену помилки біля
форму для відповідних
оновлення полів форми
користувача
39
40
Продовження таблиці 3.5
№ Тестування Очікуваний Отриманий результат Резуль-
кей результат тат
-су кейсу
14 Надсилання Видалено Пройде-
форми для користувача ний
видалення (und_test)
користувача
41
Продовження таблиці 3.5
№ Тестування Очікуваний Отриманий результат Резуль-
кей результат тат
-су кейсу
17 Відправка Додано Пройде-
форми зображення ний
додавання
зображення з
файлом
коректного
розширення
42
Продовження таблиці 3.5
№ Тестування Очікуваний Отриманий результат Резуль-
кей результат тат
-су кейсу
20 Надсилання Видалена Пройде-
форми стаття ний
видалення
статті іншого
користувача
43
Продовження таблиці 3.5
№ Тестування Очікуваний Отриманий результат Резуль-
кей результат тат
-су кейсу
25 Надсилання Видалена Пройде-
форми категорія ний
видалення
категорії
44
Продовження таблиці 3.5
№ Тестування Очікуваний Отриманий результат Резуль-
кей результат тат
-су кейсу
28 Надсилання Видалено Пройде-
форми коментар ний
видалення
коментаря
45
ВИСНОВКИ
46
СПИСОК ВИКОРИСТАНОЇ ЛІТЕРАТУРИ
47
ДОДАТОК А
Програмний код
AppAsset.php
<?php
/**
* @link https://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license https://www.yiiframework.com/license/
*/
namespace app\assets;
use yii\web\AssetBundle;
/**
* Main application asset bundle.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class AppAsset extends AssetBundle
{
public $basePath = '@webroot';
public $baseUrl = '@web';
public $css = [
'css/site.css',
'css/style.css',
'css/custom.css',
'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-
awesome.min.css',
'https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css',
];
public $js = [
'https://cdn.jsdelivr.net/npm/flatpickr',
];
public $depends = [
'yii\web\YiiAsset',
'yii\bootstrap5\BootstrapAsset'
];
}
config/web.php
<?php
48
$params = require __DIR__ . '/params.php';
$db = require __DIR__ . '/db.php';
$config = [
'id' => 'basic',
'name' => 'Блог',
'basePath' => dirname(__DIR__),
'bootstrap' => ['log'],
'aliases' => [
'@bower' => '@vendor/bower-asset',
'@npm' => '@vendor/npm-asset',
],
'components' => [
'request' => [
'cookieValidationKey' => 'key4732rh378yh4',
'baseUrl' => '',
],
'cache' => [
'class' => 'yii\caching\FileCache',
],
'user' => [
'identityClass' => 'app\models\User',
'enableAutoLogin' => true,
'loginUrl' => ['auth/login']
],
'errorHandler' => [
'errorAction' => 'site/error',
],
'mailer' => [
'class' => \yii\symfonymailer\Mailer::class,
'viewPath' => '@app/mail',
// send all mails to a file by default.
'useFileTransport' => true,
],
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning'],
],
],
],
'db' => $db,
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
'' => 'site/index',
'<action>' => 'site/<action>',
/* admin and user modules */
49
'admin/index' => 'admin/default/index',
'user/index' => 'user/default/index',
'<action>' => 'default/<action>',
'/login' => 'site/login',
'<action>' => 'site/<action>',
'<controller:(post|comment)>/<id:\d+>/<action:
(create|update|delete)>' => '<controller>/<action>',
'<controller:(post|comment)>/<id:\d+>' =>
'<controller>/view',
'<controller:(post|comment)>s' =>
'<controller>/index',
],
],
],
'modules' => [
'admin' => [
'class' => 'app\modules\admin\Module',
],
'user' => [
'class' => 'app\modules\user\Module',
],
],
if (YII_ENV_DEV) {
// configuration adjustments for 'dev' environment
$config['bootstrap'][] = 'debug';
$config['modules']['debug'] = [
'class' => 'yii\debug\Module',
// uncomment the following to add your IP if you are not
connecting from localhost.
//'allowedIPs' => ['127.0.0.1', '::1'],
];
$config['bootstrap'][] = 'gii';
$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', '::1'],
];
}
return $config;
Основні контролери
50
AuthController.php
<?php
namespace app\controllers;
use app\models\LoginForm;
use app\models\SignupForm;
use app\models\User;
use Yii;
use yii\web\Controller;
use yii\web\Response;
/**
* Logout action.
*
* @return Response
*/
public function actionLogout()
{
Yii::$app->user->logout();
return $this->goHome();
}
51
return $this->redirect(['auth/login']);
}
}
return $this->render('/site/signup', ['model' => $model]);
}
}
SiteController.php
<?php
namespace app\controllers;
use Yii;
use yii\data\Pagination;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\web\Response;
use yii\filters\VerbFilter;
use app\models\Article;
use app\models\Comment;
use app\models\CommentForm;
use app\models\ContactForm;
use app\models\LoginForm;
use app\models\Topic;
use app\models\SearchForm;
52
];
}
/**
* {@inheritdoc}
*/
public function actions()
{
return [
'error' => [
'class' => 'yii\web\ErrorAction',
],
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
],
];
}
/**
* Displays homepage.
*
* @return string
*/
public function actionIndex()
{
/**
* Displays blog page
*/
public function actionView($id)
{
$article = Article::findOne($id);
$topics = Topic::find()->all();
$comments = $article->comments;
$commentsParent = array_filter($comments, function ($k) {
return $k['comment_id'] == null;
});
$commentsChild = array_filter($comments, function ($k) {
return ($k['comment_id'] != null && !$k['delete']);
});
$commentForm = new CommentForm();
return $this->render('single', [
'article' => $article,
'popular' => $popular,
'recent' => $recent,
'topics' => $topics,
'commentsParent' => $commentsParent,
'commentsChild' => $commentsChild,
'commentForm' => $commentForm,
]);
}
/**
* Search article
*/
public function actionSearch()
{
$model = new SearchForm();
if (Yii::$app->request->isGet) {
$model->load(Yii::$app->request->get());
$data = $model->SearchArticle(3);
54
$popular = Article::find()->orderBy('viewed desc')-
>limit(3)->all();
$topics = Topic::find()->all();
return $this->render('search', [
]);
}
}
/**
* Displays topic page
*/
public function actionTopic($id)
{
$query = Article::find()->where(['topic_id' => $id]);
$count = $query->count();
return $this->render('topic', [
'articles' => $articles,
'pagination' => $pagination,
55
'popular' => $popular,
'recent' => $recent,
'topics' => $topics,
]);
}
/**
* Displays comment
*/
public function actionComment($id, $id_comment = null)
{
$model = new CommentForm();
if (Yii::$app->request->isPost) {
$model->load(Yii::$app->request->post());
if ($model->saveComment($id, $id_comment)) {
return $this->redirect(['site/view', 'id' => $id]);
}
}
}
/**
* Deletes comment
*/
public function actionCommentDelete($id, $id_comment)
{
if (Yii::$app->request->isPost) {
$data = Comment::findOne($id_comment);
if ($data->user_id == Yii::$app->user->id) {
$data->delete = true;
$data->save(false);
}
return $this->redirect(['site/view', 'id' => $id]);
}
}
/**
* Displays contact page.
*
* @return Response|string
*/
public function actionContact()
{
$model = new ContactForm();
if ($model->load(Yii::$app->request->post()) && $model-
>contact(Yii::$app->params['adminEmail'])) {
Yii::$app->session->setFlash('contactFormSubmitted');
return $this->refresh();
}
return $this->render('contact', [
'model' => $model,
]);
}
56
/**
* Displays about page.
*
* @return string
*/
public function actionAbout()
{
return $this->render('about');
}
}
Моделі
Article.php
<?php
namespace app\models;
use Yii;
/**
* This is the model class for table "article".
*
* @property int $id
* @property string|null $title
* @property string|null $description
* @property string|null $date
* @property string|null $image
* @property string|null $tag
* @property int|null $viewed
* @property int|null $topic_id
* @property int|null $user_id
*
* @property Comment[] $comments
* @property Topic $topic
* @property User $user
*/
class Article extends \yii\db\ActiveRecord
{
/**
* {@inheritdoc}
*/
public static function tableName()
{
return 'article';
}
/**
* {@inheritdoc}
*/
public function rules()
57
{
return [
[['title', 'description', 'date', 'user_id',
'topic_id'], 'required'],
[['description'], 'string'],
[['date'], 'safe'],
[['viewed', 'topic_id', 'user_id'], 'integer'],
[['title', 'image', 'tag'], 'string', 'max' => 255],
[['user_id'], 'exist', 'skipOnError' => true,
'targetClass' => User::class, 'targetAttribute' => ['user_id' =>
'id']],
[['topic_id'], 'exist', 'skipOnError' => true,
'targetClass' => Topic::class, 'targetAttribute' => ['topic_id' =>
'id']],
];
}
/**
* {@inheritdoc}
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'title' => 'Title',
'description' => 'Description',
'date' => 'Date',
'image' => 'Image',
'tag' => 'Tag',
'viewed' => 'Viewed',
'topic_id' => 'Topic ID',
'user_id' => 'User ID',
];
}
/**
* Gets query for [[Comments]].
*
* @return \yii\db\ActiveQuery
*/
public function getComments()
{
return $this->hasMany(Comment::class, ['article_id' =>
'id']);
}
/**
* Gets query for [[Topic]].
*
* @return \yii\db\ActiveQuery
*/
public function getTopic()
{
58
return $this->hasOne(Topic::class, ['id' => 'topic_id']);
}
/**
* Gets query for [[User]].
*
* @return \yii\db\ActiveQuery
*/
public function getUser()
{
return $this->hasOne(User::class, ['id' => 'user_id']);
}
return $this->save();
}
/**
59
* View counter for article
*/
ArticleSearch.php
<?php
namespace app\models;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use app\models\Article;
/**
* ArticleSearch represents the model behind the search form of
`app\models\Article`.
*/
class ArticleSearch extends Article
{
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['id', 'viewed', 'topic_id', 'user_id'], 'integer'],
[['title', 'description', 'date', 'image', 'tag'],
'safe'],
];
}
/**
* {@inheritdoc}
*/
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* Creates data provider instance with search query applied
*
* @param array $params
*
* @return ActiveDataProvider
60
*/
public function search($params)
{
$query = Article::find();
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to
return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
return $dataProvider;
}
}
Comment.php
<?php
namespace app\models;
use Yii;
/**
* This is the model class for table "comment".
*
* @property int $id
* @property string|null $text
* @property int|null $user_id
61
* @property int|null $comment_id
* @property int|null $article_id
* @property string|null $date
* @property int|null $delete
*
* @property Article $article
* @property Comment $comment
* @property Comment[] $comments
* @property User $user
*/
class Comment extends \yii\db\ActiveRecord
{
/**
* {@inheritdoc}
*/
public static function tableName()
{
return 'comment';
}
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['date', 'text', 'article_id'], 'required'],
[['user_id', 'comment_id', 'article_id', 'delete'],
'integer'],
[['date'], 'safe'],
[['text'], 'string', 'max' => 255],
[['article_id'], 'exist', 'skipOnError' => true,
'targetClass' => Article::class, 'targetAttribute' => ['article_id'
=> 'id']],
[['user_id'], 'exist', 'skipOnError' => true,
'targetClass' => User::class, 'targetAttribute' => ['user_id' =>
'id']],
[['comment_id'], 'exist', 'skipOnError' => true,
'targetClass' => Comment::class, 'targetAttribute' => ['comment_id'
=> 'id']],
];
}
/**
* {@inheritdoc}
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'text' => 'Text',
'user_id' => 'User ID',
'comment_id' => 'Comment ID',
62
'article_id' => 'Article ID',
'date' => 'Date',
'delete' => 'Delete',
];
}
/**
* Gets query for [[Article]].
*
* @return \yii\db\ActiveQuery
*/
public function getArticle()
{
return $this->hasOne(Article::class, ['id' =>
'article_id']);
}
/**
* Gets query for [[Comment]].
*
* @return \yii\db\ActiveQuery
*/
public function getComment()
{
return $this->hasOne(Comment::class, ['id' =>
'comment_id']);
}
/**
* Gets query for [[Comments]].
*
* @return \yii\db\ActiveQuery
*/
public function getComments()
{
return $this->hasMany(Comment::class, ['comment_id' =>
'id']);
}
/**
* Gets query for [[User]].
*
* @return \yii\db\ActiveQuery
*/
public function getUser()
{
return $this->hasOne(User::class, ['id' => 'user_id']);
}
CommentForm.php
<?php
namespace app\models;
use Yii;
use yii\base\Model;
$comment->article_id = $article_id;
if ($comment_id != null) {
$comment->comment_id = $comment_id;
}
$comment->date = date('Y-m-d');
return $comment->save();
}
}
CommentSearch.php
<?php
namespace app\models;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use app\models\Comment;
/**
64
* CommentSearch represents the model behind the search form of
`app\models\Comment`.
*/
class CommentSearch extends Comment
{
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['id', 'user_id', 'comment_id', 'article_id',
'delete'], 'integer'],
[['text', 'date'], 'safe'],
];
}
/**
* {@inheritdoc}
*/
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* Creates data provider instance with search query applied
*
* @param array $params
*
* @return ActiveDataProvider
*/
public function search($params)
{
$query = Comment::find();
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to
return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
return $dataProvider;
}
}
ImageUpload.php
<?php
namespace app\models;
use Yii;
use yii\base\Model;
use yii\web\UploadedFile;
if ($this->validate()) {
$this->deleteCurrentImage($currentImage);
$filename = strtolower(md5(uniqid($file->baseName)) .
'.' . $file->extension);
$file->saveAs(Yii::getAlias('@web') . 'uploads/' .
$filename);
return $filename;
}
}
66
public function deleteCurrentImage($currentImage)
{
if (
file_exists(Yii::getAlias('@web') . 'uploads/' .
$currentImage) &&
is_file(Yii::getAlias('@web') . 'uploads/' .
$currentImage)
) {
unlink(Yii::getAlias('@web') . 'uploads/' .
$currentImage);
}
}
}
LoginForm.php
<?php
namespace app\models;
use Yii;
use yii\base\Model;
/**
* LoginForm is the model behind the login form.
*
* @property-read User|null $user
*
*/
class LoginForm extends Model
{
public $username;
public $password;
public $rememberMe = true;
/**
* @return array the validation rules.
*/
public function rules()
{
return [
// username and password are both required
[['username', 'password'], 'required'],
// rememberMe must be a boolean value
['rememberMe', 'boolean'],
// password is validated by validatePassword()
['password', 'validatePassword'],
];
}
/**
* Validates the password.
67
* This method serves as the inline validation for password.
*
* @param string $attribute the attribute currently being
validated
* @param array $params the additional name-value pairs given in
the rule
*/
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors()) {
$user = $this->getUser();
if (!$user || !$user->validatePassword($this->password))
{
$this->addError($attribute, 'Неправильний логін або
пароль.');
}
}
}
/**
* Logs in a user using the provided username and password.
* @return bool whether the user is logged in successfully
*/
public function login()
{
if ($this->validate()) {
return Yii::$app->user->login($this->getUser(), $this-
>rememberMe ? 3600*24*30 : 0);
}
return false;
}
/**
* Finds user by [[username]]
*
* @return User|null
*/
public function getUser()
{
if ($this->_user === false) {
$this->_user = User::findByUsername($this->username);
}
return $this->_user;
}
}
SearchForm.php
<?php
namespace app\models;
68
use Yii;
use yii\base\Model;
use yii\data\Pagination;
SignupForm.php
<?php
namespace app\models;
use yii\base\Model;
69
[['login'], 'unique', 'targetClass' => 'app\models\
User', 'targetAttribute' => 'login'],
];
}
Topic.php
<?php
namespace app\models;
use Yii;
/**
* This is the model class for table "topic".
*
* @property int $id
* @property string|null $name
*
* @property Article[] $articles
*/
class Topic extends \yii\db\ActiveRecord
{
/**
* {@inheritdoc}
*/
public static function tableName()
{
return 'topic';
}
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['name'], 'required'],
[['name'], 'string', 'max' => 255],
];
}
/**
70
* {@inheritdoc}
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'name' => 'Name',
];
}
/**
* Gets query for [[Articles]].
*
* @return \yii\db\ActiveQuery
*/
public function getArticles()
{
return $this->hasMany(Article::class, ['topic_id' => 'id']);
}
}
TopicSearch.php
<?php
namespace app\models;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use app\models\Topic;
/**
* TopicSearch represents the model behind the search form of `app\
models\Topic`.
*/
class TopicSearch extends Topic
{
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['id'], 'integer'],
[['name'], 'safe'],
];
}
/**
* {@inheritdoc}
*/
public function scenarios()
{
71
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* Creates data provider instance with search query applied
*
* @param array $params
*
* @return ActiveDataProvider
*/
public function search($params)
{
$query = Topic::find();
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to
return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
return $dataProvider;
}
}
User.php
<?php
namespace app\models;
use Yii;
use yii\web\IdentityInterface;
/**
* This is the model class for table "user".
*
72
* @property int $id
* @property string|null $name
* @property string|null $login
* @property string|null $password
* @property string|null $image
*
* @property Article[] $articles
* @property Comment[] $comments
*/
class User extends \yii\db\ActiveRecord implements IdentityInterface
{
/**
* {@inheritdoc}
*/
public static function tableName()
{
return 'user';
}
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['name', 'login', 'password'], 'required'],
['login', 'email'],
[
['login'], 'unique',
'message' => 'Цей логін вже використовується.'
],
[['name', 'login', 'password'], 'string'],
[['name', 'login', 'password', 'image'], 'string', 'max'
=> 255],
];
}
/**
* {@inheritdoc}
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'name' => 'Name',
'login' => 'Login',
'password' => 'Password',
'image' => 'Image',
];
}
/**
* Gets query for [[Articles]].
73
*
* @return \yii\db\ActiveQuery
*/
public function getArticles()
{
return $this->hasMany(Article::class, ['user_id' => 'id']);
}
/**
* Gets query for [[Comments]].
*
* @return \yii\db\ActiveQuery
*/
public function getComments()
{
return $this->hasMany(Comment::class, ['user_id' => 'id']);
}
/**
* @inheritDoc
*/
public static function findIdentity($id)
{
return User::findOne($id);
}
/**
74
* @inheritDoc
*/
public static function findIdentityByAccessToken($token, $type =
null)
{
// TODO: Implement findIdentityByAccessToken() method.
}
/**
* @inheritDoc
*/
public function getId()
{
return $this->id;
}
/**
* @inheritDoc
*/
public function getAuthKey()
{
// TODO: Implement getAuthKey() method.
}
/**
* @inheritDoc
*/
public function validateAuthKey($authKey)
{
// TODO: Implement validateAuthKey() method.
}
75
public function create()
{
$hash = Yii::$app->getSecurity()-
>generatePasswordHash($this->password);
$this->password = $hash;
// dd($this);
return $this->save(false);
}
return $this->save(false);
}
UserSearch.php
<?php
namespace app\models;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use app\models\User;
/**
* UserSearch represents the model behind the search form of `app\
models\User`.
*/
class UserSearch extends User
{
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['id'], 'integer'],
[['name', 'login', 'password', 'image'], 'safe'],
];
76
}
/**
* {@inheritdoc}
*/
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* Creates data provider instance with search query applied
*
* @param array $params
*
* @return ActiveDataProvider
*/
public function search($params)
{
$query = User::find();
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to
return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
return $dataProvider;
}
}
Міграції
77
<?php
use yii\db\Migration;
/**
* Handles the creation of table `{{%topic}}`.
*/
class m240106_201024_create_topic_table extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->createTable('{{%topic}}', [
'id' => $this->primaryKey(),
'name' => $this->string()->notNull(),
]);
}
/**
* {@inheritdoc}
*/
public function safeDown()
{
$this->dropTable('{{%topic}}');
}
}
<?php
use yii\db\Migration;
/**
* Handles the creation of table `{{%user}}`.
*/
class m240106_201056_create_user_table extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->createTable('{{%user}}', [
'id' => $this->primaryKey(),
'name' => $this->string()->notNull(),
'login' => $this->string()->notNull(),
'password' => $this->string()->notNull(),
'image' => $this->string(),
'is_admin' => $this->boolean()->notNull(),
]);
}
78
/**
* {@inheritdoc}
*/
public function safeDown()
{
$this->dropTable('{{%user}}');
}
}
<?php
use yii\db\Migration;
/**
* Handles the creation of table `{{%article}}`.
*/
class m240106_201103_create_article_table extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->createTable('{{%article}}', [
'id' => $this->primaryKey()->notNull(),
'title' => $this->string()->notNull(),
'description' => $this->text()->notNull(),
'date' => $this->date()->notNull(),
'image' => $this->string(),
'tag' => $this->string(),
'viewed' => $this->integer()->notNull()->default(0),
'topic_id' => $this->integer()->notNull(),
'user_id' => $this->integer()->notNull(),
]);
// create index for column `topic_id`
$this->createIndex(
'idx-topic_id',
'article',
'topic_id'
);
// add foreign key for table `topic`
$this->addForeignKey(
'fk-topic_id',
'article',
'topic_id',
'topic',
'id',
'CASCADE'
);
// create index for column `user_id`
$this->createIndex(
'idx-post-user_id',
79
'article',
'user_id'
);
// add foreign key for table `user`
$this->addForeignKey(
'fk-post-user_id',
'article',
'user_id',
'user',
'id',
'CASCADE'
);
}
/**
* {@inheritdoc}
*/
public function safeDown()
{
$this->dropTable('{{%article}}');
}
}
<?php
use yii\db\Migration;
/**
* Handles the creation of table `{{%comment}}`.
*/
class m240106_201117_create_comment_table extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->createTable('{{%comment}}', [
'id' => $this->primaryKey(),
'text' => $this->string()->notNull(),
'user_id' => $this->integer(),
'comment_id' => $this->integer(),
'article_id' => $this->integer()->notNull(),
'date' => $this->date()->notNull(),
'delete' => $this->boolean(),
]);
// create index for column `user_id`
$this->createIndex(
'idx-post-user_id',
'comment',
'user_id'
);
80
// add foreign key for table `user`
$this->addForeignKey(
'fk-comment-post-user_id',
'comment',
'user_id',
'user',
'id',
'CASCADE'
);
// create index for column `article_id`
$this->createIndex(
'idx-article_id',
'comment',
'article_id'
);
// add foreign key for table `article`
$this->addForeignKey(
'fk-article_id',
'comment',
'article_id',
'article',
'id',
'CASCADE'
);
// create index for column `comment_id`
$this->createIndex(
'idx-comment_id',
'comment',
'comment_id'
);
// add foreign key
$this->addForeignKey(
'fk-comment_id',
'comment',
'comment_id',
'comment',
'id',
'CASCADE'
);
}
/**
* {@inheritdoc}
*/
public function safeDown()
{
$this->dropTable('{{%comment}}');
}
}
Стилі
81
style.css
@import url(https://fonts.googleapis.com/css?
family=Open+Sans:400,400italic,700,600);
@import url(https://fonts.googleapis.com/css?
family=Merriweather:400,700,400italic);
body {
font-family: 'Open Sans', sans-serif;
background: #ccccc4;
}
.main-content {
margin-top: 100px;
}
.row {
margin-right: -15px;
margin-left: -15px;
}
.post {
background: #fff;
margin-bottom: 50px;
}
article {
border: 1px solid #d6d8d0;
}
.post-content {
padding: 0 43px;
overflow: hidden;
}
.post-thumb img {
width: 100%;
}
.post-thumb {
position: relative;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-flex-direction: column;
-ms-flex-direction: column;
82
flex-direction: column;
}
.entry-header {
margin: 40px 0 23px;
}
.entry-header h1 {
margin-top: 0;
font-size: 24px;
}
span,
p,
input {
font-family: 'Merriweather', serif;
line-height: 26px;
}
.post .social-share {
border-top: 1px solid #EEE;
padding: 15px 0 75px;
}
.social-share-title {
padding: 9px 0;
font-style: italic;
font-size: 13px;
}
/* .pagination {
display: inline-block;
padding-left: 0;
margin: 0 0 50px;
border-radius: 4px;
} */
.entry-header h1 a:hover {
color: #7b7c58;
}
.entry-header h1 a {
font-weight: 600;
color: #333333;
-webkit-transition: all 0.5s;
transition: all 0.5s;
font-size: 24px;
line-height: 34px;
}
.social-share ul li {
list-style: none;
display: inline-block;
}
.social-share ul li a i {
height: 30px;
width: 30px;
line-height: 30px;
font-size: 15px;
border-radius: 50%;
margin: 3px;
border: 1px solid #e5e5e5;
-webkit-transition: all 0.4s;
transition: all 0.4s;
color: #C2C2C2;
}
.social-share span a {
84
color: #333;
}
.pagination>li>a,
.pagination>li>span {
position: relative;
float: left;
padding: 6px 12px;
margin-left: 5px;
line-height: 1.42857143;
color: #333;
text-decoration: none;
background-color: #fff;
border: 0;
-webkit-transition: all 0.3s;
transition: all 0.3s;
font-weight: 600;
}
.widget {
padding: 37px 30px;
margin-bottom: 35px;
background-color: #fff;
border: 1px solid #eee;
}
.widget-title {
font-size: 18px;
margin: 0 0 25px;
letter-spacing: 0.5px;
color: #333333;
}
.widget .popular-post:first-of-type {
padding-top: 0;
}
.popular-post {
border-bottom: 1px solid #eee;
padding: 25px 0 20px;
}
.popular-img {
position: relative;
width: 100%;
margin-bottom: 15px;
}
.popular-img img {
width: 100%;
}
85
img {
max-width: 100%;
}
.p-overlay {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: rgba(0, 0, 0, 0.5);
opacity: 0;
-webkit-transition: all 0.44s;
transition: all 0.44s;
}
.p-content a {
font-size: 12px;
color: #333333;
text-decoration: none;
display: block;
font-weight: 600;
transition: all 0.44s;
}
.p-content span {
color: #666666;
font-size: 12px;
font-style: italic;
}
/* comments */
.leave-comment {
background-color: #fff;
margin: 30px 0;
padding: 20px;
color: #212121;
}
.send-btn {
background: #333;
color: #fff;
text-transform: uppercase;
-webkit-transition: all .33s;
transition: all .33s;
border-radius: 0;
}
.leave-comment h4 {
font-size: 14px;
text-transform: uppercase;
font-weight: 700;
}
86
.leave-comment input,
.leave-comment textarea {
background-color: #FAFAFA;
color: #999999;
border-radius: 0;
font-size: 14px;
line-height: 28px;
padding: 20px;
border-color: #eee;
-webkit-box-shadow: inset 0 0 0 rgba(0, 0, 0, .075);
box-shadow: inset 0 0 0 rgba(0, 0, 0, .075);
}
body .comment {
position: relative;
border-bottom: 1px solid #c7b8b8;
padding: 10px;
min-height: 120px;
background-color: white;
}
body .comment-img {
float: left;
margin-right: 10px;
width: 100px;
height: 100px;
overflow: hidden;
position: relative;
}
body .comment-body {
margin-left: 110px;
}
body .comment-top {
display: flex;
justify-content: space-between;
}
body .comment-date {
font-size: 12px;
}
body .comment-text {
word-break: break-word;
padding-right: 10px;
}
87
body .comment-childs-container {
transition: max-height .4s;
}
body .comment-more-button {
text-align: center;
padding: 8px 8px 8px 30px;
border: 1px solid #e2e2e2;
border-top: none;
cursor: pointer;
}
body .comment-childs {
padding: 10px 0 10px 50px;
}
a.replay.btn.pull-right {
padding: 8px 7px;
display: inline-block;
font-size: 12px;
cursor: pointer;
margin-left: 10px;
color: #333;
background-color: white;
border: 1px solid #e2e2e2;
}
img.img-round {
border-radius: 50%;
height: 100px;
width: 100px;
}
i.fa.fa-trash {
font-size: 2.5em;
}
.comment-delete {
float: right;
margin-top: -35px;
}
.leave-comment-child {
background-color: white;
padding: 10px;
}
.post-list .post-content {
padding: 0 20px 0 0;
overflow: hidden;
}
88
.post-list .entry-header {
margin: 22px 0;
}
.entry-header h6 a {
color: #686945;
font-weight: 700;
font-size: 12px;
}
.entry-header h6 a:hover {
color: #7b7c58;
}
.entry-header h1 a,
.entry-header h6 a {
text-decoration: none;
letter-spacing: 0.5px;
}
.post-grid .entry-header h1 a,
.post-list .entry-header h1 a {
font-size: 18px;
line-height: 24px;
}
.search-form {
height: 90px;
}
.form-group.field-searchform-text.required {
font-size: 17px;
float: left;
width: 80%;
}
.btn-search {
float: left;
width: 20%;
padding: 4px;
background: #686945;
color: white;
font-size: 17px;
border: 1px solid grey;
border-radius: 5px;
border-left: none;
cursor: pointer;
height: 38px;
89
margin-top: 1px;
}
.btn-search:hover {
background: #7b7c58;
}
custom.css
a {
text-decoration: none;
display: inline-block;
color: #686945;
}
a:hover {
color: #73770f;
}
/* .form-control, .form-control:focus {
border-color: #686945;
}
form label {
border-color: #686945;
} */
form .btn {
background-color: #686945;
border: 1px solid #686945;
font-size: 15px;
}
.form-check-label::before{
color: #686945;
}
select {
background-color: #fff;
border: 1px solid #686945;
color: #686945;
cursor: pointer;
appearance: none;
background-image: url('data:image/svg+xml;utf8,<svg
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
fill="%23686945" width="18px" height="18px"><path d="M7 10l5 5 5-5z"
/></svg>');
background-repeat: no-repeat;
background-position: right center;
padding-right: 20px;
}
select:hover {
90
border-color: #A29E71;
}
form .my-btn-success {
background-color: #198754;
border-color: #198754;
}
form .my-btn-primary {
background-color: #0a58ca;
border-color: #0a58ca;
}
form .my-btn-primary:hover,
form .my-btn-primary:active {
background-color: #A29E71;
border-color: #A29E71;
}
/* адмінка */
.module-default-index {
display: block;
background-color: #f5f5f5;
margin: 50px 30px;
padding: 15px;
}
.module-default-index > p {
margin-top: 20px;
}
.module-default-index .features {
margin: 0;
padding: 15px 10px;
display: flex;
justify-content: center;
height: auto;
}
Layout
admin.php
<?php
use app\assets\AppAsset;
use app\widgets\Alert;
use yii\bootstrap5\Breadcrumbs;
use yii\bootstrap5\Html;
use yii\bootstrap5\Nav;
use yii\bootstrap5\NavBar;
AppAsset::register($this);
$this->registerCsrfMetaTags();
$this->registerMetaTag(['charset' => Yii::$app->charset],
'charset');
$this->registerMetaTag(['name' => 'viewport', 'content' =>
'width=device-width, initial-scale=1, shrink-to-fit=no']);
$this->registerMetaTag(['name' => 'description', 'content' => $this-
>params['meta_description'] ?? '']);
$this->registerMetaTag(['name' => 'keywords', 'content' => $this-
>params['meta_keywords'] ?? '']);
$this->registerLinkTag(['rel' => 'icon', 'type' => 'image/x-icon',
'href' => Yii::getAlias('@web/favicon.ico')]);
92
?>
<?php $this->beginPage() ?>
<!DOCTYPE html>
<html lang="<?= Yii::$app->language ?>" class="h-100">
<head>
<title><?= Html::encode($this->title) ?></title>
<?php $this->head() ?>
</head>
<header id="header">
<?php
NavBar::begin([
'brandLabel' => Yii::$app->name,
'brandUrl' => Yii::$app->homeUrl,
'options' => ['class' => 'navbar-expand-md navbar-dark
bg-dark fixed-top']
]);
echo Nav::widget([
'options' => ['class' => 'navbar-nav navbar-right'],
'items' => [
['label' => 'Головна', 'url' =>
['/admin/default/index']],
['label' => 'Користувачі', 'url' =>
['/admin/user/index']],
['label' => 'Статті', 'url' =>
['/admin/article/index']],
['label' => 'Коментарі', 'url' =>
['/admin/comment/index']],
['label' => 'Категорії', 'url' =>
['/admin/topic/index']],
'<li class="nav-item">'
. Html::beginForm(['/auth/logout'])
. Html::submitButton(
'Вийти (' . Yii::$app->user->identity-
>username . ')',
['class' => 'nav-link btn btn-link logout']
)
. Html::endForm()
. '</li>',
],
]);
NavBar::end();
?>
</header>
93
<?= Breadcrumbs::widget(['links' => $this-
>params['breadcrumbs']]) ?>
<?php endif ?>
<?= Alert::widget() ?>
<?= $content ?>
</div>
</main>
</html>
<?php $this->endPage() ?>
user.php
<?php
use app\assets\AppAsset;
use app\widgets\Alert;
use yii\bootstrap5\Breadcrumbs;
use yii\bootstrap5\Html;
use yii\bootstrap5\Nav;
use yii\bootstrap5\NavBar;
AppAsset::register($this);
$this->registerCsrfMetaTags();
$this->registerMetaTag(['charset' => Yii::$app->charset],
'charset');
$this->registerMetaTag(['name' => 'viewport', 'content' =>
'width=device-width, initial-scale=1, shrink-to-fit=no']);
$this->registerMetaTag(['name' => 'description', 'content' => $this-
>params['meta_description'] ?? '']);
$this->registerMetaTag(['name' => 'keywords', 'content' => $this-
>params['meta_keywords'] ?? '']);
$this->registerLinkTag(['rel' => 'icon', 'type' => 'image/x-icon',
'href' => Yii::getAlias('@web/favicon.ico')]);
?>
94
<?php $this->beginPage() ?>
<!DOCTYPE html>
<html lang="<?= Yii::$app->language ?>" class="h-100">
<head>
<title><?= Html::encode($this->title) ?></title>
<?php $this->head() ?>
</head>
<header id="header">
<?php
NavBar::begin([
'brandLabel' => Yii::$app->name,
'brandUrl' => Yii::$app->homeUrl,
'options' => ['class' => 'navbar-expand-md navbar-dark
bg-dark fixed-top']
]);
echo Nav::widget([
'options' => ['class' => 'navbar-nav navbar-right'],
'items' => [
['label' => 'Головна', 'url' =>
['/user/default/index']],
['label' => 'Обліковий запис', 'url' =>
['/user/user/index']],
['label' => 'Статті', 'url' =>
['/user/article/index']],
'<li class="nav-item">'
. Html::beginForm(['/auth/logout'])
. Html::submitButton(
'Вийти (' . Yii::$app->user->identity-
>username . ')',
['class' => 'nav-link btn btn-link logout']
)
. Html::endForm()
. '</li>',
],
]);
NavBar::end();
?>
</header>
</html>
<?php $this->endPage() ?>
Модуль адміністратора
Module.php
<?php
namespace app\modules\admin;
use yii\filters\AccessControl;
/**
* admin module definition class
*/
class Module extends \yii\base\Module
{
/**
* {@inheritdoc}
*/
public $layout = '/admin';
public $controllerNamespace = 'app\modules\admin\controllers';
/**
* {@inheritdoc}
*/
public function init()
{
parent::init();
96
'access' => [
'class' => AccessControl::class,
'denyCallback' => function ($rule, $action) {
throw new \yii\web\NotFoundHttpException();
},
'rules' => [
[
'allow' => true,
'matchCallback' => function ($rule, $action)
{
if (isset(\Yii::$app->user->identity)) {
return (\Yii::$app->user->identity-
>isAdmin());
} else {
return false;
}
}
]
]
]
];
}
}
ArticleController.php
<?php
namespace app\modules\admin\controllers;
use app\models\Article;
use app\models\ArticleSearch;
use app\models\ImageUpload;
use Yii;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii\web\UploadedFile;
/**
* ArticleController implements the CRUD actions for Article model.
*/
class ArticleController extends Controller
{
/**
* @inheritDoc
*/
public function behaviors()
{
return array_merge(
parent::behaviors(),
[
'verbs' => [
97
'class' => VerbFilter::class,
'actions' => [
'delete' => ['POST'],
],
],
]
);
}
/**
* Lists all Article models.
*
* @return string
*/
public function actionIndex()
{
$searchModel = new ArticleSearch();
$dataProvider = $searchModel->search($this->request-
>queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single Article model.
* @param int $id ID
* @return string
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new Article model.
* If creation is successful, the browser will be redirected to
the 'view' page.
* @return string|\yii\web\Response
*/
public function actionCreate()
{
$model = new Article();
if ($this->request->isPost) {
if ($model->load($this->request->post()) && $model-
>saveArticle()) {
98
return $this->redirect(['view', 'id' => $model-
>id]);
}
} else {
$model->loadDefaultValues();
}
return $this->render('create', [
'model' => $model,
]);
}
/**
* Updates an existing Article model.
* If update is successful, the browser will be redirected to
the 'view' page.
* @param int $id ID
* @return string|\yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionUpdate($id)
{
$model = $this->findModel($id);
return $this->render('update', [
'model' => $model,
]);
}
/**
* Set image for article
*/
public function actionSetImage($id)
{
$model = new ImageUpload();
if (Yii::$app->request->isPost) {
$article = $this->findModel($id);
99
return $this->render('image', ['model' => $model]);
}
/**
* Deletes an existing Article model.
* If deletion is successful, the browser will be redirected to
the 'index' page.
* @param int $id ID
* @return \yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionDelete($id)
{
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
/**
* Finds the Article model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be
thrown.
* @param int $id ID
* @return Article the loaded model
* @throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = Article::findOne(['id' => $id])) !== null) {
return $model;
}
CommentController.php
<?php
namespace app\modules\admin\controllers;
use app\models\Comment;
use app\models\CommentSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
/**
* CommentController implements the CRUD actions for Comment model.
*/
class CommentController extends Controller
100
{
/**
* @inheritDoc
*/
public function behaviors()
{
return array_merge(
parent::behaviors(),
[
'verbs' => [
'class' => VerbFilter::class,
'actions' => [
'delete' => ['POST'],
],
],
]
);
}
/**
* Lists all Comment models.
*
* @return string
*/
public function actionIndex()
{
$searchModel = new CommentSearch();
$dataProvider = $searchModel->search($this->request-
>queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single Comment model.
* @param int $id ID
* @return string
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new Comment model.
* If creation is successful, the browser will be redirected to
the 'view' page.
101
* @return string|\yii\web\Response
*/
public function actionCreate()
{
$model = new Comment();
if ($this->request->isPost) {
if ($model->load($this->request->post()) && $model-
>save()) {
return $this->redirect(['view', 'id' => $model-
>id]);
}
} else {
$model->loadDefaultValues();
}
return $this->render('create', [
'model' => $model,
]);
}
/**
* Updates an existing Comment model.
* If update is successful, the browser will be redirected to
the 'view' page.
* @param int $id ID
* @return string|\yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
// public function actionUpdate($id)
// {
// $model = $this->findModel($id);
// return $this->render('update', [
// 'model' => $model,
// ]);
// }
/**
* Deletes an existing Comment model.
* If deletion is successful, the browser will be redirected to
the 'index' page.
* @param int $id ID
* @return \yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionDelete($id)
{
102
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
/**
* Finds the Comment model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be
thrown.
* @param int $id ID
* @return Comment the loaded model
* @throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = Comment::findOne(['id' => $id])) !== null) {
return $model;
}
TopicController.php
<?php
namespace app\modules\admin\controllers;
use app\models\Topic;
use app\models\TopicSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
/**
* TopicController implements the CRUD actions for Topic model.
*/
class TopicController extends Controller
{
/**
* @inheritDoc
*/
public function behaviors()
{
return array_merge(
parent::behaviors(),
[
'verbs' => [
'class' => VerbFilter::class,
'actions' => [
'delete' => ['POST'],
103
],
],
]
);
}
/**
* Lists all Topic models.
*
* @return string
*/
public function actionIndex()
{
$searchModel = new TopicSearch();
$dataProvider = $searchModel->search($this->request-
>queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single Topic model.
* @param int $id ID
* @return string
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new Topic model.
* If creation is successful, the browser will be redirected to
the 'view' page.
* @return string|\yii\web\Response
*/
public function actionCreate()
{
$model = new Topic();
if ($this->request->isPost) {
if ($model->load($this->request->post()) && $model-
>save()) {
return $this->redirect(['view', 'id' => $model-
>id]);
}
} else {
104
$model->loadDefaultValues();
}
return $this->render('create', [
'model' => $model,
]);
}
/**
* Updates an existing Topic model.
* If update is successful, the browser will be redirected to
the 'view' page.
* @param int $id ID
* @return string|\yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionUpdate($id)
{
$model = $this->findModel($id);
return $this->render('update', [
'model' => $model,
]);
}
/**
* Deletes an existing Topic model.
* If deletion is successful, the browser will be redirected to
the 'index' page.
* @param int $id ID
* @return \yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionDelete($id)
{
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
/**
* Finds the Topic model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be
thrown.
* @param int $id ID
* @return Topic the loaded model
* @throws NotFoundHttpException if the model cannot be found
*/
105
protected function findModel($id)
{
if (($model = Topic::findOne(['id' => $id])) !== null) {
return $model;
}
UserController.php
<?php
namespace app\modules\admin\controllers;
use Yii;
use app\models\ImageUpload;
use app\models\User;
use app\models\UserSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\web\UploadedFile;
use yii\filters\VerbFilter;
/**
* UserController implements the CRUD actions for User model.
*/
class UserController extends Controller
{
/**
* @inheritDoc
*/
public function behaviors()
{
return array_merge(
parent::behaviors(),
[
'verbs' => [
'class' => VerbFilter::class,
'actions' => [
'delete' => ['POST'],
],
],
]
);
}
/**
* Lists all User models.
*
* @return string
106
*/
public function actionIndex()
{
$searchModel = new UserSearch();
$dataProvider = $searchModel->search($this->request-
>queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single User model.
* @param int $id ID
* @return string
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new User model.
* If creation is successful, the browser will be redirected to
the 'view' page.
* @return string|\yii\web\Response
*/
public function actionCreate()
{
$model = new User();
if ($this->request->isPost) {
if ($model->load($this->request->post()) && $model-
>validate()) {
if ($model->create()) {
return $this->redirect(['view', 'id' => $model-
>id]);
}
}
} else {
$model->loadDefaultValues();
}
return $this->render('create', [
'model' => $model,
]);
}
107
/**
* Updates an existing User model.
* If update is successful, the browser will be redirected to
the 'view' page.
* @param int $id ID
* @return string|\yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionUpdate($id)
{
$model = $this->findModel($id);
return $this->render('update', [
'model' => $model,
]);
}
/**
* Update user image
*/
public function actionSetImage($id)
{
$modelUser = new ImageUpload;
if (Yii::$app->request->isPost) {
$user = $this->findModel($id);
$file = UploadedFile::getInstance($modelUser, 'image');
if ($user->saveImage($modelUser->uploadFile($file,
$user->image))) {
return $this->redirect(['view', 'id' => $user->id]);
}
}
return $this->render('image', ['model' => $modelUser]);
}
/**
* Deletes an existing User model.
* If deletion is successful, the browser will be redirected to
the 'index' page.
* @param int $id ID
* @return \yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionDelete($id)
{
$this->findModel($id)->delete();
108
return $this->redirect(['index']);
}
/**
* Finds the User model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be
thrown.
* @param int $id ID
* @return User the loaded model
* @throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = User::findOne(['id' => $id])) !== null) {
return $model;
}
views/default/index.php
<?php
use yii\helpers\Url;
?>
<div class="module-default-index">
<h1>Вітаємо на панелі адміністратора!</h1>
<p>Даний модуль дозволяє вам:</p>
<div class="features">
<div class="feature">
<p>Управляти користувачами блогу</p>
<a href="<?= Url::toRoute('/admin/user/') ?>">
Управління користувачами
</a>
</div>
<div class="feature">
<p>Переглядати та редагувати статті</p>
<a href="<?= Url::toRoute('/admin/article/') ?>">
Перегляд та редагування статей
</a>
</div>
<div class="feature">
<p>Працювати з категоріями</p>
<a href="<?= Url::toRoute('/admin/topic/') ?>">
Категорії статей
109
</a>
</div>
<div class="feature">
<p>Та коментарями до статей</p>
<a href="<?= Url::toRoute('/admin/comment') ?>">
Коментарі до статей
</a>
</div>
</div>
</div>
Модуль користувача
Module.php
<?php
namespace app\modules\user;
use Yii;
use yii\filters\AccessControl;
/**
* user module definition class
*/
class Module extends \yii\base\Module
{
/**
* {@inheritdoc}
*/
public $layout = '/user';
public $controllerNamespace = 'app\modules\user\controllers';
/**
* {@inheritdoc}
*/
public function init()
{
parent::init();
ArticleController.php
<?php
namespace app\modules\user\controllers;
use app\models\Article;
use app\models\ArticleSearch;
use app\models\ImageUpload;
use Yii;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii\web\UploadedFile;
/**
* ArticleController implements the CRUD actions for Article model.
*/
class ArticleController extends Controller
{
/**
* @inheritDoc
*/
public function behaviors()
{
return array_merge(
parent::behaviors(),
[
'verbs' => [
'class' => VerbFilter::class,
'actions' => [
'delete' => ['POST'],
],
],
]
);
111
}
/**
* Lists all Article models.
*
* @return string
*/
public function actionIndex()
{
$searchModel = new ArticleSearch();
// $dataProvider = $searchModel->search($this->request-
>queryParams);
$params['ArticleSearch']['user_id'] = Yii::$app->user->id;
$dataProvider = $searchModel->search($params);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single Article model.
* @param int $id ID
* @return string
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionView($id)
{
$this->checkUserAccess($id);
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new Article model.
* If creation is successful, the browser will be redirected to
the 'view' page.
* @return string|\yii\web\Response
*/
public function actionCreate()
{
$model = new Article();
if ($this->request->isPost) {
if ($model->load($this->request->post()) && $model-
>save()) {
return $this->redirect(['view', 'id' => $model-
>id]);
}
112
} else {
$model->loadDefaultValues();
}
return $this->render('create', [
'model' => $model,
]);
}
/**
* Updates an existing Article model.
* If update is successful, the browser will be redirected to
the 'view' page.
* @param int $id ID
* @return string|\yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionUpdate($id)
{
$this->checkUserAccess($id);
$model = $this->findModel($id);
return $this->render('update', [
'model' => $model,
]);
}
/**
* Set image for article
*/
public function actionSetImage($id)
{
$model = new ImageUpload();
if (Yii::$app->request->isPost) {
$article = $this->findModel($id);
/**
* Deletes an existing Article model.
* If deletion is successful, the browser will be redirected to
the 'index' page.
* @param int $id ID
* @return \yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionDelete($id)
{
$this->checkUserAccess($id);
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
/**
* Finds the Article model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be
thrown.
* @param int $id ID
* @return Article the loaded model
* @throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = Article::findOne(['id' => $id])) !== null) {
return $model;
}
/**
* check if user has access
*/
public function checkUserAccess($id)
{
$model1 = $this->findModel($id);
if ($model1->user_id != Yii::$app->user->id) {
throw new \yii\web\NotFoundHttpException();
}
return true;
}
}
UserController.php
<?php
114
namespace app\modules\user\controllers;
use app\models\ImageUpload;
use app\models\User;
use app\models\UserSearch;
use Yii;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\web\UploadedFile;
use yii\filters\VerbFilter;
/**
* UserController implements the CRUD actions for User model.
*/
class UserController extends Controller
{
/**
* @inheritDoc
*/
public function behaviors()
{
return array_merge(
parent::behaviors(),
[
'verbs' => [
'class' => VerbFilter::class,
'actions' => [
'delete' => ['POST'],
],
],
]
);
}
/**
* Lists all User models.
*
* @return string
*/
public function actionIndex()
{
$searchModel = new UserSearch();
// $dataProvider = $searchModel->search($this->request-
>queryParams);
$params['UserSearch']['id'] = Yii::$app->user->id;
$dataProvider = $searchModel->search($params);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
115
}
/**
* Displays a single User model.
* @param int $id ID
* @return string
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionView($id)
{
$this->checkUserAccess($id);
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new User model.
* If creation is successful, the browser will be redirected to
the 'view' page.
* @return string|\yii\web\Response
*/
public function actionCreate()
{
$model = new User();
if ($this->request->isPost) {
if ($model->load($this->request->post()) && $model-
>create()) {
return $this->redirect(['view', 'id' => $model-
>id]);
}
} else {
$model->loadDefaultValues();
}
return $this->render('create', [
'model' => $model,
]);
}
/**
* Updates an existing User model.
* If update is successful, the browser will be redirected to
the 'view' page.
* @param int $id ID
* @return string|\yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionUpdate($id)
{
$this->checkUserAccess($id);
116
$model = $this->findModel($id);
return $this->render('update', [
'model' => $model,
]);
}
/**
* Update user image
*/
public function actionSetImage($id)
{
$modelUser = new ImageUpload;
if (Yii::$app->request->isPost) {
$user = $this->findModel($id);
$file = UploadedFile::getInstance($modelUser, 'image');
if ($user->saveImage($modelUser->uploadFile($file,
$user->image))) {
return $this->redirect(['view', 'id' => $user->id]);
}
}
return $this->render('image', ['model' => $modelUser]);
}
/**
* Deletes an existing User model.
* If deletion is successful, the browser will be redirected to
the 'index' page.
* @param int $id ID
* @return \yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionDelete($id)
{
$this->checkUserAccess($id);
$this->findModel($id)->delete();
return $this->redirect(['/site/index']);
}
/**
* Finds the User model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be
thrown.
* @param int $id ID
* @return User the loaded model
117
* @throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = User::findOne(['id' => $id])) !== null) {
return $model;
}
views/default/index.php
<?php
use yii\helpers\Url;
?>
<div class="module-default-index">
<h1>Вітаємо на панелі управління!</h1>
<p>Даний модуль дозволяє вам:</p>
<div class="features">
<div class="feature">
<p>Управляти своїм обліковим записом</p>
<a href="<?= Url::toRoute('/user/user/') ?>">
Управління обліковим записом
</a>
</div>
<div class="feature">
<p>Переглядати та редагувати власні статті</p>
<a href="<?= Url::toRoute('/user/article/') ?>">
Перегляд та редагування статей
</a>
</div>
</div>
</div>
118
ДОДАТОК Б
-
-- База даних: `yii_blog`
--
-- --------------------------------------------------------
--
-- Структура таблиці `article`
--
--
-- Дамп даних таблиці `article`
--
120
(11, 'Овочеві сніданки: здорові та поживні', 'Здорові сніданки
можуть бути не лише смачними, але і насиченими вітамінами та
мінералами, особливо якщо ви включаєте овочі у своє ранкове меню.
Овочі додають свіжості та смаку, а також роблять ваш сніданок більш
ситним та корисним.\r\n\r\nОднією з простих та смачних ідей є омлет
з овочами. Розбийте яйця, додайте нарізані помідори, шпинат та тонко
нарізані паперчіки. Після цього приготуйте на сковороді, і ви
отримаєте насичений омлет, який наповнить вас енергією та
вітамінами.\r\n\r\nЩе однією ідеєю є овочевий тост. Використовуйте
цільнозерновий хліб та обсмажте його на грилі. Покладіть на тост шар
гуакамоле або нарізаного авокадо, та додайте свіжі томати та огірки.
Цей сніданок не лише смачний, але і повний корисних речовин.\r\n\r\
nДля тих, хто віддає перевагу холодним сніданкам, овочевий смузі -
чудовий вибір. Змішайте шпинат, огірок, ягоди та банан у блендері з
невеликою кількістю грецького йогурту. Ви отримаєте смачний та
освіжаючий напій, який надасть вам енергії на весь день.\r\n\r\
nНехай ваш ранок буде насиченим та здоровим завдяки цим овочевим
сніданковим ідеям.', '2023-10-10',
'a76f38e51ce706d06bfae24d2cc6dc8a.jpg', 'Овочі сніданки', 3, 1, 3),
(12, 'Кавові інновації: смачні сніданки з кавою', 'Ранкова кава може
бути не лише засобом прокинутися, але й частиною смачного та
насиченого сніданку. Сполучивши смак кави з іншими інгредієнтами, ви
можете створити справжні інновації у своїй ранковій кулінарії.\r\n\
r\nОднією з цікавих ідей є кавовий смузі. Змішайте свіжозаварену
каву з бананом, мигдальним маслом та льодом у блендері. Результат -
освіжаючий напій з чарівним поєднанням кавового аромату та солодкого
смаку банана.\r\n\r\nКавові вівсяні котлети - ще одна інноваційна
ідея для сніданку. Додайте свіжозаварену каву до готових овсяних
відерець, покладіть на верх начинку з ягід та горіхів. Це не лише
ситно, але й додасть вашому сніданку особливий кавовий шарм.\r\n\r\
nДля тих, хто любить випічку, кавовий банановий хліб - ідеальний
вибір. Додайте до тіста свіжозавареної кави та кубики вареного
банана. Після випічки ви отримаєте ароматний та смачний хліб, який
відмінно поєднується з кавою.\r\n\r\nНехай ваші ранкові сніданки
стануть справжніми кавовими інноваціями, даруючи вам насолоду та
енергію на весь день.', '2023-11-05',
'39a6c80eff767e06b13a9dfdfb5daca0.jpg', 'Сніданки кава', 21, 1, 3),
(13, 'Ідеї сніданків для активного ранку', 'Розпочати день з енергії
та життєвого натхнення - ось що дозволить ця колекція ідей для
сніданків. Відповідно до вашого стилю життя та вподобань, ці
сніданки створені для тих, хто бажає активного та продуктивного
ранку.\r\n\r\n\"Суперфуд сенсація\"\r\nІнтегруйте суперфуди у свій
сніданок, отримуючи всі необхідні вітаміни та мінерали для підтримки
енергії та здоров\'я.\r\n\r\n\"Страви для спортсменів\"\r\nДля тих,
хто займається фізичною активністю, ця тема пропонує сніданки, які
нададуть вам силу та витривалість на тренування та весь день.\r\n\r\
n\"Ароматні ранкові ритуали\"\r\nЗбалансуйте смакові відчуття своєї
чашки чаю з смачним та здоровим сніданком, створюючи ароматні
комбінації.\r\n\r\nЦі ідеї дозволяють вам насолоджуватися смачними
сніданками, що заряджають вас позитивною енергією та готують до
активного ранку.', '2023-11-18',
'68c2775703e3a4db42e50912026f066d.jpg', 'Сніданки', 1, 1, 3),
121
(14, 'Сніданки для смаку та здоров\'я', 'Ця тема спрямована на тих,
хто цінує гастрономічний насолодження, але при цьому прагне
зберігати баланс та здоров\'я у своєму харчуванні. Ви знайдете
сніданкові ідеї, які поєднують у собі вишуканий смак і корисність.\
r\n\r\n\"Колорит смаку: екзотичні сніданки для палати\"\r\nРозширте
свій кулінарний світ за допомогою страв з різних кухонь світу,
додаючи екзотичні інгредієнти до свого ранкового меню.\r\n\r\
n\"Трошки солодкого, трошки солоного: комбіновані сніданки\"\r\
nДодайте різноманіття до свого ранкового харчування, комбінувавши
солодкі та солоні компоненти у ваших сніданкових стравах.\r\n\r\
n\"Сніданок-подорож: смакові враження з різних куточків світу\"\r\
nВирушайте у кулінарну подорож, спробовуючи сніданки, що
інспіруються традиціями різних культур.\r\n\r\nЦя тема розкриє перед
вами світ гастрономічних можливостей, даруючи якісні смакові
враження та стимулюючи здоровий підхід до харчування.', '2023-12-
03', '025f78771eb97dc8537b48067e936212.jpg', 'Сніданки', 2, 1, 2),
(15, '5 ключових принципів збалансованої дієти', 'Збалансована дієта
визначається раціоном, який забезпечує всі необхідні поживні
речовини для нормального функціонування організму. При виборі
продуктів харчування та готуванні страв важливо дотримуватися певних
ключових принципів, щоб забезпечити необхідний рівновагу.\r\n\r\
nОдин з ключових принципів - різноманітність. Важливо включати до
раціону різні типи продуктів, щоб отримати широкий спектр поживних
речовин. Кожна група харчових продуктів має свої унікальні корисні
речовини, які важливі для здоров\'я.\r\n\r\nЩе одним ключовим
аспектом є баланс між білками, жирами та вуглеводами. Правильне
співвідношення цих складових допомагає забезпечити ефективне
функціонування організму. Наприклад, збільшений вміст білків
корисний для м\'язів, але важливо не забувати про рослинні джерела
цих елементів.\r\n\r\nНе менш важливим принципом є вибір цільових
продуктів. Важливо враховувати вміст корисних речовин та вибирати ті
продукти, які допоможуть досягти вашого конкретного харчового
плану.\r\n\r\nКонтроль розмірів порцій - ще один аспект. Великі
порції можуть привести до перевищення калорій, тоді як малі можуть
не забезпечити достатньої кількості поживних речовин. Тому важливо
слідкувати за розмірами порцій та слухати власний організм.\r\n\r\
nНе останнім, але дуже важливим принципом є гідна питома кількість
води. Правильне забезпечення організму вологою допомагає
підтримувати ефективне травлення, регулювати температуру та виводити
токсини.\r\n\r\nЦі принципи допоможуть вам створити збалансований
підхід до харчування та забезпечити вашому організму необхідні
ресурси для активного та здорового життя.', '2023-10-10',
'3beabd71dad5cd43e3af2abb2d9b79a6.png', 'Збалансована дієта', 2, 2,
2),
(16, 'Білки, вуглеводи, жири: як правильно балансувати елементи
живлення', 'Ваше здоров\'я і енергія залежать від правильного
балансу білків, вуглеводів та жирів в вашому харчуванні. Білки - це
будівельні блоки для тіла, які важливі для росту та відновлення
тканин. Вуглеводи - основне джерело енергії, яке живить ваш мозок і
м\'язи. Жири - необхідні для підтримки здоров\'я шкіри та волосся, а
також для правильного функціонування органів.\r\n\r\nВажливо
розуміти, що кожен з цих елементів виконує унікальні функції, і їх
правильне співвідношення забезпечує гармонійне функціонування
122
організму. Більше не завжди означає краще, тому важливо
дотримуватися збалансованого підходу.\r\n\r\nБілки мають різні
джерела, включаючи м\'ясо, рибу, яйця, молоко та рослинні продукти,
такі як боби та горох. Вуглеводи можна отримати зі злаків, фруктів
та овочів, а також зі складників, які містять велику кількість
волокон. Жири також повинні бути різноманітними, включаючи
поліненасичені жири з риби та оріхів, а також ненасичені жири з
рослинних олій та авокадо.\r\n\r\nПам\'ятайте, що ваші потреби в цих
елементах можуть змінюватися залежно від стилю життя, віку та
фізичної активності. Слухайте свій організм і дбайте про те, щоб
забезпечити йому всі необхідні складові для здоров\'я та життєвої
сили.', '2023-12-08', '5734b5d4b1a6e1c20fccc6bf69e84880.jpg',
'Збалансована дієта', 20, 2, 3);
-- --------------------------------------------------------
--
-- Структура таблиці `comment`
--
--
-- Дамп даних таблиці `comment`
--
-- --------------------------------------------------------
--
-- Структура таблиці `migration`
--
123
--
-- Дамп даних таблиці `migration`
--
-- --------------------------------------------------------
--
-- Структура таблиці `topic`
--
--
-- Дамп даних таблиці `topic`
--
-- --------------------------------------------------------
--
-- Структура таблиці `user`
--
--
-- Дамп даних таблиці `user`
--
124
INSERT INTO `user` (`id`, `name`, `login`, `password`, `image`,
`is_admin`) VALUES
(1, 'admin', 'admin@gmail.com',
'$2y$13$7DzIXWRWK4etK9MyHBYXteB554YpIqvUWWpJqCVEwtX7.K8pvsmFG',
'd00a5c831d5a8cecef4c7f732e5c5a4b.png', 1),
(2, 'user1', 'user1@gmail.com',
'$2y$13$1Tkqj9qW05i71u/couVgqOCn/7ry2BjkPGoDt22L./zv.6AZ46Ho2',
'84cc959d2024be2f2878e4049c12f1d9.png', 0),
(3, 'user2', 'user2@gmail.com',
'$2y$13$N090AywE0XtOjaP5OU24..bFRd/7Sxt1U.85uLtjCFD8yaYY/9q2m',
'3704c8730e00223c6e24dd3efe5888ff.png', 0),
(17, 'test', 'test@gmail.com',
'$2y$13$3DoI/EOLl1r9p3jK0iHhH.3MFIbGo1ZAku1omXlT32R0CphM/Lm5G',
NULL, 0);
--
-- Індекси збережених таблиць
--
--
-- Індекси таблиці `article`
--
ALTER TABLE `article`
ADD PRIMARY KEY (`id`),
ADD KEY `idx-topic_id` (`topic_id`),
ADD KEY `idx-post-user_id` (`user_id`);
--
-- Індекси таблиці `comment`
--
ALTER TABLE `comment`
ADD PRIMARY KEY (`id`),
ADD KEY `idx-post-user_id` (`user_id`),
ADD KEY `idx-article_id` (`article_id`),
ADD KEY `idx-comment_id` (`comment_id`);
--
-- Індекси таблиці `migration`
--
ALTER TABLE `migration`
ADD PRIMARY KEY (`version`);
--
-- Індекси таблиці `topic`
--
ALTER TABLE `topic`
ADD PRIMARY KEY (`id`);
--
-- Індекси таблиці `user`
--
ALTER TABLE `user`
ADD PRIMARY KEY (`id`);
125
--
-- AUTO_INCREMENT для збережених таблиць
--
--
-- AUTO_INCREMENT для таблиці `article`
--
ALTER TABLE `article`
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=30;
--
-- AUTO_INCREMENT для таблиці `comment`
--
ALTER TABLE `comment`
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=14;
--
-- AUTO_INCREMENT для таблиці `topic`
--
ALTER TABLE `topic`
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7;
--
-- AUTO_INCREMENT для таблиці `user`
--
ALTER TABLE `user`
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=23;
--
-- Обмеження зовнішнього ключа збережених таблиць
--
--
-- Обмеження зовнішнього ключа таблиці `article`
--
ALTER TABLE `article`
ADD CONSTRAINT `fk-post-user_id` FOREIGN KEY (`user_id`)
REFERENCES `user` (`id`) ON DELETE CASCADE,
ADD CONSTRAINT `fk-topic_id` FOREIGN KEY (`topic_id`) REFERENCES
`topic` (`id`) ON DELETE CASCADE;
--
-- Обмеження зовнішнього ключа таблиці `comment`
--
ALTER TABLE `comment`
ADD CONSTRAINT `fk-article_id` FOREIGN KEY (`article_id`)
REFERENCES `article` (`id`) ON DELETE CASCADE,
ADD CONSTRAINT `fk-comment-post-user_id` FOREIGN KEY (`user_id`)
REFERENCES `user` (`id`) ON DELETE CASCADE,
ADD CONSTRAINT `fk-comment_id` FOREIGN KEY (`comment_id`)
REFERENCES `comment` (`id`) ON DELETE CASCADE;
COMMIT;
126
127