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