一、前言

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


二、Let's go

1.数据库迁移

articlestagsarticle_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 IlluminateDatabaseEloquentSoftDeletingTrait;

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 IlluminateDatabaseEloquentSoftDeletingTrait;

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 ></span> Publish Article</a></li>


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

$ php artisan generate:view articles.create


articles/create.blade.php 
@extends('_layouts.default')

@section('main')
<div >
  <div >
      <h1>Publish Article</h1>
      <hr/>
    @if ($errors->has())
    <div  data-am-alert>
      <p>{{ $errors->first() }}</p>
    </div>
    @endif
    {{ Form::open(array('url' => 'article', 'class' => 'am-form')) }}
        <div >
          <label for="title">Title</label>
          <input  name="title" type="text" value="{{ Input::old('title') }}"/>
        </div>
        <div >
          <label for="content">Content</label>
          <textarea  name="content" rows="20">{{ Input::old('content') }}</textarea>
          <p >
              <button  type="button" ><span ></span> Preview</button>
          </p>
        </div>
        <div >
          <label for="tags">Tags</label>
          <input  name="tags" type="text" value="{{ Input::old('tags') }}"/>
          <p >Separate multiple tags with a comma ","</p>
        </div>
        <p><button type="submit" ><span ></span> Publish</button></p>
    {{ Form::close() }}
  </div>
</div>

<div  >
  <div >
    <div >
      <h4 ></h4>
      <span data-am-modal-close
            >×</span>
    </div>
    <div >
    </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


appcontrollersArticleController.phproutes.php 
Route::resource('article', 'ArticleController');


对应路由如下:

ArticleController.phpcreate 
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.文章预览

MarkdownMarkdowncomposer.jsonrequire
"maxhoffmann/parsedown-laravel": "dev-master"


composer updateconfig/app.php 
'providers' => array(
    ...
    'MaxHoffmannParsedownParsedownServiceProvider'
),

'aliases' => array(
    ...
    'Markdown'        => 'MaxHoffmannParsedownParsedownFacade',
),


ArticleController.php 
public function preview() {
    return Markdown::parse(Input::get('content'));
}


routes.php 
Route::post('article/preview', array('before' => 'auth', 'uses' => 'ArticleController@preview'));


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));
}


tagsregex,Articleresolved_contentresolved_contentresolved_content
$ php artisan generate:view articles.show


articles/show.blade.php 
@extends('_layouts.default')

@section('main')
<article >
  <div >
      <div >
        <br/>
        <div >
          <h1 >{{{ $article->title }}}</h1>
          <p >Author: <a >{{{ $article->user->nickname }}}</a> Datetime: {{ $article->updated_at }}</p>
        </div>
        <div >
            <blockquote>
              Tags:
              @foreach ($article->tags as $tag)
                  <a >{{ $tag->name }}</a>
              @endforeach
            </blockquote>            
          </p>
          <p>{{ $article->resolved_content }}</p>
        </div>
        <br/>
      </div>
  </div>
</article>
@stop


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

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

6.小结

User