From 58c5a7d443bb238cb45f7620b6b2460c28adb8d7 Mon Sep 17 00:00:00 2001 From: Jonny Barnes Date: Tue, 11 Apr 2023 21:16:06 +0100 Subject: [PATCH] Re-add search functionality --- .env.example | 3 +- .env.github | 3 +- app/Http/Controllers/SearchController.php | 25 ++++ app/Models/Note.php | 4 +- composer.json | 1 + composer.lock | 77 +++++++++++- config/scout.php | 142 ++++++++++++++++++++++ resources/views/master.blade.php | 6 +- resources/views/search.blade.php | 25 ++-- routes/web.php | 4 + tests/Feature/SearchTest.php | 26 ++++ 11 files changed, 297 insertions(+), 19 deletions(-) create mode 100644 app/Http/Controllers/SearchController.php create mode 100644 config/scout.php create mode 100644 tests/Feature/SearchTest.php diff --git a/.env.example b/.env.example index 1f2d8334..da14b175 100644 --- a/.env.example +++ b/.env.example @@ -68,7 +68,8 @@ TWITTER_CONSUMER_SECRET= TWITTER_ACCESS_TOKEN= TWITTER_ACCESS_TOKEN_SECRET= -SCOUT_DRIVER=pgsql +SCOUT_DRIVER=database +SCOUT_QUEUE=false PIWIK=false PIWIK_ID=1 diff --git a/.env.github b/.env.github index dbc69b67..63cfb2db 100644 --- a/.env.github +++ b/.env.github @@ -50,7 +50,8 @@ TWITTER_CONSUMER_SECRET= TWITTER_ACCESS_TOKEN= TWITTER_ACCESS_TOKEN_SECRET= -SCOUT_DRIVER=pgsql +SCOUT_DRIVER=database +SCOUT_QUEUE=false PIWIK=false diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php new file mode 100644 index 00000000..1ffb9e26 --- /dev/null +++ b/app/Http/Controllers/SearchController.php @@ -0,0 +1,25 @@ +input('q'); + + $notes = Note::search($search) + ->paginate(); + + /** @var Note $note */ + foreach ($notes as $note) { + $note->load('place', 'media', 'client'); + } + + return view('search', compact('search', 'notes')); + } +} diff --git a/app/Models/Note.php b/app/Models/Note.php index af70aca6..a9778f0b 100644 --- a/app/Models/Note.php +++ b/app/Models/Note.php @@ -19,6 +19,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Facades\Cache; use Jonnybarnes\IndieWeb\Numbers; +use Laravel\Scout\Searchable; use League\CommonMark\Environment\Environment; use League\CommonMark\Extension\Autolink\AutolinkExtension; use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension; @@ -34,6 +35,7 @@ use Spatie\CommonMarkHighlighter\IndentedCodeRenderer; class Note extends Model { use HasFactory; + use Searchable; use SoftDeletes; /** @@ -96,7 +98,7 @@ class Note extends Model } /** - * @return array + * @return array */ public function toSearchableArray(): array { diff --git a/composer.json b/composer.json index 20321605..7ff52e9b 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,7 @@ "laravel/framework": "^10.0", "laravel/horizon": "^5.0", "laravel/sanctum": "^3.0", + "laravel/scout": "^10.1", "laravel/tinker": "^2.0", "lcobucci/jwt": "^5.0", "league/commonmark": "^2.0", diff --git a/composer.lock b/composer.lock index 8991f397..1cf18b45 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5e3ba7f9ad88d53aa871be437f02a0c5", + "content-hash": "617504ea1be00f742145197dbe3b5792", "packages": [ { "name": "aws/aws-crt-php", @@ -2165,6 +2165,81 @@ }, "time": "2023-01-13T15:41:49+00:00" }, + { + "name": "laravel/scout", + "version": "v10.1.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/scout.git", + "reference": "ee57968e7ec6943316f6cc66190baf4de8fb3cef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/scout/zipball/ee57968e7ec6943316f6cc66190baf4de8fb3cef", + "reference": "ee57968e7ec6943316f6cc66190baf4de8fb3cef", + "shasum": "" + }, + "require": { + "illuminate/bus": "^9.0|^10.0", + "illuminate/contracts": "^9.0|^10.0", + "illuminate/database": "^9.0|^10.0", + "illuminate/http": "^9.0|^10.0", + "illuminate/pagination": "^9.0|^10.0", + "illuminate/queue": "^9.0|^10.0", + "illuminate/support": "^9.0|^10.0", + "php": "^8.0" + }, + "require-dev": { + "algolia/algoliasearch-client-php": "^3.2", + "meilisearch/meilisearch-php": "^1.0", + "mockery/mockery": "^1.0", + "orchestra/testbench": "^7.0|^8.0", + "php-http/guzzle7-adapter": "^1.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "algolia/algoliasearch-client-php": "Required to use the Algolia engine (^3.2).", + "meilisearch/meilisearch-php": "Required to use the Meilisearch engine (^1.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Scout\\ScoutServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Scout\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Scout provides a driver based solution to searching your Eloquent models.", + "keywords": [ + "algolia", + "laravel", + "search" + ], + "support": { + "issues": "https://github.com/laravel/scout/issues", + "source": "https://github.com/laravel/scout" + }, + "time": "2023-04-11T16:38:05+00:00" + }, { "name": "laravel/serializable-closure", "version": "v1.3.0", diff --git a/config/scout.php b/config/scout.php new file mode 100644 index 00000000..02f1f767 --- /dev/null +++ b/config/scout.php @@ -0,0 +1,142 @@ + env('SCOUT_DRIVER', 'algolia'), + + /* + |-------------------------------------------------------------------------- + | Index Prefix + |-------------------------------------------------------------------------- + | + | Here you may specify a prefix that will be applied to all search index + | names used by Scout. This prefix may be useful if you have multiple + | "tenants" or applications sharing the same search infrastructure. + | + */ + + 'prefix' => env('SCOUT_PREFIX', ''), + + /* + |-------------------------------------------------------------------------- + | Queue Data Syncing + |-------------------------------------------------------------------------- + | + | This option allows you to control if the operations that sync your data + | with your search engines are queued. When this is set to "true" then + | all automatic data syncing will get queued for better performance. + | + */ + + 'queue' => env('SCOUT_QUEUE', false), + + /* + |-------------------------------------------------------------------------- + | Database Transactions + |-------------------------------------------------------------------------- + | + | This configuration option determines if your data will only be synced + | with your search indexes after every open database transaction has + | been committed, thus preventing any discarded data from syncing. + | + */ + + 'after_commit' => false, + + /* + |-------------------------------------------------------------------------- + | Chunk Sizes + |-------------------------------------------------------------------------- + | + | These options allow you to control the maximum chunk size when you are + | mass importing data into the search engine. This allows you to fine + | tune each of these chunk sizes based on the power of the servers. + | + */ + + 'chunk' => [ + 'searchable' => 500, + 'unsearchable' => 500, + ], + + /* + |-------------------------------------------------------------------------- + | Soft Deletes + |-------------------------------------------------------------------------- + | + | This option allows to control whether to keep soft deleted records in + | the search indexes. Maintaining soft deleted records can be useful + | if your application still needs to search for the records later. + | + */ + + 'soft_delete' => false, + + /* + |-------------------------------------------------------------------------- + | Identify User + |-------------------------------------------------------------------------- + | + | This option allows you to control whether to notify the search engine + | of the user performing the search. This is sometimes useful if the + | engine supports any analytics based on this application's users. + | + | Supported engines: "algolia" + | + */ + + 'identify' => env('SCOUT_IDENTIFY', false), + + /* + |-------------------------------------------------------------------------- + | Algolia Configuration + |-------------------------------------------------------------------------- + | + | Here you may configure your Algolia settings. Algolia is a cloud hosted + | search engine which works great with Scout out of the box. Just plug + | in your application ID and admin API key to get started searching. + | + */ + + 'algolia' => [ + 'id' => env('ALGOLIA_APP_ID', ''), + 'secret' => env('ALGOLIA_SECRET', ''), + ], + + /* + |-------------------------------------------------------------------------- + | Meilisearch Configuration + |-------------------------------------------------------------------------- + | + | Here you may configure your Meilisearch settings. Meilisearch is an open + | source search engine with minimal configuration. Below, you can state + | the host and key information for your own Meilisearch installation. + | + | See: https://docs.meilisearch.com/guides/advanced_guides/configuration.html + | + */ + + 'meilisearch' => [ + 'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'), + 'key' => env('MEILISEARCH_KEY'), + 'index-settings' => [ + // 'users' => [ + // 'filterableAttributes'=> ['id', 'name', 'email'], + // ], + ], + ], + +]; diff --git a/resources/views/master.blade.php b/resources/views/master.blade.php index df94fe72..b33e6459 100644 --- a/resources/views/master.blade.php +++ b/resources/views/master.blade.php @@ -51,9 +51,9 @@
-{{--
--}} -{{-- --}} -{{--
--}} +
+ +

Built with love: Colophon

diff --git a/resources/views/search.blade.php b/resources/views/search.blade.php index 2e07c957..8e6d0b35 100644 --- a/resources/views/search.blade.php +++ b/resources/views/search.blade.php @@ -3,17 +3,18 @@ @section('title')Search « @stop @section('content') -

Search Results

-@foreach($notes as $note) -
-@include('templates.note', ['note' => $note]) -
-@endforeach -{{ $notes->links() }} -@stop +

Search Results

+

Searching for “{{ $search }}”

-@section('scripts') - @include('templates.mapbox-links') - - +
+ + + + @foreach ($notes as $note) + @include('templates.note', ['note' => $note]) + @endforeach +
+ + {{ $notes->links() }} @stop diff --git a/routes/web.php b/routes/web.php index 81976c0d..67211c17 100644 --- a/routes/web.php +++ b/routes/web.php @@ -31,6 +31,7 @@ use App\Http\Controllers\MicropubController; use App\Http\Controllers\MicropubMediaController; use App\Http\Controllers\NotesController; use App\Http\Controllers\PlacesController; +use App\Http\Controllers\SearchController; use App\Http\Controllers\ShortURLsController; use App\Http\Controllers\TokenEndpointController; use App\Http\Controllers\WebMentionsController; @@ -205,6 +206,9 @@ Route::group(['domain' => config('url.longurl')], function () { // Micropub Route::redirect('/micropub/create', '/notes/new'); + + // Search + Route::get('search', [SearchController::class, 'search']); }); // Short URL diff --git a/tests/Feature/SearchTest.php b/tests/Feature/SearchTest.php new file mode 100644 index 00000000..a0dd43bc --- /dev/null +++ b/tests/Feature/SearchTest.php @@ -0,0 +1,26 @@ +create(); + Note::Factory()->create(['note' => 'hello world']); + + $response = $this->get('/search?q=hello'); + + $response->assertStatus(200); + $response->assertViewIs('search'); + $response->assertViewHas('search'); + $response->assertViewHas('notes'); + $response->assertSee('hello world'); + } +}