本书未发布

路由

未匹配的标注

有时,你会向扩展包的使用者公开路由信息。

因为我们提供了一个 Post 模型,因此我们添加了 RESTful 路由。为了方便演示,我们只实现了 RESTful 路由的 3 个方法。

  • 显示所有帖子 (index)
  • 显示指定单个帖子 (show)
  • 保存新的天子 (store)

控制器(Controllers)

创建基本的控制器

我们需要创建一个 PostController

为了充分利用 Laravel 控制器提供的便利方法,我们首先在 src/Http/Controllers 目录下创建一个 Controller.php 文件(类似于 Laravel 项目的文件夹结构)。

// 'src/Http/Controllers/Controller.php'
<?php

namespace JohnDoe\BlogPackage\Http\Controllers;

use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

创建一个控制器并继承基本控制器

现在,让我们在 src/Http/Controllers 目录下创建一个 PostController 文件,首先让我们在 store 方法开始吧。

// 'src/Http/Controllers/PostController'
<?php

namespace JohnDoe\BlogPackage\Http\Controllers;

class PostController extends Controller
{
    public function index()
    {
        //
    }

    public function show()
    {
        //
    }

    public function store()
    {
        // 首先进行用户的权限认证
        // 如果权限不足 则不可以创建
        if (! auth()->check()) {
            abort (403, 'Only authenticated users can create new posts.');
        }

        request()->validate([
            'title' => 'required',
            'body'  => 'required',
        ]);

        // 通过权限认证后 当前登录的用户就是该帖子的作者
        $author = auth()->user();

        $post = $author->posts()->create([
            'title'     => request('title'),
            'body'      => request('body'),
        ]);

        return redirect(route('posts.show', $post));
    }
}

路由

定义路由信息

现在控制器我们已经写好了,接下来在 src 目录下创建一个 routes/ 文件夹,并创建一个包含上面提到的 RESTful 路由信息的 web.php 文件。

// 'routes/web.php' 
<?php

use Illuminate\Support\Facades\Route;
use JohnDoe\BlogPackage\Http\Controllers\PostController;

Route::get('/posts', [PostController::class, 'index'])->name('posts.index');
Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');
Route::post('/posts', [PostController::class, 'store'])->name('posts.store');

在服务提供者中注册路由

如果要使用路由信息,我们需要在服务提供者的 boot() 方法中注册才可以:

// 'BlogPackageServiceProvider.php'
public function boot()
{
  // ... other things 
  $this->loadRoutesFrom(__DIR__.'../../routes/web.php');
}

视图

PostControllerindex()show() 方法需要有显示帖子信息的页面。

创建 blade 视图文件

src 目录下创建一个 resources/ 文件夹,在该文件夹中,创建一个 views 的子文件夹用于存放所有的视图文件。在 views 文件夹中,我们需要创建一个 posts 的子文件夹,用于存放关于帖子的所有视图文件。

  1. resources/views/posts/index.blade.php:
<h1>Showing all Posts</h1>

@forelse ($posts as $post)
    <li>{{ $post->title }}</li>
@empty
    <p> 'No posts yet' </p>
@endforelse
  1. resources/views/posts/show.blade.php:
<h1>{{ $post->title }}</h1>

<p> {{ $post->body }}</p>

注意:实际项目中,这些视图文件都是扩展其他主布局文件的。

在服务提供者中注册视图

现在我们已经有了一些视图,接下来需要在服务提供者的 boot() 方法中注册,否则是无法使用的。
重要:我们需要为 loadViewsFrom() 方法的第二个参数提供一个 key,因为从控制器中调整到视图文件时需要使用(在下一节中有详细的示例)。

// 'BlogPackageServiceProvider.php'
public function boot()
{
  // ... other things
  $this->loadViewsFrom(__DIR__.'/../resources/views', 'blogpackage');
}

控制器跳转视图

现在我们可以在  PostController 中向帖子展示视图跳转了。(因为要查询帖子信息,不要忘记引入 Post 模型哦)

提示:主要 view() 方法的第一个参数中的 blogpackage::,该前缀与我们在上一节中在服务提供者绑定的前缀一致才可以。

// 'src/Http/Controllers/PostController.php'
use JohnDoe\BlogPackage\Models\Post;

public function index()
{
    $posts = Post::all();

    return view('blogpackage::posts.index', compact('posts'));
}

public function show()
{
    $post = Post::findOrFail(request('post'));

    return view('blogpackage::posts.show', compact('post'));
}

自定义视图

你可以希望扩展包的视图用户可以自定义,与数据库迁移类似,我们使用  published 方法,需要注意 published 的第二个参数是 views,需要将 published 方法添加到服务提供者的 boot() 方法里。

// 'BlogPackageServiceProvider.php'
if ($this->app->runningInConsole()) {
  // publish database migrations

  $this->publishes([
    __DIR__.'/../resources/views' => resource_path('views/vendor/blogpackage'),
  ], 'views');

}

然后扩展包的用户就可以通过下面的命令来导出视图:

php artisan vendor:publish --provider="JohnDoe\BlogPackage\BlogPackageServiceProvider" --tag="views"

资源

在扩展包中使用视图时,我们可能希望包含 CSS 或 JavaScript 文件。

创建资源目录

如果视图文件需要包含 CSS 或 JavaScript 文件,请在 resources/ 文件夹中创建 assets 目录。视图文件中可以包含多个 CSS 或 JavaScript 文件,为了方便管理,我们需要创建 2 个文件夹,分别是 cssjs 来存储这些文件。有个不成文的约定是将主文件的文件名为 app.jsapp.css

自定义资源

就像视图一样,我们可以让用户自定义资源。具体的代码如下,只需要注意 publishes 方法的第二个参数为 assets 即可。第一个参数的目录信息要根据实际情况情况进行设置即可。

// 'BlogPackageServiceProvider.php'
if ($this->app->runningInConsole()) {
  // publish database migrations

  // publish views

  $this->publishes([
    __DIR__.'/../resources/assets' => public_path('blogpackage'),
    ], 'assets');

}

用户可使用下面的命令自动导出资源:

php artisan vendor:publish --provider="JohnDoe\BlogPackage\BlogPackageServiceProvider" --tag="assets"

引用资源

在视图中引用 CSS 和 JavaScript 文件,代码如下:

<script src="{{ asset('blogpackage/js/app.js') }}"></script>
<link href="{{ asset('blogpackage/css/app.css') }}" rel="stylesheet">

测试路由

让我们验证是否可以显示帖子内容、创建一条新帖子以及显示所有的帖子。

Feature test

tests/Feature 目录创建一个 CreatePostTest.php 测试文件,并在文件中添加以下测试代码,代码中包含对用户权限的验证:

// 'tests/Feature/CreatePostTest.php'
<?php

namespace JohnDoe\BlogPackage\Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use JohnDoe\BlogPackage\Models\Post;
use JohnDoe\BlogPackage\Tests\TestCase;
use JohnDoe\BlogPackage\Tests\User;

class CreatePostTest extends TestCase
{
    use RefreshDatabase;

    /** @test */
    function authenticated_users_can_create_a_post()
    {
        // 确保数据库中没有任何帖子
        $this->assertCount(0, Post::all());

        $author = factory(User::class)->create();

        $response = $this->actingAs($author)->post(route('posts.store'), [
            'title' => 'My first fake title',
            'body'  => 'My first fake body',
        ]);

        $this->assertCount(1, Post::all());

        tap(Post::first(), function ($post) use ($response, $author) {
            $this->assertEquals('My first fake title', $post->title);
            $this->assertEquals('My first fake body', $post->body);
            $this->assertTrue($post->author->is($author));
            $response->assertRedirect(route('posts.show', $post));
        });
    }
}

同时,我们还要验证在创建帖子时标题与内容不能为空:

// 'tests/Feature/CreatePostTest.php'
/** @test */
function a_post_requires_a_title_and_a_body()
{
    $author = factory(User::class)->create();

    $this->actingAs($author)->post(route('posts.store'), [
        'title' => '',
        'body'  => 'Some valid body',
    ])->assertSessionHasErrors('title');

    $this->actingAs($author)->post(route('posts.store'), [
        'title' => 'Some valid title',
        'body'  => '',
    ])->assertSessionHasErrors('body');
}

接下来,让我们验证没有登录的用户(或游客)无法创建新帖子:

// 'tests/Feature/CreatePostTest.php'
/** @test */
function guests_can_not_create_posts()
{
    // We're starting from an unauthenticated state
    $this->assertFalse(auth()->check());

    $this->post(route('posts.store'), [
       'title' => 'A valid title',
       'body'  => 'A valid body',
    ])->assertForbidden();
}

最后,让我们一起验证帖子列表页面是否可以正确显示:

// 'tests/Feature/CreatePostTest.php'
/** @test */
function all_posts_are_shown_via_the_index_route()
{
    // Given we have a couple of Posts
    factory(Post::class)->create([
        'title' => 'Post number 1'
    ]);
    factory(Post::class)->create([
        'title' => 'Post number 2'
    ]);
    factory(Post::class)->create([
        'title' => 'Post number 3'
    ]);

    // We expect them to all show up
    // with their title on the index route
    $this->get(route('posts.index'))
        ->assertSee('Post number 1')
        ->assertSee('Post number 2')
        ->assertSee('Post number 3')
        ->assertDontSee('Post number 4');
}

/** @test */
function a_single_post_is_shown_via_the_show_route()
{
    $post = factory(Post::class)->create([
        'title' => 'The single post title',
        'body'  => 'The single post body',
    ]);

    $this->get(route('posts.show', $post))
        ->assertSee('The single post title')
        ->assertSee('The single post body');
}

Tip: 在测试中获取异常、错误消息时,禁用正常的异常处理很有必要,这样可帮助我们了解异常的来源。你可以通过测试开始时声明 $this->withoutExceptionHandling(); 来实现。

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/laravel-package...

译文地址:https://learnku.com/docs/laravel-package...

上一篇 下一篇
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
贡献者:1
讨论数量: 0
发起讨论 只看当前版本


暂无话题~