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-closeclass="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官网
- 中文文档1、中文文档2
- 实验楼论坛
- Laravel中文网问答社区
- PHPHub中文社区
- API文档
- laravel.io
- LARACASTS
最后的最后~小编希望通过这个教程能帮助大家更好的认识laravel
laravel大型项目系列教程(四)之显示文章列表和用户修改文章相关推荐
- Laravel大型项目系列教程(五)之文章和标签管理
Laravel大型项目系列教程(五)之文章和标签管理 本节教程将大概完成文章和标签管理. 1.文章管理 首先创建管理后台文章列表视图: $ php artisan generate:view admi ...
- Laravel大型项目系列教程(三)之发表文章
Laravel大型项目系列教程(三)之发表文章 一.前言 上一节教程中完成了用户管理,这节教程将大概完成发表Markdown格式文章并展示的功能. 二.Let's go 1.数据库迁移 文章模块中我们 ...
- Laravel大型项目系列教程(二)之用户管理
Laravel大型项目系列教程(二) 一.前言 本节教程将大概实现用户的注册.修改个人信息.管理用户功能 二.Let's go 1.创建用户注册视图 <span style="font ...
- Laravel大型项目系列教程(一)
Laravel大型项目系列教程(一) 一.课程概述 1.课程介绍 本教程将使用Laravel完成一个多用户的博客系统,大概会包含如下内容: 路由管理. 用户管理,如用户注册.修改信息.锁定用户等. 文 ...
- Laravel大型项目系列教程(七)之7 扩展包和Artisan开发
本节教程将讲解扩展包开发和Artisan扩展开发,并浏览不同分辨率下的自适应效果.本节结束后整个教程就结束了,文章最后有完整版程序代码的下载. 1.扩展包开发 在前面开发中,我们经常要用到通知,如修改 ...
- laravel大型项目系列教程(六)之优化、单元测试以及部署
本节教程将讲解错误处理.配置文件的使用.单元测试以及部署到Apache服务器. 1.错误处理 如果用户访问的URL不存在或者服务器存在错误时,我们不希望返货一个错误的页面,而想返回一个友好提示的页面, ...
- docker 打包镜像_Spring Boot2 系列教程(四十一)部署 Spring Boot 到远程 Docker 容器
不知道各位小伙伴在生产环境都是怎么部署 Spring Boot 的,打成 jar 直接一键运行?打成 war 扔到 Tomcat 容器中运行?不过据松哥了解,容器化部署应该是目前的主流方案. 不同于传 ...
- 一步一步使用ABP框架搭建正式项目系列教程之本地化详解
返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 本篇目录 扯扯本地化 ABP中的本地化 小结 扯扯本地化 本节来说说本地化,也有叫国际化.全球化的,不管怎么个叫法,反正道理都是一 ...
- 史上最详细的Android Studio系列教程四--Gradle基础
史上最详细的Android Studio系列教程四--Gradle基础 转载于:https://www.cnblogs.com/zhujiabin/p/5125917.html
最新文章
- 速度提升270倍!微软和浙大联合推出全新语音合成系统FastSpeech
- 【错误记录】Visual Studio 中配置 NDK 头文件路径
- AutoML - 数据增广
- 2021暑假实习-SSM超市积分管理系统-day10笔记
- 局域网PING的TIME值都超高的一种解决方案
- http-helloworld
- 一张图带你了解JRE、JDK、JVM
- 如何直观的看出主题模型学习结果的好坏
- 使用VS2008在windows平台上试用Kinect
- 传闻称马斯克从创始人手中偷走了特斯拉公司,马斯克回击...
- rabbitmq-server 安装方法
- 【NLP】综述 | 跨语言自然语言处理笔记
- 网络层———IPv4(1)
- Excel双样本T检验之异方差检验
- 计算机基础知识之工作总结,计算机教师工作总结(精选3篇)
- 给JAVA初学者的建议(转载治phphot的一个牛人给java初学者的建议)
- 一个出身寒门的状元之死全文【原文】
- c语言处理nc程序,NC程序的语言问题
- 【虚拟机】虚拟化架构与系统部署(Windows系统安装)
- win10显示桌面计算机图标怎么删除,怎么设置显示或隐藏win10系统桌面上的我的电脑图标...
热门文章
- day17.Python中lambda表达式应用
- 实验室信息管理系统(南京浩展软件)
- Html.BeginForm() vs Ajax.BeginForm() in MVC3
- 【带流程眼镜的思考】消除“等待”就是提高效率
- C# 对象名无效 问题
- Linux学习日记:第二天
- 习题3-11 Kickdown UVA - 1588
- js-数组方法的使用和详谈
- luogu3810 【模板】三维偏序(陌上花开)
- 机械硬盘提示操作无法完成,因为磁盘管理控制台视图不是最新状态要如何办啊...