{"id":12146,"date":"2020-05-06T00:00:00","date_gmt":"2020-05-05T16:00:00","guid":{"rendered":"https:\/\/fgchen.com\/wpedu2\/2020\/05\/06\/%e3%80%90%e7%ad%86%e8%a8%98-laravel%e3%80%91%e5%bb%ba%e7%ab%8b%e7%ac%ac%e4%b8%80%e6%94%aflaravel%e6%87%89%e7%94%a8%e7%a8%8b%e5%bc%8f\/"},"modified":"2026-03-30T14:41:08","modified_gmt":"2026-03-30T06:41:08","slug":"%e3%80%90%e7%ad%86%e8%a8%98-laravel%e3%80%91%e5%bb%ba%e7%ab%8b%e7%ac%ac%e4%b8%80%e6%94%aflaravel%e6%87%89%e7%94%a8%e7%a8%8b%e5%bc%8f","status":"publish","type":"post","link":"https:\/\/fgchen.com\/wpedu\/2020\/05\/%e3%80%90%e7%ad%86%e8%a8%98-laravel%e3%80%91%e5%bb%ba%e7%ab%8b%e7%ac%ac%e4%b8%80%e6%94%aflaravel%e6%87%89%e7%94%a8%e7%a8%8b%e5%bc%8f\/","title":{"rendered":"\u3010\u7b46\u8a18-Laravel\u3011\u5efa\u7acb\u7b2c\u4e00\u652fLaravel\u61c9\u7528\u7a0b\u5f0f"},"content":{"rendered":"<h1>\u6839\u64da\uff1a<a href=\"https:\/\/laravel-news.com\/your-first-laravel-application\" target=\"_blank\" rel=\"noopener noreferrer\">Laravel Tutorial: Step by Step Guide to Building Your First Laravel Application<\/a><\/h1>\n\n<ul>\n    <li>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">composer create-project --prefer-dist laravel\/laravel links \"7.*\"<\/pre>\n&nbsp;<\/li>\n    <li>\u4f7f\u7528phpMyAdmin\u5efa\u7acb\u4e00\u500b\u4f9b\u5c08\u6848\u4f7f\u7528\u7684\u8cc7\u6599\u5eab\u3001\u4f7f\u7528\u8005\u5e33\u865f\/\u5bc6\u78bc\uff0c\u4fee\u6539.env<\/li>\n<\/ul>\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">DB_CONNECTION=mysql\nDB_HOST=127.0.0.1\nDB_PORT=3306\nDB_DATABASE=wellsche_lara_links\nDB_USERNAME=wellsche_laravel\nDB_PASSWORD=XXXXXXXX<\/pre>\n\n<ul>\n    <li>php artisan migrate<\/li>\n<\/ul>\n\n\u9047\u5230\u932f\u8aa4\uff0c\u8aaa\u662fkey\u7684\u9577\u5ea6\u8d85\u904e\uff0c\u4fee\u6539 app\/Providers\/AppServiceProvider.php\uff0c\u52a0\u4e0a\uff1a\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">use IlluminateSupportFacadesSchema;\n(\u7565)\npublic function boot()\n{\n    \/\/\n    Schema::defaultStringLength(191);\n}<\/pre>\n\n<ul>\n    <li>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">composer require laravel\/ui\nphp artisan ui bootstrap --auth<\/pre>\n&nbsp;<\/li>\n<\/ul>\n\n\u63d0\u793a\uff1aPlease run &#8220;npm install &amp;&amp; npm run dev&#8221; to compile your fresh scaffolding.\n\n<ul>\n    <li>npm install &amp;&amp; npm run dev<\/li>\n    <li>php artisan make:migration create_links_table &#8211;create=links<\/li>\n<\/ul>\n\n\u4e0a\u9762\u6307\u4ee4\u6703\u5728database\/migrations\u4e0b\u7522\u751f\u4e00\u500b{{\u5efa\u7acb\u65e5\u671f}}_create_links_table.php\uff0c\u7de8\u8f2f\u9019\u500b\u6a94\u6848\uff0c\u4fee\u6539\/\u52a0\u5165up\u65b9\u6cd5\uff1a\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">Schema::create('links', function (Blueprint $table) {\n      $table-&gt;increments('id');\n      $table-&gt;string('title');\n      $table-&gt;string('url')-&gt;unique();\n      $table-&gt;text('description');\n      $table-&gt;timestamps();\n});<\/pre>\n\n\u57f7\u884c\uff1a\n\n<ul>\n    <li>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">php artisan migrate\nphp artisan migrate:fresh\nphp artisan make:model --factory Link<\/pre>\n&nbsp;<\/li>\n    <li><\/li>\n    <li>\u7de8\u8f2f database\/factories\/LinkFactory.php\uff0c\u52a0\u5165\uff1a<\/li>\n<\/ul>\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">&lt;?php\n\/** @var IlluminateDatabaseEloquentFactory $factory *\/\nuse AppLink;\nuse FakerGenerator as Faker;\n$factory-&gt;define(Link::class, function (Faker $faker) {\n    return [\n        'title' =&gt; substr($faker-&gt;sentence(2), 0, -1),\n        'url' =&gt; $faker-&gt;url,\n        'description' =&gt; $faker-&gt;paragraph,\n    ];\n});<\/pre>\n\n\u4e0a\u9762\u4f7f\u7528$faker-&gt;sentence()\u65b9\u6cd5\u7522\u751f\u4e00\u500b\u6a19\u984c\uff0c\u4e26\u7528substr\u53bb\u9664\u53e5\u5b50\u6700\u5f8c\u7684\u9ede\u3002\n\n<ul>\n    <li>php artisan make:seeder LinksTableSeeder<\/li>\n    <li>\u7de8\u8f2f database\/seeds\/LinksTableSeeder.php\uff0c\u52a0\u5165\uff1a<\/li>\n<\/ul>\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">public function run()\n{\n    factory(AppLink::class, 5)-&gt;create();\n}<\/pre>\n\n<ul>\n    <li>\u7de8\u8f2fdatabase\/seeds\/DatabaseSeeder.php\uff0c\u52a0\u5165\uff1a<\/li>\n<\/ul>\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">public function run()\n{\n    $this-&gt;call(LinksTableSeeder::class);\n}<\/pre>\n\n\u4e0a\u9762\u7684\u65b9\u6cd5\u7528\u4f86\u555f\u7528LinksTableSeeder\u3002\n\n<ul>\n    <li>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">php artisan migrate:fresh --seed<\/pre>\n&nbsp;<\/li>\n<\/ul>\n\n\u5c0dlinks\u8cc7\u6599\u8868\u683c\u63d2\u5165\u4e00\u7b46\u8cc7\u6599\uff1a\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"sql\">INSERT INTO `links` (`id`, `title`, `url`, `description`, `created_at`, `updated_at`) VALUES\n(1, 'Rerum doloremque', 'http:\/\/russel.info\/suscipit-et-iste-debitis-beatae-repudiandae-eveniet.html', 'Dolorem voluptas voluptatum voluptatem consequuntur amet dolore odit. Asperiores ullam alias vel soluta ut in. Facere quia et sit laudantium culpa ea possimus.', '2020-04-04 16:44:33', '2020-04-04 16:44:33');<\/pre>\n\n<h3>Routing and Views<\/h3>\n\n\u7de8\u8f2f routes\/web.php\uff0c\u539f\u672c\u5167\u5bb9\u70ba\uff1a\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\">Route::get('\/', function () {\n    return view('welcome');\n});\n<\/pre>\n\n\u6539\u70ba\uff1a\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\">Route::get('\/', function () {\n    $links = AppLink::all();\n    return view('welcome', ['links' =&gt; $links]);\n});<\/pre>\n\n&nbsp;\n\u63a5\u8457\u7de8\u8f2f resources\/views\/welcome.blade.php\uff0c\u52a0\u5165\u4e00\u500b\u8ff4\u5708\u6558\u5708\u4f86\u986f\u793a\u6240\u6709\u7684\u9023\u7d50\uff1a\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">@foreach ($links as $link)\n    &lt;a href=\"{{ $link-&gt;url }}\"&gt;{{ $link-&gt;title }}&lt;\/a&gt;\n@endforeach<\/pre>\n\n\u6574\u500bwelcome.blade.php\u7a0b\u5f0f\u78bc\u5217\u8868\u5982\u4e0b\uff1a\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\">&lt;body&gt;\n    &lt;div class=\"flex-center position-ref full-height\"&gt;\n        @if (Route::has('login'))\n            &lt;div class=\"top-right links\"&gt;\n                @auth\n                    &lt;a href=\"{{ url('\/home') }}\"&gt;Home&lt;\/a&gt;\n                @else\n                    &lt;a href=\"{{ route('login') }}\"&gt;Login&lt;\/a&gt;\n                    &lt;a href=\"{{ route('register') }}\"&gt;Register&lt;\/a&gt;\n                @endauth\n            &lt;\/div&gt;\n        @endif\n        &lt;div class=\"content\"&gt;\n            &lt;div class=\"title m-b-md\"&gt;\n                Laravel\n            &lt;\/div&gt;\n            &lt;div class=\"links\"&gt;\n                @foreach ($links as $link)\n                    &lt;a href=\"{{ $link-&gt;url }}\"&gt;{{ $link-&gt;title }}&lt;\/a&gt;\n                @endforeach\n            &lt;\/div&gt;\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;<\/pre>\n\n\u555f\u52d5Web\u670d\u52d9\uff1a\n\n<ul>\n    <li>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">php artisan serve --host=fgchen.com<\/pre>\n\u7d50\u679c\u986f\u793a\uff1aLaravel development server started: http:\/\/fgchen.com:8001\u00a0 \u00a0(\u4e0d\u4e00\u5b9a\u662f8001\uff0c\u6703\u5f9e8000\u958b\u59cb\u627e\u6c92\u7528\u7684\u57e0)<\/li>\n<\/ul>\n\n\u770b\u5230\u5e95\u4e0b\u7684\u756b\u9762\uff1a\n<a href=\"https:\/\/fgchen.com\/wp\/wp-content\/uploads\/2020\/05\/laravel-link-web-1.png\"><img fetchpriority=\"high\" decoding=\"async\" class=\"size-full wp-image-16184 alignnone\" src=\"https:\/\/fgchen.com\/wp\/wp-content\/uploads\/2020\/05\/laravel-link-web-1.png\" alt=\"\" width=\"913\" height=\"601\" \/><\/a>\nlinks\u8cc7\u6599\u8868\u683c\u7684\u5167\u5bb9 \uff1a\n<a href=\"https:\/\/fgchen.com\/wp\/wp-content\/uploads\/2020\/05\/laravel-link-data.png\"><img decoding=\"async\" class=\"alignnone wp-image-16185\" src=\"https:\/\/fgchen.com\/wp\/wp-content\/uploads\/2020\/05\/laravel-link-data.png\" alt=\"\" width=\"1380\" height=\"443\" \/><\/a>\n&nbsp;\n\n<h2>\u88fd\u4f5c\u986f\u793a\u9023\u7d50\u65b0\u589e\u8868\u55ae<\/h2>\n\n<ul>\n    <li>\u7de8\u8f2froutes\/web.php\uff0c\u52a0\u5165submit\u9801\u9762\u8def\u7531\uff1a<\/li>\n<\/ul>\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">Route::get('\/submit', function () {\n    return view('submit');\n});<\/pre>\n\n<ul>\n    <li>\u5efa\u7acbresources\/views\/submit.blade.php\uff0c\u5167\u5bb9\uff1a<\/li>\n<\/ul>\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\">@extends('layouts.app')\n@section('content')\n    &lt;div class=\"container\"&gt;\n        &lt;div class=\"row\"&gt;\n            &lt;h1&gt;Submit a link&lt;\/h1&gt;\n        &lt;\/div&gt;\n        &lt;div class=\"row\"&gt;\n            &lt;form action=\"\/submit\" method=\"post\"&gt;\n                @csrf\n                @if ($errors-&gt;any())\n                    &lt;div class=\"alert alert-danger\" role=\"alert\"&gt;\n                        Please fix the following errors\n                    &lt;\/div&gt;\n                @endif\n                &lt;div class=\"form-group\"&gt;\n                    &lt;label for=\"title\"&gt;Title&lt;\/label&gt;\n                    &lt;input type=\"text\" class=\"form-control @error('title') is-invalid @enderror\" id=\"title\" name=\"title\" placeholder=\"Title\" value=\"{{ old('title') }}\"&gt;\n                    @error('title')\n                        &lt;div class=\"invalid-feedback\"&gt;{{ $message }}&lt;\/div&gt;\n                    @enderror\n                &lt;\/div&gt;\n                &lt;div class=\"form-group\"&gt;\n                    &lt;label for=\"url\"&gt;Url&lt;\/label&gt;\n                    &lt;input type=\"text\" class=\"form-control @error('url') is-invalid @enderror\" id=\"url\" name=\"url\" placeholder=\"URL\" value=\"{{ old('url') }}\"&gt;\n                    @error('url')\n                        &lt;div class=\"invalid-feedback\"&gt;{{ $message }}&lt;\/div&gt;\n                    @enderror\n                &lt;\/div&gt;\n                &lt;div class=\"form-group\"&gt;\n                    &lt;label for=\"description\"&gt;Description&lt;\/label&gt;\n                    &lt;textarea class=\"form-control @error('description') is-invalid @enderror\" id=\"description\" name=\"description\" placeholder=\"description\"&gt;{{ old('description') }}&lt;\/textarea&gt;\n                    @error('description')\n                        &lt;div class=\"invalid-feedback\"&gt;{{ $message }}&lt;\/div&gt;\n                    @enderror\n                &lt;\/div&gt;\n                &lt;button type=\"submit\" class=\"btn btn-primary\"&gt;Submit&lt;\/button&gt;\n            &lt;\/form&gt;\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n@endsection\n<\/pre>\n\n<ul>\n    <li>\u700f\u89bd\u5668\u8f38\u5165\uff1ahttp:\/\/fgchen.com:8001\/submit (\u57e0\u865f\u4e0d\u4e00\u5b9a\u662f8001)<\/li>\n<\/ul>\n\n<a href=\"https:\/\/fgchen.com\/wp\/wp-content\/uploads\/2020\/05\/laravel-link-submit.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-16204\" src=\"https:\/\/fgchen.com\/wp\/wp-content\/uploads\/2020\/05\/laravel-link-submit.png\" alt=\"\" width=\"1067\" height=\"611\" \/><\/a>\n\u5e95\u4e0b\uff0c\u8aaa\u660e\u4e0a\u9762\u7a0b\u5f0f\u78bc\u4e3b\u8981\u7684\u91cd\u9ede\u3002\nblade\u6a21\u677f\u689d\u4ef6\u7528\u4f86\u6e2c\u8a66\u6aa2\u9a57\u662f\u5426\u6709\u4efb\u4f55\u9a57\u8b49\u4e0a\u7684\u932f\u8aa4\uff0c\u4e00\u65e6\u6709\u932f\u8aa4\u767c\u751f\uff0c\u555f\u52d5\u7a0b\u5f0f\u6703\u767c\u51fa\u8b66\u544a\u8a0a\u606f\uff0c\u63d0\u793a\u4f7f\u7528\u8005\u5fc5\u9700\u4fee\u6b63\u4e0d\u5408\u6cd5\u7684\u8868\u55ae\u6b04\u4f4d\u503c\u3002\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\">@if ($errors-&gt;any())\n    &lt;div class=\"alert alert-danger\" role=\"alert\"&gt;\n        Please fix the following errors\n    &lt;\/div&gt;\n@endif<\/pre>\n\n\u6bcf\u4e00\u500b\u8868\u55ae\u6b04\u4f4d\u9032\u884c\u6709\u6548\u6027\u7684\u6aa2\u67e5\uff0c\u5982\u679c\u6709\u932f\u8aa4\u6703\u986f\u793a\u4e00\u500b\u932f\u8aa4\u8a0a\u3001\u4e26\u4e14\u8f38\u51fa\u4e00\u500b<code>\"has-error\"\u985e\u5225<\/code>: (\u9019\u662fBootstrap\u7684\u8868\u55ae\u9a57\u8b49\u6a5f\u5236\uff0c\u8acb\u53c3\u8003<a href=\"https:\/\/www.w3schools.com\/bootstrap\/bootstrap_forms.asp\" target=\"_blank\" rel=\"noopener noreferrer\">Bootstrap Forms<\/a>\u6587\u4ef6\u8aaa\u660e)\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\">&lt;div class=\"form-group\"&gt;\n    &lt;label for=\"title\"&gt;Title&lt;\/label&gt;\n    &lt;input type=\"text\" class=\"form-control @error('title') is-invalid @enderror\" id=\"title\" name=\"title\" placeholder=\"Title\" value=\"{{ old('title') }}\"&gt;\n    @error('title')\n        &lt;div class=\"invalid-feedback\"&gt;{{ $message }}&lt;\/div&gt;\n    @enderror\n&lt;\/div&gt;<\/pre>\n\n\u5982\u679c\u4f7f\u7528\u9001\u51fa\u7121\u6548\u7684\u8cc7\u6599\uff0c\u7db2\u9801\u8def\u7531\u6703\u5132\u5b58\u6b64\u6703\u8b70\u968e\u6bb5\u7684\u9a57\u8b49\uff0c\u4e26\u4e14\u5c07\u4f7f\u7528\u8005\u91cd\u5c0e\u5411\u56de\u5230\u8a72\u8868\u55ae\u3002<code>{{ old('title') }}<\/code>\u6703\u586b\u4e0a\u539f\u5148\u9001\u51fa\u7684\u8cc7\u6599\uff0c\u5982\u679c\u4e00\u500b\u4f7f\u7528\u8005\u5fd8\u4e86\u586b\u5beb\u67d0\u4e9b\u6b04\u4f4d\uff0c\u90a3\u9ebc\u9019\u4e9b\u5df2\u586b\u7684\u6b04\u4f4d\u8cc7\u6599\u6703\u5728\u9a57\u8b49\u932f\u8aa4\u8207\u986f\u793a\u932f\u8aa4\u8a0a\u606f\u5f8c\u586b\u5beb\u81f3\u91cd\u5c0e\u5411\u5f8c\u7684\u8868\u55ae\u3002\n\u5982\u679c\u4e00\u500b\u6b04\u4f4d\u8cc7\u6599\u51fa\u73fe\u932f\u8aa4\uff0c<code><span class=\"\">@err<\/span>or<\/code>\u6307\u793a\u5668(directive)\u63d0\u4f9b\u4e00\u500b\u932f\u8aa4\u8a0a\u606f\u8b8a\u6578\uff0c\u8b93\u4f60\u5728\u8a72\u6307\u793a\u5668\u5340\u584a\u4e2d\u4f7f\u7528:\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">@error('title')\n    &lt;div class=\"invalid-feedback\"&gt;{{ $message }}&lt;\/div&gt;\n@enderror<\/pre>\n\n\u53e6\u5916\u4e00\u7a2e\u7528\u4f86\u6aa2\u67e5\u8207\u986f\u793a\u932f\u8aa4\u7684\u65b9\u5f0f\u662f\u4f7f\u7528 <code>$error<\/code> \u8b8a\u6578\uff0c\u53ef\u5728\u4e00\u500b\u9a57\u8b49\u5931\u6548\u8207\u91cd\u5c0e\u5411\u5f8c\u7528\u65bcview\u4e2d:\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">@if($errors-&gt;has('title'))\n    &lt;div class=\"invalid-feedback\"&gt;{{ $errors-&gt;first('title') }}&lt;\/div&gt;\n@endif<\/pre>\n\n<code><span class=\"\">@<\/span>error<\/code> \u6307\u793a\u5668\u4e5f\u4f7f\u7528\u4e86\u76f8\u540c\u7684\u8b8a\u6578\uff0c\u4e8c\u7a2e\u65b9\u5f0f\u4f60\u53ef\u4ee5\u81ea\u5df1\u7684\u559c\u597d\uff0c\u9078\u64c7\u4f7f\u7528\u3002\n\n<h2>Submitting the Form\/\u9001\u51fa\u8868\u55ae\u5167\u5bb9<\/h2>\n\n\u4e00\u5207\u5c31\u7dd2\u5f8c\uff0c\u6211\u5011\u6e96\u5099\u5c07\u8868\u55ae\u5167\u5bb9\u9001\u51fa\uff0c\u518d\u6b21\u7de8\u8f2froutes\/web.php\u9019\u500b\u6a94\u6848\uff0c\u65b0\u589e\u9801\u9762\u8def\u7531\u4f86\u9032\u884cPOST\u7684\u9700\u6c42\uff1a\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">use IlluminateHttpRequest;\nRoute::post('\/submit', function (Request $request) {\n    $data = $request-&gt;validate([\n        'title' =&gt; 'required|max:255',\n        'url' =&gt; 'required|url|max:255',\n        'description' =&gt; 'required|max:255',\n    ]);\n    $link = tap(new AppLink($data))-&gt;save();\n    return redirect('\/');\n});<\/pre>\n\n\u6ce8\u610f\uff1aIlluminateHttpRequest \u9019\u884c\u52d9\u5fc5\u8981\u653e\u5728web.php\u9802\u7aef\u8655\u3002\n\u9019\u500b\u8def\u7531\u76f8\u8f03\u65bc\u5176\u4ed6\u7684\u8def\u7531\u8907\u96dc\u4e86\u4e9b\u3002\n\u9996\u5148\uff0c\u6211\u5011\u6ce8\u5165\u4e86 IlluminateHttpRequest\u7269\u4ef6, \u6b64\u7269\u4ef6\u7528\u4f86\u8655\u7406POST\u6a5f\u5236\u7684\u8cc7\u6599\uff0c\u4ee5\u53caPOST\u9700\u6c42\u7684\u5176\u4ed6\u8cc7\u6599\u3002\n\u63a5\u8457\uff0c\u6211\u5011\u4f7f\u7528\u4e86\u8a72\u9700\u6c42\u7684 <code>validate()<\/code> \u65b9\u6cd5\u4f86\u9a57\u8b49\u8a72\u8868\u55ae\u8cc7\u6599\uff0c\u9019\u500b\u9a57\u8b49\u7684\u8868\u55ae\u65b9\u6cd5\u51fa\u73fe\u65bc<a href=\"https:\/\/laravel-news.com\/laravel-5-5\">Laravel 5.5<\/a>\uff0c\u9019\u662f\u4e00\u500b\u5f88\u597d\u7684\u5feb\u6377\u65b9\u6cd5\u4f86\u4f7f\u7528\u9a57\u8b49\u7684\u5176\u4ed6\u65b9\u6cd5\uff0c\u984d\u5916\u7684\u597d\u8655\u662f\u9a57\u8b49\u7684\u6b04\u4f4d\u5132\u5b58\u5728\u56de\u50b3\u8b8a\u6578\u00a0<code>$data<\/code> \u4e2d\uff0c\u6211\u5011\u53ef\u4ee5\u7528\u4ed6\u5011\u4f86\u6f14\u5316\u6211\u5011\u7684\u6a21\u578b\u3002\n\u6211\u5011\u9700\u8981\u4e09\u500b\u6b04\u4f4d\uff0c\u4e26\u4e14\u4f7f\u7528\u7ba1\u7dda\u5b57\u5143\/pipe character (&#8216;|&#8217;)\u5b9a\u7fa9\u591a\u7d44\u7684\u898f\u5247(\u4e5f\u5c31\u662f\u898f\u5247\u7684\u7d44\u5408)\u3002 we can define multiple rules. \u9019\u4e09\u500b\u6b04\u4f4d\u6240\u80fd\u5b9a\u7fa9\u7684\u898f\u5247\u6700\u5927\u9577\u5ea6\u70ba255\u500b\u5b57\u5143\uff0c<code>url<\/code>\u6b04\u4f4d\u9700\u8981\u4e00\u500b\u6709\u6548\u7684URL(\u8d85\u9023\u7d50)\u3002\n\u5982\u679c\u9a57\u8b49\u5931\u6557\uff0c\u4e00\u500b\u4f8b\u5916\u6703\u88ab\u4e1f\u51fa\u4f86\uff0c\u7db2\u9801\u8def\u7531\u6703\u56de\u50b3\u7d66\u4f7f\u7528\u8005\u539f\u59cb\u7684\u8f38\u5165\u8cc7\u6599\uff0c\u4ee5\u53ca\u9a57\u8b49\u7684\u932f\u8aa4\u3002\n\u63a5\u8457\uff0c\u6211\u5011\u4f7f\u7528<code>tap()<\/code>\u8f14\u52a9\u65b9\u6cd5\u4f86\u5efa\u7acb\u4e00\u500b\u65b0\u7684 <code>Link<\/code> \u6a21\u578b\u7269\u4ef6\/\u5be6\u4f8b\uff0c\u4e26\u5132\u5b58\u8d77\u4f86\u3002\u4f7f\u7528 tap \u53ef\u4ee5\u5141\u8a31\u6211\u5011\u4f86\u559a\u7528l <code>save()<\/code> \uff0c\u4e26\u4e14\uff0c\u5728\u5132\u5b58\u5b8c\u6210\u5f8c\uff0c\u56de\u50b3\u8a72\u6a21\u578b\u7269\u4ef6\u3002\n\u4e00\u822c\u4f86\u8aaa\uff0c\u6211\u5011\u5fc5\u9808\u5728\u6c92\u6709\u4f7f\u7528tap\u7684\u60c5\u6cc1\u4e0b\u57f7\u884c\u4e0b\u5217\u5de5\u4f5c\uff0c\u53ea\u662f\u589e\u52a0\u4e86\u4e00\u4e9b\u4e9b\u7684\u8a9e\u6cd5\u7cd6 (\u4ec0\u9ebc\u662f\u8a9e\u6cd5\u7cd6\uff1fGoogle\u4e00\u4e0b\u4ed6\u7684\u610f\u6db5\u3002)\u3002\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">$link = new AppLink($data);\n$link-&gt;save();\nreturn $link;<\/pre>\n\n\u5982\u679c\u4f60\u8981\u4f7f\u7528\u8cc7\u6599(data)\u4f86\u6f14\u5316\u4e00\u500b\u65b0\u7684\u6a21\u578b\uff0c\u6211\u5011\u53ef\u900f\u904e&#8221;\u6279\u91cf\u8ce6\u503c(mass assignment)&#8221;\u7684\u65b9\u5f0f\u5141\u8a31\u6240\u6709\u7684\u6b04\u4f4d\u53ef\u4ee5\u662f&#8221;fillable&#8221;\u7684\u3002 &#8220;fillable&#8221;\u5c6c\u6027\u7684\u8a2d\u7f6e\u76ee\u7684\u53ef\u4ee5\u7528\u4f86\u907f\u514d\u6b04\u4f4d\u88ab\u5927\u91cf\u7684\u8ce6\u4e88\u4e0d\u662f\u4f60\u5728\u9663\u5217\u88cf\u6240\u6307\u5b9a\u7684\u90a3\u4e9b\u8cc7\u6599\u3002 (\u4ee5\u5f8c\u770b\u5230\u5be6\u4f8b\u518d\u4f86\u88dc\u5145\u8aaa\u660e\u3002)\nmass assignment \u88ab\u7ffb\u8b6f\u6210\u6279\u91cf\u8ce6\u503c\uff0c\u53c3\u8003\u6587\u7ae0\uff1a<a href=\"https:\/\/codertw.com\/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80\/206570\/\" target=\"_blank\" rel=\"noopener noreferrer\">Laravel\u4e2d\u6279\u91cf\u8ce6\u503cMass-Assignment\u7684\u771f\u6b63\u542b\u7fa9\u8a73\u89e3<\/a>\n\u601d\u8003\u4e00\u5206\u9418\uff1a\u5728\u8cc7\u6599\u6a21\u578b\u4e2d\uff0c\u6211\u5011\u6b63\u5f9e\u90a3\u4e9b\u8acb\u6c42\u8207\u6279\u91cf\u8ce6\u503c\u53d6\u5f97\u4f7f\u7528\u7684\u8f38\u5165\uff0c\u9700\u5c0d\u4f7f\u7528\u8005\u8f38\u5165\u8cc7\u6599\u7684\u5371\u96aa\u6027\u6709\u6240\u5bdf\u77e5\uff0c\u907f\u514d\u4f7f\u7528\u8005\u76f4\u63a5\u900f\u904e\u4e00\u500b\u8868\u55ae\u4f86\u64cd\u5f04\u8cc7\u6599\u3002\n\u5728\u6211\u5011\u7684\u4f8b\u5b50\u4e2d\uff0c\u6211\u5011\u9a57\u8b49\u6bcf\u4e00\u500b\u6b04\u4f4d\uff0c\u4f7f\u5f97\u9019\u4e9b\u6b04\u4f4d\u5728\u6279\u91cf\u8ce6\u503c\u7684\u904e\u7a0b\u4e2d\u662f\u5b89\u5168\u7684\u3002\u70ba\u4e86\u5141\u8a31\u6211\u5011\u7684\u6a21\u578b\u53ef\u4ee5\u6307\u5b9a\u503c\u7d66\u90a3\u4e9b\u6b04\u4f4d\uff0c\u958b\u555f\u00a0<code>app\/Link.php<\/code> \u6a94\u6848\uff0c\u4e26\u4e14\u900f\u904e\u66f4\u65b0\u9032\u884c\u6aa2\u8996\uff0c\u50cf\u5e95\u4e0b\u7684\u65b9\u5f0f\uff1a\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\">&lt;?php\nnamespace App;\nuse IlluminateDatabaseEloquentModel;\nclass Link extends Model\n{\n    protected $fillable = [\n        'title',\n        'url',\n        'description'\n    ];\n}<\/pre>\n\n\u5982\u679c\u4e0d\u8981\u6279\u91cf\u8ce6\u503c\uff0c\u53ef\u4ee5\u50cf\u6211\u5011\u5e95\u4e0b\u7684\u7a0b\u5f0f\u78bc\u9032\u884c\uff1a\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">$data = $request-&gt;validate([\n    'title' =&gt; 'required|max:255',\n    'url' =&gt; 'required|url|max:255',\n    'description' =&gt; 'required|max:255',\n]);\n$link = new AppLink;\n$link-&gt;title = $data['title'];\n$link-&gt;url = $data['url'];\n$link-&gt;description = $data['description'];\n\/\/ Save the model\n$link-&gt;save();<\/pre>\n\n\u6700\u5f8c\u4e00\u4ef6\u6211\u5011\u5728POST\u8def\u7531\u6240\u505a\u7684\u4e8b\u60c5\uff1a\u5728\u5132\u5b58\u9023\u7d50\u6210\u529f\u4e4b\u5f8c\uff0c\u8b93\u4f7f\u7528\u8005\u9801\u9762\u5c0e\u5411\u56de\u5230\u9996\u9801\u3002\n\u6b64\u6642\u6b64\u523b\uff0c\u6211\u5011\u7684\u8868\u55ae\u61c9\u8a72\u8981\u907f\u514d\u9001\u51fa\u90a3\u4e9b\u5305\u542b\u6709\u4e0d\u5408\u6cd5\u6b04\u4f4d\u7684\u9023\u7d50\u3002\n\u5982\u679c\u8a72\u8868\u55ae\u901a\u904e\u9a57\u8b49\uff0c\u8cc7\u6599\u61c9\u8a72\u88ab\u5132\u5b58\u5728\u8cc7\u6599\u5eab\u4e2d\uff0c\u7136\u5f8c\u4f7f\u7528\u8005\u9801\u9762\u88ab\u5c0e\u5411\u81f3\u9996\u9801\u3002\n\n<h2>Testing the Form Submission\/\u6e2c\u5f0f\u8868\u55ae\u7684\u63d0\u4ea4<\/h2>\n\n\u6211\u5011\u6709\u4e00\u500b\u57fa\u672c\u7684\u5de5\u4f5c\u8868\u55ae\uff0c\u4f46\u662f\u6211\u5011\u61c9\u8a72\u78ba\u5b9a\u7684\u662f\u6301\u7e8c\u5730\u9032\u884c\u6e2c\u8a66\u7684\u64b0\u5beb\u3002We have a basic working form, but we should make sure it continues to work by writing tests.\nLaravel\u628aHTTP\u6e2c\u8a66\u8b8a\u6210\u4e00\u4ef6\u5bb9\u6613\u7684\u4e8b\uff0c\u90a3\u4e9b\u662f\u5728\u8def\u7531\u8207\u4e2d\u4ecb\u8edf\u9ad4\u4e0a\u7684\u6574\u5408\u6e2c\u8a66\u57f7\u884c\uff0c\u6211\u5011\u5beb\u4e86\u5c11\u91cf\u7684\u529f\u80fd\u6e2c\u8a66\u4f86\u9a57\u8b49\u6211\u5011\u7684\u7a0b\u5f0f\u78bc\u662f\u5982\u5982\u9810\u671f\u5730\u5de5\u4f5c\u3002\n\u5728\u6211\u5011\u958b\u59cb\u4e4b\u524d\uff0c\u6211\u5011\u9700\u8981\u5c0d\u5728\u00a0<code>phpunit.xml<\/code>\u00a0 \u6a94\u6848\u88cf\u7684\u4e8b\u60c5\u9032\u884c\u4e9b\u5fae\u7684\u8abf\u6574\uff0c\u6211\u5011\u4e5f\u624d\u597d\u4f7f\u7528\u4e00\u500b\u5167\u7f6e\u65bc\u8a18\u61b6\u9ad4\u4e2d\u7684SQLite\u8cc7\u6599\u5eab\u7ba1\u7406\u7cfb\u7d71\u3002\u4f60\u5fc5\u9808\u78ba\u4fdd\u9069\u7576\u7684PHP\u6a21\u7d44\u662f\u5df2\u5b89\u88dd\u597d\u7684\u3002\n\u5728Laravel 7, \u5c08\u6848\u7684<code>phpunit.xml<\/code> \u6a94\u6848\u7d44\u8a2d\u5b9a\u4e86\u4e00\u500b\u5167\u7f6e\u65bc\u8a18\u61b6\u9ad4\u4e2d\u7684 SQLite\u8cc7\u6599\u5eab\u3002. \u5982\u679c\u4f60\u6b63\u5728\u4f7f\u7528\u4e00\u500b\u8f03\u820a\u7248\u672c\u7684Laravel\uff0c\u70ba\u4e86\u8b8a\u66f4\u8a72\u8cc7\u6599\u5eab\u7684\u9023\u7d50\uff0c\u4f60\u9700\u8981\u52a0\u4e0a\u5982\u4e0b\u7684\u64cd\u4f5c\uff1a:\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">&lt;php&gt;\n        &lt;!-- ... --&gt;\n    &lt;env name=\"DB_CONNECTION\" value=\"sqlite\"\/&gt;\n    &lt;env name=\"DB_DATABASE\" value=\":memory:\"\/&gt;\n        &lt;!-- ... --&gt;\n&lt;\/php&gt;<\/pre>\n\n\u63a5\u8457\uff0c\u79fb\u9664\u6389\u90a3\u4e9b\u4f34\u96a8\u8457Laravel\u800c\u4f86\u7684\u9810\u7f6e\u6e2c\u8a66\uff1a\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">rm tests\/Feature\/ExampleTest.php\n<\/pre>\n\n\u6211\u5011\u5df2\u7d93\u6e96\u5099\u597d\u8981\u900f\u904eHTTP\u8acb\u6c42\u4f86\u6e2c\u8a66<code>\/submit<\/code>\u8868\u55ae\uff0c\u76ee\u7684\u662f\u8981\u78ba\u4fdd\u9801\u9762\u8def\u7531\u7684\u9a57\u8b49\u3001\u5132\u5b58\u3001\u8207\u9801\u9762\u5c0e\u5411\u6b63\u5e38\u5de5\u4f5c\u3002\n\u9996\u5148\uff0c\u6211\u5011\u5efa\u7acb\u4e00\u500b\u65b0\u529f\u80fd\u6e2c\u8a66\u4f86\u770b\u770b\u9801\u9762\u8def\u7531\u662f\u5426\u6b63\u5e38\uff1a(First, let\u2019s create a new feature test to test against our route:)\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">php artisan make:test SubmitLinksTest\n<\/pre>\n\n\u9019\u500b\u547d\u4ee4\u4ee5\u9069\u7576\u7684\u76f8\u4f9d\u6027\u5efa\u7acb\u4e86\u4e00\u500b\u65b0\u7684\u6e2c\u8a66\u6a94\u6848\uff0c\u5176\u4e2d\u5305\u542b\u4e86\u4e00\u500b\u6211\u5011\u8981\u7528\u4f86\u9a57\u8b49\u6211\u5011\u6709\u6548\u7684\u9023\u7d50\u662f\u5426\u88ab\u5132\u5b58\u81f3\u8cc7\u6599\u5eab\u7684\u4e00\u500b<code>RefreshDatabase<\/code>\u3002 The command creates a new testing file with the proper dependencies, including a <code>RefreshDatabase<\/code> trait that we are going to use to verify that our links are being saved to the database when valid.\n\u958b\u555f\u65b0\u7684\u00a0 <code>tests\/Feature\/SubmitLinksTest.php<\/code> \u6a94\u6848\uff0c\u5728\u8a72\u985e\u5225\u4e2d\u5beb\u4e86\u5e7e\u500b\u9aa8\u67b6\u6e2c\u8a66 (let\u2019s define a few skeleton tests in the body of the class that we are going to flesh out):\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">\/** @test *\/\nfunction guest_can_submit_a_new_link() {}\n\/** @test *\/\nfunction link_is_not_created_if_validation_fails() {}\n\/** @test *\/\nfunction link_is_not_created_with_an_invalid_url() {}\n\/** @test *\/\nfunction max_length_fails_when_too_long() {}\n\/** @test *\/\nfunction max_length_succeeds_when_under_max() {}<\/pre>\n\nThese tests should give you a high-level overview of what we are going to test: \u9019\u4e9b\u6e2c\u8a66\u61c9\u8a72\u7d66\u4e86\u4f60\u5011\u4e00\u4e9b\u6211\u5011\u5373\u5c07\u9032\u884c\u6e2c\u8a66\u7684\u4e00\u500b\u5f9e\u9ad8\u5c64\u6b21\u7684\u6982\u89c0\n\u8a3b\uff1averification \u9a57\u8b49\u5efa\u9020\u51fa\u4f86\u7684\u4e8b\u7269\u662f\u5426\u6b63\u78ba\uff0cvalidation \u78ba\u8a8d\u88fd\u9020\u4e8b\u7269\u7684\u904e\u7a0b\u662f\u5426\u6b63\u78ba\u3002\u9019\u8ddf\u88fd\u9020\u696d\u7684QC\u8207QA\u6709\u985e\u4f3c\u7684\u610f\u6db5\uff0cQC\u7522\u54c1\u6b63\u78ba\u6027\u6aa2\u67e5\uff0cQA\u88fd\u9020\u904e\u7a0b\u7684\u6b63\u78ba\u78ba\u8a8d\u3002\n\n<ol>\n    <li>Verify that valid links get saved in the database \u9a57\u8b49\u5408\u6cd5\u7684\u9023\u7d50\u5132\u5b58\u5230\u8cc7\u6599\u5eab\u3002<\/li>\n    <li>When validation fails, links are not in the database \u7576\u5408\u6cd5\u6027\u78ba\u8a8d\u5931\u6557\uff0c\u9023\u7d50\u6c92\u6709\u5132\u5b58\u81f3\u8cc7\u6599\u5eab\u3002<\/li>\n    <li>Invalid URLs are not allowed \u975e\u6cd5\u7684URLs\u662f\u4e0d\u5141\u8a31\u7684<\/li>\n    <li>Validation should fail when the fields are longer than the\u00a0<code>max:255<\/code> validation rule \u7576\u6b04\u4f4d\u503c\u9577\u5ea6\u8d85\u904e255\u500b\u5b57\u5143\uff0c\u5408\u6cd5\u6027\u78ba\u8a8d\u4e0d\u901a\u904e\u3002<\/li>\n    <li>Validation should succeed when the fields are long enough according to\u00a0<code>max:255<\/code>. \u7576\u6b04\u4f4d\u503c\u9577\u5ea6\u5728255\u500b\u5b57\u5143\u5167\uff0c\u5408\u6cd5\u6027\u78ba\u8a8d\u901a\u904e\u3002<\/li>\n<\/ol>\n\n\u6211\u5011\u53ef\u80fd\u6703\u6f0f\u6389\u4e00\u4e9b\u4e8b\u60c5\uff0c\u4f46\u56e0\u70ba\u9019\u662f\u5beb\u7d66\u521d\u5165\u9580\u7684\u7b2c\u4e00\u652fLaravel\u61c9\u7528\uff0c\u9019\u88cf\u5c31\u53ea\u5217\u51fa\u4e00\u4e9b\u57fa\u672c\u7684HTTP\u6e2c\u8a66\u3002\n\n<h3>Saving a valid link\/\u5408\u898f\u9023\u7d50\u7684\u5132\u5b58<\/h3>\n\nThe first test we\u2019ll write is the test that verifies that valid data gets stored in the database: \u6211\u5011\u8981\u9032\u884c\u7684\u7b2c\u4e00\u500b\u6e2c\u8a66\u662f\u9a57\u8b49\u5408\u898f\u7684\u9023\u7d50\u5132\u5b58\u81f3\u8cc7\u6599\u5eab\u6b63\u78ba\u6027\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">&lt;?php\nnamespace TestsFeature;\nuse IlluminateValidationValidationException;\nuse TestsTestCase;\nuse IlluminateFoundationTestingRefreshDatabase;\nclass SubmitLinksTest extends TestCase\n{\n    use RefreshDatabase;\n    \/** @test *\/\n    function guest_can_submit_a_new_link()\n    {\n        $response = $this-&gt;post('\/submit', [\n            'title' =&gt; 'Example Title',\n            'url' =&gt; 'http:\/\/example.com',\n            'description' =&gt; 'Example description.',\n        ]);\n        $this-&gt;assertDatabaseHas('links', [\n            'title' =&gt; 'Example Title'\n        ]);\n        $response\n            -&gt;assertStatus(302)\n            -&gt;assertHeader('Location', url('\/'));\n        $this\n            -&gt;get('\/')\n            -&gt;assertSee('Example Title');\n    }\n}<\/pre>\n\nTake note of the\u00a0<code>RefreshDatabase<\/code>\u00a0trait which makes sure that each test has a new database to give each test a pristine database environment with all the migrations.\n<code>RefreshDatabase<\/code>\u7528\u4f86\u78ba\u4fdd\u6bcf\u4e00\u500b\u6e2c\u8a66\u90fd\u6709\u4e00\u500b\u539f\u59cb\u4e7e\u6de8\u7684\u8cc7\u6599\u5eab\u74b0\u5883\u3002\nOur first test submits valid post data, which returns a response object that we can use to assert that our route responded as expected. We verify that the database contains a record with the title we just created. \u6211\u5011\u7684\u7b2c\u4e00\u500b\u6e2c\u8a66\u63d0\u4ea4\u4e86\u5408\u898f\u7684post\u8cc7\u6599\uff0c\u7576\u4e2d\u56de\u50b3\u4e86\u4e00\u500b\u56de\u61c9\u7269\u4ef6\uff0c\u900f\u904e\u9019\u500b\u56de\u61c9\u7269\u4ef6\uff0c\u6211\u5011\u53ef\u4ee5\u6e2c\u8a66\u6211\u5011\u7684\u8def\u7531\u56de\u61c9\u662f\u5426\u6b63\u5e38\u5de5\u4f5c\u3002\u6211\u5011\u9a57\u8b49\u8cc7\u6599\u5eab\u5305\u542b\u4e86\u4e00\u500b\u6211\u5011\u6240\u5efa\u7acb\u7684\u6a19\u984c\u8cc7\u6599\u3002\n\u5bcc\u570b\u8a3b\uff1aassert\uff0c\u9019\u500b\u55ae\u5b57\u53eb\u65b7\u8a00\uff0c\u5728Java\u88cf\uff0cassert\u7528\u4f86\u65b7\u8a00\u67d0\u4ef6\u4e8b\u5fc5\u7136\u767c\u751f\uff0c\u5982\u679c\u6c92\u6709\u767c\u751f\u2026\u5c31\u4e1f\u51fa\u4e00\u500bassertion error\u3002\n\u63a5\u8457\uff0c\u6211\u5011\u9a57\u8b49\u56de\u61c9\u662f\u4e00\u500b302\u72c0\u614b\u78bc\uff0c\u4ee5\u53ca\u4e00\u500b\u6307\u5411\u9996\u9801\u7684\u00a0<code>Location<\/code> \u6a94\u982d\u8cc7\u6599\u3002 Next, we verify that the response was a <code>302<\/code>\u00a0status code with a\u00a0<code>Location<\/code>\u00a0header pointing to the homepage.\n\u6700\u5f8c\uff0c\u6211\u5011\u56de\u5230\u9996\u9801\u78ba\u9023\u7d50\u662f\u5426\u6b63\u78ba\u5730\u88ab\u986f\u793a\u5728\u9996\u9801\u4e0a\u3002Last, we request the home page and verify that the link title is visible on the homepage.\n\u8b93\u6211\u5011\u57f7\u884c\u6211\u5011\u7684\u7b2c\u4e00\u500b\u6e2c\u8a66\u4f86\u78ba\u8a8d\u9810\u671f\u7684\u4e8b\u60c5\u662f\u5426\u901a\u904e\u9a57\u8b49\u3002Let\u2019s run our first test to make sure things pass as expected.\nLaravel 7 adds a new <code>artisan test<\/code>\u00a0command, or you can use\u00a0<code>phpunit<\/code>:\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">php artisan test\n# Or run phpunit directly\nvendor\/bin\/phpunit<\/pre>\n\nYou should see that the test suite passes: (\u756b\u9762\u8981\u52a0\u4e0a\u4f86)\n\n<h3>Testing Failed Validation<\/h3>\n\n\u7576\u4e00\u500b\u4f7f\u7528\u8005\u63d0\u4ea4\u4e00\u500b\u4e0d\u597d\u7684\u8cc7\u6599\uff0c\u6211\u5011\u9810\u671f\u9a57\u8b49\u904e\u7a0b\u6703\u89f8\u767c\u4e00\u500b\u4f8b\u5916\uff0c\u5f9e\u800c\u78ba\u8a8d\u6211\u5011\u7684\u78ba\u8a8d\u5c64\u6b21\u7684\u5de5\u4f5c\u60c5\u5f62\uff1a\nWhen a user generally submits bad data, we expect the validation to trigger an exception and we can use that to make sure our validation layer is working:\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">\/** @test *\/\nfunction link_is_not_created_if_validation_fails()\n{\n    $response = $this-&gt;post('\/submit');\n    $response-&gt;assertSessionHasErrors(['title', 'url', 'description']);\n}<\/pre>\n\n&nbsp;\n\u6211\u5011\u4f7f\u7528Laravel\u2019s\u00a0<code>assertSessionHasErrors()<\/code> \u4f86\u767c\u51fasession\u904e\u7a0b\u4e2d\u767c\u751f\u5408\u898f\u932f\u8aa4\u7684assertion\u3002\u56e0\u70ba\u6211\u5011\u63d0\u4ea4\u4e86\u7a7a\u8cc7\u6599\u7d66\u9801\u9762\u8def\u7531\uff0c\u6211\u5011\u9810\u671frequired\u898f\u5247\u6703\u89f8\u767c(\u9019\u82f1\u6587\u5beb\u5f97\u602a\uff0ctrigger\u5f8c\u9762\u6c92\u6709\u76ee\u6a19\u8a5e\u2026\u00a0 S V O)\nWe use Laravel\u2019s\u00a0<code>assertSessionHasErrors()<\/code>\u00a0to make sure that the session has validation errors for each of our required fields. Because we submitted empty data to the route, we expect the\u00a0<code>required<\/code>\u00a0rule will trigger for each field.\nLet\u2019s run the test suite to verify our work thus far:\n\u8b93\u6211\u5011\u57f7\u884c\u9019\u7cfb\u5217\u7684\u6e2c\u8a66\u4f86\u9a57\u8b49\u6211\u5011\u76ee\u524d\u70ba\u6b62\u7684\u5de5\u4f5c\uff1a\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">$ php artisan test tests\/Feature\/SubmitLinksTest\n   PASS  TestsFeatureSubmitLinksTest\n  \u2713 guest can submit a new link\n  \u2713 link is not created if validation fails\n  Tests:  2 passed\n  Time:   0.32s<\/pre>\n\n<h3>Testing URL Validation<\/h3>\n\nWe expect only valid URLs to pass validation so that our application doesn\u2019t try to display invalid data. \u6211\u5011\u9810\u671f\u50c5\u6709\u5408\u6cd5\u7684URLs\u53ef\u4ee5\u901a\u904e\u5408\u6cd5\u6027\u78ba\u8a8d\uff0c\u78ba\u4fdd\u6211\u5011\u7684\u61c9\u7528\u7a0b\u5f0f\u4e0d\u6703\u8a66\u8457\u986f\u793a\u4e0d\u5408\u6cd5\u7684\u8cc7\u6599\u3002\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">\/** @test *\/\nfunction link_is_not_created_with_an_invalid_url()\n{\n    $this-&gt;withoutExceptionHandling();\n    $cases = ['\/\/invalid-url.com', '\/invalid-url', 'foo.com'];\n    foreach ($cases as $case) {\n        try {\n            $response = $this-&gt;post('\/submit', [\n                'title' =&gt; 'Example Title',\n                'url' =&gt; $case,\n                'description' =&gt; 'Example description',\n            ]);\n        } catch (ValidationException $e) {\n            $this-&gt;assertEquals(\n                'The url format is invalid.',\n                $e-&gt;validator-&gt;errors()-&gt;first('url')\n            );\n            continue;\n        }\n        $this-&gt;fail(\"The URL $case passed validation when it should have failed.\");\n    }\n}<\/pre>\n\nLaravel has a\u00a0<code>withoutExceptionHandling()<\/code>\u00a0method which disables Laravel\u2019s route exception handling code used to generate an HTTP response after an exception.\n<code>withoutExceptionHandling()<\/code>\u65b9\u6cd5\u95dc\u9589\u9801\u9762\u8def\u7531\u7684\u4f8b\u5916\u8655\u7f6e\u3002\nWe use this to our advantage so we can inspect the validation exception object and assert against the error messages.\n\u6211\u5011\u8a2d\u7f6e\u4e26\u8d70\u8a2a\u6bcf\u4e00\u500b\u72c0\u6cc1(\u4f60\u53ef\u4ee5\u52a0\u4e0a\u4f60\u6240\u60f3\u8981\u6db5\u84cb\u7684\u60c5\u5883)\uff0c\u4e26\u5728\u6bcf\u4e00\u6b21\u7684\u8d70\u8a2a\u4e2d\u88dc\u6349<code>ValidationException<\/code>\u7269\u4ef6\u3002\nWe loop through various cases (add your own if you\u2019d like to cover more scenarios) and catch instances of\u00a0<code>ValidationException<\/code>. If the text makes it past the exception handling, we manually fail the test because we expect the route throws a\u00a0<code>ValidationExcepiton<\/code>\u00a0exception each time.\n\u5728catch\u5340\u584a\u4e2d\uff0c\u6211\u5011\u4f7f\u7528validator \u7269\u4ef6\u4f86\u6aa2\u67e5url\u932f\u8aa4\uff0c\u4e26\u4e14\u4e1f\u51fa\u5be6\u969b\u7684\u932f\u8aa4\u8a0a\u606f\u3002 The <code>catch<\/code>\u00a0block uses the validator object to check the\u00a0<code>url<\/code> error and asserts that the actual error message matches the expected validation error message.\nI like using the\u00a0<code>try\/catch<\/code>\u00a0technique, followed by a\u00a0<code>$this-&gt;fail()<\/code>\u00a0as a safety harness instead of using exception annotations provided by PHPUnit. Be sure to\u00a0<code>return<\/code>\u00a0in the caught exception to avoid confusing test failures. I feel catching the exception allows the ability to do assertions that wouldn\u2019t otherwise be possible and provides a more granular control that I like in most cases.\n\n<h3>Testing Max Length Validation<\/h3>\n\nWe will test a few scenarios with the\u00a0<code>max:255<\/code>\u00a0validations rules: when the field fails max-length validation with a length of\u00a0<code>256<\/code>\u00a0characters, and when the field is long enough to pass validation at\u00a0<code>255<\/code>\u00a0characters.\nAlthough Laravel contains the\u00a0<code>max<\/code>\u00a0validation rule functionality, I like to test it to verify that my application applies the rules. If someone removes the\u00a0<code>max<\/code>\u00a0validation rule, then the tests will catch it.\nI like to test the threshold of min and max validation rules as an extra caution to make sure my application respects the min and max boundaries I set.\nFirst, let\u2019s test the \u201cmax length\u201d scenario:\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">\/** @test *\/\nfunction max_length_fails_when_too_long()\n{\n    $this-&gt;withoutExceptionHandling();\n    $title = str_repeat('a', 256);\n    $description = str_repeat('a', 256);\n    $url = 'http:\/\/';\n    $url .= str_repeat('a', 256 - strlen($url));\n    try {\n        $this-&gt;post('\/submit', compact('title', 'url', 'description'));\n    } catch(ValidationException $e) {\n        $this-&gt;assertEquals(\n            'The title may not be greater than 255 characters.',\n            $e-&gt;validator-&gt;errors()-&gt;first('title')\n        );\n        $this-&gt;assertEquals(\n            'The url may not be greater than 255 characters.',\n            $e-&gt;validator-&gt;errors()-&gt;first('url')\n        );\n        $this-&gt;assertEquals(\n            'The description may not be greater than 255 characters.',\n            $e-&gt;validator-&gt;errors()-&gt;first('description')\n        );\n        return;\n    }\n    $this-&gt;fail('Max length should trigger a ValidationException');\n}<\/pre>\n\nAgain, we disable exception handling and create data that is one character too long to pass validation.\nWe assert each field to make sure they all have a max length validation error message.\nLast, we need to\u00a0<code>return<\/code>\u00a0in the caught exception and use the\u00a0<code>$this-&gt;fail()<\/code>\u00a0as a safety harness to fail the test.\nNext, we test the \u201cunder the max\u201d scenario:\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">\/** @test *\/\nfunction max_length_succeeds_when_under_max()\n{\n    $url = 'http:\/\/';\n    $url .= str_repeat('a', 255 - strlen($url));\n    $data = [\n        'title' =&gt; str_repeat('a', 255),\n        'url' =&gt; $url,\n        'description' =&gt; str_repeat('a', 255),\n    ];\n    $this-&gt;post('\/submit', $data);\n    $this-&gt;assertDatabaseHas('links', $data);\n}<\/pre>\n\nWe make the form data long enough to pass\u00a0<code>max:255<\/code>\u00a0validation and assert that the data is in the database after submitting the data.\nRun the test suite and make sure everything is passing:\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">$ php artisan test tests\/Feature\/SubmitLinksTest\n   PASS  TestsFeatureSubmitLinksTest\n  \u2713 guest can submit a new link\n  \u2713 link is not created if validation fails\n  \u2713 link is not created with an invalid url\n  \u2713 max length fails when too long\n  \u2713 max length succeeds when under max\n  Tests:  5 passed\n  Time:   0.58s<\/pre>\n\n<h1>\u5b8c\u7d50<\/h1>\n\n\u8a3b\uff1a\u5f8c\u53f0\u7d42\u7aef\u6a5f\u7684\u547d\u4ee4\u5217\u7de8\u8f2f\u5668\u6c92\u6709Windows\u8996\u7a97\u9019\u908a\u7684\u7de8\u8f2f\u597d\u7528\uff0c\u53ef\u4ee5\u7528\u865b\u64ec\u4e3b\u6a5f\u5f8c\u53f0\u7684\u7db2\u9801\u7248\u7684\u7de8\u8f2f\u5668\u6216\u8005\u76f4\u63a5\u900f\u904eBitvise-FTP\u958b\u555f\u6a94\u6848\uff0c\u4e26\u6307\u5b9a\u7528Atom\u4f86\u7de8\u8f2f\u9060\u7aef\u7684\u6a94\u6848\u3002\n<a href=\"https:\/\/fgchen.com\/wp\/wp-content\/uploads\/2020\/05\/laravel-link-web-2.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-16190\" src=\"https:\/\/fgchen.com\/wp\/wp-content\/uploads\/2020\/05\/laravel-link-web-2.png\" alt=\"\" width=\"1920\" height=\"1040\" \/><\/a>\n&nbsp;\n&nbsp;\n&nbsp;\n&nbsp;","protected":false},"excerpt":{"rendered":"<p>\u6839\u64da\uff1aLaravel Tutorial: Step by Step Guide  &hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_import_markdown_pro_load_document_selector":0,"_import_markdown_pro_submit_text_textarea":"","fifu_image_url":"","fifu_image_alt":"","footnotes":""},"categories":[266],"tags":[185],"class_list":["post-12146","post","type-post","status-publish","format-standard","hentry","category-266","tag-laravel"],"_links":{"self":[{"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/posts\/12146","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/comments?post=12146"}],"version-history":[{"count":1,"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/posts\/12146\/revisions"}],"predecessor-version":[{"id":13598,"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/posts\/12146\/revisions\/13598"}],"wp:attachment":[{"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/media?parent=12146"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/categories?post=12146"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fgchen.com\/wpedu\/wp-json\/wp\/v2\/tags?post=12146"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}