Laravel基础
Laravel 基础
01.Laravel入门和安装
Composer安装Laravel步骤
要使用 Composer 安装 Laravel,请按照以下步骤操作:
-  确保已经安装了 Composer。如果还没有安装,请访问 https://getcomposer.org/download/ 下载并安装。 
-  打开命令行或终端。 
-  使用 cd命令导航到你的项目目录,例如:cd /path/to/your/project
-  在项目目录中,运行以下命令创建一个新的Laravel项目: composer create-project --prefer-dist laravel/laravel your_project_name其中, your_project_name是你的项目名称。这将下载 Laravel 的最新版本并创建一个新的项目。
-  安装完成之后进入项目 cd your_project_name
-  将 .env.example文件复制并重命名为.env:cp .env.example .env
-  在 .env文件中配置数据库连接信息。例如: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
-  生成应用密钥: php artisan key:generate
-  运行数据库迁移和数据填充(如果有): php artisan migrate php artisan db:seed
-  最后,启动开发服务器: php artisan serve
这将在本地启动一个 HTTP 服务器,通常在 http://localhost:8000 上运行。现在你可以开始构建你的 Laravel 应用了。
02.路由的定义和控制器
一.路由的定义
-  什么是路由?路由就是提供接受 HTTP 请求的路径,并和程序交互的功能; 
-  简单点理解,就是为了提供访问程序的 URL 地址,所做的一些设置工作; 
-  phpstorm 支持 cmd 操作,左下角 Terminal 按钮展开,调整字体即可; 
-  输入 php artisan serve 命令后,即支持 localhost:8000 内置服务器; 
-  路由的定义文件在根目录 routes/web.php 中,可以看到welcome 页面; 
-  我们创建一个路由,让它返回一段信息,并设置响应的 url 地址; Route::get('index', function () {return "<h1>Hello World</h1>"; });
-  在路由定义上,我们采用了::get()这个方法,它接受的就是GET 提交; 
-  ::post()、::put()、::delete()是表单和 Ajax 的提交接受方式; 
-  ::any()表示不管你是哪种提交方式,我智能的全部接收响应; 
-  ::match()表示接收你指定的提交方式,用数组作为参数传递; // 使用 match 必须有三个参数, any 是两个参数 Route::match(['get', 'post'], 'match', function () {return "Matched"; });
-  在路由的规则和闭包区域,我们可以设置和传递路由参数; Route::get('index/{id}', function ($id) {return "<h1>Hello World $id</h1>"; }); // http://localhost:8000/index/5
-  上面例子中{id}表示在 url 的动态参数,比如数字 5; 
-  那么闭包的$id,可以接受 url 传递过来的 5,最终输出 5; 
二.创建控制器
-  MVC 模式中 C 代表控制器,用于接收 HTTP 请求,从而进行逻辑处理; 
-  有两种方式可以创建控制器,IDE 直接创建,或使用命令生成一个; php artisan make:controller TaskController
-  控制器目录在 app\Http\Controllers 下,使用 IED 会自动生成命名空间; <?php namespace App\Http\Controllers;class TaskConterollers extends Controller {public function index(){return "task index";}public function read($id){return "id is $id";} }
-  通过设置路由来访问创建好的控制器,参数二:控制器@方法名; use App\Http\Controllers\TaskConterollers;Route::get("/task/read/{id}", [TaskConterollers::class, 'read'])->where(['id' => '[0-9]+', 'name' => '[a-z]+']);
03.路由参数.重定向.视图
一.路由参数
-  上一节课,我们已经学习了部分路由参数的功能,比如动态传递 {id};
-  那么,有时这个参数需要进行约束,我们可以使用正则来限定必须是数字; //单个参数 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]+']);
-  如果想让约束 id 只能是 0-9 之间作用域全局范围,可以在模型绑定器里设置; 
-  模型绑定器路径为: app\Providers\RouteServiceProvider 的boot()方法;public function boot() {$this->pattern('id', '[0-9]+'); // 全局都有效 }
-  如果 id 已经被全局约束,在某个局部你想让它脱离约束,可以如下操作: // 全局范围的约束解除 Route::get("/task/read/{id}", [TaskConterollers::class, 'read'])->where('id', '.*');
二.路由重定向
-  可以设置访问一个路由的 URI,跳转到另一个路由的 URI,具体如下: Route::redirect('index', 'task'); Route::redirect('index', 'task', 301); // 状态码
-  还有一个方法,可以直接让路由跳转返回 301 状态码而不用设置: Route::permanentRedirect('index', 'task');
三.视图路由
-  在使用视图路由之前,我们先要创建一个视图(MVC)中的 V 部分; 
-  使用视图路由,有三个参数:1.URI(必);2.名称(必);3.参数(选); //参数 1:URI,localhost:8000/task //参数 2:view,resources/views/task.blade.php //参数 3:传参,{{$id}} Route::view('task', 'task', ['id'=>10]);
-  对于视图页面的创建,在 resources/views 下创建 task.blade.php; {{--静态页面--}} task{{$id}}
-  当然,也可以使用助手函数 view()方法来实现这个功能; //这里 view()的参数 1 是视图名称 //参数 2 传参,可选; Route::get('task', function () {return view('task', ['id'=>10]); });
-  也可以将路由直接指向控制器的方法,通过方法实现 view()引入视图; public function index() {return view('task', ['id'=>10]); }
04.路由命名和分组
一.路由命名
-  给一个制定好的路由进行命名,可以生成 URL 地址或进行重定向; Route::get('task', 'TaskController@index')->name('task.index');
-  在控制器区域,使用助手函数 route()来获取路由生成的 URL 地址; //生成 url 地址,http://localhost:8000/task route('task.index'); PS:URL 是 URI 的子集,更多区别请百度;
-  route()助手的第二参数为参数,第三参数为是否包含域名URL;// 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}
-  使用 redirect()助手结合 route()生成一个重定向跳转,注意不要自我死跳; //生成重定向 return redirect()->route('task.index', ['id'=>10]);
二.路由分组
-  路由分组功能是为了让大量路由共享路由属性,包括中间件、命名空间等; //一个空的分组路由 Route::group([], function () {Route::get('index/{id}', function ($id) {return 'index'.$id;});Route::get('task/{id}', function ($id) {return 'task'.$id;}); });
-  可以将中间件作用域路由分组中,有两种写法,至于中间件?后续章节讲解; //引入中间件,方法一 Route::group(['middleware'=>'中间名'], function () {}); //引入中间件,方法二 Route::middleware(['中间件'])->group(function () {});
-  可以设置路由路径前缀,通过 prefix 来设置,也有两种方法,具体如下: //引入路由前缀,方法一 Route::group(['prefix'=>'api'],function () {}); //引入路由前缀,方法二 Route::prefix('api')->group(function () {});
-  可以设置子域名,从而限定路由可执行的域名,有两种方法,具体如下: //引入子域名,方法一 Route::group(['domain'=>'127.0.0.1'], function () {}); //引入子域名,方法二 Route::domain('127.0.0.1')->group(function () {});
-  可以设置命名空间,让命名空间分配给控制器,让其得以访问,具体如下: //命名空间,方法一 Route::group(['namespace'=>'Admin'],function () {}); //命名空间,方法二 Route::namespace('Admin')->group(function () {}); PS:在 Controller 目录下创建 Admin 目录,再其目录下创建的控制器命名空间如下:namespace App\Http\Controllers\Admin;
-  可以设置名称前缀,方式两种,也可以嵌套,具体如下: //名称前缀,方式一 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.回退.当前路由.单行为
一.单行为控制器
-  之前的课程,我们简单的创建和定义了控制器,并继承了控制器基类; 
-  为何要继承基类?因为继承基类后,可以使用基类的方法,比如中间件等; 
-  继承基类后除了支持中间件快捷使用,还支持验证、列队等快捷方法; public function __construct() {$this->middleware('中间件'); }
-  如果你想要定义一个只执行一个方法的控制器,可以使用单行为控制器; 
-  单行为控制器使用 __invoke()方法,可以使用命令行创建;命令: php artisan make:controller OneController --invokable//手工创建 class OneController extends Controller {public function __invoke(){return '单行为控制器';} }
-  单行为控制器,路由定义就不需要指定特定的方法,指定控制器即可 
-  单行为控制器只是语义上的单行为,并没有限制创建更多方法访问; Route::get('one', 'OneController');
二.路由回退
-  如果我们跳转到了一个不存在路由时,会产生 404 错误,体验不佳; 
-  可以使用回退路由,让不存在的路由自动跳转到你指定的页面去; 
-  注意:由于执行顺序问题,必须把回退路由放在所有路由的最底部; Route::fallback(function () {return redirect('/'); });
-  当然,你也可以制作一个自己的 404 页面,用回退路由加载这个页面; Route::fallback(function () {return view('404'); });
三.当前路由
-  我们可以通过使用 ::current()系列方法,来获取当前路由的访问信息;Route::get('index', function () {//当前路由信息dump(Route::current());//返回当前路由的名称return Route::currentRouteName();//返回当前路由指向的方法return Route::currentRouteAction(); })->name('localhost.index');
06.响应设置和重定向
一.响应设置
-  路由和控制器处理完业务都会返回一个发送到浏览器的响应:return; 
-  比如字符串会直接输出,而数组则会输出 json 格式,本身是Response 对象; return [1, 2, 3]; //输出 json 格式 return response([1, 2, 3]); //同上 return response()->json([1, 2, 3]); //同上
-  如果使用 response()输出的话,可以设置状态码和响应头信息;return response('index', 201); //可以设置 HTTP 请求状态码
-  也可以给 HTTP 添加或修改标头,比如将 html 解析模式改成文本plain 模式; return response('<b>index</b>')->header('Content-Type', 'text/plain'); //文本解析模式
-  结合上面的响应操作,再结合 view()视图功能,显示纯 HTML 代码页面; return response()->view('task', ['id'=>10], 201)->header('Content-Type', 'text/plain');
二.路由重定向
-  重定向使用助手函数 redirect()的 to()方法,注意需要return 才能跳转 return redirect()->to('/'); //跳到首页 return redirect()->to('task'); //跳转到 task return redirect()->to('task/url'); //跳转到 task/url
-  也可以直接使用快捷方式直接进行跳转; 常用 
return redirect('/'); //跳到首页
return redirect('task'); //跳转到 task
return redirect('task/url'); //跳转到 task/url
-  redirect()助手有一个对应的 facade 模式对象;return Redirect::to('/'); //facade 模式,但需要 use 引入
-  使用 redirect()的 route()方法,可以跳转到指定的命名路由URI;return redirect()->route('task.index'); //注意和 route()方法区别
-  使用 redirect()的back()方法,可以重定向到上一个页面中;常用return redirect()->back(); return back(); //快捷方式
-  使用 redirect()的 action()方法,可以直接重定向到控制器方法;return redirect()->action('TaskController@index'); //需注册路由 return redirect()->action('TaskController@index', ['id'=>10]);
-  使用 redirect()的 away()方法,跳转到外部链接;return redirect()->away('http://www.baidu.com'); //不带任何编码
07.资源控制器
一.资源控制器
-  声明:资源控制器是某个特定场景下的产物,完全理解需要PHP项目基础; 
-  比如开发过博客系统,留言帖子系统之类,具有类似思维,否则你懂的…; 
-  只是学习了PHP基础,就立刻学习框架的同学,可以过一遍即可(不影响后续)…; 
-  有一种控制器专门处理CURD(增删改查),方法很多且方法名基本固定; 
-  对于这种控制器,我们可以将它设置为资源型控制器,不要大量设置路由; 
-  这里推荐直接使用命令行生成资源路由,比如: BlogControllerphp artisan make:controller BlogController --resource
-  生成了的资源控制器会产生 7 个方法,配置好路由后会自动生成相关内容 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 删除一条数据 
-  如果我们注册了资源路由,那么如上图的资源路由 URI 和名称均自动创建生效; http://localhost:8000/blogs/10/edit //可以访问到edit方法return route('blogs.store'); //可以通过助手route()了解是否注册
-  还有一条命令可以直接查看目前可用的路由以及命名; 常用 php artisan route:list
-  我们也可以限制资源路由只开放部分方法或排除部分方法,可以用命令查看; // 只有 index 和 show 可以访问 Route::resource('blogs', BlogController::class)->only(['index', 'show']); // index 和 show 不可访问,剩下的都可以 Route::resource('blogs', BlogController::class)->except(['index', 'show']);
-  资源控制器还有一种不需要HTML页面方法的API路由,只提供数据接口; //API资源,并不需要HTML页面(create,edit),会排除 Route::apiResource('blogs', 'BlogController');//批量方式 Route::apiResources(['blogs' => 'BlogController' ]);
-  当然,也支持一开始就生成一个不包含HTML页面方法的资源控制器; 
-  要注意的是,对应的资源路由,直接使用api资源路由即可;创建时就说是API php artisan make:controller CommentController --apiRoute::apiResource('comments', CommentController::class);
08.资源嵌套.浅嵌套.自定义
-  声明:资源控制器是某个特定场景下的产物,完全理解需要PHP项目基础; 
-  比如开发过博客系统、留言帖子系统之类,具有类似思维,否则你懂的…; 
-  只是学习了PHP基础就立刻学习框架的同学,可以过一遍即可(不影响后续); 
-  在一篇博文(Blog)下有多条评论(Comment),编辑某条博文下的一条评论; 
-  以上需求,可以通过嵌套资源路由来实现这个功能: php artisan make:controller CommentController --resource//嵌套资源路由 Route::resource('blogs.comments', CommentController::class);HTTP类型 路由URI 控制器方法 路由命名 GET blogs/{blog}/commentsindex()blogs.comments.indexGET blogs/{blog}/comments/createcreate()blogs.comments.createPOST blogs/{blog}/commentsstore()blogs.comments.storeGET blogs/{blog}/comments/{comment}show()blogs.comments.showGET blogs/{blog}/comments/{comment}/editedit()blogs.comments.editPUT/PATCH blogs/{blog}/comments/{comment}update()blogs.comments.updateDELETE blogs/{blog}/comments/{comment}destroy()blogs.comments.destroy
-  以上需求,可以通过嵌套资源路由来实现这个功能,编辑方法以及传参如下: public function edit($blog_id, $comment_id) {return '编辑博文下的评论,博文id: ' . $blog_id . ',评论id: ' . $comment_id; }
-  而实际上,每个 id 都是独立唯一的,并不需要父 id 和子id 同时存在; 
-  为了优化资源嵌套,通过路由方法 ->shallow()实现浅层嵌套方法; 取消父级// 浅层嵌套 Route::resource('blogs.comments', 'CommentController')->shallow();
-  实现后的路由及参数传递更精准: HTTP类型 路由URI 控制器方法 路由命名 GET blogs/{blog}/commentsindex()blogs.comments.indexGET blogs/{blog}/comments/createcreate()blogs.comments.createPOST blogs/{blog}/commentsstore()blogs.comments.storeGET comments/{comment}show()blogs.comments.showGET comments/{comment}/editedit()blogs.comments.editPUT/PATCH comments/{comment}update()blogs.comments.updateDELETE comments/{comment}destroy()blogs.comments.destroypublic function edit($id) {return '评论id: ' . $id; }
-  如果觉得资源路由命名过长,可以自己自定义,有两种方式: ->name('index', 'b.c.i'); ->names(['index' => 'b.c.i']);
-  如果觉得资源路由的参数不符合你的心意,也可以改变: ->parameter('blogs', 'id'); ->parameters(['blogs' => 'blog_id', 'comments' => 'comment_id' ]);
09.表单伪造和CSRF保护
一.表单伪造
-  之前一直用的 GET 请求方式,而表单可以实现 POST 方式,我们来实验下: 
-  先在 TaskController 创建两个方法,一个表单页,一个接受表单数据路由; public function form() {return view('form'); } //表单页 Route::get('task/form', 'TaskController@form'); //接受表单数据 Route::any('task/getform', function () {return \Illuminate\Support\Facades\Request::method(); });
-  表单页以 post 发送,路由也使用 post 接受,以下表单提交会出现419 错误; <form action="/task/getform" method="post">用户名:<input type="text" name="user"><button type="submit">提交</button> </form>
-  这是为了避免被跨站请求伪造攻击,框架提供了 CSRF 令牌保护,请求时验证; <input type="hidden" name="_token" value="{{csrf_token()}}">
-  表单可以实现 POST 提交方式,那其它提交方式该如何实现呢?可以采用伪造技术; <input type="hidden" name="_method" value="PUT">
-  对于 CSRF 令牌保护和表单伪造提交方式,也支持快捷方式的声明,如下: @csrf @method('PUT')
-  如果我们想让某些 URL 关闭 csrf 验证,可以设置 csrf 白名单; 
-  白名单具体设置位置在:中间件目录下的 VerifyCsrfToken.php 文件; 
-  当然,不建议直接注释掉这个验证 csrf 功能的中间件; protected $except = ['api/*', ];
10.数据库配置入门
一.配置数据库
-  框架支持原生、查询构造器和 Eloquent ORM(关系型对象映射器)来操作数据库; 
-  数据库的配置在 config/database.php,如果是本地可以直接配置.env 文件;
-  我们通过.env 文件配置数据库连接的相关信息,以提供给database 读取; 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',... ]
-  我们可以直接创建一个新的控制器 DataController 来测试数据库部分; 
-  数据库有一个专用类 DB,可以用它来实现原生查询和构造器查询; //使用 DB 类的 select()方法执行原生 SQL $user = DB::select('select * from laravel_user'); return $user;
-  查询构造器主要通过 DB 类的各种数据库操作方法来实现,比如选定一条; //这里省去了 laravel_,需要在 database.php 配置 $user = DB::table('user')->find(19); return [$user];
-  由于火狐浏览器自动将 JSON 显示的很美化,而 find()只返回对象; return response()->json($user);
-  使用 Eloquent ORM 模型来操作数据库,使用命令在 Http 目录下创建模型; php artisan make:model Http/Models/User//使用 Eloquent ORM 构建 $user = User::all(); return $user;
上面使用模型来操作数据后,报错提示数据表是复数:users;
而我们真实的数据库表为:laravel_user,为何会这样???
前缀可以在 database.php 修改添加:laravel_,最终变为:laravel_users;
由于模型编码规范要求数据表是复数,这里的复数并不是单纯加s;
可能会加 es,可能会加 ies,也可能是 child 编程 children 之类的;
可以使用字符串助手:Str::plural()来判断英文单词的复数情况:
return Str::plural('bus'); //buses
return Str::plural('user'); //users
return Str::plural('child'); //children
你可以根据规范去更改数据表名称,或者强制使用现有的数据表名;
protected $table = 'user';
11.构造器的查询.分块.聚合
一.构造器查询
-  table()方法引入相应的表,get()方法可以查询当前表的所有数据; //获取全部结果 $users = DB::table('users')->get();
-  first()方法,可以获取到第一条数据; //获取第一条数据 $users = DB::table('users')->first();
-  value(字段名)方法,可以获取到第一条数据的指定字段的值; //获取第一条数据的 email 字段值 $users = DB::table('users')->value('email');
-  find(id)方法,可以获取指定 id 的一条数据; //通过 id 获取指定一条数据 $users = DB::table('users')->find(20);
-  pluck(字段名)可以获取所有数据单列值的集合; //获取单列值的集合 $users = DB::table('users')->pluck('username'); $users = DB::table('users')->pluck('username', 'email');
二.分块.聚合
-  如果你一次性处理成千上万条记录,防止读取出错,可以使用chunk()方法; //切割分块执行,每次读取 3 条,id 排序; DB::table('users')->orderBy('id')->chunk(3, function ($users) {foreach ($users as $user) {echo $user->username;}echo '------<br>'; });
-  构造器查询提供了:count()、max()、min()、avg()和 sum()聚合查询; //聚合查询 return DB::table('users')->count(); return DB::table('users')->max('price'); return DB::table('users')->avg('price');
-  构造器查询两个判断记录是否存在的方法:exists()和 doesntexists()方法; //判断是否存在 return DB::table('users')->where('id', 19)->exists(); return DB::table('users')->where('id', 18)->doesntExist();
PS:这里 DB::第一个使用静态,返回查询对象,然后使用->where 等各种查询方法,这些查询方法返回的还是查询对象,所以可以继续连缀操作。最后当遇到比如get()返回结果等方法时,停止连缀。所以,返回结果必须放在最后。
12.构造器的查询表达式
一.select 查询
-  select()方法可以制定你想要的列,而不是所有列;//设置显示的列,设置列别名 $users = DB::table('users')->select('username as name', 'email')->get();
-  addSelect()方法,可以在你基础的查询构造器上再增加想要显示的字段;//给已经构建好的查询添加更多字段 $base = DB::table('users')->select('username as name', 'email'); $users = $base->addSelect('gender')->get();
-  DB::raw()方法可以在 select()内部实现原生表达式,否则解析错误; //结合原生 SQL 实现复杂查询 $users = DB::table('users')->select(DB::raw('COUNT(*) AS id, gender'))->groupBy('gender')->get();
-  也可以直接使用 selectRaw()方法实现内部原生; //或者直接使用 selectRaw()方法实现原生 $users = DB::table('users')->selectRaw('COUNT(*) AS count, gender')->groupBy('gender')->get();
-  还可以通过 havingRaw()方法实现更精准的分组筛选; //使用 havingRaw 方法实现分组筛选 $users = DB::table('users')->selectRaw('COUNT(*) AS count, gender')->groupBy('gender')->havingRaw('count>5')->get();
二.where 查询
-  where()查询,即条件查询,完整形式需要字段表达式和值三个; //where 查询完整形式 $users = DB::table('users')->where('id', '=', 19)->get();
-  大部分情况下,是等于用的比较多,就可以省略掉=号参数; //where 查询完整形式 $users = DB::table('users')->where('id', 19)->get();
-  当然,还有>、<、>=、<=、<>、like 等操作符; users = DB::table('users')->where('price', '>=', 95)->get(); $users = DB::table('users')->where('username', 'like', '%小%')->get();
-  如果条件较多,可以用数组来分别添加条件,具体如下: //如果条件都是等于,查看 SQL 语句用->toSql()替换->get() $users = DB::table('users')->where(['price' => 90,'gender' => '男' ])->get(); //如果条件非等于 $users = DB::table('users')->where([['price', '>=', 90],['gender', '=', '男'] ])->get();
13.构造器的where派生查询
一.where 派生查询
-  orWhere()方法,可以通过连缀实现两个或以上的 or 条件查询; //where() + orWhere 实现 or 条件查询 $users = DB::table('users')->where('price', '>', 95)->orWhere('gender', '女')->toSql();
-  通过闭包,我们还可以构建更加复杂的 orWhere 查询; //orWhere()结合闭包查询 $users = DB::table('users') ->where('price', '>', '95') ->orWhere(function ($query) {$query->where('gender', '女')->where('username', 'like', '%小%'); })->toSql();
-  whereBetween()可以实现区间查询,比如价格在一个区间内的用户; //whereBetween 查询区间价格 60~90 之间 $users = DB::table('users')->whereBetween('price', [60, 90])->toSql(); // PS:这里还支持相关三种:whereNotBetween/orWhereBetween/orWhereNotBetween;
-  whereIn()可以实现数组匹配查询,比如匹配出数组里指定的数据; //whereIn 查询数组里匹配的数值 $users = DB::table('users')->whereIn('id', [20,30,50])->toSql();
-  whereNull()可以查询字段为 Null 的记录; //whereNull 查询字段值为 Null 的记录 $users = DB::table('users')->whereNull('uid')->toSql();PS:这里还支持相关三种:whereNotNull/orWhereNull/orWhereNotNull; 
-  whereDate()可以查询指定日期的记录; //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.构造器的排序分组.子查询
一.排序分组
-  使用 whereColumn()方法实现两个字段相等的查询结果;//判断两个相等的字段,同样支持 orWhereColumn() //支持符号'create_time','>', 'update_time' //支持符号支持数组多个字段格式['create_time','>', 'update_time'] $users = DB::table('users')->whereColumn('create_time', 'update_time')->get();
-  使用 orderBy()方法实现 desc 或 asc 排序功能。//支持 orderByRaw 和 orderByDesc 倒序方法 $users = DB::table('users')->orderBy('id', 'desc')->get();
-  使用 latest()方法设置时间倒序来排,默认时间字段是 created_at;//按照创建时间倒序排,默认字段 created_at $users = DB::table('users')->latest('create_time')->toSql();
-  使用 inRandomOrder()方法来随机排序,得到一个随机列表;//随机排序 $users = DB::table('users')->inRandomOrder()->get();
-  使用 skip()和 take()限制结果集,或使用 offset()和 limit(); //从第 3 条开始,显示 3 条 $users = DB::table('users')->skip(2)->take(3)->toSql(); $users = DB::table('users')->offset(2)->limit(3)->get();
-  使用 when()方法可以设置条件选择,执行相应的 SQL 语句; //when 实现条件选择 $users = DB::table('users')->when(true, function ($query) {$query->where('id', 19); }, function ($query) {$query->where('username', '辉夜'); })->get();
-  如果 MySQL 在 5.7+,有支持 JSON 数据的新特性; $users = DB::table('users')->where('list->id', 19)->first();
二.子查询
-  使用 whereExists()方法实现一个子查询结果,返回相应的主查询;//通过 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,一般用于子查询的手段,目的是减少开销,提升效率,深入请搜索;
-  也可以使用 where(字段,function())闭包,来实现一个子查询; //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 查询
-  使用 join 实现内联接的多表查询,比如三张表进行 inner join 查询; $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();
-  也可以使用 leftjoin 左连接或 rightjoin 右连接实现多表查询; $users = DB::table('users')->leftJoin('books', 'users.id', '=', 'books.user_id')->rightjoin('profiles', 'users.id', '=', 'profiles.user_id')->get();
-  使用 crossjoin 交叉连接查询,会生成笛卡尔积,再用 distinct()取消重复; $users = DB::table('users')->crossJoin('books')->select('username', 'email')->distinct()->get();
-  如果你想要实现闭包查询,和 where 类似,只不过要用 on 和orOn 方法; $users = DB::table('users')->join('books', function ($join) {//支持 orOn 连缀$join->on('users.id', '=', 'books.user_id');})->toSql(); // PS:on()方法后面如果想要再增加筛选条件,可以追加 where();
-  使用 joinSub 实现子连接查询,将对应的内容合并在一起输出; //子连接查询 $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();
-  使用 union()或 unionAll()方法实现两个查询的合并操作; //union 取消重复,unionAll 不取消重复 $query = DB::table('users'); $users = DB::table('users')->union($query)->get();
16.构造器的增删改
一.增删改操作
-  使用 insert()方法可以新增一条或多条记录; //新增一条记录 DB::table('users')->insert(['username' => '李白','password' => '123456','email' => 'libai@163.com','details' => '123' ]); //新增多条记录 DB::table('users')->insert([[...],[...] ]);
-  使用 insertOrIgnore()方法,可以忽略重复插入数据的错误; //忽略重复新增数据的错误 DB::table('users')->insertOrIgnore(['id' => 304,'username' => '李白','password' => '123456','email' => 'libai@163.com','details' => '123' ]);
-  使用 insertGetId()方法,获取新增后的自增 ID; //获取新增后返回的 ID $id = DB::table('users')->insertGetId(['username' => '李白','password' => '123456','email' => 'libai@163.com','details' => '123' ]); return $id;
-  使用 update()方法,可以通过条件更新一条数据内容; //更新修改一条数据 DB::table('users')->where('id', 304)->update(['username' => '李红','email' => 'lihong@163.com']);
-  使用 updateOrInsert()方法,可以先进行查找修改,如不存在,则新增; //参数 1:修改的条件 //参数 2:修改的内容(新增的内容) DB::table('users')->updateOrInsert(['id'=>307],['username'=>'李黑', 'password'=>'654321', 'details'=>'123'] );
-  对于 json 数据,新增和修改的方法和正常数据类似; //新增时,转换为 json 数据 'list' => json_encode(['id'=>19]) //修改时,使用 list->id 指定 DB::table('users')->where('id', 306)->update(['list->id' => 20]);
-  更新数据时,可以使用自增 increment()和自减 decrement()方法; //默认自增/自减为 1,可设置 DB::table('users')->where('id', 306)->increment('price'); DB::table('users')->where('id', 306)->increment('price', 2);
-  使用 delete()删除数据,一般来说要加上 where 条件,否则清空; //删除一条数据 DB::table('users')->delete(307); DB::table('users')->where('id', 307)->delete(); //清空 DB::table('users')->delete(); DB::table('users')->truncate();
17.模型的定义
一.默认设置
-  框架可以使用 Eloquent ORM 进行数据库交互,也就是关系对象模型; 
-  在数据库入门阶段,我们已经创建了一个 User.php 模型,如下: php artisan make:model Http/Models/User默认在 app 目录
-  而调用的时候,我们也知道表名要遵循它默认规则,修改为复数,或特定; class User extends Model {protected $table = 'user'; }
-  系统假定你的主键为 id,如果你要修改默认主键,可以特定; protected $primaryKey = 'xid';
-  系统假定你的主键 id 为自增性,意味着是主键会自动转换int 类型; 
-  如果你希望不是非自增,非数值类型主键,可以设置取消; public $incrementing = false;
-  如果你主键不是一个整数,那么需要 $keyType设置为 string;protected $keyType = 'string';
-  系统默认情况下会接管 created_at 和 updated_at 两个时间戳列; 
-  如果不想让系统干涉这两个列,可以设置 false 取消; public $timestamps = false;
-  如果你想自定义时间戳的格式,可以设置; protected $dateFormat = 'U';
-  可以更改创建时间 created_at 和更新时间 updated_at 字段名; const CREATED_AT = 'create_time'; const UPDATED_AT = 'update_time';
-  默认读取 database.php配置的数据库连接,也可以在模型端局部更改;protected $connection = 'mysql';
二.模型定义
-  之前在查询构造器部分,把常用的数据库操作基本讲完,模型大体相同; 
-  比如,我们要查询所有数据,直接使用模型::all()即可; //查询所有记录 $users = User::get(); //或 all() return [$users];
-  也可以像查询构造器一样,添加各种各样的条件,写法一样; //查询性别为男,价格大于 90,限制显示 2 条 $users = User::where([['gender', '=', '男'],['price', '>', 95] ])->limit(2)->get();
-  虽然安装了插件,但模型还是没有代码提示,可以通过安装插件解决; composer require barryvdh/laravel-ide-helperphp artisan ide-helper:generate – 为 Facades 生成注释 php artisan ide-helper:models – 为数据模型生成注释 php artisan ide-helper:meta – 生成 PhpStorm Meta file
-  其它查询方法基本和查询构造器一样,如果有不一样,参考错误提示; 
-  这里列出官网给出示例的方法,对照实验(结合详细文档,重复较多); (1) .find(1) //通过主键查找 (2) .first() //查找第一个 (3) .firstWhere() //找到查询中的首个 (4) .find([1,2,3]) //通过数组查找 (5) .firstOr() //查找首个返回,支持闭包 (6) .firstOrFail() //找不到时返回异常 (7) .count()、max()等集合 //集合操作PS:还有很多在查询构造器中的方法,比如排序、分组子查询等等都可以使用(并未一一验证)。 
18.模型的增删改
一.增删改操作
-  新增方法如下,注意:默认模型接管 created_at 和 updated_at; $users = new User(); $users->username = '辉夜'; $users->password = '123'; $users->email = 'huiye@163.com'; $users->details = '123'; $users->save();
-  更新,只要是查找到一条数据的情况下使用 save()就是更新; $users = User::find(321); $users->username = '夜辉'; $users->save();
-  使用 update()方法实现批量更新; User::where('username', '夜辉')->update(['username' => '辉夜']);
-  使用 create()方法实现新增,但需要在模型端设置批量赋值的许可; User::create(['username' => '辉夜','password' => '123','email' => 'huiye@163.com','details' => '123', ]); //许可批量赋值,默认不可 protected $fillable = ['username','password','email','details' ]; //不许可的批量赋值,不可和$fillable 同时使用 //protected $guarded = ['uid']; //如果取消批量赋值限制,直接如下 protected $guarded = [];PS:必须在模型中定义批量赋值的可填充字段,否则无法生效;防止用户不小心设置新值; 
-  使用 delete()方法,可以删除数据; $users = User::find(332); $users->delete(); //批量删除 $users = User::where('username', '夜辉'); $users->delete();
-  如果你是通过主键 id 删除,那使用 destroy(id)方法,免去查询操作; //通过主键删除 User::destroy(328);
19.批量赋值和软删除
一.批量赋值
- 上一节增删改中,新增中我们发现需要进行批量赋值的许可;
- 一般情况下,是为了防止提交过来的字段在部分场景中不需要或不能;
- 所以,我们需要通过黑白名单机制进行过滤掉必要的字段;
//通过提交过来的数据一次性新增
User::create(\Request::all());
二.软删除
-  什么叫软删除?它相对于真实的删除,而并非真正的删除,只是隐藏了; 
-  首先,需要在数据库创建一个字段 deleted_at(默认),用于判断是否被软删除; 
-  默认设置这个字段为空(null),如果写入数据,成为非空状态,则说明被删除; 
-  开启软删除的功能,需要在模型端设置一下: use SoftDeletes; //开启软删除功能
-  当开启了软删除功能,之前的删除操作,都会变成更新操作,给deleted_at 赋值; //删除一 $users = User::find(82); $users->delete(); //删除二 User::destroy(81);
-  而当我们开启了软删除的功能后,此时通过正常的数据获取列表,会自动隐藏; //软删除的数据不可见 $users = User::get(); return [$users]; //单独获取被软删除的数据不行 $users = User::find(82); return [$users];
-  如果需要查询包含软删除的数据,通过 withTrashed()方法实现;//获取包含软删除的数据 $users = User::withTrashed()->get(); return [$users]; //获取某个被软删除的数据(即使不是软删除的也可以搜索到) $users = User::withTrashed()->find(82); return [$users];
-  如果只想搜索出被软删除的数据,通过 onlyTrashed()方法实现; //获取所有软删除的数据 $users = User::onlyTrashed()->get(); return [$users]; //获取某个被软删除的数据(只有软删除的数据才可以被搜索到) $users = User::onlyTrashed()->find(82); return [$users];
-  如果要对这个数据进行软删除的判断,是否是被软删除的,可以使用trashed(); //判断是否是被软删除的数据 $users = User::withTrashed()->find(81); return $users->trashed();
-  如果想将软删除的数据恢复正常(类似从回收站还原),使用restore()方法; //将被软删除的数据回复正常 $users = User::onlyTrashed()->find(82); $users->restore();
-  如果开启了软删除,还需要强行真实的永久删除,可以使用forceDelete()方法; //开启软删除时的真实永久删除 $users = User::onlyTrashed()->find(82); $users->forceDelete();
20.模型的作用域
一.本地作用域
-  很多情况下,我们在数据查找时有一部分条件会被重复且大量使用; 
-  而这个条件,可能只是在这个模型对应的数据表使用,别的表并不使用; 
-  那么这种情况,可以使用本地作用域的方式,将常用的 SQL 封装起来; 
-  比如:用户模块中,我们大量查询需要查询性别为男,且其它条件的SQL; $users = User::where('gender', '男')->where('price', '>', 90)->get();PS:我们可以将性别为男这个片段,封装成一个单独的方法,然后统一在这个模型下调用; //App\Http\Models; //本地作用域,搜索自动添加为“男”的条件 //语法:scope 开头,后面名称尽可能包含语义 public function scopeGenderMale($query) {return $query->where('gender', '男'); } //当然,如果感觉单词太长,直接 gm()也行 $users = User::genderMale()->where('price', '>', 90)->get();
-  上面的方法比较死板,适合简单粗暴,如果想要灵活多变,支持传递参数; //参数可以是 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); }
二.全局作用域
-  全局作用域,顾名思义就是在任意地方都可以有效的封装条件; 
-  比如有个需求,不管在哪里操作,总是显示 status 为 1 的用户; 
-  首先在 app 目录下创建一个用于全局作用域的目录:Scopes; 
-  创建一个用于设置 status 为 1 的全局作用域的类,它需要实现scope 接口; 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);} }
-  此时,还不能实现全局,因为需要在模型设置个开关,让其富有灵活性; //启用全局作用域 protected static function booted() {parent::booted(); // TODO: Change the autogenerated stubstatic::addGlobalScope(new StatusScope()); }PS:而在控制器端,并不需要做任何设置,即可自动添加 status=1 的条件; 
-  当然,如果这个全局只是针对某个模块,并不需要创建一个全局类,直接闭包即可; static::addGlobalScope('status', function (Builder $builder) {return $builder->where('status', 1); });PS:注意 Builder 引入的文件和全局类引入的文件一致,如果引入别的同名类会错; 
-  如果某个查询,并不需要这个全局条件,可以单独移出掉; //取消名称为 status 的全局 $users = User::withoutGlobalScope('status')->get(); //取消全局类的条件 $users = User::withoutGlobalScope(StatusScope::class)->get();PS:还有 withoutGlobalScopes([])方法,传递参数取消多个全局; 
21.模型的访问器和修改器
一.访问器
-  访问器:就是在获取数据列表时,拦截属性并对属性进行修改的过程; 
-  比如,我们在输出性别时,在性别左右加上括号,或给邮件转换为大写; //访问器,前固定 get,后固定 Attribute,Gender 是字段名 //参数$value 是源字段值,可修改返回 public function getGenderAttribute($value) {return '【'.$value.'】'; }PS:如果字段名是两个单词中间是下划线: user_name,那么方法名:getUserNameAttribute()
-  我们也可以创建一个虚拟字段,用已有的数据字段进行整合,不过要进行数据追加; //将虚拟字段追加到数据对象列表里去 protected $appends = ['info']; //创建一个虚拟字段 public function getInfoAttribute() {return $this->username.'-'.$this->gender; }PS:注意,如果 gender 之前已经有访问器修改过,上面的方法会得到修改过的结果; PS:如果要使用源字段进行创建虚拟字段,需要使用下面这种: return $this->attributes['username'].'-'.$this->attributes['gender'];
二.修改器
-  修改器,相对于访问器,是在写入的时候拦截,进行修改再写入; //修改器,写入数据时,将邮箱转换为大写 public function setEmailAttribute($value) {$this->attributes['email'] = strtoupper($value); }
-  可以添加默认的日期列,默认 created_at 和 updated_at; //设置可以自动写入日期的列 protected $dates = ['details' ];
-  可以设置字段输出的类型,比如设置一个布尔型,输出时就是true 和false; //设置字段类型 protected $casts = ['details' => 'boolean' ];
22.集合的使用
一.创建集合
-  什么是集合?即:它是一种更具读取性和处理能力的数组封装; 
-  比如,我们从数据库得到的数据列表,它就是一种集合; 
-  数据集合,提供了大量的方法方便我们进行各种操作; 
-  除了数据库对象返回的数据集合之外,我们还可以自行创建数据集合; //创建一个数据集合 $collection = collect(['张三', '李四', '王五', null]); //使用 dd 查看它的类型 dd($collection); //直接 return 可以返回 return $collection;
-  数据集合提供了大概有三十多个(31?)处理数据集合的方法,可链式调用; 
-  这里我们把最常用的演示一遍,剩下的所有,可以自行参考手册方法列表; //以底层数组形式输出 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:下一节,我们会花一节课,把最常用的方法再运行一遍加深印象; 
-  如果三十多个方法都没有你要的,还可以自定义方法,比如说所有英文大写; $collection = collect(['Mr.Zhang', '李四', '王五', null]); Collection::macro('toUpper', function () {//dd($this);return $this->map(function ($value) {return strtoupper($value);}); }); return $collection->toUpper();
23.集合的常用方法
一.常用方法
-  all()方法,转换为属性形式输出,使用 dd 方法看类型; $collection = collect([1, 2, 2, 3, 4, 4, 4]); dd($collection->all()); // PS:$collection->dd()方法可以以 dd()模式输出,还有 dump()模式;
-  avg()方法返回平均值; //返回平均值 $collection = collect([1, 2, 3, 4]); return $collection->avg(); //返回分组平均值 $collection = collect([['男'=>1], ['女'=>1], ['男'=>3]]); return $collection->avg('男');
-  count()方法返回集合总数; return $collection->count(); // PS:相关的还有 sum()、min()、max()等统计;
-  countBy()方法返回数值出现的次数或回调函数指定值出现的次数; $collection = collect([1, 2, 2, 3, 4, 4, 4]); return $collection->countBy(); //回调搜索相同指定片段的值的次数 $collection = collect(['xiaoxin@163.com', 'yihu@163.com', 'xiaoying@qq.com']);return $collection->countBy(function ($value) {return substr(strrchr($value, '@'), 1); }); // PS:相关的还有 groupBy()、keyBy()方法;
-  diff()方法返回集合数组之间不相同的部分,组合新的集合; //diff 返回两个集合中不相同的 $collection = collect([1, 2, 3, 4, 5]); return $collection->diff([3, 5]); // PS:其中还有 diffAssoc()、diffKeys()派生方法;
-  duplicates()返回重复的值; $collection = collect([1, 2, 2, 3, 4, 5, 5, 6]); return $collection->duplicates(); //严格派生方法:duplicatesStrict()
-  first()返回成立后的第一个值; //返回判断成立的第一条数值 $collection = collect([1, 2, 3, 4]); return $collection->first(function ($value) { return $value > 2; }); // PS:相关的还有 every()、except()、only()、firstWhere()、last()等方法;
-  flatten()将多维数组转换为一维; $collection = collect(['name'=>'Mr.Lee', 'details'=>['gender'=>'男', 'age'=>100]]); return $collection->flatten();
-  get()通过键名找值; $collection = collect(['name'=>'Mr.Lee', 'gender'=>'男']); return $collection->get('name'); // PS:相关的还有 pluck()等;
-  has()判断集合中是否存在指定键; return $collection->has('name');
-  pop()移出集合中最后一个值; $collection = collect([1, 2, 3, 4, 5]); //$collection->pop(); return $collection; // PS:相关的还有 pull()、push()、put()方法
-  slice()返回指定值后续的集合; $collection = collect([1, 2, 3, 4, 5]); return $collection->slice(3);PS:相关的还有 splice()等方法; 
-  sort()返回指定值后续的集合; $collection = collect([3, 1 , 5, 2, 7]); return $collection->sort()->values(); //需要配合 values()方法PS:相关的有 sortBy()、sortByDesc()、sortKeys()等; 
-  where()系列方法,和数据库条件一样; $collection = collect([['name'=>'Mr.Lee', 'gender'=>'男'],['name'=>'Miss.Zhang', 'gender'=>'女'] ]); return $collection->where('name', 'Mr.Lee');
24.模型的数据集合
一.数据集合
-  数据集合,就是已经将模型方法 get()获取到的数据再进行处理; 
-  比如:map()方法,通过它可以实现类似访问器一样对字段进行处理的效果; $users = User::get(); //使用集合方法 map 可以对输出的字段进行过滤 $women = $users->map(function ($user) {$user->email = strtoupper($user->email);return $user; }); return [$women]PS:数据集合支持连缀操作,和数据库连缀一样; 
-  使用 reject()方法,可以获取条件之外的数据内容; $women = $users->reject(function ($user) {return $user->gender != '女'; })->map(function ($user) {return $user; });
-  下面列出常用的集合方法列表: //判断集合中是否包含指定的模型实例 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.模型的一对一关联
一.关联概念
- 关联模型,即:两张或以上的表进行一定规则的绑定关联;
- 比如:一个学生(学生表)对应一张个人信息卡(信息表),这种就是一对一;
- 再比如:一篇博文(帖子表)对应多个评论(评论表),这种就是一对多;
- 再再比如:一个用户(用户表)对应多个职位(权限表), 而一个职位又可以有多个用户;那么,这种就是多对多关联;
- 自然,还有更多更复杂的关联,都是基于此的,我们只探讨这三种;
- 既然是关联,当然会有绑定的概念,当有数据库操作,关联表也会跟着变动;
- 这就是关联模型的意义;
二.一对一关联
-  一对一关联,我们选择两张表来做演示,首先看下两张表的结果对比; 
-  由于之前 Models 下的 User.php 模型代码很多了,改成.bak 后重建; 
-  创建两个 model,User.php 和 Profile.php,并使用命令实现提示; php artisan ide-helper:models//User.php,一对一关联 Profile 表 public function profile() {//参数 1 或:'App\Http\Models\Profile'//参数 2:默认为 user_id,如不是需要指明//参数 3:默认 id,如不是需要指明return $this->hasOne(Profile::class, 'user_id', 'id'); }
-  注意:Profile.php 只要建立空类即可,不需要其它,一对一调用如下: //注意:->profile 不要加括号,以属性方式访问 $profiles = User::find(19)->profile; return $profiles;
-  一对一也支持反向关联:定向反向关联;具体通过在 Profile 设置即可; //profile.php //参数 1 为主表类 //参数 2,3 和正向一致,默认对应可以不写 public function user() {return $this->belongsTo(User::class, 'user_id', 'id'); } $users = Profile::find(1)->user; return $users;
