Compare commits

...

3 Commits

Author SHA1 Message Date
Maik Müller ac2ecfac48 let's test live 2021-09-19 21:32:45 +02:00
Maik Müller 2f0096cab2 remove console.log 2021-09-19 14:10:18 +02:00
Maik Müller bbc25d9af0 basically working 2021-09-19 03:47:34 +02:00
21 changed files with 399 additions and 84 deletions

2
.github/FUNDING.yml vendored
View File

@ -1 +1 @@
github: :vendor_name
github: moltox

View File

@ -1,6 +1,6 @@
# Changelog
All notable changes to `:package_name` will be documented in this file.
All notable changes to `column-multi-filter` will be documented in this file.
## 1.0.0 - 202X-XX-XX

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) :vendor_name <author@domain.com>
Copyright (c) moltox <author@domain.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,4 +1,4 @@
# :package_description
# Filters multiple columns
[![Latest Version on Packagist](https://img.shields.io/packagist/v/:vendor_slug/:package_slug.svg?style=flat-square)](https://packagist.org/packages/:vendor_slug/:package_slug)
[![GitHub Tests Action Status](https://img.shields.io/github/workflow/status/:vendor_slug/:package_slug/run-tests?label=tests)](https://github.com/:vendor_slug/:package_slug/actions?query=workflow%3Arun-tests+branch%3Amain)
@ -8,7 +8,7 @@
---
This repo can be used to scaffold a Laravel package. Follow these steps to get started:
1. Press the "Use template" button at the top of this repo to create a new repo with the contents of this skeleton
1. Press the "Use template" button at the top of this repo to create a new repo with the contents of this column-multi-filter
2. Run "php ./configure.php" to run a script that will replace all placeholders throughout all the files
3. Remove this block of text.
4. Have fun creating your package.
@ -19,7 +19,7 @@ This is where your description should go. Limit it to a paragraph or two. Consid
## Support us
[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/:package_name.jpg?t=1" width="419px" />](https://spatie.be/github-ad-click/:package_name)
[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/column-multi-filter.jpg?t=1" width="419px" />](https://spatie.be/github-ad-click/column-multi-filter)
We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).
@ -36,13 +36,13 @@ composer require :vendor_slug/:package_slug
You can publish and run the migrations with:
```bash
php artisan vendor:publish --provider="VendorName\Skeleton\SkeletonServiceProvider" --tag=":package_slug-migrations"
php artisan vendor:publish --provider="VendorName\ColumnMultiFilter\ColumnMultiFilterServiceProvider" --tag=":package_slug-migrations"
php artisan migrate
```
You can publish the config file with:
```bash
php artisan vendor:publish --provider="VendorName\Skeleton\SkeletonServiceProvider" --tag=":package_slug-config"
php artisan vendor:publish --provider="VendorName\ColumnMultiFilter\ColumnMultiFilterServiceProvider" --tag=":package_slug-config"
```
This is the contents of the published config file:
@ -55,8 +55,8 @@ return [
## Usage
```php
$skeleton = new VendorName\Skeleton();
echo $skeleton->echoPhrase('Hello, VendorName!');
$column-multi-filter = new VendorName\ColumnMultiFilter();
echo $column-multi-filter->echoPhrase('Hello, VendorName!');
```
## Testing
@ -79,7 +79,7 @@ Please review [our security policy](../../security/policy) on how to report secu
## Credits
- [:author_name](https://github.com/:author_username)
- [Maik Mueller](https://github.com/moltox)
- [All Contributors](../../contributors)
## License

View File

@ -1,17 +1,17 @@
{
"name": ":vendor_slug/:package_slug",
"description": ":package_description",
"name": "moltox/column-multi-filter",
"description": "Filters multiple columns",
"keywords": [
":vendor_name",
"moltox",
"laravel",
":package_slug"
"column-multi-filter"
],
"homepage": "https://github.com/:vendor_slug/:package_slug",
"homepage": "https://github.com/moltox/column-multi-filter",
"license": "MIT",
"authors": [
{
"name": ":author_name",
"email": "author@domain.com",
"name": "Maik Mueller",
"email": "maik@muelleronline.org",
"role": "Developer"
}
],
@ -30,13 +30,13 @@
},
"autoload": {
"psr-4": {
"VendorName\\Skeleton\\": "src",
"VendorName\\Skeleton\\Database\\Factories\\": "database/factories"
"Moltox\\ColumnMultiFilter\\": "src",
"Moltox\\ColumnMultiFilter\\Database\\Factories\\": "database/factories"
}
},
"autoload-dev": {
"psr-4": {
"VendorName\\Skeleton\\Tests\\": "tests"
"Moltox\\ColumnMultiFilter\\Tests\\": "tests"
}
},
"scripts": {
@ -49,10 +49,10 @@
"extra": {
"laravel": {
"providers": [
"VendorName\\Skeleton\\SkeletonServiceProvider"
"Moltox\\ColumnMultiFilter\\ColumnMultiFilterServiceProvider"
],
"aliases": {
"Skeleton": "VendorName\\Skeleton\\SkeletonFacade"
"ColumnMultiFilter": "Moltox\\ColumnMultiFilter\\ColumnMultiFilterFacade"
}
}
},

View File

@ -0,0 +1,20 @@
<?php
use Moltox\ColumnMultiFilter\Classes\Filter;
return [
'default' => [
'type' => Filter::LIKE,
'logic' => Filter::LOGIC_VALUE
],
'uri_relation_column_separator' => '.',
'log_enabled' => true,
'log_channel' => 'default',
// Types
// Search
];

View File

@ -1,5 +0,0 @@
<?php
// config for VendorName/Skeleton
return [
];

57
config/types.md Normal file
View File

@ -0,0 +1,57 @@
# Regex tmpl
([\w_-]*)\[([\w]*)\]=([\w_-]*)
# Search #
The search filter supports exact, partial, start, end, and word_start matching strategies:
partial strategy uses LIKE %text% to search for fields that contain text.
start strategy uses LIKE text% to search for fields that start with text.
end strategy uses LIKE %text to search for fields that end with text.
word_start strategy uses LIKE text% OR LIKE % text% to search for fields that contain words starting with text.
Syntax: ?property[]=foo&property[]=bar
optional brackets!
([\w_-]*)(\[(i?exact|i?partial|i?start|i?end|i?word_start)?\])?=([\w_\-\d]*) /gm
# Date Filter #
The date filter allows to filter a collection by date intervals.
Syntax: ?property[<after|before|strictly_after|strictly_before>]=value
The value can take any date format supported by the \DateTime constructor.
The after and before filters will filter including the value whereas strictly_after and strictly_before will
filter excluding the value.
# Boolean Fitler #
The boolean filter allows you to search on boolean fields and values.
Syntax: ?property=<true|false|1|0>
# Numeric Filter #
The numeric filter allows you to search on numeric fields and values.
Syntax: ?property=<int|bigint|decimal...>
# Range Filter #
The range filter allows you to filter by a value lower than, greater than, lower than or equal, greater than or equal and between two values.
Syntax: ?property[<lt|gt|lte|gte|between>]=value
# Exists Filter
The exists filter allows you to select items based on a nullable field value. It will also check the emptiness of a collection association.
Syntax: ?exists[property]=<true|false|1|0>

View File

@ -69,11 +69,11 @@ function remove_prefix(string $prefix, string $content): string {
}
function replaceForWindows(): array {
return preg_split('/\\r\\n|\\r|\\n/', run('dir /S /B * | findstr /v /i .github | findstr /v /i vendor | findstr /v /i '.basename(__FILE__).' | findstr /r /i /M /F:/ ":author :vendor :package VendorName skeleton vendor_name vendor_slug author@domain.com"'));
return preg_split('/\\r\\n|\\r|\\n/', run('dir /S /B * | findstr /v /i .github | findstr /v /i vendor | findstr /v /i '.basename(__FILE__).' | findstr /r /i /M /F:/ ":author :vendor :package VendorName column-multi-filter vendor_name vendor_slug author@domain.com"'));
}
function replaceForAllOtherOSes(): array {
return explode(PHP_EOL, run('grep -E -r -l -i ":author|:vendor|:package|VendorName|skeleton|vendor_name|vendor_slug|author@domain.com" --exclude-dir=vendor ./* ./.github/* | grep -v ' . basename(__FILE__)));
return explode(PHP_EOL, run('grep -E -r -l -i ":author|:vendor|:package|VendorName|column-multi-filter|vendor_name|vendor_slug|author@domain.com" --exclude-dir=vendor ./* ./.github/* | grep -v ' . basename(__FILE__)));
}
$gitName = run('git config user.name');
@ -121,26 +121,26 @@ $files = (str_starts_with(strtoupper(PHP_OS), 'WIN') ? replaceForWindows() : rep
foreach ($files as $file) {
replace_in_file($file, [
':author_name' => $authorName,
':author_username' => $authorUsername,
'Maik Mueller' => $authorName,
'moltox' => $authorUsername,
'author@domain.com' => $authorEmail,
':vendor_name' => $vendorName,
'moltox' => $vendorName,
':vendor_slug' => $vendorSlug,
'VendorName' => $vendorNamespace,
':package_name' => $packageName,
'column-multi-filter' => $packageName,
':package_slug' => $packageSlug,
'Skeleton' => $className,
'skeleton' => $packageSlug,
':package_description' => $description,
'ColumnMultiFilter' => $className,
'column-multi-filter' => $packageSlug,
'Filters multiple columns' => $description,
]);
match (true) {
str_contains($file, 'src/Skeleton.php') => rename($file, './src/' . $className . '.php'),
str_contains($file, 'src/SkeletonServiceProvider.php') => rename($file, './src/' . $className . 'ServiceProvider.php'),
str_contains($file, 'src/SkeletonFacade.php') => rename($file, './src/' . $className . 'Facade.php'),
str_contains($file, 'src/Commands/SkeletonCommand.php') => rename($file, './src/Commands/' . $className . 'Command.php'),
str_contains($file, 'database/migrations/create_skeleton_table.php.stub') => rename($file, './database/migrations/create_' . $packageSlugWithoutPrefix . '_table.php.stub'),
str_contains($file, 'config/skeleton.php') => rename($file, './config/' . $packageSlugWithoutPrefix . '.php'),
str_contains($file, 'src/ColumnMultiFilter.php') => rename($file, './src/' . $className . '.php'),
str_contains($file, 'src/ColumnMultiFilterServiceProvider.php') => rename($file, './src/' . $className . 'ServiceProvider.php'),
str_contains($file, 'src/ColumnMultiFilterFacade.php') => rename($file, './src/' . $className . 'Facade.php'),
str_contains($file, 'src/Commands/ColumnMultiFilterCommand.php') => rename($file, './src/Commands/' . $className . 'Command.php'),
str_contains($file, 'database/migrations/create_column_multi_filter_table.php.stub') => rename($file, './database/migrations/create_' . $packageSlugWithoutPrefix . '_table.php.stub'),
str_contains($file, 'config/column-multi-filter.php') => rename($file, './config/' . $packageSlugWithoutPrefix . '.php'),
default => [],
};
}

View File

@ -1,6 +1,6 @@
<?php
namespace VendorName\Skeleton\Database\Factories;
namespace VendorName\ColumnMultiFilter\Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;

View File

@ -8,7 +8,7 @@ return new class extends Migration
{
public function up()
{
Schema::create('skeleton_table', function (Blueprint $table) {
Schema::create('column-multi-filter_table', function (Blueprint $table) {
$table->id();
// add fields

17
src/Classes/Filter.php Normal file
View File

@ -0,0 +1,17 @@
<?php
namespace Moltox\ColumnMultiFilter\Classes;
class Filter
{
public const BETWEEN = "between-filter";
public const BOOL = "boolean-filter";
public const LIKE = "like-filter";
public const WHERE = "where-filter";
public const LOGIC_AND = "and";
public const LOGIC_OR = "or";
public const LOGIC_VALUE = "logic-value"; // get logic by parsing value (ie looking for " etc
}

203
src/ColumnMultiFilter.php Normal file
View File

@ -0,0 +1,203 @@
<?php
namespace Moltox\ColumnMultiFilter;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Moltox\ColumnMultiFilter\Classes\Filter;
use Moltox\ColumnMultiFilter\Exceptions\ColumnMultiFilterException;
trait ColumnMultiFilter
{
/**
* @param Builder $query
*
* @return Builder
*/
public function scopeMultiFilter(Builder $query): Builder
{
//Log::debug("Request: " . print_r(request()->all(), true));
if (request()->has('filter')) {
$filters = request()->get('filter');
$this->log('Filter found: '.print_r($filters, true));
return $this->filterQueryBuilder($query, $filters);
}
return $query;
}
protected function filterQueryBuilder(Builder $query, array $filters)
{
foreach ($filters as $column => $value) {
if ($this->isColumnFilterable($column)) {
$this->log(sprintf('Filterable column found: %30s with value: %s', $column, $value));
$filterType = $this->getFilterType($column);
$logic = $this->getLogic($column);
$operator = $this->getOperator($filterType);
$value = trim($value);
$this->addFilter($query, $column, $operator, $value, $logic);
}
}
return $query;
}
protected function isColumnFilterable(string $column)
{
return Arr::has($this->multiFilter, $column);
}
protected function getFilterType($column)
{
$type = config('column-multi-filter.default.type', Filter::LIKE);
if (Arr::has($this->multiFilter[$column], 'type')) {
$type = $this->multiFilter[$column]['type'];
}
return $type;
}
/**
*
* Returns the where logic for the given column
*
* @param $column
*
* @return \Illuminate\Config\Repository|\Illuminate\Contracts\Foundation\Application|mixed
*/
protected function getLogic($column)
{
$logic = config('column-multi-filter.default.logic', Filter::LIKE);
if (Arr::has($this->multiFilter[$column], 'logic')) {
$logic = $this->multiFilter[$column]['logic'];
}
return $logic;
}
protected function getOperator($type)
{
return match ($type) {
Filter::LIKE => "like",
default => "=",
};
}
/**
*
* @throws ColumnMultiFilterException
*/
protected function addFilter(Builder $query, string $column, string $operator, mixed $value, mixed $logic)
{
if ($this->isColumnRelational($column)) {
$exp = explode(config('column-multi-filter.seperator', '.'), $column);
if (count($exp) > 2) {
throw new ColumnMultiFilterException($column, 3);
} else {
if (count($exp) === 2) {
list ($relation, $column) = $exp;
$query->whereHas(Str::camel($relation), function ($qb) use ($column, $operator, $value, $logic) {
/**
* @var Builder $qb
*/
$qb = $this->addWhereIfValid($qb, $column, $operator, $value, $logic);
});
}
}
} else {
$query = $this->addWhereIfValid($query, $column, $operator, $value, $logic);
}
return $query;
}
/**
* @param string $column
*
* @return bool
*/
private function isColumnRelational(string $column): bool
{
return count($this->splitRelatedColumn($column)) > 1;
}
private function splitRelatedColumn($relationColumn): array
{
return explode(config('column-multi-sort.uri_relation_column_separator', '.'), $relationColumn);
}
private function addWhereIfValid(Builder $query, string $column, string $operator, string $value, string $logic)
{
if (trim($value) === '') {
return $query;
}
$query->where($column, $operator, $value, $logic);
return $query;
}
private function log(string $message, $logLevel = 'info', $force = false)
{
if (config('column-multi-filter.log_enabled', false) || $force) {
Log::channel(env('LOG_CHANNEL', 'stack'))->log($logLevel, '[MultiSort] '.$message);
}
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Moltox\ColumnMultiFilter;
use Illuminate\Support\Facades\Facade;
/**
* @see \moltox\ColumnMultiFilter\ColumnMultiFilter
*/
class ColumnMultiFilterFacade extends Facade
{
protected static function getFacadeAccessor()
{
return 'column-multi-filter';
}
}

View File

@ -1,12 +1,11 @@
<?php
namespace VendorName\Skeleton;
namespace Moltox\ColumnMultiFilter;
use Spatie\LaravelPackageTools\Package;
use Spatie\LaravelPackageTools\PackageServiceProvider;
use VendorName\Skeleton\Commands\SkeletonCommand;
class SkeletonServiceProvider extends PackageServiceProvider
class ColumnMultiFilterServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
@ -16,10 +15,11 @@ class SkeletonServiceProvider extends PackageServiceProvider
* More info: https://github.com/spatie/laravel-package-tools
*/
$package
->name('skeleton')
->name('column-multi-filter')
->hasConfigFile()
->hasViews()
->hasMigration('create_skeleton_table')
->hasCommand(SkeletonCommand::class);
// ->hasViews()
// ->hasMigration('create_column_multi_filter_table')
// ->hasCommand(ColumnMultiFilterCommand::class)
;
}
}

View File

@ -1,12 +1,12 @@
<?php
namespace VendorName\Skeleton\Commands;
namespace Moltox\ColumnMultiFilter\Commands;
use Illuminate\Console\Command;
class SkeletonCommand extends Command
class ColumnMultiFilterCommand extends Command
{
public $signature = 'skeleton';
public $signature = 'column-multi-filter';
public $description = 'My command';

View File

@ -0,0 +1,30 @@
<?php
namespace Moltox\ColumnMultiFilter\Exceptions;
use Exception;
class ColumnMultiFilterException extends Exception
{
public function __construct($message = '', $code = 0, Exception $previous = null)
{
switch ($code) {
case 0:
$message = 'Invalid filter argument.';
break;
case 1:
$message = 'Relation \''.$message.'\' does not exist.';
break;
case 2:
$message = 'Relation \''.$message.'\' is not instance of HasOne or BelongsTo.'; //hasMany
break;
case 3:
$message = 'Too many relations in ' . $message;
}
parent::__construct($message, $code, $previous);
}
}

View File

@ -1,7 +0,0 @@
<?php
namespace VendorName\Skeleton;
class Skeleton
{
}

View File

@ -1,16 +0,0 @@
<?php
namespace VendorName\Skeleton;
use Illuminate\Support\Facades\Facade;
/**
* @see \VendorName\Skeleton\Skeleton
*/
class SkeletonFacade extends Facade
{
protected static function getFacadeAccessor()
{
return 'skeleton';
}
}

View File

@ -1,5 +1,5 @@
<?php
use VendorName\Skeleton\Tests\TestCase;
use VendorName\ColumnMultiFilter\Tests\TestCase;
uses(TestCase::class)->in(__DIR__);

View File

@ -1,10 +1,10 @@
<?php
namespace VendorName\Skeleton\Tests;
namespace Moltox\ColumnMultiFilter\Tests;
use Illuminate\Database\Eloquent\Factories\Factory;
use Orchestra\Testbench\TestCase as Orchestra;
use VendorName\Skeleton\SkeletonServiceProvider;
use VendorName\ColumnMultiFilter\ColumnMultiFilterServiceProvider;
class TestCase extends Orchestra
{
@ -13,14 +13,14 @@ class TestCase extends Orchestra
parent::setUp();
Factory::guessFactoryNamesUsing(
fn (string $modelName) => 'VendorName\\Skeleton\\Database\\Factories\\'.class_basename($modelName).'Factory'
fn (string $modelName) => 'VendorName\\ColumnMultiFilter\\Database\\Factories\\'.class_basename($modelName).'Factory'
);
}
protected function getPackageProviders($app)
{
return [
SkeletonServiceProvider::class,
ColumnMultiFilterServiceProvider::class,
];
}
@ -29,7 +29,7 @@ class TestCase extends Orchestra
config()->set('database.default', 'testing');
/*
$migration = include __DIR__.'/../database/migrations/create_skeleton_table.php.stub';
$migration = include __DIR__.'/../database/migrations/create_column_multi_filter_table.php.stub';
$migration->up();
*/
}