mgussekloo/laravel-facet-filter
Composer 安装命令:
composer require mgussekloo/laravel-facet-filter
包简介
Simple facet filtering in Laravel projects, hassle free.
README 文档
README
Laravel Facet Filter
This package provides simple facet filtering (sometimes called Faceted Search or Faceted Navigation) in Laravel projects. It helps narrow down query results based on the attributes of your models.
- Free, no dependencies
- Easy to use in any project
- Easy to customize
- There's a demo project to get you started
- Ongoing support (last update: july 2025)
Contributing
Please contribute to this package either by creating a pull request or reporting an issue.
Installation
This package can be installed through Composer.
composer require mgussekloo/laravel-facet-filter
Get started
Update your models
Add a Facettable trait and a facetDefinitions() method to models you'd like to filter:
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. ] ]; } }
Build an index
In most cases you'll want to create an index to filter large datasets efficiently. (Don't want to build an index? Skip ahead to filtering collections.)
First, run the migrations:
php artisan vendor:publish --tag="facet-filter-migrations"
php artisan migrate
To build the index, create an Artisan command that queries your Facettable models. Run it periodically or whenever your data changes.
$products = Product::with(['sizes'])->get(); // get some products $products->buildIndex(); // build the index
Get results
Within a controller, apply the facet filter to a query
$filter = request()->all(); // the filter looks like ['main-color' => ['green']] $products = Product::facetFilter($filter)->get();
Basic frontend example
Here's a simple demo project that demonstrates a basic frontend.
<div class="flex"> <div class="w-1/4 flex-0"> @foreach ($products->getFacets() 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>
To see an example of a Livewire implementation, see this gist.
Facet details
$facets = $products->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' ] */
Customization
Advanced indexing
Extend the Indexer to customize behavior, e.g. to save a "range bracket" value instead of a "individual price" value to the index.
class MyCustomIndexer extends \Mgussekloo\FacetFilter\Indexer { public function buildValues($facet, $model) { $values = parent::buildValues($facet, $model); if ($facet->fieldname == 'price') { if ($model->price > 1000) { return 'Expensive'; } if ($model->price > 500) { return '500 - 1000'; } if ($model->price > 250) { return '250 - 500'; } return '0 - 250'; } return $values; } }
You may overwrite the indexer() method on your Facettable model to return an instance of your custom indexer:
use App\MyCustomIndexer; class Product extends Model { use HasFactory; use Facettable; public static function indexer() { return new MyCustomIndexer(); } }
$products = Products::get(); $products->buildIndex(); // uses MyCustomIndexer
Updating a larger index over time
$indexer = new Indexer(); $perPage = 1000; $currentPage = Cache::get('facetIndexingPage', 1); $products = Product::with(['sizes'])->paginate($perPage, ['*'], 'page', $currentPage); if ($currentPage == 1) { $indexer->resetIndex(); // clear entire index } $indexer->buildIndex($products); if ($products->hasMorePages()) {} // next iteration, increase currentPage with one }
Custom facets
Provide custom attributes and an optional custom Facet class in the facet definitions.
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 ] ]; }
Filtering collections
It's possible to apply facet filtering to a collection without building an index. Facettable models return a FacettableCollection, which provides an indexlessFacetFilter() method.
$products = Product::all(); // returns a "FacettableCollection" $products = $products->indexlessFacetFilter($filter);
Pagination
Example:
$products = Product::facetFilter($filter)->paginate(10); $pagination = $products->appends(request()->input())->links();
Notes on caching
By default Facet Filter caches some heavy operations through the non-persistent 'array' cache driver. Caches are based on the model and filter, not the query bindings or specifics. Use a cacheTag to distinguish between queries.
// if you have two facet filter queries on the same model... Product::where('published', false)->cacheTag('unpublished')->facetFilter($filter)->get(); // ...make sure to distinguish between queries Product::where('published', true)->cacheTag('published')->facetFilter($filter)->get();
The default Indexer clears the cache for all models when rebuilding the index. You can do it manually:
Product::forgetCache(); // clears cache for all cache tags Product::forgetCache('unpublished'); // clears cache for only this cachetag use Mgussekloo\FacetFilter\Facades\FacetCache; FacetCache::forgetCache(); // clears cache for all models and cache tags
Config
You can configure a peristent cache driver through config/facet-filter.php. Be aware of any caching related issues, e.g. if you have any user specific results this may be problematic.
php artisan vendor:publish --tag=facet-filter-config
'classes' => [ 'facet' => Mgussekloo\FacetFilter\Models\Facet::class, 'facetrow' => Mgussekloo\FacetFilter\Models\FacetRow::class, ], 'table_names' => [ 'facetrows' => 'facetrows', ], 'cache' => [ 'expiration_time' => \DateInterval::createFromDateString('24 hours'), 'key' => 'mgussekloo.facetfilter.cache', 'store' => 'array', ],
License
The MIT License (MIT). Please see License File for more information.
mgussekloo/laravel-facet-filter 适用场景与选型建议
mgussekloo/laravel-facet-filter 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 765 次下载、GitHub Stars 达 58, 最近一次更新时间为 2022 年 07 月 15 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「search」 「filter」 「navigation」 「laravel」 「facet」 「faceted search」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 mgussekloo/laravel-facet-filter 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 mgussekloo/laravel-facet-filter 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 mgussekloo/laravel-facet-filter 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
A Laravel package to retrieve data from Google Search Console
A block to display a list of links to child pages, or pages in current level
Nova searchable filter for belongsTo relationships.
Indexed Search Autocomplete - Extends the TYPO3 Core Extension Indexed_Search searchform with an autocomplete feature.
Abstraction Layer to index and search entities
Symfony DataGridBundle
统计信息
- 总下载量: 765
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 58
- 点击次数: 15
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2022-07-15
