博主认为,最好的教程一定是官方文档。而要快速建立关于某个语言或框架的知识结构,一定是直接上手做一个小项目。
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 (最新的)
composer global require "laravel/installer"
如果此时输入 laravel
一定会提示 laravel: command not found
这是因为该命令不在环境变量中
查看环境变量:
$ 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 中
export PATH="~/.config/composer/vendor/bin:$PATH"
下面可以用 laravel
命令了。
切换到工作目录下,比如 todo55
laravel new
这样就会在当前目录下安装 laravel
下面我们查看下 laravel 的版本号
$ php artisan --version
Laravel Framework 5.5.20
通过 composer 安装 5.1
将 5.1 版本的放在目录 todo51
下
在 todo51
目录下
composer create-project laravel/laravel ./ "5.1.*"
同样看下版本号:
$ php artisan --version
Laravel Framework version 5.1.46 (LTS)
设置权限
storage
和 bootstrap/cache
目录, web 服务器必须具有可写权限。
对我来说,我需要给组 www-data 写权限
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
<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>
看看是否监听了端口
netstat -lntp
一切正常,
将通过 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 打开
'debug' => env('APP_DEBUG', true),
下面我们再刷新,看看错误是什么
RuntimeException
No application encryption key has been specified.
下面直接将 .env.example
拷贝一份重命名为 .env
然后运行命令
5$ php artisan key:generate
Application key [base64:aJqES8WcAQJJ0jjoMKeQojV0TWnae5ALs8JQnm/uW5w=] set successfully.
下面再打开网站看看,这次正常了:
Task List 创建
数据库迁移
首先对数据库帐密进行设置,有两处 config\database.php
和 .env
.env
是主配置文件,会覆盖 database.php
中的参数
我们设置好 .env
中关于 db 的部分
执行:
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
其中的内容如下:
<?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
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
}
下面执行数据迁移:
php artisan migrate
然后我们进入数据库中看看,tasks
表被创建出来了
下面试一下回滚:
php artisan migrate:rollback
在看看数据库中的表都被删除了。
php artisan migrate:refresh
全部回滚重建
数据库填充
我们将创建一个 Task model 对应我们的表 tasks
默认规则是模型类名的复数作为与其对应的表名,除非在模型类中明确指定了其它名称。
php artisan make:model Task
他在 app 目录下创建了 Task.php
内容如下:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
//
}
下面我们创建自动填充文件:
php artisan make:seeder TasksTableSeeder
他将在 database/seeds
目录下创建 TasksTableSeeder.php
,这个文件就是数据库填充文件了,他的命名规则是数据库表名加上 TableSeeder 并且首字母大写,表名一般是复数,所以我们有了 TasksTableSeeder
#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 添加内容:
<?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);
}
}
最后我们来运行命令:
php artisan db:seed
之后我们就可以在数据库中看看表中数据了:
路由
routes/web.php
文件用于定义 web 界面的路由。这里面的路由都会被分配给 web 中间件组,它提供了会话状态和 CSRF 保护等功能。定义在 routes/api.php
中的路由都是无状态的,并且被分配了 api 中间件组。
所以我们在 web.php 中添加以下内容
<?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
<!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
@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
中的路由:
// 这里不能少
use Illuminate\Http\Request;
Route::post('/task', function (Request $request) {
$validator = Validator::make($request->all(), [
'name' => 'required|max:255',
]);
if ($validator->fails()) {
return redirect('/')
->withInput()
->withErrors($validator);
}
// Create The Task...
});
这样去掉 resources/views/tasks.blade.php 中的注释
@include('common.errors')
补上模版:
resources/views/common/errors.blade.php
@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
的内容:
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' => 'required|max: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 ,发现已经添加到数据库表中了。
展示已经有的 tasks
File:routes/web.php
Route::get('/', function () {
$tasks = Task::orderBy('created_at', 'asc')->get();
return view('tasks', [
'tasks' => $tasks,
]);
});
File:resoures/views/tasks.blade.php
@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
上面的文件中我们留了一个删除按钮没有写
下面补上:
<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') }}
等同于
<input type="hidden" name="_method" value="DELETE">
File:routes/web.php
Route::delete('/task/{id}', function ($id) {
Task::findOrFail($id)->delete();
return redirect('/');
});
最后的效果如下: