第五章,我了解了怎么往数据库中存储数据,但是有很多数据需要本地或远程存储。本章,我们将了解 文件系统,内存中的存储,文件上传和操作,非关系数据存储,会话,缓存,日志记录,Cookie和全文本搜索。

本地和云文件管理

Laravel 提供了一系列文件操作工具,通过 Storage facade 和一些 助手函数。

Laravel 的文件系统可以连接 本地的文件系统, S3rackspace 和 FTP。S3 和 Rackspace 文件驱动程序由 Flysystem 提供, 并且往 laravel 中添加其余的 Flysystem 支持的程序也很简单,如Dropbox或WebDAV。

配置文件

Laravel 文件管理的配置文件位于 config/filesystems.php 。 每个链接被叫做 “disk”,如下是一些开箱即用的 disk:

  1. 'disks' => [
  2. 'local' => [
  3. 'dirver' => 'local', // 本地文件系统
  4. 'root' => storage_path('app'), // 存储位置在 storage/app
  5. ],
  6. 'public' => [
  7. 'dirver' => 'local',
  8. 'root' => storage_path('app/public'),
  9. 'url' => env('APP_URL') . '/storage',
  10. 'visibility' => 'public',
  11. ],
  12. 's3' => [
  13. 'dirver' => 's3', // 亚马逊 对象存储服务 云端存储
  14. 'key' => env('AWS_ACCESS_KEY_ID'),
  15. 'secret' => env('AWS_SECRET_ACCESS_KEY'),
  16. 'region' => env('AWS_DEFAULT_REGION'),
  17. 'bucket' => env('AWS_BUCKET'),
  18. 'url' => env('AWS_URL'),
  19. ]
  20. ];

storage_path() 助手函数,链接到 laravel 配置的 storage 目录, storage/. , 所以, storage_path('public') 将将返回 storage/public

public disk 也是使用本地文件系统,默认路径是 storage/app/public , 如果你想让该目录对公众开放,你需要创建一个软链接,映射该目录到 public 目录下, Artisan 命令可以帮助你:

php artisan storage:link

public/storage(对外可访问的位置) ==> storage/app/public(实际存放文件的位置) [http://lara.test/storage/9@2x.png](http://lara.test/storage/9@2x.png) 实际访问的文件服务器地址是 /path/to/myapp/storage/app/public/9@2x.png

使用 Storage Facade

config/filesystems.php 配置了默认的 disk ,当调用没有指定 disk 的 Storage facade 时, 将使用默认 disk。 指定 disk 的方法如下:

Storage::disk('s3')->get('file.jpg');

每个文件系统都提供了如下方法:

  • get(‘file.jpg’)
    Retrieves the file at file.jpg
  • put(‘file.jpg’, $contentsOrStream)
    Puts the given file contents to file.jpg
  • putFile(‘myDir’, $file)
    Puts the contents of a provided file (in the form of an instance of either
    Illuminate\Http\File or Illuminate\Http\UploadedFile ) to the myDir direc‐
    tory, but with Laravel managing the entire streaming process and naming the file
  • exists(‘file.jpg’)
    Returns a Boolean indicating whether file.jpg exists
  • getVisibility(‘myPath’)
    Gets the visibility for the given path (“public” or “private”)
  • setVisibility(‘myPath’)
    Sets the visibility for the given path (“public” or “private”)
  • copy(‘file.jpg’, ‘newfile.jpg’)
    Copies file.jpg to newfile.jpg
  • move(‘file.jpg’, ‘newfile.jpg’)
    Moves file.jpg to newfile.jpg
  • prepend(‘my.log’, ‘log text’)
    Adds content at the beginning of my.log
  • append(‘my.log’, ‘log text’)
    Adds content to the end of my.log
  • delete(‘file.jpg’)
    Deletes file.jpg
  • size(‘file.jpg’)
    Returns the size in bytes of file.jpg
  • lastModified(‘file.jpg’)
    Returns the Unix timestamp when file.jpg was last modified
  • files(‘myDir’)
    Returns an array of filenames in the directory myDir
  • allFiles(‘myDir’)
    Returns an array of filenames in the directory myDir and all subdirectories
  • directories(‘myDir’)
    Returns an array of directory names in the directory myDir
  • allDirectories(‘myDir’)
    Returns an array of directory names in the directory myDir and all subdirectories
  • makeDirectory(‘myDir’)
    Creates a new directory
  • deleteDirectory(‘myDir’)
    Deletes myDir

添加其他 Flysystem 提供商

如果你想添加一种其他的 Flysystem 提供商,你需要 ‘’继承’’ 本机的存储系统。在服务提供者的 root() 方法中——可以是 AppServiceProvider, 但是更合适的方式是为每一个新的储存系统创建一个新的 服务提供者。

// 某个服务提供者
public function boot()
{
    Storage::extend('dropbox', function($app, $config){
        $client = new DropboxClient($config['accessToken'], $config['clientIdentifier']);

        return new Filesystem(new DropboxAdapter($client));
    });
}

基本的文件上传和操作

Storage facade 的一个常规用处是接受用户上传的文件。以下是常见的流程

class DogsController
{
    public function updatePicture(Request $request, Dog $dog){
        Storage::put(
            "dog/{$dog->id}",
            file_get_contents($request->file('picture')->getRealPath())
        );
    }
}

我们 put() 一个叫 dog/id 的文件,并且从上传文件获取内容。每一个上传文件都属于 SplFileInfo 类,它提供了 一个getRealPath() 方法, 返回文件的地址 。所以,我们看可以我们得到了用户上传文件的最终上传路径,请使用 file_get_contents() 进行读取,并将其传递到 Storage::put()中。

这里,我们获取了文件变量, 在存储之前,我们可以对它做任何事——如果是图片的话,可以使用图像处理包,处理它,校验它,如果不符合我们的要求就拒绝它。

如果,我们想上传文件到 S3 , 并且 config/filesystems.php 已经配置好了, 只需要调用 Storage::disk('s3')->put() 即可,以下是稍微复杂的例子:

class DogsController
{
    public function updatePicture(Resquest $request, Dog $dog){
        $original = $request->file('picture');

        // 修改 图片 大小 最大宽度为 150
        $image = Image::make($original)->resize(150, null, function ($constraint){
            $constraint->aspectRatio();
        })->encode('jpg', 75);

        Storage::put("dogs/thumbs/{$dog->id}", $image->getEncoded());
    }
}

上例使用了 intervention/image 图片处理包。你也可以使用其他的。重点是,存储前你可以对图片做任何修改。

简单文件下载

就像上传一样简单,直接上代码:

public function downloadMyFile()
{
    return Storage::download('my-file.pdf');
}

Sessions

session 在普通网页应用中,是存储不同页面状态的主要工具。Laravel 的 session 管理支持的驱动包括 files, cookies,database,Memcached or Redis,或者内存数组(在页面请求后过期,仅适用于测试)。

你可以在 config/session.php 中配置 session 。选择是否加密, 选择哪种驱动(默认 file), 指定其余信息,如 session 的长度或者要使用的文件或数据库表。session 文档

session 管理工具提供一些 api ,用于存储和获取数据:session()->put('user_id')session()->get('user_id')。 注意 请避免将任何内容 以 flash 作为 key 保存到 session,因为 Laravel 在内部将其用于 flash(仅可用于下一页请求)会话存储。

获取 Session

最常用的获取 session 的方法是:

session()->get('user_id');

你也可以使用 Request 对象的 session() 方法:

Route::get('dashboard', function(Request $request) {
    $request->session()->get('user_id');
});

或者你可以注入一个 Illuminate\Session\Store 实例:

Route::get('dashboard', function (Illuminate\Session\Store $session) {
    return $session->get('user_id');
});

最后,你还可以使用 session() 助手函数。不传参数获取一个 session 实例,可以跟 get()put() 方法:

// Get
$value = session()->get('key');
$value = session('key');
$value = session('key', 'default');  // 设置默认值

// Put
session()->put('key', 'value');
session(['key', 'value']);

session 实例上可用的方法

最常用的是 get()put(), 我们来看一下其他的。

  • session()->get($key, $fallbackValue)
    get() 返回名字是 $key 的 session 值, 如果没有,则返回 $fallbackValue (如果 $fallbackValue 没有设置,则返回 null ), $fallbackValue 可以是 字符串 或者 闭包, 如下:

    $points = session()->get('points');
    $points = session()->get('points', 0);
    $points = session()->get('points', function(){
      return (new PointGetterService)->getPoint();
    });
    
  • session()->put($key, $value)
    $key 存储一个 $valuesession

  • session()->push($key, $value)
    如果某个 session 的值是数组,你可以用 push() 往数组中追加值:

    session()->push('friends', ['Weimin', 'Yaqiong', 'Bengqun']);
    session()->push('friends', 'DuanMuDaShu');
    
  • session()->has($key)
    检查 是否存在以 $key 命名的 session:

    if(session()->has('points')){
      // do something
    }
    
  • 你可以传入一个数组, 当 数组中的 key 对应的 session 都存在时,才返回 true.
    ps: 如果一个 session 被设置了, 但是 值为 null ,则 session()->has() 返回 false

  • session()->exists($key)
    检查 $key 对应的 session, 是否被设置, 和 has() 不同的是,如果值被设置成 null ,也会返回 true
  • session->all()
    all() 返回所有 session , 包括框架默认设置的。你可能会看到 _token (CSRF tokens), _previous(上一个页面,back() 重定向时候使用) , 和 falsh (仅供 flash storage 使用)。
  • session()->forget($key)session()->flush()
    forget() 移除一个之前设置的 session 值。 flush() 移除所有 session,包括框架自带的: ```php session()->put(‘a’, ‘awesome’); session()->put(‘b’, ‘be cool’);

session()->forget(‘a’); // 释放 a 的 session, b 依然被设置

session()->flush(); // session 为空


- `session()->pull($key, $fallbackValue)`<br />`pull()` 和 `get()` 一样, 唯一的不同是获取 session 后会删除这个值。
- `session()->regenerate()`<br />很少用,如果你要重新生成你的 `session` ID ,请使用。

<a name="993690f5"></a>
### 闪存会话存储 (Flash Session Storage)

一种常见的 session 存储应用模式是存储一个只在下一个页面加载时使用的值。 如 ,你可能存储一个 “数据提交成功” 的提示信息。 你可以在下一个页面加载时手动获取它然后擦除它,但是如果你在很多地方使用这种模式的话,很累吧。你可以把这些值存入 闪存会话存储 : 它被设置只存在到下一个页面请求。

laravel 提供了两个有用的方法:

- `session()->flash($key, $value)`<br />以 `$key` 存储一个 `$value` 到 session ,该值仅用于下一个页面请求。 和 `put()` 类似。
- `session()->reflash() and session()->keep($key)`<br />如果您需要上一页的 Flash 会话数据来处理另一个请求,则可以使用 `reflash()` 将所有内容 重新存储截至到下一个请求的,或者使用 `keep($key)` 仅为下一个请求重新存储一个值。 `keep()` 也可以接受要重新存储的值的 key 的数组。

<a name="a8d99b0b"></a>
## 缓存 Cache

缓存的结构类似于 session, 你提供一个 key ,larave 为你存储值。最大的不同是,缓存的存储是对整个项目而言的,`session`的存储是对每个用户而言的。这意味着,缓存经常用来存储来自数据库查询,API调用或其他查询缓慢的结果。

缓存的配置文件是 `config/cache.php` , 和 `session` 一样,你可以指定和配置任何一种缓存驱动,你也可以设置默认驱动。laravel 默认使用文件缓存,你也可以选择使用 `Memcached` `Redis` `APC` 或 数据库,设置你也可以写你自己的 缓存驱动。[cache文档](https://laravel.com/docs/master/cache)

laravel 5.8 之前你给缓存方法传递一个整数,代表缓存时间保存的 分钟数, 5.8+ 之后,代表缓存多少秒。

<a name="b06ce698"></a>
### 获取缓存

使用 `facade` :

```php
$users = Cache::get('users');

你也可以从容器中获取对象:

Route::get('users', function((Illuminate\Contracts\Cache\Repository $cache){
    return $cache->get('users');
})

全局帮助函数:

// Get from cache
$users = cache('key', 'default value');
$users = cache()->get('key', 'default value');

// 存入缓存 并持续 $second 秒
$users = cache(['key' => 'value'], $second);
$users = cache()->put('key', 'value', $second);

缓存实例的方法

  • cache()->get($key, $fallbackValue)cache()->pull($key, $fallbackValue)
    get()pull() 都是获取一个key 是 $key 缓存值,唯一区别 pull() 在获取缓存值后会释放该缓存。
  • cache()->put($key, $value, $secondOrExpiration)
    $key 存储一个 $valuecache 中, 并设置过期时间(秒数), 如果你想设置失效的 日期/时间 而不是秒数, 你也可以在第三个参数传入一个 Carbon 对象

    cache()->put('key', 'value', now()->addDay());
    
  • cache()->add($key, $value)
    add()put() 很像,如果 $key 对应的值已经存在,它将不会设置。同时,这个方法返回一个 boolen 值 代表是否添加了缓存:

    $someDate = now();
    cache()->add('someDate', $someDate); // retuen true
    $someOtherDate = now()->addHour();
    cache()->add('someDate', $someOtherDate) // return false
    
  • cache()->forever($key, $value)
    设置一个缓存,不会过期,除非被 forget()

  • cache()->has($key)
    返回是否有 名为 $key 的缓存
  • cache()->remember($key, $seconds, $closure)cache()->rememberForever($key, $closure)
    remember() 提供了一种常见的工作流: 查询某个值是否 以 $key 为名存在于缓存中,如果存在返回它,如果不存在,先获取并以 $key 为名保存到缓存中,然后再返回它。
    remember 第一个参数,是要查询的缓存键名,第二个参数,要缓存的时间,第三个参数,是一个闭包,定义了如果 $key 的键名不存在,怎么去查找值。rememberForever() 类似,只是它不需要设置过期时间。eg:

    // 从缓存中获取以 'users' 为名字的缓存内容,
    // 如果未找到,则执行 Users::all() , 然后缓存到 'users' 中,持续 7200 秒,然后返回它
    $users = cache()->remember('users', 7200, function(){
      return Users::all();
    });
    
  • cache()->increment($key, $amount)cache()->decrement($key, $amount)
    自增,自减,起步值默认是 0 ,步伐宽度默认是 1。

  • cache()->forget($key)cache()->flush()
    forget($key) 清除 $key 的缓存, flush() 清除所有缓存。

Cookies

cookies 可以同时存在于 requestresponse 上,所以它和 sessioncache 是不同的。

Laravel 中的 Cookie

Laravel 中 cookie 可以存在于三个地方,它可以依附于请求( request )上,这意味着,当用户访问页面时,会携带一个cookie 。你可以通过 Cookie facade 获取它,也可以从 request 对象上获取它。

它们可以跟着响应( response )被发送 , 这意味着,响应将指导用户的浏览器保存 cookie,以便于下一次访问。在返回响应之前你可以追加 cookie 到响应对象上。

最后,cookie 可以存在于队列中,当你使用 Cookie facade 设置 cookie 时,你已经把它放到一个名叫 “CookieJar” 的队列上了,这个队列将通过 AddQueuedCookiesToResponse 中间件往响应上添加和移除 cookie

cookie 工具

你可以使用三种工具获取或设置 cookieCookie facade ,cookie() 助手函数,requestresponse 对象。

Cookie facade

Cookie facade 是功能最齐全的选择。不仅允许你获取和设置 cookie, 还允许你把加到 response 上的 cookie 放到队列中,以下是它提供的方法:

  • Cookie::get($key)
    获取一个名字为 $key 的 cookie ,它来自于 请求。
  • Cookie::has($key)
    检查请求中是否有名为 $key 的 cookie,返回 Boolean 值。
  • Cookie::make(...params)
    如果你想设置一个 cookie,但不想把它放到队列中,你可以使用 Cookie::make() 。这样使用,最大的可能性是,先设置一个 cookie ,然后手动把它绑定到响应对象上。
    以下是 make()的参数,按顺序:
    • $name cookie 的名字
    • $value cookie 的内容
    • $minutes 指定 cookie 存在多少分钟
    • $path
    • $domain
    • $secure 表明 cookie 是否只应该发送到安全( HTTPS )的网站
    • $httpOnly 表明 cookie 是否只能通过 http 协议连接
    • $raw 表明 cookie 是否不使用 URL 编码
    • $sameSite 表明 cookie 是否对跨站请求可用,其值为 laxstrictnull
  • Cookie::make()
    返回一个 Symfony\Component\HttpFoundation\Cookie 实例
  • Cookie::queue(Cookie || params)
    如果你使用 Cookie::make() , 你还需要把 cookie 绑定到响应上。Cookie::queue()Cookie::make() 有一样的语法,但是它通过中间件把自动创建 cookie 并绑定其到响应上放入队列。
    你还可以把你创建好的 cookie 作为参数直接传递给 Cookie::queue()
    最简单创建 cookie 并绑定它到响应:
    Cookie::queue('dismissed-popup', true, 15);
    

Cookie 的默认设置 Cookie facade 实例使用的 CookieJar 是从 session 配置中读取默认设置的。如果你更改了 config/session.php 中的任何 session 和 cookie 配置,那么使用 Cookie facade 设置的使用了这些配置的所有 cookie 都会应用到。

当队列中的 cookie 没有被设置时? Cookie 仅仅是作为响应的一部分返回。所以,如果你使用 Cookie facade 设置 cookie 放入队列,然后你的响应没有正确返回(例如,exit() 或者其他脚本中断),那么,你的 cookie 将不会被设置。

cookie() 助手函数

cookie() 函数不传递参数,返回一个 CookieJar 实例。然而,和 Cookie facade 相比,has()get() 函数只有 Cookie Facade 上有,CookieJar 实例没有这两个函数。所以,某种程度上说, cookie() 助手函数有些鸡肋。

cookie() 助手函数最有用的是创建一个 cookie ,传递参数时,和 Cookie::make() 一样:

// 创建一个 cookie
$cookie = cookie('dismissed-popup', true, 15);

请求和响应中的 cookie

因为 cookie 作为请求的一部分被获取,作为响应的一部分被设置,这是 Illuminate 对象( 请求与响应)就是 cookie 生存的地方。Cookie facade 的 get() , has() , queue() 方法只是 请求与响应对象的操作 cookie 的代理。

所以,最简单的操作 cookie 的方法是使用 请求和响应对象。

从 Request 对象获取 cookie 。 你可以使用 Request 对象的 cookie() 方法获取 cookie:

Route::get('dashboard', function (Illuminate\Http\Request $request) {
    $userDismissedPopup = $request->cookie('dismissed-popup', false);
});

参数 1是 cookie 的名字, 参数 2 是如果 cookie 不存在的默认值。

设置 cookie 到 Reponse 对象。无论 Response 对象是否准备好,你都可以使用 cookie() 方法(5.3 之前是 withCookie() )添加 cookie 到 Response 对象上:

Route::get('dashboard', function () {
    $cookie = cookie('saw-dashboard', true);
    return Response::view('dashboard')
        ->cookie($cookie);
});

如果你是一个 Laravel 新手,推荐使用 request 和 response 对象处理 cookie。虽然多做了一点工作,但是避免了之后的程序员,可能不理解 CookieJar 队列的问题。

日志

日志的最主要目的是增加 “可发现性”,即增强使你明白你的应用在任何时刻发生了什么的能力。

日志就是一些短信息,有时携带这某些数据,这些数据是对人可读且友好的。日志是在程序执行期间代码生成的,目的是方便你理解程序在执行时发生了什么。每个日志必须在特殊级别被捕获,可以从 emergency(非常严重)到 debug(几乎没什么有意义的发生)。

没有修改的情况下, Laravel 会把所有日志写在 storage/logs/laravel.log 文件里,每个日志看起来像这样:

[2020-08-08 10:37:00] local.ERROR: Something went wrong.

你可以看见 日期, 时间, 环境, 错误等级, 和错误信息都位于一行里。然而,Laravel 默认也会记录到任何没有捕获的异常到日志中,这种情况下,你将看到完整的堆栈信息。

什么时候需要为什么需要使用日志

日志最常见的应用场景是,对已经发生的事件做半一次性的记录, 这些记录你可能感兴趣,但是你绝对不会通过程序获取与访问。日志更多地是帮助了解应用程序发生了什么事情,而不是创建程序可以使用的结构化数据。

例如,你有一个记录用户登录或其他有意思的事情的代码,这就是一个登录数据库日志的应用场景。然而,你只是偶尔有兴趣,而且不确定是否需要关心,或者是否需要通过编程的方式获取这些信息,你可以抛出一个 debug 或者 info 等级的日志,然后忘记这回事。

日志也常用于,有什么地方出错时,你需要查看某个值,或者一天中的某个时间你不在跟前,你需要查看某个值。在代码中添加一条日志语句,把你需要的数据写进日志,是常见的解决方案。

写日志

最简单的方式是使用 Log facade,Laravel 提供了严格的日志等级, 遵循 RFC 5424

Log::emergency($msg);
Log::alert($msg);
Log::critical($msg);
Log::error($msg);
Log::warning($msg);
Log::notice($msg);
Log::info($msg);
Log::debug($msg);

你也可以传递第二个参数,是一个数组:

Log::error('some message',['user'=>$user])

不同的日志配置,附件信息可能会被以不同的方式捕获,以下是默认配置:

[2018-09-27 20:53:31] local.ERROR: Failed to upload user image. {
    "user":"[object] (App\\User: {
        \"id\":1,
        \"name\":\"Matt\",
        \"email\":\"matt@tighten.co\",
        \"email_verified_at\":null,
        \"api_token\":\"long-token-here\",
        \"created_at\":\"2018-09-22 21:39:55\",
        \"updated_at\":\"2018-09-22 21:40:08\"
    })"
}

Log Channels

在 Laravel 5.6 即之后,我们配置和捕获日志的方式发生了相当大的变化,引入了多通道(channels) 和多驱动(dirvers)的思想。

你可以将你的日志配置成一种或者多种预定义的类型,只需要将类型涉及的各种配置信息传递给特定的驱动即可。

日志的类型称为 channelstack , single , daily , slack , stderr , syslog , 和 errorlog 都是开箱即用的。每个通道连接到单个 驱动, 可用的驱动有 single , daily , slack , syslog , errorlog , monolog , custom , 和 stack

以下我们介绍几个,你还可以在 日志文档 查看详情 。

The single channel

single channel 会把每条日志写到一个文件里,这个文件路径需要设置在path 这里。 默认配置如下:

'single' => [
    'dirver' => 'single',
    'path' => storage_path('logs/laravel.log'),
    'level' => 'debug'
];

level 设置为 debug, 意味着只有 debugdebug 以上的日志等级才会被记录。位置位于 storage/logs/laravel.log.

The daily channel

daily channel 会按照日期分割日志文件, 即每天创建一个新日志文件,记录当天日志。默认配置:

'daily' => [
    'driver' => 'daily',
    'path' => storage_path('logs/laravel.log'),
    'level' => 'debug',
    'days' => 7
];

它类似于 single,但是我们现在可以设置日志保留多少天然后才可以被清理,并且我们将日期附加到我们指定的文件名之后。例如,以上配置将生成一个名为 storage/logs/laravel-{yyyy-mm-dd}.log 的文件。

The slack channel

发送日志到 slack 频道 (一个聊天工具)。

The stack channel

默认的日志 channel。laravel 5.7+ 默认配置如下:

'stack' => [
    'driver' => 'stack',
    'channels' => ['daily'],
    'ignore_exceptions' => 'false'
],

stack channel 允许你使用不止一种 channel, 你可以配置在 channel 数组中。所有,尽管它是 laravel 默认配置,5.8+ 之后 channel 数组设置的是 daily (如上),所以,其实它现在使用的还是 daily channel。

如果你想让 info 以上等级的日志放到 daily 文件, critical 等级以上的日志发送给 Slack。请使用如下配置:

'channels' => [
    'stack' => [
        'driver' => 'stack',
        'channels' => ['daily', 'slack'],
    ],

    'daily' => [
        'driver' => 'daily',
        'path' => storage_path('logs/laravel.log'),
        'level' => 'info',
        'days' => 14
    ],

    'slack' => [
        'driver' => 'slack',
        'url' => env('LOG_SLACK_WEBHOOK_URL'),
        'username' => 'Laravel log',
        'emoji' => ':boom:',
        'level' => 'critical'
    ],
]

在特定频道写日志

有时你想控制日志写到哪个频道,如下:

Log::channel('slack')->info('This message will go to slack');

更多的配置

见文档

使用 Laravel Scout 做全文搜索

Laravle Scout 是一个为你的 Eloquent Models 提供全文搜索的包。Scout 让搜索和索引你的 Eloquent models 的内容变得容易;它使用 Algolia 驱动,你也可以使用其他驱动,我们使用 Algolia 为例。

安装 Scout

首先, Larvael 5.3 + 执行如下命令:

composer require larvel/scout

ps: Laravle 5.5 版本之前需要添加 Laravel\Scout\ScoutServiceProvider::classconfig/app.php 的服务提供者里。

其次,设置你的 Scout 配置,执行:

php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"

并且,黏贴你的 Algolia 证书 到 config/scout.php 文件。

最后,安装 Aligolia SDK:

composer require algolia/algoliiasearch-client-php

索引你的模型

在你的模型中,导入 Laravel\Scout\Serachable trait。 (我们使用 Review ,一个书籍评论表,举例)

你可以使用 toSearchableArray() 方法定义那些属性是可以被搜索的 (默认是映射 toArray()方法 ),你也可以使用 searchableAs() 方法定义模型索引的名字( 默认是 表名 )。

Scout 订阅了你标记的模型的 create / delete / update 事件。 当你创建,删除或者修改数据时,Scout 将会同步改变到 Algolia。它既能使这些改变同步,又能, 如果你使用了队列,队列也将更新。

搜索索引

Scout 同步很简单。例如,查找 Review 中含有 Llew 的:

Review::search('Llew')->get();

你可以使用 query,就像你使用正常的 Eloquent 一样:

// 分页
Review::search('Llew')->paginate(20);

Review::search('Llew')->where('account_id', 2)->get();

这些搜索 (searches) 返回什么呢?一个模型的 collection, 数据库的记录。 IDs 被存储在 Algolia,返回一个匹配 IDs 的列表;然后 Scout 从数据库拉取这些记录,作为 Eloquent 对象返回。

你不需要使用复杂的 SQL WHERE 语句,它为对比检查提供了一个简单的基本框架,就像你在代码中看到的一样简单。

队列和Scout

现在,每次修改数据库记录,你的应用都需要请求 Algolia 一次。这会拖慢应用程序的速度,所以使用 Scout 将这些 action 放入队列,是容易的。

config/scout.php 文件中, 设置 queue 为 true,这样这些修改建立索引将变成异步的。 现在,你的全文索引被操作为“最终一致性”;你的数据库将及时得到更新,你的搜索索引将会进入队列,按照队列工作进度,尽快更新。

不使用索引执行操作

如果你想执行系列操作,但是不想触发全文索引的响应。你可以把这些操作放到 withoutSyncingToSearch() 内:

Review::withoutSyncingToSearch(function(){
    // 一些操作
    factory(Review::class 10)->create();
})

索引前提条件

有时,你可能想当达到什么条件时,在索引记录。 在模型中使用 shouldBeSearchable()

public function shouldBeSearchable(){
    return $this->isApproved();
}

通过代码手动触发索引

如果你想手动触发为你的模型建立全文索引,你可以通过代码,也可以通过命令行。

任何 Eloquent query 后调用 searchable(), query 的结果都会触发索引。

Review::all()->searchable();

关联方法上也可以使用:

$user->reviews()->searchable();

如果你想解除索引,也可以在任何 query 上使用 unsearchable() 方法:

Review::where('sucky', true)->unsearchable();

通过命令行手动触发索引

php artisan scout:import "App\Review"

这将触发 Review 模型所有记录索引。