Laravel大型项目系列教程(五)之文章和标签管理
一、前言
本节教程将大概完成文章和标签管理以及标签关联。
二、Let's go
1.文章管理
首先创建管理后台文章列表视图:
$ php artisan generate:view admin.articles.list
修改views/admin/articles/list.blade.php:
@extends('_layouts.default')
@section('main')
<div class="am-g am-g-fixed blog-g-fixed">
<div class="am-u-sm-12">
<table class="am-table am-table-hover am-table-striped ">
<thead>
<tr>
<th>Title</th>
<th>Tags</th>
<th>Author</th>
<th>Managment</th>
</tr>
</thead>
<tbody>
@foreach ($articles as $article)
<tr>
<td><a href="{{ URL::route('article.show', $article->id) }}">{{{ $article->title }}}</a></td>
<td>
@foreach ($article->tags as $tag)
<span class="am-badge am-badge-success am-radius">{{ $tag->name }}</span>
@endforeach
</td>
<td><a href="{{ URL::to('user/' . $article->user->id . '/articles') }}">{{{ $article->user->nickname }}}</a></td>
<td>
<a href="{{ URL::to('article/'. $article->id . '/edit') }}" class="am-btn am-btn-xs am-btn-primary"><span class="am-icon-pencil"></span> Edit</a>
{{ Form::open(array('url' => 'article/' . $article->id, 'method' => 'DELETE', 'style' => 'display: inline;')) }}
<button type="button" class="am-btn am-btn-xs am-btn-danger" id="delete{{ $article->id }}"><span class="am-icon-remove"></span> Delete</button>
{{ Form::close() }}
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
<div class="am-modal am-modal-confirm" tabindex="-1" id="my-confirm">
<div class="am-modal-dialog">
<div class="am-modal-bd">
</div>
<div class="am-modal-footer">
<span class="am-modal-btn" data-am-modal-cancel>No</span>
<span class="am-modal-btn" data-am-modal-confirm>Yes</span>
</div>
</div>
</div>
<script>
$(function() {
$('[id^=delete]').on('click', function() {
$('.am-modal-bd').text('Sure you want to delete it?');
$('#my-confirm').modal({
relatedTarget: this,
onConfirm: function(options) {
$(this.relatedTarget).parent().submit();
},
onCancel: function() {
}
});
});
});
</script>
@stop
在nav.blade.php中增加一个Articles的超链接:
<li class="{{ (isset($page) and ($page == 'articles')) ? 'am-active' : '' }}"><a href="{{ URL::to('admin/articles') }}">Articles</a></li>
创建一个管理员控制器,在app/controllers下创建一个名为AdminController.php的文件,修改:
class AdminController extends \BaseController {
public function articles()
{
return View::make('admin.articles.list')->with('articles', Article::with('user', 'tags')->orderBy('created_at', 'desc')->get())->with('page', 'articles');
}
}
在Route::group(array('prefix' => 'admin')中增加:
Route::get('articles', 'AdminController@articles');
管理文章可以重用上节教程写的业务逻辑,修改下ArticleController.php,把destroy()中最后的Redirect::to('home')改成Redirect::back(), 再修改一下home.blade.php,加一个是否是管理员的判断,这样当点击作者跳转到用户主页时,除了作者自己管理员也能操作文章:
@if ($user->id == Auth::id() or (Auth::check() and Auth::user()->is_admin))
现在点击导航栏的Articles,就会出现所有的文章:

这样管理员就可以操作所有的文章了。
我们还可以再修改下admin/users/list.blade.php,当点击用户列表的昵称时也会跳转到用户主页:
<a href="{{ URL::to('user/' . $user->id . '/articles') }}">{{{ $user->nickname }}}</a>
现在访问用户列表页面:

2.显示标签列表
创建一个标签列表视图:
$ php artisan generate:view admin.tags.list
修改admin/tags/list.blade.php:
@extends('_layouts.default')
@section('main')
<div class="am-g am-g-fixed blog-g-fixed">
<div class="am-u-sm-12">
<table class="am-table am-table-hover am-table-striped ">
<thead>
<tr>
<th>TagName</th>
<th>ArticleCount</th>
<th>CreateDateTime</th>
<th>Managment</th>
</tr>
</thead>
<tbody>
@foreach ($tags as $tag)
<tr>
<td>{{{ $tag->name }}}</td>
<td>{{ $tag->count }}</td>
<td>{{ $tag->created_at->format('Y-m-d H:i') }}</td>
<td>
<a href="{{ URL::to('tag/'. $tag->id . '/edit') }}" class="am-btn am-btn-xs am-btn-primary"><span class="am-icon-pencil"></span> Edit</a>
{{ Form::open(array('url' => 'tag/' . $tag->id, 'method' => 'DELETE', 'style' => 'display: inline;')) }}
<button type="button" class="am-btn am-btn-xs am-btn-danger" id="delete{{ $tag->id }}"><span class="am-icon-remove"></span> Delete</button>
{{ Form::close() }}
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
<div class="am-modal am-modal-confirm" tabindex="-1" id="my-confirm">
<div class="am-modal-dialog">
<div class="am-modal-bd">
</div>
<div class="am-modal-footer">
<span class="am-modal-btn" data-am-modal-cancel>No</span>
<span class="am-modal-btn" data-am-modal-confirm>Yes</span>
</div>
</div>
</div>
<script>
$(function() {
$('[id^=delete]').on('click', function() {
$('.am-modal-bd').text('Sure you want to delete it?');
$('#my-confirm').modal({
relatedTarget: this,
onConfirm: function(options) {
$(this.relatedTarget).parent().submit();
},
onCancel: function() {
}
});
});
});
</script>
@stop
再在nav.blade.php中增加Tags选项:
<li class="{{ (isset($page) and ($page == 'tags')) ? 'am-active' : '' }}"><a href="{{ URL::to('admin/tags') }}">Tags</a></li>
在Route::group(array('prefix' => 'admin')中增加:
Route::get('tags', 'AdminController@tags');
在AdminController.php中增加:
public function tags() {
return View::make('admin.tags.list')->with('tags', Tag::all()->orderBy('count', 'desc'));
}
现在点击导航栏上方的Tags超链接:

3.修改标签
创建修改标签的视图:
$ php artisan generate:view tags.edit
修改views/tags/edit.blade.php:
@extends('_layouts.default')
@section('main')
<div class="am-g am-g-fixed">
<div class="am-u-sm-12">
<h1>Edit Tag</h1>
<hr/>
@if (Session::has('message'))
<div class="am-alert am-alert-{{ Session::get('message')['type'] }}" data-am-alert>
<p>{{ Session::get('message')['content'] }}</p>
</div>
@endif
@if ($errors->has())
<div class="am-alert am-alert-danger" data-am-alert>
<p>{{ $errors->first() }}</p>
</div>
@endif
{{ Form::model($tag, array('url' => URL::route('tag.update', $tag->id), 'method' => 'PUT', 'class' => "am-form")) }}
<div class="am-form-group">
{{ Form::label('name', 'TagName') }}
{{ Form::text('name', Input::old('name')) }}
</div>
<p><button type="submit" class="am-btn am-btn-success">
<span class="am-icon-pencil"></span> Modify</button>
</p>
{{ Form::close() }}
</div>
</div>
@stop
创建标签控制器:
$ php artisan generate:controller TagsController
修改TagsController.php:
public function __construct()
{
$this->beforeFilter('auth', array('only' => array('create', 'store', 'edit', 'update', 'destroy')));
$this->beforeFilter('csrf', array('only' => array('store', 'update', 'destroy')));
} public function edit($id)
{
return View::make('tags.edit')->with('tag', Tag::find($id));
} public function update($id)
{
$rules = array(
'name' => array('required', 'regex:/^\w+$/'),
);
$validator = Validator::make(Input::only('name'), $rules);
if ($validator->passes()) {
Tag::find($id)->update(Input::only('name'));
return Redirect::back()->with('message', array('type' => 'success', 'content' => 'Modify tag successfully'));
} else {
return Redirect::back()->withInput()->withErrors($validator);
}
}
把这个控制器加到routes.php中:
Route::resource('tag', 'TagController');
现在就能修改标签了:


4.删除标签
修改TagsController.php:
public function destroy($id)
{
$tag = Tag::find($id);
$tag->count = 0;
$tag->save();
foreach ($tag->articles as $article) {
$tag->articles()->detach($article->id);
}
return Redirect::back();
}
我这里删除标签只是把它的文章数置为0,然后清除与相关文章的关联,你可以自己试下删除一个标签,再看看文章的标签是否去除了。
5.关联标签
当我们点击首页文章、标签栏和显示文章内容的标签的时候应该跳转到显示相应标签下所有文章的页面:


我们对上述地方加上超链接地址:
<a href="{{ URL::to('tag/' . $tag->id . '/articles') }}">{{ $tag->name }}</a>
创建指定标签的文章列表视图:
$ php artisan generate:view articles.specificTag
修改views/articles/specificTag.blade.php:
@extends('_layouts.default')
@section('main')
<div class="am-g am-g-fixed">
<div class="am-u-sm-12">
<br/>
<blockquote>Tag: <span class="am-badge am-badge-success am-radius">{{{ $tag->name }}}</span></blockquote>
@foreach ($articles as $article)
<article class="blog-main">
<h3 class="am-article-title blog-title">
<a href="{{ URL::route('article.show', $article->id) }}">{{{ $article->title }}}</a>
</h3>
<h4 class="am-article-meta blog-meta">
by <a href="{{ URL::to('user/' . $article->user->id . '/articles') }}">{{{ $article->user->nickname }}}</a> posted on {{ $article->created_at->format('Y/m/d H:i') }} under
@foreach ($article->tags as $tag)
<a href="{{ URL::to('tag/' . $tag->id . '/articles') }}" style="color: #fff;" class="am-badge am-badge-success am-radius">{{ $tag->name }}</a>
@endforeach
</h4>
<div class="am-g">
<div class="am-u-sm-12">
@if ($article->summary)
<p>{{ $article->summary }}</p>
@endif
<hr class="am-article-divider"/>
</div>
</div>
</article>
@endforeach
{{ $articles->links() }}
</div>
</div>
@stop
在TagController.php增加:
public function articles($id)
{
$tag = Tag::find($id);
$articles = $tag->articles()->orderBy('created_at', 'desc')->paginate(10);
return View::make('articles.specificTag')->with('tag', $tag)->with('articles', $articles);
}
在routes.php的Route::resource('tag', 'TagController');的上方增加:
Route::get('tag/{id}/articles', 'TagController@articles');
现在当我们点击页面上的标签时,就会显示该标签下的所有文章了:

6.显示所有标签
我们还需要一个显示所有标签的页面,先创建视图:
$ php artisan generate:view tags.list
修改views/tags/list.blade.php:
@extends('_layouts.default')
@section('main')
<div class="am-g am-g-fixed">
<div class="am-u-sm-12">
<h1>All Tags</h1>
<hr/>
@foreach ($tags as $tag)
<a href="{{ URL::to('tag/' . $tag->id . '/articles') }}" class="am-badge am-radius {{ array('', 'am-badge-primary', 'am-badge-secondary', 'am-badge-success', 'am-badge-warning', 'am-badge-danger')[rand(0, 5)] }}">{{{ $tag->name }}} {{ $tag->count }}</a>
@endforeach
<br/>
<br/>
</div>
</div>
@stop
现在点击首页的Tags链接时就会跳转到显示所有标签的页面了:


7.小结
本节教程就到此结束了,这个博客系统想要实现的功能也基本完成了,下节开始将讲解优化、单元测试、部署和扩展开发等内容,你可以继续完善,例如在管理文章和标签的时候提供一个搜索功能,给它们都加上分页,在首页加上一个搜索文章的功能,给文章加上评论功能等等,在评论功能方面现在有很多第三方评论插件,可以快速帮你实现。
最后的代码下载:
$ git clone https://github.com/shiyanlou/laravel-blog-5.git
本文详细出自实验楼
转载请注明出处
Laravel大型项目系列教程(五)之文章和标签管理的更多相关文章
- Laravel大型项目系列教程(二)之用户管理
Laravel大型项目系列教程(二) 一.前言 本节教程将大概实现用户的注册.修改个人信息.管理用户功能. 二.Let's go 1.创建用户注册视图 $ php artisan generate:v ...
- Laravel大型项目系列教程(三)之发表文章
Laravel大型项目系列教程(三)之发表文章 一.前言 上一节教程中完成了用户管理,这节教程将大概完成发表Markdown格式文章并展示的功能. 二.Let's go 1.数据库迁移 文章模块中我们 ...
- Laravel大型项目系列教程(一)
Laravel大型项目系列教程(一) 一.课程概述 1.课程介绍 本教程将使用Laravel完成一个多用户的博客系统,大概会包含如下内容: 路由管理. 用户管理,如用户注册.修改信息.锁定用户等. 文 ...
- Laravel大型项目系列教程(四)显示文章列表和用户修改文章
小编心语:不知不觉已经第四部分了,非常感谢很多人给小编提的意见,改了很多bug,希望以后能继续帮小编找找茬~小编也不希望误导大家~这一节,主要讲的 是如何显示文章列表和让用户修改文章,小编预告一下(一 ...
- ABP框架搭建项目系列教程基础版完结篇
返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 经过前面十二篇的基础教程,现在终于该做个总结了. 回顾 第一篇,我们建议新手朋友们先通过ABP官网的启动模板生成解决方案,因为这样 ...
- 一步一步使用ABP框架搭建正式项目系列教程之本地化详解
返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 本篇目录 扯扯本地化 ABP中的本地化 小结 扯扯本地化 本节来说说本地化,也有叫国际化.全球化的,不管怎么个叫法,反正道理都是一 ...
- Android Studio系列教程五--Gradle命令详解与导入第三方包
Android Studio系列教程五--Gradle命令详解与导入第三方包 2015 年 01 月 05 日 DevTools 本文为个人原创,欢迎转载,但请务必在明显位置注明出处!http://s ...
- CRL快速开发框架系列教程五(使用缓存)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- C#微信公众号开发系列教程五(接收事件推送与消息排重)
微信公众号开发系列教程一(调试环境部署) 微信公众号开发系列教程一(调试环境部署续:vs远程调试) C#微信公众号开发系列教程二(新手接入指南) C#微信公众号开发系列教程三(消息体签名及加解密) C ...
随机推荐
- ASP.NET Core "完整发布,自带运行时" 到jexus
一.阅读前须知 1.使用 jexus整合asp.net core的优点: 1)支持多站点,同一端口可以同时支持任何多的asp.net core应用程序: 2)应用程序启动.停 ...
- 【AutoMapper官方文档】DTO与Domin Model相互转换(下)
写在前面 AutoMapper目录: [AutoMapper官方文档]DTO与Domin Model相互转换(上) [AutoMapper官方文档]DTO与Domin Model相互转换(中) [Au ...
- Linux3 在VMware中搭建CentOS6.5虚拟机
前言: 本文主要是我在大家hadoop集群之前 ,需啊先安装CentOS虚拟机,记录在此,作为参考.如果能帮助到其他人,自然是更好啦. =========================== ...
- 【原】关于Python中setuptools安装的问题
在生成package的时候,需要在setup.py中引入setuptools包,可是却报告如下错误: ImportError: No module named setuptools 解决办法就是下载s ...
- Electron的环境配置
原文地址http://huisky.com/blog/161218121551123 本文介绍了Electron的环境配置,包括Electron下载.nodejs下载安装.NPM+Bower安装配置. ...
- python __getitem__, __setitem__ 实现属性的索引式存取
class MyDictionary(object): """docstring for MyDictionary""" kv = {} d ...
- Node.js:console模块
console模块提供了一个简单的调试功能,类似与web浏览器的javscript console. 下面简单介绍下该模块的使用以及用途,我使用了ES6的模版字符串(使用反引号标识),有兴趣的可以去了 ...
- 读书笔记--SQL必知必会19--存储过程
不同的DBMS对存储过程的实现不同,差异巨大,这里不涉及具体的DBMS,仅仅说明存储过程的简单含义. 19.1 存储过程 简单来说,存储过程就是为以后使用而保存的一条或多条SQL语句. 可以将存储过程 ...
- Android keycode列表
整理备忘! 基本按键 KEYCODE_0 按键'0' 7 KEYCODE_1 按键'1' 8 KEYCODE_2 按键'2' 9 KEYCODE_3 按键'3' 10 KEYCODE_4 按键'4' ...
- JS魔法堂:不完全国际化&本地化手册 之 拓展篇
前言 最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已. ...