laravel
Git | Docs | API | Cast | News | Laravel学院 Lravel中文网
laravel-git | laravel 库 | Laravel 契约 | october库 | lavalite cms库 | Flarepoint-crm | lavalite 契约
laravel horizon
Create your own PHP CLI Tool with Laravel Zero
Laravel 深入核心系列教程
基于laravel, vue, element构建的后台系统
blade-ui-kit/blade-icons
php artisan event:generate BlogView
在 app/Events 和 app/Listensers 目录下生成 BlogView.php 和 BlogViewListener.php 文件
在 app/Providers/ 目录下的 EventServiceProvider.php 中注册事件监听器映射关系,如下:
protected $listen = [
'App\Events\BlogView' => [
'App\Listeners\BlogViewListener',
],
];
生成Policy
php artisan make:policy ArticlePolicy,保存在 app/Policies 目录下
在 app/Providers/AuthServiceProvider.php 的 policies 数组中注册该策略,将 Article 模型与对应的 ArticlePolicy 策略进行绑定。
protected $policies = [
Article::class => ArticlePolicy::class,
];
public function boot()
{
$this->registerPolicies();
}
定义policy
class ArticlePolicy
{
public function update(User $user, Article $article)
{
return $user->id == $article->user_id;
}
}
使用Policy
class ArticleController extends Controller
{
public function edit(Request $request, Article $article)
{
// 校验用户是否有操作权限
$this->authorize('update', $article);
// 更新文章
$article->fill($request->all());
$article->save();
}
}
Application.php中加载provider;
public function registerConfiguredProviders()
{
// 这里是读取配置文件中app.provider中的服务
$providers = Collection::make($this->config['app.providers'])
->partition(function ($provider) {
return Str::startsWith($provider, 'Illuminate\\');
});
// 这里是读取bootstrap/cache/packages.php里面的信息,如果没有这个文件,就去读取每个包的composer.json里面的extra.laravel里面的providers,保存进入cache/services。
$providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);
// 加载provider里面的各个Service
(new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
->load($providers->collapse()->toArray());
}
composer.json中配置laravl加载providers和alias, 不需要手动在config/app.php配置
"extra": {
"laravel": {
"providers": [
"Dingo\\Api\\Provider\\LaravelServiceProvider"
],
"aliases": {
"API": "Dingo\\Api\\Facade\\API"
}
}
},
运行composer install后,配置信息在bootstrap/cache/provider.php
文件定义路由
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->group(base_path('routes/api.php'));
Route::prefix('api')
->middleware('api')
->group(base_path('routes/wenda.php'));
Route::prefix('api')
->middleware('api')
->group(base_path('routes/gov_affair.php'));
}
csrf: 以html形式使用(不是ajax) 我们无法在其中设置请求标头html表单,因此我们必须通过表单输入将其作为隐藏字段发送。 x-csrf令牌: 它被添加到ajax请求的请求标头中。 使用 laravel 作为后端时。 laravel 自动检查此标头,并将其与数据库中有效的 csrf 进行比较。 x-xsrf-token: 添加到ajax请求的请求标头中。 受欢迎的库(例如angular和 axios )会自动从 xsrf令牌 cookie并随每个请求一起发送。 因为它很流行,所以laravel在每个响应中都创建了该Cookie。 因此,当您使用例如 axios 和 laravel 时,不需要做任何事情。 与 x-csrf-token 相比,它的字符串更大。 cookie是在 laravel 中加密的。
隐匿绑定:
路由定义中affair与控制器依赖注入
$affairs->post('/{affair}/sort', AffairController::class . '@setSort');
affair与控制器参数对应,用$gov_affair将不能解析
public function setSort(SortFeed $request, FeedModel $affair)
{
$affair->save();
}
显示绑定:
#App\Providers\RouteProvider
public function boot() {
Route::model('task_model', Task::class);
parent::boot();
}
Route::get('task/model/{task_model}', function(\App\Models\Task $task) {
dd($task);
});
在resource中加入其它方法,要在resouce前加入
Route::get('foo/bar', 'FooController@bar');
Route::resource('foo', 'FooController');
测试
postman调试:
pm.environment.set("XSRF-TOKEN",decodeURIComponent(pm.cookies.get("XSRF-TOKEN")))
Header:
Cookie: cookieStr(copy from web)
X-XSRF-TOKEN : {{XSRF-TOKEN}}
public function setUp()
{
parent::setUp();
Session::start();
}
public function testStoreAction()
{
$response = $this->call('POST', 'questions', array(
'_token' => csrf_token(),
));
$this->assertRedirectedTo('questions');
}
public function testStoreAction()
{
Session::start();
$response = $this->call('POST', 'questions', array(
'_token' => csrf_token(),
));
$this->assertEquals(302, $response->getStatusCode());
$this->assertRedirectedTo('questions');
}
php artisan tinker
$user = new App\User;
$user->name = 'vicky';
$user->email = 'abc123@163.com';
$user->password = bcrypt('abcxxx');
$user->save();
App\User::all();
$user->find(1)-delete();
与tinker联合调试模型数据
php artisan make:factory PostFactory
生成中文,配置 app.php 添加以下配置项
'faker_locale' => 'zh_CN'
'title' => $faker->sentence(10),
'body' => $faker->sentence(30),
生成50条模拟数据
>>>namespace App\Models\Cms
>>>factory(Post::class, 50)->create()
>>>Post::count();
>>>Post::all();
>>>$post = Post::find(1);
>>>$post->delete();
$product = factory(\App\Product::class)->make(); //生成临时数据测试,create()将数据保存到数据库
$response = $this->post(route('products.store'), $product->toArray());
$response->assertSuccessful();
列出表的字段
use Illuminate\Support\Facades\Schema;
$columns = Schema::getColumnListing('posts');
dd($columns);
定制表名称
class Customer extends Model{
protected $table = 'customer';
}
生成模型数据并导入
php artisan make:seeder MovieTableSeeder
php artisan db:seed --class=MovieTableSeeder
class MovieTableSeeder extends Seeder
{
public function run()
{
$faker = Faker\Factory::create();
for($i = 0; $i < 1000; $i++) {
App\Movie::create([ 'name' => $faker->name ]);
}
}
}
Restful API Using Jwt Auth - git
Laravel 使用 JWT 实现 API Auth, 打造用户授权接口
传统的做法中,服务器会保存生成的 token, 当客户端发送来 token 时,与服务器的进行比对,但是 jwt 的不需要在服务器保存任何 token, 而是使用一套加密 / 解密算法 和 一个密钥 来对用户发来的 token 进行解密,解密成功后就可以得到这个用户的信息. 这样的做法同时也增加了多服务器时的扩展性,在传统的 token 验证中,一旦用户发来 token, 那么必须要先找到存储这个 token 的服务器是哪台服务器,然后由那一台服务器进行验证用户身份。而 jwt 的存在,只要每一台服务器都知道解密密钥,那么每一台服务器都可以拥有验证用户身份的能力.
全局辅助函数helper
Illumination/Foundation/helpers.php
创建自定义辅助函数
在这里我们把函数放在app/Support/Helpers/CustomHelper.php内:
if (! function_exists('test_function')) {
function test_function() {
echo "我是一个自定义辅助函数";
}
}
辅助函数文件载入
创建文件app/Support/Helpers/Helpers.php,并载入包含有自定义函数的文件:
<?php
$helpers = [
'CustomHelper.php'
];
foreach ($helpers as $helperFileName) {
include __DIR__ . '/' .$helperFileName;
}
在composer.json中自动载入Helpers.php文件
/*composer.json*/
{
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"App\\": "app/"
},
"files": [
"app/Support/Helpers/helpers.php"
]
}
}
重新编译autoload.php文件
运行如下命令:
composer dump-autoload php artisan optimize
中间件
HTTP 中间件为过滤进入应用的 HTTP 请求提供了一套便利的机制。 除了认证之外,中间件还可以被用来处理更多其它任务。比如:CORS 中间件可以用于为离开站点的响应添加合适的头(跨域);日志中间件可以记录所有进入站点的请求。
//在App\Http\Kernel 类中注册中间件
class CheckAge
{
public function handle($request, Closure $next)
{
if ($request->input('age') <= 200) {
return redirect('home');
}
return $next($request);
}
}
class AfterMiddleware
{
public function handle($request, Closure $next)
{
$response = $next($request);
// 之后执行动作
return $response;
}
}
容器container
class Container {
protected $binds;
protected $instances;
public function bind($abstract, $concrete) {
if ($concret instanceof Closure) {
$this->binds[$abstract] = $concrete;
} else {
$this->instances[$abstract] = $concrete;
}
}
public function make($abstract, $parameters = []) {
//闭包函数
if (isset($this->instances[$abstract])) {
return $this->instances[$abstract];
}
//array_unshift($parameters, $this);
//return call_user_func_array($this->binds[$abstract], $parameters);
//反射机制
$concrete = $this->binds[$abstract];
$reflector = new reflectionClass($concrete);
$constructor = $reflector->getConstructor();
if (is_null($constructor)) {
return $reflector->newInstance();
} else {
$dependencies = $constructor->getParameters();
$instance = $this->getDependencies($dependencies);
return $reflector->newInstanceArgs($instances);
}
}
}
创建容器:
$container = new container;
注册实例:
$container->bind('superman', new SuperMan($container->make($moduleName)));
注册闭包(延迟绑定):
$container->bind('superman', function($container, $moduleName) {
return SuperMan($container->make($moduleName));
});
注册接口
$this->app->bind('App\Supports\gameSprint', 'App\game\Surperman');
调用实例:
$superman1 = $container->make('superman', 'xpower');
$superman2 = $container->make('superman', 'ultrabomb');
$superman3 = $container->make('App\Supports\gameSprint', 'dive');
用ServiceProvider注册:
class SupermanServiceProvider extends ServiceProvider {
public function register() {
$this->app->bind('App\Supports\gameSprint',
'App\game\Surperman');
$this->app->bind('superman', function($container, $moduleName) {
return SuperMan($container->make($moduleName));
});
}
public function boot() {
$this->loadViewsFrom();
$this->publishes(); //发布配置文件
$this->app->router->group(); //设置路由
}
}
定义facade
class Superman extends Facade {
public function getFacadeAccessor() {
return 'superman';
}
}
abstract class Facade {
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();
if (! $instance) {
throw new RuntimeException('A facade root has not been set.');
}
return $instance->$method(...$args);
}
}
在app/config.php中配置:
'providers' => [
App\providers\SupermanServiceProvider::class,
];
'aliases' => [
'Superman' => App\Facades\SupermanFacade::class
];
在程序中调用:
Superman::shot();
路由定义:
Route::get('/', HomeController@index);
控制器(反射机制):
class HomeController extends Controller {
public function index(App\Supports\gameSprint superman) {
}
}
Facade工作原理
创建自定义门面Auth,继承自 Illuminate\Support\Facades\Facade 基类。 类Auth只需要实现一个方法:getFacadeAccessor。 Facade 基类使用魔术方法 __callStatic() 从Auth中调用解析对象AuthManager。
class AuthServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('auth', function ($app) {
$app['auth.loaded'] = true;
return new AuthManager($app);
}
}
class Auth extends Facade
{
protected static function getFacadeAccessor()
{
return 'auth';
}
}
路由
路由资源控制器
生成路由资源控制器:
php artisan make:Controller PostController --resource
#route::resource('post', 'PostController');
| HTTP请求方式 | URL | 控制器方法 | 路由命名 | 业务逻辑描述 |
| GET | post | index() | post.index | 展示所有文章 |
| GET | post/create | create() | post.create | 发布文章表单页面 |
| POST | post | store() | post.store | 获取表单提交数据并保存新文章 |
| GET | post/{post} | show() | post.show | 展示单个文章 |
| GET | post/{id}/edit | edit() | post.edit | 编辑文章表单页面 |
| PUT | post/{id} | update() | post.update | 获取编辑表单输入并更新文章 |
| DELETE | post/{id} | destroy() | post.desc | 删除单个文章 |
服务容器绑定的几种情形
简单绑定
$this->app->bind('HelpSpot\API', function ($app) {
return new HelpSpot\API($app->make('HttpClient'));
});
单例绑定
$this->app->singleton('HelpSpot\API', function ($app) {
return new HelpSpot\API($app->make('HttpClient'));
});
实例绑定
$api = new HelpSpot\API(new HttpClient);
$this->app->instance('HelpSpot\Api', $api);
值绑定
$this->app->when('App\Http\Controllers\UserController')
->needs('$variableName')
->give($value);
绑定接口到实现
EventPusher为接口,编写该接口的实现RedisEventPusher后,就可以将其注册到服务容器:
$this->app->bind(
'App\Contracts\EventPusher',
'App\Services\RedisEventPusher'
);
Request和Reponse是依赖Symfony框架的HttpFoundation组件来实现的。
Response code
const HTTP_CONTINUE = 100;
const HTTP_SWITCHING_PROTOCOLS = 101;
const HTTP_PROCESSING = 102; // RFC2518
const HTTP_EARLY_HINTS = 103; // RFC8297
const HTTP_OK = 200;
const HTTP_CREATED = 201;
const HTTP_ACCEPTED = 202;
const HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
const HTTP_NO_CONTENT = 204;
const HTTP_RESET_CONTENT = 205;
const HTTP_PARTIAL_CONTENT = 206;
const HTTP_MULTI_STATUS = 207; // RFC4918
const HTTP_ALREADY_REPORTED = 208; // RFC5842
const HTTP_IM_USED = 226; // RFC3229
const HTTP_MULTIPLE_CHOICES = 300;
const HTTP_MOVED_PERMANENTLY = 301;
const HTTP_FOUND = 302;
const HTTP_SEE_OTHER = 303;
const HTTP_NOT_MODIFIED = 304;
const HTTP_USE_PROXY = 305;
const HTTP_RESERVED = 306;
const HTTP_TEMPORARY_REDIRECT = 307;
const HTTP_PERMANENTLY_REDIRECT = 308; // RFC7238
const HTTP_BAD_REQUEST = 400;
const HTTP_UNAUTHORIZED = 401;
const HTTP_PAYMENT_REQUIRED = 402;
const HTTP_FORBIDDEN = 403;
const HTTP_NOT_FOUND = 404;
const HTTP_METHOD_NOT_ALLOWED = 405;
const HTTP_NOT_ACCEPTABLE = 406;
const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
const HTTP_REQUEST_TIMEOUT = 408;
const HTTP_CONFLICT = 409;
const HTTP_GONE = 410;
const HTTP_LENGTH_REQUIRED = 411;
const HTTP_PRECONDITION_FAILED = 412;
const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
const HTTP_REQUEST_URI_TOO_LONG = 414;
const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
const HTTP_EXPECTATION_FAILED = 417;
const HTTP_I_AM_A_TEAPOT = 418; // RFC2324
const HTTP_MISDIRECTED_REQUEST = 421; // RFC7540
const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918
const HTTP_LOCKED = 423; // RFC4918
const HTTP_FAILED_DEPENDENCY = 424; // RFC4918
const HTTP_TOO_EARLY = 425; // RFC-ietf-httpbis-replay-04
const HTTP_UPGRADE_REQUIRED = 426; // RFC2817
const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585
const HTTP_TOO_MANY_REQUESTS = 429; // RFC6585
const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; // RFC6585
const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
const HTTP_INTERNAL_SERVER_ERROR = 500;
const HTTP_NOT_IMPLEMENTED = 501;
const HTTP_BAD_GATEWAY = 502;
const HTTP_SERVICE_UNAVAILABLE = 503;
const HTTP_GATEWAY_TIMEOUT = 504;
const HTTP_VERSION_NOT_SUPPORTED = 505;
const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506; // RFC2295
const HTTP_INSUFFICIENT_STORAGE = 507; // RFC4918
const HTTP_LOOP_DETECTED = 508; // RFC5842
const HTTP_NOT_EXTENDED = 510; // RFC2774
const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585