diff --git a/README.md b/README.md index 626af3c..f0a5e69 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,9 @@ [![Latest Tag](https://img.shields.io/github/tag/gbrock/laravel-table.svg)](https://github.com/gbrock/laravel-table/releases) -Adds database-level sorting to Laravel models. **This project is under ongoing development.** +This package contains flexible ways of rendering Eloquent collections as dynamic HTML tables. This includes +techniques for sortable columns, automatic pagination, ~~user-definable amount of rows shown per page, batch action +handling, and extensible filtering~~ (coming soon). ## Installation @@ -14,38 +16,104 @@ Require the package in your `composer.json`: "gbrock/laravel-table": "dev-master" ``` +(Optional) Publish the views and config: + +``` +php artisan vendor:publish +``` + ## Usage -Add the trait to the models you wish to sort. +**In order to render an HTML table of Eloquent models into a view**, first create a Table object, passing in your + model collection (this could be done in your controller, repository, or any service class): + + ```php + $rows = User::get(); // Get all users from the database + $table = Table::create($rows); // Generate a Table based on these "rows" + ``` + + Then pass that object to your view: ```php -use Gbrock\Table\TableSortable; +return view('users.index', ['table' => $table]); +``` -class User extends Model { +In your view, the table object can be rendered using its `render` function: - use TableSortable; +```php +{!! $table->render() !!} ``` -Since no fields are allowed to be sorted by default for security reasons, add a `$sortable` array to your model -describing which fields are allowed to be sorted: +Which would render something like this: + +![Basic example](https://raw.githubusercontent.com/gbrock/laravel-table/master/examples/images/basic_initialization.png) + +### Sorting + +To add links in your headers which sort the indicated column, add the `Sortable` trait to the your model. Since no +fields are allowed to be sorted by default (for security reasons), also add a `sortable` array containing allowed fields. ```php -/** - * The attributes which may be used for sorting dynamically. - * - * @var array - */ -protected $sortable = ['id', 'username', 'email', 'last_login']; +use Gbrock\Table\Traits\Sortable; + +class User extends Model { + + use Sortable; + + /** + * The attributes which may be used for sorting dynamically. + * + * @var array + */ + protected $sortable = ['username', 'email', 'created_at']; +``` + +This adds the `sortable` scope to your model, which you should use when retrieving rows. Altering our first example, +the database call / Table creation: + + ```php + $rows = User::sorted()->get(); // Get all users from the database, but listen to the user Request and sort accordingly ``` -Last, use the model's `sortable` [scope](http://laravel.com/docs/5.0/eloquent#query-scopes) to get your sorted data: +Now, our table will be rendered with links in the header: + +![Sortable example](https://raw.githubusercontent.com/gbrock/laravel-table/master/examples/images/sortable_initialization.png) + +The links will contain query strings like `?sort=username&direction=asc`. + +### Pagination + +If you paginate your Eloquent collection, it will automatically be rendered below the table: + + ```php + $rows = User::sorted()->paginate(); // Get all users from the database, sort, and paginate +``` + +### Customizing the Columns + +For a bit more control, pass in a second argument to your database call / Table creation, **columns**: ```php -// This is one possible way you could get what the user requested to be sorted. -$sortField = Input::get('sort'); -$sortDir = Input::get('dir', 'desc'); // default desc + $table = Table::create($rows, ['username', 'created_at']); // Generate a Table based on these "rows" +``` + +Since our view is accessing our model's attributes, we can add or modify any column key we'd like by using +[accessors](http://laravel.com/docs/5.0/eloquent#accessors-and-mutators): -// Get the collection of rows using the "sorted" scope -$rows = User::sorted($sortField, $sortDir); +```php + protected function getRenderedCreatedAtAttribute() + { + // We access the following diff string with "$model->rendered_created_at" + return $this->created_at->diffForHumans(); + } ``` +### Customizing the View + +The default view favors the `rendered_foo_bar` attribute, if present, else it uses the `foo_bar` attribute. A copy of +the view file is located in `/resources/vendor/gbrock/tables/` after you've run `php artisan vendor:publish`. You +can copy this file wherever you'd like and alter it, then tell your table to use the new view: + +```php +$table->setView('users.table'); +``` diff --git a/config/tables.php b/config/tables.php index 18dca7a..8e65376 100644 --- a/config/tables.php +++ b/config/tables.php @@ -2,7 +2,6 @@ return [ 'key_field' => 'sort', - 'key_direction' => 'direction', - 'default_direction' => 'asc', - + 'key_direction' => 'dir', + 'default_direction' => 'desc', ]; diff --git a/examples/images/basic_initialization.png b/examples/images/basic_initialization.png index 456a7fa..7ff94b6 100644 Binary files a/examples/images/basic_initialization.png and b/examples/images/basic_initialization.png differ diff --git a/examples/images/sortable_initialization.png b/examples/images/sortable_initialization.png new file mode 100644 index 0000000..6ca6c2b Binary files /dev/null and b/examples/images/sortable_initialization.png differ diff --git a/src/Column.php b/src/Column.php index 66b18c4..865dd31 100644 --- a/src/Column.php +++ b/src/Column.php @@ -68,7 +68,7 @@ public static function create() */ public function setOptionsFromModel($model) { - if(in_array($this->getField(), $model->getSortable())) + if($model->is_sortable && in_array($this->getField(), $model->getSortable())) { // The model dictates that this column should be sortable $this->setSortable(true); @@ -82,12 +82,12 @@ public function setOptionsFromModel($model) */ public function isSorted() { - if(Request::input('sort') == $this->getField()) + if(Request::input(config('gbrock-tables.key_field')) == $this->getField()) { return true; } - if(!Request::input('sort') && $this->model && $this->model->getSortingField() == $this->getField()) + if(!Request::input(config('gbrock-tables.key_field')) && $this->model && $this->model->getSortingField() == $this->getField()) { // No sorting was requested, but this is the default field. return true; @@ -115,8 +115,8 @@ public function getSortURL($direction = false) // Generate and return a URL which may be used to sort this column return $this->generateUrl(array_filter([ - 'sort' => $this->getField(), - 'direction' => $direction, + config('gbrock-tables.key_field') => $this->getField(), + config('gbrock-tables.key_direction') => $direction, ])); } @@ -129,12 +129,12 @@ public function getDirection() if($this->isSorted()) { // If the column is currently being sorted, grab the direction from the query string - $this->direction = Request::input('direction'); + $this->direction = Request::input(config('gbrock-tables.key_direction')); } if(!$this->direction) { - $this->direction = 'asc'; + $this->direction = config('gbrock-tables.default_direction'); } return $this->direction; @@ -187,8 +187,8 @@ public function generateUrl($parameters = []) protected function getCurrentInput() { return Input::only([ - 'sort' => Request::input('sort'), - 'direction' => Request::input('direction'), + config('gbrock-tables.key_field') => Request::input(config('gbrock-tables.key_field')), + config('gbrock-tables.key_direction') => Request::input(config('gbrock-tables.key_direction')), ]); } diff --git a/src/Table.php b/src/Table.php index 05314d1..cf6237b 100644 --- a/src/Table.php +++ b/src/Table.php @@ -6,6 +6,8 @@ class Table { protected $models; protected $columns; + protected $view = 'gbrock.tables::table'; + protected $viewVars = []; /** * @param array $models @@ -41,6 +43,26 @@ public function create($rows, $columns = false) return $table; } + /** + * @return string + */ + public function getView() + { + return $this->view; + } + + /** + * @param string $view + */ + public function setView($view, $vars = true) + { + $this->view = $view; + if(is_array($vars) || !$vars) + { + $this->viewVars = $vars; + } + } + /** * Add columns based on field names * @param $columns @@ -94,11 +116,21 @@ protected function getFieldsFromModels($models) return []; } - $fields = array_keys($models->first()->toArray()); + $model = $models->first(); + + // These are the Laravel basic timestamp fields which we don't want to display, by default $timestamp_fields = ['created_at', 'updated_at', 'deleted_at']; + // Grab the basic fields from the first model + $fields = array_keys($model->toArray()); + // Remove the timestamp fields + $fields = array_diff($fields, $timestamp_fields); + if($model->isSortable) + { + // Add the fields from the model's sortable array + $fields = array_unique(array_merge($fields, $model->getSortable())); + } - // only those non-timestamp fields - return array_diff($fields, $timestamp_fields); + return $fields; } /** @@ -108,19 +140,19 @@ protected function getFieldsFromModels($models) public function render() { $this->appendPaginationLinks(); - return view('gbrock.tables::table', $this->getViewData())->render(); + return view($this->view, $this->getData())->render(); } /** * Generate the data needed to render the view. * @return array */ - protected function getViewData() + public function getData() { - return [ + return array_merge($this->viewVars, [ 'rows' => $this->getRows(), 'columns' => $this->getColumns(), - ]; + ]); } /** @@ -176,7 +208,7 @@ private function appendPaginationLinks() if(class_basename($this->models) == 'LengthAwarePaginator') { // This set of models was paginated. Make it append our current view variables. - $this->models->appends(Input::only('sort', 'direction')); + $this->models->appends(Input::only(config('gbrock-tables.key_field'), config('gbrock-tables.key_direction'))); } else { diff --git a/src/Traits/Sortable.php b/src/Traits/Sortable.php index 25da746..8ebc7a8 100644 --- a/src/Traits/Sortable.php +++ b/src/Traits/Sortable.php @@ -28,7 +28,7 @@ public function scopeSorted($query, $field = false, $direction = false) return $query; } - // If the direction requested isn't correct, assume ascending + // If the direction requested isn't correct, grab from config if($direction !== 'asc' && $direction !== 'desc') { $direction = config('gbrock-tables.default_direction'); @@ -83,5 +83,10 @@ protected function getSortingDirection() // Otherwise return the primary key return config('gbrock-tables.default_direction'); } + + public function getIsSortableAttribute() + { + return true; + } }