diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..df55cd7
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,20 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = false
+
+[*.{vue,js,scss}]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+trim_trailing_whitespace = false
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..9af3157
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,11 @@
+* text=auto
+
+/tests export-ignore
+.gitattributes export-ignore
+.gitignore export-ignore
+.scrutinizer.yml export-ignore
+.travis.yml export-ignore
+phpunit.php export-ignore
+phpunit.xml.dist export-ignore
+phpunit.xml export-ignore
+.php_cs export-ignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..497c4e2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+.idea
+*.DS_Store
+/vendor
+/coverage
+sftp-config.json
+composer.lock
+.subsplit
+.php_cs.cache
diff --git a/.php_cs b/.php_cs
new file mode 100644
index 0000000..55a35ff
--- /dev/null
+++ b/.php_cs
@@ -0,0 +1,27 @@
+
+
+This source file is subject to the MIT license that is bundled.
+EOF;
+
+return PhpCsFixer\Config::create()
+ ->setRiskyAllowed(true)
+ ->setRules(array(
+ '@Symfony' => true,
+ 'header_comment' => array('header' => $header),
+ 'array_syntax' => array('syntax' => 'short'),
+ 'ordered_imports' => true,
+ 'no_useless_else' => true,
+ 'no_useless_return' => true,
+ 'php_unit_construct' => true,
+ 'php_unit_strict' => true,
+ ))
+ ->setFinder(
+ PhpCsFixer\Finder::create()
+ ->exclude('vendor')
+ ->in(__DIR__)
+ )
+;
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5a3e6e2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,28 @@
+
laravel-eloquent-filter
+
+ An Eloquent way to filter Eloquent Models.
+
+
+## Installing
+
+```shell
+$ composer require huangbule/laravel-eloquent-filter -vvv
+```
+
+## Usage
+
+TODO
+
+## Contributing
+
+You can contribute in one of three ways:
+
+1. File bug reports using the [issue tracker](https://github.com/huangbule/laravel-eloquent-filter/issues).
+2. Answer questions or fix bugs on the [issue tracker](https://github.com/huangbule/laravel-eloquent-filter/issues).
+3. Contribute new features or update the wiki.
+
+_The code contribution process is not very formal. You just need to make sure that you follow the PSR-0, PSR-1, and PSR-2 coding guidelines. Any new code contributions must be accompanied by unit tests where applicable._
+
+## License
+
+MIT
\ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..497f33e
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,27 @@
+{
+ "name": "huangbule/laravel-eloquent-filter",
+ "description": "An Eloquent way to filter Eloquent Models",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "huangbule",
+ "email": "3314308761@qq.com"
+ }
+ ],
+ "require": {},
+ "autoload": {
+ "psr-4": {
+ "Huangbule\\LaravelEloquentFilter\\": "src"
+ }
+ },
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Huangbule\\LaravelEloquentFilter\\EloquentFilterProvider"
+ ],
+ "aliases": {
+
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/config/filter.php b/config/filter.php
new file mode 100644
index 0000000..209c010
--- /dev/null
+++ b/config/filter.php
@@ -0,0 +1,11 @@
+ '$like',
+
+ 'rule' => [
+ 'uuid' => 'uuid|$eq',
+ 'department_name' => '#department',
+ ]
+
+];
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..e47284c
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,21 @@
+
+
+
+
+ ./tests/
+
+
+
+
+ src/
+
+
+
diff --git a/src/.gitkeep b/src/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/src/Contracts/Ipreprocess.php b/src/Contracts/Ipreprocess.php
new file mode 100644
index 0000000..d9007ad
--- /dev/null
+++ b/src/Contracts/Ipreprocess.php
@@ -0,0 +1,7 @@
+mergeConfigFrom(__DIR__.'/../config/filter.php', 'filter');
+ }
+
+ public function boot()
+ {
+ if ($this->app->runningInConsole()) {
+ $this->publishes([
+ __DIR__.'/../config/filter.php' => config_path('filter.php'),
+ ], 'filter');
+
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/Exceptions/InvalidArgumentException.php b/src/Exceptions/InvalidArgumentException.php
new file mode 100644
index 0000000..7701c40
--- /dev/null
+++ b/src/Exceptions/InvalidArgumentException.php
@@ -0,0 +1,8 @@
+input()
+ * @param array $arr_filter
+ */
+ public function scopeFilter($query, $param = [], $arr_filter = [])
+ {
+ foreach ($arr_filter as $key => $column) {
+ if (Str::contains($column, ':')) {
+ list($column, $operator) = explode(":", $column);
+ } else {
+ $operator = config('filter.rule.' . $column);
+ }
+ $operator ??= config('filter.default');
+ $arr_operator = explode("|", $operator);
+
+ $relation = null;
+ $filter_command = config('filter.default');
+
+ foreach ($arr_operator as $command) {
+ if (!$command)
+ throw new InvalidArgumentException($arr_filter[$key] . " format not valid");
+
+ //check rename or not
+ if (isset($this->renamedFilterFields[$column]) && !empty($param[$column])) {
+ $old_column = $column;
+ $column = $this->renamedFilterFields[$column];
+ $param[$column] = $param[$old_column];
+ }
+
+ if (Str::startsWith($command, '$')) {
+ $filter_command = $command;
+ } elseif (Str::startsWith($command, '#')) {
+ $relation = Str::substr($command, 1);
+
+ if (!method_exists(static::class, $relation)) {
+ throw new NotFoundException("'" . get_class(new static()) . "' has not found relation : " . $relation);
+ }
+ } else {
+ $preprocess_macro = static::$preprocessMacros[$command] ?? null;
+ if ($preprocess_macro) {
+ if (!$preprocess_macro instanceof Ipreprocess)
+ throw new NotInstanceOfInterfaceException(get_class($preprocess_macro) . " not implements Ipreprocess ");
+
+ } else {
+ $process_class = Str::beforeLast(__NAMESPACE__, "Traits") . "Preprocess\\" . Str::studly($command . "Preprocess");
+ if (!class_exists($process_class)) {
+ throw new NotFoundException($process_class . " not found");
+ }
+ (new $process_class)->handle($column, $param);
+ }
+ }
+ }
+
+ if ($filter_command) {
+ $filter = Str::of($filter_command . "Filter")->substr(1)->studly()->__toString();
+ $filter = lcfirst($filter);
+ $macro = static::$filterMacros[$filter] ?? null;
+ if ($macro) {
+ if ($macro instanceof \Closure) {
+ $macro = $macro->bindTo(null, static::class);
+ $macro($query, $column, $param);
+ }
+ } else {
+ $reflection_trait = new \ReflectionClass(self::class);
+ $method_exists = $reflection_trait->hasMethod($filter);
+ if (!$method_exists)
+ throw new NotFoundException($filter . " not found");
+
+ if (!empty($param[$column])) {
+ $callback = $this->createFilterClosure($filter, $column, $param);
+ if ($relation) {
+ $query->whereHas($relation, $callback);
+ } else {
+ $callback($query);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private function createFilterClosure($filter, $column, $param)
+ {
+ return function ($qr) use ($filter, $column, $param) {
+ return $this->{$filter}($qr, $column, $param);
+ };
+ }
+
+ private function likeFilter($qr, $column, $param)
+ {
+ return $qr->where($column, 'like', '%' . $param[$column] . '%');
+ }
+
+ private function eqFilter($qr, $column, $param)
+ {
+ return $qr->where($column, $param[$column]);
+ }
+
+ private function neqFilter($qr, $column, $param)
+ {
+ return $qr->where($column, '!=', $param[$column]);
+ }
+
+ private function lteFilter($qr, $column, $param)
+ {
+ return $qr->where($column, '<=', $param[$column]);
+ }
+
+ private function ltFilter($qr, $column, $param)
+ {
+ return $qr->where($column, '<', $param[$column]);
+ }
+
+ private function gteFilter($qr, $column, $param)
+ {
+ return $qr->where($column, '>=', $param[$column]);
+ }
+
+ private function gtFilter($qr, $column, $param)
+ {
+ return $qr->where($column, '>', $param[$column]);
+ }
+
+ private function inFilter($qr, $column, $param)
+ {
+ if (!is_array($arr = $param[$column])) {
+ $arr = explode(',', $arr);
+ }
+ return $qr->whereIn($column, $arr);
+ }
+
+ private function notInFilter($qr, $column, $param)
+ {
+ if (!is_array($arr = $param[$column])) {
+ $arr = explode(',', $arr);
+ }
+ return $qr->whereNotIn($column, $arr);
+ }
+
+ private function betweenFilter($qr, $column, $param)
+ {
+ if (!is_array($arr = $param[$column])) {
+ $arr = explode(',', $arr);
+ }
+ return $qr->whereBetween($column, $arr);
+ }
+
+ private function halfOpenFilter($qr, $column, $param)
+ {
+ if (!is_array($arr = $param[$column])) {
+ $arr = explode(',', $arr);
+ }
+ if (count($arr) != 2)
+ throw new InvalidArgumentException($column . " ");
+ throw_api_exception($column . " corresponding values must be throughput or comma separated.");
+
+ return $qr->where($column, '>=', $arr[0])->where($column, '<', $arr[1]);
+ }
+
+}
diff --git a/tests/.gitkeep b/tests/.gitkeep
new file mode 100644
index 0000000..e69de29