mgussekloo/laravel-facet-filter

在 Laravel 项目中实现简单的分面过滤(有时称为分面搜索或分面导航),无需烦恼。

dev-master 2024-10-01 06:39 UTC

This package is auto-updated.

Last update: 2024-10-01 06:39:19 UTC


README

本包在 Laravel 项目中提供了简单的分面过滤(有时称为分面搜索或分面导航)。它可以帮助您根据模型属性缩小查询结果。

  • 免费,无依赖项
  • 易于在任何项目中使用
  • 易于自定义
  • 有一个演示项目可以帮助您入门

Demo

贡献

请通过创建拉取请求或报告问题来为此包做出贡献。

安装

此包可以通过Composer安装。

composer require mgussekloo/laravel-facet-filter

准备您的项目

发布并运行迁移

php artisan vendor:publish --tag="facet-filter-migrations"
php artisan migrate

更新您的模型

为应该支持分面过滤的模型添加 Facettable 特性和 facetDefinitions() 方法。

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

use Mgussekloo\FacetFilter\Traits\Facettable;

class Product extends Model
{
	use HasFactory;
	use Facettable;

	public static function facetDefinitions()
	{
		// Return an array of definitions
		return [
			[
				'title' => 'Main color', // The title will be used for the parameter.
				'fieldname' => 'color' // Model property from which to get the values.
			],
			[
				'title' => 'Sizes',
				'fieldname' => 'sizes.name' // Use dot notation to get the value from related models.
			]
		];
	}
}

构建索引

在您开始过滤之前,您必须构建一个索引。该包包含一个索引器。

use Mgussekloo\FacetFilter\Indexer;

$products = Product::with(['sizes'])->get(); // get some products

$indexer = new Indexer();

$indexer->resetIndex(); // clear the entire index or...
$indexer->resetRows($products); // clear only the provided models

$indexer->buildIndex($products); // process the models

获取结果

将分面过滤器应用于查询

$filter = request()->all(); // use the request parameters
$filter = ['main-color' => ['green']]; // (or provide your own array)

$products = Product::facetsMatchFilter($filter)->get();

构建前端

$facets = Product::getFacets();

/* You can filter and sort like any regular Laravel collection. */
$singleFacet = $facets->firstWhere('fieldname', 'color');

/* Find out stuff about the facet. */
$paramName = $singleFacet->getParamName(); // "main-color"
$options = $singleFacet->getOptions();

/*
Options look like this:
(object)[
	'value' => 'Red',
	'selected' => false,
	'total' => 3,
	'slug' => 'color_red',
	'http_query' => 'main-color%5B1%5D=red&sizes%5B0%5D=small'
]
*/

基本前端示例

这是一个简单的演示项目,展示了基本的前端。

<div class="flex">
	<div class="w-1/4 flex-0">
		@foreach ($facets as $facet)
			<p>
				<h3>{{ $facet->title }}</h3>

				@foreach ($facet->getOptions() as $option)
					<a href="?{{ $option->http_query }}" class="{{ $option->selected ? 'underline' : '' }}">{{ $option->value }} ({{ $option->total }}) </a><br />
				@endforeach
			</p><br />
		@endforeach
	</div>
	<div class="w-3/4">
		@foreach ($products as $product)
			<p>
				<h1>{{ $product->name }} ({{ $product->sizes->pluck('name')->join(', ') }})</h1>
				{{ $product->color }}<br /><br />
			</p>
		@endforeach
	</div>
</div>

Livewire 示例

这是 Livewire 的样子。

<h2>Colors</h2>
@foreach ($facet->getOptions() as $option)
	<div class="facet-checkbox-pill">
		<input
			wire:model="filter.{{ $facet->getParamName() }}"
			type="checkbox"
			id="{{ $option->slug }}"
			value="{{ $option->value }}"
		/>
		<label for="{{ $option->slug }}" class="{{ $option->selected ? 'selected' : '' }}">
			{{ $option->value }} ({{ $option->total }})
		</label>
	</div>
@endforeach

自定义

高级索引

扩展索引器来自定义行为,例如将“范围括号”值而不是“单个价格”值保存到索引中。

class MyCustomIndexer extends \Mgussekloo\FacetFilter\Indexer {
	public function buildValues($facet, $model) {
		$values = parent::buildValues($facet, $model);

		if ($facet->getSlug() == 'App\Models\Product.price') {
			foreach ($values as $index => $value) {
				if ($value > 0 && $value < 500) {
					$values[$index] = '0-490';
				}
				if ($value > 490 && $value < 1000) {
					$values[$index] = '500-990';
				}
				if ($value > 990) {
					$values[$index] = 'Expensive';
				}
			}
		}

		return $values;
	}
}

对非常大的数据集分批处理模型。

$perPage = 1000; $currentPage = Cache::get('facetIndexingPage', 1);

$products = Product::with(['sizes'])->paginate($perPage, ['*'], 'page', $currentPage);
$indexer = new Indexer($products);

if ($currentPage == 1) {
	$indexer->resetIndex();
}

$indexer->buildIndex();

if ($products->hasMorePages()) {}
	// next iteration, increase currentPage with one
}

自定义分面

在分面定义中提供自定义属性和可选的自定义Facet 类

public static function facetDefinitions()
{
	return [
		[
			'title' => 'Main color',
			'description' => 'The main color.', // optional custom attribute, you could use $facet->description when creating the frontend...
            'related_id' => 23, // ... or use $facet->related_id with your custom indexer 
			'fieldname' => 'color',
			'facet_class' => CustomFacet::class // optional Facet class with custom logic
		]
	];
}

许可

MIT 许可证(MIT)。请参阅许可文件获取更多信息。