有可能是配置缓存导致的,
运行:
1 | php artisan config:clear |
可清除配置缓存,配置缓存保存在
bootstrap/cache/config.php
,可以直接去那文件夹看看是不是缓存导致的。
还有另外一个缓存文件
bootstrap/cache/services.json
,上面的命令只是清除配置,下面的命令可以同时清除这两个缓存文件。
1 | php artisan clear-compiled |
1 | php artisan config:clear |
有可能是配置缓存导致的,
运行:
1 | php artisan config:clear |
可清除配置缓存,配置缓存保存在
bootstrap/cache/config.php
,可以直接去那文件夹看看是不是缓存导致的。
还有另外一个缓存文件
bootstrap/cache/services.json
,上面的命令只是清除配置,下面的命令可以同时清除这两个缓存文件。
1 | php artisan clear-compiled |
1 | php artisan config:clear |
Macroable
给 Builder
添加自定义方法Laravel
中提供了 Macroable
的
trait
,之前一直没有想过可以用上这个东西。
最近才想到可以这么做,源码看这里:https://github.com/laravel/framework/blob/5.6/src/Illuminate/Support/Traits/Macroable.php
目前用的是 5.1
版本,应该 5.1
版本以后的都有这个特性。
当然,你也可以在 Model
基类里面添加自定义方法,但是这样添加的方法 DB
类是用不了的。
但是使用 macro
定义到 Builder
的方法可以同时在 DB
类和 Eloquent Model
中使用(具体不赘述,自己看源码,就是利用
__call
、__callStatic
这些魔术方法)。
使用方法:调用 Macroable
的 macro
方法,绑定一个自定义方法到 Builder
类中,如:
1 | \Illuminate\Database\Query\Builder\Builder::macro('active', function () { |
调用方法是(使用 DB
类):
1 | DB::table(xxx)->active()->get(); |
或者(使用 Eloquent Model
):
1 | \App\Model\User::active()->first(); |
至于我们应该把上面那几行放哪里?
个人觉得一个比较好的地方是 Providers
中,在
app/Providers
下面新建一个 Provider
,把
macro
调用放到 Provider
的
register
方法中。如:
1 | <?php |
当然,加了 Providers
之后还要在
config/app.php
中配置这个 Provider
。
就这样。
还有个问题是,这样添加的方法 ide
无法识别,我们这时候就可以使用 @method
了,如:
1 | @method $this active() |
可以使用命令把这些 @method
写入到 Builder
头部。
一直以来,想调试框架中的某些东西,如想知道 Elpquent
的
create
方法返回值是个什么东西,
以前的话,应该就是在 create
方法调用之后,使用
dd
或者 var_dump
之类的函数打印出来
如: 1
2
3
4
5public function getTest()
{
$user = \App\User::create(['name' => 'tinker']);
dd($user);
}
这样一来,这个流程似乎有点冗长,因为我们还要打开浏览器查看
有了
tinker
,我们就可以直接在命令行运行我们想要测试的代码,如:
我们可以看到,create
方法返回了一个模型对象。
我们就把这个 tinker
当作可以交互式的
代码执行工具好了,我们在学习 Laravel
的过程中可以在这里直接测试 Laravel
框架中的一些用法,也可以进行调试什么的。
通过控制台的 cookie
信息我们会发现,每次请求之后,关键的
cookie
,如PHPSESSID
、XSRF-TOKEN
都会发生变化,并且都是很长的一串字符串。
其实这是一个 json
数组,其中包含了
iv
,value
,mac
三个字段:
这些字段都是在框架加密解密的时候使用的,加密方法是 openssl_encrypt:
对 openssl
不太了解的可以看下下面的例子:
1 | $data = 'laravel'; |
Laravel
中的话,key
就是 .env
配置文件里面的 APP_KEY
,除了 key
还有两个变化的参数就是 加密、解密的数据以及 iv
。
也就是说,如果我们需要加密 cookie
的话,我们至少得保存下
加密后的数据以及 iv
。
这样看来,mac
字段似乎有点多余,但是我们可以使用该字段来验证数据的合法性:
如果验证不通过,Laravel
也就不会对 data
进行解密操作。
虽然每次请求 cookie
都会发生变化,但是实际数据是没有变的,发生变化只是因为用来加密的
iv
变了(使用 random_bytes
方法生成)。
由于 iv
每次都变化,所以需要把 iv
也一同返回给浏览器,加上验证数据合法性的
mac
,最后返回的就是下面的数组的 json
编码后在
base64
编码的数据:
1 | [ |
App\User
1 | class User extends Model |
使用 with
查询某个 user
及其的
profile
:
1 | App\User::with([ |
上面的用法中,我们会发现,即使数据库有记录,sql
也记录了对应的查询语句,但是 profile
关联却是空的:
但是加上外键就可以得到正确结果了:
1 | App\User::with([ |
可以查找到正确的 profile
了。
这和 Laravel
框架的工作方式相关,我们先看看下面的例子:
sql
我们使用 DB::listen
方法去记录相关的 sql
语句
我们查看 log 可以发现有以下语句: 1
2select * from `users` where `id` in (?, ?) [3,4]
select * from `profiles` where `profiles`.`user_id` in (?, ?) [3,4]
我们可以明显发现,laravel
对于 user
和
profile
是独立查询的,
也就是说会得到两个集合,一个是 User
、一个是
Profile
,但是这并不是我们想要的结果,我们需要的结果是,只有一个
User
集合, 并且这个 User
集合里面有
Profile
关联。
但是结果就是这样,如果是你,你会怎么把这些数据关联起来呢?
对了,我们定义关联的时候不是定义了它们的关联方式么?
上面的 hasMany
方法默认第二第三个参数其实就是这两个集合建立关联的关键,第三个参数
user_id
、第四个参数
id
;这样一来我们就可以通过比较 Profile
的
user_id
和 User
里面的
id
,如果相等,则这个 Profile
是属于这个
User
的,我们就把该 Profile
放进
User
的 profile
关联中,最后就得到我们想要的结果了。
Xdebug
验证用 xdebug
证实一下我们的想法:
如我们所想的那样,图一的 match
方法,顾名思义就是匹配了,通过 user
模型集合和
profile
模型集合进行匹配。
图二,也证实了我们模型建立关联需要通过关联中外键的值得想法。
图三,是通过获取 user
的 localkey
,也就是
id
的值,来查找 $dictonary
中是否有对应的值,buildDictonary
方法会建立一个关联数组,key
是
user_id
(外键)的值,值是关联的数据。这样一样,由于我们没有把
user_id
也 select
出来,最后得到的
$dictonary
的结构并不是预期的那样:
其实我们本来是想要得到下面的这种:
1 | [ |
但是我们得到的却是,所有的 UserProfile
都在一个嵌套的数组里面了,这样一来,下面的 getRelationValue
得到的结果自然就是空的了。
好了,总结一下,就是:Laravel
先查询主要的数据(不带
with
),查询完了之后,取出其中的 id
列数组(不一定都是id啊,只是举个例子),将这个数组作为条件去查找关联,有多少个关联就会再去查找多少次,查找完关联之后通过得到的结果的主键和关联数据的外键比对,相等则建立关联。
总结:在关联筛选 field
的时候,也必须要把关联的外键写进去,否则,即使产生了正确的
sql
语句,但是它们建立不了关联,通过
$user->profile
得到的还是一个空集合。