承接 tobento/app-search 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

tobento/app-search

Composer 安装命令:

composer require tobento/app-search

包简介

App search support.

README 文档

README

The search app provides interfaces to search resources by filters. It comes with a default implementation and some basic searchables and filters.

Table of Contents

Getting Started

Add the latest version of the app search project running this command.

composer require tobento/app-search

Requirements

  • PHP 8.4 or greater

Documentation

App

Check out the App Skeleton if you are using the skeleton.

You may also check out the App to learn more about the app in general.

Search Boot

The search boot does the following:

  • migrates search config, view and asset files
  • implements search interfaces based on the search config file
  • boots features defined in config file
use Tobento\App\AppFactory;
use Tobento\App\Search\InputInterface;
use Tobento\App\Search\SearchInterface;

// Create the app
$app = new AppFactory()->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'public', 'public')
    ->dir($app->dir('root').'vendor', 'vendor');

// Adding boots:
$app->boot(\Tobento\App\Search\Boot\Search::class);
$app->booting();

// Implemented interfaces:
$search = $app->get(SearchInterface::class);
$input = $app->get(InputInterface::class);

// Run the app
$app->run();

Search Config

The configuration for the search is located in the app/config/search.php file at the default App Skeleton config location where you can configure the implemented search interfaces for your application and more.

Basic Usage

Searching

This example shows how a search process works in general. If you use the Search Feature, there is no need to implement anything.

use Tobento\App\Search\Filters;
use Tobento\App\Search\FiltersInterface;
use Tobento\App\Search\Input;
use Tobento\App\Search\InputInterface;
use Tobento\App\Search\Search;
use Tobento\App\Search\Searchables;
use Tobento\App\Search\SearchablesInterface;
use Tobento\App\Search\SearchInterface;
use Tobento\App\Search\SearchResultsInterface;

$search = new Search(
    filters: new Filters(), // FiltersInterface
    searchables: new Searchables(), // SearchablesInterface
);

var_dump($search instanceof SearchInterface);
// bool(true)

// Searching:
$searchResults = $search->search(
    input: new Input(['search' => ['term' => 'foo']]), // InputInterface
);

var_dump($searchResults instanceof SearchResultsInterface);
// bool(true)

// You may get the filters:
var_dump($search->filters() instanceof FiltersInterface);
// bool(true)

// You may get the searchables:
var_dump($search->searchables() instanceof SearchablesInterface);
// bool(true)

Features

Search Feature

The search feature provides a simple search page where users can search the applications content. Furthermore, it provides a searchbar which you may render on all pages.

Check out the Search section on how to add searchables, filters and more as this feature uses the implemented search interfaces.

Config

In the config file you can configure this feature:

'features' => [
    new Feature\Search(
        // The default views to render:
        view: 'search/index',
        viewSearchbar: 'search/searchbar',
        viewSearchbarResults: 'search/searchbar-results',
        
        // You may customize the searchbar input placeholder:
        searchbarInputPlaceholder: 'Search...',
        
        // If true, routes are being localized.
        localizeRoute: false,
    ),
],

Search Page

The search page is available under the https://example.com/search url.

Searchbar

In any view file, render the view named search.bar which will display a search input element:

<header><?= $view->render('search.bar') ?></header>

Search

Adding Searchables

You may use and add the Available Searchables or create your own searchables if desired.

Option 1

In the Search Config file you can configure your searchables:

use Psr\Container\ContainerInterface;
use Tobento\App\Search\Search;
use Tobento\App\Search\Searchable;
use Tobento\App\Search\Searchables;
use Tobento\App\Search\SearchInterface;
use Tobento\App\Search\Filter;
use Tobento\App\Search\Filters;

'interfaces' => [
    SearchInterface::class => static function(ContainerInterface $container): SearchInterface {
        return new Search(
            filters: new Filters(
                new Filter\SearchTerm(name: 'search.term'),
            ),
            searchables: new Searchables(
                new Searchable\Menus($container->get(\Tobento\Service\Menu\MenusInterface::class)->menu('main')),
            ),
        );
    },
],

Option 2

Use the App on method to add searchables only on demand:

use Tobento\App\Search\Searchable;
use Tobento\App\Search\SearchablesInterface;
use Tobento\App\Search\SearchInterface;

$app->on(
    SearchInterface::class,
    static function(SearchInterface $search) use ($app): void {
        // Get the searchables:
        $searchables = $search->searchables();
        // SearchablesInterface
        
        // Adding a searchable:
        $menu = $app->get(\Tobento\Service\Menu\MenusInterface::class)->menu('main');
        $searchables->add(searchable: new Searchable\Menus(menu: $menu));
    }
);

Creating Searchables

You may create searchables for specific resources if you need full search and filter control.

Inspect the following files to see different searchable implementations:

  • Tobento\App\Search\Searchable\Menu::class
  • Tobento\App\Search\Searchable\Repository::class

Example

use Tobento\App\Search\Filter;
use Tobento\App\Search\FilterInterface;
use Tobento\App\Search\FiltersInterface;
use Tobento\App\Search\SearchableInterface;
use Tobento\App\Search\SearchResult;
use Tobento\App\Search\SearchResultInterface;
use Tobento\Service\Pagination\Pagination;
use Tobento\Service\Pagination\PaginationInterface;
use Tobento\Service\Pagination\UrlGenerator;
use Tobento\Service\View\ViewInterface;

class ProductsSearchable implements SearchableInterface
{
    protected null|PaginationInterface $pagination = null;
    
    public function __construct(
        protected ProductRepository $repository,
        protected ViewInterface $view,
        protected int $priority = 0,
    ) {}
    
    public function name(): string
    {
        return 'products';
    }
    
    public function title(): string
    {
        return 'Products';
    }
    
    public function priority(): int
    {
        return $this->priority;
    }
    
    /**
     * Returns the filters.
     *
     * @return array<array-key, FilterInterface>
     */
    public function filters(): array
    {
        // Configure specific products filters defining the searchable attribute on each filter:
        return [
            new Filter\Select(
                name: 'search.product-color',
                label: 'Colors',
                searchable: $this->name(),
                options: ['red' => 'Red', 'blue' => 'Blue'],
            ),
            new Filter\Pagination(
                name: 'search.product-page',
                searchable: $this->name(),
                pagination: $this->pagination(),
            ),
        ];
    }
    
    /**
     * Returns the search results found.
     *
     * @param FiltersInterface $filters
     * @return array<array-key, SearchResultInterface>
     */
    public function search(FiltersInterface $filters): array
    {
        $where = [];
        $orderBy = [];
        $limit = [];
        
        // Use the filters you wish to apply to your query:
        foreach($filters as $filter) {
            switch ($filter::class) {
                case Filter\Select::class:
                    if ($filter->name() === 'search.product-color') {
                        // apply filter to query:
                    }
                    break;
                case Filter\SearchTerm::class:
                    // apply filter to query:
                    break;
                case Filter\Pagination::class:
                    if ($filter->name() === 'search.product-page') {
                        $this->pagination = $filter->pagination();
                        $limit = [$this->pagination->getItemsPerPage(), $this->pagination->getItemsOffset()];
                    }
                    break;
            }
        }
        
        if ($filters->has('search.product-page')) {
            $paginationFilter = $filters->get('search.product-page');
            $paginationFilter->updatePaginationTotalItems($this->repository->count(where: $where));
            $this->pagination = $paginationFilter->pagination();
            $limit = [$this->pagination->getItemsPerPage(), $this->pagination->getItemsOffset()];
        }
        
        // Do the query and create the search results:
        $results = [];
        
        foreach($this->repository->findAll(where: $where, orderBy: $orderBy, limit: $limit) as $item) {
            $results = new SearchResult(
                searchable: $this->name(),
                type: $this->title(),
                title: $item->get('title'),
                url: $item->get('url'),
                image: $item->get('image'),
                
                // you may add html for custom search result:
                html: $this->view->render(
                    view: 'product/search-result',
                    data: $item,
                ),
            );
        }
        
        return $results;
    }

    public function totalItems(): int
    {
        return $this->pagination()->getTotalItems();
    }
    
    public function pagination(): PaginationInterface
    {
        if ($this->pagination) {
            return $this->pagination;
        }
        
        return $this->pagination = new Pagination(
            totalItems: $this->repository->count(),
            currentPage: 1,
            itemsPerPage: 25,
            maxPagesToShow: 6,
            maxItemsPerPage: 100,
            urlGenerator: new UrlGenerator()->addPageUrl('?search[product-page]={num}'),
        );
    }
}

Adding Filters

Option 1

In the Search Config file you can configure your filters:

use Psr\Container\ContainerInterface;
use Tobento\App\Search\Search;
use Tobento\App\Search\Searchable;
use Tobento\App\Search\Searchables;
use Tobento\App\Search\SearchInterface;
use Tobento\App\Search\Filter;
use Tobento\App\Search\Filters;

'interfaces' => [
    SearchInterface::class => static function(ContainerInterface $container): SearchInterface {
        return new Search(
            filters: new Filters(
                new Filter\SearchTerm(name: 'search.term'),
            ),
            searchables: new Searchables(
                new Searchable\Menus($container->get(\Tobento\Service\Menu\MenusInterface::class)->menu('main')),
            ),
        );
    },
],

Option 2

Use the App on method to add filters only on demand:

use Tobento\App\Search\Filter;
use Tobento\App\Search\FiltersInterface;
use Tobento\App\Search\SearchInterface;

$app->on(
    SearchInterface::class,
    static function(SearchInterface $search) use ($app): void {
        // Get the filters:
        $filters = $search->filters();
        // FiltersInterface
        
        // Adding a filter:
        $filters->add(filter: new Filter\SearchTerm(name: 'search.term'));
    }
);

Creating Filters

You may create custom filters to fit your requirements.

Inspect the following files to see different filter implementations:

  • Tobento\App\Search\Filter\Clear::class
  • Tobento\App\Search\Filter\Searchables::class
  • Tobento\App\Search\Filter\SearchTerm::class

Available Searchables

Menu Searchable

The Menu searchable allows you to search the provided menu items using the Search Term Filter.

use Tobento\App\Search\Searchable;
use Tobento\Service\Menu\MenuInterface;

$searchable = new Searchable\Menu(
    menu: $menu, // MenuInterface
    
    // Set a unique name:
    name: 'menu',
    
    // You may set a custom title:
    title: 'Menu Items',
    
    // You may set a priority:
    priority: 100,
);

You may check out the Menu Service for more information.

Repository Searchable

The Repository searchable allows you to search the provided repository using the Search Term Filter.

use Tobento\App\Search\Searchable;
use Tobento\App\Search\SearchResult;
use Tobento\App\Search\SearchResultInterface;
use Tobento\Service\Repository\RepositoryInterface;

$searchable = new Searchable\Repository(
    repository: $repository, // RepositoryInterface
    
    // Set a unique name:
    name: 'products',
    
    // Set a title:
    title: 'Products',
    
    // Define the search attributes:
    searchAttributes: ['title', 'description'],
    
    // Map the repository items to the search results:
    toSearchResult: function(object $item, Searchable\Repository $searchable): SearchResultInterface {
        return new SearchResult(
            searchable: $searchable->name(),
            type: $searchable->title(),
            title: $item->get('title'),
            description: $item->get('description'),
            url: $item->get('url'),
            image: $item->get('image'),
        );
    },
    
    // You may set a priority:
    priority: 100,
);

You may check out the Repository Service for more information.

Available Filters

Clear Filter

This filter displays a button to clear all filters.

use Tobento\App\Search\Filter;

$filter = new Filter\Clear(
    // Set a unique name:
    name: 'search.clear',
    
    // You may set a custom label:
    label: 'Clear all',
    
    // You may set attributes for the button element:
    attributes: ['data-foo' => 'value'],
);

Input Filter

This filter displays a HTML input element to filter searchables.

use Tobento\App\Search\Filter;

$filter = new Filter\Input(
    // Set a unique name:
    name: 'search.price.from',
    
    // You may set a label:
    label: 'Price From',
    
    // You may set a description:
    description: 'Searches prices from.',
    
    // You may set a searchable the filter belongs to:
    searchable: 'products', // or null
    
    // You may change the default input type (text):
    inputType: 'range',
    
    // You may set attributes for the input element:
    inputAttributes: ['min' => '5'],
    
    // You may set a default input value:
    inputValue: '10', // or null
    
    // You may set a custom view to render:
    view: 'search/filter',
);

Pagination Filter

The main purpose of this filter is to use for the searchables pagination. Check out the Available Searchables class files to see its usage in action.

Searchables Filter

This filter displays checkboxes with the searchables to enable or disable for searching.

use Tobento\App\Search\Filter;

$filter = new Filter\Searchables(
    // Set a unique name:
    name: 'search.searchables',
    
    // You may set a custom label:
    label: 'Content',
    
    // You may set a description:
    description: 'Choose the contents you wish to search.',
    
    // You may set a custom view to render:
    view: 'search/filter',
);

Search Term Filter

This filter displays a search input element to search for the provided search term.

use Tobento\App\Search\Filter;

$filter = new Filter\SearchTerm(
    // Set a unique name:
    name: 'search.term',
    
    // You may set a label:
    label: 'Search',
    
    // You may set a description:
    description: 'Search for ...',
    
    // You may set a custom placeholder text:
    placeholder: 'Search ...',
    
    // You may set a custom view to render:
    view: 'search/filter',
);

Select Filter

This filter displays a HTML select element with options to filter searchables.

use Tobento\App\Search\Filter;

$filter = new Filter\Select(
    // Set a unique name:
    name: 'search.colors',
    
    // You may set a label:
    label: 'Colors',
    
    // You may set a description:
    description: 'Choose any colors you wish to search for.',
    
    // You may set a searchable the filter belongs to:
    searchable: 'products', // or null
    
    // Set the options to choose from:
    options: ['blue' => 'Blue', 'red' => 'Red'],
    // with optgroup options:
    // options: ['Primary' => ['blue' => 'Blue']],
    
    // You may set a default value:
    selected: ['blue'],
    
    // You may set attributes for the select element:
    selectAttributes: ['multiple'],
    
    // You may set attributes for the option elements:
    optionAttributes: ['*' => ['data-foo' => 'val']],
    
    // You may set attributes for the optgroup elements:
    optgroupAttributes: ['data-foo' => 'value'],
    
    // You may set a custom view to render:
    view: 'search/filter',
);

Credits

tobento/app-search 适用场景与选型建议

tobento/app-search 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 13 次下载、GitHub Stars 达 0, 最近一次更新时间为 2025 年 05 月 22 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「package」 「search」 「app」 「tobento」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 tobento/app-search 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 tobento/app-search 我们能提供哪些服务?
定制开发 / 二次开发

基于 tobento/app-search 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 13
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 8
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-05-22