Laravel大型项目系列教程(三)之发表文章

Laravel大型项目系列教程(三)之发表文章

一、前言

上一节教程中完成了用户管理,这节教程将大概完成发表Markdown格式文章并展示的功能。

二、Let‘s go

1.数据库迁移

文章模块中我们需要建立articlestags以及article_tag表,每篇文章会有一到多个标签,每个标签会有一到多篇文章,创建迁移文件:

$ php artisan migrate:make create_articles_table --create=articles
$ php artisan migrate:make create_tags_table --create=tags
$ php artisan migrate:make create_article_tag_table --create=article_tag

修改*_create_articles_table.php

Schema::create(‘articles‘, function(Blueprint $table)
{
    $table->increments(‘id‘);
    $table->string(‘title‘);
    $table->string(‘summary‘)->nullable();
    $table->text(‘content‘);
    $table->text(‘resolved_content‘);
    $table->integer(‘user_id‘);
    $table->softDeletes();
    $table->timestamps();
});

修改*_create_tags_table.php

Schema::create(‘tags‘, function(Blueprint $table)
{
    $table->increments(‘id‘);
    $table->string(‘name‘)->unique();
    $table->integer(‘count‘)->default(0);
    $table->softDeletes();
    $table->timestamps();
});

修改*_create_article_tag_table.php

Schema::create(‘article_tag‘, function(Blueprint $table)
{
    $table->increments(‘id‘);
    $table->integer(‘article_id‘);
    $table->integer(‘tag_id‘);
});

执行迁移:

$ php artisan migrate

2.创建Article和Tag模型

创建ArticleTag模型:

$ php artisan generate:model article
$ php artisan generate:model tag

先在User.php中增加:

public function articles()
{
    return $this->hasMany(‘Article‘);
}

一个用户会有多篇文章。

修改Article.php

use Illuminate\Database\Eloquent\SoftDeletingTrait;

class Article extends \Eloquent {

    use SoftDeletingTrait;

    protected $fillable = [‘title‘, ‘content‘];

    public function tags()
    {
        return $this->belongsToMany(‘Tag‘);
    }

    public function user()
    {
        return $this->belongsTo(‘User‘);
    }
}

一篇文章会有多个标签并属于一个用户。

修改Tag.php

use Illuminate\Database\Eloquent\SoftDeletingTrait;

class Tag extends \Eloquent {

    use SoftDeletingTrait;

    protected $fillable = [‘name‘];

    public function articles()
    {
        return $this->belongsToMany(‘Article‘);
    }
}

一个标签会有多篇文章。

上面用到了软删除belongsToMany用于多对多关联。

3.发表文章视图

首先在导航条nav.blade.php中添加一个发表文章的选项:

<li><a href="{{ URL::to(‘article/create‘) }}"><span class="am-icon-edit"></span> Publish Article</a></li>

这时候登录会发现多了一个选项:

下面创建视图:

$ php artisan generate:view articles.create

修改articles/create.blade.php

@extends(‘_layouts.default‘)

@section(‘main‘)
<div class="am-g am-g-fixed">
  <div class="am-u-sm-12">
      <h1>Publish Article</h1>
      <hr/>
    @if ($errors->has())
    <div class="am-alert am-alert-danger" data-am-alert>
      <p>{{ $errors->first() }}</p>
    </div>
    @endif
    {{ Form::open(array(‘url‘ => ‘article‘, ‘class‘ => ‘am-form‘)) }}
        <div class="am-form-group">
          <label for="title">Title</label>
          <input id="title" name="title" type="text" value="{{ Input::old(‘title‘) }}"/>
        </div>
        <div class="am-form-group">
          <label for="content">Content</label>
          <textarea id="content" name="content" rows="20">{{ Input::old(‘content‘) }}</textarea>
          <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">
          <label for="tags">Tags</label>
          <input id="tags" name="tags" type="text" value="{{ 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-send"></span> Publish</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">&times;</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中的代码已经很零乱,那是因为我们把业务逻辑写在了这个文件中,对于文章模块我们把业务逻辑写在控制器中,首先创建一个文章控制器:

$ php artisan generate:controller ArticleController

我们会发现在app\controllers下多了一个ArticleController.php文件,它是一个资源控制器,在routes.php中增加:

Route::resource(‘article‘, ‘ArticleController‘);

对应路由如下:

现在在ArticleController.php中增加过滤器并修改create方法:

public function __construct()
{
    $this->beforeFilter(‘auth‘, array(‘only‘ => array(‘create‘, ‘store‘, ‘edit‘, ‘update‘, ‘destroy‘)));
}

public function create()
{
    return View::make(‘articles.create‘);
}

这时在登录后点击Publish Article选项后,会出现发表文章的页面:

4.文章预览

这里我们将使用Markdown格式来编写文章,同时需要提供一个预览功能,先需要安装Markdown解析插件,在composer.jsonrequire中增加:

"maxhoffmann/parsedown-laravel": "dev-master"

然后composer update安装,在config/app.php中增加:

‘providers‘ => array(
    ...
    ‘MaxHoffmann\Parsedown\ParsedownServiceProvider‘
),

‘aliases‘ => array(
    ...
    ‘Markdown‘        => ‘MaxHoffmann\Parsedown\ParsedownFacade‘,
),

安装完后,在ArticleController.php中增加:

public function preview() {
    return Markdown::parse(Input::get(‘content‘));
}

routes.php中增加:

Route::post(‘article/preview‘, array(‘before‘ => ‘auth‘, ‘uses‘ => ‘[email protected]‘));

切记要添加在Route::resource(‘article‘, ‘ArticleController‘);前面。

现在来试试我们的预览功能吧,点击Preview按钮:

出现预览界面:

5.添加文章

下面就要向数据库添加文章了,在ArticleController.php中修改:

public function store()
{
    $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::create(Input::only(‘title‘, ‘content‘));
        $article->user_id = Auth::id();
        $resolved_content = Markdown::parse(Input::get(‘content‘));
        $article->resolved_content = $resolved_content;
        $tags = 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);
        } else if (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 ($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.create‘)->withInput()->withErrors($validator);
    }
}

public function show($id)
{
    return View::make(‘articles.show‘)->with(‘article‘, Article::find($id));
}

上面代码实现了保存文章和显示文章的业务逻辑,保存文章时验证tags用了regex正则表达式来验证标签是否用号分隔,有没有发现Article模型中有一个resolved_content字段,这个字段来保存解析后的内容,这样只需要在保存的时候解析一次,显示文章页面就显示resolved_content字段的内容,不需要再解析一次,这是空间换时间的做法,看个人喜好了,如果想要更好的体验,可以只在前台页面解析,保存的时候把前台解析的内容保存到resolved_content字段,前台解析Markdown有一个很好的工具StackEdit

现在就差个显示文章的视图了,创建:

$ php artisan generate:view articles.show

修改articles/show.blade.php


完成之后看看效果吧,先编辑文章:

发布后跳转到显示文章页面:

6.小结

这节教程使用了控制器,完成了发布文章并展示的功能,但还是有很多瑕疵,在代码方面,现在你可以重构下User和验证登录的路由,把它们都变成控制器,在用户体验上你可以把发布文章编辑内容时由服务器端解析改成客户端解析,推荐StackEdit,下一节教程将完成网站首页和用户主页展示文章列表和标签,并且让用户能够删除和修改文章。

本文详细出处http://www.shiyanlou.com/courses/123

时间: 2024-10-09 09:11:41

Laravel大型项目系列教程(三)之发表文章的相关文章

Laravel大型项目系列教程(三)之显示文章列表和用户修改文章

小 编心语:不知不觉已经第四部分了,非常感谢很多人给小编提的意见,改了很多bug,希望以后能继续帮小编找找茬~小编也不希望误导大家~这一节,主要讲的 是如何显示文章列表和让用户修改文章,小编预告一下(一共有八节哦)如果有不懂得地方,或者本文呢没有讲清楚的地方,敬请期待下一章节. 一.前言 上节教程中实现了发布文章的功能,本节教程中将大概实现在首页和用户主页分页显示文章和标签列表.用户能够修改删除文章. 二.Let's go 1.首页显示文章和标签列表 我们需要在首页显示文章和标签列表,修改vie

Laravel大型项目系列教程(四)显示文章列表和用户修改文章

小编心语:不知不觉已经第四部分了,非常感谢很多人给小编提的意见,改了很多bug,希望以后能继续帮小编找找茬~小编也不希望误导大家~这一节,主要讲的 是如何显示文章列表和让用户修改文章,小编预告一下(一共有八节哦)如果有不懂得地方,或者本文呢没有讲清楚的地方,敬请期待下一章节. 一.前言 上节教程中实现了发布文章的功能,本节教程中将大概实现在首页和用户主页分页显示文章和标签列表.用户能够修改删除文章. 二.Let's go 1.首页显示文章和标签列表 我们需要在首页显示文章和标签列表,修改view

laravel大型项目系列教程(四)之显示文章列表和用户修改文章

小编心语:不知不觉已经第四部分了,非常感谢很多人给小编提的意见,改了很多bug,希望以后能继续帮小编找找茬~小编也不希望误导大家~这一节,主要讲的是如何显示文章列表和让用户修改文章,小编预告一下(一共有八节哦)如果有不懂得地方,或者本文呢没有讲清楚的地方,敬请期待下一章节. 一.前言 上节教程中实现了发布文章的功能,本节教程中将大概实现在首页和用户主页分页显示文章和标签列表.用户能够修改删除文章. 二.Let's go 1.首页显示文章和标签列表 我们需要在首页显示文章和标签列表,修改views

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">

laravel大型项目系列教程(六)之优化、单元测试以及部署

本节教程将讲解错误处理.配置文件的使用.单元测试以及部署到Apache服务器. 1.错误处理 如果用户访问的URL不存在或者服务器存在错误时,我们不希望返货一个错误的页面,而想返回一个友好提示的页面,在Laravel中可以很轻松地实现,Laravel有很简单的错误和日志处理,当服务器端存在错误时,app/start/global.php里默认有一个处理所有异常的异常处理程序: App::error(function(Exception $exception) { Log::error($exce

Laravel大型项目系列教程(六)之优化、单元测试及部署

一.前言 本节教程将讲解错误处理.配置文件的使用.单元测试以及部署到Apache服务器. 二.Let's go 1.错误处理 如果用户访问的URL不存在或者服务器存在错误时,我们不希望返货一个错误的页面,而想返回一个友好提示的页面,在Laravel中可以很轻松地实现,Laravel有很简单的错误和日志处理,当服务器端存在错误时,app/start/global.php里默认有一个处理所有异常的异常处理程序: App::error(function(Exception $exception) {

Laravel大型项目系列教程(七)之7 扩展包和Artisan开发

本节教程将讲解扩展包开发和Artisan扩展开发,并浏览不同分辨率下的自适应效果.本节结束后整个教程就结束了,文章最后有完整版程序代码的下载. 1.扩展包开发 在前面开发中,我们经常要用到通知,如修改用户信息时视图要写 @if (Session::has('message')) <div class="am-alert am-alert-{{ Session::get('message')['type'] }}" data-am-alert> <p>{{ Ses

struts2 官方系列教程三:使用struts2 标签 tag

避免被爬,先贴上本帖地址:struts2 官方系列教程一:使用struts2 标签 tag http://www.cnblogs.com/linghaoxinpian/p/6901316.html 本教材假定你已完成了HelloWorld项目,你可以在 struts2 官方系列教程三:使用struts2 标签 tag 下载本章节的代码 在上一节教程中,我们在index.jsp中使用 url tag 创建了一个超链接hello.action 这节我们将探索struts2中其它tags Web应用程

CRL快速开发框架系列教程三(更新数据)

本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框架系列教程四(删除数据) CRL快速开发框架系列教程五(使用缓存) CRL快速开发框架系列教程六(分布式缓存解决方案) CRL快速开发框架系列教程七(使用事务) CRL快速开发框架系列教程八(使用CRL.Package) CRL快速开发框架系列教程九(导入/导出数据) CRL快速开发框架系列教程十(