From 6d7cfe05e334b3443e1bba6a4b72d77b68f4b26f Mon Sep 17 00:00:00 2001 From: Virgil-Adrian Teaca Date: Mon, 27 Aug 2018 23:15:40 +0300 Subject: [PATCH 1/2] Overall improvements --- app/Config/Cache.php | 2 +- modules/Roles/Controllers/Admin/Roles.php | 38 +++-- modules/Users/Controllers/Admin/Users.php | 65 +++++--- modules/Users/Views/Admin/Users/Index.php | 9 +- shared/Cache/CacheServiceProvider.php | 2 +- shared/DataTable/Column.php | 146 ++++++++++++++++++ shared/DataTable/DataTable.php | 172 ++++++++++++++-------- shared/DataTable/Factory.php | 5 +- 8 files changed, 326 insertions(+), 113 deletions(-) create mode 100644 shared/DataTable/Column.php diff --git a/app/Config/Cache.php b/app/Config/Cache.php index bbd1ef2973..3d317de6c7 100644 --- a/app/Config/Cache.php +++ b/app/Config/Cache.php @@ -14,7 +14,7 @@ | Supported: "file", "database", "apc", "memcached", "redis", "array" */ - 'driver' => 'tagged_file', + 'driver' => 'taggedFile', /* |-------------------------------------------------------------------------- diff --git a/modules/Roles/Controllers/Admin/Roles.php b/modules/Roles/Controllers/Admin/Roles.php index 011ec4cee2..55b9bcd0a2 100644 --- a/modules/Roles/Controllers/Admin/Roles.php +++ b/modules/Roles/Controllers/Admin/Roles.php @@ -64,27 +64,23 @@ protected function validator(array $data, $id = null) return $validator; } - public function data(Request $request) + protected function dataTable() { - // Authorize the current User. - if (Gate::denies('lists', Role::class)) { - throw new AuthorizationException(); - } + $format = __d('users', '%d %b %Y, %H:%M'); - $query = Role::withCount('users'); + // Create a DataTable instance. + $dataTable = DataTable::make(); - $dataTable = DataTable::make($query) - ->column('id') - ->column('name') - ->column('slug') - ->column('description'); + // Setup the columns. + $dataTable->column('id'); + $dataTable->column('name'); + $dataTable->column('slug'); + $dataTable->column('description'); $dataTable->column('users_count', 'users'); - $dataTable->column('created_at', function ($role) + $dataTable->column('created_at', function ($role) use ($format) { - $format = __d('roles', '%d %b %Y, %H:%M'); - return $role->created_at->formatLocalized($format); }); @@ -93,7 +89,19 @@ public function data(Request $request) return View::fetch('Modules/Roles::Partials/RolesTableActions', compact('role')); }); - return $dataTable->handle($request); + return $dataTable; + } + + public function data(Request $request) + { + // Authorize the current User. + if (Gate::denies('lists', Role::class)) { + throw new AuthorizationException(); + } + + $query = Role::withCount('users'); + + return $this->dataTable()->handle($query, $request); } public function index() diff --git a/modules/Users/Controllers/Admin/Users.php b/modules/Users/Controllers/Admin/Users.php index 00bcc96f59..3eb739be13 100644 --- a/modules/Users/Controllers/Admin/Users.php +++ b/modules/Users/Controllers/Admin/Users.php @@ -147,41 +147,57 @@ protected function validator(array $data, Collection $items, $id = null) return $validator; } - public function data(Request $request) + protected function dataTable() { - // Authorize the current User. - if (Gate::denies('lists', User::class)) { - throw new AuthorizationException(); - } + $dataTable = DataTable::make(); - $query = User::with('roles')->where('activated', 1); + // Setup the DataTable columns. + $dataTable->group(array('className' => 'text-center'), function ($dataTable) + { + $dataTable->column('id')->orderable(true); - $dataTable = DataTable::make($query) - ->column('id') - ->column('username') - ->column('realname') - ->column('email'); + $dataTable->column('username', array('orderable' => true, 'searchable' => true)); - $dataTable->column('roles.name', 'roles', function ($user) - { - $roles = $user->roles->lists('name'); + $dataTable->group(array('orderable' => true, 'searchable' => true), function ($dataTable) + { + $dataTable->column('roles.name', array('data' => 'roles', 'uses' => function ($user) + { + $roles = $user->roles->lists('name'); - return implode(', ', $roles); - }); + return implode(', ', $roles); + })); - $dataTable->column('created_at', function ($user) - { - $format = __d('users', '%d %b %Y, %H:%M'); + $dataTable->column('realname'); + $dataTable->column('email'); + }); + + $dataTable->column('created_at', function ($user) + { + $format = __d('users', '%d %b %Y, %H:%M'); + + return $user->created_at->formatLocalized($format); - return $user->created_at->formatLocalized($format); + })->orderable(true); }); - $dataTable->column('actions', function ($user) + $dataTable->column('actions', array('className' => 'text-right compact', function ($user) { return View::fetch('Modules/Users::Partials/UsersTableActions', compact('user')); - }); + })); + + return $dataTable; + } + + public function data(Request $request) + { + // Authorize the current User. + if (Gate::denies('lists', User::class)) { + throw new AuthorizationException(); + } + + $query = User::with('roles')->where('activated', 1); - return $dataTable->handle($request); + return $this->dataTable()->handle($query, $request); } public function index() @@ -192,7 +208,8 @@ public function index() } return $this->createView() - ->shares('title', __d('users', 'Users')); + ->shares('title', __d('users', 'Users')) + ->with('dataTable', $this->dataTable()); } public function create(Request $request) diff --git a/modules/Users/Views/Admin/Users/Index.php b/modules/Users/Views/Admin/Users/Index.php index e5277614d5..0934a6bd38 100644 --- a/modules/Users/Views/Admin/Users/Index.php +++ b/modules/Users/Views/Admin/Users/Index.php @@ -71,13 +71,8 @@ lengthMenu: [ 5, 10, 15, 20, 25, 50, 100 ], columns: [ - { data: 'id', name: 'id', orderable: true, searchable: false, className: "text-center" }, - { data: 'username', name: 'username', orderable: true, searchable: true, className: "text-center" }, - { data: 'roles', name: 'roles.name', orderable: true, searchable: true, className: "text-center" }, - { data: 'realname', name: 'realname', orderable: true, searchable: true, className: "text-center" }, - { data: 'email', name: 'email', orderable: true, searchable: true, className: "text-center" }, - { data: 'created_at', name: 'created_at', orderable: true, searchable: false, className: "text-center" }, - { data: 'actions', name: 'actions', orderable: false, searchable: false, className: "text-right compact" }, + script(); ?> + ], drawCallback: function(settings) diff --git a/shared/Cache/CacheServiceProvider.php b/shared/Cache/CacheServiceProvider.php index 83ce145e72..0136f9b586 100644 --- a/shared/Cache/CacheServiceProvider.php +++ b/shared/Cache/CacheServiceProvider.php @@ -19,7 +19,7 @@ public function boot() { $config = $this->app['config']; - $this->app['cache']->extend('tagged_file', function ($app) use ($config) + $this->app['cache']->extend('taggedFile', function ($app) use ($config) { $path = $config->get('cache.path'); diff --git a/shared/DataTable/Column.php b/shared/DataTable/Column.php new file mode 100644 index 0000000000..9a9d163184 --- /dev/null +++ b/shared/DataTable/Column.php @@ -0,0 +1,146 @@ +attributes = $attributes; + } + + /** + * Determine if the given attribute exists. + * + * @param mixed $key + * @return bool + */ + public function has($key) + { + return isset($this->attributes[$key]); + } + + /** + * Get an attribute from the column. + * + * @param mixed $key + * @param mixed $default + * @return mixed + */ + public function get($key, $default = null) + { + return isset($this->attributes[$key]) ? $this->attributes[$key] : $default; + } + + /** + * Set an attribute from the column. + * + * @param mixed $key + * @param mixed $value + * @return mixed + */ + public function set($key, $value) + { + $this->attributes[$key] = $value; + } + + /** + * Forget the value of a given attribute. + * + * @param mixed $offset + * @return void + */ + public function forget($key) + { + unset($this->attributes[$key]); + } + + /** + * Get all of the current attributes on the column. + * + * @return array + */ + public function getAttributes() + { + return $this->attributes; + } + + /** + * Determine if the given attribute exists. + * + * @param mixed $offset + * @return bool + */ + public function offsetExists($offset) + { + return $this->has($offset); + } + + /** + * Get the value for a given offset. + * + * @param mixed $offset + * @return mixed + */ + public function offsetGet($offset) + { + return $this->get($offset); + } + + /** + * Set the value for a given offset. + * + * @param mixed $offset + * @param mixed $value + * @return void + */ + public function offsetSet($offset, $value) + { + $this->set($offset, $value); + } + + /** + * Unset the value for a given offset. + * + * @param mixed $offset + * @return void + */ + public function offsetUnset($offset) + { + $this->forget($offset); + } + + /** + * Handle dynamic method calls into the method. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public function __call($method, array $parameters) + { + if (empty($parameters)) { + return $this->get($method); + } + + // The method call is for a setter. + else if (in_array($method, array('orderable', 'searchable', 'className'))) { + $this->set($method, head($parameters)); + } + + return $this; + } +} diff --git a/shared/DataTable/DataTable.php b/shared/DataTable/DataTable.php index 02a5d20471..1c42199d34 100644 --- a/shared/DataTable/DataTable.php +++ b/shared/DataTable/DataTable.php @@ -3,6 +3,7 @@ namespace Shared\DataTable; use Nova\Database\ORM\Builder as ModelBuilder; +use Nova\Database\Query\Builder as QueryBuilder; use Nova\Http\Request; use Nova\Support\Arr; use Nova\Support\Str; @@ -19,14 +20,14 @@ class DataTable protected $factory; /** - * @var Nova\Database\Query\Builder|Nova\Database\ORM\Builder + * @var array */ - protected $query; + protected $columns = array(); /** * @var array */ - protected $columns = array(); + protected $groupStack = array(); /** @@ -37,82 +38,147 @@ class DataTable * * @return array */ - public function __construct(Factory $factory, $query, array $columns) + public function __construct(Factory $factory, array $columns) { $this->factory = $factory; - $this->query = $query; - foreach ($columns as $column) { - if (! is_array($column) || is_null($name = Arr::get($column, 'name'))) { - throw new InvalidArgumentException('Invalid column specified.'); + if (is_array($column) && ! is_null($name = Arr::get($column, 'name'))) { + $this->column($name, $column); } + } + } + + /** + * Create a column group with shared attributes. + * + * @param array $attributes + * @param \Closure $callback + * @return void + */ + public function group(array $attributes, Closure $callback) + { + if (! empty($this->groupStack)) { + $lastGroup = last($this->groupStack); - $this->columns[$name] = $column; + $attributes = array_merge($lastGroup, $attributes); } - if ($query instanceof ModelBuilder) { - $baseQuery = $query->getQuery(); + $this->groupStack[] = $attributes; - if (is_null($baseQuery->columns)) { - $table = $query->getModel()->getTable(); + call_user_func($callback, $this); - $query->select($table .'.*'); - } - } + array_pop($this->groupStack); } /** * Adds a column definition to internal options. * * @param string $name - * @param string|\Closure|null $data - * @param \Closure|null $callback + * @param string|array|\Closure|null $attributes * * @return array */ - public function column($name, $data = null, Closure $callback = null) + public function column($name, $attributes = null) { if (isset($this->columns[$name])) { throw new InvalidArgumentException('Column already exists.'); - } else if (preg_match('/^[a-z]\w+/i', $name) !== 1) { + } + + // Check if the column name is valid. + else if (preg_match('/^[a-z]\w+/i', $name) !== 1) { throw new InvalidArgumentException('Invalid column name.'); } - $safeName = str_replace('.', '_', $name); + if ($attributes instanceof Closure) { + $attributes = array('uses' => $attributes); + } - if (is_null($data)) { - $data = $safeName; - } else if ($data instanceof Closure) { - list ($callback, $data) = array($data, $safeName); + // + else if (! is_array($attributes)) { + $attributes = array('data' => $attributes); } - // A standard column data. + $attributes['name'] = $name; + + if (empty($data = Arr::get($attributes, 'data'))) { + $attributes['data'] = str_replace('.', '_', $name); + } + + // Check if the column data is valid. else if (preg_match('/^[a-z]\w+/i', $data) !== 1) { - throw new InvalidArgumentException('Invalid column name.'); + throw new InvalidArgumentException('Invalid column data.'); + } + + if (! isset($attributes['uses']) && ! is_null($callback = $this->findColumnClosure($attributes))) { + $attributes['uses'] = $callback; } - $column = compact('name', 'data'); + if (! empty($this->groupStack)) { + $lastGroup = last($this->groupStack); - if (! is_null($callback)) { - $column['uses'] = $callback; + $attributes = array_merge($lastGroup, $attributes); } - $this->columns[$name] = $column; + $this->columns[$name] = $column = new Column($attributes); - return $this; + return $column; + } + + /** + * Find the Closure in an column array or returns a default callback. + * + * @param array $column + * @return \Closure + */ + protected function findColumnClosure(array $column) + { + return Arr::first($column, function ($key, $value) + { + return is_callable($value) && is_numeric($key); + }); + } + + public function script() + { + $lines = array(); + + foreach ($this->columns as $name => $column) { + $orderable = $column->get('orderable', false) ? 'true' : 'false'; + $searchable = $column->get('searchable', false) ? 'true' : 'false'; + + $lines[] = sprintf("{ data: '%s', name: '%s', orderable: %s, searchable: %s, className: '%s' }", + $column->get('data'), $name, $orderable, $searchable, $column->get('className') + ); + } + + return implode(', ', $lines); } /** * Handle a Request. * + * @param Nova\Database\Query\Builder|Nova\Database\ORM\Builder $query * @param Nova\Http\Request $request * * @return array */ - public function handle(Request $request = null) + public function handle($query, Request $request = null) { - $query = $this->getQuery(); + if ($query instanceof ModelBuilder) { + $queryBuilder = $query->getQuery(); + + if (is_null($queryBuilder->columns)) { + $table = $query->getModel()->getTable(); + + $query->select($table .'.*'); + } + } + + // + else if (! $query instanceof QueryBuilder) { + throw new InvalidArgumentException('Invalid query.'); + } if (is_null($request)) { $request = $this->getRequest(); @@ -296,22 +362,18 @@ protected function createRecord($result) $record = array(); foreach ($this->columns as $name => $column) { - $field = Arr::get($column, 'uses', $name); - - $data = Arr::get($column, 'data', str_replace('.', '_', $name)); + $key = $column->get('data'); - if ($field instanceof Closure) { - $value = call_user_func($field, $result, $name, $data); - } + $callback = $column->get('uses', function ($record, $field) + { + if (! Str::contains($field, '.')) { + return $record->{$field}; + } + }); - // The column has no custom renderer. - else if (! Str::contains($field, '.')) { - $value = $result->{$field}; - } else { - continue; + if ($callback instanceof Closure) { + $record[$key] = call_user_func($callback, $result, $name); } - - $record[$data] = $value; } return $record; @@ -376,16 +438,6 @@ protected function getFactory() return $this->query; } - /** - * Returns the current query. - * - * @return \Nova\Database\Query\Builder|Nova\Database\ORM\Builder - */ - protected function getQuery() - { - return $this->query; - } - /** * Returns the options. * @@ -393,10 +445,6 @@ protected function getQuery() */ protected function getColumns() { - return array_map(function ($column) - { - return $column; - - }, $this->columns); + return array_values($this->columns); } } diff --git a/shared/DataTable/Factory.php b/shared/DataTable/Factory.php index e911249532..de0823a603 100644 --- a/shared/DataTable/Factory.php +++ b/shared/DataTable/Factory.php @@ -40,14 +40,13 @@ public function __construct(Request $request, ResponseFactory $responseFactory) /** * Create a new DataTable instance. * - * @param Nova\Database\Query\Builder|Nova\Database\ORM\Builder $query * @param array $columns * * @return array */ - public function make($query, array $columns = array()) + public function make(array $columns = array()) { - return new DataTable($this, $query, $columns); + return new DataTable($this, $columns); } /** From b5b18750cae687220ee61b10817d6789338cfc8a Mon Sep 17 00:00:00 2001 From: Virgil-Adrian Teaca Date: Mon, 27 Aug 2018 23:16:45 +0300 Subject: [PATCH 2/2] Update the application version --- app/Platform/Bootstrap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Platform/Bootstrap.php b/app/Platform/Bootstrap.php index dd209bf851..ca111a0d02 100644 --- a/app/Platform/Bootstrap.php +++ b/app/Platform/Bootstrap.php @@ -14,7 +14,7 @@ // Define The Application Version //-------------------------------------------------------------------------- -define('VERSION', '4.0.90'); +define('VERSION', '4.0.91'); //-------------------------------------------------------------------------- // Set PHP Error Reporting Options