😅Create Page Signup, login, article (ok)
https://github.com/M-E-M-F-I-S/yii2-demo-blog
PreviousHow to Program With Yii2: Working With the Database and Active Record (ok)NextMigrate Full (ok)
Last updated
https://github.com/M-E-M-F-I-S/yii2-demo-blog
Last updated
<?php
namespace app\controllers;
use app\models\SignupForm;
use Yii;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\web\Response;
use yii\filters\VerbFilter;
use app\models\LoginForm;
use app\models\ContactForm;
class SiteController extends Controller
{
/**
* {@inheritdoc}
*/
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'only' => ['logout'],
'rules' => [
[
'actions' => ['logout'],
'allow' => true,
'roles' => ['@'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'logout' => ['post'],
],
],
];
}
/**
* {@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()
{
return $this->render('index');
}
/**
* Login action.
*
* @return Response|string
*/
public function actionLogin()
{
if (!Yii::$app->user->isGuest) {
return $this->goHome();
}
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->login()) {
return $this->goBack();
}
$model->password = '';
return $this->render('login', [
'model' => $model,
]);
}
public function actionSignup()
{
$model = new SignupForm();
if ($model->load(Yii::$app->request->post()) && $model->signup()) {
return $this->redirect(Yii::$app->homeUrl);
}
return $this->render('signup', [
'model' => $model,
]);
}
/**
* Logout action.
*
* @return Response
*/
public function actionLogout()
{
Yii::$app->user->logout();
return $this->goHome();
}
/**
* 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,
]);
}
/**
* Displays about page.
*
* @return string
*/
public function actionAbout()
{
return $this->render('about');
}
}
<?php
use yii\db\Migration;
/**
* Class m220724_073246_create_tables
*/
class m220724_073246_create_tables extends Migration
{
/**
* {@inheritdoc}
*/
public function up()
{
$this->createTable('user', [
'id' => $this->primaryKey(),
'username' => $this->string(55)->notNull(),
'password' => $this->string(255)->notNull(),
'auth_key' => $this->string(255)->notNull(),
'access_token' => $this->string(255)->notNull(),
]);
$this->createTable('article', [
'id' => $this->primaryKey(),
'title' => $this->string(1024)->notNull(),
'slug' => $this->string(1024)->notNull(),
'body' => $this->text()->notNull(),
'created_at' => $this->integer()->notNull(),
'updated_at' => $this->integer()->notNull(),
'created_by' => $this->integer()->notNull(),
]);
$this->addForeignKey('article_created_by_fk', 'article', 'created_by', 'user', 'id');
}
/**
* {@inheritdoc}
*/
public function down()
{
$this->dropTable('user');
$this->dropTable('article');
}
}
<?php
/** @var yii\web\View $this */
/** @var string $content */
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);
?>
<?php $this->beginPage() ?>
<!DOCTYPE html>
<html lang="<?= Yii::$app->language ?>" class="h-100">
<head>
<meta charset="<?= Yii::$app->charset ?>">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<?php $this->registerCsrfMetaTags() ?>
<title><?= Html::encode($this->title) ?></title>
<?php $this->head() ?>
</head>
<body class="d-flex flex-column h-100">
<?php $this->beginBody() ?>
<header>
<?php
NavBar::begin([
'brandLabel' => Yii::$app->name,
'brandUrl' => Yii::$app->homeUrl,
'options' => [
'class' => 'navbar navbar-expand-md navbar-dark bg-dark fixed-top',
],
]);
echo Nav::widget([
'options' => ['class' => 'navbar-nav'],
'items' => [
['label' => 'Home', 'url' => [Yii::$app->homeUrl]],
['label' => 'About', 'url' => ['/site/about']],
['label' => 'Contact', 'url' => ['/site/contact']],
Yii::$app->user->isGuest ? (['label' => 'Login', 'url' => ['/site/login']]
) : ('<li>'
. Html::beginForm(['/site/logout'], 'post', ['class' => 'form-inline'])
. Html::submitButton(
'Logout (' . Yii::$app->user->identity->username . ')',
['class' => 'btn btn-link logout']
)
. Html::endForm()
. '</li>'
),
Yii::$app->user->isGuest ? (['label' => 'Signup', 'url' => ['/site/signup']]
) : ''
],
]);
NavBar::end();
?>
</header>
<main role="main" class="flex-shrink-0">
<div class="container">
<?= Breadcrumbs::widget([
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
]) ?>
<?= Alert::widget() ?>
<?= $content ?>
</div>
</main>
<footer class="footer mt-auto py-3 text-muted">
<div class="container">
<p class="float-left">© My Company <?= date('Y') ?></p>
<p class="float-right"><?= Yii::powered() ?></p>
</div>
</footer>
<?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage() ?>
<?php
/** @var yii\web\View $this */
/** @var yii\bootstrap4\ActiveForm $form */
/** @var app\models\LoginForm $model */
use yii\bootstrap5\ActiveForm;
use yii\bootstrap5\Html;
$this->title = 'Login';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-login">
<h1><?= Html::encode($this->title) ?></h1>
<p>Please fill out the following fields to login:</p>
<?php $form = ActiveForm::begin([
'id' => 'login-form',
'layout' => 'horizontal',
'fieldConfig' => [
'template' => "{label}\n{input}\n{error}",
'labelOptions' => ['class' => 'col-lg-1 col-form-label mr-lg-3'],
'inputOptions' => ['class' => 'col-lg-3 form-control'],
'errorOptions' => ['class' => 'col-lg-7 invalid-feedback'],
],
]); ?>
<?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?>
<?= $form->field($model, 'password')->passwordInput() ?>
<?= $form->field($model, 'rememberMe')->checkbox([
'template' => "<div class=\"offset-lg-1 col-lg-3 custom-control custom-checkbox\">{input} {label}</div>\n<div class=\"col-lg-8\">{error}</div>",
]) ?>
<div class="form-group">
<div class="offset-lg-1 col-lg-11">
<?= Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>
</div>
</div>
<?php ActiveForm::end(); ?>
<div class="offset-lg-1" style="color:#999;">
You may login with <strong>admin/admin</strong> or <strong>demo/demo</strong>.<br>
To modify the username/password, please check out the code <code>app\models\User::$users</code>.
</div>
</div>
<?php
/** @var yii\web\View $this */
/** @var yii\bootstrap4\ActiveForm $form */
/** @var app\models\SignupForm $model */
use yii\bootstrap5\ActiveForm;
use yii\bootstrap5\Html;
$this->title = 'Signup';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-login">
<h1><?= Html::encode($this->title) ?></h1>
<p>Please fill out the following fields to signup:</p>
<?php $form = ActiveForm::begin([
'id' => 'signup-form',
'layout' => 'horizontal',
'fieldConfig' => [
'template' => "{label}\n{input}\n{error}",
'labelOptions' => ['class' => 'col-lg-1 col-form-label mr-lg-3'],
'inputOptions' => ['class' => 'col-lg-3 form-control'],
'errorOptions' => ['class' => 'col-lg-7 invalid-feedback'],
],
]); ?>
<?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?>
<?= $form->field($model, 'password')->passwordInput() ?>
<?= $form->field($model, 'password_repeat')->passwordInput() ?>
<div class="form-group">
<div class="offset-lg-1 col-lg-11">
<?= Html::submitButton('Signup', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>
</div>
</div>
<?php ActiveForm::end(); ?>
</div>
<?php
namespace app\widgets;
use Yii;
/**
* Alert widget renders a message from session flash. All flash messages are displayed
* in the sequence they were assigned using setFlash. You can set message as following:
*
* ```php
* Yii::$app->session->setFlash('error', 'This is the message');
* Yii::$app->session->setFlash('success', 'This is the message');
* Yii::$app->session->setFlash('info', 'This is the message');
* ```
*
* Multiple messages could be set as follows:
*
* ```php
* Yii::$app->session->setFlash('error', ['Error 1', 'Error 2']);
* ```
*
* @author Kartik Visweswaran <kartikv2@gmail.com>
* @author Alexander Makarov <sam@rmcreative.ru>
*/
class Alert extends \yii\bootstrap5\Widget
{
/**
* @var array the alert types configuration for the flash messages.
* This array is setup as $key => $value, where:
* - key: the name of the session flash variable
* - value: the bootstrap alert type (i.e. danger, success, info, warning)
*/
public $alertTypes = [
'error' => 'alert-danger',
'danger' => 'alert-danger',
'success' => 'alert-success',
'info' => 'alert-info',
'warning' => 'alert-warning'
];
/**
* @var array the options for rendering the close button tag.
* Array will be passed to [[\yii\bootstrap\Alert::closeButton]].
*/
public $closeButton = [];
/**
* {@inheritdoc}
*/
public function run()
{
$session = Yii::$app->session;
$appendClass = isset($this->options['class']) ? ' ' . $this->options['class'] : '';
foreach (array_keys($this->alertTypes) as $type) {
$flash = $session->getFlash($type);
foreach ((array) $flash as $i => $message) {
echo \yii\bootstrap4\Alert::widget([
'body' => $message,
'closeButton' => $this->closeButton,
'options' => array_merge($this->options, [
'id' => $this->getId() . '-' . $type . '-' . $i,
'class' => $this->alertTypes[$type] . $appendClass,
]),
]);
}
$session->removeFlash($type);
}
}
}
<?php
namespace app\controllers;
use app\models\Article;
use app\models\ArticleSearch;
use yii\filters\AccessControl;
use yii\helpers\VarDumper;
use yii\web\Controller;
use yii\web\ForbiddenHttpException;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
/**
* ArticleController implements the CRUD actions for Article model.
*/
class ArticleController extends Controller
{
/**
* @inheritDoc
*/
public function behaviors()
{
return array_merge(
parent::behaviors(),
[
'access' => [
'class' => AccessControl::class,
'only' => ['create', 'update', 'delete'],
'rules' => [
[
'actions' => ['create', 'update', 'delete'],
'allow' => true,
'roles' => ['@']
]
]
],
'verbs' => [
'class' => VerbFilter::className(),
'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 $slug slug
* @return string
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionView($slug)
{
return $this->render('view', [
'model' => $this->findModel($slug),
]);
}
/**
* 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 ($model->load($this->request->post()) && $model->save()) {
return $this->redirect(['view', 'slug' => $model->slug]);
} else {
\Yii::error("Article was not saved. " . VarDumper::dumpAsString($model->errors));
}
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 $slug slug
* @return string|\yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionUpdate($slug)
{
$model = $this->findModel($slug);
if ($model->created_by !== \Yii::$app->user->id) {
throw new ForbiddenHttpException("You don`t have permission to update other user articles");
}
if ($this->request->isPost && $model->load($this->request->post()) && $model->save()) {
return $this->redirect(['view', 'slug' => $model->slug]);
}
return $this->render('update', [
'model' => $model,
]);
}
/**
* Deletes an existing Article model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* @param int $slug slug
* @return \yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionDelete($slug)
{
$model = $this->findModel($slug);
if ($model->created_by !== \Yii::$app->user->id) {
throw new ForbiddenHttpException("You don`t have permission to delete other user articles");
}
$model->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 $slug slug
* @return Article the loaded model
* @throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($slug)
{
if (($model = Article::findOne(['slug' => $slug])) !== null) {
return $model;
}
throw new NotFoundHttpException('The requested page does not exist.');
}
}
<?php
namespace app\models;
use Yii;
use yii\behaviors\BlameableBehavior;
use yii\behaviors\SluggableBehavior;
use yii\behaviors\TimestampBehavior;
use yii\helpers\Html;
/**
* This is the model class for table "article".
*
* @property int $id
* @property string $title
* @property string $slug
* @property string $body
* @property string|null $created_at
* @property string|null $updated_at
* @property int $created_by
*
* @property User $createdBy
*/
class Article extends \yii\db\ActiveRecord
{
/**
* {@inheritdoc}
*/
public static function tableName()
{
return 'article';
}
public function behaviors()
{
return [
TimestampBehavior::class,
[
'class' => BlameableBehavior::class,
'updatedByAttribute' => false,
],
[
'class' => SluggableBehavior::class,
'attribute' => 'title'
]
];
}
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['title', 'body'], 'required'],
[['body'], 'string'],
[['created_at', 'updated_at'], 'safe'],
[['created_by'], 'integer'],
[['title', 'slug'], 'string', 'max' => 1024],
[['created_by'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['created_by' => 'id']],
];
}
/**
* {@inheritdoc}
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'title' => 'Title',
'slug' => 'Slug',
'body' => 'Body',
'created_at' => 'Created At',
'updated_at' => 'Updated At',
'created_by' => 'Created By',
];
}
/**
* Gets query for [[CreatedBy]].
*
* @return \yii\db\ActiveQuery
*/
public function getCreatedBy()
{
return $this->hasOne(User::className(), ['id' => 'created_by']);
}
public function getEncodedBody()
{
return Html::encode($this->body);
}
}
<?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', 'created_by'], 'integer'],
[['title', 'slug', 'body', 'created_at', 'updated_at'], '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 = Article::find()->orderBy('created_at DESC');
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$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;
}
// grid filtering conditions
$query->andFilterWhere([
'id' => $this->id,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'created_by' => $this->created_by,
]);
$query->andFilterWhere(['like', 'title', $this->title])
->andFilterWhere(['like', 'slug', $this->slug])
->andFilterWhere(['like', 'body', $this->body]);
return $dataProvider;
}
}
<?php
use yii\helpers\Url;
use yii\helpers\Html;
use yii\helpers\StringHelper;
/** @var $model \app\models\Article */
?>
<div class="article-item">
<a href="<?= Url::to(['/article/view', 'slug' => $model->slug]) ?>">
<h3><?= Html::encode($model->title) ?></h3>
</a>
<div>
<?= StringHelper::truncateWords($model->getEncodedBody(), 50) ?>
</div>
<p class="text-muted text-right">
<small>
Created At: <b><?= Yii::$app->formatter->asRelativeTime($model->created_at) ?></b>
By: <b><?= $model->createdBy->username; ?></b>
</small>
</p>
<hr>
</div>
<?php
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/* @var $this yii\web\View */
/* @var $model app\models\Article */
/* @var $form yii\widgets\ActiveForm */
?>
<div class="article-form">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'title')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'slug')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'body')->textarea(['rows' => 6]) ?>
<div class="form-group">
<?= Html::submitButton('Save', ['class' => 'btn btn-success']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/* @var $this yii\web\View */
/* @var $model app\models\ArticleSearch */
/* @var $form yii\widgets\ActiveForm */
?>
<div class="article-search">
<?php $form = ActiveForm::begin([
'action' => ['index'],
'method' => 'get',
]); ?>
<?= $form->field($model, 'id') ?>
<?= $form->field($model, 'title') ?>
<?= $form->field($model, 'slug') ?>
<?= $form->field($model, 'body') ?>
<?= $form->field($model, 'created_at') ?>
<div class="form-group">
<?= Html::submitButton('Search', ['class' => 'btn btn-primary']) ?>
<?= Html::resetButton('Reset', ['class' => 'btn btn-outline-secondary']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
<?php
use yii\helpers\Html;
/* @var $this yii\web\View */
/* @var $model app\models\Article */
$this->title = 'Create Article';
$this->params['breadcrumbs'][] = ['label' => 'Articles', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="article-create">
<h1><?= Html::encode($this->title) ?></h1>
<?= $this->render('_form', [
'model' => $model,
]) ?>
</div>
<?php
use yii\helpers\Html;
use yii\widgets\ListView;
/* @var $this yii\web\View */
/* @var $searchModel app\models\ArticleSearch */
/* @var $dataProvider yii\data\ActiveDataProvider */
$this->title = 'Articles';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="article-index">
<h1><?= Html::encode($this->title) ?></h1>
<?php if (!Yii::$app->user->isGuest) : ?>
<p>
<?= Html::a('Create Article', ['create'], ['class' => 'btn btn-success']) ?>
</p>
<?php endif; ?>
<?= ListView::widget([
'dataProvider' => $dataProvider,
'itemView' => '_article_item'
]); ?>
</div>
<?php
use yii\helpers\Html;
/* @var $this yii\web\View */
/* @var $model app\models\Article */
$this->title = 'Update Article: ' . $model->title;
$this->params['breadcrumbs'][] = ['label' => 'Articles', 'url' => ['index']];
$this->params['breadcrumbs'][] = ['label' => $model->title, 'url' => ['view', 'slug' => $model->slug]];
$this->params['breadcrumbs'][] = 'Update';
?>
<div class="article-update">
<h1><?= Html::encode($this->title) ?></h1>
<?= $this->render('_form', [
'model' => $model,
]) ?>
</div>
<?php
use yii\helpers\Html;
use yii\widgets\DetailView;
/* @var $this yii\web\View */
/* @var $model app\models\Article */
$this->title = $model->title;
$this->params['breadcrumbs'][] = ['label' => 'Articles', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
\yii\web\YiiAsset::register($this);
?>
<div class="article-view">
<h1><?= Html::encode($this->title) ?></h1>
<p class="text-muted">
<small>
Created At: <b><?= Yii::$app->formatter->asRelativeTime($model->created_at) ?></b>
By: <b><?= $model->createdBy->username; ?></b>
</small>
</p>
<?php if (!Yii::$app->user->isGuest) : ?>
<p>
<?= Html::a('Update', ['update', 'slug' => $model->slug], ['class' => 'btn btn-primary']) ?>
<?= Html::a('Delete', ['delete', 'slug' => $model->slug], [
'class' => 'btn btn-danger',
'data' => [
'confirm' => 'Are you sure you want to delete this item?',
'method' => 'post',
],
]) ?>
</p>
<?php endif; ?>
<div>
<?= $model->getEncodedBody(); ?>
</div>
</div>