Skip to content

Commit da30c28

Browse files
committed
initial version of package
0 parents  commit da30c28

File tree

12 files changed

+826
-0
lines changed

12 files changed

+826
-0
lines changed

.DS_Store

6 KB
Binary file not shown.

composer.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "sectheater/laravel-datatables",
3+
"description": "Modern Laravel Datatable",
4+
"keywords" : ["Laravel Datatable", "Datatables", "Datatables Vue", "Datatables React"],
5+
"type": "library",
6+
"license": "MIT",
7+
"authors": [
8+
{
9+
"name": "Mohammed Osama",
10+
"email": "[email protected]"
11+
},
12+
{
13+
"name": "Mohammed Zayed",
14+
"email": "[email protected]"
15+
},
16+
],
17+
"minimum-stability": "dev",
18+
"require": {},
19+
"autoload": {
20+
"psr-4": {
21+
"Laravel\\DataTables\\" : "src/"
22+
}
23+
},
24+
"extra": {
25+
"laravel": {
26+
"providers": [
27+
"Laravel\\DataTables\\LaravelDataTablesServiceProvider"
28+
]
29+
}
30+
}
31+
32+
33+
34+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Laravel\DataTables\Commands;
4+
5+
use Illuminate\Console\Command;
6+
7+
class RegisterServiceDataTableCommand extends Command
8+
{
9+
public function handle()
10+
{
11+
12+
}
13+
}

src/Contracts/Displayable.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace Laravel\DataTables\Contracts;
4+
5+
interface Displayable
6+
{
7+
public function getCustomColumnNames();
8+
9+
public function getDisplayableColumns();
10+
11+
public function getUpdatableColumns();
12+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Laravel\DataTables\Exceptions;
4+
5+
use Exception;
6+
7+
class EloquentBuilderWasSetToNullException extends Exception
8+
{
9+
/**
10+
* @var string
11+
*/
12+
public $message = 'You have set your Eloquent query builder to null, perhaps you forgot to return the builder at your callback.';
13+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Laravel\DataTables;
4+
5+
use Illuminate\Support\ServiceProvider;
6+
7+
class LaravelDataTablesServiceProvider extends ServiceProvider
8+
{
9+
//TODO: ask the user for the preset (react/vue/angular)
10+
public function register()
11+
{
12+
if ($this->app->runningInConsole()) {
13+
$this->registerConsoleCommands();
14+
}
15+
$this->registerPublishables();
16+
}
17+
18+
public function registerPublishables()
19+
{
20+
$publishablePath = __DIR__ . '/publishable';
21+
$this->publishes([
22+
$publishablePath . '/js' => resource_path('js/laravel-datatables/components'),
23+
], 'datatable');
24+
}
25+
26+
protected function registerConsoleCommands()
27+
{
28+
$this->commands(\Laravel\DataTables\Commands\RegisterServiceDataTableCommand::class);
29+
}
30+
}
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
<?php
2+
namespace Laravel\Datatables\Services;
3+
4+
use Arr;
5+
use Schema;
6+
use Illuminate\Http\Request;
7+
use Illuminate\Database\QueryException;
8+
use Illuminate\Support\Traits\Macroable;
9+
use Illuminate\Database\Eloquent\Builder;
10+
use Illuminate\Database\Eloquent\Collection;
11+
use Laravel\DataTables\Contracts\Displayable;
12+
use Laravel\DataTables\Exceptions\EloquentBuilderWasSetToNullException;
13+
14+
abstract class BaseDatatableService implements Displayable
15+
{
16+
use Macroable;
17+
18+
/**
19+
* @var Builder
20+
*/
21+
public $builder;
22+
23+
/**
24+
* load the relationships associated with the collection that will be returned.
25+
* @var array
26+
*/
27+
public $relations;
28+
29+
/**
30+
* get/set the eloquent builder
31+
* @return Builder
32+
*/
33+
public function builder(): Builder
34+
{
35+
/**
36+
* @var mixed
37+
*/
38+
static $builder = null;
39+
40+
if (!is_null($builder)) {
41+
return $builder;
42+
}
43+
$builder = $this->query();
44+
45+
return $builder;
46+
}
47+
48+
public function filename()
49+
{
50+
return vsprintf('%name_%date', [
51+
'name' => ucfirst($this->getTable()),
52+
'date' => date('Y-m-d H:i:s'),
53+
]);
54+
}
55+
56+
/**
57+
* @param $columns
58+
*/
59+
public function getColumnsWithoutPrimaryKey($columns)
60+
{
61+
$primaryKey = $this->builder()->getModel()->getKeyName();
62+
63+
return array_filter($columns, fn($column) => $primaryKey !== $column);
64+
}
65+
66+
/**
67+
* @return mixed
68+
*/
69+
public function getCustomColumnNames(): array
70+
{
71+
if (method_exists($this->builder()->getModel(), 'getCustomColumnNames')) {
72+
return $this->builder()->getModel()->getCustomColumnNames();
73+
}
74+
75+
return [
76+
77+
];
78+
}
79+
80+
/**
81+
* @return mixed
82+
*/
83+
public function getDisplayableColumns(): array
84+
{
85+
if (method_exists($this->builder()->getModel(), 'getDisplayableColumns')) {
86+
return array_values($this->builder()->getModel()->getDisplayableColumns());
87+
}
88+
89+
return array_values(
90+
array_diff(
91+
$this->getDatabaseColumnNames(),
92+
$this->builder()->getModel()->getHidden()
93+
)
94+
);
95+
}
96+
97+
/**
98+
* @return Collection
99+
*/
100+
101+
//
102+
public function getRecords(Request $request = null, callable $callback = null): Collection
103+
{
104+
$builder = $this->builder();
105+
// we will check if the request has a query string for search. the query string for searching must contain column, operator which identified at resolveQueryParts method in form of the keys of the array. and the value that user is trying to search for
106+
// example: http://localhost:8000/api/posts?column=title&operator=contains&value=hello
107+
if ($request && $this->hasSearchQuery($request)) {
108+
$builder = $this->buildSearchQuery($builder, $request);
109+
}
110+
// Turn on the flexibility for the programmer to apply his own query to chain on the current query then we will retrieve back the query builder after the programmer applies his logic and proceed our own queries.
111+
if ($callback) {
112+
$builder = $callback($builder);
113+
throw_unless($builder, new EloquentBuilderWasSetToNullException);
114+
}
115+
// we will try to parse the query and return the output of it, if anything goes wrong, by default we will be returning an empty collection.
116+
try {
117+
return $builder->select(...$this->getSelectableColumns())->limit($request->limit)->get($this->getDisplayableColumns());
118+
} catch (QueryException $e) {
119+
return collect([]);
120+
}
121+
}
122+
123+
/**
124+
* @return array
125+
*/
126+
public function getSelectableColumns(): array
127+
{
128+
return $this->getDatabaseColumnNames();
129+
}
130+
131+
/**
132+
* @return string
133+
*/
134+
public function getTable(): string
135+
{
136+
return $this->builder()->getModel()->getTable();
137+
}
138+
139+
/**
140+
* get the columns that user can see at the frontend to update.
141+
* @return array
142+
*/
143+
public function getUpdatableColumns(): array
144+
{
145+
if (method_exists($this->builder()->getModel(), 'getUpdatableColumns')) {
146+
return array_values($this->getColumnsWithoutPrimaryKey($this->builder()->getModel()->getUpdatableColumns()));
147+
}
148+
149+
return array_values($this->getColumnsWithoutPrimaryKey($this->getDisplayableColumns()));
150+
}
151+
152+
abstract public function query(): Builder;
153+
154+
/**
155+
* return the response skeleton.
156+
* @return array
157+
*/
158+
public function response(callable $callback = null): array
159+
{
160+
161+
return [
162+
'table' => $this->getTable(),
163+
'displayable' => $this->getDisplayableColumns(),
164+
'records' => $this->getRecords(request(), $callback)->load((array) $this->relations),
165+
'updatable' => $this->getUpdatableColumns(),
166+
'custom_columns' => $this->getCustomColumnNames(),
167+
'allow' => [
168+
'creatable' => $this->allowCreating ?? false,
169+
'deletable' => $this->allowDeleting ?? false,
170+
'updatable' => $this->allowUpdating ?? false,
171+
],
172+
];
173+
}
174+
175+
/**
176+
* @param Builder $builder
177+
*/
178+
protected function buildSearchQuery(Builder $builder, Request $request): Builder
179+
{
180+
['operator' => $operator, 'value' => $value] = $this->resolveQueryParts($request->operator, $request->value);
181+
182+
return $builder->where($request->column, $operator, $value);
183+
}
184+
185+
/**
186+
* [getDatabaseColumnNames]
187+
* @return array
188+
*/
189+
protected function getDatabaseColumnNames(): array
190+
{
191+
return Schema::getColumnListing($this->getTable());
192+
}
193+
194+
/**
195+
* @param $request
196+
* @return int
197+
*/
198+
protected function hasSearchQuery(Request $request): bool
199+
{
200+
return count(array_filter($request->only(['column', 'operator', 'value']))) === 3;
201+
}
202+
203+
/**
204+
* @param $operator
205+
* @param $value
206+
* @return array
207+
*/
208+
protected function resolveQueryParts($operator, $value)
209+
{
210+
return Arr::get([
211+
'equals' => [
212+
'operator' => '=',
213+
'value' => $value,
214+
],
215+
'contains' => [
216+
'operator' => 'LIKE',
217+
'value' => "%{$value}%",
218+
],
219+
'starts_with' => [
220+
'operator' => 'LIKE',
221+
'value' => "{$value}%",
222+
],
223+
'ends_with' => [
224+
'operator' => 'LIKE',
225+
'value' => "%{$value}",
226+
227+
],
228+
'greater_than' => [
229+
'operator' => '>',
230+
'value' => $value,
231+
232+
],
233+
'less_than' => [
234+
'operator' => '<',
235+
'value' => $value,
236+
237+
],
238+
], $operator);
239+
}
240+
}

src/Traits/Creatable.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Laravel\DataTables\Traits;
4+
5+
use Arr;
6+
use Illuminate\Database\Eloquent\Model;
7+
trait Creatable
8+
{
9+
/**
10+
* @var mixed
11+
*/
12+
public $allowCreation = false;
13+
14+
/**
15+
* @param array $data
16+
* @return mixed
17+
*/
18+
public function create(array $data): ? Model
19+
{
20+
if (!$this->allowCreation) {
21+
return null;
22+
}
23+
24+
return $this->builder()->create(
25+
Arr::only(
26+
$data,
27+
$this->getUpdatableColumns()
28+
)
29+
);
30+
}
31+
}

0 commit comments

Comments
 (0)