写点什么

从零开始搭建完整的电影全栈系统(二)——简单的 WEB 展示网站的搭建

用户头像
刘强西
关注
发布于: 2020 年 09 月 09 日

先预览下网站的主要界面,一个列表页,一个详情页:



本着简洁不简单的原则,力求界面尽量简洁,功能尽量实用。

列表页可以根据影片名称、影片介绍、影片分类、演员、导演、地区、语言等搜索影片信息,还可以根据相应字段排序,使用的是 yii 框架提供的 GridView 小部件实现的。

详情页即展示一部影片的详情和播放地址。


再次说明:本人不会储存任何影片播放文件,所有的影片信息来自其他网站,仅供说明展示教程功能。


Yii 框架的安装见:Yii框架的安装


本系列教程使用的是 yii2 高级项目模板自带三个入口。分别是 frontend(前台)、backend(后台)、console(命令行入口)。关于 yii 的多入口、目录结构,基础知识不展开。


安装好 yii 后,将《从零开始搭建完整的电影全栈系统(一)》中存放影视信息和播放地址的两张表 voddetail 和 playurl 导入到对应数据库。


使用 Yii 自带的脚手架工具 gii 根据数据库生成对应的 MVC 文件。gii 工具号称可以帮我们写代码,很方便就生成了 model 类,view 视图、控制器。


VodDetail:


<?php
namespace common\models;
use Yii;use yii\helpers\ArrayHelper;
/** * This is the model class for table "vod_detail". * * @property int $id * @property string $url 采集的url * @property string $url_id 采集的url经过加密生成的唯一字符串 * @property string $vod_title 视频名称 * @property string|null $vod_sub_title 视频别名 * @property string|null $vod_blurb 简介 * @property string|null $vod_content 详细介绍 * @property int|null $vod_status 状态 * @property string|null $vod_type 视频分类 * @property string|null $vod_class 扩展分类 * @property string|null $vod_tag * @property string|null $vod_pic_url 图片url * @property string|null $vod_pic_path 图片下载保存路径 * @property string|null $vod_pic_thumb * @property string|null $vod_actor 演员 * @property string|null $vod_director 导演 * @property string|null $vod_writer 编剧 * @property string|null $vod_remarks 影片版本 * @property int|null $vod_pubdate * @property string|null $vod_area 地区 * @property string|null $vod_lang 语言 * @property string|null $vod_year 年代 * @property int|null $vod_hits 总浏览数 * @property int|null $vod_hits_day 一天浏览数 * @property int|null $vod_hits_week 一周浏览数 * @property int|null $vod_hits_month 一月浏览数 * @property int|null $vod_up 顶数 * @property int|null $vod_down 踩数 * @property float|null $vod_score 总评分 * @property int|null $vod_score_all * @property int|null $vod_score_num * @property int|null $vod_create_time 创建时间 * @property int|null $vod_update_time 更新时间 * @property int|null $vod_lately_hit_time 最后浏览时间 */class VodDetail extends \yii\db\ActiveRecord{ /** * {@inheritdoc} */ public static function tableName() { return 'vod_detail'; }
/** * {@inheritdoc} */ public function rules() { return [ [['url', 'url_id', 'vod_title'], 'required'], [['vod_content'], 'string'], [['vod_status', 'vod_pubdate', 'vod_hits', 'vod_hits_day', 'vod_hits_week', 'vod_hits_month', 'vod_up', 'vod_down', 'vod_score_all', 'vod_score_num', 'vod_create_time', 'vod_update_time', 'vod_lately_hit_time'], 'integer'], [['vod_score'], 'number'], [['url'], 'string', 'max' => 500], [['url_id'], 'string', 'max' => 100], [['vod_title', 'vod_sub_title', 'vod_blurb', 'vod_type', 'vod_class', 'vod_tag', 'vod_pic_url', 'vod_pic_path', 'vod_pic_thumb', 'vod_actor', 'vod_director', 'vod_writer', 'vod_remarks', 'vod_area', 'vod_lang', 'vod_year'], 'string', 'max' => 255], [['url_id'], 'unique'], ]; }
/** * {@inheritdoc} */ public function attributeLabels() { return [ 'id' => 'ID', 'url' => 'Url', 'url_id' => 'Url ID', 'vod_title' => '影片名称', 'vod_sub_title' => '影片副标题', 'vod_blurb' => 'Vod Blurb', 'vod_content' => '影片介绍', 'vod_status' => '状态', 'vod_type' => '影片分类', 'vod_class' => '扩展分类', 'vod_tag' => '标签', 'vod_pic_url' => '图片Url链接', 'vod_pic_path' => '图片本地路径', 'vod_pic_thumb' => '缩略图本地路径', 'vod_actor' => '演员', 'vod_director' => '导演', 'vod_writer' => '编剧', 'vod_remarks' => '备注', 'vod_pubdate' => 'Vod Pubdate', 'vod_area' => '地区', 'vod_lang' => '语言', 'vod_year' => '年代', 'vod_hits' => '浏览次数', 'vod_hits_day' => '日浏览次数', 'vod_hits_week' => '周浏览次数', 'vod_hits_month' => '月浏览次数', 'vod_up' => '顶次数', 'vod_down' => '踩次数', 'vod_score' => '评分', 'vod_score_all' => '总评分', 'vod_score_num' => '评分次数', 'vod_create_time' => '收录时间', 'vod_update_time' => '更新时间', 'vod_lately_hit_time' => '最近浏览时间', ]; }
/*** * @return \yii\db\ActiveQuery * 获取视频对应的播放地址 */ public function getPlayurls() { return $this->hasMany(PlayUrl::className(), ['url_id' => 'url_id']);// $playurls = $this->hasMany(PlayUrl::className(), ['url_id' => 'url_id']);// return ArrayHelper::index($playurls, null, 'play_from'); }}
复制代码


PlayUrl:


<?php
namespace common\models;
use Yii;
/** * This is the model class for table "play_url". * * @property int $id * @property string|null $play_title * @property string|null $play_from * @property string $play_url * @property string $play_url_aes 将url生成唯一字符串 * @property string $url_id 关联vod_detail url_id * @property int|null $create_time * @property int|null $update_time */class PlayUrl extends \yii\db\ActiveRecord{ /** * {@inheritdoc} */ public static function tableName() { return 'play_url'; }
/** * {@inheritdoc} */ public function rules() { return [ [['play_url', 'play_url_aes', 'url_id'], 'required'], [['create_time', 'update_time'], 'integer'], [['play_title', 'play_from', 'play_url'], 'string', 'max' => 255], [['play_url_aes', 'url_id'], 'string', 'max' => 100], [['play_url_aes'], 'unique'], ]; }
/** * {@inheritdoc} */ public function attributeLabels() { return [ 'id' => 'ID', 'play_title' => 'Play Title', 'play_from' => 'Play From', 'play_url' => 'Play Url', 'play_url_aes' => 'Play Url Aes', 'url_id' => 'Url ID', 'create_time' => 'Create Time', 'update_time' => 'Update Time', ]; }}
复制代码

VodDetailSearch:


<?php
namespace common\models;
use yii\base\Model;use yii\data\ActiveDataProvider;use common\models\VodDetail;
/** * VodDetailSearch represents the model behind the search form of `common\models\VodDetail`. */class VodDetailSearch extends VodDetail{ /** * {@inheritdoc} */ public function rules() { return [ [['id', 'vod_status', 'vod_pubdate', 'vod_hits', 'vod_hits_day', 'vod_hits_week', 'vod_hits_month', 'vod_up', 'vod_down', 'vod_score_all', 'vod_score_num', 'vod_create_time', 'vod_update_time', 'vod_lately_hit_time'], 'integer'], [['url', 'url_id', 'vod_title', 'vod_sub_title', 'vod_blurb', 'vod_content', 'vod_type', 'vod_class', 'vod_tag', 'vod_pic_url', 'vod_pic_path', 'vod_pic_thumb', 'vod_actor', 'vod_director', 'vod_writer', 'vod_remarks', 'vod_area', 'vod_lang', 'vod_year'], 'safe'], [['vod_score'], 'number'], ]; }
/** * {@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 = VodDetail::find();
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([ 'query' => $query, 'pagination' => ['pageSize' => 50], 'sort' => [ 'defaultOrder' => ['vod_update_time' => SORT_DESC],// 'attributes' => ['id', 'title'], ], ]);
$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, 'vod_status' => $this->vod_status, 'vod_pubdate' => $this->vod_pubdate, 'vod_hits' => $this->vod_hits, 'vod_hits_day' => $this->vod_hits_day, 'vod_hits_week' => $this->vod_hits_week, 'vod_hits_month' => $this->vod_hits_month, 'vod_up' => $this->vod_up, 'vod_down' => $this->vod_down, 'vod_score' => $this->vod_score, 'vod_score_all' => $this->vod_score_all, 'vod_score_num' => $this->vod_score_num, 'vod_create_time' => $this->vod_create_time, 'vod_update_time' => $this->vod_update_time, 'vod_lately_hit_time' => $this->vod_lately_hit_time, ]);
$query->andFilterWhere(['like', 'url', $this->url]) ->andFilterWhere(['like', 'url_id', $this->url_id]) ->andFilterWhere(['like', 'vod_title', $this->vod_title]) ->andFilterWhere(['like', 'vod_sub_title', $this->vod_sub_title]) ->andFilterWhere(['like', 'vod_blurb', $this->vod_blurb]) ->andFilterWhere(['like', 'vod_content', $this->vod_content]) ->andFilterWhere(['like', 'vod_type', $this->vod_type]) ->andFilterWhere(['like', 'vod_class', $this->vod_class]) ->andFilterWhere(['like', 'vod_tag', $this->vod_tag]) ->andFilterWhere(['like', 'vod_pic_url', $this->vod_pic_url]) ->andFilterWhere(['like', 'vod_pic_path', $this->vod_pic_path]) ->andFilterWhere(['like', 'vod_pic_thumb', $this->vod_pic_thumb]) ->andFilterWhere(['like', 'vod_actor', $this->vod_actor]) ->andFilterWhere(['like', 'vod_director', $this->vod_director]) ->andFilterWhere(['like', 'vod_writer', $this->vod_writer]) ->andFilterWhere(['like', 'vod_remarks', $this->vod_remarks]) ->andFilterWhere(['like', 'vod_area', $this->vod_area]) ->andFilterWhere(['like', 'vod_lang', $this->vod_lang]) ->andFilterWhere(['like', 'vod_year', $this->vod_year]);
return $dataProvider; }}
复制代码


VodDetailController:


<?php
namespace frontend\controllers;
use Yii;use common\models\VodDetail;use common\models\VodDetailSearch;use yii\web\Controller;use yii\web\NotFoundHttpException;use yii\filters\VerbFilter;
/** * VodDetailController implements the CRUD actions for VodDetail model. */class VodDetailController extends Controller{ /** * {@inheritdoc} */ public function behaviors() { return [ 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => [ 'delete' => ['POST'], ], ], ]; }
/** * Lists all VodDetail models. * @return mixed */ public function actionIndex() { $searchModel = new VodDetailSearch(); $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [ 'searchModel' => $searchModel, 'dataProvider' => $dataProvider, ]); }
/** * Displays a single VodDetail model. * @param integer $id * @return mixed * @throws NotFoundHttpException if the model cannot be found */ public function actionView($id) { return $this->render('view', [ 'model' => $this->findModel($id), ]); }
/** * Creates a new VodDetail model. * If creation is successful, the browser will be redirected to the 'view' page. * @return mixed */ public function actionCreate() { $model = new VodDetail();
if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); }
return $this->render('create', [ 'model' => $model, ]); }
/** * Updates an existing VodDetail model. * If update is successful, the browser will be redirected to the 'view' page. * @param integer $id * @return mixed * @throws NotFoundHttpException if the model cannot be found */ public function actionUpdate($id) { $model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); }
return $this->render('update', [ 'model' => $model, ]); }
/** * Deletes an existing VodDetail model. * If deletion is successful, the browser will be redirected to the 'index' page. * @param integer $id * @return mixed * @throws NotFoundHttpException if the model cannot be found * @throws \Throwable * @throws \yii\db\StaleObjectException */ public function actionDelete($id) { $this->findModel($id)->delete();
return $this->redirect(['index']); }
/** * Finds the VodDetail model based on its primary key value. * If the model is not found, a 404 HTTP exception will be thrown. * @param integer $id * @return VodDetail the loaded model * @throws NotFoundHttpException if the model cannot be found */ protected function findModel($id) { if (($model = VodDetail::findOne($id)) !== null) { return $model; }
throw new NotFoundHttpException('The requested page does not exist.'); }}
复制代码


视图文件:


影视列表:


<?php
use yii\helpers\Html;use yii\grid\GridView;
/* @var $this yii\web\View *//* @var $searchModel common\models\VodDetailSearch *//* @var $dataProvider yii\data\ActiveDataProvider */
$this->title = '影片列表';$this->params['breadcrumbs'][] = $this->title;?>
<div class="vod-detail-index">
<h1><?= Html::encode($this->title) ?></h1>
<!-- <p>--> <!-- --><? //= Html::a('Create Vod Detail', ['create'], ['class' => 'btn btn-success']) ?> <!-- </p>-->
<?php echo $this->render('_search', ['model' => $searchModel]); ?>
<?= GridView::widget([ 'dataProvider' => $dataProvider, 'filterModel' => $searchModel,// 'showHeader' => false, 'showFooter' => false, 'columns' => [ //['class' => 'yii\grid\SerialColumn'],
//'id', //'url:url', //'url_id:url', //'vod_title', ['attribute' => 'vod_title', 'value' => function ($data) { return Html::a($data->vod_title, ['vod-detail/view', 'id' => $data->id], ['title' => '详情']); }, 'format' => ['html']], //'vod_sub_title', //'vod_blurb', //'vod_content:ntext', //'vod_status', //'vod_type', ['attribute' => 'vod_type', 'value' => function ($data) { return Html::a($data->vod_type, ['vod-detail/index', 'VodDetailSearch' => ['vod_type'=>$data->vod_type]], ['title' => '分类']); }, 'format' => ['html']], //'vod_class', //'vod_tag', //'vod_pic_url:url', //'vod_pic_path', //'vod_pic_thumb', //'vod_actor', //'vod_director', //'vod_writer', 'vod_remarks', //'vod_pubdate', //'vod_area', //'vod_lang', //'vod_year', ['attribute' => 'vod_year', 'value' => function ($data) { return Html::a($data->vod_year, ['vod-detail/index', 'VodDetailSearch' => ['vod_year'=>$data->vod_year]], ['title' => '年代']); }, 'format' => ['html']], //'vod_hits', //'vod_hits_day', //'vod_hits_week', //'vod_hits_month', //'vod_up', //'vod_down', //'vod_score', //'vod_score_all', //'vod_score_num', //'vod_create_time:datetime', //'vod_update_time:datetime', ['attribute' => 'vod_update_time', 'format' => ['date', 'php:Y-m-d H:i:s']], //'vod_lately_hit_time:datetime',
/*[ 'class' => 'yii\grid\ActionColumn', 'template' => '{view}', ],*/ ], 'pager' => [ //'options' => ['class' => 'hidden']//关闭分页(默认开启) /* 默认不显示的按钮设置 */ 'firstPageLabel' => '首页', 'prevPageLabel' => '上一页', 'nextPageLabel' => '下一页', 'lastPageLabel' => '尾页' ] ]); ?>

</div>
复制代码


影视详情:


<?php
use yii\helpers\ArrayHelper;use yii\helpers\Html;use yii\helpers\Url;use yii\widgets\DetailView;
/* @var $this yii\web\View *//* @var $model common\models\VodDetail */
$this->title = $model->vod_title;$this->params['breadcrumbs'][] = ['label' => '影片列表', 'url' => ['index']];$this->params['breadcrumbs'][] = $this->title;\yii\web\YiiAsset::register($this);$playurlsList = ArrayHelper::index($model->playurls, null, 'play_from');?><div class="stui-pannel clearfix"> <div class="stui-content clearfix"> <div class="stui-content__thumb"> <?= Html::tag('a', Html::img($model->vod_pic_url, ['class' => 'img-responsive', 'alt' => $model->vod_title]), ['class' => 'thumb', 'href' => Url::toRoute(['vod-detail/view', 'id' => $model->id]), 'title' => $model->vod_title]) ?>
<p class="margin-0 text-center hidden-xs"><a class="copy_btn text-muted" href="javascript:;" data-text=<?= Url::to('', true) ?>>复制图片地址</a> </p> </div> <div class="stui-content__detail"> <?= Html::tag('h1', $model->vod_title . Html::tag('small', $model->vod_score, ['class' => 'text-red']), ['class' => 'title']) ?> <?= Html::tag('p', Html::tag('span', '别名:' . $model->vod_sub_title, ['class' => 'text-muted hidden-xs']), ['class' => 'data hidden-xs']) ?> <?= Html::tag('p', Html::tag('span', '地区:' . $model->vod_area, ['class' => 'text-muted']), ['class' => 'data hidden-md hidden-lg hidden-sm']) ?> <p class="data"> <?= Html::tag('span', '状态:', ['class' => 'text-muted']) ?> <?= Html::tag('span', $model->vod_remarks, ['class' => 'text-red']) ?> <span class="split-line hidden-xs"></span> <span class="text-muted hidden-xs">更新时间:</span><?= Html::tag('span', date('Y-m-d H:i:s', $model->vod_update_time)) ?> </p> <p class="data"><span class="text-muted">主演:</span><?php echo $model->vod_actor ?></p> <p class="data"><span class="text-muted">导演:</span><?php echo $model->vod_director ?></p> <p class="data hidden-xs"> <span class="text-muted">分类:</span><?php echo $model->vod_type ?> <span class="split-line"></span> <!--<span class="text-muted">扩展:</span><a href="/index.php/vod/search/class/%E6%AC%A7%E7%BE%8E%E5%89%A7.html" target="_blank">欧美剧</a>&nbsp;--> <span class="split-line"></span> <span class="text-muted">地区:</span><?php echo $model->vod_area ?> <span class="split-line"></span> <span class="text-muted">年份:</span><?php echo $model->vod_year ?> </p> <p class="data hidden-xs"> <span class="text-muted">语言:</span><?php echo $model->vod_lang ?> <span class="split-line"></span> <!--<span class="text-muted">集数:</span>0 <span class="split-line"></span> <span class="text-muted">时长:</span> <span class="split-line"></span> <span class="text-muted">豆瓣ID:</span>0 </p>--> <p class="data hidden-xs"> <span class="text-muted">TAG:</span> <span class="split-line"><?php echo $model->vod_tag ?></span> </p> </div> </div></div><div class="stui-pannel clearfix" id="desc"> <div class="stui-pannel__head clearfix"> <h3 class="title">剧情介绍</h3> </div> <div class="stui-content__desc col-pd clearfix"> <?= nl2br($model->vod_content); ?> </div></div><?php foreach ($playurlsList as $key => $playurls): ?> <div class="stui-pannel clearfix" id="playlist"> <div class="stui-pannel__head clearfix"> <span class="more text-muted pull-right hidden-xs">无需安装任何插件</span> <h3 class="title"> 播放类型:<?php echo $key ?></h3> </div>

<ul class="stui-content__playlist clearfix"> <?php foreach ($playurls as $key => $playurl): ?> <li class="list-group-item"> <span class="badge">用App打开</span> <a href="javascript:;" class="copy_text"><?= $playurl->play_title ?><span class="hidden-xs"><?= '$'. $playurl->play_url ?></span></a> </li> <!--<li> <a class="text-muted pull-right" href="/index.php/vod/play/id/43801/sid/1/nid/1.html"> &gt;</a> <a href="javascript:;" class="copy_text"><?/*= $playurl->play_title */?><span class="hidden-xs"><?/*= '$'. $playurl->play_url */?></span></a> </li>--> <?php endforeach; ?> <!-- end 播放地址 --> </ul> <div class="stui-pannel__foot clearfix"> <span class="text-muted pull-right hidden-xs">tips:点击链接可以复制哦</span> <a class="copy_checked text-red" href="javascript:;">复制全部</a> </div> </div><?php endforeach; ?>
复制代码


别忘了注入自定义的 js 和 css 文件。


整个 WEB 网站的搭建自行代码写的最多的就是影视详情页的模板。列表页排序、搜索、分页是自定义 yii 自带的 GridView 小部件实现的。


对 Yii 基础知识不熟悉的同学可以自行学习。

整理完成后代码发布与 github。


欢迎各位大佬入裙交流:



发布于: 2020 年 09 月 09 日阅读数: 47
用户头像

刘强西

关注

还未添加个人签名 2019.06.12 加入

还未添加个人简介

评论

发布
暂无评论
从零开始搭建完整的电影全栈系统(二)——简单的WEB展示网站的搭建