石头杨 2016-08-23 18:03:35 11011次浏览 7条评论 6 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; ?>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;标签:<?php foreach ($tags as $tags) {
	echo "<kbd>".$tags['tag']."</kbd>&nbsp;&nbsp;";
} ?></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 时,查找所有标签,并查找改 posttag 传到前台
    {
        $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']],
    ];

总结

文章写完,源码正上传至百度云盘。链接稍后发在评论区。

觉得很赞
您需要登录后才可以评论。登录 | 立即注册