yii2博客学习笔记(二) [ 2.0 版本 ]
上篇文章发布,多数阅读的,就是没有评论的,也不知道写的怎么,大家有没问题?大家可以在评论区一起交流交流,今天带着第二篇来见大家了。
首先声明一下,对于第二篇与第一篇中类似的部分不会太过于详述。觉得不够详细的部分可以参考第一篇。附上链接yii2博客项目笔记(一)。
初步分析
- 由于 post 和 tag 表为多对多关系,所以需要建立中间表 post_tag_pivot 。
- tag 支持上传标签图片。涉及文件上传。
- 新建和修改 post 时支持添加和修改 tag ,采用多项复选框。
教程开始
一、数据表的建立
命令行执行 .\yii migrate/create tags
创建 Tags 数据表,然后修改目录 console\migrations
下生成的文件内容如下:
<?php
use yii\db\Migration;
class m160818_013927_tags extends Migration
{
public function up()
{
$tableOptions = null;
if ($this->db->driverName === 'mysql') {
// http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
}
$this->createTable('{{%tags}}', [
'id' => $this->primaryKey(),
'tag' => $this->string()->notNull(), //标签名称
'meta_description' => $this->string(), //标签描述
'tag_img' => $this->string(), //标签图片
'created_at' => $this->integer()->notNull(),
'updated_at' => $this->integer()->notNull(),
], $tableOptions);
}
public function down()
{
}
/*
// Use safeUp/safeDown to run migration code within a transaction
public function safeUp()
{
}
public function safeDown()
{
}
*/
}
命令行执行 .\yii migrate/create post_tag_pivot
创建 post_tag_pivot 数据表,然后修改目录 console\migrations
下生成的文件内容如下:
<?php
use yii\db\Migration;
class m160818_014008_post_tag_pivot extends Migration
{
public function up()
{
$tableOptions = null;
if ($this->db->driverName === 'mysql') {
// http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
}
$this->createTable('{{%post_tag_pivot}}', [
'id' => $this->primaryKey(),
'post_id' => $this->integer()->unsigned()->notNull(), //对应posts表id
'tag_id' => $this->integer()->unsigned()->notNull(), //对应tags表id
], $tableOptions);
$this->createIndex( //建立索引
'idx-post_tag_pivot-post_id', //建立索引的名字
'post_tag_pivot', //建立索引的表
'post_id' //建立索引的键
);
$this->createIndex( //同上
'idx-post_tag_pivot-tag_id',
'post_tag_pivot',
'tag_id'
);
}
public function down()
{
}
/*
// Use safeUp/safeDown to run migration code within a transaction
public function safeUp()
{
}
public function safeDown()
{
}
*/
}
数据表就建立完成了。
二、tags 表的增删改查
打开 Gii 工具。选择 Model Generator。依次建立 tags 和 post_tag_pivot 模型。Namespace 选择 common\models。之后修改 common\models 文件夹下 tags 文件内容如下。
<?php
namespace common\models;
use Yii;
use yii\behaviors\TimestampBehavior;
use yii\web\UploadedFile;
/**
* This is the model class for table "tags".
*
* @property integer $id
* @property string $tag
* @property string $meta_description
* @property string $tag_img
* @property integer $created_at
* @property integer $updated_at
*/
class Tags extends \yii\db\ActiveRecord
{
/**
* @inheritdoc
*/
public static function tableName()
{
return 'tags';
}
public function behaviors()
{
return [
TimestampBehavior::className(),
];
}
/**
* @inheritdoc
*/
public function rules()
{
return [
[['tag'], 'required'],
[['tag', 'meta_description', 'tag_img'], 'string', 'max' => 255],
];
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'tag' => 'Tag',
'meta_description' => 'Meta Description',
'tag_img' => 'Tag Img',
'layout' => 'Layout',
'created_at' => 'Created At',
'updated_at' => 'Updated At',
];
}
/**
* @inheritdoc
* @return TagsQuery the active query used by this AR class.
*/
public function getPosts()
{
return $this->hasMany(Posts::className(),['id'=>'post_id'])->viaTable('post_tag_pivot',['tag_id'=>'id'])->where(['status'=>1]);
}
}
添加 TimestampBehavior
行为就不多说了,第一篇有讲。 这里还添加了一个 getPosts()
方法,作用是通过中间表,从tags 表中查询 posts信息,详细了解看手册关于 ActiveRecord(活动记录)部分。
选择CRUD Generator,生成 tags 表的增删改查。不需要生成 post_tag_pivot 表的增删改查。
标签上传功能的实现。
在 backend\models 目录下,新建 TagFrom 模型,作为 tag_img 文件上传的模型,内容如下。
<?php
/**
* @Author: Admin
* @Date: 2016-08-18 13:04:17
* @Last Modified by: Admin
* @Last Modified time: 2016-08-23 15:50:54
* @version
*/
namespace backend\models;
use yii\base\Model;
use yii\web\UploadedFile;
use common\models\Tags;
use yii\helpers\Url;
class TagForm extends Model
{
/**
* @var UploadedFile
*/
// public $tag;
// public $meta_description;
public $tag_img;
// public $layout;
public function rules()
{
return [
[['tag_img'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'],
];
}
public function upload()
{
if ($this->validate()) {
$randName=date("Y").date("m").date("d").date("H").date("i").date("s").rand(100, 999).'-'.md5(microtime()); //生成随机文件名
$uploadPath= 'uploads/' . $randName . '.' . $this->tag_img->extension; //设置保存路径, 为 backend\web\uploads
$this->tag_img->saveAs($uploadPath); //保存文件
return Url::to('advanced/backend/web/'.$uploadPath,true); //返回文件的 url 路径,使用 Url 帮助类。
} else {
return false;
}
}
}
然后修改 backend\controllers 目录下 TagController.php 文件
<?php
namespace backend\controllers;
use Yii;
use common\models\Tags;
use backend\models\TagSearch;
use backend\models\TagForm;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii\web\UploadedFile;
use yii\helpers\Url;
/**
* TagController implements the CRUD actions for Tags model.
*/
class TagController extends Controller
{
/**
* @inheritdoc
*/
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
];
}
/**
* Lists all Tags models.
* @return mixed
*/
public function actionIndex()
{
$searchModel = new TagSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single Tags model.
* @param integer $id
* @return mixed
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new Tags model.
* If creation is successful, the browser will be redirected to the 'view' page.
* @return mixed
*/
public function actionCreate()
{
$model = new Tags(); //新建 Tags 模型
$uploadImg =new TagForm(); //新建 TagForm 模型
$request = Yii::$app->request;
if ($this->save($request,$model,$uploadImg)) { // 调用自定义函数 save 保存表单信息和上传图片。
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
'uploadImg'=>$uploadImg, // 把 TagForm 模型上传到前台
]);
}
}
/**
* Updates an existing Tags model.
* If update is successful, the browser will be redirected to the 'view' page.
* @param integer $id
* @return mixed
*/
public function actionUpdate($id)
{
$model = $this->findModel($id);
$uploadImg = new TagForm(); //新建 TagForm 模型
$request = Yii::$app->request;
if ($this->save($request,$model,$uploadImg)) { // 调用自定义函数 save 保存表单信息和上传图片。
return $this->redirect(['view', 'id' => $model->id]);
}else {
return $this->render('update', [
'model' => $model,
'uploadImg'=>$uploadImg // 把 TagForm 模型上传到前台
]);
}
}
/**
* Deletes an existing Tags model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* @param integer $id
* @return mixed
*/
public function actionDelete($id)
{
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
/**
* Finds the Tags model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* @param integer $id
* @return Tags the loaded model
* @throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = Tags::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
public function save($request,$model,$uploadImg) // 自定义函数
{
if ($request->isPost) {
$tag = $request->post('Tags');
$model->tag = $tag['tag'];
$model->meta_description = $tag['meta_description'];
if ($uploadImg->tag_img= UploadedFile::getInstance($uploadImg, 'tag_img')){ // 调用 UploadedFile 获得上传文件实例传递给 $uploadImg->tag_img
if($uploadPath = $uploadImg->upload()) //上传文件,返回文件路径
$model->tag_img = $uploadPath;
}
if ($model->save()) {
return true;
}else{
return false;
}
}else{
return false;
}
}
}
}
然后修改视图文件
// backend\views\tag\_form.php
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/* @var $this yii\web\View */
/* @var $model common\models\Tags */
/* @var $form yii\widgets\ActiveForm */
?>
<div class="tags-form">
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]); ?>
<?= $form->field($model, 'tag')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'meta_description')->textInput(['maxlength' => true]) ?>
<?= $form->field($uploadImg, 'tag_img')->fileInput() ?> //文件输入框
<?= $form->field($model, 'layout')->textInput(['maxlength' => true]) ?>
<div class="form-group">
<?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
// backend\views\tag\create.php
<?php
use yii\helpers\Html;
/* @var $this yii\web\View */
/* @var $model common\models\Tags */
$this->title = 'Create Tags';
$this->params['breadcrumbs'][] = ['label' => 'Tags', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="tags-create">
<h1><?= Html::encode($this->title) ?></h1>
<?= $this->render('_form', [
'model' => $model,
'uploadImg'=>$uploadImg //传输 TagForm 模型。
]) ?>
</div>
// backend\views\tag\update.php
<?php
use yii\helpers\Html;
/* @var $this yii\web\View */
/* @var $model common\models\Tags */
$this->title = 'Update Tags: ' . $model->id;
$this->params['breadcrumbs'][] = ['label' => 'Tags', 'url' => ['index']];
$this->params['breadcrumbs'][] = ['label' => $model->id, 'url' => ['view', 'id' => $model->id]];
$this->params['breadcrumbs'][] = 'Update';
?>
<div class="tags-update">
<h1><?= Html::encode($this->title) ?></h1>
<?= $this->render('_form', [
'model' => $model,
'uploadImg'=>$uploadImg //传输 TagForm 模型。
]) ?>
</div>
前台建立 Tag 显示
接着在 Gii 选择 Controller Generator。Controller Class 填写 frontend\controllers\TagController 。 View Path 填写 @frontend/views/tag。生成控制器文件。
接着打开 frontend\controllers\TagController.php 文件,修改内容如下:
<?php
namespace frontend\controllers;
use common\models\Tags;
class TagController extends \yii\web\Controller
{
public function actionIndex()
{
$tags = Tags::find()->all();
return $this->render('index',[
'tags'=>$tags]);
}
}
在修改 frontend\controllers\view\tag\index.php 文件,修改内容如下:
<?php
/* @var $this yii\web\View */
use yii\helpers\Url;
$this->title = '标签';
$this->params['breadcrumbs'][] = $this->title;
?>
<h1>标签</h1>
<div class="row">
<?php foreach ($tags as $tags) {
?>
<div class="col-md-3">
<div class="media">
<div class="media-left">
<a href="#">
<img class="media-object" width="54px" height="54px" src="<?php echo "$tags->tag_img"; ?>" alt="<?php echo "$tags->tag"; ?>">
</a>
</div>
<div class="media-body">
<h4 class="media-heading"><a href="<?php echo Url::toRoute(['post/index','id'=>$tags->id]); ?>"><?php echo "$tags->tag"; ?></a></h4> //设置 Url ,点击标签名时,显示该标签所有的文章
<p><?php echo "$tags->meta_description"; ?></p>
</div>
</div>
</div>
<?php } ?>
</div>
接着修改 frontend\controllers\PostController.php 文件的 actionIndex 方法如下
public function actionIndex($id='')
{
if (!empty($id)) {
$tags =Tags::findOne($id);
$posts = $tags->getPosts()->orderBy(['id'=>SORT_DESC]); //根据标签 Id 查找所有 Post
}else{
$posts = Posts::find()->where(['status' => 1])->orderBy(['id'=>SORT_DESC]);
;
}
$countuPosts = clone $posts ;
$pages = new Pagination(['totalCount' => $countuPosts->count(),'pageSize'=>5]);
$models = $posts->offset($pages->offset)
->limit($pages->limit)
->all();
return $this->render('index', [
'models' => $models,
'pages' => $pages,
]);
}
在文章详情页显示标签
<?php
/* @var $this yii\web\View */
$this->title = $post->title;
$this->params['breadcrumbs'][] = ['label' => 'Posts', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<h1><?php echo $post->title; ?></h1>
<p class="text-muted">作者:<?php echo $post->author; ?> 标签:<?php foreach ($tags as $tags) {
echo "<kbd>".$tags['tag']."</kbd> ";
} ?></p>
<p>
<?php echo yii\helpers\Markdown::process($post->content, 'gfm'); ?>
</p>
新建 post 表时添加 tag
在 common\models\Posts.php 下添加 getTags 方法 中间表查询。
public function getTags()
{
return $this->hasMany(Tags::className(),['id'=>'tag_id'])->viaTable('post_tag_pivot',['post_id'=>'id']);
}
修改 backend\controllers\PostContrlloer.php 内容如下
<?php
namespace backend\controllers;
use Yii;
use common\models\Posts;
use common\models\Tags;
use common\models\PostTagPivot;
use backend\models\PostsSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
/**
* PostController implements the CRUD actions for Posts model.
*/
class PostController extends Controller
{
/**
* @inheritdoc
*/
public function actionSeeder(){
$faker = \Faker\Factory::create('zh_CN');
$a=0;
for ($i=0; $i <20 ; $i++) {
$posts = new Posts();
$posts->title = $faker->text($maxNbChars = 20);
$posts->author = $faker->name;
$posts->content = $faker->text($maxNbChars = 3000);
if ($posts->insert()) {
$a+=1;
}else{
}
}
echo "添加".$a."条数据";
}
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
];
}
/**
* Lists all Posts models.
* @return mixed
*/
public function actionIndex()
{
$searchModel = new PostsSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single Posts model.
* @param integer $id
* @return mixed
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new Posts model.
* If creation is successful, the browser will be redirected to the 'view' page.
* @return mixed
*/
public function actionCreate() //创建 post 时,查找所有标签,提供选择
{
$model = new Posts();
$tags = Tags::find()->all(); //查找所有标签
$request = Yii::$app->request;
$checkTags=""; //已选择的标签,由于新建标签所以 $checkTags 为空。
if ($model->load($request->post()) && $model->save() && $this->saveTag($model->attributes['id'],$request->post('tags'))) { //新建 post 时获得 post 的id,然后通过自定义方法 saveTag保存 tags
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
'tags'=> $tags, //传递所有标签到前台
'checkTags'=>$checkTags, //传递已选择标签到前台
]);
}
}
/**
* Updates an existing Posts model.
* If update is successful, the browser will be redirected to the 'view' page.
* @param integer $id
* @return mixed
*/
public function actionUpdate($id) //修改 post 时,查找所有标签,并查找改 post 的 tag 传到前台
{
$model = Posts::findOne($id);
$checkTag = $model->tags; //根据 post 的 id 查找已选择的 tag
$tags = Tags::find()->all();
$request = Yii::$app->request;
$checkTags=array();
foreach ( $checkTag as $checkTag) {
$checkTags[]=$checkTag->id; //把查找到的 $checkTag 提取出已选择 tag 的 id。
}
if ($model->load($request->post()) && $model->save() && $this->saveTag($id,$request->post('tags'))) { //修改 post 时获得 post 的id,然后通过自定义方法 saveTag 保存 tags
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('update', [
'model' => $model,
'tags'=> $tags,
'checkTags'=>$checkTags,
]);
}
}
public function saveTag($id,$checkTag)
{
PostTagPivot::deleteAll("post_id = $id"); //首先根据 post 的 id 删除 Post_Tag_Pivot 表建立的关系
if ($checkTag) { //根据前台传回来的选择写入数据库。
foreach ($checkTag as $key => $value) {
$PostTagPivot = new PostTagPivot();
$PostTagPivot->post_id = $id;
$PostTagPivot->tag_id = $value;
$PostTagPivot->insert();
}
}
return true;
}
/**
* Deletes an existing Posts model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* @param integer $id
* @return mixed
*/
public function actionDelete($id)
{
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
/**
* Finds the Posts model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* @param integer $id
* @return Posts the loaded model
* @throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = Posts::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
}
修改视图。
// backend\views\post\_form.php
<?php
use yii\helpers\Html;
use yii\helpers\ArrayHelper;
use yii\widgets\ActiveForm;
/* @var $this yii\web\View */
/* @var $model common\models\Posts */
/* @var $form yii\widgets\ActiveForm */
?>
<div class="posts-form">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'title')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'author')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'content')->widget('yidashi\markdown\Markdown',['language' => 'zh']); ?>
<?= Html::label('标签', 'tags', ['class' => 'control-label']) ?> //添加标签文本
<?= Html::checkboxList('tags', $checkTags, ArrayHelper::map($tags, 'id', 'tag')) ?> //通过 html 助手生成表单, $checkTags 是已选择的数组。
<?= $form->field($model, 'status')->textInput() ?>
<div class="form-group">
<?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
// backend\views\post\create.php
<?php
use yii\helpers\Html;
/* @var $this yii\web\View */
/* @var $model common\models\Posts */
$this->title = 'Create Posts';
$this->params['breadcrumbs'][] = ['label' => 'Posts', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="posts-create">
<h1><?= Html::encode($this->title) ?></h1>
<?= $this->render('_form', [
'model' => $model,
'tags'=> $tags,
'checkTags'=>$checkTags,
]) ?>
</div>
// backend\views\post\update.php
<?php
use yii\helpers\Html;
/* @var $this yii\web\View */
/* @var $model common\models\Posts */
$this->title = '修改文章: ' . $model->title;
$this->params['breadcrumbs'][] = ['label' => 'Posts', 'url' => ['index']];
$this->params['breadcrumbs'][] = ['label' => $model->title, 'url' => ['view', 'id' => $model->id]];
$this->params['breadcrumbs'][] = 'Update';
?>
<div class="posts-update">
<h1><?= Html::encode($this->title) ?></h1>
<?= $this->render('_form', [
'model' => $model,
'tags'=> $tags,
'checkTags'=>$checkTags,
]) ?>
</div>
最后在 backend\views\layouts\mian.php,在main.php布局文件中 $menuItems 修改为
$menuItems = [
['label' => 'Home', 'url' => ['/site/index']],
['label' => '文章', 'url' => ['/post/index']],
['label' => '标签', 'url' => ['/tag/index']],
];
在 frontend\views\layouts\mian.php,在main.php布局文件中 $menuItems 修改为
$menuItems = [
['label' => '博客', 'url' => ['/post/index']],
['label' => '标签', 'url' => ['/tag/index']],
['label' => 'Home', 'url' => ['/site/index']],
['label' => 'About', 'url' => ['/site/about']],
['label' => 'Contact', 'url' => ['/site/contact']],
];
总结
文章写完,源码正上传至百度云盘。链接稍后发在评论区。
石头杨
最后登录:2021-01-23
在线时长:13小时6分
- 粉丝39
- 金钱325
- 威望120
- 积分1655
共 7 条评论
源码链接奉上 http://pan.baidu.com/s/1o8EmhZk
失效了地址
发布教程时请尽量放些截图,谢谢。
好的,今后一定注意
对着自己的程序参考,很有帮助,加油
非常的好,希望出一点专题,针对一些难点,特别是那个文档里没有的,谢谢
非常的好,希望出一点专题,针对一些难点,特别是那个文档里没有的,谢谢
date("Y").date("m").date("d").date("H").date("i").date("s")
为什么不写成
date("YmdHis")
我在前台内容详情页加了个判断,不然标签为空会报错