From 8c206535169f43913ca0c3d5bd695c0982a82a69 Mon Sep 17 00:00:00 2001 From: Matt Kingshott <51963402+mattkingshott@users.noreply.github.com> Date: Mon, 22 Nov 2021 15:43:52 +0000 Subject: [PATCH] preparations for v2 --- .github/.php-cs-fixer.php | 44 ----------- .github/workflows/style.yml | 2 +- resources/version.svg | 4 +- src/Macros/OrderByFuzzy.php | 1 - src/Macros/WhereFuzzy.php | 75 +++++++++++-------- src/Matchers/AcronymMatcher.php | 3 - src/Matchers/BaseMatcher.php | 5 -- src/Matchers/ConsecutiveCharactersMatcher.php | 5 -- src/Matchers/ExactMatcher.php | 1 - src/Matchers/InStringMatcher.php | 3 - src/Matchers/StartOfStringMatcher.php | 3 - src/Matchers/StartOfWordsMatcher.php | 3 - src/Matchers/StudlyCaseMatcher.php | 5 -- src/Matchers/TimesInStringMatcher.php | 1 - src/ServiceProvider.php | 19 +++-- .../2014_10_12_000000_create_users_table.php | 6 +- tests/PackageTest.php | 22 ++---- tests/User.php | 1 - tools/.php-cs-fixer.php | 52 +++++++++++++ 19 files changed, 119 insertions(+), 136 deletions(-) delete mode 100644 .github/.php-cs-fixer.php create mode 100644 tools/.php-cs-fixer.php diff --git a/.github/.php-cs-fixer.php b/.github/.php-cs-fixer.php deleted file mode 100644 index c96089d..0000000 --- a/.github/.php-cs-fixer.php +++ /dev/null @@ -1,44 +0,0 @@ -notPath('bootstrap/*') - ->notPath('storage/*') - ->notPath('storage/*') - ->notPath('resources/view/mail/*') - ->in([__DIR__ . '/../src', __DIR__ . '/../tests']) - ->name('*.php') - ->notName('*.blade.php') - ->ignoreDotFiles(true) - ->ignoreVCS(true); - -return PhpCsFixer\Config::create() - ->setRules([ - '@PSR2' => true, - 'array_syntax' => ['syntax' => 'short'], - 'ordered_imports' => ['sort_algorithm' => 'length'], - 'no_unused_imports' => true, - 'not_operator_with_successor_space' => true, - 'trailing_comma_in_multiline_array' => true, - 'phpdoc_scalar' => true, - 'unary_operator_spaces' => true, - 'binary_operator_spaces' => [ - 'align_double_arrow' => true, - 'align_equals' => true, - ], - 'blank_line_before_statement' => [ - 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], - ], - 'phpdoc_single_line_var_spacing' => true, - 'phpdoc_var_without_name' => true, - 'method_argument_space' => [ - 'on_multiline' => 'ensure_fully_multiline', - 'keep_multiple_spaces_after_comma' => true, - ], - 'method_chaining_indentation' => true, - 'object_operator_without_whitespace' => true, - 'no_superfluous_phpdoc_tags' => true, - 'function_declaration' => [ - 'closure_function_spacing' => 'none', - ], - ]) - ->setFinder($finder); diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index e4e81fe..4a34685 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -13,7 +13,7 @@ jobs: - name: Run PHP CS Fixer uses: docker://oskarstark/php-cs-fixer-ga with: - args: --config=.github/.php_cs --allow-risky=yes + args: --config=tools/.php-cs-fixer.php --allow-risky=yes - name: Extract branch name shell: bash diff --git a/resources/version.svg b/resources/version.svg index 3900245..31628f1 100644 --- a/resources/version.svg +++ b/resources/version.svg @@ -14,7 +14,7 @@ stable stable - v1.1.0 - v1.1.0 + v2.0.0 + v2.0.0 diff --git a/src/Macros/OrderByFuzzy.php b/src/Macros/OrderByFuzzy.php index 2e7cdf0..c5c4826 100755 --- a/src/Macros/OrderByFuzzy.php +++ b/src/Macros/OrderByFuzzy.php @@ -6,7 +6,6 @@ class OrderByFuzzy { - /** * Construct a fuzzy search expression. * diff --git a/src/Macros/WhereFuzzy.php b/src/Macros/WhereFuzzy.php index 45b0295..cec37c2 100755 --- a/src/Macros/WhereFuzzy.php +++ b/src/Macros/WhereFuzzy.php @@ -2,12 +2,12 @@ namespace Quest\Macros; -use Illuminate\Database\Query\Builder; use Quest\Matchers\ExactMatcher; use Illuminate\Support\Facades\DB; use Quest\Matchers\AcronymMatcher; use Quest\Matchers\InStringMatcher; use Quest\Matchers\StudlyCaseMatcher; +use Illuminate\Database\Query\Builder; use Quest\Matchers\StartOfWordsMatcher; use Quest\Matchers\StartOfStringMatcher; use Quest\Matchers\TimesInStringMatcher; @@ -16,20 +16,19 @@ class WhereFuzzy { - /** * The weights for the pattern matching classes. * **/ protected static array $matchers = [ - ExactMatcher::class => 100, - StartOfStringMatcher::class => 50, - AcronymMatcher::class => 42, + ExactMatcher::class => 100, + StartOfStringMatcher::class => 50, + AcronymMatcher::class => 42, ConsecutiveCharactersMatcher::class => 40, - StartOfWordsMatcher::class => 35, - StudlyCaseMatcher::class => 32, - InStringMatcher::class => 30, - TimesInStringMatcher::class => 8, + StartOfWordsMatcher::class => 35, + StudlyCaseMatcher::class => 32, + InStringMatcher::class => 30, + TimesInStringMatcher::class => 8, ]; /** @@ -38,33 +37,35 @@ class WhereFuzzy **/ public static function make(Builder $builder, $field, $value): Builder { - $value = static::escapeValue($value); + $value = static::escapeValue($value); $nativeField = '`' . str_replace('.', '`.`', trim($field, '` ')) . '`'; - if (!is_array($builder->columns) || empty($builder->columns)) { + if (! is_array($builder->columns) || empty($builder->columns)) { $builder->columns = ['*']; } + $builder ->addSelect([static::pipeline($field, $nativeField, $value)]) ->having('fuzzy_relevance_' . str_replace('.', '_', $field), '>', 0); static::calculateTotalRelevanceColumn($builder); + return $builder; } /** - * Construct a fuzzy search expression. + * Construct a fuzzy OR search expression. * **/ public static function makeOr(Builder $builder, $field, $value): Builder { - - $value = static::escapeValue($value); + $value = static::escapeValue($value); $nativeField = '`' . str_replace('.', '`.`', trim($field, '` ')) . '`'; - if (!is_array($builder->columns) || empty($builder->columns)) { + if (! is_array($builder->columns) || empty($builder->columns)) { $builder->columns = ['*']; } + $builder ->addSelect([static::pipeline($field, $nativeField, $value)]) ->orHaving('fuzzy_relevance_' . str_replace('.', '_', $field), '>', 0); @@ -75,24 +76,28 @@ public static function makeOr(Builder $builder, $field, $value): Builder } /** - * Manage relevance columns SUM for total relevance ORDER - * Searches all relevance columns and parses the relevance expressions to create the total relevance column - * and creates the order statement for it - * @param $builder - * @return bool + * Manage relevance columns SUM for total relevance ORDER. + * + * Searches all relevance columns and parses the relevance + * expressions to create the total relevance column + * and creates the order statement for it. + * */ protected static function calculateTotalRelevanceColumn($builder): bool { - if (!empty($builder->columns)) { + if (! empty($builder->columns)) { $existingRelevanceColumns = []; - $sumColumnIdx = null; + $sumColumnIdx = null; + // search for fuzzy_relevance_* columns and _fuzzy_relevance_ position foreach ($builder->columns as $as => $column) { if ($column instanceof Expression) { if (stripos($column->getValue(), 'AS fuzzy_relevance_')) { $matches = []; + preg_match('/AS (fuzzy_relevance_.*)$/', $column->getValue(), $matches); - if (!empty($matches[1])) { + + if (! empty($matches[1])) { $existingRelevanceColumns[$as] = $matches[1]; } } elseif (stripos($column->getValue(), 'AS _fuzzy_relevance_')) { @@ -100,49 +105,55 @@ protected static function calculateTotalRelevanceColumn($builder): bool } } } + // glue together all relevance expresions under _fuzzy_relevance_ column $relevanceTotalColumn = ''; + foreach ($existingRelevanceColumns as $as => $column) { - $relevanceTotalColumn .= (!empty($relevanceTotalColumn) ? ' + ' : '') + $relevanceTotalColumn .= (! empty($relevanceTotalColumn) ? ' + ' : '') . '(' . str_ireplace(' AS ' . $column, '', $builder->columns[$as]->getValue()) . ')'; } + $relevanceTotalColumn .= ' AS _fuzzy_relevance_'; if (is_null($sumColumnIdx)) { // no sum column yet, just add this one - $builder - ->addSelect([new Expression($relevanceTotalColumn)]); + $builder->addSelect([new Expression($relevanceTotalColumn)]); } else { // update the existing one $builder->columns[$sumColumnIdx] = new Expression($relevanceTotalColumn); } + // only add the _fuzzy_relevance_ ORDER once if ( - !$builder->orders - || ($builder->orders - && array_search('_fuzzy_relevance_', + ! $builder->orders + || ( + $builder->orders + && array_search( + '_fuzzy_relevance_', array_column($builder->orders, 'column') ) === false ) ) { $builder->orderBy('_fuzzy_relevance_', 'desc'); } + return true; } + return false; } /** - * Escape value input for fuzzy search - * @param $value - * @return false|string + * Escape value input for fuzzy search. */ protected static function escapeValue($value) { $value = str_replace(['"', "'", '`'], '', $value); $value = substr(DB::connection()->getPdo()->quote($value), 1, -1); + return $value; } diff --git a/src/Matchers/AcronymMatcher.php b/src/Matchers/AcronymMatcher.php index 28f9b14..43314d8 100644 --- a/src/Matchers/AcronymMatcher.php +++ b/src/Matchers/AcronymMatcher.php @@ -4,15 +4,12 @@ class AcronymMatcher extends BaseMatcher { - /** * The operator to use for the WHERE clause. * **/ protected string $operator = 'LIKE'; - - /** * Format the given search term. * diff --git a/src/Matchers/BaseMatcher.php b/src/Matchers/BaseMatcher.php index a002ec2..e5b5fb4 100755 --- a/src/Matchers/BaseMatcher.php +++ b/src/Matchers/BaseMatcher.php @@ -4,15 +4,12 @@ abstract class BaseMatcher { - /** * The weight to apply to the match. * **/ protected int $multiplier; - - /** * Constructor. * @@ -22,8 +19,6 @@ public function __construct(int $multiplier) $this->multiplier = $multiplier; } - - /** * The default process for building the query string. * diff --git a/src/Matchers/ConsecutiveCharactersMatcher.php b/src/Matchers/ConsecutiveCharactersMatcher.php index 6c5d026..bda4c7f 100644 --- a/src/Matchers/ConsecutiveCharactersMatcher.php +++ b/src/Matchers/ConsecutiveCharactersMatcher.php @@ -4,15 +4,12 @@ class ConsecutiveCharactersMatcher extends BaseMatcher { - /** * The operator to use for the WHERE clause. * **/ protected string $operator = 'LIKE'; - - /** * The process for building the query string. * @@ -25,8 +22,6 @@ public function buildQueryString(string $field, string $value) : string "(CHAR_LENGTH('$value') / CHAR_LENGTH(REPLACE($field, ' ', '')))), 0)"; } - - /** * Format the given search term. * diff --git a/src/Matchers/ExactMatcher.php b/src/Matchers/ExactMatcher.php index 7152f44..7450fdd 100755 --- a/src/Matchers/ExactMatcher.php +++ b/src/Matchers/ExactMatcher.php @@ -4,7 +4,6 @@ class ExactMatcher extends BaseMatcher { - /** * The operator to use for the WHERE clause. * diff --git a/src/Matchers/InStringMatcher.php b/src/Matchers/InStringMatcher.php index c0b9ec7..66a073e 100755 --- a/src/Matchers/InStringMatcher.php +++ b/src/Matchers/InStringMatcher.php @@ -4,15 +4,12 @@ class InStringMatcher extends BaseMatcher { - /** * The operator to use for the WHERE clause. * **/ protected string $operator = 'LIKE'; - - /** * Format the given search term. * diff --git a/src/Matchers/StartOfStringMatcher.php b/src/Matchers/StartOfStringMatcher.php index e6e2e53..d68cb24 100755 --- a/src/Matchers/StartOfStringMatcher.php +++ b/src/Matchers/StartOfStringMatcher.php @@ -4,15 +4,12 @@ class StartOfStringMatcher extends BaseMatcher { - /** * The operator to use for the WHERE clause. * **/ protected string $operator = 'LIKE'; - - /** * Format the given search term. * diff --git a/src/Matchers/StartOfWordsMatcher.php b/src/Matchers/StartOfWordsMatcher.php index b8d6e3e..cb07ba0 100755 --- a/src/Matchers/StartOfWordsMatcher.php +++ b/src/Matchers/StartOfWordsMatcher.php @@ -4,15 +4,12 @@ class StartOfWordsMatcher extends BaseMatcher { - /** * The operator to use for the WHERE clause. * **/ protected string $operator = 'LIKE'; - - /** * Format the given search term. * diff --git a/src/Matchers/StudlyCaseMatcher.php b/src/Matchers/StudlyCaseMatcher.php index 7bba21c..3a2a60b 100644 --- a/src/Matchers/StudlyCaseMatcher.php +++ b/src/Matchers/StudlyCaseMatcher.php @@ -4,15 +4,12 @@ class StudlyCaseMatcher extends BaseMatcher { - /** * The operator to use for the WHERE clause. * **/ protected string $operator = 'LIKE BINARY'; - - /** * The process for building the query string. * @@ -23,8 +20,6 @@ public function buildQueryString(string $field, string $value) : string "$field {$this->operator} '{$this->formatSearchString($value)}', {$this->multiplier}, 0)"; } - - /** * Format the given search term. * diff --git a/src/Matchers/TimesInStringMatcher.php b/src/Matchers/TimesInStringMatcher.php index 8304561..1c879e8 100755 --- a/src/Matchers/TimesInStringMatcher.php +++ b/src/Matchers/TimesInStringMatcher.php @@ -4,7 +4,6 @@ class TimesInStringMatcher extends BaseMatcher { - /** * The process for building the query string. * diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 7bfd8c2..71ee442 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -2,14 +2,14 @@ namespace Quest; -use Illuminate\Database\Query\Builder; -use Quest\Macros\OrderByFuzzy; +use Closure; use Quest\Macros\WhereFuzzy; +use Quest\Macros\OrderByFuzzy; +use Illuminate\Database\Query\Builder; use Illuminate\Support\ServiceProvider as Provider; class ServiceProvider extends Provider { - /** * Bootstrap any application services. * @@ -18,21 +18,26 @@ public function boot(): void { Builder::macro('orderByFuzzy', fn($fields) => OrderByFuzzy::make($this, $fields)); - Builder::macro('whereFuzzy', function ($field, $value = null) { + Builder::macro('whereFuzzy', function($field, $value = null) { // check if first param is a closure and execute it if it is, passing the current builder as parameter // so when $query->orWhereFuzzy, $query will be the current query builder, not a new instance - if ($field instanceof \Closure) { + if ($field instanceof Closure) { $field($this); + return $this; } + // if $query->orWhereFuzzy is called in the closure, or directly by the query builder, do this return WhereFuzzy::make($this, $field, $value); }); - Builder::macro('orWhereFuzzy', function ($field, $value = null) { - if ($field instanceof \Closure) { + + Builder::macro('orWhereFuzzy', function($field, $value = null) { + if ($field instanceof Closure) { $field($this); + return $this; } + return WhereFuzzy::makeOr($this, $field, $value); }); } diff --git a/support/migrations/2014_10_12_000000_create_users_table.php b/support/migrations/2014_10_12_000000_create_users_table.php index d6fcd64..5aecdbd 100644 --- a/support/migrations/2014_10_12_000000_create_users_table.php +++ b/support/migrations/2014_10_12_000000_create_users_table.php @@ -6,7 +6,6 @@ class CreateUsersTable extends Migration { - /** * Run the migrations. * @@ -21,8 +20,6 @@ public function up() : void }); } - - /** * Reverse the migrations. * @@ -31,5 +28,4 @@ public function down() : void { Schema::dropIfExists('users'); } - -} \ No newline at end of file +} diff --git a/tests/PackageTest.php b/tests/PackageTest.php index 2cdf9e9..e1d1ad9 100644 --- a/tests/PackageTest.php +++ b/tests/PackageTest.php @@ -8,7 +8,6 @@ class PackageTest extends TestCase { - /** * Setup the test environment. * @@ -17,8 +16,7 @@ protected function setUp(): void { parent::setUp(); - app()['config']->set('database.default', 'mysql'); - $dbSetup = [ + $setup = [ 'driver' => 'mysql', 'url' => env('DATABASE_URL'), 'host' => env('DB_HOST', '127.0.0.1'), @@ -34,7 +32,9 @@ protected function setUp(): void 'strict' => true, 'engine' => null, ]; - app()['config']->set('database.connections.mysql', $dbSetup); + + app()['config']->set('database.default', 'mysql'); + app()['config']->set('database.connections.mysql', $setup); (new ServiceProvider(app()))->boot(); @@ -48,7 +48,6 @@ protected function setUp(): void DB::table('users')->insert(['name' => 'William Doe', 'nickname' => 'willy', 'country' => 'Italy']); } - /** @test */ public function it_can_perform_a_fuzzy_search_and_receive_one_result() { @@ -60,7 +59,6 @@ public function it_can_perform_a_fuzzy_search_and_receive_one_result() $this->assertEquals('Jane Doe', $results->first()->name); } - /** @test */ public function it_can_perform_a_fuzzy_search_and_receive_multiple_results() { @@ -73,7 +71,6 @@ public function it_can_perform_a_fuzzy_search_and_receive_multiple_results() $this->assertEquals('Jane Doe', $results[1]->name); } - /** @test */ public function it_can_perform_a_fuzzy_search_and_paginate_multiple_results() { @@ -90,7 +87,6 @@ public function it_can_perform_a_fuzzy_search_and_paginate_multiple_results() $this->assertEquals('Jane Doe', $results->items()[0]->name); } - /** @test */ public function it_can_perform_a_fuzzy_search_across_multiple_fields() { @@ -103,7 +99,6 @@ public function it_can_perform_a_fuzzy_search_across_multiple_fields() $this->assertEquals('Jane Doe', $results[0]->name); } - /** @test */ public function it_can_order_a_fuzzy_search_by_one_field() { @@ -118,7 +113,6 @@ public function it_can_order_a_fuzzy_search_by_one_field() $this->assertEquals('Jane Doe', $results[1]->name); } - /** @test */ public function it_can_order_a_fuzzy_search_by_multiple_fields() { @@ -133,7 +127,6 @@ public function it_can_order_a_fuzzy_search_by_multiple_fields() $this->assertEquals('Jane Doe', $results[1]->name); } - /** @test */ public function it_can_perform_an_eloquent_fuzzy_search() { @@ -147,7 +140,7 @@ public function it_can_perform_an_eloquent_fuzzy_search() /** @test */ public function it_can_perform_an_eloquent_fuzzy_or_search() { - $results = User::whereFuzzy(function ($query) { + $results = User::whereFuzzy(function($query) { $query->orWhereFuzzy('name', 'jndoe'); $query->orWhereFuzzy('nickname', 'jndoe'); }) @@ -155,10 +148,11 @@ public function it_can_perform_an_eloquent_fuzzy_or_search() $this->assertEquals('John Doe', $results->first()->name); } + /** @test */ public function it_can_perform_an_eloquent_fuzzy_or_search_with_order() { - $results = User::whereFuzzy(function ($query) { + $results = User::whereFuzzy(function($query) { $query->orWhereFuzzy('name', 'jad'); $query->orWhereFuzzy('nickname', 'jndoe'); }) @@ -171,7 +165,7 @@ public function it_can_perform_an_eloquent_fuzzy_or_search_with_order() /** @test */ public function it_can_perform_an_eloquent_fuzzy_and_search_with_fuzzy_order() { - $results = User::whereFuzzy(function ($query) { + $results = User::whereFuzzy(function($query) { $query->whereFuzzy('name', 'jad'); $query->whereFuzzy('nickname', 'jndoe'); }) diff --git a/tests/User.php b/tests/User.php index 2ac9e86..9f44eeb 100644 --- a/tests/User.php +++ b/tests/User.php @@ -6,7 +6,6 @@ class User extends Model { - /** * Disable timestamps. * diff --git a/tools/.php-cs-fixer.php b/tools/.php-cs-fixer.php new file mode 100644 index 0000000..7378429 --- /dev/null +++ b/tools/.php-cs-fixer.php @@ -0,0 +1,52 @@ +notPath(dirname(__DIR__, 1) . '/bootstrap/*') + ->notPath(dirname(__DIR__, 1) . '/storage/*') + ->notPath(dirname(__DIR__, 1) . '/vendor') + ->notPath(dirname(__DIR__, 1) . '/resources/view/mail/*') + ->in([ + dirname(__DIR__, 1) . '/src', + dirname(__DIR__, 1) . '/tests', + ]) + ->name('*.php') + ->notName('*.blade.php') + ->ignoreDotFiles(true) + ->ignoreVCS(true); + +return (new PhpCsFixer\Config()) + ->setRules([ + '@PSR2' => true, + 'array_syntax' => ['syntax' => 'short'], + 'ordered_imports' => ['sort_algorithm' => 'length'], + 'no_unused_imports' => true, + 'not_operator_with_successor_space' => true, + 'trailing_comma_in_multiline' => ['elements' => ['arrays']], + 'phpdoc_scalar' => true, + 'unary_operator_spaces' => true, + 'binary_operator_spaces' => [ + 'operators' => ['=' => 'align', '=>' => 'align'], + ], + 'blank_line_before_statement' => [ + 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], + ], + 'phpdoc_single_line_var_spacing' => true, + 'phpdoc_var_without_name' => true, + 'class_attributes_separation' => [ + 'elements' => [ + 'method' => 'one', + 'property' => 'one', + ], + ], + 'method_argument_space' => [ + 'on_multiline' => 'ensure_fully_multiline', + 'keep_multiple_spaces_after_comma' => true, + ], + 'method_chaining_indentation' => true, + 'object_operator_without_whitespace' => true, + 'no_superfluous_phpdoc_tags' => true, + 'function_declaration' => [ + 'closure_function_spacing' => 'none', + ], + ]) + ->setFinder($finder);