Valet 是 Mac 极简主义者的 Laravel 开发环境。
确保 ~/.composer/vendor/bin 目录在系统的「PATH」中。
vi ~/.bash_profile
> export PATH=$PATH:~/.composer/vendor/bin
source ~/.bash_profile
echo $PATH
安装Valet
brew update && brew install php
composer global require laravel/valet
valet install
关闭Valet: valet stop
开启Valet: valet start
重启Valet: valet restart
绑定应用: cd betterlife && valet link
访问网站: http://betterlife.test/
查看列表: valet links
取消绑定: valet unlink betterlife
composer require laravel/telescope --dev
php artisan telescope:install
php artisan migrate
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=betterlife
DB_USERNAME=root
DB_PASSWORD=
创建权限认证: Laravel UI
composer require laravel/ui
php artisan ui bootstrap --auth
npm install && npm run dev
npm run watch
创建权限认证: Laravel Breeze
使用Blade模版语言
composer require laravel/breeze --dev
php artisan breeze:install
npm install && npm run dev
php artisan migrate
npm run watch
使用Inertia.js前端Vue或React实现
php artisan breeze:install vue
或
php artisan breeze:install react
npm install && npm run dev
php artisan migrate
npm run watch
提供权限认证后台Api支持
php artisan breeze:install api
php artisan migrate
创建权限认证: Laravel Jetstream
Jetstream 官方文档:
安装Jetstream: composer require laravel/jetstream
Jetstream 使用 Tailwind CSS 设计样式,并提供 Livewire 或 Inertia.js 驱动的前端脚手架技术栈。
Livewire + Blade
php artisan jetstream:install livewire
php artisan jetstream:install livewire --teams
npm install && npm run dev
php artisan migrate
Inertia + Vue
php artisan jetstream:install inertia
php artisan jetstream:install inertia --teams
npm install && npm run dev
php artisan migrate
移除Jetstream
composer remove laravel/jetstream
自定义权限认证: https://laravel.com/docs/8.x/authentication#authenticating-users
创建表: blogs
php artisan make:migration create_blogs_table --create=blogs
表字段定义
Schema::create('blogs', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->string('url')->unique();
$table->text('description');
$table->timestamps();
});
建模和创建模拟数据
建模和数据工厂: php artisan make:model --factory Blog
在工厂类里定义模拟数据数据
return [
'title' => substr($this->faker->sentence(2), 0, -1),
'url' => $this->faker->url,
'description' => $this->faker->paragraph,
];
创建Blog Seeder: php artisan make:seeder BlogsTableSeeder
修改新生成的文件: database/seeders/BlogsTableSeeder
public function run()
{
\App\Models\Blog::factory(5)->create();
}
修改原本存在的文件: database/seeders/DatabaseSeeder.php
public function run()
{
$this->call(BlogsTableSeeder::class);
}
重新同步表和模拟数据到数据库: php artisan migrate:fresh --seed
使用Tink Shell查看模型数据: php artisan tinker
\App\Models\Blog::first();
Routing 和 Views
在文件 routes/web.php 里添加路由
Route::get('/', function () {
$blogs = \App\Models\Blog::all();
return view('welcome', ['blogs' => $blogs]);
});
修改文件: resources/views/welcome.blade.php
<div class="blogs">
@foreach ($blogs as $blog)
<a href="{{ $blog->url }}">{{ $blog->title }}</a><br/>
@endforeach
</div>
创建权限认证: Laravel UI
composer require laravel/ui
php artisan ui bootstrap --auth
npm install && npm run dev
npm run watch
创建Form
Route::get('/edit', function () {
return view('edit');
});
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
<h1>Submit a blog</h1>
</div>
<div class="row">
<form action="/edit" method="post">
@csrf
@if ($errors->any())
<div class="alert alert-danger" role="alert">
Please fix the following errors
</div>
@endif
<div class="form-group">
<label for="title">Title</label>
<input type="text" class="form-control @error('title') is-invalid @enderror" id="title" name="title" placeholder="Title" value="{{ old('title') }}">
@error('title')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="url">Url</label>
<input type="text" class="form-control @error('url') is-invalid @enderror" id="url" name="url" placeholder="URL" value="{{ old('url') }}">
@error('url')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea class="form-control @error('description') is-invalid @enderror" id="description" name="description" placeholder="description">{{ old('description') }}</textarea>
@error('description')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div><br/>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
@endsection
use Illuminate\Http\Request;
Route::post('/edit', function (Request $request) {
$data = $request->validate([
'title' => 'required|max:255',
'url' => 'required|url|max:255',
'description' => 'required|max:255',
]);
$blog = tap(new App\Models\Blog($data))->save();
return redirect('/');
});
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Blog extends Model
{
use HasFactory;
protected $fillable = [
'title',
'url',
'description'
];
}
添加测试
<php>
...
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
...
</php>
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Validation\ValidationException;
use Tests\TestCase;
class EditBlogsTest extends TestCase
{
use RefreshDatabase;
/**
* A basic feature test example.
*
* @return void
*/
public function test_example()
{
$response = $this->get('/');
$response->assertStatus(200);
}
/** @test */
public function guest_can_submit_a_new_blog() {
$response = $this->post('/edit', [
'title' => 'Example Title',
'url' => 'http://example.com',
'description' => 'Example description.',
]);
$this->assertDatabaseHas('blogs', [
'title' => 'Example Title'
]);
$response
->assertStatus(302)
->assertHeader('Location', url('/'));
$this
->get('/')
->assertSee('Example Title');
}
/** @test */
public function blog_is_not_created_if_validation_fails() {
$response = $this->post('/edit');
$response->assertSessionHasErrors(['title', 'url', 'description']);
}
/** @test */
public function blog_is_not_created_with_an_invalid_url() {
$this->withoutExceptionHandling();
$cases = ['//invalid-url.com', '/invalid-url', 'foo.com'];
foreach ($cases as $case) {
try {
$response = $this->post('/edit', [
'title' => 'Example Title',
'url' => $case,
'description' => 'Example description',
]);
} catch (ValidationException $e) {
$this->assertEquals(
'The url must be a valid URL.',
$e->validator->errors()->first('url')
);
continue;
}
$this->fail("The URL $case passed validation when it should have failed.");
}
}
/** @test */
public function max_length_fails_when_too_long() {
$this->withoutExceptionHandling();
$title = str_repeat('a', 256);
$description = str_repeat('a', 256);
$url = 'http://';
$url .= str_repeat('a', 256 - strlen($url));
try {
$this->post('/edit', compact('title', 'url', 'description'));
} catch(ValidationException $e) {
$this->assertEquals(
'The title must not be greater than 255 characters.',
$e->validator->errors()->first('title')
);
$this->assertEquals(
'The url must not be greater than 255 characters.',
$e->validator->errors()->first('url')
);
$this->assertEquals(
'The description must not be greater than 255 characters.',
$e->validator->errors()->first('description')
);
return;
}
$this->fail('Max length should trigger a ValidationException');
}
/** @test */
public function max_length_succeeds_when_under_max() {
$url = 'http://';
$url .= str_repeat('a', 255 - strlen($url));
$data = [
'title' => str_repeat('a', 255),
'url' => $url,
'description' => str_repeat('a', 255),
];
$this->post('/edit', $data);
$this->assertDatabaseHas('blogs', $data);
}
}
// 可使用Laravel的Helper方法
// [dd()](https://laravel.com/docs/8.x/helpers#method-dd)
// 在Visual Studio Code编辑器里如果函数语法提示错误,需修改Visual Studio Code配置:
// - "intelephense.files.associations": ["*.php", "*.phtml", "*.inc", "*.module", "*.install", "*.theme", ".engine", ".profile", ".info", ".test"]
$value1 = "Hello";
$value2 = "World";
$value3 = "Skygreen";
dd($value1, $value2, $value3);
$v = [$value1, $value2, $value3];
use Symfony\Component\VarDumper\VarDumper;
VarDumper::dump($v);
// [可使用Laravel的Collections](https://laravel.com/docs/8.x/collections)
// 在Visual Studio Code编辑器里如果函数语法提示错误,需修改Visual Studio Code配置:
// - "intelephense.files.associations": ["*.php", "*.phtml", "*.inc", "*.module", "*.install", "*.theme", ".engine", ".profile", ".info", ".test"]
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
Collection::macro('toUpper', function () {
return $this->map(function ($value) {
return Str::upper($value);
});
});
$collection = collect(['first', 'second']);
$upper = $collection->toUpper();
echo $upper;
Nginx 配置: https://laravel.com/docs/8.x/deployment#nginx
生产环境性能优化