2

I've configured Laravel Scout and can use ::search() on my models. The same models also use SoftDeletes. How can I combine the ::search() with withTrashed()?

The code below does not work.

MyModel::search($request->input('search'))->withTrashed()->paginate(10);

The below does work but does not include the trashed items.

MyModel::search($request->input('search'))->paginate(10);

Update 1 I found in the scout/ModelObserver that deleted items are made unsearchable. Which is a bummer; I wanted my users to be able to search through their trash.

Update 2 I tried using ::withoutSyncingToSearch, as suggested by @camelCase, which I had high hopes for, but this also didn't work.

$model = MyModel::withTrashed()->where('slug', $slug)->firstOrFail();

if ($model->deleted_at) {
    $model->forceDelete();
} else {
    MyModel::withoutSyncingToSearch(function () use ($model) {
        $model->delete();
    });
}

This caused an undefined offset when searching for a deleted item. By the way, I'm using the TNTSearch driver for Laravel Scout. I don't know if this is an error with TNTSearch or with Laravel Scout.

4

2 回答 2

2

我已经为您的问题制定了解决方案。我将提交一个pull requestforScout希望将其与官方软件包合并。

这种方法允许您在搜索中包含软删除模型:

App\User::search('search query string')->withTrashed()->get();

仅在搜索中显示软删除模型:

App\User::search('search query string')->onlyTrashed()->get();

您需要修改3个文件:

生成器.php

添加laravel\scout\src\Builder.php以下内容:

/**
 * Whether the search should include soft deleted models.
 *
 * @var boolean
 */
public $withTrashed = false;

/**
 * Whether the search should only include soft deleted models.
 *
 * @var boolean
 */
public $onlyTrashed = false;

/**
 * Specify the search should include soft deletes
 * 
 * @return $this
 */
public function withTrashed()
{
    $this->withTrashed = true;

    return $this;
}

/**
 * Specify the search should only include soft deletes
 *
 * @return $this
 */
public function onlyTrashed()
{
    $this->onlyTrashed = true;

    return $this;
}

/**
 * Paginate the given query into a simple paginator.
 *
 * @param  int  $perPage
 * @param  string  $pageName
 * @param  int|null  $page
 * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
 */
public function paginate($perPage = null, $pageName = 'page', $page = null)
{
    $engine = $this->engine();

    $page = $page ?: Paginator::resolveCurrentPage($pageName);

    $perPage = $perPage ?: $this->model->getPerPage();

    $results = Collection::make($engine->map(
        $rawResults = $engine->paginate($this, $perPage, $page), $this->model, $this->withTrashed, $this->onlyTrashed
    )); // $this->withTrashed, $this->onlyTrashed is new

    $paginator = (new LengthAwarePaginator($results, $engine->getTotalCount($rawResults), $perPage, $page, [
        'path' => Paginator::resolveCurrentPath(),
        'pageName' => $pageName,
    ]));

    return $paginator->appends('query', $this->query);
}

引擎.php

laravel\scout\src\Engines\Engine.php修改以下内容:

/**
 * Map the given results to instances of the given model.
 *
 * @param  mixed  $results
 * @param  \Illuminate\Database\Eloquent\Model  $model
 * @param  boolean  $withTrashed // New
 * @return \Illuminate\Database\Eloquent\Collection
 */
abstract public function map($results, $model, $withTrashed, $onlyTrashed); // $withTrashed, $onlyTrashed is new

/**
 * Get the results of the given query mapped onto models.
 *
 * @param  \Laravel\Scout\Builder  $builder
 * @return \Illuminate\Database\Eloquent\Collection
 */
public function get(Builder $builder)
{
    return Collection::make($this->map(
        $this->search($builder), $builder->model, $builder->withTrashed, $builder->onlyTrashed // $builder->withTrashed, $builder->onlyTrashed is new
    ));
}

最后,您只需要修改您的相关搜索引擎。我正在使用 Algolia,但该map方法似乎与TNTSearch.

AlgoliaEngine.php

laravel\scout\src\Engines\AlgoliaEngine.php修改map方法以匹配abstract我们上面修改的类:

/**
 * Map the given results to instances of the given model.
 *
 * @param  mixed  $results
 * @param  \Illuminate\Database\Eloquent\Model  $model
 * @param  boolean  $withTrashed // New
 * @return \Illuminate\Database\Eloquent\Collection
 */
public function map($results, $model, $withTrashed, $onlyTrashed) // $withTrashed, $onlyTrashed is new
{
    if (count($results['hits']) === 0) {
        return Collection::make();
    }

    $keys = collect($results['hits'])
                    ->pluck('objectID')->values()->all();

    $modelQuery = $model->whereIn(
        $model->getQualifiedKeyName(), $keys
    );

    if ($withTrashed) $modelQuery->withTrashed(); // This is where the query will include deleted items, if specified
    if ($onlyTrashed) $modelQuery->onlyTrashed(); // This is where the query will only include deleted items, if specified

    $models = $modelQuery->get()->keyBy($model->getKeyName());

    return Collection::make($results['hits'])->map(function ($hit) use ($model, $models) {
        $key = $hit['objectID'];

        if (isset($models[$key])) {
            return $models[$key];
        }
    })->filter();
}

TNTSearchEngine.php

/**
 * Map the given results to instances of the given model.
 *
 * @param mixed                               $results
 * @param \Illuminate\Database\Eloquent\Model $model
 *
 * @return Collection
 */
public function map($results, $model, $withTrashed, $onlyTrashed)
{
    if (count($results['ids']) === 0) {
        return Collection::make();
    }

    $keys = collect($results['ids'])->values()->all();

    $model_query = $model->whereIn(
        $model->getQualifiedKeyName(), $keys
    );

    if ($withTrashed) $model_query->withTrashed();
    if ($onlyTrashed) $model_query->onlyTrashed();

    $models = $model_query->get()->keyBy($model->getKeyName());

    return collect($results['ids'])->map(function ($hit) use ($models) {
        if (isset($models[$hit])) {
            return $models[$hit];
        }
    })->filter();
}

让我知道它是如何工作的。

注意:这种方法仍然需要您withoutSyncingToSearch在删除模型时使用该方法手动暂停同步;否则搜索条件将更新为unsearchable().

于 2016-12-16T15:57:51.553 回答
1

这是我的解决方案。

// will return matched ids of my model instance
$searchResultIds = MyModel::search($request->input('search'))->raw()['ids'];

MyModel::whereId('id', $searchResultIds)->onlyTrashed()->get();
于 2018-06-22T04:21:13.980 回答