博主认为,最好的教程一定是官方文档。而要快速建立关于某个语言或框架的知识结构,一定是直接上手做一个小项目。 Laravel 5.5 于今年 8 月份发布,是下一个 LTS 版本,从这个版本发布开始会停止对 Laravel 5.1 的 bug 修复,而对 5.1 的安全修复直到 2018 年 7 月份。 LTS 是指长期支持版本,是 Laravel 能提供的最长时间维护的版本,一般发行版只提供 6 个月的 Bug 修复支持,一年的安全修复支持。所以商业用途最好使用 LTS 版本。 关于 LTS 可参考 Laravel 的发布路线图 网上关于 Laravel 的教程很多,但是教程的基本内容无非是了解 laravel 的 MVC ,然后做数据的增删改查。 官方文档其实就很好,该 tutorials 中包括两个部分,一个是 Beginner Task List 另一个是 Intermediate Task List 。可惜的是 5.5 目前还没有这部分的内容。 但是差异也不是很大,原文地址 Beginner Task List 本文是以上面的 5.1 文档为参考,以 5.5 为开发框架,进行了实践后,记录下来的。 本文中使用的环境是 ubuntu + apache + mysql + php ,使用的 hyper-v 虚拟机,关于这部分,对读者可能没有价值,可跳过。但对博主个人有记录以备后查的作用,所以会写下来。
安装 Laravel我同时安装了 Laravel 5.1 和 Laravel 5.5 ,一个给到 51 端口,一个给到 55 端口。
环境要求Laravel 5.5 系统要求
Laravel 5.1 系统要求
PHP >= 7.0.0 OpenSSL PHP Extension PDO PHP Extension Mbstring PHP Extension Tokenizer PHP Extension XML PHP Extension
PHP >= 5.5.9 OpenSSL PHP Extension PDO PHP Extension Mbstring PHP Extension Tokenizer PHP Extension
通过 Laravel installer 安装 5.5 (最新的)1 composer global require "laravel/installer"
如果此时输入 laravel
一定会提示 laravel: command not found
这是因为该命令不在环境变量中 查看环境变量:
1 2 $ echo $PATH /home/thankit/bin:/home/thankit/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
一般情况下的 composer vendor bin 目录在:
MacOS: $HOME/.composer/vendor/bin
GNU / Linux Distributions: $HOME/.config/composer/vendor/bin
下面我们要将该变量添加到 $PATH 中
1 export PATH="~/.config/composer/vendor/bin:$PATH"
下面可以用 laravel
命令了。 切换到工作目录下,比如 todo55
这样就会在当前目录下安装 laravel 下面我们查看下 laravel 的版本号
1 2 $ php artisan --version Laravel Framework 5.5.20
通过 composer 安装 5.1将 5.1 版本的放在目录 todo51
下 在 todo51
目录下
1 composer create-project laravel/laravel ./ "5.1.*"
同样看下版本号:
1 2 $ php artisan --version Laravel Framework version 5.1.46 (LTS)
设置权限storage
和 bootstrap/cache
目录, web 服务器必须具有可写权限。 对我来说,我需要给组 www-data 写权限
1 chmod g+w -R storage bootstrap/cache
设置虚拟目录 (可跳过)确保 mod_rewrite
开启了,这样 public/.htaccess
就有作用了。 以下是针对 Apache 的 启用 Apache rewrite 模块 a2enmod rewrite
我将让 todo51
监听 51 端口,而 todo55
监听 55 端口 首先将 varnish 监听端口从 80 上改到 6082 其次修改 /etc/apache2/ports.conf
增加监听的端口 然后修改 /etc/apache2/sites-available/000-default.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <VirtualHost *:51> DocumentRoot /home/thankit/www/laravel/todo51/public <Directory /home/thankit/www/laravel/todo51/public> AllowOverride all # Options +FollowSymLinks # RewriteEngine On # RewriteCond %{REQUEST_FILENAME} !-d # RewriteCond %{REQUEST_FILENAME} !-f # RewriteRule ^ index.php [L] </Directory> </VirtualHost> <VirtualHost *:55> DocumentRoot /home/thankit/www/laravel/todo55/public <Directory /home/thankit/www/laravel/todo55/public> AllowOverride all # Options +FollowSymLinks # RewriteEngine On # RewriteCond %{REQUEST_FILENAME} !-d # RewriteCond %{REQUEST_FILENAME} !-f # RewriteRule ^ index.php [L] </Directory> </VirtualHost>
看看是否监听了端口
一切正常, 将通过 http://192.168.0.251:51/
访问 5.1 的 todo 将通过 http://192.168.0.251:55/
访问 5.5 的 todo 下面我们得看看 5.5 为什么报错了。 我们进入 todo5.5 的目录,修改 config/app.php
将 debug 打开
1 'debug' => env('APP_DEBUG', true),
下面我们再刷新,看看错误是什么
1 2 RuntimeException No application encryption key has been specified.
下面直接将 .env.example
拷贝一份重命名为 .env
然后运行命令
1 2 5$ php artisan key:generate Application key [base64:aJqES8WcAQJJ0jjoMKeQojV0TWnae5ALs8JQnm/uW5w=] set successfully.
下面再打开网站看看,这次正常了:
Task List 创建 数据库迁移首先对数据库帐密进行设置,有两处 config\database.php
和 .env
.env
是主配置文件,会覆盖 database.php
中的参数 我们设置好 .env
中关于 db 的部分 执行:
1 php artisan make:migration create_tasks_table --create=tasks
他将在 database/migrations/
目录下创建类似 YYYY_M_DD_HHMMSS_create_tableName_table.php
的文件,就像2017_11_08_064436_create_tasks_table.php
其中的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateTasksTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('tasks', function (Blueprint $table) { $table->increments('id'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('tasks'); } }
该类包含了两个方法,up()
(执行迁移命令时创建表结构)和 down()
(执行回滚时删除表结构) 更多的参考 Database: Migrations 下面我们给他添加一个字段 name
1 2 3 4 5 6 7 8 public function up() { Schema::create('tasks', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->timestamps(); }); }
下面执行数据迁移:
然后我们进入数据库中看看,tasks
表被创建出来了 下面试一下回滚:
1 php artisan migrate:rollback
在看看数据库中的表都被删除了。 php artisan migrate:refresh
全部回滚重建
数据库填充我们将创建一个 Task model 对应我们的表 tasks 默认规则是模型类名的复数作为与其对应的表名,除非在模型类中明确指定了其它名称。
1 php artisan make:model Task
他在 app 目录下创建了 Task.php
内容如下:
1 2 3 4 5 6 7 8 9 10 <?php namespace App; use Illuminate\Database\Eloquent\Model; class Task extends Model { // }
下面我们创建自动填充文件:
1 php artisan make:seeder TasksTableSeeder
他将在 database/seeds
目录下创建 TasksTableSeeder.php
,这个文件就是数据库填充文件了,他的命名规则是数据库表名加上 TableSeeder 并且首字母大写,表名一般是复数,所以我们有了 TasksTableSeeder
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #File: database/seeds/TasksTableSeeder.php <?php use App\Task; use Illuminate\Database\Seeder; class TasksTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('tasks')->insert(['name' => str_random(10)]); Task::create(['name' => 'thankit']); } }
然后将 DatabaseSeeder.php 添加内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { // $this->call(UsersTableSeeder::class); $this->call(TasksTableSeeder::class); } }
最后我们来运行命令:
之后我们就可以在数据库中看看表中数据了:
路由routes/web.php
文件用于定义 web 界面的路由。这里面的路由都会被分配给 web 中间件组,它提供了会话状态和 CSRF 保护等功能。定义在 routes/api.php
中的路由都是无状态的,并且被分配了 api 中间件组。 所以我们在 web.php 中添加以下内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php use Illuminate\Http\Request; Route::get('/', function () { return view('tasks'); }); /** * Add A New Task */ Route::post('/task', function (Request $request) { // }); /** * Delete An Existing Task */ Route::delete('/task/{id}', function ($id) { // });
创建父 blade 模版File:resources/views/layouts/app.blade.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <!DOCTYPE html> <html lang="en"> <head> <title>Laravel Quickstart - Basic</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" crossorigin="anonymous"> <!-- CSS And JavaScript --> </head> <body> <div class="container"> <nav class="navbar navbar-default"> <!-- Navbar Contents --> </nav> </div> @yield('content') </body> </html>
子 blade 模版File:resources/views/tasks.blade.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @extends('layouts.app') @section('content') <!-- Bootstrap Boilerplate... --> <div class="panel-body"> <!-- Display Validation Errors --> <?php // @include('common.errors') ;?> <!-- New Task Form --> <form action="/task" method="POST" class="form-horizontal"> {{ csrf_field() }} <!-- Task Name --> <div class="form-group"> <label for="task" class="col-sm-3 control-label">Task</label> <div class="col-sm-6"> <input type="text" name="name" id="task-name" class="form-control"> </div> </div> <!-- Add Task Button --> <div class="form-group"> <div class="col-sm-offset-3 col-sm-6"> <button type="submit" class="btn btn-default"> <i class="fa fa-plus"></i> Add Task </button> </div> </div> </form> </div> <!-- TODO: Current Tasks --> @endsection
解释,还是英文的说的好理解,所以附上: the @extends
directive informs Blade that we are using the layout we defined at resources/views/layouts/app.blade.php
. All of the content between @section('content')
and @endsection
will be injected into the location of the @yield('content')
directive within the app.blade.php
layout 起码看起来有点样子了
验证输入并增加 tasks修改 web.php
中的路由:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // 这里不能少 use Illuminate\Http\Request; Route::post('/task', function (Request $request) { $validator = Validator::make($request->all(), [ 'name' => 'requiredmax:255', ]); if ($validator->fails()) { return redirect('/') ->withInput() ->withErrors($validator); } // Create The Task... });
这样去掉 resources/views/tasks.blade.php 中的注释
1 @include('common.errors')
补上模版: resources/views/common/errors.blade.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @if (count($errors) > 0) <!-- Form Error List --> <div class="alert alert-danger"> <strong>Whoops! Something went wrong!</strong> <br><br> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> @endif
现在如果我们提交空值,就会有错误提示了。 下面我们要增加 tasks 所以修改 web.php
的内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 use App\Task; use Illuminate\Http\Request; /** * Display All Tasks */ Route::get('/', function () { return view('tasks'); }); /** * Add A New Task */ Route::post('/task', function (Request $request) { $validator = Validator::make($request->all(), [ 'name' => 'requiredmax:255', ]); if ($validator->fails()) { return redirect('/') ->withInput(['name' => 'reminder']) ->withErrors($validator); } // Create The Task... $task = new Task; $task->name = $request->name; $task->save(); return redirect('/'); }); ...
所以接下来我们测试提交一个 task ,发现已经添加到数据库表中了。
展示已经有的 tasksFile:routes/web.php
1 2 3 4 5 6 Route::get('/', function () { $tasks = Task::orderBy('created_at', 'asc')->get(); return view('tasks', [ 'tasks' => $tasks, ]); });
File:resoures/views/tasks.blade.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 @extends('layouts.app') @section('content') <!-- Create Task Form...这里有省略 --> <!-- Current Tasks --> @if (count($tasks) > 0) <div class="panel panel-default"> <div class="panel-heading"> Current Tasks </div> <div class="panel-body"> <table class="table table-striped task-table"> <!-- Table Headings --> <thead> <th>Task</th> <th> </th> </thead> <!-- Table Body --> <tbody> @foreach ($tasks as $task) <tr> <!-- Task Name --> <td class="table-text"> <div>{{ $task->name }}</div> </td> <td> <!-- TODO: Delete Button --> </td> </tr> @endforeach </tbody> </table> </div> </div> @endif @endsection
效果如下:
删除 tasks上面的文件中我们留了一个删除按钮没有写 下面补上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <tr> <!-- Task Name --> <td class="table-text"> <div>{{ $task->name }}</div> </td> <td> <form action="/task/{{ $task->id }}" method="POST"> {{ csrf_field() }} {{ method_field('DELETE') }} <button type="submit" class="btn btn-danger">Delete Task</button> </form> </td> </tr>
注意这里的 {{ method_field('DELETE') }}
等同于
1 <input type="hidden" name="_method" value="DELETE">
File:routes/web.php
1 2 3 4 Route::delete('/task/{id}', function ($id) { Task::findOrFail($id)->delete(); return redirect('/'); });
最后的效果如下: