Laravel大型项目系列教程(四)显示文章列表和用户修改文章
小编心语:不知不觉已经第四部分了,非常感谢很多人给小编提的意见,改了很多bug,希望以后能继续帮小编找找茬~小编也不希望误导大家~这一节,主要讲的 是如何显示文章列表和让用户修改文章,小编预告一下(一共有八节哦)如果有不懂得地方,或者本文呢没有讲清楚的地方,敬请期待下一章节。
一、前言
上节教程中实现了发布文章的功能,本节教程中将大概实现在首页和用户主页分页显示文章和标签列表、用户能够修改删除文章。
二、Let's go
1.首页显示文章和标签列表
我们需要在首页显示文章和标签列表,修改views/index.blade.php
:
@extends('_layouts.default') @section('main')
<div class="am-g am-g-fixed">
<div class="am-u-md-8">
@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="#">{{{ $article->user->nickname }}}</a> posted on {{ $article->created_at->format('Y/m/d H:i') }} under
@foreach ($article->tags as $tag)
<a href="#" 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
</div> <div class="am-u-md-4 blog-sidebar">
<br/>
<div class="am-panel-group">
<section class="am-panel am-panel-default">
<div class="am-panel-hd"><span class="am-icon-tags"></span> Tags</div>
<ul class="am-list">
@for ($i = 0, $len = count($tags); $i < $len; $i++)
<li>
<a href="#">{{ $tags[$i]->name }}
@if ($i == 0)
<span class="am-fr am-badge am-badge-danger am-round">{{ $tags[$i]->count }}</span>
@elseif ($i == 1)
<span class="am-fr am-badge am-badge-warning am-round">{{ $tags[$i]->count }}</span>
@elseif ($i == 2)
<span class="am-fr am-badge am-badge-success am-round">{{ $tags[$i]->count }}</span>
@else
<span class="am-fr am-badge am-round">{{ $tags[$i]->count }}</span>
@endif
</a>
</li>
@endfor
</ul>
</section>
</div>
</div>
</div>
@stop
在custom.css
中增加:
@media only screen and (min-width: 641px) {
.blog-sidebar {
font-size: 1.4rem;
}
} .blog-main {
padding: 20px 0;
} .blog-title {
margin: 10px 0 20px 0;
} .blog-meta {
font-size: 14px;
margin: 10px 0 20px 0;
color: #222;
} .blog-meta a {
color: #27ae60;
}
修改routes.php
:
Route::get('/', function()
{
$articles = Article::with('user', 'tags')->orderBy('created_at', 'desc')->paginate(10);
$tags = Tag::where('count', '>', '0')->orderBy('count', 'desc')->orderBy('updated_at', 'desc')->take(10)->get();
return View::make('index')->with('articles', $articles)->with('tags', $tags);
});
上面Article::with()
使用了预加载,可以减少查询次数。
发布几篇文章然后访问首页:
2.实现用户主页
我们在发表文章后应该能在用户主页看到文章列表,并能对文章进行操作,先在导航栏nav.blade.php
的@else
上面添加一个按钮My Articles
:
<div class="am-topbar-right">
<a href="{{ URL::to('user/'. Auth::id() . '/articles') }}" class="am-btn am-btn-primary am-topbar-btn am-btn-sm topbar-link-btn"><span class="am-icon-list"></span> My Articles</a>
</div>
修改home.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>
@if ($user->id == Auth::id())
<th>Managment</th>
@endif
</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>
@if ($user->id == Auth::id())
<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>
@endif
</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
先添加一个UserController
:
$ php artisan generate:controller UserController
在UserController.php
中增加:
public function articles(User $user)
{
return View::make('home')->with('user', $user)->with('articles', Article::with('tags')->where('user_id', '=', $user->id)->orderBy('created_at', 'desc')->get());
}
在routes.php
中增加:
Route::get('user/{user}/articles', 'UserController@articles');
并修改原来的Route::get('home')
:
Route::get('home', array('before' => 'auth', function()
{
return View::make('home')->with('user', Auth::user())->with('articles', Article::with('tags')->where('user_id', '=', Auth::id())->orderBy('created_at', 'desc')->get());
}));
现在当用户登录或点击My Articles
按钮后会跳转到用户主页显示文章列表,并且点击标题时能跳转到显示文章内容页面:
用户主页完成了,另外当在首页和文章内容页面点击作者时也能跳转到相应用户的主页,但是如果不是本用户是没有操作权限的,给views/index.blade.php
中的作者增加链接地址:
<a href="{{ URL::to('user/' . $article->user->id . '/articles') }}">{{{ $article->user->nickname }}}</a>
给articles/show.blade.php
中的作者增加链接地址:
<a href="{{ URL::to('user/' . $article->user->id . '/articles') }}" style="cursor: pointer;">{{{ $article->user->nickname }}}</a>
现在点击这两个超链接的时候就能跳转到相应用户的主页了,但是没有操作权限:
3.首页分页显示文章
当文章很多时,我们就要分页显示了,Laravel已经为我们实现好了分页逻辑,但它默认的是Bootstrap的样式,由于我们使用AmazeUI,所以需要自定义表示器。先在app
目录创建一个名为Blog
的文件夹,这个文件夹中主要放置我们自己写的扩展类,在其中新建一个名为PaginationPresenter.php
的文件,修改:
class PaginationPresenter extends Illuminate\Pagination\Presenter { public function getActivePageWrapper($text)
{
return '<li class="am-active"><a href="">'.$text.'</a></li>';
} public function getDisabledTextWrapper($text)
{
return '<li class="am-disabled"><a href="">'.$text.'</a></li>';
} public function getPageLinkWrapper($url, $page, $rel = null)
{
return '<li><a href="'.$url.'">'.$page.'</a></li>';
}
}
完成之后这个类还不能被找到,需要在composer.josn
中的autoload classmap
中增加"app/Blog"
,然后执行:
$ composer dump-autoload
这样这个类就能被找到了,现在创建分页链接的视图:
$ php artisan generate:view pagination
修改pagination.blade.php
:
<ul class="am-pagination am-pagination-centered">
{{ with(new PaginationPresenter($paginator))->render() }}
</ul>
完成后修改app/config/view.php
中的pagination
的值为pagination
,在routes.php
中Route::get('/')
内paginate()
的参数就是指定每页显示的数量,由于我文章比较少,暂时把它设为2,最后在views/index.blade.php
中文章显示之后添加{{ $articles->links() }}
,现在访问首页就会看到如下分页链接了:
4.修改文章的视图
这步要实现在用户主页能够修改文章了,自己的文章只能自己或者管理员修改,在ArticleController.php
中增加一个过滤器:
public function canOperation($route, $request)
{
if (!(Auth::user()->is_admin or Auth::id() == Article::find(Route::input('article'))->user_id))
{
return Redirect::to('/');
}
}
上面的Route::input('article')
可以获得路由参数,这里就是文章的id
值,然后在构造函数中添加使用过滤器,再添加一个csrf
过滤器:
$this->beforeFilter('csrf', array('only' => array('store', 'update', 'destroy')));
$this->beforeFilter('@canOperation', array('only' => array('edit', 'update', 'destroy')));
创建修改文章的视图:
$ php artisan generate:view articles.edit
修改articles/edit.blade.php
:
@extends('_layouts.default') @section('main')
<div class="am-g am-g-fixed">
<div class="am-u-sm-12">
<h1>Edit Article</h1>
<hr/>
@if ($errors->has())
<div class="am-alert am-alert-danger" data-am-alert>
<p>{{ $errors->first() }}</p>
</div>
@endif
{{ Form::model($article, array('url' => URL::route('article.update', $article->id), 'method' => 'PUT', 'class' => "am-form")) }}
<div class="am-form-group">
{{ Form::label('title', 'Title') }}
{{ Form::text('title', Input::old('title')) }}
</div>
<div class="am-form-group">
{{ Form::label('content', 'Content') }}
{{ Form::textarea('content', Input::old('content'), array('rows' => '20')) }}
<p class="am-form-help">
<button id="preview" type="button" class="am-btn am-btn-xs am-btn-primary"><span class="am-icon-eye"></span> Preview</button>
</p>
</div>
<div class="am-form-group">
{{ Form::label('tags', 'Tags') }}
{{ Form::text('tags', Input::old('tags')) }}
<p class="am-form-help">Separate multiple tags with a comma ","</p>
</div>
<p><button type="submit" class="am-btn am-btn-success">
<span class="am-icon-pencil"></span> Modify</button>
</p>
{{ Form::close() }}
</div>
</div> <div class="am-popup" id="preview-popup">
<div class="am-popup-inner">
<div class="am-popup-hd">
<h4 class="am-popup-title"></h4>
<span data-am-modal-close
class="am-close">×</span>
</div>
<div class="am-popup-bd">
</div>
</div>
</div>
<script>
$(function() {
$('#preview').on('click', function() {
$('.am-popup-title').text($('#title').val());
$.post('preview', {'content': $('#content').val()}, function(data, status) {
$('.am-popup-bd').html(data);
});
$('#preview-popup').modal();
});
});
</script>
@stop
在routes.php
中增加:
Route::post('article/{id}/preview', array('before' => 'auth', 'uses' => 'ArticleController@preview'));
这是为了修改文章时能够预览文章。
在ArticleController.php
中修改:
public function edit($id)
{
$article = Article::with('tags')->find($id);
$tags = '';
for ($i = 0, $len = count($article->tags); $i < $len; $i++) {
$tags .= $article->tags[$i]->name . ($i == $len - 1 ? '' : ',');
}
$article->tags = $tags;
return View::make('articles.edit')->with('article', $article);
}
现在在用户主页点击修改文章时会跳转到修改页面:
5.修改文章
在ArticleController.php
添加修改文章的业务逻辑:
public function update($id)
{
$rules = [
'title' => 'required|max:100',
'content' => 'required',
'tags' => array('required', 'regex:/^\w+$|^(\w+,)+\w+$/'),
];
$validator = Validator::make(Input::all(), $rules);
if ($validator->passes()) {
$article = Article::with('tags')->find($id);
$article->update(Input::only('title', 'content'));
$resolved_content = Markdown::parse(Input::get('content'));
$article->resolved_content = $resolved_content;
$tags = array_unique(explode(',', Input::get('tags')));
if (str_contains($resolved_content, '<p>')) {
$start = strpos($resolved_content, '<p>');
$length = strpos($resolved_content, '</p>') - $start - 3;
$article->summary = substr($resolved_content, $start + 3, $length);
} elseif (str_contains($resolved_content, '</h')) {
$start = strpos($resolved_content, '<h');
$length = strpos($resolved_content, '</h') - $start - 4;
$article->summary = substr($resolved_content, $start + 4, $length);
}
$article->save();
foreach ($article->tags as $tag) {
if (($index = array_search($tag->name, $tags)) !== false) {
unset($tags[$index]);
} else {
$tag->count--;
$tag->save();
$article->tags()->detach($tag->id);
}
}
foreach ($tags as $tagName) {
$tag = Tag::whereName($tagName)->first();
if (!$tag) {
$tag = Tag::create(array('name' => $tagName));
}
$tag->count++;
$article->tags()->save($tag);
}
return Redirect::route('article.show', $article->id);
} else {
return Redirect::route('article.edit', $id)->withInput()->withErrors($validator);
}
}
这部分较难的是对Tag
的处理,可能我的方法不是最好的。
这样就能真正的实现修改了:
6.删除文章
在ArticleController.php
中增加:
public function destroy($id)
{
$article = Article::find($id);
foreach ($article->tags as $tag) {
$tag->count--;
$tag->save();
$article->tags()->detach($tag->id);
}
$article->delete();
return Redirect::to('home');
}
我们这里删除文章其实是软删除,它在数据库中还是存在的。
当点击Yes
后会发现文章被删除了。
7.小结
本节教程完成了在首页和用户主页显示文章和标签列表,用户能够管理自己的文章,但只在首页实现了分页显示文章,你可以自己实现在用户主页也分页显示,在删除文章确认提示的时候,你可以加上要删除文章的标题,这样用户体验会更好,你完全可以按你的想法进行实现。这节就到此结束了,我们已经实现了管理员用户管理,下节就将完善管理员模块,实现文章和标签管理。
别忘记还有最后的代码下载:
$ git clone https://github.com/shiyanlou/laravel-blog-4.git
看到这里,有没有很想去动手实践一下,点击这里,为你免费搭建好了在线编程环境,让你不再为搭建环境而烦恼
Laravel大型项目系列教程(四)显示文章列表和用户修改文章的更多相关文章
- Laravel大型项目系列教程(三)之发表文章
Laravel大型项目系列教程(三)之发表文章 一.前言 上一节教程中完成了用户管理,这节教程将大概完成发表Markdown格式文章并展示的功能. 二.Let's go 1.数据库迁移 文章模块中我们 ...
- Laravel大型项目系列教程(二)之用户管理
Laravel大型项目系列教程(二) 一.前言 本节教程将大概实现用户的注册.修改个人信息.管理用户功能. 二.Let's go 1.创建用户注册视图 $ php artisan generate:v ...
- Laravel大型项目系列教程(一)
Laravel大型项目系列教程(一) 一.课程概述 1.课程介绍 本教程将使用Laravel完成一个多用户的博客系统,大概会包含如下内容: 路由管理. 用户管理,如用户注册.修改信息.锁定用户等. 文 ...
- Laravel大型项目系列教程(五)之文章和标签管理
一.前言 本节教程将大概完成文章和标签管理以及标签关联. 二.Let's go 1.文章管理 首先创建管理后台文章列表视图: $ php artisan generate:view admin.art ...
- 一步一步使用ABP框架搭建正式项目系列教程之本地化详解
返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 本篇目录 扯扯本地化 ABP中的本地化 小结 扯扯本地化 本节来说说本地化,也有叫国际化.全球化的,不管怎么个叫法,反正道理都是一 ...
- ABP框架搭建项目系列教程基础版完结篇
返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 经过前面十二篇的基础教程,现在终于该做个总结了. 回顾 第一篇,我们建议新手朋友们先通过ABP官网的启动模板生成解决方案,因为这样 ...
- C#微信公众号开发系列教程四(接收普通消息)
微信公众号开发系列教程一(调试环境部署) 微信公众号开发系列教程一(调试环境部署续:vs远程调试) C#微信公众号开发系列教程二(新手接入指南) C#微信公众号开发系列教程三(消息体签名及加解密) C ...
- Android Studio系列教程四--Gradle基础
Android Studio系列教程四--Gradle基础 2014 年 12 月 18 日 DevTools 本文为个人原创,欢迎转载,但请务必在明显位置注明出处!http://stormzhang ...
- NGUI系列教程四(自定义Atlas,Font)
今天我们来看一下怎么自定义NGUIAtlas,制作属于自己风格的UI.第一部分:自定义 Atlas1 . 首先我们要准备一些图标素材,也就是我们的UI素材,将其导入到unity工程中.2. 全选我们需 ...
随机推荐
- 【腾讯优测干货分享】Android内存泄漏的简单检查与分析方法
本文来自于Dev Club 开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d14047603a5bf1242ad01b 导语 内存泄漏问题大约是An ...
- 需要UWP Vendor一名
工作地点北京,海淀,微软大厦2号楼,小冰项目组.
- C#服务器获取客户端IP地址以及归属地探秘
背景:博主本是一位Windows桌面应用程序开发工程师,对网络通信一知半解.一日老婆逛完某宝,问:"为什么他们知道我的地址呢,他们是怎么获取我的地址的呢?" 顺着这个问题我们的探秘 ...
- Atitit 会话层和表示层的异同
Atitit 会话层和表示层的异同 会话层 这一层也称为会晤层或对话层.在会话层及以上的更高层次中,数据传送的单位没有另外再取名字,一般都可称为报文. 会话层虽然不参与具体的数据传输,但它却对数据传输 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(20)-权限管理系统-根据权限获取菜单
系列目录 不知不觉到20讲,真是漫长的日子,可惜最近工作挺忙,要不可以有更多的时间来更新,多谢大家的一路支持.如果你觉得好,记得帮我点击推荐^-^ 我们在之前已经插入一些真实数据,其中包含了一个用户和 ...
- 梯度提升树(GBDT)原理小结
在集成学习之Adaboost算法原理小结中,我们对Boosting家族的Adaboost算法做了总结,本文就对Boosting家族中另一个重要的算法梯度提升树(Gradient Boosting De ...
- blocking and unblocking mechanism for linux drivern code
概念: 1> 阻塞操作 是指在执行设备操作时,若不能获得资源,则挂起进程直到满足操作条件后再进行操作.被挂起的进程进入休眠,被从调度器移走,直到条件满足: 2> 非阻塞操作 在 ...
- 使用OWIN作为WebAPI的宿主
前言 好吧,也没什么好说的,就是个技术的总结,直接生成MVC的项目,感觉好重,虽然各种东西很全 ...也许我是处女座? - -, OWIN呃,这里我就不解释了,自己也是一知半解,可以参考 Open W ...
- C#开发微信门户及应用(31)--微信语义理解接口的实现和处理
微信语义理解接口提供从用户自然语言输入到结构化解析的技术实现,使用先进的自然语言处理技术给开发者提供一站式的语义解析方案.该平台覆盖多个垂直领域的语义场景,部分领域还可以支持取得最终的展示结果.开发者 ...
- Chart: Who pays the most in Seattle for software engineers
http://www.geekwire.com/2012/chart-pays-seattle-software-engineers/ Chart: Who pays the most in Seat ...