Laravel基础

Laravel 基础

01.Laravel入门和安装

Composer安装Laravel步骤

要使用 Composer 安装 Laravel,请按照以下步骤操作:

  1. 确保已经安装了 Composer。如果还没有安装,请访问 https://getcomposer.org/download/ 下载并安装。

  2. 打开命令行或终端。

  3. 使用 cd 命令导航到你的项目目录,例如:

    powershell 复制代码
    cd /path/to/your/project
  4. 在项目目录中,运行以下命令创建一个新的Laravel项目:

    lua 复制代码
    composer create-project --prefer-dist laravel/laravel your_project_name

    其中,your_project_name 是你的项目名称。这将下载 Laravel 的最新版本并创建一个新的项目。

  5. 安装完成之后进入项目

    bash 复制代码
    cd your_project_name
  6. .env.example 文件复制并重命名为 .env

    bash 复制代码
    cp .env.example .env
  7. .env 文件中配置数据库连接信息。例如:

    ini 复制代码
    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=your_database_name
    DB_USERNAME=your_database_username
    DB_PASSWORD=your_database_password
  8. 生成应用密钥:

    vbnet 复制代码
    php artisan key:generate
  9. 运行数据库迁移和数据填充(如果有):

    undefined 复制代码
    php artisan migrate
    php artisan db:seed
  10. 最后,启动开发服务器

    php 复制代码
    php artisan serve

这将在本地启动一个 HTTP 服务器,通常在 http://localhost:8000 上运行。现在你可以开始构建你的 Laravel 应用了。

02.路由的定义和控制器

一.路由的定义
  1. 什么是路由?路由就是提供接受 HTTP 请求的路径,并和程序交互的功能;

  2. 简单点理解,就是为了提供访问程序的 URL 地址,所做的一些设置工作;

  3. phpstorm 支持 cmd 操作,左下角 Terminal 按钮展开,调整字体即可;

  4. 输入 php artisan serve 命令后,即支持 localhost:8000 内置服务器;

  5. 路由的定义文件在根目录 routes/web.php 中,可以看到welcome 页面;

  6. 我们创建一个路由,让它返回一段信息,并设置响应的 url 地址;

    php 复制代码
    Route::get('index', function () {
       return "<h1>Hello World</h1>";
    });
  7. 在路由定义上,我们采用了::get()这个方法,它接受的就是GET 提交;

  8. ::post()、::put()、::delete()是表单和 Ajax 的提交接受方式;

  9. ::any()表示不管你是哪种提交方式,我智能的全部接收响应;

  10. ::match()表示接收你指定的提交方式,用数组作为参数传递;

    php 复制代码
    // 使用 match 必须有三个参数, any 是两个参数
    Route::match(['get', 'post'], 'match', function () {
        return "Matched";
    });
  11. 在路由的规则和闭包区域,我们可以设置和传递路由参数;

    php 复制代码
    Route::get('index/{id}', function ($id) {
       return "<h1>Hello World $id</h1>";
    });
    // http://localhost:8000/index/5
  12. 上面例子中{id}表示在 url 的动态参数,比如数字 5;

  13. 那么闭包的$id,可以接受 url 传递过来的 5,最终输出 5;

二.创建控制器
  1. MVC 模式中 C 代表控制器,用于接收 HTTP 请求,从而进行逻辑处理;

  2. 有两种方式可以创建控制器,IDE 直接创建,或使用命令生成一个;

    php artisan make:controller TaskController

  3. 控制器目录在 app\Http\Controllers 下,使用 IED 会自动生成命名空间;

    php 复制代码
    <?php
    namespace App\Http\Controllers;
    
    class TaskConterollers extends Controller
    {
        public function index()
        {
            return "task index";
        }
        public function read($id){
            return "id is $id";
        }
    }
  4. 通过设置路由来访问创建好的控制器,参数二:控制器@方法名;

    php 复制代码
    use App\Http\Controllers\TaskConterollers;
    
    Route::get("/task/read/{id}", [TaskConterollers::class, 'read'])
        ->where(['id' => '[0-9]+', 'name' => '[a-z]+']);

03.路由参数.重定向.视图

一.路由参数
  1. 上一节课,我们已经学习了部分路由参数的功能,比如动态传递{id}

  2. 那么,有时这个参数需要进行约束,我们可以使用正则来限定必须是数字;

    php 复制代码
    //单个参数
    Route::get("/task/read/{id}", [TaskConterollers::class, 'read'])
            ->where('id', '[0-9]+');
    
    // 多个参数
    Route::get("/task/read/{id}", [TaskConterollers::class, 'read'])
        ->where(['id' => '[0-9]+', 'name' => '[a-z]+']);
  3. 如果想让约束 id 只能是 0-9 之间作用域全局范围,可以在模型绑定器里设置;

  4. 模型绑定器路径为:app\Providers\RouteServiceProvider 的boot()方法;

    php 复制代码
    public function boot()
    {
    	$this->pattern('id', '[0-9]+'); // 全局都有效
    }
  5. 如果 id 已经被全局约束,在某个局部你想让它脱离约束,可以如下操作:

    php 复制代码
    // 全局范围的约束解除
    Route::get("/task/read/{id}", [TaskConterollers::class, 'read'])
            ->where('id', '.*');
二.路由重定向
  1. 可以设置访问一个路由的 URI,跳转到另一个路由的 URI,具体如下:

    php 复制代码
    Route::redirect('index', 'task');
    Route::redirect('index', 'task', 301);  // 状态码
  2. 还有一个方法,可以直接让路由跳转返回 301 状态码而不用设置:

    php 复制代码
    Route::permanentRedirect('index', 'task');
三.视图路由
  1. 在使用视图路由之前,我们先要创建一个视图(MVC)中的 V 部分;

  2. 使用视图路由,有三个参数:1.URI(必);2.名称(必);3.参数(选);

    php 复制代码
    //参数 1:URI,localhost:8000/task
    //参数 2:view,resources/views/task.blade.php
    //参数 3:传参,{{$id}}
    Route::view('task', 'task', ['id'=>10]);
  3. 对于视图页面的创建,在 resources/views 下创建 task.blade.php;

    php 复制代码
    {{--静态页面--}}
    task{{$id}}
  4. 当然,也可以使用助手函数 view()方法来实现这个功能;

    php 复制代码
    //这里 view()的参数 1 是视图名称
    //参数 2 传参,可选;
    Route::get('task', function () {
    	return view('task', ['id'=>10]);
    });
  5. 也可以将路由直接指向控制器的方法,通过方法实现 view()引入视图;

    php 复制代码
    public function index()
    {
    	return view('task', ['id'=>10]);
    }

04.路由命名和分组

一.路由命名
  1. 给一个制定好的路由进行命名,可以生成 URL 地址或进行重定向;

    php 复制代码
    Route::get('task', 'TaskController@index')
    	->name('task.index');
  2. 在控制器区域,使用助手函数 route()来获取路由生成的 URL 地址;

    php 复制代码
    //生成 url 地址,http://localhost:8000/task
    route('task.index'); PS:URL 是 URI 的子集,更多区别请百度;
  3. route()助手的第二参数为参数,第三参数为是否包含域名URL;

    php 复制代码
    // http://localhost:8000/task?id=10
    route('task.index', ['id'=>10]);
    // /task?id=10
    $url = route('task.index', ['id'=>10], false);
    // PS:如果需要更改成/task/10 模式,路由需要相应更改 task/{id}
  4. 使用 redirect()助手结合 route()生成一个重定向跳转,注意不要自我死跳;

    php 复制代码
    //生成重定向
    return redirect()->route('task.index', ['id'=>10]);
二.路由分组
  1. 路由分组功能是为了让大量路由共享路由属性,包括中间件、命名空间等;

    php 复制代码
    //一个空的分组路由
    Route::group([], function () {
        Route::get('index/{id}', function ($id) {
        	return 'index'.$id;
        });
        Route::get('task/{id}', function ($id) {
        	return 'task'.$id;
        });
    });
  2. 可以将中间件作用域路由分组中,有两种写法,至于中间件?后续章节讲解;

    php 复制代码
    //引入中间件,方法一
    Route::group(['middleware'=>'中间名'], function () {});
    //引入中间件,方法二
    Route::middleware(['中间件'])->group(function () {});
  3. 可以设置路由路径前缀,通过 prefix 来设置,也有两种方法,具体如下:

    php 复制代码
    //引入路由前缀,方法一
    Route::group(['prefix'=>'api'],function () {});
    //引入路由前缀,方法二
    Route::prefix('api')->group(function () {});
  4. 可以设置子域名,从而限定路由可执行的域名,有两种方法,具体如下:

    php 复制代码
    //引入子域名,方法一
    Route::group(['domain'=>'127.0.0.1'], function () {});
    //引入子域名,方法二
    Route::domain('127.0.0.1')->group(function () {});
  5. 可以设置命名空间,让命名空间分配给控制器,让其得以访问,具体如下:

    php 复制代码
    //命名空间,方法一
    Route::group(['namespace'=>'Admin'],function () {});
    //命名空间,方法二
    Route::namespace('Admin')->group(function () {});
    PS:在 Controller 目录下创建 Admin 目录,再其目录下创建的控制器命名空间如下:namespace App\Http\Controllers\Admin;
  6. 可以设置名称前缀,方式两种,也可以嵌套,具体如下:

    php 复制代码
    //名称前缀,方式一
    Route::group(['as'=>'task.'], function () {
        Route::get('task', 'TaskController@index')->name('index');
        Route::get('task/url', 'TaskController@url');
    });
    //名称前缀,方式二
    Route::name('task.')->group( function () {});
    //生成 URL
    $url = route('task.index');
    return $url;
    //嵌套方式命名前缀
    Route::name('task.')->group(function () {
        Route::name('abc.')->group(function () {
        	Route::get('task', 'TaskController@index')->name('index');
        });
        Route::get('task/url', 'TaskController@url');
    });
    //生成 URL
    $url = route('task.abc.index');
    return $url;

05.回退.当前路由.单行为

一.单行为控制器
  1. 之前的课程,我们简单的创建和定义了控制器,并继承了控制器基类;

  2. 为何要继承基类?因为继承基类后,可以使用基类的方法,比如中间件等;

  3. 继承基类后除了支持中间件快捷使用,还支持验证、列队等快捷方法;

    php 复制代码
    public function __construct()
    {
    	$this->middleware('中间件');
    }
  4. 如果你想要定义一个只执行一个方法的控制器,可以使用单行为控制器;

  5. 单行为控制器使用__invoke()方法,可以使用命令行创建;

    命令:php artisan make:controller OneController --invokable

    php 复制代码
    //手工创建
    class OneController extends Controller
    {
        public function __invoke()
        {
    	    return '单行为控制器';
        }
    }
  6. 单行为控制器,路由定义就不需要指定特定的方法,指定控制器即可

  7. 单行为控制器只是语义上的单行为,并没有限制创建更多方法访问;

    php 复制代码
    Route::get('one', 'OneController');
二.路由回退
  1. 如果我们跳转到了一个不存在路由时,会产生 404 错误,体验不佳;

  2. 可以使用回退路由,让不存在的路由自动跳转到你指定的页面去;

  3. 注意:由于执行顺序问题,必须把回退路由放在所有路由的最底部;

    php 复制代码
    Route::fallback(function () {
    	return redirect('/');
    });
  4. 当然,你也可以制作一个自己的 404 页面,用回退路由加载这个页面;

    php 复制代码
    Route::fallback(function () {
    	return view('404');
    });
三.当前路由
  1. 我们可以通过使用::current()系列方法,来获取当前路由的访问信息;

    php 复制代码
    Route::get('index', function () {
        //当前路由信息
        dump(Route::current());
        //返回当前路由的名称
        return Route::currentRouteName();
        //返回当前路由指向的方法
        return Route::currentRouteAction();
    })->name('localhost.index');

06.响应设置和重定向

一.响应设置
  1. 路由和控制器处理完业务都会返回一个发送到浏览器的响应:return;

  2. 比如字符串会直接输出,而数组则会输出 json 格式,本身是Response 对象;

    php 复制代码
    return [1, 2, 3]; //输出 json 格式
    return response([1, 2, 3]); //同上
    return response()->json([1, 2, 3]); //同上
  3. 如果使用 response()输出的话,可以设置状态码和响应头信息;

    php 复制代码
    return response('index', 201); //可以设置 HTTP 请求状态码
  4. 也可以给 HTTP 添加或修改标头,比如将 html 解析模式改成文本plain 模式;

    php 复制代码
    return response('<b>index</b>')
    		->header('Content-Type', 'text/plain'); //文本解析模式
  5. 结合上面的响应操作,再结合 view()视图功能,显示纯 HTML 代码页面;

    php 复制代码
    return response()->view('task', ['id'=>10], 201)
    				->header('Content-Type', 'text/plain');
二.路由重定向
  1. 重定向使用助手函数 redirect()的 to()方法,注意需要return 才能跳转

    php 复制代码
    return redirect()->to('/'); //跳到首页
    return redirect()->to('task'); //跳转到 task
    return redirect()->to('task/url'); //跳转到 task/url
  2. 也可以直接使用快捷方式直接进行跳转; 常用

php 复制代码
return redirect('/'); //跳到首页
return redirect('task'); //跳转到 task
return redirect('task/url'); //跳转到 task/url
  1. redirect()助手有一个对应的 facade 模式对象;

    php 复制代码
    return Redirect::to('/'); //facade 模式,但需要 use 引入
  2. 使用 redirect()的 route()方法,可以跳转到指定的命名路由URI;

    php 复制代码
    return redirect()->route('task.index'); //注意和 route()方法区别
  3. 使用 redirect()back()方法,可以重定向到上一个页面中;常用

    php 复制代码
    return redirect()->back();
    return back(); //快捷方式
  4. 使用 redirect()的 action()方法,可以直接重定向到控制器方法;

    php 复制代码
    return redirect()->action('TaskController@index'); //需注册路由
    return redirect()->action('TaskController@index', ['id'=>10]);
  5. 使用 redirect()的 away()方法,跳转到外部链接;

    php 复制代码
    return redirect()->away('http://www.baidu.com'); //不带任何编码

07.资源控制器

一.资源控制器
  1. 声明:资源控制器是某个特定场景下的产物,完全理解需要PHP项目基础;

  2. 比如开发过博客系统,留言帖子系统之类,具有类似思维,否则你懂的...;

  3. 只是学习了PHP基础,就立刻学习框架的同学,可以过一遍即可(不影响后续)...;

  4. 有一种控制器专门处理CURD(增删改查),方法很多且方法名基本固定;

  5. 对于这种控制器,我们可以将它设置为资源型控制器,不要大量设置路由;

  6. 这里推荐直接使用命令行生成资源路由,比如:BlogController

    bash 复制代码
    php artisan make:controller BlogController --resource
  7. 生成了的资源控制器会产生 7 个方法,配置好路由后会自动生成相关内容

    php 复制代码
    Route::resource('blogs', BlogController::class); //单个资源路由
    
    //批量定义资源路由
    // 多个路由
    Route::resources([
        "blogs" => BlogController::class,
        "tasks" => TaskController::class
    ]); 
    HTTP类型 路由URI 控制器方法 路由命名 描述
    GET blogs index() blogs.index 获得数据列表
    GET blogs/create create() blogs.create 创建页面(表单页)
    POST blogs store() blogs.store 创建页的接受处理
    GET blogs/{blog} show() blogs.show 获得一条数据
    GET blogs/{blog}/edit edit() blogs.edit 编辑(表单页)
    PUT/PATCH blogs/{blog} update() blogs.update 从编辑页中接受处理
    DELETE blogs/{blog} destroy() blogs.destroy 删除一条数据
  8. 如果我们注册了资源路由,那么如上图的资源路由 URI 和名称均自动创建生效;

    php 复制代码
    http://localhost:8000/blogs/10/edit
    //可以访问到edit方法
    
    return route('blogs.store');
    //可以通过助手route()了解是否注册
  9. 还有一条命令可以直接查看目前可用的路由以及命名; 常用

    php artisan route:list

  10. 我们也可以限制资源路由只开放部分方法或排除部分方法,可以用命令查看;

    php 复制代码
    // 只有 index 和 show 可以访问
    Route::resource('blogs', BlogController::class)
        	->only(['index', 'show']); 
    
    // index 和 show 不可访问,剩下的都可以 
    Route::resource('blogs', BlogController::class)
        ->except(['index', 'show']); 
  11. 资源控制器还有一种不需要HTML页面方法的API路由,只提供数据接口;

    php 复制代码
    //API资源,并不需要HTML页面(create,edit),会排除
    Route::apiResource('blogs', 'BlogController');
    
    //批量方式
    Route::apiResources([
        'blogs' => 'BlogController'
    ]);
  12. 当然,也支持一开始就生成一个不包含HTML页面方法的资源控制器;

  13. 要注意的是,对应的资源路由,直接使用api资源路由即可;创建时就说是API

    bash 复制代码
    php artisan make:controller CommentController --api
    php 复制代码
    Route::apiResource('comments', CommentController::class);

08.资源嵌套.浅嵌套.自定义

  1. 声明:资源控制器是某个特定场景下的产物,完全理解需要PHP项目基础;

  2. 比如开发过博客系统、留言帖子系统之类,具有类似思维,否则你懂的...;

  3. 只是学习了PHP基础就立刻学习框架的同学,可以过一遍即可(不影响后续);

  4. 在一篇博文(Blog)下有多条评论(Comment),编辑某条博文下的一条评论;

  5. 以上需求,可以通过嵌套资源路由来实现这个功能:

    bash 复制代码
    php artisan make:controller CommentController --resource
    php 复制代码
    //嵌套资源路由
    Route::resource('blogs.comments', CommentController::class);
    HTTP类型 路由URI 控制器方法 路由命名
    GET blogs/{blog}/comments index() blogs.comments.index
    GET blogs/{blog}/comments/create create() blogs.comments.create
    POST blogs/{blog}/comments store() blogs.comments.store
    GET blogs/{blog}/comments/{comment} show() blogs.comments.show
    GET blogs/{blog}/comments/{comment}/edit edit() blogs.comments.edit
    PUT/PATCH blogs/{blog}/comments/{comment} update() blogs.comments.update
    DELETE blogs/{blog}/comments/{comment} destroy() blogs.comments.destroy
  6. 以上需求,可以通过嵌套资源路由来实现这个功能,编辑方法以及传参如下:

    php 复制代码
    public function edit($blog_id, $comment_id) {
        return '编辑博文下的评论,博文id: ' . $blog_id . ',评论id: ' . $comment_id;
    }
  7. 而实际上,每个 id 都是独立唯一的,并不需要父 id 和子id 同时存在;

  8. 为了优化资源嵌套,通过路由方法->shallow()实现浅层嵌套方法; 取消父级

    php 复制代码
    // 浅层嵌套
    Route::resource('blogs.comments', 'CommentController')->shallow();
  9. 实现后的路由及参数传递更精准:

    HTTP类型 路由URI 控制器方法 路由命名
    GET blogs/{blog}/comments index() blogs.comments.index
    GET blogs/{blog}/comments/create create() blogs.comments.create
    POST blogs/{blog}/comments store() blogs.comments.store
    GET comments/{comment} show() blogs.comments.show
    GET comments/{comment}/edit edit() blogs.comments.edit
    PUT/PATCH comments/{comment} update() blogs.comments.update
    DELETE comments/{comment} destroy() blogs.comments.destroy
    php 复制代码
    public function edit($id) {
        return '评论id: ' . $id;
    }
  10. 如果觉得资源路由命名过长,可以自己自定义,有两种方式:

    php 复制代码
    ->name('index', 'b.c.i');
    ->names(['index' => 'b.c.i']);
  11. 如果觉得资源路由的参数不符合你的心意,也可以改变:

    php 复制代码
    ->parameter('blogs', 'id');
    ->parameters([
        'blogs' => 'blog_id', 
        'comments' => 'comment_id'
    ]);

09.表单伪造和CSRF保护

一.表单伪造

  1. 之前一直用的 GET 请求方式,而表单可以实现 POST 方式,我们来实验下:

  2. 先在 TaskController 创建两个方法,一个表单页,一个接受表单数据路由;

    php 复制代码
    public function form()
    {
    	return view('form');
    }
    //表单页
    Route::get('task/form', 'TaskController@form');
    //接受表单数据
    Route::any('task/getform', function () {
    	return \Illuminate\Support\Facades\Request::method();
    });
  3. 表单页以 post 发送,路由也使用 post 接受,以下表单提交会出现419 错误;

    html 复制代码
    <form action="/task/getform" method="post">
    	用户名:<input type="text" name="user">
    	<button type="submit">提交</button>
    </form>
  4. 这是为了避免被跨站请求伪造攻击,框架提供了 CSRF 令牌保护,请求时验证;

    html 复制代码
    <input type="hidden" name="_token" value="{{csrf_token()}}">
  5. 表单可以实现 POST 提交方式,那其它提交方式该如何实现呢?可以采用伪造技术;

    html 复制代码
    <input type="hidden" name="_method" value="PUT">
  6. 对于 CSRF 令牌保护和表单伪造提交方式,也支持快捷方式的声明,如下:

    php 复制代码
    @csrf
    @method('PUT')
  7. 如果我们想让某些 URL 关闭 csrf 验证,可以设置 csrf 白名单;

  8. 白名单具体设置位置在:中间件目录下的 VerifyCsrfToken.php 文件;

  9. 当然,不建议直接注释掉这个验证 csrf 功能的中间件;

    php 复制代码
    protected $except = [
    	'api/*',
    ];

10.数据库配置入门

一.配置数据库
  1. 框架支持原生、查询构造器和 Eloquent ORM(关系型对象映射器)来操作数据库;

  2. 数据库的配置在 config/database.php,如果是本地可以直接配置.env 文件;

  3. 我们通过.env 文件配置数据库连接的相关信息,以提供给database 读取;

    json 复制代码
    DB_CONNECTION=mysql //.env
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=grade
    DB_USERNAME=root
    DB_PASSWORD=123456
    'mysql' => [ //.database.php
    	'driver' => 'mysql',
    	...
    ]
  4. 我们可以直接创建一个新的控制器 DataController 来测试数据库部分;

  5. 数据库有一个专用类 DB,可以用它来实现原生查询和构造器查询;

    php 复制代码
    //使用 DB 类的 select()方法执行原生 SQL
    $user = DB::select('select * from laravel_user');
    return $user;
  6. 查询构造器主要通过 DB 类的各种数据库操作方法来实现,比如选定一条;

    php 复制代码
    //这里省去了 laravel_,需要在 database.php 配置
    $user = DB::table('user')->find(19);
    return [$user];
  7. 由于火狐浏览器自动将 JSON 显示的很美化,而 find()只返回对象;

    php 复制代码
    return response()->json($user);
  8. 使用 Eloquent ORM 模型来操作数据库,使用命令在 Http 目录下创建模型;

    php artisan make:model Http/Models/User

    php 复制代码
    //使用 Eloquent ORM 构建
    $user = User::all();
    return $user;

上面使用模型来操作数据后,报错提示数据表是复数:users;

而我们真实的数据库表为:laravel_user,为何会这样???

前缀可以在 database.php 修改添加:laravel_,最终变为:laravel_users;

由于模型编码规范要求数据表是复数,这里的复数并不是单纯加s;

可能会加 es,可能会加 ies,也可能是 child 编程 children 之类的;

可以使用字符串助手:Str::plural()来判断英文单词的复数情况:

php 复制代码
return Str::plural('bus'); //buses
return Str::plural('user'); //users
return Str::plural('child'); //children

你可以根据规范去更改数据表名称,或者强制使用现有的数据表名;

protected $table = 'user';

11.构造器的查询.分块.聚合

一.构造器查询
  1. table()方法引入相应的表,get()方法可以查询当前表的所有数据;

    php 复制代码
    //获取全部结果
    $users = DB::table('users')->get();
  2. first()方法,可以获取到第一条数据;

    php 复制代码
    //获取第一条数据
    $users = DB::table('users')->first();
  3. value(字段名)方法,可以获取到第一条数据的指定字段的值;

    php 复制代码
    //获取第一条数据的 email 字段值
    $users = DB::table('users')->value('email');
  4. find(id)方法,可以获取指定 id 的一条数据;

    php 复制代码
    //通过 id 获取指定一条数据
    $users = DB::table('users')->find(20);
  5. pluck(字段名)可以获取所有数据单列值的集合;

    php 复制代码
    //获取单列值的集合
    $users = DB::table('users')->pluck('username');
    $users = DB::table('users')->pluck('username', 'email');
二.分块.聚合
  1. 如果你一次性处理成千上万条记录,防止读取出错,可以使用chunk()方法;

    php 复制代码
    //切割分块执行,每次读取 3 条,id 排序;
    DB::table('users')->orderBy('id')->chunk(3, function ($users) {
        foreach ($users as $user) {
    	    echo $user->username;
        }
        echo '------<br>';
    });
  2. 构造器查询提供了:count()、max()、min()、avg()和 sum()聚合查询;

    php 复制代码
    //聚合查询
    return DB::table('users')->count();
    return DB::table('users')->max('price');
    return DB::table('users')->avg('price');
  3. 构造器查询两个判断记录是否存在的方法:exists()和 doesntexists()方法;

    php 复制代码
    //判断是否存在
    return DB::table('users')->where('id', 19)->exists();
    return DB::table('users')->where('id', 18)->doesntExist();

PS:这里 DB::第一个使用静态,返回查询对象,然后使用->where 等各种查询方法,这些查询方法返回的还是查询对象,所以可以继续连缀操作。最后当遇到比如get()返回结果等方法时,停止连缀。所以,返回结果必须放在最后。

12.构造器的查询表达式

一.select 查询
  1. select()方法可以制定你想要的列,而不是所有列;

    php 复制代码
    //设置显示的列,设置列别名
    $users = DB::table('users')->select('username as name', 'email')->get();
  2. addSelect()方法,可以在你基础的查询构造器上再增加想要显示的字段;

    php 复制代码
    //给已经构建好的查询添加更多字段
    $base = DB::table('users')->select('username as name', 'email');
    $users = $base->addSelect('gender')->get();
  3. DB::raw()方法可以在 select()内部实现原生表达式,否则解析错误;

    php 复制代码
    //结合原生 SQL 实现复杂查询
    $users = DB::table('users')->select(DB::raw('COUNT(*) AS id, gender'))
                ->groupBy('gender')
                ->get();
  4. 也可以直接使用 selectRaw()方法实现内部原生;

    php 复制代码
    //或者直接使用 selectRaw()方法实现原生
    $users = DB::table('users')->selectRaw('COUNT(*) AS count, gender')
                ->groupBy('gender')
                ->get();
  5. 还可以通过 havingRaw()方法实现更精准的分组筛选;

    php 复制代码
    //使用 havingRaw 方法实现分组筛选
    $users = DB::table('users')->selectRaw('COUNT(*) AS count, gender')
                ->groupBy('gender')
                ->havingRaw('count>5')
                ->get();
二.where 查询
  1. where()查询,即条件查询,完整形式需要字段表达式和值三个;

    php 复制代码
    //where 查询完整形式
    $users = DB::table('users')->where('id', '=', 19)->get();
  2. 大部分情况下,是等于用的比较多,就可以省略掉=号参数;

    php 复制代码
    //where 查询完整形式
    $users = DB::table('users')->where('id', 19)->get();
  3. 当然,还有>、<、>=、<=、<>、like 等操作符;

    php 复制代码
    users = DB::table('users')->where('price', '>=', 95)->get();
    $users = DB::table('users')->where('username', 'like', '%小%')->get();
  4. 如果条件较多,可以用数组来分别添加条件,具体如下:

    php 复制代码
    //如果条件都是等于,查看 SQL 语句用->toSql()替换->get()
    $users = DB::table('users')->where([
        'price' => 90,
        'gender' => '男'
    ])->get();
    //如果条件非等于
    $users = DB::table('users')->where([
        ['price', '>=', 90],
        ['gender', '=', '男']
    ])->get();

13.构造器的where派生查询

一.where 派生查询
  1. orWhere()方法,可以通过连缀实现两个或以上的 or 条件查询;

    php 复制代码
    //where() + orWhere 实现 or 条件查询
    $users = DB::table('users')
            ->where('price', '>', 95)
            ->orWhere('gender', '女')
            ->toSql();
  2. 通过闭包,我们还可以构建更加复杂的 orWhere 查询;

    php 复制代码
    //orWhere()结合闭包查询
    $users = DB::table('users')
    ->where('price', '>', '95')
    ->orWhere(function ($query) {
    	$query->where('gender', '女')
    	->where('username', 'like', '%小%');
    })->toSql();
  3. whereBetween()可以实现区间查询,比如价格在一个区间内的用户;

    php 复制代码
    //whereBetween 查询区间价格 60~90 之间
    $users = DB::table('users')->whereBetween('price', [60, 90])->toSql();
    // PS:这里还支持相关三种:whereNotBetween/orWhereBetween/orWhereNotBetween;
  4. whereIn()可以实现数组匹配查询,比如匹配出数组里指定的数据;

    php 复制代码
    //whereIn 查询数组里匹配的数值
    $users = DB::table('users')->whereIn('id', [20,30,50])->toSql();
  5. whereNull()可以查询字段为 Null 的记录;

    php 复制代码
    //whereNull 查询字段值为 Null 的记录
    $users = DB::table('users')->whereNull('uid')->toSql();

    PS:这里还支持相关三种:whereNotNull/orWhereNull/orWhereNotNull;

  6. whereDate()可以查询指定日期的记录;

    php 复制代码
    //whereYear 查询指定日期的记录,或大于
    $users = DB::table('users')->whereDate('create_time', '2018-12-11')->toSql();

    // PS:这里还支持相关四种:whereYear/whereMonth/whereDay/whereTime,支持or 前缀;

// PS:三个参数支持大于小于之类的操作 orWhereDate('create_time','>', '2018-12-11')

14.构造器的排序分组.子查询

一.排序分组
  1. 使用 whereColumn()方法实现两个字段相等的查询结果;

    php 复制代码
    //判断两个相等的字段,同样支持 orWhereColumn()
    //支持符号'create_time','>', 'update_time'
    //支持符号支持数组多个字段格式['create_time','>', 'update_time']
    $users = DB::table('users')
            ->whereColumn('create_time', 'update_time')
            ->get();
  2. 使用 orderBy()方法实现 desc 或 asc 排序功能。

    php 复制代码
    //支持 orderByRaw 和 orderByDesc 倒序方法
    $users = DB::table('users')
            ->orderBy('id', 'desc')
            ->get();
  3. 使用 latest()方法设置时间倒序来排,默认时间字段是 created_at;

    php 复制代码
    //按照创建时间倒序排,默认字段 created_at
    $users = DB::table('users')->latest('create_time')->toSql();
  4. 使用 inRandomOrder()方法来随机排序,得到一个随机列表;

    php 复制代码
    //随机排序
    $users = DB::table('users')->inRandomOrder()->get();
  5. 使用 skip()和 take()限制结果集,或使用 offset()和 limit();

    php 复制代码
    //从第 3 条开始,显示 3 条
    $users = DB::table('users')->skip(2)->take(3)->toSql();
    $users = DB::table('users')->offset(2)->limit(3)->get();
  6. 使用 when()方法可以设置条件选择,执行相应的 SQL 语句;

    php 复制代码
    //when 实现条件选择
    $users = DB::table('users')->when(true, function ($query) {
    	$query->where('id', 19);
    }, function ($query) {
    	$query->where('username', '辉夜');
    })->get();
  7. 如果 MySQL 在 5.7+,有支持 JSON 数据的新特性;

    $users = DB::table('users')->where('list->id', 19)->first();

二.子查询
  1. 使用 whereExists()方法实现一个子查询结果,返回相应的主查询;

    php 复制代码
    //通过 books 表数据,查询到 users 表关联的所有用户
    $users = DB::table('users')->whereExists(function ($query) {
        $query->selectRaw(1)
        	->from('books')
    		->whereRaw('laravel_books.user_id = laravel_users.id');
    })->toSql();
    
    // whereRaw 这句也可以替代为:whereColumn('books.user_id','users.id');
    // PS:select 1 from,一般用于子查询的手段,目的是减少开销,提升效率,深入请搜索;
  2. 也可以使用 where(字段,function())闭包,来实现一个子查询;

    php 复制代码
    //id=子查询返回的 user_id
    $users = DB::table('users')->where('id', function ($query) {
        $query->select('user_id')
            ->from('books')
            ->whereColumn('books.user_id','users.id');
    })->toSql();

15.构造器的join查询

一.join 查询
  1. 使用 join 实现内联接的多表查询,比如三张表进行 inner join 查询;

    php 复制代码
    $users = DB::table('users')
        ->join('books', 'users.id', '=', 'books.user_id')
        ->join('profiles', 'users.id', '=', 'profiles.user_id')
        ->select('users.id', 'users.username', 'users.email',
        'books.title', 'profiles.hobby')
        ->get();
  2. 也可以使用 leftjoin 左连接或 rightjoin 右连接实现多表查询;

    php 复制代码
    $users = DB::table('users')
        ->leftJoin('books', 'users.id', '=', 'books.user_id')
        ->rightjoin('profiles', 'users.id', '=', 'profiles.user_id')
        ->get();
  3. 使用 crossjoin 交叉连接查询,会生成笛卡尔积,再用 distinct()取消重复;

    php 复制代码
    $users = DB::table('users')
        ->crossJoin('books')
        ->select('username', 'email')
        ->distinct()
        ->get();
  4. 如果你想要实现闭包查询,和 where 类似,只不过要用 on 和orOn 方法;

    php 复制代码
    $users = DB::table('users')
        ->join('books', function ($join) {
            //支持 orOn 连缀
            $join->on('users.id', '=', 'books.user_id');
        })->toSql();
    // PS:on()方法后面如果想要再增加筛选条件,可以追加 where();
  5. 使用 joinSub 实现子连接查询,将对应的内容合并在一起输出;

    php 复制代码
    //子连接查询
    $query = DB::table('books')->selectRaw('user_id,title');
    $users = DB::table('users')->joinSub($query,'books', function ($join) {
    	$join->on('users.id', '=', 'books.user_id');
    })->get();
  6. 使用 union()或 unionAll()方法实现两个查询的合并操作;

    php 复制代码
    //union 取消重复,unionAll 不取消重复
    $query = DB::table('users');
    $users = DB::table('users')
            ->union($query)
            ->get();

16.构造器的增删改

一.增删改操作
  1. 使用 insert()方法可以新增一条或多条记录;

    php 复制代码
    //新增一条记录
    DB::table('users')->insert([
        'username' => '李白',
        'password' => '123456',
        'email' => '[email protected]',
        'details' => '123'
    ]);
    //新增多条记录
    DB::table('users')->insert([
        [...],
        [...]
    ]);
  2. 使用 insertOrIgnore()方法,可以忽略重复插入数据的错误;

    php 复制代码
    //忽略重复新增数据的错误
    DB::table('users')->insertOrIgnore([
        'id' => 304,
        'username' => '李白',
        'password' => '123456',
        'email' => '[email protected]',
        'details' => '123'
    ]);
  3. 使用 insertGetId()方法,获取新增后的自增 ID;

    php 复制代码
    //获取新增后返回的 ID
    $id = DB::table('users')->insertGetId([
        'username' => '李白',
        'password' => '123456',
        'email' => '[email protected]',
        'details' => '123'
    ]);
    return $id;
  4. 使用 update()方法,可以通过条件更新一条数据内容;

    php 复制代码
    //更新修改一条数据
    DB::table('users')->where('id', 304)
        ->update([
            'username' => '李红',
            'email' => '[email protected]'
        ]);
  5. 使用 updateOrInsert()方法,可以先进行查找修改,如不存在,则新增;

    php 复制代码
    //参数 1:修改的条件
    //参数 2:修改的内容(新增的内容)
    DB::table('users')->updateOrInsert(
        ['id'=>307],
        ['username'=>'李黑', 'password'=>'654321', 'details'=>'123']
    );
  6. 对于 json 数据,新增和修改的方法和正常数据类似;

    php 复制代码
    //新增时,转换为 json 数据
    'list' => json_encode(['id'=>19])
    //修改时,使用 list->id 指定
    DB::table('users')->where('id', 306)
        ->update([
    	    'list->id' => 20
        ]);
  7. 更新数据时,可以使用自增 increment()和自减 decrement()方法;

    php 复制代码
    //默认自增/自减为 1,可设置
    DB::table('users')->where('id', 306)->increment('price');
    DB::table('users')->where('id', 306)->increment('price', 2);
  8. 使用 delete()删除数据,一般来说要加上 where 条件,否则清空;

    php 复制代码
    //删除一条数据
    DB::table('users')->delete(307);
    DB::table('users')->where('id', 307)->delete();
    //清空
    DB::table('users')->delete();
    DB::table('users')->truncate();

17.模型的定义

一.默认设置
  1. 框架可以使用 Eloquent ORM 进行数据库交互,也就是关系对象模型;

  2. 在数据库入门阶段,我们已经创建了一个 User.php 模型,如下:

    php artisan make:model Http/Models/User 默认在 app 目录

  3. 而调用的时候,我们也知道表名要遵循它默认规则,修改为复数,或特定;

    php 复制代码
    class User extends Model
    {
    	protected $table = 'user';
    }
  4. 系统假定你的主键为 id,如果你要修改默认主键,可以特定;

    protected $primaryKey = 'xid';

  5. 系统假定你的主键 id 为自增性,意味着是主键会自动转换int 类型;

  6. 如果你希望不是非自增,非数值类型主键,可以设置取消;

    public $incrementing = false;

  7. 如果你主键不是一个整数,那么需要$keyType 设置为 string;

    protected $keyType = 'string';

  8. 系统默认情况下会接管 created_at 和 updated_at 两个时间戳列;

  9. 如果不想让系统干涉这两个列,可以设置 false 取消;

    public $timestamps = false;

  10. 如果你想自定义时间戳的格式,可以设置;

    protected $dateFormat = 'U';

  11. 可以更改创建时间 created_at 和更新时间 updated_at 字段名;

    php 复制代码
    const CREATED_AT = 'create_time';
    const UPDATED_AT = 'update_time';
  12. 默认读取 database.php 配置的数据库连接,也可以在模型端局部更改;

    protected $connection = 'mysql';

二.模型定义
  1. 之前在查询构造器部分,把常用的数据库操作基本讲完,模型大体相同;

  2. 比如,我们要查询所有数据,直接使用模型::all()即可;

    php 复制代码
    //查询所有记录
    $users = User::get(); //或 all()
    return [$users];
  3. 也可以像查询构造器一样,添加各种各样的条件,写法一样;

    php 复制代码
    //查询性别为男,价格大于 90,限制显示 2 条
    $users = User::where([
        ['gender', '=', '男'],
        ['price', '>', 95]
    ])->limit(2)->get();
  4. 虽然安装了插件,但模型还是没有代码提示,可以通过安装插件解决;

    bash 复制代码
    composer require barryvdh/laravel-ide-helper
    
    php artisan ide-helper:generate -- 为 Facades 生成注释
    php artisan ide-helper:models -- 为数据模型生成注释
    php artisan ide-helper:meta -- 生成 PhpStorm Meta file
  5. 其它查询方法基本和查询构造器一样,如果有不一样,参考错误提示;

  6. 这里列出官网给出示例的方法,对照实验(结合详细文档,重复较多);

    php 复制代码
    (1) .find(1) //通过主键查找
    (2) .first() //查找第一个
    (3) .firstWhere() //找到查询中的首个
    (4) .find([1,2,3]) //通过数组查找
    (5) .firstOr() //查找首个返回,支持闭包
    (6) .firstOrFail() //找不到时返回异常
    (7) .count()、max()等集合 //集合操作

    PS:还有很多在查询构造器中的方法,比如排序、分组子查询等等都可以使用(并未一一验证)。

18.模型的增删改

一.增删改操作
  1. 新增方法如下,注意:默认模型接管 created_at 和 updated_at;

    php 复制代码
    $users = new User();
    $users->username = '辉夜';
    $users->password = '123';
    $users->email = '[email protected]';
    $users->details = '123';
    $users->save();
  2. 更新,只要是查找到一条数据的情况下使用 save()就是更新;

    php 复制代码
    $users = User::find(321);
    $users->username = '夜辉';
    $users->save();
  3. 使用 update()方法实现批量更新;

    php 复制代码
    User::where('username', '夜辉')
        ->update([
    	    'username' => '辉夜'
        ]);
  4. 使用 create()方法实现新增,但需要在模型端设置批量赋值的许可;

    php 复制代码
    User::create([
        'username' => '辉夜',
        'password' => '123',
        'email' => '[email protected]',
        'details' => '123',
    ]);
    //许可批量赋值,默认不可
    protected $fillable = [
        'username',
        'password',
        'email',
        'details'
    ];
    //不许可的批量赋值,不可和$fillable 同时使用
    //protected $guarded = ['uid'];
    //如果取消批量赋值限制,直接如下
    protected $guarded = [];

    PS:必须在模型中定义批量赋值的可填充字段,否则无法生效;防止用户不小心设置新值;

  5. 使用 delete()方法,可以删除数据;

    php 复制代码
    $users = User::find(332);
    $users->delete();
    //批量删除
    $users = User::where('username', '夜辉');
    $users->delete();
  6. 如果你是通过主键 id 删除,那使用 destroy(id)方法,免去查询操作;

    php 复制代码
    //通过主键删除
    User::destroy(328);

19.批量赋值和软删除

一.批量赋值
  1. 上一节增删改中,新增中我们发现需要进行批量赋值的许可;
  2. 一般情况下,是为了防止提交过来的字段在部分场景中不需要或不能;
  3. 所以,我们需要通过黑白名单机制进行过滤掉必要的字段;
php 复制代码
//通过提交过来的数据一次性新增
User::create(\Request::all());
二.软删除
  1. 什么叫软删除?它相对于真实的删除,而并非真正的删除,只是隐藏了;

  2. 首先,需要在数据库创建一个字段 deleted_at(默认),用于判断是否被软删除;

  3. 默认设置这个字段为空(null),如果写入数据,成为非空状态,则说明被删除;

  4. 开启软删除的功能,需要在模型端设置一下:

    use SoftDeletes; //开启软删除功能

  5. 当开启了软删除功能,之前的删除操作,都会变成更新操作,给deleted_at 赋值;

    php 复制代码
    //删除一
    $users = User::find(82);
    $users->delete();
    //删除二
    User::destroy(81);
  6. 而当我们开启了软删除的功能后,此时通过正常的数据获取列表,会自动隐藏;

    php 复制代码
    //软删除的数据不可见
    $users = User::get();
    return [$users];
    //单独获取被软删除的数据不行
    $users = User::find(82);
    return [$users];
  7. 如果需要查询包含软删除的数据,通过 withTrashed()方法实现;

    php 复制代码
    //获取包含软删除的数据
    $users = User::withTrashed()->get();
    return [$users];
    //获取某个被软删除的数据(即使不是软删除的也可以搜索到)
    $users = User::withTrashed()->find(82);
    return [$users];
  8. 如果只想搜索出被软删除的数据,通过 onlyTrashed()方法实现;

    php 复制代码
    //获取所有软删除的数据
    $users = User::onlyTrashed()->get();
    return [$users];
    //获取某个被软删除的数据(只有软删除的数据才可以被搜索到)
    $users = User::onlyTrashed()->find(82);
    return [$users];
  9. 如果要对这个数据进行软删除的判断,是否是被软删除的,可以使用trashed();

    php 复制代码
    //判断是否是被软删除的数据
    $users = User::withTrashed()->find(81);
    return $users->trashed();
  10. 如果想将软删除的数据恢复正常(类似从回收站还原),使用restore()方法;

    php 复制代码
    //将被软删除的数据回复正常
    $users = User::onlyTrashed()->find(82);
    $users->restore();
  11. 如果开启了软删除,还需要强行真实的永久删除,可以使用forceDelete()方法;

    php 复制代码
    //开启软删除时的真实永久删除
    $users = User::onlyTrashed()->find(82);
    $users->forceDelete();

20.模型的作用域

一.本地作用域
  1. 很多情况下,我们在数据查找时有一部分条件会被重复且大量使用;

  2. 而这个条件,可能只是在这个模型对应的数据表使用,别的表并不使用;

  3. 那么这种情况,可以使用本地作用域的方式,将常用的 SQL 封装起来;

  4. 比如:用户模块中,我们大量查询需要查询性别为男,且其它条件的SQL;

    php 复制代码
    $users = User::where('gender', '男')
        ->where('price', '>', 90)
        ->get();

    PS:我们可以将性别为男这个片段,封装成一个单独的方法,然后统一在这个模型下调用;

    php 复制代码
    //App\Http\Models;
    //本地作用域,搜索自动添加为"男"的条件
    //语法:scope 开头,后面名称尽可能包含语义
    public function scopeGenderMale($query)
    {
    	return $query->where('gender', '男');
    }
    //当然,如果感觉单词太长,直接 gm()也行
    $users = User::genderMale()
            ->where('price', '>', 90)
            ->get();
  5. 上面的方法比较死板,适合简单粗暴,如果想要灵活多变,支持传递参数;

    php 复制代码
    //参数可以是 1 个或多个
    $users = User::gender('女', -3)
        ->where('price', '>', 90)
        ->get();
    //参数 2 和 3,接受控制器传递过来的 1,2
    public function scopeGender($query, $value, $value2)
    {
    	return $query->where('gender', $value)->where('status', $value2);
    }
二.全局作用域
  1. 全局作用域,顾名思义就是在任意地方都可以有效的封装条件;

  2. 比如有个需求,不管在哪里操作,总是显示 status 为 1 的用户;

  3. 首先在 app 目录下创建一个用于全局作用域的目录:Scopes;

  4. 创建一个用于设置 status 为 1 的全局作用域的类,它需要实现scope 接口;

    php 复制代码
    namespace App\Scopes;
    //这里引用代码自动生成
    use Illuminate\Database\Eloquent\Builder;
    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\Scope;
    class StatusScope implements Scope
    {
        /**
        * @inheritDoc
        */
        public function apply(Builder $builder, Model $model)
        {
            // TODO: Implement apply() method.
            return $builder->where('status', 1);
        }
    }
  5. 此时,还不能实现全局,因为需要在模型设置个开关,让其富有灵活性;

    php 复制代码
    //启用全局作用域
    protected static function booted()
    {
        parent::booted(); // TODO: Change the autogenerated stub
        static::addGlobalScope(new StatusScope());
    }

    PS:而在控制器端,并不需要做任何设置,即可自动添加 status=1 的条件;

  6. 当然,如果这个全局只是针对某个模块,并不需要创建一个全局类,直接闭包即可;

    php 复制代码
    static::addGlobalScope('status', function (Builder $builder) {
    	return $builder->where('status', 1);
    });

    PS:注意 Builder 引入的文件和全局类引入的文件一致,如果引入别的同名类会错;

  7. 如果某个查询,并不需要这个全局条件,可以单独移出掉;

    php 复制代码
    //取消名称为 status 的全局
    $users = User::withoutGlobalScope('status')->get();
    //取消全局类的条件
    $users = User::withoutGlobalScope(StatusScope::class)->get();

    PS:还有 withoutGlobalScopes([])方法,传递参数取消多个全局;

21.模型的访问器和修改器

一.访问器
  1. 访问器:就是在获取数据列表时,拦截属性并对属性进行修改的过程;

  2. 比如,我们在输出性别时,在性别左右加上括号,或给邮件转换为大写;

    php 复制代码
    //访问器,前固定 get,后固定 Attribute,Gender 是字段名
    //参数$value 是源字段值,可修改返回
    public function getGenderAttribute($value)
    {
    	return '【'.$value.'】';
    }

    PS:如果字段名是两个单词中间是下划线:user_name,那么方法名:getUserNameAttribute()

  3. 我们也可以创建一个虚拟字段,用已有的数据字段进行整合,不过要进行数据追加;

    php 复制代码
    //将虚拟字段追加到数据对象列表里去
    protected $appends = ['info'];
    //创建一个虚拟字段
    public function getInfoAttribute()
    {
    	return $this->username.'-'.$this->gender;
    }

    PS:注意,如果 gender 之前已经有访问器修改过,上面的方法会得到修改过的结果;

    PS:如果要使用源字段进行创建虚拟字段,需要使用下面这种:

    return $this->attributes['username'].'-'.$this->attributes['gender'];

二.修改器
  1. 修改器,相对于访问器,是在写入的时候拦截,进行修改再写入;

    php 复制代码
    //修改器,写入数据时,将邮箱转换为大写
    public function setEmailAttribute($value)
    {
    	$this->attributes['email'] = strtoupper($value);
    }
  2. 可以添加默认的日期列,默认 created_at 和 updated_at;

    php 复制代码
    //设置可以自动写入日期的列
    protected $dates = [
    	'details'
    ];
  3. 可以设置字段输出的类型,比如设置一个布尔型,输出时就是true 和false;

    php 复制代码
    //设置字段类型
    protected $casts = [
    	'details' => 'boolean'
    ];

22.集合的使用

一.创建集合
  1. 什么是集合?即:它是一种更具读取性和处理能力的数组封装;

  2. 比如,我们从数据库得到的数据列表,它就是一种集合;

  3. 数据集合,提供了大量的方法方便我们进行各种操作;

  4. 除了数据库对象返回的数据集合之外,我们还可以自行创建数据集合;

    php 复制代码
    //创建一个数据集合
    $collection = collect(['张三', '李四', '王五', null]);
    //使用 dd 查看它的类型
    dd($collection);
    //直接 return 可以返回
    return $collection;
  5. 数据集合提供了大概有三十多个(31?)处理数据集合的方法,可链式调用;

  6. 这里我们把最常用的演示一遍,剩下的所有,可以自行参考手册方法列表;

    php 复制代码
    //以底层数组形式输出
    return $collection->all();
    //map 方法,类似访问器,可修改输出
    return $collection->map(function ($value, $key) {
    	return $key.'['.$value.']';
    });
    //支持链式,reject 移出非 true 的值
    return $collection->reject(function ($value, $key) {
    	return $value === null;
    })->map(function ($value, $key) {
    	return $key.'['.$value.']';
    });
    //filter 筛选为 true 的值,和 reject 相反
    return $collection->filter(function ($value, $key) {
    	return $value === null;
    });
    
    //search 找到后返回 key,找不到返回 false
    return $collection->search('王五');
    //集合的分割
    return $collection->chunk(2);
    //迭代输出
    $collection->each(function ($item, $key) {
    	echo $item;
    });

    PS:这里就介绍这么多,更多的我们去手册扫一遍。做项目时,凭着记忆回头查,慢慢就熟了;PS:下一节,我们会花一节课,把最常用的方法再运行一遍加深印象;

  7. 如果三十多个方法都没有你要的,还可以自定义方法,比如说所有英文大写;

    php 复制代码
    $collection = collect(['Mr.Zhang', '李四', '王五', null]);
    Collection::macro('toUpper', function () {
        //dd($this);
        return $this->map(function ($value) {
    	    return strtoupper($value);
        });
    });
    return $collection->toUpper();

23.集合的常用方法

一.常用方法
  1. all()方法,转换为属性形式输出,使用 dd 方法看类型;

    php 复制代码
    $collection = collect([1, 2, 2, 3, 4, 4, 4]);
    dd($collection->all());
    // PS:$collection->dd()方法可以以 dd()模式输出,还有 dump()模式;
  2. avg()方法返回平均值;

    php 复制代码
    //返回平均值
    $collection = collect([1, 2, 3, 4]);
    return $collection->avg();
    //返回分组平均值
    $collection = collect([['男'=>1], ['女'=>1], ['男'=>3]]);
    return $collection->avg('男');
  3. count()方法返回集合总数;

    php 复制代码
    return $collection->count();
    // PS:相关的还有 sum()、min()、max()等统计;
  4. countBy()方法返回数值出现的次数或回调函数指定值出现的次数;

    php 复制代码
    $collection = collect([1, 2, 2, 3, 4, 4, 4]);
    return $collection->countBy();
    //回调搜索相同指定片段的值的次数
    $collection = collect(['[email protected]', '[email protected]', '[email protected]']);return $collection->countBy(function ($value) {
    	return substr(strrchr($value, '@'), 1);
    });
    // PS:相关的还有 groupBy()、keyBy()方法;
  5. diff()方法返回集合数组之间不相同的部分,组合新的集合;

    php 复制代码
    //diff 返回两个集合中不相同的
    $collection = collect([1, 2, 3, 4, 5]);
    return $collection->diff([3, 5]);
    // PS:其中还有 diffAssoc()、diffKeys()派生方法;
  6. duplicates()返回重复的值;

    php 复制代码
    $collection = collect([1, 2, 2, 3, 4, 5, 5, 6]);
    return $collection->duplicates(); //严格派生方法:duplicatesStrict()
  7. first()返回成立后的第一个值;

    php 复制代码
    //返回判断成立的第一条数值
    $collection = collect([1, 2, 3, 4]);
    return $collection->first(function ($value) {
    return $value > 2;
    });
    // PS:相关的还有 every()、except()、only()、firstWhere()、last()等方法;
  8. flatten()将多维数组转换为一维;

    php 复制代码
    $collection = collect(['name'=>'Mr.Lee', 'details'=>['gender'=>'男', 'age'=>100]]);
    return $collection->flatten();
  9. get()通过键名找值;

    php 复制代码
    $collection = collect(['name'=>'Mr.Lee', 'gender'=>'男']);
    return $collection->get('name');
    // PS:相关的还有 pluck()等;
  10. has()判断集合中是否存在指定键;

    php 复制代码
    return $collection->has('name');
  11. pop()移出集合中最后一个值;

    php 复制代码
    $collection = collect([1, 2, 3, 4, 5]);
    //$collection->pop();
    return $collection;
    // PS:相关的还有 pull()、push()、put()方法
  12. slice()返回指定值后续的集合;

    php 复制代码
    $collection = collect([1, 2, 3, 4, 5]);
    return $collection->slice(3);

    PS:相关的还有 splice()等方法;

  13. sort()返回指定值后续的集合;

    php 复制代码
    $collection = collect([3, 1 , 5, 2, 7]);
    return $collection->sort()->values(); //需要配合 values()方法

    PS:相关的有 sortBy()、sortByDesc()、sortKeys()等;

  14. where()系列方法,和数据库条件一样;

    php 复制代码
    $collection = collect([
        ['name'=>'Mr.Lee', 'gender'=>'男'],
        ['name'=>'Miss.Zhang', 'gender'=>'女']
    ]);
    return $collection->where('name', 'Mr.Lee');

24.模型的数据集合

一.数据集合
  1. 数据集合,就是已经将模型方法 get()获取到的数据再进行处理;

  2. 比如:map()方法,通过它可以实现类似访问器一样对字段进行处理的效果;

    php 复制代码
    $users = User::get();
    //使用集合方法 map 可以对输出的字段进行过滤
    $women = $users->map(function ($user) {
        $user->email = strtoupper($user->email);
        return $user;
    });
    return [$women]

    PS:数据集合支持连缀操作,和数据库连缀一样;

  3. 使用 reject()方法,可以获取条件之外的数据内容;

    php 复制代码
    $women = $users->reject(function ($user) {
    	return $user->gender != '女';
    })->map(function ($user) {
    	return $user;
    });
  4. 下面列出常用的集合方法列表:

    php 复制代码
    //判断集合中是否包含指定的模型实例
    return $users->contains(19);
    return $users->contains(User::find(19));
    //返回不在集合中的所有模型
    return $users->diff(User::whereIn('id', [19,20,21])->get());
    //返回给定主键外的所有模型
    return $users->except([19,20,21]);
    //集合也有 find 方法
    return $users->find(19);
    //返回集合的数量
    return $users->count();
    //返回所有模型的主键
    return $users->modelKeys();
    //返回主键的所有模型
    return $users->only([19,20,21]);
    //返回集合中的唯一模型
    return $users->unique();

25.模型的一对一关联

一.关联概念
  1. 关联模型,即:两张或以上的表进行一定规则的绑定关联;
  2. 比如:一个学生(学生表)对应一张个人信息卡(信息表),这种就是一对一;
  3. 再比如:一篇博文(帖子表)对应多个评论(评论表),这种就是一对多;
  4. 再再比如:一个用户(用户表)对应多个职位(权限表), 而一个职位又可以有多个用户;那么,这种就是多对多关联;
  5. 自然,还有更多更复杂的关联,都是基于此的,我们只探讨这三种;
  6. 既然是关联,当然会有绑定的概念,当有数据库操作,关联表也会跟着变动;
  7. 这就是关联模型的意义;
二.一对一关联
  1. 一对一关联,我们选择两张表来做演示,首先看下两张表的结果对比;

  2. 由于之前 Models 下的 User.php 模型代码很多了,改成.bak 后重建;

  3. 创建两个 model,User.php 和 Profile.php,并使用命令实现提示;

    php artisan ide-helper:models

    php 复制代码
    //User.php,一对一关联 Profile 表
    public function profile()
    {
        //参数 1 或:'App\Http\Models\Profile'
        //参数 2:默认为 user_id,如不是需要指明
        //参数 3:默认 id,如不是需要指明
    	return $this->hasOne(Profile::class, 'user_id', 'id');
    }
  4. 注意:Profile.php 只要建立空类即可,不需要其它,一对一调用如下:

    php 复制代码
    //注意:->profile 不要加括号,以属性方式访问
    $profiles = User::find(19)->profile;
    return $profiles;
  5. 一对一也支持反向关联:定向反向关联;具体通过在 Profile 设置即可;

    php 复制代码
    //profile.php
    //参数 1 为主表类
    //参数 2,3 和正向一致,默认对应可以不写
    public function user()
    {
    	return $this->belongsTo(User::class, 'user_id', 'id');
    }
    $users = Profile::find(1)->user;
    return $users;

26.模型的一对多关联

27.模型的多对多关联

28.模型的关联查询

29.Debugbar调试器

30.模型的预加载

31.模型的关联写入

32.多对多的关联写入

33.请求和依赖注入

34.请求的常用方法

35.生成URL

36.Cookie

37.Session

38.理解中间件

39.中间件进阶

40.Blade模板入门

41.模版的流程控制

42.模板的继承布局

43.表单快速验证

44.验证类的使用

45.创建手动验证

46.验证规则大全

47.数据分页

相关推荐
小马过河R1 天前
不加载PHP OpenTelemetry SDK实现Trace‌与Logs
开发语言·分布式·微服务·云原生·php
源码师傅1 天前
PHP+MySQL开发语言 在线下单订水送水小程序源码及搭建指南
php·送水小程序·桶装水小程序·在线下单送水小程序源码·桶装水送货上门小程序·订水线上商城
专注代码七年1 天前
php:5.6-apache Docker镜像中安装 gd mysqli 库 【亲测可用】
php·apache
夕水1 天前
分享一些实用的PHP函数(对比js/ts实现)(1)
后端·php
浩浩测试一下1 天前
reverse_ssh 建立反向 SSH 连接指南 混淆&&AV [好东西哟]
运维·开发语言·网络·安全·网络安全·ssh·php
hl200503301 天前
PHP中的语句
开发语言·php
即安莉2 天前
ESP8266远程控制:实现网络通信与设备控制
开发语言·stm32·单片机·嵌入式硬件·php
吃着火锅x唱着歌2 天前
PHP7内核剖析 学习笔记 第九章 PHP基础语法的实现
笔记·学习·php
Ashlee_code3 天前
TRS收益互换平台开发实践:从需求分析到系统实现
java·数据结构·c++·python·架构·php·需求分析
Estar.Lee3 天前
PHP实战:安全实现文件上传功能教程
android·开发语言·php