laravel_laravel swoole - CSDN
laravel 订阅
Laravel是一套简洁、优雅的PHP Web开发框架(PHP Web Framework)。它可以让你从面条一样杂乱的代码中解脱出来;它可以帮你构建一个完美的网络APP,而且每行代码都可以简洁、富于表达力。在Laravel中已经具有了一套高级的PHP ActiveRecord实现 -- Eloquent ORM。它能方便的将“约束(constraints)”应用到关系的双方,这样你就具有了对数据的完全控制,而且享受到ActiveRecord的所有便利。Eloquent原生支持Fluent中查询构造器(query-builder)的所有方法。 展开全文
Laravel是一套简洁、优雅的PHP Web开发框架(PHP Web Framework)。它可以让你从面条一样杂乱的代码中解脱出来;它可以帮你构建一个完美的网络APP,而且每行代码都可以简洁、富于表达力。在Laravel中已经具有了一套高级的PHP ActiveRecord实现 -- Eloquent ORM。它能方便的将“约束(constraints)”应用到关系的双方,这样你就具有了对数据的完全控制,而且享受到ActiveRecord的所有便利。Eloquent原生支持Fluent中查询构造器(query-builder)的所有方法。
信息
定    义
简洁、优雅的PHP Web开发框架
特    点
简洁、富于表达力
当前版本分支
为5.x
外文名
Laravel
作    者
泰勒.奥特威尔(taylor otwell)
Laravel功能特点
1、语法更富有表现力你知道下面这行代码里 “true” 代表什么意思么?$uri = Url::create(‘some/uri’, array(), array(), true); 另外,你知道其他参数在这里的意思么(除了第一个)?当然你不知道。因为这行代码没有表现力。再看看这段代码:$url = URL::to_secure(‘some/uri’);这个表达式使用HTTPS协议创建了一条URL链接, 事实上,上面两种写法都在做同样的事情,但哪一个更一目了然,更富有表现力呢?2、高质量的文档Laravel 有一个非常棒的的社区支持。Laravel代码本身的表现力和良好的文档使PHP程序编写令人愉快。3、丰富的扩展包Bundle是Laravel中对扩展包的称呼。它可以是任何东西 -- 大到完整的ORM,小到除错(debug)工具,仅仅复制&粘贴就能安装任何扩展包!Laravel的扩展包由世界各地的开发者贡献,而且还在不断增加中。4、开源、托管在GITHUB上Laravel是完全开源的。所有代码都可以从Github上获取,并且欢迎你贡献出自己的力量。
收起全文
精华内容
参与话题
  • Laravel框架基础到项目实战 完整版

    万人学习 2019-05-22 18:55:35
    Laravel是一套简洁、优雅的PHP Web开发框架(PHP Web Framework)。它可以让你从面条一样杂乱的代码...laravel框架功能较多,但是语法相对简单,在学习中API相对比较复杂,丰富的第三方组件是laravel框架学习的一个难点。
  • PHP+Laravel实战博客项目

    千人学习 2019-11-07 16:16:12
    1.项目的需求分析和程序详细设计 2.Git服务器搭建和项目版本管理应用 3.登录、注册、前后面模板开发 4.... 5.... 6.... 7.... PHP基本应用,接口技术、Composer和Laravel5.5
  • laravel入门教程(一)

    万次阅读 2018-06-03 13:20:05
    laravel入门教程(一) 本教程是针对 laravel 5 来讲解的 0.1、一个简单的示例 //文件:routes/web.php <?php Route::get('/','WelcomeController@index'); //文件:app/...

    laravel入门教程(一)

    本教程是针对 laravel 5 来讲解的

    0.1、一个简单的示例

    //文件:routes/web.php
    <?php
    Route::get('/','WelcomeController@index');
    
    //文件:app/Http/Controllers/WelcomeController.php
    <?php
    namescpace app\Http\Controllers;
    
    class WelcomeController{
        public function index(){
            return 'Hello, World!';
        }
    }
    

    访问http://xxxxxxxxx.xxx/ 就可以看见“Hello,World!”。
    实际上,这种写法等价于另一种更为简便的写法

    //文件:routes/web.php
    <?php
    Route::get('/',function(){
        return 'Hello, World!';
    });

    但是,为了保持与后面讲解内容的一致性和代码编写的一致性,最好使用第一种方法。第一种方法的第一个文件中定义了路由,当访问‘/’的路由的时候,就去找WelcomeController里面的index方法。第二个文件定义的就是WelcomeController控制器以及它的index方法。

    0.2、一个访问数据库数据的demo

    //文件:routes/web.php
    <?php
    Route::get('/',function{
        return Greeting::first()->body;
    });
    //文件:app/Greeting.php
    //这个是模型文件
    <?php
    use Illuminate\Database\Eloquent\Model;
    
    class Greeting extends Model{}
    
    //文件:database/migrations/2018_06_03_123123_create_greetings_table.php
    //这个是数据库迁移文件,后面会详细讲解
    <?php
     use Illuminate\Database\Migrations\Migration;
     use Illuminate\Database\Schema\Blueprint;
     class CreateGreetingsTable extends Migration{
         public function up(){
             Schema::create('greetings',function(Blueprint $table){
                $table->increments('id');
                $table->string('body');
                $table->timestamps();
             });
         }//创建数据库的文件,这些文件都可以使用artisan命令自动生成,内容自己填充
    
         public function down(){
             Schema::drop('greetings');
         }//销毁数据库时的操作
     }
    

    如果在greetings数据库里面存入一条数据:“Hello, World!”。就可以在首页看到了。

    下面开始正式学习laravel

    1、创建一个新的laravel项目

    1.1、使用Laravel安装工具来安装Laravel
    (1)、开发全局安装Composer
    (2)、运行

    composer global require “laravel/installer=~1.1”

    安装laravel安装工具。启动一个全向的Laravel项目:

    laravel new projectName

    此命令会在当前目录下创建一个新的名为 projectName 的子目录,并在其中安装 一个全新的Laravel项目

    1.2、使用Composer的create-project 来安装Laravel
    (1)、输入以下命令:

    composer create-project laravel/laravel projectName –prefer-dist

    此命令会在当前目录下创建一个新的名为 projectName 的子目录,并在其中安装 一个全新的Laravel项目

    1.3、创建好Laravel项目之后,生成的项目结构大致如下(因为版本不一样,可能稍有区别)

    新安装的 Laravel 应用包含许多文件夹:
    app目录包含了应用的核心代码;
    bootstrap目录包含了少许文件用于框架的启动和自动载入配置,还有一个cache文件夹用于包含框架生成的启动文件以提高性能;

    config目录包含了应用所有的配置文件;

    database目录包含了数据迁移及填充文件,如果你喜欢的话还可以将其作为 SQLite 数据库存放目录;

    public目录包含了前端控制器和资源文件(图片、JavaScript、CSS等);

    resources目录包含了视图文件及原生资源文件(LESS、SASS、CoffeeScript),以及本地化文件;

    storage目录包含了编译过的Blade模板、基于文件的session、文件缓存,以及其它由框架生成的文件,该文件夹被细分为成app、framework和logs子目录,app目录用于存放应用要使用的文件,framework目录用于存放框架生成的文件和缓存,最后,logs目录包含应用的日志文件;

    tests目录包含自动化测试,其中已经提供了一个开箱即用的PHPUnit示例;

    vendor目录包含Composer依赖;

    App目录

    应用的核心代码位于app目录下,默认情况下,该目录位于命名空间 App 下, 并且被 Composer 通过 PSR-4自动载入标准 自动加载。你可以通过Artisan命令app:name来修改该命名空间。

    app目录下包含多个子目录,如Console、Http、Providers等。Console和Http目录提供了进入应用核心的API,HTTP协议和CLI是和应用进行交互的两种机制,但实际上并不包含应用逻辑。换句话说,它们只是两个向应用发布命令的方式。Console目录包含了所有的Artisan命令,Http目录包含了控制器、中间件和请求等。

    Jobs目录是放置队列任务的地方,应用中的任务可以被队列化,也可以在当前请求生命周期内同步执行。

    Events目录是放置事件类的地方,事件可以用于通知应用其它部分给定的动作已经发生,并提供灵活的解耦的处理。

    Listeners目录包含事件的处理器类,处理器接收一个事件并提供对该事件发生后的响应逻辑,比如,UserRegistered事件可以被SendWelcomeEmail监听器处理。

    Exceptions目录包含应用的异常处理器,同时还是处理应用抛出的任何异常的好地方。

    注意:app目录中的很多类都可以通过Artisan命令生成,要查看所有有效的命令,可以在终端中运行php artisan list make命令。

    2、配置

    内容包括数据库连接,队列以及邮件设置都放置在config文件夹中。这里的每一个文件都将返回一个数组,数组中的每个值都可以通过一个配置键进行访问,该配置键有文件名和后续的键组成,以点好(.)进行分隔。所以,可以在config/services.php中创建如下所示的信息。

    // config/services.php
    return [
        'sparkpost' => [
            'secret' => 'aaaaaa'
        ]
    ];

    现在可以使用

    config(‘services.sparkpost.secret)

    访问配置好的变量了。

    每个环境中的任何配置变量都应该放在.env文件中(而不是提交给源代码控制)。如果希望在每个环境使用不同的Bugsnag API密钥,可以将配置信息从.env中提取出来,如下所示:

    //config/services.php
    return [
        'bugsnag' => [
            'api_key' => env('BUGSNAG_API_KEY')
        ]
    ];

    .env()这个函数可以从.env文件中提取出该键名所对应的值。现在可以将该键名对应的信息添加到.env(当前环境的设置)和.env.example(适用于所有环境的模板)文件中。

    BUGSNAG_API_KEY = oinfp9813410942

    3、路由和控制器

    在学习web框架时,定义好应用程序的路由是第一个也是最重要的一个环节。没有路由,就无法与终端用户进行交互。

    在一个laravel应用程序中,一般是在routes/web.php中定义路由。如果是api的路由,则在api.php中定义路由。示例0.1就是一个路由定义的例子。

    在详细介绍路由之前,先介绍几个概念:

    闭包:闭包是php版本的匿名函数。闭包是一个函数,可以将它作为一个对象传递,并赋值给一个变量,将其作为参数传递给其他的函数和方法,甚至进行序列化。
    中间件:laravel的请求和响应的过程包含很多封装起来的内容,包括所谓的中间件。仅仅定义路由闭包以及控制器方法,还不足以将输出发送给浏览器,所以这里采用返回内容的方式,这样返回的内筒可以继续在response stack以及中间件中运行(即继续在程序中处理该返回的内容),运行完成后再返回给浏览器共终端用户查看。

    demo3.1

    //简单的网站
    
    Route::get('/',function(){
        return view('welcome');
    });
    
    Route::get('about',function(){
        return view('about');
    });
    
    Route::get('products',function(){
        return view('products');
    });
    Route::get('services',function(){
        return view('services');
    });

    上面的代码等价于

    $router->get('/',function(){
        return view('welcome');
    });
    
    $router->get('about',function(){
        return view('about');
    });
    
    $router->get('products',function(){
        return view('products');
    });
    $router->get('services',function(){
        return view('services');
    });

    常用的HTTP方法有:GET/POST/PUT/DELETE/PATCH
    进行替换就可以了。例如:

    Route::get('/',function(){
        return '123';
    });
    
    Route::post('/',function(){
        return '456';
    });
    
    Route::put('/',function(){});
    Route::delete('/',function(){});
    Route::any('/',function(){});
    Route::match(['get','post'],'/',function(){});

    另一种方法是将控制器名称和方法作为字符串传递给闭包

    Route::get('/','WelcomeController@index');

    路由参数:如果定义的路由具有参数(可变的额URL地址段),那么可以在路由中定义它们,并将它们传递给闭包。

    //路由参数示例
    Route::get('users/{id}/friends',function(){
        //
    });

    一般只有get方法这样写参数。
    在录用参数({id})中使用相同的名称,以及将对应的名字添加到路由定义的方法参数中(function{$sid)是十分常见的。除非使用路由/模型绑定,否则定义的路由参数与哪个方法参数相匹配仅由它们的顺序(从左到右)决定,如以下代码所示。

    Route::get('users'/{userId}/comments/{commentId}',function(
        $thisIsActuallyTheRouteId,
        $thisisReallyTheCommentId
        ){
        //
    });

    也就是说,可以让它们使用不同的名称,也可以使用相同的名称。这里建议使它们的名称保持一致,以免未来开发人员在使用的时候可能因为不一致的命名而出现问题。
    还可以用过在参数名称后添加一个问号(?)来实现路由参数的选择。

    //可选路由参数
    Route::get('users/{id?}',function($id = 'fallbackId'){
        //
    });

    在这种情况下,应该为相应的路由变量设置好默认值。并且可以使用正则表达式来定义一个路由,这个时候,只有该参数满足特定的模式时才会匹配

    //通过正则表达式来定义路由
    Route::get(;users/{id}',function($id){
        //
    })->where('id','[0-9]+');
    
    Route::get('users/{username}',function($username){
        //
    })->where('username','[A-Za-z]+');
    
    Route::get('posts/{id}/{slug}',function($id,$slug){
        //
    })->where(['id'] => '[0-9]+',]slug' => '[A-Za-z]+');

    路由名称
    url()助手函数:写相对路径,可以自动补全站点全称

    //在routes/web.php中定义具有名称的路由
    Route::get('members/{id}','MembersController@show')->name('members.show');
    
    //使用route()助手在视图中链接路由
    <a href="<php echo route('members.show',['id' = 4]); ?>">

    路由组
    允许多个路由组合在一起,并且可以将任何共享的配置应用于整个组,从而避免配置信息的重复。

    //定义一个路由组
    Route::group([],function(){
        Route::get('hello',function(){
            return 'Hello';
        });
        Route::get('world',function(){
            return ' World!';
        });
    });

    传入的空数组允许传递各种配置信息,这些配置将对组内的所有路由都生效。

    中间件
    路由组最常见的功能就是将中间件应用于一组路由中,但是在其他方面,路由组也常常被应用在权限控制方面。

    //将一组路由限制为只允许登录用户访问
    Route::group(['middleware' => 'auth'],function(){
        Route::get('aaa',function(){
            return view('aaaa');
        });
        Route::get('bbb',function(){
                return view('bbbb');
        });
    });

    此时中间件auth对aaa和bbb这两者都会生效。在此示例中,表示用户必须登录后才能查看控制中心(aaa)或账户页面(bbb)。

    中间件在控制器中的应用:
    通常,在控制器中使用中间件比在路由中使用中间件更为清晰和直接。可以在控制器中调用middleware()方法来使用中间件。参数代表中间件的名称,可以使用modifier方法(only()和except())来确定将由哪些方式接收中间件

    class DashboardController extends Controller{
        public function __construct(){
            $this->middleware('auth');
            $this->middleware('admin-auth')->only('admin');
            $this->middleware('team-member')->except('admin');
        }
    }

    路径前缀

    //为一组路由设置路径前缀
    Route::group(['prefix' => 'api'],function(){
        Route::get('/',function(){
            //设置path /api
        });
    
        Route::get('users',function(){
            //设置path /api/users
        });
    });

    子域名路由
    子域名路由的作用域是子域名,而不是路由前缀

    //子域名路由
    Route::group(['domain' => 'api.myapp.com'],function(){
        Route::get('/',function(){
            //
        });
    });
    
    //参数化的子域名路由
    Route::group(['domain'=> '{acount}.myapp.com'],function(){
        Route::get('/',function($acount){
            //
        });
        Route::get('users/{id}',function($acount,$id){
            //
        });
    });
    展开全文
  • Laravel--使用介绍

    千次阅读 2018-08-02 16:18:24
    Laravel--使用介绍   1、简介 Laravel 应用默认的目录结构试图为不管是大型应用还是小型应用提供一个好的起点,当然,你可以自己按照喜好重新组织应用目录结构,Laravel 对类在何处被加载没有任何限制——只要 ...

                                         Laravel--使用介绍

     

    1、简介

    Laravel 应用默认的目录结构试图为不管是大型应用还是小型应用提供一个好的起点,当然,你可以自己按照喜好重新组织应用目录结构,Laravel 对类在何处被加载没有任何限制——只要 Composer 可以自动载入它们即可。

    Models目录在哪里?

    许多初学者都会困惑Laravel为什么没有models目录,我可以负责任的告诉大家,这是故意的。因为models这个词对不同人而言有不同的含义,容易造成歧义,有些开发者认为应用的模型指的是业务逻辑,另外一些人则认为模型指的是与关联数据库的交互。

    正是因为这个原因,我们默认将Eloquent的模型直接放置到app目录下,从而允许开发者自行选择放置的位置,关注我的博客,larave详解,会告诉你的;

    2、根目录

    App目录

    app目录包含了应用的核心代码,此外你为应用编写的代码绝大多数也会放到这里;

    Bootstrap目录

    bootstrap目录包含了少许文件,用于框架的启动和自动载入配置,还有一个cache文件夹用于包含框架为提升性能所生成的文件,如路由和服务缓存文件;

    Config目录

    config目录包含了应用所有的配置文件,建议通读一遍这些配置文件以便熟悉所有配置项;

    Database目录

    database目录包含了数据迁移及填充文件,如果你喜欢的话还可以将其作为 SQLite 数据库存放目录;

    Public目录

    public目录包含了入口文件index.php和前端资源文件(图片、JavaScript、CSS等);

    Resources目录

    resources目录包含了视图文件及原生资源文件(LESS、SASS、CoffeeScript),以及本地化文件;

    Routes目录

    routes 目录包含了应用的所有路由定义。Laravel默认提供了三个路由文件:web.phpapi.phpconsole.php

    web.php文件包含的路由都会应用web中间件组,具备Session、CSRF防护以及Cookie加密功能,如果应用无需提供无状态的、RESTful风格的API,所有路由都会定义在web.php文件。

    api.php 文件包含的路由应用了api中间件组,具备频率限制功能,这些路由是无状态的,所以请求通过这些路由进入应用需要通过token进行认证并且不能访问Session状态。

    console.php 文件用于定义所有基于闭包的控制台命令,每个闭包都被绑定到一个控制台命令并且允许与命令行IO方法进行交互,尽管这个文件并不定义HTTP路由,但是它定义了基于控制台的应用入口(路由)。

    Storage目录

    storage目录包含了编译过的Blade模板、基于文件的session、文件缓存,以及其它由框架生成的文件,该目录被细分为成appframeworklogs子母录,app目录用于存放应用要使用的文件,framework目录用于存放框架生成的文件和缓存,最后,logs目录包含应用的日志文件;

    storage/app/public 目录用于存储用户生成的文件,比如可以被公开访问的用户头像,要达到被访问的目的,你还需要在public目录下生成一个软连接storage 指向这个目录。你可以通过php artisan storage:link 命令生成这个软链接。

    Tests目录

    tests目录包含自动化测试,其中已经提供了一个开箱即用的PHPUnit示例;每一个测试类都要以 Test 开头,你可以通过phpunitphp vendor/bin/phpunit 命令来运行测试。

    Vendor目录

    vendor目录包含Composer依赖。

    3、App目录

    应用的核心代码位于app目录下,默认情况下,该目录位于命名空间 App 下,  并且被 Composer 通过 PSR-4自动载入标准 自动加载。

    app目录下包含多个子目录,如ConsoleHttpProviders等。ConsoleHttp目录提供了进入应用核心的API,HTTP协议和CLI是和应用进行交互的两种机制,但实际上并不包含应用逻辑。换句话说,它们只是两个向应用发布命令的方式。Console目录包含了所有的Artisan命令,Http目录包含了控制器、中间件和请求等。

    其他目录将会在你通过Artisan命令make生成相应类的时候生成到app目录下。例如,app/Jobs目录直到你执行make:job 命令生成任务类时才会出现在app目录下。

    注意:app目录中的很多类都可以通过Artisan命令生成,要查看所有有效的命令,可以在终端中运行php artisan list make命令。

    Console目录

    Console目录包含应用所有自定义的Artisan命令,这些命令类可以使用make:command命令生成。该目录下还有console核心类,在这里可以注册自定义的Artisan命令以及定义调度任务。

    Events目录

    这个目录默认不存在,但是可以通过 event:generate make:event 命令创建。该目录用于存放事件类。事件类用于告知应用其他部分某个事件发生并提供灵活的、解耦的处理机制。

    Exceptions目录

    Exceptions目录包含应用的异常处理器,同时还是处理应用抛出的任何异常的好地方。如果你想要自定义异常如何记录异常或渲染,需要修改 Handler 类。

    Http目录

    Http 目录包含了控制器、中间件以及表单请求等,几乎所有进入应用的请求处理都在这里进行。

    Jobs目录

    该目录默认不存在,可以通过执行 make:job 命令生成,Jobs目录用于存放队列任务,应用中的任务可以被队列化,也可以在当前请求生命周期内同步执行。同步执行的任务有时也被看作命令,因为它们实现了命令模式。

    Listeners目录

    这个目录默认不存在,可以通过执行 event:generate make:listener 命令创建。Listeners目录包含处理事件的类(事件监听器),事件监听器接收一个事件并提供对该事件发生后的响应逻辑,例如,UserRegistered事件可以被SendWelcomeEmail监听器处理。

    Mail目录

    这个目录默认不存在,但是可以通过执行 make:mail 命令生成,Mail目录包含邮件发送类,邮件对象允许你在一个地方封装构建邮件所需的所有业务逻辑,然后使用Mail::send 方法发送邮件。

    Notifications目录

    这个目录默认不存在,你可以通过执行 make:notification 命令创建,Notifications 目录包含应用发送的所有通知,比如事件发生通知。Laravel的通知功能将通知发送和通知驱动解耦,你可以通过邮件,也可以通过Slack、短信或者数据库发送通知。

    Policies目录

    这个目录默认不存在,你可以通过执行 make:policy 命令来创建,Policies 目录包含了所有的授权策略类,策略用于判断某个用户是否有权限去访问指定资源。更多详情,请查看授权文档

    Providers目录

    Providers 目录包含应用的所有服务提供者。服务提供者在启动应用过程中绑定服务到容器、注册事件以及执行其他任务以为即将到来的请求处理做准备。

    在新安装的Laravel应用中,该目录已经包含了一些服务提供者,你可以按需添加自己的服务提供者到该目录。

    技术特点

    1、Bundle是Laravel的扩展包组织形式或称呼。Laravel的扩展包仓库已经相当成熟了,可以很容易的帮你把扩展包(bundle)安装到你的应用中。你可以选择下载一个扩展包(bundle)然后拷贝到bundles目录,或者通过命令行工具“Artisan”自动安装。

     

    2、在Laravel中已经具有了一套高级的PHP ActiveRecord实现 -- Eloquent ORM。它能方便的将“约束(constraints)”应用到关系的双方,这样你就具有了对数据的完全控制,而且享受到ActiveRecord的所有便利。Eloquent原生支持Fluent中查询构造器(query-builder)的所有方法。

     

    3、应用逻辑(Application Logic)可以在控制器(controllers)中实现,也可以直接集成到路由(route)声明中,并且语法和Sinatra框架类似。Laravel的设计理念是:给开发者以最大的灵活性,既能创建非常小的网站也能构建大型的企业应用。

     

    4、反向路由(Reverse Routing)赋予你通过路由(routes)名称创建链接(URI)的能力。只需使用路由名称(route name),Laravel就会自动帮你创建正确的URI。这样你就可以随时改变你的路由(routes),Laravel会帮你自动更新所有相关的链接。

     

    5、Restful控制器(Restful Controllers)是一项区分GET和POST请求逻辑的可选方式。比如在一个用户登陆逻辑中,你声明了一个get_login()的动作(action)来处理获取登陆页面的服务;同时也声明了一个post_login()动作(action)来校验表单POST过来的数据,并且在验证之后,做出重新转向(redirect)到登陆页面还是转向控制台的决定。

     

    6、自动加载类(Class Auto-loading)简化了类(class)的加载工作,以后就可以不用去维护自动加载配置表和非必须的组件加载工作了。当你想加载任何库(library)或模型(model)时,立即使用就行了,Laravel会自动帮你加载需要的文件。

     

    7、视图组装器(View Composers)本质上就是一段代码,这段代码在视图(View)加载时会自动执行。最好的例子就是博客中的侧边随机文章推荐,“视图组装器”中包含了加载随机文章推荐的逻辑,这样,你只需要加载内容区域的视图(view)就行了,其它的事情Laravel会帮你自动完成。

     

    8、反向控制容器(IoC container)提供了生成新对象、随时实例化对象、访问单例(singleton)对象的便捷方式。反向控制(IoC)意味着你几乎不需要特意去加载外部的库(libraries),就可以在代码中的任意位置访问这些对象,并且不需要忍受繁杂、冗余的代码结构。

     

    9、迁移(Migrations)就像是版本控制(version control)工具,不过,它管理的是数据库范式,并且直接集成在了Laravel中。你可以使用“Artisan”命令行工具生成、执行“迁移”指令。当你的小组成员改变了数据库范式的时候,你就可以轻松的通过版本控制工具更新当前工程,然后执行“迁移"指令即可,好了,你的数据库已经是最新的了!

     

    10、单元测试(Unit-Testing)是Laravel中很重要的部分。Laravel自身就包含数以百计的测试用例,以保障任何一处的修改不会影响其它部分的功能,这就是为什么在业内Laravel被认为是最稳版本的原因之一。Laravel也提供了方便的功能,让你自己的代码容易的进行单元测试。通过Artisan命令行工具就可以运行所有的测试用例。

     

    11、自动分页(Automatic Pagination)功能避免了在你的业务逻辑中混入大量无关分页配置代码。方便的是不需要记住当前页,只要从数据库中获取总的条目数量,然后使用limit/offset获取选定的数据,最后调用‘paginate’方法,让Laravel将各页链接输出到指定的视图(View)中即可,Laravel会替你自动完成所有工作。Laravel的自动分页系统被设计为容易实现、易于修改。虽然Laravel可以自动处理这些工作,但是不要忘了调用相应方法和手动配置分页系统哦
     

    配置即一切

    一切皆于需求,后台从0开始搭建,但是写了一两个页面后发现太多的是对单表的增删改查操作,于是就想到了,能不能做一个快速搭建的后台。想到一句话,配置即一切。如果一个CURD后台能只进行配置就自动生成,该是多么美妙的事情,那么就开始搭建这么个结构。

     

    首先配置文件应该怎么设计

    起初想到将配置文件放到config目录下,但是想想还是放弃了这个想法,那样子可能会导致有一个“万能”文件,又臭又长。那么,其次,这个功能只针对单表,所以,是不是可以将配置文件放置在Model中,后来也觉得这个想法不大好,这个配置文件是承担页面展示的功能的,如果放在Model中就算是入侵了Model层了。所以最后决定放在了Controller中。

     

    最后的效果大概是什么样子的?

    后台大概会有几个页面:

    列表页:

    列表页中有查询操作,编辑,删除按钮,新建按钮。

    新建页面:

     

    编辑页面:

     

    好了,对应这几个页面,我们可以设置配置项了。

     

    基本想法是搭建一个FormController,所有以后需要配置生成后台的controller就继承这个FormController就好了。在FormController中定义属性:

    复制代码

    class FormController extends BaseController {
     
         // 对应的模型
         protected $model;
     
         // 所有的字段
         protected $fields_all;
     
         // 列表页显示的字段
         protected $fields_show;
     
         // 编辑页面显示的字段
         protected $fields_edit;
     
         // 创建页面显示的字段
         protected $fields_create;
    }

    复制代码

    定义了Model,来表示这个Controller是对那个Model进行单表操作的。

    定义了fields_all属性,来将所有的字段来进行一个说明和定义。这个定义和说明就包括字段显示名字,字段是否要进行搜索,字段类型是什么。

    对于列表页,不是所有属性都显示出来,所以定义一个fieldsshow,这个数组存放的是

    fields_all中的一些字段,用来显示的字段。

    对于编辑页面,要显示的字段就放在$field_edit中

    对于创建页面,要显示的字段就放在$field_create中

     

    好了,现在继承FormController的类就只需要这么配置就好;

    复制代码

    <?php
     
    // 账号管理系统
    class BadminController extends FormController
    {
     
         public function __construct()
         {
              $this->model = '\Badmin';
              $this->fields_all = [
                   'id' => [
                        'show' => '序号',
                   ],
                   'nickname' => [
                        'show' => '昵称',
                        'search' => "nickname like CONCAT('%', ?, '%')"
                   ],
                   'username' => [
                        'show' => '用户名',
                   ],
                   'email' => [
                        'show' => '邮箱',
                   ],
                   'password' => [
                        'show' => '密码',
                   ],
                   'created_at' => [
                        'show' => '创建时间',
                   ],
                   'updated_at' => [
                        'show' => '更新时间',
                   ],
              ];
     
              $this->fields_show = ['id' ,'nickname', 'username', 'email', 'created_at'];
              $this->fields_edit = ['nickname', 'username'];
              $this->fields_create = ['nickname', 'username', 'email', 'password'];
              parent::__construct();
         }
    } 

    复制代码

    在构造函数中定义model,fieldsall,

    fields_show, fieldsedit,

    fields_create。

    对于fields_all,key为数据库对应的字段名,value为一个数组,show是显示名,如果你在列表页希望这个字段能进行搜索,就设置下search属性。

     

    路由

    下面是路由,laravel中路由基本有三种:

    Route::get('order/{id}',['as'=>'order.detail','uses'=>'OrderController@show']);
    Route::controller('preview', 'PreviewController');
    Route::resource('badmin', 'BadminController');

     

    第三种已经完全定义好了增删改查操作,看起来能省我不少的事情,好吧,我就使用这个resource来做了。

     

    所以在route.php中我只需要定义这么一条就ok了

     

    // 管理员账号管理
    Route::resource('badmin', 'BadminController');

     

    Controller 

    下面写FromController中的resource方法

     

    按照laravel的resource定义的,需要填充的方法有:

     

    我习惯在构造函数中把一些诸如Input,全局定义的东西都share到模版中,代码如下:

    复制代码

         public function __construct()
         {
     
              // TODO:做一些基础的判断,如果没有的话就抛出异常
             
              $route = Route::currentRouteAction();
              list($this->controller, $action) = explode('@', $route);
              View::share('controller', $this->controller);
     
              $fields_show = array();
              foreach ($this->fields_show as $field) {
                   $fields_show[$field] = $this->fields_all[$field];
              }
              View::share('fields_show', $fields_show);
     
              $fields_edit = array();
              foreach ($this->fields_edit as $field) {
                   $fields_edit[$field] = $this->fields_all[$field];
              }
              View::share('fields_edit', $fields_edit);
     
              $fields_create = array();
              foreach ($this->fields_create as $field) {
                   $fields_create[$field] = $this->fields_all[$field];
              }
              View::share('fields_create', $fields_create);
     
              View::share('input', Input::all());
         }

    复制代码

     

    这里把controller放到外面是为了在view中可以使用诸如:

    action(controller.′@destroy′,

    model->id),

    的路径定义

     

    index函数:

     

    复制代码

     
         public function index()
         {
              $model = new $this->model;
              $builder = $model->orderBy('id', 'desc');
     
              $input = Input::all();
              foreach ($input as $field => $value) {
                   if (empty($value)) {
                        continue;
                   }
                   if (!isset($this->fields_all[$field])) {
                        continue;
                   }
                   $search = $this->fields_all[$field];
                   $builder->whereRaw($search['search'], [$value]);
              }
              $models = $builder->paginate(20);
     
              return View::make('form.index', [
                   'models' => $models,
              ]);
         }
     

    复制代码

    $builder在laravel中真是太TMD好用了,对于这里的搜索,我使用whereRaw进行prepare查询。这里还有一个点,之前在fields_all设计的时候,我定义的直接是一个 'search' => "nickname like CONCAT('%', ?, '%')"  这里定义搜索字段的时候其实有很多种设计方法,比如定义为

    ‘search’ => [
         'type' => 'like',
         'value' => '%?%'
    ]

     

     

    但是考虑到使用这个FromController的都是程序员,所以这里的search直接使用预处理的语句,然后在index中直接使用whereRaw,这样使得配置文件的易读性增加了。

     

    下面是

    create函数:

         public function create()
         {
              return View::make('form.create', []);
         }
     

     

    store函数:

    复制代码

         public function store()
         {
              $model = new $this->model;
              $model->fill(Input::all());
              $model->save();
              return Redirect::to(action($this->controller . '@index'));
         }
     

    复制代码

     

    这里的model的fill是不是很简单,爽到爆。当然model中还是需要定义fillable字段

     

    edit,update,destory函数

    如法炮制就好

     

    复制代码

         public function edit($id)
         {
              $model = new $this->model;
              $model = $model->find($id);
              return View::make('form.edit', compact('model'));
         }
     
         public function update($id)
         {
              $model = new $this->model;
              $model = $model->find($id);
              $model->fill(Input::all());
              $model->save();
     
              return Redirect::to(action($this->controller . '@index'));
         }
     
         public function destroy($id)
         {
              $model = new $this->model;
              $model->destroy($id);
             
              return Redirect::to(action($this->controller . '@index'));
         }
     

    复制代码

     

     View

    下面就是view的编写。

    view大概就只要三个页面,列表页面,编辑页面,创建页面

     

    列表页面注意事项:

    1 使用laravel自带分页,注意记得带上本页的输入参数,这个时候,构造函数中share的Input就很有用了

    {{models−>appends(

    input)->links()}}

     

    2 可以使用laravel自带的from操作,比如删除操作由于需要调用HTTP的DELETE 方法,可以这么写

    复制代码

                     {{ Form::open(array(
                      'id' => "delete_{$model->id}",
                      'url' => action($controller . '@destroy', $model->id),
                      'class' => 'dropdown-toggle')) }}
                        {{ Form::hidden('_method', 'DELETE') }}
                      {{ Form::close() }}

    复制代码

     

     

    其实自己写DELETE也行,就是在From表单中多传递一个_method隐藏域

     

    3 搜索直接使用一个form就可以搞定了

    复制代码

         <form class="form-inline" role="form" action="{{action($controller . '@index')}}">
              @foreach ($fields_show as $field => $field_info)
                @if (isset($field_info['search']))
                <div class="form-group">
                  <label class="col-sm-3 control-label">{{$field_info['show']}}</label>
                  <div class="col-md-3">
                    <input name="{{$field}}" type="input" class="form-control" placeholder="" value="@if (isset($input[$field])){{$input[$field]}}@endif">
                  </div>
                </div>
                @endif
              @endforeach
              <input type="submit" class="btn btn-success" value="查询" />
            </form>

    复制代码

     

    编辑页面和创建页面

    简单到只需要一个form就能搞定了

     

    复制代码

      <form class="form-horizontal"
              role="form"
              action="{{action($controller . "@update", $model->id)}}" method='POST'>
              <input type="hidden" name="_method" value="PUT">
                @foreach ($fields_edit as $field => $field_info)
                <div class="form-group">
                  <label class="col-sm-2 control-label">{{$field_info['show']}}</label>
                  <div class="col-sm-10">
                    <input name="{{$field}}" type="text" class="form-control" placeholder="" value="{{$model->$field}}">
                  </div>
                </div>
                <div class="line line-dashed line-lg pull-in"></div>
                @endforeach
                <div class="col-sm-4 col-sm-offset-2">
                  <button type="submit" class="btn btn-primary">提交</button>
                </div>
              </form>

    复制代码

     

     

    记得resource中更新的操作是要使用PUT方式,删除的操作要使用DELETE方式。

    至于view的模版,我这里使用的是一款叫notebook的模版,它是基于bootstrap的,你也可以使用其他更好看的模版来写。

     

    好了,至此这么个快速搭建CURD的结构就完成了。现在可以在运营人员给需求的时候,很牛逼地说,等我一分钟,我就给你一个世界~~

    laravel改变搜索的对象变数组

    路径  App/http/config/database.php

    找到:  'fetch' => PDO::FETCH_CLASS,
    改成:  'fetch' => PDO::FETCH_ASSOC,

    后记

    其实回想下,这整个结构不算复杂。配置即一切的思想能解决很多问题。但是依赖配置的路子最怕的是几个事情:

    1 配置文件过于复杂。(如果你的配置文件过于复杂,已经超过了敲代码本身需要了解的东西,那么这个配置项的学习成本就太太太高了)

    2 配置字段语意不清。(配置的字段名字和意思不对,字段名和变量名一样重要!)

     

    当然这个就是个初步,改进的几个点还有

    1 所有字段都使用input标签,需要在配置中加入其它标签类型

    2 是不是考虑view中所有的东西都使用laravel自带的form对应字段?

     

    展开全文
  • Laravel的核心概念

    万次阅读 多人点赞 2020-03-27 10:26:01
    确实如此,这篇文章读完你可能并不能从无到有写出一个博客,但知道Laravel的核心概念之后,当你再次写起Laravel时,会变得一目了然胸有成竹。PHP的生命周期万物皆有他的生命周期。熟悉Android的同学一定熟悉Android...

    工欲善其事,必先利其器。在开发Xblog的过程中,稍微领悟了一点Laravel的思想。确实如此,这篇文章读完你可能并不能从无到有写出一个博客,但知道Laravel的核心概念之后,当你再次写起Laravel时,会变得一目了然胸有成竹。

    PHP的生命周期

    万物皆有他的生命周期。熟悉Android的同学一定熟悉Android最经典的Activity的生命周期,Laravel 也不例外,Laravel应用程序也有自己的生命周期。Laravel是什么?一个PHP框架。所以要想真正说清Laravel的生命周期,必须说清PHP的生命周期。原文参考这里,这里做个总结。
    PHP有两种运行模式,WEB模式和CLI(命令行)模式。当我们在终端敲入php这个命令的时候,使用的是CLI模式;当使用Nginx或者别web服务器作为宿主处理一个到来的请求时,会调用PHP运行,此时使用的是WEB模式。当我们请求一个PHP文件时,比如Laravel 的public\index.php文件时,PHP为了完成这次请求,会发生5个阶段的生命周期切换:

    1. 模块初始化(MINIT),即调用php.ini中指明的扩展的初始化函数进行初始化工作,如mysql扩展。
    2. 请求初始化(RINIT),即初始化为执行本次脚本所需要的变量名称和变量值内容的符号表,如$_SESSION变量。
    3. 执行该PHP脚本。
    4. 请求处理完成(Request Shutdown),按顺序调用各个模块的RSHUTDOWN方法,对每个变量调用unset函数,如unset $_SESSION变量。
    5. 关闭模块(Module Shutdown) , PHP调用每个扩展的MSHUTDOWN方法,这是各个模块最后一次释放内存的机会。这意味着没有下一个请求了。

    WEB模式和CLI(命令行)模式很相似,区别是:CLI 模式会在每次脚本执行经历完整的5个周期,因为你脚本执行完不会有下一个请求;而WEB模式为了应对并发,可能采用多线程,因此生命周期15有可能只执行一次,下次请求到来时重复2-4的生命周期,这样就节省了系统模块初始化所带来的开销。

    可以看到,PHP生命周期是很对称的。说了这么多,就是为了定位Laravel运行在哪里,没错,Laravel仅仅运行再第三个阶段:
    这里写图片描述

    知道这些有什么用?你可以优化你的Laravel代码,可以更加深入的了解Larave的singleton(单例)。至少你知道了,每一次请求结束,Php的变量都会unset,Laravel的singleton只是在某一次请求过程中的singleton;你在Laravel 中的静态变量也不能在多个请求之间共享,因为每一次请求结束都会unset。理解这些概念,是写高质量代码的第一步,也是最关键的一步。因此记住,PHP是一种脚本语言,所有的变量只会在这一次请求中生效,下次请求之时已被重置,而不像Java静态变量拥有全局作用。

    好了,开始Laravel的生命周期。

    Laravel的生命周期

    概述

    Laravel 的生命周期从public\index.php开始,从public\index.php结束。
    这里写图片描述

    注意:以下几图箭头均代表Request流向

    这么说有点草率,但事实确实如此。下面是public\index.php的全部源码(Laravel源码的注释是最好的Laravel文档),更具体来说可以分为四步:

    1. require __DIR__.'/../bootstrap/autoload.php';
    
    2. $app = require_once __DIR__.'/../bootstrap/app.php';
       $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
    
    3. $response = $kernel->handle(
        $request = Illuminate\Http\Request::capture()
       );
       $response->send();
    
    4. $kernel->terminate($request, $response);
    

    这四步详细的解释是:

    1.注册加载composer自动生成的class loader,包括所有你composer require的依赖(对应代码1).
    2.生成容器Container,Application实例,并向容器注册核心组件(HttpKernelConsoleKernelExceptionHandler)(对应代码2,容器很重要,后面详细讲解)。
    3.处理请求,生成并发送响应(对应代码3,毫不夸张的说,你99%的代码都运行在这个小小的handle方法里面)。
    4.请求结束,进行回调(对应代码4,还记得可终止中间件吗?没错,就是在这里回调的)。

    这里写图片描述

    启动Laravel基础服务

    我们不妨再详细一点:
    第一步注册加载composer自动生成的class loader就是加载初始化第三方依赖,不属于Laravel核心,到此为止。
    第二步生成容器Container,并向容器注册核心组件,这里牵涉到了容器Container和合同Contracts,这是Laravel的重点,下面将详细讲解。
    重点是第三步处理请求,生成并发送响应。
    首先Laravel框架捕获到用户发到public\index.php的请求,生成Illuminate\Http\Request实例,传递给这个小小的handle方法。在方法内部,将该$request实例绑定到第二步生成的$app容器上。让后在该请求真正处理之前,调用bootstrap方法,进行必要的加载和注册,如检测环境,加载配置,注册Facades(假象),注册服务提供者,启动服务提供者等等。这是一个启动数组,具体在Illuminate\Foundation\Http\Kernel中,包括:

    protected $bootstrappers = [
        'Illuminate\Foundation\Bootstrap\DetectEnvironment',
        'Illuminate\Foundation\Bootstrap\LoadConfiguration',
        'Illuminate\Foundation\Bootstrap\ConfigureLogging',
        'Illuminate\Foundation\Bootstrap\HandleExceptions',
        'Illuminate\Foundation\Bootstrap\RegisterFacades',
        'Illuminate\Foundation\Bootstrap\RegisterProviders',
        'Illuminate\Foundation\Bootstrap\BootProviders',
    ];
    

    看类名知意,Laravel是按顺序遍历执行注册这些基础服务的,注意顺序:Facades先于ServiceProvidersFacades也是重点,后面说,这里简单提一下,注册Facades就是注册config\app.php中的aliases 数组,你使用的很多类,如AuthCache,DB等等都是Facades;而ServiceProviders的register方法永远先于boot方法执行,以免产生boot方法依赖某个实例而该实例还未注册的现象。

    所以,你可以在ServiceProviders的register方法中使用任何Facades,在ServiceProvidersboot方法中使用任何register方法中注册的实例或者Facades,这样绝不会产生依赖某个类而未注册的现象。

    将请求传递给路由

    注意到目前为止,Laravel 还没有执行到你所写的主要代码(ServiceProviders中的除外),因为还没有将请求传递给路由。

    在Laravel基础的服务启动之后,就要把请求传递给路由了。传递给路由是通过Pipeline(另开篇章讲解)来传递的,但是Pipeline有一堵墙,在传递给路由之前所有请求都要经过,这堵墙定义在app\Http\Kernel.php中的$middleware数组中,没错就是中间件,默认只有一个CheckForMaintenanceMode中间件,用来检测你的网站是否暂时关闭。这是一个全局中间件,所有请求都要经过,你也可以添加自己的全局中间件。

    然后遍历所有注册的路由,找到最先符合的第一个路由,经过它的路由中间件,进入到控制器或者闭包函数,执行你的具体逻辑代码。

    所以,在请求到达你写的代码之前,Laravel已经做了大量工作,请求也经过了千难万险,那些不符合或者恶意的的请求已被Laravel隔离在外。
    这里写图片描述

    服务容器

    服务容器就是一个普通的容器,用来装类的实例,然后在需要的时候再取出来。用更专业的术语来说是服务容器实现了控制反转(Inversion of Control,缩写为IoC),意思是正常情况下类A需要一个类B的时候,我们需要自己去new类B,意味着我们必须知道类B的更多细节,比如构造函数,随着项目的复杂性增大,这种依赖是毁灭性的。控制反转的意思就是,将类A主动获取类B的过程颠倒过来变成被动,类A只需要声明它需要什么,然后由容器提供。
    这里写图片描述

    这样做的好处是,类A不依赖于类B的实现,这样在一定程度上解决了耦合问题。

    在Laravel的服务容器中,为了实现控制反转,可以有以下两种:

    1. 依赖注入(Dependency Injection)。
    2. 绑定。

    依赖注入

    依赖注入是一种类型提示,举官网的例子:

    class UserController extends Controller
    {
        /**
         * The user repository implementation.
         *
         * @var UserRepository
         */
        protected $users;
    
        /**
         * Create a new controller instance.
         *
         * @param  UserRepository  $users
         * @return void
         */
        public function __construct(UserRepository $users)
        {
            $this->users = $users;
        }
    
        /**
         * Show the profile for the given user.
         *
         * @param  int  $id
         * @return Response
         */
        public function show($id)
        {
            $user = $this->users->find($id);
    
            return view('user.profile', ['user' => $user]);
        }
    }
    

    这里UserController需要一个UserRepository实例,我们只需在构造方法中声明我们需要的类型,容器在实例化UserController时会自动生成UserRepository的实例(或者实现类,因为UserRepository可以为接口),而不用主动去获取UserRepository的实例,这样也就避免了了解UserRepository的更多细节,也不用解决UserRepository所产生的依赖,我们所做的仅仅是声明我们所需要的类型,所有的依赖问题都交给容器去解决。(Xblog使用了Repository的是设计模式,大家可以参考)

    绑定

    绑定操作一般在ServiceProviders中的register方法中,最基本的绑定是容器的bind方法,它接受一个类的别名或者全名和一个闭包来获取实例:

    $this->app->bind('XblogConfig', function ($app) {
        return new MapRepository();
    });
    

    还有一个singleton方法,和bind写法没什么区别。你也可以绑定一个已经存在的对象到容器中,上文中提到的request实例就是通过这种方法绑定到容器的:$this->app->instance('request', $request);。绑定之后,我们可以通过一下几种方式来获取绑定实例:

    1.  app('XblogConfig');
    
    2.  app()->make('XblogConfig');
    
    3.  app()['XblogConfig'];
    
    4.  resolve('XblogConfig');
    

    以上四种方法均会返回获得MapRepository的实例,唯一的区别是,在一次请求的生命周期中bind方法的闭包会在每一次调用以上四种方法时执行,singleton方法的闭包只会执行一次。在使用中,如果每一个类要获的不同的实例,或者需要“个性化”的实例时,这时我们需要用bind方法以免这次的使用对下次的使用造成影响;如果实例化一个类比较耗时或者类的方法不依赖该生成的上下文,那么我们可以使用singleton方法绑定。singleton方法绑定的好处就是,如果在一次请求中我们多次使用某个类,那么只生成该类的一个实例将节省时间和空间。

    你也可以绑定接口与实现,例如:

    $app->singleton(
        Illuminate\Contracts\Http\Kernel::class,
        App\Http\Kernel::class
    );
    

    上文讲述的Laravel的生命周期的第二步,Laravel默认(在bootstrap\app.php文件中)绑定了Illuminate\Contracts\Http\KernelIlluminate\Contracts\Console\KernelIlluminate\Contracts\Debug\ExceptionHandler接口的实现类,这些是实现类框架的默认自带的。但是你仍然可以自己去实现。

    还有一种上下文绑定,就是相同的接口,在不同的类中可以自动获取不同的实现,例如:

    $this->app->when(PhotoController::class)
              ->needs(Filesystem::class)
              ->give(function () {
                  return Storage::disk('local');
              });
    
    $this->app->when(VideoController::class)
              ->needs(Filesystem::class)
              ->give(function () {
                  return Storage::disk('s3');
              });
    

    上述表明,同样的接口Filesystem,使用依赖注入时,在PhotoController中获取的是local存储而在VideoController中获取的是s3存储。

    Contracts & Facades(契约&门面)

    Laravel 还有一个强大之处是,比如你只需在配置文件中指明你需要的缓存驱动(redismemcachedfile…),Laravel 就自动办你切换到这种驱动,而不需要你针对某种驱动更改逻辑和代码。Why? 很简单,Laravel定义了一系列Contracts(翻译:契约),本质上是一系列PHP接口,一系列的标准,用来解耦具体需求对实现的依赖关系。其实真正强大的公司是制定标准的公司,程序也是如此,好的标准(接口)尤为重要。当程序变得越来大,这种通过合同或者接口来解耦所带来的可扩展性和可维护性是无可比拟的。
    这里写图片描述

    上图不使用Contracts的情况下,对于一种逻辑,我们只能得到一种结果(方块),如果变更需求,意味着我们必须重构代码和逻辑。但是在使用Contracts的情况下,我们只需要按照接口写好逻辑,然后提供不同的实现,就可以在不改动代码逻辑的情况下获得更加多态的结果。

    这么说有点抽象,举一个真实的例子。在完成Xblog的初期,我使用了缓存,所以导致Repository中充满了和cache相关的方法:rememberflushforget等等。后来国外网友反映,简单的博客并不一定需要缓存。所以我决定把它变成可选,但因为代码中充满和cache相关的方法,实现起来并不是很容易。于是想起Laravel的重要概念Contracts。于是,我把与缓存有关的方法抽象出来形成一个Contracts:XblogCache,实际操作只与Contracts有关,这样问题就得到了解决,而几乎没有改变原有的逻辑。XblogCache的代码如下(源码点击这里):

    namespace App\Contracts;
    use Closure;
    interface XblogCache
    {
        public function setTag($tag);
        public function setTime($time_in_minute);
        public function remember($key, Closure $entity, $tag = null);
        public function forget($key, $tag = null);
        public function clearCache($tag = null);
        public function clearAllCache();
    }
    

    然后,我又完成了两个实现类:CacheableNoCache

    1. 实现具体缓存。
    class Cacheable implements XblogCache
    {
    	public $tag;
    	public $cacheTime;
    	public function setTag($tag)
    	{
    		$this->tag = $tag;
    	}
    	public function remember($key, Closure $entity, $tag = null)
    	{
    		return cache()->tags($tag == null ? $this->tag : $tag)->remember($key, $this->cacheTime, $entity);
    	}
    	public function forget($key, $tag = null)
    	{
    		cache()->tags($tag == null ? $this->tag : $tag)->forget($key);
    	}
    	public function clearCache($tag = null)
    	{
    		cache()->tags($tag == null ? $this->tag : $tag)->flush();
    	}
    	public function clearAllCache()
    	{
    		cache()->flush();
    	}
    	public function setTime($time_in_minute)
    	{
    		$this->cacheTime = $time_in_minute;
    	}
    }
    
    1. 不缓存。
    class NoCache implements XblogCache
    {
    	public function setTag($tag)
    	{
        // Do Nothing
    	}
    	public function setTime($time_in_minute)
    	{
        // Do Nothing
    	}
    	public function remember($key, Closure $entity, $tag = null)
    	{
    	    /**
    	     * directly return
    	     */
    	    return $entity();
    	}
    	public function forget($key, $tag = null)
    	{
    	    // Do Nothing
    	}
    	public function clearCache($tag = null)
    	{
    	    // Do Nothing
    	}
    	public function clearAllCache()
    	{
    	    // Do Nothing
    	}
    }
    

    然后再利用容器的绑定,根据不同的配置,返回不同的实现(源码):

    public function register()
    {
    	$this->app->bind('XblogCache', function ($app) {
    		if (config('cache.enable') == 'true') {
    			return new Cacheable();
    		} else {
    			return new NoCache();
    		}
    	});
    }
    

    这样,就实现了缓存的切换而不需要更改你的具体逻辑代码。当然依靠接口而不依靠具体实现的好处不仅仅这些。实际上,Laravel所有的核心服务都是实现了某个Contracts接口(都在Illuminate\Contracts\文件夹下面),而不是依赖具体的实现,所以完全可以在不改动框架的前提下,使用自己的代码改变Laravel框架核心服务的实现方式。

    说一说Facades。在我们学习了容器的概念后,Facades就变得十分简单了。在我们把类的实例绑定到容器的时候相当于给类起了个别名,然后覆盖Facade的静态方法getFacadeAccessor并返回你的别名,然后你就可以使用你自己的Facade的静态方法来调用你绑定类的动态方法了。其实Facade类利用了__callStatic() 这个魔术方法来延迟调用容器中的对象的方法,这里不过多讲解,你只需要知道Facade实现了将对它调用的静态方法映射到绑定类的动态方法上,这样你就可以使用简单类名调用而不需要记住长长的类名。这也是Facades的中文翻译为假象的原因。

    总结

    Laravel强大之处不仅仅在于它给你提供了一系列脚手架,比如超级好用的ORM,基于Carbon的时间处理,以及文件存储等等功能。但是Laravel的核心非常非常简单:利用容器抽象解耦,实现高扩展性。容器和抽象是所有大型框架必须解决的问题,像Java的Spring,Android的Dagger2等等都是围绕这几个问题的。所以本质上讲,Laravel之所以强大出名,是因为它的设计,思想,可扩展性。而Laravel的好用功能只是官方基于这些核心提供的脚手架,你同样也可以很轻松的添加自己的脚手架。

    所以不要觉得Laravel强大是因为他提供的很多功能,而是它的设计模式和思想。

    1. 理解Laravel生命周期和请求的生命周期概念。
    2. 所有的静态变量和单例,在下一个请求到来时都会重新初始化。
    3. 将耗时的类或者频繁使用的类用singleton绑定。
    4. 将变化选项的抽象为Contracts,依赖接口不依赖具体实现。
    展开全文
  • 关联关系 One To One 假设User模型关联了Phone模型,要定义这样一个关联,需要在User模型中定义一个phone方法,该方法返回一个hasOne方法定义的关联 <?... namespace App; use Illuminate\Database\Eloquent\Model;...
        

    关联关系

    One To One

    假设User模型关联了Phone模型,要定义这样一个关联,需要在User模型中定义一个phone方法,该方法返回一个hasOne方法定义的关联

    <?php
    
    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    
    class User extends Model
    {
    
        public function phone()
        {
            return $this->hasOne('App\Phone');
        }
    }
    

    hasOne方法的第一个参数为要关联的模型,定义好之后,可以使用下列语法查询到关联属性了

    $phone = User::find(1)->phone;
    

    Eloquent会假定关联的外键是基于模型名称的,因此Phone模型会自动使用user_id字段作为外键,可以使用第二个参数和第三个参数覆盖

    return $this->hasOne('App\Phone', 'foreign_key');
    return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
    

    定义反向关系

    定义上述的模型之后,就可以使用User模型获取Phone模型了,当然也可以通过Phone模型获取所属的User了,这就用到了belongsTo方法了

    <?php
    
    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Phone extends Model
    {
    
        public function user()
        {
            return $this->belongsTo('App\User');
            // return $this->belongsTo('App\User', 'foreign_key');
            // return $this->belongsTo('App\User', 'foreign_key', 'other_key');
    
        }
    }
    

    One To Many

    假设有一个帖子,它有很多关联的评论信息,这种情况下应该使用一对多的关联,使用hasMany方法

    <?php
    
    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Post extends Model
    {
    
        public function comments()
        {
            return $this->hasMany('App\Comment');
        }
    }
    

    查询操作

    $comments = App\Post::find(1)->comments;
    foreach ($comments as $comment) {
        //
    }
    
    $comments = App\Post::find(1)->comments()->where('title', 'foo')->first();
    

    定义反向关联

    反向关联也是使用belongsTo方法,参考One To One部分。

    $comment = App\Comment::find(1);
    echo $comment->post->title;
    

    Many To Many

    多对多关联因为多了一个中间表,实现起来比hasOnehasMany复杂一些。

    考虑这样一个场景,用户可以属于多个角色,一个角色也可以属于多个用户。这就引入了三个表: users, roles, role_user。其中role_user表为关联表,包含两个字段user_idrole_id

    多对多关联需要使用belongsToMany方法

    
    <?php
    
    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    
    class User extends Model
    {
    
        public function roles()
        {
            // 指定关联表
            // return $this->belongsToMany('App\Role', 'role_user');
            // 指定关联表,关联字段
            // return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id');
    
            return $this->belongsToMany('App\Role');
        }
    }
    

    上述定义了一个用户属于多个角色,一旦该关系确立,就可以查询了

    $user = App\User::find(1);
    foreach ($user->roles as $role) {
        //
    }
    
    $roles = App\User::find(1)->roles()->orderBy('name')->get();
    

    反向关联关系

    反向关系与正向关系实现一样

    <?php
    
    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Role extends Model
    {
    
        public function users()
        {
            return $this->belongsToMany('App\User');
        }
    }
    

    检索中间表的列值

    对多对多关系来说,引入了一个中间表,因此需要有方法能够查询到中间表的列值,比如关系确立的时间等,使用pivot属性查询中间表

    
    $user = App\User::find(1);
    
    foreach ($user->roles as $role) {
        echo $role->pivot->created_at;
    }
    

    上述代码访问了中间表的created_at字段。

    注意的是,默认情况下之后模型的键可以通过pivot对象进行访问,如果中间表包含了额外的属性,在指定关联关系的时候,需要使用withPivot方法明确的指定列名

    return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');
    

    如果希望中间表自动维护created_atupdated_at字段的话,需要使用withTimestamps()

    return $this->belongsToMany('App\Role')->withTimestamps();
    

    Has Many Through

    这种关系比较强大,假设这样一个场景:Country模型下包含了多个User模型,而每个User模型又包含了多个Post模型,也就是说一个国家有很多用户,而这些用户都有很多帖子,我们希望查询某个国家的所有帖子,怎么实现呢,这就用到了Has Many Through关系

    countries
        id - integer
        name - string
    
    users
        id - integer
        country_id - integer
        name - string
    
    posts
        id - integer
        user_id - integer
        title - string
    

    可以看到,posts表中并不直接包含country_id,但是它通过users表与countries表建立了关系

    使用Has Many Through关系

    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Country extends Model
    {
    
        public function posts()
        {
            // return $this->hasManyThrough('App\Post', 'App\User', 'country_id', 'user_id');
    
            return $this->hasManyThrough('App\Post', 'App\User');
        }
    }
    

    方法hasManyThrough的第一个参数是我们希望访问的模型名称,第二个参数是中间模型名称。

    HasManyThrough hasManyThrough( 
        string $related, 
        string $through, 
        string|null $firstKey = null, 
        string|null $secondKey = null, 
        string|null $localKey = null
    )
    

    Polymorphic Relations (多态关联)

    多态关联使得同一个模型使用一个关联就可以属于多个不同的模型,假设这样一个场景,我们有一个帖子表和一个评论表,用户既可以对帖子执行喜欢操作,也可以对评论执行喜欢操作,这样的情况下该怎么处理呢?

    表结构如下

    posts
        id - integer
        title - string
        body - text
    
    comments
        id - integer
        post_id - integer
        body - text
    
    likes
        id - integer
        likeable_id - integer
        likeable_type - string
    

    可以看到,我们使用likes表中的likeable_type字段判断该记录喜欢的是帖子还是评论,表结构有了,接下来就该定义模型了

    <?php
    
    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Like extends Model
    {
    
        public function likeable()
        {
            return $this->morphTo();
        }
    }
    
    class Post extends Model
    {
    
        public function likes()
        {
            return $this->morphMany('App\Like', 'likeable');
        }
    }
    
    class Comment extends Model
    {
    
        public function likes()
        {
            return $this->morphMany('App\Like', 'likeable');
        }
    }
    

    默认情况下,likeable_type的类型是关联的模型的完整名称,比如这里就是App\PostApp\Comment

    通常情况下我们可能会使用自定义的值标识关联的表名,因此,这就需要自定义这个值了,我们需要在项目的服务提供者对象的boot方法中注册关联关系,比如AppServiceProviderboot方法中

    use Illuminate\Database\Eloquent\Relations\Relation;
    
    Relation::morphMap([
        'posts' => App\Post::class,
        'likes' => App\Like::class,
    ]);
    

    检索多态关系

    访问一个帖子所有的喜欢

    $post = App\Post::find(1);  
    foreach ($post->likes as $like) {
        //
    }
    

    访问一个喜欢的帖子或者评论

    
    $like = App\Like::find(1);   
    $likeable = $like->likeable;
    

    上面的例子中,返回的likeable会根据该记录的类型返回帖子或者评论。

    多对多的多态关联

    多对多的关联使用方法morphToManymorphedByMany,这里就不多废话了。

    关联关系查询

    在Eloquent中,所有的关系都是使用函数定义的,可以在不执行关联查询的情况下获取关联的实例。假设我们有一个博客系统,User模型关联了很多Post模型:

    public function posts()
    {
       return $this->hasMany('App\Post');
    }
    

    你可以像下面这样查询关联并且添加额外的约束

    $user = App\User::find(1);
    $user->posts()->where('active', 1)->get();
    

    如果不需要对关联的属性添加约束,可以直接作为模型的属性访问,例如上面的例子,我们可以使用下面的方式访问User的Post

    $user = App\User::find(1);
    foreach ($user->posts as $post) {
        //
    }
    

    动态的属性都是延迟加载的,它们只有在被访问的时候才会去查询数据库,与之对应的是预加载,预加载可以使用关联查询出所有数据,减少执行sql的数量。

    查询关系存在性

    使用has方法可以基于关系的存在性返回结果

    // 检索至少有一个评论的所有帖子...
    $posts = App\Post::has('comments')->get();
    
    // Retrieve all posts that have three or more comments...
    $posts = Post::has('comments', '>=', 3)->get();
    // Retrieve all posts that have at least one comment with votes...
    $posts = Post::has('comments.votes')->get();
    

    如果需要更加强大的功能,可以使用whereHasorWhereHas方法,把where条件放到has语句中。

    // 检索所有至少存在一个匹配foo%的评论的帖子
    $posts = Post::whereHas('comments', function ($query) {
        $query->where('content', 'like', 'foo%');
    })->get();
    

    预加载

    在访问Eloquent模型的时候,默认情况下所有的关联关系都是延迟加载的,在使用的时候才会开始加载,这就造成了需要执行大量的sql的问题,使用预加载功能可以使用关联查询出所有结果

    <?php
    
    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Book extends Model
    {
        public function author()
        {
            return $this->belongsTo('App\Author');
        }
    }
    

    接下来我们检索所有的书和他们的作者

    $books = App\Book::all();
    
    foreach ($books as $book) {
        echo $book->author->name;
    }
    

    上面的查询将会执行一个查询查询出所有的书,然后在遍历的时候再执行N个查询查询出作者信息,显然这样做是非常低效的,幸好我们还有预加载功能,可以将这N+1个查询减少到2个查询,在查询的时候,可以使用with方法指定哪个关系需要预加载。

    $books = App\Book::with('author')->get();
    foreach ($books as $book) {
        echo $book->author->name;
    }
    

    对于该操作,会执行下列两个sql

    select * from books
    select * from authors where id in (1, 2, 3, 4, 5, ...)
    

    预加载多个关系

    $books = App\Book::with('author', 'publisher')->get();
    

    嵌套的预加载

    $books = App\Book::with('author.contacts')->get();
    

    带约束的预加载

    $users = App\User::with(['posts' => function ($query) {
        $query->where('title', 'like', '%first%');
    }])->get();
    
    $users = App\User::with(['posts' => function ($query) {
        $query->orderBy('created_at', 'desc');
    }])->get();
    

    延迟预加载

    有时候,在上级模型已经检索出来之后,可能会需要预加载关联数据,可以使用load方法

    $books = App\Book::all();
    if ($someCondition) {
        $books->load('author', 'publisher');
    }
    
    $books->load(['author' => function ($query) {
        $query->orderBy('published_date', 'asc');
    }]);
    

    关联模型插入

    save方法

    保存单个关联模型

    $comment = new App\Comment(['message' => 'A new comment.']);
    $post = App\Post::find(1);
    $post->comments()->save($comment);
    

    保存多个关联模型

    $post = App\Post::find(1); 
    $post->comments()->saveMany([
        new App\Comment(['message' => 'A new comment.']),
        new App\Comment(['message' => 'Another comment.']),
    ]);
    

    save方法和多对多关联

    多对多关联可以为save的第二个参数指定关联表中的属性

    App\User::find(1)->roles()->save($role, ['expires' => $expires]);
    

    上述代码会更新中间表的expires字段。

    create方法

    使用create方法与save方法的不同在于它是使用数组的形式创建关联模型的

    $post = App\Post::find(1);
    $comment = $post->comments()->create([
        'message' => 'A new comment.',
    ]);
    

    更新 "Belongs To" 关系

    更新belongsTo关系的时候,可以使用associate方法,该方法会设置子模型的外键

    $account = App\Account::find(10);
    $user->account()->associate($account);
    $user->save();
    

    要移除belongsTo关系的话,使用dissociate方法

    $user->account()->dissociate();
    $user->save();
    

    Many to Many 关系

    中间表查询条件

    当查询时需要对使用中间表作为查询条件时,可以使用wherePivotwherePivotInorWherePivotorWherePivotIn添加查询条件。

    $enterprise->with(['favorites' => function($query) {
        $query->wherePivot('enterprise_id', '=', 12)->select('id');
    }]);
    

    Attaching / Detaching

    $user = App\User::find(1);
    // 为用户添加角色
    $user->roles()->attach($roleId);
    // 为用户添加角色,更新中间表的expires字段
    $user->roles()->attach($roleId, ['expires' => $expires]);
    
    // 移除用户的单个角色
    $user->roles()->detach($roleId);
    // 移除用户的所有角色
    $user->roles()->detach();
    

    attachdetach方法支持数组参数,同时添加和移除多个

    $user = App\User::find(1);
    $user->roles()->detach([1, 2, 3]);
    $user->roles()->attach([1 => ['expires' => $expires], 2, 3]);
    

    更新中间表(关联表)字段

    使用updateExistingPivot方法更新中间表

    $user = App\User::find(1);
    $user->roles()->updateExistingPivot($roleId, $attributes);
    

    同步中间表(同步关联关系)

    使用sync方法,可以指定两个模型之间只存在指定的关联关系

    $user->roles()->sync([1, 2, 3]);
    $user->roles()->sync([1 => ['expires' => true], 2, 3]);
    

    上述两个方法都会让用户只存在1,2,3三个角色,如果用户之前存在其他角色,则会被删除。

    更新父模型的时间戳

    假设场景如下,我们为一个帖子增加了一个新的评论,我们希望这个时候帖子的更新时间会相应的改变,这种行为在Eloquent中是非常容易实现的。

    在子模型中使用$touches属性实现该功能

    <?php
    
    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Comment extends Model
    {
    
        protected $touches = ['post'];
    
        public function post()
        {
            return $this->belongsTo('App\Post');
        }
    }
    

    现在,更新评论的时候,帖子的updated_at字段也会被更新

    $comment = App\Comment::find(1);
    $comment->text = 'Edit to this comment!';
    $comment->save();
    

    参考: Eloquent: Relationships

    展开全文
  • 我们在使用Laravel ORM的Model方法find, get, first方法获取数据对象时返回的数据对象的attributes属性数组里会包含数据表中所有的字段对应的键值关系, 那么如何在ORM查询时只返回数据表中指定字段的数据呢?...
  • 使用Eloquent ['eləkwənt] 时,数据库查询构造器的方法对模型类也是也用的,使用上只是省略了DB::table('表名')部分。 在模型中使用protected成员变量$table指定绑定的表名。 <?...namespace App;...
  • Laravel中执行数据库操作有两种方式,一种是使用\DB外观对象的静态方法直接执行sql查询,另外一种是使用Model类的静态方法(实际上也是Facade的实现,使用静态访问方式访问Model的方法,内部采用了__callStatic...
  • laravel/lumen框架model层查询出来的对象转化数组 最近项目用到lumen写接口,所以看了一下手册,发现lumen已经更新到(6.X)了, 以前也用过laravel5.2感觉差距还是挺大的,话不多说,贴代码: model层代码 //...
  • A.队列的使用 1.队列配置文件存储在 config/queue.php 根据自己的情况进行配置 2..env文件 QUEUE_DRIVER=database(根据个人情况配置,redis等) 3.创建jobs表(不用数据库的可以不用建表) ...
  • 热烈庆祝 Laravel 5.5 LTS 发布! 实际上 Laravel 上一个 LTS 选择 5.1 是非常不明智的,因为 5.2 增加了许许多多优秀的特性。现在好了,大家都用最新的长期支持版本 5.5 吧! Laravel 5.5 中文文档:...
  • 优化PHP程序

    2011-10-10 22:03:58
    优化PHP程序 转载:http://zhaoqi7577.blog.163.com/blog/static/90154965200872812238412/ 网上很多文章介绍优化php程序,是通过安装Zend Optimizer之类的加速软件实现的,但这种加速是有限的。本
  • laravel使用redis报错

    千次阅读 2019-07-21 21:37:43
    当你使用redis时,也许会报这个错误: (1/1) ConnectionException ...����Ŀ����������ܾ����޷����ӡ� [tcp://127.0.0.1:6379] ...at AbstractConnecti...
  • 在安装laravel的时候,我们一般是download github上的laravel/laravel,随后执行composer install,在这个过程中,你会发现composer其中的一项工作是 安装laravel/framework到vendor目录中,(为了看清楚这个过程,...
  • Laravel Broadcasting广播机制(Redis + socket.io)-学习实例

    万次阅读 多人点赞 2018-05-26 09:32:51
    借助Laravel Broadcasting你可以使用上时下很热的Websocket技术。
  • Laravel入门

    万次阅读 2017-03-28 08:47:36
    一、laravel简介及安装 1.简介:Laravel是一套简洁、优雅的PHP Web开发框架(PHP Web Framework)。它可以让你从面条一样杂乱的代码中解脱出来;它可以帮你构建一个完美的网络APP,而且每行代码都可以简洁、富于表达力...
  • Laravel 5.5 异常处理 & 错误日志

    千次阅读 2018-09-24 17:45:42
    此外,Laravel 还集成了 Monolog 日志库以便提供各种功能强大的日志处理器,默认情况下,Laravel 已经为我们配置了一些处理器,我们可以选择单个日志文件,也可以选择记录错误信息到系统日志。 配置 调试模式 ...
  • laravel框架

    2020-05-22 23:30:11
    laravel框架laravel框架laravel框架laravel框架laravel框架laravel框架laravel框架laravel框架laravel框架laravel框架laravel框架laravel框架laravel框架laravel框架laravel框架laravel框架laravel框架laravel框架...
  • Laravel 5.5 Redis 队列基本使用指南

    千次阅读 2018-10-27 19:34:03
    composer require laravel/horizon 配置 参考:Horizon | 《Laravel 5.5 中文文档》 | PHP / Laravel 社区文档 运行 Laravel 项目根目录执行 php artisan horizon Supervisor 配置(后台运行) 参考:链接直达 开始...
1 2 3 4 5 ... 20
收藏数 98,899
精华内容 39,559
关键字:

laravel