diff --git a/README.md b/README.md
index c7297de..c3278e0 100755
--- a/README.md
+++ b/README.md
@@ -64,7 +64,7 @@ Add the package to your application service providers in `config/app.php`
Publish the package configuration file to your application.
- $ php artisan vendor:publish --provider="Kyslik\ColumnSortable\ColumnSortableServiceProvider" --tag="columnsortable"
+ $ php artisan vendor:publish --provider="Kyslik\ColumnSortable\ColumnSortableServiceProvider" --tag="config"
See configuration file [(`config/columnsortable.php`)](https://github.com/Kyslik/column-sortable/blob/master/src/config/columnsortable.php) yourself and make adjustments as you wish.
@@ -72,22 +72,25 @@ See configuration file [(`config/columnsortable.php`)](https://github.com/Kyslik
Use `Sortable` trait inside your `Eloquent` model(s). Define `$sortable` array (see example code below).
->`Scheme::hasColumn()` is run only when `$sortable` is not defined - less DB hits per request.
+> **Note**: `Scheme::hasColumn()` is run only when `$sortable` is not defined - less DB hits per request.
```
use Kyslik\ColumnSortable\Sortable;
-class User extends Model implements AuthenticatableContract, CanResetPasswordContract {
-
+class User extends Model implements AuthenticatableContract, CanResetPasswordContract
+{
use Authenticatable, CanResetPassword, Sortable;
...
- protected $sortable = ['id',
- 'name',
- 'email',
- 'created_at',
- 'updated_at'];
+ public $sortable = ['id',
+ 'name',
+ 'email',
+ 'created_at',
+ 'updated_at'];
+
+ ...
+}
```
You're set to go.
@@ -110,19 +113,19 @@ Sortablelink blade extension distinguishes between "types" (numeric, amount and
```
'columns' => [
- 'numeric' => [
- 'rows' => ['created_at', 'updated_at', 'level', 'id'],
- 'class' => 'fa fa-sort-numeric'
- ],
- 'amount' => [
- 'rows' => ['price'],
- 'class' => 'fa fa-sort-amount'
- ],
- 'alpha' => [
- 'rows' => ['name', 'description', 'email', 'slug'],
- 'class' => 'fa fa-sort-alpha',
- ],
+ 'numeric' => [
+ 'rows' => ['created_at', 'updated_at', 'level', 'id'],
+ 'class' => 'fa fa-sort-numeric'
],
+ 'amount' => [
+ 'rows' => ['price'],
+ 'class' => 'fa fa-sort-amount'
+ ],
+ 'alpha' => [
+ 'rows' => ['name', 'description', 'email', 'slug'],
+ 'class' => 'fa fa-sort-alpha',
+ ],
+],
```
Rest of the [config file](https://github.com/Kyslik/column-sortable/blob/master/src/config/columnsortable.php) should be crystal clear and I advise you to read it.
@@ -145,25 +148,27 @@ Route::get('users', ['as' => 'users.index', 'uses' => 'HomeController@index']);
public function index(User $user)
{
$users = $user->sortable()->paginate(10);
-
- return view('user.index')->withUsers($users);
+
+ return view('user.index')->withUsers($users);
}
```
-You can set default sort (when nothing is in (URL) query strings yet).
+You can set default sort when nothing is in (URL) query strings yet.
+> **For example**: page is loaded for first time, default order is [configurable](https://github.com/Kyslik/column-sortable/blob/master/src/config/columnsortable.php#L77) (asc)
```
+$users = $user->sortable('name')->paginate(10);
+//generate ->orderBy('name', 'asc')
+
+$users = $user->sortable(['name'])->paginate(10);
//generate ->orderBy('name', 'asc')
-$users = $user->sortable(['name'])->paginate(10); //default order is asc
-//generate ->orderBy('id', 'desc')
-$users = $user->sortable(['id' => 'desc'])->paginate(10);
+$users = $user->sortable(['name' => 'desc'])->paginate(10);
+//generate ->orderBy('name', 'desc')
```
### View
-In Laravel 5.2 and 5.3 **\Input** facade is not aliased by default. To do so, open `config/app.php` and add `'Input' => Illuminate\Support\Facades\Input::class,` to *aliases* array.
-
_pagination included_
```
@@ -173,14 +178,16 @@ _pagination included_
@foreach ($users as $user)
{{ $user->name }}
@endforeach
-{!! $users->appends(\Input::except('page'))->render() !!}
+{!! $users->appends(\Request::except('page'))->render() !!}
```
+>**Note**: Blade's ability to recognize directives depends on having space before directive itself `
@sortablelink('Name')`
+
# One To One Relation sorting
-## Define HasOne relation
+## Define hasOne relation
-In order to make relation sorting work, you have to define **hasOne()** relation in your model in question.
+In order to make relation sorting work, you have to define **hasOne()** relation in your model.
```
/**
@@ -188,13 +195,13 @@ In order to make relation sorting work, you have to define **hasOne()** relation
*/
public function detail()
{
- return $this->hasOne('App\UserDetail');
+ return $this->hasOne(App\UserDetail::class);
}
```
In *User* model we define **hasOne** relation to *UserDetail* model (which holds phone number and address details).
-## Define `$sortable` array
+## Define `$sortable` arrays
Define `$sortable` array in both models (else, package uses `Scheme::hasColumn()` which is extra database query).
@@ -202,7 +209,7 @@ Define `$sortable` array in both models (else, package uses `Scheme::hasColumn()
for *User*
```
-protected $sortable = ['id', 'name', 'email', 'created_at', 'updated_at'];
+public $sortable = ['id', 'name', 'email', 'created_at', 'updated_at'];
```
for *UserDetail*
@@ -211,8 +218,6 @@ for *UserDetail*
public $sortable = ['address', 'phone_number'];
```
->note that `$sortable` array in *UserDetail* is declared as **public** and not protected because we need to access it inside *User* model.
-
## Blade and relation sorting
In order to tell package to sort using relation:
@@ -220,7 +225,7 @@ In order to tell package to sort using relation:
```
@sortablelink ('detail.phone_number', 'phone')
```
->package works with relation "name" that you define in model instead of table name.
+>**Note**: package works with relation "name" (method) that you define in model instead of table name.
In config file you can set your own separator if `.` (dot) is not what you want.
@@ -249,4 +254,4 @@ try {
}
```
->I strongly recommend to catch **ColumnSortableException** because there is a user input in question (GET parameter) and any user can modify it in such way that package throws ColumnSortableException with code 0.
+>**Note**: I strongly recommend to catch **ColumnSortableException** because there is a user input in question (GET parameter) and any user can modify it in such way that package throws ColumnSortableException with code 0.
diff --git a/phpunit.xml b/phpunit.xml
index 5d69e79..916a770 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -7,12 +7,10 @@
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
- stopOnFailure="false"
- syntaxCheck="false"
->
+ stopOnFailure="false">
- ./tests/
+ ./tests
diff --git a/src/ColumnSortable/ColumnSortableServiceProvider.php b/src/ColumnSortable/ColumnSortableServiceProvider.php
index fbe3bc9..694557d 100755
--- a/src/ColumnSortable/ColumnSortableServiceProvider.php
+++ b/src/ColumnSortable/ColumnSortableServiceProvider.php
@@ -1,10 +1,16 @@
-publishes([$config_path => config_path('columnsortable.php')], 'columnsortable');
- $this->registerBladeExtensions();
+ $this->publishes([
+ __DIR__ . '/../config/columnsortable.php' => config_path('columnsortable.php')
+ ], 'config');
+
+ Blade::directive('sortablelink', function ($expression) {
+ $expression = ($expression[0] === '(') ? substr($expression, 1, -1) : $expression;
+ return "";
+ });
}
/**
@@ -31,24 +42,6 @@ public function boot()
*/
public function register()
{
- $config_path = __DIR__ . '/../config/columnsortable.php';
- $this->mergeConfigFrom($config_path, 'columnsortable');
- }
-
- /**
- * Register Blade extensions.
- *
- * @return void
- */
- protected function registerBladeExtensions()
- {
- $blade = $this->app['view']->getEngineResolver()->resolve('blade')->getCompiler();
-
- $blade->directive('sortablelink', function ($expression) {
- if ($expression[0] === '(') {
- return "";
- }
- return "";
- });
+ $this->mergeConfigFrom(__DIR__ . '/../config/columnsortable.php', 'columnsortable');
}
}
diff --git a/src/ColumnSortable/Sortable.php b/src/ColumnSortable/Sortable.php
index 180db4d..718ea3a 100755
--- a/src/ColumnSortable/Sortable.php
+++ b/src/ColumnSortable/Sortable.php
@@ -15,135 +15,52 @@
*/
trait Sortable
{
- /**
- * @param array $parameters
- *
- * @return string
- */
- public static function link(array $parameters)
- {
- if (count($parameters) === 1) {
- $title = self::getOneToOneSortOrNull($parameters[0]);
- $title = (is_null($title)) ? $parameters[0] : $title[1];
- } else {
- $title = $parameters[1];
- }
-
- $sort = $sortOriginal = $parameters[0];
- unset($parameters);
-
- $formatting_function = Config::get('columnsortable.formatting_function', null);
-
- if (!is_null($formatting_function) && function_exists($formatting_function)) {
- $title = call_user_func($formatting_function, $title);
- }
-
- $icon = Config::get('columnsortable.default_icon_set');
-
- if ($oneToOneSort = self::getOneToOneSortOrNull($sort)) {
- $sort = $oneToOneSort[1];
- }
-
- foreach (Config::get('columnsortable.columns') as $key => $value) {
- if (in_array($sort, $value['rows'])) {
- $icon = $value['class'];
- }
- }
-
- if (Request::get('sort') == $sortOriginal && in_array(Request::get('order'), ['asc', 'desc'])) {
- $asc_suffix = Config::get('columnsortable.asc_suffix', '-asc');
- $desc_suffix = Config::get('columnsortable.desc_suffix', '-desc');
- $icon = $icon . (Request::get('order') === 'asc' ? $asc_suffix : $desc_suffix);
- $order = Request::get('order') === 'desc' ? 'asc' : 'desc';
- } else {
- $icon = Config::get('columnsortable.sortable_icon');
- $order = Config::get('columnsortable.default_order_unsorted', 'asc');
- }
-
- $parameters = [
- 'sort' => $sortOriginal,
- 'order' => $order,
- ];
-
- $queryString = http_build_query(array_merge(array_filter(Request::except('sort', 'order', 'page')),
- $parameters));
- $anchorClass = Config::get('columnsortable.anchor_class', null);
- if ($anchorClass !== null) {
- $anchorClass = 'class="' . $anchorClass . '"';
- }
-
- $iconAndTextSeparator = Config::get('columnsortable.icon_text_separator', '');
-
- $clickableIcon = Config::get('columnsortable.clickable_icon', false);
- $trailingTag = $iconAndTextSeparator . '' . '';
- if ($clickableIcon === false) {
- $trailingTag = '' . $iconAndTextSeparator . '';
- }
-
- return '' . htmlentities($title) . $trailingTag;
- }
-
- /**
- * @param $sort
- * @return array|null
- * @throws ColumnSortableException
- */
- private static function getOneToOneSortOrNull($sort)
- {
- $separator = Config::get('columnsortable.uri_relation_column_separator', '.');
- if (str_contains($sort, $separator)) {
- $oneToOneSort = explode($separator, $sort);
- if (count($oneToOneSort) !== 2) {
- throw new ColumnSortableException();
- }
- return $oneToOneSort;
- }
-
- return null;
- }
-
/**
* @param \Illuminate\Database\Query\Builder $query
* @param array|null $default
*
* @return \Illuminate\Database\Query\Builder
*/
- public function scopeSortable($query, array $default = null)
+ public function scopeSortable($query, $default = null)
{
if (Request::has('sort') && Request::has('order')) {
+
return $this->queryOrderBuilder($query, Request::only(['sort', 'order']));
} elseif (!is_null($default)) {
- $default_array = $this->formatDefaultArray($default);
- if (Config::get('columnsortable.allow_request_modification', true) && !empty($default_array)) {
- Request::merge($default_array);
+ $defaultSortArray = $this->getDefaultSortArray($default);
+ if (Config::get('columnsortable.allow_request_modification', true) && !empty($defaultSortArray)) {
+ Request::merge($defaultSortArray);
}
- return $this->queryOrderBuilder($query, $default_array);
+
+ return $this->queryOrderBuilder($query, $defaultSortArray);
} else {
+
return $query;
}
}
/**
* @param \Illuminate\Database\Query\Builder $query
- * @param array $a
+ * @param array $sortArray
* @return \Illuminate\Database\Query\Builder
* @throws ColumnSortableException
*/
- private function queryOrderBuilder($query, array $a)
+ private function queryOrderBuilder($query, array $sortArray)
{
$model = $this;
+ //dd($model);
+ $direction = array_get($sortArray, 'order', 'asc');
- $order = array_get($a, 'order', 'asc');
- if (!in_array($order, ['asc', 'desc'])) {
- $order = Config::get('columnsortable.default_order', 'asc');
+ if (!in_array($direction, ['asc', 'desc'])) {
+ $direction = Config::get('columnsortable.default_order', 'asc');
}
- $sort = array_get($a, 'sort', null);
+ $sort = array_get($sortArray, 'sort', null);
+
if (!is_null($sort)) {
- if ($oneToOneSort = $this->getOneToOneSortOrNull($sort)) {
+ if ($oneToOneSort = SortableLink::getOneToOneSortOrNull($sort)) {
$relationName = $oneToOneSort[0];
$sort = $oneToOneSort[1];
-
try {
$relation = $query->getRelation($relationName);
$query = $this->queryJoinBuilder($query, $relation);
@@ -157,7 +74,7 @@ private function queryOrderBuilder($query, array $a)
}
if ($this->columnExists($model, $sort)) {
- return $query->orderBy($sort, $order);
+ return $query->orderBy($sort, $direction);
}
}
@@ -199,26 +116,29 @@ private function columnExists($model, $column)
}
/**
- * @param array $a
+ * @param array|string $sort
*
* @return array
*/
- private function formatDefaultArray(array $a)
+ private function getDefaultSortArray($sort)
{
- $order = Config::get('columnsortable.default_order', 'asc');
- reset($a);
-
- if ((bool)count(array_filter(array_keys($a), 'is_string'))) {
- $sort = key($a);
- $order = array_get($a, $sort, $order);
- } else {
- $sort = current($a);
+ if (empty($sort)) {
+ return [];
}
- if (!$sort) {
- return [];
+ $configDefaultOrder = Config::get('columnsortable.default_order', 'asc');
+
+ if (is_string($sort)) {
+ return ['sort' => $sort, 'order' => $configDefaultOrder];
}
- return ['sort' => $sort, 'order' => $order];
+ reset($sort);
+ $each = each($sort);
+
+ if ($each[0] === 0) {
+ return ['sort' => $each[1], 'order' => $configDefaultOrder];
+ } else {
+ return ['sort' => $each[0], 'order' => $each[1]];
+ }
}
}
diff --git a/src/ColumnSortable/SortableLink.php b/src/ColumnSortable/SortableLink.php
new file mode 100644
index 0000000..7752f5a
--- /dev/null
+++ b/src/ColumnSortable/SortableLink.php
@@ -0,0 +1,105 @@
+ $value) {
+ if (in_array($sort, $value['rows'])) {
+ $icon = $value['class'];
+ }
+ }
+
+ if (Request::get('sort') == $sortOriginal && in_array(Request::get('order'), ['asc', 'desc'])) {
+ $asc_suffix = Config::get('columnsortable.asc_suffix', '-asc');
+ $desc_suffix = Config::get('columnsortable.desc_suffix', '-desc');
+ $icon = $icon . (Request::get('order') === 'asc' ? $asc_suffix : $desc_suffix);
+ $order = Request::get('order') === 'desc' ? 'asc' : 'desc';
+ } else {
+ $icon = Config::get('columnsortable.sortable_icon');
+ $order = Config::get('columnsortable.default_order_unsorted', 'asc');
+ }
+
+ $parameters = [
+ 'sort' => $sortOriginal,
+ 'order' => $order,
+ ];
+
+ $queryString = http_build_query(array_merge(array_filter(Request::except('sort', 'order', 'page')),
+ $parameters));
+ $anchorClass = Config::get('columnsortable.anchor_class', null);
+ if ($anchorClass !== null) {
+ $anchorClass = 'class="' . $anchorClass . '"';
+ }
+
+ $iconAndTextSeparator = Config::get('columnsortable.icon_text_separator', '');
+
+ $clickableIcon = Config::get('columnsortable.clickable_icon', false);
+ $trailingTag = $iconAndTextSeparator . '' . '';
+ if ($clickableIcon === false) {
+ $trailingTag = '' . $iconAndTextSeparator . '';
+ }
+
+ return '' . htmlentities($title) . $trailingTag;
+ }
+
+ /**
+ * @param $sort
+ * @return array|null
+ * @throws ColumnSortableException
+ */
+ public static function getOneToOneSortOrNull($sort)
+ {
+ $separator = Config::get('columnsortable.uri_relation_column_separator', '.');
+
+ if (str_contains($sort, $separator)) {
+ $oneToOneSort = explode($separator, $sort);
+ if (count($oneToOneSort) !== 2) {
+ throw new ColumnSortableException();
+ }
+
+ return $oneToOneSort;
+ }
+
+ return null;
+ }
+}
diff --git a/src/config/columnsortable.php b/src/config/columnsortable.php
index 8a29637..12f694f 100755
--- a/src/config/columnsortable.php
+++ b/src/config/columnsortable.php
@@ -72,7 +72,7 @@
'allow_request_modification' => true,
/*
- default order for: $user->sortable(['id']) usage
+ default order for: $user->sortable('id') usage
*/
'default_order' => 'asc',