Merge pull request #450 from shikorism/feature/312-tag-normalize
正規化したタグ名で検索
This commit is contained in:
commit
43ed36ccb7
@ -5,8 +5,8 @@ FROM php:7.3-apache
|
|||||||
ENV APACHE_DOCUMENT_ROOT /var/www/html/public
|
ENV APACHE_DOCUMENT_ROOT /var/www/html/public
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y git libpq-dev unzip \
|
&& apt-get install -y git libpq-dev unzip libicu-dev \
|
||||||
&& docker-php-ext-install pdo_pgsql \
|
&& docker-php-ext-install pdo_pgsql intl \
|
||||||
&& pecl install xdebug \
|
&& pecl install xdebug \
|
||||||
&& curl -sS https://getcomposer.org/installer | php \
|
&& curl -sS https://getcomposer.org/installer | php \
|
||||||
&& mv composer.phar /usr/local/bin/composer \
|
&& mv composer.phar /usr/local/bin/composer \
|
||||||
|
61
app/Console/Commands/NormalizeTags.php
Normal file
61
app/Console/Commands/NormalizeTags.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Tag;
|
||||||
|
use App\Utilities\Formatter;
|
||||||
|
use DB;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class NormalizeTags extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'tissue:tag:normalize';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Normalize tags';
|
||||||
|
|
||||||
|
private $formatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new command instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Formatter $formatter)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->formatter = $formatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$start = hrtime(true);
|
||||||
|
|
||||||
|
DB::transaction(function () {
|
||||||
|
/** @var Tag $tag */
|
||||||
|
foreach (Tag::query()->cursor() as $tag) {
|
||||||
|
$normalizedName = $this->formatter->normalizeTagName($tag->name);
|
||||||
|
$this->line("{$tag->name} : {$normalizedName}");
|
||||||
|
$tag->normalized_name = $normalizedName;
|
||||||
|
$tag->save();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$elapsed = (hrtime(true) - $start) / 1e+9;
|
||||||
|
$this->info("Done! ({$elapsed} sec)");
|
||||||
|
}
|
||||||
|
}
|
@ -4,20 +4,30 @@ namespace App\Http\Controllers;
|
|||||||
|
|
||||||
use App\Ejaculation;
|
use App\Ejaculation;
|
||||||
use App\Tag;
|
use App\Tag;
|
||||||
|
use App\Utilities\Formatter;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class SearchController extends Controller
|
class SearchController extends Controller
|
||||||
{
|
{
|
||||||
|
/** @var Formatter */
|
||||||
|
private $formatter;
|
||||||
|
|
||||||
|
public function __construct(Formatter $formatter)
|
||||||
|
{
|
||||||
|
$this->formatter = $formatter;
|
||||||
|
}
|
||||||
|
|
||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
$inputs = $request->validate([
|
$inputs = $request->validate([
|
||||||
'q' => 'required'
|
'q' => 'required'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$q = $this->normalizeQuery($inputs['q']);
|
||||||
$results = Ejaculation::query()
|
$results = Ejaculation::query()
|
||||||
->whereHas('tags', function ($query) use ($inputs) {
|
->whereHas('tags', function ($query) use ($q) {
|
||||||
$query->where('name', 'like', "%{$inputs['q']}%");
|
$query->where('normalized_name', 'like', "%{$q}%");
|
||||||
})
|
})
|
||||||
->whereHas('user', function ($query) {
|
->whereHas('user', function ($query) {
|
||||||
$query->where('is_protected', false);
|
$query->where('is_protected', false);
|
||||||
@ -41,11 +51,17 @@ class SearchController extends Controller
|
|||||||
'q' => 'required'
|
'q' => 'required'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$q = $this->normalizeQuery($inputs['q']);
|
||||||
$results = Tag::query()
|
$results = Tag::query()
|
||||||
->where('name', 'like', "%{$inputs['q']}%")
|
->where('normalized_name', 'like', "%{$q}%")
|
||||||
->paginate(50)
|
->paginate(50)
|
||||||
->appends($inputs);
|
->appends($inputs);
|
||||||
|
|
||||||
return view('search.relatedTag')->with(compact('inputs', 'results'));
|
return view('search.relatedTag')->with(compact('inputs', 'results'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function normalizeQuery(string $query): string
|
||||||
|
{
|
||||||
|
return $this->formatter->normalizeTagName($query);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
10
app/Tag.php
10
app/Tag.php
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
|
use App\Utilities\Formatter;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class Tag extends Model
|
class Tag extends Model
|
||||||
@ -15,6 +16,15 @@ class Tag extends Model
|
|||||||
'name'
|
'name'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
protected static function boot()
|
||||||
|
{
|
||||||
|
parent::boot();
|
||||||
|
|
||||||
|
self::creating(function (Tag $tag) {
|
||||||
|
$tag->normalized_name = app(Formatter::class)->normalizeTagName($tag->name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public function ejaculations()
|
public function ejaculations()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany('App\Ejaculation')->withTimestamps();
|
return $this->belongsToMany('App\Ejaculation')->withTimestamps();
|
||||||
|
@ -132,4 +132,12 @@ class Formatter
|
|||||||
|
|
||||||
return $bytes . 'B';
|
return $bytes . 'B';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function normalizeTagName(string $name)
|
||||||
|
{
|
||||||
|
$name = \Normalizer::normalize($name, \Normalizer::FORM_KC);
|
||||||
|
$name = mb_strtolower($name);
|
||||||
|
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,12 @@
|
|||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2",
|
"php": "^7.2",
|
||||||
|
"ext-dom": "*",
|
||||||
|
"ext-intl": "*",
|
||||||
|
"ext-json": "*",
|
||||||
|
"ext-libxml": "*",
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"ext-pdo": "*",
|
||||||
"anhskohbo/no-captcha": "^3.0",
|
"anhskohbo/no-captcha": "^3.0",
|
||||||
"doctrine/dbal": "^2.9",
|
"doctrine/dbal": "^2.9",
|
||||||
"erusev/parsedown": "^1.7",
|
"erusev/parsedown": "^1.7",
|
||||||
|
104
composer.lock
generated
104
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "2c0bd951a595d4856079c5a13a72e651",
|
"content-hash": "1bba68b609be6a0dcdaf05d72e8eb759",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "anhskohbo/no-captcha",
|
"name": "anhskohbo/no-captcha",
|
||||||
@ -394,20 +394,6 @@
|
|||||||
"sqlserver",
|
"sqlserver",
|
||||||
"sqlsrv"
|
"sqlsrv"
|
||||||
],
|
],
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://www.doctrine-project.org/sponsorship.html",
|
|
||||||
"type": "custom"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://www.patreon.com/phpdoctrine",
|
|
||||||
"type": "patreon"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal",
|
|
||||||
"type": "tidelift"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2020-04-20T17:19:26+00:00"
|
"time": "2020-04-20T17:19:26+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1617,12 +1603,6 @@
|
|||||||
"transform",
|
"transform",
|
||||||
"write"
|
"write"
|
||||||
],
|
],
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://github.com/sponsors/nyamsprod",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2020-03-17T15:15:35+00:00"
|
"time": "2020-03-17T15:15:35+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1707,12 +1687,6 @@
|
|||||||
"sftp",
|
"sftp",
|
||||||
"storage"
|
"storage"
|
||||||
],
|
],
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://offset.earth/frankdejonge",
|
|
||||||
"type": "other"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2020-05-18T15:13:39+00:00"
|
"time": "2020-05-18T15:13:39+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1839,16 +1813,6 @@
|
|||||||
"logging",
|
"logging",
|
||||||
"psr-3"
|
"psr-3"
|
||||||
],
|
],
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://github.com/Seldaek",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
|
|
||||||
"type": "tidelift"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2020-05-22T08:12:19+00:00"
|
"time": "2020-05-22T08:12:19+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -5875,12 +5839,6 @@
|
|||||||
"profiler",
|
"profiler",
|
||||||
"webprofiler"
|
"webprofiler"
|
||||||
],
|
],
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://github.com/barryvdh",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2020-05-05T10:53:32+00:00"
|
"time": "2020-05-05T10:53:32+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -5952,12 +5910,6 @@
|
|||||||
"phpstorm",
|
"phpstorm",
|
||||||
"sublime"
|
"sublime"
|
||||||
],
|
],
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://github.com/barryvdh",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2020-04-22T09:57:26+00:00"
|
"time": "2020-04-22T09:57:26+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -6063,16 +6015,6 @@
|
|||||||
"ssl",
|
"ssl",
|
||||||
"tls"
|
"tls"
|
||||||
],
|
],
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://packagist.com",
|
|
||||||
"type": "custom"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
|
|
||||||
"type": "tidelift"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2020-04-08T08:27:21+00:00"
|
"time": "2020-04-08T08:27:21+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -6154,16 +6096,6 @@
|
|||||||
"dependency",
|
"dependency",
|
||||||
"package"
|
"package"
|
||||||
],
|
],
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://packagist.com",
|
|
||||||
"type": "custom"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
|
|
||||||
"type": "tidelift"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2020-05-06T08:28:10+00:00"
|
"time": "2020-05-06T08:28:10+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -6536,12 +6468,6 @@
|
|||||||
"flare",
|
"flare",
|
||||||
"reporting"
|
"reporting"
|
||||||
],
|
],
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://www.patreon.com/spatie",
|
|
||||||
"type": "patreon"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2020-03-02T15:52:04+00:00"
|
"time": "2020-03-02T15:52:04+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -8088,12 +8014,6 @@
|
|||||||
"highlight.php",
|
"highlight.php",
|
||||||
"syntax"
|
"syntax"
|
||||||
],
|
],
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://github.com/allejo",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2020-03-02T05:59:21+00:00"
|
"time": "2020-03-02T05:59:21+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -8758,16 +8678,6 @@
|
|||||||
"parser",
|
"parser",
|
||||||
"validator"
|
"validator"
|
||||||
],
|
],
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://github.com/Seldaek",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "https://tidelift.com/funding/github/packagist/seld/jsonlint",
|
|
||||||
"type": "tidelift"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2020-04-30T19:05:18+00:00"
|
"time": "2020-04-30T19:05:18+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -9032,8 +8942,8 @@
|
|||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Arne Blankerts",
|
"name": "Arne Blankerts",
|
||||||
"email": "arne@blankerts.de",
|
"role": "Developer",
|
||||||
"role": "Developer"
|
"email": "arne@blankerts.de"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
|
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
|
||||||
@ -9097,7 +9007,13 @@
|
|||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": {
|
"platform": {
|
||||||
"php": "^7.2"
|
"php": "^7.2",
|
||||||
|
"ext-dom": "*",
|
||||||
|
"ext-intl": "*",
|
||||||
|
"ext-json": "*",
|
||||||
|
"ext-libxml": "*",
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"ext-pdo": "*"
|
||||||
},
|
},
|
||||||
"platform-dev": [],
|
"platform-dev": [],
|
||||||
"plugin-api-version": "1.1.0"
|
"plugin-api-version": "1.1.0"
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddNormalizedNameToTags extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('tags', function (Blueprint $table) {
|
||||||
|
$table->string('normalized_name')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('tags', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('normalized_name');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -70,4 +70,30 @@ class FormatterTest extends TestCase
|
|||||||
$formatter->profileImageSrcSet($profileImageProvider, 128, 2)
|
$formatter->profileImageSrcSet($profileImageProvider, 128, 2)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideNormalizeTagName
|
||||||
|
*/
|
||||||
|
public function testNormalizeTagName($input, $expected)
|
||||||
|
{
|
||||||
|
$formatter = new Formatter();
|
||||||
|
|
||||||
|
$normalized = $formatter->normalizeTagName($input);
|
||||||
|
$this->assertSame($expected, $normalized);
|
||||||
|
$this->assertSame($expected, $formatter->normalizeTagName($normalized));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideNormalizeTagName()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'LowerCase' => ['example', 'example'],
|
||||||
|
'UpperCase' => ['EXAMPLE', 'example'],
|
||||||
|
'HalfWidthKana' => ['ティッシュ', 'ティッシュ'],
|
||||||
|
'FullWidthAlphabet' => ['Tissue', 'tissue'],
|
||||||
|
'組み文字1' => ['13㎝', '13cm'],
|
||||||
|
'組み文字2' => ['13㌢㍍', '13センチメートル'],
|
||||||
|
'Script' => ['ℬ𝒶𝒷𝓊𝓂𝒾', 'babumi'],
|
||||||
|
'NFD' => ['オカズ', 'オカズ'],
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user