laravel

Git | Docs | API | Cast | News | Laravel学院 Lravel中文网

laravel-git | laravel 库 | Laravel 契约 | october库 | lavalite cms库 | Flarepoint-crm | lavalite 契约


laravel horizon

largezhou - git 后台管理

Create your own PHP CLI Tool with Laravel Zero

利用 OPcache 扩展提升 PHP7 性能

Laravel 从入门到精通系列教程

Laravel 深入核心系列教程

Laravel 从学徒到工匠


基于laravel, vue, element构建的后台系统

Laravel 和 Vue 的项目搭建:基础篇

基于 Laravel + Vue 构建前后端分离应用


blade-ui-kit/blade-icons

Laravel 上微信小程序登录

Faker - git


laravel Carbon函数

Laravel Service Provider 概念详解

Laravel 中自定义异常处理

Laravel 中的事件监听

Laravel 模型过滤(Filter)设计

Laravel 学习笔记: 授权策略(Policy)

消息通知Notification

laravel 自带消息notification

极光推送在 Laravel 通知的支持

Laravel--Jobs(同步异步)消息队列Queue

schedule

Laravel 集合(Collection)的基础用法

Laravel Exceptions——异常与错误处理

用Docker搭建Laravel开发环境

Laravel核心代码学习

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');

PHPUnit 测试

测试

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');
}

使用Tinker来调试Laravel

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 ]);
        }
    }
}

laracms

laravel horizon

laravel5.7+Vue+ Element 环境搭建

Laravel API 系列教程

Restful API Using Jwt Auth - git

Restful API Using Jwt Auth

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