Last active
April 4, 2024 20:28
-
-
Save jeff-silva/c3c82b72ff1fcd5816c82b8326164844 to your computer and use it in GitHub Desktop.
Laravel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
1) Insert method above in Controller.php | |
public function generateRoutes() | |
{ | |
$refClass = new \ReflectionClass(get_called_class()); | |
// Methods Attributes | |
foreach(get_class_methods($this) as $method) { | |
foreach($refClass->getMethod($method)->getAttributes() as $attr) { | |
$args = $attr->getArguments() ?? []; | |
$args['controller'] = get_called_class(); | |
$args['controllerMethod'] = $method; | |
(new \ReflectionClass($attr->getName()))->newInstanceArgs($args); | |
} | |
} | |
// Class Attributes | |
foreach($refClass->getAttributes() as $attr) { | |
$args = $attr->getArguments() ?? []; | |
$args['controller'] = get_called_class(); | |
(new \ReflectionClass($attr->getName()))->newInstanceArgs($args); | |
} | |
} | |
2) In the controller constructor, call $this->generateRoutes() | |
3) Create app/Helper/Router folder and then inside files above | |
*/ | |
// To create route, put this on methods | |
// #[Create(method: 'get', path: '/app/test', name: 'app.test', middleware: 'api')] | |
namespace App\Helper\Router; | |
use Illuminate\Support\Facades\Route; | |
class Create | |
{ | |
public function __construct($controller, $controllerMethod, $method, $path, $name = null, $middleware = null) | |
{ | |
$method = is_array($method) ? $method : [$method]; | |
$r = Route::match($method, $path, [$controller, $controllerMethod])->name($name); | |
if ($middleware) | |
$r->middleware($middleware); | |
} | |
} | |
// To create apiResource, put this on class | |
// #[Resource(name: 'get', middleware: 'api')] | |
namespace App\Helper\Router; | |
use Illuminate\Support\Facades\Route; | |
class Resource | |
{ | |
public function __construct($name, $controller, $middleware = null) | |
{ | |
$r = Route::apiResource($name, $controller); | |
if ($middleware) | |
$r->middleware($middleware); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Console\Commands; | |
use Illuminate\Console\Command; | |
class AppMake extends Command | |
{ | |
/** | |
* The name and signature of the console command. | |
* | |
* @var string | |
*/ | |
protected $signature = 'app:make'; | |
/** | |
* The console command description. | |
* | |
* @var string | |
*/ | |
protected $description = 'Generate models, controllers, routes and stores from database schema'; | |
/** | |
* Create a new command instance. | |
* | |
* @return void | |
*/ | |
public function __construct() | |
{ | |
parent::__construct(); | |
} | |
public function getDatabaseSchema() { | |
$env_database = env('DB_DATABASE'); | |
$fks = \DB::select("SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE CONSTRAINT_SCHEMA='{$env_database}' "); | |
$tables = []; | |
foreach(\DB::select('SHOW TABLE STATUS') as $table) { | |
$table = (object) array_merge([ | |
'Singular' => \Str::singular($table->Name), | |
'Plural' => \Str::plural($table->Name), | |
'Route' => str_replace('_', '-', \Str::kebab($table->Name)), | |
], (array) $table); | |
$table->Model = new \stdClass; | |
$table->Model->Name = \Str::studly($table->Singular); | |
$table->Model->Namespace = "\App\Models\\{$table->Model->Name}"; | |
$table->Model->File = "app/Models/{$table->Model->Name}.php"; | |
$table->Model->FileExists = file_exists(base_path($table->Model->File)); | |
$table->Controller = new \stdClass; | |
$table->Controller->Name = \Str::studly($table->Singular).'Controller'; | |
$table->Controller->Namespace = "\App\Http\Controllers\\{$table->Controller->Name}"; | |
$table->Controller->File = "app/Http/Controllers/{$table->Controller->Name}.php"; | |
$table->Controller->FileExists = file_exists(base_path($table->Controller->File)); | |
$table->FieldNames = []; | |
$table->Fields = []; | |
$table->ForeignKeys = []; | |
foreach($fks as $fk) { | |
if ($fk->TABLE_NAME==$table->Name AND $fk->REFERENCED_TABLE_NAME) { | |
$table->ForeignKeys[] = $fk; | |
} | |
} | |
foreach(\DB::select("SHOW FULL COLUMNS FROM `{$table->Name}` ") as $col) { | |
$table->FieldNames[] = $col->Field; | |
$table->Fields[ $col->Field ] = $col; | |
} | |
$table->Fields = (object) $table->Fields; | |
$tables[ $table->Name ] = $table; | |
} | |
foreach($tables as $table_name=>$table) { | |
foreach($table->ForeignKeys as $i => $fk) { | |
$fk->Model = new \stdClass; | |
if ($fk->REFERENCED_TABLE_NAME AND isset($tables[ $fk->REFERENCED_TABLE_NAME ])) { | |
$fk->Model = $tables[ $fk->REFERENCED_TABLE_NAME ]->Model; | |
} | |
} | |
} | |
return $tables; | |
} | |
public function bladeCompile($data=[], $html='') { | |
$_generated = \Illuminate\View\Compilers\BladeCompiler::compileString($html); | |
ob_start() and extract($data, EXTR_SKIP); | |
try { eval('?> '.$_generated); } | |
catch (\Exception $e) { ob_get_clean(); throw $e; } | |
return ob_get_clean(); | |
} | |
public function generateFiles() { | |
// dd( $this->getDatabaseSchema() ); | |
$signature = sprintf("/*\n * %s\n * Generated at %s\n * To generate again, run \"php artisan {$this->signature}\".\n */", env('APP_NAME'), date('d/m/Y H:i')); | |
// dd($signature); | |
$routes = ['<?php', '', $signature, '']; | |
$tables = $this->getDatabaseSchema(); | |
foreach($tables as $table) { | |
$modelFillable = "[\n\t\t'". implode("',\n\t\t'", $table->FieldNames) ."'\n\t]"; | |
$foreignKeysMethods = []; | |
foreach($table->ForeignKeys as $fk) { | |
// $methodName = \Str::studly($fk->CONSTRAINT_NAME); | |
$methodName = \Str::of($fk->CONSTRAINT_NAME)->lower()->camel(); | |
$foreignKeysMethods[] = "public function {$methodName}() {\n\t\treturn \$this->hasOne({$fk->Model->Name}::class);\n\t}"; | |
} | |
$foreignKeysMethods = implode("\n\n\t", $foreignKeysMethods); | |
$files[] = ['path' => $table->Model->File, 'content' => <<<EOF | |
<?php | |
{$signature} | |
namespace App\Models; | |
class {$table->Model->Name} extends \Illuminate\Database\Eloquent\Model { | |
use \App\Traits\Model; | |
protected \$fillable = {$modelFillable}; | |
public function validate(\$data=[]) { | |
return \Validator::make(\$data, [ | |
'name' => ['required'], | |
]); | |
} | |
{$foreignKeysMethods} | |
} | |
EOF]; | |
$files[] = ['path' => $table->Controller->File, 'content' => <<<EOF | |
<?php | |
{$signature} | |
namespace App\Http\Controllers; | |
use Illuminate\Http\Request; | |
class {$table->Controller->Name} extends Controller | |
{ | |
/** | |
* Buscar {$table->Controller->Name} | |
* | |
* Parâmetros aceitos: | |
* search: Termo de busca | |
* page: Página | |
* orderby: Nome da coluna de parâmetro para ordenação | |
* order: asc ou desc | |
*/ | |
public function search(Request \$request) { | |
return (new {$table->Model->Namespace})->search(\$request->all()); | |
} | |
/** | |
* Retornar {$table->Controller->Name} por id | |
*/ | |
public function find(\$id) { | |
return {$table->Model->Namespace}::find(\$id); | |
} | |
/** | |
* Salvar dados de {$table->Controller->Name} | |
*/ | |
public function save(Request \$request) { | |
return (new {$table->Model->Namespace})->store(\$request->all()); | |
} | |
/** | |
* Deletar {$table->Controller->Name} | |
*/ | |
public function delete(\$id) { | |
return {$table->Model->Namespace}::find(\$id)->remove(); | |
} | |
} | |
EOF]; | |
$files[] = ['path' => "resources/nuxt/store/{$table->Model->Name}.js", 'content' => <<<EOF | |
{$signature} | |
export const state = () => ({ | |
model: {}, | |
search: { | |
search: "", | |
page: 1, | |
perpage: 10, | |
orderby: "id", | |
order: "desc", | |
}, | |
result: { | |
loading: false, | |
current_page: 1, | |
from: 1, | |
per_page: 10, | |
to: 1, | |
total: 0, | |
data: [], | |
}, | |
}); | |
export const mutations = { | |
set(state, tevep) { | |
state.tevep = tevep; | |
}, | |
}; | |
export const actions = { | |
find(id) { | |
this.\$axios.get(`/api/{$table->Route}/find/\${id}`).then(resp => { | |
// this.set(aa); | |
}); | |
}, | |
search() { | |
this.\$axios.get(`/api/{$table->Route}/search`).then(resp => { | |
// this.set(aa); | |
}); | |
}, | |
save() { | |
this.\$axios.post(`/api/{$table->Route}/save`).then(resp => { | |
// this.set(aa); | |
}); | |
}, | |
delete(id) { | |
this.\$axios.post(`/api/{$table->Route}/delete/\${id}`).then(resp => { | |
// this.set(aa); | |
}); | |
}, | |
}; | |
EOF]; | |
$routes[] = "/* {$table->Model->Namespace} routes */"; | |
$routes[] = "Route::get('{$table->Route}/search', '{$table->Controller->Namespace}@search')->name('{$table->Singular}.search');"; | |
$routes[] = "Route::get('{$table->Route}/find/{id}', '{$table->Controller->Namespace}@find')->name('{$table->Singular}.find');"; | |
$routes[] = "Route::post('{$table->Route}/save/', '{$table->Controller->Namespace}@save')->name('{$table->Singular}.save');"; | |
$routes[] = "Route::post('{$table->Route}/delete/{id}', '{$table->Controller->Namespace}@delete')->name('{$table->Singular}.delete');"; | |
$routes[] = ''; | |
} | |
foreach($files as $file) { | |
$this->comment($file['path']); | |
$file['path'] = base_path(str_replace('/', DIRECTORY_SEPARATOR, $file['path'])); | |
$info = pathinfo($file['path']); | |
if (! file_exists($info['dirname'])) { | |
mkdir($info['dirname'], 0755, true); | |
} | |
file_put_contents($file['path'], $file['content']); | |
} | |
file_put_contents(base_path('routes/api_generated.php'), implode("\n", $routes)); | |
// dd($tables); | |
} | |
/** | |
* Execute the console command. | |
* | |
* @return int | |
*/ | |
public function handle() | |
{ | |
$this->comment('Generating files'); | |
$this->generateFiles(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
Artisan::command('app:hash', function () { | |
$pass = $this->ask('Senha'); | |
$this->comment(\Illuminate\Support\Facades\Hash::make($pass)); | |
})->describe('Generate password to set in database'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Gerando rotas com PHP8 metadata | |
* https://samirmhsnv.medium.com/laravel-routing-with-controller-only-using-php-8-attributes-on-laravel-routing-d6c22cd43140 | |
*/ | |
namespace App\Http\Controllers; | |
use Illuminate\Foundation\Auth\Access\AuthorizesRequests; | |
use Illuminate\Foundation\Bus\DispatchesJobs; | |
use Illuminate\Foundation\Validation\ValidatesRequests; | |
use Illuminate\Routing\Controller as BaseController; | |
class Controller extends BaseController | |
{ | |
use AuthorizesRequests, DispatchesJobs, ValidatesRequests; | |
function __construct() | |
{ | |
$this->onInit(); | |
} | |
public function onInit() | |
{ | |
$this->autoRoutes(); | |
} | |
public function route($methods, $path, $callback) | |
{ | |
$methods = is_array($methods)? $methods: [ $methods ]; | |
$prefix = (new \ReflectionClass($this))->getShortName(); | |
$prefix = (string) \Str::of(str_replace('Controller', '', $prefix))->studly()->kebab(); | |
if (! $prefix) return; | |
$path = $prefix .'/'. trim($path, '/'); | |
$call = [get_class($this), $callback]; | |
$name = $prefix .'-'. (string) \Str::of($callback)->kebab(); | |
return Route::match($methods, $path, $call)->name($name); | |
} | |
public function autoRoutes($params = []) | |
{ | |
$params = array_merge([ | |
'except' => [], | |
], $params); | |
if (!in_array('find', $params['except'])) { | |
$this->route('get', '/find/:id', 'find'); | |
} | |
if (!in_array('search', $params['except'])) { | |
$this->route('get', '/search', 'search'); | |
} | |
if (!in_array('save', $params['except'])) { | |
$this->route('post', '/save', 'save'); | |
} | |
if (!in_array('delete', $params['except'])) { | |
$this->route('post', '/delete', 'delete'); | |
} | |
} | |
public function find($id) | |
{ | |
return func_get_args(); | |
} | |
public function search() | |
{ | |
return func_get_args(); | |
} | |
public function save() | |
{ | |
return func_get_args(); | |
} | |
public function delete() | |
{ | |
return func_get_args(); | |
} | |
} | |
// app/Http/Controllers/TestController.php | |
namespace App\Http\Controllers; | |
use Illuminate\Http\Request; | |
use Illuminate\Support\Facades\Route; | |
class TestController extends Controller | |
{ | |
public function onInit() | |
{ | |
// api/test/random | |
// $this->route($method, $path, $function) | |
$this->route('get', 'random', 'random'); | |
} | |
public function random(Request $request) | |
{ | |
$return['start'] = rand(0, 99); | |
$return['final'] = rand(0, 99); | |
$return['items'] = array_map(function($number) { | |
$return['number'] = $number; | |
return $return; | |
}, range($return['start'], $return['final'])); | |
return $return; | |
} | |
} | |
// routes/api.php | |
new \App\Http\Controllers\TestController; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// https://stackoverflow.com/questions/50639622/set-default-host-value-for-php-artisan-serve | |
// 1) php artisan make:command CustomServeCommand | |
// 2) customize this file if needed | |
// 3) php artisan serve | |
namespace App\Console\Commands; | |
use Illuminate\Foundation\Console\ServeCommand; | |
use Symfony\Component\Console\Input\InputOption; | |
class CustomServeCommand extends ServeCommand | |
{ | |
/** | |
* Get the console command options. | |
* | |
* @return array | |
*/ | |
protected function getOptions() | |
{ | |
return [ | |
['host', null, InputOption::VALUE_OPTIONAL, 'The host address to serve the application on.', '0.0.0.0'],//default 127.0.0.1 | |
['port', null, InputOption::VALUE_OPTIONAL, 'The port to serve the application on.', 8000], | |
]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
function ddd() { | |
call_user_func_array('dump', func_get_args()); | |
$lines[] = 'document.querySelectorAll(".sf-dump-str-collapse").forEach(item => { console.log(item); });'; | |
$lines[] = 'document.querySelectorAll(".sf-dump-compact").forEach(item => { item.className = "sf-dump-expanded"; });'; | |
$lines[] = 'document.querySelectorAll(".sf-dump-str").forEach(item => { item.className = "sf-dump-str sf-dump-str-expand"; });'; | |
echo "<script>\n" . implode("\n", $lines) . "\n</script>"; die; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public function validate() { | |
$rules = [ | |
'name' => 'required', | |
'email' => "email:rfc,dns|unique:users,email,{$this->id}", | |
]; | |
$messages = [ | |
'name.required' => 'Informe o nome', | |
'email.email' => 'E-mail inválido', | |
'email.unique' => 'E-mail já utilizado', | |
]; | |
$validator = \Validator::make($this->toArray(), $rules, $messages); | |
if ($validator->fails()) { | |
throw new \Exception(json_encode($validator->errors())); | |
} | |
return false; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// Filename: app/Providers/AppServiceProvider.php | |
/* | |
* Ex: module Test with routes: | |
* 1) Create dir app/Mods/Test | |
* 2) Create file app/Mods/Test/Http/routes.php | |
*/ | |
namespace App\Providers; | |
use Illuminate\Support\ServiceProvider; | |
class AppServiceProvider extends ServiceProvider | |
{ | |
public $dirmods = 'Mods'; | |
public $modules = array(); | |
/** | |
* Bootstrap any application services. | |
* | |
* @return void | |
*/ | |
public function boot() | |
{ | |
foreach($this->modules as $mod) { | |
// Views | |
if ($mod['views']) { | |
$this->loadViewsFrom($mod['views'], $mod['name']); | |
} | |
// Lang | |
if ($mod['lang']) { | |
$this->loadTranslationsFrom($mod['views'], $mod['name']); | |
} | |
// Routes | |
if ($mod['routes']) { | |
\Route::group(['middleware'=>['web']], function() use($mod) { | |
include $mod['routes']; | |
}); | |
} | |
} | |
// dd($this); | |
} | |
/** | |
* Register any application services. | |
* | |
* @return void | |
*/ | |
public function register() | |
{ | |
foreach(glob(__DIR__ . "/../{$this->dirmods}/*") as $dir) { | |
$dir = realpath($dir); | |
if (is_dir($dir)) { | |
$namespace = basename($dir); | |
if ($namespace[0]=='_') continue; | |
$this->modules[] = [ | |
'dir' => $dir, | |
'namespace' => $namespace, | |
'name' => strtolower($namespace), | |
'url' => asset("/{$this->dirmods}/{$namespace}"), | |
'serviceProvider' => realpath(app_path("/{$this->dirmods}/{$namespace}/Providers/{$namespace}ServiceProvider.php"))? "\\App\\{$this->dirmods}\\{$namespace}\\Providers\\{$namespace}ServiceProvider": null, | |
'routes' => realpath(app_path("/{$this->dirmods}/{$namespace}/Http/routes.php")), | |
'views' => realpath(app_path("/{$this->dirmods}/{$namespace}/Resources/views")), | |
'lang' => realpath(app_path("/{$this->dirmods}/{$namespace}/Resources/lang")), | |
]; | |
} | |
} | |
foreach($this->modules as $mod) { | |
if ($mod['serviceProvider']) { | |
$this->app->register($mod['serviceProvider']); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// Baseado em https://github.com/williamoliveira/eloquent-array-query-builder | |
class Model extends Eloquent { | |
public function scopeQuerySearch($query) { | |
return $query; | |
} | |
} | |
Model::querySearch([ | |
// Predefined fields | |
'select' => 'id,name', | |
'orderby' => 'id:desc', // order by id desc | |
'orderby' => ['id:desc', 'name:asc'], // order by id desc, name asc | |
'page' => 1, | |
'perpage' => 10, | |
'q' => 'test', // where any field like '%{$q}%' | |
// if field is a $fillable item: | |
'age' => 35, // where age=35 | |
'age' => [35,36], // where age in (35, 36) | |
'age' => ['op' => '=', 'value' => 35], // where age=35 | |
'age' => ['op' => '>', 'value' => 35], // where age > 35 | |
'age' => ['op' => '>=', 'value' => 35], // where age >= 35 | |
'age' => ['op' => '<', 'value' => 35], // where age < 35 | |
'age' => ['op' => '<=', 'value' => 35], // where age <= 35 | |
'age' => ['op' => 'in', 'value' => [35, 36]], // where age in (35, 36) | |
'age' => ['op' => 'between', 'value' => [35, 36]], // where age between (35, 36) | |
'age' => ['op' => 'empty'], // where (age is null or age='') | |
'age' => [ | |
'relation' => 'or', | |
['field'=>'age', 'op'=>'=', 'value'=>35], | |
['field'=>'age', 'op'=>'=', 'value'=>36], | |
], // where ((age = 35) or (age = 36)) | |
'name' => ['op' => 'like', 'value' => 'john'], // where name like '%john%' | |
]); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Models; | |
use Illuminate\Database\Eloquent\Model; | |
class Example extends Model | |
{ | |
// Retorna SQL final | |
// $model->where('active', 1)->toRawSql(); | |
public function scopeToRawSql($query) { | |
return call_user_func(function($query) { | |
$sql = $query->toSql(); | |
$bindings = $query->getBindings(); | |
$needle = '?'; | |
foreach ($bindings as $replace){ | |
$pos = strpos($sql, $needle); | |
if ($pos !== false) { | |
if (gettype($replace) === "string") { | |
$replace = ' "'.addslashes($replace).'" '; | |
} | |
$sql = substr_replace($sql, $replace, $pos, strlen($needle)); | |
} | |
} | |
return $sql; | |
}, $query); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
Works like ->with() method, but using joins instead subqueries | |
*/ | |
class Product extends Eloquent { | |
public function scopeWithJoin($query, $joins=[], $side='left') { | |
foreach($joins as $join) { | |
$e = explode(':', $join); | |
$method = $e[0]; | |
$fields = isset($e[1])? $e[1]: ''; | |
$fields = array_filter(explode(',', $fields), 'strlen'); | |
$build = call_user_func([$this, $method]); | |
if (! is_a($build, 'Illuminate\Database\Eloquent\Relations\BelongsTo')) { | |
continue; | |
} | |
$related = $build->getRelated(); | |
$parent = $build->getParent(); | |
$related_field = implode('.', [$related->getTable(), $related->getKeyName()]); | |
$there_field = implode('.', [$parent->getTable(), $build->getForeignKey()]); | |
$query->leftJoin($related->getTable(), $related_field, '=', $there_field); | |
$fields = empty($fields)? $related->getFillable(): $fields; | |
$selectRaw = array_map(function($field) use($related) { | |
if (strpos($field, ' as ') !== false) { | |
return $related->getTable() .'.'. $field; | |
} | |
return implode('.', [$related->getTable(), $field]) .' as '. implode('_', [$related->getTable(), $field]); | |
}, $fields); | |
$query->selectRaw(implode(', ', $selectRaw)); | |
} | |
return $query; | |
} | |
} | |
// How to use | |
Product::withJoin([ | |
'category:id,slug,name', | |
'image', | |
]); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Search; | |
use Illuminate\Support\Str; | |
class Search | |
{ | |
public $model = null; | |
public $params = null; | |
public $builder = null; | |
public function __construct($params = [], $model = null) | |
{ | |
$this->model = $model ? $model : $this->model; | |
$this->params = $this->paramsDefault($params); | |
$this->builder = $this->builder(); | |
} | |
public function params() | |
{ | |
return []; | |
} | |
public function search($query, $params) | |
{ | |
return $query; | |
} | |
public function options($query, $params, $options) | |
{ | |
return $options; | |
} | |
public function builder() | |
{ | |
$query = app($this->model)->query(); | |
$model = $query->getModel(); | |
$params = $this->params; | |
$query = $this->search($query, $params); | |
// ?q=the+terms | |
if ($params->q) { | |
$query->where(function ($q) use ($params, $model) { | |
$terms = array_values(array_filter(preg_split('/[^a-zA-Z0-9]/', $params->q))); | |
foreach ($model->getFillable() as $field) { | |
foreach ($terms as $term) { | |
if (empty ($q->getQuery()->wheres)) { | |
$q->where($field, 'like', "%{$term}%"); | |
continue; | |
} | |
$q->orWhere($field, 'like', "%{$term}%"); | |
} | |
} | |
}); | |
} | |
// ?find=123 | |
if ($params->find) { | |
$query->where(function ($q) use ($params, $model) { | |
$q->where('id', $params->find); | |
if (in_array('slug', $model->getFillable())) { | |
$q->orWhere('slug', $params->find); | |
} | |
}); | |
} | |
// ?hidden=field1,field2 | |
// ?hidden[]=field1&hidden[]=field2 | |
if ($params->hidden) { | |
$hidden = is_array($params->hidden) ? $params->hidden : explode(',', $params->hidden); | |
$query->exclude($hidden); | |
} | |
// ?with=relation1,relation2 | |
// ?with[]=relation1&with[]=relation2 | |
if ($params->with) { | |
$withs = is_array($params->with) ? $params->with : explode(',', $params->with); | |
$query->with($withs); | |
} | |
// ?limit=10 | |
if ($params->limit) { | |
$query->take($params->limit); | |
} | |
// ?order=id:desc,name:asc | |
// ?order[]=id:desc&order[]=name:asc | |
if ($params->order) { | |
$orders = is_array($params->order) ? $params->order : explode(',', $params->order); | |
foreach ($orders as $order) { | |
if (in_array($order, ['rand', 'random'])) { | |
$query->orderByRaw('RAND()'); | |
continue; | |
} | |
list($field, $type) = explode(':', $order); | |
$query->orderBy($field, $type); | |
} | |
} | |
return $query; | |
} | |
protected function paramsDefault($merge = []) | |
{ | |
return (object) array_merge( | |
[ | |
'q' => null, | |
'find' => null, | |
'hidden' => null, | |
'page' => 1, | |
'per_page' => 10, | |
'limit' => null, | |
'order' => 'updated_at:desc', | |
'with' => null, | |
], | |
$this->params(), | |
$merge | |
); | |
} | |
protected function optionsDefault($query, $params) | |
{ | |
$options = []; | |
$options['withs'] = []; | |
$relationTypes = [ | |
'Illuminate\Database\Eloquent\Relations\HasOne', | |
'Illuminate\Database\Eloquent\Relations\HasMany', | |
'Illuminate\Database\Eloquent\Relations\BelongsTo', | |
'Illuminate\Database\Eloquent\Relations\BelongsToMany', | |
]; | |
foreach ((new \ReflectionClass($query->getModel()))->getMethods() as $refMethod) { | |
if ($returnType = $refMethod->getReturnType()) { | |
if (in_array($returnType->getName(), $relationTypes)) { | |
$options['withs'][] = $refMethod->getName(); | |
} | |
} | |
} | |
return $this->options($query, $params, $options); | |
} | |
public function first() | |
{ | |
return $this->builder->first(); | |
} | |
public function get() | |
{ | |
return $this->builder->get(); | |
} | |
public function paginate() | |
{ | |
$query = $this->builder; | |
$params = $this->params; | |
$pagination = $query->paginate($params->per_page, ['*'], 'page', $params->page)->toArray(); | |
return [ | |
'pagination' => [ | |
'current_page' => $pagination['current_page'], | |
'last_page' => $pagination['last_page'], | |
'total' => $pagination['total'], | |
'from' => $pagination['from'], | |
'to' => $pagination['to'], | |
], | |
'data' => $pagination['data'], | |
'params' => $params, | |
'options' => $this->optionsDefault($query, $params), | |
]; | |
} | |
public function sql() | |
{ | |
return Str::replaceArray('?', collect($this->builder->getBindings()) | |
->map(function ($i) { | |
if (is_object($i)) { | |
$i = (string) $i; | |
} | |
return (is_string($i)) ? "'$i'" : $i; | |
})->all(), $this->builder->toSql()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment