| 1 |
<?php |
| 2 |
|
| 3 |
namespace Templately\Modules\BlockPatterns\REST; |
| 4 |
|
| 5 |
use Templately\API\API; |
| 6 |
use Templately\Modules\BlockPatterns\PatternRegistrar; |
| 7 |
use Templately\Modules\BlockPatterns\PatternSync; |
| 8 |
use WP_REST_Request; |
| 9 |
|
| 10 |
/** |
| 11 |
* Live catalog search for the block inserter (R&D). |
| 12 |
* |
| 13 |
* The registered catalog is capped at ~100 items — the ceiling exists because |
| 14 |
* WordPress resolves every registered pattern when it builds the editor payload |
| 15 |
* — so inserter search only ever sees the most-downloaded slice of a catalog in |
| 16 |
* the thousands. This endpoint answers what the user actually typed, and the |
| 17 |
* client injects the results into the editor's pattern settings for the life of |
| 18 |
* the query. |
| 19 |
* |
| 20 |
* The results are NOT registered and NOT cached into the plan-keyed list. They |
| 21 |
* are a transient answer, and the only lasting effect is the per-user allow-list |
| 22 |
* `PatternSync::search()` writes so the ids it returned can be fetched by |
| 23 |
* `block-patterns/content`. |
| 24 |
* |
| 25 |
* Returns patterns in the editor's own shape via `PatternRegistrar::describe_item()`, |
| 26 |
* the same code that builds a registered pattern — so an injected result cannot |
| 27 |
* drift from a registered one. |
| 28 |
*/ |
| 29 |
class Search extends API { |
| 30 |
private $endpoint = 'block-patterns/search'; |
| 31 |
|
| 32 |
/** Bounds one response; the inserter shows a handful at a time regardless. */ |
| 33 |
const MAX_LIMIT = 40; |
| 34 |
|
| 35 |
public function permission_check( WP_REST_Request $request ) { |
| 36 |
$this->request = $request; |
| 37 |
|
| 38 |
return is_user_logged_in() && current_user_can( 'edit_posts' ); |
| 39 |
} |
| 40 |
|
| 41 |
public function register_routes() { |
| 42 |
$this->get( $this->endpoint, [ $this, 'search' ] ); |
| 43 |
} |
| 44 |
|
| 45 |
public function search() { |
| 46 |
$term = trim( (string) $this->get_param( 'q', '' ) ); |
| 47 |
$limit = min( self::MAX_LIMIT, max( 1, (int) $this->get_param( 'limit', 24, 'absint' ) ) ); |
| 48 |
|
| 49 |
// Below two characters every query matches half the catalog, which costs a |
| 50 |
// cloud round trip to tell the user nothing. |
| 51 |
if ( mb_strlen( $term ) < 2 ) { |
| 52 |
return $this->success( [ 'patterns' => [], 'term' => $term ] ); |
| 53 |
} |
| 54 |
|
| 55 |
$sync = PatternSync::get_instance(); |
| 56 |
if ( null === $sync->plan_key() ) { |
| 57 |
return $this->success( [ 'patterns' => [], 'term' => $term ] ); |
| 58 |
} |
| 59 |
|
| 60 |
$registrar = PatternRegistrar::get_instance(); |
| 61 |
$patterns = []; |
| 62 |
|
| 63 |
foreach ( $sync->search( $term, $limit ) as $item ) { |
| 64 |
$patterns[] = $registrar->describe_item( $item ); |
| 65 |
} |
| 66 |
|
| 67 |
return $this->success( [ 'patterns' => $patterns, 'term' => $term ] ); |
| 68 |
} |
| 69 |
} |
| 70 |
|