先預(yù)覽下網(wǎng)站的主要界面,一個列表頁,一個詳情頁:
本著簡潔不簡單的原則,力求界面盡量簡潔,功能盡量實用。
列表頁可以根據(jù)影片名稱、影片介紹、影片分類、演員、導(dǎo)演、地區(qū)、語言等搜索影片信息,還可以根據(jù)相應(yīng)字段排序,使用的是yii框架提供的GridView小部件實現(xiàn)的。
詳情頁即展示一部影片的詳情和播放地址。
再次說明:本人不會儲存任何影片播放文件,所有的影片信息來自其他網(wǎng)站,僅供說明展示教程功能。
Yii框架的安裝見:Yii框架的安裝
本系列教程使用的是yii2 高級項目模板自帶三個入口。分別是frontend(前臺)、backend(后臺)、console(命令行入口)。關(guān)于yii的多入口、目錄結(jié)構(gòu),基礎(chǔ)知識不展開。
安裝好yii后,將《從零開始搭建完整的電影全棧系統(tǒng)(一)》中存放影視信息和播放地址的兩張表 vod_detail和play_url導(dǎo)入到對應(yīng)數(shù)據(jù)庫。
使用Yii自帶的腳手架工具gii根據(jù)數(shù)據(jù)庫生成對應(yīng)的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經(jīng)過加密生成的唯一字符串
* @property string $vod_title 視頻名稱
* @property string|null $vod_sub_title 視頻別名
* @property string|null $vod_blurb 簡介
* @property string|null $vod_content 詳細(xì)介紹
* @property int|null $vod_status 狀態(tài)
* @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 導(dǎo)演
* @property string|null $vod_writer 編劇
* @property string|null $vod_remarks 影片版本
* @property int|null $vod_pubdate
* @property string|null $vod_area 地區(qū)
* @property string|null $vod_lang 語言
* @property string|null $vod_year 年代
* @property int|null $vod_hits 總瀏覽數(shù)
* @property int|null $vod_hits_day 一天瀏覽數(shù)
* @property int|null $vod_hits_week 一周瀏覽數(shù)
* @property int|null $vod_hits_month 一月瀏覽數(shù)
* @property int|null $vod_up 頂數(shù)
* @property int|null $vod_down 踩數(shù)
* @property float|null $vod_score 總評分
* @property int|null $vod_score_all
* @property int|null $vod_score_num
* @property int|null $vod_create_time 創(chuàng)建時間
* @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' => '影片副標(biāo)題',
'vod_blurb' => 'Vod Blurb',
'vod_content' => '影片介紹',
'vod_status' => '狀態(tài)',
'vod_type' => '影片分類',
'vod_class' => '擴展分類',
'vod_tag' => '標(biāo)簽',
'vod_pic_url' => '圖片Url鏈接',
'vod_pic_path' => '圖片本地路徑',
'vod_pic_thumb' => '縮略圖本地路徑',
'vod_actor' => '演員',
'vod_director' => '導(dǎo)演',
'vod_writer' => '編劇',
'vod_remarks' => '備注',
'vod_pubdate' => 'Vod Pubdate',
'vod_area' => '地區(qū)',
'vod_lang' => '語言',
'vod_year' => '年代',
'vod_hits' => '瀏覽次數(shù)',
'vod_hits_day' => '日瀏覽次數(shù)',
'vod_hits_week' => '周瀏覽次數(shù)',
'vod_hits_month' => '月瀏覽次數(shù)',
'vod_up' => '頂次數(shù)',
'vod_down' => '踩次數(shù)',
'vod_score' => '評分',
'vod_score_all' => '總評分',
'vod_score_num' => '評分次數(shù)',
'vod_create_time' => '收錄時間',
'vod_update_time' => '更新時間',
'vod_lately_hit_time' => '最近瀏覽時間',
];
}
/***
* @return \yii\db\ActiveQuery
* 獲取視頻對應(yīng)的播放地址
*/
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 關(guān)聯(lián)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']//關(guān)閉分頁(默認(rèn)開啟)
/* 默認(rèn)不顯示的按鈕設(shè)置 */
'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) ?>>復(fù)制圖片地址</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', '地區(qū):' . $model->vod_area, ['class' => 'text-muted']), ['class' => 'data hidden-md hidden-lg hidden-sm']) ?>
<p class="data">
<?= Html::tag('span', '狀態(tài):', ['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">導(dǎo)演:</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> -->
<span class="split-line"></span>
<span class="text-muted">地區(qū):</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">集數(shù):</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"> ></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:點擊鏈接可以復(fù)制哦</span>
<a class="copy_checked text-red" href="javascript:;">復(fù)制全部</a>
</div>
</div>
<?php endforeach; ?>
別忘了注入自定義的js和css文件。
整個WEB網(wǎng)站的搭建自行代碼寫的最多的就是影視詳情頁的模板。列表頁排序、搜索、分頁是自定義yii自帶的GridView小部件實現(xiàn)的。
對Yii基礎(chǔ)知識不熟悉的同學(xué)可以自行學(xué)習(xí)。
整理完成后代碼發(fā)布與github。